|  | /* | 
|  | * 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 <string.h> | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <string> | 
|  |  | 
|  | #include "aom/aom_codec.h" | 
|  | #include "aom/aom_integer.h" | 
|  | #include "aom_ports/mem_ops.h" | 
|  | #include "av1/common/obu_util.h" | 
|  | #include "tools/obu_parser.h" | 
|  |  | 
|  | namespace aom_tools { | 
|  |  | 
|  | // Basic OBU syntax | 
|  | // 8 bits: Header | 
|  | //   7 | 
|  | //     forbidden bit | 
|  | //   6,5,4,3 | 
|  | //     type bits | 
|  | //   2 | 
|  | //     extension flag bit | 
|  | //   1 | 
|  | //     has size field bit | 
|  | //   0 | 
|  | //     reserved bit | 
|  | const uint32_t kObuForbiddenBitMask = 0x1; | 
|  | const uint32_t kObuForbiddenBitShift = 7; | 
|  | const uint32_t kObuTypeBitsMask = 0xF; | 
|  | const uint32_t kObuTypeBitsShift = 3; | 
|  | const uint32_t kObuExtensionFlagBitMask = 0x1; | 
|  | const uint32_t kObuExtensionFlagBitShift = 2; | 
|  | const uint32_t kObuHasSizeFieldBitMask = 0x1; | 
|  | const uint32_t kObuHasSizeFieldBitShift = 1; | 
|  |  | 
|  | // When extension flag bit is set: | 
|  | // 8 bits: extension header | 
|  | // 7,6,5 | 
|  | //   temporal ID | 
|  | // 4,3 | 
|  | //   spatial ID | 
|  | // 2,1,0 | 
|  | //   reserved bits | 
|  | const uint32_t kObuExtTemporalIdBitsMask = 0x7; | 
|  | const uint32_t kObuExtTemporalIdBitsShift = 5; | 
|  | const uint32_t kObuExtSpatialIdBitsMask = 0x3; | 
|  | const uint32_t kObuExtSpatialIdBitsShift = 3; | 
|  |  | 
|  | 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_FRAME: | 
|  | case OBU_REDUNDANT_FRAME_HEADER: | 
|  | case OBU_TILE_LIST: | 
|  | 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 = static_cast<OBU_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->has_extension = | 
|  | (obu_header_byte >> kObuExtensionFlagBitShift) & kObuExtensionFlagBitMask; | 
|  | obu_header->has_size_field = | 
|  | (obu_header_byte >> kObuHasSizeFieldBitShift) & kObuHasSizeFieldBitMask; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ParseObuExtensionHeader(uint8_t ext_header_byte, ObuHeader *obu_header) { | 
|  | obu_header->temporal_layer_id = | 
|  | (ext_header_byte >> kObuExtTemporalIdBitsShift) & | 
|  | kObuExtTemporalIdBitsMask; | 
|  | obu_header->spatial_layer_id = | 
|  | (ext_header_byte >> kObuExtSpatialIdBitsShift) & kObuExtSpatialIdBitsMask; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void PrintObuHeader(const ObuHeader *header) { | 
|  | printf( | 
|  | "  OBU type:        %s\n" | 
|  | "      extension:   %s\n", | 
|  | aom_obu_type_to_string(static_cast<OBU_TYPE>(header->type)), | 
|  | header->has_extension ? "yes" : "no"); | 
|  | if (header->has_extension) { | 
|  | printf( | 
|  | "      temporal_id: %d\n" | 
|  | "      spatial_id:  %d\n", | 
|  | header->temporal_layer_id, header->spatial_layer_id); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool DumpObu(const uint8_t *data, int length, int *obu_overhead_bytes) { | 
|  | const int kObuHeaderSizeBytes = 1; | 
|  | const int kMinimumBytesRequired = 1 + kObuHeaderSizeBytes; | 
|  | int consumed = 0; | 
|  | int obu_overhead = 0; | 
|  | ObuHeader obu_header; | 
|  | while (consumed < length) { | 
|  | const int remaining = length - consumed; | 
|  | if (remaining < kMinimumBytesRequired) { | 
|  | fprintf(stderr, | 
|  | "OBU parse error. Did not consume all data, %d bytes remain.\n", | 
|  | remaining); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int obu_header_size = 0; | 
|  |  | 
|  | memset(&obu_header, 0, sizeof(obu_header)); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | ++obu_overhead; | 
|  | ++obu_header_size; | 
|  |  | 
|  | if (obu_header.has_extension) { | 
|  | const uint8_t obu_ext_header_byte = | 
|  | *(data + consumed + kObuHeaderSizeBytes); | 
|  | if (!ParseObuExtensionHeader(obu_ext_header_byte, &obu_header)) { | 
|  | fprintf(stderr, "OBU extension parsing failed at offset %d.\n", | 
|  | consumed + kObuHeaderSizeBytes); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ++obu_overhead; | 
|  | ++obu_header_size; | 
|  | } | 
|  |  | 
|  | PrintObuHeader(&obu_header); | 
|  |  | 
|  | uint64_t obu_size = 0; | 
|  | size_t length_field_size = 0; | 
|  | if (aom_uleb_decode(data + consumed + obu_header_size, | 
|  | remaining - obu_header_size, &obu_size, | 
|  | &length_field_size) != 0) { | 
|  | fprintf(stderr, "OBU size parsing failed at offset %d.\n", | 
|  | consumed + obu_header_size); | 
|  | return false; | 
|  | } | 
|  | int current_obu_length = static_cast<int>(obu_size); | 
|  | if (obu_header_size + static_cast<int>(length_field_size) + | 
|  | current_obu_length > | 
|  | remaining) { | 
|  | fprintf(stderr, "OBU parsing failed: not enough OBU data.\n"); | 
|  | return false; | 
|  | } | 
|  | consumed += obu_header_size + static_cast<int>(length_field_size) + | 
|  | current_obu_length; | 
|  | printf("      length:      %d\n", | 
|  | static_cast<int>(obu_header_size + length_field_size + | 
|  | current_obu_length)); | 
|  | } | 
|  |  | 
|  | if (obu_overhead_bytes != nullptr) *obu_overhead_bytes = obu_overhead; | 
|  | printf("  TU size: %d\n", consumed); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace aom_tools |