summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordamienv <damienv@chromium.org>2014-10-09 07:35:16 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-09 14:35:30 +0000
commit8012df46f572e5a7bc2f33ff41e5d470e0da8a69 (patch)
tree31d2c73850d92a01e89635338089df717983948a /media
parentf4adb2a5430e64d66b28a627a824cc12e013035a (diff)
downloadchromium_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.cc88
-rw-r--r--media/formats/mp2t/es_adapter_video.h14
-rw-r--r--media/formats/mp2t/es_adapter_video_unittest.cc28
-rw-r--r--media/formats/mp2t/es_parser_h264.cc15
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) {