Move AVCDecoderConfiguration parsing to media/filters
Change-Id: I7daa616e53bdef2206ce145f907b8f55cde87740
This commit is contained in:
parent
3842a5b43c
commit
8fc7f51d81
|
@ -92,25 +92,5 @@ std::string VideoStreamInfo::ToString() const {
|
||||||
nalu_length_size_);
|
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 media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -28,9 +28,6 @@ enum VideoCodec {
|
||||||
class VideoStreamInfo : public StreamInfo {
|
class VideoStreamInfo : public StreamInfo {
|
||||||
public:
|
public:
|
||||||
/// Construct an initialized video stream info object.
|
/// 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_width is the width of the pixel. 0 if unknown.
|
||||||
/// @param pixel_height is the height of the pixels. 0 if unknown.
|
/// @param pixel_height is the height of the pixels. 0 if unknown.
|
||||||
VideoStreamInfo(int track_id,
|
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_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
|
||||||
void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; }
|
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:
|
private:
|
||||||
~VideoStreamInfo() override;
|
~VideoStreamInfo() override;
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,7 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
|
||||||
const uint32_t kTimeScale = 10;
|
const uint32_t kTimeScale = 10;
|
||||||
const uint64_t kVideoStreamDuration = 200;
|
const uint64_t kVideoStreamDuration = 200;
|
||||||
const VideoCodec kH264Codec = kCodecH264;
|
const VideoCodec kH264Codec = kCodecH264;
|
||||||
const uint8_t kH264Profile = 1;
|
const char* kCodecString = "avc1.010101";
|
||||||
const uint8_t kH264CompatibleProfile = 1;
|
|
||||||
const uint8_t kH264Level = 1;
|
|
||||||
const char* kLanuageUndefined = "und";
|
const char* kLanuageUndefined = "und";
|
||||||
const uint16_t kWidth = 720;
|
const uint16_t kWidth = 720;
|
||||||
const uint16_t kHeight = 480;
|
const uint16_t kHeight = 480;
|
||||||
|
@ -57,8 +55,7 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
|
||||||
params.time_scale = kTimeScale;
|
params.time_scale = kTimeScale;
|
||||||
params.duration = kVideoStreamDuration;
|
params.duration = kVideoStreamDuration;
|
||||||
params.codec = kH264Codec;
|
params.codec = kH264Codec;
|
||||||
params.codec_string = VideoStreamInfo::GetCodecString(
|
params.codec_string = kCodecString;
|
||||||
kCodecH264, kH264Profile, kH264CompatibleProfile, kH264Level);
|
|
||||||
params.language = kLanuageUndefined;
|
params.language = kLanuageUndefined;
|
||||||
params.width = kWidth;
|
params.width = kWidth;
|
||||||
params.height = kHeight;
|
params.height = kHeight;
|
||||||
|
|
|
@ -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<uint8_t>& 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
|
|
@ -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 <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint8_t>& 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_
|
|
@ -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 <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<uint8_t>(
|
||||||
|
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<uint8_t>(
|
||||||
|
kAvcDecoderConfigurationData,
|
||||||
|
kAvcDecoderConfigurationData + arraysize(kAvcDecoderConfigurationData))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AVCDecoderConfigurationTest, GetCodecString) {
|
||||||
|
EXPECT_EQ("avc1.123456",
|
||||||
|
AVCDecoderConfiguration::GetCodecString(0x12, 0x34, 0x56));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
|
@ -13,6 +13,8 @@
|
||||||
'target_name': 'filters',
|
'target_name': 'filters',
|
||||||
'type': '<(component)',
|
'type': '<(component)',
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'avc_decoder_configuration.cc',
|
||||||
|
'avc_decoder_configuration.h',
|
||||||
'h264_bit_reader.cc',
|
'h264_bit_reader.cc',
|
||||||
'h264_bit_reader.h',
|
'h264_bit_reader.h',
|
||||||
'h264_byte_to_unit_stream_converter.cc',
|
'h264_byte_to_unit_stream_converter.cc',
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
'target_name': 'filters_unittest',
|
'target_name': 'filters_unittest',
|
||||||
'type': '<(gtest_target_type)',
|
'type': '<(gtest_target_type)',
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'avc_decoder_configuration_unittest.cc',
|
||||||
'h264_bit_reader_unittest.cc',
|
'h264_bit_reader_unittest.cc',
|
||||||
'h264_byte_to_unit_stream_converter_unittest.cc',
|
'h264_byte_to_unit_stream_converter_unittest.cc',
|
||||||
'h264_parser_unittest.cc',
|
'h264_parser_unittest.cc',
|
||||||
|
|
|
@ -12,62 +12,6 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
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
|
// Implemented according to ISO/IEC 14496-10:2005 7.4.2.1 Sequence parameter set
|
||||||
// RBSP semantics.
|
// RBSP semantics.
|
||||||
bool ExtractResolutionFromSps(const H264SPS& sps,
|
bool ExtractResolutionFromSps(const H264SPS& sps,
|
||||||
|
@ -133,8 +77,6 @@ bool ExtractResolutionFromSps(const H264SPS& sps,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef RCHECK
|
|
||||||
|
|
||||||
bool H264SliceHeader::IsPSlice() const {
|
bool H264SliceHeader::IsPSlice() const {
|
||||||
return (slice_type % 5 == kPSlice);
|
return (slice_type % 5 == kPSlice);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,28 +17,9 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
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
|
// On success, |coded_width| and |coded_height| contains coded resolution after
|
||||||
// cropping; |pixel_width:pixel_height| contains pixel aspect ratio, 1:1 is
|
// cropping; |pixel_width:pixel_height| contains pixel aspect ratio, 1:1 is
|
||||||
// assigned if it is not present in SPS.
|
// 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;
|
struct H264SPS;
|
||||||
bool ExtractResolutionFromSps(const H264SPS& sps,
|
bool ExtractResolutionFromSps(const H264SPS& sps,
|
||||||
uint32_t* coded_width,
|
uint32_t* coded_width,
|
||||||
|
|
|
@ -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) {
|
TEST(H264ParserTest, ExtractResolutionFromSpsData) {
|
||||||
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
|
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
|
||||||
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
|
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
|
||||||
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
|
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
|
||||||
0x60, 0x0F, 0x16, 0x2D, 0x96};
|
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_width = 0;
|
||||||
uint32_t coded_height = 0;
|
uint32_t coded_height = 0;
|
||||||
uint32_t pixel_width = 0;
|
uint32_t pixel_width = 0;
|
||||||
uint32_t pixel_height = 0;
|
uint32_t pixel_height = 0;
|
||||||
ASSERT_TRUE(ExtractResolutionFromSpsData(kSps, arraysize(kSps), &coded_width,
|
ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width,
|
||||||
&coded_height, &pixel_width,
|
&coded_height, &pixel_width,
|
||||||
&pixel_height));
|
&pixel_height));
|
||||||
EXPECT_EQ(720u, coded_width);
|
EXPECT_EQ(720u, coded_width);
|
||||||
EXPECT_EQ(360u, coded_height);
|
EXPECT_EQ(360u, coded_height);
|
||||||
EXPECT_EQ(8u, pixel_width);
|
EXPECT_EQ(8u, pixel_width);
|
||||||
|
@ -110,13 +95,18 @@ TEST(H264ParserTest, ExtractResolutionFromSpsDataWithCropping) {
|
||||||
0x9F, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00,
|
0x9F, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00,
|
||||||
0x00, 0x03, 0x03, 0x00, 0xF1, 0x42, 0x99, 0x60};
|
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_width = 0;
|
||||||
uint32_t coded_height = 0;
|
uint32_t coded_height = 0;
|
||||||
uint32_t pixel_width = 0;
|
uint32_t pixel_width = 0;
|
||||||
uint32_t pixel_height = 0;
|
uint32_t pixel_height = 0;
|
||||||
ASSERT_TRUE(ExtractResolutionFromSpsData(kSps, arraysize(kSps), &coded_width,
|
ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSPS(sps_id), &coded_width,
|
||||||
&coded_height, &pixel_width,
|
&coded_height, &pixel_width,
|
||||||
&pixel_height));
|
&pixel_height));
|
||||||
EXPECT_EQ(320u, coded_width);
|
EXPECT_EQ(320u, coded_width);
|
||||||
EXPECT_EQ(180u, coded_height);
|
EXPECT_EQ(180u, coded_height);
|
||||||
EXPECT_EQ(1u, pixel_width);
|
EXPECT_EQ(1u, pixel_width);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "packager/media/base/offset_byte_queue.h"
|
#include "packager/media/base/offset_byte_queue.h"
|
||||||
#include "packager/media/base/timestamp.h"
|
#include "packager/media/base/timestamp.h"
|
||||||
#include "packager/media/base/video_stream_info.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_byte_to_unit_stream_converter.h"
|
||||||
#include "packager/media/filters/h264_parser.h"
|
#include "packager/media/filters/h264_parser.h"
|
||||||
#include "packager/media/formats/mp2t/mp2t_common.h"
|
#include "packager/media/formats/mp2t/mp2t_common.h"
|
||||||
|
@ -361,10 +362,9 @@ bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) {
|
||||||
kMpeg2Timescale,
|
kMpeg2Timescale,
|
||||||
kInfiniteDuration,
|
kInfiniteDuration,
|
||||||
kCodecH264,
|
kCodecH264,
|
||||||
VideoStreamInfo::GetCodecString(kCodecH264,
|
AVCDecoderConfiguration::GetCodecString(decoder_config_record[1],
|
||||||
decoder_config_record[1],
|
decoder_config_record[2],
|
||||||
decoder_config_record[2],
|
decoder_config_record[3]),
|
||||||
decoder_config_record[3]),
|
|
||||||
std::string(),
|
std::string(),
|
||||||
coded_width,
|
coded_width,
|
||||||
coded_height,
|
coded_height,
|
||||||
|
|
|
@ -35,6 +35,8 @@ const uint32_t kVideoResolution = 0x00480000; // 72 dpi.
|
||||||
const uint16_t kVideoFrameCount = 1;
|
const uint16_t kVideoFrameCount = 1;
|
||||||
const uint16_t kVideoDepth = 0x0018;
|
const uint16_t kVideoDepth = 0x0018;
|
||||||
|
|
||||||
|
const uint32_t kCompressorNameSize = 32u;
|
||||||
|
|
||||||
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
||||||
bool IsFitIn32Bits(uint64_t a) {
|
bool IsFitIn32Bits(uint64_t a) {
|
||||||
return a <= std::numeric_limits<uint32_t>::max();
|
return a <= std::numeric_limits<uint32_t>::max();
|
||||||
|
@ -873,63 +875,25 @@ uint32_t HandlerReference::ComputeSize() {
|
||||||
return atom_size;
|
return atom_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord()
|
CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {}
|
||||||
: version(0),
|
CodecConfigurationRecord::~CodecConfigurationRecord() {}
|
||||||
profile_indication(0),
|
FourCC CodecConfigurationRecord::BoxType() const {
|
||||||
profile_compatibility(0),
|
// CodecConfigurationRecord should be parsed according to format recovered in
|
||||||
avc_level(0),
|
// VideoSampleEntry. |box_type| is determined dynamically there.
|
||||||
length_size(0) {}
|
return box_type;
|
||||||
|
}
|
||||||
|
|
||||||
AVCDecoderConfigurationRecord::~AVCDecoderConfigurationRecord() {}
|
bool CodecConfigurationRecord::ReadWrite(BoxBuffer* buffer) {
|
||||||
FourCC AVCDecoderConfigurationRecord::BoxType() const { return FOURCC_AVCC; }
|
|
||||||
|
|
||||||
bool AVCDecoderConfigurationRecord::ReadWrite(BoxBuffer* buffer) {
|
|
||||||
RCHECK(Box::ReadWrite(buffer));
|
RCHECK(Box::ReadWrite(buffer));
|
||||||
if (buffer->Reading()) {
|
if (buffer->Reading()) {
|
||||||
RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos()));
|
RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos()));
|
||||||
BufferReader buffer_reader(&data[0], data.size());
|
|
||||||
return ParseData(&buffer_reader);
|
|
||||||
} else {
|
} else {
|
||||||
RCHECK(buffer->ReadWriteVector(&data, data.size()));
|
RCHECK(buffer->ReadWriteVector(&data, data.size()));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AVCDecoderConfigurationRecord::ParseData(BufferReader* reader) {
|
uint32_t CodecConfigurationRecord::ComputeSize() {
|
||||||
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() {
|
|
||||||
atom_size = 0;
|
atom_size = 0;
|
||||||
if (!data.empty())
|
if (!data.empty())
|
||||||
atom_size = kBoxSize + data.size();
|
atom_size = kBoxSize + data.size();
|
||||||
|
@ -1009,10 +973,16 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format == FOURCC_AVC1 ||
|
const FourCC actual_format = GetActualFormat();
|
||||||
(format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) {
|
switch (actual_format) {
|
||||||
RCHECK(buffer->ReadWriteChild(&avcc));
|
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));
|
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1022,7 +992,7 @@ uint32_t VideoSampleEntry::ComputeSize() {
|
||||||
sizeof(height) + sizeof(kVideoResolution) * 2 +
|
sizeof(height) + sizeof(kVideoResolution) * 2 +
|
||||||
sizeof(kVideoFrameCount) + sizeof(kVideoDepth) +
|
sizeof(kVideoFrameCount) + sizeof(kVideoDepth) +
|
||||||
pixel_aspect.ComputeSize() + sinf.ComputeSize() +
|
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.
|
6 + 4 + 16 + 2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined.
|
||||||
return atom_size;
|
return atom_size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#ifndef MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_
|
#ifndef MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_
|
||||||
#define MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_
|
#define MEDIA_FORMATS_MP4_BOX_DEFINITIONS_H_
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
||||||
|
@ -165,27 +164,12 @@ struct HandlerReference : FullBox {
|
||||||
TrackType type;
|
TrackType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AVCDecoderConfigurationRecord : Box {
|
struct CodecConfigurationRecord : Box {
|
||||||
DECLARE_BOX_METHODS(AVCDecoderConfigurationRecord);
|
DECLARE_BOX_METHODS(CodecConfigurationRecord);
|
||||||
bool ParseData(BufferReader* reader);
|
|
||||||
|
|
||||||
// Contains full avc decoder configuration record as defined in iso14496-15
|
FourCC box_type;
|
||||||
// 5.2.4.1, including possible extension bytes described in paragraph 3.
|
// Contains full codec configuration record, including possible extension boxes.
|
||||||
// Known fields defined in the spec are also parsed and included in this
|
|
||||||
// structure.
|
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
uint8_t version;
|
|
||||||
uint8_t profile_indication;
|
|
||||||
uint8_t profile_compatibility;
|
|
||||||
uint8_t avc_level;
|
|
||||||
uint8_t length_size;
|
|
||||||
|
|
||||||
typedef std::vector<uint8_t> SPS;
|
|
||||||
typedef std::vector<uint8_t> PPS;
|
|
||||||
|
|
||||||
std::vector<SPS> sps_list;
|
|
||||||
std::vector<PPS> pps_list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PixelAspectRatioBox : Box {
|
struct PixelAspectRatioBox : Box {
|
||||||
|
@ -197,6 +181,10 @@ struct PixelAspectRatioBox : Box {
|
||||||
|
|
||||||
struct VideoSampleEntry : Box {
|
struct VideoSampleEntry : Box {
|
||||||
DECLARE_BOX_METHODS(VideoSampleEntry);
|
DECLARE_BOX_METHODS(VideoSampleEntry);
|
||||||
|
// Returns actual format of this sample entry.
|
||||||
|
FourCC GetActualFormat() const {
|
||||||
|
return format == FOURCC_ENCV ? sinf.format.format : format;
|
||||||
|
}
|
||||||
|
|
||||||
FourCC format;
|
FourCC format;
|
||||||
uint16_t data_reference_index;
|
uint16_t data_reference_index;
|
||||||
|
@ -205,9 +193,7 @@ struct VideoSampleEntry : Box {
|
||||||
|
|
||||||
PixelAspectRatioBox pixel_aspect;
|
PixelAspectRatioBox pixel_aspect;
|
||||||
ProtectionSchemeInfo sinf;
|
ProtectionSchemeInfo sinf;
|
||||||
|
CodecConfigurationRecord codec_config_record;
|
||||||
// Currently expected to be present regardless of format.
|
|
||||||
AVCDecoderConfigurationRecord avcc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ElementaryStreamDescriptor : FullBox {
|
struct ElementaryStreamDescriptor : FullBox {
|
||||||
|
@ -219,6 +205,10 @@ struct ElementaryStreamDescriptor : FullBox {
|
||||||
|
|
||||||
struct AudioSampleEntry : Box {
|
struct AudioSampleEntry : Box {
|
||||||
DECLARE_BOX_METHODS(AudioSampleEntry);
|
DECLARE_BOX_METHODS(AudioSampleEntry);
|
||||||
|
// Returns actual format of this sample entry.
|
||||||
|
FourCC GetActualFormat() const {
|
||||||
|
return format == FOURCC_ENCA ? sinf.format.format : format;
|
||||||
|
}
|
||||||
|
|
||||||
FourCC format;
|
FourCC format;
|
||||||
uint16_t data_reference_index;
|
uint16_t data_reference_index;
|
||||||
|
|
|
@ -165,8 +165,8 @@ inline bool operator==(const HandlerReference& lhs,
|
||||||
return lhs.type == rhs.type;
|
return lhs.type == rhs.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const AVCDecoderConfigurationRecord& lhs,
|
inline bool operator==(const CodecConfigurationRecord& lhs,
|
||||||
const AVCDecoderConfigurationRecord& rhs) {
|
const CodecConfigurationRecord& rhs) {
|
||||||
return lhs.data == rhs.data;
|
return lhs.data == rhs.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ inline bool operator==(const VideoSampleEntry& lhs,
|
||||||
lhs.data_reference_index == rhs.data_reference_index &&
|
lhs.data_reference_index == rhs.data_reference_index &&
|
||||||
lhs.width == rhs.width && lhs.height == rhs.height &&
|
lhs.width == rhs.width && lhs.height == rhs.height &&
|
||||||
lhs.pixel_aspect == rhs.pixel_aspect && lhs.sinf == rhs.sinf &&
|
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) {
|
inline bool operator==(const ESDescriptor& lhs, const ESDescriptor& rhs) {
|
||||||
|
|
|
@ -274,26 +274,24 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
|
|
||||||
void Modify(PixelAspectRatioBox* pasp) { pasp->v_spacing *= 8; }
|
void Modify(PixelAspectRatioBox* pasp) { pasp->v_spacing *= 8; }
|
||||||
|
|
||||||
void Fill(AVCDecoderConfigurationRecord* avcc) {
|
void Fill(CodecConfigurationRecord* codec_config_record) {
|
||||||
const uint8_t kAvccData[] = {
|
const uint8_t kAvccData[] = {
|
||||||
0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x18, 0x67, 0x64, 0x00,
|
0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x18, 0x67, 0x64, 0x00,
|
||||||
0x1f, 0xac, 0xd9, 0x40, 0x50, 0x05, 0xbb, 0x01, 0x10, 0x00, 0x00,
|
0x1f, 0xac, 0xd9, 0x40, 0x50, 0x05, 0xbb, 0x01, 0x10, 0x00, 0x00,
|
||||||
0x3e, 0x90, 0x00, 0x0e, 0xa6, 0x00, 0xf1, 0x83, 0x19, 0x60, 0x01,
|
0x3e, 0x90, 0x00, 0x0e, 0xa6, 0x00, 0xf1, 0x83, 0x19, 0x60, 0x01,
|
||||||
0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0};
|
0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0};
|
||||||
BufferReader buffer_reader(kAvccData, arraysize(kAvccData));
|
codec_config_record->data.assign(kAvccData,
|
||||||
CHECK(avcc->ParseData(&buffer_reader));
|
kAvccData + arraysize(kAvccData));
|
||||||
avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modify(AVCDecoderConfigurationRecord* avcc) {
|
void Modify(CodecConfigurationRecord* codec_config_record) {
|
||||||
const uint8_t kAvccData[] = {
|
const uint8_t kAvccData[] = {
|
||||||
0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00,
|
0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00,
|
||||||
0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00,
|
0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00,
|
||||||
0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96,
|
0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96,
|
||||||
0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c};
|
0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c};
|
||||||
BufferReader buffer_reader(kAvccData, arraysize(kAvccData));
|
codec_config_record->data.assign(kAvccData,
|
||||||
CHECK(avcc->ParseData(&buffer_reader));
|
kAvccData + arraysize(kAvccData));
|
||||||
avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(VideoSampleEntry* encv) {
|
void Fill(VideoSampleEntry* encv) {
|
||||||
|
@ -303,12 +301,12 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
encv->height = 600;
|
encv->height = 600;
|
||||||
Fill(&encv->pixel_aspect);
|
Fill(&encv->pixel_aspect);
|
||||||
Fill(&encv->sinf);
|
Fill(&encv->sinf);
|
||||||
Fill(&encv->avcc);
|
Fill(&encv->codec_config_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modify(VideoSampleEntry* encv) {
|
void Modify(VideoSampleEntry* encv) {
|
||||||
encv->height += 600;
|
encv->height += 600;
|
||||||
Modify(&encv->avcc);
|
Modify(&encv->codec_config_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(ElementaryStreamDescriptor* esds) {
|
void Fill(ElementaryStreamDescriptor* esds) {
|
||||||
|
@ -725,7 +723,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
bool IsOptional(const ProtectionSchemeInfo* box) { return true; }
|
bool IsOptional(const ProtectionSchemeInfo* box) { return true; }
|
||||||
bool IsOptional(const EditList* box) { return true; }
|
bool IsOptional(const EditList* box) { return true; }
|
||||||
bool IsOptional(const Edit* 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 PixelAspectRatioBox* box) { return true; }
|
||||||
bool IsOptional(const ElementaryStreamDescriptor* box) { return true; }
|
bool IsOptional(const ElementaryStreamDescriptor* box) { return true; }
|
||||||
bool IsOptional(const CompositionTimeToSample* box) { return true; }
|
bool IsOptional(const CompositionTimeToSample* box) { return true; }
|
||||||
|
@ -755,7 +753,7 @@ typedef testing::Types<
|
||||||
EditList,
|
EditList,
|
||||||
Edit,
|
Edit,
|
||||||
HandlerReference,
|
HandlerReference,
|
||||||
AVCDecoderConfigurationRecord,
|
CodecConfigurationRecord,
|
||||||
PixelAspectRatioBox,
|
PixelAspectRatioBox,
|
||||||
VideoSampleEntry,
|
VideoSampleEntry,
|
||||||
ElementaryStreamDescriptor,
|
ElementaryStreamDescriptor,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/media/file/file_closer.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_definitions.h"
|
||||||
#include "packager/media/formats/mp4/box_reader.h"
|
#include "packager/media/formats/mp4/box_reader.h"
|
||||||
#include "packager/media/formats/mp4/es_descriptor.h"
|
#include "packager/media/formats/mp4/es_descriptor.h"
|
||||||
|
@ -355,73 +355,70 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
desc_idx = 0;
|
desc_idx = 0;
|
||||||
const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx];
|
const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx];
|
||||||
|
|
||||||
if (!(entry.format == FOURCC_AVC1 ||
|
uint32_t coded_width = entry.width;
|
||||||
(entry.format == FOURCC_ENCV &&
|
uint32_t coded_height = entry.height;
|
||||||
entry.sinf.format.format == FOURCC_AVC1))) {
|
uint32_t pixel_width = entry.pixel_aspect.h_spacing;
|
||||||
LOG(ERROR) << "Unsupported video format 0x"
|
uint32_t pixel_height = entry.pixel_aspect.v_spacing;
|
||||||
<< std::hex << entry.format << " in stsd box.";
|
if (pixel_width == 0 && pixel_height == 0) {
|
||||||
return false;
|
pixel_width = 1;
|
||||||
|
pixel_height = 1;
|
||||||
}
|
}
|
||||||
|
std::string codec_string;
|
||||||
|
uint8_t nalu_length_size = 0;
|
||||||
|
|
||||||
const std::string codec_string =
|
const FourCC actual_format = entry.GetActualFormat();
|
||||||
VideoStreamInfo::GetCodecString(kCodecH264,
|
switch (actual_format) {
|
||||||
entry.avcc.profile_indication,
|
case FOURCC_AVC1: {
|
||||||
entry.avcc.profile_compatibility,
|
AVCDecoderConfiguration avc_config;
|
||||||
entry.avcc.avc_level);
|
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;
|
if (coded_width != avc_config.coded_width() ||
|
||||||
uint32_t coded_height = 0;
|
coded_height != avc_config.coded_height()) {
|
||||||
uint32_t pixel_width = 0;
|
LOG(WARNING) << "Resolution in VisualSampleEntry (" << coded_width
|
||||||
uint32_t pixel_height = 0;
|
<< "," << 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()) {
|
if (pixel_width != avc_config.pixel_width() ||
|
||||||
LOG(ERROR) << "Cannot find sps in avc decoder configuration record.";
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
const std::vector<uint8_t>& 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;
|
bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
|
||||||
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
|
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
|
||||||
streams.push_back(new VideoStreamInfo(track->header.track_id,
|
streams.push_back(new VideoStreamInfo(
|
||||||
timescale,
|
track->header.track_id, timescale, duration, kCodecH264,
|
||||||
duration,
|
codec_string, track->media.header.language, coded_width, coded_height,
|
||||||
kCodecH264,
|
pixel_width, pixel_height,
|
||||||
codec_string,
|
0, // trick_play_rate
|
||||||
track->media.header.language,
|
nalu_length_size, vector_as_array(&entry.codec_config_record.data),
|
||||||
coded_width,
|
entry.codec_config_record.data.size(), is_encrypted));
|
||||||
coded_height,
|
|
||||||
pixel_width,
|
|
||||||
pixel_height,
|
|
||||||
0, // trick_play_rate
|
|
||||||
entry.avcc.length_size,
|
|
||||||
&entry.avcc.data[0],
|
|
||||||
entry.avcc.data.size(),
|
|
||||||
is_encrypted));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
||||||
video.format = FOURCC_AVC1;
|
video.format = FOURCC_AVC1;
|
||||||
video.width = video_info->width();
|
video.width = video_info->width();
|
||||||
video.height = video_info->height();
|
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) {
|
if (pixel_width != 1 || pixel_height != 1) {
|
||||||
video.pixel_aspect.h_spacing = pixel_width;
|
video.pixel_aspect.h_spacing = pixel_width;
|
||||||
video.pixel_aspect.v_spacing = pixel_height;
|
video.pixel_aspect.v_spacing = pixel_height;
|
||||||
|
|
|
@ -109,11 +109,13 @@ scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
||||||
extra_data_size = codec_private.size();
|
extra_data_size = codec_private.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
return scoped_refptr<VideoStreamInfo>(
|
// TODO(kqyang): Generate codec string.
|
||||||
new VideoStreamInfo(track_num, kWebMTimeScale, 0, video_codec,
|
std::string codec_string;
|
||||||
VideoStreamInfo::GetCodecString(video_codec, 0, 0, 0),
|
|
||||||
"", width_after_crop, height_after_crop, sar_x, sar_y,
|
return scoped_refptr<VideoStreamInfo>(new VideoStreamInfo(
|
||||||
0, 0, extra_data, extra_data_size, is_encrypted));
|
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) {
|
bool WebMVideoClient::OnUInt(int id, int64_t val) {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
#include "packager/media/base/video_stream_info.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/mp2t/adts_header.h"
|
||||||
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
||||||
#include "packager/media/formats/mp4/es_descriptor.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();
|
stream_config = &stream_infos_[i]->extra_data();
|
||||||
}
|
}
|
||||||
DCHECK(stream_config);
|
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 =
|
VideoStreamInfo* video_stream_info =
|
||||||
reinterpret_cast<VideoStreamInfo*>(stream_infos_[i].get());
|
reinterpret_cast<VideoStreamInfo*>(stream_infos_[i].get());
|
||||||
uint32_t coded_width = 0;
|
AVCDecoderConfiguration avc_config;
|
||||||
uint32_t coded_height = 0;
|
if (!avc_config.Parse(*stream_config)) {
|
||||||
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)) {
|
|
||||||
LOG(WARNING) << "Failed to parse AVCDecoderConfigurationRecord. "
|
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);
|
video_stream_info->set_extra_data(decoder_config_record);
|
||||||
if (!ExtractResolutionFromDecoderConfig(
|
if (!avc_config.Parse(decoder_config_record)) {
|
||||||
vector_as_array(&decoder_config_record),
|
|
||||||
decoder_config_record.size(),
|
|
||||||
&coded_width, &coded_height,
|
|
||||||
&pixel_width, &pixel_height)) {
|
|
||||||
LOG(ERROR) << "Failed to parse AVCDecoderConfigurationRecord.";
|
LOG(ERROR) << "Failed to parse AVCDecoderConfigurationRecord.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pixel_width != video_stream_info->pixel_width() ||
|
video_stream_info->set_codec_string(avc_config.GetCodecString());
|
||||||
pixel_height != video_stream_info->pixel_height()) {
|
|
||||||
|
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 ||
|
LOG_IF(WARNING, video_stream_info->pixel_width() != 0 ||
|
||||||
video_stream_info->pixel_height() != 0)
|
video_stream_info->pixel_height() != 0)
|
||||||
<< "Pixel aspect ratio in WVM metadata ("
|
<< "Pixel aspect ratio in WVM metadata ("
|
||||||
|
@ -875,22 +866,24 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) {
|
||||||
<< video_stream_info->pixel_height()
|
<< video_stream_info->pixel_height()
|
||||||
<< ") does not match with SAR in "
|
<< ") does not match with SAR in "
|
||||||
"AVCDecoderConfigurationRecord ("
|
"AVCDecoderConfigurationRecord ("
|
||||||
<< pixel_width << "," << pixel_height
|
<< avc_config.pixel_width() << ","
|
||||||
|
<< avc_config.pixel_height()
|
||||||
<< "). Use AVCDecoderConfigurationRecord.";
|
<< "). Use AVCDecoderConfigurationRecord.";
|
||||||
video_stream_info->set_pixel_width(pixel_width);
|
video_stream_info->set_pixel_width(avc_config.pixel_width());
|
||||||
video_stream_info->set_pixel_height(pixel_height);
|
video_stream_info->set_pixel_height(avc_config.pixel_height());
|
||||||
}
|
}
|
||||||
if (coded_width != video_stream_info->width() ||
|
if (avc_config.coded_width() != video_stream_info->width() ||
|
||||||
coded_height != video_stream_info->height()) {
|
avc_config.coded_height() != video_stream_info->height()) {
|
||||||
LOG(WARNING) << "Resolution in WVM metadata ("
|
LOG(WARNING) << "Resolution in WVM metadata ("
|
||||||
<< video_stream_info->width() << ","
|
<< video_stream_info->width() << ","
|
||||||
<< video_stream_info->height()
|
<< video_stream_info->height()
|
||||||
<< ") does not match with resolution in "
|
<< ") does not match with resolution in "
|
||||||
"AVCDecoderConfigurationRecord ("
|
"AVCDecoderConfigurationRecord ("
|
||||||
<< coded_width << "," << coded_height
|
<< avc_config.coded_width() << ","
|
||||||
|
<< avc_config.coded_height()
|
||||||
<< "). Use AVCDecoderConfigurationRecord.";
|
<< "). Use AVCDecoderConfigurationRecord.";
|
||||||
video_stream_info->set_width(coded_width);
|
video_stream_info->set_width(avc_config.coded_width());
|
||||||
video_stream_info->set_height(coded_height);
|
video_stream_info->set_height(avc_config.coded_height());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue