diff options
author | damienv <damienv@chromium.org> | 2014-10-09 07:35:16 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-09 14:35:30 +0000 |
commit | 8012df46f572e5a7bc2f33ff41e5d470e0da8a69 (patch) | |
tree | 31d2c73850d92a01e89635338089df717983948a /media | |
parent | f4adb2a5430e64d66b28a627a824cc12e013035a (diff) | |
download | chromium_src-8012df46f572e5a7bc2f33ff41e5d470e0da8a69.zip chromium_src-8012df46f572e5a7bc2f33ff41e5d470e0da8a69.tar.gz chromium_src-8012df46f572e5a7bc2f33ff41e5d470e0da8a69.tar.bz2 |
Support some non-compliant MPEG-2 TS streams w.r.t. timestamps.
BUG=420227
Review URL: https://codereview.chromium.org/625043002
Cr-Commit-Position: refs/heads/master@{#298876}
Diffstat (limited to 'media')
-rw-r--r-- | media/formats/mp2t/es_adapter_video.cc | 88 | ||||
-rw-r--r-- | media/formats/mp2t/es_adapter_video.h | 14 | ||||
-rw-r--r-- | media/formats/mp2t/es_adapter_video_unittest.cc | 28 | ||||
-rw-r--r-- | media/formats/mp2t/es_parser_h264.cc | 15 |
4 files changed, 113 insertions, 32 deletions
diff --git a/media/formats/mp2t/es_adapter_video.cc b/media/formats/mp2t/es_adapter_video.cc index 5e604a9..70b16e3 100644 --- a/media/formats/mp2t/es_adapter_video.cc +++ b/media/formats/mp2t/es_adapter_video.cc @@ -33,7 +33,9 @@ EsAdapterVideo::EsAdapterVideo( has_valid_frame_(false), last_frame_duration_( base::TimeDelta::FromMilliseconds(kDefaultFrameDurationMs)), - buffer_index_(0) { + buffer_index_(0), + has_valid_initial_timestamp_(false), + discarded_frame_count_(0) { } EsAdapterVideo::~EsAdapterVideo() { @@ -55,8 +57,11 @@ void EsAdapterVideo::Reset() { buffer_list_.clear(); emitted_pts_.clear(); - discarded_frames_min_pts_ = base::TimeDelta(); - discarded_frames_dts_.clear(); + has_valid_initial_timestamp_ = false; + min_pts_ = base::TimeDelta(); + min_dts_ = DecodeTimestamp(); + + discarded_frame_count_ = 0; } void EsAdapterVideo::OnConfigChanged( @@ -67,29 +72,59 @@ void EsAdapterVideo::OnConfigChanged( ProcessPendingBuffers(false); } -void EsAdapterVideo::OnNewBuffer( +bool EsAdapterVideo::OnNewBuffer( const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { + if (stream_parser_buffer->timestamp() == kNoTimestamp()) { + if (has_valid_frame_) { + // There is currently no error concealment for a missing timestamp + // in the middle of the stream. + DVLOG(1) << "Missing timestamp in the middle of the stream"; + return false; + } + + if (!has_valid_initial_timestamp_) { + // MPEG-2 TS requires the first access unit to be given a timestamp. + // However, some streams do not comply with this requirement. + // So simply drop the frame if it is a leading frame with no timestamp. + DVLOG(1) + << "Stream not compliant: ignoring leading frame with no timestamp"; + return true; + } + + // In all the other cases, this frame will be replaced by the following + // valid key frame, using timestamp interpolation. + DCHECK(has_valid_initial_timestamp_); + DCHECK_GE(discarded_frame_count_, 1); + discarded_frame_count_++; + return true; + } + + // At this point, timestamps of the incoming frame are valid. + if (!has_valid_initial_timestamp_) { + min_pts_ = stream_parser_buffer->timestamp(); + min_dts_ = stream_parser_buffer->GetDecodeTimestamp(); + has_valid_initial_timestamp_ = true; + } + if (stream_parser_buffer->timestamp() < min_pts_) + min_pts_ = stream_parser_buffer->timestamp(); + // Discard the incoming frame: // - if it is not associated with any config, - // - or if only non-key frames have been added to a new segment. + // - or if no valid key frame has been found so far. if (!has_valid_config_ || (!has_valid_frame_ && !stream_parser_buffer->IsKeyframe())) { - if (discarded_frames_dts_.empty() || - discarded_frames_min_pts_ > stream_parser_buffer->timestamp()) { - discarded_frames_min_pts_ = stream_parser_buffer->timestamp(); - } - discarded_frames_dts_.push_back( - stream_parser_buffer->GetDecodeTimestamp()); - return; + discarded_frame_count_++; + return true; } has_valid_frame_ = true; - if (!discarded_frames_dts_.empty()) + if (discarded_frame_count_ > 0) ReplaceDiscardedFrames(stream_parser_buffer); buffer_list_.push_back(stream_parser_buffer); ProcessPendingBuffers(false); + return true; } void EsAdapterVideo::ProcessPendingBuffers(bool flush) { @@ -160,16 +195,26 @@ base::TimeDelta EsAdapterVideo::GetNextFramePts(base::TimeDelta current_pts) { void EsAdapterVideo::ReplaceDiscardedFrames( const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { - DCHECK(!discarded_frames_dts_.empty()); + DCHECK_GT(discarded_frame_count_, 0); DCHECK(stream_parser_buffer->IsKeyframe()); - // PTS is interpolated between the min PTS of discarded frames - // and the PTS of the first valid buffer. - base::TimeDelta pts = discarded_frames_min_pts_; + // PTS/DTS are interpolated between the min PTS/DTS of discarded frames + // and the PTS/DTS of the first valid buffer. + // Note: |pts_delta| and |dts_delta| are calculated using integer division. + // Interpolation thus accumulutes small errors. However, since timestamps + // are given in microseconds, only a high number of discarded frames + // (in the order of 10000s) could have an impact and create a gap (from MSE + // point of view) between the last interpolated frame and + // |stream_parser_buffer|. + base::TimeDelta pts = min_pts_; base::TimeDelta pts_delta = - (stream_parser_buffer->timestamp() - pts) / discarded_frames_dts_.size(); + (stream_parser_buffer->timestamp() - pts) / discarded_frame_count_; + DecodeTimestamp dts = min_dts_; + base::TimeDelta dts_delta = + (stream_parser_buffer->GetDecodeTimestamp() - dts) / + discarded_frame_count_; - while (!discarded_frames_dts_.empty()) { + for (int i = 0; i < discarded_frame_count_; i++) { scoped_refptr<StreamParserBuffer> frame = StreamParserBuffer::CopyFrom( stream_parser_buffer->data(), @@ -177,13 +222,14 @@ void EsAdapterVideo::ReplaceDiscardedFrames( stream_parser_buffer->IsKeyframe(), stream_parser_buffer->type(), stream_parser_buffer->track_id()); - frame->SetDecodeTimestamp(discarded_frames_dts_.front()); + frame->SetDecodeTimestamp(dts); frame->set_timestamp(pts); frame->set_duration(pts_delta); buffer_list_.push_back(frame); pts += pts_delta; - discarded_frames_dts_.pop_front(); + dts += dts_delta; } + discarded_frame_count_ = 0; } } // namespace mp2t diff --git a/media/formats/mp2t/es_adapter_video.h b/media/formats/mp2t/es_adapter_video.h index 2127235..f231169 100644 --- a/media/formats/mp2t/es_adapter_video.h +++ b/media/formats/mp2t/es_adapter_video.h @@ -48,7 +48,8 @@ class MEDIA_EXPORT EsAdapterVideo { void OnConfigChanged(const VideoDecoderConfig& video_decoder_config); // Provide a new video buffer. - void OnNewBuffer( + // Returns true when successful. + bool OnNewBuffer( const scoped_refptr<StreamParserBuffer>& stream_parser_buffer); private: @@ -85,10 +86,13 @@ class MEDIA_EXPORT EsAdapterVideo { BufferQueue buffer_list_; std::list<base::TimeDelta> emitted_pts_; - // - Minimum PTS of discarded frames. - // - DTS of discarded frames. - base::TimeDelta discarded_frames_min_pts_; - std::list<DecodeTimestamp> discarded_frames_dts_; + // Minimum PTS/DTS since the last Reset. + bool has_valid_initial_timestamp_; + base::TimeDelta min_pts_; + DecodeTimestamp min_dts_; + + // Number of frames to replace with the first valid key frame. + int discarded_frame_count_; DISALLOW_COPY_AND_ASSIGN(EsAdapterVideo); }; diff --git a/media/formats/mp2t/es_adapter_video_unittest.cc b/media/formats/mp2t/es_adapter_video_unittest.cc index a5446cf..906d8e1 100644 --- a/media/formats/mp2t/es_adapter_video_unittest.cc +++ b/media/formats/mp2t/es_adapter_video_unittest.cc @@ -45,8 +45,12 @@ GenerateFakeBuffers(const int* frame_pts_ms, buffers[k] = StreamParserBuffer::CopyFrom( dummy_buffer, arraysize(dummy_buffer), is_key_frame[k], DemuxerStream::VIDEO, 0); - buffers[k]->set_timestamp( - base::TimeDelta::FromMilliseconds(frame_pts_ms[k])); + if (frame_pts_ms[k] < 0) { + buffers[k]->set_timestamp(kNoTimestamp()); + } else { + buffers[k]->set_timestamp( + base::TimeDelta::FromMilliseconds(frame_pts_ms[k])); + } } return buffers; } @@ -144,5 +148,25 @@ TEST_F(EsAdapterVideoTest, LeadingNonKeyFrames) { RunAdapterTest(buffer_queue)); } +TEST_F(EsAdapterVideoTest, LeadingKeyFrameWithNoTimestamp) { + int pts_ms[] = {-1, 40, 50, 120, 150, 180}; + bool is_key_frame[] = {true, false, false, true, false, false}; + StreamParserBuffer::BufferQueue buffer_queue = + GenerateFakeBuffers(pts_ms, is_key_frame, arraysize(pts_ms)); + + EXPECT_EQ("(40,Y) (40,Y) (30,Y) (30,N) (30,N)", + RunAdapterTest(buffer_queue)); +} + +TEST_F(EsAdapterVideoTest, LeadingFramesWithNoTimestamp) { + int pts_ms[] = {-1, -1, 50, 120, 150, 180}; + bool is_key_frame[] = {false, false, false, true, false, false}; + StreamParserBuffer::BufferQueue buffer_queue = + GenerateFakeBuffers(pts_ms, is_key_frame, arraysize(pts_ms)); + + EXPECT_EQ("(70,Y) (30,Y) (30,N) (30,N)", + RunAdapterTest(buffer_queue)); +} + } // namespace mp2t } // namespace media diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc index d847b34..dc85cdc 100644 --- a/media/formats/mp2t/es_parser_h264.cc +++ b/media/formats/mp2t/es_parser_h264.cc @@ -200,10 +200,18 @@ bool EsParserH264::ParseFromEsQueue() { bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size, bool is_key_frame, int pps_id) { // Get the access unit timing info. + // Note: |current_timing_desc.pts| might be |kNoTimestamp()| at this point + // if: + // - the stream is not fully MPEG-2 compliant. + // - or if the stream relies on H264 VUI parameters to compute the timestamps. + // See H.222 spec: section 2.7.5 "Conditional coding of timestamps". + // This part is not yet implemented in EsParserH264. + // |es_adapter_| will take care of the missing timestamps. TimingDesc current_timing_desc = GetTimingDescriptor(access_unit_pos); - if (current_timing_desc.pts == kNoTimestamp()) - return false; + DVLOG_IF(1, current_timing_desc.pts == kNoTimestamp()) + << "Missing timestamp"; + // If only the PTS is provided, copy the PTS into the DTS. if (current_timing_desc.dts == kNoDecodeTimestamp()) { current_timing_desc.dts = DecodeTimestamp::FromPresentationTime(current_timing_desc.pts); @@ -245,8 +253,7 @@ bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size, 0); stream_parser_buffer->SetDecodeTimestamp(current_timing_desc.dts); stream_parser_buffer->set_timestamp(current_timing_desc.pts); - es_adapter_.OnNewBuffer(stream_parser_buffer); - return true; + return es_adapter_.OnNewBuffer(stream_parser_buffer); } bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) { |