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 #include <string.h>
12 
13 #include <cstdio>
14 #include <string>
15 
16 #include "aom/aom_codec.h"
17 #include "aom/aom_integer.h"
18 #include "aom_ports/mem_ops.h"
19 #include "av1/common/obu_util.h"
20 #include "tools/obu_parser.h"
21 
22 namespace aom_tools {
23 
24 // Basic OBU syntax
25 // 8 bits: Header
26 //   7
27 //     forbidden bit
28 //   6,5,4,3
29 //     type bits
30 //   2
31 //     extension flag bit
32 //   1
33 //     has size field bit
34 //   0
35 //     reserved bit
36 const uint32_t kObuForbiddenBitMask = 0x1;
37 const uint32_t kObuForbiddenBitShift = 7;
38 const uint32_t kObuTypeBitsMask = 0xF;
39 const uint32_t kObuTypeBitsShift = 3;
40 const uint32_t kObuExtensionFlagBitMask = 0x1;
41 const uint32_t kObuExtensionFlagBitShift = 2;
42 const uint32_t kObuHasSizeFieldBitMask = 0x1;
43 const uint32_t kObuHasSizeFieldBitShift = 1;
44 
45 // When extension flag bit is set:
46 // 8 bits: extension header
47 // 7,6,5
48 //   temporal ID
49 // 4,3
50 //   spatial ID
51 // 2,1,0
52 //   reserved bits
53 const uint32_t kObuExtTemporalIdBitsMask = 0x7;
54 const uint32_t kObuExtTemporalIdBitsShift = 5;
55 const uint32_t kObuExtSpatialIdBitsMask = 0x3;
56 const uint32_t kObuExtSpatialIdBitsShift = 3;
57 
ValidObuType(int obu_type)58 bool ValidObuType(int obu_type) {
59   switch (obu_type) {
60     case OBU_SEQUENCE_HEADER:
61     case OBU_TEMPORAL_DELIMITER:
62     case OBU_FRAME_HEADER:
63     case OBU_TILE_GROUP:
64     case OBU_METADATA:
65     case OBU_FRAME:
66     case OBU_REDUNDANT_FRAME_HEADER:
67     case OBU_TILE_LIST:
68     case OBU_PADDING: return true;
69   }
70   return false;
71 }
72 
ParseObuHeader(uint8_t obu_header_byte,ObuHeader * obu_header)73 bool ParseObuHeader(uint8_t obu_header_byte, ObuHeader *obu_header) {
74   const int forbidden_bit =
75       (obu_header_byte >> kObuForbiddenBitShift) & kObuForbiddenBitMask;
76   if (forbidden_bit) {
77     fprintf(stderr, "Invalid OBU, forbidden bit set.\n");
78     return false;
79   }
80 
81   obu_header->type = static_cast<OBU_TYPE>(
82       (obu_header_byte >> kObuTypeBitsShift) & kObuTypeBitsMask);
83   if (!ValidObuType(obu_header->type)) {
84     fprintf(stderr, "Invalid OBU type: %d.\n", obu_header->type);
85     return false;
86   }
87 
88   obu_header->has_extension =
89       (obu_header_byte >> kObuExtensionFlagBitShift) & kObuExtensionFlagBitMask;
90   obu_header->has_size_field =
91       (obu_header_byte >> kObuHasSizeFieldBitShift) & kObuHasSizeFieldBitMask;
92   return true;
93 }
94 
ParseObuExtensionHeader(uint8_t ext_header_byte,ObuHeader * obu_header)95 bool ParseObuExtensionHeader(uint8_t ext_header_byte, ObuHeader *obu_header) {
96   obu_header->temporal_layer_id =
97       (ext_header_byte >> kObuExtTemporalIdBitsShift) &
98       kObuExtTemporalIdBitsMask;
99   obu_header->spatial_layer_id =
100       (ext_header_byte >> kObuExtSpatialIdBitsShift) & kObuExtSpatialIdBitsMask;
101 
102   return true;
103 }
104 
PrintObuHeader(const ObuHeader * header)105 void PrintObuHeader(const ObuHeader *header) {
106   printf(
107       "  OBU type:        %s\n"
108       "      extension:   %s\n",
109       aom_obu_type_to_string(static_cast<OBU_TYPE>(header->type)),
110       header->has_extension ? "yes" : "no");
111   if (header->has_extension) {
112     printf(
113         "      temporal_id: %d\n"
114         "      spatial_id:  %d\n",
115         header->temporal_layer_id, header->temporal_layer_id);
116   }
117 }
118 
DumpObu(const uint8_t * data,int length,int * obu_overhead_bytes)119 bool DumpObu(const uint8_t *data, int length, int *obu_overhead_bytes) {
120   const int kObuHeaderSizeBytes = 1;
121   const int kMinimumBytesRequired = 1 + kObuHeaderSizeBytes;
122   int consumed = 0;
123   int obu_overhead = 0;
124   ObuHeader obu_header;
125   while (consumed < length) {
126     const int remaining = length - consumed;
127     if (remaining < kMinimumBytesRequired) {
128       fprintf(stderr,
129               "OBU parse error. Did not consume all data, %d bytes remain.\n",
130               remaining);
131       return false;
132     }
133 
134     int obu_header_size = 0;
135 
136     memset(&obu_header, 0, sizeof(obu_header));
137     const uint8_t obu_header_byte = *(data + consumed);
138     if (!ParseObuHeader(obu_header_byte, &obu_header)) {
139       fprintf(stderr, "OBU parsing failed at offset %d.\n", consumed);
140       return false;
141     }
142 
143     ++obu_overhead;
144     ++obu_header_size;
145 
146     if (obu_header.has_extension) {
147       const uint8_t obu_ext_header_byte =
148           *(data + consumed + kObuHeaderSizeBytes);
149       if (!ParseObuExtensionHeader(obu_ext_header_byte, &obu_header)) {
150         fprintf(stderr, "OBU extension parsing failed at offset %d.\n",
151                 consumed + kObuHeaderSizeBytes);
152         return false;
153       }
154 
155       ++obu_overhead;
156       ++obu_header_size;
157     }
158 
159     PrintObuHeader(&obu_header);
160 
161     uint64_t obu_size = 0;
162     size_t length_field_size = 0;
163     if (aom_uleb_decode(data + consumed + obu_header_size,
164                         remaining - obu_header_size, &obu_size,
165                         &length_field_size) != 0) {
166       fprintf(stderr, "OBU size parsing failed at offset %d.\n",
167               consumed + obu_header_size);
168       return false;
169     }
170     int current_obu_length = static_cast<int>(obu_size);
171     if (obu_header_size + static_cast<int>(length_field_size) +
172             current_obu_length >
173         remaining) {
174       fprintf(stderr, "OBU parsing failed: not enough OBU data.\n");
175       return false;
176     }
177     consumed += obu_header_size + static_cast<int>(length_field_size) +
178                 current_obu_length;
179     printf("      length:      %d\n",
180            static_cast<int>(obu_header_size + length_field_size +
181                             current_obu_length));
182   }
183 
184   if (obu_overhead_bytes != nullptr) *obu_overhead_bytes = obu_overhead;
185   printf("  TU size: %d\n", consumed);
186 
187   return true;
188 }
189 
190 }  // namespace aom_tools
191