From 8fc7f51d81d62054f035240048f786886c9993e1 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Mon, 26 Oct 2015 17:52:57 -0700 Subject: [PATCH] Move AVCDecoderConfiguration parsing to media/filters Change-Id: I7daa616e53bdef2206ce145f907b8f55cde87740 --- packager/media/base/video_stream_info.cc | 20 --- packager/media/base/video_stream_info.h | 10 -- .../media/event/muxer_listener_test_helper.cc | 7 +- .../filters/avc_decoder_configuration.cc | 77 ++++++++++++ .../media/filters/avc_decoder_configuration.h | 67 ++++++++++ .../avc_decoder_configuration_unittest.cc | 54 ++++++++ packager/media/filters/filters.gyp | 3 + packager/media/filters/h264_parser.cc | 58 --------- packager/media/filters/h264_parser.h | 19 --- .../media/filters/h264_parser_unittest.cc | 42 +++---- packager/media/formats/mp2t/es_parser_h264.cc | 8 +- packager/media/formats/mp4/box_definitions.cc | 72 ++++------- packager/media/formats/mp4/box_definitions.h | 36 ++---- .../formats/mp4/box_definitions_comparison.h | 6 +- .../formats/mp4/box_definitions_unittest.cc | 22 ++-- .../media/formats/mp4/mp4_media_parser.cc | 115 +++++++++--------- packager/media/formats/mp4/mp4_muxer.cc | 2 +- .../media/formats/webm/webm_video_client.cc | 12 +- .../media/formats/wvm/wvm_media_parser.cc | 47 +++---- 19 files changed, 354 insertions(+), 323 deletions(-) create mode 100644 packager/media/filters/avc_decoder_configuration.cc create mode 100644 packager/media/filters/avc_decoder_configuration.h create mode 100644 packager/media/filters/avc_decoder_configuration_unittest.cc diff --git a/packager/media/base/video_stream_info.cc b/packager/media/base/video_stream_info.cc index b6dd918da3..25c1ccde73 100644 --- a/packager/media/base/video_stream_info.cc +++ b/packager/media/base/video_stream_info.cc @@ -92,25 +92,5 @@ std::string VideoStreamInfo::ToString() const { nalu_length_size_); } -std::string VideoStreamInfo::GetCodecString(VideoCodec codec, - uint8_t profile, - uint8_t compatible_profiles, - uint8_t level) { - switch (codec) { - case kCodecVP8: - return "vp8"; - case kCodecVP9: - return "vp9"; - case kCodecH264: { - const uint8_t bytes[] = {profile, compatible_profiles, level}; - return "avc1." + - base::StringToLowerASCII(base::HexEncode(bytes, arraysize(bytes))); - } - default: - NOTIMPLEMENTED() << "Unknown Codec: " << codec; - return "unknown"; - } -} - } // namespace media } // namespace edash_packager diff --git a/packager/media/base/video_stream_info.h b/packager/media/base/video_stream_info.h index 821701e92e..948354d5ae 100644 --- a/packager/media/base/video_stream_info.h +++ b/packager/media/base/video_stream_info.h @@ -28,9 +28,6 @@ enum VideoCodec { class VideoStreamInfo : public StreamInfo { public: /// Construct an initialized video stream info object. - /// If @a codec is @a kCodecH264 and either @pixel_width and @pixel_height is - /// 0 (unknown), then this tries to parse @extra_data to extract the pixel - /// width and height from it. /// @param pixel_width is the width of the pixel. 0 if unknown. /// @param pixel_height is the height of the pixels. 0 if unknown. VideoStreamInfo(int track_id, @@ -72,13 +69,6 @@ class VideoStreamInfo : public StreamInfo { void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; } void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; } - /// @param profile,compatible_profiles,level are only used by H.264 codec. - /// @return The codec string. - static std::string GetCodecString(VideoCodec codec, - uint8_t profile, - uint8_t compatible_profiles, - uint8_t level); - private: ~VideoStreamInfo() override; diff --git a/packager/media/event/muxer_listener_test_helper.cc b/packager/media/event/muxer_listener_test_helper.cc index ae5dddaa47..772012efac 100644 --- a/packager/media/event/muxer_listener_test_helper.cc +++ b/packager/media/event/muxer_listener_test_helper.cc @@ -41,9 +41,7 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() { const uint32_t kTimeScale = 10; const uint64_t kVideoStreamDuration = 200; const VideoCodec kH264Codec = kCodecH264; - const uint8_t kH264Profile = 1; - const uint8_t kH264CompatibleProfile = 1; - const uint8_t kH264Level = 1; + const char* kCodecString = "avc1.010101"; const char* kLanuageUndefined = "und"; const uint16_t kWidth = 720; const uint16_t kHeight = 480; @@ -57,8 +55,7 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() { params.time_scale = kTimeScale; params.duration = kVideoStreamDuration; params.codec = kH264Codec; - params.codec_string = VideoStreamInfo::GetCodecString( - kCodecH264, kH264Profile, kH264CompatibleProfile, kH264Level); + params.codec_string = kCodecString; params.language = kLanuageUndefined; params.width = kWidth; params.height = kHeight; diff --git a/packager/media/filters/avc_decoder_configuration.cc b/packager/media/filters/avc_decoder_configuration.cc new file mode 100644 index 0000000000..835c53a304 --- /dev/null +++ b/packager/media/filters/avc_decoder_configuration.cc @@ -0,0 +1,77 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/filters/avc_decoder_configuration.h" + +#include "packager/base/stl_util.h" +#include "packager/base/strings/string_number_conversions.h" +#include "packager/base/strings/string_util.h" +#include "packager/media/base/buffer_reader.h" +#include "packager/media/filters/h264_parser.h" +#include "packager/media/formats/mp4/rcheck.h" + +namespace edash_packager { +namespace media { + +AVCDecoderConfiguration::AVCDecoderConfiguration() + : version_(0), + profile_indication_(0), + profile_compatibility_(0), + avc_level_(0), + length_size_(0) {} + +AVCDecoderConfiguration::~AVCDecoderConfiguration() {} + +bool AVCDecoderConfiguration::Parse(const std::vector& data) { + BufferReader reader(vector_as_array(&data), data.size()); + RCHECK(reader.Read1(&version_) && version_ == 1 && + reader.Read1(&profile_indication_) && + reader.Read1(&profile_compatibility_) && reader.Read1(&avc_level_)); + + uint8_t length_size_minus_one; + RCHECK(reader.Read1(&length_size_minus_one)); + length_size_ = (length_size_minus_one & 0x3) + 1; + + uint8_t num_sps; + RCHECK(reader.Read1(&num_sps)); + num_sps &= 0x1f; + if (num_sps < 1) { + LOG(ERROR) << "No SPS found."; + return false; + } + + uint16_t sps_length = 0; + RCHECK(reader.Read2(&sps_length)); + + H264Parser parser; + int sps_id = 0; + RCHECK(parser.ParseSPSFromArray(reader.data() + reader.pos(), sps_length, + &sps_id) == H264Parser::kOk); + return ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width_, + &coded_height_, &pixel_width_, + &pixel_height_); + // It is unlikely to have more than one SPS in practice. Also there's + // no way to change the {coded,pixel}_{width,height} dynamically from + // VideoStreamInfo. So skip the rest (if there are any). +} + +std::string AVCDecoderConfiguration::GetCodecString() const { + return GetCodecString(profile_indication_, profile_compatibility_, + avc_level_); +} + +std::string AVCDecoderConfiguration::GetCodecString( + uint8_t profile_indication, + uint8_t profile_compatibility, + uint8_t avc_level) { + const uint8_t bytes[] = {profile_indication, profile_compatibility, + avc_level}; + return "avc1." + + base::StringToLowerASCII(base::HexEncode(bytes, arraysize(bytes))); +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/avc_decoder_configuration.h b/packager/media/filters/avc_decoder_configuration.h new file mode 100644 index 0000000000..116f62baac --- /dev/null +++ b/packager/media/filters/avc_decoder_configuration.h @@ -0,0 +1,67 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef MEDIA_FILTERS_AVC_DECODER_CONFIGURATION_H_ +#define MEDIA_FILTERS_AVC_DECODER_CONFIGURATION_H_ + +#include +#include +#include + +#include "packager/base/macros.h" + +namespace edash_packager { +namespace media { + +/// Class for parsing AVC decoder configuration. +class AVCDecoderConfiguration { + public: + AVCDecoderConfiguration(); + ~AVCDecoderConfiguration(); + + /// Parses input to extract AVC decoder configuration data. + /// @return false if there is parsing errors. + bool Parse(const std::vector& data); + + /// @return The codec string. + std::string GetCodecString() const; + + uint8_t version() const { return version_; } + uint8_t profile_indication() const { return profile_indication_; } + uint8_t profile_compatibility() const { return profile_compatibility_; } + uint8_t avc_level() const { return avc_level_; } + uint8_t length_size() const { return length_size_; } + uint32_t coded_width() const { return coded_width_; } + uint32_t coded_height() const { return coded_height_; } + uint32_t pixel_width() const { return pixel_width_; } + uint32_t pixel_height() const { return pixel_height_; } + + /// Static version of GetCodecString. + /// @return The codec string. + static std::string GetCodecString(uint8_t profile_indication, + uint8_t profile_compatibility, + uint8_t avc_level); + + private: + uint8_t version_; + uint8_t profile_indication_; + uint8_t profile_compatibility_; + uint8_t avc_level_; + uint8_t length_size_; + + // Extracted from SPS. + uint32_t coded_width_; + uint32_t coded_height_; + uint32_t pixel_width_; + uint32_t pixel_height_; + + DISALLOW_COPY_AND_ASSIGN(AVCDecoderConfiguration); +}; + +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_FILTERS_AVC_DECODER_CONFIGURATION_H_ diff --git a/packager/media/filters/avc_decoder_configuration_unittest.cc b/packager/media/filters/avc_decoder_configuration_unittest.cc new file mode 100644 index 0000000000..038037e981 --- /dev/null +++ b/packager/media/filters/avc_decoder_configuration_unittest.cc @@ -0,0 +1,54 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/filters/avc_decoder_configuration.h" + +#include + +namespace edash_packager { +namespace media { + +TEST(AVCDecoderConfigurationTest, Success) { + const uint8_t kAvcDecoderConfigurationData[] = { + 0x01, 0x64, 0x00, 0x1E, 0xFF, 0xE1, 0x00, 0x1D, 0x67, 0x64, 0x00, 0x1E, + 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, + 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x0F, 0x16, 0x2D, + 0x96, 0x01, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0}; + + AVCDecoderConfiguration avc_config; + ASSERT_TRUE(avc_config.Parse(std::vector( + kAvcDecoderConfigurationData, + kAvcDecoderConfigurationData + arraysize(kAvcDecoderConfigurationData)))); + + EXPECT_EQ(1u, avc_config.version()); + EXPECT_EQ(0x64, avc_config.profile_indication()); + EXPECT_EQ(0u, avc_config.profile_compatibility()); + EXPECT_EQ(0x1E, avc_config.avc_level()); + EXPECT_EQ(4u, avc_config.length_size()); + EXPECT_EQ(720u, avc_config.coded_width()); + EXPECT_EQ(360u, avc_config.coded_height()); + EXPECT_EQ(8u, avc_config.pixel_width()); + EXPECT_EQ(9u, avc_config.pixel_height()); + + EXPECT_EQ("avc1.64001e", avc_config.GetCodecString()); +} + +TEST(AVCDecoderConfigurationTest, FailOnInsufficientData) { + const uint8_t kAvcDecoderConfigurationData[] = {0x01, 0x64, 0x00, 0x1E}; + + AVCDecoderConfiguration avc_config; + ASSERT_FALSE(avc_config.Parse(std::vector( + kAvcDecoderConfigurationData, + kAvcDecoderConfigurationData + arraysize(kAvcDecoderConfigurationData)))); +} + +TEST(AVCDecoderConfigurationTest, GetCodecString) { + EXPECT_EQ("avc1.123456", + AVCDecoderConfiguration::GetCodecString(0x12, 0x34, 0x56)); +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/filters.gyp b/packager/media/filters/filters.gyp index 0e485f2f35..75127466dc 100644 --- a/packager/media/filters/filters.gyp +++ b/packager/media/filters/filters.gyp @@ -13,6 +13,8 @@ 'target_name': 'filters', 'type': '<(component)', 'sources': [ + 'avc_decoder_configuration.cc', + 'avc_decoder_configuration.h', 'h264_bit_reader.cc', 'h264_bit_reader.h', 'h264_byte_to_unit_stream_converter.cc', @@ -28,6 +30,7 @@ 'target_name': 'filters_unittest', 'type': '<(gtest_target_type)', 'sources': [ + 'avc_decoder_configuration_unittest.cc', 'h264_bit_reader_unittest.cc', 'h264_byte_to_unit_stream_converter_unittest.cc', 'h264_parser_unittest.cc', diff --git a/packager/media/filters/h264_parser.cc b/packager/media/filters/h264_parser.cc index afc91ad9ef..74afe61707 100644 --- a/packager/media/filters/h264_parser.cc +++ b/packager/media/filters/h264_parser.cc @@ -12,62 +12,6 @@ namespace edash_packager { namespace media { -#define RCHECK(x) \ - do { \ - if (!(x)) { \ - LOG(ERROR) << "Failure while parsing AVCDecoderConfig: " << #x; \ - return false; \ - } \ - } while (0) - -bool ExtractResolutionFromDecoderConfig(const uint8_t* avc_decoder_config_data, - size_t avc_decoder_config_data_size, - uint32_t* coded_width, - uint32_t* coded_height, - uint32_t* pixel_width, - uint32_t* pixel_height) { - BufferReader reader(avc_decoder_config_data, avc_decoder_config_data_size); - uint8_t value = 0; - // version check, must be 1. - RCHECK(reader.Read1(&value)); - RCHECK(value == 1); - - // Skip avc profile, profile compatibility, avc level, and length size. - RCHECK(reader.SkipBytes(4)); - - // Reserved and num sps. - RCHECK(reader.Read1(&value)); - - const uint8_t num_sps = value & 0x1F; - if (num_sps < 1) { - LOG(ERROR) << "No SPS found."; - return false; - } - uint16_t sps_length = 0; - RCHECK(reader.Read2(&sps_length)); - - return ExtractResolutionFromSpsData(reader.data() + reader.pos(), sps_length, - coded_width, coded_height, pixel_width, - pixel_height); - // It is unlikely to have more than one SPS in practice. Also there's - // no way to change the {coded,pixel}_{width,height} dynamically from - // VideoStreamInfo. So skip the rest (if there are any). -} - -bool ExtractResolutionFromSpsData(const uint8_t* sps_data, - size_t sps_data_size, - uint32_t* coded_width, - uint32_t* coded_height, - uint32_t* pixel_width, - uint32_t* pixel_height) { - H264Parser parser; - int sps_id; - RCHECK(parser.ParseSPSFromArray(sps_data, sps_data_size, &sps_id) == - H264Parser::kOk); - return ExtractResolutionFromSps(*parser.GetSPS(sps_id), coded_width, - coded_height, pixel_width, pixel_height); -} - // Implemented according to ISO/IEC 14496-10:2005 7.4.2.1 Sequence parameter set // RBSP semantics. bool ExtractResolutionFromSps(const H264SPS& sps, @@ -133,8 +77,6 @@ bool ExtractResolutionFromSps(const H264SPS& sps, return true; } -#undef RCHECK - bool H264SliceHeader::IsPSlice() const { return (slice_type % 5 == kPSlice); } diff --git a/packager/media/filters/h264_parser.h b/packager/media/filters/h264_parser.h index c1a5f874b2..37dbf33dff 100644 --- a/packager/media/filters/h264_parser.h +++ b/packager/media/filters/h264_parser.h @@ -17,28 +17,9 @@ namespace edash_packager { namespace media { -// |avc_decoder_config_data| must be AVCDecoderConfigurationRecord specified -// in ISO/IEC 14496-15. -// On success, |coded_width| and |coded_height| contains coded resolution after -// cropping; |pixel_width:pixel_height| contains pixel aspect ratio, 1:1 is -// assigned if it is not present. -bool ExtractResolutionFromDecoderConfig(const uint8_t* avc_decoder_config_data, - size_t avc_decoder_config_data_size, - uint32_t* coded_width, - uint32_t* coded_height, - uint32_t* pixel_width, - uint32_t* pixel_height); - -// |sps_data| must be a valid SPS specified in ISO/IEC 14496-10. // On success, |coded_width| and |coded_height| contains coded resolution after // cropping; |pixel_width:pixel_height| contains pixel aspect ratio, 1:1 is // assigned if it is not present in SPS. -bool ExtractResolutionFromSpsData(const uint8_t* sps_data, - size_t sps_data_size, - uint32_t* coded_width, - uint32_t* coded_height, - uint32_t* pixel_width, - uint32_t* pixel_height); struct H264SPS; bool ExtractResolutionFromSps(const H264SPS& sps, uint32_t* coded_width, diff --git a/packager/media/filters/h264_parser_unittest.cc b/packager/media/filters/h264_parser_unittest.cc index 236c98949e..c5fb7c55ae 100644 --- a/packager/media/filters/h264_parser_unittest.cc +++ b/packager/media/filters/h264_parser_unittest.cc @@ -65,39 +65,24 @@ TEST(H264ParserTest, StreamFileParsing) { } } -TEST(H264ParserTest, ExtractResolutionFromDecoderConfig) { - const uint8_t kDecoderConfig[] = { - 0x01, 0x64, 0x00, 0x1E, 0xFF, 0xE1, 0x00, 0x1D, 0x67, 0x64, 0x00, 0x1E, - 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, - 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x0F, 0x16, 0x2D, - 0x96, 0x01, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0}; - - uint32_t coded_width = 0; - uint32_t coded_height = 0; - uint32_t pixel_width = 0; - uint32_t pixel_height = 0; - ASSERT_TRUE(ExtractResolutionFromDecoderConfig( - kDecoderConfig, arraysize(kDecoderConfig), &coded_width, &coded_height, - &pixel_width, &pixel_height)); - EXPECT_EQ(720u, coded_width); - EXPECT_EQ(360u, coded_height); - EXPECT_EQ(8u, pixel_width); - EXPECT_EQ(9u, pixel_height); -} - TEST(H264ParserTest, ExtractResolutionFromSpsData) { const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x0F, 0x16, 0x2D, 0x96}; + H264Parser parser; + int sps_id = 0; + ASSERT_EQ(H264Parser::kOk, + parser.ParseSPSFromArray(kSps, arraysize(kSps), &sps_id)); + uint32_t coded_width = 0; uint32_t coded_height = 0; uint32_t pixel_width = 0; uint32_t pixel_height = 0; - ASSERT_TRUE(ExtractResolutionFromSpsData(kSps, arraysize(kSps), &coded_width, - &coded_height, &pixel_width, - &pixel_height)); + ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width, + &coded_height, &pixel_width, + &pixel_height)); EXPECT_EQ(720u, coded_width); EXPECT_EQ(360u, coded_height); EXPECT_EQ(8u, pixel_width); @@ -110,13 +95,18 @@ TEST(H264ParserTest, ExtractResolutionFromSpsDataWithCropping) { 0x9F, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x00, 0xF1, 0x42, 0x99, 0x60}; + H264Parser parser; + int sps_id = 0; + ASSERT_EQ(H264Parser::kOk, + parser.ParseSPSFromArray(kSps, arraysize(kSps), &sps_id)); + uint32_t coded_width = 0; uint32_t coded_height = 0; uint32_t pixel_width = 0; uint32_t pixel_height = 0; - ASSERT_TRUE(ExtractResolutionFromSpsData(kSps, arraysize(kSps), &coded_width, - &coded_height, &pixel_width, - &pixel_height)); + ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width, + &coded_height, &pixel_width, + &pixel_height)); EXPECT_EQ(320u, coded_width); EXPECT_EQ(180u, coded_height); EXPECT_EQ(1u, pixel_width); diff --git a/packager/media/formats/mp2t/es_parser_h264.cc b/packager/media/formats/mp2t/es_parser_h264.cc index eaec7856e4..7a5d651799 100644 --- a/packager/media/formats/mp2t/es_parser_h264.cc +++ b/packager/media/formats/mp2t/es_parser_h264.cc @@ -12,6 +12,7 @@ #include "packager/media/base/offset_byte_queue.h" #include "packager/media/base/timestamp.h" #include "packager/media/base/video_stream_info.h" +#include "packager/media/filters/avc_decoder_configuration.h" #include "packager/media/filters/h264_byte_to_unit_stream_converter.h" #include "packager/media/filters/h264_parser.h" #include "packager/media/formats/mp2t/mp2t_common.h" @@ -361,10 +362,9 @@ bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) { kMpeg2Timescale, kInfiniteDuration, kCodecH264, - VideoStreamInfo::GetCodecString(kCodecH264, - decoder_config_record[1], - decoder_config_record[2], - decoder_config_record[3]), + AVCDecoderConfiguration::GetCodecString(decoder_config_record[1], + decoder_config_record[2], + decoder_config_record[3]), std::string(), coded_width, coded_height, diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 89d41fe6da..c87ec81e41 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -35,6 +35,8 @@ const uint32_t kVideoResolution = 0x00480000; // 72 dpi. const uint16_t kVideoFrameCount = 1; const uint16_t kVideoDepth = 0x0018; +const uint32_t kCompressorNameSize = 32u; + // Utility functions to check if the 64bit integers can fit in 32bit integer. bool IsFitIn32Bits(uint64_t a) { return a <= std::numeric_limits::max(); @@ -873,63 +875,25 @@ uint32_t HandlerReference::ComputeSize() { return atom_size; } -AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord() - : version(0), - profile_indication(0), - profile_compatibility(0), - avc_level(0), - length_size(0) {} +CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {} +CodecConfigurationRecord::~CodecConfigurationRecord() {} +FourCC CodecConfigurationRecord::BoxType() const { + // CodecConfigurationRecord should be parsed according to format recovered in + // VideoSampleEntry. |box_type| is determined dynamically there. + return box_type; +} -AVCDecoderConfigurationRecord::~AVCDecoderConfigurationRecord() {} -FourCC AVCDecoderConfigurationRecord::BoxType() const { return FOURCC_AVCC; } - -bool AVCDecoderConfigurationRecord::ReadWrite(BoxBuffer* buffer) { +bool CodecConfigurationRecord::ReadWrite(BoxBuffer* buffer) { RCHECK(Box::ReadWrite(buffer)); if (buffer->Reading()) { RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos())); - BufferReader buffer_reader(&data[0], data.size()); - return ParseData(&buffer_reader); } else { RCHECK(buffer->ReadWriteVector(&data, data.size())); } return true; } -bool AVCDecoderConfigurationRecord::ParseData(BufferReader* reader) { - RCHECK(reader->Read1(&version) && version == 1 && - reader->Read1(&profile_indication) && - reader->Read1(&profile_compatibility) && - reader->Read1(&avc_level)); - - uint8_t length_size_minus_one; - RCHECK(reader->Read1(&length_size_minus_one)); - length_size = (length_size_minus_one & 0x3) + 1; - - uint8_t num_sps; - RCHECK(reader->Read1(&num_sps)); - num_sps &= 0x1f; - - sps_list.resize(num_sps); - for (int i = 0; i < num_sps; i++) { - uint16_t sps_length; - RCHECK(reader->Read2(&sps_length) && - reader->ReadToVector(&sps_list[i], sps_length)); - } - - uint8_t num_pps; - RCHECK(reader->Read1(&num_pps)); - - pps_list.resize(num_pps); - for (int i = 0; i < num_pps; i++) { - uint16_t pps_length; - RCHECK(reader->Read2(&pps_length) && - reader->ReadToVector(&pps_list[i], pps_length)); - } - - return true; -} - -uint32_t AVCDecoderConfigurationRecord::ComputeSize() { +uint32_t CodecConfigurationRecord::ComputeSize() { atom_size = 0; if (!data.empty()) atom_size = kBoxSize + data.size(); @@ -1009,10 +973,16 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) { } } - if (format == FOURCC_AVC1 || - (format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) { - RCHECK(buffer->ReadWriteChild(&avcc)); + const FourCC actual_format = GetActualFormat(); + switch (actual_format) { + case FOURCC_AVC1: + codec_config_record.box_type = FOURCC_AVCC; + break; + default: + LOG(ERROR) << FourCCToString(actual_format) << " is not supported."; + return false; } + RCHECK(buffer->ReadWriteChild(&codec_config_record)); RCHECK(buffer->TryReadWriteChild(&pixel_aspect)); return true; } @@ -1022,7 +992,7 @@ uint32_t VideoSampleEntry::ComputeSize() { sizeof(height) + sizeof(kVideoResolution) * 2 + sizeof(kVideoFrameCount) + sizeof(kVideoDepth) + pixel_aspect.ComputeSize() + sinf.ComputeSize() + - avcc.ComputeSize() + 32 + // 32 bytes comparessor_name. + codec_config_record.ComputeSize() + kCompressorNameSize + 6 + 4 + 16 + 2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined. return atom_size; } diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index cc87fe3fcb..652a6d94d1 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -5,7 +5,6 @@ #ifndef MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_ #define MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_ -#include #include #include "packager/media/formats/mp4/aac_audio_specific_config.h" @@ -165,27 +164,12 @@ struct HandlerReference : FullBox { TrackType type; }; -struct AVCDecoderConfigurationRecord : Box { - DECLARE_BOX_METHODS(AVCDecoderConfigurationRecord); - bool ParseData(BufferReader* reader); +struct CodecConfigurationRecord : Box { + DECLARE_BOX_METHODS(CodecConfigurationRecord); - // Contains full avc decoder configuration record as defined in iso14496-15 - // 5.2.4.1, including possible extension bytes described in paragraph 3. - // Known fields defined in the spec are also parsed and included in this - // structure. + FourCC box_type; + // Contains full codec configuration record, including possible extension boxes. std::vector data; - - uint8_t version; - uint8_t profile_indication; - uint8_t profile_compatibility; - uint8_t avc_level; - uint8_t length_size; - - typedef std::vector SPS; - typedef std::vector PPS; - - std::vector sps_list; - std::vector pps_list; }; struct PixelAspectRatioBox : Box { @@ -197,6 +181,10 @@ struct PixelAspectRatioBox : Box { struct VideoSampleEntry : Box { DECLARE_BOX_METHODS(VideoSampleEntry); + // Returns actual format of this sample entry. + FourCC GetActualFormat() const { + return format == FOURCC_ENCV ? sinf.format.format : format; + } FourCC format; uint16_t data_reference_index; @@ -205,9 +193,7 @@ struct VideoSampleEntry : Box { PixelAspectRatioBox pixel_aspect; ProtectionSchemeInfo sinf; - - // Currently expected to be present regardless of format. - AVCDecoderConfigurationRecord avcc; + CodecConfigurationRecord codec_config_record; }; struct ElementaryStreamDescriptor : FullBox { @@ -219,6 +205,10 @@ struct ElementaryStreamDescriptor : FullBox { struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); + // Returns actual format of this sample entry. + FourCC GetActualFormat() const { + return format == FOURCC_ENCA ? sinf.format.format : format; + } FourCC format; uint16_t data_reference_index; diff --git a/packager/media/formats/mp4/box_definitions_comparison.h b/packager/media/formats/mp4/box_definitions_comparison.h index f196ce1ccf..8ff2813f82 100644 --- a/packager/media/formats/mp4/box_definitions_comparison.h +++ b/packager/media/formats/mp4/box_definitions_comparison.h @@ -165,8 +165,8 @@ inline bool operator==(const HandlerReference& lhs, return lhs.type == rhs.type; } -inline bool operator==(const AVCDecoderConfigurationRecord& lhs, - const AVCDecoderConfigurationRecord& rhs) { +inline bool operator==(const CodecConfigurationRecord& lhs, + const CodecConfigurationRecord& rhs) { return lhs.data == rhs.data; } @@ -181,7 +181,7 @@ inline bool operator==(const VideoSampleEntry& lhs, lhs.data_reference_index == rhs.data_reference_index && lhs.width == rhs.width && lhs.height == rhs.height && lhs.pixel_aspect == rhs.pixel_aspect && lhs.sinf == rhs.sinf && - lhs.avcc == rhs.avcc; + lhs.codec_config_record == rhs.codec_config_record; } inline bool operator==(const ESDescriptor& lhs, const ESDescriptor& rhs) { diff --git a/packager/media/formats/mp4/box_definitions_unittest.cc b/packager/media/formats/mp4/box_definitions_unittest.cc index 2c8ce28c63..4ad162691f 100644 --- a/packager/media/formats/mp4/box_definitions_unittest.cc +++ b/packager/media/formats/mp4/box_definitions_unittest.cc @@ -274,26 +274,24 @@ class BoxDefinitionsTestGeneral : public testing::Test { void Modify(PixelAspectRatioBox* pasp) { pasp->v_spacing *= 8; } - void Fill(AVCDecoderConfigurationRecord* avcc) { + void Fill(CodecConfigurationRecord* codec_config_record) { const uint8_t kAvccData[] = { 0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x18, 0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50, 0x05, 0xbb, 0x01, 0x10, 0x00, 0x00, 0x3e, 0x90, 0x00, 0x0e, 0xa6, 0x00, 0xf1, 0x83, 0x19, 0x60, 0x01, 0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0}; - BufferReader buffer_reader(kAvccData, arraysize(kAvccData)); - CHECK(avcc->ParseData(&buffer_reader)); - avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData)); + codec_config_record->data.assign(kAvccData, + kAvccData + arraysize(kAvccData)); } - void Modify(AVCDecoderConfigurationRecord* avcc) { + void Modify(CodecConfigurationRecord* codec_config_record) { const uint8_t kAvccData[] = { 0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96, 0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c}; - BufferReader buffer_reader(kAvccData, arraysize(kAvccData)); - CHECK(avcc->ParseData(&buffer_reader)); - avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData)); + codec_config_record->data.assign(kAvccData, + kAvccData + arraysize(kAvccData)); } void Fill(VideoSampleEntry* encv) { @@ -303,12 +301,12 @@ class BoxDefinitionsTestGeneral : public testing::Test { encv->height = 600; Fill(&encv->pixel_aspect); Fill(&encv->sinf); - Fill(&encv->avcc); + Fill(&encv->codec_config_record); } void Modify(VideoSampleEntry* encv) { encv->height += 600; - Modify(&encv->avcc); + Modify(&encv->codec_config_record); } void Fill(ElementaryStreamDescriptor* esds) { @@ -725,7 +723,7 @@ class BoxDefinitionsTestGeneral : public testing::Test { bool IsOptional(const ProtectionSchemeInfo* box) { return true; } bool IsOptional(const EditList* box) { return true; } bool IsOptional(const Edit* box) { return true; } - bool IsOptional(const AVCDecoderConfigurationRecord* box) { return true; } + bool IsOptional(const CodecConfigurationRecord* box) { return true; } bool IsOptional(const PixelAspectRatioBox* box) { return true; } bool IsOptional(const ElementaryStreamDescriptor* box) { return true; } bool IsOptional(const CompositionTimeToSample* box) { return true; } @@ -755,7 +753,7 @@ typedef testing::Types< EditList, Edit, HandlerReference, - AVCDecoderConfigurationRecord, + CodecConfigurationRecord, PixelAspectRatioBox, VideoSampleEntry, ElementaryStreamDescriptor, diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 1e60aeb1be..0598a83555 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -20,7 +20,7 @@ #include "packager/media/base/video_stream_info.h" #include "packager/media/file/file.h" #include "packager/media/file/file_closer.h" -#include "packager/media/filters/h264_parser.h" +#include "packager/media/filters/avc_decoder_configuration.h" #include "packager/media/formats/mp4/box_definitions.h" #include "packager/media/formats/mp4/box_reader.h" #include "packager/media/formats/mp4/es_descriptor.h" @@ -355,73 +355,70 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { desc_idx = 0; const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; - if (!(entry.format == FOURCC_AVC1 || - (entry.format == FOURCC_ENCV && - entry.sinf.format.format == FOURCC_AVC1))) { - LOG(ERROR) << "Unsupported video format 0x" - << std::hex << entry.format << " in stsd box."; - return false; + uint32_t coded_width = entry.width; + uint32_t coded_height = entry.height; + uint32_t pixel_width = entry.pixel_aspect.h_spacing; + uint32_t pixel_height = entry.pixel_aspect.v_spacing; + if (pixel_width == 0 && pixel_height == 0) { + pixel_width = 1; + pixel_height = 1; } + std::string codec_string; + uint8_t nalu_length_size = 0; - const std::string codec_string = - VideoStreamInfo::GetCodecString(kCodecH264, - entry.avcc.profile_indication, - entry.avcc.profile_compatibility, - entry.avcc.avc_level); + const FourCC actual_format = entry.GetActualFormat(); + switch (actual_format) { + case FOURCC_AVC1: { + AVCDecoderConfiguration avc_config; + if (!avc_config.Parse(entry.codec_config_record.data)) { + LOG(ERROR) << "Failed to parse avcc."; + return false; + } + codec_string = avc_config.GetCodecString(); + nalu_length_size = avc_config.length_size(); - uint32_t coded_width = 0; - uint32_t coded_height = 0; - uint32_t pixel_width = 0; - uint32_t pixel_height = 0; + if (coded_width != avc_config.coded_width() || + coded_height != avc_config.coded_height()) { + LOG(WARNING) << "Resolution in VisualSampleEntry (" << coded_width + << "," << coded_height + << ") does not match with resolution in " + "AVCDecoderConfigurationRecord (" + << avc_config.coded_width() << "," + << avc_config.coded_height() + << "). Use AVCDecoderConfigurationRecord."; + coded_width = avc_config.coded_width(); + coded_height = avc_config.coded_height(); + } - if (entry.avcc.sps_list.empty()) { - LOG(ERROR) << "Cannot find sps in avc decoder configuration record."; + if (pixel_width != avc_config.pixel_width() || + pixel_height != avc_config.pixel_height()) { + LOG_IF(WARNING, pixel_width != 1 || pixel_height != 1) + << "Pixel aspect ratio in PASP box (" << pixel_width << "," + << pixel_height + << ") does not match with SAR in AVCDecoderConfigurationRecord " + "(" + << avc_config.pixel_width() << "," << avc_config.pixel_height() + << "). Use AVCDecoderConfigurationRecord."; + pixel_width = avc_config.pixel_width(); + pixel_height = avc_config.pixel_height(); + } + break; + } + default: + LOG(ERROR) << "Unsupported video format " + << FourCCToString(actual_format) << " in stsd box."; return false; } - const std::vector& sps = entry.avcc.sps_list[0]; - if (!ExtractResolutionFromSpsData(&sps[0], sps.size(), &coded_width, - &coded_height, &pixel_width, - &pixel_height)) { - LOG(ERROR) << "Failed to parse SPS."; - return false; - } - - LOG_IF(WARNING, - entry.width != coded_width || entry.height != coded_height) - << "Resolution in VisualSampleEntry (" << entry.width << "," - << entry.height << ") does not match with resolution in " - "AVCDecoderConfigurationRecord (" - << coded_width << "," << coded_height - << "). Use AVCDecoderConfigurationRecord."; - - if (entry.pixel_aspect.h_spacing != 0 || entry.pixel_aspect.v_spacing != 0) { - LOG_IF(WARNING, entry.pixel_aspect.h_spacing != pixel_width || - entry.pixel_aspect.v_spacing != pixel_height) - << "Pixel aspect ratio in PASP box (" - << entry.pixel_aspect.h_spacing << "," - << entry.pixel_aspect.v_spacing - << ") does not match with SAR in AVCDecoderConfigurationRecord (" - << pixel_width << "," << pixel_height - << "). Use AVCDecoderConfigurationRecord."; - } bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted; - streams.push_back(new VideoStreamInfo(track->header.track_id, - timescale, - duration, - kCodecH264, - codec_string, - track->media.header.language, - coded_width, - coded_height, - pixel_width, - pixel_height, - 0, // trick_play_rate - entry.avcc.length_size, - &entry.avcc.data[0], - entry.avcc.data.size(), - is_encrypted)); + streams.push_back(new VideoStreamInfo( + track->header.track_id, timescale, duration, kCodecH264, + codec_string, track->media.header.language, coded_width, coded_height, + pixel_width, pixel_height, + 0, // trick_play_rate + nalu_length_size, vector_as_array(&entry.codec_config_record.data), + entry.codec_config_record.data.size(), is_encrypted)); } } diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index c31907d472..e477a0ca7a 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -179,7 +179,7 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info, video.format = FOURCC_AVC1; video.width = video_info->width(); video.height = video_info->height(); - video.avcc.data = video_info->extra_data(); + video.codec_config_record.data = video_info->extra_data(); if (pixel_width != 1 || pixel_height != 1) { video.pixel_aspect.h_spacing = pixel_width; video.pixel_aspect.v_spacing = pixel_height; diff --git a/packager/media/formats/webm/webm_video_client.cc b/packager/media/formats/webm/webm_video_client.cc index 201993d7d9..c4b08e831e 100644 --- a/packager/media/formats/webm/webm_video_client.cc +++ b/packager/media/formats/webm/webm_video_client.cc @@ -109,11 +109,13 @@ scoped_refptr WebMVideoClient::GetVideoStreamInfo( extra_data_size = codec_private.size(); } - return scoped_refptr( - new VideoStreamInfo(track_num, kWebMTimeScale, 0, video_codec, - VideoStreamInfo::GetCodecString(video_codec, 0, 0, 0), - "", width_after_crop, height_after_crop, sar_x, sar_y, - 0, 0, extra_data, extra_data_size, is_encrypted)); + // TODO(kqyang): Generate codec string. + std::string codec_string; + + return scoped_refptr(new VideoStreamInfo( + track_num, kWebMTimeScale, 0, video_codec, codec_string, std::string(), + width_after_crop, height_after_crop, sar_x, sar_y, 0, 0, extra_data, + extra_data_size, is_encrypted)); } bool WebMVideoClient::OnUInt(int id, int64_t val) { diff --git a/packager/media/formats/wvm/wvm_media_parser.cc b/packager/media/formats/wvm/wvm_media_parser.cc index 0af8fb74d2..551237cfaa 100644 --- a/packager/media/formats/wvm/wvm_media_parser.cc +++ b/packager/media/formats/wvm/wvm_media_parser.cc @@ -16,7 +16,7 @@ #include "packager/media/base/media_sample.h" #include "packager/media/base/status.h" #include "packager/media/base/video_stream_info.h" -#include "packager/media/filters/h264_parser.h" +#include "packager/media/filters/avc_decoder_configuration.h" #include "packager/media/formats/mp2t/adts_header.h" #include "packager/media/formats/mp4/aac_audio_specific_config.h" #include "packager/media/formats/mp4/es_descriptor.h" @@ -841,33 +841,24 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) { stream_config = &stream_infos_[i]->extra_data(); } DCHECK(stream_config); - stream_infos_[i]->set_codec_string(VideoStreamInfo::GetCodecString( - kCodecH264, (*stream_config)[1], (*stream_config)[2], - (*stream_config)[3])); VideoStreamInfo* video_stream_info = reinterpret_cast(stream_infos_[i].get()); - uint32_t coded_width = 0; - uint32_t coded_height = 0; - uint32_t pixel_width = 0; - uint32_t pixel_height = 0; - if (!ExtractResolutionFromDecoderConfig( - vector_as_array(stream_config), stream_config->size(), - &coded_width, &coded_height, &pixel_width, &pixel_height)) { + AVCDecoderConfiguration avc_config; + if (!avc_config.Parse(*stream_config)) { LOG(WARNING) << "Failed to parse AVCDecoderConfigurationRecord. " - "Using computed configuration record instead."; + "Using computed configuration record instead."; video_stream_info->set_extra_data(decoder_config_record); - if (!ExtractResolutionFromDecoderConfig( - vector_as_array(&decoder_config_record), - decoder_config_record.size(), - &coded_width, &coded_height, - &pixel_width, &pixel_height)) { + if (!avc_config.Parse(decoder_config_record)) { LOG(ERROR) << "Failed to parse AVCDecoderConfigurationRecord."; return false; } } - if (pixel_width != video_stream_info->pixel_width() || - pixel_height != video_stream_info->pixel_height()) { + video_stream_info->set_codec_string(avc_config.GetCodecString()); + + if (avc_config.pixel_width() != video_stream_info->pixel_width() || + avc_config.pixel_height() != + video_stream_info->pixel_height()) { LOG_IF(WARNING, video_stream_info->pixel_width() != 0 || video_stream_info->pixel_height() != 0) << "Pixel aspect ratio in WVM metadata (" @@ -875,22 +866,24 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) { << video_stream_info->pixel_height() << ") does not match with SAR in " "AVCDecoderConfigurationRecord (" - << pixel_width << "," << pixel_height + << avc_config.pixel_width() << "," + << avc_config.pixel_height() << "). Use AVCDecoderConfigurationRecord."; - video_stream_info->set_pixel_width(pixel_width); - video_stream_info->set_pixel_height(pixel_height); + video_stream_info->set_pixel_width(avc_config.pixel_width()); + video_stream_info->set_pixel_height(avc_config.pixel_height()); } - if (coded_width != video_stream_info->width() || - coded_height != video_stream_info->height()) { + if (avc_config.coded_width() != video_stream_info->width() || + avc_config.coded_height() != video_stream_info->height()) { LOG(WARNING) << "Resolution in WVM metadata (" << video_stream_info->width() << "," << video_stream_info->height() << ") does not match with resolution in " "AVCDecoderConfigurationRecord (" - << coded_width << "," << coded_height + << avc_config.coded_width() << "," + << avc_config.coded_height() << "). Use AVCDecoderConfigurationRecord."; - video_stream_info->set_width(coded_width); - video_stream_info->set_height(coded_height); + video_stream_info->set_width(avc_config.coded_width()); + video_stream_info->set_height(avc_config.coded_height()); } } }