summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-28 00:07:54 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-28 00:07:54 +0000
commit930c7473b7ee905ceec94850034ce4e6fa441ca1 (patch)
treebe9275385886253b39daa8ac488894679d0b0d63 /media
parent371fcec03ecd24e18dd8af63bb2b020650af6dbd (diff)
downloadchromium_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.cc8
-rw-r--r--media/filters/audio_renderer_base.h2
-rw-r--r--media/filters/ffmpeg_audio_decoder.cc11
-rw-r--r--media/filters/video_renderer_base.cc15
-rw-r--r--media/filters/video_renderer_base.h2
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);
};