| /* | |
| * Copyright 2020 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "pc/rtp_transmission_manager.h" | |
| #include <cstdint> | |
| #include <iterator> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/algorithm/container.h" | |
| #include "absl/functional/any_invocable.h" | |
| #include "absl/strings/string_view.h" | |
| #include "api/audio_options.h" | |
| #include "api/crypto/crypto_options.h" | |
| #include "api/environment/environment.h" | |
| #include "api/make_ref_counted.h" | |
| #include "api/media_stream_interface.h" | |
| #include "api/media_types.h" | |
| #include "api/peer_connection_interface.h" | |
| #include "api/rtc_error.h" | |
| #include "api/rtp_parameters.h" | |
| #include "api/rtp_receiver_interface.h" | |
| #include "api/rtp_sender_interface.h" | |
| #include "api/rtp_transceiver_direction.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/sequence_checker.h" | |
| #include "media/base/media_channel.h" | |
| #include "media/base/media_config.h" | |
| #include "media/base/media_engine.h" | |
| #include "pc/audio_rtp_receiver.h" | |
| #include "pc/channel_interface.h" | |
| #include "pc/codec_vendor.h" | |
| #include "pc/connection_context.h" | |
| #include "pc/legacy_stats_collector_interface.h" | |
| #include "pc/rtp_receiver.h" | |
| #include "pc/rtp_receiver_proxy.h" | |
| #include "pc/rtp_sender.h" | |
| #include "pc/rtp_sender_proxy.h" | |
| #include "pc/rtp_transceiver.h" | |
| #include "pc/usage_pattern.h" | |
| #include "pc/video_rtp_receiver.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/crypto_random.h" | |
| #include "rtc_base/logging.h" | |
| #include "rtc_base/system/plan_b_only.h" | |
| namespace webrtc { | |
| namespace { | |
| const char kDefaultAudioSenderId[] = "defaulta0"; | |
| const char kDefaultVideoSenderId[] = "defaultv0"; | |
| template <typename T> | |
| MediaType TrackType(T& track) { | |
| return track->kind() == MediaStreamTrackInterface::kAudioKind | |
| ? MediaType::AUDIO | |
| : MediaType::VIDEO; | |
| } | |
| std::optional<uint32_t> GetSenderSsrc(const std::vector<RtpSenderInfo>& infos, | |
| absl::string_view stream_id, | |
| absl::string_view sender_id) { | |
| auto it = absl::c_find_if(infos, [&](const RtpSenderInfo& info) { | |
| return info.stream_id == stream_id && info.sender_id == sender_id; | |
| }); | |
| return it == infos.end() ? std::nullopt | |
| : std::optional<uint32_t>((*it).first_ssrc); | |
| } | |
| } // namespace | |
| RtpTransmissionManager::RtpTransmissionManager( | |
| const Environment& env, | |
| Call* call, | |
| bool is_unified_plan, | |
| ConnectionContext* context, | |
| CodecLookupHelper* codec_lookup_helper, | |
| UsagePattern* usage_pattern, | |
| PeerConnectionObserver* observer, | |
| LegacyStatsCollectorInterface* legacy_stats, | |
| absl::AnyInvocable<void()> on_negotiation_needed) | |
| : env_(env), | |
| is_unified_plan_(is_unified_plan), | |
| call_(call), | |
| context_(context), | |
| codec_lookup_helper_(codec_lookup_helper), | |
| usage_pattern_(usage_pattern), | |
| observer_(observer), | |
| legacy_stats_(legacy_stats), | |
| on_negotiation_needed_(std::move(on_negotiation_needed)), | |
| weak_ptr_factory_(this) {} | |
| void RtpTransmissionManager::Close() { | |
| closed_ = true; | |
| observer_ = nullptr; | |
| } | |
| // Implementation of SetStreamsObserver | |
| void RtpTransmissionManager::OnSetStreams() { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(IsUnifiedPlan()); | |
| OnNegotiationNeeded(); | |
| } | |
| // Function to call back to the PeerConnection when negotiation is needed | |
| void RtpTransmissionManager::OnNegotiationNeeded() { | |
| on_negotiation_needed_(); | |
| } | |
| std::vector<RtpHeaderExtensionCapability> | |
| RtpTransmissionManager::GetDefaultHeaderExtensions(MediaType media_type) { | |
| if (media_type == MediaType::AUDIO) { | |
| return media_engine()->voice().GetRtpHeaderExtensions(&env_.field_trials()); | |
| } | |
| RTC_DCHECK_EQ(media_type, MediaType::VIDEO); | |
| return media_engine()->video().GetRtpHeaderExtensions(&env_.field_trials()); | |
| } | |
| void RtpTransmissionManager::RunWithObserver( | |
| absl::AnyInvocable<void(PeerConnectionObserver*) &&> task) { // NOLINT | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(observer_); | |
| std::move(task)(observer_); | |
| } | |
| PLAN_B_ONLY VoiceMediaSendChannelInterface* | |
| RtpTransmissionManager::voice_media_send_channel() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| return GetAudioTransceiver()->internal()->voice_media_send_channel(); | |
| } | |
| PLAN_B_ONLY VideoMediaSendChannelInterface* | |
| RtpTransmissionManager::video_media_send_channel() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| return GetVideoTransceiver()->internal()->video_media_send_channel(); | |
| } | |
| PLAN_B_ONLY VoiceMediaReceiveChannelInterface* | |
| RtpTransmissionManager::voice_media_receive_channel() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| return GetAudioTransceiver()->internal()->voice_media_receive_channel(); | |
| } | |
| PLAN_B_ONLY VideoMediaReceiveChannelInterface* | |
| RtpTransmissionManager::video_media_receive_channel() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| return GetVideoTransceiver()->internal()->video_media_receive_channel(); | |
| } | |
| PLAN_B_ONLY RTCErrorOr<scoped_refptr<RtpSenderInterface>> | |
| RtpTransmissionManager::AddTrackPlanB( | |
| scoped_refptr<MediaStreamTrackInterface> track, | |
| const std::vector<std::string>& stream_ids, | |
| const std::vector<RtpEncodingParameters>* init_send_encodings) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| if (stream_ids.size() > 1u) { | |
| return LOG_ERROR(RTCError::UnsupportedOperation() | |
| << "AddTrack with more than one stream is not supported " | |
| "with Plan B semantics."); | |
| } | |
| std::vector<std::string> adjusted_stream_ids; | |
| if (stream_ids.empty()) { | |
| adjusted_stream_ids.push_back(CreateRandomUuid()); | |
| } else { | |
| adjusted_stream_ids.reserve(stream_ids.size()); | |
| absl::c_copy_if(stream_ids, std::back_inserter(adjusted_stream_ids), | |
| [&](const auto& id) { | |
| return !absl::c_linear_search(adjusted_stream_ids, id); | |
| }); | |
| } | |
| MediaType media_type = TrackType(track); | |
| RtpTransceiver* transceiver = media_type == MediaType::AUDIO | |
| ? GetAudioTransceiver()->internal() | |
| : GetVideoTransceiver()->internal(); | |
| scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender = | |
| transceiver->AddSenderPlanB(track, track->id(), adjusted_stream_ids, | |
| init_send_encodings | |
| ? *init_send_encodings | |
| : std::vector<RtpEncodingParameters>(1)); | |
| RTC_DCHECK(new_sender->internal()->stream_ids() == adjusted_stream_ids); | |
| std::optional<uint32_t> ssrc = | |
| GetSenderSsrc(media_type == MediaType::AUDIO ? local_audio_sender_infos_ | |
| : local_video_sender_infos_, | |
| adjusted_stream_ids[0], track->id()); | |
| if (ssrc) { | |
| new_sender->internal()->SetSsrc(*ssrc); | |
| } | |
| NoteUsageEvent(media_type == MediaType::AUDIO ? UsageEvent::AUDIO_ADDED | |
| : UsageEvent::VIDEO_ADDED); | |
| return scoped_refptr<RtpSenderInterface>(new_sender); | |
| } | |
| RTCErrorOr<scoped_refptr<RtpSenderInterface>> | |
| RtpTransmissionManager::AddTrackUnifiedPlan( | |
| const MediaConfig& media_config, | |
| const AudioOptions& audio_options, | |
| const VideoOptions& video_options, | |
| const CryptoOptions& crypto_options, | |
| VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, | |
| scoped_refptr<MediaStreamTrackInterface> track, | |
| const std::vector<std::string>& stream_ids, | |
| const std::vector<RtpEncodingParameters>* init_send_encodings) { | |
| RTC_DCHECK(IsUnifiedPlan()); | |
| auto transceiver = | |
| FindFirstTransceiverForAddedTrack(track, init_send_encodings); | |
| if (transceiver) { | |
| RTC_LOG(LS_INFO) << "Reusing an existing " | |
| << MediaTypeToString(transceiver->media_type()) | |
| << " transceiver for AddTrack."; | |
| if (transceiver->stopping()) { | |
| return LOG_ERROR(RTCError::InvalidParameter() | |
| << "The existing transceiver is stopping."); | |
| } | |
| if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { | |
| transceiver->internal()->set_direction( | |
| RtpTransceiverDirection::kSendRecv); | |
| } else if (transceiver->direction() == RtpTransceiverDirection::kInactive) { | |
| transceiver->internal()->set_direction( | |
| RtpTransceiverDirection::kSendOnly); | |
| } | |
| transceiver->sender()->SetTrack(track.get()); | |
| transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); | |
| transceiver->internal()->set_reused_for_addtrack(true); | |
| } else { | |
| MediaType media_type = TrackType(track); | |
| RTC_LOG(LS_INFO) << "Adding " << MediaTypeToString(media_type) | |
| << " transceiver in response to a call to AddTrack."; | |
| std::string sender_id = track->id(); | |
| // Avoid creating a sender with an existing ID by generating a random ID. | |
| // This can happen if this is the second time AddTrack has created a sender | |
| // for this track. | |
| if (FindSenderById(sender_id)) { | |
| sender_id = CreateRandomUuid(); | |
| } | |
| transceiver = CreateAndAddTransceiver( | |
| media_config, audio_options, video_options, crypto_options, | |
| video_bitrate_allocator_factory, media_type, track, stream_ids, | |
| init_send_encodings | |
| ? *init_send_encodings | |
| : std::vector<RtpEncodingParameters>(1, RtpEncodingParameters{}), | |
| /*header_extensions_to_negotiate=*/{}, sender_id, /*receiver_id=*/""); | |
| transceiver->internal()->set_created_by_addtrack(true); | |
| transceiver->internal()->set_direction(RtpTransceiverDirection::kSendRecv); | |
| } | |
| return transceiver->sender(); | |
| } | |
| scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> | |
| RtpTransmissionManager::CreateAndAddTransceiver( | |
| const MediaConfig& media_config, | |
| const AudioOptions& audio_options, | |
| const VideoOptions& video_options, | |
| const CryptoOptions& crypto_options, | |
| VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, | |
| MediaType media_type, | |
| scoped_refptr<MediaStreamTrackInterface> track, | |
| const std::vector<std::string>& stream_ids, | |
| const std::vector<RtpEncodingParameters>& init_send_encodings, | |
| const std::vector<RtpHeaderExtensionCapability>& | |
| header_extensions_to_negotiate, | |
| absl::string_view sender_id, | |
| absl::string_view receiver_id) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| // Ensure that the new sender does not have an ID that is already in use by | |
| // another sender. | |
| // Allow receiver IDs to conflict since those come from remote SDP (which | |
| // could be invalid, but should not cause a crash). | |
| RTC_DCHECK(!FindSenderById(sender_id)); | |
| std::vector<RtpHeaderExtensionCapability> header_extensions = | |
| std::move(header_extensions_to_negotiate); | |
| if (env_.field_trials().IsEnabled("WebRTC-HeaderExtensionNegotiateMemory")) { | |
| // If we have already negotiated header extensions for this type, | |
| // and it is not stopped, | |
| // reuse the negotiated state for new transceivers of the same type. | |
| for (const auto& transceiver : transceivers()->List()) { | |
| if (transceiver->media_type() == media_type && !transceiver->stopping()) { | |
| header_extensions = transceiver->GetHeaderExtensionsToNegotiate(); | |
| break; | |
| } | |
| } | |
| } | |
| if (header_extensions.empty()) { | |
| header_extensions = GetDefaultHeaderExtensions(media_type); | |
| } | |
| RtpSenderBase::SetStreamsObserver* observer = | |
| IsUnifiedPlan() ? this : nullptr; | |
| auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create( | |
| signaling_thread(), | |
| make_ref_counted<RtpTransceiver>( | |
| env_, call_, media_config, sender_id, receiver_id, media_type, track, | |
| stream_ids, std::move(init_send_encodings), context_, | |
| codec_lookup_helper_, legacy_stats_, observer, audio_options, | |
| video_options, crypto_options, video_bitrate_allocator_factory, | |
| std::move(header_extensions), | |
| [this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() { | |
| if (this_weak_ptr) { | |
| this_weak_ptr->OnNegotiationNeeded(); | |
| } | |
| })); | |
| transceivers()->Add(transceiver); | |
| NoteUsageEvent(media_type == MediaType::AUDIO ? UsageEvent::AUDIO_ADDED | |
| : UsageEvent::VIDEO_ADDED); | |
| return transceiver; | |
| } | |
| scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> | |
| RtpTransmissionManager::FindFirstTransceiverForAddedTrack( | |
| scoped_refptr<MediaStreamTrackInterface> track, | |
| const std::vector<RtpEncodingParameters>* init_send_encodings) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(track); | |
| if (init_send_encodings != nullptr) { | |
| return nullptr; | |
| } | |
| const MediaType media_type = TrackType(track); | |
| for (auto& transceiver : transceivers()->List()) { | |
| if (!transceiver->sender()->track() && | |
| transceiver->media_type() == media_type && !transceiver->stopped() && | |
| !transceiver->internal()->has_ever_been_used_to_send()) { | |
| return transceiver; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| std::vector<scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>> | |
| RtpTransmissionManager::GetSendersInternal() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| std::vector<scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>> | |
| all_senders; | |
| for (const auto& transceiver : transceivers_.List()) { | |
| if (IsUnifiedPlan() && transceiver->internal()->stopped()) | |
| continue; | |
| RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() | |
| auto senders = transceiver->internal()->senders(); | |
| RTC_ALLOW_PLAN_B_DEPRECATION_END() | |
| all_senders.insert(all_senders.end(), senders.begin(), senders.end()); | |
| } | |
| return all_senders; | |
| } | |
| std::vector<scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>> | |
| RtpTransmissionManager::GetReceiversInternal() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| std::vector<scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>> | |
| all_receivers; | |
| for (const auto& transceiver : transceivers_.List()) { | |
| if (IsUnifiedPlan() && transceiver->internal()->stopped()) | |
| continue; | |
| RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() | |
| auto receivers = transceiver->internal()->receivers(); | |
| RTC_ALLOW_PLAN_B_DEPRECATION_END() | |
| all_receivers.insert(all_receivers.end(), receivers.begin(), | |
| receivers.end()); | |
| } | |
| return all_receivers; | |
| } | |
| PLAN_B_ONLY scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> | |
| RtpTransmissionManager::GetAudioTransceiver() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| // This method only works with Plan B SDP, where there is a single | |
| // audio/video transceiver. | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| for (auto transceiver : transceivers_.List()) { | |
| if (transceiver->media_type() == MediaType::AUDIO) { | |
| return transceiver; | |
| } | |
| } | |
| RTC_DCHECK_NOTREACHED(); | |
| return nullptr; | |
| } | |
| PLAN_B_ONLY scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> | |
| RtpTransmissionManager::GetVideoTransceiver() const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| // This method only works with Plan B SDP, where there is a single | |
| // audio/video transceiver. | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| for (auto transceiver : transceivers_.List()) { | |
| if (transceiver->media_type() == MediaType::VIDEO) { | |
| return transceiver; | |
| } | |
| } | |
| RTC_DCHECK_NOTREACHED(); | |
| return nullptr; | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::AddTrackPlanB( | |
| MediaStreamTrackInterface* track, | |
| MediaStreamInterface* stream) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(track); | |
| RTC_DCHECK(stream); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| auto sender = FindSenderForTrack(track); | |
| if (sender) { | |
| // We already have a sender for this track, so just change the stream_id | |
| // so that it's correct in the next call to CreateOffer. | |
| sender->internal()->set_stream_ids({stream->id()}); | |
| return; | |
| } | |
| // Normal case; we've never seen this track before. | |
| MediaType media_type = TrackType(track); | |
| RtpTransceiver* transceiver = media_type == MediaType::AUDIO | |
| ? GetAudioTransceiver()->internal() | |
| : GetVideoTransceiver()->internal(); | |
| scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender = | |
| transceiver->AddSenderPlanB( | |
| scoped_refptr<MediaStreamTrackInterface>(track), track->id(), | |
| {stream->id()}, {}); | |
| // If the sender has already been configured in SDP, we call SetSsrc, | |
| // which will connect the sender to the underlying transport. This can | |
| // occur if a local session description that contains the ID of the sender | |
| // is set before AddStream is called. It can also occur if the local | |
| // session description is not changed and RemoveStream is called, and | |
| // later AddStream is called again with the same stream. | |
| std::optional<uint32_t> ssrc = | |
| GetSenderSsrc(media_type == MediaType::AUDIO ? local_audio_sender_infos_ | |
| : local_video_sender_infos_, | |
| stream->id(), track->id()); | |
| if (ssrc) { | |
| new_sender->internal()->SetSsrc(*ssrc); | |
| } | |
| } | |
| // TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around | |
| // indefinitely, when we have unified plan SDP. | |
| PLAN_B_ONLY void RtpTransmissionManager::RemoveTrackPlanB( | |
| MediaStreamTrackInterface* track, | |
| MediaStreamInterface* stream) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| auto sender = FindSenderForTrack(track); | |
| if (!sender) { | |
| RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() | |
| << " doesn't exist."; | |
| return; | |
| } | |
| RtpTransceiver* transceiver = TrackType(track) == MediaType::AUDIO | |
| ? GetAudioTransceiver()->internal() | |
| : GetVideoTransceiver()->internal(); | |
| transceiver->RemoveSenderPlanB(sender.get()); | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::CreateAudioReceiverPlanB( | |
| MediaStreamInterface* stream, | |
| const RtpSenderInfo& remote_sender_info) { | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| RTC_DCHECK(!closed_); | |
| std::vector<scoped_refptr<MediaStreamInterface>> streams; | |
| streams.push_back(scoped_refptr<MediaStreamInterface>(stream)); | |
| // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use | |
| // the constructor taking stream IDs instead. | |
| auto audio_receiver = make_ref_counted<AudioRtpReceiver>( | |
| worker_thread(), remote_sender_info.sender_id, streams, false, | |
| voice_media_receive_channel()); | |
| auto task = (remote_sender_info.sender_id == kDefaultAudioSenderId) | |
| ? audio_receiver->GetSetupForUnsignaledMediaChannel() | |
| : audio_receiver->GetSetupForMediaChannel( | |
| remote_sender_info.first_ssrc); | |
| worker_thread()->BlockingCall([&]() mutable { std::move(task)(); }); | |
| auto receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create( | |
| signaling_thread(), worker_thread(), std::move(audio_receiver)); | |
| GetAudioTransceiver()->internal()->AddReceiverPlanB(receiver); | |
| RunWithObserver( | |
| [&](auto observer) { observer->OnAddTrack(receiver, streams); }); | |
| NoteUsageEvent(UsageEvent::AUDIO_ADDED); | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::CreateVideoReceiverPlanB( | |
| MediaStreamInterface* stream, | |
| const RtpSenderInfo& remote_sender_info) { | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| RTC_DCHECK(!closed_); | |
| std::vector<scoped_refptr<MediaStreamInterface>> streams; | |
| streams.push_back(scoped_refptr<MediaStreamInterface>(stream)); | |
| // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use | |
| // the constructor taking stream IDs instead. | |
| auto video_receiver = make_ref_counted<VideoRtpReceiver>( | |
| worker_thread(), remote_sender_info.sender_id, streams); | |
| auto task = video_receiver->GetSetupForMediaChannel( | |
| remote_sender_info.sender_id == kDefaultVideoSenderId | |
| ? std::nullopt | |
| : std::optional<uint32_t>(remote_sender_info.first_ssrc), | |
| video_media_receive_channel()); | |
| worker_thread()->BlockingCall([&]() mutable { std::move(task)(); }); | |
| auto receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create( | |
| signaling_thread(), worker_thread(), std::move(video_receiver)); | |
| GetVideoTransceiver()->internal()->AddReceiverPlanB(receiver); | |
| RunWithObserver( | |
| [&](auto observer) { observer->OnAddTrack(receiver, streams); }); | |
| NoteUsageEvent(UsageEvent::VIDEO_ADDED); | |
| } | |
| // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote | |
| // description. | |
| PLAN_B_ONLY scoped_refptr<RtpReceiverInterface> | |
| RtpTransmissionManager::RemoveAndStopReceiver( | |
| const RtpSenderInfo& remote_sender_info) { | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| auto receiver = FindReceiverById(remote_sender_info.sender_id); | |
| if (!receiver) { | |
| RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " | |
| << remote_sender_info.sender_id << " doesn't exist."; | |
| return nullptr; | |
| } | |
| if (receiver->media_type() == MediaType::AUDIO) { | |
| GetAudioTransceiver()->internal()->RemoveReceiverPlanB(receiver.get()); | |
| } else { | |
| GetVideoTransceiver()->internal()->RemoveReceiverPlanB(receiver.get()); | |
| } | |
| return receiver; | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::OnRemoteSenderAddedPlanB( | |
| const RtpSenderInfo& sender_info, | |
| MediaStreamInterface* stream, | |
| MediaType media_type) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| RTC_LOG(LS_INFO) << "Creating " << MediaTypeToString(media_type) | |
| << " receiver for track_id=" << sender_info.sender_id | |
| << " and stream_id=" << sender_info.stream_id; | |
| if (media_type == MediaType::AUDIO) { | |
| CreateAudioReceiverPlanB(stream, sender_info); | |
| } else if (media_type == MediaType::VIDEO) { | |
| CreateVideoReceiverPlanB(stream, sender_info); | |
| } else { | |
| RTC_DCHECK_NOTREACHED() << "Invalid media type"; | |
| } | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::OnRemoteSenderRemovedPlanB( | |
| const RtpSenderInfo& sender_info, | |
| MediaStreamInterface* stream, | |
| MediaType media_type) { | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_LOG(LS_INFO) << "Removing " << MediaTypeToString(media_type) | |
| << " receiver for track_id=" << sender_info.sender_id | |
| << " and stream_id=" << sender_info.stream_id; | |
| scoped_refptr<RtpReceiverInterface> receiver; | |
| if (media_type == MediaType::AUDIO) { | |
| // When the MediaEngine audio channel is destroyed, the RemoteAudioSource | |
| // will be notified which will end the AudioRtpReceiver::track(). | |
| receiver = RemoveAndStopReceiver(sender_info); | |
| scoped_refptr<AudioTrackInterface> audio_track = | |
| stream->FindAudioTrack(sender_info.sender_id); | |
| if (audio_track) { | |
| stream->RemoveTrack(audio_track); | |
| } | |
| } else if (media_type == MediaType::VIDEO) { | |
| // Stopping or destroying a VideoRtpReceiver will end the | |
| // VideoRtpReceiver::track(). | |
| receiver = RemoveAndStopReceiver(sender_info); | |
| scoped_refptr<VideoTrackInterface> video_track = | |
| stream->FindVideoTrack(sender_info.sender_id); | |
| if (video_track) { | |
| // There's no guarantee the track is still available, e.g. the track may | |
| // have been removed from the stream by an application. | |
| stream->RemoveTrack(video_track); | |
| } | |
| } else { | |
| RTC_DCHECK_NOTREACHED() << "Invalid media type"; | |
| } | |
| if (receiver) { | |
| RTC_DCHECK(!closed_); | |
| RunWithObserver([&](auto observer) { observer->OnRemoveTrack(receiver); }); | |
| } | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::OnLocalSenderAdded( | |
| const RtpSenderInfo& sender_info, | |
| MediaType media_type) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| RTC_DCHECK(!IsUnifiedPlan()); | |
| auto sender = FindSenderById(sender_info.sender_id); | |
| if (!sender) { | |
| RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " | |
| << sender_info.sender_id | |
| << " has been configured in the local description."; | |
| return; | |
| } | |
| if (sender->media_type() != media_type) { | |
| RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" | |
| " description with an unexpected media type."; | |
| return; | |
| } | |
| sender->internal()->set_stream_ids({sender_info.stream_id}); | |
| sender->internal()->SetSsrc(sender_info.first_ssrc); | |
| } | |
| PLAN_B_ONLY void RtpTransmissionManager::OnLocalSenderRemoved( | |
| const RtpSenderInfo& sender_info, | |
| MediaType media_type) { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| auto sender = FindSenderById(sender_info.sender_id); | |
| if (!sender) { | |
| // This is the normal case. I.e., RemoveStream has been called and the | |
| // SessionDescriptions has been renegotiated. | |
| return; | |
| } | |
| // A sender has been removed from the SessionDescription but it's still | |
| // associated with the PeerConnection. This only occurs if the SDP doesn't | |
| // match with the calls to CreateSender, AddStream and RemoveStream. | |
| if (sender->media_type() != media_type) { | |
| RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" | |
| " description with an unexpected media type."; | |
| return; | |
| } | |
| sender->internal()->SetSsrc(0); | |
| } | |
| std::vector<RtpSenderInfo>* RtpTransmissionManager::GetRemoteSenderInfos( | |
| MediaType media_type) { | |
| RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO); | |
| return (media_type == MediaType::AUDIO) ? &remote_audio_sender_infos_ | |
| : &remote_video_sender_infos_; | |
| } | |
| std::vector<RtpSenderInfo>* RtpTransmissionManager::GetLocalSenderInfos( | |
| MediaType media_type) { | |
| RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO); | |
| return (media_type == MediaType::AUDIO) ? &local_audio_sender_infos_ | |
| : &local_video_sender_infos_; | |
| } | |
| scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> | |
| RtpTransmissionManager::FindSenderForTrack( | |
| MediaStreamTrackInterface* track) const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| for (const auto& transceiver : transceivers_.List()) { | |
| RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() | |
| for (auto sender : transceiver->internal()->senders()) { | |
| if (sender->track() == track) { | |
| return sender; | |
| } | |
| } | |
| RTC_ALLOW_PLAN_B_DEPRECATION_END() | |
| } | |
| return nullptr; | |
| } | |
| scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> | |
| RtpTransmissionManager::FindSenderById(absl::string_view sender_id) const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| for (const auto& transceiver : transceivers_.List()) { | |
| RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); | |
| // Under Unified Plan, senders() always has exactly one entry, | |
| // and one can use sender() not senders(). | |
| // Since this function is used both in Plan B and Unified, this is | |
| // left as-is for now. | |
| for (auto sender : transceiver->internal()->senders()) { | |
| if (sender->id() == sender_id) { | |
| return sender; | |
| } | |
| } | |
| RTC_ALLOW_PLAN_B_DEPRECATION_END(); | |
| } | |
| return nullptr; | |
| } | |
| PLAN_B_ONLY scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> | |
| RtpTransmissionManager::FindReceiverById(absl::string_view receiver_id) const { | |
| RTC_DCHECK_RUN_ON(signaling_thread()); | |
| for (const auto& transceiver : transceivers_.List()) { | |
| for (auto receiver : transceiver->internal()->receivers()) { | |
| if (receiver->id() == receiver_id) { | |
| return receiver; | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| const MediaEngineInterface* RtpTransmissionManager::media_engine() const { | |
| return context_->media_engine(); | |
| } | |
| } // namespace webrtc |