blob: fb904aec7a2183c3b2260862b7d871c78cee7721 [file] [log] [blame]
/*
* 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 kObuForbiddenBitMask = 0x1;
const uint32_t kObuForbiddenBitShift = 7;
const uint32_t kObuTypeBitsMask = 0xF;
const uint32_t kObuTypeBitsShift = 3;
const uint32_t kObuReservedBitsMask = 0x3;
const uint32_t kObuReservedBitsShift = 1;
const uint32_t kObuExtensionFlagBitMask = 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 kObuExtTemporalIdBitsMask = 0x7;
const uint32_t kObuExtTemporalIdBitsShift = 5;
const uint32_t kObuExtSpatialIdBitsMask = 0x3;
const uint32_t kObuExtSpatialIdBitsShift = 3;
const uint32_t kObuExtQualityIdBitsMask = 0x3;
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 >> kObuForbiddenBitShift) & kObuForbiddenBitMask;
if (forbidden_bit) {
fprintf(stderr, "Invalid OBU, forbidden bit set.\n");
return false;
}
obu_header->type = (obu_header_byte >> kObuTypeBitsShift) & kObuTypeBitsMask;
if (!ValidObuType(obu_header->type)) {
fprintf(stderr, "Invalid OBU type: %d.\n", obu_header->type);
return false;
}
obu_header->reserved =
(obu_header_byte >> kObuReservedBitsShift) & kObuReservedBitsMask;
if (obu_header->reserved != 0) {
fprintf(stderr, "Invalid OBU: reserved bit(s) set.\n");
return false;
}
obu_header->has_extension = obu_header_byte & kObuExtensionFlagBitMask;
return true;
}
bool ParseObuExtensionHeader(uint8_t ext_header_byte,
ObuExtensionHeader *ext_header) {
ext_header->temporal_id = (ext_header_byte >> kObuExtTemporalIdBitsShift) &
kObuExtTemporalIdBitsMask;
ext_header->spatial_id =
(ext_header_byte >> kObuExtSpatialIdBitsShift) & kObuExtSpatialIdBitsMask;
ext_header->quality_id =
(ext_header_byte >> kObuExtQualityIdBitsShift) & kObuExtQualityIdBitsMask;
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