diff options
author | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-25 01:28:39 +0000 |
---|---|---|
committer | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-25 01:28:39 +0000 |
commit | 5f04c353a604c8e3f654019c9c267529455a8fbf (patch) | |
tree | e56168cdbb4df164d4d0dd9677b94835be9bb403 | |
parent | a380186d005aaa91bf62dbefca4f6fd17329afba (diff) | |
download | chromium_src-5f04c353a604c8e3f654019c9c267529455a8fbf.zip chromium_src-5f04c353a604c8e3f654019c9c267529455a8fbf.tar.gz chromium_src-5f04c353a604c8e3f654019c9c267529455a8fbf.tar.bz2 |
create a separate WebMediaPlayer for URL derived from media stream
When the "src" of <video> is derived from media stream, WebMediaPlayerMS is created.
Its real time charateristics allows it to simplify some controls in WebMediaPlayer, e.g., no buffering, no preload, etc.
WebMediaPlayerMS has 2 different VideoFrameProviders: LocalVideoCapture(for local preview) and RTCVideoRender(for remote view).
Audio will be added in the following patches.
BUG=142988,110938
TEST=turn on WebMediaPlayer_MS, run apprtc.appspot.com/?debug=loopback
Review URL: https://chromiumcodereview.appspot.com/10918052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158490 0039d316-1c4b-4281-b951-d872f2087c98
23 files changed, 1377 insertions, 16 deletions
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index c49779b..2a8648f 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -802,6 +802,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableGpuBenchmarking, switches::kEnableLogging, switches::kDisableMediaSource, + switches::kEnableWebMediaPlayerMS, switches::kEnablePartialSwap, switches::kEnablePerTilePainting, switches::kEnableRendererSideMixing, diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index f41d930..6e09766 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -119,6 +119,8 @@ 'renderer/media/audio_renderer_mixer_manager.h', 'renderer/media/capture_video_decoder.cc', 'renderer/media/capture_video_decoder.h', + 'renderer/media/local_video_capture.cc', + 'renderer/media/local_video_capture.h', 'renderer/media/media_stream_center.h', 'renderer/media/media_stream_dependency_factory.h', 'renderer/media/media_stream_dispatcher.h', @@ -136,6 +138,8 @@ 'renderer/media/renderer_webaudiodevice_impl.h', 'renderer/media/rtc_video_decoder.cc', 'renderer/media/rtc_video_decoder.h', + 'renderer/media/rtc_video_renderer.cc', + 'renderer/media/rtc_video_renderer.h', 'renderer/media/stream_texture_factory_impl_android.cc', 'renderer/media/stream_texture_factory_impl_android.h', 'renderer/media/video_capture_impl.cc', diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index f10405c..fc63c93 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -293,6 +293,10 @@ const char kEnableLogging[] = "enable-logging"; // Disable Media Source API on <audio>/<video> elements. const char kDisableMediaSource[] = "disable-media-source"; +// Enables using WebMediaPlayerMS for src of <audio>/<video> derived from +// media stream. +const char kEnableWebMediaPlayerMS[] = "enable-web-media-player-ms"; + // On Windows, converts the page to the currently-installed monitor profile. // This does NOT enable color management for images. The source is still // assumed to be sRGB. diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index b247e42..80f9123 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -104,6 +104,7 @@ extern const char kEnableGestureTapHighlight[]; extern const char kEnableGpuBenchmarking[]; CONTENT_EXPORT extern const char kEnableLogging[]; extern const char kDisableMediaSource[]; +extern const char kEnableWebMediaPlayerMS[]; extern const char kEnableMonitorProfile[]; extern const char kEnablePartialSwap[]; extern const char kEnableUIReleaseFrontSurface[]; diff --git a/content/renderer/media/local_video_capture.cc b/content/renderer/media/local_video_capture.cc new file mode 100644 index 0000000..f9dd216 --- /dev/null +++ b/content/renderer/media/local_video_capture.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2012 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 "content/renderer/media/local_video_capture.h" + +#include "content/renderer/media/video_capture_impl_manager.h" +#include "media/base/video_util.h" +#include "media/video/capture/video_capture_proxy.h" + +using media::CopyYPlane; +using media::CopyUPlane; +using media::CopyVPlane; + +namespace content { + +LocalVideoCapture::LocalVideoCapture( + media::VideoCaptureSessionId video_stream_id, + VideoCaptureImplManager* vc_manager, + const media::VideoCaptureCapability& capability, + const base::Closure& error_cb, + const RepaintCB& repaint_cb) + : video_stream_id_(video_stream_id), + vc_manager_(vc_manager), + capability_(capability), + error_cb_(error_cb), + repaint_cb_(repaint_cb), + capture_engine_(NULL), + message_loop_proxy_(base::MessageLoopProxy::current()), + ALLOW_THIS_IN_INITIALIZER_LIST( + handler_proxy_(new media::VideoCaptureHandlerProxy( + this, message_loop_proxy_))), + state_(video_capture::kStopped) { + DVLOG(3) << "LocalVideoCapture::ctor"; + DCHECK(vc_manager); +} + +LocalVideoCapture::~LocalVideoCapture() { + DVLOG(3) << "LocalVideoCapture::dtor"; +} + +void LocalVideoCapture::Start() { + DVLOG(3) << "LocalVideoCapture::Start"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK_EQ(state_, video_capture::kStopped); + + capture_engine_ = vc_manager_->AddDevice(video_stream_id_, this); + state_ = video_capture::kStarted; + AddRef(); // Will be balanced in OnRemoved(). + capture_engine_->StartCapture(handler_proxy_.get(), capability_); +} + +void LocalVideoCapture::Stop() { + DVLOG(3) << "LocalVideoCapture::Stop"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (capture_engine_) { + state_ = video_capture::kStopping; + capture_engine_->StopCapture(handler_proxy_.get()); + capture_engine_ = NULL; + } +} + +void LocalVideoCapture::Play() { + DVLOG(3) << "LocalVideoCapture::Play"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (capture_engine_ && state_ == video_capture::kPaused) { + state_ = video_capture::kStarted; + } +} + +void LocalVideoCapture::Pause() { + DVLOG(3) << "LocalVideoCapture::Pause"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (capture_engine_ && state_ == video_capture::kStarted) { + state_ = video_capture::kPaused; + } +} + +void LocalVideoCapture::OnStarted(media::VideoCapture* capture) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + NOTIMPLEMENTED(); +} + +void LocalVideoCapture::OnStopped(media::VideoCapture* capture) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + NOTIMPLEMENTED(); +} + +void LocalVideoCapture::OnPaused(media::VideoCapture* capture) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + NOTIMPLEMENTED(); +} + +void LocalVideoCapture::OnError(media::VideoCapture* capture, + int error_code) { + DVLOG(3) << "LocalVideoCapture::OnError, " << error_code; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + error_cb_.Run(); +} + +void LocalVideoCapture::OnRemoved(media::VideoCapture* capture) { + DVLOG(3) << "LocalVideoCapture::OnRemoved"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + vc_manager_->RemoveDevice(video_stream_id_, this); + Release(); // Balance the AddRef() in StartCapture(). +} + +void LocalVideoCapture::OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info) { + DVLOG(3) << "LocalVideoCapture::OnDeviceInfoReceived"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); +} + +void LocalVideoCapture::OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { + DVLOG(3) << "LocalVideoCapture::OnBufferReady, state:" << state_; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK(buf); + + if (state_ != video_capture::kStarted) { + capture->FeedBuffer(buf); + return; + } + + gfx::Size natural_size(buf->width, buf->height); + scoped_refptr<media::VideoFrame> current_frame = + media::VideoFrame::CreateFrame(media::VideoFrame::YV12, + natural_size, natural_size, + buf->timestamp - base::Time()); + uint8* buffer = buf->memory_pointer; + + // Assume YV12 format. + DCHECK_EQ(capability_.color, media::VideoCaptureCapability::kI420); + if (capability_.color != media::VideoCaptureCapability::kI420) + return; + + int y_width = buf->width; + int y_height = buf->height; + int uv_width = buf->width / 2; + int uv_height = buf->height / 2; + CopyYPlane(buffer, y_width, y_height, current_frame); + buffer += y_width * y_height; + CopyUPlane(buffer, uv_width, uv_height, current_frame); + buffer += uv_width * uv_height; + CopyVPlane(buffer, uv_width, uv_height, current_frame); + + capture->FeedBuffer(buf); + repaint_cb_.Run(current_frame); +} + +} // namespace content diff --git a/content/renderer/media/local_video_capture.h b/content/renderer/media/local_video_capture.h new file mode 100644 index 0000000..bbf67dc --- /dev/null +++ b/content/renderer/media/local_video_capture.h @@ -0,0 +1,79 @@ +// Copyright (c) 2012 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. + +#ifndef CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_CAPTURE_H_ +#define CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_CAPTURE_H_ + +#include "content/common/content_export.h" +#include "content/common/media/video_capture.h" +#include "media/video/capture/video_capture.h" +#include "webkit/media/video_frame_provider.h" + +namespace base { +class MessageLoopProxy; +} + +namespace media { +class VideoCaptureHandlerProxy; +} + +class VideoCaptureImplManager; + +namespace content { + +// This class takes raw frames from video capture engine via VideoCaptureProxy +// and passes them to media player as a video frame provider. +// This class lives on main thread. +class CONTENT_EXPORT LocalVideoCapture + : NON_EXPORTED_BASE(public webkit_media::VideoFrameProvider), + public media::VideoCapture::EventHandler { + public: + LocalVideoCapture( + media::VideoCaptureSessionId video_stream_id, + VideoCaptureImplManager* vc_manager, + const media::VideoCaptureCapability& capability, + const base::Closure& error_cb, + const RepaintCB& repaint_cb); + + // webkit_media::VideoFrameProvider implementation. + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Play() OVERRIDE; + virtual void Pause() OVERRIDE; + + // VideoCapture::EventHandler implementation. + virtual void OnStarted(media::VideoCapture* capture) OVERRIDE; + virtual void OnStopped(media::VideoCapture* capture) OVERRIDE; + virtual void OnPaused(media::VideoCapture* capture) OVERRIDE; + virtual void OnError(media::VideoCapture* capture, int error_code) OVERRIDE; + virtual void OnRemoved(media::VideoCapture* capture) OVERRIDE; + virtual void OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) OVERRIDE; + virtual void OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info) OVERRIDE; + + protected: + virtual ~LocalVideoCapture(); + + private: + friend class LocalVideoCaptureTest; + + media::VideoCaptureSessionId video_stream_id_; + scoped_refptr<VideoCaptureImplManager> vc_manager_; + media::VideoCaptureCapability capability_; + base::Closure error_cb_; + RepaintCB repaint_cb_; + media::VideoCapture* capture_engine_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + scoped_ptr<media::VideoCaptureHandlerProxy> handler_proxy_; + video_capture::State state_; + + DISALLOW_COPY_AND_ASSIGN(LocalVideoCapture); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_CAPTURE_H_ diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc index 8a7180a..648fe2a 100644 --- a/content/renderer/media/media_stream_impl.cc +++ b/content/renderer/media/media_stream_impl.cc @@ -11,11 +11,13 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "content/renderer/media/capture_video_decoder.h" +#include "content/renderer/media/local_video_capture.h" #include "content/renderer/media/media_stream_extra_data.h" #include "content/renderer/media/media_stream_source_extra_data.h" #include "content/renderer/media/media_stream_dependency_factory.h" #include "content/renderer/media/media_stream_dispatcher.h" #include "content/renderer/media/rtc_video_decoder.h" +#include "content/renderer/media/rtc_video_renderer.h" #include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/media/webrtc_uma_histograms.h" #include "media/base/message_loop_factory.h" @@ -143,6 +145,50 @@ WebKit::WebMediaStreamDescriptor MediaStreamImpl::GetMediaStream( return WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url); } +bool MediaStreamImpl::IsMediaStream(const GURL& url) { + DCHECK(CalledOnValidThread()); + WebKit::WebMediaStreamDescriptor descriptor(GetMediaStream(url)); + + if (descriptor.isNull() || !descriptor.extraData()) + return false; // This is not a valid stream. + + MediaStreamExtraData* extra_data = + static_cast<MediaStreamExtraData*>(descriptor.extraData()); + webrtc::MediaStreamInterface* stream = extra_data->local_stream(); + if (stream && stream->video_tracks() && stream->video_tracks()->count() > 0) + return true; + stream = extra_data->remote_stream(); + if (stream && stream->video_tracks() && stream->video_tracks()->count() > 0) + return true; + return false; +} + +scoped_refptr<webkit_media::VideoFrameProvider> +MediaStreamImpl::GetVideoFrameProvider( + const GURL& url, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) { + DCHECK(CalledOnValidThread()); + WebKit::WebMediaStreamDescriptor descriptor(GetMediaStream(url)); + + if (descriptor.isNull() || !descriptor.extraData()) + return NULL; // This is not a valid stream. + + DVLOG(1) << "MediaStreamImpl::GetVideoFrameProvider stream:" + << UTF16ToUTF8(descriptor.label()); + + MediaStreamExtraData* extra_data = + static_cast<MediaStreamExtraData*>(descriptor.extraData()); + if (extra_data->local_stream()) + return CreateLocalVideoFrameProvider(extra_data->local_stream(), + error_cb, repaint_cb); + if (extra_data->remote_stream()) + return CreateRemoteVideoFrameProvider(extra_data->remote_stream(), + error_cb, repaint_cb); + NOTREACHED(); + return NULL; +} + scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) { @@ -307,6 +353,52 @@ void MediaStreamImpl::FrameWillClose(WebKit::WebFrame* frame) { } } +scoped_refptr<webkit_media::VideoFrameProvider> +MediaStreamImpl::CreateLocalVideoFrameProvider( + webrtc::MediaStreamInterface* stream, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) { + if (!stream->video_tracks() || stream->video_tracks()->count() == 0) + return NULL; + + int video_session_id = + media_stream_dispatcher_->video_session_id(stream->label(), 0); + media::VideoCaptureCapability capability; + capability.width = kVideoCaptureWidth; + capability.height = kVideoCaptureHeight; + capability.frame_rate = kVideoCaptureFramePerSecond; + capability.color = media::VideoCaptureCapability::kI420; + capability.expected_capture_delay = 0; + capability.interlaced = false; + + DVLOG(1) << "MediaStreamImpl::CreateLocalVideoFrameProvider video_session_id:" + << video_session_id; + + return new content::LocalVideoCapture( + video_session_id, + vc_manager_.get(), + capability, + error_cb, + repaint_cb); +} + +scoped_refptr<webkit_media::VideoFrameProvider> +MediaStreamImpl::CreateRemoteVideoFrameProvider( + webrtc::MediaStreamInterface* stream, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) { + if (!stream->video_tracks() || stream->video_tracks()->count() == 0) + return NULL; + + DVLOG(1) << "MediaStreamImpl::CreateRemoteVideoFrameProvider label:" + << stream->label(); + + return new content::RTCVideoRenderer( + stream->video_tracks()->at(0), + error_cb, + repaint_cb); +} + scoped_refptr<media::VideoDecoder> MediaStreamImpl::CreateLocalVideoDecoder( webrtc::MediaStreamInterface* stream, media::MessageLoopFactory* message_loop_factory) { diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h index 7279e37..1ffca32 100644 --- a/content/renderer/media/media_stream_impl.h +++ b/content/renderer/media/media_stream_impl.h @@ -61,6 +61,11 @@ class CONTENT_EXPORT MediaStreamImpl const WebKit::WebUserMediaRequest& user_media_request) OVERRIDE; // webkit_media::MediaStreamClient implementation. + virtual bool IsMediaStream(const GURL& url) OVERRIDE; + virtual scoped_refptr<webkit_media::VideoFrameProvider> GetVideoFrameProvider( + const GURL& url, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) OVERRIDE; virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) OVERRIDE; @@ -123,6 +128,16 @@ class CONTENT_EXPORT MediaStreamImpl typedef std::map<std::string, WebKit::WebFrame*> LocalNativeStreamMap; typedef scoped_refptr<webrtc::LocalMediaStreamInterface> LocalNativeStreamPtr; + scoped_refptr<webkit_media::VideoFrameProvider> + CreateLocalVideoFrameProvider( + webrtc::MediaStreamInterface* stream, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb); + scoped_refptr<webkit_media::VideoFrameProvider> + CreateRemoteVideoFrameProvider( + webrtc::MediaStreamInterface* stream, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb); scoped_refptr<media::VideoDecoder> CreateLocalVideoDecoder( webrtc::MediaStreamInterface* stream, media::MessageLoopFactory* message_loop_factory); diff --git a/content/renderer/media/rtc_video_renderer.cc b/content/renderer/media/rtc_video_renderer.cc new file mode 100644 index 0000000..14444fa --- /dev/null +++ b/content/renderer/media/rtc_video_renderer.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2012 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 "content/renderer/media/rtc_video_renderer.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop_proxy.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "third_party/libjingle/source/talk/base/timeutils.h" +#include "third_party/libjingle/source/talk/media/base/videoframe.h" + +using media::CopyYPlane; +using media::CopyUPlane; +using media::CopyVPlane; + +namespace content { + +RTCVideoRenderer::RTCVideoRenderer( + webrtc::VideoTrackInterface* video_track, + const base::Closure& error_cb, + const RepaintCB& repaint_cb) + : error_cb_(error_cb), + repaint_cb_(repaint_cb), + message_loop_proxy_(base::MessageLoopProxy::current()), + state_(kStopped), + video_track_(video_track) { +} + +RTCVideoRenderer::~RTCVideoRenderer() { +} + +void RTCVideoRenderer::Start() { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK_EQ(state_, kStopped); + + if (video_track_) + video_track_->AddRenderer(this); + state_ = kStarted; +} + +void RTCVideoRenderer::Stop() { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (video_track_) { + state_ = kStopped; + video_track_->RemoveRenderer(this); + video_track_ = NULL; + } +} + +void RTCVideoRenderer::Play() { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (video_track_ && state_ == kPaused) { + state_ = kStarted; + } +} + +void RTCVideoRenderer::Pause() { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (video_track_ && state_ == kStarted) { + state_ = kPaused; + } +} + +void RTCVideoRenderer::SetSize(int width, int height) { +} + +void RTCVideoRenderer::RenderFrame(const cricket::VideoFrame* frame) { + base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( + frame->GetTimeStamp() / talk_base::kNumNanosecsPerMillisec); + gfx::Size size(frame->GetWidth(), frame->GetHeight()); + scoped_refptr<media::VideoFrame> video_frame = + media::VideoFrame::CreateFrame(media::VideoFrame::YV12, + size, + size, + timestamp); + + // Aspect ratio unsupported; DCHECK when there are non-square pixels. + DCHECK_EQ(frame->GetPixelWidth(), 1u); + DCHECK_EQ(frame->GetPixelHeight(), 1u); + + int y_rows = frame->GetHeight(); + int uv_rows = frame->GetHeight() / 2; // YV12 format. + CopyYPlane(frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame); + CopyUPlane(frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame); + CopyVPlane(frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame); + + message_loop_proxy_->PostTask( + FROM_HERE, base::Bind(&RTCVideoRenderer::DoRenderFrameOnMainThread, + this, video_frame)); +} + +void RTCVideoRenderer::DoRenderFrameOnMainThread( + scoped_refptr<media::VideoFrame> video_frame) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + if (state_ != kStarted) { + return; + } + + repaint_cb_.Run(video_frame); +} + +} // namespace content diff --git a/content/renderer/media/rtc_video_renderer.h b/content/renderer/media/rtc_video_renderer.h new file mode 100644 index 0000000..925e70b --- /dev/null +++ b/content/renderer/media/rtc_video_renderer.h @@ -0,0 +1,73 @@ +// Copyright (c) 2012 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. + +#ifndef CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_RENDERER_H_ +#define CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_RENDERER_H_ + +#include "base/callback.h" +#include "content/common/content_export.h" +#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" +#include "ui/gfx/size.h" +#include "webkit/media/video_frame_provider.h" + +namespace base { +class MessageLoopProxy; +} + +namespace content { + +// RTCVideoRenderer is a webkit_media::VideoFrameProvider designed for rendering +// Video MediaStreamTracks, +// http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediastreamtrack +// RTCVideoRenderer implements webrtc::VideoRendererInterface in order to render +// video frames provided from a webrtc::VideoTrackInteface. +// RTCVideoRenderer register itself to the Video Track when the +// VideoFrameProvider is started and deregisters itself when it is stopped. +// Calls to webrtc::VideoTrackInterface must occur on the main thread. +class CONTENT_EXPORT RTCVideoRenderer + : NON_EXPORTED_BASE(public webkit_media::VideoFrameProvider), + NON_EXPORTED_BASE(public webrtc::VideoRendererInterface) { + public: + RTCVideoRenderer( + webrtc::VideoTrackInterface* video_track, + const base::Closure& error_cb, + const RepaintCB& repaint_cb); + + // webkit_media::VideoFrameProvider implementation. Called on the main thread. + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Play() OVERRIDE; + virtual void Pause() OVERRIDE; + + // webrtc::VideoRendererInterface implementation. May be called on + // a different thread. + virtual void SetSize(int width, int height) OVERRIDE; + virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE; + + protected: + virtual ~RTCVideoRenderer(); + + private: + enum State { + kStarted, + kPaused, + kStopped, + }; + + void DoRenderFrameOnMainThread(scoped_refptr<media::VideoFrame> video_frame); + + base::Closure error_cb_; + RepaintCB repaint_cb_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + State state_; + + // The video track the renderer is connected to. + scoped_refptr<webrtc::VideoTrackInterface> video_track_; + + DISALLOW_COPY_AND_ASSIGN(RTCVideoRenderer); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_LOCAL_VIDEO_RENDERER_H_ diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index bf8ed52..37fa6f3 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -194,6 +194,7 @@ #include "webkit/glue/weburlresponse_extradata_impl.h" #include "webkit/gpu/webgraphicscontext3d_in_process_impl.h" #include "webkit/media/webmediaplayer_impl.h" +#include "webkit/media/webmediaplayer_ms.h" #include "webkit/plugins/npapi/plugin_list.h" #include "webkit/plugins/npapi/webplugin_delegate.h" #include "webkit/plugins/npapi/webplugin_delegate_impl.h" @@ -2577,8 +2578,7 @@ WebMediaPlayer* RenderViewImpl::createMediaPlayer( } WebGraphicsContext3DCommandBufferImpl* context3d = NULL; - if (!CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableAcceleratedVideoDecode)) + if (!cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) context3d = RenderThreadImpl::current()->GetGpuVDAContext3D(); if (context3d) { scoped_refptr<base::MessageLoopProxy> factories_loop = @@ -2604,6 +2604,16 @@ WebMediaPlayer* RenderViewImpl::createMediaPlayer( audio_source_provider, message_loop_factory, media_stream_impl_, render_media_log); if (!media_player) { + // TODO(wjia): when all patches related to WebMediaPlayerMS have been + // landed, remove the switch. Refer to crbug.com/142988. + if (cmd_line->HasSwitch(switches::kEnableWebMediaPlayerMS)) { + EnsureMediaStreamImpl(); + if (media_stream_impl_ && media_stream_impl_->IsMediaStream(url)) { + return new webkit_media::WebMediaPlayerMS( + frame, client, AsWeakPtr(), media_stream_impl_, render_media_log); + } + } + media_player = new webkit_media::WebMediaPlayerImpl( frame, client, AsWeakPtr(), collection, audio_source_provider, audio_source_provider, message_loop_factory, media_stream_impl_, diff --git a/media/video/capture/video_capture.h b/media/video/capture/video_capture.h index d0aef98..a63843e 100644 --- a/media/video/capture/video_capture.h +++ b/media/video/capture/video_capture.h @@ -43,6 +43,7 @@ class MEDIA_EXPORT VideoCapture { }; // TODO(wjia): add error codes. + // TODO(wjia): support weak ptr. // Callbacks provided by client for notification of events. class MEDIA_EXPORT EventHandler { public: diff --git a/webkit/media/media_stream_client.h b/webkit/media/media_stream_client.h index ab1e767..edbb9b8 100644 --- a/webkit/media/media_stream_client.h +++ b/webkit/media/media_stream_client.h @@ -5,7 +5,9 @@ #ifndef WEBKIT_MEDIA_MEDIA_STREAM_CLIENT_H_ #define WEBKIT_MEDIA_MEDIA_STREAM_CLIENT_H_ +#include "base/callback.h" #include "base/memory/ref_counted.h" +#include "webkit/media/video_frame_provider.h" class GURL; @@ -20,6 +22,14 @@ namespace webkit_media { // the media stream. class MediaStreamClient { public: + // Check if the |url| is derived from a media stream object. + virtual bool IsMediaStream(const GURL& url) = 0; + + virtual scoped_refptr<VideoFrameProvider> GetVideoFrameProvider( + const GURL& url, + const base::Closure& error_cb, + const VideoFrameProvider::RepaintCB& repaint_cb) = 0; + virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) = 0; diff --git a/webkit/media/simple_video_frame_provider.cc b/webkit/media/simple_video_frame_provider.cc new file mode 100644 index 0000000..afdf3f8 --- /dev/null +++ b/webkit/media/simple_video_frame_provider.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2012 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/media/simple_video_frame_provider.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop_proxy.h" +#include "media/base/video_frame.h" + +namespace webkit_media { + +SimpleVideoFrameProvider::SimpleVideoFrameProvider( + const gfx::Size& size, + const base::TimeDelta& frame_duration, + const base::Closure& error_cb, + const VideoFrameProvider::RepaintCB& repaint_cb) + : message_loop_proxy_(base::MessageLoopProxy::current()), + size_(size), + state_(kStopped), + frame_duration_(frame_duration), + error_cb_(error_cb), + repaint_cb_(repaint_cb) { +} + +SimpleVideoFrameProvider::~SimpleVideoFrameProvider() {} + +void SimpleVideoFrameProvider::Start() { + DVLOG(1) << "SimpleVideoFrameProvider::Start"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + state_ = kStarted; + message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&SimpleVideoFrameProvider::GenerateFrame, this)); +} + +void SimpleVideoFrameProvider::Stop() { + DVLOG(1) << "SimpleVideoFrameProvider::Stop"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + state_ = kStopped; +} + +void SimpleVideoFrameProvider::Play() { + DVLOG(1) << "SimpleVideoFrameProvider::Play"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (state_ == kPaused) + state_ = kStarted; +} + +void SimpleVideoFrameProvider::Pause() { + DVLOG(1) << "SimpleVideoFrameProvider::Pause"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (state_ == kStarted) + state_ = kPaused; +} + +void SimpleVideoFrameProvider::GenerateFrame() { + DVLOG(1) << "SimpleVideoFrameProvider::GenerateFrame"; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (state_ == kStopped) + return; + + if (state_ == kStarted) { + // Always allocate a new frame. + scoped_refptr<media::VideoFrame> video_frame = + media::VideoFrame::CreateFrame(media::VideoFrame::YV12, + size_, + size_, + current_time_); + + // TODO(wjia): set pixel data to pre-defined patterns if it's desired to + // verify frame content. + + repaint_cb_.Run(video_frame); + } + + current_time_ += frame_duration_; + message_loop_proxy_->PostDelayedTask( + FROM_HERE, + base::Bind(&SimpleVideoFrameProvider::GenerateFrame, this), + frame_duration_); +} + +} // namespace webkit_media diff --git a/webkit/media/simple_video_frame_provider.h b/webkit/media/simple_video_frame_provider.h new file mode 100644 index 0000000..e217fa0 --- /dev/null +++ b/webkit/media/simple_video_frame_provider.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 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. + +#ifndef WEBKIT_MEDIA_SIMPLE_VIDEO_FRAME_PROVIDER_H_ +#define WEBKIT_MEDIA_SIMPLE_VIDEO_FRAME_PROVIDER_H_ + +#include "base/time.h" +#include "ui/gfx/size.h" +#include "webkit/media/video_frame_provider.h" + +namespace base { +class MessageLoopProxy; +} + +namespace webkit_media { + +// A simple implementation of VideoFrameProvider generates raw frames and +// passes them to webmediaplayer. +class SimpleVideoFrameProvider : public VideoFrameProvider { + public: + SimpleVideoFrameProvider( + const gfx::Size& size, + const base::TimeDelta& frame_duration, + const base::Closure& error_cb, + const RepaintCB& repaint_cb); + + // VideoFrameProvider implementation. + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Play() OVERRIDE; + virtual void Pause() OVERRIDE; + + protected: + virtual ~SimpleVideoFrameProvider(); + + private: + enum State { + kStarted, + kPaused, + kStopped, + }; + + void GenerateFrame(); + + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + gfx::Size size_; + State state_; + + base::TimeDelta current_time_; + base::TimeDelta frame_duration_; + base::Closure error_cb_; + RepaintCB repaint_cb_; + + DISALLOW_COPY_AND_ASSIGN(SimpleVideoFrameProvider); +}; + +} // namespace webkit_media + +#endif // WEBKIT_MEDIA_SIMPLE_VIDEO_FRAME_PROVIDER_H_ diff --git a/webkit/media/video_frame_provider.cc b/webkit/media/video_frame_provider.cc new file mode 100644 index 0000000..40818f2 --- /dev/null +++ b/webkit/media/video_frame_provider.cc @@ -0,0 +1,13 @@ +// Copyright (c) 2012 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/media/video_frame_provider.h" + +namespace webkit_media { + +VideoFrameProvider::VideoFrameProvider() {} + +VideoFrameProvider::~VideoFrameProvider() {} + +} // namespace webkit_media diff --git a/webkit/media/video_frame_provider.h b/webkit/media/video_frame_provider.h new file mode 100644 index 0000000..f6a15cb --- /dev/null +++ b/webkit/media/video_frame_provider.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 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. + +#ifndef WEBKIT_MEDIA_VIDEO_FRAME_PROVIDER_H_ +#define WEBKIT_MEDIA_VIDEO_FRAME_PROVIDER_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" + +namespace media { +class VideoFrame; +} + +namespace webkit_media { + +// Define an interface to provide a sequence of video frames to clients. +// TODO(wjia): remove ref count. +class VideoFrameProvider + : public base::RefCountedThreadSafe<VideoFrameProvider> { + public: + typedef base::Callback<void(const scoped_refptr<media::VideoFrame>&)> + RepaintCB; + + // Start to provide video frames to the caller. + virtual void Start() = 0; + + // Stop to provide video frames to the caller. + virtual void Stop() = 0; + + // Resume to provide video frames to the caller after being paused. + virtual void Play() = 0; + + // Put the provider in pause state and the caller will not receive video + // frames, but the provider might still generate video frames which are + // thrown away. + virtual void Pause() = 0; + + protected: + friend class base::RefCountedThreadSafe<VideoFrameProvider>; + VideoFrameProvider(); + virtual ~VideoFrameProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(VideoFrameProvider); +}; + +} // namespace webkit_media + +#endif // WEBKIT_MEDIA_VIDEO_FRAME_PROVIDER_H_ diff --git a/webkit/media/webkit_media.gypi b/webkit/media/webkit_media.gypi index 77bb53f..54cc585 100644 --- a/webkit/media/webkit_media.gypi +++ b/webkit/media/webkit_media.gypi @@ -51,11 +51,17 @@ 'filter_helpers.h', 'media_stream_client.h', 'preload.h', + 'simple_video_frame_provider.cc', + 'simple_video_frame_provider.h', 'skcanvas_video_renderer.cc', 'skcanvas_video_renderer.h', + 'video_frame_provider.cc', + 'video_frame_provider.h', 'webmediaplayer_delegate.h', 'webmediaplayer_impl.cc', 'webmediaplayer_impl.h', + 'webmediaplayer_ms.cc', + 'webmediaplayer_ms.h', 'webmediaplayer_proxy.cc', 'webmediaplayer_proxy.h', 'webmediaplayer_util.cc', diff --git a/webkit/media/webmediaplayer_ms.cc b/webkit/media/webmediaplayer_ms.cc new file mode 100644 index 0000000..6e09d9d --- /dev/null +++ b/webkit/media/webmediaplayer_ms.cc @@ -0,0 +1,398 @@ +// Copyright (c) 2012 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/media/webmediaplayer_ms.h" + +#include <limits> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/message_loop.h" +#include "base/metrics/histogram.h" +#include "media/base/media_log.h" +#include "media/base/video_frame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebVideoFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h" +#include "webkit/media/media_stream_client.h" +#include "webkit/media/video_frame_provider.h" +#include "webkit/media/webmediaplayer_delegate.h" +#include "webkit/media/webmediaplayer_util.h" +#include "webkit/media/webvideoframe_impl.h" + +using WebKit::WebCanvas; +using WebKit::WebMediaPlayer; +using WebKit::WebRect; +using WebKit::WebSize; + +namespace webkit_media { + +WebMediaPlayerMS::WebMediaPlayerMS( + WebKit::WebFrame* frame, + WebKit::WebMediaPlayerClient* client, + base::WeakPtr<WebMediaPlayerDelegate> delegate, + MediaStreamClient* media_stream_client, + media::MediaLog* media_log) + : frame_(frame), + network_state_(WebMediaPlayer::NetworkStateEmpty), + ready_state_(WebMediaPlayer::ReadyStateHaveNothing), + buffered_(static_cast<size_t>(1)), + client_(client), + delegate_(delegate), + media_stream_client_(media_stream_client), + video_frame_provider_started_(false), + paused_(true), + pending_repaint_(false), + got_first_frame_(false), + total_frame_count_(0), + dropped_frame_count_(0), + media_log_(media_log) { + DVLOG(1) << "WebMediaPlayerMS::ctor"; + DCHECK(media_stream_client); + media_log_->AddEvent( + media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED)); +} + +WebMediaPlayerMS::~WebMediaPlayerMS() { + DVLOG(1) << "WebMediaPlayerMS::dtor"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (video_frame_provider_) { + video_frame_provider_->Stop(); + } + + media_log_->AddEvent( + media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED)); + + if (delegate_) + delegate_->PlayerGone(this); +} + +void WebMediaPlayerMS::load(const WebKit::WebURL& url, CORSMode cors_mode) { + DVLOG(1) << "WebMediaPlayerMS::load"; + DCHECK(thread_checker_.CalledOnValidThread()); + + GURL gurl(url); + + setVolume(GetClient()->volume()); + SetNetworkState(WebMediaPlayer::NetworkStateLoading); + SetReadyState(WebMediaPlayer::ReadyStateHaveNothing); + media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec())); + + // Check if this url is media stream. + video_frame_provider_ = media_stream_client_->GetVideoFrameProvider( + url, + base::Bind(&WebMediaPlayerMS::OnSourceError, AsWeakPtr()), + base::Bind(&WebMediaPlayerMS::OnFrameAvailable, AsWeakPtr())); + if (video_frame_provider_) { + SetNetworkState(WebMediaPlayer::NetworkStateLoaded); + GetClient()->sourceOpened(); + GetClient()->setOpaque(true); + SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata); + SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); + RepaintInternal(); + } else { + SetNetworkState(WebMediaPlayer::NetworkStateNetworkError); + } +} + +void WebMediaPlayerMS::cancelLoad() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebMediaPlayerMS::play() { + DVLOG(1) << "WebMediaPlayerMS::play"; + DCHECK(thread_checker_.CalledOnValidThread()); + + paused_ = false; + if (video_frame_provider_) { + if (video_frame_provider_started_) { + video_frame_provider_->Play(); + } else { + video_frame_provider_started_ = true; + video_frame_provider_->Start(); + } + } + // TODO(wjia): add audio. See crbug.com/142988. + + media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY)); + + if (delegate_) + delegate_->DidPlay(this); +} + +void WebMediaPlayerMS::pause() { + DVLOG(1) << "WebMediaPlayerMS::pause"; + DCHECK(thread_checker_.CalledOnValidThread()); + + if (video_frame_provider_) + video_frame_provider_->Pause(); + // TODO(wjia): add audio. See crbug.com/142988. + paused_ = true; + + media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE)); + + if (delegate_) + delegate_->DidPause(this); +} + +bool WebMediaPlayerMS::supportsFullscreen() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return true; +} + +bool WebMediaPlayerMS::supportsSave() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return false; +} + +void WebMediaPlayerMS::seek(float seconds) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebMediaPlayerMS::setEndTime(float seconds) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebMediaPlayerMS::setRate(float rate) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebMediaPlayerMS::setVolume(float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + // TODO(wjia): set audio volume. See crbug.com/142988. + NOTIMPLEMENTED(); +} + +void WebMediaPlayerMS::setVisible(bool visible) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebMediaPlayerMS::setPreload(WebMediaPlayer::Preload preload) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +bool WebMediaPlayerMS::totalBytesKnown() { + DCHECK(thread_checker_.CalledOnValidThread()); + return false; +} + +bool WebMediaPlayerMS::hasVideo() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return (video_frame_provider_ != NULL); +} + +bool WebMediaPlayerMS::hasAudio() const { + DCHECK(thread_checker_.CalledOnValidThread()); + // TODO(wjia): add audio support. See crbug.com/142988. + return false; +} + +WebKit::WebSize WebMediaPlayerMS::naturalSize() const { + DCHECK(thread_checker_.CalledOnValidThread()); + + gfx::Size size; + if (current_frame_) + size = current_frame_->natural_size(); + DVLOG(1) << "WebMediaPlayerMS::naturalSize, " << size.ToString(); + return WebKit::WebSize(size); +} + +bool WebMediaPlayerMS::paused() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return paused_; +} + +bool WebMediaPlayerMS::seeking() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return false; +} + +float WebMediaPlayerMS::duration() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return std::numeric_limits<float>::infinity(); +} + +float WebMediaPlayerMS::currentTime() const { + DCHECK(thread_checker_.CalledOnValidThread()); + if (current_frame_.get()) { + return current_frame_->GetTimestamp().InSecondsF(); + } + return 0.0f; +} + +int WebMediaPlayerMS::dataRate() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return 0; +} + +WebMediaPlayer::NetworkState WebMediaPlayerMS::networkState() const { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(1) << "WebMediaPlayerMS::networkState, state:" << network_state_; + return network_state_; +} + +WebMediaPlayer::ReadyState WebMediaPlayerMS::readyState() const { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(1) << "WebMediaPlayerMS::readyState, state:" << ready_state_; + return ready_state_; +} + +const WebKit::WebTimeRanges& WebMediaPlayerMS::buffered() { + DCHECK(thread_checker_.CalledOnValidThread()); + return buffered_; +} + +float WebMediaPlayerMS::maxTimeSeekable() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return 0.0f; +} + +bool WebMediaPlayerMS::didLoadingProgress() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return false; +} + +unsigned long long WebMediaPlayerMS::totalBytes() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return 0; +} + +void WebMediaPlayerMS::setSize(const WebSize& size) { + DCHECK(thread_checker_.CalledOnValidThread()); + // Don't need to do anything as we use the dimensions passed in via paint(). +} + +void WebMediaPlayerMS::paint(WebCanvas* canvas, + const WebRect& rect, + uint8_t alpha) { + DVLOG(1) << "WebMediaPlayerMS::paint"; + DCHECK(thread_checker_.CalledOnValidThread()); + + video_renderer_.Paint(current_frame_, canvas, rect, alpha); +} + +bool WebMediaPlayerMS::hasSingleSecurityOrigin() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return true; +} + +bool WebMediaPlayerMS::didPassCORSAccessCheck() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return true; +} + +WebMediaPlayer::MovieLoadType WebMediaPlayerMS::movieLoadType() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return WebMediaPlayer::MovieLoadTypeUnknown; +} + +float WebMediaPlayerMS::mediaTimeForTimeValue(float timeValue) const { + return ConvertSecondsToTimestamp(timeValue).InSecondsF(); +} + +unsigned WebMediaPlayerMS::decodedFrameCount() const { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(1) << "WebMediaPlayerMS::decodedFrameCount, " << total_frame_count_; + return total_frame_count_; +} + +unsigned WebMediaPlayerMS::droppedFrameCount() const { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(1) << "WebMediaPlayerMS::droppedFrameCount, " << dropped_frame_count_; + return dropped_frame_count_; +} + +unsigned WebMediaPlayerMS::audioDecodedByteCount() const { + DCHECK(thread_checker_.CalledOnValidThread()); + NOTIMPLEMENTED(); + return 0; +} + +unsigned WebMediaPlayerMS::videoDecodedByteCount() const { + DCHECK(thread_checker_.CalledOnValidThread()); + NOTIMPLEMENTED(); + return 0; +} + +WebKit::WebVideoFrame* WebMediaPlayerMS::getCurrentFrame() { + DVLOG(1) << "WebMediaPlayerMS::getCurrentFrame"; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!pending_repaint_); + pending_repaint_ = true; + if (current_frame_.get()) + return new webkit_media::WebVideoFrameImpl(current_frame_); + return NULL; +} + +void WebMediaPlayerMS::putCurrentFrame( + WebKit::WebVideoFrame* web_video_frame) { + DVLOG(1) << "WebMediaPlayerMS::putCurrentFrame"; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(pending_repaint_); + pending_repaint_ = false; + if (web_video_frame) { + delete web_video_frame; + } +} + +void WebMediaPlayerMS::OnFrameAvailable( + const scoped_refptr<media::VideoFrame>& frame) { + DVLOG(1) << "WebMediaPlayerMS::OnFrameAvailable"; + DCHECK(thread_checker_.CalledOnValidThread()); + ++total_frame_count_; + if (!got_first_frame_) { + got_first_frame_ = true; + start_time_ = frame->GetTimestamp(); + } + current_frame_ = frame; + current_frame_->SetTimestamp(frame->GetTimestamp() - start_time_); + if (pending_repaint_) { + // TODO(wjia): Figure out how to calculate dropped frame count for + // both S/W and H/W compositing. + // ++dropped_frame_count_; + } else { + GetClient()->repaint(); + } +} + +void WebMediaPlayerMS::RepaintInternal() { + DVLOG(1) << "WebMediaPlayerMS::RepaintInternal"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!pending_repaint_) { + GetClient()->repaint(); + } +} + +void WebMediaPlayerMS::OnSourceError() { + DVLOG(1) << "WebMediaPlayerMS::OnSourceError"; + DCHECK(thread_checker_.CalledOnValidThread()); + SetNetworkState(WebMediaPlayer::NetworkStateFormatError); + RepaintInternal(); +} + +void WebMediaPlayerMS::SetNetworkState(WebMediaPlayer::NetworkState state) { + DCHECK(thread_checker_.CalledOnValidThread()); + network_state_ = state; + // Always notify to ensure client has the latest value. + GetClient()->networkStateChanged(); +} + +void WebMediaPlayerMS::SetReadyState(WebMediaPlayer::ReadyState state) { + DCHECK(thread_checker_.CalledOnValidThread()); + ready_state_ = state; + // Always notify to ensure client has the latest value. + GetClient()->readyStateChanged(); +} + +WebKit::WebMediaPlayerClient* WebMediaPlayerMS::GetClient() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(client_); + return client_; +} + +} // namespace webkit_media diff --git a/webkit/media/webmediaplayer_ms.h b/webkit/media/webmediaplayer_ms.h new file mode 100644 index 0000000..e49adc0 --- /dev/null +++ b/webkit/media/webmediaplayer_ms.h @@ -0,0 +1,170 @@ +// Copyright (c) 2012 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. + +#ifndef WEBKIT_MEDIA_WEBMEDIAPLAYER_MS_H_ +#define WEBKIT_MEDIA_WEBMEDIAPLAYER_MS_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "googleurl/src/gurl.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayer.h" +#include "webkit/media/skcanvas_video_renderer.h" + +namespace WebKit { +class WebFrame; +class WebMediaPlayerClient; +} + +namespace media { +class MediaLog; +} + +namespace webkit_media { + +class MediaStreamClient; +class VideoFrameProvider; +class WebMediaPlayerDelegate; + +// WebMediaPlayerMS delegates calls from WebCore::MediaPlayerPrivate to +// Chrome's media player when "src" is from media stream. +// +// WebMediaPlayerMS works with multiple objects, the most important ones are: +// +// VideoFrameProvider +// provides video frames for rendering. +// +// TODO(wjia): add AudioPlayer. +// AudioPlayer +// plays audio streams. +// +// WebKit::WebMediaPlayerClient +// WebKit client of this media player object. +class WebMediaPlayerMS + : public WebKit::WebMediaPlayer, + public base::SupportsWeakPtr<WebMediaPlayerMS> { + public: + // Construct a WebMediaPlayerMS with reference to the client, and + // a MediaStreamClient which provides VideoFrameProvider. + WebMediaPlayerMS(WebKit::WebFrame* frame, + WebKit::WebMediaPlayerClient* client, + base::WeakPtr<WebMediaPlayerDelegate> delegate, + MediaStreamClient* media_stream_client, + media::MediaLog* media_log); + virtual ~WebMediaPlayerMS(); + + virtual void load(const WebKit::WebURL& url, CORSMode cors_mode) OVERRIDE; + virtual void cancelLoad() OVERRIDE; + + // Playback controls. + virtual void play() OVERRIDE; + virtual void pause() OVERRIDE; + virtual bool supportsFullscreen() const OVERRIDE; + virtual bool supportsSave() const OVERRIDE; + virtual void seek(float seconds) OVERRIDE; + virtual void setEndTime(float seconds) OVERRIDE; + virtual void setRate(float rate) OVERRIDE; + virtual void setVolume(float volume) OVERRIDE; + virtual void setVisible(bool visible) OVERRIDE; + virtual void setPreload(WebKit::WebMediaPlayer::Preload preload) OVERRIDE; + virtual bool totalBytesKnown() OVERRIDE; + virtual const WebKit::WebTimeRanges& buffered() OVERRIDE; + virtual float maxTimeSeekable() const OVERRIDE; + + // Methods for painting. + virtual void setSize(const WebKit::WebSize& size) OVERRIDE; + + virtual void paint(WebKit::WebCanvas* canvas, + const WebKit::WebRect& rect, + uint8_t alpha) OVERRIDE; + + // True if the loaded media has a playable video/audio track. + virtual bool hasVideo() const OVERRIDE; + virtual bool hasAudio() const OVERRIDE; + + // Dimensions of the video. + virtual WebKit::WebSize naturalSize() const OVERRIDE; + + // Getters of playback state. + virtual bool paused() const OVERRIDE; + virtual bool seeking() const OVERRIDE; + virtual float duration() const OVERRIDE; + virtual float currentTime() const OVERRIDE; + + // Get rate of loading the resource. + virtual int32 dataRate() const OVERRIDE; + + // Internal states of loading and network. + virtual WebKit::WebMediaPlayer::NetworkState networkState() const OVERRIDE; + virtual WebKit::WebMediaPlayer::ReadyState readyState() const OVERRIDE; + + virtual bool didLoadingProgress() const OVERRIDE; + virtual unsigned long long totalBytes() const OVERRIDE; + + virtual bool hasSingleSecurityOrigin() const OVERRIDE; + virtual bool didPassCORSAccessCheck() const OVERRIDE; + virtual WebKit::WebMediaPlayer::MovieLoadType movieLoadType() const OVERRIDE; + + virtual float mediaTimeForTimeValue(float timeValue) const OVERRIDE; + + virtual unsigned decodedFrameCount() const OVERRIDE; + virtual unsigned droppedFrameCount() const OVERRIDE; + virtual unsigned audioDecodedByteCount() const OVERRIDE; + virtual unsigned videoDecodedByteCount() const OVERRIDE; + + virtual WebKit::WebVideoFrame* getCurrentFrame() OVERRIDE; + virtual void putCurrentFrame(WebKit::WebVideoFrame* web_video_frame) OVERRIDE; + + private: + // The callback for VideoFrameProvider to signal a new frame is available. + void OnFrameAvailable(const scoped_refptr<media::VideoFrame>& frame); + // Need repaint due to state change. + void RepaintInternal(); + + // The callback for source to report error. + void OnSourceError(); + + // Helpers that set the network/ready state and notifies the client if + // they've changed. + void SetNetworkState(WebKit::WebMediaPlayer::NetworkState state); + void SetReadyState(WebKit::WebMediaPlayer::ReadyState state); + + // Getter method to |client_|. + WebKit::WebMediaPlayerClient* GetClient(); + + WebKit::WebFrame* frame_; + + WebKit::WebMediaPlayer::NetworkState network_state_; + WebKit::WebMediaPlayer::ReadyState ready_state_; + + WebKit::WebTimeRanges buffered_; + + // Used for DCHECKs to ensure methods calls executed in the correct thread. + base::ThreadChecker thread_checker_; + + WebKit::WebMediaPlayerClient* client_; + + base::WeakPtr<WebMediaPlayerDelegate> delegate_; + + MediaStreamClient* media_stream_client_; + scoped_refptr<VideoFrameProvider> video_frame_provider_; + bool video_frame_provider_started_; + bool paused_; + scoped_refptr<media::VideoFrame> current_frame_; + bool pending_repaint_; + bool got_first_frame_; + base::TimeDelta start_time_; + unsigned total_frame_count_; + unsigned dropped_frame_count_; + SkCanvasVideoRenderer video_renderer_; + + scoped_refptr<media::MediaLog> media_log_; + + DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerMS); +}; + +} // namespace webkit_media + +#endif // WEBKIT_MEDIA_WEBMEDIAPLAYER_MS_H_ diff --git a/webkit/support/test_media_stream_client.cc b/webkit/support/test_media_stream_client.cc index f82b891..70bb8cb 100644 --- a/webkit/support/test_media_stream_client.cc +++ b/webkit/support/test_media_stream_client.cc @@ -8,11 +8,11 @@ #include "media/base/message_loop_factory.h" #include "media/base/pipeline.h" #include "media/filters/video_frame_generator.h" - #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamComponent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" +#include "webkit/media/simple_video_frame_provider.h" using namespace WebKit; @@ -36,6 +36,29 @@ bool IsMockMediaStreamWithVideo(const WebURL& url) { namespace webkit_support { +TestMediaStreamClient::TestMediaStreamClient() {} + +TestMediaStreamClient::~TestMediaStreamClient() {} + +bool TestMediaStreamClient::IsMediaStream(const GURL& url) { + return IsMockMediaStreamWithVideo(url); +} + +scoped_refptr<webkit_media::VideoFrameProvider> +TestMediaStreamClient::GetVideoFrameProvider( + const GURL& url, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) { + if (!IsMockMediaStreamWithVideo(url)) + return NULL; + + return new webkit_media::SimpleVideoFrameProvider( + gfx::Size(kVideoCaptureWidth, kVideoCaptureHeight), + base::TimeDelta::FromMilliseconds(kVideoCaptureFrameDurationMs), + error_cb, + repaint_cb); +} + scoped_refptr<media::VideoDecoder> TestMediaStreamClient::GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) { // This class is installed in a chain of possible VideoDecoder creators diff --git a/webkit/support/test_media_stream_client.h b/webkit/support/test_media_stream_client.h index 471d0c4..500dd2a 100644 --- a/webkit/support/test_media_stream_client.h +++ b/webkit/support/test_media_stream_client.h @@ -9,28 +9,23 @@ #ifndef WEBKIT_SUPPORT_TEST_MEDIA_STREAM_CLIENT_H_ #define WEBKIT_SUPPORT_TEST_MEDIA_STREAM_CLIENT_H_ +#include "base/callback_forward.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h" #include "webkit/media/media_stream_client.h" namespace webkit_support { -// TODO(tommyw): Remove deprecated class after corresponding -// WebKit patch is committed. -class MediaStreamUtil { - public: - virtual bool IsMockStream(const WebKit::WebURL& url) = 0; -}; - class TestMediaStreamClient : public webkit_media::MediaStreamClient { public: - // TODO(tommyw): Remove deprecated constructor after - // corresponding WebKit patch is committed. - explicit TestMediaStreamClient(MediaStreamUtil* media_stream_util) {} - - TestMediaStreamClient() {} - virtual ~TestMediaStreamClient() {} + TestMediaStreamClient(); + virtual ~TestMediaStreamClient(); // Implement webkit_media::MediaStreamClient. + virtual bool IsMediaStream(const GURL& url) OVERRIDE; + virtual scoped_refptr<webkit_media::VideoFrameProvider> GetVideoFrameProvider( + const GURL& url, + const base::Closure& error_cb, + const webkit_media::VideoFrameProvider::RepaintCB& repaint_cb) OVERRIDE; virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( const GURL& url, media::MessageLoopFactory* message_loop_factory) OVERRIDE; diff --git a/webkit/support/webkit_support.cc b/webkit/support/webkit_support.cc index 435c0f4..166acab 100644 --- a/webkit/support/webkit_support.cc +++ b/webkit/support/webkit_support.cc @@ -61,7 +61,9 @@ #include "webkit/media/android/webmediaplayer_in_process_android.h" #include "webkit/media/android/webmediaplayer_manager_android.h" #endif +#include "webkit/media/media_stream_client.h" #include "webkit/media/webmediaplayer_impl.h" +#include "webkit/media/webmediaplayer_ms.h" #include "webkit/plugins/npapi/plugin_list.h" #include "webkit/plugins/npapi/webplugin_impl.h" #include "webkit/plugins/npapi/webplugin_page_delegate.h" @@ -413,6 +415,15 @@ WebKit::WebMediaPlayer* CreateMediaPlayer( scoped_ptr<media::FilterCollection> collection( new media::FilterCollection()); + if (media_stream_client && media_stream_client->IsMediaStream(url)) { + return new webkit_media::WebMediaPlayerMS( + frame, + client, + base::WeakPtr<webkit_media::WebMediaPlayerDelegate>(), + media_stream_client, + new media::MediaLog()); + } + return new webkit_media::WebMediaPlayerImpl( frame, client, |