Add pixel_{width,height} to VideoStreamInfo
- Added logic to container parsers to extract pixel width and height. - If the container doesn't contain the pixel width and height of the stream and the stream is H264, this parses the SPS to get the sar_width and sar_height. - If extracted sar_width and sar_height are 0s then they imply 1s. - Add hb2_v_frag.mp4 for test media. This does not have a 'pasp' box and has sar_width = 8 sar_height = 9. Change-Id: I4a06ce95582547bec19adb7905e7612c5a1f359e
This commit is contained in:
parent
2cf673055c
commit
c3c971ebd9
|
@ -78,6 +78,7 @@
|
||||||
'../../base/base.gyp:base',
|
'../../base/base.gyp:base',
|
||||||
'../../third_party/curl/curl.gyp:libcurl',
|
'../../third_party/curl/curl.gyp:libcurl',
|
||||||
'../../third_party/openssl/openssl.gyp:openssl',
|
'../../third_party/openssl/openssl.gyp:openssl',
|
||||||
|
'../filters/filters.gyp:filters',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,14 +39,12 @@ StreamInfo::~StreamInfo() {}
|
||||||
|
|
||||||
std::string StreamInfo::ToString() const {
|
std::string StreamInfo::ToString() const {
|
||||||
return base::StringPrintf(
|
return base::StringPrintf(
|
||||||
"type: %s\n codec_string: %s\n time_scale: %d\n duration: %" PRIu64 " "
|
"type: %s\n codec_string: %s\n time_scale: %d\n duration: "
|
||||||
"(%.1f seconds)\n language: %s\n is_encrypted: %s\n",
|
"%" PRIu64 " (%.1f seconds)\n language: %s\n is_encrypted: %s\n",
|
||||||
(stream_type_ == kStreamAudio ? "Audio" : "Video"),
|
(stream_type_ == kStreamAudio ? "Audio" : "Video"),
|
||||||
codec_string_.c_str(),
|
codec_string_.c_str(),
|
||||||
time_scale_,
|
time_scale_, duration_,
|
||||||
duration_,
|
static_cast<double>(duration_) / time_scale_, language_.c_str(),
|
||||||
static_cast<double>(duration_) / time_scale_,
|
|
||||||
language_.c_str(),
|
|
||||||
is_encrypted_ ? "true" : "false");
|
is_encrypted_ ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
|
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/base/strings/string_util.h"
|
#include "packager/base/strings/string_util.h"
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/base/strings/stringprintf.h"
|
||||||
#include "packager/media/base/limits.h"
|
#include "packager/media/base/limits.h"
|
||||||
|
#include "packager/media/filters/h264_parser.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -36,6 +39,7 @@ std::string VideoCodecToString(VideoCodec video_codec) {
|
||||||
return "UnknownVideoCodec";
|
return "UnknownVideoCodec";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
VideoStreamInfo::VideoStreamInfo(int track_id,
|
VideoStreamInfo::VideoStreamInfo(int track_id,
|
||||||
|
@ -46,6 +50,8 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
||||||
const std::string& language,
|
const std::string& language,
|
||||||
uint16_t width,
|
uint16_t width,
|
||||||
uint16_t height,
|
uint16_t height,
|
||||||
|
uint32_t pixel_width,
|
||||||
|
uint32_t pixel_height,
|
||||||
int16_t trick_play_rate,
|
int16_t trick_play_rate,
|
||||||
uint8_t nalu_length_size,
|
uint8_t nalu_length_size,
|
||||||
const uint8_t* extra_data,
|
const uint8_t* extra_data,
|
||||||
|
@ -63,8 +69,20 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
||||||
codec_(codec),
|
codec_(codec),
|
||||||
width_(width),
|
width_(width),
|
||||||
height_(height),
|
height_(height),
|
||||||
|
pixel_width_(pixel_width),
|
||||||
|
pixel_height_(pixel_height),
|
||||||
trick_play_rate_(trick_play_rate),
|
trick_play_rate_(trick_play_rate),
|
||||||
nalu_length_size_(nalu_length_size) {
|
nalu_length_size_(nalu_length_size) {
|
||||||
|
// If H264 and the pixel width and height were not passed in, parse the extra
|
||||||
|
// data to get the sar width and height.
|
||||||
|
if ((pixel_width_ == 0 || pixel_height_ == 0) &&
|
||||||
|
codec == kCodecH264 &&
|
||||||
|
extra_data && extra_data_size > 0) {
|
||||||
|
ExtractSarFromDecoderConfig(extra_data, extra_data_size, &pixel_width_,
|
||||||
|
&pixel_height_);
|
||||||
|
DVLOG_IF(2, pixel_width_ == 0 || pixel_height_ == 0)
|
||||||
|
<< "Failed to extract sar_width and sar_height.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStreamInfo::~VideoStreamInfo() {}
|
VideoStreamInfo::~VideoStreamInfo() {}
|
||||||
|
@ -78,14 +96,13 @@ bool VideoStreamInfo::IsValidConfig() const {
|
||||||
|
|
||||||
std::string VideoStreamInfo::ToString() const {
|
std::string VideoStreamInfo::ToString() const {
|
||||||
return base::StringPrintf(
|
return base::StringPrintf(
|
||||||
"%s codec: %s\n width: %d\n height: %d\n trick_play_rate: %d\n"
|
"%s codec: %s\n width: %d\n height: %d\n pixel_width: %d\n pixel_height: "
|
||||||
" nalu_length_size: %d\n",
|
"%d\n trick_play_rate: %d\n nalu_length_size: %d\n",
|
||||||
StreamInfo::ToString().c_str(),
|
StreamInfo::ToString().c_str(),
|
||||||
VideoCodecToString(codec_).c_str(),
|
VideoCodecToString(codec_).c_str(),
|
||||||
width_,
|
width_, height_,
|
||||||
height_,
|
pixel_width_, pixel_height_,
|
||||||
trick_play_rate_,
|
trick_play_rate_, nalu_length_size_);
|
||||||
nalu_length_size_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string VideoStreamInfo::GetCodecString(VideoCodec codec,
|
std::string VideoStreamInfo::GetCodecString(VideoCodec codec,
|
||||||
|
|
|
@ -28,6 +28,11 @@ 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_height is the height of the pixels. 0 if unknown.
|
||||||
VideoStreamInfo(int track_id,
|
VideoStreamInfo(int track_id,
|
||||||
uint32_t time_scale,
|
uint32_t time_scale,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
|
@ -36,6 +41,8 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
const std::string& language,
|
const std::string& language,
|
||||||
uint16_t width,
|
uint16_t width,
|
||||||
uint16_t height,
|
uint16_t height,
|
||||||
|
uint32_t pixel_width,
|
||||||
|
uint32_t pixel_height,
|
||||||
int16_t trick_play_rate,
|
int16_t trick_play_rate,
|
||||||
uint8_t nalu_length_size,
|
uint8_t nalu_length_size,
|
||||||
const uint8_t* extra_data,
|
const uint8_t* extra_data,
|
||||||
|
@ -51,6 +58,12 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
VideoCodec codec() const { return codec_; }
|
VideoCodec codec() const { return codec_; }
|
||||||
uint16_t width() const { return width_; }
|
uint16_t width() const { return width_; }
|
||||||
uint16_t height() const { return height_; }
|
uint16_t height() const { return height_; }
|
||||||
|
/// Returns the pixel width.
|
||||||
|
/// @return 0 if unknown.
|
||||||
|
uint32_t pixel_width() const { return pixel_width_; }
|
||||||
|
/// Returns the pixel height.
|
||||||
|
/// @return 0 if unknown.
|
||||||
|
uint32_t pixel_height() const { return pixel_height_; }
|
||||||
uint8_t nalu_length_size() const { return nalu_length_size_; }
|
uint8_t nalu_length_size() const { return nalu_length_size_; }
|
||||||
int16_t trick_play_rate() const { return trick_play_rate_; }
|
int16_t trick_play_rate() const { return trick_play_rate_; }
|
||||||
|
|
||||||
|
@ -67,6 +80,11 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
uint16_t width_;
|
uint16_t width_;
|
||||||
uint16_t height_;
|
uint16_t height_;
|
||||||
|
|
||||||
|
// pixel_width_:pixel_height_ is the sample aspect ratio.
|
||||||
|
// 0 means unknown.
|
||||||
|
uint32_t pixel_width_;
|
||||||
|
uint32_t pixel_height_;
|
||||||
int16_t trick_play_rate_; // Non-zero for trick-play streams.
|
int16_t trick_play_rate_; // Non-zero for trick-play streams.
|
||||||
|
|
||||||
// Specifies the normalized size of the NAL unit length field. Can be 1, 2 or
|
// Specifies the normalized size of the NAL unit length field. Can be 1, 2 or
|
||||||
|
|
|
@ -63,6 +63,10 @@ scoped_refptr<StreamInfo> CreateVideoStreamInfo(
|
||||||
param.language,
|
param.language,
|
||||||
param.width,
|
param.width,
|
||||||
param.height,
|
param.height,
|
||||||
|
// TODO(rkuroiwa): Once MedianInfo proto change that
|
||||||
|
// adds pizel_{width,height} lands, add tests.
|
||||||
|
0, // No pixel width.
|
||||||
|
0, // No pixel height.
|
||||||
0, // trick_play_rate
|
0, // trick_play_rate
|
||||||
param.nalu_length_size,
|
param.nalu_length_size,
|
||||||
vector_as_array(¶m.extra_data),
|
vector_as_array(¶m.extra_data),
|
||||||
|
|
|
@ -7,10 +7,73 @@
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/memory/scoped_ptr.h"
|
#include "packager/base/memory/scoped_ptr.h"
|
||||||
#include "packager/base/stl_util.h"
|
#include "packager/base/stl_util.h"
|
||||||
|
#include "packager/media/base/buffer_reader.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
#define RCHECK(x) \
|
||||||
|
do { \
|
||||||
|
if (!(x)) { \
|
||||||
|
LOG(ERROR) << "Failure while parsing AVCDecoderConfig: " << #x; \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void ExtractSarFromDecoderConfig(
|
||||||
|
const uint8_t* avc_decoder_config_data, size_t avc_decoder_config_data_size,
|
||||||
|
uint32_t* sar_width, uint32_t* sar_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);
|
||||||
|
|
||||||
|
// avc profile. No value check.
|
||||||
|
RCHECK(reader.Read1(&value));
|
||||||
|
|
||||||
|
// profile compatibility. No value check.
|
||||||
|
RCHECK(reader.Read1(&value));
|
||||||
|
|
||||||
|
// avc level indication. No value check.
|
||||||
|
RCHECK(reader.Read1(&value));
|
||||||
|
|
||||||
|
// reserved and length sized minus one.
|
||||||
|
RCHECK(reader.Read1(&value));
|
||||||
|
// upper 6 bits are reserved and must be 111111.
|
||||||
|
RCHECK((value & 0xFC) == 0xFC);
|
||||||
|
|
||||||
|
// reserved and num sps.
|
||||||
|
RCHECK(reader.Read1(&value));
|
||||||
|
// upper 3 bits are reserved for 0b111.
|
||||||
|
RCHECK((value & 0xE0) == 0xE0);
|
||||||
|
|
||||||
|
const uint8_t num_sps = value & 0x1F;
|
||||||
|
if (num_sps < 1) {
|
||||||
|
LOG(ERROR) << "No SPS found.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t sps_length = 0;
|
||||||
|
RCHECK(reader.Read2(&sps_length));
|
||||||
|
|
||||||
|
H264Parser parser;
|
||||||
|
int sps_id;
|
||||||
|
RCHECK(parser.ParseSPSFromArray(reader.data() + reader.pos(), sps_length,
|
||||||
|
&sps_id) == H264Parser::kOk);
|
||||||
|
const H264SPS& sps = *parser.GetSPS(sps_id);
|
||||||
|
// 0 means it wasn't in the SPS and therefore assume 1.
|
||||||
|
*sar_width = sps.sar_width == 0 ? 1 : sps.sar_width;
|
||||||
|
*sar_height = sps.sar_height == 0 ? 1 : sps.sar_height;
|
||||||
|
DVLOG(2) << "Found sar_width: " << *sar_width
|
||||||
|
<< " sar_height: " << *sar_height;
|
||||||
|
|
||||||
|
// It is unlikely to have more than one SPS in practice. Also there's
|
||||||
|
// no way to change the sar_{width,height} dynamically from VideoStreamInfo.
|
||||||
|
// So skip the rest (if there are any).
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef RCHECK
|
||||||
|
|
||||||
bool H264SliceHeader::IsPSlice() const {
|
bool H264SliceHeader::IsPSlice() const {
|
||||||
return (slice_type % 5 == kPSlice);
|
return (slice_type % 5 == kPSlice);
|
||||||
}
|
}
|
||||||
|
@ -888,6 +951,28 @@ H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
|
||||||
return kOk;
|
return kOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
H264Parser::Result H264Parser::ParseSPSFromArray(
|
||||||
|
const uint8_t* sps_data,
|
||||||
|
size_t sps_data_length,
|
||||||
|
int* sps_id) {
|
||||||
|
br_.Initialize(sps_data, sps_data_length);
|
||||||
|
|
||||||
|
int data;
|
||||||
|
READ_BITS_OR_RETURN(1, &data);
|
||||||
|
// First bit must be 0.
|
||||||
|
TRUE_OR_RETURN(data == 0);
|
||||||
|
int nal_ref_idc;
|
||||||
|
READ_BITS_OR_RETURN(2, &nal_ref_idc);
|
||||||
|
// From the spec "nal_ref_idc shall not be equal to 0 for sequence parameter
|
||||||
|
// set".
|
||||||
|
TRUE_OR_RETURN(nal_ref_idc != 0);
|
||||||
|
int nal_unit_type;
|
||||||
|
READ_BITS_OR_RETURN(5, &nal_unit_type);
|
||||||
|
TRUE_OR_RETURN(nal_unit_type == H264NALU::kSPS);
|
||||||
|
|
||||||
|
return ParseSPS(sps_id);
|
||||||
|
}
|
||||||
|
|
||||||
H264Parser::Result H264Parser::ParseRefPicListModification(
|
H264Parser::Result H264Parser::ParseRefPicListModification(
|
||||||
int num_ref_idx_active_minus1,
|
int num_ref_idx_active_minus1,
|
||||||
H264ModificationOfPicNum* ref_list_mods) {
|
H264ModificationOfPicNum* ref_list_mods) {
|
||||||
|
|
|
@ -17,6 +17,16 @@
|
||||||
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, |sar_width| and |sar_height| are set to the data specified in
|
||||||
|
// |avc_decoder_config_data|.
|
||||||
|
// If the value is 0 or ommited in |avc_decoder_config_data|, then 1 is
|
||||||
|
// assigned.
|
||||||
|
void ExtractSarFromDecoderConfig(
|
||||||
|
const uint8_t* avc_decoder_config_data, size_t avc_decoder_config_data_size,
|
||||||
|
uint32_t* sar_width, uint32_t* sar_height);
|
||||||
|
|
||||||
// For explanations of each struct and its members, see H.264 specification
|
// For explanations of each struct and its members, see H.264 specification
|
||||||
// at http://www.itu.int/rec/T-REC-H.264.
|
// at http://www.itu.int/rec/T-REC-H.264.
|
||||||
struct H264NALU {
|
struct H264NALU {
|
||||||
|
@ -314,6 +324,11 @@ class H264Parser {
|
||||||
Result ParseSPS(int* sps_id);
|
Result ParseSPS(int* sps_id);
|
||||||
Result ParsePPS(int* pps_id);
|
Result ParsePPS(int* pps_id);
|
||||||
|
|
||||||
|
// Samme as ParseSPS but instead uses |sps_data|.
|
||||||
|
Result ParseSPSFromArray(const uint8_t* sps_data,
|
||||||
|
size_t sps_data_size,
|
||||||
|
int* sps_id);
|
||||||
|
|
||||||
// Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not
|
// Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not
|
||||||
// present.
|
// present.
|
||||||
const H264SPS* GetSPS(int sps_id);
|
const H264SPS* GetSPS(int sps_id);
|
||||||
|
|
|
@ -363,6 +363,8 @@ bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) {
|
||||||
std::string(),
|
std::string(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
sps->sar_width == 0 ? 1 : sps->sar_width,
|
||||||
|
sps->sar_height == 0 ? 1 : sps->sar_height,
|
||||||
0,
|
0,
|
||||||
H264ByteToUnitStreamConverter::kUnitStreamNaluLengthSize,
|
H264ByteToUnitStreamConverter::kUnitStreamNaluLengthSize,
|
||||||
decoder_config_record.data(),
|
decoder_config_record.data(),
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "packager/base/stl_util.h"
|
#include "packager/base/stl_util.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/timestamp.h"
|
#include "packager/media/base/timestamp.h"
|
||||||
|
#include "packager/media/base/video_stream_info.h"
|
||||||
#include "packager/media/filters/h264_parser.h"
|
#include "packager/media/filters/h264_parser.h"
|
||||||
#include "packager/media/formats/mp2t/es_parser_h264.h"
|
#include "packager/media/formats/mp2t/es_parser_h264.h"
|
||||||
#include "packager/media/test/test_data_util.h"
|
#include "packager/media/test/test_data_util.h"
|
||||||
|
@ -131,6 +132,8 @@ class EsParserH264Test : public testing::Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewVideoConfig(scoped_refptr<StreamInfo>& config) {
|
void NewVideoConfig(scoped_refptr<StreamInfo>& config) {
|
||||||
|
DVLOG(1) << config->ToString();
|
||||||
|
stream_map_[config->track_id()] = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sample_count() const { return sample_count_; }
|
size_t sample_count() const { return sample_count_; }
|
||||||
|
@ -143,6 +146,8 @@ class EsParserH264Test : public testing::Test {
|
||||||
std::vector<Packet> access_units_;
|
std::vector<Packet> access_units_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
typedef std::map<int, scoped_refptr<StreamInfo> > StreamMap;
|
||||||
|
StreamMap stream_map_;
|
||||||
size_t sample_count_;
|
size_t sample_count_;
|
||||||
bool first_frame_is_key_frame_;
|
bool first_frame_is_key_frame_;
|
||||||
};
|
};
|
||||||
|
@ -198,7 +203,6 @@ void EsParserH264Test::ProcessPesPackets(
|
||||||
es_parser.Flush();
|
es_parser.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
|
TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
|
||||||
LoadStream("bear.h264");
|
LoadStream("bear.h264");
|
||||||
|
|
||||||
|
@ -280,6 +284,21 @@ TEST_F(EsParserH264Test, NonIFrameStart) {
|
||||||
EXPECT_TRUE(first_frame_is_key_frame());
|
EXPECT_TRUE(first_frame_is_key_frame());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that the parser can get the the sar width and height.
|
||||||
|
TEST_F(EsParserH264Test, PixelWidthPixelHeight) {
|
||||||
|
LoadStream("bear.h264");
|
||||||
|
std::vector<Packet> pes_packets(access_units_);
|
||||||
|
ProcessPesPackets(pes_packets);
|
||||||
|
|
||||||
|
const int kVideoTrackId = 0;
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_height());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mp2t
|
} // namespace mp2t
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -378,6 +378,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
track->media.header.language,
|
track->media.header.language,
|
||||||
entry.width,
|
entry.width,
|
||||||
entry.height,
|
entry.height,
|
||||||
|
entry.pixel_aspect.h_spacing,
|
||||||
|
entry.pixel_aspect.v_spacing,
|
||||||
0, // trick_play_rate
|
0, // trick_play_rate
|
||||||
entry.avcc.length_size,
|
entry.avcc.length_size,
|
||||||
&entry.avcc.data[0],
|
&entry.avcc.data[0],
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
|
#include "packager/media/base/video_stream_info.h"
|
||||||
#include "packager/media/formats/mp4/mp4_media_parser.h"
|
#include "packager/media/formats/mp4/mp4_media_parser.h"
|
||||||
#include "packager/media/test/test_data_util.h"
|
#include "packager/media/test/test_data_util.h"
|
||||||
|
|
||||||
|
@ -41,6 +42,8 @@ class MP4MediaParserTest : public testing::Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
typedef std::map<int, scoped_refptr<StreamInfo> > StreamMap;
|
||||||
|
StreamMap stream_map_;
|
||||||
scoped_ptr<MP4MediaParser> parser_;
|
scoped_ptr<MP4MediaParser> parser_;
|
||||||
size_t num_streams_;
|
size_t num_streams_;
|
||||||
size_t num_samples_;
|
size_t num_samples_;
|
||||||
|
@ -70,6 +73,7 @@ class MP4MediaParserTest : public testing::Test {
|
||||||
iter != streams.end();
|
iter != streams.end();
|
||||||
++iter) {
|
++iter) {
|
||||||
DVLOG(2) << (*iter)->ToString();
|
DVLOG(2) << (*iter)->ToString();
|
||||||
|
stream_map_[(*iter)->track_id()] = *iter;
|
||||||
}
|
}
|
||||||
num_streams_ = streams.size();
|
num_streams_ = streams.size();
|
||||||
num_samples_ = 0;
|
num_samples_ = 0;
|
||||||
|
@ -106,6 +110,65 @@ TEST_F(MP4MediaParserTest, UnalignedAppend) {
|
||||||
EXPECT_EQ(201u, num_samples_);
|
EXPECT_EQ(201u, num_samples_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that the pixel width and pixel height are extracted correctly if
|
||||||
|
// the container has a 'pasp' box.
|
||||||
|
TEST_F(MP4MediaParserTest, PixelWidthPixelHeightFromPaspBox) {
|
||||||
|
// This content has a 'pasp' box that has the aspect ratio.
|
||||||
|
EXPECT_TRUE(ParseMP4File("bear-1280x720.mp4", 512));
|
||||||
|
|
||||||
|
// Track ID 2 has the video stream which should have pixel width and height
|
||||||
|
// both 1.
|
||||||
|
const int kVideoTrackId = 2;
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_height());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that pixel width and height can be extracted from the
|
||||||
|
// extra data (AVCDecoderConfigurationRecord) for H264.
|
||||||
|
// No 'pasp' box.
|
||||||
|
TEST_F(MP4MediaParserTest,
|
||||||
|
PixelWidthPixelHeightFromAVCDecoderConfigurationRecord) {
|
||||||
|
// This file doesn't have pasp. SPS for the video has
|
||||||
|
// sar_width = sar_height = 0. So the stream info should return 1 for both
|
||||||
|
// pixel_width and pixel_height.
|
||||||
|
EXPECT_TRUE(ParseMP4File("hb2_v_frag.mp4", 512));
|
||||||
|
|
||||||
|
// Track ID 1 has the video stream which should have pixel width and height
|
||||||
|
// both 1.
|
||||||
|
const int kVideoTrackId = 1;
|
||||||
|
EXPECT_EQ(8u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(9u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_height());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that pixel width and height can be extracted from the
|
||||||
|
// extra data (AVCDecoderConfigurationRecord) for H264.
|
||||||
|
// If sar_width and sar_height are not set, then they should both be 1.
|
||||||
|
TEST_F(MP4MediaParserTest,
|
||||||
|
PixelWidthPixelHeightFromAVCDecoderConfigurationRecordNotSet) {
|
||||||
|
// This file doesn't have pasp. SPS for the video has
|
||||||
|
// sar_width = sar_height = 0. So the stream info should return 1 for both
|
||||||
|
// pixel_width and pixel_height.
|
||||||
|
EXPECT_TRUE(ParseMP4File("bear-1280x720-av_frag.mp4", 512));
|
||||||
|
|
||||||
|
// Track ID 1 has the video stream which should have pixel width and height
|
||||||
|
// both 1.
|
||||||
|
const int kVideoTrackId = 1;
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
|
||||||
|
->pixel_height());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MP4MediaParserTest, BytewiseAppend) {
|
TEST_F(MP4MediaParserTest, BytewiseAppend) {
|
||||||
// Ensure no incremental errors occur when parsing
|
// Ensure no incremental errors occur when parsing
|
||||||
EXPECT_TRUE(ParseMP4File("bear-1280x720-av_frag.mp4", 1));
|
EXPECT_TRUE(ParseMP4File("bear-1280x720-av_frag.mp4", 1));
|
||||||
|
|
|
@ -569,6 +569,8 @@ bool WvmMediaParser::ParseIndexEntry() {
|
||||||
uint32_t time_scale = kMpeg2ClockRate;
|
uint32_t time_scale = kMpeg2ClockRate;
|
||||||
uint16_t video_width = 0;
|
uint16_t video_width = 0;
|
||||||
uint16_t video_height = 0;
|
uint16_t video_height = 0;
|
||||||
|
uint32_t pixel_width = 0;
|
||||||
|
uint32_t pixel_height = 0;
|
||||||
uint8_t nalu_length_size = kNaluLengthSize;
|
uint8_t nalu_length_size = kNaluLengthSize;
|
||||||
uint8_t num_channels = 0;
|
uint8_t num_channels = 0;
|
||||||
int audio_pes_stream_id = 0;
|
int audio_pes_stream_id = 0;
|
||||||
|
@ -584,147 +586,154 @@ bool WvmMediaParser::ParseIndexEntry() {
|
||||||
|
|
||||||
for (uint8_t idx = 0; idx < num_index_entries; ++idx) {
|
for (uint8_t idx = 0; idx < num_index_entries; ++idx) {
|
||||||
if (index_metadata_max_size < (2 * sizeof(uint8_t)) + sizeof(uint32_t)) {
|
if (index_metadata_max_size < (2 * sizeof(uint8_t)) + sizeof(uint32_t)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint8_t tag = *read_ptr_index;
|
|
||||||
++read_ptr_index;
|
|
||||||
uint8_t type = *read_ptr_index;
|
|
||||||
++read_ptr_index;
|
|
||||||
uint32_t length = ntohlFromBuffer(read_ptr_index);
|
|
||||||
read_ptr_index += sizeof(uint32_t);
|
|
||||||
index_metadata_max_size -= (2 * sizeof(uint8_t)) + sizeof(uint32_t);
|
|
||||||
if (index_metadata_max_size < length) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int value = 0;
|
uint8_t tag = *read_ptr_index;
|
||||||
Tag tagtype = Unset;
|
++read_ptr_index;
|
||||||
std::vector<uint8_t> binary_data(length);
|
uint8_t type = *read_ptr_index;
|
||||||
switch (Type(type)) {
|
++read_ptr_index;
|
||||||
case Type_uint8:
|
uint32_t length = ntohlFromBuffer(read_ptr_index);
|
||||||
if (length == sizeof(uint8_t)) {
|
read_ptr_index += sizeof(uint32_t);
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
index_metadata_max_size -= (2 * sizeof(uint8_t)) + sizeof(uint32_t);
|
||||||
} else {
|
if (index_metadata_max_size < length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
int64_t value = 0;
|
||||||
case Type_int8:
|
Tag tagtype = Unset;
|
||||||
if (length == sizeof(int8_t)) {
|
std::vector<uint8_t> binary_data(length);
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
switch (Type(type)) {
|
||||||
} else {
|
case Type_uint8:
|
||||||
return false;
|
if (length == sizeof(uint8_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_uint16:
|
return false;
|
||||||
if (length == sizeof(uint16_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_int8:
|
||||||
return false;
|
if (length == sizeof(int8_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_int16:
|
return false;
|
||||||
if (length == sizeof(int16_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_uint16:
|
||||||
return false;
|
if (length == sizeof(uint16_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_uint32:
|
return false;
|
||||||
if (length == sizeof(uint32_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_int16:
|
||||||
return false;
|
if (length == sizeof(int16_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_int32:
|
return false;
|
||||||
if (length == sizeof(int32_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_uint32:
|
||||||
return false;
|
if (length == sizeof(uint32_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_uint64:
|
return false;
|
||||||
if (length == sizeof(uint64_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_int32:
|
||||||
return false;
|
if (length == sizeof(int32_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_int64:
|
return false;
|
||||||
if (length == sizeof(int64_t)) {
|
}
|
||||||
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
break;
|
||||||
} else {
|
case Type_uint64:
|
||||||
return false;
|
if (length == sizeof(uint64_t)) {
|
||||||
}
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
case Type_string:
|
return false;
|
||||||
case Type_BinaryData:
|
}
|
||||||
memcpy(&binary_data[0], read_ptr_index, length);
|
break;
|
||||||
tagtype = Tag(tag);
|
case Type_int64:
|
||||||
break;
|
if (length == sizeof(int64_t)) {
|
||||||
default:
|
tagtype = GetTag(tag, length, read_ptr_index, &value);
|
||||||
break;
|
} else {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Type_string:
|
||||||
|
case Type_BinaryData:
|
||||||
|
memcpy(&binary_data[0], read_ptr_index, length);
|
||||||
|
tagtype = Tag(tag);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (tagtype) {
|
switch (tagtype) {
|
||||||
case TrackDuration:
|
case TrackDuration:
|
||||||
track_duration = value;
|
track_duration = value;
|
||||||
break;
|
break;
|
||||||
case TrackTrickPlayRate:
|
case TrackTrickPlayRate:
|
||||||
trick_play_rate = value;
|
trick_play_rate = value;
|
||||||
break;
|
break;
|
||||||
case VideoStreamId:
|
case VideoStreamId:
|
||||||
video_pes_stream_id = value;
|
video_pes_stream_id = value;
|
||||||
break;
|
break;
|
||||||
case AudioStreamId:
|
case AudioStreamId:
|
||||||
audio_pes_stream_id = value;
|
audio_pes_stream_id = value;
|
||||||
break;
|
break;
|
||||||
case VideoWidth:
|
case VideoWidth:
|
||||||
video_width = (uint16_t)value;
|
video_width = (uint16_t)value;
|
||||||
break;
|
break;
|
||||||
case VideoHeight:
|
case VideoHeight:
|
||||||
video_height = (uint16_t)value;
|
video_height = (uint16_t)value;
|
||||||
break;
|
break;
|
||||||
case AudioNumChannels:
|
case AudioNumChannels:
|
||||||
num_channels = (uint8_t)value;
|
num_channels = (uint8_t)value;
|
||||||
break;
|
break;
|
||||||
case VideoType:
|
case VideoType:
|
||||||
has_video = true;
|
has_video = true;
|
||||||
break;
|
break;
|
||||||
case AudioType:
|
case AudioType:
|
||||||
has_audio = true;
|
has_audio = true;
|
||||||
break;
|
break;
|
||||||
default:
|
case VideoPixelWidth:
|
||||||
break;
|
pixel_width = static_cast<uint32_t>(value);
|
||||||
}
|
break;
|
||||||
|
case VideoPixelHeight:
|
||||||
|
pixel_height = static_cast<uint32_t>(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
read_ptr_index += length;
|
read_ptr_index += length;
|
||||||
index_metadata_max_size -= length;
|
index_metadata_max_size -= length;
|
||||||
}
|
}
|
||||||
// End Index metadata
|
// End Index metadata
|
||||||
index_size = read_ptr_index - &index_data_[0];
|
index_size = read_ptr_index - &index_data_[0];
|
||||||
|
|
||||||
// Extra data for both audio and video streams not set here, but in
|
// Extra data for both audio and video streams not set here, but in
|
||||||
// Output().
|
// Output().
|
||||||
if (has_video) {
|
if (has_video) {
|
||||||
VideoCodec video_codec = kCodecH264;
|
VideoCodec video_codec = kCodecH264;
|
||||||
stream_infos_.push_back(new VideoStreamInfo(
|
stream_infos_.push_back(new VideoStreamInfo(
|
||||||
stream_id_count_, time_scale, track_duration, video_codec,
|
stream_id_count_, time_scale, track_duration, video_codec,
|
||||||
video_codec_string, std::string(), video_width, video_height,
|
video_codec_string, std::string(), video_width, video_height,
|
||||||
trick_play_rate, nalu_length_size, NULL, 0, true));
|
pixel_width, pixel_height, trick_play_rate, nalu_length_size, NULL, 0,
|
||||||
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
true));
|
||||||
base::UintToString(video_pes_stream_id)] =
|
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
||||||
stream_id_count_++;
|
base::UintToString(video_pes_stream_id)] =
|
||||||
}
|
stream_id_count_++;
|
||||||
if (has_audio) {
|
}
|
||||||
AudioCodec audio_codec = kCodecAAC;
|
if (has_audio) {
|
||||||
stream_infos_.push_back(new AudioStreamInfo(
|
AudioCodec audio_codec = kCodecAAC;
|
||||||
stream_id_count_, time_scale, track_duration, audio_codec,
|
stream_infos_.push_back(new AudioStreamInfo(
|
||||||
audio_codec_string, std::string(), kAacSampleSizeBits, num_channels,
|
stream_id_count_, time_scale, track_duration, audio_codec,
|
||||||
sampling_frequency, NULL, 0, true));
|
audio_codec_string, std::string(), kAacSampleSizeBits, num_channels,
|
||||||
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
sampling_frequency, NULL, 0, true));
|
||||||
base::UintToString(audio_pes_stream_id)] =
|
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
||||||
stream_id_count_++;
|
base::UintToString(audio_pes_stream_id)] =
|
||||||
}
|
stream_id_count_++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
index_program_id_++;
|
index_program_id_++;
|
||||||
|
|
|
@ -163,6 +163,18 @@ TEST_F(WvmMediaParserTest, ParseWvmWithoutKeySource) {
|
||||||
EXPECT_EQ(kExpectedVideoFrameCount, video_frame_count_);
|
EXPECT_EQ(kExpectedVideoFrameCount, video_frame_count_);
|
||||||
EXPECT_EQ(kExpectedAudioFrameCount, audio_frame_count_);
|
EXPECT_EQ(kExpectedAudioFrameCount, audio_frame_count_);
|
||||||
EXPECT_EQ(kExpectedEncryptedSampleCount, encrypted_sample_count_);
|
EXPECT_EQ(kExpectedEncryptedSampleCount, encrypted_sample_count_);
|
||||||
|
|
||||||
|
// Also verify that the pixel width and height have the right values.
|
||||||
|
// Track 0 and 2 are videos and they both have pixel_width = 8 and
|
||||||
|
// pixel_height = 9.
|
||||||
|
EXPECT_EQ(8u, reinterpret_cast<VideoStreamInfo*>(stream_map_[0].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(8u, reinterpret_cast<VideoStreamInfo*>(stream_map_[2].get())
|
||||||
|
->pixel_width());
|
||||||
|
EXPECT_EQ(9u, reinterpret_cast<VideoStreamInfo*>(stream_map_[0].get())
|
||||||
|
->pixel_height());
|
||||||
|
EXPECT_EQ(9u, reinterpret_cast<VideoStreamInfo*>(stream_map_[2].get())
|
||||||
|
->pixel_height());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvmMediaParserTest, ParseWvmInitWithoutKeySource) {
|
TEST_F(WvmMediaParserTest, ParseWvmInitWithoutKeySource) {
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue