Modified H.264 elementary stream parsing code to work with remux framework.

Change-Id: I81dfe0952073c4a5cd6f5fcaf14fe21050d26cb6
This commit is contained in:
Thomas Inskip 2014-04-07 17:39:14 -07:00
parent 8df0e1ad0a
commit cbf4978ffa
18 changed files with 160 additions and 137 deletions

View File

@ -88,6 +88,8 @@
'muxer.h', 'muxer.h',
'muxer_options.cc', 'muxer_options.cc',
'muxer_options.h', 'muxer_options.h',
'offset_byte_queue.cc',
'offset_byte_queue.h',
'producer_consumer_queue.h', 'producer_consumer_queue.h',
'request_signer.cc', 'request_signer.cc',
'request_signer.h', 'request_signer.h',
@ -121,6 +123,7 @@
'container_names_unittest.cc', 'container_names_unittest.cc',
'fake_prng.cc', # For rsa_key_unittest 'fake_prng.cc', # For rsa_key_unittest
'fake_prng.h', # For rsa_key_unittest 'fake_prng.h', # For rsa_key_unittest
'offset_byte_queue_unittest.cc',
'producer_consumer_queue_unittest.cc', 'producer_consumer_queue_unittest.cc',
'rsa_key_unittest.cc', 'rsa_key_unittest.cc',
'rsa_test_data.cc', # For rsa_key_unittest 'rsa_test_data.cc', # For rsa_key_unittest

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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/basictypes.h"
#include "base/logging.h" #include "base/logging.h"

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_ #ifndef MEDIA_BASE_OFFSET_BYTE_QUEUE_H_
#define MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_ #define MEDIA_BASE_OFFSET_BYTE_QUEUE_H_
#include "base/basictypes.h" #include "base/basictypes.h"
#include "media/base/byte_queue.h" #include "media/base/byte_queue.h"
@ -66,4 +66,4 @@ class OffsetByteQueue {
} // namespace media } // namespace media
#endif // MEDIA_FORMATS_MP4_OFFSET_BYTE_QUEUE_H_ #endif // MEDIA_BASE_OFFSET_BYTE_QUEUE_H_

View File

@ -6,7 +6,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.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" #include "testing/gtest/include/gtest/gtest.h"
namespace media { namespace media {

View File

@ -17,7 +17,7 @@ namespace mp2t {
class EsParser { class EsParser {
public: 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) {} EsParser(uint32 track_id) : track_id_(track_id) {}
virtual ~EsParser() {} virtual ~EsParser() {}

View File

@ -52,7 +52,7 @@ static bool LookForSyncWord(const uint8* raw_es, int raw_es_size,
DCHECK_GE(pos, 0); DCHECK_GE(pos, 0);
DCHECK_LE(pos, raw_es_size); DCHECK_LE(pos, raw_es_size);
int max_offset = raw_es_size - kADTSHeaderMinSize; int max_offset = raw_es_size - kAdtsHeaderMinSize;
if (pos >= max_offset) { if (pos >= max_offset) {
// Do not change the position if: // Do not change the position if:
// - max_offset < 0: not enough bytes to get a full header // - 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; continue;
int frame_size = ExtractAdtsFrameSize(cur_buf); int frame_size = ExtractAdtsFrameSize(cur_buf);
if (frame_size < kADTSHeaderMinSize) { if (frame_size < kAdtsHeaderMinSize) {
// Too short to be an ADTS frame. // Too short to be an ADTS frame.
continue; continue;
} }
@ -136,7 +136,7 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
<< " frame_size=" << frame_size; << " frame_size=" << frame_size;
DVLOG(LOG_LEVEL_ES) DVLOG(LOG_LEVEL_ES)
<< "ADTS header: " << "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. // Do not process the frame if this one is a partial frame.
int remaining_size = raw_es_size - es_position; 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; break;
// Update the audio configuration if needed. // Update the audio configuration if needed.
DCHECK_GE(frame_size, kADTSHeaderMinSize); DCHECK_GE(frame_size, kAdtsHeaderMinSize);
if (!UpdateAudioConfiguration(&raw_es[es_position])) if (!UpdateAudioConfiguration(&raw_es[es_position]))
return false; return false;
@ -202,7 +202,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
} }
size_t frequency_index = ExtractAdtsFrequencyIndex(adts_header); size_t frequency_index = ExtractAdtsFrequencyIndex(adts_header);
if (frequency_index >= kADTSFrequencyTableSize) { if (frequency_index >= kAdtsFrequencyTableSize) {
// Frequency index 13 & 14 are reserved // Frequency index 13 & 14 are reserved
// while 15 means that the frequency is explicitly written // while 15 means that the frequency is explicitly written
// (not supported). // (not supported).
@ -211,14 +211,14 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
size_t channel_configuration = ExtractAdtsChannelConfig(adts_header); size_t channel_configuration = ExtractAdtsChannelConfig(adts_header);
if (channel_configuration == 0 || if (channel_configuration == 0 ||
channel_configuration >= kADTSChannelLayoutTableSize) { channel_configuration >= kAdtsNumChannelsTableSize) {
// TODO(damienv): Add support for inband channel configuration. // TODO(damienv): Add support for inband channel configuration.
return false; return false;
} }
// TODO(damienv): support HE-AAC frequency doubling (SBR) // TODO(damienv): support HE-AAC frequency doubling (SBR)
// based on the incoming ADTS profile. // 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; int adts_profile = (adts_header[2] >> 6) & 0x3;
// The following code is written according to ISO 14496 Part 3 Table 1.11 and // 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) ? std::min(2 * samples_per_second, 48000)
: samples_per_second; : samples_per_second;
last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo> last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo>(
(new AudioStreamInfo( new AudioStreamInfo(
track_id(), track_id(),
kMpeg2Timescale, kMpeg2Timescale,
kInfiniteDuration, kInfiniteDuration,
@ -238,7 +238,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
std::string(), // TODO(tinskip): calculate codec string. std::string(), // TODO(tinskip): calculate codec string.
std::string(), std::string(),
16, 16,
kADTSChannelLayoutTable[channel_configuration], kAdtsNumChannelsTable[channel_configuration],
samples_per_second, samples_per_second,
NULL, // TODO(tinskip): calculate AudioSpecificConfig. NULL, // TODO(tinskip): calculate AudioSpecificConfig.
0, 0,

View File

@ -25,7 +25,8 @@ namespace mp2t {
class EsParserAdts : public EsParser { class EsParserAdts : public EsParser {
public: public:
typedef base::Callback<void(scoped_refptr<AudioStreamInfo>&)> NewAudioConfigCB; typedef base::Callback<void(
scoped_refptr<AudioStreamInfo>&)> NewAudioConfigCB;
EsParserAdts(uint32 track_id, EsParserAdts(uint32 track_id,
const NewAudioConfigCB& new_audio_config_cb, const NewAudioConfigCB& new_audio_config_cb,

View File

@ -7,27 +7,40 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "media/base/buffers.h" #include "media/base/media_sample.h"
#include "media/base/stream_parser_buffer.h" #include "media/base/offset_byte_queue.h"
#include "media/base/video_frame.h" #include "media/base/timestamp.h"
#include "media/base/video_stream_info.h"
#include "media/filters/h264_parser.h" #include "media/filters/h264_parser.h"
#include "media/formats/common/offset_byte_queue.h"
#include "media/formats/mp2t/mp2t_common.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 media {
namespace mp2t { namespace mp2t {
namespace {
// An AUD NALU is at least 4 bytes: // An AUD NALU is at least 4 bytes:
// 3 bytes for the start code + 1 byte for the NALU type. // 3 bytes for the start code + 1 byte for the NALU type.
const int kMinAUDSize = 4; const int kMinAUDSize = 4;
// Size of H.264 NALU length output by this SDK.
const uint8 kCommonNaluLengthSize = 4;
} // anonymous namespace
EsParserH264::EsParserH264( EsParserH264::EsParserH264(
uint32 track_id,
const NewVideoConfigCB& new_video_config_cb, const NewVideoConfigCB& new_video_config_cb,
const EmitBufferCB& emit_buffer_cb) const EmitSampleCB& emit_sample_cb)
: new_video_config_cb_(new_video_config_cb), : EsParser(track_id),
emit_buffer_cb_(emit_buffer_cb), new_video_config_cb_(new_video_config_cb),
emit_sample_cb_(emit_sample_cb),
es_queue_(new media::OffsetByteQueue()), es_queue_(new media::OffsetByteQueue()),
h264_parser_(new H264Parser()), h264_parser_(new H264Parser()),
current_access_unit_pos_(0), current_access_unit_pos_(0),
@ -37,9 +50,7 @@ EsParserH264::EsParserH264(
EsParserH264::~EsParserH264() { EsParserH264::~EsParserH264() {
} }
bool EsParserH264::Parse(const uint8* buf, int size, bool EsParserH264::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
base::TimeDelta pts,
base::TimeDelta dts) {
// Note: Parse is invoked each time a PES packet has been reassembled. // Note: Parse is invoked each time a PES packet has been reassembled.
// Unfortunately, a PES packet does not necessarily map // Unfortunately, a PES packet does not necessarily map
// to an h264 access unit, although the HLS recommendation is to use one PES // 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 // HLS recommendation: "In AVC video, you should have both a DTS and a
// PTS in each PES header". // PTS in each PES header".
// However, some streams do not comply with this recommendation. // However, some streams do not comply with this recommendation.
DVLOG_IF(1, pts == kNoTimestamp()) << "Each video PES should have a PTS"; DVLOG_IF(1, pts == kNoTimestamp) << "Each video PES should have a PTS";
if (pts != kNoTimestamp()) { if (pts != kNoTimestamp) {
TimingDesc timing_desc; TimingDesc timing_desc;
timing_desc.pts = pts; 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. // Link the end of the byte queue with the incoming timing descriptor.
timing_desc_list_.push_back( timing_desc_list_.push_back(
@ -84,7 +95,7 @@ void EsParserH264::Reset() {
current_access_unit_pos_ = 0; current_access_unit_pos_ = 0;
next_access_unit_pos_ = 0; next_access_unit_pos_ = 0;
timing_desc_list_.clear(); timing_desc_list_.clear();
last_video_decoder_config_ = VideoDecoderConfig(); last_video_decoder_config_ = scoped_refptr<VideoStreamInfo>();
} }
bool EsParserH264::FindAUD(int64* stream_pos) { bool EsParserH264::FindAUD(int64* stream_pos) {
@ -203,7 +214,7 @@ bool EsParserH264::ParseInternal() {
// does not necessarily start with an SPS/PPS/IDR. // does not necessarily start with an SPS/PPS/IDR.
// TODO(damienv): Should be able to differentiate a missing SPS/PPS // TODO(damienv): Should be able to differentiate a missing SPS/PPS
// from a slice header parsing error. // from a slice header parsing error.
if (last_video_decoder_config_.IsValidConfig()) if (last_video_decoder_config_)
return false; return false;
} else { } else {
pps_id_for_access_unit = shdr.pic_parameter_set_id; 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 EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size,
bool is_key_frame, int pps_id) { bool is_key_frame, int pps_id) {
// Get the access unit timing info. // Get the access unit timing info.
TimingDesc current_timing_desc = {kNoTimestamp(), kNoTimestamp()}; TimingDesc current_timing_desc = {kNoTimestamp, kNoTimestamp};
while (!timing_desc_list_.empty() && while (!timing_desc_list_.empty() &&
timing_desc_list_.front().first <= access_unit_pos) { timing_desc_list_.front().first <= access_unit_pos) {
current_timing_desc = timing_desc_list_.front().second; current_timing_desc = timing_desc_list_.front().second;
timing_desc_list_.pop_front(); timing_desc_list_.pop_front();
} }
if (current_timing_desc.pts == kNoTimestamp()) if (current_timing_desc.pts == kNoTimestamp)
return false; return false;
// Update the video decoder configuration if needed. // 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 // In this case, the initial frames are conveyed to the upper layer with
// an invalid VideoDecoderConfig and it's up to the upper layer // an invalid VideoDecoderConfig and it's up to the upper layer
// to process this kind of frame accordingly. // to process this kind of frame accordingly.
if (last_video_decoder_config_.IsValidConfig()) if (last_video_decoder_config_)
return false; return false;
} else { } else {
const H264SPS* sps = h264_parser_->GetSPS(pps->seq_parameter_set_id); 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 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
// type and allow multiple video tracks. See https://crbug.com/341581. // type and allow multiple video tracks. See https://crbug.com/341581.
scoped_refptr<StreamParserBuffer> stream_parser_buffer = scoped_refptr<MediaSample> media_sample =
StreamParserBuffer::CopyFrom( MediaSample::CopyFrom(
es, es,
access_unit_size, access_unit_size,
is_key_frame, is_key_frame);
DemuxerStream::VIDEO, media_sample->set_dts(current_timing_desc.dts);
0); media_sample->set_pts(current_timing_desc.pts);
stream_parser_buffer->SetDecodeTimestamp(current_timing_desc.dts); emit_sample_cb_.Run(media_sample);
stream_parser_buffer->set_timestamp(current_timing_desc.pts);
emit_buffer_cb_.Run(stream_parser_buffer);
return true; return true;
} }
bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) { bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) {
// Set the SAR to 1 when not specified in the H264 stream. // TODO(tinskip): Generate an error if video configuration change is detected.
int sar_width = (sps->sar_width == 0) ? 1 : sps->sar_width; if (last_video_decoder_config_) {
int sar_height = (sps->sar_height == 0) ? 1 : sps->sar_height; // 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. // TODO(damienv): a MAP unit can be either 16 or 32 pixels.
// although it's 16 pixels for progressive non MBAFF frames. // although it's 16 pixels for progressive non MBAFF frames.
gfx::Size coded_size((sps->pic_width_in_mbs_minus1 + 1) * 16, uint16 width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
(sps->pic_height_in_map_units_minus1 + 1) * 16); uint16 height = (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;
VideoDecoderConfig video_decoder_config( last_video_decoder_config_ = scoped_refptr<VideoStreamInfo>(
new VideoStreamInfo(
track_id(),
kMpeg2Timescale,
kInfiniteDuration,
kCodecH264, kCodecH264,
VIDEO_CODEC_PROFILE_UNKNOWN, std::string(), // TODO(tinskip): calculate codec string.
VideoFrame::YV12, std::string(),
coded_size, width,
visible_rect, height,
natural_size, kCommonNaluLengthSize,
NULL, 0, NULL, // TODO(tinskip): calculate AVCDecoderConfigurationRecord.
false); 0,
false));
if (!video_decoder_config.Matches(last_video_decoder_config_)) {
DVLOG(1) << "Profile IDC: " << sps->profile_idc; DVLOG(1) << "Profile IDC: " << sps->profile_idc;
DVLOG(1) << "Level IDC: " << sps->level_idc; DVLOG(1) << "Level IDC: " << sps->level_idc;
DVLOG(1) << "Pic width: " << coded_size.width(); DVLOG(1) << "Pic width: " << width;
DVLOG(1) << "Pic height: " << coded_size.height(); DVLOG(1) << "Pic height: " << height;
DVLOG(1) << "log2_max_frame_num_minus4: " DVLOG(1) << "log2_max_frame_num_minus4: "
<< sps->log2_max_frame_num_minus4; << sps->log2_max_frame_num_minus4;
DVLOG(1) << "SAR: width=" << sps->sar_width DVLOG(1) << "SAR: width=" << sps->sar_width
<< " height=" << sps->sar_height; << " 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; return true;
} }
} // namespace mp2t } // namespace mp2t
} // namespace media } // namespace media

View File

@ -12,16 +12,17 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.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" #include "media/formats/mp2t/es_parser.h"
namespace media { namespace media {
class OffsetByteQueue;
class VideoStreamInfo;
namespace filters {
class H264Parser; class H264Parser;
struct H264SPS; struct H264SPS;
class OffsetByteQueue; } // namespace filters
} } // namespace media
namespace media { namespace media {
namespace mp2t { 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" // 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;" // "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: public:
typedef base::Callback<void(const VideoDecoderConfig&)> NewVideoConfigCB; typedef base::Callback<void(
scoped_refptr<VideoStreamInfo>&)> NewVideoConfigCB;
EsParserH264(const NewVideoConfigCB& new_video_config_cb, EsParserH264(uint32 track_id,
const EmitBufferCB& emit_buffer_cb); const NewVideoConfigCB& new_video_config_cb,
const EmitSampleCB& emit_sample_cb);
virtual ~EsParserH264(); virtual ~EsParserH264();
// EsParser implementation. // EsParser implementation.
virtual bool Parse(const uint8* buf, int size, virtual bool Parse(const uint8* buf, int size, int64 pts, int64 dts) OVERRIDE;
base::TimeDelta pts,
base::TimeDelta dts) OVERRIDE;
virtual void Flush() OVERRIDE; virtual void Flush() OVERRIDE;
virtual void Reset() OVERRIDE; virtual void Reset() OVERRIDE;
private: private:
struct TimingDesc { struct TimingDesc {
base::TimeDelta dts; int64 dts;
base::TimeDelta pts; int64 pts;
}; };
// Find the AUD located at or after |*stream_pos|. // 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. // Update the video decoder config based on an H264 SPS.
// Return true if successful. // Return true if successful.
bool UpdateVideoDecoderConfig(const H264SPS* sps); bool UpdateVideoDecoderConfig(const filters::H264SPS* sps);
// Callbacks to pass the stream configuration and the frames. // Callbacks to pass the stream configuration and the frames.
NewVideoConfigCB new_video_config_cb_; NewVideoConfigCB new_video_config_cb_;
EmitBufferCB emit_buffer_cb_; EmitSampleCB emit_sample_cb_;
// Bytes of the ES stream that have not been emitted yet. // Bytes of the ES stream that have not been emitted yet.
scoped_ptr<media::OffsetByteQueue> es_queue_; scoped_ptr<media::OffsetByteQueue> es_queue_;
@ -83,16 +84,15 @@ class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) {
// H264 parser state. // H264 parser state.
// - |current_access_unit_pos_| is pointing to an annexB syncword // - |current_access_unit_pos_| is pointing to an annexB syncword
// representing the first NALU of an H264 access unit. // 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 current_access_unit_pos_;
int64 next_access_unit_pos_; int64 next_access_unit_pos_;
// Last video decoder config. // Last video decoder config.
VideoDecoderConfig last_video_decoder_config_; scoped_refptr<VideoStreamInfo> last_video_decoder_config_;
}; };
} // namespace mp2t } // namespace mp2t
} // namespace media } // namespace media
#endif #endif

View File

@ -10,14 +10,21 @@
#include "base/files/memory_mapped_file.h" #include "base/files/memory_mapped_file.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "media/base/stream_parser_buffer.h" #include "media/base/media_sample.h"
#include "media/base/test_data_util.h" #include "media/base/timestamp.h"
#include "media/filters/h264_parser.h" #include "media/filters/h264_parser.h"
#include "media/formats/mp2t/es_parser_h264.h" #include "media/formats/mp2t/es_parser_h264.h"
#include "media/test/test_data_util.h"
#include "testing/gtest/include/gtest/gtest.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 { namespace media {
class VideoDecoderConfig; class VideoStreamInfo;
namespace mp2t { namespace mp2t {
@ -116,20 +123,20 @@ void AppendAUD(
class EsParserH264Test : public testing::Test { class EsParserH264Test : public testing::Test {
public: public:
EsParserH264Test() : buffer_count_(0) { EsParserH264Test() : sample_count_(0) {
} }
void LoadStream(const char* filename); void LoadStream(const char* filename);
void ProcessPesPackets(const std::vector<Packet>& pes_packets); void ProcessPesPackets(const std::vector<Packet>& pes_packets);
void EmitBuffer(scoped_refptr<StreamParserBuffer> buffer) { void EmitSample(scoped_refptr<MediaSample>& sample) {
buffer_count_++; 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. // Stream with AUD NALUs.
std::vector<uint8> stream_; std::vector<uint8> stream_;
@ -138,7 +145,7 @@ class EsParserH264Test : public testing::Test {
std::vector<Packet> access_units_; std::vector<Packet> access_units_;
protected: protected:
size_t buffer_count_; size_t sample_count_;
}; };
void EsParserH264Test::LoadStream(const char* filename) { void EsParserH264Test::LoadStream(const char* filename) {
@ -159,9 +166,13 @@ void EsParserH264Test::LoadStream(const char* filename) {
void EsParserH264Test::ProcessPesPackets( void EsParserH264Test::ProcessPesPackets(
const std::vector<Packet>& pes_packets) { const std::vector<Packet>& pes_packets) {
// Duration of one 25fps video frame in 90KHz clock units.
const uint32 kMpegTicksPerFrame = 3600;
EsParserH264 es_parser( EsParserH264 es_parser(
0,
base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)), 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; size_t au_idx = 0;
for (size_t k = 0; k < pes_packets.size(); k++) { 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. // Check whether the PES packet includes the start of an access unit.
// The timings are relevant only in this case. // The timings are relevant only in this case.
base::TimeDelta pts = kNoTimestamp(); int64 pts = kNoTimestamp;
base::TimeDelta dts = kNoTimestamp(); int64 dts = kNoTimestamp;
if (cur_pes_offset <= access_units_[au_idx].offset && if (cur_pes_offset <= access_units_[au_idx].offset &&
cur_pes_offset + cur_pes_size > 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( ASSERT_TRUE(
@ -199,7 +210,7 @@ TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
// Process each PES packet. // Process each PES packet.
ProcessPesPackets(pes_packets); ProcessPesPackets(pes_packets);
ASSERT_EQ(buffer_count(), access_units_.size()); ASSERT_EQ(sample_count(), access_units_.size());
} }
TEST_F(EsParserH264Test, NonAlignedPesPacket) { TEST_F(EsParserH264Test, NonAlignedPesPacket) {
@ -223,7 +234,7 @@ TEST_F(EsParserH264Test, NonAlignedPesPacket) {
// Process each PES packet. // Process each PES packet.
ProcessPesPackets(pes_packets); ProcessPesPackets(pes_packets);
ASSERT_EQ(buffer_count(), access_units_.size()); ASSERT_EQ(sample_count(), access_units_.size());
} }
TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) { TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
@ -253,9 +264,8 @@ TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
// Process each PES packet. // Process each PES packet.
ProcessPesPackets(pes_packets); ProcessPesPackets(pes_packets);
ASSERT_EQ(buffer_count(), access_units_.size()); ASSERT_EQ(sample_count(), access_units_.size());
} }
} // namespace mp2t } // namespace mp2t
} // namespace media } // namespace media

View File

@ -22,6 +22,8 @@
'es_parser.h', 'es_parser.h',
'es_parser_adts.cc', 'es_parser_adts.cc',
'es_parser_adts.h', 'es_parser_adts.h',
'es_parser_h264.cc',
'es_parser_h264.h',
], ],
'dependencies': [ 'dependencies': [
'../../base/media_base.gyp:base', '../../base/media_base.gyp:base',
@ -31,10 +33,12 @@
'target_name': 'mp2t_unittest', 'target_name': 'mp2t_unittest',
'type': '<(gtest_target_type)', 'type': '<(gtest_target_type)',
'sources': [ 'sources': [
'es_parser_h264_unittest.cc',
], ],
'dependencies': [ 'dependencies': [
'../../../testing/gtest.gyp:gtest', '../../../testing/gtest.gyp:gtest',
'../../../testing/gmock.gyp:gmock', '../../../testing/gmock.gyp:gmock',
'../../filters/filters.gyp:filters',
'../../test/media_test.gyp:media_test_support', '../../test/media_test.gyp:media_test_support',
'mp2t', 'mp2t',
] ]

View File

@ -48,8 +48,6 @@
'mp4_muxer.h', 'mp4_muxer.h',
'multi_segment_segmenter.cc', 'multi_segment_segmenter.cc',
'multi_segment_segmenter.h', 'multi_segment_segmenter.h',
'offset_byte_queue.cc',
'offset_byte_queue.h',
'rcheck.h', 'rcheck.h',
'segmenter.cc', 'segmenter.cc',
'segmenter.h', 'segmenter.h',
@ -76,7 +74,6 @@
'decoding_time_iterator_unittest.cc', 'decoding_time_iterator_unittest.cc',
'es_descriptor_unittest.cc', 'es_descriptor_unittest.cc',
'mp4_media_parser_unittest.cc', 'mp4_media_parser_unittest.cc',
'offset_byte_queue_unittest.cc',
'sync_sample_iterator_unittest.cc', 'sync_sample_iterator_unittest.cc',
'track_run_iterator_unittest.cc', 'track_run_iterator_unittest.cc',
], ],

View File

@ -14,7 +14,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "media/base/media_parser.h" #include "media/base/media_parser.h"
#include "media/formats/mp4/offset_byte_queue.h" #include "media/base/offset_byte_queue.h"
namespace media { namespace media {

View File

@ -17,8 +17,8 @@ const size_t kADTSFrequencyTableSize = arraysize(kADTSFrequencyTable);
// The following conversion table is extracted from ISO 14496 Part 3 - // The following conversion table is extracted from ISO 14496 Part 3 -
// Table 1.17 - Channel Configuration. // Table 1.17 - Channel Configuration.
const int kADTSChannelLayoutTable[] = { const int kADTSNumChannelsTable[] = {
0, 1, 2, 2, 4, 5, 6, 8 }; 0, 1, 2, 2, 4, 5, 6, 8 };
const size_t kADTSChannelLayoutTableSize = arraysize(kADTSChannelLayoutTable); const size_t kADTSNumChannelsTableSize = arraysize(kADTSNumChannelsTable);
} // namespace media } // namespace media

View File

@ -10,15 +10,15 @@
namespace media { namespace media {
enum { enum {
kADTSHeaderMinSize = 7, kAdtsHeaderMinSize = 7,
kSamplesPerAACFrame = 1024, kSamplesPerAACFrame = 1024,
}; };
extern const int kADTSFrequencyTable[]; extern const int kAdtsFrequencyTable[];
extern const size_t kADTSFrequencyTableSize; extern const size_t kAdtsFrequencyTableSize;
extern const int kADTSChannelLayoutTable[]; extern const int kAdtsNumChannelsTable[];
extern const size_t kADTSChannelLayoutTableSize; extern const size_t kAdtsNumChannelsTableSize;
} // namespace media } // namespace media

View File

@ -63,3 +63,10 @@ test-25fps.h264:
with: with:
ffmpeg -i third_party/WebKit/LayoutTests/media/content/test-25fps.mp4 \ ffmpeg -i third_party/WebKit/LayoutTests/media/content/test-25fps.mp4 \
-vcodec copy -vbsf h264_mp4toannexb -an test-25fps.h264 -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

BIN
media/test/data/bear.h264 Normal file

Binary file not shown.

View File

@ -76,6 +76,7 @@
'media/event/media_event.gyp:media_event_unittest', 'media/event/media_event.gyp:media_event_unittest',
'media/file/file.gyp:file_unittest', 'media/file/file.gyp:file_unittest',
'media/filters/filters.gyp:filters_unittest', 'media/filters/filters.gyp:filters_unittest',
'media/formats/mp2t/mp2t.gyp:mp2t_unittest',
'media/formats/mp4/mp4.gyp:mp4_unittest', 'media/formats/mp4/mp4.gyp:mp4_unittest',
'mpd/mpd.gyp:mpd_unittest', 'mpd/mpd.gyp:mpd_unittest',
'packager_test', 'packager_test',