feat: Generate the entire AV1 codec string when the colr atom is present (#1205)
As per the AV1 spec, the codec string may contain optional color values. This extracts the missing color information from the mp4 `colr` atom, if present, and generates the full AV1 codec string. Closes #1007
This commit is contained in:
parent
6d745460b6
commit
cc9a691aef
|
@ -35,6 +35,7 @@ enum FourCC : uint32_t {
|
||||||
FOURCC_cbcs = 0x63626373,
|
FOURCC_cbcs = 0x63626373,
|
||||||
FOURCC_cenc = 0x63656e63,
|
FOURCC_cenc = 0x63656e63,
|
||||||
FOURCC_cens = 0x63656e73,
|
FOURCC_cens = 0x63656e73,
|
||||||
|
FOURCC_colr = 0x636f6c72,
|
||||||
FOURCC_co64 = 0x636f3634,
|
FOURCC_co64 = 0x636f3634,
|
||||||
FOURCC_cmfc = 0x636d6663,
|
FOURCC_cmfc = 0x636d6663,
|
||||||
FOURCC_cmfs = 0x636d6673,
|
FOURCC_cmfs = 0x636d6673,
|
||||||
|
@ -102,6 +103,8 @@ enum FourCC : uint32_t {
|
||||||
FOURCC_mp4v = 0x6d703476,
|
FOURCC_mp4v = 0x6d703476,
|
||||||
FOURCC_mvex = 0x6d766578,
|
FOURCC_mvex = 0x6d766578,
|
||||||
FOURCC_mvhd = 0x6d766864,
|
FOURCC_mvhd = 0x6d766864,
|
||||||
|
FOURCC_nclc = 0x6e636c63,
|
||||||
|
FOURCC_nclx = 0x6e636c78,
|
||||||
FOURCC_nmhd = 0x6e6d6864,
|
FOURCC_nmhd = 0x6e6d6864,
|
||||||
FOURCC_pasp = 0x70617370,
|
FOURCC_pasp = 0x70617370,
|
||||||
FOURCC_payl = 0x7061796c,
|
FOURCC_payl = 0x7061796c,
|
||||||
|
|
|
@ -81,12 +81,27 @@ bool AV1CodecConfigurationRecord::Parse(const uint8_t* data, size_t data_size) {
|
||||||
// mandatory fields.
|
// mandatory fields.
|
||||||
// All the other fields (including their leading '.') are optional, mutually
|
// All the other fields (including their leading '.') are optional, mutually
|
||||||
// inclusive (all or none) fields.
|
// inclusive (all or none) fields.
|
||||||
// Since some of the optional fields (e.g. colorPrimaries) are not present in
|
|
||||||
// AV1CodecConfigurationRecord, we omit all the optional fields.
|
// When color info is NOT available, generate the basic codec string without the
|
||||||
|
// optional fields
|
||||||
std::string AV1CodecConfigurationRecord::GetCodecString() const {
|
std::string AV1CodecConfigurationRecord::GetCodecString() const {
|
||||||
return base::StringPrintf("av01.%d.%02d%c.%02d", profile_, level_,
|
return base::StringPrintf("av01.%d.%02d%c.%02d", profile_, level_,
|
||||||
tier_ ? 'H' : 'M', bit_depth_);
|
tier_ ? 'H' : 'M', bit_depth_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When color info IS available, generate the full codec string with optional
|
||||||
|
// fields
|
||||||
|
std::string AV1CodecConfigurationRecord::GetCodecString(
|
||||||
|
uint16_t color_primaries,
|
||||||
|
uint16_t transfer_characteristics,
|
||||||
|
uint16_t matrix_coefficients,
|
||||||
|
uint8_t video_full_range_flag) const {
|
||||||
|
return base::StringPrintf(
|
||||||
|
"av01.%d.%02d%c.%02d.%d.%d%d%d.%02d.%02d.%02d.%d", profile_, level_,
|
||||||
|
tier_ ? 'H' : 'M', bit_depth_, mono_chrome_, chroma_subsampling_x_,
|
||||||
|
chroma_subsampling_y_, chroma_sample_position_, color_primaries,
|
||||||
|
transfer_characteristics, matrix_coefficients, video_full_range_flag);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -33,6 +33,11 @@ class AV1CodecConfigurationRecord {
|
||||||
/// @return The codec string.
|
/// @return The codec string.
|
||||||
std::string GetCodecString() const;
|
std::string GetCodecString() const;
|
||||||
|
|
||||||
|
std::string GetCodecString(uint16_t color_primaries,
|
||||||
|
uint16_t transfer_characteristics,
|
||||||
|
uint16_t matrix_coefficients,
|
||||||
|
uint8_t video_full_range_flag) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int profile_ = 0;
|
int profile_ = 0;
|
||||||
int level_ = 0;
|
int level_ = 0;
|
||||||
|
|
|
@ -28,6 +28,8 @@ TEST(AV1CodecConfigurationRecordTest, Success) {
|
||||||
std::end(kAV1CodecConfigurationData))));
|
std::end(kAV1CodecConfigurationData))));
|
||||||
|
|
||||||
EXPECT_EQ(av1_config.GetCodecString(), "av01.0.04M.10");
|
EXPECT_EQ(av1_config.GetCodecString(), "av01.0.04M.10");
|
||||||
|
EXPECT_EQ(av1_config.GetCodecString(10, 8, 4, 1),
|
||||||
|
"av01.0.04M.10.0.112.10.08.04.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AV1CodecConfigurationRecordTest, Success2) {
|
TEST(AV1CodecConfigurationRecordTest, Success2) {
|
||||||
|
@ -47,6 +49,8 @@ TEST(AV1CodecConfigurationRecordTest, Success2) {
|
||||||
std::end(kAV1CodecConfigurationData))));
|
std::end(kAV1CodecConfigurationData))));
|
||||||
|
|
||||||
EXPECT_EQ(av1_config.GetCodecString(), "av01.1.21H.12");
|
EXPECT_EQ(av1_config.GetCodecString(), "av01.1.21H.12");
|
||||||
|
EXPECT_EQ(av1_config.GetCodecString(1, 1, 1, 0),
|
||||||
|
"av01.1.21H.12.1.010.01.01.01.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AV1CodecConfigurationRecordTest, InsufficientData) {
|
TEST(AV1CodecConfigurationRecordTest, InsufficientData) {
|
||||||
|
|
|
@ -1462,6 +1462,39 @@ size_t CodecConfiguration::ComputeSizeInternal() {
|
||||||
return HeaderSize() + (box_type == FOURCC_vpcC ? 4 : 0) + data.size();
|
return HeaderSize() + (box_type == FOURCC_vpcC ? 4 : 0) + data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorParameters::ColorParameters() = default;
|
||||||
|
ColorParameters::~ColorParameters() = default;
|
||||||
|
|
||||||
|
FourCC ColorParameters::BoxType() const {
|
||||||
|
return FOURCC_colr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColorParameters::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
|
if (buffer->reader()) {
|
||||||
|
RCHECK((buffer->reader())->ReadFourCC(&color_parameter_type) &&
|
||||||
|
(buffer->reader())->Read2(&color_primaries) &&
|
||||||
|
(buffer->reader())->Read2(&transfer_characteristics) &&
|
||||||
|
(buffer->reader())->Read2(&matrix_coefficients));
|
||||||
|
// Type nclc does not contain video_full_range_flag data, and thus, it has 1
|
||||||
|
// less byte than nclx. Only extract video_full_range_flag if of type nclx.
|
||||||
|
if (color_parameter_type == FOURCC_nclx) {
|
||||||
|
RCHECK((buffer->reader())->Read1(&video_full_range_flag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(caitlinocallaghan) Add the ability to write the colr atom and include
|
||||||
|
// it in the muxed mp4.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ColorParameters::ComputeSizeInternal() {
|
||||||
|
// This box is optional. Skip it if it is not initialized.
|
||||||
|
if (color_parameter_type == FOURCC_NULL)
|
||||||
|
return 0;
|
||||||
|
return HeaderSize() + kFourCCSize + sizeof(color_primaries) +
|
||||||
|
sizeof(transfer_characteristics) + sizeof(matrix_coefficients) +
|
||||||
|
sizeof(video_full_range_flag);
|
||||||
|
}
|
||||||
|
|
||||||
PixelAspectRatio::PixelAspectRatio() = default;
|
PixelAspectRatio::PixelAspectRatio() = default;
|
||||||
PixelAspectRatio::~PixelAspectRatio() = default;
|
PixelAspectRatio::~PixelAspectRatio() = default;
|
||||||
|
|
||||||
|
@ -1597,6 +1630,7 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
RCHECK(buffer->ReadWriteChild(&extra_codec_config));
|
RCHECK(buffer->ReadWriteChild(&extra_codec_config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCHECK(buffer->TryReadWriteChild(&colr));
|
||||||
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
|
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
|
||||||
|
|
||||||
// Somehow Edge does not support having sinf box before codec_configuration,
|
// Somehow Edge does not support having sinf box before codec_configuration,
|
||||||
|
|
|
@ -268,6 +268,16 @@ struct CodecConfiguration : Box {
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ColorParameters : Box {
|
||||||
|
DECLARE_BOX_METHODS(ColorParameters);
|
||||||
|
|
||||||
|
FourCC color_parameter_type = FOURCC_NULL;
|
||||||
|
uint16_t color_primaries = 1;
|
||||||
|
uint16_t transfer_characteristics = 1;
|
||||||
|
uint16_t matrix_coefficients = 1;
|
||||||
|
uint8_t video_full_range_flag = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PixelAspectRatio : Box {
|
struct PixelAspectRatio : Box {
|
||||||
DECLARE_BOX_METHODS(PixelAspectRatio);
|
DECLARE_BOX_METHODS(PixelAspectRatio);
|
||||||
|
|
||||||
|
@ -297,6 +307,7 @@ struct VideoSampleEntry : Box {
|
||||||
uint16_t width = 0u;
|
uint16_t width = 0u;
|
||||||
uint16_t height = 0u;
|
uint16_t height = 0u;
|
||||||
|
|
||||||
|
ColorParameters colr;
|
||||||
PixelAspectRatio pixel_aspect;
|
PixelAspectRatio pixel_aspect;
|
||||||
ProtectionSchemeInfo sinf;
|
ProtectionSchemeInfo sinf;
|
||||||
CodecConfiguration codec_configuration;
|
CodecConfiguration codec_configuration;
|
||||||
|
|
|
@ -326,6 +326,14 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
Modify(&metadata->id3v2);
|
Modify(&metadata->id3v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Fill(ColorParameters* colr) {
|
||||||
|
colr->color_parameter_type = FOURCC_nclc;
|
||||||
|
colr->color_primaries = 9;
|
||||||
|
colr->transfer_characteristics = 16;
|
||||||
|
colr->matrix_coefficients = 9;
|
||||||
|
colr->video_full_range_flag = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Fill(PixelAspectRatio* pasp) {
|
void Fill(PixelAspectRatio* pasp) {
|
||||||
pasp->h_spacing = 5;
|
pasp->h_spacing = 5;
|
||||||
pasp->v_spacing = 8;
|
pasp->v_spacing = 8;
|
||||||
|
@ -360,6 +368,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
entry->data_reference_index = 1;
|
entry->data_reference_index = 1;
|
||||||
entry->width = 800;
|
entry->width = 800;
|
||||||
entry->height = 600;
|
entry->height = 600;
|
||||||
|
Fill(&entry->colr);
|
||||||
Fill(&entry->pixel_aspect);
|
Fill(&entry->pixel_aspect);
|
||||||
Fill(&entry->sinf);
|
Fill(&entry->sinf);
|
||||||
Fill(&entry->codec_configuration);
|
Fill(&entry->codec_configuration);
|
||||||
|
|
|
@ -604,7 +604,15 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
LOG(ERROR) << "Failed to parse av1c.";
|
LOG(ERROR) << "Failed to parse av1c.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Generate the full codec string if the colr atom is present.
|
||||||
|
if (entry.colr.color_parameter_type != FOURCC_NULL) {
|
||||||
|
codec_string = av1_config.GetCodecString(
|
||||||
|
entry.colr.color_primaries, entry.colr.transfer_characteristics,
|
||||||
|
entry.colr.matrix_coefficients,
|
||||||
|
entry.colr.video_full_range_flag);
|
||||||
|
} else {
|
||||||
codec_string = av1_config.GetCodecString();
|
codec_string = av1_config.GetCodecString();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FOURCC_avc1:
|
case FOURCC_avc1:
|
||||||
|
|
Loading…
Reference in New Issue