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