1 /* 2 * libjingle 3 * Copyright 2012, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/app/webrtc/mediastreamsignaling.h" 29 30 #include <vector> 31 32 #include "talk/app/webrtc/audiotrack.h" 33 #include "talk/app/webrtc/mediastreamproxy.h" 34 #include "talk/app/webrtc/mediaconstraintsinterface.h" 35 #include "talk/app/webrtc/mediastreamtrackproxy.h" 36 #include "talk/app/webrtc/remoteaudiosource.h" 37 #include "talk/app/webrtc/remotevideocapturer.h" 38 #include "talk/app/webrtc/sctputils.h" 39 #include "talk/app/webrtc/videosource.h" 40 #include "talk/app/webrtc/videotrack.h" 41 #include "talk/base/bytebuffer.h" 42 #include "talk/base/stringutils.h" 43 #include "talk/media/sctp/sctpdataengine.h" 44 45 static const char kDefaultStreamLabel[] = "default"; 46 static const char kDefaultAudioTrackLabel[] = "defaulta0"; 47 static const char kDefaultVideoTrackLabel[] = "defaultv0"; 48 49 namespace webrtc { 50 51 using talk_base::scoped_ptr; 52 using talk_base::scoped_refptr; 53 54 static bool ParseConstraints( 55 const MediaConstraintsInterface* constraints, 56 cricket::MediaSessionOptions* options, bool is_answer) { 57 bool value; 58 size_t mandatory_constraints_satisfied = 0; 59 60 if (FindConstraint(constraints, 61 MediaConstraintsInterface::kOfferToReceiveAudio, 62 &value, &mandatory_constraints_satisfied)) { 63 // |options-|has_audio| can only change from false to 64 // true, but never change from true to false. This is to make sure 65 // CreateOffer / CreateAnswer doesn't remove a media content 66 // description that has been created. 67 options->has_audio |= value; 68 } else { 69 // kOfferToReceiveAudio defaults to true according to spec. 70 options->has_audio = true; 71 } 72 73 if (FindConstraint(constraints, 74 MediaConstraintsInterface::kOfferToReceiveVideo, 75 &value, &mandatory_constraints_satisfied)) { 76 // |options->has_video| can only change from false to 77 // true, but never change from true to false. This is to make sure 78 // CreateOffer / CreateAnswer doesn't remove a media content 79 // description that has been created. 80 options->has_video |= value; 81 } else { 82 // kOfferToReceiveVideo defaults to false according to spec. But 83 // if it is an answer and video is offered, we should still accept video 84 // per default. 85 options->has_video |= is_answer; 86 } 87 88 if (FindConstraint(constraints, 89 MediaConstraintsInterface::kVoiceActivityDetection, 90 &value, &mandatory_constraints_satisfied)) { 91 options->vad_enabled = value; 92 } 93 94 if (FindConstraint(constraints, 95 MediaConstraintsInterface::kUseRtpMux, 96 &value, &mandatory_constraints_satisfied)) { 97 options->bundle_enabled = value; 98 } else { 99 // kUseRtpMux defaults to true according to spec. 100 options->bundle_enabled = true; 101 } 102 if (FindConstraint(constraints, 103 MediaConstraintsInterface::kIceRestart, 104 &value, &mandatory_constraints_satisfied)) { 105 options->transport_options.ice_restart = value; 106 } else { 107 // kIceRestart defaults to false according to spec. 108 options->transport_options.ice_restart = false; 109 } 110 111 if (!constraints) { 112 return true; 113 } 114 return mandatory_constraints_satisfied == constraints->GetMandatory().size(); 115 } 116 117 // Returns true if if at least one media content is present and 118 // |options.bundle_enabled| is true. 119 // Bundle will be enabled by default if at least one media content is present 120 // and the constraint kUseRtpMux has not disabled bundle. 121 static bool EvaluateNeedForBundle(const cricket::MediaSessionOptions& options) { 122 return options.bundle_enabled && 123 (options.has_audio || options.has_video || options.has_data()); 124 } 125 126 // Factory class for creating remote MediaStreams and MediaStreamTracks. 127 class RemoteMediaStreamFactory { 128 public: 129 explicit RemoteMediaStreamFactory(talk_base::Thread* signaling_thread, 130 cricket::ChannelManager* channel_manager) 131 : signaling_thread_(signaling_thread), 132 channel_manager_(channel_manager) { 133 } 134 135 talk_base::scoped_refptr<MediaStreamInterface> CreateMediaStream( 136 const std::string& stream_label) { 137 return MediaStreamProxy::Create( 138 signaling_thread_, MediaStream::Create(stream_label)); 139 } 140 141 AudioTrackInterface* AddAudioTrack(webrtc::MediaStreamInterface* stream, 142 const std::string& track_id) { 143 return AddTrack<AudioTrackInterface, AudioTrack, AudioTrackProxy>( 144 stream, track_id, RemoteAudioSource::Create().get()); 145 } 146 147 VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream, 148 const std::string& track_id) { 149 return AddTrack<VideoTrackInterface, VideoTrack, VideoTrackProxy>( 150 stream, track_id, VideoSource::Create(channel_manager_, 151 new RemoteVideoCapturer(), 152 NULL).get()); 153 } 154 155 private: 156 template <typename TI, typename T, typename TP, typename S> 157 TI* AddTrack(MediaStreamInterface* stream, const std::string& track_id, 158 S* source) { 159 talk_base::scoped_refptr<TI> track( 160 TP::Create(signaling_thread_, T::Create(track_id, source))); 161 track->set_state(webrtc::MediaStreamTrackInterface::kLive); 162 if (stream->AddTrack(track)) { 163 return track; 164 } 165 return NULL; 166 } 167 168 talk_base::Thread* signaling_thread_; 169 cricket::ChannelManager* channel_manager_; 170 }; 171 172 MediaStreamSignaling::MediaStreamSignaling( 173 talk_base::Thread* signaling_thread, 174 MediaStreamSignalingObserver* stream_observer, 175 cricket::ChannelManager* channel_manager) 176 : signaling_thread_(signaling_thread), 177 data_channel_factory_(NULL), 178 stream_observer_(stream_observer), 179 local_streams_(StreamCollection::Create()), 180 remote_streams_(StreamCollection::Create()), 181 remote_stream_factory_(new RemoteMediaStreamFactory(signaling_thread, 182 channel_manager)), 183 last_allocated_sctp_even_sid_(-2), 184 last_allocated_sctp_odd_sid_(-1) { 185 options_.has_video = false; 186 options_.has_audio = false; 187 } 188 189 MediaStreamSignaling::~MediaStreamSignaling() { 190 } 191 192 void MediaStreamSignaling::TearDown() { 193 OnAudioChannelClose(); 194 OnVideoChannelClose(); 195 OnDataChannelClose(); 196 } 197 198 bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const { 199 if (sid < 0 || sid > static_cast<int>(cricket::kMaxSctpSid)) 200 return false; 201 202 return FindDataChannelBySid(sid) < 0; 203 } 204 205 // Gets the first unused odd/even id based on the DTLS role. If |role| is 206 // SSL_CLIENT, the allocated id starts from 0 and takes even numbers; otherwise, 207 // the id starts from 1 and takes odd numbers. Returns false if no id can be 208 // allocated. 209 bool MediaStreamSignaling::AllocateSctpSid(talk_base::SSLRole role, int* sid) { 210 int& last_id = (role == talk_base::SSL_CLIENT) ? 211 last_allocated_sctp_even_sid_ : last_allocated_sctp_odd_sid_; 212 213 do { 214 last_id += 2; 215 } while (last_id <= static_cast<int>(cricket::kMaxSctpSid) && 216 !IsSctpSidAvailable(last_id)); 217 218 if (last_id > static_cast<int>(cricket::kMaxSctpSid)) { 219 return false; 220 } 221 222 *sid = last_id; 223 return true; 224 } 225 226 bool MediaStreamSignaling::HasDataChannels() const { 227 return !rtp_data_channels_.empty() || !sctp_data_channels_.empty(); 228 } 229 230 bool MediaStreamSignaling::AddDataChannel(DataChannel* data_channel) { 231 ASSERT(data_channel != NULL); 232 if (data_channel->data_channel_type() == cricket::DCT_RTP) { 233 if (rtp_data_channels_.find(data_channel->label()) != 234 rtp_data_channels_.end()) { 235 LOG(LS_ERROR) << "DataChannel with label " << data_channel->label() 236 << " already exists."; 237 return false; 238 } 239 rtp_data_channels_[data_channel->label()] = data_channel; 240 } else { 241 ASSERT(data_channel->data_channel_type() == cricket::DCT_SCTP); 242 sctp_data_channels_.push_back(data_channel); 243 } 244 return true; 245 } 246 247 bool MediaStreamSignaling::AddDataChannelFromOpenMessage( 248 const cricket::ReceiveDataParams& params, 249 const talk_base::Buffer& payload) { 250 if (!data_channel_factory_) { 251 LOG(LS_WARNING) << "Remote peer requested a DataChannel but DataChannels " 252 << "are not supported."; 253 return false; 254 } 255 256 std::string label; 257 InternalDataChannelInit config; 258 config.id = params.ssrc; 259 if (!ParseDataChannelOpenMessage(payload, &label, &config)) { 260 LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " 261 << params.ssrc; 262 return false; 263 } 264 config.open_handshake_role = InternalDataChannelInit::kAcker; 265 266 scoped_refptr<DataChannel> channel( 267 data_channel_factory_->CreateDataChannel(label, &config)); 268 if (!channel.get()) { 269 LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message."; 270 return false; 271 } 272 sctp_data_channels_.push_back(channel); 273 stream_observer_->OnAddDataChannel(channel); 274 return true; 275 } 276 277 void MediaStreamSignaling::RemoveSctpDataChannel(int sid) { 278 for (SctpDataChannels::iterator iter = sctp_data_channels_.begin(); 279 iter != sctp_data_channels_.end(); 280 ++iter) { 281 if ((*iter)->id() == sid) { 282 sctp_data_channels_.erase(iter); 283 284 if (talk_base::IsEven(sid) && sid <= last_allocated_sctp_even_sid_) { 285 last_allocated_sctp_even_sid_ = sid - 2; 286 } else if (talk_base::IsOdd(sid) && sid <= last_allocated_sctp_odd_sid_) { 287 last_allocated_sctp_odd_sid_ = sid - 2; 288 } 289 return; 290 } 291 } 292 } 293 294 bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) { 295 if (local_streams_->find(local_stream->label()) != NULL) { 296 LOG(LS_WARNING) << "MediaStream with label " << local_stream->label() 297 << "already exist."; 298 return false; 299 } 300 local_streams_->AddStream(local_stream); 301 302 // Find tracks that has already been configured in SDP. This can occur if a 303 // local session description that contains the MSID of these tracks is set 304 // before AddLocalStream is called. It can also occur if the local session 305 // description is not changed and RemoveLocalStream 306 // is called and later AddLocalStream is called again with the same stream. 307 AudioTrackVector audio_tracks = local_stream->GetAudioTracks(); 308 for (AudioTrackVector::const_iterator it = audio_tracks.begin(); 309 it != audio_tracks.end(); ++it) { 310 const TrackInfo* track_info = FindTrackInfo(local_audio_tracks_, 311 local_stream->label(), 312 (*it)->id()); 313 if (track_info) { 314 OnLocalTrackSeen(track_info->stream_label, track_info->track_id, 315 track_info->ssrc, cricket::MEDIA_TYPE_AUDIO); 316 } 317 } 318 319 VideoTrackVector video_tracks = local_stream->GetVideoTracks(); 320 for (VideoTrackVector::const_iterator it = video_tracks.begin(); 321 it != video_tracks.end(); ++it) { 322 const TrackInfo* track_info = FindTrackInfo(local_video_tracks_, 323 local_stream->label(), 324 (*it)->id()); 325 if (track_info) { 326 OnLocalTrackSeen(track_info->stream_label, track_info->track_id, 327 track_info->ssrc, cricket::MEDIA_TYPE_VIDEO); 328 } 329 } 330 return true; 331 } 332 333 void MediaStreamSignaling::RemoveLocalStream( 334 MediaStreamInterface* local_stream) { 335 AudioTrackVector audio_tracks = local_stream->GetAudioTracks(); 336 for (AudioTrackVector::const_iterator it = audio_tracks.begin(); 337 it != audio_tracks.end(); ++it) { 338 const TrackInfo* track_info = FindTrackInfo(local_audio_tracks_, 339 local_stream->label(), 340 (*it)->id()); 341 if (track_info) { 342 stream_observer_->OnRemoveLocalAudioTrack(local_stream, *it, 343 track_info->ssrc); 344 } 345 } 346 VideoTrackVector video_tracks = local_stream->GetVideoTracks(); 347 for (VideoTrackVector::const_iterator it = video_tracks.begin(); 348 it != video_tracks.end(); ++it) { 349 const TrackInfo* track_info = FindTrackInfo(local_video_tracks_, 350 local_stream->label(), 351 (*it)->id()); 352 if (track_info) { 353 stream_observer_->OnRemoveLocalVideoTrack(local_stream, *it); 354 } 355 } 356 357 local_streams_->RemoveStream(local_stream); 358 stream_observer_->OnRemoveLocalStream(local_stream); 359 } 360 361 bool MediaStreamSignaling::GetOptionsForOffer( 362 const MediaConstraintsInterface* constraints, 363 cricket::MediaSessionOptions* options) { 364 UpdateSessionOptions(); 365 if (!ParseConstraints(constraints, &options_, false)) { 366 return false; 367 } 368 options_.bundle_enabled = EvaluateNeedForBundle(options_); 369 *options = options_; 370 return true; 371 } 372 373 bool MediaStreamSignaling::GetOptionsForAnswer( 374 const MediaConstraintsInterface* constraints, 375 cricket::MediaSessionOptions* options) { 376 UpdateSessionOptions(); 377 378 // Copy the |options_| to not let the flag MediaSessionOptions::has_audio and 379 // MediaSessionOptions::has_video affect subsequent offers. 380 cricket::MediaSessionOptions current_options = options_; 381 if (!ParseConstraints(constraints, ¤t_options, true)) { 382 return false; 383 } 384 current_options.bundle_enabled = EvaluateNeedForBundle(current_options); 385 *options = current_options; 386 return true; 387 } 388 389 // Updates or creates remote MediaStream objects given a 390 // remote SessionDesription. 391 // If the remote SessionDesription contains new remote MediaStreams 392 // the observer OnAddStream method is called. If a remote MediaStream is missing 393 // from the remote SessionDescription OnRemoveStream is called. 394 void MediaStreamSignaling::OnRemoteDescriptionChanged( 395 const SessionDescriptionInterface* desc) { 396 const cricket::SessionDescription* remote_desc = desc->description(); 397 talk_base::scoped_refptr<StreamCollection> new_streams( 398 StreamCollection::Create()); 399 400 // Find all audio rtp streams and create corresponding remote AudioTracks 401 // and MediaStreams. 402 const cricket::ContentInfo* audio_content = GetFirstAudioContent(remote_desc); 403 if (audio_content) { 404 const cricket::AudioContentDescription* desc = 405 static_cast<const cricket::AudioContentDescription*>( 406 audio_content->description); 407 UpdateRemoteStreamsList(desc->streams(), desc->type(), new_streams); 408 remote_info_.default_audio_track_needed = 409 desc->direction() == cricket::MD_SENDRECV && desc->streams().empty(); 410 } 411 412 // Find all video rtp streams and create corresponding remote VideoTracks 413 // and MediaStreams. 414 const cricket::ContentInfo* video_content = GetFirstVideoContent(remote_desc); 415 if (video_content) { 416 const cricket::VideoContentDescription* desc = 417 static_cast<const cricket::VideoContentDescription*>( 418 video_content->description); 419 UpdateRemoteStreamsList(desc->streams(), desc->type(), new_streams); 420 remote_info_.default_video_track_needed = 421 desc->direction() == cricket::MD_SENDRECV && desc->streams().empty(); 422 } 423 424 // Update the DataChannels with the information from the remote peer. 425 const cricket::ContentInfo* data_content = GetFirstDataContent(remote_desc); 426 if (data_content) { 427 const cricket::DataContentDescription* data_desc = 428 static_cast<const cricket::DataContentDescription*>( 429 data_content->description); 430 if (talk_base::starts_with( 431 data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) { 432 UpdateRemoteRtpDataChannels(data_desc->streams()); 433 } 434 } 435 436 // Iterate new_streams and notify the observer about new MediaStreams. 437 for (size_t i = 0; i < new_streams->count(); ++i) { 438 MediaStreamInterface* new_stream = new_streams->at(i); 439 stream_observer_->OnAddRemoteStream(new_stream); 440 } 441 442 // Find removed MediaStreams. 443 if (remote_info_.IsDefaultMediaStreamNeeded() && 444 remote_streams_->find(kDefaultStreamLabel) != NULL) { 445 // The default media stream already exists. No need to do anything. 446 } else { 447 UpdateEndedRemoteMediaStreams(); 448 remote_info_.msid_supported |= remote_streams_->count() > 0; 449 } 450 MaybeCreateDefaultStream(); 451 } 452 453 void MediaStreamSignaling::OnLocalDescriptionChanged( 454 const SessionDescriptionInterface* desc) { 455 const cricket::ContentInfo* audio_content = 456 GetFirstAudioContent(desc->description()); 457 if (audio_content) { 458 if (audio_content->rejected) { 459 RejectRemoteTracks(cricket::MEDIA_TYPE_AUDIO); 460 } 461 const cricket::AudioContentDescription* audio_desc = 462 static_cast<const cricket::AudioContentDescription*>( 463 audio_content->description); 464 UpdateLocalTracks(audio_desc->streams(), audio_desc->type()); 465 } 466 467 const cricket::ContentInfo* video_content = 468 GetFirstVideoContent(desc->description()); 469 if (video_content) { 470 if (video_content->rejected) { 471 RejectRemoteTracks(cricket::MEDIA_TYPE_VIDEO); 472 } 473 const cricket::VideoContentDescription* video_desc = 474 static_cast<const cricket::VideoContentDescription*>( 475 video_content->description); 476 UpdateLocalTracks(video_desc->streams(), video_desc->type()); 477 } 478 479 const cricket::ContentInfo* data_content = 480 GetFirstDataContent(desc->description()); 481 if (data_content) { 482 const cricket::DataContentDescription* data_desc = 483 static_cast<const cricket::DataContentDescription*>( 484 data_content->description); 485 if (talk_base::starts_with( 486 data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) { 487 UpdateLocalRtpDataChannels(data_desc->streams()); 488 } 489 } 490 } 491 492 void MediaStreamSignaling::OnAudioChannelClose() { 493 RejectRemoteTracks(cricket::MEDIA_TYPE_AUDIO); 494 } 495 496 void MediaStreamSignaling::OnVideoChannelClose() { 497 RejectRemoteTracks(cricket::MEDIA_TYPE_VIDEO); 498 } 499 500 void MediaStreamSignaling::OnDataChannelClose() { 501 // Use a temporary copy of the RTP/SCTP DataChannel list because the 502 // DataChannel may callback to us and try to modify the list. 503 RtpDataChannels temp_rtp_dcs; 504 temp_rtp_dcs.swap(rtp_data_channels_); 505 RtpDataChannels::iterator it1 = temp_rtp_dcs.begin(); 506 for (; it1 != temp_rtp_dcs.end(); ++it1) { 507 it1->second->OnDataEngineClose(); 508 } 509 510 SctpDataChannels temp_sctp_dcs; 511 temp_sctp_dcs.swap(sctp_data_channels_); 512 SctpDataChannels::iterator it2 = temp_sctp_dcs.begin(); 513 for (; it2 != temp_sctp_dcs.end(); ++it2) { 514 (*it2)->OnDataEngineClose(); 515 } 516 } 517 518 void MediaStreamSignaling::UpdateSessionOptions() { 519 options_.streams.clear(); 520 if (local_streams_ != NULL) { 521 for (size_t i = 0; i < local_streams_->count(); ++i) { 522 MediaStreamInterface* stream = local_streams_->at(i); 523 524 AudioTrackVector audio_tracks(stream->GetAudioTracks()); 525 if (!audio_tracks.empty()) { 526 options_.has_audio = true; 527 } 528 529 // For each audio track in the stream, add it to the MediaSessionOptions. 530 for (size_t j = 0; j < audio_tracks.size(); ++j) { 531 scoped_refptr<MediaStreamTrackInterface> track(audio_tracks[j]); 532 options_.AddStream(cricket::MEDIA_TYPE_AUDIO, track->id(), 533 stream->label()); 534 } 535 536 VideoTrackVector video_tracks(stream->GetVideoTracks()); 537 if (!video_tracks.empty()) { 538 options_.has_video = true; 539 } 540 // For each video track in the stream, add it to the MediaSessionOptions. 541 for (size_t j = 0; j < video_tracks.size(); ++j) { 542 scoped_refptr<MediaStreamTrackInterface> track(video_tracks[j]); 543 options_.AddStream(cricket::MEDIA_TYPE_VIDEO, track->id(), 544 stream->label()); 545 } 546 } 547 } 548 549 // Check for data channels. 550 RtpDataChannels::const_iterator data_channel_it = rtp_data_channels_.begin(); 551 for (; data_channel_it != rtp_data_channels_.end(); ++data_channel_it) { 552 const DataChannel* channel = data_channel_it->second; 553 if (channel->state() == DataChannel::kConnecting || 554 channel->state() == DataChannel::kOpen) { 555 // |streamid| and |sync_label| are both set to the DataChannel label 556 // here so they can be signaled the same way as MediaStreams and Tracks. 557 // For MediaStreams, the sync_label is the MediaStream label and the 558 // track label is the same as |streamid|. 559 const std::string& streamid = channel->label(); 560 const std::string& sync_label = channel->label(); 561 options_.AddStream(cricket::MEDIA_TYPE_DATA, streamid, sync_label); 562 } 563 } 564 } 565 566 void MediaStreamSignaling::UpdateRemoteStreamsList( 567 const cricket::StreamParamsVec& streams, 568 cricket::MediaType media_type, 569 StreamCollection* new_streams) { 570 TrackInfos* current_tracks = GetRemoteTracks(media_type); 571 572 // Find removed tracks. Ie tracks where the track id or ssrc don't match the 573 // new StreamParam. 574 TrackInfos::iterator track_it = current_tracks->begin(); 575 while (track_it != current_tracks->end()) { 576 const TrackInfo& info = *track_it; 577 cricket::StreamParams params; 578 if (!cricket::GetStreamBySsrc(streams, info.ssrc, ¶ms) || 579 params.id != info.track_id) { 580 OnRemoteTrackRemoved(info.stream_label, info.track_id, media_type); 581 track_it = current_tracks->erase(track_it); 582 } else { 583 ++track_it; 584 } 585 } 586 587 // Find new and active tracks. 588 for (cricket::StreamParamsVec::const_iterator it = streams.begin(); 589 it != streams.end(); ++it) { 590 // The sync_label is the MediaStream label and the |stream.id| is the 591 // track id. 592 const std::string& stream_label = it->sync_label; 593 const std::string& track_id = it->id; 594 uint32 ssrc = it->first_ssrc(); 595 596 talk_base::scoped_refptr<MediaStreamInterface> stream = 597 remote_streams_->find(stream_label); 598 if (!stream) { 599 // This is a new MediaStream. Create a new remote MediaStream. 600 stream = remote_stream_factory_->CreateMediaStream(stream_label); 601 remote_streams_->AddStream(stream); 602 new_streams->AddStream(stream); 603 } 604 605 const TrackInfo* track_info = FindTrackInfo(*current_tracks, stream_label, 606 track_id); 607 if (!track_info) { 608 current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc)); 609 OnRemoteTrackSeen(stream_label, track_id, it->first_ssrc(), media_type); 610 } 611 } 612 } 613 614 void MediaStreamSignaling::OnRemoteTrackSeen(const std::string& stream_label, 615 const std::string& track_id, 616 uint32 ssrc, 617 cricket::MediaType media_type) { 618 MediaStreamInterface* stream = remote_streams_->find(stream_label); 619 620 if (media_type == cricket::MEDIA_TYPE_AUDIO) { 621 AudioTrackInterface* audio_track = 622 remote_stream_factory_->AddAudioTrack(stream, track_id); 623 stream_observer_->OnAddRemoteAudioTrack(stream, audio_track, ssrc); 624 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { 625 VideoTrackInterface* video_track = 626 remote_stream_factory_->AddVideoTrack(stream, track_id); 627 stream_observer_->OnAddRemoteVideoTrack(stream, video_track, ssrc); 628 } else { 629 ASSERT(false && "Invalid media type"); 630 } 631 } 632 633 void MediaStreamSignaling::OnRemoteTrackRemoved( 634 const std::string& stream_label, 635 const std::string& track_id, 636 cricket::MediaType media_type) { 637 MediaStreamInterface* stream = remote_streams_->find(stream_label); 638 639 if (media_type == cricket::MEDIA_TYPE_AUDIO) { 640 talk_base::scoped_refptr<AudioTrackInterface> audio_track = 641 stream->FindAudioTrack(track_id); 642 if (audio_track) { 643 audio_track->set_state(webrtc::MediaStreamTrackInterface::kEnded); 644 stream->RemoveTrack(audio_track); 645 stream_observer_->OnRemoveRemoteAudioTrack(stream, audio_track); 646 } 647 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { 648 talk_base::scoped_refptr<VideoTrackInterface> video_track = 649 stream->FindVideoTrack(track_id); 650 if (video_track) { 651 video_track->set_state(webrtc::MediaStreamTrackInterface::kEnded); 652 stream->RemoveTrack(video_track); 653 stream_observer_->OnRemoveRemoteVideoTrack(stream, video_track); 654 } 655 } else { 656 ASSERT(false && "Invalid media type"); 657 } 658 } 659 660 void MediaStreamSignaling::RejectRemoteTracks(cricket::MediaType media_type) { 661 TrackInfos* current_tracks = GetRemoteTracks(media_type); 662 for (TrackInfos::iterator track_it = current_tracks->begin(); 663 track_it != current_tracks->end(); ++track_it) { 664 const TrackInfo& info = *track_it; 665 MediaStreamInterface* stream = remote_streams_->find(info.stream_label); 666 if (media_type == cricket::MEDIA_TYPE_AUDIO) { 667 AudioTrackInterface* track = stream->FindAudioTrack(info.track_id); 668 // There's no guarantee the track is still available, e.g. the track may 669 // have been removed from the stream by javascript. 670 if (track) { 671 track->set_state(webrtc::MediaStreamTrackInterface::kEnded); 672 } 673 } 674 if (media_type == cricket::MEDIA_TYPE_VIDEO) { 675 VideoTrackInterface* track = stream->FindVideoTrack(info.track_id); 676 // There's no guarantee the track is still available, e.g. the track may 677 // have been removed from the stream by javascript. 678 if (track) { 679 track->set_state(webrtc::MediaStreamTrackInterface::kEnded); 680 } 681 } 682 } 683 } 684 685 void MediaStreamSignaling::UpdateEndedRemoteMediaStreams() { 686 std::vector<scoped_refptr<MediaStreamInterface> > streams_to_remove; 687 for (size_t i = 0; i < remote_streams_->count(); ++i) { 688 MediaStreamInterface*stream = remote_streams_->at(i); 689 if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { 690 streams_to_remove.push_back(stream); 691 } 692 } 693 694 std::vector<scoped_refptr<MediaStreamInterface> >::const_iterator it; 695 for (it = streams_to_remove.begin(); it != streams_to_remove.end(); ++it) { 696 remote_streams_->RemoveStream(*it); 697 stream_observer_->OnRemoveRemoteStream(*it); 698 } 699 } 700 701 void MediaStreamSignaling::MaybeCreateDefaultStream() { 702 if (!remote_info_.IsDefaultMediaStreamNeeded()) 703 return; 704 705 bool default_created = false; 706 707 scoped_refptr<MediaStreamInterface> default_remote_stream = 708 remote_streams_->find(kDefaultStreamLabel); 709 if (default_remote_stream == NULL) { 710 default_created = true; 711 default_remote_stream = 712 remote_stream_factory_->CreateMediaStream(kDefaultStreamLabel); 713 remote_streams_->AddStream(default_remote_stream); 714 } 715 if (remote_info_.default_audio_track_needed && 716 default_remote_stream->GetAudioTracks().size() == 0) { 717 remote_audio_tracks_.push_back(TrackInfo(kDefaultStreamLabel, 718 kDefaultAudioTrackLabel, 0)); 719 720 OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultAudioTrackLabel, 0, 721 cricket::MEDIA_TYPE_AUDIO); 722 } 723 if (remote_info_.default_video_track_needed && 724 default_remote_stream->GetVideoTracks().size() == 0) { 725 remote_video_tracks_.push_back(TrackInfo(kDefaultStreamLabel, 726 kDefaultVideoTrackLabel, 0)); 727 OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultVideoTrackLabel, 0, 728 cricket::MEDIA_TYPE_VIDEO); 729 } 730 if (default_created) { 731 stream_observer_->OnAddRemoteStream(default_remote_stream); 732 } 733 } 734 735 MediaStreamSignaling::TrackInfos* MediaStreamSignaling::GetRemoteTracks( 736 cricket::MediaType type) { 737 if (type == cricket::MEDIA_TYPE_AUDIO) 738 return &remote_audio_tracks_; 739 else if (type == cricket::MEDIA_TYPE_VIDEO) 740 return &remote_video_tracks_; 741 ASSERT(false && "Unknown MediaType"); 742 return NULL; 743 } 744 745 MediaStreamSignaling::TrackInfos* MediaStreamSignaling::GetLocalTracks( 746 cricket::MediaType media_type) { 747 ASSERT(media_type == cricket::MEDIA_TYPE_AUDIO || 748 media_type == cricket::MEDIA_TYPE_VIDEO); 749 750 return (media_type == cricket::MEDIA_TYPE_AUDIO) ? 751 &local_audio_tracks_ : &local_video_tracks_; 752 } 753 754 void MediaStreamSignaling::UpdateLocalTracks( 755 const std::vector<cricket::StreamParams>& streams, 756 cricket::MediaType media_type) { 757 TrackInfos* current_tracks = GetLocalTracks(media_type); 758 759 // Find removed tracks. Ie tracks where the track id, stream label or ssrc 760 // don't match the new StreamParam. 761 TrackInfos::iterator track_it = current_tracks->begin(); 762 while (track_it != current_tracks->end()) { 763 const TrackInfo& info = *track_it; 764 cricket::StreamParams params; 765 if (!cricket::GetStreamBySsrc(streams, info.ssrc, ¶ms) || 766 params.id != info.track_id || params.sync_label != info.stream_label) { 767 OnLocalTrackRemoved(info.stream_label, info.track_id, info.ssrc, 768 media_type); 769 track_it = current_tracks->erase(track_it); 770 } else { 771 ++track_it; 772 } 773 } 774 775 // Find new and active tracks. 776 for (cricket::StreamParamsVec::const_iterator it = streams.begin(); 777 it != streams.end(); ++it) { 778 // The sync_label is the MediaStream label and the |stream.id| is the 779 // track id. 780 const std::string& stream_label = it->sync_label; 781 const std::string& track_id = it->id; 782 uint32 ssrc = it->first_ssrc(); 783 const TrackInfo* track_info = FindTrackInfo(*current_tracks, 784 stream_label, 785 track_id); 786 if (!track_info) { 787 current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc)); 788 OnLocalTrackSeen(stream_label, track_id, it->first_ssrc(), 789 media_type); 790 } 791 } 792 } 793 794 void MediaStreamSignaling::OnLocalTrackSeen( 795 const std::string& stream_label, 796 const std::string& track_id, 797 uint32 ssrc, 798 cricket::MediaType media_type) { 799 MediaStreamInterface* stream = local_streams_->find(stream_label); 800 if (!stream) { 801 LOG(LS_WARNING) << "An unknown local MediaStream with label " 802 << stream_label << " has been configured."; 803 return; 804 } 805 806 if (media_type == cricket::MEDIA_TYPE_AUDIO) { 807 AudioTrackInterface* audio_track = stream->FindAudioTrack(track_id); 808 if (!audio_track) { 809 LOG(LS_WARNING) << "An unknown local AudioTrack with id , " 810 << track_id << " has been configured."; 811 return; 812 } 813 stream_observer_->OnAddLocalAudioTrack(stream, audio_track, ssrc); 814 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { 815 VideoTrackInterface* video_track = stream->FindVideoTrack(track_id); 816 if (!video_track) { 817 LOG(LS_WARNING) << "An unknown local VideoTrack with id , " 818 << track_id << " has been configured."; 819 return; 820 } 821 stream_observer_->OnAddLocalVideoTrack(stream, video_track, ssrc); 822 } else { 823 ASSERT(false && "Invalid media type"); 824 } 825 } 826 827 void MediaStreamSignaling::OnLocalTrackRemoved( 828 const std::string& stream_label, 829 const std::string& track_id, 830 uint32 ssrc, 831 cricket::MediaType media_type) { 832 MediaStreamInterface* stream = local_streams_->find(stream_label); 833 if (!stream) { 834 // This is the normal case. Ie RemoveLocalStream has been called and the 835 // SessionDescriptions has been renegotiated. 836 return; 837 } 838 // A track has been removed from the SessionDescription but the MediaStream 839 // is still associated with MediaStreamSignaling. This only occurs if the SDP 840 // doesn't match with the calls to AddLocalStream and RemoveLocalStream. 841 842 if (media_type == cricket::MEDIA_TYPE_AUDIO) { 843 AudioTrackInterface* audio_track = stream->FindAudioTrack(track_id); 844 if (!audio_track) { 845 return; 846 } 847 stream_observer_->OnRemoveLocalAudioTrack(stream, audio_track, ssrc); 848 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { 849 VideoTrackInterface* video_track = stream->FindVideoTrack(track_id); 850 if (!video_track) { 851 return; 852 } 853 stream_observer_->OnRemoveLocalVideoTrack(stream, video_track); 854 } else { 855 ASSERT(false && "Invalid media type."); 856 } 857 } 858 859 void MediaStreamSignaling::UpdateLocalRtpDataChannels( 860 const cricket::StreamParamsVec& streams) { 861 std::vector<std::string> existing_channels; 862 863 // Find new and active data channels. 864 for (cricket::StreamParamsVec::const_iterator it =streams.begin(); 865 it != streams.end(); ++it) { 866 // |it->sync_label| is actually the data channel label. The reason is that 867 // we use the same naming of data channels as we do for 868 // MediaStreams and Tracks. 869 // For MediaStreams, the sync_label is the MediaStream label and the 870 // track label is the same as |streamid|. 871 const std::string& channel_label = it->sync_label; 872 RtpDataChannels::iterator data_channel_it = 873 rtp_data_channels_.find(channel_label); 874 if (!VERIFY(data_channel_it != rtp_data_channels_.end())) { 875 continue; 876 } 877 // Set the SSRC the data channel should use for sending. 878 data_channel_it->second->SetSendSsrc(it->first_ssrc()); 879 existing_channels.push_back(data_channel_it->first); 880 } 881 882 UpdateClosingDataChannels(existing_channels, true); 883 } 884 885 void MediaStreamSignaling::UpdateRemoteRtpDataChannels( 886 const cricket::StreamParamsVec& streams) { 887 std::vector<std::string> existing_channels; 888 889 // Find new and active data channels. 890 for (cricket::StreamParamsVec::const_iterator it = streams.begin(); 891 it != streams.end(); ++it) { 892 // The data channel label is either the mslabel or the SSRC if the mslabel 893 // does not exist. Ex a=ssrc:444330170 mslabel:test1. 894 std::string label = it->sync_label.empty() ? 895 talk_base::ToString(it->first_ssrc()) : it->sync_label; 896 RtpDataChannels::iterator data_channel_it = 897 rtp_data_channels_.find(label); 898 if (data_channel_it == rtp_data_channels_.end()) { 899 // This is a new data channel. 900 CreateRemoteDataChannel(label, it->first_ssrc()); 901 } else { 902 data_channel_it->second->SetReceiveSsrc(it->first_ssrc()); 903 } 904 existing_channels.push_back(label); 905 } 906 907 UpdateClosingDataChannels(existing_channels, false); 908 } 909 910 void MediaStreamSignaling::UpdateClosingDataChannels( 911 const std::vector<std::string>& active_channels, bool is_local_update) { 912 RtpDataChannels::iterator it = rtp_data_channels_.begin(); 913 while (it != rtp_data_channels_.end()) { 914 DataChannel* data_channel = it->second; 915 if (std::find(active_channels.begin(), active_channels.end(), 916 data_channel->label()) != active_channels.end()) { 917 ++it; 918 continue; 919 } 920 921 if (is_local_update) 922 data_channel->SetSendSsrc(0); 923 else 924 data_channel->RemotePeerRequestClose(); 925 926 if (data_channel->state() == DataChannel::kClosed) { 927 rtp_data_channels_.erase(it); 928 it = rtp_data_channels_.begin(); 929 } else { 930 ++it; 931 } 932 } 933 } 934 935 void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label, 936 uint32 remote_ssrc) { 937 if (!data_channel_factory_) { 938 LOG(LS_WARNING) << "Remote peer requested a DataChannel but DataChannels " 939 << "are not supported."; 940 return; 941 } 942 scoped_refptr<DataChannel> channel( 943 data_channel_factory_->CreateDataChannel(label, NULL)); 944 if (!channel.get()) { 945 LOG(LS_WARNING) << "Remote peer requested a DataChannel but" 946 << "CreateDataChannel failed."; 947 return; 948 } 949 channel->SetReceiveSsrc(remote_ssrc); 950 stream_observer_->OnAddDataChannel(channel); 951 } 952 953 void MediaStreamSignaling::OnDataTransportCreatedForSctp() { 954 SctpDataChannels::iterator it = sctp_data_channels_.begin(); 955 for (; it != sctp_data_channels_.end(); ++it) { 956 (*it)->OnTransportChannelCreated(); 957 } 958 } 959 960 void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) { 961 SctpDataChannels::iterator it = sctp_data_channels_.begin(); 962 for (; it != sctp_data_channels_.end(); ++it) { 963 if ((*it)->id() < 0) { 964 int sid; 965 if (!AllocateSctpSid(role, &sid)) { 966 LOG(LS_ERROR) << "Failed to allocate SCTP sid."; 967 continue; 968 } 969 (*it)->SetSctpSid(sid); 970 } 971 } 972 } 973 974 975 void MediaStreamSignaling::OnRemoteSctpDataChannelClosed(uint32 sid) { 976 int index = FindDataChannelBySid(sid); 977 if (index < 0) { 978 LOG(LS_WARNING) << "Unexpected sid " << sid 979 << " of the remotely closed DataChannel."; 980 return; 981 } 982 sctp_data_channels_[index]->Close(); 983 } 984 985 const MediaStreamSignaling::TrackInfo* 986 MediaStreamSignaling::FindTrackInfo( 987 const MediaStreamSignaling::TrackInfos& infos, 988 const std::string& stream_label, 989 const std::string track_id) const { 990 991 for (TrackInfos::const_iterator it = infos.begin(); 992 it != infos.end(); ++it) { 993 if (it->stream_label == stream_label && it->track_id == track_id) 994 return &*it; 995 } 996 return NULL; 997 } 998 999 int MediaStreamSignaling::FindDataChannelBySid(int sid) const { 1000 for (size_t i = 0; i < sctp_data_channels_.size(); ++i) { 1001 if (sctp_data_channels_[i]->id() == sid) { 1002 return static_cast<int>(i); 1003 } 1004 } 1005 return -1; 1006 } 1007 1008 } // namespace webrtc 1009