blob: 8f4d3a0cf12162ce5c706ca8f704e0dac5644bb0 [file] [log] [blame] [edit]
/*
* 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)
/*!\brief Maximum OBU header size in bytes. */
#define OBU_HEADER_SIZE 2
#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);
}
static int read_nbyte_from_file(FILE *f, size_t obu_header_size,
uint8_t *buffer, ObuHeader *obu_header) {
if (!f) {
return -2;
}
size_t bytes_read = fread(buffer, sizeof(uint8_t), obu_header_size, f);
if (feof(f) && bytes_read == 0) {
return -1;
} else if (bytes_read != obu_header_size) {
fprintf(stderr, "obudec: Failure reading OBU header.\n");
return -2;
}
obu_header->obu_extension_flag = (buffer[0] >> 7) & 1; // obu_extension_flag
obu_header->type = (buffer[0] >> 2) & 31; // obu_type
obu_header->obu_tlayer_id = (buffer[0]) & 3; // obu_temporal
if (obu_header->obu_extension_flag) {
obu_header->obu_mlayer_id = (buffer[1] >> 5) & 7; // obu_layer (mlayer)
obu_header->obu_xlayer_id = (buffer[1]) & 31; // obu_layer (xlayer)
} else {
obu_header->obu_mlayer_id = 0; // obu_layer (mlayer)
obu_header->obu_xlayer_id = 0; // obu_layer (xlayer)
}
return 0;
}
static int peek_obu_from_file(FILE *f, size_t obu_header_size, uint8_t *buffer,
ObuHeader *obu_header) {
if (!f) {
return -2;
}
unsigned long fpos = ftell(f);
const int status =
read_nbyte_from_file(f, obu_header_size, buffer, obu_header);
if (status == -2) {
fprintf(stderr, "obudec: Failure peeking OBU header.\n");
return -2;
} else if (status == -1) {
// bytes_read is already 0
return -1;
}
fseek(f, fpos, SEEK_SET);
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;
FILE *f = avx_ctx->file;
if (!f) {
return 0;
}
while (1) {
{
size_t obu_payload_size_bytelength = 0;
uint64_t obu_payload_size = 0;
if (is_annexb) {
if (obudec_read_leb128(f, &detect_buf[0], &obu_payload_size_bytelength,
&obu_payload_size) != 0) {
fprintf(stderr, "file_type: Failure reading obu size\n");
rewind(f);
return 0;
} else if (feof(f)) {
break;
}
obu_payload_size -= OBU_HEADER_SIZE;
} else {
fprintf(stderr, "file_type: OBU size is required\n");
rewind(f);
return 0;
}
ObuHeader obu_header;
memset(&obu_header, 0, sizeof(obu_header));
const int read_status =
read_nbyte_from_file(f, OBU_HEADER_SIZE, &detect_buf[0], &obu_header);
if (read_status == -2) {
fprintf(stderr, "file_type: Failure reading an OBU.\n");
rewind(f);
return 0;
} else if (read_status == -1) { // end of file
break;
}
fseek(f, obu_payload_size, SEEK_CUR);
}
} // while
// move the file pointer back to the beginning
rewind(f);
// fseek(f, 0, SEEK_SET);
return 1;
}
int obudec_read_temporal_unit(struct ObuDecInputContext *obu_ctx,
uint8_t **buffer, size_t *bytes_read,
size_t *buffer_size) {
FILE *f = obu_ctx->avx_ctx->file;
if (!f) return -1;
*buffer_size = 0;
*bytes_read = 0;
if (feof(f)) {
return 1;
}
size_t tu_size = 0;
unsigned long fpos = ftell(f);
uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 };
int first_td = 1;
while (1) {
ObuHeader obu_header;
memset(&obu_header, 0, sizeof(obu_header));
uint64_t obu_size = 0;
size_t obu_size_bytelength = 0;
if (obu_ctx->is_annexb) {
if (obudec_read_leb128(f, &detect_buf[0], &obu_size_bytelength,
&obu_size) != 0) {
fprintf(stderr, "obudec: Failure reading frame unit header\n");
} else if (feof(f)) {
if (tu_size == 0)
return 1;
else
break;
}
}
const int read_status =
peek_obu_from_file(f, OBU_HEADER_SIZE, &detect_buf[0], &obu_header);
if (read_status == -2) {
return -1;
} else if (read_status == -1) {
// end of file
return 1;
}
if ((obu_header.type == OBU_TEMPORAL_DELIMITER && first_td != 1)) {
break;
} else {
if (obu_header.type == OBU_TEMPORAL_DELIMITER) first_td = 0;
fseek(f, obu_size, SEEK_CUR);
tu_size += (obu_size + obu_size_bytelength);
}
} // while
fseek(f, fpos, SEEK_SET);
#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 (!feof(f)) {
// save from frame unit size
if (fread(*buffer, sizeof(uint8_t), tu_size, f) != tu_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); }