Add support for WebM colour element

Change-Id: I359cde97171118d3b928a9dd6650d11fade8f4a9
This commit is contained in:
KongQun Yang 2017-04-25 12:02:08 -07:00
parent 84ff940e22
commit 1e2da22c88
26 changed files with 387 additions and 35 deletions

View File

@ -252,22 +252,22 @@ class PackagerAppTest(unittest.TestCase):
self.packager.Package( self.packager.Package(
self._GetStreams(['video'], self._GetStreams(['video'],
output_format='webm', output_format='webm',
test_files=['bear-640x360-vp9-altref.webm']), test_files=['bear-320x180-vp9-altref.webm']),
self._GetFlags(encryption=True)) 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], self._VerifyDecryption(self.output[0],
'bear-640x360-vp9-altref-dec-golden.webm') 'bear-320x180-vp9-altref-dec-golden.webm')
def testPackageWithWebmVp9FullSampleEncryption(self): def testPackageWithWebmVp9FullSampleEncryption(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['video'], self._GetStreams(['video'],
output_format='webm', 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._GetFlags(encryption=True, vp9_subsample_encryption=False))
self._DiffGold(self.output[0], self._DiffGold(self.output[0],
'bear-640x360-vp9-fullsample-enc-golden.webm') 'bear-320x180-vp9-fullsample-enc-golden.webm')
self._VerifyDecryption(self.output[0], self._VerifyDecryption(self.output[0],
'bear-640x360-vp9-altref-dec-golden.webm') 'bear-320x180-vp9-altref-dec-golden.webm')
def testPackageAvcTsWithEncryption(self): def testPackageAvcTsWithEncryption(self):
# Currently we only support live packaging for ts. # Currently we only support live packaging for ts.

Binary file not shown.

Binary file not shown.

View File

@ -152,7 +152,7 @@ bool VP8Parser::Parse(const uint8_t* data,
// VP8 uses an 8-bit YUV 4:2:0 format. // VP8 uses an 8-bit YUV 4:2:0 format.
// http://tools.ietf.org/html/rfc6386 Section 2. // http://tools.ietf.org/html/rfc6386 Section 2.
writable_codec_config()->set_bit_depth(8); writable_codec_config()->set_bit_depth(8);
writable_codec_config()->set_chroma_subsampling( writable_codec_config()->SetChromaSubsampling(
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA); VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA);
VPxFrameInfo vpx_frame; VPxFrameInfo vpx_frame;

View File

@ -269,7 +269,7 @@ bool ReadBitDepthAndColorSpace(BitReader* reader,
if (chroma_subsampling == if (chroma_subsampling ==
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA) { VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA) {
LOG(ERROR) << "4:2:0 color not supported in profile " LOG(ERROR) << "4:2:0 color not supported in profile "
<< codec_config->profile(); << static_cast<int>(codec_config->profile());
return false; return false;
} }
@ -293,7 +293,7 @@ bool ReadBitDepthAndColorSpace(BitReader* reader,
} }
} }
codec_config->set_video_full_range_flag(yuv_full_range); 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()) VLOG(3) << "\n profile " << static_cast<int>(codec_config->profile())
<< "\n bit depth " << static_cast<int>(codec_config->bit_depth()) << "\n bit depth " << static_cast<int>(codec_config->bit_depth())
@ -511,7 +511,7 @@ bool VP9Parser::Parse(const uint8_t* data,
// specification of either the color format or color sub-sampling in // specification of either the color format or color sub-sampling in
// profile 0. VP9 specifies that the default color format should be // profile 0. VP9 specifies that the default color format should be
// YUV 4:2:0 in this case (normative). // YUV 4:2:0 in this case (normative).
writable_codec_config()->set_chroma_subsampling( writable_codec_config()->SetChromaSubsampling(
VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA); VPCodecConfigurationRecord::CHROMA_420_COLLOCATED_WITH_LUMA);
writable_codec_config()->set_bit_depth(8); writable_codec_config()->set_bit_depth(8);
} }

View File

@ -112,6 +112,7 @@ bool VPCodecConfigurationRecord::ParseMP4(const std::vector<uint8_t>& data) {
return true; return true;
} }
// http://wiki.webmproject.org/vp9-codecprivate
bool VPCodecConfigurationRecord::ParseWebM(const std::vector<uint8_t>& data) { bool VPCodecConfigurationRecord::ParseWebM(const std::vector<uint8_t>& data) {
BufferReader reader(data.data(), data.size()); BufferReader reader(data.data(), data.size());
@ -246,6 +247,70 @@ void VPCodecConfigurationRecord::MergeFrom(
} }
codec_initialization_data_ = other.codec_initialization_data_; 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 } // namespace media

View File

@ -121,6 +121,34 @@ enum AVColorSpace {
AVCOL_SPC_NB 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 for parsing or writing VP codec configuration record.
class VPCodecConfigurationRecord { class VPCodecConfigurationRecord {
public: public:
@ -131,6 +159,12 @@ class VPCodecConfigurationRecord {
CHROMA_444 = 3, CHROMA_444 = 3,
CHROMA_440 = 4, CHROMA_440 = 4,
}; };
enum ChromaSitingValues {
kUnspecified = 0,
kLeftCollocated = 1,
kTopCollocated = kLeftCollocated,
kHalf = 2,
};
VPCodecConfigurationRecord(); VPCodecConfigurationRecord();
VPCodecConfigurationRecord( VPCodecConfigurationRecord(
@ -164,16 +198,17 @@ class VPCodecConfigurationRecord {
/// @return The codec string. /// @return The codec string.
std::string GetCodecString(Codec codec) const; std::string GetCodecString(Codec codec) const;
// Merges the values from the given configuration. If there are values in /// Merges the values from the given configuration. If there are values in
// both |*this| and |other|, the values in |other| take precedence. /// both |*this| and |other|, |*this| is not updated.
void MergeFrom(const VPCodecConfigurationRecord& other); 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_profile(uint8_t profile) { profile_ = profile; }
void set_level(uint8_t level) { level_ = level; } void set_level(uint8_t level) { level_ = level; }
void set_bit_depth(uint8_t bit_depth) { bit_depth_ = bit_depth; } 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) { void set_video_full_range_flag(bool video_full_range_flag) {
video_full_range_flag_ = video_full_range_flag; video_full_range_flag_ = video_full_range_flag;
} }
@ -187,6 +222,28 @@ class VPCodecConfigurationRecord {
matrix_coefficients_ = matrix_coefficients; 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 profile() const { return profile_.value_or(0); }
uint8_t level() const { return level_.value_or(10); } uint8_t level() const { return level_.value_or(10); }
uint8_t bit_depth() const { return bit_depth_.value_or(8); } uint8_t bit_depth() const { return bit_depth_.value_or(8); }
@ -205,8 +262,13 @@ class VPCodecConfigurationRecord {
uint8_t matrix_coefficients() const { uint8_t matrix_coefficients() const {
return matrix_coefficients_.value_or(AVCOL_SPC_UNSPECIFIED); return matrix_coefficients_.value_or(AVCOL_SPC_UNSPECIFIED);
} }
uint8_t chroma_location() const {
return chroma_location_ ? *chroma_location_ : AVCHROMA_LOC_UNSPECIFIED;
}
private: private:
void UpdateChromaSubsamplingIfNeeded();
base::Optional<uint8_t> profile_; base::Optional<uint8_t> profile_;
base::Optional<uint8_t> level_; base::Optional<uint8_t> level_;
base::Optional<uint8_t> bit_depth_; base::Optional<uint8_t> bit_depth_;
@ -217,6 +279,9 @@ class VPCodecConfigurationRecord {
base::Optional<uint8_t> matrix_coefficients_; base::Optional<uint8_t> matrix_coefficients_;
std::vector<uint8_t> codec_initialization_data_; 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 // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
// generated copy constructor and assignment operator. Since the internal data // generated copy constructor and assignment operator. Since the internal data
// is small, the performance impact is minimal. // is small, the performance impact is minimal.

View File

@ -76,5 +76,90 @@ TEST(VPCodecConfigurationRecordTest, WriteWebM) {
data); 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 media
} // namespace shaka } // namespace shaka

View File

@ -255,11 +255,26 @@ Status Segmenter::InitializeVideoTrack(const VideoStreamInfo* info,
"Unable to parse VP9 codec configuration"); "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; std::vector<uint8_t> codec_config;
vp_config.WriteWebM(&codec_config); vp_config.WriteWebM(&codec_config);
if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) { if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
return Status(error::INTERNAL_ERROR, return Status(error::INTERNAL_ERROR,
"Private codec data required for VP9 streams"); "Private codec data required for VPx streams");
} }
} else { } else {
LOG(ERROR) << "Only VP8 and VP9 video codecs are supported."; LOG(ERROR) << "Only VP8 and VP9 video codecs are supported.";

View File

@ -30,6 +30,7 @@ WebMClusterParser::WebMClusterParser(
int64_t timecode_scale, int64_t timecode_scale,
std::shared_ptr<AudioStreamInfo> audio_stream_info, std::shared_ptr<AudioStreamInfo> audio_stream_info,
std::shared_ptr<VideoStreamInfo> video_stream_info, std::shared_ptr<VideoStreamInfo> video_stream_info,
const VPCodecConfigurationRecord& vp_config,
int64_t audio_default_duration, int64_t audio_default_duration,
int64_t video_default_duration, int64_t video_default_duration,
const WebMTracksParser::TextTracks& text_tracks, const WebMTracksParser::TextTracks& text_tracks,
@ -39,9 +40,11 @@ WebMClusterParser::WebMClusterParser(
const MediaParser::NewSampleCB& new_sample_cb, const MediaParser::NewSampleCB& new_sample_cb,
const MediaParser::InitCB& init_cb, const MediaParser::InitCB& init_cb,
KeySource* decryption_key_source) KeySource* decryption_key_source)
: timecode_multiplier_(timecode_scale / 1000.0), : timecode_multiplier_(timecode_scale /
static_cast<double>(kMicrosecondsPerMillisecond)),
audio_stream_info_(audio_stream_info), audio_stream_info_(audio_stream_info),
video_stream_info_(video_stream_info), video_stream_info_(video_stream_info),
vp_config_(vp_config),
ignored_tracks_(ignored_tracks), ignored_tracks_(ignored_tracks),
audio_encryption_key_id_(audio_encryption_key_id), audio_encryption_key_id_(audio_encryption_key_id),
video_encryption_key_id_(video_encryption_key_id), video_encryption_key_id_(video_encryption_key_id),
@ -441,15 +444,11 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
return false; return false;
} }
VPCodecConfigurationRecord codec_config; vp_config_.MergeFrom(vpx_parser->codec_config());
if (!video_stream_info_->codec_config().empty())
codec_config.ParseWebM(video_stream_info_->codec_config());
codec_config.MergeFrom(vpx_parser->codec_config());
video_stream_info_->set_codec_string( video_stream_info_->set_codec_string(
codec_config.GetCodecString(video_stream_info_->codec())); vp_config_.GetCodecString(video_stream_info_->codec()));
std::vector<uint8_t> config_serialized; std::vector<uint8_t> config_serialized;
codec_config.WriteMP4(&config_serialized); vp_config_.WriteMP4(&config_serialized);
video_stream_info_->set_codec_config(config_serialized); video_stream_info_->set_codec_config(config_serialized);
streams.push_back(video_stream_info_); streams.push_back(video_stream_info_);
init_cb_.Run(streams); init_cb_.Run(streams);

View File

@ -101,6 +101,8 @@ class WebMClusterParser : public WebMParserClient {
/// be NULL if there are no audio tracks available. /// be NULL if there are no audio tracks available.
/// @param video_stream_info references video stream information. It will /// @param video_stream_info references video stream information. It will
/// be NULL if there are no video tracks available. /// be NULL if there are no video tracks available.
/// @param vp_config references vp configuration record. Only useful for
/// video.
/// @param audio_default_duration indicates default duration for audio /// @param audio_default_duration indicates default duration for audio
/// samples. /// samples.
/// @param video_default_duration indicates default duration for video /// @param video_default_duration indicates default duration for video
@ -120,6 +122,7 @@ class WebMClusterParser : public WebMParserClient {
WebMClusterParser(int64_t timecode_scale, WebMClusterParser(int64_t timecode_scale,
std::shared_ptr<AudioStreamInfo> audio_stream_info, std::shared_ptr<AudioStreamInfo> audio_stream_info,
std::shared_ptr<VideoStreamInfo> video_stream_info, std::shared_ptr<VideoStreamInfo> video_stream_info,
const VPCodecConfigurationRecord& vp_config,
int64_t audio_default_duration, int64_t audio_default_duration,
int64_t video_default_duration, int64_t video_default_duration,
const WebMTracksParser::TextTracks& text_tracks, const WebMTracksParser::TextTracks& text_tracks,
@ -188,6 +191,7 @@ class WebMClusterParser : public WebMParserClient {
std::shared_ptr<AudioStreamInfo> audio_stream_info_; std::shared_ptr<AudioStreamInfo> audio_stream_info_;
std::shared_ptr<VideoStreamInfo> video_stream_info_; std::shared_ptr<VideoStreamInfo> video_stream_info_;
VPCodecConfigurationRecord vp_config_;
std::set<int64_t> ignored_tracks_; std::set<int64_t> ignored_tracks_;
std::unique_ptr<DecryptorSource> decryptor_source_; std::unique_ptr<DecryptorSource> decryptor_source_;

View File

@ -407,8 +407,9 @@ class WebMClusterParserTest : public testing::Test {
video_stream_info_->set_codec(video_codec); video_stream_info_->set_codec(video_codec);
return new WebMClusterParser( return new WebMClusterParser(
kTimecodeScale, audio_stream_info_, video_stream_info_, kTimecodeScale, audio_stream_info_, video_stream_info_,
audio_default_duration, video_default_duration, text_tracks, VPCodecConfigurationRecord(), audio_default_duration,
ignored_tracks, audio_encryption_key_id, video_encryption_key_id, video_default_duration, text_tracks, ignored_tracks,
audio_encryption_key_id, video_encryption_key_id,
base::Bind(&WebMClusterParserTest::NewSampleEvent, base::Bind(&WebMClusterParserTest::NewSampleEvent,
base::Unretained(this)), base::Unretained(this)),
init_cb, &mock_key_source_); init_cb, &mock_key_source_);

View File

@ -63,6 +63,21 @@ const int kWebMIdCodecID = 0x86;
const int kWebMIdCodecName = 0x258688; const int kWebMIdCodecName = 0x258688;
const int kWebMIdCodecPrivate = 0x63A2; const int kWebMIdCodecPrivate = 0x63A2;
const int kWebMIdCodecState = 0xA4; const int kWebMIdCodecState = 0xA4;
const int kWebMIdColor = 0x55B0;
const int kWebMIdColorMatrixCoefficients = 0x55B1;
const int kWebMIdColorBitsPerChannel = 0x55B2;
const int kWebMIdColorChromaSubsamplingHorz = 0x55B3;
const int kWebMIdColorChromaSubsamplingVert = 0x55B4;
const int kWebMIdColorCbSamplingHorz = 0x55B5;
const int kWebMIdColorCbSamplingVert = 0x55B6;
const int kWebMIdColorChromaSitingHorz = 0x55B7;
const int kWebMIdColorChromaSitingVert = 0x55B8;
const int kWebMIdColorRange = 0x55B9;
const int kWebMIdColorTransferCharacteristics = 0x55BA;
const int kWebMIdColorPrimaries = 0x55BB;
const int kWebMIdColorMaxCLL = 0x55BC;
const int kWebMIdColorMaxFALL = 0x55BD;
const int kWebMIdColorMasteringMetadata = 0x55D0;
const int kWebMIdColorSpace = 0x2EB524; const int kWebMIdColorSpace = 0x2EB524;
const int kWebMIdContentCompAlgo = 0x4254; const int kWebMIdContentCompAlgo = 0x4254;
const int kWebMIdContentCompression = 0x5034; const int kWebMIdContentCompression = 0x5034;

View File

@ -207,6 +207,7 @@ int WebMMediaParser::ParseInfoAndTracks(const uint8_t* data, int size) {
cluster_parser_.reset(new WebMClusterParser( cluster_parser_.reset(new WebMClusterParser(
info_parser.timecode_scale(), audio_stream_info, video_stream_info, info_parser.timecode_scale(), audio_stream_info, video_stream_info,
tracks_parser.vp_config(),
tracks_parser.GetAudioDefaultDuration(timecode_scale_in_us), tracks_parser.GetAudioDefaultDuration(timecode_scale_in_us),
tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us), tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us),
tracks_parser.text_tracks(), tracks_parser.ignored_tracks(), tracks_parser.text_tracks(), tracks_parser.ignored_tracks(),

View File

@ -197,6 +197,24 @@ static const ElementIdInfo kVideoIds[] = {
{UINT, kWebMIdAspectRatioType}, {UINT, kWebMIdAspectRatioType},
{BINARY, kWebMIdColorSpace}, {BINARY, kWebMIdColorSpace},
{FLOAT, kWebMIdFrameRate}, {FLOAT, kWebMIdFrameRate},
{LIST, kWebMIdColor},
};
static const ElementIdInfo kColorIds[] = {
{UINT, kWebMIdColorMatrixCoefficients},
{UINT, kWebMIdColorBitsPerChannel},
{UINT, kWebMIdColorChromaSubsamplingHorz},
{UINT, kWebMIdColorChromaSubsamplingVert},
{UINT, kWebMIdColorCbSamplingHorz},
{UINT, kWebMIdColorCbSamplingVert},
{UINT, kWebMIdColorChromaSitingHorz},
{UINT, kWebMIdColorChromaSitingVert},
{UINT, kWebMIdColorRange},
{UINT, kWebMIdColorTransferCharacteristics},
{UINT, kWebMIdColorPrimaries},
{UINT, kWebMIdColorMaxCLL},
{UINT, kWebMIdColorMaxFALL},
{LIST, kWebMIdColorMasteringMetadata},
}; };
static const ElementIdInfo kAudioIds[] = { static const ElementIdInfo kAudioIds[] = {
@ -382,6 +400,7 @@ static const ListElementInfo kListElementInfo[] = {
LIST_ELEMENT_INFO(kWebMIdTrackEntry, 2, kTrackEntryIds), LIST_ELEMENT_INFO(kWebMIdTrackEntry, 2, kTrackEntryIds),
LIST_ELEMENT_INFO(kWebMIdTrackTranslate, 3, kTrackTranslateIds), LIST_ELEMENT_INFO(kWebMIdTrackTranslate, 3, kTrackTranslateIds),
LIST_ELEMENT_INFO(kWebMIdVideo, 3, kVideoIds), LIST_ELEMENT_INFO(kWebMIdVideo, 3, kVideoIds),
LIST_ELEMENT_INFO(kWebMIdColor, 4, kColorIds),
LIST_ELEMENT_INFO(kWebMIdAudio, 3, kAudioIds), LIST_ELEMENT_INFO(kWebMIdAudio, 3, kAudioIds),
LIST_ELEMENT_INFO(kWebMIdTrackOperation, 3, kTrackOperationIds), LIST_ELEMENT_INFO(kWebMIdTrackOperation, 3, kTrackOperationIds),
LIST_ELEMENT_INFO(kWebMIdTrackCombinePlanes, 4, kTrackCombinePlanesIds), LIST_ELEMENT_INFO(kWebMIdTrackCombinePlanes, 4, kTrackCombinePlanesIds),

View File

@ -224,9 +224,9 @@ bool WebMTracksParser::OnListEnd(int id) {
video_default_duration_ = default_duration_; video_default_duration_ = default_duration_;
DCHECK(!video_stream_info_); DCHECK(!video_stream_info_);
vp_config_ = video_client_.GetVpCodecConfig(codec_private_);
video_stream_info_ = video_client_.GetVideoStreamInfo( video_stream_info_ = video_client_.GetVideoStreamInfo(
video_track_num_, codec_id_, codec_private_, video_track_num_, codec_id_, !video_encryption_key_id_.empty());
!video_encryption_key_id_.empty());
if (!video_stream_info_) if (!video_stream_info_)
return false; return false;
} else { } else {

View File

@ -69,6 +69,8 @@ class WebMTracksParser : public WebMParserClient {
return text_tracks_; return text_tracks_;
} }
const VPCodecConfigurationRecord& vp_config() const { return vp_config_; }
private: private:
// WebMParserClient implementation. // WebMParserClient implementation.
WebMParserClient* OnListStart(int id) override; WebMParserClient* OnListStart(int id) override;
@ -103,6 +105,7 @@ class WebMTracksParser : public WebMParserClient {
std::shared_ptr<AudioStreamInfo> audio_stream_info_; std::shared_ptr<AudioStreamInfo> audio_stream_info_;
WebMVideoClient video_client_; WebMVideoClient video_client_;
VPCodecConfigurationRecord vp_config_;
std::shared_ptr<VideoStreamInfo> video_stream_info_; std::shared_ptr<VideoStreamInfo> video_stream_info_;
DISALLOW_COPY_AND_ASSIGN(WebMTracksParser); DISALLOW_COPY_AND_ASSIGN(WebMTracksParser);

View File

@ -45,12 +45,17 @@ void WebMVideoClient::Reset() {
display_height_ = -1; display_height_ = -1;
display_unit_ = -1; display_unit_ = -1;
alpha_mode_ = -1; alpha_mode_ = -1;
vp_config_ = VPCodecConfigurationRecord();
chroma_subsampling_horz_ = -1;
chroma_subsampling_vert_ = -1;
chroma_siting_horz_ = -1;
chroma_siting_vert_ = -1;
} }
std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo( std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
int64_t track_num, int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private,
bool is_encrypted) { bool is_encrypted) {
Codec video_codec = kUnknownCodec; Codec video_codec = kUnknownCodec;
if (codec_id == "V_VP8") { if (codec_id == "V_VP8") {
@ -110,12 +115,33 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
return std::make_shared<VideoStreamInfo>( return std::make_shared<VideoStreamInfo>(
track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified, track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified,
std::string(), codec_private.data(), codec_private.size(), std::string(), nullptr, 0, width_after_crop, height_after_crop, sar_x,
width_after_crop, height_after_crop, sar_x, sar_y, 0, 0, std::string(), sar_y, 0, 0, std::string(), is_encrypted);
is_encrypted); }
const VPCodecConfigurationRecord& WebMVideoClient::GetVpCodecConfig(
const std::vector<uint8_t>& codec_private) {
vp_config_.ParseWebM(codec_private);
if (chroma_subsampling_horz_ != -1 && chroma_subsampling_vert_ != -1) {
vp_config_.SetChromaSubsampling(chroma_subsampling_horz_,
chroma_subsampling_vert_);
}
if (chroma_siting_horz_ != -1 && chroma_siting_vert_ != -1) {
vp_config_.SetChromaLocation(chroma_siting_horz_, chroma_siting_vert_);
}
return vp_config_;
}
WebMParserClient* WebMVideoClient::OnListStart(int id) {
return id == kWebMIdColor ? this : WebMParserClient::OnListStart(id);
}
bool WebMVideoClient::OnListEnd(int id) {
return id == kWebMIdColor ? true : WebMParserClient::OnListEnd(id);
} }
bool WebMVideoClient::OnUInt(int id, int64_t val) { bool WebMVideoClient::OnUInt(int id, int64_t val) {
VPCodecConfigurationRecord vp_config;
int64_t* dst = NULL; int64_t* dst = NULL;
switch (id) { switch (id) {
@ -149,6 +175,41 @@ bool WebMVideoClient::OnUInt(int id, int64_t val) {
case kWebMIdAlphaMode: case kWebMIdAlphaMode:
dst = &alpha_mode_; dst = &alpha_mode_;
break; break;
case kWebMIdColorMatrixCoefficients:
vp_config.set_matrix_coefficients(static_cast<uint8_t>(val));
break;
case kWebMIdColorBitsPerChannel:
vp_config.set_bit_depth(static_cast<uint8_t>(val));
break;
case kWebMIdColorChromaSubsamplingHorz:
dst = &chroma_subsampling_horz_;
break;
case kWebMIdColorChromaSubsamplingVert:
dst = &chroma_subsampling_vert_;
break;
case kWebMIdColorChromaSitingHorz:
dst = &chroma_siting_horz_;
break;
case kWebMIdColorChromaSitingVert:
dst = &chroma_siting_vert_;
break;
case kWebMIdColorRange:
if (val == 0)
vp_config.set_video_full_range_flag(false);
else if (val == 1)
vp_config.set_video_full_range_flag(true);
// Ignore for other values of val.
break;
case kWebMIdColorTransferCharacteristics:
vp_config.set_transfer_characteristics(static_cast<uint8_t>(val));
break;
case kWebMIdColorPrimaries:
vp_config.set_color_primaries(static_cast<uint8_t>(val));
break;
case kWebMIdColorMaxCLL:
case kWebMIdColorMaxFALL:
NOTIMPLEMENTED() << "HDR is not supported yet.";
return true;
default: default:
return true; return true;
} }

View File

@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "packager/media/base/video_stream_info.h" #include "packager/media/base/video_stream_info.h"
#include "packager/media/codecs/vp_codec_configuration_record.h"
#include "packager/media/formats/webm/webm_parser.h" #include "packager/media/formats/webm/webm_parser.h"
namespace shaka { namespace shaka {
@ -26,19 +27,25 @@ class WebMVideoClient : public WebMParserClient {
void Reset(); void Reset();
/// Create a VideoStreamInfo with the data in |track_num|, |codec_id|, /// Create a VideoStreamInfo with the data in |track_num|, |codec_id|,
/// |codec_private|, |is_encrypted| and the fields parsed from the last video /// |is_encrypted| and the fields parsed from the last video track element
/// track element this object was used to parse. /// this object was used to parse.
/// @return A VideoStreamInfo if successful. /// @return A VideoStreamInfo if successful.
/// @return An empty pointer if there was unexpected values in the /// @return An empty pointer if there was unexpected values in the
/// provided parameters or video track element fields. /// provided parameters or video track element fields.
std::shared_ptr<VideoStreamInfo> GetVideoStreamInfo( std::shared_ptr<VideoStreamInfo> GetVideoStreamInfo(
int64_t track_num, int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private,
bool is_encrypted); bool is_encrypted);
/// Extracts VPCodecConfigurationRecord parsed from codec private data and
/// Colour element.
const VPCodecConfigurationRecord& GetVpCodecConfig(
const std::vector<uint8_t>& codec_private);
private: private:
// WebMParserClient implementation. // WebMParserClient implementation.
WebMParserClient* OnListStart(int id) override;
bool OnListEnd(int id) override;
bool OnUInt(int id, int64_t val) override; bool OnUInt(int id, int64_t val) override;
bool OnBinary(int id, const uint8_t* data, int size) override; bool OnBinary(int id, const uint8_t* data, int size) override;
bool OnFloat(int id, double val) override; bool OnFloat(int id, double val) override;
@ -54,6 +61,12 @@ class WebMVideoClient : public WebMParserClient {
int64_t display_unit_; int64_t display_unit_;
int64_t alpha_mode_; int64_t alpha_mode_;
VPCodecConfigurationRecord vp_config_;
int64_t chroma_subsampling_horz_;
int64_t chroma_subsampling_vert_;
int64_t chroma_siting_horz_;
int64_t chroma_siting_vert_;
DISALLOW_COPY_AND_ASSIGN(WebMVideoClient); DISALLOW_COPY_AND_ASSIGN(WebMVideoClient);
}; };

View File

@ -4,6 +4,12 @@
bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240. bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240.
bear-320x240-vp9-opus.webm - Same as above, but with vp9 and opus codec. bear-320x240-vp9-opus.webm - Same as above, but with vp9 and opus codec.
bear-320x240-vp9-altref.webm - Same as above, but enabled altref:
ffmpeg -i bear-320x180.mp4 -c:v libvpx-vp9 -pass 1 -threads 8 -speed 4 \
-frame-parallel 1 -an -f webm /dev/null
ffmpeg -i bear-320x180.mp4 -c:v libvpx-vp9 -pass 2 -threads 8 -speed 1 \
-frame-parallel 1 -auto-alt-ref 1 -lag-in-frames 25 -an -f webm \
bear-320x240-vp9-altref.webm
no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks
like there are no tracks. like there are no tracks.
nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file

Binary file not shown.