diff options
22 files changed, 1259 insertions, 90 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fcc312c..e75d5de 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2037,6 +2037,7 @@ '../content/renderer/active_notification_tracker_unittest.cc', '../content/renderer/media/audio_message_filter_unittest.cc', '../content/renderer/media/audio_renderer_impl_unittest.cc', + '../content/renderer/media/capture_video_decoder_unittest.cc', '../content/renderer/media/media_stream_dispatcher_unittest.cc', '../content/renderer/media/rtc_video_decoder_unittest.cc', '../content/renderer/media/video_capture_impl_unittest.cc', diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 33a13a1..6c3c2bf 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -68,9 +68,13 @@ 'renderer/media/audio_message_filter.h', 'renderer/media/audio_renderer_impl.cc', 'renderer/media/audio_renderer_impl.h', + 'renderer/media/capture_video_decoder.cc', + 'renderer/media/capture_video_decoder.h', 'renderer/media/media_stream_dispatcher.cc', 'renderer/media/media_stream_dispatcher.h', 'renderer/media/media_stream_dispatcher_eventhandler.h', + 'renderer/media/media_stream_impl.cc', + 'renderer/media/media_stream_impl.h', 'renderer/media/rtc_video_decoder.cc', 'renderer/media/rtc_video_decoder.h', 'renderer/media/video_capture_impl.cc', @@ -212,6 +216,12 @@ '../third_party/webrtc/video_engine/main/source/video_engine_core.gyp:video_engine_core', '../third_party/webrtc/voice_engine/main/source/voice_engine_core.gyp:voice_engine_core', ], + 'sources': [ + 'renderer/media/video_capture_module_impl.cc', + 'renderer/media/video_capture_module_impl.h', + 'renderer/media/video_capture_module_impl_device_info.cc', + 'renderer/media/video_capture_module_impl_device_info.h', + ], }], ], }, diff --git a/content/renderer/media/capture_video_decoder.cc b/content/renderer/media/capture_video_decoder.cc new file mode 100644 index 0000000..359b8c7 --- /dev/null +++ b/content/renderer/media/capture_video_decoder.cc @@ -0,0 +1,271 @@ +// Copyright (c) 2011 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/capture_video_decoder.h" + +#include "content/renderer/media/video_capture_impl_manager.h" +#include "media/base/filter_host.h" +#include "media/base/limits.h" +#include "media/base/media_format.h" + +CaptureVideoDecoder::CaptureVideoDecoder( + base::MessageLoopProxy* message_loop_proxy, + media::VideoCaptureSessionId video_stream_id, + VideoCaptureImplManager* vc_manager, + const media::VideoCapture::VideoCaptureCapability& capability) + : message_loop_proxy_(message_loop_proxy), + vc_manager_(vc_manager), + capability_(capability), + state_(kUnInitialized), + pending_stop_cb_(NULL), + video_stream_id_(video_stream_id), + capture_engine_(NULL) { + DCHECK(vc_manager); +} + +CaptureVideoDecoder::~CaptureVideoDecoder() {} + +void CaptureVideoDecoder::Initialize(media::DemuxerStream* demuxer_stream, + media::FilterCallback* filter_callback, + media::StatisticsCallback* stat_callback) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::InitializeOnDecoderThread, + make_scoped_refptr(demuxer_stream), + filter_callback, stat_callback)); +} + +const media::MediaFormat& CaptureVideoDecoder::media_format() { + return media_format_; +} + +void CaptureVideoDecoder::ProduceVideoFrame( + scoped_refptr<media::VideoFrame> video_frame) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, + &CaptureVideoDecoder::ProduceVideoFrameOnDecoderThread, video_frame)); +} + +bool CaptureVideoDecoder::ProvidesBuffer() { + return true; +} + +void CaptureVideoDecoder::Play(media::FilterCallback* callback) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::PlayOnDecoderThread, + callback)); +} + +void CaptureVideoDecoder::Pause(media::FilterCallback* callback) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::PauseOnDecoderThread, + callback)); +} + +void CaptureVideoDecoder::Stop(media::FilterCallback* callback) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::StopOnDecoderThread, + callback)); +} + +void CaptureVideoDecoder::Seek(base::TimeDelta time, + const media::FilterStatusCB& cb) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::SeekOnDecoderThread, + time, + cb)); +} + +void CaptureVideoDecoder::OnStarted(media::VideoCapture* capture) { + NOTIMPLEMENTED(); +} + +void CaptureVideoDecoder::OnStopped(media::VideoCapture* capture) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::OnStoppedOnDecoderThread, + capture)); +} + +void CaptureVideoDecoder::OnPaused(media::VideoCapture* capture) { + NOTIMPLEMENTED(); +} + +void CaptureVideoDecoder::OnError(media::VideoCapture* capture, + int error_code) { + NOTIMPLEMENTED(); +} + +void CaptureVideoDecoder::OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { + DCHECK(buf); + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CaptureVideoDecoder::OnBufferReadyOnDecoderThread, + capture, + buf)); +} + +void CaptureVideoDecoder::OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info) { + NOTIMPLEMENTED(); +} + +void CaptureVideoDecoder::InitializeOnDecoderThread( + media::DemuxerStream* demuxer_stream, + media::FilterCallback* filter_callback, + media::StatisticsCallback* stat_callback) { + VLOG(1) << "InitializeOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + capture_engine_ = vc_manager_->AddDevice(video_stream_id_, this); + + available_frames_.clear(); + media_format_.SetAsInteger(media::MediaFormat::kWidth, capability_.width); + media_format_.SetAsInteger(media::MediaFormat::kHeight, capability_.height); + media_format_.SetAsInteger( + media::MediaFormat::kSurfaceFormat, + static_cast<int>(media::VideoFrame::YV12)); + + statistics_callback_.reset(stat_callback); + filter_callback->Run(); + delete filter_callback; + state_ = kNormal; +} + +void CaptureVideoDecoder::ProduceVideoFrameOnDecoderThread( + scoped_refptr<media::VideoFrame> video_frame) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + available_frames_.push_back(video_frame); +} + +void CaptureVideoDecoder::PlayOnDecoderThread(media::FilterCallback* callback) { + VLOG(1) << "PlayOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + callback->Run(); + delete callback; +} + +void CaptureVideoDecoder::PauseOnDecoderThread( + media::FilterCallback* callback) { + VLOG(1) << "PauseOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + state_ = kPaused; + media::VideoDecoder::Pause(callback); +} + +void CaptureVideoDecoder::StopOnDecoderThread(media::FilterCallback* callback) { + VLOG(1) << "StopOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + pending_stop_cb_ = callback; + state_ = kStopped; + capture_engine_->StopCapture(this); +} + +void CaptureVideoDecoder::SeekOnDecoderThread(base::TimeDelta time, + const media::FilterStatusCB& cb) { + VLOG(1) << "SeekOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + state_ = kSeeking; + // Create output buffer pool and pass the frames to renderer + // so that the renderer can complete the seeking + for (size_t i = 0; i < media::Limits::kMaxVideoFrames; ++i) { + VideoFrameReady(media::VideoFrame::CreateBlackFrame(capability_.width, + capability_.height)); + } + + cb.Run(media::PIPELINE_OK); + state_ = kNormal; + capture_engine_->StartCapture(this, capability_); +} + +void CaptureVideoDecoder::OnStoppedOnDecoderThread( + media::VideoCapture* capture) { + VLOG(1) << "OnStoppedOnDecoderThread."; + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + if (pending_stop_cb_) { + pending_stop_cb_->Run(); + delete pending_stop_cb_; + pending_stop_cb_ = NULL; + } + vc_manager_->RemoveDevice(video_stream_id_, this); +} + +void CaptureVideoDecoder::OnBufferReadyOnDecoderThread( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + if (available_frames_.size() == 0 || kNormal != state_) { + capture->FeedBuffer(buf); + return; + } + + scoped_refptr<media::VideoFrame> video_frame = available_frames_.front(); + available_frames_.pop_front(); + + if (buf->width != capability_.width || buf->height != capability_.height) { + capability_.width = buf->width; + capability_.height = buf->height; + media_format_.SetAsInteger(media::MediaFormat::kWidth, capability_.width); + media_format_.SetAsInteger(media::MediaFormat::kHeight, capability_.height); + host()->SetVideoSize(capability_.width, capability_.height); + } + + // Check if there's a size change. + if (static_cast<int>(video_frame->width()) != capability_.width || + static_cast<int>(video_frame->height()) != capability_.height) { + // Allocate new buffer based on the new size. + video_frame = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, + capability_.width, + capability_.height, + media::kNoTimestamp, + media::kNoTimestamp); + } + + video_frame->SetTimestamp(buf->timestamp - start_time_); + video_frame->SetDuration(base::TimeDelta::FromMilliseconds(33)); + + uint8* buffer = buf->memory_pointer; + + // TODO(wjia): de-duplicating pixel date copying code. + uint8* y_plane = video_frame->data(media::VideoFrame::kYPlane); + for (size_t row = 0; row < video_frame->height(); ++row) { + memcpy(y_plane, buffer, capability_.width); + y_plane += video_frame->stride(media::VideoFrame::kYPlane); + buffer += capability_.width; + } + size_t uv_width = capability_.width / 2; + uint8* u_plane = video_frame->data(media::VideoFrame::kUPlane); + for (size_t row = 0; row < video_frame->height(); row += 2) { + memcpy(u_plane, buffer, uv_width); + u_plane += video_frame->stride(media::VideoFrame::kUPlane); + buffer += uv_width; + } + uint8* v_plane = video_frame->data(media::VideoFrame::kVPlane); + for (size_t row = 0; row < video_frame->height(); row += 2) { + memcpy(v_plane, buffer, uv_width); + v_plane += video_frame->stride(media::VideoFrame::kVPlane); + buffer += uv_width; + } + + VideoFrameReady(video_frame); + capture->FeedBuffer(buf); +} diff --git a/content/renderer/media/capture_video_decoder.h b/content/renderer/media/capture_video_decoder.h new file mode 100644 index 0000000..2ca923f --- /dev/null +++ b/content/renderer/media/capture_video_decoder.h @@ -0,0 +1,103 @@ +// Copyright (c) 2011 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_CAPTURE_VIDEO_DECODER_H_ +#define CONTENT_RENDERER_MEDIA_CAPTURE_VIDEO_DECODER_H_ + +#include <deque> + +#include "base/time.h" +#include "media/base/filters.h" +#include "media/base/video_frame.h" +#include "media/video/capture/video_capture.h" + +namespace base { +class MessageLoopProxy; +} +class VideoCaptureImplManager; + +// A filter takes raw frames from video capture engine and passes them to media +// engine as a video decoder filter. +class CaptureVideoDecoder + : public media::VideoDecoder, + public media::VideoCapture::EventHandler { + public: + CaptureVideoDecoder( + base::MessageLoopProxy* message_loop_proxy, + media::VideoCaptureSessionId video_stream_id, + VideoCaptureImplManager* vc_manager, + const media::VideoCapture::VideoCaptureCapability& capability); + virtual ~CaptureVideoDecoder(); + + // Filter implementation. + virtual void Play(media::FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const media::FilterStatusCB& cb); + virtual void Pause(media::FilterCallback* callback); + virtual void Stop(media::FilterCallback* callback); + + // Decoder implementation. + virtual void Initialize(media::DemuxerStream* demuxer_stream, + media::FilterCallback* filter_callback, + media::StatisticsCallback* stat_callback); + virtual const media::MediaFormat& media_format(); + virtual void ProduceVideoFrame(scoped_refptr<media::VideoFrame> video_frame); + virtual bool ProvidesBuffer(); + + // VideoCapture::EventHandler implementation. + virtual void OnStarted(media::VideoCapture* capture); + virtual void OnStopped(media::VideoCapture* capture); + virtual void OnPaused(media::VideoCapture* capture); + virtual void OnError(media::VideoCapture* capture, int error_code); + virtual void OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf); + virtual void OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info); + + private: + friend class CaptureVideoDecoderTest; + + enum DecoderState { + kUnInitialized, + kNormal, + kSeeking, + kStopped, + kPaused + }; + + void PlayOnDecoderThread(media::FilterCallback* callback); + void SeekOnDecoderThread(base::TimeDelta time, + const media::FilterStatusCB& cb); + void PauseOnDecoderThread(media::FilterCallback* callback); + void StopOnDecoderThread(media::FilterCallback* callback); + + void InitializeOnDecoderThread(media::DemuxerStream* demuxer_stream, + media::FilterCallback* filter_callback, + media::StatisticsCallback* stat_callback); + void ProduceVideoFrameOnDecoderThread( + scoped_refptr<media::VideoFrame> video_frame); + + void OnStoppedOnDecoderThread(media::VideoCapture* capture); + void OnBufferReadyOnDecoderThread( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf); + + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + scoped_refptr<VideoCaptureImplManager> vc_manager_; + media::VideoCapture::VideoCaptureCapability capability_; + DecoderState state_; + media::MediaFormat media_format_; + std::deque<scoped_refptr<media::VideoFrame> > available_frames_; + media::FilterCallback* pending_stop_cb_; + scoped_ptr<media::StatisticsCallback> statistics_callback_; + + media::VideoCaptureSessionId video_stream_id_; + media::VideoCapture* capture_engine_; + base::Time start_time_; + + DISALLOW_COPY_AND_ASSIGN(CaptureVideoDecoder); +}; + +#endif // CONTENT_RENDERER_MEDIA_CAPTURE_VIDEO_DECODER_H_ diff --git a/content/renderer/media/capture_video_decoder_unittest.cc b/content/renderer/media/capture_video_decoder_unittest.cc new file mode 100644 index 0000000..6852bee --- /dev/null +++ b/content/renderer/media/capture_video_decoder_unittest.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2011 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 "base/bind.h" +#include "content/renderer/media/capture_video_decoder.h" +#include "content/renderer/media/video_capture_impl.h" +#include "content/renderer/media/video_capture_impl_manager.h" +#include "media/base/filters.h" +#include "media/base/limits.h" +#include "media/base/mock_callback.h" +#include "media/base/mock_filter_host.h" +#include "media/base/mock_filters.h" +#include "media/base/mock_task.h" +#include "media/base/pipeline_status.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Return; +using ::testing::StrictMock; + +static const media::VideoCaptureSessionId kVideoStreamId = 1; + +ACTION_P(ReturnFrameFromRenderer, decoder) { + decoder->ProduceVideoFrame(arg0); +} + +ACTION_P3(CreateDataBufferFromCapture, decoder, vc_impl, data_buffer_number) { + for (int i = 0; i < data_buffer_number; i++) { + media::VideoCapture::VideoFrameBuffer* buffer; + buffer = new media::VideoCapture::VideoFrameBuffer(); + buffer->width = arg1.width; + buffer->height = arg1.height; + int length = buffer->width * buffer->height * 3 / 2; + buffer->memory_pointer = new uint8[length]; + buffer->buffer_size = length; + decoder->OnBufferReady(vc_impl, buffer); + } +} + +ACTION(DeleteDataBuffer) { + delete[] arg0->memory_pointer; +} + +ACTION_P2(CaptureStopped, decoder, vc_impl) { + decoder->OnStopped(vc_impl); +} + +class MockVideoCaptureImpl : public VideoCaptureImpl { + public: + MockVideoCaptureImpl(const media::VideoCaptureSessionId id, + scoped_refptr<base::MessageLoopProxy> ml_proxy, + VideoCaptureMessageFilter* filter) + : VideoCaptureImpl(id, ml_proxy, filter) { + } + virtual ~MockVideoCaptureImpl() {} + + MOCK_METHOD2(StartCapture, + void(media::VideoCapture::EventHandler* handler, + const VideoCaptureCapability& capability)); + MOCK_METHOD1(StopCapture, void(media::VideoCapture::EventHandler* handler)); + MOCK_METHOD1(FeedBuffer, void(scoped_refptr<VideoFrameBuffer> buffer)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImpl); +}; + +class MockVideoCaptureImplManager : public VideoCaptureImplManager { + public: + MockVideoCaptureImplManager() {} + virtual ~MockVideoCaptureImplManager() {} + + MOCK_METHOD2(AddDevice, + media::VideoCapture*(media::VideoCaptureSessionId id, + media::VideoCapture::EventHandler* handler)); + MOCK_METHOD2(RemoveDevice, + void(media::VideoCaptureSessionId id, + media::VideoCapture::EventHandler* handler)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImplManager); +}; + +class CaptureVideoDecoderTest : public ::testing::Test { + protected: + CaptureVideoDecoderTest() { + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + message_loop_proxy_ = + base::MessageLoopProxy::CreateForCurrentThread().get(); + vc_manager_ = new MockVideoCaptureImplManager(); + media::VideoCapture::VideoCaptureCapability capability; + capability.width = 176; + capability.height = 144; + capability.max_fps = 30; + capability.expected_capture_delay = 0; + capability.raw_type = media::VideoFrame::I420; + capability.interlaced = false; + capability.resolution_fixed = false; + + decoder_ = new CaptureVideoDecoder(message_loop_proxy_, + kVideoStreamId, vc_manager_, capability); + renderer_ = new media::MockVideoRenderer(); + + decoder_->set_host(&host_); + decoder_->set_consume_video_frame_callback( + base::Bind(&media::MockVideoRenderer::ConsumeVideoFrame, + base::Unretained(renderer_.get()))); + EXPECT_CALL(statistics_callback_object_, OnStatistics(_)) + .Times(AnyNumber()); + } + + virtual ~CaptureVideoDecoderTest() { + message_loop_->RunAllPending(); + } + + media::StatisticsCallback* NewStatisticsCallback() { + return NewCallback(&statistics_callback_object_, + &media::MockStatisticsCallback::OnStatistics); + } + + // Fixture members. + scoped_refptr<CaptureVideoDecoder> decoder_; + scoped_refptr<MockVideoCaptureImplManager> vc_manager_; + scoped_refptr<media::MockVideoRenderer> renderer_; + media::MockStatisticsCallback statistics_callback_object_; + StrictMock<media::MockFilterHost> host_; + scoped_ptr<MessageLoop> message_loop_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + + private: + DISALLOW_COPY_AND_ASSIGN(CaptureVideoDecoderTest); +}; + +TEST_F(CaptureVideoDecoderTest, Play) { + int data_buffer_number = 1; + media::VideoCapture::EventHandler* capture_client = + static_cast<media::VideoCapture::EventHandler*>(decoder_); + scoped_ptr<MockVideoCaptureImpl> vc_impl( + new MockVideoCaptureImpl(kVideoStreamId, + message_loop_proxy_, + new VideoCaptureMessageFilter())); + + EXPECT_CALL(*vc_manager_, AddDevice(_, _)) + .WillOnce(Return(vc_impl.get())); + decoder_->Initialize(NULL, + media::NewExpectedCallback(), + NewStatisticsCallback()); + message_loop_->RunAllPending(); + + EXPECT_CALL(*renderer_, ConsumeVideoFrame(_)) + .WillRepeatedly(ReturnFrameFromRenderer(decoder_.get())); + EXPECT_CALL(*vc_impl, StartCapture(capture_client, _)) + .Times(1) + .WillOnce(CreateDataBufferFromCapture(capture_client, vc_impl.get(), + data_buffer_number)); + EXPECT_CALL(*vc_impl, FeedBuffer(_)) + .Times(data_buffer_number) + .WillRepeatedly(DeleteDataBuffer()); + decoder_->Seek(base::TimeDelta(), + media::NewExpectedStatusCB(media::PIPELINE_OK)); + decoder_->Play(media::NewExpectedCallback()); + message_loop_->RunAllPending(); + + EXPECT_CALL(*vc_impl, StopCapture(capture_client)) + .Times(1) + .WillOnce(CaptureStopped(capture_client, vc_impl.get())); + EXPECT_CALL(*vc_manager_, RemoveDevice(_, _)) + .WillOnce(Return()); + decoder_->Stop(media::NewExpectedCallback()); + message_loop_->RunAllPending(); +} diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc new file mode 100644 index 0000000..20ad098 --- /dev/null +++ b/content/renderer/media/media_stream_impl.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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/media_stream_impl.h" + +#include "base/string_util.h" +#include "content/renderer/media/capture_video_decoder.h" +#include "content/renderer/media/video_capture_impl_manager.h" +#include "googleurl/src/gurl.h" +#include "media/base/message_loop_factory.h" +#include "media/base/pipeline.h" + +namespace { + +static const int kVideoCaptureWidth = 352; +static const int kVideoCaptureHeight = 288; +static const int kVideoCaptureFramePerSecond = 30; + +static const int kStartOpenSessionId = 1; + +} // namespace + +MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) + : vc_manager_(vc_manager) { +} + +MediaStreamImpl::~MediaStreamImpl() {} + +scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( + const GURL& url, media::MessageLoopFactory* message_loop_factory) { + bool raw_media = (url.spec().find(media::kRawMediaScheme) == 0); + media::VideoDecoder* decoder = NULL; + if (raw_media) { + media::VideoCapture::VideoCaptureCapability capability; + capability.width = kVideoCaptureWidth; + capability.height = kVideoCaptureHeight; + capability.max_fps = kVideoCaptureFramePerSecond; + capability.expected_capture_delay = 0; + capability.raw_type = media::VideoFrame::I420; + capability.interlaced = false; + capability.resolution_fixed = false; + + decoder = new CaptureVideoDecoder( + message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(), + kStartOpenSessionId, vc_manager_.get(), capability); + } + return decoder; +} diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h new file mode 100644 index 0000000..b338a31 --- /dev/null +++ b/content/renderer/media/media_stream_impl.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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_STREAM_MEDIA_STREAM_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_IMPL_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "webkit/glue/media/media_stream_client.h" + +class VideoCaptureImplManager; + +// A implementation of StreamClient to provide supporting functions, such as +// GetVideoDecoder. +class MediaStreamImpl + : public webkit_glue::MediaStreamClient, + public base::RefCountedThreadSafe<MediaStreamImpl> { + public: + explicit MediaStreamImpl(VideoCaptureImplManager* vc_manager); + virtual ~MediaStreamImpl(); + + // Implement webkit_glue::StreamClient. + virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( + const GURL& url, media::MessageLoopFactory* message_loop_factory); + + private: + scoped_refptr<VideoCaptureImplManager> vc_manager_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamImpl); +}; + +#endif // CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_IMPL_H_ diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc index b4236a0..2ac180b 100644 --- a/content/renderer/media/video_capture_impl.cc +++ b/content/renderer/media/video_capture_impl.cc @@ -22,15 +22,15 @@ bool VideoCaptureImpl::CaptureStarted() { } int VideoCaptureImpl::CaptureWidth() { - return width_; + return current_params_.width; } int VideoCaptureImpl::CaptureHeight() { - return height_; + return current_params_.height; } int VideoCaptureImpl::CaptureFrameRate() { - return frame_rate_; + return current_params_.frame_per_second; } VideoCaptureImpl::VideoCaptureImpl( @@ -39,17 +39,14 @@ VideoCaptureImpl::VideoCaptureImpl( VideoCaptureMessageFilter* filter) : VideoCapture(), message_filter_(filter), - session_id_(id), ml_proxy_(ml_proxy), device_id_(0), - width_(0), - height_(0), - frame_rate_(0), video_type_(media::VideoFrame::I420), - new_width_(0), - new_height_(0), state_(kStopped) { DCHECK(filter); + memset(¤t_params_, 0, sizeof(current_params_)); + memset(&new_params_, 0, sizeof(new_params_)); + current_params_.session_id = new_params_.session_id = id; } VideoCaptureImpl::~VideoCaptureImpl() { @@ -102,6 +99,11 @@ void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { NewRunnableMethod(this, &VideoCaptureImpl::DoStopCapture, handler)); } +void VideoCaptureImpl::FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) { + ml_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &VideoCaptureImpl::DoFeedBuffer, buffer)); +} + void VideoCaptureImpl::OnBufferCreated( base::SharedMemoryHandle handle, int length, int buffer_id) { @@ -151,7 +153,8 @@ void VideoCaptureImpl::DoStartCapture( } if (capability.resolution_fixed && master_clients_.size() && - (capability.width != width_ || capability.height != height_)) { + (capability.width != current_params_.width || + capability.height != current_params_.height)) { // Can't have 2 master clients with different resolutions. handler->OnError(this, 1); return; @@ -167,11 +170,14 @@ void VideoCaptureImpl::DoStartCapture( if (state_ == kStarted) { // Take the resolution of master client. if (capability.resolution_fixed && - (capability.width != width_ || capability.height != height_)) { - new_width_ = capability.width; - new_height_ = capability.height; + (capability.width != current_params_.width || + capability.height != current_params_.height || + capability.max_fps != current_params_.frame_per_second)) { + new_params_.width = capability.width; + new_params_.height = capability.height; + new_params_.frame_per_second = capability.max_fps; DLOG(INFO) << "StartCapture: Got master client with new resolution (" - << new_width_ << ", " << new_height_ << ") " + << new_params_.width << ", " << new_params_.height << ") " << "during started, try to restart."; StopDevice(); } @@ -181,10 +187,11 @@ void VideoCaptureImpl::DoStartCapture( if (state_ == kStopping) { if (capability.resolution_fixed || !pending_start()) { - new_width_ = capability.width; - new_height_ = capability.height; + new_params_.width = capability.width; + new_params_.height = capability.height; + new_params_.frame_per_second = capability.max_fps; DLOG(INFO) << "StartCapture: Got new resolution (" - << new_width_ << ", " << new_height_ << ") " + << new_params_.width << ", " << new_params_.height << ") " << ", already in stopping."; } handler->OnStarted(this); @@ -193,12 +200,14 @@ void VideoCaptureImpl::DoStartCapture( DCHECK_EQ(clients_.size(), 1ul); video_type_ = capability.raw_type; - new_width_ = 0; - new_height_ = 0; - width_ = capability.width; - height_ = capability.height; + new_params_.width = 0; + new_params_.height = 0; + new_params_.frame_per_second = 0; + current_params_.width = capability.width; + current_params_.height = capability.height; + current_params_.frame_per_second = capability.max_fps; DLOG(INFO) << "StartCapture: resolution (" - << width_ << ", " << height_ << "). "; + << current_params_.width << ", " << current_params_.height << ")"; StartCaptureInternal(); } @@ -229,23 +238,27 @@ void VideoCaptureImpl::DoStopCapture( // clients, except no client case? if (clients_.size() > 0) { DLOG(INFO) << "StopCapture: No master client."; - int maxw = 0; - int maxh = 0; + int max_width = 0; + int max_height = 0; + int frame_rate = 0; for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { - if (it->second.width > maxw && it->second.height > maxh) { - maxw = it->second.width; - maxh = it->second.height; + if (it->second.width > max_width && it->second.height > max_height) { + max_width = it->second.width; + max_height = it->second.height; + frame_rate = it->second.max_fps; } } if (state_ == kStarted) { // Only handle resolution reduction. - if (maxw < width_ && maxh < height_) { - new_width_ = maxw; - new_height_ = maxh; + if (max_width < current_params_.width && + max_height < current_params_.height) { + new_params_.width = max_width; + new_params_.height = max_height; + new_params_.frame_per_second = frame_rate; DLOG(INFO) << "StopCapture: New smaller resolution (" - << new_width_ << ", " << new_height_ << ") " + << new_params_.width << ", " << new_params_.height << ") " << "), stopping ..."; StopDevice(); } @@ -253,21 +266,42 @@ void VideoCaptureImpl::DoStopCapture( } if (state_ == kStopping) { - new_width_ = maxw; - new_height_ = maxh; + new_params_.width = max_width; + new_params_.height = max_height; + new_params_.frame_per_second = frame_rate; DLOG(INFO) << "StopCapture: New resolution (" - << new_width_ << ", " << new_height_ << ") " + << new_params_.width << ", " << new_params_.height << ") " << "), during stopping."; return; } } else { - new_width_ = width_ = 0; - new_height_ = height_ = 0; + new_params_.width = current_params_.width = 0; + new_params_.height = current_params_.height = 0; + new_params_.frame_per_second = current_params_.frame_per_second = 0; DLOG(INFO) << "StopCapture: No more client, stopping ..."; StopDevice(); } } +void VideoCaptureImpl::DoFeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) { + DCHECK(ml_proxy_->BelongsToCurrentThread()); + DCHECK(client_side_dibs_.find(buffer) != client_side_dibs_.end()); + + CachedDIB::iterator it; + for (it = cached_dibs_.begin(); it != cached_dibs_.end(); it++) { + if (buffer == it->second->mapped_memory) + break; + } + + DCHECK(it != cached_dibs_.end()); + if (client_side_dibs_[buffer] <= 1) { + client_side_dibs_.erase(buffer); + Send(new VideoCaptureHostMsg_BufferReady(device_id_, it->first)); + } else { + client_side_dibs_[buffer]--; + } +} + void VideoCaptureImpl::DoBufferCreated( base::SharedMemoryHandle handle, int length, int buffer_id) { @@ -279,10 +313,10 @@ void VideoCaptureImpl::DoBufferCreated( base::SharedMemory* dib = new base::SharedMemory(handle, false); dib->Map(length); buffer = new VideoFrameBuffer(); - buffer->memory_pointer = dib->memory(); + buffer->memory_pointer = static_cast<uint8*>(dib->memory()); buffer->buffer_size = length; - buffer->width = width_; - buffer->height = height_; + buffer->width = current_params_.width; + buffer->height = current_params_.height; DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer); cached_dibs_[buffer_id] = dib_buffer; @@ -300,12 +334,10 @@ void VideoCaptureImpl::DoBufferReceived(int buffer_id, base::Time timestamp) { DCHECK(cached_dibs_.find(buffer_id) != cached_dibs_.end()); buffer = cached_dibs_[buffer_id]->mapped_memory; - // TODO(wjia): handle buffer sharing with downstream modules. for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { it->first->OnBufferReady(this, buffer); } - - Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); + client_side_dibs_[buffer] = clients_.size(); } void VideoCaptureImpl::DoStateChanged(const media::VideoCapture::State& state) { @@ -321,6 +353,7 @@ void VideoCaptureImpl::DoStateChanged(const media::VideoCapture::State& state) { case media::VideoCapture::kStopped: state_ = kStopped; DLOG(INFO) << "OnStateChanged: stopped!, device_id = " << device_id_; + STLDeleteValues(&cached_dibs_); if (pending_start()) RestartCapture(); break; @@ -366,19 +399,12 @@ void VideoCaptureImpl::DoDelegateAdded(int32 device_id) { } void VideoCaptureImpl::StopDevice() { - if (!ml_proxy_->BelongsToCurrentThread()) { - ml_proxy_->PostTask(FROM_HERE, - NewRunnableMethod(this, &VideoCaptureImpl::StopDevice)); - return; - } + DCHECK(ml_proxy_->BelongsToCurrentThread()); if (state_ == kStarted) { state_ = kStopping; Send(new VideoCaptureHostMsg_Stop(device_id_)); - width_ = height_ = 0; - STLDeleteContainerPairSecondPointers(cached_dibs_.begin(), - cached_dibs_.end()); - cached_dibs_.clear(); + current_params_.width = current_params_.height = 0; } } @@ -386,12 +412,16 @@ void VideoCaptureImpl::RestartCapture() { DCHECK(ml_proxy_->BelongsToCurrentThread()); DCHECK_EQ(state_, kStopped); - width_ = new_width_; - height_ = new_height_; - new_width_ = 0; - new_height_ = 0; + current_params_.width = new_params_.width; + current_params_.height = new_params_.height; + current_params_.frame_per_second = new_params_.frame_per_second; - DLOG(INFO) << "RestartCapture, " << width_ << ", " << height_; + new_params_.width = 0; + new_params_.height = 0; + new_params_.frame_per_second = 0; + + DLOG(INFO) << "RestartCapture, " << current_params_.width << ", " + << current_params_.height; StartCaptureInternal(); } @@ -399,13 +429,7 @@ void VideoCaptureImpl::StartCaptureInternal() { DCHECK(ml_proxy_->BelongsToCurrentThread()); DCHECK(device_id_); - media::VideoCaptureParams params; - params.width = width_; - params.height = height_; - params.frame_per_second = frame_rate_; - params.session_id = session_id_; - - Send(new VideoCaptureHostMsg_Start(device_id_, params)); + Send(new VideoCaptureHostMsg_Start(device_id_, current_params_)); state_ = kStarted; for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { it->first->OnStarted(this); diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h index b603652..d61e8df 100644 --- a/content/renderer/media/video_capture_impl.h +++ b/content/renderer/media/video_capture_impl.h @@ -14,7 +14,6 @@ #include "content/renderer/media/video_capture_message_filter.h" #include "media/video/capture/video_capture.h" -#include "ui/gfx/surface/transport_dib.h" namespace base { class MessageLoopProxy; @@ -28,6 +27,7 @@ class VideoCaptureImpl virtual void StartCapture(media::VideoCapture::EventHandler* handler, const VideoCaptureCapability& capability); virtual void StopCapture(media::VideoCapture::EventHandler* handler); + virtual void FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer); virtual bool CaptureStarted(); virtual int CaptureWidth(); virtual int CaptureHeight(); @@ -43,18 +43,13 @@ class VideoCaptureImpl virtual void OnDelegateAdded(int32 device_id); bool pending_start() { - return (new_width_ > 0 && new_height_ > 0); + return (new_params_.width > 0 && new_params_.height > 0); } private: friend class VideoCaptureImplManager; friend class VideoCaptureImplTest; - - enum State { - kStarted, - kStopping, - kStopped - }; + friend class MockVideoCaptureImpl; struct DIBBuffer { public: @@ -74,6 +69,7 @@ class VideoCaptureImpl void DoStartCapture(media::VideoCapture::EventHandler* handler, const VideoCaptureCapability& capability); void DoStopCapture(media::VideoCapture::EventHandler* handler); + void DoFeedBuffer(scoped_refptr<VideoFrameBuffer> buffer); void DoBufferCreated(base::SharedMemoryHandle handle, int length, int buffer_id); @@ -92,7 +88,6 @@ class VideoCaptureImpl virtual void Send(IPC::Message* message); scoped_refptr<VideoCaptureMessageFilter> message_filter_; - media::VideoCaptureSessionId session_id_; scoped_refptr<base::MessageLoopProxy> ml_proxy_; int device_id_; @@ -100,6 +95,11 @@ class VideoCaptureImpl typedef std::map<int, DIBBuffer*> CachedDIB; CachedDIB cached_dibs_; + // DIBs at client side. The mapped value |int| means number of clients which + // hold this dib. + typedef std::map<media::VideoCapture::VideoFrameBuffer*, int> ClientSideDIB; + ClientSideDIB client_side_dibs_; + typedef std::map<media::VideoCapture::EventHandler*, VideoCaptureCapability> ClientInfo; ClientInfo clients_; @@ -107,13 +107,14 @@ class VideoCaptureImpl ClientInfo pending_clients_; - int width_; - int height_; - int frame_rate_; media::VideoFrame::Format video_type_; - int new_width_; - int new_height_; + // The parameter is being used in current capture session. A capture session + // starts with StartCapture and ends with StopCapture. + media::VideoCaptureParams current_params_; + + // The parameter will be used in next capture session. + media::VideoCaptureParams new_params_; State state_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureImpl); diff --git a/content/renderer/media/video_capture_impl_manager.h b/content/renderer/media/video_capture_impl_manager.h index 28c0e42..282eb3c 100644 --- a/content/renderer/media/video_capture_impl_manager.h +++ b/content/renderer/media/video_capture_impl_manager.h @@ -31,14 +31,14 @@ class VideoCaptureImplManager // by |id| to VideoCaptureImplManager's list of opened device list. // A pointer to VideoCapture is returned to client so that client can // operate on that pointer, such as StartCaptrue, StopCapture. - media::VideoCapture* AddDevice( + virtual media::VideoCapture* AddDevice( media::VideoCaptureSessionId id, media::VideoCapture::EventHandler* handler); // Called by video capture client |handler| to remove device referenced // by |id| from VideoCaptureImplManager's list of opened device list. - void RemoveDevice(media::VideoCaptureSessionId id, - media::VideoCapture::EventHandler* handler); + virtual void RemoveDevice(media::VideoCaptureSessionId id, + media::VideoCapture::EventHandler* handler); VideoCaptureMessageFilter* video_capture_message_filter() const { return filter_; diff --git a/content/renderer/media/video_capture_module_impl.cc b/content/renderer/media/video_capture_module_impl.cc new file mode 100644 index 0000000..ad1f908 --- /dev/null +++ b/content/renderer/media/video_capture_module_impl.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2011 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/video_capture_module_impl.h" + +#include "content/renderer/media/video_capture_impl_manager.h" + +// static +webrtc::VideoCaptureModule* webrtc::VideoCaptureModule::Create( + const WebRtc_Word32 id, const WebRtc_UWord8* device_unique_id_utf8) { + NOTREACHED(); + return NULL; +} + +VideoCaptureModuleImpl::VideoCaptureModuleImpl( + const media::VideoCaptureSessionId id, + VideoCaptureImplManager* vc_manager) + : webrtc::videocapturemodule::VideoCaptureImpl(id), + session_id_(id), + thread_("VideoCaptureModuleImpl"), + vc_manager_(vc_manager), + state_(media::VideoCapture::kStopped), + got_first_frame_(false), + width_(-1), + height_(-1), + frame_rate_(-1), + video_type_(webrtc::kVideoI420), + capture_engine_(NULL), + pending_start_(false) { + DCHECK(vc_manager_); + Init(); +} + +VideoCaptureModuleImpl::~VideoCaptureModuleImpl() { + vc_manager_->RemoveDevice(session_id_, this); + thread_.Stop(); +} + +void VideoCaptureModuleImpl::Init() { + thread_.Start(); + message_loop_proxy_ = thread_.message_loop_proxy(); + capture_engine_ = vc_manager_->AddDevice(session_id_, this); +} + +WebRtc_Word32 VideoCaptureModuleImpl::StartCapture( + const webrtc::VideoCaptureCapability& capability) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &VideoCaptureModuleImpl::StartCaptureOnCaptureThread, + capability)); + return 0; +} + +WebRtc_Word32 VideoCaptureModuleImpl::StopCapture() { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &VideoCaptureModuleImpl::StopCaptureOnCaptureThread)); + return 0; +} + +bool VideoCaptureModuleImpl::CaptureStarted() { + return state_ == media::VideoCapture::kStarted; +} + +WebRtc_Word32 VideoCaptureModuleImpl::CaptureSettings( + webrtc::VideoCaptureCapability& settings) { + settings.width = width_; + settings.height = height_; + settings.maxFPS = frame_rate_; + settings.expectedCaptureDelay = 120; + settings.rawType = webrtc::kVideoI420; + return 0; +} + +void VideoCaptureModuleImpl::OnStarted(media::VideoCapture* capture) { + NOTIMPLEMENTED(); +} + +void VideoCaptureModuleImpl::OnStopped(media::VideoCapture* capture) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &VideoCaptureModuleImpl::OnStoppedOnCaptureThread, + capture)); +} + +void VideoCaptureModuleImpl::OnPaused(media::VideoCapture* capture) { + NOTIMPLEMENTED(); +} + +void VideoCaptureModuleImpl::OnError(media::VideoCapture* capture, + int error_code) { + NOTIMPLEMENTED(); +} + +void VideoCaptureModuleImpl::OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { + message_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &VideoCaptureModuleImpl::OnBufferReadyOnCaptureThread, + capture, buf)); +} + +void VideoCaptureModuleImpl::OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info) { + NOTIMPLEMENTED(); +} + +void VideoCaptureModuleImpl::StartCaptureOnCaptureThread( + const webrtc::VideoCaptureCapability& capability) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK_NE(state_, media::VideoCapture::kStarted); + + if (state_ == media::VideoCapture::kStopping) { + VLOG(1) << "Got a new StartCapture in Stopping state!!! "; + pending_start_ = true; + pending_cap_ = capability; + return; + } + + VLOG(1) << "StartCaptureOnCaptureThread: " << capability.width << ", " + << capability.height; + + StartCaptureInternal(capability); + return; +} + +void VideoCaptureModuleImpl::StartCaptureInternal( + const webrtc::VideoCaptureCapability& capability) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + DCHECK_EQ(capability.rawType, webrtc::kVideoI420); + + video_type_ = capability.rawType; + width_ = capability.width; + height_ = capability.height; + frame_rate_ = capability.maxFPS; + state_ = media::VideoCapture::kStarted; + + media::VideoCapture::VideoCaptureCapability cap; + cap.width = capability.width; + cap.height = capability.height; + cap.max_fps = capability.maxFPS; + cap.raw_type = media::VideoFrame::I420; + cap.resolution_fixed = true; + capture_engine_->StartCapture(this, cap); +} + +void VideoCaptureModuleImpl::StopCaptureOnCaptureThread() { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + if (pending_start_) { + VLOG(1) << "Got a StopCapture with one pending start!!! "; + pending_start_ = false; + return; + } + + if (state_ != media::VideoCapture::kStarted) { + VLOG(1) << "Got a StopCapture while not started!!! "; + return; + } + + VLOG(1) << "StopCaptureOnCaptureThread. "; + state_ = media::VideoCapture::kStopping; + + capture_engine_->StopCapture(this); + return; +} + +void VideoCaptureModuleImpl::OnStoppedOnCaptureThread( + media::VideoCapture* capture) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + VLOG(1) << "Capture Stopped!!! "; + state_ = media::VideoCapture::kStopped; + got_first_frame_ = false; + width_ = -1; + height_ = -1; + frame_rate_ = -1; + + if (pending_start_) { + VLOG(1) << "restart pending start "; + pending_start_ = false; + StartCaptureInternal(pending_cap_); + } +} + +void VideoCaptureModuleImpl::OnBufferReadyOnCaptureThread( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { + DCHECK(message_loop_proxy_->BelongsToCurrentThread()); + + if (state_ != media::VideoCapture::kStarted) + return; + + if (!got_first_frame_) { + got_first_frame_ = true; + start_time_ = buf->timestamp; + } + + frameInfo_.width = buf->width; + frameInfo_.height = buf->height; + frameInfo_.rawType = video_type_; + + IncomingFrame( + static_cast<WebRtc_UWord8*>(buf->memory_pointer), + static_cast<WebRtc_Word32>(buf->buffer_size), + frameInfo_, + static_cast<WebRtc_Word64>( + (buf->timestamp - start_time_).InMicroseconds())); + + capture->FeedBuffer(buf); +} diff --git a/content/renderer/media/video_capture_module_impl.h b/content/renderer/media/video_capture_module_impl.h new file mode 100644 index 0000000..5df07e3 --- /dev/null +++ b/content/renderer/media/video_capture_module_impl.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 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_VIDEO_CAPTURE_MODULE_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_MODULE_IMPL_H_ + +#include "base/compiler_specific.h" +#include "base/threading/thread.h" +#include "media/video/capture/video_capture.h" +#include "third_party/webrtc/common_types.h" +#include "third_party/webrtc/modules/video_capture/main/interface/video_capture_defines.h" +#include "third_party/webrtc/modules/video_capture/main/source/video_capture_impl.h" + +class VideoCaptureImplManager; + +// An implementation of webrtc::VideoCaptureModule takes raw frames from video +// capture engine and passed them to webrtc VideoEngine. +class VideoCaptureModuleImpl + : public webrtc::videocapturemodule::VideoCaptureImpl, + public media::VideoCapture::EventHandler { + public: + VideoCaptureModuleImpl(const media::VideoCaptureSessionId id, + VideoCaptureImplManager* vc_manager); + virtual ~VideoCaptureModuleImpl(); + + // Override webrtc::videocapturemodule::VideoCaptureImpl implementation. + virtual WebRtc_Word32 StartCapture( + const webrtc::VideoCaptureCapability& capability) OVERRIDE; + virtual WebRtc_Word32 StopCapture() OVERRIDE; + virtual bool CaptureStarted() OVERRIDE; + virtual WebRtc_Word32 CaptureSettings( + webrtc::VideoCaptureCapability& settings) OVERRIDE; + + // media::VideoCapture::EventHandler implementation. + virtual void OnStarted(media::VideoCapture* capture); + virtual void OnStopped(media::VideoCapture* capture); + virtual void OnPaused(media::VideoCapture* capture); + virtual void OnError(media::VideoCapture* capture, int error_code); + virtual void OnBufferReady( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf); + virtual void OnDeviceInfoReceived( + media::VideoCapture* capture, + const media::VideoCaptureParams& device_info); + + private: + void Init(); + + void StartCaptureOnCaptureThread( + const webrtc::VideoCaptureCapability& capability); + void StopCaptureOnCaptureThread(); + void StartCaptureInternal(const webrtc::VideoCaptureCapability& capability); + + void OnStoppedOnCaptureThread(media::VideoCapture* capture); + void OnBufferReadyOnCaptureThread( + media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf); + + // The id identifies which video capture device is used for this video + // capture session. + media::VideoCaptureSessionId session_id_; + base::Thread thread_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + // The video capture manager handles open/close of video capture devices. + scoped_refptr<VideoCaptureImplManager> vc_manager_; + media::VideoCapture::State state_; + bool got_first_frame_; + WebRtc_UWord32 width_; + WebRtc_UWord32 height_; + WebRtc_Word32 frame_rate_; + webrtc::RawVideoType video_type_; + webrtc::VideoCaptureCapability frameInfo_; + base::Time start_time_; + // The video capture module generating raw frame data. + media::VideoCapture* capture_engine_; + bool pending_start_; + webrtc::VideoCaptureCapability pending_cap_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureModuleImpl); +}; + +DISABLE_RUNNABLE_METHOD_REFCOUNT(VideoCaptureModuleImpl); + +#endif // CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_MODULE_IMPL_H_ diff --git a/content/renderer/media/video_capture_module_impl_device_info.cc b/content/renderer/media/video_capture_module_impl_device_info.cc new file mode 100644 index 0000000..f647fd5 --- /dev/null +++ b/content/renderer/media/video_capture_module_impl_device_info.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2011 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/video_capture_module_impl_device_info.h" + +#include "base/string_util.h" + +static const char* kLocalDeviceName = "chromecamera"; + +// static +webrtc::VideoCaptureModule::DeviceInfo* +webrtc::VideoCaptureModule::CreateDeviceInfo(const WebRtc_Word32 id) { + return new VideoCaptureModuleImplDeviceInfo(id); +} + +// static +void webrtc::VideoCaptureModule::DestroyDeviceInfo( + webrtc::VideoCaptureModule::DeviceInfo* device_info) { + VideoCaptureModuleImplDeviceInfo* dev_info = + static_cast<VideoCaptureModuleImplDeviceInfo*>(device_info); + delete dev_info; +} + +VideoCaptureModuleImplDeviceInfo::VideoCaptureModuleImplDeviceInfo( + const WebRtc_Word32 id) { +} + +VideoCaptureModuleImplDeviceInfo::~VideoCaptureModuleImplDeviceInfo() {} + +WebRtc_UWord32 VideoCaptureModuleImplDeviceInfo::NumberOfDevices() { + return 1; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::GetDeviceName( + WebRtc_UWord32 device_number, + WebRtc_UWord8* device_name_utf8, + WebRtc_UWord32 device_name_length, + WebRtc_UWord8* device_unique_id_utf8, + WebRtc_UWord32 device_unique_id_utf8_length, + WebRtc_UWord8* /* product_unique_id_utf8 */, + WebRtc_UWord32 /* product_unique_id_utf8_length */) { + base::strlcpy(reinterpret_cast<char*>(device_name_utf8), + kLocalDeviceName, device_name_length); + + base::strlcpy(reinterpret_cast<char*>(device_unique_id_utf8), + kLocalDeviceName, device_unique_id_utf8_length); + + return 0; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::NumberOfCapabilities( + const WebRtc_UWord8* deviceUniqueIdUTF8) { + return 0; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::GetCapability( + const WebRtc_UWord8* deviceUniqueIdUTF8, + const WebRtc_UWord32 deviceCapabilityNumber, + webrtc::VideoCaptureCapability& capability) { + return -1; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::GetOrientation( + const WebRtc_UWord8* deviceUniqueIdUTF8, + webrtc::VideoCaptureRotation& orientation) { + orientation = webrtc::kCameraRotate0; + return -1; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::GetBestMatchedCapability( + const WebRtc_UWord8* deviceUniqueIdUTF8, + const webrtc::VideoCaptureCapability requested, + webrtc::VideoCaptureCapability& resulting) { + return -1; +} + +WebRtc_Word32 VideoCaptureModuleImplDeviceInfo::DisplayCaptureSettingsDialogBox( + const WebRtc_UWord8* /* device_unique_id_utf8*/, + const WebRtc_UWord8* /* dialog_title_utf8*/, + void* /* parent_window */, + WebRtc_UWord32 /* position_x */, + WebRtc_UWord32 /* position_y */) { + return -1; +} diff --git a/content/renderer/media/video_capture_module_impl_device_info.h b/content/renderer/media/video_capture_module_impl_device_info.h new file mode 100644 index 0000000..8cf9699 --- /dev/null +++ b/content/renderer/media/video_capture_module_impl_device_info.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 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_VIDEO_CAPTURE_MODULE_IMPL_DEVICE_INFO_H_ +#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_MODULE_IMPL_DEVICE_INFO_H_ + +#include "base/basictypes.h" +#include "third_party/webrtc/modules/video_capture/main/interface/video_capture.h" + +// An implementation of webrtc::VideoCaptureModule::DeviceInfo returns device +// information about video capture on Chrome platform. Actually, this is a dummy +// class. The real device management is done by media stream on Chrome. +class VideoCaptureModuleImplDeviceInfo + : public webrtc::VideoCaptureModule::DeviceInfo { + public: + explicit VideoCaptureModuleImplDeviceInfo(const WebRtc_Word32 id); + virtual ~VideoCaptureModuleImplDeviceInfo(); + + // webrtc::VideoCaptureModule::DeviceInfo implementation. + virtual WebRtc_UWord32 NumberOfDevices(); + virtual WebRtc_Word32 GetDeviceName( + WebRtc_UWord32 device_number, + WebRtc_UWord8* device_name_utf8, + WebRtc_UWord32 device_name_length, + WebRtc_UWord8* device_unique_id_utf8, + WebRtc_UWord32 device_unique_id_utf8_ength, + WebRtc_UWord8* product_unique_id_utf8 = 0, + WebRtc_UWord32 product_unique_id_utf8_length = 0); + virtual WebRtc_Word32 NumberOfCapabilities( + const WebRtc_UWord8* deviceUniqueIdUTF8); + virtual WebRtc_Word32 GetCapability( + const WebRtc_UWord8* deviceUniqueIdUTF8, + const WebRtc_UWord32 deviceCapabilityNumber, + webrtc::VideoCaptureCapability& capability); + virtual WebRtc_Word32 GetOrientation( + const WebRtc_UWord8* deviceUniqueIdUTF8, + webrtc::VideoCaptureRotation& orientation); + virtual WebRtc_Word32 GetBestMatchedCapability( + const WebRtc_UWord8* deviceUniqueIdUTF8, + const webrtc::VideoCaptureCapability requested, + webrtc::VideoCaptureCapability& resulting); + virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( + const WebRtc_UWord8* device_unique_id_utf8, + const WebRtc_UWord8* dialog_title_utf8, + void* parent_window, + WebRtc_UWord32 position_x, + WebRtc_UWord32 position_y); + + private: + DISALLOW_COPY_AND_ASSIGN(VideoCaptureModuleImplDeviceInfo); +}; + +#endif // CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_MODULE_IMPL_DEVICE_INFO_H_ diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc index 56271f3..e2d4e2f 100644 --- a/content/renderer/render_view.cc +++ b/content/renderer/render_view.cc @@ -48,6 +48,7 @@ #include "content/renderer/load_progress_tracker.h" #include "content/renderer/media/audio_message_filter.h" #include "content/renderer/media/audio_renderer_impl.h" +#include "content/renderer/media/media_stream_impl.h" #include "content/renderer/navigation_state.h" #include "content/renderer/notification_provider.h" #include "content/renderer/p2p/socket_dispatcher.h" @@ -407,6 +408,11 @@ RenderView::RenderView(RenderThreadBase* render_thread, new MHTMLGenerator(this); + if (command_line.HasSwitch(switches::kEnableMediaStream)) { + media_stream_impl_ = new MediaStreamImpl( + RenderThread::current()->video_capture_impl_manager()); + } + content::GetContentClient()->renderer()->RenderViewCreated(this); } @@ -1937,7 +1943,8 @@ WebMediaPlayer* RenderView::createMediaPlayer( scoped_ptr<webkit_glue::WebMediaPlayerImpl> result( new webkit_glue::WebMediaPlayerImpl(client, collection.release(), - message_loop_factory.release())); + message_loop_factory.release(), + media_stream_impl_.get())); if (!result->Initialize(frame, cmd_line->HasSwitch(switches::kSimpleDataSource), video_renderer)) { diff --git a/content/renderer/render_view.h b/content/renderer/render_view.h index 34e2fa1..43c1b66 100644 --- a/content/renderer/render_view.h +++ b/content/renderer/render_view.h @@ -57,6 +57,7 @@ class FilePath; class GeolocationDispatcher; class GURL; class LoadProgressTracker; +class MediaStreamImpl; class NavigationState; class NotificationProvider; class P2PSocketDispatcher; @@ -1094,6 +1095,9 @@ class RenderView : public RenderWidget, // Device orientation dispatcher attached to this view; lazily initialized. DeviceOrientationDispatcher* device_orientation_dispatcher_; + // MediaStreamImpl attached to this view; lazily initialized. + scoped_refptr<MediaStreamImpl> media_stream_impl_; + // Handles accessibility requests into the renderer side, as well as // maintains the cache and other features of the accessibility tree. scoped_ptr<WebKit::WebAccessibilityCache> accessibility_; diff --git a/media/video/capture/video_capture.h b/media/video/capture/video_capture.h index 65789fb..9a197e2 100644 --- a/media/video/capture/video_capture.h +++ b/media/video/capture/video_capture.h @@ -24,6 +24,7 @@ class VideoCapture { kStarted, kPaused, kStopped, + kStopping, kError, }; @@ -42,7 +43,7 @@ class VideoCapture { int height; int stride; size_t buffer_size; - void* memory_pointer; + uint8* memory_pointer; base::Time timestamp; private: @@ -98,9 +99,8 @@ class VideoCapture { // Request video capture to stop capturing for client |handler|. virtual void StopCapture(EventHandler* handler) = 0; - // TODO(wjia): Add FeedBuffer when buffer sharing is needed between video - // capture and downstream module. - // virtual void FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) = 0; + // Feed buffer to video capture when done with it. + virtual void FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) = 0; virtual bool CaptureStarted() = 0; virtual int CaptureWidth() = 0; diff --git a/webkit/glue/media/media_stream_client.h b/webkit/glue/media/media_stream_client.h new file mode 100644 index 0000000..2f51d91 --- /dev/null +++ b/webkit/glue/media/media_stream_client.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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_GLUE_MEDIA_MEDIA_STREAM_CLIENT_H_ +#define WEBKIT_GLUE_MEDIA_MEDIA_STREAM_CLIENT_H_ + +#include "base/memory/ref_counted.h" + +class GURL; + +namespace media { +class VideoDecoder; +class MessageLoopFactory; +} + +namespace webkit_glue { + +// Define an interface for media stream client to get some information about +// the media stream. +class MediaStreamClient { + public: + virtual scoped_refptr<media::VideoDecoder> GetVideoDecoder( + const GURL& url, + media::MessageLoopFactory* message_loop_factory) = 0; + + protected: + virtual ~MediaStreamClient() {} +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_MEDIA_MEDIA_STREAM_CLIENT_H_ diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc index acc44bc..102116e 100644 --- a/webkit/glue/webmediaplayer_impl.cc +++ b/webkit/glue/webmediaplayer_impl.cc @@ -27,6 +27,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebVideoFrame.h" #include "webkit/glue/media/buffered_data_source.h" #include "webkit/glue/media/simple_data_source.h" +#include "webkit/glue/media/media_stream_client.h" #include "webkit/glue/media/video_renderer_impl.h" #include "webkit/glue/media/web_video_renderer.h" #include "webkit/glue/webvideoframe_impl.h" @@ -271,7 +272,8 @@ void WebMediaPlayerImpl::Proxy::PutCurrentFrame( WebMediaPlayerImpl::WebMediaPlayerImpl( WebKit::WebMediaPlayerClient* client, media::FilterCollection* collection, - media::MessageLoopFactory* message_loop_factory) + media::MessageLoopFactory* message_loop_factory, + MediaStreamClient* media_stream_client) : network_state_(WebKit::WebMediaPlayer::Empty), ready_state_(WebKit::WebMediaPlayer::HaveNothing), main_loop_(NULL), @@ -282,7 +284,8 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( seeking_(false), playback_rate_(0.0f), client_(client), - proxy_(NULL) { + proxy_(NULL), + media_stream_client_(media_stream_client) { // Saves the current message loop. DCHECK(!main_loop_); main_loop_ = MessageLoop::current(); @@ -377,6 +380,17 @@ void WebMediaPlayerImpl::load(const WebKit::WebURL& url) { DCHECK(MessageLoop::current() == main_loop_); DCHECK(proxy_); + if (media_stream_client_) { + scoped_refptr<media::VideoDecoder> new_decoder = + media_stream_client_->GetVideoDecoder(url, message_loop_factory_.get()); + if (new_decoder.get()) { + // Remove the default decoder. + scoped_refptr<media::VideoDecoder> old_videodecoder; + filter_collection_->SelectVideoDecoder(&old_videodecoder); + filter_collection_->AddVideoDecoder(new_decoder.get()); + } + } + if (chunk_demuxer_factory_.get() && chunk_demuxer_factory_->IsUrlSupported(url.spec())) { media_data_sink_.reset(chunk_demuxer_factory_->CreateMediaDataSink()); diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h index 53cac08..49ede0d 100644 --- a/webkit/glue/webmediaplayer_impl.h +++ b/webkit/glue/webmediaplayer_impl.h @@ -79,6 +79,7 @@ class WebFrame; namespace webkit_glue { class MediaResourceLoaderBridgeFactory; +class MediaStreamClient; class WebVideoRenderer; class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, @@ -187,7 +188,8 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, // Callers must call |Initialize()| before they can use the object. WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client, media::FilterCollection* collection, - media::MessageLoopFactory* message_loop_factory); + media::MessageLoopFactory* message_loop_factory, + MediaStreamClient* media_stream_client); virtual ~WebMediaPlayerImpl(); // Finalizes initialization of the object. @@ -331,6 +333,8 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, scoped_ptr<media::ChunkDemuxerFactory> chunk_demuxer_factory_; scoped_ptr<media::MediaDataSink> media_data_sink_; + MediaStreamClient* media_stream_client_; + #if WEBKIT_USING_CG scoped_ptr<skia::PlatformCanvas> skia_canvas_; #endif diff --git a/webkit/support/webkit_support.cc b/webkit/support/webkit_support.cc index fd84ee6..18d5c8a 100644 --- a/webkit/support/webkit_support.cc +++ b/webkit/support/webkit_support.cc @@ -303,7 +303,8 @@ WebKit::WebMediaPlayer* CreateMediaPlayer(WebFrame* frame, scoped_ptr<webkit_glue::WebMediaPlayerImpl> result( new webkit_glue::WebMediaPlayerImpl(client, collection.release(), - message_loop_factory.release())); + message_loop_factory.release(), + NULL)); if (!result->Initialize(frame, false, video_renderer)) { return NULL; } diff --git a/webkit/tools/test_shell/test_webview_delegate.cc b/webkit/tools/test_shell/test_webview_delegate.cc index 9c6e2b7..61cc860 100644 --- a/webkit/tools/test_shell/test_webview_delegate.cc +++ b/webkit/tools/test_shell/test_webview_delegate.cc @@ -623,7 +623,8 @@ WebMediaPlayer* TestWebViewDelegate::createMediaPlayer( scoped_ptr<webkit_glue::WebMediaPlayerImpl> result( new webkit_glue::WebMediaPlayerImpl(client, collection.release(), - message_loop_factory.release())); + message_loop_factory.release(), + NULL)); if (!result->Initialize(frame, false, video_renderer)) { return NULL; } |