2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
15 #include "third_party/libmkv/EbmlWriter.h"
16 #include "third_party/libmkv/EbmlIDs.h"
18 void Ebml_Write(struct EbmlGlobal
*glob
,
19 const void *buffer_in
,
21 (void) fwrite(buffer_in
, 1, len
, glob
->stream
);
24 #define WRITE_BUFFER(s) \
25 for (i = len - 1; i >= 0; i--) { \
26 x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
27 Ebml_Write(glob, &x, 1); \
30 void Ebml_Serialize(struct EbmlGlobal
*glob
,
31 const void *buffer_in
,
43 switch (buffer_size
) {
62 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
63 * one, but not a 32 bit one.
65 static void Ebml_SerializeUnsigned32(struct EbmlGlobal
*glob
,
66 unsigned int class_id
,
68 const unsigned char sizeSerialized
= 4 | 0x80;
69 Ebml_WriteID(glob
, class_id
);
70 Ebml_Serialize(glob
, &sizeSerialized
, sizeof(sizeSerialized
), 1);
71 Ebml_Serialize(glob
, &ui
, sizeof(ui
), 4);
74 static void Ebml_StartSubElement(struct EbmlGlobal
*glob
,
76 unsigned int class_id
) {
77 const uint64_t kEbmlUnknownLength
= LITERALU64(0x01FFFFFF, 0xFFFFFFFF);
78 Ebml_WriteID(glob
, class_id
);
79 *ebmlLoc
= ftello(glob
->stream
);
80 Ebml_Serialize(glob
, &kEbmlUnknownLength
, sizeof(kEbmlUnknownLength
), 8);
83 static void Ebml_EndSubElement(struct EbmlGlobal
*glob
, EbmlLoc
*ebmlLoc
) {
87 /* Save the current stream pointer. */
88 pos
= ftello(glob
->stream
);
90 /* Calculate the size of this element. */
91 size
= pos
- *ebmlLoc
- 8;
92 size
|= LITERALU64(0x01000000, 0x00000000);
94 /* Seek back to the beginning of the element and write the new size. */
95 fseeko(glob
->stream
, *ebmlLoc
, SEEK_SET
);
96 Ebml_Serialize(glob
, &size
, sizeof(size
), 8);
98 /* Reset the stream pointer. */
99 fseeko(glob
->stream
, pos
, SEEK_SET
);
102 void write_webm_seek_element(struct EbmlGlobal
*ebml
,
105 uint64_t offset
= pos
- ebml
->position_reference
;
107 Ebml_StartSubElement(ebml
, &start
, Seek
);
108 Ebml_SerializeBinary(ebml
, SeekID
, id
);
109 Ebml_SerializeUnsigned64(ebml
, SeekPosition
, offset
);
110 Ebml_EndSubElement(ebml
, &start
);
113 void write_webm_seek_info(struct EbmlGlobal
*ebml
) {
118 char version_string
[64];
120 /* Save the current stream pointer. */
121 pos
= ftello(ebml
->stream
);
123 if (ebml
->seek_info_pos
)
124 fseeko(ebml
->stream
, ebml
->seek_info_pos
, SEEK_SET
);
126 ebml
->seek_info_pos
= pos
;
128 Ebml_StartSubElement(ebml
, &start
, SeekHead
);
129 write_webm_seek_element(ebml
, Tracks
, ebml
->track_pos
);
130 write_webm_seek_element(ebml
, Cues
, ebml
->cue_pos
);
131 write_webm_seek_element(ebml
, Info
, ebml
->segment_info_pos
);
132 Ebml_EndSubElement(ebml
, &start
);
134 /* Create and write the Segment Info. */
136 strcpy(version_string
, "vpxenc");
138 strcpy(version_string
, "vpxenc ");
139 strncat(version_string
,
140 vpx_codec_version_str(),
141 sizeof(version_string
) - 1 - strlen(version_string
));
144 frame_time
= (uint64_t)1000 * ebml
->framerate
.den
145 / ebml
->framerate
.num
;
146 ebml
->segment_info_pos
= ftello(ebml
->stream
);
147 Ebml_StartSubElement(ebml
, &startInfo
, Info
);
148 Ebml_SerializeUnsigned(ebml
, TimecodeScale
, 1000000);
149 Ebml_SerializeFloat(ebml
, Segment_Duration
,
150 (double)(ebml
->last_pts_ms
+ frame_time
));
151 Ebml_SerializeString(ebml
, 0x4D80, version_string
);
152 Ebml_SerializeString(ebml
, 0x5741, version_string
);
153 Ebml_EndSubElement(ebml
, &startInfo
);
156 void write_webm_file_header(struct EbmlGlobal
*glob
,
157 const vpx_codec_enc_cfg_t
*cfg
,
158 const struct vpx_rational
*fps
,
159 stereo_format_t stereo_fmt
,
160 unsigned int fourcc
) {
164 unsigned int trackNumber
= 1;
165 uint64_t trackID
= 0;
166 unsigned int pixelWidth
= cfg
->g_w
;
167 unsigned int pixelHeight
= cfg
->g_h
;
169 /* Write the EBML header. */
170 Ebml_StartSubElement(glob
, &start
, EBML
);
171 Ebml_SerializeUnsigned(glob
, EBMLVersion
, 1);
172 Ebml_SerializeUnsigned(glob
, EBMLReadVersion
, 1);
173 Ebml_SerializeUnsigned(glob
, EBMLMaxIDLength
, 4);
174 Ebml_SerializeUnsigned(glob
, EBMLMaxSizeLength
, 8);
175 Ebml_SerializeString(glob
, DocType
, "webm");
176 Ebml_SerializeUnsigned(glob
, DocTypeVersion
, 2);
177 Ebml_SerializeUnsigned(glob
, DocTypeReadVersion
, 2);
178 Ebml_EndSubElement(glob
, &start
);
180 /* Open and begin writing the segment element. */
181 Ebml_StartSubElement(glob
, &glob
->startSegment
, Segment
);
182 glob
->position_reference
= ftello(glob
->stream
);
183 glob
->framerate
= *fps
;
184 write_webm_seek_info(glob
);
186 /* Open and write the Tracks element. */
187 glob
->track_pos
= ftello(glob
->stream
);
188 Ebml_StartSubElement(glob
, &trackStart
, Tracks
);
190 /* Open and write the Track entry. */
191 Ebml_StartSubElement(glob
, &start
, TrackEntry
);
192 Ebml_SerializeUnsigned(glob
, TrackNumber
, trackNumber
);
193 glob
->track_id_pos
= ftello(glob
->stream
);
194 Ebml_SerializeUnsigned32(glob
, TrackUID
, trackID
);
195 Ebml_SerializeUnsigned(glob
, TrackType
, 1);
196 Ebml_SerializeString(glob
, CodecID
,
197 fourcc
== VP8_FOURCC
? "V_VP8" : "V_VP9");
198 Ebml_StartSubElement(glob
, &videoStart
, Video
);
199 Ebml_SerializeUnsigned(glob
, PixelWidth
, pixelWidth
);
200 Ebml_SerializeUnsigned(glob
, PixelHeight
, pixelHeight
);
201 Ebml_SerializeUnsigned(glob
, StereoMode
, stereo_fmt
);
202 Ebml_EndSubElement(glob
, &videoStart
);
204 /* Close Track entry. */
205 Ebml_EndSubElement(glob
, &start
);
207 /* Close Tracks element. */
208 Ebml_EndSubElement(glob
, &trackStart
);
210 /* Segment element remains open. */
213 void write_webm_block(struct EbmlGlobal
*glob
,
214 const vpx_codec_enc_cfg_t
*cfg
,
215 const vpx_codec_cx_pkt_t
*pkt
) {
216 unsigned int block_length
;
217 unsigned char track_number
;
218 uint16_t block_timecode
= 0;
221 int start_cluster
= 0, is_keyframe
;
223 /* Calculate the PTS of this frame in milliseconds. */
224 pts_ms
= pkt
->data
.frame
.pts
* 1000
225 * (uint64_t)cfg
->g_timebase
.num
/ (uint64_t)cfg
->g_timebase
.den
;
227 if (pts_ms
<= glob
->last_pts_ms
)
228 pts_ms
= glob
->last_pts_ms
+ 1;
230 glob
->last_pts_ms
= pts_ms
;
232 /* Calculate the relative time of this block. */
233 if (pts_ms
- glob
->cluster_timecode
> SHRT_MAX
)
236 block_timecode
= (uint16_t)pts_ms
- glob
->cluster_timecode
;
238 is_keyframe
= (pkt
->data
.frame
.flags
& VPX_FRAME_IS_KEY
);
239 if (start_cluster
|| is_keyframe
) {
240 if (glob
->cluster_open
)
241 Ebml_EndSubElement(glob
, &glob
->startCluster
);
243 /* Open the new cluster. */
245 glob
->cluster_open
= 1;
246 glob
->cluster_timecode
= (uint32_t)pts_ms
;
247 glob
->cluster_pos
= ftello(glob
->stream
);
248 Ebml_StartSubElement(glob
, &glob
->startCluster
, Cluster
);
249 Ebml_SerializeUnsigned(glob
, Timecode
, glob
->cluster_timecode
);
251 /* Save a cue point if this is a keyframe. */
253 struct cue_entry
*cue
, *new_cue_list
;
255 new_cue_list
= realloc(glob
->cue_list
,
256 (glob
->cues
+ 1) * sizeof(struct cue_entry
));
258 glob
->cue_list
= new_cue_list
;
260 fatal("Failed to realloc cue list.");
262 cue
= &glob
->cue_list
[glob
->cues
];
263 cue
->time
= glob
->cluster_timecode
;
264 cue
->loc
= glob
->cluster_pos
;
269 /* Write the Simple Block. */
270 Ebml_WriteID(glob
, SimpleBlock
);
272 block_length
= (unsigned int)pkt
->data
.frame
.sz
+ 4;
273 block_length
|= 0x10000000;
274 Ebml_Serialize(glob
, &block_length
, sizeof(block_length
), 4);
277 track_number
|= 0x80;
278 Ebml_Write(glob
, &track_number
, 1);
280 Ebml_Serialize(glob
, &block_timecode
, sizeof(block_timecode
), 2);
285 if (pkt
->data
.frame
.flags
& VPX_FRAME_IS_INVISIBLE
)
287 Ebml_Write(glob
, &flags
, 1);
289 Ebml_Write(glob
, pkt
->data
.frame
.buf
, (unsigned int)pkt
->data
.frame
.sz
);
292 void write_webm_file_footer(struct EbmlGlobal
*glob
, int hash
) {
294 EbmlLoc start_cue_point
;
295 EbmlLoc start_cue_tracks
;
298 if (glob
->cluster_open
)
299 Ebml_EndSubElement(glob
, &glob
->startCluster
);
301 glob
->cue_pos
= ftello(glob
->stream
);
302 Ebml_StartSubElement(glob
, &start_cues
, Cues
);
304 for (i
= 0; i
< glob
->cues
; i
++) {
305 struct cue_entry
*cue
= &glob
->cue_list
[i
];
306 Ebml_StartSubElement(glob
, &start_cue_point
, CuePoint
);
307 Ebml_SerializeUnsigned(glob
, CueTime
, cue
->time
);
309 Ebml_StartSubElement(glob
, &start_cue_tracks
, CueTrackPositions
);
310 Ebml_SerializeUnsigned(glob
, CueTrack
, 1);
311 Ebml_SerializeUnsigned64(glob
, CueClusterPosition
,
312 cue
->loc
- glob
->position_reference
);
313 Ebml_EndSubElement(glob
, &start_cue_tracks
);
315 Ebml_EndSubElement(glob
, &start_cue_point
);
318 Ebml_EndSubElement(glob
, &start_cues
);
320 /* Close the Segment. */
321 Ebml_EndSubElement(glob
, &glob
->startSegment
);
323 /* Patch up the seek info block. */
324 write_webm_seek_info(glob
);
326 /* Patch up the track id. */
327 fseeko(glob
->stream
, glob
->track_id_pos
, SEEK_SET
);
328 Ebml_SerializeUnsigned32(glob
, TrackUID
, glob
->debug
? 0xDEADBEEF : hash
);
330 fseeko(glob
->stream
, 0, SEEK_END
);