Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017, Alliance for Open Media. All rights reserved |
| 3 | * |
| 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. |
| 10 | */ |
| 11 | |
| 12 | #include <stdio.h> |
| 13 | #include <stdlib.h> |
| 14 | #include <string.h> |
| 15 | |
| 16 | #include "./obudec.h" |
| 17 | |
| 18 | #include "aom_ports/mem_ops.h" |
| 19 | #include "av1/common/common.h" |
Tom Finegan | 13ee28c | 2018-02-26 19:37:41 -0800 | [diff] [blame] | 20 | #include "av1/decoder/obu.h" |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 21 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 22 | #define OBU_BUFFER_SIZE (500 * 1024) |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 23 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 24 | #define OBU_HEADER_SIZE 1 |
| 25 | #define OBU_EXTENSION_SIZE 1 |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 26 | #define OBU_MAX_LENGTH_FIELD_SIZE 8 |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 27 | #define OBU_DETECTION_SIZE \ |
| 28 | (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE + OBU_MAX_LENGTH_FIELD_SIZE) |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 29 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 30 | // Reads unsigned LEB128 integer and returns 0 upon successful read and decode. |
| 31 | // Stores raw bytes in 'value_buffer', length of the number in 'value_length', |
| 32 | // and decoded value in 'value'. |
| 33 | static int obudec_read_leb128(FILE *f, uint8_t *value_buffer, |
| 34 | size_t *value_length, uint64_t *value) { |
| 35 | if (!f || !value_buffer || !value_length || !value) return -1; |
| 36 | for (int len = 0; len < OBU_MAX_LENGTH_FIELD_SIZE; ++len) { |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 37 | const size_t num_read = fread(&value_buffer[len], 1, 1, f); |
| 38 | if (num_read != 1) { |
| 39 | // Ran out of data before completing read of value. |
| 40 | return -1; |
| 41 | } |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 42 | if ((value_buffer[len] >> 7) == 0) { |
| 43 | *value_length = (size_t)(len + 1); |
| 44 | break; |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | return aom_uleb_decode(value_buffer, OBU_MAX_LENGTH_FIELD_SIZE, value, NULL); |
| 49 | } |
| 50 | |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 51 | // Reads OBU size from infile and returns 0 upon success. Returns obu_size via |
| 52 | // output pointer obu_size. Returns -1 when reading or parsing fails. Always |
| 53 | // returns FILE pointer to position at time of call. Returns 0 and sets obu_size |
| 54 | // to 0 when end of file is reached. |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 55 | static int obudec_read_obu_size(FILE *infile, uint64_t *obu_size, |
| 56 | size_t *length_field_size) { |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 57 | if (!infile || !obu_size) return 1; |
| 58 | |
| 59 | uint8_t read_buffer[OBU_MAX_LENGTH_FIELD_SIZE] = { 0 }; |
| 60 | size_t bytes_read = fread(read_buffer, 1, OBU_MAX_LENGTH_FIELD_SIZE, infile); |
| 61 | *obu_size = 0; |
| 62 | |
| 63 | if (bytes_read == 0) { |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | const int seek_pos = (int)bytes_read; |
| 68 | if (seek_pos != 0 && fseek(infile, -seek_pos, SEEK_CUR) != 0) return 1; |
| 69 | |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 70 | if (aom_uleb_decode(read_buffer, bytes_read, obu_size, length_field_size) != |
| 71 | 0) { |
| 72 | return 1; |
| 73 | } |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 74 | |
| 75 | return 0; |
| 76 | } |
| 77 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 78 | // Reads OBU header from 'f'. The 'buffer_capacity' passed in must be large |
| 79 | // enough to store an OBU header with extension (2 bytes). Raw OBU data is |
| 80 | // written to 'obu_data', parsed OBU header values are written to 'obu_header', |
| 81 | // and total bytes read from file are written to 'bytes_read'. Returns 0 for |
| 82 | // success, and non-zero on failure. When end of file is reached, the return |
| 83 | // value is 0 and the 'bytes_read' value is set to 0. |
| 84 | static int obudec_read_obu_header(FILE *f, size_t buffer_capacity, |
| 85 | uint8_t *obu_data, ObuHeader *obu_header, |
| 86 | size_t *bytes_read) { |
| 87 | if (!f || buffer_capacity < (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE) || |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 88 | !obu_data || !obu_header || !bytes_read) { |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 89 | return -1; |
| 90 | } |
| 91 | *bytes_read = fread(obu_data, 1, 1, f); |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 92 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 93 | if (feof(f) && *bytes_read == 0) { |
| 94 | return 0; |
| 95 | } else if (*bytes_read != 1) { |
| 96 | fprintf(stderr, "obudec: Failure reading OBU header.\n"); |
| 97 | return -1; |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 98 | } |
| 99 | |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 100 | const int has_extension = (obu_data[0] >> 2) & 0x1; |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 101 | if (has_extension) { |
| 102 | if (fread(&obu_data[1], 1, 1, f) != 1) { |
| 103 | fprintf(stderr, "obudec: Failure reading OBU extension."); |
| 104 | return -1; |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 105 | } |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 106 | ++*bytes_read; |
| 107 | } |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 108 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 109 | size_t obu_bytes_parsed = 0; |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 110 | const aom_codec_err_t parse_result = |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 111 | aom_read_obu_header(obu_data, *bytes_read, &obu_bytes_parsed, obu_header); |
| 112 | if (parse_result != AOM_CODEC_OK || *bytes_read != obu_bytes_parsed) { |
| 113 | fprintf(stderr, "obudec: Error parsing OBU header.\n"); |
| 114 | return -1; |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 115 | } |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 116 | |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 117 | return 0; |
| 118 | } |
| 119 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 120 | // Reads OBU payload from 'f' and returns 0 for success when all payload bytes |
| 121 | // are read from the file. Payload data is written to 'obu_data', and actual |
| 122 | // bytes read written to 'bytes_read'. |
| 123 | static int obudec_read_obu_payload(FILE *f, uint64_t payload_length, |
| 124 | uint8_t *obu_data, size_t *bytes_read) { |
| 125 | if (!f || payload_length == 0 || !obu_data || !bytes_read) return -1; |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 126 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 127 | if (fread(obu_data, 1, (size_t)payload_length, f) != payload_length) { |
| 128 | fprintf(stderr, "obudec: Failure reading OBU payload.\n"); |
| 129 | return -1; |
| 130 | } |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 131 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 132 | *bytes_read += payload_length; |
| 133 | return 0; |
| 134 | } |
| 135 | |
| 136 | static int obudec_read_one_obu(FILE *f, size_t buffer_capacity, |
| 137 | uint8_t *obu_data, uint64_t *obu_length, |
| 138 | ObuHeader *obu_header) { |
| 139 | const size_t kMinimumBufferSize = OBU_DETECTION_SIZE; |
| 140 | if (!f || !obu_data || !obu_length || !obu_header || |
| 141 | buffer_capacity < kMinimumBufferSize) { |
| 142 | return -1; |
| 143 | } |
| 144 | |
| 145 | size_t bytes_read = 0; |
| 146 | if (obudec_read_obu_header(f, buffer_capacity, obu_data, obu_header, |
| 147 | &bytes_read) != 0) { |
| 148 | return -1; |
| 149 | } else if (bytes_read == 0) { |
| 150 | *obu_length = 0; |
Hui Su | e19c321 | 2018-02-21 14:32:18 -0800 | [diff] [blame] | 151 | return 0; |
| 152 | } |
Tom Finegan | 41150ad | 2018-01-23 11:42:55 -0800 | [diff] [blame] | 153 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 154 | uint64_t obu_payload_length = 0; |
| 155 | size_t leb128_length = 0; |
| 156 | if (obudec_read_leb128(f, &obu_data[bytes_read], &leb128_length, |
| 157 | &obu_payload_length) != 0) { |
| 158 | fprintf(stderr, "obudec: Failure reading OBU payload length.\n"); |
| 159 | return -1; |
| 160 | } |
| 161 | bytes_read += leb128_length; |
| 162 | |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 163 | if (UINT64_MAX - bytes_read < obu_payload_length) return -1; |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 164 | if (bytes_read + obu_payload_length > buffer_capacity) { |
| 165 | *obu_length = bytes_read + obu_payload_length; |
| 166 | return -1; |
| 167 | } |
| 168 | |
| 169 | if (obu_payload_length > 0 && |
| 170 | obudec_read_obu_payload(f, obu_payload_length, &obu_data[bytes_read], |
| 171 | &bytes_read) != 0) { |
| 172 | return -1; |
| 173 | } |
| 174 | |
| 175 | *obu_length = bytes_read; |
| 176 | return 0; |
| 177 | } |
| 178 | |
| 179 | int file_is_obu(struct ObuDecInputContext *obu_ctx) { |
| 180 | if (!obu_ctx || !obu_ctx->avx_ctx) return 0; |
| 181 | |
| 182 | struct AvxInputContext *avx_ctx = obu_ctx->avx_ctx; |
| 183 | uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 }; |
| 184 | |
| 185 | FILE *f = avx_ctx->file; |
| 186 | uint64_t obu_length = 0; |
| 187 | ObuHeader obu_header; |
| 188 | memset(&obu_header, 0, sizeof(obu_header)); |
| 189 | |
| 190 | if (obudec_read_one_obu(f, OBU_DETECTION_SIZE, &detect_buf[0], &obu_length, |
| 191 | &obu_header) != 0) { |
| 192 | fprintf(stderr, "obudec: Failure reading first OBU.\n"); |
| 193 | rewind(f); |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 194 | return 0; |
| 195 | } |
Tom Finegan | 9d9ec1f | 2018-03-14 17:55:18 -0700 | [diff] [blame] | 196 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 197 | if (obu_header.type != OBU_TEMPORAL_DELIMITER) return 0; |
Tom Finegan | 41150ad | 2018-01-23 11:42:55 -0800 | [diff] [blame] | 198 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 199 | if (obu_header.has_length_field) { |
| 200 | uint64_t obu_payload_length = 0; |
| 201 | size_t leb128_length = 0; |
| 202 | const size_t obu_length_offset = obu_header.has_length_field ? 1 : 2; |
| 203 | if (aom_uleb_decode(&detect_buf[obu_length_offset], sizeof(leb128_length), |
| 204 | &obu_payload_length, &leb128_length) != 0) { |
| 205 | fprintf(stderr, "obudec: Failure decoding OBU payload length.\n"); |
| 206 | rewind(f); |
| 207 | return 0; |
| 208 | } |
| 209 | if (obu_payload_length != 0) { |
| 210 | fprintf( |
| 211 | stderr, |
| 212 | "obudec: Invalid OBU_TEMPORAL_DELIMITER payload length (non-zero)."); |
| 213 | rewind(f); |
| 214 | return 0; |
| 215 | } |
| 216 | } else { |
| 217 | fprintf(stderr, "obudec: OBU size fields required, cannot decode input.\n"); |
| 218 | rewind(f); |
Tom Finegan | ccb8cc4 | 2018-02-22 14:08:13 -0800 | [diff] [blame] | 219 | return 0; |
| 220 | } |
| 221 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 222 | // Appears that input is valid Section 5 AV1 stream. |
| 223 | obu_ctx->buffer = (uint8_t *)calloc(OBU_BUFFER_SIZE, 1); |
| 224 | if (!obu_ctx->buffer) { |
| 225 | fprintf(stderr, "Out of memory.\n"); |
| 226 | rewind(f); |
| 227 | return 0; |
| 228 | } |
| 229 | obu_ctx->buffer_capacity = OBU_BUFFER_SIZE; |
| 230 | memcpy(obu_ctx->buffer, &detect_buf[0], obu_length); |
| 231 | obu_ctx->bytes_buffered = obu_length; |
| 232 | |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 233 | return 1; |
| 234 | } |
| 235 | |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 236 | int obudec_read_temporal_unit(struct ObuDecInputContext *obu_ctx, |
| 237 | uint8_t **buffer, size_t *bytes_read, |
| 238 | #if CONFIG_SCALABILITY |
| 239 | size_t *buffer_size, int last_layer_id |
| 240 | #else |
| 241 | size_t *buffer_size |
Cyril Concolato | 6c78883 | 2017-10-30 16:30:35 -0700 | [diff] [blame] | 242 | #endif |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 243 | ) { |
| 244 | FILE *f = obu_ctx->avx_ctx->file; |
Tom Finegan | 85db49b | 2018-03-23 10:49:51 -0700 | [diff] [blame] | 245 | if (!f) return -1; |
Tom Finegan | 66ae916 | 2018-03-19 18:12:50 -0700 | [diff] [blame] | 246 | |
| 247 | *buffer_size = 0; |
| 248 | *bytes_read = 0; |
| 249 | |
| 250 | if (feof(f)) { |
| 251 | return 1; |
| 252 | } |
| 253 | |
| 254 | while (1) { |
| 255 | ObuHeader obu_header; |
| 256 | memset(&obu_header, 0, sizeof(obu_header)); |
| 257 | |
| 258 | uint64_t obu_size = 0; |
| 259 | uint8_t *data = obu_ctx->buffer + obu_ctx->bytes_buffered; |
| 260 | const size_t capacity = obu_ctx->buffer_capacity - obu_ctx->bytes_buffered; |
| 261 | |
| 262 | if (obudec_read_one_obu(f, capacity, data, &obu_size, &obu_header) != 0) { |
| 263 | fprintf(stderr, "obudec: read_one_obu failed in TU loop\n"); |
| 264 | return -1; |
| 265 | } |
| 266 | |
| 267 | if (obu_header.type == OBU_TEMPORAL_DELIMITER || obu_size == 0 |
| 268 | #if CONFIG_SCALABILITY |
| 269 | || (obu_header.has_extension && |
| 270 | obu_header.enhancement_layer_id > last_layer_id) |
| 271 | #endif |
| 272 | ) { |
| 273 | const uint64_t tu_size = obu_ctx->bytes_buffered; |
| 274 | |
| 275 | #if defined AOM_MAX_ALLOCABLE_MEMORY |
| 276 | if (tu_size > AOM_MAX_ALLOCABLE_MEMORY) { |
| 277 | fprintf(stderr, "obudec: Temporal Unit size exceeds max alloc size.\n"); |
| 278 | return -1; |
| 279 | } |
| 280 | #endif |
| 281 | uint8_t *new_buffer = (uint8_t *)realloc(*buffer, tu_size); |
| 282 | if (!new_buffer) { |
| 283 | free(*buffer); |
| 284 | fprintf(stderr, "obudec: Out of memory.\n"); |
| 285 | return -1; |
| 286 | } |
| 287 | *buffer = new_buffer; |
| 288 | *bytes_read = tu_size; |
| 289 | *buffer_size = tu_size; |
| 290 | memcpy(*buffer, obu_ctx->buffer, tu_size); |
| 291 | |
| 292 | memmove(obu_ctx->buffer, data, obu_size); |
| 293 | obu_ctx->bytes_buffered = obu_size; |
| 294 | break; |
| 295 | } else { |
| 296 | obu_ctx->bytes_buffered += obu_size; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | return 0; |
| 301 | } |
| 302 | |
| 303 | void obudec_free(struct ObuDecInputContext *obu_ctx) { free(obu_ctx->buffer); } |