| /* | 
 |  * Copyright (c) 2021, Alliance for Open Media. All rights reserved | 
 |  * | 
 |  * This source code is subject to the terms of the BSD 3-Clause Clear License | 
 |  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear | 
 |  * License was not distributed with this source code in the LICENSE file, you | 
 |  * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.  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 | 
 |  * aomedia.org/license/patent-license/. | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 |  | 
 | #include "common/obudec.h" | 
 |  | 
 | #include "aom_dsp/aom_dsp_common.h" | 
 | #include "aom_ports/mem_ops.h" | 
 | #include "av1/common/common.h" | 
 | #include "av1/common/obu_util.h" | 
 |  | 
 | #define OBU_BUFFER_SIZE (500 * 1024) | 
 |  | 
 | #define OBU_HEADER_SIZE 1 | 
 | #define OBU_EXTENSION_SIZE 1 | 
 | #define OBU_MAX_LENGTH_FIELD_SIZE 8 | 
 |  | 
 | #define OBU_MAX_HEADER_SIZE \ | 
 |   (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE + 2 * OBU_MAX_LENGTH_FIELD_SIZE) | 
 |  | 
 | #define OBU_DETECTION_SIZE \ | 
 |   (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE + 4 * OBU_MAX_LENGTH_FIELD_SIZE) | 
 |  | 
 | // Reads unsigned LEB128 integer and returns 0 upon successful read and decode. | 
 | // Stores raw bytes in 'value_buffer', length of the number in 'value_length', | 
 | // and decoded value in 'value'. | 
 | static int obudec_read_leb128(FILE *f, uint8_t *value_buffer, | 
 |                               size_t *value_length, uint64_t *value) { | 
 |   if (!f || !value_buffer || !value_length || !value) return -1; | 
 |   size_t len; | 
 |   for (len = 0; len < OBU_MAX_LENGTH_FIELD_SIZE; ++len) { | 
 |     const size_t num_read = fread(&value_buffer[len], 1, 1, f); | 
 |     if (num_read == 0) { | 
 |       if (len == 0 && feof(f)) { | 
 |         *value_length = 0; | 
 |         return 0; | 
 |       } | 
 |       // Ran out of data before completing read of value. | 
 |       return -1; | 
 |     } | 
 |     if ((value_buffer[len] >> 7) == 0) { | 
 |       ++len; | 
 |       *value_length = len; | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   return aom_uleb_decode(value_buffer, len, value, NULL); | 
 | } | 
 |  | 
 | // Reads OBU header from 'f'. The 'buffer_capacity' passed in must be large | 
 | // enough to store an OBU header with extension (2 bytes). Raw OBU data is | 
 | // written to 'obu_data', parsed OBU header values are written to 'obu_header', | 
 | // and total bytes read from file are written to 'bytes_read'. Returns 0 for | 
 | // success, and non-zero on failure. When end of file is reached, the return | 
 | // value is 0 and the 'bytes_read' value is set to 0. | 
 | static int obudec_read_obu_header(FILE *f, size_t buffer_capacity, | 
 |                                   int is_annexb, uint8_t *obu_data, | 
 |                                   ObuHeader *obu_header, size_t *bytes_read) { | 
 |   if (!f || buffer_capacity < (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE) || | 
 |       !obu_data || !obu_header || !bytes_read) { | 
 |     return -1; | 
 |   } | 
 |   *bytes_read = fread(obu_data, 1, 1, f); | 
 |  | 
 |   if (feof(f) && *bytes_read == 0) { | 
 |     return 0; | 
 |   } else if (*bytes_read != 1) { | 
 |     fprintf(stderr, "obudec: Failure reading OBU header.\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   const int has_extension = (obu_data[0] >> 2) & 0x1; | 
 |   if (has_extension) { | 
 |     if (fread(&obu_data[1], 1, 1, f) != 1) { | 
 |       fprintf(stderr, "obudec: Failure reading OBU extension."); | 
 |       return -1; | 
 |     } | 
 |     ++*bytes_read; | 
 |   } | 
 |  | 
 |   size_t obu_bytes_parsed = 0; | 
 |   const aom_codec_err_t parse_result = aom_read_obu_header( | 
 |       obu_data, *bytes_read, &obu_bytes_parsed, obu_header, is_annexb); | 
 |   if (parse_result != AOM_CODEC_OK || *bytes_read != obu_bytes_parsed) { | 
 |     fprintf(stderr, "obudec: Error parsing OBU header.\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | // Reads OBU payload from 'f' and returns 0 for success when all payload bytes | 
 | // are read from the file. Payload data is written to 'obu_data', and actual | 
 | // bytes read added to 'bytes_read'. | 
 | static int obudec_read_obu_payload(FILE *f, size_t payload_length, | 
 |                                    uint8_t *obu_data, size_t *bytes_read) { | 
 |   if (!f || payload_length == 0 || !obu_data || !bytes_read) return -1; | 
 |  | 
 |   if (fread(obu_data, 1, payload_length, f) != payload_length) { | 
 |     fprintf(stderr, "obudec: Failure reading OBU payload.\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   *bytes_read += payload_length; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int obudec_read_obu_header_and_size(FILE *f, size_t buffer_capacity, | 
 |                                            int is_annexb, uint8_t *buffer, | 
 |                                            size_t *bytes_read, | 
 |                                            size_t *payload_length, | 
 |                                            ObuHeader *obu_header) { | 
 |   const size_t kMinimumBufferSize = OBU_MAX_HEADER_SIZE; | 
 |   if (!f || !buffer || !bytes_read || !payload_length || !obu_header || | 
 |       buffer_capacity < kMinimumBufferSize) { | 
 |     return -1; | 
 |   } | 
 |  | 
 |   size_t leb128_length_obu = 0; | 
 |   size_t leb128_length_payload = 0; | 
 |   uint64_t obu_size = 0; | 
 |   if (is_annexb) { | 
 |     if (obudec_read_leb128(f, &buffer[0], &leb128_length_obu, &obu_size) != 0) { | 
 |       fprintf(stderr, "obudec: Failure reading OBU size length.\n"); | 
 |       return -1; | 
 |     } else if (leb128_length_obu == 0) { | 
 |       *payload_length = 0; | 
 |       return 0; | 
 |     } | 
 |     if (obu_size > UINT32_MAX) { | 
 |       fprintf(stderr, "obudec: OBU payload length too large.\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   size_t header_size = 0; | 
 |   if (obudec_read_obu_header(f, buffer_capacity - leb128_length_obu, is_annexb, | 
 |                              buffer + leb128_length_obu, obu_header, | 
 |                              &header_size) != 0) { | 
 |     return -1; | 
 |   } else if (header_size == 0) { | 
 |     *payload_length = 0; | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (!obu_header->has_size_field) { | 
 |     assert(is_annexb); | 
 |     if (obu_size < header_size) { | 
 |       fprintf(stderr, "obudec: OBU size is too small.\n"); | 
 |       return -1; | 
 |     } | 
 |     *payload_length = (size_t)obu_size - header_size; | 
 |   } else { | 
 |     uint64_t u64_payload_length = 0; | 
 |     if (obudec_read_leb128(f, &buffer[leb128_length_obu + header_size], | 
 |                            &leb128_length_payload, &u64_payload_length) != 0) { | 
 |       fprintf(stderr, "obudec: Failure reading OBU payload length.\n"); | 
 |       return -1; | 
 |     } | 
 |     if (u64_payload_length > UINT32_MAX) { | 
 |       fprintf(stderr, "obudec: OBU payload length too large.\n"); | 
 |       return -1; | 
 |     } | 
 |  | 
 |     *payload_length = (size_t)u64_payload_length; | 
 |   } | 
 |  | 
 |   *bytes_read = leb128_length_obu + header_size + leb128_length_payload; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int obudec_grow_buffer(size_t growth_amount, uint8_t **obu_buffer, | 
 |                               size_t *obu_buffer_capacity) { | 
 |   if (!*obu_buffer || !obu_buffer_capacity || growth_amount == 0) { | 
 |     return -1; | 
 |   } | 
 |  | 
 |   const size_t capacity = *obu_buffer_capacity; | 
 |   if (SIZE_MAX - growth_amount < capacity) { | 
 |     fprintf(stderr, "obudec: cannot grow buffer, capacity will roll over.\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   const size_t new_capacity = capacity + growth_amount; | 
 |  | 
 | #if defined AOM_MAX_ALLOCABLE_MEMORY | 
 |   if (new_capacity > AOM_MAX_ALLOCABLE_MEMORY) { | 
 |     fprintf(stderr, "obudec: OBU size exceeds max alloc size.\n"); | 
 |     return -1; | 
 |   } | 
 | #endif | 
 |  | 
 |   uint8_t *new_buffer = (uint8_t *)realloc(*obu_buffer, new_capacity); | 
 |   if (!new_buffer) { | 
 |     fprintf(stderr, "obudec: Failed to allocate compressed data buffer.\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   *obu_buffer = new_buffer; | 
 |   *obu_buffer_capacity = new_capacity; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int obudec_read_one_obu(FILE *f, uint8_t **obu_buffer, | 
 |                                size_t obu_bytes_buffered, | 
 |                                size_t *obu_buffer_capacity, size_t *obu_length, | 
 |                                ObuHeader *obu_header, int is_annexb) { | 
 |   if (!f || !(*obu_buffer) || !obu_buffer_capacity || !obu_length || | 
 |       !obu_header) { | 
 |     return -1; | 
 |   } | 
 |  | 
 |   size_t bytes_read = 0; | 
 |   size_t obu_payload_length = 0; | 
 |   size_t available_buffer_capacity = *obu_buffer_capacity - obu_bytes_buffered; | 
 |  | 
 |   if (available_buffer_capacity < OBU_MAX_HEADER_SIZE) { | 
 |     if (obudec_grow_buffer(AOMMAX(*obu_buffer_capacity, OBU_MAX_HEADER_SIZE), | 
 |                            obu_buffer, obu_buffer_capacity) != 0) { | 
 |       *obu_length = bytes_read; | 
 |       return -1; | 
 |     } | 
 |     available_buffer_capacity += | 
 |         AOMMAX(*obu_buffer_capacity, OBU_MAX_HEADER_SIZE); | 
 |   } | 
 |  | 
 |   const int status = obudec_read_obu_header_and_size( | 
 |       f, available_buffer_capacity, is_annexb, *obu_buffer + obu_bytes_buffered, | 
 |       &bytes_read, &obu_payload_length, obu_header); | 
 |   if (status < 0) return status; | 
 |  | 
 |   if (obu_payload_length > SIZE_MAX - bytes_read) return -1; | 
 |  | 
 |   if (obu_payload_length > 256 * 1024 * 1024) { | 
 |     fprintf(stderr, "obudec: Read invalid OBU size (%u)\n", | 
 |             (unsigned int)obu_payload_length); | 
 |     *obu_length = bytes_read + obu_payload_length; | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (bytes_read + obu_payload_length > available_buffer_capacity && | 
 |       obudec_grow_buffer(AOMMAX(*obu_buffer_capacity, obu_payload_length), | 
 |                          obu_buffer, obu_buffer_capacity) != 0) { | 
 |     *obu_length = bytes_read + obu_payload_length; | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (obu_payload_length > 0 && | 
 |       obudec_read_obu_payload(f, obu_payload_length, | 
 |                               *obu_buffer + obu_bytes_buffered + bytes_read, | 
 |                               &bytes_read) != 0) { | 
 |     return -1; | 
 |   } | 
 |  | 
 |   *obu_length = bytes_read; | 
 |   return 0; | 
 | } | 
 |  | 
 | int file_is_obu(struct ObuDecInputContext *obu_ctx) { | 
 |   if (!obu_ctx || !obu_ctx->avx_ctx) return 0; | 
 |  | 
 |   struct AvxInputContext *avx_ctx = obu_ctx->avx_ctx; | 
 |   uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 }; | 
 |   const int is_annexb = obu_ctx->is_annexb; | 
 | #if CONFIG_MULTIVIEW_CORE | 
 |   FILE *f = avx_ctx->file[0]; | 
 | #else | 
 |   FILE *f = avx_ctx->file; | 
 | #endif | 
 |   size_t payload_length = 0; | 
 |   ObuHeader obu_header; | 
 |   memset(&obu_header, 0, sizeof(obu_header)); | 
 |   size_t length_of_unit_size = 0; | 
 |   size_t annexb_header_length = 0; | 
 |   uint64_t unit_size = 0; | 
 |  | 
 |   if (is_annexb) { | 
 |     // read the size of first temporal unit | 
 |     if (obudec_read_leb128(f, &detect_buf[0], &length_of_unit_size, | 
 |                            &unit_size) != 0) { | 
 |       fprintf(stderr, "obudec: Failure reading temporal unit header\n"); | 
 |       return 0; | 
 |     } | 
 |  | 
 |     // read the size of first frame unit | 
 |     if (obudec_read_leb128(f, &detect_buf[length_of_unit_size], | 
 |                            &annexb_header_length, &unit_size) != 0) { | 
 |       fprintf(stderr, "obudec: Failure reading frame unit header\n"); | 
 |       return 0; | 
 |     } | 
 |     annexb_header_length += length_of_unit_size; | 
 |   } | 
 |  | 
 |   size_t bytes_read = 0; | 
 |   if (obudec_read_obu_header_and_size( | 
 |           f, OBU_DETECTION_SIZE - annexb_header_length, is_annexb, | 
 |           &detect_buf[annexb_header_length], &bytes_read, &payload_length, | 
 |           &obu_header) != 0) { | 
 |     fprintf(stderr, "obudec: Failure reading first OBU.\n"); | 
 |     rewind(f); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (is_annexb) { | 
 |     bytes_read += annexb_header_length; | 
 |   } | 
 |  | 
 |   if (obu_header.type != OBU_TEMPORAL_DELIMITER && | 
 |       obu_header.type != OBU_SEQUENCE_HEADER) { | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (obu_header.has_size_field) { | 
 |     if (obu_header.type == OBU_TEMPORAL_DELIMITER && payload_length != 0) { | 
 |       fprintf( | 
 |           stderr, | 
 |           "obudec: Invalid OBU_TEMPORAL_DELIMITER payload length (non-zero)."); | 
 |       rewind(f); | 
 |       return 0; | 
 |     } | 
 |   } else if (!is_annexb) { | 
 |     fprintf(stderr, "obudec: OBU size fields required, cannot decode input.\n"); | 
 |     rewind(f); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   // Appears that input is valid Section 5 AV1 stream. | 
 |   obu_ctx->buffer = (uint8_t *)malloc(OBU_BUFFER_SIZE); | 
 |   if (!obu_ctx->buffer) { | 
 |     fprintf(stderr, "Out of memory.\n"); | 
 |     rewind(f); | 
 |     return 0; | 
 |   } | 
 |   obu_ctx->buffer_capacity = OBU_BUFFER_SIZE; | 
 |  | 
 |   memcpy(obu_ctx->buffer, &detect_buf[0], bytes_read); | 
 |   obu_ctx->bytes_buffered = bytes_read; | 
 |   // If the first OBU is a SEQUENCE_HEADER, then it will have a payload. | 
 |   // We need to read this in so that our buffer only contains complete OBUs. | 
 |   if (payload_length > 0) { | 
 |     if (payload_length > (obu_ctx->buffer_capacity - bytes_read)) { | 
 |       fprintf(stderr, "obudec: First OBU's payload is too large\n"); | 
 |       rewind(f); | 
 |       return 0; | 
 |     } | 
 |  | 
 |     size_t payload_bytes = 0; | 
 |     const int status = obudec_read_obu_payload( | 
 |         f, payload_length, &obu_ctx->buffer[bytes_read], &payload_bytes); | 
 |     if (status < 0) { | 
 |       rewind(f); | 
 |       return 0; | 
 |     } | 
 |     obu_ctx->bytes_buffered += payload_bytes; | 
 |   } | 
 |   return 1; | 
 | } | 
 |  | 
 | int obudec_read_temporal_unit(struct ObuDecInputContext *obu_ctx, | 
 |                               uint8_t **buffer, size_t *bytes_read, | 
 |                               size_t *buffer_size) { | 
 | #if CONFIG_MULTIVIEW_CORE | 
 |   FILE *f = obu_ctx->avx_ctx->file[0]; | 
 | #else | 
 |   FILE *f = obu_ctx->avx_ctx->file; | 
 | #endif | 
 |   if (!f) return -1; | 
 |  | 
 |   *buffer_size = 0; | 
 |   *bytes_read = 0; | 
 |  | 
 |   if (feof(f)) { | 
 |     return 1; | 
 |   } | 
 |  | 
 |   size_t tu_size; | 
 |   size_t obu_size = 0; | 
 |   size_t length_of_temporal_unit_size = 0; | 
 |   uint8_t tuheader[OBU_MAX_LENGTH_FIELD_SIZE] = { 0 }; | 
 |  | 
 |   if (obu_ctx->is_annexb) { | 
 |     uint64_t size = 0; | 
 |  | 
 |     if (obu_ctx->bytes_buffered == 0) { | 
 |       if (obudec_read_leb128(f, &tuheader[0], &length_of_temporal_unit_size, | 
 |                              &size) != 0) { | 
 |         fprintf(stderr, "obudec: Failure reading temporal unit header\n"); | 
 |         return -1; | 
 |       } | 
 |       if (size == 0 && feof(f)) { | 
 |         return 1; | 
 |       } | 
 |     } else { | 
 |       // temporal unit size was already stored in buffer | 
 |       if (aom_uleb_decode(obu_ctx->buffer, obu_ctx->bytes_buffered, &size, | 
 |                           &length_of_temporal_unit_size) != 0) { | 
 |         fprintf(stderr, "obudec: Failure reading temporal unit header\n"); | 
 |         return -1; | 
 |       } | 
 |     } | 
 |  | 
 |     if (size > UINT32_MAX || size + length_of_temporal_unit_size > UINT32_MAX) { | 
 |       fprintf(stderr, "obudec: TU too large.\n"); | 
 |       return -1; | 
 |     } | 
 |  | 
 |     size += length_of_temporal_unit_size; | 
 |     tu_size = (size_t)size; | 
 |   } else { | 
 |     while (1) { | 
 |       ObuHeader obu_header; | 
 |       memset(&obu_header, 0, sizeof(obu_header)); | 
 |  | 
 |       if (obudec_read_one_obu(f, &obu_ctx->buffer, obu_ctx->bytes_buffered, | 
 |                               &obu_ctx->buffer_capacity, &obu_size, &obu_header, | 
 |                               0) != 0) { | 
 |         fprintf(stderr, "obudec: read_one_obu failed in TU loop\n"); | 
 |         return -1; | 
 |       } | 
 |  | 
 |       if (obu_header.type == OBU_TEMPORAL_DELIMITER || obu_size == 0) { | 
 |         tu_size = obu_ctx->bytes_buffered; | 
 |         break; | 
 |       } else { | 
 |         obu_ctx->bytes_buffered += obu_size; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 | #if defined AOM_MAX_ALLOCABLE_MEMORY | 
 |   if (tu_size > AOM_MAX_ALLOCABLE_MEMORY) { | 
 |     fprintf(stderr, "obudec: Temporal Unit size exceeds max alloc size.\n"); | 
 |     return -1; | 
 |   } | 
 | #endif | 
 |   if (tu_size > 0) { | 
 |     uint8_t *new_buffer = (uint8_t *)realloc(*buffer, tu_size); | 
 |     if (!new_buffer) { | 
 |       free(*buffer); | 
 |       fprintf(stderr, "obudec: Out of memory.\n"); | 
 |       return -1; | 
 |     } | 
 |     *buffer = new_buffer; | 
 |   } | 
 |   *bytes_read = tu_size; | 
 |   *buffer_size = tu_size; | 
 |  | 
 |   if (!obu_ctx->is_annexb) { | 
 |     memcpy(*buffer, obu_ctx->buffer, tu_size); | 
 |  | 
 |     // At this point, (obu_ctx->buffer + obu_ctx->bytes_buffered + obu_size) | 
 |     // points to the end of the buffer. | 
 |     memmove(obu_ctx->buffer, obu_ctx->buffer + obu_ctx->bytes_buffered, | 
 |             obu_size); | 
 |     obu_ctx->bytes_buffered = obu_size; | 
 |   } else { | 
 |     if (!feof(f)) { | 
 |       size_t data_size; | 
 |       size_t offset; | 
 |       if (!obu_ctx->bytes_buffered) { | 
 |         data_size = tu_size - length_of_temporal_unit_size; | 
 |         memcpy(*buffer, &tuheader[0], length_of_temporal_unit_size); | 
 |         offset = length_of_temporal_unit_size; | 
 |       } else { | 
 |         const size_t copy_size = AOMMIN(obu_ctx->bytes_buffered, tu_size); | 
 |         memcpy(*buffer, obu_ctx->buffer, copy_size); | 
 |         offset = copy_size; | 
 |         data_size = tu_size - copy_size; | 
 |         obu_ctx->bytes_buffered -= copy_size; | 
 |       } | 
 |  | 
 |       if (fread(*buffer + offset, 1, data_size, f) != data_size) { | 
 |         fprintf(stderr, "obudec: Failed to read full temporal unit\n"); | 
 |         return -1; | 
 |       } | 
 |     } | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | void obudec_free(struct ObuDecInputContext *obu_ctx) { free(obu_ctx->buffer); } |