Index: content/renderer/media/media_stream_impl.cc |
diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc |
index 7573e78c92a41037accf09060e1e5548fc4f6ce4..06b6fc33152b5ed21085c45d2d0a134c94ebfbc4 100644 |
--- a/content/renderer/media/media_stream_impl.cc |
+++ b/content/renderer/media/media_stream_impl.cc |
@@ -4,12 +4,32 @@ |
#include "content/renderer/media/media_stream_impl.h" |
-#include "base/string_util.h" |
+#include <utility> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/utf_string_conversions.h" |
#include "content/renderer/media/capture_video_decoder.h" |
+#include "content/renderer/media/media_stream_dependency_factory.h" |
+#include "content/renderer/media/media_stream_dispatcher.h" |
+#include "content/renderer/media/peer_connection_handler.h" |
+#include "content/renderer/media/rtc_video_decoder.h" |
#include "content/renderer/media/video_capture_impl_manager.h" |
-#include "googleurl/src/gurl.h" |
+#include "content/renderer/media/video_capture_module_impl.h" |
+#include "content/renderer/media/webrtc_audio_device_impl.h" |
+#include "content/renderer/p2p/ipc_network_manager.h" |
+#include "content/renderer/p2p/ipc_socket_factory.h" |
+#include "content/renderer/p2p/socket_dispatcher.h" |
+#include "jingle/glue/thread_wrapper.h" |
#include "media/base/message_loop_factory.h" |
-#include "media/base/pipeline.h" |
+#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" |
+#include "third_party/libjingle/source/talk/session/phone/dummydevicemanager.h" |
+#include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" |
namespace { |
@@ -17,24 +37,211 @@ static const int kVideoCaptureWidth = 352; |
static const int kVideoCaptureHeight = 288; |
static const int kVideoCaptureFramePerSecond = 30; |
-static const int kStartOpenSessionId = 1; |
+} // namespace |
-// TODO(wjia): remove this string when full media stream code is checked in. |
-static const char kRawMediaScheme[] = "mediastream"; |
+int MediaStreamImpl::next_request_id_ = 0; |
-} // namespace |
+MediaStreamImpl::MediaStreamImpl( |
+ MediaStreamDispatcher* media_stream_dispatcher, |
+ content::P2PSocketDispatcher* p2p_socket_dispatcher, |
+ VideoCaptureImplManager* vc_manager, |
+ MediaStreamDependencyFactory* dependency_factory) |
+ : dependency_factory_(dependency_factory), |
+ media_stream_dispatcher_(media_stream_dispatcher), |
+ media_engine_(NULL), |
+ p2p_socket_dispatcher_(p2p_socket_dispatcher), |
+ network_manager_(NULL), |
+ vc_manager_(vc_manager), |
+ peer_connection_handler_(NULL), |
+ message_loop_proxy_(base::MessageLoopProxy::current()), |
+ signaling_thread_(NULL), |
+ worker_thread_(NULL), |
+ chrome_worker_thread_("Chrome_libJingle_WorkerThread"), |
+ vcm_created_(false) { |
+} |
+ |
+MediaStreamImpl::~MediaStreamImpl() { |
+ DCHECK(!peer_connection_handler_); |
+ if (dependency_factory_.get()) |
+ dependency_factory_->DeletePeerConnectionFactory(); |
+ if (network_manager_) { |
+ // The network manager needs to free its resources on the thread they were |
+ // created, which is the worked thread. |
+ if (chrome_worker_thread_.IsRunning()) { |
+ chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( |
+ &MediaStreamImpl::DeleteIpcNetworkManager, |
+ base::Unretained(this))); |
+ } else { |
+ LOG(ERROR) << "Leaking network manager."; |
tommi (sloooow) - chröme
2011/12/21 12:41:42
If we don't ever expect this to happen, we could h
Henrik Grunell
2012/01/05 09:23:49
It should not happen, changed to NOTREACHED.
|
+ } |
+ } |
+} |
+ |
+WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( |
+ WebKit::WebPeerConnectionHandlerClient* client) { |
+ DCHECK(CalledOnValidThread()); |
tommi (sloooow) - chröme
2011/12/21 12:41:42
thanks! :)
Henrik Grunell
2012/01/05 09:23:49
You're welcome... :-P
|
+ if (peer_connection_handler_) { |
+ DVLOG(1) << "A PeerConnection already exists"; |
+ return NULL; |
+ } |
+ |
+ if (!media_engine_) { |
+ media_engine_ = dependency_factory_->CreateWebRtcMediaEngine(); |
+ } |
+ |
+ if (!signaling_thread_) { |
+ jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
+ jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
+ signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
+ } |
+ |
+ if (!worker_thread_) { |
+ if (!chrome_worker_thread_.IsRunning()) { |
+ if (!chrome_worker_thread_.Start()) { |
+ LOG(ERROR) << "Could not start worker thread"; |
+ delete media_engine_; |
+ media_engine_ = NULL; |
+ signaling_thread_ = NULL; |
+ return NULL; |
+ } |
+ } |
+ base::WaitableEvent event(true, false); |
+ chrome_worker_thread_.message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&MediaStreamImpl::InitializeWorkerThread, this, |
+ &worker_thread_, &event)); |
+ event.Wait(); |
+ DCHECK(worker_thread_); |
+ } |
+ |
+ if (!network_manager_) |
+ network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_); |
+ |
+ if (!socket_factory_.get()) { |
+ socket_factory_.reset( |
+ new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
+ } |
+ |
+ if (!dependency_factory_->PeerConnectionFactoryCreated()) { |
+ if (!dependency_factory_->CreatePeerConnectionFactory(media_engine_, |
+ worker_thread_)) { |
+ LOG(ERROR) << "Could not initialize PeerConnection factory"; |
+ return NULL; |
+ } |
+ } |
+ |
+ peer_connection_handler_ = new PeerConnectionHandler( |
+ client, |
+ this, |
+ dependency_factory_.get(), |
+ signaling_thread_, |
+ p2p_socket_dispatcher_, |
+ network_manager_, |
+ socket_factory_.get()); |
+ |
+ return peer_connection_handler_; |
+} |
+ |
+void MediaStreamImpl::ClosePeerConnection() { |
+ DCHECK(CalledOnValidThread()); |
+ rtc_video_decoder_ = NULL; |
+ media_engine_->SetVideoCaptureModule(NULL); |
+ vcm_created_ = false; |
+ peer_connection_handler_ = NULL; |
+} |
+ |
+bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { |
+ DCHECK(CalledOnValidThread()); |
+ if (vcm_created_) |
+ return true; |
+ // Set the capture device. |
+ // TODO(grunell): Instead of using the first track, the selected track |
+ // should be used. |
+ int id = media_stream_dispatcher_->video_session_id(label, 0); |
+ if (id == media_stream::StreamDeviceInfo::kNoId) |
+ return false; |
+ webrtc::VideoCaptureModule* vcm = |
+ new VideoCaptureModuleImpl(id, vc_manager_.get()); |
+ vcm_created_ = true; |
+ media_engine_->SetVideoCaptureModule(vcm); |
+ return true; |
+} |
+ |
+void MediaStreamImpl::requestUserMedia( |
+ const WebKit::WebUserMediaRequest& user_media_request, |
+ const WebKit::WebVector<WebKit::WebMediaStreamSource>& |
+ media_stream_source_vector) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(!user_media_request.isNull()); |
+ |
+ int request_id = next_request_id_++; |
-MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) |
- : vc_manager_(vc_manager) { |
+ bool audio = user_media_request.audio(); |
+ media_stream::StreamOptions::VideoOption video_option = |
+ media_stream::StreamOptions::kNoCamera; |
+ if (user_media_request.video()) { |
+ // If no preference is set, use user facing camera. |
+ video_option = media_stream::StreamOptions::kFacingUser; |
+ if (user_media_request.cameraPreferenceUser() && |
+ user_media_request.cameraPreferenceEnvironment()) { |
+ video_option = media_stream::StreamOptions::kFacingBoth; |
+ } else if (user_media_request.cameraPreferenceEnvironment()) { |
+ video_option = media_stream::StreamOptions::kFacingEnvironment; |
+ } |
+ } |
+ |
+ std::string security_origin = UTF16ToUTF8( |
+ user_media_request.securityOrigin().toString()); |
+ |
+ DVLOG(1) << "MediaStreamImpl::generateStream(" << request_id << ", [ " |
+ << (audio ? "audio " : "") |
+ << ((user_media_request.cameraPreferenceUser()) ? |
+ "video_facing_user " : "") |
+ << ((user_media_request.cameraPreferenceEnvironment()) ? |
+ "video_facing_environment " : "") << "], " |
+ << security_origin << ")"; |
+ |
+ user_media_requests_.insert( |
+ std::pair<int, WebKit::WebUserMediaRequest>( |
+ request_id, user_media_request)); |
+ |
+ media_stream_dispatcher_->GenerateStream( |
+ request_id, |
+ this, |
+ media_stream::StreamOptions(audio, video_option), |
+ security_origin); |
} |
-MediaStreamImpl::~MediaStreamImpl() {} |
+void MediaStreamImpl::cancelUserMediaRequest( |
+ const WebKit::WebUserMediaRequest& user_media_request) { |
+ // TODO(grunell): Implement. |
+ NOTIMPLEMENTED(); |
+} |
scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
- const GURL& url, media::MessageLoopFactory* message_loop_factory) { |
- bool raw_media = (url.spec().find(kRawMediaScheme) == 0); |
- media::VideoDecoder* decoder = NULL; |
- if (raw_media) { |
+ const GURL& url, |
+ media::MessageLoopFactory* message_loop_factory) { |
+ DCHECK(CalledOnValidThread()); |
+ WebKit::WebMediaStreamDescriptor descriptor( |
+ WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
+ if (descriptor.isNull()) |
+ return NULL; // This is not a valid stream. |
+ WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; |
+ descriptor.sources(source_vector); |
+ std::string label; |
+ for (size_t i = 0; i < source_vector.size(); ++i) { |
+ if (source_vector[i].type() == WebKit::WebMediaStreamSource::TypeVideo) { |
+ label = UTF16ToUTF8(source_vector[i].id()); |
+ break; |
+ } |
+ } |
+ if (label.empty()) |
+ return NULL; |
+ |
+ scoped_refptr<media::VideoDecoder> decoder; |
+ if (media_stream_dispatcher_->IsStream(label)) { |
+ // It's a local stream. |
+ int video_session_id = media_stream_dispatcher_->video_session_id(label, 0); |
media::VideoCapture::VideoCaptureCapability capability; |
capability.width = kVideoCaptureWidth; |
capability.height = kVideoCaptureHeight; |
@@ -42,10 +249,117 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
capability.expected_capture_delay = 0; |
capability.raw_type = media::VideoFrame::I420; |
capability.interlaced = false; |
- |
decoder = new CaptureVideoDecoder( |
- message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(), |
- kStartOpenSessionId, vc_manager_.get(), capability); |
+ message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoderThread"), |
+ video_session_id, |
+ vc_manager_.get(), |
+ capability); |
+ } else { |
+ // It's a remote stream. |
+ size_t found = label.rfind("-remote"); |
+ if (found != std::string::npos) |
+ label = label.substr(0, found); |
+ if (rtc_video_decoder_.get()) { |
+ // The renderer is used by PeerConnection, release it first. |
+ if (peer_connection_handler_) |
+ peer_connection_handler_->SetVideoRenderer(label, NULL); |
+ } |
+ rtc_video_decoder_ = new RTCVideoDecoder( |
+ message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"), |
+ url.spec()); |
+ decoder = rtc_video_decoder_; |
+ if (peer_connection_handler_) |
+ peer_connection_handler_->SetVideoRenderer(label, rtc_video_decoder_); |
} |
return decoder; |
} |
+ |
+void MediaStreamImpl::OnStreamGenerated( |
+ int request_id, |
+ const std::string& label, |
+ const media_stream::StreamDeviceInfoArray& audio_array, |
+ const media_stream::StreamDeviceInfoArray& video_array) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // We only support max one audio track and one video track. If the UI |
+ // for selecting device starts to allow several devices, we must implement |
+ // handling for this. |
+ DCHECK_LE(audio_array.size(), 1u); |
+ DCHECK_LE(video_array.size(), 1u); |
+ WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( |
+ audio_array.size() + video_array.size()); |
+ |
+ WebKit::WebString track_label_audio(UTF8ToUTF16("AudioDevice")); |
+ WebKit::WebString track_label_video(UTF8ToUTF16("VideoCapture")); |
+ size_t track_num = source_vector.size(); |
+ while (track_num--) { |
+ if (track_num < audio_array.size()) { |
+ source_vector[track_num].initialize( |
+ UTF8ToUTF16(label), |
+ WebKit::WebMediaStreamSource::TypeAudio, |
+ track_label_audio); |
+ } else { |
+ source_vector[track_num].initialize( |
+ UTF8ToUTF16(label), |
+ WebKit::WebMediaStreamSource::TypeVideo, |
+ track_label_video); |
+ } |
+ } |
+ |
+ MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
+ if (it == user_media_requests_.end()) { |
+ DVLOG(1) << "Request ID not found"; |
+ return; |
+ } |
+ WebKit::WebUserMediaRequest user_media_request = it->second; |
+ user_media_requests_.erase(it); |
+ stream_labels_.push_back(label); |
+ |
+ user_media_request.requestSucceeded(source_vector); |
+} |
+ |
+void MediaStreamImpl::OnStreamGenerationFailed(int request_id) { |
+ DCHECK(CalledOnValidThread()); |
+ DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" |
+ << request_id << ")"; |
+ MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
+ if (it == user_media_requests_.end()) { |
+ DVLOG(1) << "Request ID not found"; |
+ return; |
+ } |
+ WebKit::WebUserMediaRequest user_media_request = it->second; |
+ user_media_requests_.erase(it); |
+ |
+ user_media_request.requestFailed(); |
+} |
+ |
+void MediaStreamImpl::OnVideoDeviceFailed(const std::string& label, |
+ int index) { |
+ DCHECK(CalledOnValidThread()); |
+ DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed(" |
+ << label << ", " << index << ")"; |
+ // TODO(grunell): Implement. Currently not supported in WebKit. |
+ NOTIMPLEMENTED(); |
+} |
+ |
+void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label, |
+ int index) { |
+ DCHECK(CalledOnValidThread()); |
+ DVLOG(1) << "MediaStreamImpl::OnAudioDeviceFailed(" |
+ << label << ", " << index << ")"; |
+ // TODO(grunell): Implement. Currently not supported in WebKit. |
+ NOTIMPLEMENTED(); |
+} |
+ |
+void MediaStreamImpl::InitializeWorkerThread(talk_base::Thread** thread, |
+ base::WaitableEvent* event) { |
+ jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
+ jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
+ *thread = jingle_glue::JingleThreadWrapper::current(); |
+ event->Signal(); |
+} |
+ |
+void MediaStreamImpl::DeleteIpcNetworkManager() { |
+ delete network_manager_; |
tommi (sloooow) - chröme
2011/12/21 12:41:42
nit:
DCHECK_EQ(MessageLoop::current(), chrome_wor
Henrik Grunell
2012/01/05 09:23:49
Done.
|
+ network_manager_ = NULL; |
+} |