From cbf4978ffa4be49848d571c0a864541d41dcfdc0 Mon Sep 17 00:00:00 2001 From: Thomas Inskip Date: Mon, 7 Apr 2014 17:39:14 -0700 Subject: [PATCH] Modified H.264 elementary stream parsing code to work with remux framework. Change-Id: I81dfe0952073c4a5cd6f5fcaf14fe21050d26cb6 --- media/base/media_base.gyp | 3 + .../mp4 => base}/offset_byte_queue.cc | 2 +- .../{formats/mp4 => base}/offset_byte_queue.h | 6 +- .../offset_byte_queue_unittest.cc | 2 +- media/formats/mp2t/es_parser.h | 2 +- media/formats/mp2t/es_parser_adts.cc | 20 +-- media/formats/mp2t/es_parser_adts.h | 3 +- media/formats/mp2t/es_parser_h264.cc | 146 +++++++++--------- media/formats/mp2t/es_parser_h264.h | 38 ++--- media/formats/mp2t/es_parser_h264_unittest.cc | 44 ++++-- media/formats/mp2t/mp2t.gyp | 4 + media/formats/mp4/mp4.gyp | 3 - media/formats/mp4/mp4_media_parser.h | 2 +- media/formats/mpeg/adts_constants.cc | 4 +- media/formats/mpeg/adts_constants.h | 10 +- media/test/data/README | 7 + media/test/data/bear.h264 | Bin 0 -> 29262 bytes packager.gyp | 1 + 18 files changed, 160 insertions(+), 137 deletions(-) rename media/{formats/mp4 => base}/offset_byte_queue.cc (96%) rename media/{formats/mp4 => base}/offset_byte_queue.h (93%) rename media/{formats/mp4 => base}/offset_byte_queue_unittest.cc (97%) create mode 100644 media/test/data/bear.h264 diff --git a/media/base/media_base.gyp b/media/base/media_base.gyp index 52f3e7ef6d..4c38ac2405 100644 --- a/media/base/media_base.gyp +++ b/media/base/media_base.gyp @@ -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 diff --git a/media/formats/mp4/offset_byte_queue.cc b/media/base/offset_byte_queue.cc similarity index 96% rename from media/formats/mp4/offset_byte_queue.cc rename to media/base/offset_byte_queue.cc index c785b864aa..35c4a0529c 100644 --- a/media/formats/mp4/offset_byte_queue.cc +++ b/media/base/offset_byte_queue.cc @@ -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" diff --git a/media/formats/mp4/offset_byte_queue.h b/media/base/offset_byte_queue.h similarity index 93% rename from media/formats/mp4/offset_byte_queue.h rename to media/base/offset_byte_queue.h index ce718154d4..59f3fdeba5 100644 --- a/media/formats/mp4/offset_byte_queue.h +++ b/media/base/offset_byte_queue.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_ diff --git a/media/formats/mp4/offset_byte_queue_unittest.cc b/media/base/offset_byte_queue_unittest.cc similarity index 97% rename from media/formats/mp4/offset_byte_queue_unittest.cc rename to media/base/offset_byte_queue_unittest.cc index 44cdb63890..3ba42be86a 100644 --- a/media/formats/mp4/offset_byte_queue_unittest.cc +++ b/media/base/offset_byte_queue_unittest.cc @@ -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 { diff --git a/media/formats/mp2t/es_parser.h b/media/formats/mp2t/es_parser.h index 5758a4c085..cc4b887008 100644 --- a/media/formats/mp2t/es_parser.h +++ b/media/formats/mp2t/es_parser.h @@ -17,7 +17,7 @@ namespace mp2t { class EsParser { public: - typedef base::Callback)> EmitSampleCB; + typedef base::Callback&)> EmitSampleCB; EsParser(uint32 track_id) : track_id_(track_id) {} virtual ~EsParser() {} diff --git a/media/formats/mp2t/es_parser_adts.cc b/media/formats/mp2t/es_parser_adts.cc index 8fa980d186..813e1ff8e9 100644 --- a/media/formats/mp2t/es_parser_adts.cc +++ b/media/formats/mp2t/es_parser_adts.cc @@ -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 - (new AudioStreamInfo( + last_audio_decoder_config_ = scoped_refptr( + 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, diff --git a/media/formats/mp2t/es_parser_adts.h b/media/formats/mp2t/es_parser_adts.h index af12d46f71..a2e43ca9da 100644 --- a/media/formats/mp2t/es_parser_adts.h +++ b/media/formats/mp2t/es_parser_adts.h @@ -25,7 +25,8 @@ namespace mp2t { class EsParserAdts : public EsParser { public: - typedef base::Callback&)> NewAudioConfigCB; + typedef base::Callback&)> NewAudioConfigCB; EsParserAdts(uint32 track_id, const NewAudioConfigCB& new_audio_config_cb, diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc index 691678ce81..bc507cf167 100644 --- a/media/formats/mp2t/es_parser_h264.cc +++ b/media/formats/mp2t/es_parser_h264.cc @@ -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(); } 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 stream_parser_buffer = - StreamParserBuffer::CopyFrom( + scoped_refptr 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( + 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 - diff --git a/media/formats/mp2t/es_parser_h264.h b/media/formats/mp2t/es_parser_h264.h index bf4f4cc1d9..7783dcee8e 100644 --- a/media/formats/mp2t/es_parser_h264.h +++ b/media/formats/mp2t/es_parser_h264.h @@ -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 NewVideoConfigCB; + typedef base::Callback&)> 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 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 h264_parser_; + scoped_ptr h264_parser_; int64 current_access_unit_pos_; int64 next_access_unit_pos_; // Last video decoder config. - VideoDecoderConfig last_video_decoder_config_; + scoped_refptr last_video_decoder_config_; }; } // namespace mp2t } // namespace media #endif - diff --git a/media/formats/mp2t/es_parser_h264_unittest.cc b/media/formats/mp2t/es_parser_h264_unittest.cc index 6e141ba7bf..b79ab1b716 100644 --- a/media/formats/mp2t/es_parser_h264_unittest.cc +++ b/media/formats/mp2t/es_parser_h264_unittest.cc @@ -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& pes_packets); - void EmitBuffer(scoped_refptr buffer) { - buffer_count_++; + void EmitSample(scoped_refptr& sample) { + sample_count_++; } - void NewVideoConfig(const VideoDecoderConfig& config) { + void NewVideoConfig(scoped_refptr& config) { } - size_t buffer_count() const { return buffer_count_; } + size_t sample_count() const { return sample_count_; } // Stream with AUD NALUs. std::vector stream_; @@ -138,7 +145,7 @@ class EsParserH264Test : public testing::Test { std::vector 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& 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 - diff --git a/media/formats/mp2t/mp2t.gyp b/media/formats/mp2t/mp2t.gyp index cc9a1a6768..021ab38fcd 100644 --- a/media/formats/mp2t/mp2t.gyp +++ b/media/formats/mp2t/mp2t.gyp @@ -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', ] diff --git a/media/formats/mp4/mp4.gyp b/media/formats/mp4/mp4.gyp index 4e90d48082..f673b760b0 100644 --- a/media/formats/mp4/mp4.gyp +++ b/media/formats/mp4/mp4.gyp @@ -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', ], diff --git a/media/formats/mp4/mp4_media_parser.h b/media/formats/mp4/mp4_media_parser.h index c26cdd5904..ea0f37dde9 100644 --- a/media/formats/mp4/mp4_media_parser.h +++ b/media/formats/mp4/mp4_media_parser.h @@ -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 { diff --git a/media/formats/mpeg/adts_constants.cc b/media/formats/mpeg/adts_constants.cc index f5ef47c879..7451dcebb8 100644 --- a/media/formats/mpeg/adts_constants.cc +++ b/media/formats/mpeg/adts_constants.cc @@ -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 diff --git a/media/formats/mpeg/adts_constants.h b/media/formats/mpeg/adts_constants.h index fdd45cb900..2f53b22944 100644 --- a/media/formats/mpeg/adts_constants.h +++ b/media/formats/mpeg/adts_constants.h @@ -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 diff --git a/media/test/data/README b/media/test/data/README index ff82bb6b7d..11dbc166d4 100644 --- a/media/test/data/README +++ b/media/test/data/README @@ -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 diff --git a/media/test/data/bear.h264 b/media/test/data/bear.h264 new file mode 100644 index 0000000000000000000000000000000000000000..b0baa697efc20b2cae69be1f84b294f053cecbc0 GIT binary patch literal 29262 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 literal 0 HcmV?d00001 diff --git a/packager.gyp b/packager.gyp index bd9d482d4a..3a6100adea 100644 --- a/packager.gyp +++ b/packager.gyp @@ -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',