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"
19 /* MSVS uses _f{seek,tell}i64 */
20 #define fseeko _fseeki64
21 #define ftello _ftelli64
23 /* MinGW defines off_t as long, and uses f{seek,tell}o64/off64_t for large
25 #define fseeko fseeko64
26 #define ftello ftello64
30 #define LITERALU64(hi, lo) ((((uint64_t)hi) << 32) | lo)
32 void Ebml_Write(struct EbmlGlobal *glob,
33 const void *buffer_in,
35 (void) fwrite(buffer_in, 1, len, glob->stream);
38 #define WRITE_BUFFER(s) \
39 for (i = len - 1; i >= 0; i--) { \
40 x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
41 Ebml_Write(glob, &x, 1); \
44 void Ebml_Serialize(struct EbmlGlobal *glob,
45 const void *buffer_in,
57 switch (buffer_size) {
76 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
77 * one, but not a 32 bit one.
79 static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob,
80 unsigned int class_id,
82 const unsigned char sizeSerialized = 4 | 0x80;
83 Ebml_WriteID(glob, class_id);
84 Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1);
85 Ebml_Serialize(glob, &ui, sizeof(ui), 4);
88 static void Ebml_StartSubElement(struct EbmlGlobal *glob,
90 unsigned int class_id) {
91 const uint64_t kEbmlUnknownLength = LITERALU64(0x01FFFFFF, 0xFFFFFFFF);
92 Ebml_WriteID(glob, class_id);
93 *ebmlLoc = ftello(glob->stream);
94 Ebml_Serialize(glob, &kEbmlUnknownLength, sizeof(kEbmlUnknownLength), 8);
97 static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
101 /* Save the current stream pointer. */
102 pos = ftello(glob->stream);
104 /* Calculate the size of this element. */
105 size = pos - *ebmlLoc - 8;
106 size |= LITERALU64(0x01000000, 0x00000000);
108 /* Seek back to the beginning of the element and write the new size. */
109 fseeko(glob->stream, *ebmlLoc, SEEK_SET);
110 Ebml_Serialize(glob, &size, sizeof(size), 8);
112 /* Reset the stream pointer. */
113 fseeko(glob->stream, pos, SEEK_SET);
116 void write_webm_seek_element(struct EbmlGlobal *ebml,
119 uint64_t offset = pos - ebml->position_reference;
121 Ebml_StartSubElement(ebml, &start, Seek);
122 Ebml_SerializeBinary(ebml, SeekID, id);
123 Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
124 Ebml_EndSubElement(ebml, &start);
127 void write_webm_seek_info(struct EbmlGlobal *ebml) {
132 char version_string[64];
134 /* Save the current stream pointer. */
135 pos = ftello(ebml->stream);
137 if (ebml->seek_info_pos)
138 fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
140 ebml->seek_info_pos = pos;
142 Ebml_StartSubElement(ebml, &start, SeekHead);
143 write_webm_seek_element(ebml, Tracks, ebml->track_pos);
144 write_webm_seek_element(ebml, Cues, ebml->cue_pos);
145 write_webm_seek_element(ebml, Info, ebml->segment_info_pos);
146 Ebml_EndSubElement(ebml, &start);
148 /* Create and write the Segment Info. */
150 strcpy(version_string, "vpxenc");
152 strcpy(version_string, "vpxenc ");
153 strncat(version_string,
154 vpx_codec_version_str(),
155 sizeof(version_string) - 1 - strlen(version_string));
158 frame_time = (uint64_t)1000 * ebml->framerate.den
159 / ebml->framerate.num;
160 ebml->segment_info_pos = ftello(ebml->stream);
161 Ebml_StartSubElement(ebml, &startInfo, Info);
162 Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
163 Ebml_SerializeFloat(ebml, Segment_Duration,
164 (double)(ebml->last_pts_ms + frame_time));
165 Ebml_SerializeString(ebml, 0x4D80, version_string);
166 Ebml_SerializeString(ebml, 0x5741, version_string);
167 Ebml_EndSubElement(ebml, &startInfo);
170 void write_webm_file_header(struct EbmlGlobal *glob,
171 const vpx_codec_enc_cfg_t *cfg,
172 const struct vpx_rational *fps,
173 stereo_format_t stereo_fmt,
174 unsigned int fourcc) {
178 unsigned int trackNumber = 1;
179 uint64_t trackID = 0;
180 unsigned int pixelWidth = cfg->g_w;
181 unsigned int pixelHeight = cfg->g_h;
183 /* Write the EBML header. */
184 Ebml_StartSubElement(glob, &start, EBML);
185 Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
186 Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
187 Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
188 Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
189 Ebml_SerializeString(glob, DocType, "webm");
190 Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
191 Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
192 Ebml_EndSubElement(glob, &start);
194 /* Open and begin writing the segment element. */
195 Ebml_StartSubElement(glob, &glob->startSegment, Segment);
196 glob->position_reference = ftello(glob->stream);
197 glob->framerate = *fps;
198 write_webm_seek_info(glob);
200 /* Open and write the Tracks element. */
201 glob->track_pos = ftello(glob->stream);
202 Ebml_StartSubElement(glob, &trackStart, Tracks);
204 /* Open and write the Track entry. */
205 Ebml_StartSubElement(glob, &start, TrackEntry);
206 Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
207 glob->track_id_pos = ftello(glob->stream);
208 Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
209 Ebml_SerializeUnsigned(glob, TrackType, 1);
210 Ebml_SerializeString(glob, CodecID,
211 fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
212 Ebml_StartSubElement(glob, &videoStart, Video);
213 Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
214 Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
215 Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
216 Ebml_EndSubElement(glob, &videoStart);
218 /* Close Track entry. */
219 Ebml_EndSubElement(glob, &start);
221 /* Close Tracks element. */
222 Ebml_EndSubElement(glob, &trackStart);
224 /* Segment element remains open. */
227 void write_webm_block(struct EbmlGlobal *glob,
228 const vpx_codec_enc_cfg_t *cfg,
229 const vpx_codec_cx_pkt_t *pkt) {
230 unsigned int block_length;
231 unsigned char track_number;
232 uint16_t block_timecode = 0;
235 int start_cluster = 0, is_keyframe;
237 /* Calculate the PTS of this frame in milliseconds. */
238 pts_ms = pkt->data.frame.pts * 1000
239 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
241 if (pts_ms <= glob->last_pts_ms)
242 pts_ms = glob->last_pts_ms + 1;
244 glob->last_pts_ms = pts_ms;
246 /* Calculate the relative time of this block. */
247 if (pts_ms - glob->cluster_timecode > SHRT_MAX)
250 block_timecode = (uint16_t)pts_ms - glob->cluster_timecode;
252 is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
253 if (start_cluster || is_keyframe) {
254 if (glob->cluster_open)
255 Ebml_EndSubElement(glob, &glob->startCluster);
257 /* Open the new cluster. */
259 glob->cluster_open = 1;
260 glob->cluster_timecode = (uint32_t)pts_ms;
261 glob->cluster_pos = ftello(glob->stream);
262 Ebml_StartSubElement(glob, &glob->startCluster, Cluster);
263 Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
265 /* Save a cue point if this is a keyframe. */
267 struct cue_entry *cue, *new_cue_list;
269 new_cue_list = realloc(glob->cue_list,
270 (glob->cues + 1) * sizeof(struct cue_entry));
272 glob->cue_list = new_cue_list;
274 fatal("Failed to realloc cue list.");
276 cue = &glob->cue_list[glob->cues];
277 cue->time = glob->cluster_timecode;
278 cue->loc = glob->cluster_pos;
283 /* Write the Simple Block. */
284 Ebml_WriteID(glob, SimpleBlock);
286 block_length = (unsigned int)pkt->data.frame.sz + 4;
287 block_length |= 0x10000000;
288 Ebml_Serialize(glob, &block_length, sizeof(block_length), 4);
291 track_number |= 0x80;
292 Ebml_Write(glob, &track_number, 1);
294 Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
299 if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
301 Ebml_Write(glob, &flags, 1);
303 Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz);
306 void write_webm_file_footer(struct EbmlGlobal *glob, int hash) {
308 EbmlLoc start_cue_point;
309 EbmlLoc start_cue_tracks;
312 if (glob->cluster_open)
313 Ebml_EndSubElement(glob, &glob->startCluster);
315 glob->cue_pos = ftello(glob->stream);
316 Ebml_StartSubElement(glob, &start_cues, Cues);
318 for (i = 0; i < glob->cues; i++) {
319 struct cue_entry *cue = &glob->cue_list[i];
320 Ebml_StartSubElement(glob, &start_cue_point, CuePoint);
321 Ebml_SerializeUnsigned(glob, CueTime, cue->time);
323 Ebml_StartSubElement(glob, &start_cue_tracks, CueTrackPositions);
324 Ebml_SerializeUnsigned(glob, CueTrack, 1);
325 Ebml_SerializeUnsigned64(glob, CueClusterPosition,
326 cue->loc - glob->position_reference);
327 Ebml_EndSubElement(glob, &start_cue_tracks);
329 Ebml_EndSubElement(glob, &start_cue_point);
332 Ebml_EndSubElement(glob, &start_cues);
334 /* Close the Segment. */
335 Ebml_EndSubElement(glob, &glob->startSegment);
337 /* Patch up the seek info block. */
338 write_webm_seek_info(glob);
340 /* Patch up the track id. */
341 fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
342 Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
344 fseeko(glob->stream, 0, SEEK_END);