aboutsummaryrefslogtreecommitdiff
path: root/src/obu_parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/obu_parser.cc')
-rw-r--r--src/obu_parser.cc141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/obu_parser.cc b/src/obu_parser.cc
index 9e9166a..d1815ed 100644
--- a/src/obu_parser.cc
+++ b/src/obu_parser.cc
@@ -21,6 +21,7 @@
#include <cstddef>
#include <cstdint>
#include <cstring>
+#include <memory>
#include "src/buffer_pool.h"
#include "src/decoder_impl.h"
@@ -2877,4 +2878,144 @@ StatusCode ObuParser::ParseOneFrame(RefCountedBufferPtr* const current_frame) {
return kStatusOk;
}
+// AV1CodecConfigurationBox specification:
+// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox.
+// static
+std::unique_ptr<uint8_t[]> ObuParser::GetAV1CodecConfigurationBox(
+ const uint8_t* data, size_t size, size_t* const av1c_size) {
+ if (data == nullptr || av1c_size == nullptr) return nullptr;
+
+ ObuSequenceHeader sequence_header;
+ size_t sequence_header_offset;
+ size_t sequence_header_size;
+ const StatusCode status =
+ ParseBasicStreamInfo(data, size, &sequence_header,
+ &sequence_header_offset, &sequence_header_size);
+ if (status != kStatusOk) {
+ *av1c_size = 0;
+ return nullptr;
+ }
+
+ *av1c_size = 4 + sequence_header_size;
+ std::unique_ptr<uint8_t[]> av1c_ptr(new (std::nothrow) uint8_t[*av1c_size]);
+ if (av1c_ptr == nullptr) {
+ *av1c_size = 0;
+ return nullptr;
+ }
+ uint8_t* av1c = av1c_ptr.get();
+ // unsigned int (1) marker = 1;
+ // unsigned int (7) version = 1;
+ av1c[0] = 0x81;
+
+ // unsigned int (3) seq_profile;
+ // unsigned int (5) seq_level_idx_0;
+ const uint8_t seq_level_idx_0 = ((sequence_header.level[0].major - 2) << 2) |
+ sequence_header.level[0].minor;
+ av1c[1] = (sequence_header.profile << 5) | seq_level_idx_0;
+
+ // unsigned int (1) seq_tier_0;
+ // unsigned int (1) high_bitdepth;
+ // unsigned int (1) twelve_bit;
+ // unsigned int (1) monochrome;
+ // unsigned int (1) chroma_subsampling_x;
+ // unsigned int (1) chroma_subsampling_y;
+ // unsigned int (2) chroma_sample_position;
+ const auto high_bitdepth =
+ static_cast<uint8_t>(sequence_header.color_config.bitdepth > 8);
+ const auto twelve_bit =
+ static_cast<uint8_t>(sequence_header.color_config.bitdepth == 12);
+ av1c[2] =
+ (sequence_header.tier[0] << 7) | (high_bitdepth << 6) |
+ (twelve_bit << 5) |
+ (static_cast<uint8_t>(sequence_header.color_config.is_monochrome) << 4) |
+ (sequence_header.color_config.subsampling_x << 3) |
+ (sequence_header.color_config.subsampling_y << 2) |
+ sequence_header.color_config.chroma_sample_position;
+
+ // unsigned int (3) reserved = 0;
+ // unsigned int (1) initial_presentation_delay_present;
+ // if (initial_presentation_delay_present) {
+ // unsigned int (4) initial_presentation_delay_minus_one;
+ // } else {
+ // unsigned int (4) reserved = 0;
+ // }
+ av1c[3] = 0;
+
+ // unsigned int (8) configOBUs[];
+ memcpy(av1c + 4, data + sequence_header_offset, sequence_header_size);
+
+ return av1c_ptr;
+}
+
+// static
+StatusCode ObuParser::ParseBasicStreamInfo(const uint8_t* data, size_t size,
+ ObuSequenceHeader* sequence_header,
+ size_t* sequence_header_offset,
+ size_t* sequence_header_size) {
+ DecoderState state;
+ ObuParser parser(nullptr, 0, 0, nullptr, &state);
+ if (!parser.InitBitReader(data, size)) {
+ LIBGAV1_DLOG(ERROR, "Failed to initialize bit reader.");
+ return kStatusOutOfMemory;
+ }
+ while (!parser.bit_reader_->Finished()) {
+ const size_t obu_start_offset = parser.bit_reader_->byte_offset();
+ if (!parser.ParseHeader()) {
+ LIBGAV1_DLOG(ERROR, "Failed to parse OBU Header.");
+ return kStatusBitstreamError;
+ }
+ const ObuHeader& obu_header = parser.obu_headers_.back();
+ if (!obu_header.has_size_field) {
+ LIBGAV1_DLOG(
+ ERROR,
+ "has_size_field is zero. libgav1 does not support such streams.");
+ return kStatusUnimplemented;
+ }
+ size_t obu_size;
+ if (!parser.bit_reader_->ReadUnsignedLeb128(&obu_size)) {
+ LIBGAV1_DLOG(ERROR, "Could not read OBU size.");
+ return kStatusBitstreamError;
+ }
+ if (size - parser.bit_reader_->byte_offset() < obu_size) {
+ LIBGAV1_DLOG(ERROR, "Not enough bits left to parse OBU %zu vs %zu.",
+ size - parser.bit_reader_->bit_offset(), obu_size);
+ return kStatusBitstreamError;
+ }
+ if (obu_header.type != kObuSequenceHeader) {
+ parser.obu_headers_.pop_back();
+ parser.bit_reader_->SkipBytes(obu_size);
+ continue;
+ }
+ const size_t obu_start_position = parser.bit_reader_->bit_offset();
+ if (!parser.ParseSequenceHeader(false)) {
+ LIBGAV1_DLOG(ERROR, "Failed to parse SequenceHeader OBU.");
+ return kStatusBitstreamError;
+ }
+ const size_t parsed_obu_size_in_bits =
+ parser.bit_reader_->bit_offset() - obu_start_position;
+ const uint64_t obu_size_in_bits = static_cast<uint64_t>(obu_size) * 8;
+ if (obu_size_in_bits < parsed_obu_size_in_bits) {
+ LIBGAV1_DLOG(
+ ERROR,
+ "Parsed OBU size (%zu bits) is greater than expected OBU size "
+ "(%zu bytes)..",
+ parsed_obu_size_in_bits, obu_size);
+ return kStatusBitstreamError;
+ }
+ if (!parser.bit_reader_->VerifyAndSkipTrailingBits(
+ static_cast<size_t>(obu_size_in_bits - parsed_obu_size_in_bits))) {
+ LIBGAV1_DLOG(
+ ERROR, "Error when verifying trailing bits for the sequence header.");
+ return kStatusBitstreamError;
+ }
+ *sequence_header = parser.sequence_header_;
+ *sequence_header_offset = obu_start_offset;
+ *sequence_header_size =
+ parser.bit_reader_->byte_offset() - obu_start_offset;
+ return kStatusOk;
+ }
+ // Sequence header was never found.
+ return kStatusBitstreamError;
+}
+
} // namespace libgav1