diff --git a/common/hdr_util.cc b/common/hdr_util.cc index f0d121e..2ef90d9 100644 --- a/common/hdr_util.cc +++ b/common/hdr_util.cc @@ -7,12 +7,15 @@ // be found in the AUTHORS file in the root of the source tree. #include "hdr_util.h" +#include #include #include #include "mkvparser/mkvparser.h" namespace libwebm { +const int Vp9CodecFeatures::kValueNotPresent = INT_MAX; + bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc, PrimaryChromaticityPtr* muxer_pc) { muxer_pc->reset(new (std::nothrow) @@ -135,19 +138,23 @@ bool CopyColour(const mkvparser::Colour& parser_colour, // See the following link for more information: // http://www.webmproject.org/vp9/profiles/ bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, - int* profile, int* level) { + Vp9CodecFeatures* features) { const int kVpxCodecPrivateMinLength = 3; - if (!private_data || !profile || !level || length < kVpxCodecPrivateMinLength) + if (!private_data || !features || length < kVpxCodecPrivateMinLength) return false; const uint8_t kVp9ProfileId = 1; const uint8_t kVp9LevelId = 2; + const uint8_t kVp9BitDepthId = 3; + const uint8_t kVp9ChromaSubsamplingId = 4; const int kVpxFeatureLength = 1; int offset = 0; - // Set profile and level to not set. - *profile = -1; - *level = -1; + // Set features to not set. + features->profile = Vp9CodecFeatures::kValueNotPresent; + features->level = Vp9CodecFeatures::kValueNotPresent; + features->bit_depth = Vp9CodecFeatures::kValueNotPresent; + features->chroma_subsampling = Vp9CodecFeatures::kValueNotPresent; do { const uint8_t id_byte = private_data[offset++]; const uint8_t length_byte = private_data[offset++]; @@ -157,9 +164,11 @@ bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, const int priv_profile = static_cast(private_data[offset++]); if (priv_profile < 0 || priv_profile > 3) return false; - if (*profile != -1 && *profile != priv_profile) + if (features->profile != Vp9CodecFeatures::kValueNotPresent && + features->profile != priv_profile) { return false; - *profile = priv_profile; + } + features->profile = priv_profile; } else if (id_byte == kVp9LevelId) { const int priv_level = static_cast(private_data[offset++]); @@ -169,14 +178,34 @@ bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, for (int i = 0; i < kNumLevels; ++i) { if (priv_level == levels[i]) { - if (*level != -1 && *level != priv_level) + if (features->level != Vp9CodecFeatures::kValueNotPresent && + features->level != priv_level) { return false; - *level = priv_level; + } + features->level = priv_level; break; } } - if (*level == -1) + if (features->level == Vp9CodecFeatures::kValueNotPresent) return false; + } else if (id_byte == kVp9BitDepthId) { + const int priv_profile = static_cast(private_data[offset++]); + if (priv_profile != 8 && priv_profile != 10 && priv_profile != 12) + return false; + if (features->bit_depth != Vp9CodecFeatures::kValueNotPresent && + features->bit_depth != priv_profile) { + return false; + } + features->bit_depth = priv_profile; + } else if (id_byte == kVp9ChromaSubsamplingId) { + const int priv_profile = static_cast(private_data[offset++]); + if (priv_profile != 0 && priv_profile != 2 && priv_profile != 3) + return false; + if (features->chroma_subsampling != Vp9CodecFeatures::kValueNotPresent && + features->chroma_subsampling != priv_profile) { + return false; + } + features->chroma_subsampling = priv_profile; } else { // Invalid ID. return false; diff --git a/common/hdr_util.h b/common/hdr_util.h index aff00b0..8f3b8c0 100644 --- a/common/hdr_util.h +++ b/common/hdr_util.h @@ -28,6 +28,25 @@ namespace libwebm { // TODO(tomfinegan): These should be moved to libwebm_utils once c++11 is // required by libwebm. +// Features of the VP9 codec that may be set in the CodecPrivate of a VP9 video +// stream. A value of kValueNotPresent represents that the value was not set in +// the CodecPrivate. +struct Vp9CodecFeatures { + static const int kValueNotPresent; + + Vp9CodecFeatures() + : profile(kValueNotPresent), + level(kValueNotPresent), + bit_depth(kValueNotPresent), + chroma_subsampling(kValueNotPresent) {} + ~Vp9CodecFeatures(){}; + + int profile; + int level; + int bit_depth; + int chroma_subsampling; +}; + typedef std::auto_ptr PrimaryChromaticityPtr; bool CopyPrimaryChromaticity(const mkvparser::PrimaryChromaticity& parser_pc, @@ -43,12 +62,9 @@ bool ColourValuePresent(long long value); bool CopyColour(const mkvparser::Colour& parser_colour, mkvmuxer::Colour* muxer_colour); -// Returns true if |profile| and |level| are set to valid values. Returns VP9 -// profile in |profile| or -1 if there was no profile set in CodecPrivate data. -// Returns VP9 level in |level| or -1 if there was no level set in CodecPrivate -// data. +// Returns true if |features| is set to one or more valid values. bool ParseVpxCodecPrivate(const uint8_t* private_data, int32_t length, - int* profile, int* level); + Vp9CodecFeatures* features); } // namespace libwebm diff --git a/testing/parser_tests.cc b/testing/parser_tests.cc index 51608e5..1d92798 100644 --- a/testing/parser_tests.cc +++ b/testing/parser_tests.cc @@ -666,67 +666,99 @@ TEST_F(ParserTest, StereoModeParsedCorrectly) { TEST_F(ParserTest, Vp9CodecLevelTest) { const int kCodecPrivateLength = 3; const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11}; - int profile; - int level; - EXPECT_EQ(true, libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], - kCodecPrivateLength, &profile, - &level)); - EXPECT_EQ(-1, profile); - EXPECT_EQ(11, level); + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(11, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); } TEST_F(ParserTest, Vp9CodecProfileTest) { const int kCodecPrivateLength = 3; const uint8_t good_codec_private_profile[kCodecPrivateLength] = {1, 1, 1}; - int profile; - int level; - EXPECT_EQ(true, libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], - kCodecPrivateLength, &profile, - &level)); - EXPECT_EQ(1, profile); - EXPECT_EQ(-1, level); + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecBitDepthTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_profile[kCodecPrivateLength] = {3, 1, 8}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(8, features.bit_depth); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, + features.chroma_subsampling); +} + +TEST_F(ParserTest, Vp9CodecChromaSubsamplingTest) { + const int kCodecPrivateLength = 3; + const uint8_t good_codec_private_profile[kCodecPrivateLength] = {4, 1, 0}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level); + EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth); + EXPECT_EQ(0, features.chroma_subsampling); } TEST_F(ParserTest, Vp9CodecProfileLevelTest) { const int kCodecPrivateLength = 6; const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11}; - int profile; - int level; - EXPECT_EQ(true, - libwebm::ParseVpxCodecPrivate( - &codec_private[0], kCodecPrivateLength, &profile, &level)); - EXPECT_EQ(1, profile); - EXPECT_EQ(11, level); + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(11, features.level); +} + +TEST_F(ParserTest, Vp9CodecAllTest) { + const int kCodecPrivateLength = 12; + const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11, + 3, 1, 8, 4, 1, 0}; + libwebm::Vp9CodecFeatures features; + EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0], + kCodecPrivateLength, &features)); + EXPECT_EQ(1, features.profile); + EXPECT_EQ(11, features.level); + EXPECT_EQ(8, features.bit_depth); + EXPECT_EQ(0, features.chroma_subsampling); } TEST_F(ParserTest, Vp9CodecPrivateBadTest) { const int kCodecPrivateLength = 3; - int profile; - int level; - + libwebm::Vp9CodecFeatures features; // Test invalid codec private data; all of these should return false. const uint8_t bad_codec_private[kCodecPrivateLength] = {0, 0, 0}; - EXPECT_EQ(false, libwebm::ParseVpxCodecPrivate(NULL, kCodecPrivateLength, - &profile, &level)); - EXPECT_EQ(false, libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, - &profile, &level)); - EXPECT_EQ(false, - libwebm::ParseVpxCodecPrivate( - &bad_codec_private[0], kCodecPrivateLength, &profile, &level)); + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(NULL, kCodecPrivateLength, &features)); + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], + kCodecPrivateLength, &features)); const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11}; // Test parse of codec private chunks, but lie about length. - EXPECT_EQ(false, libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, - &profile, &level)); - EXPECT_EQ(false, libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], - 0, &profile, &level)); - EXPECT_EQ(false, - libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], - kCodecPrivateLength, NULL, &level)); - EXPECT_EQ(false, - libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], - kCodecPrivateLength, &profile, NULL)); + EXPECT_FALSE( + libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], 0, + &features)); + EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], + kCodecPrivateLength, NULL)); } + } // namespace test int main(int argc, char* argv[]) { diff --git a/webm_info.cc b/webm_info.cc index ddeb83e..20df398 100644 --- a/webm_info.cc +++ b/webm_info.cc @@ -35,7 +35,7 @@ using libwebm::Indent; using libwebm::kNanosecondsPerSecond; using libwebm::kNanosecondsPerSecondi; -const char VERSION_STRING[] = "1.0.4.0"; +const char VERSION_STRING[] = "1.0.4.1"; struct Options { Options(); @@ -379,21 +379,25 @@ bool OutputTracks(const mkvparser::Segment& segment, const Options& options, const std::string codec_id = track->GetCodecId(); const std::string v_vp9 = "V_VP9"; if (codec_id == v_vp9) { - int profile; - int level; - + libwebm::Vp9CodecFeatures features; if (!libwebm::ParseVpxCodecPrivate(private_data, static_cast(private_size), - &profile, &level)) { + &features)) { fprintf(stderr, "Error parsing VpxCodecPrivate.\n"); return false; } - if (profile != -1) - fprintf(o, "%sVP9 profile : %d\n", indent->indent_str().c_str(), - profile); - if (level != -1) - fprintf(o, "%sVP9 level : %d\n", indent->indent_str().c_str(), - level); + if (features.profile != -1) + fprintf(o, "%sVP9 profile : %d\n", + indent->indent_str().c_str(), features.profile); + if (features.level != -1) + fprintf(o, "%sVP9 level : %d\n", + indent->indent_str().c_str(), features.level); + if (features.bit_depth != -1) + fprintf(o, "%sVP9 bit_depth : %d\n", + indent->indent_str().c_str(), features.bit_depth); + if (features.chroma_subsampling != -1) + fprintf(o, "%sVP9 chroma subsampling : %d\n", + indent->indent_str().c_str(), features.chroma_subsampling); } } }