1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
7 * Authors: Mike Matsnev
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
29 int mk_seekFile(mk_Writer
*w
, uint64_t pos
) {
30 if (fseek(w
->fp
, pos
, SEEK_SET
))
41 char *mk_laceXiph(uint64_t *sizes
, uint8_t num_frames
, uint64_t *output_size
) {
44 uint64_t alloc_size
= num_frames
* 6; // Complete guess. We'll realloc if we need more space, though.
45 char *laced
= calloc(alloc_size
, sizeof(char));
49 laced
[offset
++] = num_frames
;
50 for (i
= 0; i
< num_frames
; i
++)
52 for (j
= sizes
[i
]; j
>= 255 ; j
-= 255)
54 laced
[offset
++] = 255;
55 if (offset
+ 1 >= alloc_size
) {
56 int avg_sz
= offset
/ (i
- 1); // Compute approximate average bytes/frame
57 alloc_size
+= avg_sz
* (num_frames
- i
); // Add our average + number of frames left to size
58 if ((laced
= realloc(laced
, alloc_size
)) == NULL
)
65 if (output_size
!= NULL
)
66 *output_size
= offset
;
71 mk_Writer
*mk_createWriter(const char *filename
, int64_t timescale
, uint8_t vlc_compat
) {
72 mk_Writer
*w
= calloc(1, sizeof(*w
));
76 w
->root
= mk_createContext(w
, NULL
, 0);
77 if (w
->root
== NULL
) {
82 if ((w
->cues
= mk_createContext(w
, w
->root
, MATROSKA_ID_CUES
)) == NULL
) // Cues
84 mk_destroyContexts(w
);
90 if ((w
->cluster
.seekhead
= mk_createContext(w
, w
->root
, MATROSKA_ID_SEEKHEAD
)) == NULL
) // SeekHead
92 mk_destroyContexts(w
);
98 w
->fp
= fopen(filename
, "wb");
100 mk_destroyContexts(w
);
105 w
->timescale
= timescale
;
106 w
->vlc_compat
= vlc_compat
;
111 int mk_writeHeader(mk_Writer
*w
, const char *writingApp
) {
118 md5_starts(&w
->segment_md5
); /* Initalize MD5 */
120 CHECK(mk_writeEbmlHeader(w
, "matroska", MATROSKA_VERSION
, MATROSKA_VERSION
));
122 if ((c
= mk_createContext(w
, w
->root
, MATROSKA_ID_SEGMENT
)) == NULL
) // Segment
124 CHECK(mk_flushContextID(c
));
125 w
->segment_ptr
= c
->d_cur
;
126 CHECK(mk_closeContext(c
, &w
->segment_ptr
));
129 CHECK(mk_writeVoid(w
->root
, 0x100)); // 256 bytes should be enough room for our Seek entries.
130 CHECK(mk_writeVoid(w
->root
, 0x800)); // 2048 bytes for Chapters.
133 w
->seek_data
.seekhead
= 0x80000000;
134 CHECK(mk_writeSeekHead(w
, &w
->seekhead_ptr
));
135 w
->seek_data
.seekhead
= 0;
138 if ((c
= mk_createContext(w
, w
->root
, MATROSKA_ID_INFO
)) == NULL
) // SegmentInfo
140 w
->seek_data
.segmentinfo
= w
->root
->d_cur
- w
->segment_ptr
;
141 CHECK(mk_writeVoid(c
, 16)); /* Reserve space for a SegmentUID, we'll write the it later. */
142 CHECK(mk_writeStr(c
, MATROSKA_ID_MUXINGAPP
, PACKAGE_STRING
)); // MuxingApp
143 CHECK(mk_writeStr(c
, MATROSKA_ID_WRITINGAPP
, writingApp
)); // WritingApp
144 CHECK(mk_writeUInt(c
, MATROSKA_ID_TIMECODESCALE
, w
->timescale
)); // TimecodeScale
145 CHECK(mk_writeFloat(c
, MATROSKA_ID_DURATION
, 0)); // Duration
146 w
->duration_ptr
= c
->d_cur
- 4;
147 CHECK(mk_closeContext(c
, &offset
));
148 w
->duration_ptr
+= offset
;
149 w
->segmentuid_ptr
= offset
;
151 w
->seek_data
.tracks
= w
->root
->d_cur
- w
->segment_ptr
;
154 CHECK(mk_closeContext(w
->tracks
, 0));
156 CHECK(mk_flushContextData(w
->root
));
159 w
->def_duration
= w
->tracks_arr
[0]->default_duration
;
163 static int mk_closeCluster(mk_Writer
*w
) {
164 if (w
->cluster
.context
== NULL
)
167 CHECK(mk_closeContext(w
->cluster
.context
, 0));
168 w
->cluster
.context
= NULL
;
169 CHECK(mk_flushContextData(w
->root
));
173 int mk_flushFrame(mk_Writer
*w
, mk_Track
*track
) {
175 int64_t delta
, ref
= 0;
176 unsigned fsize
, bgsize
;
177 uint8_t flags
, c_delta
[2];
182 if (!track
->in_frame
)
185 delta
= track
->frame
.timecode
/w
->timescale
- w
->cluster
.tc_scaled
;
186 if (delta
> 2000ll || delta
< -2000ll)
187 CHECK(mk_closeCluster(w
));
189 if (w
->cluster
.context
== NULL
) {
190 w
->cluster
.tc_scaled
= track
->frame
.timecode
/ w
->timescale
;
191 w
->cluster
.context
= mk_createContext(w
, w
->root
, MATROSKA_ID_CLUSTER
); // Cluster
192 if (w
->cluster
.context
== NULL
)
195 w
->cluster
.pointer
= w
->f_pos
- w
->segment_ptr
;
198 * Dirty HACK: This is for HandBrake/VLC. The first timecode we get isnt't
199 * always 0, if not we set it to 0.
201 if (w
->cluster
.count
== 0 && w
->vlc_compat
&& track
->frame
.timecode
!= 0) {
202 w
->cluster
.tc_scaled
= 0;
203 delta
= track
->frame
.timecode
/w
->timescale
;
209 CHECK(mk_writeSeek(w
, w
->cluster
.seekhead
, MATROSKA_ID_CLUSTER
, w
->cluster
.pointer
));
211 CHECK(mk_writeUInt(w
->cluster
.context
, MATROSKA_ID_CLUSTERTIMECODE
, w
->cluster
.tc_scaled
)); // Cluster Timecode
213 w
->cluster
.block_count
= 0;
216 /* Calculate the encoded lacing sizes. */
217 switch (track
->frame
.lacing
) {
219 laced
= mk_laceXiph(track
->frame
.lacing_sizes
, track
->frame
.lacing_num_frames
, &length
);
224 length
+= mk_ebmlSizeSize(track
->frame
.lacing_sizes
[0]) + 1; // Add one for the frame count.
225 for (i
= 1; i
< track
->frame
.lacing_num_frames
; i
++)
227 u_size
= llabs(track
->frame
.lacing_sizes
[i
] - track
->frame
.lacing_sizes
[i
-1]);
228 length
+= mk_ebmlSizeSize((u_size
) << 1); // Shift by one so we get the right size for a signed number.
232 case MK_LACING_FIXED
:
234 laced
= calloc(1, sizeof(char));
235 laced
[0] = track
->frame
.lacing_num_frames
;
241 fsize
= track
->frame
.data
? track
->frame
.data
->d_cur
: 0;
242 bgsize
= fsize
+ 4 + mk_ebmlSizeSize(fsize
+ 4 + length
) + 1 + length
;
243 if (!track
->frame
.keyframe
) {
244 ref
= track
->prev_frame_tc_scaled
- w
->cluster
.tc_scaled
- delta
;
245 bgsize
+= 1 + 1 + mk_ebmlSIntSize(ref
);
248 CHECK(mk_writeID(w
->cluster
.context
, MATROSKA_ID_BLOCKGROUP
)); // BlockGroup
249 CHECK(mk_writeSize(w
->cluster
.context
, bgsize
));
250 CHECK(mk_writeID(w
->cluster
.context
, MATROSKA_ID_BLOCK
)); // Block
251 CHECK(mk_writeSize(w
->cluster
.context
, fsize
+ 4 + length
)); // Block size
252 CHECK(mk_writeSize(w
->cluster
.context
, track
->track_id
)); // track number
254 w
->cluster
.block_count
++;
256 c_delta
[0] = delta
>> 8;
258 CHECK(mk_appendContextData(w
->cluster
.context
, c_delta
, 2)); // Timecode relative to Cluster.
260 // flags = ( track->frame.keyframe << 8 ) | track->frame.lacing;
261 flags
= track
->frame
.lacing
<< 1; // Flags: Bit 5-6 describe what type of lacing to use.
262 CHECK(mk_appendContextData(w
->cluster
.context
, &flags
, 1));
263 if (track
->frame
.lacing
) {
264 if (track
->frame
.lacing
== MK_LACING_EBML
) {
265 CHECK(mk_appendContextData(w
->cluster
.context
, &track
->frame
.lacing_num_frames
, 1)); // Number of frames in lace-1
266 CHECK(mk_writeSize(w
->cluster
.context
, track
->frame
.lacing_sizes
[0])); // Size of 1st frame.
267 for (i
= 1; i
< track
->frame
.lacing_num_frames
; i
++)
269 CHECK(mk_writeSSize(w
->cluster
.context
, track
->frame
.lacing_sizes
[i
] - track
->frame
.lacing_sizes
[i
-1])); // Size difference between previous size and this size.
271 } else if (length
> 0 && laced
!= NULL
) {
272 CHECK(mk_appendContextData(w
->cluster
.context
, laced
, length
));
278 if (track
->frame
.data
) {
279 CHECK(mk_appendContextData(w
->cluster
.context
, track
->frame
.data
->data
, track
->frame
.data
->d_cur
));
280 track
->frame
.data
->d_cur
= 0;
282 if (!track
->frame
.keyframe
)
283 CHECK(mk_writeSInt(w
->cluster
.context
, MATROSKA_ID_REFERENCEBLOCK
, ref
)); // ReferenceBlock
285 /* This may get a little out of hand, but it seems sane enough for now. */
286 if (track
->frame
.keyframe
&& (track
->track_type
== MK_TRACK_VIDEO
)) {
287 // if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && ((track->prev_cue_pos + 3*CLSIZE) <= w->f_pos || track->frame.timecode == 0)) {
288 if ((c
= mk_createContext(w
, w
->cues
, MATROSKA_ID_CUEPOINT
)) == NULL
) // CuePoint
290 CHECK(mk_writeUInt(c
, MATROSKA_ID_CUETIME
, (track
->frame
.timecode
/ w
->timescale
))); // CueTime
292 if ((tp
= mk_createContext(w
, c
, MATROSKA_ID_CUETRACKPOSITIONS
)) == NULL
) // CueTrackPositions
294 CHECK(mk_writeUInt(tp
, MATROSKA_ID_CUETRACK
, track
->track_id
)); // CueTrack
295 CHECK(mk_writeUInt(tp
, MATROSKA_ID_CUECLUSTERPOSITION
, w
->cluster
.pointer
)); // CueClusterPosition
296 // CHECK(mk_writeUInt(c, MATROSKA_ID_CUEBLOCKNUMBER, w->cluster.block_count)); // CueBlockNumber
297 CHECK(mk_closeContext(tp
, 0));
298 CHECK(mk_closeContext(c
, 0));
299 track
->prev_cue_pos
= w
->f_pos
;
303 track
->prev_frame_tc_scaled
= w
->cluster
.tc_scaled
+ delta
;
305 if (w
->cluster
.context
->d_cur
> CLSIZE
)
306 CHECK(mk_closeCluster(w
));
311 int mk_startFrame(mk_Writer
*w
, mk_Track
*track
) {
312 if (mk_flushFrame(w
, track
) < 0)
316 track
->frame
.keyframe
= 0;
317 track
->frame
.lacing
= MK_LACING_NONE
;
318 track
->frame
.lacing_num_frames
= 0;
319 track
->frame
.lacing_sizes
= NULL
;
324 int mk_setFrameFlags(mk_Writer
*w
, mk_Track
*track
, int64_t timestamp
, unsigned keyframe
) {
325 if (!track
->in_frame
)
328 track
->frame
.timecode
= timestamp
;
329 track
->frame
.keyframe
= keyframe
!= 0;
331 if (track
->max_frame_tc
< timestamp
)
332 track
->max_frame_tc
= timestamp
;
337 int mk_setFrameLacing(mk_Writer
*w
, mk_Track
*track
, uint8_t lacing
, uint8_t num_frames
, uint64_t sizes
[]) {
338 if (!track
->in_frame
)
340 track
->frame
.lacing_sizes
= calloc(num_frames
, sizeof(uint64_t));
342 track
->frame
.lacing
= lacing
;
343 track
->frame
.lacing_num_frames
= num_frames
;
344 memcpy(track
->frame
.lacing_sizes
, sizes
, num_frames
* sizeof(uint64_t));
349 int mk_addFrameData(mk_Writer
*w
, mk_Track
*track
, const void *data
, unsigned size
) {
350 if (!track
->in_frame
)
353 if (track
->frame
.data
== NULL
)
354 if ((track
->frame
.data
= mk_createContext(w
, NULL
, 0)) == NULL
)
357 md5_update(&w
->segment_md5
, (unsigned char *)data
, size
);
359 return mk_appendContextData(track
->frame
.data
, data
, size
);
362 int mk_writeSeek(mk_Writer
*w
, mk_Context
*c
, unsigned seek_id
, uint64_t seek_pos
) {
365 if ((s
= mk_createContext(w
, c
, MATROSKA_ID_SEEKENTRY
)) == NULL
) // Seek
367 CHECK(mk_writeUInt(s
, MATROSKA_ID_SEEKID
, seek_id
)); // SeekID
368 CHECK(mk_writeUInt(s
, MATROSKA_ID_SEEKPOSITION
, seek_pos
)); // SeekPosition
369 CHECK(mk_closeContext(s
, 0));
374 /* The offset of the SeekHead is returned in *pointer. */
375 int mk_writeSeekHead(mk_Writer
*w
, int64_t *pointer
) {
377 int64_t seekhead_ptr
;
379 if ((c
= mk_createContext(w
, w
->root
, MATROSKA_ID_SEEKHEAD
)) == NULL
) // SeekHead
382 seekhead_ptr
= w
->f_pos
;
383 if (w
->seek_data
.seekhead
)
384 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_SEEKHEAD
, w
->seek_data
.seekhead
));
385 if (w
->seek_data
.segmentinfo
)
386 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_INFO
, w
->seek_data
.segmentinfo
));
387 if (w
->seek_data
.tracks
)
388 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_TRACKS
, w
->seek_data
.tracks
));
389 if (w
->seek_data
.cues
)
390 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_CUES
, w
->seek_data
.cues
));
391 if (w
->seek_data
.attachments
)
392 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_ATTACHMENTS
, w
->seek_data
.attachments
));
393 if (w
->seek_data
.chapters
)
394 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_CHAPTERS
, w
->seek_data
.chapters
));
395 if (w
->seek_data
.tags
)
396 CHECK(mk_writeSeek(w
, c
, MATROSKA_ID_TAGS
, w
->seek_data
.tags
));
397 CHECK(mk_closeContext(c
, 0));
400 *pointer
= seekhead_ptr
;
405 int mk_close(mk_Writer
*w
) {
408 int64_t max_frame_tc
= w
->tracks_arr
[0]->max_frame_tc
;
409 uint64_t segment_size
= 0;
410 unsigned char c_size
[8];
411 unsigned char segment_uid
[16];
413 md5_finish(&w
->segment_md5
, segment_uid
);
415 for (i
= w
->num_tracks
- 1; i
>= 0; i
--)
417 tk
= w
->tracks_arr
[i
];
418 w
->tracks_arr
[i
] = NULL
;
420 if (mk_flushFrame(w
, tk
) < 0)
426 if (mk_closeCluster(w
) < 0)
429 w
->seek_data
.cues
= w
->f_pos
- w
->segment_ptr
;
430 if (mk_closeContext(w
->cues
, 0) < 0)
432 if (mk_flushContextData(w
->root
) < 0)
435 if (w
->vlc_compat
&& w
->cluster
.seekhead
) {
436 w
->seek_data
.seekhead
= w
->f_pos
- w
->segment_ptr
;
437 if (mk_closeContext(w
->cluster
.seekhead
, 0) < 0)
439 if (mk_flushContextData(w
->root
) < 0)
443 if (w
->chapters
!= NULL
)
446 if (mk_flushContextData(w
->root
) < 0)
448 if (mk_seekFile(w
, w
->segment_ptr
+ 0x100 + 3) < 0)
451 w
->seek_data
.chapters
= w
->f_pos
- w
->segment_ptr
;
453 if (mk_flushContextData(w
->root
) < 0)
456 if (mk_writeVoid(w
->root
, (0x800 - (w
->f_pos
- w
->segment_ptr
- 0x100 - 3))) < 0)
458 if (mk_flushContextData(w
->root
) < 0)
463 if (w
->wrote_header
) {
465 if (mk_seekFile(w
, w
->segment_ptr
) < 0)
469 if (mk_writeSeekHead(w
, &w
->seek_data
.seekhead
) < 0)
471 w
->seek_data
.seekhead
-= w
->segment_ptr
;
475 if (mk_flushContextData(w
->root
) < 0)
477 if (mk_writeVoid(w
->root
, (0x100 - (w
->f_pos
- w
->segment_ptr
))) < 0)
481 if (mk_flushContextData(w
->root
) < 0)
486 int i
= w
->seek_data
.segmentinfo
;
487 w
->seek_data
.segmentinfo
= 0;
488 w
->seek_data
.tracks
= 0;
489 w
->seek_data
.cues
= 0;
490 w
->seek_data
.chapters
= 0;
491 w
->seek_data
.attachments
= 0;
492 w
->seek_data
.tags
= 0;
493 if (mk_seekFile(w
, w
->segment_ptr
) < 0)
495 if (mk_writeSeekHead(w
, NULL
) < 0 ||
496 mk_flushContextData(w
->root
) < 0)
498 if (((i
+ w
->segment_ptr
) - w
->f_pos
- 2) > 1)
499 if (mk_writeVoid(w
->root
, (i
+ w
->segment_ptr
) - w
->f_pos
- 2) < 0 ||
500 mk_flushContextData(w
->root
) < 0)
504 if (mk_seekFile(w
, w
->duration_ptr
) < 0)
506 if (mk_writeFloatRaw(w
->root
, (float)((double)(max_frame_tc
+w
->def_duration
) / w
->timescale
)) < 0 ||
507 mk_flushContextData(w
->root
) < 0)
509 if (mk_seekFile(w
, w
->segment_ptr
- 8) < 0)
511 segment_size
= w
->f_eof
- w
->segment_ptr
;
512 for (i
= 7; i
> 0; --i
)
513 c_size
[i
] = segment_size
>> (8 * (7-i
));
515 if (mk_appendContextData(w
->root
, &c_size
, 8) < 0 ||
516 mk_flushContextData(w
->root
) < 0)
518 if (mk_seekFile(w
, w
->segmentuid_ptr
) < 0)
520 if (mk_writeBin(w
->root
, MATROSKA_ID_SEGMENTUID
, segment_uid
, sizeof(segment_uid
)) < 0 ||
521 mk_flushContextData(w
->root
) < 0)
525 if (mk_closeContext(w
->root
, 0) < 0)
527 mk_destroyContexts(w
);