summaryrefslogtreecommitdiffstats
path: root/media/base/pipeline_impl.cc
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-12 23:52:05 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-12 23:52:05 +0000
commit576537844b224ca246713c57e039d19d0dfefbf7 (patch)
treeb9cdb7a157abaa212fa87cfbd18ed05c0f6f71e2 /media/base/pipeline_impl.cc
parent1cf1f99e52b39e01115eeef712c139dfa63df00e (diff)
downloadchromium_src-576537844b224ca246713c57e039d19d0dfefbf7.zip
chromium_src-576537844b224ca246713c57e039d19d0dfefbf7.tar.gz
chromium_src-576537844b224ca246713c57e039d19d0dfefbf7.tar.bz2
Implemented end-of-stream callback for media::PipelineImpl.
A new method HasEnded() was added to renderer interfaces. Renderers return true when they have both received and rendered an end-of-stream buffer. For audio this translates to sending the very last buffer to the hardware. For video this translates to displaying a black frame after the very last frame has been displayed. Renderers can notify the pipeline that the value of HasEnded() has changed to true via FilterHost::NotifyEnded(). Instead of tracking which renderers have called NotifyEnded(), the pipeline uses the notification to poll every renderer. The ended callback will only be executed once every renderer returns true for HasEnded(). This has a nice benefit of being able to ignore extra NotifyEnded() calls if we already determine the pipeline has ended. With the changes to WebMediaPlayerImpl, we should now properly support both the ended event and looping. BUG=16768,17970,18433,18846 TEST=media_unittests, media layout tests, ended event, timeupdate should stop firing, looping should work, seeking after video ends Review URL: http://codereview.chromium.org/164403 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23255 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base/pipeline_impl.cc')
-rw-r--r--media/base/pipeline_impl.cc54
1 files changed, 50 insertions, 4 deletions
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index ccfce70..b9ac047 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -149,6 +149,7 @@ bool PipelineImpl::IsInitialized() const {
case kSeeking:
case kStarting:
case kStarted:
+ case kEnded:
return true;
default:
return false;
@@ -201,9 +202,12 @@ void PipelineImpl::SetVolume(float volume) {
}
base::TimeDelta PipelineImpl::GetCurrentTime() const {
+ // TODO(scherkus): perhaps replace checking state_ == kEnded with a bool that
+ // is set/get under the lock, because this is breaching the contract that
+ // |state_| is only accessed on |message_loop_|.
AutoLock auto_lock(lock_);
base::TimeDelta elapsed = clock_.Elapsed();
- if (elapsed > duration_) {
+ if (state_ == kEnded || elapsed > duration_) {
return duration_;
}
return elapsed;
@@ -262,7 +266,15 @@ PipelineError PipelineImpl::GetError() const {
return error_;
}
+void PipelineImpl::SetPipelineEndedCallback(PipelineCallback* ended_callback) {
+ DCHECK(!IsRunning())
+ << "Permanent callbacks should be set before the pipeline has started";
+ ended_callback_.reset(ended_callback);
+}
+
void PipelineImpl::SetPipelineErrorCallback(PipelineCallback* error_callback) {
+ DCHECK(!IsRunning())
+ << "Permanent callbacks should be set before the pipeline has started";
error_callback_.reset(error_callback);
}
@@ -374,6 +386,12 @@ void PipelineImpl::SetStreaming(bool streaming) {
streaming_ = streaming;
}
+void PipelineImpl::NotifyEnded() {
+ DCHECK(IsRunning());
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &PipelineImpl::NotifyEndedTask));
+}
+
void PipelineImpl::BroadcastMessage(FilterMessage message) {
DCHECK(IsRunning());
@@ -612,10 +630,10 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
DCHECK_EQ(MessageLoop::current(), message_loop_);
// Suppress seeking if we're not fully started.
- if (state_ != kStarted) {
+ if (state_ != kStarted && state_ != kEnded) {
// TODO(scherkus): should we run the callback? I'm tempted to say the API
// will only execute the first Seek() request.
- LOG(INFO) << "Media pipeline is not in started state, ignoring seek to "
+ LOG(INFO) << "Media pipeline has not started, ignoring seek to "
<< time.InMicroseconds();
delete seek_callback;
return;
@@ -623,7 +641,7 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
// We'll need to pause every filter before seeking. The state transition
// is as follows:
- // kStarted
+ // kStarted/kEnded
// kPausing (for each filter)
// kSeeking (for each filter)
// kStarting (for each filter)
@@ -639,6 +657,34 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
NewCallback(this, &PipelineImpl::OnFilterStateTransition));
}
+void PipelineImpl::NotifyEndedTask() {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ // We can only end if we were actually playing.
+ if (state_ != kStarted) {
+ return;
+ }
+
+ // Grab the renderers, if they exist.
+ scoped_refptr<AudioRenderer> audio_renderer;
+ scoped_refptr<VideoRenderer> video_renderer;
+ GetFilter(&audio_renderer);
+ GetFilter(&video_renderer);
+ DCHECK(audio_renderer || video_renderer);
+
+ // Make sure every extant renderer has ended.
+ if ((audio_renderer && !audio_renderer->HasEnded()) ||
+ (video_renderer && !video_renderer->HasEnded())) {
+ return;
+ }
+
+ // Transition to ended, executing the callback if present.
+ state_ = kEnded;
+ if (ended_callback_.get()) {
+ ended_callback_->Run();
+ }
+}
+
void PipelineImpl::BroadcastMessageTask(FilterMessage message) {
DCHECK_EQ(MessageLoop::current(), message_loop_);