commit ff01b8478e7977f651a3dc5ddc322d604196d965
parent 969a53ef2106b2a263385751000866802bd74e72
Author: Dan Baker <dbaker@mozilla.com>
Date: Mon, 1 Dec 2025 23:31:28 -0700
Bug 2000941 - Vendor libwebrtc from 2e0481aec0
Upstream commit: https://webrtc.googlesource.com/src/+/2e0481aec0846316525f3cfd06ac9636489d9174
Add AV1 QP parser.
This AV1 QP parser uses libgav1 to parse the QP. Hence, also including libgav1 in DEPS since it is currently not used in webrtc. This code is only for evaluation and therefore the dependecy will not affect the prod built size.
No-Presubmit: True
Bug: webrtc:358039777
Change-Id: Id4cbae61101ae3343f059c9404b4df4d9f784877
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/409542
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Emil Vardar (xWF) <vardar@google.com>
Cr-Commit-Position: refs/heads/main@{#45713}
Diffstat:
7 files changed, 286 insertions(+), 2 deletions(-)
diff --git a/third_party/libwebrtc/DEPS b/third_party/libwebrtc/DEPS
@@ -590,6 +590,8 @@ deps = {
'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@af5cf2b1e7f03d6f6de84477e1ca8eed1f3eb03d',
'src/third_party/libaom/source/libaom':
'https://aomedia.googlesource.com/aom.git@4727f83546cc150adeac9e2558375f3da44215df',
+ 'src/third_party/libgav1/src':
+ Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'c05bf9be660cf170d7c26bd06bb42b3322180e58',
'src/third_party/libunwindstack': {
'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@0928ad0d25e4af07c8be5ab06d0ca584f9f4ceb5',
'condition': 'checkout_android',
diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor
@@ -1,4 +1,4 @@
# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
-libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-12-02T06:28:26.322059+00:00.
+libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-12-02T06:31:13.191123+00:00.
# base of lastest vendoring
-6db6f177d3
+2e0481aec0
diff --git a/third_party/libwebrtc/video/corruption_detection/evaluation/BUILD.gn b/third_party/libwebrtc/video/corruption_detection/evaluation/BUILD.gn
@@ -8,6 +8,21 @@
import("../../../webrtc.gni")
+rtc_library("av1_qp_parser") {
+ testonly = true
+ sources = [
+ "av1_qp_parser.cc",
+ "av1_qp_parser.h",
+ ]
+ deps = [
+ "../../../api:array_view",
+ "../../../api/video:video_frame",
+ "../../../api/video:video_frame_type",
+ "//third_party/libgav1:libgav1_parser",
+ ]
+ public_configs = [ "//third_party/libgav1:libgav1_public_config" ]
+}
+
rtc_library("file_based_decoder") {
testonly = true
sources = [ "file_based_decoder.h" ]
@@ -116,6 +131,16 @@ rtc_library("webrtc_picture_pair_provider") {
}
if (rtc_include_tests) {
+ rtc_library("av1_qp_parser_unittest") {
+ testonly = true
+ sources = [ "av1_qp_parser_unittest.cc" ]
+ deps = [
+ ":av1_qp_parser",
+ "../../../api/video:video_frame",
+ "../../../test:test_support",
+ ]
+ }
+
rtc_library("test_clip_unittest") {
testonly = true
sources = [ "test_clip_unittest.cc" ]
@@ -186,6 +211,7 @@ if (rtc_include_tests) {
testonly = true
sources = []
deps = [
+ ":av1_qp_parser_unittest",
":test_clip_unittest",
":utils_unittest",
":webrtc_picture_pair_provider_unittest",
diff --git a/third_party/libwebrtc/video/corruption_detection/evaluation/DEPS b/third_party/libwebrtc/video/corruption_detection/evaluation/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/libgav1",
+]
+\ No newline at end of file
diff --git a/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser.cc b/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "video/corruption_detection/evaluation/av1_qp_parser.h"
+
+#include <cstdint>
+#include <optional>
+
+#include "api/array_view.h"
+#include "third_party/libgav1/src/src/buffer_pool.h"
+#include "third_party/libgav1/src/src/gav1/status_code.h"
+#include "third_party/libgav1/src/src/obu_parser.h"
+
+namespace webrtc {
+
+std::optional<uint32_t> Av1QpParser::Parse(ArrayView<const uint8_t> frame_data,
+ int operating_point) {
+ libgav1::RefCountedBufferPtr curr_frame;
+ libgav1::ObuParser parser(frame_data.data(), frame_data.size(),
+ operating_point, &buffer_pool_, &decoder_state_);
+ uint8_t highest_acceptable_spatial_layers_qp;
+
+ // Since the temporal unit can have more than 1 frame in scalable coding, we
+ // go through all the frame's.
+ while (parser.HasData()) {
+ // If the frame is not a keyframe, the `parser` must know the information
+ // from `sequence_header` to parse the OBU properly.
+ if (sequence_header_) {
+ parser.set_sequence_header(*sequence_header_);
+ }
+ if (parser.ParseOneFrame(&curr_frame) != libgav1::kStatusOk) {
+ return std::nullopt;
+ }
+
+ // Get QP from frame header.
+ const auto& frame_header = parser.frame_header();
+ // frame_header.quantizer.base_index returns 0 if based on `operating_point`
+ // we are not interested in higher spatial layer's QP values.
+ if (frame_header.quantizer.base_index != 0) {
+ highest_acceptable_spatial_layers_qp = frame_header.quantizer.base_index;
+ }
+
+ // Update the state for the next frame.
+ if (parser.sequence_header_changed()) {
+ sequence_header_ = parser.sequence_header();
+ }
+ decoder_state_.UpdateReferenceFrames(curr_frame,
+ frame_header.refresh_frame_flags);
+ }
+
+ return highest_acceptable_spatial_layers_qp;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser.h b/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2025 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef VIDEO_CORRUPTION_DETECTION_EVALUATION_AV1_QP_PARSER_H_
+#define VIDEO_CORRUPTION_DETECTION_EVALUATION_AV1_QP_PARSER_H_
+
+#include <cstdint>
+#include <optional>
+
+#include "api/array_view.h"
+#include "third_party/libgav1/src/src/buffer_pool.h"
+#include "third_party/libgav1/src/src/decoder_state.h"
+#include "third_party/libgav1/src/src/obu_parser.h"
+
+namespace webrtc {
+
+// In RTC we expect AV1 to be configured with `AOM_USAGE_REALTIME`, see
+// webrtc/modules/video_coding/codecs/av1/libaom_av1_encoder.cc. In this
+// mode AV1 is expected to only have one "temporal" frame per temporal unit.
+// Hence, in this implementation we do not take into consideration scenarios
+// such as having multiple frames in one temporal unit, as specified
+// in https://norkin.org/research/av1_decoder_model/index.html Fig 2.
+//
+// Although, in scalable encoding mode, AV1 can have several spatial layers in
+// one temporal unit. But these must be placed in one temporal unit as described
+// in AV1 documentation 7.5.
+//
+// To get the QP value for a specific spatial layer use:
+// `operating_point` = #total_num_spatial_layer - wanted_spatial_layer.
+// E.g. if the QP for the highest spatial layer is sought use `operating_point`
+// = 0.
+class Av1QpParser {
+ public:
+ Av1QpParser()
+ : buffer_pool_(/*on_frame_buffer_size_changed=*/nullptr,
+ /*get_frame_buffer=*/nullptr,
+ /*release_frame_buffer=*/nullptr,
+ /*callback_private_data=*/nullptr) {}
+
+ std::optional<uint32_t> Parse(ArrayView<const uint8_t> frame_data,
+ int operating_point = 0);
+
+ private:
+ libgav1::BufferPool buffer_pool_;
+ libgav1::DecoderState decoder_state_;
+ std::optional<libgav1::ObuSequenceHeader> sequence_header_ = std::nullopt;
+};
+
+} // namespace webrtc
+
+#endif // VIDEO_CORRUPTION_DETECTION_EVALUATION_AV1_QP_PARSER_H_
diff --git a/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser_unittest.cc b/third_party/libwebrtc/video/corruption_detection/evaluation/av1_qp_parser_unittest.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2025 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "video/corruption_detection/evaluation/av1_qp_parser.h"
+
+#include <cstdint>
+#include <optional>
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+#define OBU_TEMPORAL_DELIMITER 0x12, 0x0
+#define OBU_SEQUENCE_HEADER \
+ 0xa, 0xa, 0x0, 0x0, 0x0, 0x2, 0x27, 0xfe, 0xff, 0xfc, 0xc0, 0x20
+#define OBU_FRAME_1 \
+ 0x32, 0x93, 0x2, 0x10, 0x0, 0xa8, 0x80, 0x0, 0x3, 0x0, 0x10, 0x10, 0x30, \
+ 0x0, 0xd3, 0xc6, 0xc6, 0x82, 0xaa, 0x5e, 0xbf, 0x82, 0xf2, 0xa4, 0xa4, \
+ 0x29, 0xab, 0xda, 0xd7, 0x1, 0x5, 0x0, 0xb3, 0xde, 0xa8, 0x6f, 0x8d, \
+ 0xbf, 0x1b, 0xa8, 0x25, 0xc3, 0x84, 0x7c, 0x1a, 0x2b, 0x8b, 0x0, 0xff, \
+ 0x19, 0x1f, 0x45, 0x7e, 0xe0, 0xbe, 0xe1, 0x3a, 0x63, 0xc2, 0xc6, 0x6e, \
+ 0xf4, 0xc8, 0xce, 0x11, 0xe1, 0x9f, 0x48, 0x64, 0x72, 0xeb, 0xbb, 0x4f, \
+ 0xf3, 0x94, 0xb4, 0xb6, 0x9d, 0x4f, 0x4, 0x18, 0x5e, 0x5e, 0x1b, 0x65, \
+ 0x49, 0x74, 0x90, 0x13, 0x50, 0xef, 0x8c, 0xb8, 0xe8, 0xd9, 0x8e, 0x9c, \
+ 0xc9, 0x4d, 0xda, 0x60, 0x6a, 0xa, 0xf9, 0x75, 0xd0, 0x62, 0x69, 0xd, \
+ 0xf5, 0xdc, 0xa9, 0xb9, 0x4c, 0x8, 0x9e, 0x33, 0x15, 0xa3, 0xe1, 0x42, \
+ 0x0, 0xe2, 0xb0, 0x46, 0xd0, 0xf7, 0xad, 0x55, 0xbc, 0x75, 0xe9, 0xe3, \
+ 0x1f, 0xa3, 0x41, 0x11, 0xba, 0xaa, 0x81, 0xf3, 0xcb, 0x82, 0x87, 0x71, \
+ 0x0, 0xe6, 0xb9, 0x8c, 0xe1, 0xe9, 0xd3, 0x21, 0xcc, 0xcd, 0xe7, 0x12, \
+ 0xb9, 0xe, 0x43, 0x6a, 0xa3, 0x76, 0x5c, 0x35, 0x90, 0x45, 0x36, 0x52, \
+ 0xb4, 0x2d, 0xa3, 0x55, 0xde, 0x20, 0xf8, 0x80, 0xe1, 0x26, 0x46, 0x1b, \
+ 0x3f, 0x59, 0xc7, 0x2e, 0x5b, 0x4a, 0x73, 0xf8, 0xb3, 0xf4, 0x62, 0xf4, \
+ 0xf5, 0xa4, 0xc2, 0xae, 0x9e, 0xa6, 0x9c, 0x10, 0xbb, 0xe1, 0xd6, 0x88, \
+ 0x75, 0xb9, 0x85, 0x48, 0xe5, 0x7, 0x12, 0xf3, 0x11, 0x85, 0x8e, 0xa2, \
+ 0x95, 0x9d, 0xed, 0x50, 0xfb, 0x6, 0x5a, 0x1, 0x37, 0xc4, 0x8e, 0x9e, \
+ 0x73, 0x9b, 0x96, 0x64, 0xbd, 0x42, 0xb, 0x80, 0xde, 0x57, 0x86, 0xcb, \
+ 0x7d, 0xab, 0x12, 0xb2, 0xcc, 0xe6, 0xea, 0xb5, 0x89, 0xeb, 0x91, 0xb3, \
+ 0x93, 0xb2, 0x4f, 0x2f, 0x5b, 0xf3, 0x72, 0x12, 0x51, 0x56, 0x75, 0xb3, \
+ 0xdd, 0x49, 0xb6, 0x5b, 0x77, 0xbe, 0xc5, 0xd7, 0xd4, 0xaf, 0xd6, 0x6b, \
+ 0x38
+#define OBU_FRAME_2 \
+ 0x32, 0x33, 0x30, 0x3, 0xc3, 0x0, 0xa7, 0x2e, 0x46, 0xa8, 0x80, 0x0, 0x3, \
+ 0x0, 0x10, 0x1, 0x0, 0xa0, 0x0, 0xed, 0xb1, 0x51, 0x15, 0x58, 0xc7, \
+ 0x69, 0x3, 0x26, 0x35, 0xeb, 0x5a, 0x2d, 0x7a, 0x53, 0x24, 0x26, 0x20, \
+ 0xa6, 0x11, 0x7, 0x49, 0x76, 0xa3, 0xc7, 0x62, 0xf8, 0x3, 0x32, 0xb0, \
+ 0x98, 0x17, 0x3d, 0x80
+
+constexpr uint8_t kCodedFrameAv1Frame1Qp81[] = {
+ OBU_TEMPORAL_DELIMITER, OBU_SEQUENCE_HEADER, OBU_FRAME_1};
+constexpr uint8_t kCodedFrameAv1Frame2Qp81[] = {OBU_TEMPORAL_DELIMITER,
+ OBU_FRAME_2};
+
+#define OBU_SEQUENCE_HEADER_SVC_L3T1 \
+ 0x0a, 0x0d, 0x00, 0x27, 0x01, 0x01, 0x80, 0x80, 0x40, 0x40, 0x89, 0xff, \
+ 0x80, 0x34, 0x01
+#define OBU_FRAME1_SPATIAL_LAYER1 \
+ 0x32, 0x31, 0x12, 0x73, 0xc0, 0x07, 0xc0, 0x07, 0xd2, 0x80, 0x02, 0x08, \
+ 0x20, 0x84, 0x00, 0x09, 0x00, 0x05, 0x6b, 0x68, 0x4c, 0xef, 0x9e, 0xe5, \
+ 0x27, 0xff, 0x0d, 0xb1, 0x41, 0xde, 0xc9, 0xdd, 0x3c, 0x52, 0xea, 0xbf, \
+ 0xaa, 0xf5, 0x8f, 0x57, 0xaa, 0xdd, 0xe5, 0xf1, 0x19, 0x22, 0x3d, 0x5e, \
+ 0x5a, 0x16, 0x2d
+#define OBU_FRAME1_SPATIAL_LAYER2 \
+ 0x36, 0x08, 0x17, 0x31, 0xe0, 0x40, 0x02, 0x00, 0x00, 0xf7, 0xc0, 0x07, \
+ 0xc0, 0x07, 0xc0, 0xf8, 0x81, 0xff, 0xff, 0xfe, 0x2c, 0x54, 0xd8, 0x00, \
+ 0x76, 0x90
+#define OBU_FRAME1_SPATIAL_LAYER3 \
+ 0x36, 0x10, 0x11, 0x30, 0xe0, 0x84, 0x04, 0x00, 0x01, 0xf1, 0x03, 0xff, \
+ 0xff, 0xfc, 0x58, 0xa9, 0xb0, 0x00, 0x8c, 0x70
+
+constexpr uint8_t kL3T1Av1TemporaUnit[] = {
+ OBU_TEMPORAL_DELIMITER, OBU_SEQUENCE_HEADER_SVC_L3T1,
+ OBU_FRAME1_SPATIAL_LAYER1, OBU_FRAME1_SPATIAL_LAYER2,
+ OBU_FRAME1_SPATIAL_LAYER3};
+constexpr uint8_t kQpSpatialLayer1 = 40u;
+constexpr uint8_t kQpSpatialLayer2 = 241u;
+constexpr uint8_t kQpSpatialLayer3 = 241u;
+
+TEST(Av1QpParserTest, ParseQpAv1KeyFrame) {
+ Av1QpParser parser;
+ std::optional<uint32_t> qp = parser.Parse(kCodedFrameAv1Frame1Qp81);
+ EXPECT_THAT(qp, 81u);
+}
+
+TEST(Av1QpParserTest, ParseQpAv1DeltaFrameError) {
+ Av1QpParser parser_error;
+ // When the first frame is skipped the `sequence_header` is not set, hence QP
+ // must be unattainable.
+ std::optional<uint32_t> qp = parser_error.Parse(kCodedFrameAv1Frame2Qp81);
+ EXPECT_FALSE(qp.has_value());
+}
+
+TEST(Av1QpParserTest, ParseQpAv1DeltaFrameProper) {
+ Av1QpParser parser;
+ ASSERT_TRUE(parser.Parse(kCodedFrameAv1Frame1Qp81).has_value());
+ std::optional<uint32_t> qp = parser.Parse(kCodedFrameAv1Frame2Qp81);
+ EXPECT_THAT(qp, 81u);
+}
+
+TEST(Av1QpParserTest, ParseQpAv1SvcL3T1SpatialLayer1) {
+ Av1QpParser parser;
+ // `operating_point` = 2 would get the QP for the lowest resolution.
+ std::optional<uint32_t> qp = parser.Parse(kL3T1Av1TemporaUnit,
+ /*operating_point=*/2);
+ EXPECT_THAT(qp, kQpSpatialLayer1);
+}
+
+TEST(Av1QpParserTest, ParseQpAv1SvcL3T1SpatialLayer2) {
+ Av1QpParser parser;
+ // `operating_point` = 1 would get the QP for mid resolution.
+ std::optional<uint32_t> qp = parser.Parse(kL3T1Av1TemporaUnit,
+ /*operating_point=*/1);
+ EXPECT_THAT(qp, kQpSpatialLayer2);
+}
+
+TEST(Av1QpParserTest, ParseQpAv1SvcL3T1SpatialLayer3) {
+ Av1QpParser parser;
+ // `operating_point` = 0 would get the QP for the highest resolution.
+ std::optional<uint32_t> qp = parser.Parse(kL3T1Av1TemporaUnit,
+ /*operating_point=*/0);
+ EXPECT_THAT(qp, kQpSpatialLayer3);
+}
+
+} // namespace
+} // namespace webrtc