1 /* 2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved. 3 * 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. 9 */ 10 #include "webmenc.h" 11 12 #include <limits.h> 13 #include <string.h> 14 15 #include "third_party/libmkv/EbmlWriter.h" 16 #include "third_party/libmkv/EbmlIDs.h" 17 18 void Ebml_Write(struct EbmlGlobal *glob, 19 const void *buffer_in, 20 unsigned long len) { 21 (void) fwrite(buffer_in, 1, len, glob->stream); 22 } 23 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); \ 28 } 29 30 void Ebml_Serialize(struct EbmlGlobal *glob, 31 const void *buffer_in, 32 int buffer_size, 33 unsigned long len) { 34 char x; 35 int i; 36 37 /* buffer_size: 38 * 1 - int8_t; 39 * 2 - int16_t; 40 * 3 - int32_t; 41 * 4 - int64_t; 42 */ 43 switch (buffer_size) { 44 case 1: 45 WRITE_BUFFER(int8_t) 46 break; 47 case 2: 48 WRITE_BUFFER(int16_t) 49 break; 50 case 4: 51 WRITE_BUFFER(int32_t) 52 break; 53 case 8: 54 WRITE_BUFFER(int64_t) 55 break; 56 default: 57 break; 58 } 59 } 60 #undef WRITE_BUFFER 61 62 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit 63 * one, but not a 32 bit one. 64 */ 65 static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob, 66 unsigned int class_id, 67 uint64_t ui) { 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); 72 } 73 74 static void Ebml_StartSubElement(struct EbmlGlobal *glob, 75 EbmlLoc *ebmlLoc, 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); 81 } 82 83 static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) { 84 off_t pos; 85 uint64_t size; 86 87 /* Save the current stream pointer. */ 88 pos = ftello(glob->stream); 89 90 /* Calculate the size of this element. */ 91 size = pos - *ebmlLoc - 8; 92 size |= LITERALU64(0x01000000, 0x00000000); 93 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); 97 98 /* Reset the stream pointer. */ 99 fseeko(glob->stream, pos, SEEK_SET); 100 } 101 102 void write_webm_seek_element(struct EbmlGlobal *ebml, 103 unsigned int id, 104 off_t pos) { 105 uint64_t offset = pos - ebml->position_reference; 106 EbmlLoc start; 107 Ebml_StartSubElement(ebml, &start, Seek); 108 Ebml_SerializeBinary(ebml, SeekID, id); 109 Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); 110 Ebml_EndSubElement(ebml, &start); 111 } 112 113 void write_webm_seek_info(struct EbmlGlobal *ebml) { 114 off_t pos; 115 EbmlLoc start; 116 EbmlLoc startInfo; 117 uint64_t frame_time; 118 char version_string[64]; 119 120 /* Save the current stream pointer. */ 121 pos = ftello(ebml->stream); 122 123 if (ebml->seek_info_pos) 124 fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); 125 else 126 ebml->seek_info_pos = pos; 127 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); 133 134 /* Create and write the Segment Info. */ 135 if (ebml->debug) { 136 strcpy(version_string, "vpxenc"); 137 } else { 138 strcpy(version_string, "vpxenc "); 139 strncat(version_string, 140 vpx_codec_version_str(), 141 sizeof(version_string) - 1 - strlen(version_string)); 142 } 143 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); 154 } 155 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) { 161 EbmlLoc start; 162 EbmlLoc trackStart; 163 EbmlLoc videoStart; 164 unsigned int trackNumber = 1; 165 uint64_t trackID = 0; 166 unsigned int pixelWidth = cfg->g_w; 167 unsigned int pixelHeight = cfg->g_h; 168 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); 179 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); 185 186 /* Open and write the Tracks element. */ 187 glob->track_pos = ftello(glob->stream); 188 Ebml_StartSubElement(glob, &trackStart, Tracks); 189 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); 203 204 /* Close Track entry. */ 205 Ebml_EndSubElement(glob, &start); 206 207 /* Close Tracks element. */ 208 Ebml_EndSubElement(glob, &trackStart); 209 210 /* Segment element remains open. */ 211 } 212 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; 219 unsigned char flags; 220 int64_t pts_ms; 221 int start_cluster = 0, is_keyframe; 222 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; 226 227 if (pts_ms <= glob->last_pts_ms) 228 pts_ms = glob->last_pts_ms + 1; 229 230 glob->last_pts_ms = pts_ms; 231 232 /* Calculate the relative time of this block. */ 233 if (pts_ms - glob->cluster_timecode > SHRT_MAX) 234 start_cluster = 1; 235 else 236 block_timecode = (uint16_t)pts_ms - glob->cluster_timecode; 237 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); 242 243 /* Open the new cluster. */ 244 block_timecode = 0; 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); 250 251 /* Save a cue point if this is a keyframe. */ 252 if (is_keyframe) { 253 struct cue_entry *cue, *new_cue_list; 254 255 new_cue_list = realloc(glob->cue_list, 256 (glob->cues + 1) * sizeof(struct cue_entry)); 257 if (new_cue_list) 258 glob->cue_list = new_cue_list; 259 else 260 fatal("Failed to realloc cue list."); 261 262 cue = &glob->cue_list[glob->cues]; 263 cue->time = glob->cluster_timecode; 264 cue->loc = glob->cluster_pos; 265 glob->cues++; 266 } 267 } 268 269 /* Write the Simple Block. */ 270 Ebml_WriteID(glob, SimpleBlock); 271 272 block_length = (unsigned int)pkt->data.frame.sz + 4; 273 block_length |= 0x10000000; 274 Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); 275 276 track_number = 1; 277 track_number |= 0x80; 278 Ebml_Write(glob, &track_number, 1); 279 280 Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); 281 282 flags = 0; 283 if (is_keyframe) 284 flags |= 0x80; 285 if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) 286 flags |= 0x08; 287 Ebml_Write(glob, &flags, 1); 288 289 Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz); 290 } 291 292 void write_webm_file_footer(struct EbmlGlobal *glob, int hash) { 293 EbmlLoc start_cues; 294 EbmlLoc start_cue_point; 295 EbmlLoc start_cue_tracks; 296 unsigned int i; 297 298 if (glob->cluster_open) 299 Ebml_EndSubElement(glob, &glob->startCluster); 300 301 glob->cue_pos = ftello(glob->stream); 302 Ebml_StartSubElement(glob, &start_cues, Cues); 303 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); 308 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); 314 315 Ebml_EndSubElement(glob, &start_cue_point); 316 } 317 318 Ebml_EndSubElement(glob, &start_cues); 319 320 /* Close the Segment. */ 321 Ebml_EndSubElement(glob, &glob->startSegment); 322 323 /* Patch up the seek info block. */ 324 write_webm_seek_info(glob); 325 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); 329 330 fseeko(glob->stream, 0, SEEK_END); 331 } 332