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 
     11 #include "./webmdec.h"
     12 
     13 #include <cstring>
     14 #include <cstdio>
     15 
     16 #include "third_party/libwebm/mkvparser/mkvparser.h"
     17 #include "third_party/libwebm/mkvparser/mkvreader.h"
     18 
     19 namespace {
     20 
     21 void reset(struct WebmInputContext *const webm_ctx) {
     22   if (webm_ctx->reader != NULL) {
     23     mkvparser::MkvReader *const reader =
     24         reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
     25     delete reader;
     26   }
     27   if (webm_ctx->segment != NULL) {
     28     mkvparser::Segment *const segment =
     29         reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
     30     delete segment;
     31   }
     32   if (webm_ctx->buffer != NULL) {
     33     delete[] webm_ctx->buffer;
     34   }
     35   webm_ctx->reader = NULL;
     36   webm_ctx->segment = NULL;
     37   webm_ctx->buffer = NULL;
     38   webm_ctx->cluster = NULL;
     39   webm_ctx->block_entry = NULL;
     40   webm_ctx->block = NULL;
     41   webm_ctx->block_frame_index = 0;
     42   webm_ctx->video_track_index = 0;
     43   webm_ctx->timestamp_ns = 0;
     44   webm_ctx->is_key_frame = false;
     45 }
     46 
     47 void get_first_cluster(struct WebmInputContext *const webm_ctx) {
     48   mkvparser::Segment *const segment =
     49       reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
     50   const mkvparser::Cluster *const cluster = segment->GetFirst();
     51   webm_ctx->cluster = cluster;
     52 }
     53 
     54 void rewind_and_reset(struct WebmInputContext *const webm_ctx,
     55                       struct VpxInputContext *const vpx_ctx) {
     56   rewind(vpx_ctx->file);
     57   reset(webm_ctx);
     58 }
     59 
     60 }  // namespace
     61 
     62 int file_is_webm(struct WebmInputContext *webm_ctx,
     63                  struct VpxInputContext *vpx_ctx) {
     64   mkvparser::MkvReader *const reader = new mkvparser::MkvReader(vpx_ctx->file);
     65   webm_ctx->reader = reader;
     66   webm_ctx->reached_eos = 0;
     67 
     68   mkvparser::EBMLHeader header;
     69   long long pos = 0;
     70   if (header.Parse(reader, pos) < 0) {
     71     rewind_and_reset(webm_ctx, vpx_ctx);
     72     return 0;
     73   }
     74 
     75   mkvparser::Segment *segment;
     76   if (mkvparser::Segment::CreateInstance(reader, pos, segment)) {
     77     rewind_and_reset(webm_ctx, vpx_ctx);
     78     return 0;
     79   }
     80   webm_ctx->segment = segment;
     81   if (segment->Load() < 0) {
     82     rewind_and_reset(webm_ctx, vpx_ctx);
     83     return 0;
     84   }
     85 
     86   const mkvparser::Tracks *const tracks = segment->GetTracks();
     87   const mkvparser::VideoTrack *video_track = NULL;
     88   for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) {
     89     const mkvparser::Track *const track = tracks->GetTrackByIndex(i);
     90     if (track->GetType() == mkvparser::Track::kVideo) {
     91       video_track = static_cast<const mkvparser::VideoTrack *>(track);
     92       webm_ctx->video_track_index = static_cast<int>(track->GetNumber());
     93       break;
     94     }
     95   }
     96 
     97   if (video_track == NULL || video_track->GetCodecId() == NULL) {
     98     rewind_and_reset(webm_ctx, vpx_ctx);
     99     return 0;
    100   }
    101 
    102   if (!strncmp(video_track->GetCodecId(), "V_VP8", 5)) {
    103     vpx_ctx->fourcc = VP8_FOURCC;
    104   } else if (!strncmp(video_track->GetCodecId(), "V_VP9", 5)) {
    105     vpx_ctx->fourcc = VP9_FOURCC;
    106   } else {
    107     rewind_and_reset(webm_ctx, vpx_ctx);
    108     return 0;
    109   }
    110 
    111   vpx_ctx->framerate.denominator = 0;
    112   vpx_ctx->framerate.numerator = 0;
    113   vpx_ctx->width = static_cast<uint32_t>(video_track->GetWidth());
    114   vpx_ctx->height = static_cast<uint32_t>(video_track->GetHeight());
    115 
    116   get_first_cluster(webm_ctx);
    117 
    118   return 1;
    119 }
    120 
    121 int webm_read_frame(struct WebmInputContext *webm_ctx, uint8_t **buffer,
    122                     size_t *buffer_size) {
    123   // This check is needed for frame parallel decoding, in which case this
    124   // function could be called even after it has reached end of input stream.
    125   if (webm_ctx->reached_eos) {
    126     return 1;
    127   }
    128   mkvparser::Segment *const segment =
    129       reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
    130   const mkvparser::Cluster *cluster =
    131       reinterpret_cast<const mkvparser::Cluster *>(webm_ctx->cluster);
    132   const mkvparser::Block *block =
    133       reinterpret_cast<const mkvparser::Block *>(webm_ctx->block);
    134   const mkvparser::BlockEntry *block_entry =
    135       reinterpret_cast<const mkvparser::BlockEntry *>(webm_ctx->block_entry);
    136   bool block_entry_eos = false;
    137   do {
    138     long status = 0;
    139     bool get_new_block = false;
    140     if (block_entry == NULL && !block_entry_eos) {
    141       status = cluster->GetFirst(block_entry);
    142       get_new_block = true;
    143     } else if (block_entry_eos || block_entry->EOS()) {
    144       cluster = segment->GetNext(cluster);
    145       if (cluster == NULL || cluster->EOS()) {
    146         *buffer_size = 0;
    147         webm_ctx->reached_eos = 1;
    148         return 1;
    149       }
    150       status = cluster->GetFirst(block_entry);
    151       block_entry_eos = false;
    152       get_new_block = true;
    153     } else if (block == NULL ||
    154                webm_ctx->block_frame_index == block->GetFrameCount() ||
    155                block->GetTrackNumber() != webm_ctx->video_track_index) {
    156       status = cluster->GetNext(block_entry, block_entry);
    157       if (block_entry == NULL || block_entry->EOS()) {
    158         block_entry_eos = true;
    159         continue;
    160       }
    161       get_new_block = true;
    162     }
    163     if (status || block_entry == NULL) {
    164       return -1;
    165     }
    166     if (get_new_block) {
    167       block = block_entry->GetBlock();
    168       if (block == NULL) return -1;
    169       webm_ctx->block_frame_index = 0;
    170     }
    171   } while (block_entry_eos ||
    172            block->GetTrackNumber() != webm_ctx->video_track_index);
    173 
    174   webm_ctx->cluster = cluster;
    175   webm_ctx->block_entry = block_entry;
    176   webm_ctx->block = block;
    177 
    178   const mkvparser::Block::Frame &frame =
    179       block->GetFrame(webm_ctx->block_frame_index);
    180   ++webm_ctx->block_frame_index;
    181   if (frame.len > static_cast<long>(*buffer_size)) {
    182     delete[] * buffer;
    183     *buffer = new uint8_t[frame.len];
    184     if (*buffer == NULL) {
    185       return -1;
    186     }
    187     webm_ctx->buffer = *buffer;
    188   }
    189   *buffer_size = frame.len;
    190   webm_ctx->timestamp_ns = block->GetTime(cluster);
    191   webm_ctx->is_key_frame = block->IsKey();
    192 
    193   mkvparser::MkvReader *const reader =
    194       reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
    195   return frame.Read(reader, *buffer) ? -1 : 0;
    196 }
    197 
    198 int webm_guess_framerate(struct WebmInputContext *webm_ctx,
    199                          struct VpxInputContext *vpx_ctx) {
    200   uint32_t i = 0;
    201   uint8_t *buffer = NULL;
    202   size_t buffer_size = 0;
    203   while (webm_ctx->timestamp_ns < 1000000000 && i < 50) {
    204     if (webm_read_frame(webm_ctx, &buffer, &buffer_size)) {
    205       break;
    206     }
    207     ++i;
    208   }
    209   vpx_ctx->framerate.numerator = (i - 1) * 1000000;
    210   vpx_ctx->framerate.denominator =
    211       static_cast<int>(webm_ctx->timestamp_ns / 1000);
    212   delete[] buffer;
    213 
    214   get_first_cluster(webm_ctx);
    215   webm_ctx->block = NULL;
    216   webm_ctx->block_entry = NULL;
    217   webm_ctx->block_frame_index = 0;
    218   webm_ctx->timestamp_ns = 0;
    219   webm_ctx->reached_eos = 0;
    220 
    221   return 0;
    222 }
    223 
    224 void webm_free(struct WebmInputContext *webm_ctx) { reset(webm_ctx); }
    225