Home | History | Annotate | Download | only in libvpx
      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