Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017, Alliance for Open Media. All rights reserved |
| 3 | * |
| 4 | * This source code is subject to the terms of the BSD 2 Clause License and |
| 5 | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| 6 | * was not distributed with this source code in the LICENSE file, you can |
| 7 | * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| 8 | * Media Patent License 1.0 was not distributed with this source code in the |
| 9 | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| 10 | */ |
| 11 | |
| 12 | #include <cstdio> |
| 13 | #include <string> |
| 14 | |
| 15 | // TODO(tomfinegan): Remove the aom_config.h include as soon as possible. At |
Tom Finegan | ff86395 | 2017-12-22 11:41:14 -0800 | [diff] [blame] | 16 | // present it's required because aom_config.h determines if the library writes |
| 17 | // OBUs. |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 18 | #include "./aom_config.h" |
| 19 | |
Tom Finegan | 8fe8d51 | 2018-03-14 10:38:49 -0700 | [diff] [blame] | 20 | #include "aom/aom_codec.h" |
Tom Finegan | 41150ad | 2018-01-23 11:42:55 -0800 | [diff] [blame] | 21 | #include "aom/aom_integer.h" |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 22 | #include "aom_ports/mem_ops.h" |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 23 | #include "av1/decoder/obu.h" |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 24 | #include "tools/obu_parser.h" |
| 25 | |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 26 | namespace aom_tools { |
| 27 | |
| 28 | // Basic OBU syntax |
| 29 | // 4 bytes: length |
| 30 | // 8 bits: Header |
| 31 | // 7 |
| 32 | // forbidden bit |
| 33 | // 6,5,4,3 |
| 34 | // type bits |
| 35 | // 2,1 |
| 36 | // reserved bits |
| 37 | // 0 |
| 38 | // extension bit |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 39 | const uint32_t kObuForbiddenBitMask = 0x1; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 40 | const uint32_t kObuForbiddenBitShift = 7; |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 41 | const uint32_t kObuTypeBitsMask = 0xF; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 42 | const uint32_t kObuTypeBitsShift = 3; |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 43 | const uint32_t kObuExtensionFlagBitMask = 0x1; |
| 44 | const uint32_t kObuExtensionFlagBitShift = 2; |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 45 | const uint32_t kObuHasPayloadLengthFlagBitMask = 0x1; |
| 46 | const uint32_t kObuHasPayloadLengthFlagBitShift = 1; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 47 | |
| 48 | // When extension bit is set: |
| 49 | // 8 bits: extension header |
| 50 | // 7,6,5 |
| 51 | // temporal ID |
| 52 | // 4,3 |
| 53 | // spatial ID |
| 54 | // 2,1 |
| 55 | // quality ID |
| 56 | // 0 |
| 57 | // reserved bit |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 58 | const uint32_t kObuExtTemporalIdBitsMask = 0x7; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 59 | const uint32_t kObuExtTemporalIdBitsShift = 5; |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 60 | const uint32_t kObuExtSpatialIdBitsMask = 0x3; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 61 | const uint32_t kObuExtSpatialIdBitsShift = 3; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 62 | |
| 63 | bool ValidObuType(int obu_type) { |
| 64 | switch (obu_type) { |
| 65 | case OBU_SEQUENCE_HEADER: |
| 66 | case OBU_TEMPORAL_DELIMITER: |
| 67 | case OBU_FRAME_HEADER: |
| 68 | case OBU_TILE_GROUP: |
Tom Finegan | 8fe8d51 | 2018-03-14 10:38:49 -0700 | [diff] [blame] | 69 | case OBU_REDUNDANT_FRAME_HEADER: |
Vignesh Venkatasubramanian | 3871a9e | 2018-03-13 15:16:53 -0700 | [diff] [blame] | 70 | case OBU_FRAME: |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 71 | case OBU_METADATA: |
| 72 | case OBU_PADDING: return true; |
| 73 | } |
| 74 | return false; |
| 75 | } |
| 76 | |
| 77 | bool ParseObuHeader(uint8_t obu_header_byte, ObuHeader *obu_header) { |
| 78 | const int forbidden_bit = |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 79 | (obu_header_byte >> kObuForbiddenBitShift) & kObuForbiddenBitMask; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 80 | if (forbidden_bit) { |
| 81 | fprintf(stderr, "Invalid OBU, forbidden bit set.\n"); |
| 82 | return false; |
| 83 | } |
| 84 | |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 85 | obu_header->type = static_cast<OBU_TYPE>( |
| 86 | (obu_header_byte >> kObuTypeBitsShift) & kObuTypeBitsMask); |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 87 | if (!ValidObuType(obu_header->type)) { |
| 88 | fprintf(stderr, "Invalid OBU type: %d.\n", obu_header->type); |
| 89 | return false; |
| 90 | } |
| 91 | |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 92 | obu_header->has_extension = |
| 93 | (obu_header_byte >> kObuExtensionFlagBitShift) & kObuExtensionFlagBitMask; |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 94 | obu_header->has_length_field = |
| 95 | (obu_header_byte >> kObuHasPayloadLengthFlagBitShift) & |
| 96 | kObuHasPayloadLengthFlagBitMask; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 97 | return true; |
| 98 | } |
| 99 | |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 100 | bool ParseObuExtensionHeader(uint8_t ext_header_byte, ObuHeader *obu_header) { |
| 101 | obu_header->temporal_layer_id = |
| 102 | (ext_header_byte >> kObuExtTemporalIdBitsShift) & |
| 103 | kObuExtTemporalIdBitsMask; |
| 104 | obu_header->enhancement_layer_id = |
Tom Finegan | 2be4e4d | 2017-12-04 10:22:43 -0800 | [diff] [blame] | 105 | (ext_header_byte >> kObuExtSpatialIdBitsShift) & kObuExtSpatialIdBitsMask; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 106 | |
| 107 | return true; |
| 108 | } |
| 109 | |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 110 | void PrintObuHeader(const ObuHeader *header) { |
| 111 | printf( |
| 112 | " OBU type: %s\n" |
| 113 | " extension: %s\n", |
Tom Finegan | 8fe8d51 | 2018-03-14 10:38:49 -0700 | [diff] [blame] | 114 | aom_obu_type_to_string(static_cast<OBU_TYPE>(header->type)), |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 115 | header->has_extension ? "yes" : "no"); |
| 116 | if (header->has_extension) { |
| 117 | printf( |
| 118 | " temporal_id: %d\n" |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 119 | " spatial_id: %d\n", |
| 120 | header->temporal_layer_id, header->temporal_layer_id); |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 121 | } |
| 122 | } |
| 123 | |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 124 | bool DumpObu(const uint8_t *data, int length, int *obu_overhead_bytes) { |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 125 | const int kObuHeaderLengthSizeBytes = 1; |
Tom Finegan | f2d40f6 | 2018-02-01 11:52:49 -0800 | [diff] [blame] | 126 | const int kMinimumBytesRequired = 1 + kObuHeaderLengthSizeBytes; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 127 | int consumed = 0; |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 128 | int obu_overhead = 0; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 129 | ObuHeader obu_header; |
| 130 | while (consumed < length) { |
| 131 | const int remaining = length - consumed; |
| 132 | if (remaining < kMinimumBytesRequired) { |
| 133 | if (remaining > 0) { |
| 134 | fprintf(stderr, |
| 135 | "OBU parse error. Did not consume all data, %d bytes " |
| 136 | "remain.\n", |
| 137 | remaining); |
| 138 | } |
| 139 | return false; |
| 140 | } |
| 141 | |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 142 | size_t length_field_size = 0; |
| 143 | int current_obu_length = 0; |
| 144 | int obu_header_size = 0; |
Tom Finegan | 41150ad | 2018-01-23 11:42:55 -0800 | [diff] [blame] | 145 | |
Yaowu Xu | 68dc87e | 2018-02-28 14:41:50 -0800 | [diff] [blame] | 146 | obu_overhead += (int)length_field_size; |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 147 | |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 148 | if (current_obu_length > remaining) { |
| 149 | fprintf(stderr, |
| 150 | "OBU parsing failed at offset %d with bad length of %d " |
| 151 | "and %d bytes left.\n", |
| 152 | consumed, current_obu_length, remaining); |
| 153 | return false; |
| 154 | } |
Yaowu Xu | 68dc87e | 2018-02-28 14:41:50 -0800 | [diff] [blame] | 155 | consumed += (int)length_field_size; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 156 | |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 157 | memset(&obu_header, 0, sizeof(obu_header)); |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 158 | const uint8_t obu_header_byte = *(data + consumed); |
| 159 | if (!ParseObuHeader(obu_header_byte, &obu_header)) { |
| 160 | fprintf(stderr, "OBU parsing failed at offset %d.\n", consumed); |
| 161 | return false; |
| 162 | } |
| 163 | |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 164 | ++obu_overhead; |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 165 | ++obu_header_size; |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 166 | |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 167 | if (obu_header.has_extension) { |
| 168 | const uint8_t obu_ext_header_byte = |
| 169 | *(data + consumed + kObuHeaderLengthSizeBytes); |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 170 | if (!ParseObuExtensionHeader(obu_ext_header_byte, &obu_header)) { |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 171 | fprintf(stderr, "OBU extension parsing failed at offset %d.\n", |
| 172 | consumed); |
| 173 | return false; |
| 174 | } |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 175 | |
| 176 | ++obu_overhead; |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 177 | ++obu_header_size; |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | PrintObuHeader(&obu_header); |
| 181 | |
Vignesh Venkatasubramanian | 01ec41d | 2018-03-13 15:29:23 -0700 | [diff] [blame] | 182 | uint64_t obu_size = 0; |
| 183 | aom_uleb_decode(data + consumed + obu_header_size, remaining, &obu_size, |
| 184 | &length_field_size); |
| 185 | current_obu_length = static_cast<int>(obu_size); |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 186 | consumed += obu_header_size + length_field_size + current_obu_length; |
| 187 | printf(" length: %d\n", |
| 188 | static_cast<int>(obu_header_size + length_field_size + |
| 189 | current_obu_length)); |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 190 | } |
| 191 | |
Tom Finegan | f412906 | 2018-02-08 08:32:42 -0800 | [diff] [blame] | 192 | if (obu_overhead_bytes != nullptr) *obu_overhead_bytes = obu_overhead; |
Tom Finegan | 04450b1 | 2018-03-19 18:12:28 -0700 | [diff] [blame] | 193 | printf(" TU size: %d\n", consumed); |
Tom Finegan | a6f987b | 2018-02-07 10:02:53 -0800 | [diff] [blame] | 194 | |
Tom Finegan | c019233 | 2017-12-02 09:31:45 -0800 | [diff] [blame] | 195 | return true; |
| 196 | } |
| 197 | |
| 198 | } // namespace aom_tools |