| OLD | NEW |
| 1 // Copyright (c) 2012 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/peer_connection_handler.h" | 5 #include "content/renderer/media/peer_connection_handler.h" |
| 6 | 6 |
| 7 #include <stdlib.h> | 7 #include <utility> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/string_number_conversions.h" | 12 #include "base/string_number_conversions.h" |
| 13 #include "base/synchronization/waitable_event.h" | |
| 14 #include "base/threading/thread.h" | |
| 15 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 16 #include "content/renderer/media/media_stream_dependency_factory.h" | 14 #include "content/renderer/media/media_stream_dependency_factory.h" |
| 17 #include "content/renderer/media/media_stream_impl.h" | 15 #include "content/renderer/media/media_stream_impl.h" |
| 18 #include "content/renderer/p2p/ipc_network_manager.h" | |
| 19 #include "content/renderer/p2p/ipc_socket_factory.h" | |
| 20 #include "third_party/libjingle/source/talk/base/thread.h" | |
| 21 #include "third_party/libjingle/source/talk/p2p/base/portallocator.h" | |
| 22 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amDescriptor.h" | 16 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amDescriptor.h" |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amSource.h" | 17 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStre
amSource.h" |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConne
ctionHandlerClient.h" | 18 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPeerConne
ctionHandlerClient.h" |
| 25 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" | 19 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" |
| 26 | 20 |
| 27 PeerConnectionHandler::PeerConnectionHandler( | 21 PeerConnectionHandler::PeerConnectionHandler( |
| 28 WebKit::WebPeerConnectionHandlerClient* client, | 22 WebKit::WebPeerConnectionHandlerClient* client, |
| 29 MediaStreamImpl* msi, | 23 MediaStreamImpl* msi, |
| 30 MediaStreamDependencyFactory* dependency_factory, | 24 MediaStreamDependencyFactory* dependency_factory) |
| 31 talk_base::Thread* signaling_thread, | |
| 32 content::P2PSocketDispatcher* socket_dispatcher, | |
| 33 content::IpcNetworkManager* network_manager, | |
| 34 content::IpcPacketSocketFactory* socket_factory) | |
| 35 : client_(client), | 25 : client_(client), |
| 36 media_stream_impl_(msi), | 26 media_stream_impl_(msi), |
| 37 dependency_factory_(dependency_factory), | 27 dependency_factory_(dependency_factory), |
| 38 message_loop_proxy_(base::MessageLoopProxy::current()), | 28 message_loop_proxy_(base::MessageLoopProxy::current()) { |
| 39 signaling_thread_(signaling_thread), | |
| 40 socket_dispatcher_(socket_dispatcher), | |
| 41 network_manager_(network_manager), | |
| 42 socket_factory_(socket_factory), | |
| 43 call_state_(NOT_STARTED) { | |
| 44 } | 29 } |
| 45 | 30 |
| 46 PeerConnectionHandler::~PeerConnectionHandler() { | 31 PeerConnectionHandler::~PeerConnectionHandler() { |
| 47 if (native_peer_connection_.get()) { | |
| 48 native_peer_connection_->RegisterObserver(NULL); | |
| 49 native_peer_connection_->Close(); | |
| 50 } | |
| 51 } | 32 } |
| 52 | 33 |
| 53 bool PeerConnectionHandler::SetVideoRenderer( | 34 void PeerConnectionHandler::SetVideoRenderer( |
| 54 const std::string& stream_label, | 35 const std::string& stream_label, |
| 55 cricket::VideoRenderer* renderer) { | 36 webrtc::VideoRendererWrapperInterface* renderer) { |
| 56 return native_peer_connection_->SetVideoRenderer(stream_label, renderer); | 37 webrtc::MediaStreamInterface* stream = |
| 38 native_peer_connection_->remote_streams()->find(stream_label); |
| 39 webrtc::VideoTracks* video_tracks = stream->video_tracks(); |
| 40 // We assume there is only one enabled video track. |
| 41 for(size_t i = 0; i < video_tracks->count(); ++i) { |
| 42 webrtc::VideoTrackInterface* video_track = video_tracks->at(i); |
| 43 if (video_track->enabled()) { |
| 44 video_track->SetRenderer(renderer); |
| 45 return; |
| 46 } |
| 47 } |
| 48 DVLOG(1) << "No enabled video track."; |
| 57 } | 49 } |
| 58 | 50 |
| 59 void PeerConnectionHandler::initialize( | 51 void PeerConnectionHandler::initialize( |
| 60 const WebKit::WebString& server_configuration, | 52 const WebKit::WebString& server_configuration, |
| 61 const WebKit::WebSecurityOrigin& security_origin) { | 53 const WebKit::WebSecurityOrigin& security_origin) { |
| 62 // We support the following server configuration format: | 54 native_peer_connection_ = dependency_factory_->CreatePeerConnection( |
| 63 // "STUN <address>:<port>". We only support STUN at the moment. | 55 UTF16ToUTF8(server_configuration), |
| 64 // TODO(grunell): Support TURN. | 56 this); |
| 65 | |
| 66 // Strip "STUN ". | |
| 67 std::string strip_string = "STUN "; | |
| 68 std::string config = UTF16ToUTF8(server_configuration); | |
| 69 size_t pos = config.find(strip_string); | |
| 70 if (pos != 0) { | |
| 71 DVLOG(1) << "Invalid configuration string."; | |
| 72 return; | |
| 73 } | |
| 74 config = config.substr(strip_string.length()); | |
| 75 // Parse out port. | |
| 76 pos = config.find(':'); | |
| 77 if (pos == std::string::npos) { | |
| 78 DVLOG(1) << "Invalid configuration string."; | |
| 79 return; | |
| 80 } | |
| 81 int port = 0; | |
| 82 bool success = base::StringToInt(config.substr(pos+1), &port); | |
| 83 if (!success || (port == 0)) { | |
| 84 DVLOG(1) << "Invalid configuration string."; | |
| 85 return; | |
| 86 } | |
| 87 // Get address. | |
| 88 std::string address = config.substr(0, pos); | |
| 89 | |
| 90 webkit_glue::P2PTransport::Config p2p_config; | |
| 91 p2p_config.stun_server = address; | |
| 92 p2p_config.stun_server_port = port; | |
| 93 | |
| 94 port_allocator_.reset(dependency_factory_->CreatePortAllocator( | |
| 95 socket_dispatcher_, | |
| 96 network_manager_, | |
| 97 socket_factory_, | |
| 98 p2p_config)); | |
| 99 | |
| 100 native_peer_connection_.reset(dependency_factory_->CreatePeerConnection( | |
| 101 port_allocator_.get(), | |
| 102 signaling_thread_)); | |
| 103 native_peer_connection_->RegisterObserver(this); | |
| 104 } | 57 } |
| 105 | 58 |
| 106 void PeerConnectionHandler::produceInitialOffer( | 59 void PeerConnectionHandler::produceInitialOffer( |
| 107 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& | 60 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& |
| 108 pending_add_streams) { | 61 pending_add_streams) { |
| 109 // We currently don't support creating an initial offer without a stream. | 62 AddStreams(pending_add_streams); |
| 110 // Native PeerConnection will anyway create the initial offer when the first | 63 native_peer_connection_->CommitStreamChanges(); |
| 111 // (and only) stream is added, so it will be done when processPendingStreams | |
| 112 // is called if not here. | |
| 113 if (pending_add_streams.isEmpty()) { | |
| 114 DVLOG(1) << "Can't produce initial offer with no stream."; | |
| 115 return; | |
| 116 } | |
| 117 // TODO(grunell): Support several streams. | |
| 118 DCHECK_EQ(pending_add_streams.size(), 1u); | |
| 119 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; | |
| 120 pending_add_streams[0].sources(source_vector); | |
| 121 DCHECK_GT(source_vector.size(), 0u); | |
| 122 std::string label = UTF16ToUTF8(source_vector[0].id()); | |
| 123 AddStream(label); | |
| 124 } | 64 } |
| 125 | 65 |
| 126 void PeerConnectionHandler::handleInitialOffer(const WebKit::WebString& sdp) { | 66 void PeerConnectionHandler::handleInitialOffer(const WebKit::WebString& sdp) { |
| 127 native_peer_connection_->SignalingMessage(UTF16ToUTF8(sdp)); | 67 native_peer_connection_->ProcessSignalingMessage(UTF16ToUTF8(sdp)); |
| 128 } | 68 } |
| 129 | 69 |
| 130 void PeerConnectionHandler::processSDP(const WebKit::WebString& sdp) { | 70 void PeerConnectionHandler::processSDP(const WebKit::WebString& sdp) { |
| 131 native_peer_connection_->SignalingMessage(UTF16ToUTF8(sdp)); | 71 native_peer_connection_->ProcessSignalingMessage(UTF16ToUTF8(sdp)); |
| 132 } | 72 } |
| 133 | 73 |
| 134 void PeerConnectionHandler::processPendingStreams( | 74 void PeerConnectionHandler::processPendingStreams( |
| 135 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& | 75 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& |
| 136 pending_add_streams, | 76 pending_add_streams, |
| 137 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& | 77 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& |
| 138 pending_remove_streams) { | 78 pending_remove_streams) { |
| 139 // TODO(grunell): Support several streams. | 79 AddStreams(pending_add_streams); |
| 140 if (!pending_add_streams.isEmpty()) { | 80 RemoveStreams(pending_remove_streams); |
| 141 DCHECK_EQ(pending_add_streams.size(), 1u); | 81 native_peer_connection_->CommitStreamChanges(); |
| 142 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; | |
| 143 pending_add_streams[0].sources(source_vector); | |
| 144 DCHECK_GT(source_vector.size(), 0u); | |
| 145 std::string label = UTF16ToUTF8(source_vector[0].id()); | |
| 146 AddStream(label); | |
| 147 } | |
| 148 // Currently we ignore remove stream, no support in native PeerConnection. | |
| 149 } | 82 } |
| 150 | 83 |
| 151 void PeerConnectionHandler::sendDataStreamMessage( | 84 void PeerConnectionHandler::sendDataStreamMessage( |
| 152 const char* data, | 85 const char* data, |
| 153 size_t length) { | 86 size_t length) { |
| 154 // TODO(grunell): Implement. Not supported in native PeerConnection. | 87 // TODO(grunell): Implement. |
| 155 NOTIMPLEMENTED(); | 88 NOTIMPLEMENTED(); |
| 156 } | 89 } |
| 157 | 90 |
| 158 void PeerConnectionHandler::stop() { | 91 void PeerConnectionHandler::stop() { |
| 159 if (native_peer_connection_.get()) | 92 // TODO(ronghuawu): There's an issue with signaling messages being sent during |
| 160 native_peer_connection_->RegisterObserver(NULL); | 93 // close. We need to investigate further. Not calling Close() on native |
| 161 native_peer_connection_.reset(); | 94 // PeerConnection is OK for now. |
| 162 // The close function will delete us. | 95 native_peer_connection_ = NULL; |
| 163 media_stream_impl_->ClosePeerConnection(); | 96 media_stream_impl_->ClosePeerConnection(); |
| 164 } | 97 } |
| 165 | 98 |
| 99 void PeerConnectionHandler::OnError() { |
| 100 // TODO(grunell): Implement. |
| 101 NOTIMPLEMENTED(); |
| 102 } |
| 103 |
| 104 void PeerConnectionHandler::OnMessage(const std::string& msg) { |
| 105 // TODO(grunell): Implement. |
| 106 NOTIMPLEMENTED(); |
| 107 } |
| 108 |
| 166 void PeerConnectionHandler::OnSignalingMessage(const std::string& msg) { | 109 void PeerConnectionHandler::OnSignalingMessage(const std::string& msg) { |
| 167 if (!message_loop_proxy_->BelongsToCurrentThread()) { | 110 if (!message_loop_proxy_->BelongsToCurrentThread()) { |
| 168 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | 111 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
| 169 &PeerConnectionHandler::OnSignalingMessage, | 112 &PeerConnectionHandler::OnSignalingMessage, |
| 170 base::Unretained(this), | 113 base::Unretained(this), |
| 171 msg)); | 114 msg)); |
| 172 return; | 115 return; |
| 173 } | 116 } |
| 174 client_->didGenerateSDP(UTF8ToUTF16(msg)); | 117 client_->didGenerateSDP(UTF8ToUTF16(msg)); |
| 175 } | 118 } |
| 176 | 119 |
| 177 void PeerConnectionHandler::OnAddStream(const std::string& stream_id, | 120 void PeerConnectionHandler::OnStateChange(StateType state_changed) { |
| 178 bool video) { | 121 // TODO(grunell): Implement. |
| 179 if (!video) | 122 NOTIMPLEMENTED(); |
| 123 } |
| 124 |
| 125 void PeerConnectionHandler::OnAddStream(webrtc::MediaStreamInterface* stream) { |
| 126 if (!stream) |
| 180 return; | 127 return; |
| 181 | 128 |
| 182 if (!message_loop_proxy_->BelongsToCurrentThread()) { | 129 if (!message_loop_proxy_->BelongsToCurrentThread()) { |
| 183 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | 130 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
| 184 &PeerConnectionHandler::OnAddStreamCallback, | 131 &PeerConnectionHandler::OnAddStreamCallback, |
| 185 base::Unretained(this), | 132 base::Unretained(this), |
| 186 stream_id)); | 133 stream)); |
| 187 } else { | 134 } else { |
| 188 OnAddStreamCallback(stream_id); | 135 OnAddStreamCallback(stream); |
| 189 } | 136 } |
| 190 } | 137 } |
| 191 | 138 |
| 192 void PeerConnectionHandler::OnRemoveStream( | 139 void PeerConnectionHandler::OnRemoveStream( |
| 193 const std::string& stream_id, | 140 webrtc::MediaStreamInterface* stream) { |
| 194 bool video) { | 141 if (!stream) |
| 195 if (!video) | |
| 196 return; | 142 return; |
| 197 | 143 |
| 198 if (!message_loop_proxy_->BelongsToCurrentThread()) { | 144 if (!message_loop_proxy_->BelongsToCurrentThread()) { |
| 199 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( | 145 message_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
| 200 &PeerConnectionHandler::OnRemoveStreamCallback, | 146 &PeerConnectionHandler::OnRemoveStreamCallback, |
| 201 base::Unretained(this), | 147 base::Unretained(this), |
| 202 remote_label_)); | 148 stream)); |
| 203 } else { | 149 } else { |
| 204 OnRemoveStreamCallback(remote_label_); | 150 OnRemoveStreamCallback(stream); |
| 205 } | 151 } |
| 206 } | 152 } |
| 207 | 153 |
| 208 void PeerConnectionHandler::AddStream(const std::string label) { | 154 void PeerConnectionHandler::AddStreams( |
| 209 // TODO(grunell): Fix code in this function after a new native PeerConnection | 155 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams) { |
| 210 // version has been rolled out. | 156 for (size_t i = 0; i < streams.size(); ++i) { |
| 211 if (call_state_ == NOT_STARTED) { | 157 talk_base::scoped_refptr<webrtc::LocalMediaStreamInterface> stream = |
| 212 // TODO(grunell): Add audio and/or video depending on what's enabled | 158 dependency_factory_->CreateLocalMediaStream( |
| 213 // in the stream. | 159 UTF16ToUTF8(streams[i].label())); |
| 214 std::string audio_label = label; | 160 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector; |
| 215 audio_label.append("-audio"); | 161 streams[i].sources(source_vector); |
| 216 native_peer_connection_->AddStream(audio_label, false); // Audio | 162 |
| 217 native_peer_connection_->AddStream(label, true); // Video | 163 // Get and add all tracks. |
| 218 call_state_ = INITIATING; | 164 for (size_t j = 0; j < source_vector.size(); ++j) { |
| 165 webrtc::MediaStreamTrackInterface* track = |
| 166 media_stream_impl_->GetLocalMediaStreamTrack( |
| 167 UTF16ToUTF8(source_vector[j].id())); |
| 168 DCHECK(track); |
| 169 if (source_vector[j].type() == WebKit::WebMediaStreamSource::TypeVideo) { |
| 170 stream->AddTrack(static_cast<webrtc::VideoTrackInterface*>(track)); |
| 171 } else { |
| 172 stream->AddTrack(static_cast<webrtc::AudioTrackInterface*>(track)); |
| 173 } |
| 174 } |
| 175 |
| 176 native_peer_connection_->AddStream(stream); |
| 219 } | 177 } |
| 220 if (call_state_ == INITIATING || call_state_ == RECEIVING) { | 178 } |
| 221 local_label_ = label; | 179 |
| 222 if (media_stream_impl_->SetVideoCaptureModule(label)) | 180 void PeerConnectionHandler::RemoveStreams( |
| 223 native_peer_connection_->SetVideoCapture(""); | 181 const WebKit::WebVector<WebKit::WebMediaStreamDescriptor>& streams) { |
| 224 if (call_state_ == INITIATING) | 182 for (size_t i = 0; i < streams.size(); ++i) { |
| 225 native_peer_connection_->Connect(); | 183 webrtc::MediaStreamInterface* stream = |
| 226 else if (call_state_ == RECEIVING) | 184 native_peer_connection_->remote_streams()->find( |
| 227 call_state_ = SENDING_AND_RECEIVING; | 185 UTF16ToUTF8(streams[i].label())); |
| 228 } else { | 186 native_peer_connection_->RemoveStream( |
| 229 DLOG(ERROR) << "Multiple streams not supported"; | 187 static_cast<webrtc::LocalMediaStreamInterface*>(stream)); |
| 230 } | 188 } |
| 231 } | 189 } |
| 232 | 190 |
| 233 void PeerConnectionHandler::OnAddStreamCallback( | 191 void PeerConnectionHandler::OnAddStreamCallback( |
| 234 const std::string& stream_label) { | 192 webrtc::MediaStreamInterface* stream) { |
| 235 // TODO(grunell): Fix code in this function after a new native PeerConnection | 193 DCHECK(remote_streams_.find(stream) == remote_streams_.end()); |
| 236 // version has been rolled out. | 194 WebKit::WebMediaStreamDescriptor descriptor = |
| 237 if (call_state_ == NOT_STARTED) { | 195 CreateWebKitStreamDescriptor(stream); |
| 238 remote_label_ = stream_label; | 196 remote_streams_.insert( |
| 239 call_state_ = RECEIVING; | 197 std::pair<webrtc::MediaStreamInterface*, |
| 240 } else if (call_state_ == INITIATING) { | 198 WebKit::WebMediaStreamDescriptor>(stream, descriptor)); |
| 241 remote_label_ = local_label_; | |
| 242 remote_label_ += "-remote"; | |
| 243 call_state_ = SENDING_AND_RECEIVING; | |
| 244 } | |
| 245 | |
| 246 // TODO(grunell): Support several tracks. | |
| 247 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( | |
| 248 static_cast<size_t>(2)); | |
| 249 source_vector[0].initialize(WebKit::WebString::fromUTF8(remote_label_), | |
| 250 WebKit::WebMediaStreamSource::TypeVideo, | |
| 251 WebKit::WebString::fromUTF8("RemoteVideo")); | |
| 252 source_vector[1].initialize(WebKit::WebString::fromUTF8(remote_label_), | |
| 253 WebKit::WebMediaStreamSource::TypeAudio, | |
| 254 WebKit::WebString::fromUTF8("RemoteAudio")); | |
| 255 WebKit::WebMediaStreamDescriptor descriptor; | |
| 256 descriptor.initialize(UTF8ToUTF16(remote_label_), source_vector); | |
| 257 client_->didAddRemoteStream(descriptor); | 199 client_->didAddRemoteStream(descriptor); |
| 258 } | 200 } |
| 259 | 201 |
| 260 void PeerConnectionHandler::OnRemoveStreamCallback( | 202 void PeerConnectionHandler::OnRemoveStreamCallback( |
| 261 const std::string& stream_label) { | 203 webrtc::MediaStreamInterface* stream) { |
| 262 // TODO(grunell): Support several tracks. | 204 RemoteStreamMap::iterator it = remote_streams_.find(stream); |
| 263 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( | 205 if (it == remote_streams_.end()) { |
| 264 static_cast<size_t>(2)); | 206 NOTREACHED() << "Stream not found"; |
| 265 source_vector[0].initialize(WebKit::WebString::fromUTF8(stream_label), | 207 return; |
| 266 WebKit::WebMediaStreamSource::TypeVideo, | 208 } |
| 267 WebKit::WebString::fromUTF8("RemoteVideo")); | 209 WebKit::WebMediaStreamDescriptor descriptor = it->second; |
| 268 source_vector[1].initialize(WebKit::WebString::fromUTF8(stream_label), | 210 DCHECK(!descriptor.isNull()); |
| 269 WebKit::WebMediaStreamSource::TypeAudio, | 211 remote_streams_.erase(it); |
| 270 WebKit::WebString::fromUTF8("RemoteAudio")); | |
| 271 WebKit::WebMediaStreamDescriptor descriptor; | |
| 272 descriptor.initialize(UTF8ToUTF16(stream_label), source_vector); | |
| 273 client_->didRemoveRemoteStream(descriptor); | 212 client_->didRemoveRemoteStream(descriptor); |
| 274 } | 213 } |
| 214 |
| 215 WebKit::WebMediaStreamDescriptor |
| 216 PeerConnectionHandler::CreateWebKitStreamDescriptor( |
| 217 webrtc::MediaStreamInterface* stream) { |
| 218 webrtc::AudioTracks* audio_tracks = stream->audio_tracks(); |
| 219 webrtc::VideoTracks* video_tracks = stream->video_tracks(); |
| 220 WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector( |
| 221 audio_tracks->count() + video_tracks->count()); |
| 222 |
| 223 // Add audio tracks. |
| 224 size_t i = 0; |
| 225 for (; i < audio_tracks->count(); ++i) { |
| 226 webrtc::AudioTrackInterface* audio_track = audio_tracks->at(i); |
| 227 DCHECK(audio_track); |
| 228 source_vector[i].initialize( |
| 229 // TODO(grunell): Set id to something unique. |
| 230 UTF8ToUTF16(audio_track->label()), |
| 231 WebKit::WebMediaStreamSource::TypeAudio, |
| 232 UTF8ToUTF16(audio_track->label())); |
| 233 } |
| 234 |
| 235 // Add video tracks. |
| 236 for (i = 0; i < video_tracks->count(); ++i) { |
| 237 webrtc::VideoTrackInterface* video_track = video_tracks->at(i); |
| 238 DCHECK(video_track); |
| 239 source_vector[audio_tracks->count() + i].initialize( |
| 240 // TODO(grunell): Set id to something unique. |
| 241 UTF8ToUTF16(video_track->label()), |
| 242 WebKit::WebMediaStreamSource::TypeVideo, |
| 243 UTF8ToUTF16(video_track->label())); |
| 244 } |
| 245 |
| 246 WebKit::WebMediaStreamDescriptor descriptor; |
| 247 descriptor.initialize(UTF8ToUTF16(stream->label()), source_vector); |
| 248 |
| 249 return descriptor; |
| 250 } |
| OLD | NEW |