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.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
|
||||||
|
|
|
@ -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"
|
|
@ -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_
|
|
@ -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 {
|
|
@ -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() {}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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',
|
||||||
],
|
],
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Binary file not shown.
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue