From: Laurent Aimar Date: Fri, 22 Jul 2005 15:51:10 +0000 (+0000) Subject: * all: Added matroska writing. Patch by Mike Matsnev. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d945b153baa4a81cb40a92e4c09b0e2f16081408;p=libx264 * all: Added matroska writing. Patch by Mike Matsnev. git-svn-id: svn://svn.videolan.org/x264/trunk@280 df754926-b1dd-0310-bc7b-ec298dee348c --- diff --git a/Makefile b/Makefile index b80570e1..561aef8b 100644 --- a/Makefile +++ b/Makefile @@ -59,8 +59,8 @@ libx264.a: .depend $(OBJS) $(OBJASM) ar rc libx264.a $(OBJS) $(OBJASM) ranlib libx264.a -x264$(EXE): libx264.a x264.o - $(CC) -o $@ x264.o libx264.a $(LDFLAGS) +x264$(EXE): libx264.a x264.o matroska.o + $(CC) -o $@ x264.o matroska.o libx264.a $(LDFLAGS) x264vfw.dll: libx264.a $(wildcard vfw/*.c vfw/*.h) make -C vfw/build/cygwin @@ -74,7 +74,7 @@ checkasm: testing/checkasm.o libx264.a .depend: config.mak config.h rm -f .depend # Hacky - because gcc 2.9x doesn't have -MT - $(foreach SRC, $(SRCS) x264.c, ( echo -n "`dirname $(SRC)`/" && $(CC) $(CFLAGS) $(SRC) -MM -g0 ) 1>> .depend;) + $(foreach SRC, $(SRCS) x264.c matroska.c, ( echo -n "`dirname $(SRC)`/" && $(CC) $(CFLAGS) $(SRC) -MM -g0 ) 1>> .depend;) config.h: $(wildcard .svn/entries */.svn/entries */*/.svn/entries) ./version.sh @@ -85,7 +85,7 @@ include .depend endif clean: - rm -f $(OBJS) $(OBJASM) config.h *.a x264.o x264 x264.exe .depend TAGS + rm -f $(OBJS) $(OBJASM) config.h *.a x264.o matroska.o x264 x264.exe .depend TAGS rm -rf vfw/build/cygwin/bin distclean: clean diff --git a/matroska.c b/matroska.c new file mode 100755 index 00000000..95add2d1 --- /dev/null +++ b/matroska.c @@ -0,0 +1,525 @@ +/***************************************************************************** + * matroska.c: + ***************************************************************************** + * Copyright (C) 2005 x264 project + * $Id: $ + * + * Authors: Mike Matsnev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#else +#include +#endif + +#include "matroska.h" + +#define CLSIZE 1048576 + +struct mk_Context { + struct mk_Context *next, **prev, *parent; + struct mk_Writer *owner; + unsigned id; + + void *data; + unsigned d_cur, d_max; +}; + +typedef struct mk_Context mk_Context; + +struct mk_Writer { + FILE *fp; + + unsigned duration_ptr; + + mk_Context *root, *cluster, *frame; + mk_Context *freelist; + mk_Context *actlist; + + int64_t def_duration; + int64_t timescale; + int64_t cluster_tc_scaled; + int64_t frame_tc, prev_frame_tc_scaled, max_frame_tc; + + char wrote_header, in_frame, keyframe; +}; + +static mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) { + mk_Context *c; + + if (w->freelist) { + c = w->freelist; + w->freelist = w->freelist->next; + } else { + c = malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + } + + if (c == NULL) + return NULL; + + c->parent = parent; + c->owner = w; + c->id = id; + + if (c->owner->actlist) + c->owner->actlist->prev = &c->next; + c->next = c->owner->actlist; + c->prev = &c->owner->actlist; + + return c; +} + +static int mk_appendContextData(mk_Context *c, const void *data, unsigned size) { + unsigned ns = c->d_cur + size; + + if (ns > c->d_max) { + void *dp; + unsigned dn = c->d_max ? c->d_max << 1 : 16; + while (ns > dn) + dn <<= 1; + + dp = realloc(c->data, dn); + if (dp == NULL) + return -1; + + c->data = dp; + c->d_max = dn; + } + + memcpy((char*)c->data + c->d_cur, data, size); + + c->d_cur = ns; + + return 0; +} + +static void mk_writeID(mk_Context *c, unsigned id) { + unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id }; + + if (c_id[0]) + mk_appendContextData(c, c_id, 4); + else if (c_id[1]) + mk_appendContextData(c, c_id+1, 3); + else if (c_id[2]) + mk_appendContextData(c, c_id+2, 2); + else + mk_appendContextData(c, c_id+3, 1); +} + +static void mk_writeSize(mk_Context *c, unsigned size) { + unsigned char c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size }; + + if (size < 0x7f) { + c_size[4] |= 0x80; + mk_appendContextData(c, c_size+4, 1); + } else if (size < 0x3fff) { + c_size[3] |= 0x40; + mk_appendContextData(c, c_size+3, 2); + } else if (size < 0x1fffff) { + c_size[2] |= 0x20; + mk_appendContextData(c, c_size+2, 3); + } else if (size < 0x0fffffff) { + c_size[1] |= 0x10; + mk_appendContextData(c, c_size+1, 4); + } else + mk_appendContextData(c, c_size, 5); +} + +static void mk_flushContextID(mk_Context *c) { + unsigned char ff = 0xff; + + if (c->id == 0) + return; + + mk_writeID(c->parent, c->id); + mk_appendContextData(c->parent, &ff, 1); + + c->id = 0; +} + +static void mk_flushContextData(mk_Context *c) { + if (c->d_cur == 0) + return; + + if (c->parent) + mk_appendContextData(c->parent, c->data, c->d_cur); + else + fwrite(c->data, c->d_cur, 1, c->owner->fp); + + c->d_cur = 0; +} + +static unsigned mk_closeContext(mk_Context *c, unsigned off) { + if (c->id) { + mk_writeID(c->parent, c->id); + mk_writeSize(c->parent, c->d_cur); + } + + if (c->parent) + off += c->parent->d_cur; + + mk_flushContextData(c); + + if (c->next) + c->next->prev = c->prev; + *(c->prev) = c->next; + c->next = c->owner->freelist; + c->owner->freelist = c; + + return off; +} + +static void mk_destroyContexts(mk_Writer *w) { + mk_Context *cur, *next; + + for (cur = w->freelist; cur; cur = next) { + next = cur->next; + free(cur->data); + free(cur); + } + + for (cur = w->actlist; cur; cur = next) { + next = cur->next; + free(cur->data); + free(cur); + } + + w->freelist = w->actlist = w->root = NULL; +} + +static void mk_writeStr(mk_Context *c, unsigned id, const char *str) { + size_t len = strlen(str); + + mk_writeID(c, id); + mk_writeSize(c, len); + mk_appendContextData(c, str, len); +} + +static void mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) { + mk_writeID(c, id); + mk_writeSize(c, size); + mk_appendContextData(c, data, size); +} + +static void mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui) { + unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui }; + unsigned i = 0; + + mk_writeID(c, id); + while (i < 7 && c_ui[i] == 0) + ++i; + mk_writeSize(c, 8 - i); + mk_appendContextData(c, c_ui+i, 8 - i); +} + +static void mk_writeSInt(mk_Context *c, unsigned id, int64_t si) { + unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si }; + unsigned i = 0; + + mk_writeID(c, id); + if (si < 0) + while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80) + ++i; + else + while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80)) + ++i; + mk_writeSize(c, 8 - i); + mk_appendContextData(c, c_si+i, 8 - i); +} + +static void mk_writeFloatRaw(mk_Context *c, float f) { + union { + float f; + unsigned u; + } u; + unsigned char c_f[4]; + + u.f = f; + c_f[0] = u.u >> 24; + c_f[1] = u.u >> 16; + c_f[2] = u.u >> 8; + c_f[3] = u.u; + + mk_appendContextData(c, c_f, 4); +} + +static void mk_writeFloat(mk_Context *c, unsigned id, float f) { + mk_writeID(c, id); + mk_writeSize(c, 4); + mk_writeFloatRaw(c, f); +} + +static unsigned mk_ebmlSizeSize(unsigned s) { + if (s < 0x7f) + return 1; + if (s < 0x3fff) + return 2; + if (s < 0x1fffff) + return 3; + if (s < 0x0fffffff) + return 4; + return 5; +} + +static unsigned mk_ebmlSIntSize(int64_t si) { + unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si }; + unsigned i = 0; + + if (si < 0) + while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80) + ++i; + else + while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80)) + ++i; + + return 8 - i; +} + +mk_Writer *mk_createWriter(const char *filename) { + mk_Writer *w = malloc(sizeof(*w)); + if (w == NULL) + return NULL; + + memset(w, 0, sizeof(*w)); + + w->root = mk_createContext(w, NULL, 0); + if (w->root == NULL) { + free(w); + return NULL; + } + + w->fp = fopen(filename, "wb"); + if (w->fp == NULL) { + mk_destroyContexts(w); + free(w); + return NULL; + } + + w->timescale = 1000000; + + return w; +} + +int mk_writeHeader(mk_Writer *w, const char *writingApp, + const char *codecID, + const void *codecPrivate, unsigned codecPrivateSize, + int64_t default_frame_duration, + int64_t timescale, + unsigned width, unsigned height, + unsigned d_width, unsigned d_height) +{ + mk_Context *c, *ti, *v; + + if (w->wrote_header) + return -1; + + w->timescale = timescale; + w->def_duration = default_frame_duration; + + if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML + return -1; + mk_writeUInt(c, 0x4286, 1); // EBMLVersion + mk_writeUInt(c, 0x42f7, 1); // EBMLReadVersion + mk_writeUInt(c, 0x42f2, 4); // EBMLMaxIDLength + mk_writeUInt(c, 0x42f3, 8); // EBMLMaxSizeLength + mk_writeStr(c, 0x4282, "matroska"); // DocType + mk_writeUInt(c, 0x4287, 1); // DocTypeVersion + mk_writeUInt(c, 0x4285, 1); // DocTypeReadversion + mk_closeContext(c, 0); + + if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment + return -1; + mk_flushContextID(c); + mk_closeContext(c, 0); + + if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo + return -1; + mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0"); + mk_writeStr(c, 0x5741, writingApp); + mk_writeUInt(c, 0x2ad7b1, w->timescale); + mk_writeFloat(c, 0x4489, 0); + w->duration_ptr = mk_closeContext(c, c->d_cur - 4); + + if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks + return -1; + if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry + return -1; + mk_writeUInt(ti, 0xd7, 1); // TrackNumber + mk_writeUInt(ti, 0x73c5, 1); // TrackUID + mk_writeUInt(ti, 0x83, 1); // TrackType + mk_writeUInt(ti, 0x9c, 0); // FlagLacing + mk_writeStr(ti, 0x86, codecID); // CodecID + if (codecPrivateSize) + mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize); // CodecPrivate + if (default_frame_duration) + mk_writeUInt(ti, 0x23e383, default_frame_duration); // DefaultDuration + + if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video + return -1; + mk_writeUInt(v, 0xb0, width); + mk_writeUInt(v, 0xba, height); + mk_writeUInt(v, 0x54b0, d_width); + mk_writeUInt(v, 0x54ba, d_height); + mk_closeContext(v, 0); + + mk_closeContext(ti, 0); + + mk_closeContext(c, 0); + + mk_flushContextData(w->root); + + w->wrote_header = 1; + + return 0; +} + +static void mk_closeCluster(mk_Writer *w) { + if (w->cluster == NULL) + return; + mk_closeContext(w->cluster, 0); + w->cluster = NULL; + mk_flushContextData(w->root); +} + +int mk_flushFrame(mk_Writer *w) +{ + int64_t delta, ref = 0; + unsigned fsize, bgsize; + unsigned char c_delta_flags[3]; + + if (!w->in_frame) + return 0; + + delta = w->frame_tc/w->timescale - w->cluster_tc_scaled; + if (delta > 32767ll || delta < -32768ll) + mk_closeCluster(w); + + if (w->cluster == NULL) { + w->cluster_tc_scaled = w->frame_tc / w->timescale; + w->cluster = mk_createContext(w, w->root, 0x1f43b675); // Cluster + if (w->cluster == NULL) + return -1; + + mk_writeUInt(w->cluster, 0xe7, w->cluster_tc_scaled); // Timecode + + delta = 0; + } + + fsize = w->frame ? w->frame->d_cur : 0; + bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1; + if (!w->keyframe) { + ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta; + bgsize += 1 + 1 + mk_ebmlSIntSize(ref); + } + + mk_writeID(w->cluster, 0xa0); // BlockGroup + mk_writeSize(w->cluster, bgsize); + mk_writeID(w->cluster, 0xa1); // Block + mk_writeSize(w->cluster, fsize + 4); + mk_writeSize(w->cluster, 1); // track number + + c_delta_flags[0] = delta >> 8; + c_delta_flags[1] = delta; + c_delta_flags[2] = 0; + mk_appendContextData(w->cluster, c_delta_flags, 3); + if (w->frame) { + mk_appendContextData(w->cluster, w->frame->data, w->frame->d_cur); + w->frame->d_cur = 0; + } + if (!w->keyframe) + mk_writeSInt(w->cluster, 0xfb, ref); // ReferenceBlock + + w->in_frame = 0; + w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta; + + if (w->cluster->d_cur > CLSIZE) + mk_closeCluster(w); + + return 0; +} + +int mk_startFrame(mk_Writer *w) { + if (mk_flushFrame(w) < 0) + return -1; + + w->in_frame = 1; + w->keyframe = 0; + + return 0; +} + +int mk_setFrameFlags(mk_Writer *w,int64_t timestamp, int keyframe) { + if (!w->in_frame) + return -1; + + w->frame_tc = timestamp; + w->keyframe = keyframe != 0; + + if (w->max_frame_tc < timestamp) + w->max_frame_tc = timestamp; + + return 0; +} + +int mk_addFrameData(mk_Writer *w, const void *data, unsigned size) { + if (!w->in_frame) + return -1; + + if (w->frame == NULL) + if ((w->frame = mk_createContext(w, NULL, 0)) == NULL) + return -1; + + return mk_appendContextData(w->frame, data, size); +} + +void mk_close(mk_Writer *w) { + mk_flushFrame(w); + mk_closeCluster(w); + if (w->wrote_header) { + fseek(w->fp, w->duration_ptr, SEEK_SET); + mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)); + mk_flushContextData(w->root); + } + mk_destroyContexts(w); + fclose(w->fp); + free(w); +} + +#ifdef TESTING +int main(int argc, char **argv) { + char f[4] = { 1, 2, 3, 4 }; + mk_Writer *w = mk_createWriter("foo.mkv"); + mk_writeHeader(w, "mktest", "V_MPEG/ISO/AVC", NULL, 0, 40000000, 352, 288, 3, 4); + mk_startFrame(w); + mk_setFrameFlags(w, 0, 1); + mk_addFrameData(w, f, 1); + mk_startFrame(w); + mk_setFrameFlags(w, 40000000, 0); + mk_addFrameData(w, f, 2); + mk_startFrame(w); + mk_setFrameFlags(w, 80000000, 0); + mk_addFrameData(w, f, 4); + mk_close(w); + return 0; +} +#endif diff --git a/matroska.h b/matroska.h new file mode 100755 index 00000000..61a62239 --- /dev/null +++ b/matroska.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * matroska.c: + ***************************************************************************** + * Copyright (C) 2005 x264 project + * $Id: $ + * + * Authors: Mike Matsnev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef _MATROSKA_H +#define _MATROSKA_H 1 + +typedef struct mk_Writer mk_Writer; + +mk_Writer *mk_createWriter( const char *filename ); + +int mk_writeHeader( mk_Writer *w, const char *writingApp, + const char *codecID, + const void *codecPrivate, unsigned codecPrivateSize, + int64_t default_frame_duration, + int64_t timescale, + unsigned width, unsigned height, + unsigned d_width, unsigned d_height ); + +int mk_startFrame( mk_Writer *w ); +int mk_addFrameData( mk_Writer *w, const void *data, unsigned size ); +int mk_setFrameFlags( mk_Writer *w, int64_t timestamp, int keyframe ); +void mk_close( mk_Writer *w ); + +#endif diff --git a/x264.c b/x264.c index f0ba4b81..d580f482 100644 --- a/x264.c +++ b/x264.c @@ -45,12 +45,16 @@ #include #endif + #include "common/common.h" #include "x264.h" + #ifndef _MSC_VER #include "config.h" #endif +#include "matroska.h" + #define DATA_MAX 3000000 uint8_t data[DATA_MAX]; @@ -114,6 +118,12 @@ static int set_eop_mp4( hnd_t handle, x264_picture_t *p_picture ); static int close_file_mp4( hnd_t handle ); #endif +static int open_file_mkv( char *psz_filename, hnd_t *p_handle ); +static int set_param_mkv( hnd_t handle, x264_param_t *p_param ); +static int write_nalu_mkv( hnd_t handle, uint8_t *p_nal, int i_size ); +static int set_eop_mkv( hnd_t handle, x264_picture_t *p_picture ); +static int close_file_mkv( hnd_t handle ); + static void Help( x264_param_t *defaults ); static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); static int Encode( x264_param_t *param, cli_opt_t *opt ); @@ -157,6 +167,7 @@ static void Help( x264_param_t *defaults ) " or AVI or Avisynth if compiled with AVIS support (%s).\n" "Outfile type is selected by filename:\n" " .264 -> Raw bytestream\n" + " .mkv -> Matroska\n" " .mp4 -> MP4 if compiled with GPAC support (%s)\n" "\n" "Options:\n" @@ -570,6 +581,14 @@ static int Parse( int argc, char **argv, return -1; #endif } + else if( !strncasecmp(optarg + strlen(optarg) - 4, ".mkv", 4) ) + { + p_open_outfile = open_file_mkv; + p_write_nalu = write_nalu_mkv; + p_set_outfile_param = set_param_mkv; + p_set_eop = set_eop_mkv; + p_close_outfile = close_file_mkv; + } if( !strcmp(optarg, "-") ) opt->hout = stdout; else if( p_open_outfile( optarg, &opt->hout ) ) @@ -1573,3 +1592,230 @@ static int set_eop_mp4( hnd_t handle, x264_picture_t *p_picture ) } #endif + + +/* -- mkv muxing support ------------------------------------------------- */ +typedef struct +{ + mk_Writer *w; + + uint8_t *sps, *pps; + int sps_len, pps_len; + + int width, height, d_width, d_height; + + int64_t frame_duration; + int fps_num; + + int b_header_written; + char b_writing_frame; +} mkv_t; + +static int write_header_mkv( mkv_t *p_mkv ) +{ + int ret; + uint8_t *avcC; + int avcC_len; + + if( p_mkv->sps == NULL || p_mkv->pps == NULL || + p_mkv->width == 0 || p_mkv->height == 0 || + p_mkv->d_width == 0 || p_mkv->d_height == 0) + return -1; + + avcC_len = 5 + 1 + 2 + p_mkv->sps_len + 1 + 2 + p_mkv->pps_len; + avcC = malloc(avcC_len); + if (avcC == NULL) + return -1; + + avcC[0] = 1; + avcC[1] = p_mkv->sps[1]; + avcC[2] = p_mkv->sps[2]; + avcC[3] = p_mkv->sps[3]; + avcC[4] = 0xfe; // nalu size length is three bytes + avcC[5] = 0xe1; // one sps + + avcC[6] = p_mkv->sps_len >> 8; + avcC[7] = p_mkv->sps_len; + + memcpy(avcC+8, p_mkv->sps, p_mkv->sps_len); + + avcC[8+p_mkv->sps_len] = 1; // one pps + avcC[9+p_mkv->sps_len] = p_mkv->pps_len >> 8; + avcC[10+p_mkv->sps_len] = p_mkv->pps_len; + + memcpy( avcC+11+p_mkv->sps_len, p_mkv->pps, p_mkv->pps_len ); + + ret = mk_writeHeader( p_mkv->w, "x264", "V_MPEG4/ISO/AVC", + avcC, avcC_len, p_mkv->frame_duration, 50000, + p_mkv->width, p_mkv->height, + p_mkv->d_width, p_mkv->d_height ); + + free( avcC ); + + p_mkv->b_header_written = 1; + + return ret; +} + +static int open_file_mkv( char *psz_filename, hnd_t *p_handle ) +{ + mkv_t *p_mkv; + + *p_handle = NULL; + + p_mkv = malloc(sizeof(*p_mkv)); + if (p_mkv == NULL) + return -1; + + memset(p_mkv, 0, sizeof(*p_mkv)); + + p_mkv->w = mk_createWriter(psz_filename); + if (p_mkv->w == NULL) + { + free(p_mkv); + return -1; + } + + *p_handle = p_mkv; + + return 0; +} + +static int set_param_mkv( hnd_t handle, x264_param_t *p_param ) +{ + mkv_t *p_mkv = handle; + int64_t dw, dh; + + if( p_param->i_fps_num > 0 ) + { + p_mkv->frame_duration = (int64_t)p_param->i_fps_den * + (int64_t)1000000000 / p_param->i_fps_num; + p_mkv->fps_num = p_param->i_fps_num; + } + else + { + p_mkv->frame_duration = 0; + p_mkv->fps_num = 1; + } + + p_mkv->width = p_param->i_width; + p_mkv->height = p_param->i_height; + + if( p_param->vui.i_sar_width && p_param->vui.i_sar_height ) + { + dw = (int64_t)p_param->i_width * p_param->vui.i_sar_width; + dh = (int64_t)p_param->i_height * p_param->vui.i_sar_height; + } + else + { + dw = p_param->i_width; + dh = p_param->i_height; + } + + if( dw > 0 && dh > 0 ) + { + for (;;) + { + int64_t c = dw % dh; + if( c == 0 ) + break; + dw = dh; + dh = c; + } + } + + p_mkv->d_width = (int)dw; + p_mkv->d_height = (int)dh; + + return 0; +} + +static int write_nalu_mkv( hnd_t handle, uint8_t *p_nalu, int i_size ) +{ + mkv_t *p_mkv = handle; + uint8_t type = p_nalu[4] & 0x1f; + uint8_t dsize[3]; + int psize; + + switch( type ) + { + // sps + case 0x07: + if( !p_mkv->sps ) + { + p_mkv->sps = malloc(i_size - 4); + if (p_mkv->sps == NULL) + return -1; + p_mkv->sps_len = i_size - 4; + memcpy(p_mkv->sps, p_nalu + 4, i_size - 4); + } + break; + + // pps + case 0x08: + if( !p_mkv->pps ) + { + p_mkv->pps = malloc(i_size - 4); + if (p_mkv->pps == NULL) + return -1; + p_mkv->pps_len = i_size - 4; + memcpy(p_mkv->pps, p_nalu + 4, i_size - 4); + } + break; + + // slice, sei + case 0x1: + case 0x5: + case 0x6: + if (!p_mkv->b_writing_frame) + { + p_mkv->b_writing_frame = 1; + mk_startFrame(p_mkv->w); + } + psize = i_size - 4 ; + dsize[0] = psize >> 16; + dsize[1] = psize >> 8; + dsize[2] = psize; + mk_addFrameData(p_mkv->w, dsize, 3); + mk_addFrameData(p_mkv->w, p_nalu + 4, i_size - 4); + break; + + default: + break; + } + + if( !p_mkv->b_header_written && p_mkv->pps && p_mkv->sps && + !write_header_mkv(p_mkv) ) + return -1; + + return i_size; +} + +static int set_eop_mkv( hnd_t handle, x264_picture_t *p_picture ) +{ + mkv_t *p_mkv = handle; + int64_t i_stamp = (int64_t)p_picture->i_pts * (int64_t)1000000000 / + p_mkv->fps_num; + + p_mkv->b_writing_frame = 0; + + return mk_setFrameFlags( p_mkv->w, i_stamp, + p_picture->i_type == X264_TYPE_IDR ); +} + +static int close_file_mkv( hnd_t handle ) +{ + mkv_t *p_mkv = handle; + + if( p_mkv->sps ) + free( p_mkv->sps ); + if( p_mkv->pps ) + free( p_mkv->pps ); + + mk_close(p_mkv->w); + + free( p_mkv ); + + return 0; +} +