Skip to content

Commit

Permalink
Add support for WebM colour element
Browse files Browse the repository at this point in the history
Change-Id: I359cde97171118d3b928a9dd6650d11fade8f4a9
  • Loading branch information
kqyang committed May 2, 2017
1 parent 84ff940 commit 1e2da22
Show file tree
Hide file tree
Showing 26 changed files with 387 additions and 35 deletions.
12 changes: 6 additions & 6 deletions packager/app/test/packager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,22 +252,22 @@ def testPackageWithWebmSubsampleEncryption(self):
self.packager.Package(
self._GetStreams(['video'],
output_format='webm',
test_files=['bear-640x360-vp9-altref.webm']),
test_files=['bear-320x180-vp9-altref.webm']),
self._GetFlags(encryption=True))
self._DiffGold(self.output[0], 'bear-640x360-vp9-altref-enc-golden.webm')
self._DiffGold(self.output[0], 'bear-320x180-vp9-altref-enc-golden.webm')
self._VerifyDecryption(self.output[0],
'bear-640x360-vp9-altref-dec-golden.webm')
'bear-320x180-vp9-altref-dec-golden.webm')

def testPackageWithWebmVp9FullSampleEncryption(self):
self.packager.Package(
self._GetStreams(['video'],
output_format='webm',
test_files=['bear-640x360-vp9-altref.webm']),
test_files=['bear-320x180-vp9-altref.webm']),
self._GetFlags(encryption=True, vp9_subsample_encryption=False))
self._DiffGold(self.output[0],
'bear-640x360-vp9-fullsample-enc-golden.webm')
'bear-320x180-vp9-fullsample-enc-golden.webm')
self._VerifyDecryption(self.output[0],
'bear-640x360-vp9-altref-dec-golden.webm')
'bear-320x180-vp9-altref-dec-golden.webm')

def testPackageAvcTsWithEncryption(self):
# Currently we only support live packaging for ts.
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion packager/media/codecs/vp8_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ bool VP8Parser::Parse(const uint8_t* data,
// VP8 uses an 8-bit YUV 4:2:0 format.
// http://tools.ietf.org/html/rfc6386 Section 2.
writable_codec_config()->set_bit_depth(8);
writable_codec_config()->set_chroma_subsampling(
writable_codec_config()->SetChromaSubsampling(
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA);

VPxFrameInfo vpx_frame;
Expand Down
6 changes: 3 additions & 3 deletions packager/media/codecs/vp9_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ bool ReadBitDepthAndColorSpace(BitReader* reader,
if (chroma_subsampling ==
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA) {
LOG(ERROR) << "4:2:0 color not supported in profile "
<< codec_config->profile();
<< static_cast<int>(codec_config->profile());
return false;
}

Expand All @@ -293,7 +293,7 @@ bool ReadBitDepthAndColorSpace(BitReader* reader,
}
}
codec_config->set_video_full_range_flag(yuv_full_range);
codec_config->set_chroma_subsampling(chroma_subsampling);
codec_config->SetChromaSubsampling(chroma_subsampling);

VLOG(3) << "\n profile " << static_cast<int>(codec_config->profile())
<< "\n bit depth " << static_cast<int>(codec_config->bit_depth())
Expand Down Expand Up @@ -511,7 +511,7 @@ bool VP9Parser::Parse(const uint8_t* data,
// specification of either the color format or color sub-sampling in
// profile 0. VP9 specifies that the default color format should be
// YUV 4:2:0 in this case (normative).
writable_codec_config()->set_chroma_subsampling(
writable_codec_config()->SetChromaSubsampling(
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA);
writable_codec_config()->set_bit_depth(8);
}
Expand Down
65 changes: 65 additions & 0 deletions packager/media/codecs/vp_codec_configuration_record.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ bool VPCodecConfigurationRecord::ParseMP4(const std::vector<uint8_t>& data) {
return true;
}

// http://wiki.webmproject.org/vp9-codecprivate
bool VPCodecConfigurationRecord::ParseWebM(const std::vector<uint8_t>& data) {
BufferReader reader(data.data(), data.size());

Expand Down Expand Up @@ -246,6 +247,70 @@ void VPCodecConfigurationRecord::MergeFrom(
}
codec_initialization_data_ = other.codec_initialization_data_;
}

MergeField("chroma location", other.chroma_location_, &chroma_location_);
UpdateChromaSubsamplingIfNeeded();
}

void VPCodecConfigurationRecord::SetChromaSubsampling(uint8_t subsampling_x,
uint8_t subsampling_y) {
VLOG(3) << "Set Chroma subsampling " << static_cast<int>(subsampling_x) << " "
<< static_cast<int>(subsampling_y);
if (subsampling_x == 0 && subsampling_y == 0) {
chroma_subsampling_ = CHROMA_444;
} else if (subsampling_x == 0 && subsampling_y == 1) {
chroma_subsampling_ = CHROMA_440;
} else if (subsampling_x == 1 && subsampling_y == 0) {
chroma_subsampling_ = CHROMA_422;
} else if (subsampling_x == 1 && subsampling_y == 1) {
// VP9 assumes that chrome samples are collocated with luma samples if
// there is no explicit signaling outside of VP9 bitstream.
chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
} else {
LOG(WARNING) << "Unexpected chroma subsampling values: "
<< static_cast<int>(subsampling_x) << " "
<< static_cast<int>(subsampling_y);
}
UpdateChromaSubsamplingIfNeeded();
}

void VPCodecConfigurationRecord::SetChromaSubsampling(
ChromaSubsampling chroma_subsampling) {
chroma_subsampling_ = chroma_subsampling;
UpdateChromaSubsamplingIfNeeded();
}

void VPCodecConfigurationRecord::SetChromaLocation(uint8_t chroma_siting_x,
uint8_t chroma_siting_y) {
VLOG(3) << "Set Chroma Location " << static_cast<int>(chroma_siting_x) << " "
<< static_cast<int>(chroma_siting_y);
if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kTopCollocated) {
chroma_location_ = AVCHROMA_LOC_TOPLEFT;
} else if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kHalf) {
chroma_location_ = AVCHROMA_LOC_LEFT;
} else if (chroma_siting_x == kHalf && chroma_siting_y == kTopCollocated) {
chroma_location_ = AVCHROMA_LOC_TOP;
} else if (chroma_siting_x == kHalf && chroma_siting_y == kHalf) {
chroma_location_ = AVCHROMA_LOC_CENTER;
} else {
LOG(WARNING) << "Unexpected chroma siting values: "
<< static_cast<int>(chroma_siting_x) << " "
<< static_cast<int>(chroma_siting_y);
}
UpdateChromaSubsamplingIfNeeded();
}

void VPCodecConfigurationRecord::UpdateChromaSubsamplingIfNeeded() {
// Use chroma location to fix the chroma subsampling format.
if (chroma_location_ && chroma_subsampling_ &&
(*chroma_subsampling_ == CHROMA_420_VERTICAL ||
*chroma_subsampling_ == CHROMA_420_COLLOCATED_WITH_LUMA)) {
if (*chroma_location_ == AVCHROMA_LOC_TOPLEFT)
chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
else if (*chroma_location_ == AVCHROMA_LOC_LEFT)
chroma_subsampling_ = CHROMA_420_VERTICAL;
VLOG(3) << "Chroma subsampling " << static_cast<int>(*chroma_subsampling_);
}
}

} // namespace media
Expand Down
75 changes: 70 additions & 5 deletions packager/media/codecs/vp_codec_configuration_record.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,34 @@ enum AVColorSpace {
AVCOL_SPC_NB
};

/// Location of chroma samples.
///
/// Illustration showing the location of the first (top left) chroma sample of
/// the image, the left shows only luma, the right shows the location of the
/// chroma sample, the 2 could be imagined to overlay each other but are drawn
/// separately due to limitations of ASCII
///
/// 1st 2nd 1st 2nd horizontal luma sample positions
/// v v v v
/// ______ ______
/// 1st luma line > |X X ... |3 4 X ... X are luma samples,
/// | |1 2 1-6 are possible chroma positions
/// 2nd luma line > |X X ... |5 6 X ... 0 is undefined/unknown position
enum AVChromaLocation {
AVCHROMA_LOC_UNSPECIFIED = 0,
/// MPEG-2/4 4:2:0, H.264 default for 4:2:0
AVCHROMA_LOC_LEFT = 1,
/// MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0
AVCHROMA_LOC_CENTER = 2,
/// ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2
AVCHROMA_LOC_TOPLEFT = 3,
AVCHROMA_LOC_TOP = 4,
AVCHROMA_LOC_BOTTOMLEFT = 5,
AVCHROMA_LOC_BOTTOM = 6,
/// Not part of ABI
AVCHROMA_LOC_NB
};

/// Class for parsing or writing VP codec configuration record.
class VPCodecConfigurationRecord {
public:
Expand All @@ -131,6 +159,12 @@ class VPCodecConfigurationRecord {
CHROMA_444 = 3,
CHROMA_440 = 4,
};
enum ChromaSitingValues {
kUnspecified = 0,
kLeftCollocated = 1,
kTopCollocated = kLeftCollocated,
kHalf = 2,
};

VPCodecConfigurationRecord();
VPCodecConfigurationRecord(
Expand Down Expand Up @@ -164,16 +198,17 @@ class VPCodecConfigurationRecord {
/// @return The codec string.
std::string GetCodecString(Codec codec) const;

// Merges the values from the given configuration. If there are values in
// both |*this| and |other|, the values in |other| take precedence.
/// Merges the values from the given configuration. If there are values in
/// both |*this| and |other|, |*this| is not updated.
void MergeFrom(const VPCodecConfigurationRecord& other);

void SetChromaSubsampling(uint8_t subsampling_x, uint8_t subsampling_y);
void SetChromaSubsampling(ChromaSubsampling chroma_subsampling);
void SetChromaLocation(uint8_t chroma_siting_x, uint8_t chroma_siting_y);

void set_profile(uint8_t profile) { profile_ = profile; }
void set_level(uint8_t level) { level_ = level; }
void set_bit_depth(uint8_t bit_depth) { bit_depth_ = bit_depth; }
void set_chroma_subsampling(uint8_t chroma_subsampling) {
chroma_subsampling_ = chroma_subsampling;
}
void set_video_full_range_flag(bool video_full_range_flag) {
video_full_range_flag_ = video_full_range_flag;
}
Expand All @@ -187,6 +222,28 @@ class VPCodecConfigurationRecord {
matrix_coefficients_ = matrix_coefficients;
}

bool is_profile_set() const { return static_cast<bool>(profile_); }
bool is_level_set() const { return static_cast<bool>(level_); }
bool is_bit_depth_set() const { return static_cast<bool>(bit_depth_); }
bool is_chroma_subsampling_set() const {
return static_cast<bool>(chroma_subsampling_);
}
bool is_video_full_range_flag_set() const {
return static_cast<bool>(video_full_range_flag_);
}
bool is_color_primaries_set() const {
return static_cast<bool>(color_primaries_);
}
bool is_transfer_characteristics_set() const {
return static_cast<bool>(transfer_characteristics_);
}
bool is_matrix_coefficients_set() const {
return static_cast<bool>(matrix_coefficients_);
}
bool is_chroma_location_set() const {
return static_cast<bool>(chroma_location_);
}

uint8_t profile() const { return profile_.value_or(0); }
uint8_t level() const { return level_.value_or(10); }
uint8_t bit_depth() const { return bit_depth_.value_or(8); }
Expand All @@ -205,8 +262,13 @@ class VPCodecConfigurationRecord {
uint8_t matrix_coefficients() const {
return matrix_coefficients_.value_or(AVCOL_SPC_UNSPECIFIED);
}
uint8_t chroma_location() const {
return chroma_location_ ? *chroma_location_ : AVCHROMA_LOC_UNSPECIFIED;
}

private:
void UpdateChromaSubsamplingIfNeeded();

base::Optional<uint8_t> profile_;
base::Optional<uint8_t> level_;
base::Optional<uint8_t> bit_depth_;
Expand All @@ -217,6 +279,9 @@ class VPCodecConfigurationRecord {
base::Optional<uint8_t> matrix_coefficients_;
std::vector<uint8_t> codec_initialization_data_;

// Not in the decoder config. It is there to help determine chroma subsampling
// format.
base::Optional<uint8_t> chroma_location_;
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
// generated copy constructor and assignment operator. Since the internal data
// is small, the performance impact is minimal.
Expand Down
85 changes: 85 additions & 0 deletions packager/media/codecs/vp_codec_configuration_record_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,90 @@ TEST(VPCodecConfigurationRecordTest, WriteWebM) {
data);
}

TEST(VPCodecConfigurationRecordTest, SetAttributes) {
VPCodecConfigurationRecord vp_config;
// None of the members are set.
EXPECT_FALSE(vp_config.is_profile_set());
EXPECT_FALSE(vp_config.is_level_set());
EXPECT_FALSE(vp_config.is_bit_depth_set());
EXPECT_FALSE(vp_config.is_chroma_subsampling_set());
EXPECT_FALSE(vp_config.is_video_full_range_flag_set());
EXPECT_FALSE(vp_config.is_color_primaries_set());
EXPECT_FALSE(vp_config.is_transfer_characteristics_set());
EXPECT_FALSE(vp_config.is_matrix_coefficients_set());

const uint8_t kProfile = 2;
vp_config.set_profile(kProfile);
EXPECT_TRUE(vp_config.is_profile_set());
EXPECT_EQ(kProfile, vp_config.profile());
}

TEST(VPCodecConfigurationRecordTest, SetChromaSubsampling) {
VPCodecConfigurationRecord vp_config;
vp_config.SetChromaSubsampling(1, 1);
EXPECT_TRUE(vp_config.is_chroma_subsampling_set());
EXPECT_FALSE(vp_config.is_chroma_location_set());
EXPECT_EQ(VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA,
vp_config.chroma_subsampling());

vp_config.SetChromaLocation(VPCodecConfigurationRecord::kLeftCollocated,
VPCodecConfigurationRecord::kHalf);
EXPECT_TRUE(vp_config.is_chroma_location_set());
EXPECT_EQ(VPCodecConfigurationRecord::CHROMA_420_VERTICAL,
vp_config.chroma_subsampling());
}

TEST(VPCodecConfigurationRecordTest, Merge) {
const uint8_t kProfile = 2;
const uint8_t kLevel = 20;

VPCodecConfigurationRecord vp_config;
vp_config.set_profile(kProfile);

VPCodecConfigurationRecord vp_config2;
vp_config2.set_profile(kProfile - 1);
vp_config2.set_level(kLevel);

vp_config.MergeFrom(vp_config2);
EXPECT_TRUE(vp_config.is_profile_set());
EXPECT_TRUE(vp_config.is_level_set());
EXPECT_FALSE(vp_config.is_bit_depth_set());
EXPECT_FALSE(vp_config.is_chroma_subsampling_set());
EXPECT_FALSE(vp_config.is_video_full_range_flag_set());
EXPECT_FALSE(vp_config.is_color_primaries_set());
EXPECT_FALSE(vp_config.is_transfer_characteristics_set());
EXPECT_FALSE(vp_config.is_matrix_coefficients_set());

// Profile is set in the original config, so not changed.
EXPECT_EQ(kProfile, vp_config.profile());
// Merge level from the other config.
EXPECT_EQ(kLevel, vp_config.level());
}

TEST(VPCodecConfigurationRecordTest, MergeChromaSubsampling) {
VPCodecConfigurationRecord vp_config;
vp_config.SetChromaSubsampling(
VPCodecConfigurationRecord::CHROMA_420_VERTICAL);

VPCodecConfigurationRecord vp_config2;
vp_config2.SetChromaLocation(VPCodecConfigurationRecord::kLeftCollocated,
VPCodecConfigurationRecord::kTopCollocated);

vp_config.MergeFrom(vp_config2);
EXPECT_FALSE(vp_config.is_profile_set());
EXPECT_FALSE(vp_config.is_level_set());
EXPECT_FALSE(vp_config.is_bit_depth_set());
EXPECT_TRUE(vp_config.is_chroma_subsampling_set());
EXPECT_TRUE(vp_config.is_chroma_location_set());
EXPECT_FALSE(vp_config.is_video_full_range_flag_set());
EXPECT_FALSE(vp_config.is_color_primaries_set());
EXPECT_FALSE(vp_config.is_transfer_characteristics_set());
EXPECT_FALSE(vp_config.is_matrix_coefficients_set());

EXPECT_EQ(VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA,
vp_config.chroma_subsampling());
EXPECT_EQ(AVCHROMA_LOC_TOPLEFT, vp_config.chroma_location());
}

} // namespace media
} // namespace shaka
17 changes: 16 additions & 1 deletion packager/media/formats/webm/segmenter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,26 @@ Status Segmenter::InitializeVideoTrack(const VideoStreamInfo* info,
"Unable to parse VP9 codec configuration");
}

mkvmuxer::Colour colour;
if (vp_config.matrix_coefficients() != AVCOL_SPC_UNSPECIFIED) {
colour.set_matrix_coefficients(vp_config.matrix_coefficients());
}
if (vp_config.transfer_characteristics() != AVCOL_TRC_UNSPECIFIED) {
colour.set_transfer_characteristics(vp_config.transfer_characteristics());
}
if (vp_config.color_primaries() != AVCOL_PRI_UNSPECIFIED) {
colour.set_primaries(vp_config.color_primaries());
}
if (!track->SetColour(colour)) {
return Status(error::INTERNAL_ERROR,
"Failed to setup color element for VPx streams");
}

std::vector<uint8_t> codec_config;
vp_config.WriteWebM(&codec_config);
if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
return Status(error::INTERNAL_ERROR,
"Private codec data required for VP9 streams");
"Private codec data required for VPx streams");
}
} else {
LOG(ERROR) << "Only VP8 and VP9 video codecs are supported.";
Expand Down

0 comments on commit 1e2da22

Please sign in to comment.