xref: /aosp_15_r20/external/libaom/common/webmdec.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker  * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker  *
4*77c1e3ccSAndroid Build Coastguard Worker  * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker  * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker  * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker  */
11*77c1e3ccSAndroid Build Coastguard Worker 
12*77c1e3ccSAndroid Build Coastguard Worker #include "common/webmdec.h"
13*77c1e3ccSAndroid Build Coastguard Worker 
14*77c1e3ccSAndroid Build Coastguard Worker #include <cassert>
15*77c1e3ccSAndroid Build Coastguard Worker #include <cstring>
16*77c1e3ccSAndroid Build Coastguard Worker #include <cstdio>
17*77c1e3ccSAndroid Build Coastguard Worker 
18*77c1e3ccSAndroid Build Coastguard Worker #include "third_party/libwebm/mkvparser/mkvparser.h"
19*77c1e3ccSAndroid Build Coastguard Worker #include "third_party/libwebm/mkvparser/mkvreader.h"
20*77c1e3ccSAndroid Build Coastguard Worker 
21*77c1e3ccSAndroid Build Coastguard Worker namespace {
22*77c1e3ccSAndroid Build Coastguard Worker 
reset(struct WebmInputContext * const webm_ctx)23*77c1e3ccSAndroid Build Coastguard Worker void reset(struct WebmInputContext *const webm_ctx) {
24*77c1e3ccSAndroid Build Coastguard Worker   if (webm_ctx->reader != NULL) {
25*77c1e3ccSAndroid Build Coastguard Worker     mkvparser::MkvReader *const reader =
26*77c1e3ccSAndroid Build Coastguard Worker         reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
27*77c1e3ccSAndroid Build Coastguard Worker     delete reader;
28*77c1e3ccSAndroid Build Coastguard Worker   }
29*77c1e3ccSAndroid Build Coastguard Worker   if (webm_ctx->segment != NULL) {
30*77c1e3ccSAndroid Build Coastguard Worker     mkvparser::Segment *const segment =
31*77c1e3ccSAndroid Build Coastguard Worker         reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
32*77c1e3ccSAndroid Build Coastguard Worker     delete segment;
33*77c1e3ccSAndroid Build Coastguard Worker   }
34*77c1e3ccSAndroid Build Coastguard Worker   if (webm_ctx->buffer != NULL) {
35*77c1e3ccSAndroid Build Coastguard Worker     delete[] webm_ctx->buffer;
36*77c1e3ccSAndroid Build Coastguard Worker   }
37*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->reader = NULL;
38*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->segment = NULL;
39*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->buffer = NULL;
40*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->cluster = NULL;
41*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block_entry = NULL;
42*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block = NULL;
43*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block_frame_index = 0;
44*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->video_track_index = 0;
45*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->timestamp_ns = 0;
46*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->is_key_frame = false;
47*77c1e3ccSAndroid Build Coastguard Worker }
48*77c1e3ccSAndroid Build Coastguard Worker 
get_first_cluster(struct WebmInputContext * const webm_ctx)49*77c1e3ccSAndroid Build Coastguard Worker void get_first_cluster(struct WebmInputContext *const webm_ctx) {
50*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::Segment *const segment =
51*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
52*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::Cluster *const cluster = segment->GetFirst();
53*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->cluster = cluster;
54*77c1e3ccSAndroid Build Coastguard Worker }
55*77c1e3ccSAndroid Build Coastguard Worker 
rewind_and_reset(struct WebmInputContext * const webm_ctx,struct AvxInputContext * const aom_ctx)56*77c1e3ccSAndroid Build Coastguard Worker void rewind_and_reset(struct WebmInputContext *const webm_ctx,
57*77c1e3ccSAndroid Build Coastguard Worker                       struct AvxInputContext *const aom_ctx) {
58*77c1e3ccSAndroid Build Coastguard Worker   rewind(aom_ctx->file);
59*77c1e3ccSAndroid Build Coastguard Worker   reset(webm_ctx);
60*77c1e3ccSAndroid Build Coastguard Worker }
61*77c1e3ccSAndroid Build Coastguard Worker 
62*77c1e3ccSAndroid Build Coastguard Worker }  // namespace
63*77c1e3ccSAndroid Build Coastguard Worker 
file_is_webm(struct WebmInputContext * webm_ctx,struct AvxInputContext * aom_ctx)64*77c1e3ccSAndroid Build Coastguard Worker int file_is_webm(struct WebmInputContext *webm_ctx,
65*77c1e3ccSAndroid Build Coastguard Worker                  struct AvxInputContext *aom_ctx) {
66*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::MkvReader *const reader = new mkvparser::MkvReader(aom_ctx->file);
67*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->reader = reader;
68*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->reached_eos = 0;
69*77c1e3ccSAndroid Build Coastguard Worker 
70*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::EBMLHeader header;
71*77c1e3ccSAndroid Build Coastguard Worker   long long pos = 0;
72*77c1e3ccSAndroid Build Coastguard Worker   if (header.Parse(reader, pos) < 0) {
73*77c1e3ccSAndroid Build Coastguard Worker     rewind_and_reset(webm_ctx, aom_ctx);
74*77c1e3ccSAndroid Build Coastguard Worker     return 0;
75*77c1e3ccSAndroid Build Coastguard Worker   }
76*77c1e3ccSAndroid Build Coastguard Worker 
77*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::Segment *segment;
78*77c1e3ccSAndroid Build Coastguard Worker   if (mkvparser::Segment::CreateInstance(reader, pos, segment)) {
79*77c1e3ccSAndroid Build Coastguard Worker     rewind_and_reset(webm_ctx, aom_ctx);
80*77c1e3ccSAndroid Build Coastguard Worker     return 0;
81*77c1e3ccSAndroid Build Coastguard Worker   }
82*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->segment = segment;
83*77c1e3ccSAndroid Build Coastguard Worker   if (segment->Load() < 0) {
84*77c1e3ccSAndroid Build Coastguard Worker     rewind_and_reset(webm_ctx, aom_ctx);
85*77c1e3ccSAndroid Build Coastguard Worker     return 0;
86*77c1e3ccSAndroid Build Coastguard Worker   }
87*77c1e3ccSAndroid Build Coastguard Worker 
88*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::Tracks *const tracks = segment->GetTracks();
89*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::VideoTrack *video_track = NULL;
90*77c1e3ccSAndroid Build Coastguard Worker   for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) {
91*77c1e3ccSAndroid Build Coastguard Worker     const mkvparser::Track *const track = tracks->GetTrackByIndex(i);
92*77c1e3ccSAndroid Build Coastguard Worker     if (track->GetType() == mkvparser::Track::kVideo) {
93*77c1e3ccSAndroid Build Coastguard Worker       video_track = static_cast<const mkvparser::VideoTrack *>(track);
94*77c1e3ccSAndroid Build Coastguard Worker       webm_ctx->video_track_index = static_cast<int>(track->GetNumber());
95*77c1e3ccSAndroid Build Coastguard Worker       break;
96*77c1e3ccSAndroid Build Coastguard Worker     }
97*77c1e3ccSAndroid Build Coastguard Worker   }
98*77c1e3ccSAndroid Build Coastguard Worker 
99*77c1e3ccSAndroid Build Coastguard Worker   if (video_track == NULL || video_track->GetCodecId() == NULL) {
100*77c1e3ccSAndroid Build Coastguard Worker     rewind_and_reset(webm_ctx, aom_ctx);
101*77c1e3ccSAndroid Build Coastguard Worker     return 0;
102*77c1e3ccSAndroid Build Coastguard Worker   }
103*77c1e3ccSAndroid Build Coastguard Worker 
104*77c1e3ccSAndroid Build Coastguard Worker   if (!strncmp(video_track->GetCodecId(), "V_AV1", 5)) {
105*77c1e3ccSAndroid Build Coastguard Worker     aom_ctx->fourcc = AV1_FOURCC;
106*77c1e3ccSAndroid Build Coastguard Worker   } else {
107*77c1e3ccSAndroid Build Coastguard Worker     rewind_and_reset(webm_ctx, aom_ctx);
108*77c1e3ccSAndroid Build Coastguard Worker     return 0;
109*77c1e3ccSAndroid Build Coastguard Worker   }
110*77c1e3ccSAndroid Build Coastguard Worker 
111*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->framerate.denominator = 0;
112*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->framerate.numerator = 0;
113*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->width = static_cast<uint32_t>(video_track->GetWidth());
114*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->height = static_cast<uint32_t>(video_track->GetHeight());
115*77c1e3ccSAndroid Build Coastguard Worker 
116*77c1e3ccSAndroid Build Coastguard Worker   get_first_cluster(webm_ctx);
117*77c1e3ccSAndroid Build Coastguard Worker 
118*77c1e3ccSAndroid Build Coastguard Worker   return 1;
119*77c1e3ccSAndroid Build Coastguard Worker }
120*77c1e3ccSAndroid Build Coastguard Worker 
webm_read_frame(struct WebmInputContext * webm_ctx,uint8_t ** buffer,size_t * bytes_read,size_t * buffer_size)121*77c1e3ccSAndroid Build Coastguard Worker int webm_read_frame(struct WebmInputContext *webm_ctx, uint8_t **buffer,
122*77c1e3ccSAndroid Build Coastguard Worker                     size_t *bytes_read, size_t *buffer_size) {
123*77c1e3ccSAndroid Build Coastguard Worker   assert(webm_ctx->buffer == *buffer);
124*77c1e3ccSAndroid Build Coastguard Worker   // This check is needed for frame parallel decoding, in which case this
125*77c1e3ccSAndroid Build Coastguard Worker   // function could be called even after it has reached end of input stream.
126*77c1e3ccSAndroid Build Coastguard Worker   if (webm_ctx->reached_eos) {
127*77c1e3ccSAndroid Build Coastguard Worker     return 1;
128*77c1e3ccSAndroid Build Coastguard Worker   }
129*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::Segment *const segment =
130*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
131*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::Cluster *cluster =
132*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<const mkvparser::Cluster *>(webm_ctx->cluster);
133*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::Block *block =
134*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<const mkvparser::Block *>(webm_ctx->block);
135*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::BlockEntry *block_entry =
136*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<const mkvparser::BlockEntry *>(webm_ctx->block_entry);
137*77c1e3ccSAndroid Build Coastguard Worker   bool block_entry_eos = false;
138*77c1e3ccSAndroid Build Coastguard Worker   do {
139*77c1e3ccSAndroid Build Coastguard Worker     long status = 0;
140*77c1e3ccSAndroid Build Coastguard Worker     bool get_new_block = false;
141*77c1e3ccSAndroid Build Coastguard Worker     if (block_entry == NULL && !block_entry_eos) {
142*77c1e3ccSAndroid Build Coastguard Worker       status = cluster->GetFirst(block_entry);
143*77c1e3ccSAndroid Build Coastguard Worker       get_new_block = true;
144*77c1e3ccSAndroid Build Coastguard Worker     } else if (block_entry_eos || block_entry->EOS()) {
145*77c1e3ccSAndroid Build Coastguard Worker       cluster = segment->GetNext(cluster);
146*77c1e3ccSAndroid Build Coastguard Worker       if (cluster == NULL || cluster->EOS()) {
147*77c1e3ccSAndroid Build Coastguard Worker         *bytes_read = 0;
148*77c1e3ccSAndroid Build Coastguard Worker         webm_ctx->reached_eos = 1;
149*77c1e3ccSAndroid Build Coastguard Worker         return 1;
150*77c1e3ccSAndroid Build Coastguard Worker       }
151*77c1e3ccSAndroid Build Coastguard Worker       status = cluster->GetFirst(block_entry);
152*77c1e3ccSAndroid Build Coastguard Worker       block_entry_eos = false;
153*77c1e3ccSAndroid Build Coastguard Worker       get_new_block = true;
154*77c1e3ccSAndroid Build Coastguard Worker     } else if (block == NULL ||
155*77c1e3ccSAndroid Build Coastguard Worker                webm_ctx->block_frame_index == block->GetFrameCount() ||
156*77c1e3ccSAndroid Build Coastguard Worker                block->GetTrackNumber() != webm_ctx->video_track_index) {
157*77c1e3ccSAndroid Build Coastguard Worker       status = cluster->GetNext(block_entry, block_entry);
158*77c1e3ccSAndroid Build Coastguard Worker       if (block_entry == NULL || block_entry->EOS()) {
159*77c1e3ccSAndroid Build Coastguard Worker         block_entry_eos = true;
160*77c1e3ccSAndroid Build Coastguard Worker         continue;
161*77c1e3ccSAndroid Build Coastguard Worker       }
162*77c1e3ccSAndroid Build Coastguard Worker       get_new_block = true;
163*77c1e3ccSAndroid Build Coastguard Worker     }
164*77c1e3ccSAndroid Build Coastguard Worker     if (status || block_entry == NULL) {
165*77c1e3ccSAndroid Build Coastguard Worker       return -1;
166*77c1e3ccSAndroid Build Coastguard Worker     }
167*77c1e3ccSAndroid Build Coastguard Worker     if (get_new_block) {
168*77c1e3ccSAndroid Build Coastguard Worker       block = block_entry->GetBlock();
169*77c1e3ccSAndroid Build Coastguard Worker       if (block == NULL) return -1;
170*77c1e3ccSAndroid Build Coastguard Worker       webm_ctx->block_frame_index = 0;
171*77c1e3ccSAndroid Build Coastguard Worker     }
172*77c1e3ccSAndroid Build Coastguard Worker   } while (block_entry_eos ||
173*77c1e3ccSAndroid Build Coastguard Worker            block->GetTrackNumber() != webm_ctx->video_track_index);
174*77c1e3ccSAndroid Build Coastguard Worker 
175*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->cluster = cluster;
176*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block_entry = block_entry;
177*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block = block;
178*77c1e3ccSAndroid Build Coastguard Worker 
179*77c1e3ccSAndroid Build Coastguard Worker   const mkvparser::Block::Frame &frame =
180*77c1e3ccSAndroid Build Coastguard Worker       block->GetFrame(webm_ctx->block_frame_index);
181*77c1e3ccSAndroid Build Coastguard Worker   ++webm_ctx->block_frame_index;
182*77c1e3ccSAndroid Build Coastguard Worker   if (frame.len > static_cast<long>(*buffer_size)) {
183*77c1e3ccSAndroid Build Coastguard Worker     delete[] * buffer;
184*77c1e3ccSAndroid Build Coastguard Worker     *buffer = new uint8_t[frame.len];
185*77c1e3ccSAndroid Build Coastguard Worker     webm_ctx->buffer = *buffer;
186*77c1e3ccSAndroid Build Coastguard Worker     if (*buffer == NULL) {
187*77c1e3ccSAndroid Build Coastguard Worker       return -1;
188*77c1e3ccSAndroid Build Coastguard Worker     }
189*77c1e3ccSAndroid Build Coastguard Worker     *buffer_size = frame.len;
190*77c1e3ccSAndroid Build Coastguard Worker   }
191*77c1e3ccSAndroid Build Coastguard Worker   *bytes_read = frame.len;
192*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->timestamp_ns = block->GetTime(cluster);
193*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->is_key_frame = block->IsKey();
194*77c1e3ccSAndroid Build Coastguard Worker 
195*77c1e3ccSAndroid Build Coastguard Worker   mkvparser::MkvReader *const reader =
196*77c1e3ccSAndroid Build Coastguard Worker       reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
197*77c1e3ccSAndroid Build Coastguard Worker   return frame.Read(reader, *buffer) ? -1 : 0;
198*77c1e3ccSAndroid Build Coastguard Worker }
199*77c1e3ccSAndroid Build Coastguard Worker 
200*77c1e3ccSAndroid Build Coastguard Worker // Calculate the greatest common divisor between two numbers.
gcd(int a,int b)201*77c1e3ccSAndroid Build Coastguard Worker static int gcd(int a, int b) {
202*77c1e3ccSAndroid Build Coastguard Worker   int remainder;
203*77c1e3ccSAndroid Build Coastguard Worker   while (b > 0) {
204*77c1e3ccSAndroid Build Coastguard Worker     remainder = a % b;
205*77c1e3ccSAndroid Build Coastguard Worker     a = b;
206*77c1e3ccSAndroid Build Coastguard Worker     b = remainder;
207*77c1e3ccSAndroid Build Coastguard Worker   }
208*77c1e3ccSAndroid Build Coastguard Worker   return a;
209*77c1e3ccSAndroid Build Coastguard Worker }
210*77c1e3ccSAndroid Build Coastguard Worker 
webm_guess_framerate(struct WebmInputContext * webm_ctx,struct AvxInputContext * aom_ctx)211*77c1e3ccSAndroid Build Coastguard Worker int webm_guess_framerate(struct WebmInputContext *webm_ctx,
212*77c1e3ccSAndroid Build Coastguard Worker                          struct AvxInputContext *aom_ctx) {
213*77c1e3ccSAndroid Build Coastguard Worker   uint32_t i = 0;
214*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *buffer = NULL;
215*77c1e3ccSAndroid Build Coastguard Worker   size_t buffer_size = 0;
216*77c1e3ccSAndroid Build Coastguard Worker   size_t bytes_read = 0;
217*77c1e3ccSAndroid Build Coastguard Worker   assert(webm_ctx->buffer == NULL);
218*77c1e3ccSAndroid Build Coastguard Worker   while (webm_ctx->timestamp_ns < 1000000000 && i < 50) {
219*77c1e3ccSAndroid Build Coastguard Worker     if (webm_read_frame(webm_ctx, &buffer, &bytes_read, &buffer_size)) {
220*77c1e3ccSAndroid Build Coastguard Worker       break;
221*77c1e3ccSAndroid Build Coastguard Worker     }
222*77c1e3ccSAndroid Build Coastguard Worker     ++i;
223*77c1e3ccSAndroid Build Coastguard Worker   }
224*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->framerate.numerator = (i - 1) * 1000000;
225*77c1e3ccSAndroid Build Coastguard Worker   aom_ctx->framerate.denominator =
226*77c1e3ccSAndroid Build Coastguard Worker       static_cast<int>(webm_ctx->timestamp_ns / 1000);
227*77c1e3ccSAndroid Build Coastguard Worker   // Fraction might be represented in large numbers, like 49000000/980000
228*77c1e3ccSAndroid Build Coastguard Worker   // for 50fps. Simplify as much as possible.
229*77c1e3ccSAndroid Build Coastguard Worker   int g = gcd(aom_ctx->framerate.numerator, aom_ctx->framerate.denominator);
230*77c1e3ccSAndroid Build Coastguard Worker   if (g != 0) {
231*77c1e3ccSAndroid Build Coastguard Worker     aom_ctx->framerate.numerator /= g;
232*77c1e3ccSAndroid Build Coastguard Worker     aom_ctx->framerate.denominator /= g;
233*77c1e3ccSAndroid Build Coastguard Worker   }
234*77c1e3ccSAndroid Build Coastguard Worker 
235*77c1e3ccSAndroid Build Coastguard Worker   delete[] buffer;
236*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->buffer = NULL;
237*77c1e3ccSAndroid Build Coastguard Worker 
238*77c1e3ccSAndroid Build Coastguard Worker   get_first_cluster(webm_ctx);
239*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block = NULL;
240*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block_entry = NULL;
241*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->block_frame_index = 0;
242*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->timestamp_ns = 0;
243*77c1e3ccSAndroid Build Coastguard Worker   webm_ctx->reached_eos = 0;
244*77c1e3ccSAndroid Build Coastguard Worker 
245*77c1e3ccSAndroid Build Coastguard Worker   return 0;
246*77c1e3ccSAndroid Build Coastguard Worker }
247*77c1e3ccSAndroid Build Coastguard Worker 
webm_free(struct WebmInputContext * webm_ctx)248*77c1e3ccSAndroid Build Coastguard Worker void webm_free(struct WebmInputContext *webm_ctx) { reset(webm_ctx); }
249