Extract AGTM metadata in AV1Decoder.
Bug: 395659818 Change-Id: I488342bc0cd46d756e15bc35f13a559ff1f7bd20 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6352839 Reviewed-by: ccameron chromium <ccameron@chromium.org> Commit-Queue: Maryla Ustarroz-Calonge <maryla@google.com> Reviewed-by: Dale Curtis <dalecurtis@chromium.org> Cr-Commit-Position: refs/heads/main@{#1432828}
This commit is contained in:
@ -44,6 +44,8 @@ source_set("base") {
|
||||
}
|
||||
|
||||
sources = [
|
||||
"agtm.cc",
|
||||
"agtm.h",
|
||||
"amplitude_peak_detector.cc",
|
||||
"amplitude_peak_detector.h",
|
||||
"android_overlay_config.cc",
|
||||
@ -579,6 +581,7 @@ static_library("test_support") {
|
||||
source_set("unit_tests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"agtm_unittest.cc",
|
||||
"amplitude_peak_detector_unittest.cc",
|
||||
"audio_block_fifo_unittest.cc",
|
||||
"audio_buffer_converter_unittest.cc",
|
||||
|
37
media/base/agtm.cc
Normal file
37
media/base/agtm.cc
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "media/base/agtm.h"
|
||||
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
// Returns AGTM metadata if the ITU-T T.35 message contains some.
|
||||
std::optional<gfx::HdrMetadataAgtm> GetHdrMetadataAgtmFromItutT35(
|
||||
uint8_t t35_country_code,
|
||||
base::span<const uint8_t> t35_payload) {
|
||||
// itu_t_t35_terminal_provider_code (2 bytes)
|
||||
// + itu_t_t35_terminal_provider_oriented_code (2 bytes)
|
||||
// + application_identifier (1 byte) + application_version (1 byte)
|
||||
static constexpr size_t kItuT35HeaderSize = 6;
|
||||
|
||||
const bool isAgtm = t35_country_code == 0xB5 /* United States */ &&
|
||||
t35_payload.size() >= kItuT35HeaderSize &&
|
||||
t35_payload[0] == 0x58 /* placeholder (AOM) */ &&
|
||||
t35_payload[1] == 0x90 /* placeholder (AOM) */ &&
|
||||
t35_payload[2] == 0x69 /* placeholder (AGTM) */ &&
|
||||
t35_payload[3] == 0x42 /* placeholder (AGTM) */ &&
|
||||
t35_payload[4] == 0x05 /* app identifier */ &&
|
||||
t35_payload[5] == 0x00 /* app version */;
|
||||
if (!isAgtm) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto agtm_payload_span = t35_payload.subspan(kItuT35HeaderSize);
|
||||
sk_sp<SkData> agtm_skdata =
|
||||
SkData::MakeWithCopy(agtm_payload_span.data(), agtm_payload_span.size());
|
||||
return gfx::HdrMetadataAgtm(std::move(agtm_skdata));
|
||||
}
|
||||
|
||||
} // namespace media
|
23
media/base/agtm.h
Normal file
23
media/base/agtm.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef MEDIA_BASE_AGTM_H_
|
||||
#define MEDIA_BASE_AGTM_H_
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "media/base/media_export.h"
|
||||
#include "ui/gfx/hdr_metadata.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
// Returns AGTM metadata if the ITU-T T.35 message contains some.
|
||||
MEDIA_EXPORT std::optional<gfx::HdrMetadataAgtm> GetHdrMetadataAgtmFromItutT35(
|
||||
uint8_t t35_country_code,
|
||||
base::span<const uint8_t> t35_payload);
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_BASE_AGTM_H_
|
42
media/base/agtm_unittest.cc
Normal file
42
media/base/agtm_unittest.cc
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "media/base/agtm.h"
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
TEST(GetHdrMetadataAgtmFromItutT35, HasMetadata) {
|
||||
const uint8_t t35_country_code = 0xB5;
|
||||
const std::vector<uint8_t> data = {0x58, 0x90, 0x69, 0x42, 0x05,
|
||||
0x00, 0x01, 0x02, 0x03};
|
||||
const std::optional<gfx::HdrMetadataAgtm> agtm =
|
||||
GetHdrMetadataAgtmFromItutT35(t35_country_code, data);
|
||||
ASSERT_TRUE(agtm.has_value());
|
||||
EXPECT_EQ(agtm->payload->size(), 3u);
|
||||
const std::vector<uint8_t> expected = {0x01, 0x02, 0x03};
|
||||
EXPECT_TRUE(agtm->payload->equals(
|
||||
SkData::MakeWithCopy(expected.data(), expected.size()).get()));
|
||||
}
|
||||
|
||||
TEST(GetHdrMetadataAgtmFromItutT35, WrongType) {
|
||||
const uint8_t t35_country_code = 0xB5;
|
||||
const std::vector<uint8_t> data = {
|
||||
0x58, 0x90, 0x69, 0xff /* wrong value*/, 0x05, 0x00, 0x01, 0x02, 0x03};
|
||||
const std::optional<gfx::HdrMetadataAgtm> agtm =
|
||||
GetHdrMetadataAgtmFromItutT35(t35_country_code, data);
|
||||
ASSERT_FALSE(agtm.has_value());
|
||||
}
|
||||
|
||||
TEST(GetHdrMetadataAgtmFromItutT35, DataTooShort) {
|
||||
const uint8_t t35_country_code = 0xB5;
|
||||
const std::vector<uint8_t> data = {0x58, 0x90, 0x69};
|
||||
const std::optional<gfx::HdrMetadataAgtm> agtm =
|
||||
GetHdrMetadataAgtmFromItutT35(t35_country_code, data);
|
||||
ASSERT_FALSE(agtm.has_value());
|
||||
}
|
||||
|
||||
} // namespace media
|
@ -16,6 +16,7 @@
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/task/bind_post_task.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "media/base/agtm.h"
|
||||
#include "media/base/decoder_buffer.h"
|
||||
#include "media/base/limits.h"
|
||||
#include "media/base/media_log.h"
|
||||
@ -115,32 +116,6 @@ static void LogDav1dMessage(void* cookie, const char* format, va_list ap) {
|
||||
DLOG(ERROR) << log;
|
||||
}
|
||||
|
||||
// Returns AGTM metadata if the ITU-T T.35 message contains some.
|
||||
static std::optional<gfx::HdrMetadataAgtm> GetHdrMetadataAgtm(
|
||||
uint8_t t35_country_code,
|
||||
base::span<const uint8_t> t35_payload) {
|
||||
// itu_t_t35_terminal_provider_code (2 bytes)
|
||||
// + itu_t_t35_terminal_provider_oriented_code (2 bytes)
|
||||
// + application_identifier (1 byte) + application_version (1 byte)
|
||||
static constexpr size_t kItuT35HeaderSize = 6;
|
||||
|
||||
const bool isAgtm = t35_country_code == 0xB5 /* United States */ &&
|
||||
t35_payload.size() >= kItuT35HeaderSize &&
|
||||
t35_payload[0] == 0x58 /* placeholder (AOM) */ &&
|
||||
t35_payload[1] == 0x90 /* placeholder (AOM) */ &&
|
||||
t35_payload[2] == 0x69 /* placeholder (AGTM) */ &&
|
||||
t35_payload[3] == 0x42 /* placeholder (AGTM) */ &&
|
||||
t35_payload[4] == 0x05 /* app identifier */ &&
|
||||
t35_payload[5] == 0x00 /* app version */;
|
||||
if (!isAgtm) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto agtm_payload_span = t35_payload.subspan(kItuT35HeaderSize);
|
||||
sk_sp<SkData> agtm_skdata =
|
||||
SkData::MakeWithCopy(agtm_payload_span.data(), agtm_payload_span.size());
|
||||
return gfx::HdrMetadataAgtm(std::move(agtm_skdata));
|
||||
}
|
||||
|
||||
// Dynamically allocated Dav1dPicture opaque data.
|
||||
struct FrameBufferData {
|
||||
FrameBufferData(void* fb, scoped_refptr<FrameBufferPool> pool)
|
||||
@ -502,7 +477,8 @@ bool Dav1dVideoDecoder::DecodeBuffer(scoped_refptr<DecoderBuffer> buffer) {
|
||||
auto t35_payload_span = UNSAFE_BUFFERS(base::span<const uint8_t>(
|
||||
p->itut_t35->payload, p->itut_t35->payload_size));
|
||||
const std::optional<gfx::HdrMetadataAgtm> agtm =
|
||||
GetHdrMetadataAgtm(p->itut_t35->country_code, t35_payload_span);
|
||||
GetHdrMetadataAgtmFromItutT35(p->itut_t35->country_code,
|
||||
t35_payload_span);
|
||||
if (agtm.has_value()) {
|
||||
gfx::HDRMetadata hdr_metadata =
|
||||
config_.hdr_metadata().value_or(gfx::HDRMetadata());
|
||||
|
@ -7,6 +7,8 @@
|
||||
#pragma allow_unsafe_libc_calls
|
||||
#endif
|
||||
|
||||
#include "media/filters/dav1d_video_decoder.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -25,9 +27,9 @@
|
||||
#include "media/base/test_helpers.h"
|
||||
#include "media/base/video_frame.h"
|
||||
#include "media/ffmpeg/ffmpeg_common.h"
|
||||
#include "media/filters/dav1d_video_decoder.h"
|
||||
#include "media/filters/in_memory_url_protocol.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
@ -285,7 +287,8 @@ TEST_F(Dav1dVideoDecoderTest, DecodeFrame_AgtmMetadata) {
|
||||
|
||||
const auto& frame = output_frames_.front();
|
||||
ASSERT_TRUE(frame->hdr_metadata().has_value());
|
||||
EXPECT_TRUE(frame->hdr_metadata()->agtm.has_value());
|
||||
ASSERT_TRUE(frame->hdr_metadata()->agtm.has_value());
|
||||
EXPECT_EQ(frame->hdr_metadata()->agtm->payload->size(), 99u);
|
||||
}
|
||||
|
||||
// Decode |i_frame_buffer_| and then a frame with a larger width and verify
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "media/base/agtm.h"
|
||||
#include "media/base/limits.h"
|
||||
#include "media/base/media_switches.h"
|
||||
#include "media/gpu/av1_picture.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "third_party/libgav1/src/src/gav1/status_code.h"
|
||||
#include "third_party/libgav1/src/src/utils/common.h"
|
||||
#include "third_party/libgav1/src/src/utils/constants.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
#include "ui/gfx/hdr_metadata.h"
|
||||
|
||||
namespace media {
|
||||
@ -481,6 +483,22 @@ AcceleratedVideoDecoder::DecodeResult AV1Decoder::DecodeInternal() {
|
||||
hdr_metadata_->smpte_st_2086 =
|
||||
ToGfxSmpteSt2086(current_frame_->hdr_mdcv());
|
||||
}
|
||||
if (current_frame_->itut_t35_set()) {
|
||||
// SAFETY: The best we can do is trust the size provided by libgav1.
|
||||
auto t35_payload_span = UNSAFE_BUFFERS(base::span<const uint8_t>(
|
||||
current_frame_->itut_t35().payload_bytes,
|
||||
static_cast<size_t>(current_frame_->itut_t35().payload_size)));
|
||||
const std::optional<gfx::HdrMetadataAgtm> agtm =
|
||||
GetHdrMetadataAgtmFromItutT35(current_frame_->itut_t35().country_code,
|
||||
t35_payload_span);
|
||||
if (agtm.has_value()) {
|
||||
if (!hdr_metadata_.has_value()) {
|
||||
hdr_metadata_.emplace();
|
||||
}
|
||||
// Overwrite existing AGTM metadata if any.
|
||||
hdr_metadata_->agtm = agtm;
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK(current_sequence_header_->film_grain_params_present ||
|
||||
!frame_header.film_grain_params.apply_grain);
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "third_party/libgav1/src/src/utils/common.h"
|
||||
#include "third_party/libgav1/src/src/utils/constants.h"
|
||||
#include "third_party/libgav1/src/src/utils/types.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
@ -933,6 +934,47 @@ TEST_F(AV1DecoderTest, DecodeWithFrameSizeChange) {
|
||||
testing::Not(testing::Contains(DecodeResult::kDecodeError)));
|
||||
}
|
||||
|
||||
TEST_F(AV1DecoderTest, DecodeStreamWithAgtmMetadata) {
|
||||
constexpr gfx::Size kFrameSize(320, 240);
|
||||
constexpr gfx::Size kRenderSize(320, 240);
|
||||
constexpr auto kProfile = libgav1::BitstreamProfile::kProfile0;
|
||||
const std::string kAgtmStream("av1-I-frame-320x240-agtm.ivf");
|
||||
std::vector<scoped_refptr<DecoderBuffer>> buffers = ReadIVF(kAgtmStream);
|
||||
ASSERT_FALSE(buffers.empty());
|
||||
std::vector<DecodeResult> expected = {DecodeResult::kConfigChange};
|
||||
std::vector<DecodeResult> results;
|
||||
for (auto buffer : buffers) {
|
||||
::testing::InSequence sequence;
|
||||
auto av1_picture = base::MakeRefCounted<AV1Picture>();
|
||||
EXPECT_CALL(*mock_accelerator_, CreateAV1Picture(/*apply_grain=*/false))
|
||||
.WillOnce(Return(av1_picture));
|
||||
EXPECT_CALL(
|
||||
*mock_accelerator_,
|
||||
SubmitDecode(
|
||||
MatchesFrameHeader(kFrameSize, kRenderSize,
|
||||
/*show_existing_frame=*/false,
|
||||
/*show_frame=*/true),
|
||||
MatchesYUV420SequenceHeader(kProfile, /*bitdepth=*/8, kFrameSize,
|
||||
/*film_grain_params_present=*/false),
|
||||
_, NonEmptyTileBuffers(), MatchesFrameData(buffer)))
|
||||
.WillOnce(Return(AV1Decoder::AV1Accelerator::Status::kOk));
|
||||
EXPECT_CALL(*mock_accelerator_,
|
||||
OutputPicture(SameAV1PictureInstance(av1_picture)))
|
||||
.WillOnce(Return(true));
|
||||
for (DecodeResult r : Decode(buffer)) {
|
||||
results.push_back(r);
|
||||
}
|
||||
expected.push_back(DecodeResult::kRanOutOfStreamData);
|
||||
testing::Mock::VerifyAndClearExpectations(mock_accelerator_);
|
||||
}
|
||||
EXPECT_EQ(results, expected);
|
||||
const std::optional<gfx::HDRMetadata> hdr_metadata =
|
||||
decoder_->GetHDRMetadata();
|
||||
ASSERT_TRUE(hdr_metadata.has_value());
|
||||
ASSERT_TRUE(hdr_metadata->agtm.has_value());
|
||||
EXPECT_EQ(hdr_metadata->agtm->payload->size(), 99u);
|
||||
}
|
||||
|
||||
// TODO(hiroh): Add more tests: reference frame tracking, render size change,
|
||||
// profile change, bit depth change, render size different than the frame size,
|
||||
// visible rectangle change in the middle of video sequence, reset while waiting
|
||||
|
@ -850,6 +850,12 @@ Same as av1-I-frame-320x240 with --monochrome and -b=[8,10,12] aomenc options.
|
||||
#### av1-I-frame-320x240-agtm
|
||||
Same as av1-I-frame-320x240 but with an AGTM ITU_T35 metadata OBU added.
|
||||
|
||||
#### av1-I-frame-320x240-agtm.ivf
|
||||
Created by converting av1-I-frame-320x240-agtm (raw OBU) to IVF:
|
||||
```
|
||||
ffmpeg -i av1-I-frame-320x240-agtm -c:v copy av1-I-frame-320x240-agtm.ivf
|
||||
```
|
||||
|
||||
#### bear-av1-cenc.mp4
|
||||
Encrypted version of bear-av1.mp4. Encrypted by [Shaka Packager] built locally
|
||||
at commit 53aa775ea488c0ffd3a2e1cb78ad000154e414e1 using key ID [1] and key [2].
|
||||
|
BIN
media/test/data/av1-I-frame-320x240-agtm.ivf
Normal file
BIN
media/test/data/av1-I-frame-320x240-agtm.ivf
Normal file
Binary file not shown.
@ -35,6 +35,7 @@ data/av1-1-b8-02-allintra.ivf.json
|
||||
data/av1-I-frame-1280x720
|
||||
data/av1-I-frame-320x240
|
||||
data/av1-I-frame-320x240-agtm
|
||||
data/av1-I-frame-320x240-agtm.ivf
|
||||
data/av1-film_grain.ivf
|
||||
data/av1-monochrome-I-frame-320x240-10bpp
|
||||
data/av1-monochrome-I-frame-320x240-12bpp
|
||||
|
@ -47,6 +47,7 @@
|
||||
//media/test/data/av1-I-frame-1280x720
|
||||
//media/test/data/av1-I-frame-320x240
|
||||
//media/test/data/av1-I-frame-320x240-agtm
|
||||
//media/test/data/av1-I-frame-320x240-agtm.ivf
|
||||
//media/test/data/av1-film_grain.ivf
|
||||
//media/test/data/av1-monochrome-I-frame-320x240-10bpp
|
||||
//media/test/data/av1-monochrome-I-frame-320x240-12bpp
|
||||
|
Reference in New Issue
Block a user