--- /dev/null
+/*****************************************************************************
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+#include <inttypes.h>
+#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
#include <gpac/m4_author.h>
#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];
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 );
" 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"
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 ) )
}
#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;
+}
+