1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2005 Mike Matsnev
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
19 *****************************************************************************/
23 #include "common/osdep.h"
26 #define CLSIZE 1048576
27 #define CHECK(x) do { if ((x) < 0) return -1; } while (0)
30 struct mk_Context
*next
, **prev
, *parent
;
31 struct mk_Writer
*owner
;
35 unsigned d_cur
, d_max
;
38 typedef struct mk_Context mk_Context
;
43 unsigned duration_ptr
;
45 mk_Context
*root
, *cluster
, *frame
;
51 int64_t cluster_tc_scaled
;
52 int64_t frame_tc
, prev_frame_tc_scaled
, max_frame_tc
;
54 char wrote_header
, in_frame
, keyframe
;
57 static mk_Context
*mk_createContext(mk_Writer
*w
, mk_Context
*parent
, unsigned id
) {
62 w
->freelist
= w
->freelist
->next
;
64 c
= malloc(sizeof(*c
));
65 memset(c
, 0, sizeof(*c
));
75 if (c
->owner
->actlist
)
76 c
->owner
->actlist
->prev
= &c
->next
;
77 c
->next
= c
->owner
->actlist
;
78 c
->prev
= &c
->owner
->actlist
;
79 c
->owner
->actlist
= c
;
84 static int mk_appendContextData(mk_Context
*c
, const void *data
, unsigned size
) {
85 unsigned ns
= c
->d_cur
+ size
;
89 unsigned dn
= c
->d_max
? c
->d_max
<< 1 : 16;
93 dp
= realloc(c
->data
, dn
);
101 memcpy((char*)c
->data
+ c
->d_cur
, data
, size
);
108 static int mk_writeID(mk_Context
*c
, unsigned id
) {
109 unsigned char c_id
[4] = { id
>> 24, id
>> 16, id
>> 8, id
};
112 return mk_appendContextData(c
, c_id
, 4);
114 return mk_appendContextData(c
, c_id
+1, 3);
116 return mk_appendContextData(c
, c_id
+2, 2);
117 return mk_appendContextData(c
, c_id
+3, 1);
120 static int mk_writeSize(mk_Context
*c
, unsigned size
) {
121 unsigned char c_size
[5] = { 0x08, size
>> 24, size
>> 16, size
>> 8, size
};
125 return mk_appendContextData(c
, c_size
+4, 1);
129 return mk_appendContextData(c
, c_size
+3, 2);
131 if (size
< 0x1fffff) {
133 return mk_appendContextData(c
, c_size
+2, 3);
135 if (size
< 0x0fffffff) {
137 return mk_appendContextData(c
, c_size
+1, 4);
139 return mk_appendContextData(c
, c_size
, 5);
142 static int mk_flushContextID(mk_Context
*c
) {
143 unsigned char ff
= 0xff;
148 CHECK(mk_writeID(c
->parent
, c
->id
));
149 CHECK(mk_appendContextData(c
->parent
, &ff
, 1));
156 static int mk_flushContextData(mk_Context
*c
) {
161 CHECK(mk_appendContextData(c
->parent
, c
->data
, c
->d_cur
));
163 if (fwrite(c
->data
, c
->d_cur
, 1, c
->owner
->fp
) != 1)
171 static int mk_closeContext(mk_Context
*c
, unsigned *off
) {
173 CHECK(mk_writeID(c
->parent
, c
->id
));
174 CHECK(mk_writeSize(c
->parent
, c
->d_cur
));
177 if (c
->parent
&& off
!= NULL
)
178 *off
+= c
->parent
->d_cur
;
180 CHECK(mk_flushContextData(c
));
183 c
->next
->prev
= c
->prev
;
184 *(c
->prev
) = c
->next
;
185 c
->next
= c
->owner
->freelist
;
186 c
->owner
->freelist
= c
;
191 static void mk_destroyContexts(mk_Writer
*w
) {
192 mk_Context
*cur
, *next
;
194 for (cur
= w
->freelist
; cur
; cur
= next
) {
200 for (cur
= w
->actlist
; cur
; cur
= next
) {
206 w
->freelist
= w
->actlist
= w
->root
= NULL
;
209 static int mk_writeStr(mk_Context
*c
, unsigned id
, const char *str
) {
210 size_t len
= strlen(str
);
212 CHECK(mk_writeID(c
, id
));
213 CHECK(mk_writeSize(c
, len
));
214 CHECK(mk_appendContextData(c
, str
, len
));
218 static int mk_writeBin(mk_Context
*c
, unsigned id
, const void *data
, unsigned size
) {
219 CHECK(mk_writeID(c
, id
));
220 CHECK(mk_writeSize(c
, size
));
221 CHECK(mk_appendContextData(c
, data
, size
));
225 static int mk_writeUInt(mk_Context
*c
, unsigned id
, int64_t ui
) {
226 unsigned char c_ui
[8] = { ui
>> 56, ui
>> 48, ui
>> 40, ui
>> 32, ui
>> 24, ui
>> 16, ui
>> 8, ui
};
229 CHECK(mk_writeID(c
, id
));
230 while (i
< 7 && c_ui
[i
] == 0)
232 CHECK(mk_writeSize(c
, 8 - i
));
233 CHECK(mk_appendContextData(c
, c_ui
+i
, 8 - i
));
237 static int mk_writeSInt(mk_Context
*c
, unsigned id
, int64_t si
) {
238 unsigned char c_si
[8] = { si
>> 56, si
>> 48, si
>> 40, si
>> 32, si
>> 24, si
>> 16, si
>> 8, si
};
241 CHECK(mk_writeID(c
, id
));
243 while (i
< 7 && c_si
[i
] == 0xff && c_si
[i
+1] & 0x80)
246 while (i
< 7 && c_si
[i
] == 0 && !(c_si
[i
+1] & 0x80))
248 CHECK(mk_writeSize(c
, 8 - i
));
249 CHECK(mk_appendContextData(c
, c_si
+i
, 8 - i
));
253 static int mk_writeFloatRaw(mk_Context
*c
, float f
) {
258 unsigned char c_f
[4];
266 return mk_appendContextData(c
, c_f
, 4);
269 static int mk_writeFloat(mk_Context
*c
, unsigned id
, float f
) {
270 CHECK(mk_writeID(c
, id
));
271 CHECK(mk_writeSize(c
, 4));
272 CHECK(mk_writeFloatRaw(c
, f
));
276 static unsigned mk_ebmlSizeSize(unsigned s
) {
288 static unsigned mk_ebmlSIntSize(int64_t si
) {
289 unsigned char c_si
[8] = { si
>> 56, si
>> 48, si
>> 40, si
>> 32, si
>> 24, si
>> 16, si
>> 8, si
};
293 while (i
< 7 && c_si
[i
] == 0xff && c_si
[i
+1] & 0x80)
296 while (i
< 7 && c_si
[i
] == 0 && !(c_si
[i
+1] & 0x80))
302 mk_Writer
*mk_createWriter(const char *filename
) {
303 mk_Writer
*w
= malloc(sizeof(*w
));
307 memset(w
, 0, sizeof(*w
));
309 w
->root
= mk_createContext(w
, NULL
, 0);
310 if (w
->root
== NULL
) {
315 w
->fp
= fopen(filename
, "wb");
317 mk_destroyContexts(w
);
322 w
->timescale
= 1000000;
327 int mk_writeHeader(mk_Writer
*w
, const char *writingApp
,
329 const void *codecPrivate
, unsigned codecPrivateSize
,
330 int64_t default_frame_duration
,
332 unsigned width
, unsigned height
,
333 unsigned d_width
, unsigned d_height
)
335 mk_Context
*c
, *ti
, *v
;
340 w
->timescale
= timescale
;
341 w
->def_duration
= default_frame_duration
;
343 if ((c
= mk_createContext(w
, w
->root
, 0x1a45dfa3)) == NULL
) // EBML
345 CHECK(mk_writeUInt(c
, 0x4286, 1)); // EBMLVersion
346 CHECK(mk_writeUInt(c
, 0x42f7, 1)); // EBMLReadVersion
347 CHECK(mk_writeUInt(c
, 0x42f2, 4)); // EBMLMaxIDLength
348 CHECK(mk_writeUInt(c
, 0x42f3, 8)); // EBMLMaxSizeLength
349 CHECK(mk_writeStr(c
, 0x4282, "matroska")); // DocType
350 CHECK(mk_writeUInt(c
, 0x4287, 1)); // DocTypeVersion
351 CHECK(mk_writeUInt(c
, 0x4285, 1)); // DocTypeReadversion
352 CHECK(mk_closeContext(c
, 0));
354 if ((c
= mk_createContext(w
, w
->root
, 0x18538067)) == NULL
) // Segment
356 CHECK(mk_flushContextID(c
));
357 CHECK(mk_closeContext(c
, 0));
359 if ((c
= mk_createContext(w
, w
->root
, 0x1549a966)) == NULL
) // SegmentInfo
361 CHECK(mk_writeStr(c
, 0x4d80, "Haali Matroska Writer b0"));
362 CHECK(mk_writeStr(c
, 0x5741, writingApp
));
363 CHECK(mk_writeUInt(c
, 0x2ad7b1, w
->timescale
));
364 CHECK(mk_writeFloat(c
, 0x4489, 0));
365 w
->duration_ptr
= c
->d_cur
- 4;
366 CHECK(mk_closeContext(c
, &w
->duration_ptr
));
368 if ((c
= mk_createContext(w
, w
->root
, 0x1654ae6b)) == NULL
) // tracks
370 if ((ti
= mk_createContext(w
, c
, 0xae)) == NULL
) // TrackEntry
372 CHECK(mk_writeUInt(ti
, 0xd7, 1)); // TrackNumber
373 CHECK(mk_writeUInt(ti
, 0x73c5, 1)); // TrackUID
374 CHECK(mk_writeUInt(ti
, 0x83, 1)); // TrackType
375 CHECK(mk_writeUInt(ti
, 0x9c, 0)); // FlagLacing
376 CHECK(mk_writeStr(ti
, 0x86, codecID
)); // CodecID
377 if (codecPrivateSize
)
378 CHECK(mk_writeBin(ti
, 0x63a2, codecPrivate
, codecPrivateSize
)); // CodecPrivate
379 if (default_frame_duration
)
380 CHECK(mk_writeUInt(ti
, 0x23e383, default_frame_duration
)); // DefaultDuration
382 if ((v
= mk_createContext(w
, ti
, 0xe0)) == NULL
) // Video
384 CHECK(mk_writeUInt(v
, 0xb0, width
));
385 CHECK(mk_writeUInt(v
, 0xba, height
));
386 CHECK(mk_writeUInt(v
, 0x54b0, d_width
));
387 CHECK(mk_writeUInt(v
, 0x54ba, d_height
));
388 CHECK(mk_closeContext(v
, 0));
390 CHECK(mk_closeContext(ti
, 0));
392 CHECK(mk_closeContext(c
, 0));
394 CHECK(mk_flushContextData(w
->root
));
401 static int mk_closeCluster(mk_Writer
*w
) {
402 if (w
->cluster
== NULL
)
404 CHECK(mk_closeContext(w
->cluster
, 0));
406 CHECK(mk_flushContextData(w
->root
));
410 static int mk_flushFrame(mk_Writer
*w
) {
411 int64_t delta
, ref
= 0;
412 unsigned fsize
, bgsize
;
413 unsigned char c_delta_flags
[3];
418 delta
= w
->frame_tc
/w
->timescale
- w
->cluster_tc_scaled
;
419 if (delta
> 32767ll || delta
< -32768ll)
420 CHECK(mk_closeCluster(w
));
422 if (w
->cluster
== NULL
) {
423 w
->cluster_tc_scaled
= w
->frame_tc
/ w
->timescale
;
424 w
->cluster
= mk_createContext(w
, w
->root
, 0x1f43b675); // Cluster
425 if (w
->cluster
== NULL
)
428 CHECK(mk_writeUInt(w
->cluster
, 0xe7, w
->cluster_tc_scaled
)); // Timecode
433 fsize
= w
->frame
? w
->frame
->d_cur
: 0;
434 bgsize
= fsize
+ 4 + mk_ebmlSizeSize(fsize
+ 4) + 1;
436 ref
= w
->prev_frame_tc_scaled
- w
->cluster_tc_scaled
- delta
;
437 bgsize
+= 1 + 1 + mk_ebmlSIntSize(ref
);
440 CHECK(mk_writeID(w
->cluster
, 0xa0)); // BlockGroup
441 CHECK(mk_writeSize(w
->cluster
, bgsize
));
442 CHECK(mk_writeID(w
->cluster
, 0xa1)); // Block
443 CHECK(mk_writeSize(w
->cluster
, fsize
+ 4));
444 CHECK(mk_writeSize(w
->cluster
, 1)); // track number
446 c_delta_flags
[0] = delta
>> 8;
447 c_delta_flags
[1] = delta
;
448 c_delta_flags
[2] = 0;
449 CHECK(mk_appendContextData(w
->cluster
, c_delta_flags
, 3));
451 CHECK(mk_appendContextData(w
->cluster
, w
->frame
->data
, w
->frame
->d_cur
));
455 CHECK(mk_writeSInt(w
->cluster
, 0xfb, ref
)); // ReferenceBlock
458 w
->prev_frame_tc_scaled
= w
->cluster_tc_scaled
+ delta
;
460 if (w
->cluster
->d_cur
> CLSIZE
)
461 CHECK(mk_closeCluster(w
));
466 int mk_startFrame(mk_Writer
*w
) {
467 if (mk_flushFrame(w
) < 0)
476 int mk_setFrameFlags(mk_Writer
*w
,int64_t timestamp
, int keyframe
) {
480 w
->frame_tc
= timestamp
;
481 w
->keyframe
= keyframe
!= 0;
483 if (w
->max_frame_tc
< timestamp
)
484 w
->max_frame_tc
= timestamp
;
489 int mk_addFrameData(mk_Writer
*w
, const void *data
, unsigned size
) {
493 if (w
->frame
== NULL
)
494 if ((w
->frame
= mk_createContext(w
, NULL
, 0)) == NULL
)
497 return mk_appendContextData(w
->frame
, data
, size
);
500 int mk_close(mk_Writer
*w
) {
502 if (mk_flushFrame(w
) < 0 || mk_closeCluster(w
) < 0)
504 if (w
->wrote_header
) {
505 fseek(w
->fp
, w
->duration_ptr
, SEEK_SET
);
506 if (mk_writeFloatRaw(w
->root
, (float)((double)(w
->max_frame_tc
+w
->def_duration
) / w
->timescale
)) < 0 ||
507 mk_flushContextData(w
->root
) < 0)
510 mk_destroyContexts(w
);