OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/synchronization/waitable_event.h" |
| 10 #include "base/utf_string_conversions.h" |
8 #include "content/renderer/media/capture_video_decoder.h" | 11 #include "content/renderer/media/capture_video_decoder.h" |
| 12 #include "content/renderer/media/media_stream_dependency_factory.h" |
| 13 #include "content/renderer/media/media_stream_dispatcher.h" |
| 14 #include "content/renderer/media/peer_connection_handler.h" |
| 15 #include "content/renderer/media/rtc_video_decoder.h" |
9 #include "content/renderer/media/video_capture_impl_manager.h" | 16 #include "content/renderer/media/video_capture_impl_manager.h" |
10 #include "googleurl/src/gurl.h" | 17 #include "content/renderer/media/video_capture_module_impl.h" |
| 18 #include "content/renderer/media/webrtc_audio_device_impl.h" |
| 19 #include "content/renderer/p2p/ipc_network_manager.h" |
| 20 #include "content/renderer/p2p/ipc_socket_factory.h" |
| 21 #include "content/renderer/p2p/socket_dispatcher.h" |
| 22 #include "jingle/glue/thread_wrapper.h" |
11 #include "media/base/message_loop_factory.h" | 23 #include "media/base/message_loop_factory.h" |
12 #include "media/base/pipeline.h" | 24 #include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h" |
| 25 #include "third_party/libjingle/source/talk/session/phone/dummydevicemanager.h" |
| 26 #include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h" |
| 27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamDescrip
tor.h" |
| 28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistr
y.h" |
| 29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.
h" |
13 | 30 |
14 namespace { | 31 namespace { |
15 | 32 |
16 static const int kVideoCaptureWidth = 352; | 33 static const int kVideoCaptureWidth = 352; |
17 static const int kVideoCaptureHeight = 288; | 34 static const int kVideoCaptureHeight = 288; |
18 static const int kVideoCaptureFramePerSecond = 30; | 35 static const int kVideoCaptureFramePerSecond = 30; |
19 | 36 |
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 | 37 } // namespace |
26 | 38 |
27 MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager) | 39 MediaStreamImpl::MediaStreamImpl( |
28 : vc_manager_(vc_manager) { | 40 MediaStreamDispatcher* media_stream_dispatcher, |
| 41 content::P2PSocketDispatcher* p2p_socket_dispatcher, |
| 42 VideoCaptureImplManager* vc_manager, |
| 43 MediaStreamDependencyFactory* dependency_factory) |
| 44 : dependency_factory_(dependency_factory), |
| 45 media_stream_dispatcher_(media_stream_dispatcher), |
| 46 media_engine_(NULL), |
| 47 p2p_socket_dispatcher_(p2p_socket_dispatcher), |
| 48 vc_manager_(vc_manager), |
| 49 message_loop_proxy_(base::MessageLoopProxy::current()), |
| 50 signaling_thread_(NULL), |
| 51 worker_thread_(NULL), |
| 52 chrome_worker_thread_("Chrome_libJingle_WorkerThread"), |
| 53 request_id_counter_(0), |
| 54 vcm_created_(false) { |
29 } | 55 } |
30 | 56 |
31 MediaStreamImpl::~MediaStreamImpl() {} | 57 MediaStreamImpl::~MediaStreamImpl() { |
| 58 if (dependency_factory_.get()) |
| 59 dependency_factory_->DeletePeerConnectionFactory(); |
| 60 } |
| 61 |
| 62 WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler( |
| 63 WebKit::WebPeerConnectionHandlerClient* client) { |
| 64 if (peer_connection_handler_.get()) { |
| 65 VLOG(1) << "A PeerConnection already exists"; |
| 66 return NULL; |
| 67 } |
| 68 |
| 69 if (!media_engine_) { |
| 70 media_engine_ = dependency_factory_->CreateWebRtcMediaEngine(); |
| 71 } |
| 72 |
| 73 if (!signaling_thread_) { |
| 74 jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| 75 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| 76 signaling_thread_ = jingle_glue::JingleThreadWrapper::current(); |
| 77 } |
| 78 |
| 79 if (!worker_thread_) { |
| 80 if (!chrome_worker_thread_.IsRunning()) { |
| 81 if (!chrome_worker_thread_.Start()) { |
| 82 LOG(ERROR) << "Could not start worker thread"; |
| 83 delete media_engine_; |
| 84 media_engine_ = NULL; |
| 85 signaling_thread_ = NULL; |
| 86 return NULL; |
| 87 } |
| 88 } |
| 89 base::WaitableEvent event(true, false); |
| 90 chrome_worker_thread_.message_loop()->PostTask( |
| 91 FROM_HERE, |
| 92 base::Bind(&MediaStreamImpl::InitializeWorkerThread, this, |
| 93 &worker_thread_, &event)); |
| 94 event.Wait(); |
| 95 DCHECK(worker_thread_); |
| 96 } |
| 97 |
| 98 if (!dependency_factory_->PeerConnectionFactoryCreated()) { |
| 99 ipc_network_manager_.reset( |
| 100 new content::IpcNetworkManager(p2p_socket_dispatcher_)); |
| 101 ipc_socket_factory_.reset( |
| 102 new content::IpcPacketSocketFactory(p2p_socket_dispatcher_)); |
| 103 cricket::HttpPortAllocator* port_allocator = new cricket::HttpPortAllocator( |
| 104 ipc_network_manager_.get(), ipc_socket_factory_.get(), |
| 105 "PeerConnection"); |
| 106 // TODO(mallinath): The following flags were added to solve a crash in |
| 107 // HttpClient, we should probably remove them after that issue has been |
| 108 // investigated. |
| 109 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | |
| 110 cricket::PORTALLOCATOR_DISABLE_RELAY); |
| 111 |
| 112 // TODO(mallinath): PeerConnectionFactory constructor changed in latest |
| 113 // code and it no more accepts config string. Config string must be parsed |
| 114 // here and set in HttpPortAllocator. Now using standard google STUN server |
| 115 // address. |
| 116 std::vector<talk_base::SocketAddress> stun_hosts; |
| 117 stun_hosts.push_back(talk_base::SocketAddress("stun.l.google.com", 19302)); |
| 118 port_allocator->SetStunHosts(stun_hosts); |
| 119 |
| 120 if (!dependency_factory_->CreatePeerConnectionFactory(port_allocator, |
| 121 media_engine_, |
| 122 worker_thread_)) { |
| 123 LOG(ERROR) << "Could not initialize PeerConnection factory"; |
| 124 return NULL; |
| 125 } |
| 126 } |
| 127 |
| 128 peer_connection_handler_.reset(new PeerConnectionHandler( |
| 129 client, |
| 130 this, |
| 131 dependency_factory_.get(), |
| 132 signaling_thread_)); |
| 133 |
| 134 return peer_connection_handler_.get(); |
| 135 } |
| 136 |
| 137 void MediaStreamImpl::ClosePeerConnection() { |
| 138 rtc_video_decoder_ = NULL; |
| 139 media_engine_->SetVideoCaptureModule(NULL); |
| 140 vcm_created_ = false; |
| 141 peer_connection_handler_.reset(); |
| 142 } |
| 143 |
| 144 bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) { |
| 145 if (vcm_created_) |
| 146 return true; |
| 147 // Set the capture device. |
| 148 // TODO(grunell): Instead of using the first track, the selected track |
| 149 // should be used. |
| 150 int id = media_stream_dispatcher_->video_session_id(label, 0); |
| 151 if (id == media_stream::StreamDeviceInfo::kNoId) |
| 152 return false; |
| 153 webrtc::VideoCaptureModule* vcm = |
| 154 new VideoCaptureModuleImpl(id, vc_manager_.get()); |
| 155 vcm_created_ = true; |
| 156 media_engine_->SetVideoCaptureModule(vcm); |
| 157 return true; |
| 158 } |
| 159 |
| 160 void MediaStreamImpl::userMediaControllerDestroyed() { |
| 161 // TODO(grunell): Implement. |
| 162 NOTIMPLEMENTED(); |
| 163 } |
| 164 |
| 165 void MediaStreamImpl::requestUserMedia( |
| 166 const WebKit::WebUserMediaRequest& user_media_request) { |
| 167 DCHECK(!user_media_request.isNull()); |
| 168 |
| 169 int request_id = ++request_id_counter_; |
| 170 |
| 171 bool audio = user_media_request.audio(); |
| 172 media_stream::StreamOptions::VideoOption video_option = |
| 173 media_stream::StreamOptions::kNoCamera; |
| 174 if ((user_media_request.cameraPreferenceUser()) |
| 175 && (user_media_request.cameraPreferenceEnvironment())) { |
| 176 video_option = media_stream::StreamOptions::kFacingBoth; |
| 177 } else { |
| 178 if (user_media_request.cameraPreferenceEnvironment()) |
| 179 video_option = media_stream::StreamOptions::kFacingEnvironment; |
| 180 if (user_media_request.cameraPreferenceUser()) |
| 181 video_option = media_stream::StreamOptions::kFacingUser; |
| 182 } |
| 183 |
| 184 std::string security_origin = UTF16ToUTF8( |
| 185 user_media_request.securityOrigin().toString()); |
| 186 |
| 187 DVLOG(1) << "MediaStreamImpl::generateStream(" << request_id << ", [ " |
| 188 << (audio ? "audio " : "") |
| 189 << ((user_media_request.cameraPreferenceUser()) ? |
| 190 "video_facing_user " : "") |
| 191 << ((user_media_request.cameraPreferenceEnvironment()) ? |
| 192 "video_facing_environment " : "") << "], " |
| 193 << security_origin << ")"; |
| 194 |
| 195 user_media_requests_.insert( |
| 196 std::pair<int, WebKit::WebUserMediaRequest>( |
| 197 request_id, user_media_request)); |
| 198 |
| 199 media_stream_dispatcher_->GenerateStream( |
| 200 request_id, |
| 201 this, |
| 202 media_stream::StreamOptions(audio, video_option), |
| 203 security_origin); |
| 204 } |
| 205 |
| 206 void MediaStreamImpl::cancelUserMediaRequest( |
| 207 const WebKit::WebUserMediaRequest& user_media_request) { |
| 208 // TODO(grunell): Implement. |
| 209 NOTIMPLEMENTED(); |
| 210 } |
32 | 211 |
33 scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( | 212 scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder( |
34 const GURL& url, media::MessageLoopFactory* message_loop_factory) { | 213 const GURL& url, |
35 bool raw_media = (url.spec().find(kRawMediaScheme) == 0); | 214 media::MessageLoopFactory* message_loop_factory) { |
36 media::VideoDecoder* decoder = NULL; | 215 WebKit::WebMediaStreamDescriptor descriptor( |
37 if (raw_media) { | 216 WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| 217 if (descriptor.isNull()) |
| 218 return NULL; // This is not a valid stream. |
| 219 std::string label(UTF16ToUTF8(descriptor.label())); |
| 220 DCHECK(!label.empty()); |
| 221 |
| 222 scoped_refptr<media::VideoDecoder> decoder; |
| 223 if (media_stream_dispatcher_->IsStream(label)) { |
| 224 // It's a local stream. |
| 225 int video_session_id = media_stream_dispatcher_->video_session_id(label, 0); |
38 media::VideoCapture::VideoCaptureCapability capability; | 226 media::VideoCapture::VideoCaptureCapability capability; |
39 capability.width = kVideoCaptureWidth; | 227 capability.width = kVideoCaptureWidth; |
40 capability.height = kVideoCaptureHeight; | 228 capability.height = kVideoCaptureHeight; |
41 capability.max_fps = kVideoCaptureFramePerSecond; | 229 capability.max_fps = kVideoCaptureFramePerSecond; |
42 capability.expected_capture_delay = 0; | 230 capability.expected_capture_delay = 0; |
43 capability.raw_type = media::VideoFrame::I420; | 231 capability.raw_type = media::VideoFrame::I420; |
44 capability.interlaced = false; | 232 capability.interlaced = false; |
| 233 decoder = new CaptureVideoDecoder( |
| 234 message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoderThread"), |
| 235 video_session_id, |
| 236 vc_manager_.get(), |
| 237 capability); |
| 238 } else { |
| 239 // It's a remote stream. |
| 240 size_t found = label.rfind("-remote"); |
| 241 if (found != std::string::npos) |
| 242 label = label.substr(0, found); |
| 243 if (rtc_video_decoder_.get()) { |
| 244 // The renderer is used by PeerConnection, release it first. |
| 245 if (peer_connection_handler_.get()) |
| 246 peer_connection_handler_->SetVideoRenderer(label, NULL); |
45 | 247 |
46 decoder = new CaptureVideoDecoder( | 248 } |
47 message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(), | 249 rtc_video_decoder_ = new RTCVideoDecoder( |
48 kStartOpenSessionId, vc_manager_.get(), capability); | 250 message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"), |
| 251 url.spec()); |
| 252 decoder = rtc_video_decoder_; |
| 253 if (peer_connection_handler_.get()) |
| 254 peer_connection_handler_->SetVideoRenderer(label, rtc_video_decoder_); |
49 } | 255 } |
50 return decoder; | 256 return decoder; |
51 } | 257 } |
| 258 |
| 259 void MediaStreamImpl::OnStreamGenerated( |
| 260 int request_id, |
| 261 const std::string& label, |
| 262 const media_stream::StreamDeviceInfoArray& audio_array, |
| 263 const media_stream::StreamDeviceInfoArray& video_array) { |
| 264 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( |
| 265 audio_array.size() + video_array.size()); |
| 266 |
| 267 WebKit::WebString track_id(WebKit::WebString::fromUTF8("")); |
| 268 WebKit::WebString track_label_audio( |
| 269 WebKit::WebString::fromUTF8("AudioDevice")); |
| 270 WebKit::WebString track_label_video( |
| 271 WebKit::WebString::fromUTF8("VideoCapture")); |
| 272 |
| 273 size_t track_num = source_vector.size(); |
| 274 while (track_num--) { |
| 275 if (track_num < audio_array.size()) { |
| 276 source_vector[track_num].initialize( |
| 277 track_id, |
| 278 WebKit::WebMediaStreamSource::TypeAudio, |
| 279 track_label_audio); |
| 280 } else { |
| 281 source_vector[track_num].initialize( |
| 282 track_id, |
| 283 WebKit::WebMediaStreamSource::TypeVideo, |
| 284 track_label_video); |
| 285 } |
| 286 } |
| 287 |
| 288 MediaRequestMap::iterator it = user_media_requests_.find(request_id); |
| 289 WebKit::WebUserMediaRequest user_media_request = it->second; |
| 290 user_media_requests_.erase(it); |
| 291 |
| 292 user_media_request.requestSucceeded(UTF8ToUTF16(label), source_vector); |
| 293 } |
| 294 |
| 295 void MediaStreamImpl::OnStreamGenerationFailed(int request_id) { |
| 296 DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" |
| 297 << request_id << ")"; |
| 298 // TODO(grunell): Implement when available in WebKit. |
| 299 NOTIMPLEMENTED(); |
| 300 } |
| 301 |
| 302 void MediaStreamImpl::OnVideoDeviceFailed(const std::string& label, |
| 303 int index) { |
| 304 DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed(" |
| 305 << label << ", " << index << ")"; |
| 306 // TODO(grunell): Implement when available in WebKit. |
| 307 NOTIMPLEMENTED(); |
| 308 } |
| 309 |
| 310 void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label, |
| 311 int index) { |
| 312 DVLOG(1) << "MediaStreamImpl::OnAudioDeviceFailed(" |
| 313 << label << ", " << index << ")"; |
| 314 // TODO(grunell): Implement when available in WebKit. |
| 315 NOTIMPLEMENTED(); |
| 316 } |
| 317 |
| 318 void MediaStreamImpl::InitializeWorkerThread(talk_base::Thread** thread, |
| 319 base::WaitableEvent* event) { |
| 320 jingle_glue::JingleThreadWrapper::EnsureForCurrentThread(); |
| 321 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); |
| 322 *thread = jingle_glue::JingleThreadWrapper::current(); |
| 323 event->Signal(); |
| 324 } |
OLD | NEW |