Chromium Code Reviews| 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..aff1db1817e527f67996ed5c476e6953e05d04b7 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/WebMediaStreamDescriptor.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" |
| namespace { |
| @@ -17,24 +37,206 @@ 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), |
| + vc_manager_(vc_manager), |
| + port_allocator_(NULL), |
| + 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() { |
| + if (dependency_factory_.get()) |
| + dependency_factory_->DeletePeerConnectionFactory(); |
| +} |
| + |
| +WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( |
| + WebKit::WebPeerConnectionHandlerClient* client) { |
| + if (peer_connection_handler_) { |
| + VLOG(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 (!dependency_factory_->PeerConnectionFactoryCreated()) { |
| + ipc_network_manager_.reset( |
| + new content::IpcNetworkManager(p2p_socket_dispatcher_)); |
| + ipc_socket_factory_.reset( |
| + new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| + port_allocator_ = new cricket::HttpPortAllocator( |
| + ipc_network_manager_.get(), |
| + ipc_socket_factory_.get(), |
| + "PeerConnection"); |
| + // TODO(mallinath): The following flags were added to solve a crash in |
| + // HttpClient, we should probably remove them after that issue has been |
| + // investigated. |
| + port_allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | |
| + cricket::PORTALLOCATOR_DISABLE_RELAY); |
| + |
| + if (!dependency_factory_->CreatePeerConnectionFactory(port_allocator_, |
| + 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_, |
| + port_allocator_); |
| + |
| + return peer_connection_handler_; |
| +} |
| + |
| +void MediaStreamImpl::ClosePeerConnection() { |
| + rtc_video_decoder_ = NULL; |
| + media_engine_->SetVideoCaptureModule(NULL); |
| + vcm_created_ = false; |
| + peer_connection_handler_ = NULL; |
| +} |
| + |
| +bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { |
| + 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::pageDestroyed() { |
| + std::list<std::string>::iterator it; |
| + for (it = stream_labels_.begin() ; it != stream_labels_.end(); it++) |
| + media_stream_dispatcher_->StopStream(*it); |
| + stream_labels_.clear(); |
| +} |
| -MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) |
| - : vc_manager_(vc_manager) { |
| +void MediaStreamImpl::requestUserMedia( |
| + const WebKit::WebUserMediaRequest& user_media_request, |
| + const WebKit::WebVector<WebKit::WebMediaStreamSource>& |
| + media_stream_source_vector) { |
| + DCHECK(!user_media_request.isNull()); |
| + |
| + int request_id = next_request_id_++; |
|
tommi (sloooow) - chröme
2011/11/22 16:11:38
Does this need to use an atomic operation or are y
Henrik Grunell
2011/11/23 21:50:49
My understanding was that the calls from WebKit ar
|
| + |
| + 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) { |
| + 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 +244,99 @@ 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) { |
| + // 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(), static_cast<size_t>(1)); |
|
tommi (sloooow) - chröme
2011/11/22 16:11:38
1u?
(and below)
Henrik Grunell
2011/11/23 21:50:49
Better of course. Fixed.
|
| + DCHECK_LE(video_array.size(), static_cast<size_t>(1)); |
| + 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); |
| + 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) { |
| + DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" |
| + << request_id << ")"; |
| + MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
|
tommi (sloooow) - chröme
2011/11/22 16:11:38
check if the request_id was found before using the
Henrik Grunell
2011/11/23 21:50:49
Done.
|
| + 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) { |
| + DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed(" |
| + << label << ", " << index << ")"; |
| + // TODO(grunell): Implement. Currently not supported in WebKit. |
| + NOTIMPLEMENTED(); |
| +} |
| + |
| +void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label, |
| + int index) { |
| + 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(); |
| +} |