| /* |
| * Copyright (c) 2017, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 2 Clause License and |
| * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| * was not distributed with this source code in the LICENSE file, you can |
| * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| * Media Patent License 1.0 was not distributed with this source code in the |
| * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "./obudec.h" |
| |
| #include "aom_ports/mem_ops.h" |
| #include "av1/common/common.h" |
| #include "av1/decoder/obu.h" |
| |
| #define OBU_HEADER_SIZE_BYTES 1 |
| #define OBU_HEADER_EXTENSION_SIZE_BYTES 1 |
| |
| // Unsigned LEB128 OBU length field has maximum size of 8 bytes. |
| #define OBU_MAX_LENGTH_FIELD_SIZE 8 |
| #define OBU_MAX_HEADER_SIZE \ |
| (OBU_HEADER_SIZE_BYTES + OBU_HEADER_EXTENSION_SIZE_BYTES + \ |
| OBU_MAX_LENGTH_FIELD_SIZE) |
| |
| #if CONFIG_OBU_NO_IVF |
| |
| // Reads OBU size from infile and returns 0 upon success. Returns obu_size via |
| // output pointer obu_size. Returns -1 when reading or parsing fails. Always |
| // returns FILE pointer to position at time of call. Returns 0 and sets obu_size |
| // to 0 when end of file is reached. |
| int read_obu_size(FILE *infile, uint64_t *obu_size, size_t *length_field_size) { |
| if (!infile || !obu_size) return 1; |
| |
| uint8_t read_buffer[OBU_MAX_LENGTH_FIELD_SIZE] = { 0 }; |
| size_t bytes_read = fread(read_buffer, 1, OBU_MAX_LENGTH_FIELD_SIZE, infile); |
| *obu_size = 0; |
| |
| if (bytes_read == 0) { |
| return 0; |
| } |
| |
| const int seek_pos = (int)bytes_read; |
| if (seek_pos != 0 && fseek(infile, -seek_pos, SEEK_CUR) != 0) return 1; |
| |
| if (aom_uleb_decode(read_buffer, bytes_read, obu_size, length_field_size) != |
| 0) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int obu_read_temporal_unit(FILE *infile, uint8_t **buffer, size_t *bytes_read, |
| #if CONFIG_SCALABILITY |
| size_t *buffer_size, int last_layer_id) { |
| #else |
| size_t *buffer_size) { |
| #endif |
| size_t ret; |
| uint64_t obu_size = 0; |
| uint8_t *data = NULL; |
| |
| if (feof(infile)) { |
| return 1; |
| } |
| |
| *buffer_size = 0; |
| *bytes_read = 0; |
| while (1) { |
| size_t length_field_size = 0; |
| ret = read_obu_size(infile, &obu_size, &length_field_size); |
| if (ret != 0) { |
| warn("Failed to read OBU size.\n"); |
| return 1; |
| } |
| if (ret == 0 && obu_size == 0) { |
| fprintf(stderr, "Found end of stream, ending temporal unit\n"); |
| break; |
| } |
| |
| obu_size += length_field_size; |
| |
| // Expand the buffer to contain the full OBU and its length. |
| const uint8_t *old_buffer = *buffer; |
| const uint64_t alloc_size = *buffer_size + obu_size; |
| #if defined(AOM_MAX_ALLOCABLE_MEMORY) |
| *buffer = (alloc_size > AOM_MAX_ALLOCABLE_MEMORY) |
| ? NULL |
| : (uint8_t *)realloc(*buffer, (size_t)alloc_size); |
| #else |
| *buffer = (uint8_t *)realloc(*buffer, (size_t)alloc_size); |
| #endif |
| if (!*buffer) { |
| free((void *)old_buffer); |
| warn("OBU buffer alloc failed.\n"); |
| return 1; |
| } |
| |
| data = *buffer + (*buffer_size); |
| *buffer_size += obu_size; |
| ret = fread(data, 1, (size_t)obu_size, infile); |
| |
| if (ret != obu_size) { |
| warn("Failed to read OBU.\n"); |
| return 1; |
| } |
| *bytes_read += (size_t)obu_size; |
| |
| OBU_TYPE obu_type; |
| if (get_obu_type(data[length_field_size], &obu_type) != 0) { |
| warn("Invalid OBU type.\n"); |
| return 1; |
| } |
| |
| if (obu_type == OBU_TEMPORAL_DELIMITER) { |
| // Stop when a temporal delimiter is found |
| // fprintf(stderr, "Found temporal delimiter, ending temporal unit\n"); |
| // prevent decoder to start decoding another frame from this buffer |
| *bytes_read -= (size_t)obu_size; |
| break; |
| } |
| |
| #if CONFIG_SCALABILITY |
| // break if obu_extension_flag is found and enhancement_id change |
| if (data[length_field_size] & 0x1) { |
| const uint8_t obu_extension_header = |
| data[length_field_size + OBU_HEADER_SIZE_BYTES]; |
| const int curr_layer_id = (obu_extension_header >> 3) & 0x3; |
| if (curr_layer_id && (curr_layer_id > last_layer_id)) { |
| // new enhancement layer |
| *bytes_read -= (size_t)obu_size; |
| const int i_obu_size = (int)obu_size; |
| fseek(infile, -i_obu_size, SEEK_CUR); |
| break; |
| } |
| } |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| int file_is_obu(struct AvxInputContext *input_ctx) { |
| uint8_t obutd[OBU_MAX_HEADER_SIZE] = { 0 }; |
| uint64_t size = 0; |
| |
| // Parsing of Annex B OBU streams via pipe without framing not supported. This |
| // implementation requires seeking backwards in the input stream. Tell caller |
| // that this input cannot be processed. |
| if (!input_ctx->filename || strcmp(input_ctx->filename, "-") == 0) return 0; |
| |
| // Reading the first OBU TD to enable TU end detection at TD start. |
| const size_t ret = fread(obutd, 1, OBU_MAX_HEADER_SIZE, input_ctx->file); |
| if (ret != OBU_MAX_HEADER_SIZE) { |
| warn("Failed to read OBU Header, not enough data to process header.\n"); |
| return 0; |
| } |
| |
| if (aom_uleb_decode(obutd, OBU_MAX_HEADER_SIZE, &size, NULL) != 0) { |
| warn("OBU size parse failed.\n"); |
| return 0; |
| } |
| |
| const size_t obu_header_offset = aom_uleb_size_in_bytes(size); |
| |
| fseek(input_ctx->file, obu_header_offset + OBU_HEADER_SIZE_BYTES, SEEK_SET); |
| |
| if (size != 1) { |
| warn("Expected first OBU size to be 1, got %d\n", size); |
| return 0; |
| } |
| OBU_TYPE obu_type; |
| if (get_obu_type(obutd[obu_header_offset], &obu_type) != 0) { |
| warn("Invalid OBU type found while probing for OBU_TEMPORAL_DELIMITER.\n"); |
| return 0; |
| } |
| if (obu_type != OBU_TEMPORAL_DELIMITER) { |
| warn("Expected OBU TD at file start, got %d\n", obutd[obu_header_offset]); |
| return 0; |
| } |
| |
| // fprintf(stderr, "Starting to parse OBU stream\n"); |
| return 1; |
| } |
| |
| #endif |