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