Modified H.264 elementary stream parsing code to work with remux framework.
Change-Id: I81dfe0952073c4a5cd6f5fcaf14fe21050d26cb6
This commit is contained in:
parent
8df0e1ad0a
commit
cbf4978ffa
|
@ -88,6 +88,8 @@
|
|||
'muxer.h',
|
||||
'muxer_options.cc',
|
||||
'muxer_options.h',
|
||||
'offset_byte_queue.cc',
|
||||
'offset_byte_queue.h',
|
||||
'producer_consumer_queue.h',
|
||||
'request_signer.cc',
|
||||
'request_signer.h',
|
||||
|
@ -121,6 +123,7 @@
|
|||
'container_names_unittest.cc',
|
||||
'fake_prng.cc', # For rsa_key_unittest
|
||||
'fake_prng.h', # For rsa_key_unittest
|
||||
'offset_byte_queue_unittest.cc',
|
||||
'producer_consumer_queue_unittest.cc',
|
||||
'rsa_key_unittest.cc',
|
||||
'rsa_test_data.cc', # For rsa_key_unittest
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "media/formats/mp4/offset_byte_queue.h"
|
||||
#include "media/base/offset_byte_queue.h"
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/logging.h"
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_
|
||||
#define MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_
|
||||
#ifndef MEDIA_BASE_OFFSET_BYTE_QUEUE_H_
|
||||
#define MEDIA_BASE_OFFSET_BYTE_QUEUE_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "media/base/byte_queue.h"
|
||||
|
@ -66,4 +66,4 @@ class OffsetByteQueue {
|
|||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_
|
||||
#endif // MEDIA_BASE_OFFSET_BYTE_QUEUE_H_
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "media/formats/mp4/offset_byte_queue.h"
|
||||
#include "media/base/offset_byte_queue.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace media {
|
|
@ -17,7 +17,7 @@ namespace mp2t {
|
|||
|
||||
class EsParser {
|
||||
public:
|
||||
typedef base::Callback<void(scoped_refptr<MediaSample>)> EmitSampleCB;
|
||||
typedef base::Callback<void(scoped_refptr<MediaSample>&)> EmitSampleCB;
|
||||
|
||||
EsParser(uint32 track_id) : track_id_(track_id) {}
|
||||
virtual ~EsParser() {}
|
||||
|
|
|
@ -52,7 +52,7 @@ static bool LookForSyncWord(const uint8* raw_es, int raw_es_size,
|
|||
DCHECK_GE(pos, 0);
|
||||
DCHECK_LE(pos, raw_es_size);
|
||||
|
||||
int max_offset = raw_es_size - kADTSHeaderMinSize;
|
||||
int max_offset = raw_es_size - kAdtsHeaderMinSize;
|
||||
if (pos >= max_offset) {
|
||||
// Do not change the position if:
|
||||
// - max_offset < 0: not enough bytes to get a full header
|
||||
|
@ -73,7 +73,7 @@ static bool LookForSyncWord(const uint8* raw_es, int raw_es_size,
|
|||
continue;
|
||||
|
||||
int frame_size = ExtractAdtsFrameSize(cur_buf);
|
||||
if (frame_size < kADTSHeaderMinSize) {
|
||||
if (frame_size < kAdtsHeaderMinSize) {
|
||||
// Too short to be an ADTS frame.
|
||||
continue;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
|
|||
<< " frame_size=" << frame_size;
|
||||
DVLOG(LOG_LEVEL_ES)
|
||||
<< "ADTS header: "
|
||||
<< base::HexEncode(&raw_es[es_position], kADTSHeaderMinSize);
|
||||
<< base::HexEncode(&raw_es[es_position], kAdtsHeaderMinSize);
|
||||
|
||||
// Do not process the frame if this one is a partial frame.
|
||||
int remaining_size = raw_es_size - es_position;
|
||||
|
@ -144,7 +144,7 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
|
|||
break;
|
||||
|
||||
// Update the audio configuration if needed.
|
||||
DCHECK_GE(frame_size, kADTSHeaderMinSize);
|
||||
DCHECK_GE(frame_size, kAdtsHeaderMinSize);
|
||||
if (!UpdateAudioConfiguration(&raw_es[es_position]))
|
||||
return false;
|
||||
|
||||
|
@ -202,7 +202,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
|||
}
|
||||
|
||||
size_t frequency_index = ExtractAdtsFrequencyIndex(adts_header);
|
||||
if (frequency_index >= kADTSFrequencyTableSize) {
|
||||
if (frequency_index >= kAdtsFrequencyTableSize) {
|
||||
// Frequency index 13 & 14 are reserved
|
||||
// while 15 means that the frequency is explicitly written
|
||||
// (not supported).
|
||||
|
@ -211,14 +211,14 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
|||
|
||||
size_t channel_configuration = ExtractAdtsChannelConfig(adts_header);
|
||||
if (channel_configuration == 0 ||
|
||||
channel_configuration >= kADTSChannelLayoutTableSize) {
|
||||
channel_configuration >= kAdtsNumChannelsTableSize) {
|
||||
// TODO(damienv): Add support for inband channel configuration.
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(damienv): support HE-AAC frequency doubling (SBR)
|
||||
// based on the incoming ADTS profile.
|
||||
int samples_per_second = kADTSFrequencyTable[frequency_index];
|
||||
int samples_per_second = kAdtsFrequencyTable[frequency_index];
|
||||
int adts_profile = (adts_header[2] >> 6) & 0x3;
|
||||
|
||||
// The following code is written according to ISO 14496 Part 3 Table 1.11 and
|
||||
|
@ -229,8 +229,8 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
|||
? std::min(2 * samples_per_second, 48000)
|
||||
: samples_per_second;
|
||||
|
||||
last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo>
|
||||
(new AudioStreamInfo(
|
||||
last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo>(
|
||||
new AudioStreamInfo(
|
||||
track_id(),
|
||||
kMpeg2Timescale,
|
||||
kInfiniteDuration,
|
||||
|
@ -238,7 +238,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
|||
std::string(), // TODO(tinskip): calculate codec string.
|
||||
std::string(),
|
||||
16,
|
||||
kADTSChannelLayoutTable[channel_configuration],
|
||||
kAdtsNumChannelsTable[channel_configuration],
|
||||
samples_per_second,
|
||||
NULL, // TODO(tinskip): calculate AudioSpecificConfig.
|
||||
0,
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace mp2t {
|
|||
|
||||
class EsParserAdts : public EsParser {
|
||||
public:
|
||||
typedef base::Callback<void(scoped_refptr<AudioStreamInfo>&)> NewAudioConfigCB;
|
||||
typedef base::Callback<void(
|
||||
scoped_refptr<AudioStreamInfo>&)> NewAudioConfigCB;
|
||||
|
||||
EsParserAdts(uint32 track_id,
|
||||
const NewAudioConfigCB& new_audio_config_cb,
|
||||
|
|
|
@ -7,39 +7,50 @@
|
|||
#include "base/basictypes.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "media/base/buffers.h"
|
||||
#include "media/base/stream_parser_buffer.h"
|
||||
#include "media/base/video_frame.h"
|
||||
#include "media/base/media_sample.h"
|
||||
#include "media/base/offset_byte_queue.h"
|
||||
#include "media/base/timestamp.h"
|
||||
#include "media/base/video_stream_info.h"
|
||||
#include "media/filters/h264_parser.h"
|
||||
#include "media/formats/common/offset_byte_queue.h"
|
||||
#include "media/formats/mp2t/mp2t_common.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/size.h"
|
||||
|
||||
using media::filters::H264Parser;
|
||||
using media::filters::H264PPS;
|
||||
using media::filters::H264SliceHeader;
|
||||
using media::filters::H264SPS;
|
||||
using media::filters::H264NALU;
|
||||
|
||||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
namespace {
|
||||
|
||||
// An AUD NALU is at least 4 bytes:
|
||||
// 3 bytes for the start code + 1 byte for the NALU type.
|
||||
const int kMinAUDSize = 4;
|
||||
|
||||
// Size of H.264 NALU length output by this SDK.
|
||||
const uint8 kCommonNaluLengthSize = 4;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
EsParserH264::EsParserH264(
|
||||
uint32 track_id,
|
||||
const NewVideoConfigCB& new_video_config_cb,
|
||||
const EmitBufferCB& emit_buffer_cb)
|
||||
: new_video_config_cb_(new_video_config_cb),
|
||||
emit_buffer_cb_(emit_buffer_cb),
|
||||
es_queue_(new media::OffsetByteQueue()),
|
||||
h264_parser_(new H264Parser()),
|
||||
current_access_unit_pos_(0),
|
||||
next_access_unit_pos_(0) {
|
||||
const EmitSampleCB& emit_sample_cb)
|
||||
: EsParser(track_id),
|
||||
new_video_config_cb_(new_video_config_cb),
|
||||
emit_sample_cb_(emit_sample_cb),
|
||||
es_queue_(new media::OffsetByteQueue()),
|
||||
h264_parser_(new H264Parser()),
|
||||
current_access_unit_pos_(0),
|
||||
next_access_unit_pos_(0) {
|
||||
}
|
||||
|
||||
EsParserH264::~EsParserH264() {
|
||||
}
|
||||
|
||||
bool EsParserH264::Parse(const uint8* buf, int size,
|
||||
base::TimeDelta pts,
|
||||
base::TimeDelta dts) {
|
||||
bool EsParserH264::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
|
||||
// Note: Parse is invoked each time a PES packet has been reassembled.
|
||||
// Unfortunately, a PES packet does not necessarily map
|
||||
// to an h264 access unit, although the HLS recommendation is to use one PES
|
||||
|
@ -49,11 +60,11 @@ bool EsParserH264::Parse(const uint8* buf, int size,
|
|||
// HLS recommendation: "In AVC video, you should have both a DTS and a
|
||||
// PTS in each PES header".
|
||||
// However, some streams do not comply with this recommendation.
|
||||
DVLOG_IF(1, pts == kNoTimestamp()) << "Each video PES should have a PTS";
|
||||
if (pts != kNoTimestamp()) {
|
||||
DVLOG_IF(1, pts == kNoTimestamp) << "Each video PES should have a PTS";
|
||||
if (pts != kNoTimestamp) {
|
||||
TimingDesc timing_desc;
|
||||
timing_desc.pts = pts;
|
||||
timing_desc.dts = (dts != kNoTimestamp()) ? dts : pts;
|
||||
timing_desc.dts = (dts != kNoTimestamp) ? dts : pts;
|
||||
|
||||
// Link the end of the byte queue with the incoming timing descriptor.
|
||||
timing_desc_list_.push_back(
|
||||
|
@ -84,7 +95,7 @@ void EsParserH264::Reset() {
|
|||
current_access_unit_pos_ = 0;
|
||||
next_access_unit_pos_ = 0;
|
||||
timing_desc_list_.clear();
|
||||
last_video_decoder_config_ = VideoDecoderConfig();
|
||||
last_video_decoder_config_ = scoped_refptr<VideoStreamInfo>();
|
||||
}
|
||||
|
||||
bool EsParserH264::FindAUD(int64* stream_pos) {
|
||||
|
@ -203,7 +214,7 @@ bool EsParserH264::ParseInternal() {
|
|||
// does not necessarily start with an SPS/PPS/IDR.
|
||||
// TODO(damienv): Should be able to differentiate a missing SPS/PPS
|
||||
// from a slice header parsing error.
|
||||
if (last_video_decoder_config_.IsValidConfig())
|
||||
if (last_video_decoder_config_)
|
||||
return false;
|
||||
} else {
|
||||
pps_id_for_access_unit = shdr.pic_parameter_set_id;
|
||||
|
@ -228,13 +239,13 @@ bool EsParserH264::ParseInternal() {
|
|||
bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size,
|
||||
bool is_key_frame, int pps_id) {
|
||||
// Get the access unit timing info.
|
||||
TimingDesc current_timing_desc = {kNoTimestamp(), kNoTimestamp()};
|
||||
TimingDesc current_timing_desc = {kNoTimestamp, kNoTimestamp};
|
||||
while (!timing_desc_list_.empty() &&
|
||||
timing_desc_list_.front().first <= access_unit_pos) {
|
||||
current_timing_desc = timing_desc_list_.front().second;
|
||||
timing_desc_list_.pop_front();
|
||||
}
|
||||
if (current_timing_desc.pts == kNoTimestamp())
|
||||
if (current_timing_desc.pts == kNoTimestamp)
|
||||
return false;
|
||||
|
||||
// Update the video decoder configuration if needed.
|
||||
|
@ -245,7 +256,7 @@ bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size,
|
|||
// In this case, the initial frames are conveyed to the upper layer with
|
||||
// an invalid VideoDecoderConfig and it's up to the upper layer
|
||||
// to process this kind of frame accordingly.
|
||||
if (last_video_decoder_config_.IsValidConfig())
|
||||
if (last_video_decoder_config_)
|
||||
return false;
|
||||
} else {
|
||||
const H264SPS* sps = h264_parser_->GetSPS(pps->seq_parameter_set_id);
|
||||
|
@ -264,69 +275,58 @@ bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size,
|
|||
|
||||
// TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
|
||||
// type and allow multiple video tracks. See https://crbug.com/341581.
|
||||
scoped_refptr<StreamParserBuffer> stream_parser_buffer =
|
||||
StreamParserBuffer::CopyFrom(
|
||||
scoped_refptr<MediaSample> media_sample =
|
||||
MediaSample::CopyFrom(
|
||||
es,
|
||||
access_unit_size,
|
||||
is_key_frame,
|
||||
DemuxerStream::VIDEO,
|
||||
0);
|
||||
stream_parser_buffer->SetDecodeTimestamp(current_timing_desc.dts);
|
||||
stream_parser_buffer->set_timestamp(current_timing_desc.pts);
|
||||
emit_buffer_cb_.Run(stream_parser_buffer);
|
||||
is_key_frame);
|
||||
media_sample->set_dts(current_timing_desc.dts);
|
||||
media_sample->set_pts(current_timing_desc.pts);
|
||||
emit_sample_cb_.Run(media_sample);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) {
|
||||
// Set the SAR to 1 when not specified in the H264 stream.
|
||||
int sar_width = (sps->sar_width == 0) ? 1 : sps->sar_width;
|
||||
int sar_height = (sps->sar_height == 0) ? 1 : sps->sar_height;
|
||||
// TODO(tinskip): Generate an error if video configuration change is detected.
|
||||
if (last_video_decoder_config_) {
|
||||
// Varying video configurations currently not supported. Just assume that
|
||||
// the video configuration has not changed.
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(damienv): a MAP unit can be either 16 or 32 pixels.
|
||||
// although it's 16 pixels for progressive non MBAFF frames.
|
||||
gfx::Size coded_size((sps->pic_width_in_mbs_minus1 + 1) * 16,
|
||||
(sps->pic_height_in_map_units_minus1 + 1) * 16);
|
||||
gfx::Rect visible_rect(
|
||||
sps->frame_crop_left_offset,
|
||||
sps->frame_crop_top_offset,
|
||||
(coded_size.width() - sps->frame_crop_right_offset) -
|
||||
sps->frame_crop_left_offset,
|
||||
(coded_size.height() - sps->frame_crop_bottom_offset) -
|
||||
sps->frame_crop_top_offset);
|
||||
if (visible_rect.width() <= 0 || visible_rect.height() <= 0)
|
||||
return false;
|
||||
gfx::Size natural_size(
|
||||
(visible_rect.width() * sar_width) / sar_height,
|
||||
visible_rect.height());
|
||||
if (natural_size.width() == 0)
|
||||
return false;
|
||||
uint16 width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
|
||||
uint16 height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
|
||||
|
||||
VideoDecoderConfig video_decoder_config(
|
||||
kCodecH264,
|
||||
VIDEO_CODEC_PROFILE_UNKNOWN,
|
||||
VideoFrame::YV12,
|
||||
coded_size,
|
||||
visible_rect,
|
||||
natural_size,
|
||||
NULL, 0,
|
||||
false);
|
||||
last_video_decoder_config_ = scoped_refptr<VideoStreamInfo>(
|
||||
new VideoStreamInfo(
|
||||
track_id(),
|
||||
kMpeg2Timescale,
|
||||
kInfiniteDuration,
|
||||
kCodecH264,
|
||||
std::string(), // TODO(tinskip): calculate codec string.
|
||||
std::string(),
|
||||
width,
|
||||
height,
|
||||
kCommonNaluLengthSize,
|
||||
NULL, // TODO(tinskip): calculate AVCDecoderConfigurationRecord.
|
||||
0,
|
||||
false));
|
||||
DVLOG(1) << "Profile IDC: " << sps->profile_idc;
|
||||
DVLOG(1) << "Level IDC: " << sps->level_idc;
|
||||
DVLOG(1) << "Pic width: " << width;
|
||||
DVLOG(1) << "Pic height: " << height;
|
||||
DVLOG(1) << "log2_max_frame_num_minus4: "
|
||||
<< sps->log2_max_frame_num_minus4;
|
||||
DVLOG(1) << "SAR: width=" << sps->sar_width
|
||||
<< " height=" << sps->sar_height;
|
||||
|
||||
if (!video_decoder_config.Matches(last_video_decoder_config_)) {
|
||||
DVLOG(1) << "Profile IDC: " << sps->profile_idc;
|
||||
DVLOG(1) << "Level IDC: " << sps->level_idc;
|
||||
DVLOG(1) << "Pic width: " << coded_size.width();
|
||||
DVLOG(1) << "Pic height: " << coded_size.height();
|
||||
DVLOG(1) << "log2_max_frame_num_minus4: "
|
||||
<< sps->log2_max_frame_num_minus4;
|
||||
DVLOG(1) << "SAR: width=" << sps->sar_width
|
||||
<< " height=" << sps->sar_height;
|
||||
last_video_decoder_config_ = video_decoder_config;
|
||||
new_video_config_cb_.Run(video_decoder_config);
|
||||
}
|
||||
// Video config notification.
|
||||
new_video_config_cb_.Run(last_video_decoder_config_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
|
||||
|
|
|
@ -12,16 +12,17 @@
|
|||
#include "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/time/time.h"
|
||||
#include "media/base/media_export.h"
|
||||
#include "media/base/video_decoder_config.h"
|
||||
#include "media/formats/mp2t/es_parser.h"
|
||||
|
||||
namespace media {
|
||||
class OffsetByteQueue;
|
||||
class VideoStreamInfo;
|
||||
|
||||
namespace filters {
|
||||
class H264Parser;
|
||||
struct H264SPS;
|
||||
class OffsetByteQueue;
|
||||
}
|
||||
} // namespace filters
|
||||
} // namespace media
|
||||
|
||||
namespace media {
|
||||
namespace mp2t {
|
||||
|
@ -31,25 +32,25 @@ namespace mp2t {
|
|||
// Mpeg2 TS spec: "2.14 Carriage of Rec. ITU-T H.264 | ISO/IEC 14496-10 video"
|
||||
// "Each AVC access unit shall contain an access unit delimiter NAL Unit;"
|
||||
//
|
||||
class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) {
|
||||
class EsParserH264 : public EsParser {
|
||||
public:
|
||||
typedef base::Callback<void(const VideoDecoderConfig&)> NewVideoConfigCB;
|
||||
typedef base::Callback<void(
|
||||
scoped_refptr<VideoStreamInfo>&)> NewVideoConfigCB;
|
||||
|
||||
EsParserH264(const NewVideoConfigCB& new_video_config_cb,
|
||||
const EmitBufferCB& emit_buffer_cb);
|
||||
EsParserH264(uint32 track_id,
|
||||
const NewVideoConfigCB& new_video_config_cb,
|
||||
const EmitSampleCB& emit_sample_cb);
|
||||
virtual ~EsParserH264();
|
||||
|
||||
// EsParser implementation.
|
||||
virtual bool Parse(const uint8* buf, int size,
|
||||
base::TimeDelta pts,
|
||||
base::TimeDelta dts) OVERRIDE;
|
||||
virtual bool Parse(const uint8* buf, int size, int64 pts, int64 dts) OVERRIDE;
|
||||
virtual void Flush() OVERRIDE;
|
||||
virtual void Reset() OVERRIDE;
|
||||
|
||||
private:
|
||||
struct TimingDesc {
|
||||
base::TimeDelta dts;
|
||||
base::TimeDelta pts;
|
||||
int64 dts;
|
||||
int64 pts;
|
||||
};
|
||||
|
||||
// Find the AUD located at or after |*stream_pos|.
|
||||
|
@ -70,11 +71,11 @@ class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) {
|
|||
|
||||
// Update the video decoder config based on an H264 SPS.
|
||||
// Return true if successful.
|
||||
bool UpdateVideoDecoderConfig(const H264SPS* sps);
|
||||
bool UpdateVideoDecoderConfig(const filters::H264SPS* sps);
|
||||
|
||||
// Callbacks to pass the stream configuration and the frames.
|
||||
NewVideoConfigCB new_video_config_cb_;
|
||||
EmitBufferCB emit_buffer_cb_;
|
||||
EmitSampleCB emit_sample_cb_;
|
||||
|
||||
// Bytes of the ES stream that have not been emitted yet.
|
||||
scoped_ptr<media::OffsetByteQueue> es_queue_;
|
||||
|
@ -83,16 +84,15 @@ class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) {
|
|||
// H264 parser state.
|
||||
// - |current_access_unit_pos_| is pointing to an annexB syncword
|
||||
// representing the first NALU of an H264 access unit.
|
||||
scoped_ptr<H264Parser> h264_parser_;
|
||||
scoped_ptr<filters::H264Parser> h264_parser_;
|
||||
int64 current_access_unit_pos_;
|
||||
int64 next_access_unit_pos_;
|
||||
|
||||
// Last video decoder config.
|
||||
VideoDecoderConfig last_video_decoder_config_;
|
||||
scoped_refptr<VideoStreamInfo> last_video_decoder_config_;
|
||||
};
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,14 +10,21 @@
|
|||
#include "base/files/memory_mapped_file.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "media/base/stream_parser_buffer.h"
|
||||
#include "media/base/test_data_util.h"
|
||||
#include "media/base/media_sample.h"
|
||||
#include "media/base/timestamp.h"
|
||||
#include "media/filters/h264_parser.h"
|
||||
#include "media/formats/mp2t/es_parser_h264.h"
|
||||
#include "media/test/test_data_util.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
using media::filters::H264Parser;
|
||||
using media::filters::H264PPS;
|
||||
using media::filters::H264SliceHeader;
|
||||
using media::filters::H264SPS;
|
||||
using media::filters::H264NALU;
|
||||
|
||||
namespace media {
|
||||
class VideoDecoderConfig;
|
||||
class VideoStreamInfo;
|
||||
|
||||
namespace mp2t {
|
||||
|
||||
|
@ -116,20 +123,20 @@ void AppendAUD(
|
|||
|
||||
class EsParserH264Test : public testing::Test {
|
||||
public:
|
||||
EsParserH264Test() : buffer_count_(0) {
|
||||
EsParserH264Test() : sample_count_(0) {
|
||||
}
|
||||
|
||||
void LoadStream(const char* filename);
|
||||
void ProcessPesPackets(const std::vector<Packet>& pes_packets);
|
||||
|
||||
void EmitBuffer(scoped_refptr<StreamParserBuffer> buffer) {
|
||||
buffer_count_++;
|
||||
void EmitSample(scoped_refptr<MediaSample>& sample) {
|
||||
sample_count_++;
|
||||
}
|
||||
|
||||
void NewVideoConfig(const VideoDecoderConfig& config) {
|
||||
void NewVideoConfig(scoped_refptr<VideoStreamInfo>& config) {
|
||||
}
|
||||
|
||||
size_t buffer_count() const { return buffer_count_; }
|
||||
size_t sample_count() const { return sample_count_; }
|
||||
|
||||
// Stream with AUD NALUs.
|
||||
std::vector<uint8> stream_;
|
||||
|
@ -138,7 +145,7 @@ class EsParserH264Test : public testing::Test {
|
|||
std::vector<Packet> access_units_;
|
||||
|
||||
protected:
|
||||
size_t buffer_count_;
|
||||
size_t sample_count_;
|
||||
};
|
||||
|
||||
void EsParserH264Test::LoadStream(const char* filename) {
|
||||
|
@ -159,9 +166,13 @@ void EsParserH264Test::LoadStream(const char* filename) {
|
|||
|
||||
void EsParserH264Test::ProcessPesPackets(
|
||||
const std::vector<Packet>& pes_packets) {
|
||||
// Duration of one 25fps video frame in 90KHz clock units.
|
||||
const uint32 kMpegTicksPerFrame = 3600;
|
||||
|
||||
EsParserH264 es_parser(
|
||||
0,
|
||||
base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)),
|
||||
base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this)));
|
||||
base::Bind(&EsParserH264Test::EmitSample, base::Unretained(this)));
|
||||
|
||||
size_t au_idx = 0;
|
||||
for (size_t k = 0; k < pes_packets.size(); k++) {
|
||||
|
@ -177,11 +188,11 @@ void EsParserH264Test::ProcessPesPackets(
|
|||
|
||||
// Check whether the PES packet includes the start of an access unit.
|
||||
// The timings are relevant only in this case.
|
||||
base::TimeDelta pts = kNoTimestamp();
|
||||
base::TimeDelta dts = kNoTimestamp();
|
||||
int64 pts = kNoTimestamp;
|
||||
int64 dts = kNoTimestamp;
|
||||
if (cur_pes_offset <= access_units_[au_idx].offset &&
|
||||
cur_pes_offset + cur_pes_size > access_units_[au_idx].offset) {
|
||||
pts = base::TimeDelta::FromMilliseconds(au_idx * 40u);
|
||||
pts = au_idx * kMpegTicksPerFrame;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(
|
||||
|
@ -199,7 +210,7 @@ TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
|
|||
|
||||
// Process each PES packet.
|
||||
ProcessPesPackets(pes_packets);
|
||||
ASSERT_EQ(buffer_count(), access_units_.size());
|
||||
ASSERT_EQ(sample_count(), access_units_.size());
|
||||
}
|
||||
|
||||
TEST_F(EsParserH264Test, NonAlignedPesPacket) {
|
||||
|
@ -223,7 +234,7 @@ TEST_F(EsParserH264Test, NonAlignedPesPacket) {
|
|||
|
||||
// Process each PES packet.
|
||||
ProcessPesPackets(pes_packets);
|
||||
ASSERT_EQ(buffer_count(), access_units_.size());
|
||||
ASSERT_EQ(sample_count(), access_units_.size());
|
||||
}
|
||||
|
||||
TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
|
||||
|
@ -253,9 +264,8 @@ TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
|
|||
|
||||
// Process each PES packet.
|
||||
ProcessPesPackets(pes_packets);
|
||||
ASSERT_EQ(buffer_count(), access_units_.size());
|
||||
ASSERT_EQ(sample_count(), access_units_.size());
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
'es_parser.h',
|
||||
'es_parser_adts.cc',
|
||||
'es_parser_adts.h',
|
||||
'es_parser_h264.cc',
|
||||
'es_parser_h264.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../base/media_base.gyp:base',
|
||||
|
@ -31,10 +33,12 @@
|
|||
'target_name': 'mp2t_unittest',
|
||||
'type': '<(gtest_target_type)',
|
||||
'sources': [
|
||||
'es_parser_h264_unittest.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../testing/gtest.gyp:gtest',
|
||||
'../../../testing/gmock.gyp:gmock',
|
||||
'../../filters/filters.gyp:filters',
|
||||
'../../test/media_test.gyp:media_test_support',
|
||||
'mp2t',
|
||||
]
|
||||
|
|
|
@ -48,8 +48,6 @@
|
|||
'mp4_muxer.h',
|
||||
'multi_segment_segmenter.cc',
|
||||
'multi_segment_segmenter.h',
|
||||
'offset_byte_queue.cc',
|
||||
'offset_byte_queue.h',
|
||||
'rcheck.h',
|
||||
'segmenter.cc',
|
||||
'segmenter.h',
|
||||
|
@ -76,7 +74,6 @@
|
|||
'decoding_time_iterator_unittest.cc',
|
||||
'es_descriptor_unittest.cc',
|
||||
'mp4_media_parser_unittest.cc',
|
||||
'offset_byte_queue_unittest.cc',
|
||||
'sync_sample_iterator_unittest.cc',
|
||||
'track_run_iterator_unittest.cc',
|
||||
],
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "media/base/media_parser.h"
|
||||
#include "media/formats/mp4/offset_byte_queue.h"
|
||||
#include "media/base/offset_byte_queue.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ const size_t kADTSFrequencyTableSize = arraysize(kADTSFrequencyTable);
|
|||
|
||||
// The following conversion table is extracted from ISO 14496 Part 3 -
|
||||
// Table 1.17 - Channel Configuration.
|
||||
const int kADTSChannelLayoutTable[] = {
|
||||
const int kADTSNumChannelsTable[] = {
|
||||
0, 1, 2, 2, 4, 5, 6, 8 };
|
||||
const size_t kADTSChannelLayoutTableSize = arraysize(kADTSChannelLayoutTable);
|
||||
const size_t kADTSNumChannelsTableSize = arraysize(kADTSNumChannelsTable);
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -10,15 +10,15 @@
|
|||
namespace media {
|
||||
|
||||
enum {
|
||||
kADTSHeaderMinSize = 7,
|
||||
kAdtsHeaderMinSize = 7,
|
||||
kSamplesPerAACFrame = 1024,
|
||||
};
|
||||
|
||||
extern const int kADTSFrequencyTable[];
|
||||
extern const size_t kADTSFrequencyTableSize;
|
||||
extern const int kAdtsFrequencyTable[];
|
||||
extern const size_t kAdtsFrequencyTableSize;
|
||||
|
||||
extern const int kADTSChannelLayoutTable[];
|
||||
extern const size_t kADTSChannelLayoutTableSize;
|
||||
extern const int kAdtsNumChannelsTable[];
|
||||
extern const size_t kAdtsNumChannelsTableSize;
|
||||
|
||||
} // namespace media
|
||||
|
||||
|
|
|
@ -63,3 +63,10 @@ test-25fps.h264:
|
|||
with:
|
||||
ffmpeg -i third_party/WebKit/LayoutTests/media/content/test-25fps.mp4 \
|
||||
-vcodec copy -vbsf h264_mp4toannexb -an test-25fps.h264
|
||||
|
||||
// VDA test files: bear
|
||||
bear.h264:
|
||||
Using ffmpeg version 0.8.6-4:0.8.6-0ubuntu0.12.04.1, generated with
|
||||
bear.mp4 (https://chromiumcodereview.appspot.com/10805089):
|
||||
ffmpeg -i bear.mp4 -vcodec copy -vbsf h264_mp4toannexb \
|
||||
-an bear.h264
|
||||
|
|
Binary file not shown.
|
@ -76,6 +76,7 @@
|
|||
'media/event/media_event.gyp:media_event_unittest',
|
||||
'media/file/file.gyp:file_unittest',
|
||||
'media/filters/filters.gyp:filters_unittest',
|
||||
'media/formats/mp2t/mp2t.gyp:mp2t_unittest',
|
||||
'media/formats/mp4/mp4.gyp:mp4_unittest',
|
||||
'mpd/mpd.gyp:mpd_unittest',
|
||||
'packager_test',
|
||||
|
|
Loading…
Reference in New Issue