diff options
18 files changed, 442 insertions, 440 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 0cf05f0..e993306 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -701,14 +701,14 @@ 'renderer/media/rtc_video_encoder_factory.h', 'renderer/media/rtc_video_renderer.cc', 'renderer/media/rtc_video_renderer.h', - 'renderer/media/video_destination_handler.cc', - 'renderer/media/video_destination_handler.h', 'renderer/media/video_source_handler.cc', 'renderer/media/video_source_handler.h', 'renderer/media/webaudio_capturer_source.cc', 'renderer/media/webaudio_capturer_source.h', 'renderer/media/webrtc/media_stream_track_metrics.cc', 'renderer/media/webrtc/media_stream_track_metrics.h', + 'renderer/media/webrtc/video_destination_handler.cc', + 'renderer/media/webrtc/video_destination_handler.h', 'renderer/media/webrtc/webrtc_audio_sink_adapter.cc', 'renderer/media/webrtc/webrtc_audio_sink_adapter.h', 'renderer/media/webrtc/webrtc_local_audio_track_adapter.cc', @@ -780,8 +780,8 @@ ], 'sources!': [ 'renderer/media/pepper_platform_video_decoder.cc', - 'renderer/media/video_destination_handler.cc', - 'renderer/media/video_destination_handler.h', + 'renderer/media/webrtc/video_destination_handler.cc', + 'renderer/media/webrtc/video_destination_handler.h', 'renderer/render_widget_fullscreen_pepper.cc', 'renderer/render_widget_fullscreen_pepper.h', ], diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 53dd279..2289ef4 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -564,6 +564,8 @@ 'renderer/media/media_stream_video_capture_source_unittest.cc', 'renderer/media/media_stream_video_source_unittest.cc', 'renderer/media/media_stream_video_track_unittest.cc', + 'renderer/media/mock_media_stream_video_sink.cc', + 'renderer/media/mock_media_stream_video_sink.h', 'renderer/media/mock_media_stream_video_source.cc', 'renderer/media/mock_media_stream_video_source.h', 'renderer/media/render_media_log_unittest.cc', @@ -572,7 +574,6 @@ 'renderer/media/video_capture_impl_manager_unittest.cc', 'renderer/media/video_capture_impl_unittest.cc', 'renderer/media/video_capture_message_filter_unittest.cc', - 'renderer/media/video_destination_handler_unittest.cc', 'renderer/media/video_frame_compositor_unittest.cc', 'renderer/media/webaudiosourceprovider_impl_unittest.cc', 'renderer/npapi/webplugin_impl_unittest.cc', @@ -703,7 +704,7 @@ 'sources!': [ 'browser/plugin_loader_posix_unittest.cc', 'renderer/media/media_stream_video_source_unittest.cc', - 'renderer/media/video_destination_handler_unittest.cc', + 'renderer/media/webrtc/video_destination_handler_unittest.cc', ], }], ['enable_webrtc==1', { @@ -725,6 +726,7 @@ 'renderer/media/rtc_video_decoder_unittest.cc', 'renderer/media/video_source_handler_unittest.cc', 'renderer/media/webrtc/media_stream_track_metrics_unittest.cc', + 'renderer/media/webrtc/video_destination_handler_unittest.cc', 'renderer/media/webrtc/webrtc_local_audio_track_adapter_unittest.cc', 'renderer/media/webrtc/webrtc_video_capturer_adapter_unittest.cc', 'renderer/media/webrtc_audio_capturer_unittest.cc', diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc index 243d361..6091726 100644 --- a/content/renderer/media/media_stream_video_source.cc +++ b/content/renderer/media/media_stream_video_source.cc @@ -26,6 +26,17 @@ const char MediaStreamVideoSource::kMinHeight[] = "minHeight"; const char MediaStreamVideoSource::kMaxFrameRate[] = "maxFrameRate"; const char MediaStreamVideoSource::kMinFrameRate[] = "minFrameRate"; +const char* kSupportedConstraints[] = { + MediaStreamVideoSource::kMaxAspectRatio, + MediaStreamVideoSource::kMinAspectRatio, + MediaStreamVideoSource::kMaxWidth, + MediaStreamVideoSource::kMinWidth, + MediaStreamVideoSource::kMaxHeight, + MediaStreamVideoSource::kMinHeight, + MediaStreamVideoSource::kMaxFrameRate, + MediaStreamVideoSource::kMinFrameRate, +}; + const int MediaStreamVideoSource::kDefaultWidth = 640; const int MediaStreamVideoSource::kDefaultHeight = 480; const int MediaStreamVideoSource::kDefaultFrameRate = 30; @@ -283,6 +294,15 @@ MediaStreamVideoSource* MediaStreamVideoSource::GetVideoSource( return static_cast<MediaStreamVideoSource*>(source.extraData()); } +//static +bool MediaStreamVideoSource::IsConstraintSupported(const std::string& name) { + for (size_t i = 0; i < arraysize(kSupportedConstraints); ++i) { + if (kSupportedConstraints[i] == name) + return true; + } + return false; +} + MediaStreamVideoSource::MediaStreamVideoSource( MediaStreamDependencyFactory* factory) : state_(NEW), @@ -354,7 +374,6 @@ void MediaStreamVideoSource::InitAdapter() { device_info().device.type == MEDIA_TAB_VIDEO_CAPTURE || device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE; capture_adapter_ = factory_->CreateVideoCapturer(is_screencast); - capture_adapter_->SetRequestedFormat(current_format_); adapter_ = factory_->CreateVideoSource(capture_adapter_, current_constraints_); } diff --git a/content/renderer/media/media_stream_video_source.h b/content/renderer/media/media_stream_video_source.h index 651f003..11eacca 100644 --- a/content/renderer/media/media_stream_video_source.h +++ b/content/renderer/media/media_stream_video_source.h @@ -66,6 +66,9 @@ class CONTENT_EXPORT MediaStreamVideoSource // exist. webrtc::VideoSourceInterface* GetAdapter(); + // Return true if |name| is a constraint supported by MediaStreamVideoSource. + static bool IsConstraintSupported(const std::string& name); + // Constraint keys used by a video source. // Specified by draft-alvestrand-constraints-resolution-00b static const char kMinAspectRatio[]; // minAspectRatio @@ -120,6 +123,15 @@ class CONTENT_EXPORT MediaStreamVideoSource // method has been called, MediaStreamVideoSource may be deleted. virtual void StopSourceImpl() = 0; + enum State { + NEW, + RETRIEVING_CAPABILITIES, + STARTING, + STARTED, + ENDED + }; + State state() { return state_; } + private: // Creates a webrtc::VideoSourceInterface used by libjingle. void InitAdapter(); @@ -140,13 +152,6 @@ class CONTENT_EXPORT MediaStreamVideoSource // AddTrack match the format that was used to start the device. void FinalizeAddTrack(); - enum State { - NEW, - RETRIEVING_CAPABILITIES, - STARTING, - STARTED, - ENDED - }; State state_; media::VideoCaptureFormat current_format_; diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc index 5a360ca..b3392da 100644 --- a/content/renderer/media/media_stream_video_source_unittest.cc +++ b/content/renderer/media/media_stream_video_source_unittest.cc @@ -383,4 +383,26 @@ TEST_F(MediaStreamVideoSourceTest, DeliverSmallerSizeWhenTooLargeMax) { 1280, 720); } +TEST_F(MediaStreamVideoSourceTest, IsConstraintSupported) { + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMaxFrameRate)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMinFrameRate)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMaxWidth)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMinWidth)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMaxHeight)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMinHeight)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMaxAspectRatio)); + EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( + MediaStreamVideoSource::kMinAspectRatio)); + + EXPECT_FALSE(MediaStreamVideoSource::IsConstraintSupported( + "googCpuAdaptation")); +} + } // namespace content diff --git a/content/renderer/media/media_stream_video_track_unittest.cc b/content/renderer/media/media_stream_video_track_unittest.cc index 242b9ce..d2e8482 100644 --- a/content/renderer/media/media_stream_video_track_unittest.cc +++ b/content/renderer/media/media_stream_video_track_unittest.cc @@ -5,43 +5,13 @@ #include "base/strings/utf_string_conversions.h" #include "content/renderer/media/media_stream_video_track.h" #include "content/renderer/media/mock_media_stream_dependency_factory.h" +#include "content/renderer/media/mock_media_stream_video_sink.h" #include "content/renderer/media/mock_media_stream_video_source.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { -class MockVideoSink : public MediaStreamVideoSink { - public: - MockVideoSink() - : number_of_frames_(0), enabled_(true), - state_(blink::WebMediaStreamSource::ReadyStateLive) { - } - - virtual void OnVideoFrame( - const scoped_refptr<media::VideoFrame>& frame) OVERRIDE { - ++number_of_frames_; - } - - virtual void OnReadyStateChanged( - blink::WebMediaStreamSource::ReadyState state) OVERRIDE { - state_ = state; - } - - virtual void OnEnabledChanged(bool enabled) OVERRIDE { - enabled_ = enabled; - } - - int number_of_frames() const { return number_of_frames_; } - bool enabled() const { return enabled_; } - blink::WebMediaStreamSource::ReadyState state() const { return state_; } - - private: - int number_of_frames_; - bool enabled_; - blink::WebMediaStreamSource::ReadyState state_; -}; - class MediaStreamVideoTrackTest : public ::testing::Test { public: MediaStreamVideoTrackTest() @@ -84,7 +54,7 @@ TEST_F(MediaStreamVideoTrackTest, GetAdapter) { } TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) { - MockVideoSink sink; + MockMediaStreamVideoSink sink; blink::WebMediaStreamTrack track = CreateTrack(); MediaStreamVideoSink::AddToVideoTrack(&sink, track); @@ -105,7 +75,7 @@ TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) { } TEST_F(MediaStreamVideoTrackTest, SetEnabled) { - MockVideoSink sink; + MockMediaStreamVideoSink sink; blink::WebMediaStreamTrack track = CreateTrack(); MediaStreamVideoSink::AddToVideoTrack(&sink, track); @@ -131,7 +101,7 @@ TEST_F(MediaStreamVideoTrackTest, SetEnabled) { } TEST_F(MediaStreamVideoTrackTest, SourceStopped) { - MockVideoSink sink; + MockMediaStreamVideoSink sink; blink::WebMediaStreamTrack track = CreateTrack(); MediaStreamVideoSink::AddToVideoTrack(&sink, track); EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink.state()); diff --git a/content/renderer/media/mock_media_stream_dependency_factory.cc b/content/renderer/media/mock_media_stream_dependency_factory.cc index f934cce..1441d32 100644 --- a/content/renderer/media/mock_media_stream_dependency_factory.cc +++ b/content/renderer/media/mock_media_stream_dependency_factory.cc @@ -130,10 +130,6 @@ class MockRtcVideoCapturer : public WebRtcVideoCapturerAdapter { height_(0) { } - virtual void SetRequestedFormat( - const media::VideoCaptureFormat& format) OVERRIDE { - } - virtual void OnFrameCaptured( const scoped_refptr<media::VideoFrame>& frame) OVERRIDE { ++number_of_capturered_frames_; diff --git a/content/renderer/media/mock_media_stream_video_sink.cc b/content/renderer/media/mock_media_stream_video_sink.cc new file mode 100644 index 0000000..af09244 --- /dev/null +++ b/content/renderer/media/mock_media_stream_video_sink.cc @@ -0,0 +1,31 @@ +// Copyright 2014 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/mock_media_stream_video_sink.h" + +namespace content { + +MockMediaStreamVideoSink::MockMediaStreamVideoSink() + : number_of_frames_(0), + enabled_(true), + format_(media::VideoFrame::UNKNOWN), + state_(blink::WebMediaStreamSource::ReadyStateLive) { +} + +void MockMediaStreamVideoSink::OnVideoFrame( + const scoped_refptr<media::VideoFrame>& frame) { + ++number_of_frames_; + format_ = frame->format(); +} + +void MockMediaStreamVideoSink::OnReadyStateChanged( + blink::WebMediaStreamSource::ReadyState state) { + state_ = state; +} + +void MockMediaStreamVideoSink::OnEnabledChanged(bool enabled) { + enabled_ = enabled; +} + +} // namespace content diff --git a/content/renderer/media/mock_media_stream_video_sink.h b/content/renderer/media/mock_media_stream_video_sink.h new file mode 100644 index 0000000..5802f8a --- /dev/null +++ b/content/renderer/media/mock_media_stream_video_sink.h @@ -0,0 +1,38 @@ +// Copyright 2014 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_MOCK_MEDIA_STREAM_VIDEO_SINK_H_ +#define CONTENT_RENDERER_MEDIA_MOCK_MEDIA_STREAM_VIDEO_SINK_H_ + +#include "content/public/renderer/media_stream_video_sink.h" + +#include "media/base/video_frame.h" + +namespace content { + +class MockMediaStreamVideoSink : public MediaStreamVideoSink { + public: + MockMediaStreamVideoSink(); + + virtual void OnVideoFrame( + const scoped_refptr<media::VideoFrame>& frame) OVERRIDE; + virtual void OnReadyStateChanged( + blink::WebMediaStreamSource::ReadyState state) OVERRIDE; + virtual void OnEnabledChanged(bool enabled) OVERRIDE; + + int number_of_frames() const { return number_of_frames_; } + media::VideoFrame::Format format() const { return format_; } + bool enabled() const { return enabled_; } + blink::WebMediaStreamSource::ReadyState state() const { return state_; } + + private: + int number_of_frames_; + bool enabled_; + media::VideoFrame::Format format_; + blink::WebMediaStreamSource::ReadyState state_; +}; + +} // namespace content + +#endif diff --git a/content/renderer/media/rtc_media_constraints.cc b/content/renderer/media/rtc_media_constraints.cc index a476be3..7f8b79d 100644 --- a/content/renderer/media/rtc_media_constraints.cc +++ b/content/renderer/media/rtc_media_constraints.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/strings/string_util.h" #include "content/common/media/media_stream_options.h" +#include "content/renderer/media/media_stream_video_source.h" #include "third_party/WebKit/public/platform/WebMediaConstraints.h" #include "third_party/WebKit/public/platform/WebCString.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -33,6 +34,10 @@ void GetNativeMediaConstraints( if (new_constraint.key == kMediaStreamSourceInfoId) continue; + // Ignore constraints that are handled by Chrome in MediaStreamVideoSource. + if (MediaStreamVideoSource::IsConstraintSupported(new_constraint.key)) + continue; + DVLOG(3) << "MediaStreamConstraints:" << new_constraint.key << " : " << new_constraint.value; native_constraints->push_back(new_constraint); diff --git a/content/renderer/media/video_destination_handler.cc b/content/renderer/media/video_destination_handler.cc deleted file mode 100644 index a76ca89..0000000 --- a/content/renderer/media/video_destination_handler.cc +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2013 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_destination_handler.h" - -#include <string> - -#include "base/base64.h" -#include "base/logging.h" -#include "base/rand_util.h" -#include "content/renderer/media/media_stream.h" -#include "content/renderer/media/media_stream_dependency_factory.h" -#include "content/renderer/media/media_stream_registry_interface.h" -#include "content/renderer/pepper/ppb_image_data_impl.h" -#include "content/renderer/render_thread_impl.h" -#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" -#include "third_party/WebKit/public/platform/WebURL.h" -#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" -#include "url/gurl.h" - -using cricket::CaptureState; -using cricket::VideoFormat; -using webrtc::VideoTrackInterface; -using webrtc::VideoTrackVector; - -static const cricket::FourCC kEffectColorFormat = cricket::FOURCC_BGRA; - -namespace content { - -PpFrameWriter::PpFrameWriter() - : started_(false) {} - -PpFrameWriter::~PpFrameWriter() {} - -CaptureState PpFrameWriter::Start(const VideoFormat& capture_format) { - base::AutoLock auto_lock(lock_); - if (started_) { - LOG(ERROR) << "PpFrameWriter::Start - " - << "Got a StartCapture when already started!"; - return cricket::CS_FAILED; - } - started_ = true; - return cricket::CS_STARTING; -} - -void PpFrameWriter::Stop() { - base::AutoLock auto_lock(lock_); - started_ = false; - SignalStateChange(this, cricket::CS_STOPPED); -} - -bool PpFrameWriter::IsRunning() { - return started_; -} - -bool PpFrameWriter::GetPreferredFourccs(std::vector<uint32>* fourccs) { - if (!fourccs) { - LOG(ERROR) << "PpFrameWriter::GetPreferredFourccs - " - << "fourccs is NULL."; - return false; - } - // The effects plugin output BGRA. - fourccs->push_back(kEffectColorFormat); - return true; -} - -bool PpFrameWriter::GetBestCaptureFormat(const VideoFormat& desired, - VideoFormat* best_format) { - if (!best_format) { - LOG(ERROR) << "PpFrameWriter::GetBestCaptureFormat - " - << "best_format is NULL."; - return false; - } - - // Use the desired format as the best format. - best_format->width = desired.width; - best_format->height = desired.height; - best_format->fourcc = kEffectColorFormat; - best_format->interval = desired.interval; - return true; -} - -bool PpFrameWriter::IsScreencast() const { - return false; -} - -void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, - int64 time_stamp_ns) { - base::AutoLock auto_lock(lock_); - // This assumes the handler of the SignalFrameCaptured won't call Start/Stop. - // TODO(ronghuawu): Avoid the using of lock. One way is to post this call to - // libjingle worker thread, which will require an extra copy of |image_data|. - // However if pepper host can hand over the ownership of |image_data| - // then we can avoid this extra copy. - if (!started_) { - LOG(ERROR) << "PpFrameWriter::PutFrame - " - << "Called when capturer is not started."; - return; - } - if (!image_data) { - LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; - return; - } - ImageDataAutoMapper mapper(image_data); - if (!mapper.is_valid()) { - LOG(ERROR) << "PpFrameWriter::PutFrame - " - << "The image could not be mapped and is unusable."; - return; - } - const SkBitmap* bitmap = image_data->GetMappedBitmap(); - if (!bitmap) { - LOG(ERROR) << "PpFrameWriter::PutFrame - " - << "The image_data's mapped bitmap is NULL."; - return; - } - - cricket::CapturedFrame frame; - frame.elapsed_time = 0; - frame.time_stamp = time_stamp_ns; - frame.pixel_height = 1; - frame.pixel_width = 1; - frame.width = bitmap->width(); - frame.height = bitmap->height(); - if (image_data->format() == PP_IMAGEDATAFORMAT_BGRA_PREMUL) { - frame.fourcc = cricket::FOURCC_BGRA; - } else { - LOG(ERROR) << "PpFrameWriter::PutFrame - Got RGBA which is not supported."; - return; - } - frame.data_size = bitmap->getSize(); - frame.data = bitmap->getPixels(); - - // This signals to libJingle that a new VideoFrame is available. - // libJingle have no assumptions on what thread this signal come from. - SignalFrameCaptured(this, &frame); -} - -// PpFrameWriterProxy is a helper class to make sure the user won't use -// PpFrameWriter after it is released (IOW its owner - WebMediaStreamTrack - -// is released). -class PpFrameWriterProxy : public FrameWriterInterface { - public: - PpFrameWriterProxy(VideoTrackInterface* track, - PpFrameWriter* writer) - : track_(track), - writer_(writer) { - DCHECK(writer_ != NULL); - } - - virtual ~PpFrameWriterProxy() {} - - virtual void PutFrame(PPB_ImageData_Impl* image_data, - int64 time_stamp_ns) OVERRIDE { - writer_->PutFrame(image_data, time_stamp_ns); - } - - private: - scoped_refptr<VideoTrackInterface> track_; - PpFrameWriter* writer_; - - DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); -}; - -bool VideoDestinationHandler::Open( - MediaStreamDependencyFactory* factory, - MediaStreamRegistryInterface* registry, - const std::string& url, - FrameWriterInterface** frame_writer) { - if (!factory) { - factory = RenderThreadImpl::current()->GetMediaStreamDependencyFactory(); - DCHECK(factory != NULL); - } - blink::WebMediaStream stream; - if (registry) { - stream = registry->GetMediaStream(url); - } else { - stream = - blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); - } - if (stream.isNull() || !stream.extraData()) { - LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; - return false; - } - - // Create a new native video track and add it to |stream|. - std::string track_id; - // According to spec, a media stream track's id should be globally unique. - // There's no easy way to strictly achieve that. The id generated with this - // method should be unique for most of the cases but theoretically it's - // possible we can get an id that's duplicated with the existing tracks. - base::Base64Encode(base::RandBytesAsString(64), &track_id); - PpFrameWriter* writer = new PpFrameWriter(); - if (!factory->AddNativeVideoMediaTrack(track_id, &stream, writer)) { - delete writer; - return false; - } - - // Gets a handler to the native video track, which owns the |writer|. - webrtc::MediaStreamInterface* native_stream = MediaStream::GetAdapter(stream); - DCHECK(native_stream); - VideoTrackVector video_tracks = native_stream->GetVideoTracks(); - // Currently one supports one video track per media stream. - DCHECK(video_tracks.size() == 1); - - *frame_writer = new PpFrameWriterProxy(video_tracks[0].get(), writer); - return true; -} - -} // namespace content - diff --git a/content/renderer/media/video_destination_handler_unittest.cc b/content/renderer/media/video_destination_handler_unittest.cc deleted file mode 100644 index f220f9f..0000000 --- a/content/renderer/media/video_destination_handler_unittest.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2013 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 <string> - -#include "base/strings/utf_string_conversions.h" -#include "content/renderer/media/media_stream.h" -#include "content/renderer/media/mock_media_stream_dependency_factory.h" -#include "content/renderer/media/mock_media_stream_registry.h" -#include "content/renderer/media/video_destination_handler.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" -#include "third_party/WebKit/public/platform/WebString.h" - -using cricket::CapturedFrame; -using cricket::CaptureState; -using cricket::VideoCapturer; -using cricket::VideoFormat; -using cricket::VideoFormatPod; - -namespace content { - -static const std::string kTestStreamUrl = "stream_url"; -static const std::string kUnknownStreamUrl = "unknown_stream_url"; -static const VideoFormatPod kTestFormat = { - 640, 360, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY -}; - -class PpFrameWriterTest - : public ::testing::Test, - public sigslot::has_slots<> { - public: - PpFrameWriterTest() - : last_capture_state_(cricket::CS_FAILED), - captured_frame_count_(0), - captured_frame_(NULL) { - writer_.SignalStateChange.connect(this, &PpFrameWriterTest::OnStateChange); - writer_.SignalFrameCaptured.connect( - this, &PpFrameWriterTest::OnFrameCaptured); - } - - void OnStateChange(VideoCapturer* capturer, CaptureState state) { - last_capture_state_ = state; - } - - void OnFrameCaptured(VideoCapturer* capturer, const CapturedFrame* frame) { - ++captured_frame_count_; - captured_frame_ = const_cast<CapturedFrame*>(frame); - } - - protected: - PpFrameWriter writer_; - CaptureState last_capture_state_; - int captured_frame_count_; - CapturedFrame* captured_frame_; -}; - -class VideoDestinationHandlerTest : public ::testing::Test { - public: - VideoDestinationHandlerTest() : registry_(&factory_) { - registry_.Init(kTestStreamUrl); - } - - protected: - MockMediaStreamDependencyFactory factory_; - MockMediaStreamRegistry registry_; -}; - -TEST_F(PpFrameWriterTest, StartStop) { - EXPECT_FALSE(writer_.IsRunning()); - EXPECT_EQ(cricket::CS_STARTING, writer_.Start(VideoFormat(kTestFormat))); - EXPECT_TRUE(writer_.IsRunning()); - EXPECT_EQ(cricket::CS_FAILED, writer_.Start(VideoFormat(kTestFormat))); - writer_.Stop(); - EXPECT_EQ(cricket::CS_STOPPED, last_capture_state_); -} - -TEST_F(PpFrameWriterTest, GetPreferredFourccs) { - std::vector<uint32> fourccs; - EXPECT_TRUE(writer_.GetPreferredFourccs(&fourccs)); - EXPECT_EQ(1u, fourccs.size()); - EXPECT_EQ(cricket::FOURCC_BGRA, fourccs[0]); -} - -TEST_F(PpFrameWriterTest, GetBestCaptureFormat) { - VideoFormat desired(kTestFormat); - VideoFormat best_format; - EXPECT_FALSE(writer_.GetBestCaptureFormat(desired, NULL)); - EXPECT_TRUE(writer_.GetBestCaptureFormat(desired, &best_format)); - EXPECT_EQ(cricket::FOURCC_BGRA, best_format.fourcc); - - desired.fourcc = best_format.fourcc; - EXPECT_EQ(desired, best_format); -} - -TEST_F(VideoDestinationHandlerTest, Open) { - FrameWriterInterface* frame_writer = NULL; - // Unknow url will return false. - EXPECT_FALSE(VideoDestinationHandler::Open(&factory_, ®istry_, - kUnknownStreamUrl, &frame_writer)); - EXPECT_TRUE(VideoDestinationHandler::Open(&factory_, ®istry_, - kTestStreamUrl, &frame_writer)); - EXPECT_TRUE(frame_writer); - - // Verify the video track has been added. - const blink::WebMediaStream test_stream = registry_.test_stream(); - blink::WebVector<blink::WebMediaStreamTrack> video_tracks; - test_stream.videoTracks(video_tracks); - EXPECT_EQ(1u, video_tracks.size()); - - // Verify the native video track has been added. - MediaStream* native_stream = MediaStream::GetMediaStream(test_stream); - DCHECK(native_stream); - webrtc::MediaStreamInterface* webrtc_stream = - MediaStream::GetAdapter(test_stream); - DCHECK(webrtc_stream); - webrtc::VideoTrackVector webrtc_video_tracks = - webrtc_stream->GetVideoTracks(); - EXPECT_EQ(1u, webrtc_video_tracks.size()); - - delete frame_writer; -} - -} // namespace content diff --git a/content/renderer/media/webrtc/video_destination_handler.cc b/content/renderer/media/webrtc/video_destination_handler.cc new file mode 100644 index 0000000..ca3d4e4 --- /dev/null +++ b/content/renderer/media/webrtc/video_destination_handler.cc @@ -0,0 +1,195 @@ +// Copyright (c) 2013 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/webrtc/video_destination_handler.h" + +#include <string> + +#include "base/base64.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/renderer/media/media_stream.h" +#include "content/renderer/media/media_stream_dependency_factory.h" +#include "content/renderer/media/media_stream_registry_interface.h" +#include "content/renderer/media/media_stream_video_track.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/render_thread_impl.h" +#include "media/video/capture/video_capture_types.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "url/gurl.h" + +namespace content { + +PpFrameWriter::PpFrameWriter(MediaStreamDependencyFactory* factory) + : MediaStreamVideoSource(factory), first_frame_received_(false) { + DVLOG(3) << "PpFrameWriter ctor"; +} + +PpFrameWriter::~PpFrameWriter() { + DVLOG(3) << "PpFrameWriter dtor"; +} + +void PpFrameWriter::GetCurrentSupportedFormats(int max_requested_width, + int max_requested_height) { + DCHECK(CalledOnValidThread()); + DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()"; + if (format_.IsValid()) { + media::VideoCaptureFormats formats; + formats.push_back(format_); + OnSupportedFormats(formats); + } +} + +void PpFrameWriter::StartSourceImpl( + const media::VideoCaptureParams& params) { + DCHECK(CalledOnValidThread()); + DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; + OnStartDone(true); +} + +void PpFrameWriter::StopSourceImpl() { + DCHECK(CalledOnValidThread()); +} + +void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, + int64 time_stamp_ns) { + DCHECK(CalledOnValidThread()); + DVLOG(3) << "PpFrameWriter::PutFrame()"; + + if (!image_data) { + LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; + return; + } + ImageDataAutoMapper mapper(image_data); + if (!mapper.is_valid()) { + LOG(ERROR) << "PpFrameWriter::PutFrame - " + << "The image could not be mapped and is unusable."; + return; + } + const SkBitmap* bitmap = image_data->GetMappedBitmap(); + if (!bitmap) { + LOG(ERROR) << "PpFrameWriter::PutFrame - " + << "The image_data's mapped bitmap is NULL."; + return; + } + + const gfx::Size frame_size(bitmap->width(), bitmap->height()); + + if (!first_frame_received_) { + first_frame_received_ = true; + format_ = media::VideoCaptureFormat( + frame_size, + MediaStreamVideoSource::kDefaultFrameRate, + media::PIXEL_FORMAT_I420); + if (state() == MediaStreamVideoSource::RETRIEVING_CAPABILITIES) { + media::VideoCaptureFormats formats; + formats.push_back(format_); + OnSupportedFormats(formats); + } + } + + if (state() != MediaStreamVideoSource::STARTED) + return; + + const base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( + time_stamp_ns / talk_base::kNumNanosecsPerMillisec); + + scoped_refptr<media::VideoFrame> new_frame = + frame_pool_.CreateFrame(media::VideoFrame::I420, frame_size, + gfx::Rect(frame_size), frame_size, timestamp); + + libyuv::BGRAToI420(reinterpret_cast<uint8*>(bitmap->getPixels()), + bitmap->rowBytes(), + new_frame->data(media::VideoFrame::kYPlane), + new_frame->stride(media::VideoFrame::kYPlane), + new_frame->data(media::VideoFrame::kUPlane), + new_frame->stride(media::VideoFrame::kUPlane), + new_frame->data(media::VideoFrame::kVPlane), + new_frame->stride(media::VideoFrame::kVPlane), + frame_size.width(), frame_size.height()); + + DeliverVideoFrame(new_frame); +} + +// PpFrameWriterProxy is a helper class to make sure the user won't use +// PpFrameWriter after it is released (IOW its owner - WebMediaStreamSource - +// is released). +class PpFrameWriterProxy : public FrameWriterInterface { + public: + explicit PpFrameWriterProxy(const base::WeakPtr<PpFrameWriter>& writer) + : writer_(writer) { + DCHECK(writer_ != NULL); + } + + virtual ~PpFrameWriterProxy() {} + + virtual void PutFrame(PPB_ImageData_Impl* image_data, + int64 time_stamp_ns) OVERRIDE { + writer_->PutFrame(image_data, time_stamp_ns); + } + + private: + base::WeakPtr<PpFrameWriter> writer_; + + DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); +}; + +bool VideoDestinationHandler::Open( + MediaStreamDependencyFactory* factory, + MediaStreamRegistryInterface* registry, + const std::string& url, + FrameWriterInterface** frame_writer) { + DVLOG(3) << "VideoDestinationHandler::Open"; + if (!factory) { + factory = RenderThreadImpl::current()->GetMediaStreamDependencyFactory(); + DCHECK(factory != NULL); + } + blink::WebMediaStream stream; + if (registry) { + stream = registry->GetMediaStream(url); + } else { + stream = + blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); + } + if (stream.isNull()) { + LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; + return false; + } + + // Create a new native video track and add it to |stream|. + std::string track_id; + // According to spec, a media stream source's id should be unique per + // application. There's no easy way to strictly achieve that. The id + // generated with this method should be unique for most of the cases but + // theoretically it's possible we can get an id that's duplicated with the + // existing sources. + base::Base64Encode(base::RandBytesAsString(64), &track_id); + PpFrameWriter* writer = new PpFrameWriter(factory); + + // Create a new webkit video track. + blink::WebMediaStreamSource webkit_source; + blink::WebMediaStreamSource::Type type = + blink::WebMediaStreamSource::TypeVideo; + blink::WebString webkit_track_id = base::UTF8ToUTF16(track_id); + webkit_source.initialize(webkit_track_id, type, webkit_track_id); + webkit_source.setExtraData(writer); + + blink::WebMediaConstraints constraints; + constraints.initialize(); + bool track_enabled = true; + + stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( + writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), + track_enabled, factory)); + + *frame_writer = new PpFrameWriterProxy(writer->AsWeakPtr()); + return true; +} + +} // namespace content + diff --git a/content/renderer/media/video_destination_handler.h b/content/renderer/media/webrtc/video_destination_handler.h index 578fe22..e490002 100644 --- a/content/renderer/media/video_destination_handler.h +++ b/content/renderer/media/webrtc/video_destination_handler.h @@ -2,16 +2,17 @@ // 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_DESTINATION_HANDLER_H_ -#define CONTENT_RENDERER_MEDIA_VIDEO_DESTINATION_HANDLER_H_ +#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_VIDEO_DESTINATION_HANDLER_H_ +#define CONTENT_RENDERER_MEDIA_WEBRTC_VIDEO_DESTINATION_HANDLER_H_ #include <string> #include <vector> #include "base/compiler_specific.h" -#include "base/synchronization/lock.h" +#include "base/memory/weak_ptr.h" #include "content/common/content_export.h" -#include "third_party/libjingle/source/talk/media/base/videocapturer.h" +#include "content/renderer/media/media_stream_video_source.h" +#include "media/base/video_frame_pool.h" namespace content { @@ -30,43 +31,40 @@ class CONTENT_EXPORT FrameWriterInterface { virtual ~FrameWriterInterface() {} }; -// PpFrameWriter implements cricket::VideoCapturer so that it can be used in -// the native video track's video source. It also implements +// PpFrameWriter implements MediaStreamVideoSource and can therefore provide +// video frames to MediaStreamVideoTracks. It also implements // FrameWriterInterface, which will be used by the effects pepper plugin to // inject the processed frame. class CONTENT_EXPORT PpFrameWriter - : public NON_EXPORTED_BASE(cricket::VideoCapturer), - public FrameWriterInterface { + : NON_EXPORTED_BASE(public MediaStreamVideoSource), + public FrameWriterInterface, + NON_EXPORTED_BASE(public base::SupportsWeakPtr<PpFrameWriter>) { public: - PpFrameWriter(); + explicit PpFrameWriter(MediaStreamDependencyFactory* factory); virtual ~PpFrameWriter(); - // cricket::VideoCapturer implementation. - // These methods are accessed from a libJingle worker thread. - virtual cricket::CaptureState Start( - const cricket::VideoFormat& capture_format) OVERRIDE; - virtual void Stop() OVERRIDE; - virtual bool IsRunning() OVERRIDE; - virtual bool GetPreferredFourccs(std::vector<uint32>* fourccs) OVERRIDE; - virtual bool GetBestCaptureFormat(const cricket::VideoFormat& desired, - cricket::VideoFormat* best_format) OVERRIDE; - virtual bool IsScreencast() const OVERRIDE; - // FrameWriterInterface implementation. // This method will be called by the Pepper host from render thread. virtual void PutFrame(PPB_ImageData_Impl* image_data, int64 time_stamp_ns) OVERRIDE; + protected: + // MediaStreamVideoSource implementation. + virtual void GetCurrentSupportedFormats(int max_requested_width, + int max_requested_height) OVERRIDE; + virtual void StartSourceImpl( + const media::VideoCaptureParams& params) OVERRIDE; + virtual void StopSourceImpl() OVERRIDE; private: - bool started_; - // |lock_| is used to protect |started_| which will be accessed from different - // threads - libjingle worker thread and render thread. - base::Lock lock_; + // |format_| is the format currently received by this source. + media::VideoCaptureFormat format_; + bool first_frame_received_; + media::VideoFramePool frame_pool_; DISALLOW_COPY_AND_ASSIGN(PpFrameWriter); }; -// VideoDestinationHandler is a glue class between the webrtc MediaStream and +// VideoDestinationHandler is a glue class between the content MediaStream and // the effects pepper plugin host. class CONTENT_EXPORT VideoDestinationHandler { public: diff --git a/content/renderer/media/webrtc/video_destination_handler_unittest.cc b/content/renderer/media/webrtc/video_destination_handler_unittest.cc new file mode 100644 index 0000000..7a99b38 --- /dev/null +++ b/content/renderer/media/webrtc/video_destination_handler_unittest.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2013 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 <string> + +#include "base/strings/utf_string_conversions.h" +#include "content/renderer/media/media_stream.h" +#include "content/renderer/media/media_stream_video_track.h" +#include "content/renderer/media/mock_media_stream_dependency_factory.h" +#include "content/renderer/media/mock_media_stream_registry.h" +#include "content/renderer/media/mock_media_stream_video_sink.h" +#include "content/renderer/media/webrtc/video_destination_handler.h" +#include "content/renderer/pepper/pepper_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/test/ppapi_unittest.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebString.h" + +namespace content { + +static const std::string kTestStreamUrl = "stream_url"; +static const std::string kUnknownStreamUrl = "unknown_stream_url"; + +class VideoDestinationHandlerTest : public PpapiUnittest { + public: + VideoDestinationHandlerTest() : registry_(&factory_) { + registry_.Init(kTestStreamUrl); + } + + protected: + MockMediaStreamDependencyFactory factory_; + MockMediaStreamRegistry registry_; +}; + +TEST_F(VideoDestinationHandlerTest, Open) { + FrameWriterInterface* frame_writer = NULL; + // Unknow url will return false. + EXPECT_FALSE(VideoDestinationHandler::Open(&factory_, ®istry_, + kUnknownStreamUrl, &frame_writer)); + EXPECT_TRUE(VideoDestinationHandler::Open(&factory_, ®istry_, + kTestStreamUrl, &frame_writer)); + // The |frame_writer| is a proxy and is owned by who call Open. + delete frame_writer; +} + +TEST_F(VideoDestinationHandlerTest, PutFrame) { + FrameWriterInterface* frame_writer = NULL; + EXPECT_TRUE(VideoDestinationHandler::Open(&factory_, ®istry_, + kTestStreamUrl, &frame_writer)); + ASSERT_TRUE(frame_writer); + + // Verify the video track has been added. + const blink::WebMediaStream test_stream = registry_.test_stream(); + blink::WebVector<blink::WebMediaStreamTrack> video_tracks; + test_stream.videoTracks(video_tracks); + ASSERT_EQ(1u, video_tracks.size()); + + // Verify the native video track has been added. + MediaStreamVideoTrack* native_track = + MediaStreamVideoTrack::GetVideoTrack(video_tracks[0]); + ASSERT_TRUE(native_track != NULL); + + MockMediaStreamVideoSink sink; + native_track->AddSink(&sink); + + scoped_refptr<PPB_ImageData_Impl> image( + new PPB_ImageData_Impl(instance()->pp_instance(), + PPB_ImageData_Impl::ForTest())); + image->Init(PP_IMAGEDATAFORMAT_BGRA_PREMUL, 640, 360, true); + frame_writer->PutFrame(image, 10); + EXPECT_EQ(1, sink.number_of_frames()); + // TODO(perkj): Verify that the track output I420 when + // https://codereview.chromium.org/213423006/ is landed. + + native_track->RemoveSink(&sink); + + // The |frame_writer| is a proxy and is owned by who call Open. + delete frame_writer; +} + +} // namespace content diff --git a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc index 6d6f7ff..d028b3f 100644 --- a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc +++ b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc @@ -24,25 +24,6 @@ WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { base::AlignedFree(buffer_); } -void WebRtcVideoCapturerAdapter::SetRequestedFormat( - const media::VideoCaptureFormat& format) { - DCHECK_EQ(media::PIXEL_FORMAT_I420, format.pixel_format); - DVLOG(3) << "WebRtcVideoCapturerAdapter::SetRequestedFormat" - << " w = " << format.frame_size.width() - << " h = " << format.frame_size.height(); - cricket::VideoFormat supported_format(format.frame_size.width(), - format.frame_size.height(), - cricket::VideoFormat::FpsToInterval( - format.frame_rate), - cricket::FOURCC_I420); - SetCaptureFormat(&supported_format); - - // Update the desired aspect ratio so that later the video frame can be - // cropped to meet the requirement if the camera returns a different - // resolution than the |request|. - UpdateAspectRatio(format.frame_size.width(), format.frame_size.height()); -} - cricket::CaptureState WebRtcVideoCapturerAdapter::Start( const cricket::VideoFormat& capture_format) { DCHECK(!running_); diff --git a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.h b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.h index ca453e0..753bf3c 100644 --- a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.h +++ b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.h @@ -25,12 +25,6 @@ class CONTENT_EXPORT WebRtcVideoCapturerAdapter explicit WebRtcVideoCapturerAdapter(bool is_screencast); virtual ~WebRtcVideoCapturerAdapter(); - // Sets the requested format. cricket::VideoCapturer may try to scale or - // crop to this format if the frame delivered in OnFrameCaptured is not in - // this format. - // This method is virtual for testing purposes. - virtual void SetRequestedFormat(const media::VideoCaptureFormat& format); - // This method is virtual for testing purposes. virtual void OnFrameCaptured(const scoped_refptr<media::VideoFrame>& frame); diff --git a/content/renderer/pepper/pepper_video_destination_host.h b/content/renderer/pepper/pepper_video_destination_host.h index 1f48190..277b668 100644 --- a/content/renderer/pepper/pepper_video_destination_host.h +++ b/content/renderer/pepper/pepper_video_destination_host.h @@ -9,7 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" -#include "content/renderer/media/video_destination_handler.h" +#include "content/renderer/media/webrtc/video_destination_handler.h" #include "ppapi/c/pp_time.h" #include "ppapi/host/resource_host.h" |