OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/media_stream_impl.h" | 5 #include "content/renderer/media/media_stream_impl.h" |
6 | 6 |
7 #include "base/string_util.h" | 7 #include <utility> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/synchronization/waitable_event.h" |
| 12 #include "base/utf_string_conversions.h" |
8 #include "content/renderer/media/capture_video_decoder.h" | 13 #include "content/renderer/media/capture_video_decoder.h" |
| 14 #include "content/renderer/media/media_stream_dependency_factory.h" |
| 15 #include "content/renderer/media/media_stream_dispatcher.h" |
| 16 #include "content/renderer/media/peer_connection_handler.h" |
| 17 #include "content/renderer/media/rtc_video_decoder.h" |
9 #include "content/renderer/media/video_capture_impl_manager.h" | 18 #include "content/renderer/media/video_capture_impl_manager.h" |
10 #include "googleurl/src/gurl.h" | 19 #include "content/renderer/media/video_capture_module_impl.h" |
| 20 #include "content/renderer/media/webrtc_audio_device_impl.h" |
| 21 #include "content/renderer/p2p/ipc_network_manager.h" |
| 22 #include "content/renderer/p2p/ipc_socket_factory.h" |
| 23 #include "content/renderer/p2p/socket_dispatcher.h" |
| 24 #include "jingle/glue/thread_wrapper.h" |
11 #include "media/base/message_loop_factory.h" | 25 #include "media/base/message_loop_factory.h" |
12 #include "media/base/pipeline.h" | 26 #include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" |
| 27 #include "third_party/libjingle/source/talk/session/phone/dummydevicemanager.h" |
| 28 #include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h" |
| 29 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amDescriptor.h" |
| 30 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistr
y.h" |
| 31 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amSource.h" |
| 32 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" |
13 | 33 |
14 namespace { | 34 namespace { |
15 | 35 |
16 static const int kVideoCaptureWidth = 352; | 36 static const int kVideoCaptureWidth = 352; |
17 static const int kVideoCaptureHeight = 288; | 37 static const int kVideoCaptureHeight = 288; |
18 static const int kVideoCaptureFramePerSecond = 30; | 38 static const int kVideoCaptureFramePerSecond = 30; |
19 | 39 |
20 static const int kStartOpenSessionId = 1; | |
21 | |
22 // TODO(wjia): remove this string when full media stream code is checked in. | |
23 static const char kRawMediaScheme[] = "mediastream"; | |
24 | |
25 } // namespace | 40 } // namespace |
26 | 41 |
27 MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) | 42 int MediaStreamImpl::next_request_id_ = 0; |
28 : vc_manager_(vc_manager) { | 43 |
29 } | 44 MediaStreamImpl::MediaStreamImpl( |
30 | 45 MediaStreamDispatcher* media_stream_dispatcher, |
31 MediaStreamImpl::~MediaStreamImpl() {} | 46 content::P2PSocketDispatcher* p2p_socket_dispatcher, |
| 47 VideoCaptureImplManager* vc_manager, |
| 48 MediaStreamDependencyFactory* dependency_factory) |
| 49 : dependency_factory_(dependency_factory), |
| 50 media_stream_dispatcher_(media_stream_dispatcher), |
| 51 media_engine_(NULL), |
| 52 p2p_socket_dispatcher_(p2p_socket_dispatcher), |
| 53 network_manager_(NULL), |
| 54 vc_manager_(vc_manager), |
| 55 peer_connection_handler_(NULL), |
| 56 message_loop_proxy_(base::MessageLoopProxy::current()), |
| 57 signaling_thread_(NULL), |
| 58 worker_thread_(NULL), |
| 59 chrome_worker_thread_("Chrome_libJingle_WorkerThread"), |
| 60 vcm_created_(false) { |
| 61 } |
| 62 |
| 63 MediaStreamImpl::~MediaStreamImpl() { |
| 64 DCHECK(!peer_connection_handler_); |
| 65 if (dependency_factory_.get()) |
| 66 dependency_factory_->DeletePeerConnectionFactory(); |
| 67 if (network_manager_) { |
| 68 // The network manager needs to free its resources on the thread they were |
| 69 // created, which is the worked thread. |
| 70 if (chrome_worker_thread_.IsRunning()) { |
| 71 chrome_worker_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( |
| 72 &MediaStreamImpl::DeleteIpcNetworkManager, |
| 73 base::Unretained(this))); |
| 74 } else { |
| 75 NOTREACHED() << "Worker thread not running."; |
| 76 } |
| 77 } |
| 78 } |
| 79 |
| 80 WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( |
| 81 WebKit::WebPeerConnectionHandlerClient* client) { |
| 82 DCHECK(CalledOnValidThread()); |
| 83 if (peer_connection_handler_) { |
| 84 DVLOG(1) << "A PeerConnection already exists"; |
| 85 return NULL; |
| 86 } |
| 87 |
| 88 if (!media_engine_) { |
| 89 media_engine_ = dependency_factory_->CreateWebRtcMediaEngine(); |
| 90 } |
| 91 |
| 92 if (!signaling_thread_) { |
| 93 jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| 94 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| 95 signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
| 96 } |
| 97 |
| 98 if (!worker_thread_) { |
| 99 if (!chrome_worker_thread_.IsRunning()) { |
| 100 if (!chrome_worker_thread_.Start()) { |
| 101 LOG(ERROR) << "Could not start worker thread"; |
| 102 delete media_engine_; |
| 103 media_engine_ = NULL; |
| 104 signaling_thread_ = NULL; |
| 105 return NULL; |
| 106 } |
| 107 } |
| 108 base::WaitableEvent event(true, false); |
| 109 chrome_worker_thread_.message_loop()->PostTask( |
| 110 FROM_HERE, |
| 111 base::Bind(&MediaStreamImpl::InitializeWorkerThread, this, |
| 112 &worker_thread_, &event)); |
| 113 event.Wait(); |
| 114 DCHECK(worker_thread_); |
| 115 } |
| 116 |
| 117 if (!network_manager_) |
| 118 network_manager_ = new content::IpcNetworkManager(p2p_socket_dispatcher_); |
| 119 |
| 120 if (!socket_factory_.get()) { |
| 121 socket_factory_.reset( |
| 122 new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| 123 } |
| 124 |
| 125 if (!dependency_factory_->PeerConnectionFactoryCreated()) { |
| 126 if (!dependency_factory_->CreatePeerConnectionFactory(media_engine_, |
| 127 worker_thread_)) { |
| 128 LOG(ERROR) << "Could not initialize PeerConnection factory"; |
| 129 return NULL; |
| 130 } |
| 131 } |
| 132 |
| 133 peer_connection_handler_ = new PeerConnectionHandler( |
| 134 client, |
| 135 this, |
| 136 dependency_factory_.get(), |
| 137 signaling_thread_, |
| 138 p2p_socket_dispatcher_, |
| 139 network_manager_, |
| 140 socket_factory_.get()); |
| 141 |
| 142 return peer_connection_handler_; |
| 143 } |
| 144 |
| 145 void MediaStreamImpl::ClosePeerConnection() { |
| 146 DCHECK(CalledOnValidThread()); |
| 147 rtc_video_decoder_ = NULL; |
| 148 media_engine_->SetVideoCaptureModule(NULL); |
| 149 vcm_created_ = false; |
| 150 peer_connection_handler_ = NULL; |
| 151 } |
| 152 |
| 153 bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { |
| 154 DCHECK(CalledOnValidThread()); |
| 155 if (vcm_created_) |
| 156 return true; |
| 157 // Set the capture device. |
| 158 // TODO(grunell): Instead of using the first track, the selected track |
| 159 // should be used. |
| 160 int id = media_stream_dispatcher_->video_session_id(label, 0); |
| 161 if (id == media_stream::StreamDeviceInfo::kNoId) |
| 162 return false; |
| 163 webrtc::VideoCaptureModule* vcm = |
| 164 new VideoCaptureModuleImpl(id, vc_manager_.get()); |
| 165 vcm_created_ = true; |
| 166 media_engine_->SetVideoCaptureModule(vcm); |
| 167 return true; |
| 168 } |
| 169 |
| 170 void MediaStreamImpl::requestUserMedia( |
| 171 const WebKit::WebUserMediaRequest& user_media_request, |
| 172 const WebKit::WebVector<WebKit::WebMediaStreamSource>& |
| 173 media_stream_source_vector) { |
| 174 DCHECK(CalledOnValidThread()); |
| 175 DCHECK(!user_media_request.isNull()); |
| 176 |
| 177 int request_id = next_request_id_++; |
| 178 |
| 179 bool audio = user_media_request.audio(); |
| 180 media_stream::StreamOptions::VideoOption video_option = |
| 181 media_stream::StreamOptions::kNoCamera; |
| 182 if (user_media_request.video()) { |
| 183 // If no preference is set, use user facing camera. |
| 184 video_option = media_stream::StreamOptions::kFacingUser; |
| 185 if (user_media_request.cameraPreferenceUser() && |
| 186 user_media_request.cameraPreferenceEnvironment()) { |
| 187 video_option = media_stream::StreamOptions::kFacingBoth; |
| 188 } else if (user_media_request.cameraPreferenceEnvironment()) { |
| 189 video_option = media_stream::StreamOptions::kFacingEnvironment; |
| 190 } |
| 191 } |
| 192 |
| 193 std::string security_origin = UTF16ToUTF8( |
| 194 user_media_request.securityOrigin().toString()); |
| 195 |
| 196 DVLOG(1) << "MediaStreamImpl::generateStream(" << request_id << ", [ " |
| 197 << (audio ? "audio " : "") |
| 198 << ((user_media_request.cameraPreferenceUser()) ? |
| 199 "video_facing_user " : "") |
| 200 << ((user_media_request.cameraPreferenceEnvironment()) ? |
| 201 "video_facing_environment " : "") << "], " |
| 202 << security_origin << ")"; |
| 203 |
| 204 user_media_requests_.insert( |
| 205 std::pair<int, WebKit::WebUserMediaRequest>( |
| 206 request_id, user_media_request)); |
| 207 |
| 208 media_stream_dispatcher_->GenerateStream( |
| 209 request_id, |
| 210 this, |
| 211 media_stream::StreamOptions(audio, video_option), |
| 212 security_origin); |
| 213 } |
| 214 |
| 215 void MediaStreamImpl::cancelUserMediaRequest( |
| 216 const WebKit::WebUserMediaRequest& user_media_request) { |
| 217 // TODO(grunell): Implement. |
| 218 NOTIMPLEMENTED(); |
| 219 } |
32 | 220 |
33 scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( | 221 scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
34 const GURL& url, media::MessageLoopFactory* message_loop_factory) { | 222 const GURL& url, |
35 bool raw_media = (url.spec().find(kRawMediaScheme) == 0); | 223 media::MessageLoopFactory* message_loop_factory) { |
36 media::VideoDecoder* decoder = NULL; | 224 DCHECK(CalledOnValidThread()); |
37 if (raw_media) { | 225 WebKit::WebMediaStreamDescriptor descriptor( |
| 226 WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| 227 if (descriptor.isNull()) |
| 228 return NULL; // This is not a valid stream. |
| 229 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; |
| 230 descriptor.sources(source_vector); |
| 231 std::string label; |
| 232 for (size_t i = 0; i < source_vector.size(); ++i) { |
| 233 if (source_vector[i].type() == WebKit::WebMediaStreamSource::TypeVideo) { |
| 234 label = UTF16ToUTF8(source_vector[i].id()); |
| 235 break; |
| 236 } |
| 237 } |
| 238 if (label.empty()) |
| 239 return NULL; |
| 240 |
| 241 scoped_refptr<media::VideoDecoder> decoder; |
| 242 if (media_stream_dispatcher_->IsStream(label)) { |
| 243 // It's a local stream. |
| 244 int video_session_id = media_stream_dispatcher_->video_session_id(label, 0); |
38 media::VideoCapture::VideoCaptureCapability capability; | 245 media::VideoCapture::VideoCaptureCapability capability; |
39 capability.width = kVideoCaptureWidth; | 246 capability.width = kVideoCaptureWidth; |
40 capability.height = kVideoCaptureHeight; | 247 capability.height = kVideoCaptureHeight; |
41 capability.max_fps = kVideoCaptureFramePerSecond; | 248 capability.max_fps = kVideoCaptureFramePerSecond; |
42 capability.expected_capture_delay = 0; | 249 capability.expected_capture_delay = 0; |
43 capability.raw_type = media::VideoFrame::I420; | 250 capability.raw_type = media::VideoFrame::I420; |
44 capability.interlaced = false; | 251 capability.interlaced = false; |
45 | |
46 decoder = new CaptureVideoDecoder( | 252 decoder = new CaptureVideoDecoder( |
47 message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(), | 253 message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoderThread"), |
48 kStartOpenSessionId, vc_manager_.get(), capability); | 254 video_session_id, |
| 255 vc_manager_.get(), |
| 256 capability); |
| 257 } else { |
| 258 // It's a remote stream. |
| 259 size_t found = label.rfind("-remote"); |
| 260 if (found != std::string::npos) |
| 261 label = label.substr(0, found); |
| 262 if (rtc_video_decoder_.get()) { |
| 263 // The renderer is used by PeerConnection, release it first. |
| 264 if (peer_connection_handler_) |
| 265 peer_connection_handler_->SetVideoRenderer(label, NULL); |
| 266 } |
| 267 rtc_video_decoder_ = new RTCVideoDecoder( |
| 268 message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"), |
| 269 url.spec()); |
| 270 decoder = rtc_video_decoder_; |
| 271 if (peer_connection_handler_) |
| 272 peer_connection_handler_->SetVideoRenderer(label, rtc_video_decoder_); |
49 } | 273 } |
50 return decoder; | 274 return decoder; |
51 } | 275 } |
| 276 |
| 277 void MediaStreamImpl::OnStreamGenerated( |
| 278 int request_id, |
| 279 const std::string& label, |
| 280 const media_stream::StreamDeviceInfoArray& audio_array, |
| 281 const media_stream::StreamDeviceInfoArray& video_array) { |
| 282 DCHECK(CalledOnValidThread()); |
| 283 |
| 284 // We only support max one audio track and one video track. If the UI |
| 285 // for selecting device starts to allow several devices, we must implement |
| 286 // handling for this. |
| 287 DCHECK_LE(audio_array.size(), 1u); |
| 288 DCHECK_LE(video_array.size(), 1u); |
| 289 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( |
| 290 audio_array.size() + video_array.size()); |
| 291 |
| 292 WebKit::WebString track_label_audio(UTF8ToUTF16("AudioDevice")); |
| 293 WebKit::WebString track_label_video(UTF8ToUTF16("VideoCapture")); |
| 294 size_t track_num = source_vector.size(); |
| 295 while (track_num--) { |
| 296 if (track_num < audio_array.size()) { |
| 297 source_vector[track_num].initialize( |
| 298 UTF8ToUTF16(label), |
| 299 WebKit::WebMediaStreamSource::TypeAudio, |
| 300 track_label_audio); |
| 301 } else { |
| 302 source_vector[track_num].initialize( |
| 303 UTF8ToUTF16(label), |
| 304 WebKit::WebMediaStreamSource::TypeVideo, |
| 305 track_label_video); |
| 306 } |
| 307 } |
| 308 |
| 309 MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
| 310 if (it == user_media_requests_.end()) { |
| 311 DVLOG(1) << "Request ID not found"; |
| 312 return; |
| 313 } |
| 314 WebKit::WebUserMediaRequest user_media_request = it->second; |
| 315 user_media_requests_.erase(it); |
| 316 stream_labels_.push_back(label); |
| 317 |
| 318 user_media_request.requestSucceeded(source_vector); |
| 319 } |
| 320 |
| 321 void MediaStreamImpl::OnStreamGenerationFailed(int request_id) { |
| 322 DCHECK(CalledOnValidThread()); |
| 323 DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" |
| 324 << request_id << ")"; |
| 325 MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
| 326 if (it == user_media_requests_.end()) { |
| 327 DVLOG(1) << "Request ID not found"; |
| 328 return; |
| 329 } |
| 330 WebKit::WebUserMediaRequest user_media_request = it->second; |
| 331 user_media_requests_.erase(it); |
| 332 |
| 333 user_media_request.requestFailed(); |
| 334 } |
| 335 |
| 336 void MediaStreamImpl::OnVideoDeviceFailed(const std::string& label, |
| 337 int index) { |
| 338 DCHECK(CalledOnValidThread()); |
| 339 DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed(" |
| 340 << label << ", " << index << ")"; |
| 341 // TODO(grunell): Implement. Currently not supported in WebKit. |
| 342 NOTIMPLEMENTED(); |
| 343 } |
| 344 |
| 345 void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label, |
| 346 int index) { |
| 347 DCHECK(CalledOnValidThread()); |
| 348 DVLOG(1) << "MediaStreamImpl::OnAudioDeviceFailed(" |
| 349 << label << ", " << index << ")"; |
| 350 // TODO(grunell): Implement. Currently not supported in WebKit. |
| 351 NOTIMPLEMENTED(); |
| 352 } |
| 353 |
| 354 void MediaStreamImpl::InitializeWorkerThread(talk_base::Thread** thread, |
| 355 base::WaitableEvent* event) { |
| 356 jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| 357 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| 358 *thread = jingle_glue::JingleThreadWrapper::current(); |
| 359 event->Signal(); |
| 360 } |
| 361 |
| 362 void MediaStreamImpl::DeleteIpcNetworkManager() { |
| 363 DCHECK_EQ(MessageLoop::current(), chrome_worker_thread_.message_loop()); |
| 364 delete network_manager_; |
| 365 network_manager_ = NULL; |
| 366 } |
OLD | NEW |