summaryrefslogtreecommitdiffstats
path: root/webkit/glue/webmediaplayer_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/webmediaplayer_impl.cc')
-rw-r--r--webkit/glue/webmediaplayer_impl.cc711
1 files changed, 711 insertions, 0 deletions
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
new file mode 100644
index 0000000..51d67ab
--- /dev/null
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -0,0 +1,711 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webmediaplayer_impl.h"
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "media/base/limits.h"
+#include "media/base/media_format.h"
+#include "media/base/media_switches.h"
+#include "media/filters/ffmpeg_audio_decoder.h"
+#include "media/filters/ffmpeg_demuxer.h"
+#include "media/filters/ffmpeg_video_decoder.h"
+#include "media/filters/null_audio_renderer.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "webkit/glue/media/video_renderer_impl.h"
+#include "webkit/glue/media/web_video_renderer.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebRect;
+using WebKit::WebSize;
+
+namespace {
+
+// Limits the maximum outstanding repaints posted on render thread.
+// This number of 50 is a guess, it does not take too much memory on the task
+// queue but gives up a pretty good latency on repaint.
+const int kMaxOutstandingRepaints = 50;
+
+// Limits the range of playback rate.
+//
+// TODO(kylep): Revisit these.
+//
+// Vista has substantially lower performance than XP or Windows7. If you speed
+// up a video too much, it can't keep up, and rendering stops updating except on
+// the time bar. For really high speeds, audio becomes a bottleneck and we just
+// use up the data we have, which may not achieve the speed requested, but will
+// not crash the tab.
+//
+// A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
+// like a busy loop). It gets unresponsive, although its not completely dead.
+//
+// Also our timers are not very accurate (especially for ogg), which becomes
+// evident at low speeds and on Vista. Since other speeds are risky and outside
+// the norms, we think 1/16x to 16x is a safe and useful range for now.
+const float kMinRate = 0.0625f;
+const float kMaxRate = 16.0f;
+
+} // namespace
+
+namespace webkit_glue {
+
+/////////////////////////////////////////////////////////////////////////////
+// WebMediaPlayerImpl::Proxy implementation
+
+WebMediaPlayerImpl::Proxy::Proxy(MessageLoop* render_loop,
+ WebMediaPlayerImpl* webmediaplayer)
+ : render_loop_(render_loop),
+ webmediaplayer_(webmediaplayer),
+ outstanding_repaints_(0) {
+ DCHECK(render_loop_);
+ DCHECK(webmediaplayer_);
+}
+
+WebMediaPlayerImpl::Proxy::~Proxy() {
+ Detach();
+}
+
+void WebMediaPlayerImpl::Proxy::Repaint() {
+ AutoLock auto_lock(lock_);
+ if (outstanding_repaints_ < kMaxOutstandingRepaints) {
+ ++outstanding_repaints_;
+
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &WebMediaPlayerImpl::Proxy::RepaintTask));
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::SetVideoRenderer(
+ WebVideoRenderer* video_renderer) {
+ video_renderer_ = video_renderer;
+}
+
+void WebMediaPlayerImpl::Proxy::Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (video_renderer_) {
+ video_renderer_->Paint(canvas, dest_rect);
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::SetSize(const gfx::Rect& rect) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (video_renderer_) {
+ video_renderer_->SetRect(rect);
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::Detach() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ webmediaplayer_ = NULL;
+ video_renderer_ = NULL;
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineInitializationTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineSeekCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineSeekTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineEndedCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineEndedTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineErrorCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineErrorTask));
+}
+
+void WebMediaPlayerImpl::Proxy::NetworkEventCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::NetworkEventTask));
+}
+
+void WebMediaPlayerImpl::Proxy::RepaintTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ {
+ AutoLock auto_lock(lock_);
+ --outstanding_repaints_;
+ DCHECK_GE(outstanding_repaints_, 0);
+ }
+ if (webmediaplayer_) {
+ webmediaplayer_->Repaint();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineInitializationTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineInitialize();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineSeekTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineSeek();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineEndedTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineEnded();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineErrorTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineError();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::NetworkEventTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnNetworkEvent();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// WebMediaPlayerImpl implementation
+
+WebMediaPlayerImpl::WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client,
+ media::FilterFactoryCollection* factory,
+ WebVideoRendererFactoryFactory*
+ video_renderer_factory)
+ : network_state_(WebKit::WebMediaPlayer::Empty),
+ ready_state_(WebKit::WebMediaPlayer::HaveNothing),
+ main_loop_(NULL),
+ filter_factory_(factory),
+ pipeline_thread_("PipelineThread"),
+ paused_(true),
+ playback_rate_(0.0f),
+ client_(client),
+ pipeline_stopped_(false, false) {
+ // Saves the current message loop.
+ DCHECK(!main_loop_);
+ main_loop_ = MessageLoop::current();
+
+ // Make sure this gets deleted.
+ scoped_ptr<WebVideoRendererFactoryFactory>
+ scoped_video_renderer_factory(video_renderer_factory);
+
+ // Create the pipeline and its thread.
+ if (!pipeline_thread_.Start()) {
+ NOTREACHED() << "Could not start PipelineThread";
+ return;
+ }
+
+ pipeline_ = new media::PipelineImpl(pipeline_thread_.message_loop());
+
+ // Also we want to be notified of |main_loop_| destruction.
+ main_loop_->AddDestructionObserver(this);
+
+ // Creates the proxy.
+ proxy_ = new Proxy(main_loop_, this);
+
+ // Set our pipeline callbacks.
+ pipeline_->SetPipelineEndedCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineEndedCallback));
+ pipeline_->SetPipelineErrorCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineErrorCallback));
+ pipeline_->SetNetworkEventCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::NetworkEventCallback));
+
+ // Add in the default filter factories.
+ filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
+ filter_factory_->AddFactory(media::FFmpegAudioDecoder::CreateFactory());
+ filter_factory_->AddFactory(media::FFmpegVideoDecoder::CreateFactory());
+ filter_factory_->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
+ filter_factory_->AddFactory(video_renderer_factory->CreateFactory(proxy_));
+}
+
+WebMediaPlayerImpl::~WebMediaPlayerImpl() {
+ Destroy();
+
+ // Finally tell the |main_loop_| we don't want to be notified of destruction
+ // event.
+ if (main_loop_) {
+ main_loop_->RemoveDestructionObserver(this);
+ }
+}
+
+void WebMediaPlayerImpl::load(const WebKit::WebURL& url) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+ // Handle any volume changes that occured before load().
+ setVolume(GetClient()->volume());
+
+ // Initialize the pipeline.
+ SetNetworkState(WebKit::WebMediaPlayer::Loading);
+ SetReadyState(WebKit::WebMediaPlayer::HaveNothing);
+ pipeline_->Start(
+ filter_factory_.get(),
+ url.spec(),
+ NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineInitializationCallback));
+}
+
+void WebMediaPlayerImpl::cancelLoad() {
+ DCHECK(MessageLoop::current() == main_loop_);
+}
+
+void WebMediaPlayerImpl::play() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ paused_ = false;
+ pipeline_->SetPlaybackRate(playback_rate_);
+}
+
+void WebMediaPlayerImpl::pause() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ paused_ = true;
+ pipeline_->SetPlaybackRate(0.0f);
+ paused_time_ = pipeline_->GetCurrentTime();
+}
+
+bool WebMediaPlayerImpl::supportsFullscreen() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+ return true;
+}
+
+bool WebMediaPlayerImpl::supportsSave() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+ return true;
+}
+
+void WebMediaPlayerImpl::seek(float seconds) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // WebKit fires a seek(0) at the very start, however pipeline already does a
+ // seek(0) internally. Avoid doing seek(0) the second time because this will
+ // cause extra pre-rolling and will break servers without range request
+ // support.
+ //
+ // We still have to notify WebKit that time has changed otherwise
+ // HTMLMediaElement gets into an inconsistent state.
+ if (pipeline_->GetCurrentTime().ToInternalValue() == 0 && seconds == 0) {
+ GetClient()->timeChanged();
+ return;
+ }
+
+ // Drop our ready state if the media file isn't fully loaded.
+ if (!pipeline_->IsLoaded()) {
+ SetReadyState(WebKit::WebMediaPlayer::HaveMetadata);
+ }
+
+ // Try to preserve as much accuracy as possible.
+ float microseconds = seconds * base::Time::kMicrosecondsPerSecond;
+ base::TimeDelta seek_time =
+ base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds));
+
+ // Update our paused time.
+ if (paused_) {
+ paused_time_ = seek_time;
+ }
+
+ // Kick off the asynchronous seek!
+ pipeline_->Seek(
+ seek_time,
+ NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineSeekCallback));
+}
+
+void WebMediaPlayerImpl::setEndTime(float seconds) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): add method call when it has been implemented.
+ return;
+}
+
+void WebMediaPlayerImpl::setRate(float rate) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(kylep): Remove when support for negatives is added. Also, modify the
+ // following checks so rewind uses reasonable values also.
+ if (rate < 0.0f)
+ return;
+
+ // Limit rates to reasonable values by clamping.
+ if (rate != 0.0f) {
+ if (rate < kMinRate)
+ rate = kMinRate;
+ else if (rate > kMaxRate)
+ rate = kMaxRate;
+ }
+
+ playback_rate_ = rate;
+ if (!paused_) {
+ pipeline_->SetPlaybackRate(rate);
+ }
+}
+
+void WebMediaPlayerImpl::setVolume(float volume) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ pipeline_->SetVolume(volume);
+}
+
+void WebMediaPlayerImpl::setVisible(bool visible) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): add appropriate method call when pipeline has it implemented.
+ return;
+}
+
+bool WebMediaPlayerImpl::setAutoBuffer(bool autoBuffer) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return false;
+}
+
+bool WebMediaPlayerImpl::totalBytesKnown() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetTotalBytes() != 0;
+}
+
+bool WebMediaPlayerImpl::hasVideo() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->IsRendered(media::mime_type::kMajorTypeVideo);
+}
+
+bool WebMediaPlayerImpl::hasAudio() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->IsRendered(media::mime_type::kMajorTypeAudio);
+}
+
+WebKit::WebSize WebMediaPlayerImpl::naturalSize() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ size_t width, height;
+ pipeline_->GetVideoSize(&width, &height);
+ return WebKit::WebSize(width, height);
+}
+
+bool WebMediaPlayerImpl::paused() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetPlaybackRate() == 0.0f;
+}
+
+bool WebMediaPlayerImpl::seeking() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ if (ready_state_ == WebKit::WebMediaPlayer::HaveNothing)
+ return false;
+
+ return ready_state_ == WebKit::WebMediaPlayer::HaveMetadata;
+}
+
+float WebMediaPlayerImpl::duration() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+}
+
+float WebMediaPlayerImpl::currentTime() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ if (paused_) {
+ return static_cast<float>(paused_time_.InSecondsF());
+ }
+ return static_cast<float>(pipeline_->GetCurrentTime().InSecondsF());
+}
+
+int WebMediaPlayerImpl::dataRate() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): Add this method call if pipeline has it in the interface.
+ return 0;
+}
+
+const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // Update buffered_ with the most recent buffered time.
+ if (buffered_.size() > 0) {
+ float buffered_time = static_cast<float>(
+ pipeline_->GetBufferedTime().InSecondsF());
+ if (buffered_time >= buffered_[0].start)
+ buffered_[0].end = buffered_time;
+ }
+
+ return buffered_;
+}
+
+float WebMediaPlayerImpl::maxTimeSeekable() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // If we are performing streaming, we report that we cannot seek at all.
+ // We are using this flag to indicate if the data source supports seeking
+ // or not. We should be able to seek even if we are performing streaming.
+ // TODO(hclam): We need to update this when we have better caching.
+ if (pipeline_->IsStreaming())
+ return 0.0f;
+ return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+}
+
+unsigned long long WebMediaPlayerImpl::bytesLoaded() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetBufferedBytes();
+}
+
+unsigned long long WebMediaPlayerImpl::totalBytes() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetTotalBytes();
+}
+
+void WebMediaPlayerImpl::setSize(const WebSize& size) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+ proxy_->SetSize(gfx::Rect(0, 0, size.width, size.height));
+}
+
+void WebMediaPlayerImpl::paint(WebCanvas* canvas,
+ const WebRect& rect) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+#if WEBKIT_USING_SKIA
+ proxy_->Paint(canvas, rect);
+#elif WEBKIT_USING_CG
+ // Get the current scaling in X and Y.
+ CGAffineTransform mat = CGContextGetCTM(canvas);
+ float scale_x = sqrt(mat.a * mat.a + mat.b * mat.b);
+ float scale_y = sqrt(mat.c * mat.c + mat.d * mat.d);
+ float inverse_scale_x = SkScalarNearlyZero(scale_x) ? 0.0f : 1.0f / scale_x;
+ float inverse_scale_y = SkScalarNearlyZero(scale_y) ? 0.0f : 1.0f / scale_y;
+ int scaled_width = static_cast<int>(rect.width * fabs(scale_x));
+ int scaled_height = static_cast<int>(rect.height * fabs(scale_y));
+
+ // Make sure we don't create a huge canvas.
+ // TODO(hclam): Respect the aspect ratio.
+ if (scaled_width > static_cast<int>(media::Limits::kMaxCanvas))
+ scaled_width = media::Limits::kMaxCanvas;
+ if (scaled_height > static_cast<int>(media::Limits::kMaxCanvas))
+ scaled_height = media::Limits::kMaxCanvas;
+
+ // If there is no preexisting platform canvas, or if the size has
+ // changed, recreate the canvas. This is to avoid recreating the bitmap
+ // buffer over and over for each frame of video.
+ if (!skia_canvas_.get() ||
+ skia_canvas_->getDevice()->width() != scaled_width ||
+ skia_canvas_->getDevice()->height() != scaled_height) {
+ skia_canvas_.reset(
+ new skia::PlatformCanvas(scaled_width, scaled_height, true));
+ }
+
+ // Draw to our temporary skia canvas.
+ gfx::Rect normalized_rect(scaled_width, scaled_height);
+ proxy_->Paint(skia_canvas_.get(), normalized_rect);
+
+ // The mac coordinate system is flipped vertical from the normal skia
+ // coordinates. During painting of the frame, flip the coordinates
+ // system and, for simplicity, also translate the clip rectangle to
+ // start at 0,0.
+ CGContextSaveGState(canvas);
+ CGContextTranslateCTM(canvas, rect.x, rect.height + rect.y);
+ CGContextScaleCTM(canvas, inverse_scale_x, -inverse_scale_y);
+
+ // We need a local variable CGRect version for DrawToContext.
+ CGRect normalized_cgrect =
+ CGRectMake(normalized_rect.x(), normalized_rect.y(),
+ normalized_rect.width(), normalized_rect.height());
+
+ // Copy the frame rendered to our temporary skia canvas onto the passed in
+ // canvas.
+ skia_canvas_->getTopPlatformDevice().DrawToContext(canvas, 0, 0,
+ &normalized_cgrect);
+
+ CGContextRestoreGState(canvas);
+#else
+ NOTIMPLEMENTED() << "We only support rendering to skia or CG";
+#endif
+}
+
+bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
+ // TODO(scherkus): we'll need to do something smarter here if/when we start to
+ // support formats that contain references to external resources (i.e., MP4s
+ // containing links to other MP4s). See http://crbug.com/25432
+ return true;
+}
+
+WebKit::WebMediaPlayer::MovieLoadType
+ WebMediaPlayerImpl::movieLoadType() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): If the pipeline is performing streaming, we say that this is
+ // a live stream. But instead it should be a StoredStream if we have proper
+ // caching.
+ if (pipeline_->IsStreaming())
+ return WebKit::WebMediaPlayer::LiveStream;
+ return WebKit::WebMediaPlayer::Unknown;
+}
+
+void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
+ Destroy();
+ main_loop_ = NULL;
+}
+
+void WebMediaPlayerImpl::Repaint() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ GetClient()->repaint();
+}
+
+void WebMediaPlayerImpl::OnPipelineInitialize() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ // Only keep one time range starting from 0.
+ WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1));
+ new_buffered[0].start = 0.0f;
+ new_buffered[0].end =
+ static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+ buffered_.swap(new_buffered);
+
+ // Since we have initialized the pipeline, say we have everything otherwise
+ // we'll remain either loading/idle.
+ // TODO(hclam): change this to report the correct status.
+ SetReadyState(WebKit::WebMediaPlayer::HaveMetadata);
+ SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
+ if (pipeline_->IsLoaded()) {
+ SetNetworkState(WebKit::WebMediaPlayer::Loaded);
+ }
+ } else {
+ // TODO(hclam): should use pipeline_->GetError() to determine the state
+ // properly and reports error using MediaError.
+ // WebKit uses FormatError to indicate an error for bogus URL or bad file.
+ // Since we are at the initialization stage we can safely treat every error
+ // as format error. Should post a task to call to |webmediaplayer_|.
+ SetNetworkState(WebKit::WebMediaPlayer::FormatError);
+ }
+
+ // Repaint to trigger UI update.
+ Repaint();
+}
+
+void WebMediaPlayerImpl::OnPipelineSeek() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ // Update our paused time.
+ if (paused_) {
+ paused_time_ = pipeline_->GetCurrentTime();
+ }
+
+ SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
+ GetClient()->timeChanged();
+ }
+}
+
+void WebMediaPlayerImpl::OnPipelineEnded() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ GetClient()->timeChanged();
+ }
+}
+
+void WebMediaPlayerImpl::OnPipelineError() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ switch (pipeline_->GetError()) {
+ case media::PIPELINE_OK:
+ case media::PIPELINE_ERROR_INITIALIZATION_FAILED:
+ case media::PIPELINE_ERROR_REQUIRED_FILTER_MISSING:
+ case media::PIPELINE_ERROR_COULD_NOT_RENDER:
+ case media::PIPELINE_ERROR_URL_NOT_FOUND:
+ case media::PIPELINE_ERROR_NETWORK:
+ case media::PIPELINE_ERROR_READ:
+ case media::DEMUXER_ERROR_COULD_NOT_OPEN:
+ case media::DEMUXER_ERROR_COULD_NOT_PARSE:
+ case media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS:
+ case media::DEMUXER_ERROR_COULD_NOT_CREATE_THREAD:
+ // Format error.
+ SetNetworkState(WebMediaPlayer::FormatError);
+ break;
+
+ case media::PIPELINE_ERROR_DECODE:
+ case media::PIPELINE_ERROR_ABORT:
+ case media::PIPELINE_ERROR_OUT_OF_MEMORY:
+ case media::PIPELINE_ERROR_AUDIO_HARDWARE:
+ // Decode error.
+ SetNetworkState(WebMediaPlayer::DecodeError);
+ break;
+ }
+
+ // Repaint to trigger UI update.
+ Repaint();
+}
+
+void WebMediaPlayerImpl::OnNetworkEvent() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ if (pipeline_->IsNetworkActive())
+ SetNetworkState(WebKit::WebMediaPlayer::Loading);
+ else
+ SetNetworkState(WebKit::WebMediaPlayer::Idle);
+ }
+}
+
+void WebMediaPlayerImpl::SetNetworkState(
+ WebKit::WebMediaPlayer::NetworkState state) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ // Always notify to ensure client has the latest value.
+ network_state_ = state;
+ GetClient()->networkStateChanged();
+}
+
+void WebMediaPlayerImpl::SetReadyState(
+ WebKit::WebMediaPlayer::ReadyState state) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ // Always notify to ensure client has the latest value.
+ ready_state_ = state;
+ GetClient()->readyStateChanged();
+}
+
+void WebMediaPlayerImpl::Destroy() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // Make sure to kill the pipeline so there's no more media threads running.
+ // Note: stopping the pipeline might block for a long time.
+ pipeline_->Stop(NewCallback(this,
+ &WebMediaPlayerImpl::PipelineStoppedCallback));
+ pipeline_stopped_.Wait();
+ pipeline_thread_.Stop();
+
+ // And then detach the proxy, it may live on the render thread for a little
+ // longer until all the tasks are finished.
+ if (proxy_) {
+ proxy_->Detach();
+ proxy_ = NULL;
+ }
+}
+
+void WebMediaPlayerImpl::PipelineStoppedCallback() {
+ pipeline_stopped_.Signal();
+}
+
+WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(client_);
+ return client_;
+}
+
+} // namespace webkit_glue