OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |