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 *****************************************************************************/
28 int mk_seekFile(mk_Writer
*w
, uint64_t pos
) {
29 if (fseek(w
->fp
, pos
, SEEK_SET
))
40 char *mk_laceXiph(uint64_t *sizes
, uint8_t num_frames
, uint64_t *output_size
) {
43 uint64_t alloc_size
= num_frames
* 6; // Complete guess. We'll realloc if we need more space, though.
44 char *laced
= calloc(alloc_size
, sizeof(char));
48 laced
[offset
++] = num_frames
;
49 for (i
= 0; i
< num_frames
; i
++)
51 for (j
= sizes
[i
]; j
>= 255 ; j
-= 255)
53 laced
[offset
++] = 255;
54 if (offset
+ 1 >= alloc_size
) {
55 int avg_sz
= offset
/ (i
- 1); // Compute approximate average bytes/frame
56 alloc_size
+= avg_sz
* (num_frames
- i
); // Add our average + number of frames left to size
57 if ((laced
= realloc(laced
, alloc_size
)) == NULL
)
64 if (output_size
!= NULL
)
65 *output_size
= offset
;
70 mk_Writer
*mk_createWriter(const char *filename
, int64_t timescale
, uint8_t vlc_compat
) {
71 mk_Writer
*w
= calloc(1, sizeof(*w
));
75 w
->root
= mk_createContext(w
, NULL
, 0);
76 if (w
->root
== NULL
) {
81 if ((w
->cues
= mk_createContext(w
, w
->root
, 0x1c53bb6b)) == NULL
) // Cues
83 mk_destroyContexts(w
);
89 if ((w
->cluster
.seekhead
= mk_createContext(w
, w
->root
, 0x114d9b74)) == NULL
) // SeekHead
91 mk_destroyContexts(w
);
97 w
->fp
= fopen(filename
, "wb");
99 mk_destroyContexts(w
);
104 w
->timescale
= timescale
;
105 w
->vlc_compat
= vlc_compat
;
110 int mk_writeHeader(mk_Writer
*w
, const char *writingApp
) {
116 if ((c
= mk_createContext(w
, w
->root
, 0x1a45dfa3)) == NULL
) // EBML
118 CHECK(mk_writeUInt(c
, 0x4286, 1)); // EBMLVersion
119 CHECK(mk_writeUInt(c
, 0x42f7, 1)); // EBMLReadVersion
120 CHECK(mk_writeUInt(c
, 0x42f2, 4)); // EBMLMaxIDLength
121 CHECK(mk_writeUInt(c
, 0x42f3, 8)); // EBMLMaxSizeLength
122 CHECK(mk_writeStr(c
, 0x4282, "matroska")); // DocType
123 CHECK(mk_writeUInt(c
, 0x4287, 1)); // DocTypeVersion
124 CHECK(mk_writeUInt(c
, 0x4285, 1)); // DocTypeReadversion
125 CHECK(mk_closeContext(c
, 0));
127 if ((c
= mk_createContext(w
, w
->root
, 0x18538067)) == NULL
) // Segment
129 CHECK(mk_flushContextID(c
));
130 w
->segment_ptr
= c
->d_cur
;
131 CHECK(mk_closeContext(c
, &w
->segment_ptr
));
134 CHECK(mk_writeVoid(w
->root
, 0x100)); // 256 bytes should be enough room for our Seek entries.
135 CHECK(mk_writeVoid(w
->root
, 0x800)); // 2048 bytes for Chapters.
138 w
->seek_data
.seekhead
= 0x80000000;
139 CHECK(mk_writeSeekHead(w
, &w
->seekhead_ptr
));
140 w
->seek_data
.seekhead
= 0;
143 if ((c
= mk_createContext(w
, w
->root
, 0x1549a966)) == NULL
) // SegmentInfo
145 w
->seek_data
.segmentinfo
= w
->root
->d_cur
- w
->segment_ptr
;
146 CHECK(mk_writeStr(c
, 0x4d80, PACKAGE_STRING
)); // MuxingApp
147 CHECK(mk_writeStr(c
, 0x5741, writingApp
)); // WritingApp
148 CHECK(mk_writeUInt(c
, 0x2ad7b1, w
->timescale
)); // TimecodeScale
149 CHECK(mk_writeFloat(c
, 0x4489, 0)); // Duration
150 w
->duration_ptr
= c
->d_cur
- 4;
151 CHECK(mk_closeContext(c
, &w
->duration_ptr
));
153 w
->seek_data
.tracks
= w
->root
->d_cur
- w
->segment_ptr
;
156 CHECK(mk_closeContext(w
->tracks
, 0));
158 CHECK(mk_flushContextData(w
->root
));
161 w
->def_duration
= w
->tracks_arr
[0]->default_duration
;
165 static int mk_closeCluster(mk_Writer
*w
) {
166 if (w
->cluster
.context
== NULL
)
169 CHECK(mk_closeContext(w
->cluster
.context
, 0));
170 w
->cluster
.context
= NULL
;
171 CHECK(mk_flushContextData(w
->root
));
175 int mk_flushFrame(mk_Writer
*w
, mk_Track
*track
) {
177 int64_t delta
, ref
= 0;
178 unsigned fsize
, bgsize
;
179 uint8_t flags
, c_delta_flags
[2];
184 if (!track
->in_frame
)
187 delta
= track
->frame
.timecode
/w
->timescale
- w
->cluster
.tc_scaled
;
188 if (delta
> 2000ll || delta
< -2000ll)
189 CHECK(mk_closeCluster(w
));
191 if (w
->cluster
.context
== NULL
) {
192 w
->cluster
.tc_scaled
= track
->frame
.timecode
/ w
->timescale
;
193 w
->cluster
.context
= mk_createContext(w
, w
->root
, 0x1f43b675); // Cluster
194 if (w
->cluster
.context
== NULL
)
197 w
->cluster
.pointer
= w
->f_pos
- w
->segment_ptr
;
200 CHECK(mk_writeSeek(w
, w
->cluster
.seekhead
, 0x1f43b675, w
->cluster
.pointer
));
202 CHECK(mk_writeUInt(w
->cluster
.context
, 0xe7, w
->cluster
.tc_scaled
)); // Cluster Timecode
205 w
->cluster
.block_count
= 0;
208 fsize
= track
->frame
.data
? track
->frame
.data
->d_cur
: 0;
209 bgsize
= fsize
+ 4 + mk_ebmlSizeSize(fsize
+ 4) + 1;
210 if (!track
->frame
.keyframe
) {
211 ref
= track
->prev_frame_tc_scaled
- w
->cluster
.tc_scaled
- delta
;
212 bgsize
+= 1 + 1 + mk_ebmlSIntSize(ref
);
215 CHECK(mk_writeID(w
->cluster
.context
, 0xa0)); // BlockGroup
216 CHECK(mk_writeSize(w
->cluster
.context
, bgsize
));
217 CHECK(mk_writeID(w
->cluster
.context
, 0xa1)); // Block
219 switch (track
->frame
.lacing
) {
221 laced
= mk_laceXiph(track
->frame
.lacing_sizes
, track
->frame
.lacing_num_frames
, &length
);
224 length
+= mk_ebmlSizeSize(track
->frame
.lacing_sizes
[0]) + 1;
225 for (i
= 1; i
< track
->frame
.lacing_num_frames
; i
++)
226 length
+= mk_ebmlSizeSize(track
->frame
.lacing_sizes
[i
] << 1);
228 case MK_LACING_FIXED
:
230 laced
= calloc(1, sizeof(char));
231 laced
[0] = track
->frame
.lacing_num_frames
;
237 CHECK(mk_writeSize(w
->cluster
.context
, fsize
+ 4 + length
));
238 CHECK(mk_writeSize(w
->cluster
.context
, track
->track_id
)); // track number
240 w
->cluster
.block_count
++;
242 c_delta_flags
[0] = delta
>> 8;
243 c_delta_flags
[1] = delta
;
244 CHECK(mk_appendContextData(w
->cluster
.context
, c_delta_flags
, 2));
246 flags
= ( track
->frame
.keyframe
<< 8 ) | track
->frame
.lacing
;
247 CHECK(mk_appendContextData(w
->cluster
.context
, &flags
, 1));
248 if (track
->frame
.lacing
) {
249 if (track
->frame
.lacing
== MK_LACING_EBML
) {
250 CHECK(mk_appendContextData(w
->cluster
.context
, &track
->frame
.lacing_num_frames
, 1));
251 CHECK(mk_writeSize(w
->cluster
.context
, track
->frame
.lacing_sizes
[0]));
252 for (i
= 1; i
< track
->frame
.lacing_num_frames
; i
++)
254 CHECK(mk_writeSSize(w
->cluster
.context
, track
->frame
.lacing_sizes
[i
] - track
->frame
.lacing_sizes
[i
-1]));
256 } else if (length
> 0 && laced
!= NULL
) {
257 CHECK(mk_appendContextData(w
->cluster
.context
, laced
, length
));
263 if (track
->frame
.data
) {
264 CHECK(mk_appendContextData(w
->cluster
.context
, track
->frame
.data
->data
, track
->frame
.data
->d_cur
));
265 track
->frame
.data
->d_cur
= 0;
267 if (!track
->frame
.keyframe
)
268 CHECK(mk_writeSInt(w
->cluster
.context
, 0xfb, ref
)); // ReferenceBlock
270 if (track
->frame
.keyframe
&& (track
->track_type
& MK_TRACK_VIDEO
) && (track
->prev_cue_pos
+ 3*CLSIZE
) >= w
->f_pos
) {
271 if ((c
= mk_createContext(w
, w
->cues
, 0xbb)) == NULL
) // CuePoint
273 CHECK(mk_writeUInt(c
, 0xb3, track
->frame
.timecode
)); // CueTime
275 if ((tp
= mk_createContext(w
, c
, 0xb7)) == NULL
) // CueTrackPositions
277 CHECK(mk_writeUInt(tp
, 0xf7, track
->track_id
)); // CueTrack
278 CHECK(mk_writeUInt(tp
, 0xf1, w
->cluster
.pointer
)); // CueClusterPosition
279 // CHECK(mk_writeUInt(c, 0x5378, w->cluster.block_count)); // CueBlockNumber
280 CHECK(mk_closeContext(tp
, 0));
281 CHECK(mk_closeContext(c
, 0));
285 track
->prev_frame_tc_scaled
= w
->cluster
.tc_scaled
+ delta
;
287 if (w
->cluster
.context
->d_cur
> CLSIZE
)
288 CHECK(mk_closeCluster(w
));
293 int mk_startFrame(mk_Writer
*w
, mk_Track
*track
) {
294 if (mk_flushFrame(w
, track
) < 0)
298 track
->frame
.keyframe
= 0;
299 track
->frame
.lacing
= MK_LACING_NONE
;
300 track
->frame
.lacing_num_frames
= 0;
301 track
->frame
.lacing_sizes
= NULL
;
306 int mk_setFrameFlags(mk_Writer
*w
, mk_Track
*track
, int64_t timestamp
, unsigned keyframe
) {
307 if (!track
->in_frame
)
310 track
->frame
.timecode
= timestamp
;
311 track
->frame
.keyframe
= keyframe
!= 0;
313 if (track
->max_frame_tc
< timestamp
)
314 track
->max_frame_tc
= timestamp
;
319 int mk_setFrameLacing(mk_Writer
*w
, mk_Track
*track
, uint8_t lacing
, uint8_t num_frames
, uint32_t sizes
[]) {
320 if (!track
->in_frame
)
322 track
->frame
.lacing_sizes
= calloc(num_frames
, sizeof(uint32_t));
324 track
->frame
.lacing
= lacing
;
325 track
->frame
.lacing_num_frames
= num_frames
;
326 memcpy(track
->frame
.lacing_sizes
, sizes
, num_frames
);
331 int mk_addFrameData(mk_Writer
*w
, mk_Track
*track
, const void *data
, unsigned size
) {
332 if (!track
->in_frame
)
335 if (track
->frame
.data
== NULL
)
336 if ((track
->frame
.data
= mk_createContext(w
, NULL
, 0)) == NULL
)
339 return mk_appendContextData(track
->frame
.data
, data
, size
);
342 int mk_writeSeek(mk_Writer
*w
, mk_Context
*c
, unsigned seek_id
, uint64_t seek_pos
) {
345 if ((s
= mk_createContext(w
, c
, 0x4dbb)) == NULL
) // Seek
347 CHECK(mk_writeUInt(s
, 0x53ab, seek_id
)); // SeekID
348 CHECK(mk_writeUInt(s
, 0x53ac, seek_pos
)); // SeekPosition
349 CHECK(mk_closeContext(s
, 0));
354 /* The offset of the SeekHead is returned in *pointer. */
355 int mk_writeSeekHead(mk_Writer
*w
, int64_t *pointer
) {
357 int64_t seekhead_ptr
;
359 if ((c
= mk_createContext(w
, w
->root
, 0x114d9b74)) == NULL
) // SeekHead
362 seekhead_ptr
= w
->f_pos
;
363 if (w
->seek_data
.seekhead
)
364 CHECK(mk_writeSeek(w
, c
, 0x114d9b74, w
->seek_data
.seekhead
));
365 if (w
->seek_data
.segmentinfo
)
366 CHECK(mk_writeSeek(w
, c
, 0x1549a966, w
->seek_data
.segmentinfo
));
367 if (w
->seek_data
.tracks
)
368 CHECK(mk_writeSeek(w
, c
, 0x1654ae6b, w
->seek_data
.tracks
));
369 if (w
->seek_data
.cues
)
370 CHECK(mk_writeSeek(w
, c
, 0x1c53bb6b, w
->seek_data
.cues
));
371 if (w
->seek_data
.attachments
)
372 CHECK(mk_writeSeek(w
, c
, 0x1941a469, w
->seek_data
.attachments
));
373 if (w
->seek_data
.chapters
)
374 CHECK(mk_writeSeek(w
, c
, 0x1043a770, w
->seek_data
.chapters
));
375 if (w
->seek_data
.tags
)
376 CHECK(mk_writeSeek(w
, c
, 0x1254c367, w
->seek_data
.tags
));
377 CHECK(mk_closeContext(c
, 0));
380 *pointer
= seekhead_ptr
;
385 int mk_close(mk_Writer
*w
) {
388 int64_t max_frame_tc
= w
->tracks_arr
[0]->max_frame_tc
;
389 uint64_t segment_size
= 0;
390 unsigned char c_size
[8];
392 for (i
= w
->num_tracks
- 1; i
>= 0; i
--)
394 tk
= w
->tracks_arr
[i
];
395 w
->tracks_arr
[i
] = NULL
;
397 if (mk_flushFrame(w
, tk
) < 0)
403 if (mk_closeCluster(w
) < 0)
406 w
->seek_data
.cues
= w
->f_pos
- w
->segment_ptr
;
407 if (mk_closeContext(w
->cues
, 0) < 0)
409 if (mk_flushContextData(w
->root
) < 0)
412 if (w
->vlc_compat
&& w
->cluster
.seekhead
) {
413 w
->seek_data
.seekhead
= w
->f_pos
- w
->segment_ptr
;
414 if (mk_closeContext(w
->cluster
.seekhead
, 0) < 0)
416 if (mk_flushContextData(w
->root
) < 0)
420 if (w
->chapters
!= NULL
)
422 w
->seek_data
.chapters
= w
->f_pos
- w
->segment_ptr
;
425 if (mk_flushContextData(w
->root
) < 0)
427 if (mk_writeVoid(w
->root
, (0x800 - (w
->f_pos
- w
->segment_ptr
- 0x100 - 3))) < 0)
430 if (mk_flushContextData(w
->root
) < 0)
434 if (w
->wrote_header
) {
436 if (mk_seekFile(w
, w
->segment_ptr
) < 0)
440 if (mk_writeSeekHead(w
, &w
->seek_data
.seekhead
) < 0)
442 w
->seek_data
.seekhead
-= w
->segment_ptr
;
446 if (mk_flushContextData(w
->root
) < 0)
448 if (mk_writeVoid(w
->root
, (0x100 - (w
->f_pos
- w
->segment_ptr
))) < 0)
452 if (mk_flushContextData(w
->root
) < 0)
457 int i
= w
->seek_data
.segmentinfo
;
458 w
->seek_data
.segmentinfo
= 0;
459 w
->seek_data
.tracks
= 0;
460 w
->seek_data
.cues
= 0;
461 w
->seek_data
.chapters
= 0;
462 w
->seek_data
.attachments
= 0;
463 w
->seek_data
.tags
= 0;
464 if (mk_seekFile(w
, w
->segment_ptr
) < 0)
466 if (mk_writeSeekHead(w
, NULL
) < 0 ||
467 mk_flushContextData(w
->root
) < 0)
469 if (((i
+ w
->segment_ptr
) - w
->f_pos
- 2) > 1)
470 if (mk_writeVoid(w
->root
, (i
+ w
->segment_ptr
) - w
->f_pos
- 2) < 0 ||
471 mk_flushContextData(w
->root
) < 0)
475 if (mk_seekFile(w
, w
->duration_ptr
) < 0)
477 if (mk_writeFloatRaw(w
->root
, (float)((double)(max_frame_tc
+w
->def_duration
) / w
->timescale
)) < 0 ||
478 mk_flushContextData(w
->root
) < 0)
480 if (mk_seekFile(w
, w
->segment_ptr
- 8) < 0)
482 segment_size
= w
->f_eof
- w
->segment_ptr
;
483 for (i
= 7; i
> 0; --i
)
484 c_size
[i
] = segment_size
>> (8 * (7-i
));
486 if (mk_appendContextData(w
->root
, &c_size
, 8) < 0 ||
487 mk_flushContextData(w
->root
) < 0)
491 if (mk_closeContext(w
->root
, 0) < 0)
493 mk_destroyContexts(w
);