diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 00:07:54 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 00:07:54 +0000 |
commit | 930c7473b7ee905ceec94850034ce4e6fa441ca1 (patch) | |
tree | be9275385886253b39daa8ac488894679d0b0d63 /media | |
parent | 371fcec03ecd24e18dd8af63bb2b020650af6dbd (diff) | |
download | chromium_src-930c7473b7ee905ceec94850034ce4e6fa441ca1.zip chromium_src-930c7473b7ee905ceec94850034ce4e6fa441ca1.tar.gz chromium_src-930c7473b7ee905ceec94850034ce4e6fa441ca1.tar.bz2 |
Implement accurate seeking by decoding audio/video until we reach our desired timestamp.
This fixes a host of seeking, synchronization and layout test issues but does incur a CPU performance hit while seeking. In the worst case a seek could take quite some time if the content was high resolution H.264 and we attempted to seek to right before a keyframe.
We'll see how people like the new behaviour and back this change out if needed.
BUG=48984
TEST=layout tests, media_unittests
Review URL: http://codereview.chromium.org/3032030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53872 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/audio_renderer_base.cc | 8 | ||||
-rw-r--r-- | media/filters/audio_renderer_base.h | 2 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.cc | 11 | ||||
-rw-r--r-- | media/filters/video_renderer_base.cc | 15 | ||||
-rw-r--r-- | media/filters/video_renderer_base.h | 2 |
5 files changed, 33 insertions, 5 deletions
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc index f3f9294..c0690e8 100644 --- a/media/filters/audio_renderer_base.cc +++ b/media/filters/audio_renderer_base.cc @@ -67,6 +67,7 @@ void AudioRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; state_ = kSeeking; seek_callback_.reset(callback); + seek_timestamp_ = time; // Throw away everything and schedule our reads. last_fill_buffer_time_ = base::TimeDelta(); @@ -147,9 +148,14 @@ void AudioRendererBase::OnFillBufferDone(scoped_refptr<Buffer> buffer_in) { return; } - // Don't enqueue an end-of-stream buffer because it has no data. + // Don't enqueue an end-of-stream buffer because it has no data, otherwise + // discard decoded audio data until we reach our desired seek timestamp. if (buffer_in->IsEndOfStream()) { recieved_end_of_stream_ = true; + } else if (state_ == kSeeking && !buffer_in->IsEndOfStream() && + (buffer_in->GetTimestamp() + buffer_in->GetDuration()) < + seek_timestamp_) { + ScheduleRead_Locked(); } else { // Note: Calling this may schedule more reads. algorithm_->EnqueueBuffer(buffer_in); diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h index 279d59f..fc9cac6 100644 --- a/media/filters/audio_renderer_base.h +++ b/media/filters/audio_renderer_base.h @@ -135,6 +135,8 @@ class AudioRendererBase : public AudioRenderer { scoped_ptr<FilterCallback> pause_callback_; scoped_ptr<FilterCallback> seek_callback_; + base::TimeDelta seek_timestamp_; + DISALLOW_COPY_AND_ASSIGN(AudioRendererBase); }; diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc index 09b0caf..133e7dc 100644 --- a/media/filters/ffmpeg_audio_decoder.cc +++ b/media/filters/ffmpeg_audio_decoder.cc @@ -155,6 +155,17 @@ static void ConvertAudioF32ToS32(void* buffer, int buffer_size) { } void FFmpegAudioDecoder::DoDecode(Buffer* input) { + // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us + // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find + // something valid. + // TODO(hclam): remove this once fixing the issue in FFmpeg. + if (input->GetTimestamp() == StreamSample::kInvalidTimestamp && + estimated_next_timestamp_ == StreamSample::kInvalidTimestamp && + !input->IsEndOfStream()) { + DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(); + return; + } + // Due to FFmpeg API changes we no longer have const read-only pointers. AVPacket packet; av_init_packet(&packet); diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc index c50dfd4..00cf9dc 100644 --- a/media/filters/video_renderer_base.cc +++ b/media/filters/video_renderer_base.cc @@ -151,6 +151,7 @@ void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; state_ = kSeeking; seek_callback_.reset(callback); + seek_timestamp_ = time; // Throw away everything and schedule our reads. // TODO(jiesun): this should be guaranteed by pause/flush before seek happen. @@ -390,10 +391,16 @@ void VideoRendererBase::OnFillBufferDone(scoped_refptr<VideoFrame> frame) { DCHECK_GT(pending_reads_, 0u); --pending_reads_; - // Enqueue the frame. - frames_queue_ready_.push_back(frame); - DCHECK_LE(frames_queue_ready_.size(), kMaxFrames); - frame_available_.Signal(); + // Discard frames until we reach our desired seek timestamp. + if (state_ == kSeeking && !frame->IsEndOfStream() && + (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) { + frames_queue_done_.push_back(frame); + ScheduleRead_Locked(); + } else { + frames_queue_ready_.push_back(frame); + DCHECK_LE(frames_queue_ready_.size(), kMaxFrames); + frame_available_.Signal(); + } // Check for our preroll complete condition. if (state_ == kSeeking) { diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h index ff5c232..f8176f7 100644 --- a/media/filters/video_renderer_base.h +++ b/media/filters/video_renderer_base.h @@ -181,6 +181,8 @@ class VideoRendererBase : public VideoRenderer, scoped_ptr<FilterCallback> pause_callback_; scoped_ptr<FilterCallback> seek_callback_; + base::TimeDelta seek_timestamp_; + DISALLOW_COPY_AND_ASSIGN(VideoRendererBase); }; |