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