Commit c0192337 authored by Tom Finegan's avatar Tom Finegan

Add dump_obu tool.

This is a work in progress. So far this tool supports only
reading of OBU headers with and without extensions. OBU
payload parsing will be added in future commits.

Change-Id: Ie4c184ad4ae8e536e00015e9bde6a7cde8dada28
parent 983adbd4
......@@ -425,10 +425,24 @@ if (ENABLE_EXAMPLES)
endif ()
if (ENABLE_TOOLS)
if (CONFIG_OBU AND CONFIG_ADD_4BYTES_OBUSIZE)
require_cxx_flag_nomsvc("-std=c++11" YES)
add_executable(dump_obu
"${AOM_CONFIG_DIR}/usage_exit.c"
"${AOM_ROOT}/tools/dump_obu.cc"
"${AOM_ROOT}/tools/obu_parser.cc"
"${AOM_ROOT}/tools/obu_parser.h"
$<TARGET_OBJECTS:aom_common_app_util>
$<TARGET_OBJECTS:aom_decoder_app_util>)
list(APPEND AOM_TOOL_TARGETS dump_obu)
list(APPEND AOM_APP_TARGETS dump_obu)
endif ()
# Maintain a separate variable listing only the examples to facilitate
# installation of example programs into an tools sub directory of
# $AOM_DIST_DIR/bin when building the dist target.
set(AOM_TOOL_TARGETS ${AOM_DECODER_TOOL_TARGETS} ${AOM_ENCODER_TOOL_TARGETS})
list(APPEND AOM_TOOL_TARGETS
${AOM_DECODER_TOOL_TARGETS} ${AOM_ENCODER_TOOL_TARGETS})
endif ()
if (ENABLE_EXAMPLES AND CONFIG_AV1_DECODER AND CONFIG_AV1_ENCODER)
......
/*
* 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 <stdlib.h>
#include <string.h>
#include <memory>
#include <string>
#include "./aom_config.h"
#include "./ivfdec.h"
#include "tools/obu_parser.h"
#include "./tools_common.h"
#include "./webmdec.h"
namespace {
const size_t kInitialBufferSize = 100 * 1024;
struct InputContext {
InputContext() = default;
~InputContext() { free(unit_buffer); }
void Init() {
memset(avx_ctx, 0, sizeof(*avx_ctx));
#if CONFIG_WEBM_IO
memset(webm_ctx, 0, sizeof(*webm_ctx));
#endif
}
AvxInputContext *avx_ctx = nullptr;
#if CONFIG_WEBM_IO
WebmInputContext *webm_ctx = nullptr;
#endif
uint8_t *unit_buffer = nullptr;
size_t unit_buffer_size = 0;
};
void PrintUsage() {
printf("Libaom OBU dump.\nUsage: dump_obu <input_file>\n");
}
VideoFileType GetFileType(InputContext *ctx) {
if (file_is_ivf(ctx->avx_ctx)) return FILE_TYPE_IVF;
#if CONFIG_WEBM_IO
if (file_is_webm(ctx->webm_ctx, ctx->avx_ctx)) return FILE_TYPE_WEBM;
#endif
return FILE_TYPE_RAW;
}
bool ReadTemporalUnit(InputContext *ctx, size_t *unit_size) {
const VideoFileType file_type = ctx->avx_ctx->file_type;
switch (file_type) {
case FILE_TYPE_IVF: {
if (ivf_read_frame(ctx->avx_ctx->file, &ctx->unit_buffer, unit_size,
&ctx->unit_buffer_size)) {
return false;
}
break;
}
#if CONFIG_WEBM_IO
case FILE_TYPE_WEBM: {
size_t frame_size = ctx->unit_buffer_size;
if (webm_read_frame(ctx->webm_ctx, &ctx->unit_buffer, &frame_size)) {
return false;
}
if (frame_size != ctx->unit_buffer_size &&
frame_size > ctx->unit_buffer_size) {
// webmdec realloc'd the buffer, store new size.
ctx->unit_buffer_size = frame_size;
}
*unit_size = frame_size;
break;
}
#endif
default:
// TODO(tomfinegan): Abuse FILE_TYPE_RAW for AV1/OBU elementary streams?
fprintf(stderr, "Error: Unsupported file type.\n");
return false;
}
return true;
}
} // namespace
int main(int argc, const char *argv[]) {
// TODO(tomfinegan): Could do with some params for verbosity.
if (argc < 2) {
PrintUsage();
return EXIT_SUCCESS;
}
const std::string filename = argv[1];
using FilePtr = std::unique_ptr<FILE, decltype(&fclose)>;
FilePtr input_file(fopen(filename.c_str(), "rb"), &fclose);
if (input_file.get() == nullptr) {
input_file.release();
fprintf(stderr, "Error: Cannot open input file.\n");
return EXIT_FAILURE;
}
AvxInputContext avx_ctx;
InputContext input_ctx;
input_ctx.avx_ctx = &avx_ctx;
#if CONFIG_WEBM_IO
WebmInputContext webm_ctx;
input_ctx.webm_ctx = &webm_ctx;
#endif
input_ctx.Init();
avx_ctx.file = input_file.get();
avx_ctx.file_type = GetFileType(&input_ctx);
// Note: the reader utilities will realloc the buffer using realloc() etc.
// Can't have nice things like unique_ptr wrappers with that type of
// behavior underneath the function calls.
input_ctx.unit_buffer =
reinterpret_cast<uint8_t *>(calloc(kInitialBufferSize, 1));
if (!input_ctx.unit_buffer) {
fprintf(stderr, "Error: No memory, can't alloc input buffer.\n");
return EXIT_FAILURE;
}
input_ctx.unit_buffer_size = kInitialBufferSize;
size_t unit_size = 0;
int unit_number = 0;
while (ReadTemporalUnit(&input_ctx, &unit_size)) {
printf("Temporal unit %d\n", unit_number);
if (!aom_tools::DumpObu(input_ctx.unit_buffer, unit_size)) {
fprintf(stderr, "Error: Temporal Unit parse failed on unit number %d.\n",
unit_number);
return EXIT_FAILURE;
}
++unit_number;
}
return EXIT_SUCCESS;
}
/*
* 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 <cstdio>
#include <string>
// TODO(tomfinegan): Remove the aom_config.h include as soon as possible. At
// present it's required because w/out aom_config.h it's impossible to know how
// Obus are being written out by the library (because
// CONFIG_ADD_4BYTES_OBUSIZE).
#include "./aom_config.h"
#if !CONFIG_ADD_4BYTES_OBUSIZE || !CONFIG_OBU
#error "obu_parser.cc requires CONFIG_ADD_4BYTES_OBUSIZE and CONFIG_OBU"
#endif
#include "aom_ports/mem_ops.h"
#include "tools/obu_parser.h"
namespace {
const aom_tools::ObuExtensionHeader kEmptyObuExt = { 0, 0, 0, false };
const aom_tools::ObuHeader kEmptyObu = { 0, 0, false, kEmptyObuExt };
} // namespace
namespace aom_tools {
// Basic OBU syntax
// 4 bytes: length
// 8 bits: Header
// 7
// forbidden bit
// 6,5,4,3
// type bits
// 2,1
// reserved bits
// 0
// extension bit
const uint32_t kObuForbiddenBit = 0x80;
const uint32_t kObuForbiddenBitShift = 7;
const uint32_t kObuTypeBits = 0x78;
const uint32_t kObuTypeBitsShift = 3;
const uint32_t kObuReservedBits = 0x6;
const uint32_t kObuReservedBitsShift = 1;
const uint32_t kObuExtensionFlagBit = 0x1;
// When extension bit is set:
// 8 bits: extension header
// 7,6,5
// temporal ID
// 4,3
// spatial ID
// 2,1
// quality ID
// 0
// reserved bit
const uint32_t kObuExtTemporalIdBits = 0xE0;
const uint32_t kObuExtTemporalIdBitsShift = 5;
const uint32_t kObuExtSpatialIdBits = 0x18;
const uint32_t kObuExtSpatialIdBitsShift = 3;
const uint32_t kObuExtQualityIdBits = 0x6;
const uint32_t kObuExtQualityIdBitsShift = 1;
const uint32_t kObuExtReservedFlagBit = 0x1;
bool ValidObuType(int obu_type) {
switch (obu_type) {
case OBU_SEQUENCE_HEADER:
case OBU_TEMPORAL_DELIMITER:
case OBU_FRAME_HEADER:
case OBU_TILE_GROUP:
case OBU_METADATA:
case OBU_PADDING: return true;
}
return false;
}
bool ParseObuHeader(uint8_t obu_header_byte, ObuHeader *obu_header) {
const int forbidden_bit =
(obu_header_byte & kObuForbiddenBit) >> kObuForbiddenBitShift;
if (forbidden_bit) {
fprintf(stderr, "Invalid OBU, forbidden bit set.\n");
return false;
}
obu_header->type = (obu_header_byte & kObuTypeBits) >> kObuTypeBitsShift;
if (!ValidObuType(obu_header->type)) {
fprintf(stderr, "Invalid OBU type: %d.\n", obu_header->type);
return false;
}
obu_header->reserved =
(obu_header_byte & kObuReservedBits) >> kObuReservedBitsShift;
if (obu_header->reserved != 0) {
fprintf(stderr, "Invalid OBU: reserved bit(s) set.\n");
return false;
}
obu_header->has_extension = obu_header_byte & kObuExtensionFlagBit;
return true;
}
bool ParseObuExtensionHeader(uint8_t ext_header_byte,
ObuExtensionHeader *ext_header) {
ext_header->temporal_id =
(ext_header_byte & kObuExtTemporalIdBits) >> kObuExtTemporalIdBitsShift;
ext_header->spatial_id =
(ext_header_byte & kObuExtSpatialIdBits) >> kObuExtSpatialIdBitsShift;
ext_header->quality_id =
(ext_header_byte & kObuExtQualityIdBits) >> kObuExtQualityIdBitsShift;
ext_header->reserved_flag = ext_header_byte & kObuExtReservedFlagBit;
if (ext_header->reserved_flag) {
fprintf(stderr, "Invalid OBU Extension: reserved flag set.\n");
return false;
}
return true;
}
std::string ObuTypeToString(int obu_type) {
switch (obu_type) {
case OBU_SEQUENCE_HEADER: return "OBU_SEQUENCE_HEADER";
case OBU_TEMPORAL_DELIMITER: return "OBU_TEMPORAL_DELIMITER";
case OBU_FRAME_HEADER: return "OBU_FRAME_HEADER";
case OBU_TILE_GROUP: return "OBU_TILE_GROUP";
case OBU_METADATA: return "OBU_METADATA";
case OBU_PADDING: return "OBU_PADDING";
}
return "";
}
void PrintObuHeader(const ObuHeader *header) {
printf(
" OBU type: %s\n"
" extension: %s\n",
ObuTypeToString(header->type).c_str(),
header->has_extension ? "yes" : "no");
if (header->has_extension) {
printf(
" temporal_id: %d\n"
" spatial_id: %d\n"
" quality_id: %d\n",
header->ext_header.temporal_id, header->ext_header.spatial_id,
header->ext_header.quality_id);
}
}
bool DumpObu(const uint8_t *data, int length) {
const int kObuLengthFieldSizeBytes = 4; // Assumes CONFIG_ADD_4BYTES_OBUSIZE.
const int kObuHeaderLengthSizeBytes = 1;
const int kMinimumBytesRequired =
kObuLengthFieldSizeBytes + kObuHeaderLengthSizeBytes;
int consumed = 0;
ObuHeader obu_header;
while (consumed < length) {
const int remaining = length - consumed;
if (remaining < kMinimumBytesRequired) {
if (remaining > 0) {
fprintf(stderr,
"OBU parse error. Did not consume all data, %d bytes "
"remain.\n",
remaining);
}
return false;
}
const int current_obu_length = mem_get_le32(data + consumed);
if (current_obu_length > remaining) {
fprintf(stderr,
"OBU parsing failed at offset %d with bad length of %d "
"and %d bytes left.\n",
consumed, current_obu_length, remaining);
return false;
}
consumed += kObuLengthFieldSizeBytes;
obu_header = kEmptyObu;
const uint8_t obu_header_byte = *(data + consumed);
if (!ParseObuHeader(obu_header_byte, &obu_header)) {
fprintf(stderr, "OBU parsing failed at offset %d.\n", consumed);
return false;
}
if (obu_header.has_extension) {
const uint8_t obu_ext_header_byte =
*(data + consumed + kObuHeaderLengthSizeBytes);
if (!ParseObuExtensionHeader(obu_ext_header_byte,
&obu_header.ext_header)) {
fprintf(stderr, "OBU extension parsing failed at offset %d.\n",
consumed);
return false;
}
}
PrintObuHeader(&obu_header);
// TODO(tomfinegan): Parse OBU payload. For now just consume it.
consumed += current_obu_length;
}
return true;
}
} // namespace aom_tools
/*
* 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.
*/
#ifndef TOOLS_OBU_PARSER_H_
#define TOOLS_OBU_PARSER_H_
#include <cstdint>
namespace aom_tools {
// TODO(tomfinegan): The OBU and OBU metadata types are currently defined in
// av1/common/enums.h, which is not part of the libaom's public API. They are
// duped here until that's dealt with.
enum ObuType {
OBU_SEQUENCE_HEADER = 1,
OBU_TEMPORAL_DELIMITER = 2,
OBU_FRAME_HEADER = 3,
OBU_TILE_GROUP = 4,
OBU_METADATA = 5,
OBU_PADDING = 15,
};
enum ObuMetadataType {
OBU_METADATA_TYPE_PRIVATE_DATA = 0,
OBU_METADATA_TYPE_HDR_CLL = 1,
OBU_METADATA_TYPE_HDR_MDCV = 2,
};
struct ObuExtensionHeader {
int temporal_id;
int spatial_id;
int quality_id;
bool reserved_flag;
};
struct ObuHeader {
int type;
int reserved;
bool has_extension;
ObuExtensionHeader ext_header;
};
// Print information obtained from OBU(s) in data until data is exhausted or an
// error occurs. Returns true when all data is consumed successfully.
bool DumpObu(const uint8_t *data, int length);
} // namespace aom_tools
#endif // TOOLS_OBU_PARSER_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment