RTCRtpTransceiver.cpp (50869B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "jsapi/RTCRtpTransceiver.h" 6 7 #include <stdint.h> 8 9 #include <algorithm> 10 #include <set> 11 #include <string> 12 #include <tuple> 13 #include <utility> 14 #include <vector> 15 16 #include "ErrorList.h" 17 #include "MainThreadUtils.h" 18 #include "MediaEventSource.h" 19 #include "MediaTrackGraph.h" 20 #include "MediaTransportHandler.h" 21 #include "PeerConnectionImpl.h" 22 #include "PrincipalHandle.h" 23 #include "RTCDTMFSender.h" 24 #include "RTCDtlsTransport.h" 25 #include "RTCRtpReceiver.h" 26 #include "RTCRtpSender.h" 27 #include "RTCStatsIdGenerator.h" 28 #include "RTCStatsReport.h" 29 #include "RemoteTrackSource.h" 30 #include "api/video_codecs/video_codec.h" 31 #include "js/RootingAPI.h" 32 #include "jsep/JsepCodecDescription.h" 33 #include "jsep/JsepSession.h" 34 #include "jsep/JsepTrack.h" 35 #include "jsep/JsepTrackEncoding.h" 36 #include "libwebrtcglue/AudioConduit.h" 37 #include "libwebrtcglue/CodecConfig.h" 38 #include "libwebrtcglue/FrameTransformerProxy.h" 39 #include "libwebrtcglue/MediaConduitControl.h" 40 #include "libwebrtcglue/MediaConduitInterface.h" 41 #include "libwebrtcglue/RtpRtcpConfig.h" 42 #include "libwebrtcglue/VideoConduit.h" 43 #include "libwebrtcglue/WebrtcCallWrapper.h" 44 #include "mozilla/AbstractThread.h" 45 #include "mozilla/Assertions.h" 46 #include "mozilla/ErrorResult.h" 47 #include "mozilla/Maybe.h" 48 #include "mozilla/MozPromise.h" 49 #include "mozilla/Preferences.h" 50 #include "mozilla/RefPtr.h" 51 #include "mozilla/StateMirroring.h" 52 #include "mozilla/UniquePtr.h" 53 #include "mozilla/dom/Nullable.h" 54 #include "mozilla/dom/Promise.h" 55 #include "mozilla/dom/RTCRtpReceiverBinding.h" 56 #include "mozilla/dom/RTCRtpSenderBinding.h" 57 #include "mozilla/dom/RTCRtpTransceiverBinding.h" 58 #include "mozilla/dom/RTCStatsReportBinding.h" 59 #include "mozilla/fallible.h" 60 #include "mozilla/mozalloc_oom.h" 61 #include "nsCOMPtr.h" 62 #include "nsContentUtils.h" 63 #include "nsCycleCollectionParticipant.h" 64 #include "nsDebug.h" 65 #include "nsISerialEventTarget.h" 66 #include "nsISupports.h" 67 #include "nsProxyRelease.h" 68 #include "nsString.h" 69 #include "nsStringFwd.h" 70 #include "nsTArray.h" 71 #include "nsThreadUtils.h" 72 #include "nsWrapperCache.h" 73 #include "sdp/SdpAttribute.h" 74 #include "sdp/SdpEnum.h" 75 #include "sdp/SdpHelper.h" 76 #include "sdp/SdpMediaSection.h" 77 #include "systemservices/MediaUtils.h" 78 #include "transport/logging.h" 79 #include "transport/transportlayer.h" 80 #include "transportbridge/MediaPipeline.h" 81 #include "utils/PerformanceRecorder.h" 82 83 namespace mozilla { 84 85 using namespace dom; 86 87 namespace { 88 struct ConduitControlState : public AudioConduitControlInterface, 89 public VideoConduitControlInterface { 90 ConduitControlState(RTCRtpTransceiver* aTransceiver, RTCRtpSender* aSender, 91 RTCRtpReceiver* aReceiver) 92 : mTransceiver(new nsMainThreadPtrHolder<RTCRtpTransceiver>( 93 "ConduitControlState::mTransceiver", aTransceiver, false)), 94 mSender(new nsMainThreadPtrHolder<dom::RTCRtpSender>( 95 "ConduitControlState::mSender", aSender, false)), 96 mReceiver(new nsMainThreadPtrHolder<dom::RTCRtpReceiver>( 97 "ConduitControlState::mReceiver", aReceiver, false)) {} 98 99 const nsMainThreadPtrHandle<RTCRtpTransceiver> mTransceiver; 100 const nsMainThreadPtrHandle<RTCRtpSender> mSender; 101 const nsMainThreadPtrHandle<RTCRtpReceiver> mReceiver; 102 103 // MediaConduitControlInterface 104 Canonical<bool>& CanonicalReceiving() override { 105 return mReceiver->CanonicalReceiving(); 106 } 107 Canonical<bool>& CanonicalTransmitting() override { 108 return mSender->CanonicalTransmitting(); 109 } 110 Canonical<Ssrcs>& CanonicalLocalSsrcs() override { 111 return mSender->CanonicalSsrcs(); 112 } 113 Canonical<std::string>& CanonicalLocalCname() override { 114 return mSender->CanonicalCname(); 115 } 116 Canonical<std::string>& CanonicalMid() override { 117 return mTransceiver->CanonicalMid(); 118 } 119 Canonical<Ssrc>& CanonicalRemoteSsrc() override { 120 return mReceiver->CanonicalSsrc(); 121 } 122 Canonical<std::string>& CanonicalSyncGroup() override { 123 return mTransceiver->CanonicalSyncGroup(); 124 } 125 Canonical<RtpExtList>& CanonicalLocalRecvRtpExtensions() override { 126 return mReceiver->CanonicalLocalRtpExtensions(); 127 } 128 Canonical<RtpExtList>& CanonicalLocalSendRtpExtensions() override { 129 return mSender->CanonicalLocalRtpExtensions(); 130 } 131 132 // AudioConduitControlInterface 133 Canonical<Maybe<AudioCodecConfig>>& CanonicalAudioSendCodec() override { 134 return mSender->CanonicalAudioCodec(); 135 } 136 Canonical<std::vector<AudioCodecConfig>>& CanonicalAudioRecvCodecs() 137 override { 138 return mReceiver->CanonicalAudioCodecs(); 139 } 140 MediaEventSource<DtmfEvent>& OnDtmfEvent() override { 141 return mSender->GetDtmf()->OnDtmfEvent(); 142 } 143 144 // VideoConduitControlInterface 145 Canonical<Ssrcs>& CanonicalLocalVideoRtxSsrcs() override { 146 return mSender->CanonicalVideoRtxSsrcs(); 147 } 148 Canonical<Ssrc>& CanonicalRemoteVideoRtxSsrc() override { 149 return mReceiver->CanonicalVideoRtxSsrc(); 150 } 151 Canonical<Maybe<VideoCodecConfig>>& CanonicalVideoSendCodec() override { 152 return mSender->CanonicalVideoCodec(); 153 } 154 Canonical<Maybe<RtpRtcpConfig>>& CanonicalVideoSendRtpRtcpConfig() override { 155 return mSender->CanonicalVideoRtpRtcpConfig(); 156 } 157 Canonical<std::vector<VideoCodecConfig>>& CanonicalVideoRecvCodecs() 158 override { 159 return mReceiver->CanonicalVideoCodecs(); 160 } 161 Canonical<Maybe<RtpRtcpConfig>>& CanonicalVideoRecvRtpRtcpConfig() override { 162 return mReceiver->CanonicalVideoRtpRtcpConfig(); 163 } 164 Canonical<webrtc::VideoCodecMode>& CanonicalVideoCodecMode() override { 165 return mSender->CanonicalVideoCodecMode(); 166 } 167 Canonical<RefPtr<FrameTransformerProxy>>& CanonicalFrameTransformerProxySend() 168 override { 169 return mSender->CanonicalFrameTransformerProxy(); 170 } 171 Canonical<RefPtr<FrameTransformerProxy>>& CanonicalFrameTransformerProxyRecv() 172 override { 173 return mReceiver->CanonicalFrameTransformerProxy(); 174 } 175 Canonical<webrtc::DegradationPreference>& 176 CanonicalVideoDegradationPreference() override { 177 return mSender->CanonicalVideoDegradationPreference(); 178 } 179 }; 180 } // namespace 181 182 MOZ_MTLOG_MODULE("RTCRtpTransceiver") 183 184 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(RTCRtpTransceiver) 185 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(RTCRtpTransceiver) 186 tmp->Unlink(); 187 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 188 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(RTCRtpTransceiver) 190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow, mPc, mSendTrack, mReceiver, 191 mSender, mDtlsTransport, 192 mLastStableDtlsTransport) 193 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 194 195 NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCRtpTransceiver) 196 NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCRtpTransceiver) 197 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCRtpTransceiver) 198 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 199 NS_INTERFACE_MAP_ENTRY(nsISupports) 200 NS_INTERFACE_MAP_END 201 202 #define INIT_CANONICAL(name, val) \ 203 name(AbstractThread::MainThread(), val, \ 204 "RTCRtpTransceiver::" #name " (Canonical)") 205 206 RTCRtpTransceiver::RTCRtpTransceiver( 207 nsPIDOMWindowInner* aWindow, bool aPrivacyNeeded, PeerConnectionImpl* aPc, 208 MediaTransportHandler* aTransportHandler, JsepSession* aJsepSession, 209 const std::string& aTransceiverId, bool aIsVideo, 210 nsISerialEventTarget* aStsThread, dom::MediaStreamTrack* aSendTrack, 211 WebrtcCallWrapper* aCallWrapper, RTCStatsIdGenerator* aIdGenerator) 212 : mWindow(aWindow), 213 mPc(aPc), 214 mTransportHandler(aTransportHandler), 215 mTransceiverId(aTransceiverId), 216 mJsepTransceiver(*aJsepSession->GetTransceiver(mTransceiverId)), 217 mStsThread(aStsThread), 218 mCallWrapper(aCallWrapper), 219 mSendTrack(aSendTrack), 220 mIdGenerator(aIdGenerator), 221 mPrincipalPrivacy(aPrivacyNeeded ? PrincipalPrivacy::Private 222 : PrincipalPrivacy::NonPrivate), 223 mIsVideo(aIsVideo), 224 INIT_CANONICAL(mMid, std::string()), 225 INIT_CANONICAL(mSyncGroup, std::string()) {} 226 227 #undef INIT_CANONICAL 228 229 RTCRtpTransceiver::~RTCRtpTransceiver() = default; 230 231 SdpDirectionAttribute::Direction ToSdpDirection( 232 RTCRtpTransceiverDirection aDirection) { 233 switch (aDirection) { 234 case dom::RTCRtpTransceiverDirection::Sendrecv: 235 return SdpDirectionAttribute::Direction::kSendrecv; 236 case dom::RTCRtpTransceiverDirection::Sendonly: 237 return SdpDirectionAttribute::Direction::kSendonly; 238 case dom::RTCRtpTransceiverDirection::Recvonly: 239 return SdpDirectionAttribute::Direction::kRecvonly; 240 case dom::RTCRtpTransceiverDirection::Inactive: 241 case dom::RTCRtpTransceiverDirection::Stopped: 242 return SdpDirectionAttribute::Direction::kInactive; 243 } 244 MOZ_CRASH("Invalid transceiver direction!"); 245 } 246 247 static uint32_t sRemoteSourceId = 0; 248 249 // TODO(bug 1401592): Once we implement the sendEncodings stuff, there will 250 // need to be validation code in here. 251 void RTCRtpTransceiver::Init(const RTCRtpTransceiverInit& aInit, 252 ErrorResult& aRv) { 253 TrackingId trackingId(TrackingId::Source::RTCRtpReceiver, sRemoteSourceId++, 254 TrackingId::TrackAcrossProcesses::Yes); 255 if (IsVideo()) { 256 InitVideo(trackingId); 257 } else { 258 InitAudio(); 259 } 260 261 if (!IsValid()) { 262 aRv = NS_ERROR_UNEXPECTED; 263 return; 264 } 265 266 mReceiver = new RTCRtpReceiver(mWindow, mPrincipalPrivacy, mPc, 267 mTransportHandler, mCallWrapper->mCallThread, 268 mStsThread, mConduit, this, trackingId); 269 270 mSender = new RTCRtpSender(mWindow, mPc, mTransportHandler, 271 mCallWrapper->mCallThread, mStsThread, mConduit, 272 mSendTrack, aInit.mSendEncodings, this); 273 274 if (mConduit) { 275 InitConduitControl(); 276 } 277 278 mSender->SetStreamsImpl(aInit.mStreams); 279 mDirection = aInit.mDirection; 280 } 281 282 void RTCRtpTransceiver::SetDtlsTransport( 283 dom::RTCDtlsTransport* aDtlsTransport) { 284 mDtlsTransport = aDtlsTransport; 285 } 286 287 void RTCRtpTransceiver::SaveStateForRollback() { 288 mLastStableDtlsTransport = mDtlsTransport; 289 } 290 291 void RTCRtpTransceiver::RollbackToStableDtlsTransport() { 292 mDtlsTransport = mLastStableDtlsTransport; 293 } 294 295 void RTCRtpTransceiver::InitAudio() { 296 mConduit = AudioSessionConduit::Create(mCallWrapper, mStsThread); 297 298 if (!mConduit) { 299 MOZ_MTLOG(ML_ERROR, mPc->GetHandle() 300 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 301 << ": Failed to create AudioSessionConduit"); 302 // TODO(bug 1422897): We need a way to record this when it happens in the 303 // wild. 304 } 305 } 306 307 void RTCRtpTransceiver::InitVideo(const TrackingId& aRecvTrackingId) { 308 VideoSessionConduit::Options options; 309 options.mVideoLatencyTestEnable = 310 Preferences::GetBool("media.video.test_latency", false); 311 options.mMinBitrate = std::max( 312 0, 313 Preferences::GetInt("media.peerconnection.video.min_bitrate", 0) * 1000); 314 options.mStartBitrate = std::max( 315 0, Preferences::GetInt("media.peerconnection.video.start_bitrate", 0) * 316 1000); 317 options.mPrefMaxBitrate = std::max( 318 0, 319 Preferences::GetInt("media.peerconnection.video.max_bitrate", 0) * 1000); 320 if (options.mMinBitrate != 0 && 321 options.mMinBitrate < kViEMinCodecBitrate_bps) { 322 options.mMinBitrate = kViEMinCodecBitrate_bps; 323 } 324 if (options.mStartBitrate < options.mMinBitrate) { 325 options.mStartBitrate = options.mMinBitrate; 326 } 327 if (options.mPrefMaxBitrate && 328 options.mStartBitrate > options.mPrefMaxBitrate) { 329 options.mStartBitrate = options.mPrefMaxBitrate; 330 } 331 // XXX We'd love if this was a live param for testing adaptation/etc 332 // in automation 333 options.mMinBitrateEstimate = 334 std::max(0, Preferences::GetInt( 335 "media.peerconnection.video.min_bitrate_estimate", 0) * 336 1000); 337 options.mSpatialLayers = std::max( 338 1, Preferences::GetInt("media.peerconnection.video.svc.spatial", 0)); 339 options.mTemporalLayers = std::max( 340 1, Preferences::GetInt("media.peerconnection.video.svc.temporal", 0)); 341 options.mDenoising = 342 Preferences::GetBool("media.peerconnection.video.denoising", false); 343 options.mLockScaling = 344 Preferences::GetBool("media.peerconnection.video.lock_scaling", false); 345 346 mConduit = 347 VideoSessionConduit::Create(mCallWrapper, mStsThread, std::move(options), 348 mPc->GetHandle(), aRecvTrackingId); 349 350 if (!mConduit) { 351 MOZ_MTLOG(ML_ERROR, mPc->GetHandle() 352 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 353 << ": Failed to create VideoSessionConduit"); 354 // TODO(bug 1422897): We need a way to record this when it happens in the 355 // wild. 356 } 357 } 358 359 void RTCRtpTransceiver::InitConduitControl() { 360 MOZ_ASSERT(NS_IsMainThread()); 361 MOZ_ASSERT(mConduit); 362 ConduitControlState control(this, mSender, mReceiver); 363 mConduit->AsVideoSessionConduit().apply( 364 [&](auto aConduit) { aConduit->InitControl(&control); }); 365 mConduit->AsAudioSessionConduit().apply( 366 [&](auto aConduit) { aConduit->InitControl(&control); }); 367 } 368 369 void RTCRtpTransceiver::Close() { 370 // Called via PCImpl::Close 371 // Satisfies steps 7 and 9 of 372 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-close 373 // No events are fired for this. 374 mShutdown = true; 375 if (mDtlsTransport) { 376 mDtlsTransport->UpdateStateNoEvent(TransportLayer::TS_CLOSED); 377 // Might not be set if we're cycle-collecting 378 if (mDtlsTransport->IceTransport()) { 379 mDtlsTransport->IceTransport()->SetState(RTCIceTransportState::Closed); 380 } 381 } 382 StopImpl(); 383 } 384 385 void RTCRtpTransceiver::BreakCycles() { 386 mSender->BreakCycles(); 387 mReceiver->BreakCycles(); 388 mWindow = nullptr; 389 mSendTrack = nullptr; 390 mSender = nullptr; 391 mReceiver = nullptr; 392 mDtlsTransport = nullptr; 393 mLastStableDtlsTransport = nullptr; 394 mPc = nullptr; 395 } 396 397 void RTCRtpTransceiver::Unlink() { 398 if (mHandlingUnlink) { 399 BreakCycles(); 400 mHandlingUnlink = false; 401 } else if (mPc) { 402 mPc->Close(); 403 mPc->BreakCycles(); 404 } 405 } 406 407 // TODO: Only called from one place in PeerConnectionImpl, synchronously, when 408 // the JSEP engine has successfully completed an offer/answer exchange. This is 409 // a bit squirrely, since identity validation happens asynchronously in 410 // PeerConnection.sys.mjs. This probably needs to happen once all the "in 411 // parallel" steps have succeeded, but before we queue the task for JS 412 // observable state updates. 413 nsresult RTCRtpTransceiver::UpdateTransport() { 414 if (!mHasTransport) { 415 return NS_OK; 416 } 417 418 mReceiver->UpdateTransport(); 419 mSender->UpdateTransport(); 420 return NS_OK; 421 } 422 423 nsresult RTCRtpTransceiver::UpdateConduit() { 424 if (mStopped) { 425 return NS_OK; 426 } 427 428 mReceiver->UpdateConduit(); 429 mSender->MaybeUpdateConduit(); 430 431 return NS_OK; 432 } 433 434 void RTCRtpTransceiver::UpdatePrincipalPrivacy(PrincipalPrivacy aPrivacy) { 435 if (mPrincipalPrivacy == aPrivacy) { 436 return; 437 } 438 439 mPrincipalPrivacy = aPrivacy; 440 mReceiver->UpdatePrincipalPrivacy(mPrincipalPrivacy); 441 } 442 443 void RTCRtpTransceiver::ResetSync() { mSyncGroup = std::string(); } 444 445 // TODO: Only called from one place in PeerConnectionImpl, synchronously, when 446 // the JSEP engine has successfully completed an offer/answer exchange. This is 447 // a bit squirrely, since identity validation happens asynchronously in 448 // PeerConnection.sys.mjs. This probably needs to happen once all the "in 449 // parallel" steps have succeeded, but before we queue the task for JS 450 // observable state updates. 451 nsresult RTCRtpTransceiver::SyncWithMatchingVideoConduits( 452 nsTArray<RefPtr<RTCRtpTransceiver>>& transceivers) { 453 if (mStopped) { 454 return NS_OK; 455 } 456 457 if (IsVideo()) { 458 MOZ_MTLOG(ML_ERROR, mPc->GetHandle() 459 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 460 << " called when transceiver is not " 461 "video! This should never happen."); 462 MOZ_CRASH(); 463 return NS_ERROR_UNEXPECTED; 464 } 465 466 std::set<std::string> myReceiveStreamIds; 467 myReceiveStreamIds.insert(mReceiver->GetStreamIds().begin(), 468 mReceiver->GetStreamIds().end()); 469 470 for (RefPtr<RTCRtpTransceiver>& transceiver : transceivers) { 471 if (!transceiver->IsValid()) { 472 continue; 473 } 474 475 if (!transceiver->IsVideo()) { 476 // |this| is an audio transceiver, so we skip other audio transceivers 477 continue; 478 } 479 480 // Maybe could make this more efficient by cacheing this set, but probably 481 // not worth it. 482 for (const std::string& streamId : 483 transceiver->Receiver()->GetStreamIds()) { 484 if (myReceiveStreamIds.count(streamId)) { 485 // Ok, we have one video, one non-video - cross the streams! 486 mSyncGroup = streamId; 487 transceiver->mSyncGroup = streamId; 488 489 MOZ_MTLOG(ML_DEBUG, mPc->GetHandle() 490 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 491 << " Syncing " << mConduit.get() << " to " 492 << transceiver->mConduit.get()); 493 494 // The sync code in call.cc only permits sync between audio stream and 495 // one video stream. They take the first match, so there's no point in 496 // continuing here. If we want to change the default, we should sort 497 // video streams here and only call SetSyncGroup on the chosen stream. 498 break; 499 } 500 } 501 } 502 503 return NS_OK; 504 } 505 506 bool RTCRtpTransceiver::ConduitHasPluginID(uint64_t aPluginID) { 507 return mConduit && mConduit->HasCodecPluginID(aPluginID); 508 } 509 510 void RTCRtpTransceiver::SyncFromJsep(const JsepSession& aSession) { 511 MOZ_MTLOG(ML_DEBUG, mPc->GetHandle() 512 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 513 << " Syncing from JSEP transceiver"); 514 if (mShutdown) { 515 // Shutdown_m has already been called, probably due to pc.close(). Just 516 // nod and smile. 517 return; 518 } 519 520 mJsepTransceiver = *aSession.GetTransceiver(mTransceiverId); 521 522 // Transceivers can stop due to sRD, so we need to check that 523 if (!mStopped && mJsepTransceiver.IsStopped()) { 524 MOZ_MTLOG(ML_DEBUG, mPc->GetHandle() 525 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 526 << " JSEP transceiver is stopped"); 527 StopImpl(); 528 } 529 530 mReceiver->SyncFromJsep(mJsepTransceiver); 531 mSender->SyncFromJsep(mJsepTransceiver); 532 533 // mid from JSEP 534 if (mJsepTransceiver.IsAssociated()) { 535 mMid = mJsepTransceiver.GetMid(); 536 } else { 537 mMid = std::string(); 538 } 539 540 // currentDirection from JSEP, but not if "this transceiver has never been 541 // represented in an offer/answer exchange" 542 if (mJsepTransceiver.HasLevel() && mJsepTransceiver.IsNegotiated()) { 543 if (mJsepTransceiver.mRecvTrack.GetActive()) { 544 if (mJsepTransceiver.mSendTrack.GetActive()) { 545 mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Sendrecv); 546 mHasBeenUsedToSend = true; 547 } else { 548 mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Recvonly); 549 } 550 } else { 551 if (mJsepTransceiver.mSendTrack.GetActive()) { 552 mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Sendonly); 553 mHasBeenUsedToSend = true; 554 } else { 555 mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Inactive); 556 } 557 } 558 } 559 560 mShouldRemove = mJsepTransceiver.IsRemoved(); 561 mHasTransport = !mStopped && mJsepTransceiver.mTransport.mComponents; 562 } 563 564 void RTCRtpTransceiver::SyncToJsep(JsepSession& aSession) const { 565 MOZ_MTLOG(ML_DEBUG, mPc->GetHandle() 566 << "[" << mMid.Ref() << "]: " << __FUNCTION__ 567 << " Syncing to JSEP transceiver"); 568 569 aSession.ApplyToTransceiver( 570 mTransceiverId, [this, self = RefPtr<const RTCRtpTransceiver>(this)]( 571 JsepTransceiver& aTransceiver) { 572 mReceiver->SyncToJsep(aTransceiver); 573 mSender->SyncToJsep(aTransceiver); 574 aTransceiver.mJsDirection = ToSdpDirection(mDirection); 575 if (mStopping || mStopped) { 576 aTransceiver.Stop(); 577 } 578 }); 579 } 580 581 void RTCRtpTransceiver::GetKind(nsAString& aKind) const { 582 // The transceiver kind of an RTCRtpTransceiver is defined by the kind of the 583 // associated RTCRtpReceiver's MediaStreamTrack object. 584 MOZ_ASSERT(mReceiver && mReceiver->Track()); 585 mReceiver->Track()->GetKind(aKind); 586 } 587 588 void RTCRtpTransceiver::GetMid(nsAString& aMid) const { 589 if (!mMid.Ref().empty()) { 590 aMid = NS_ConvertUTF8toUTF16(mMid.Ref()); 591 } else { 592 aMid.SetIsVoid(true); 593 } 594 } 595 596 std::string RTCRtpTransceiver::GetMidAscii() const { 597 if (mMid.Ref().empty()) { 598 return std::string(); 599 } 600 601 return mMid.Ref(); 602 } 603 604 void RTCRtpTransceiver::SetDirection(RTCRtpTransceiverDirection aDirection, 605 ErrorResult& aRv) { 606 // If transceiver.[[Stopping]] is true, throw an InvalidStateError. 607 if (mStopping) { 608 aRv.ThrowInvalidStateError("Transceiver is stopping/stopped!"); 609 return; 610 } 611 612 // If newDirection is equal to transceiver.[[Direction]], abort these steps. 613 if (aDirection == mDirection) { 614 return; 615 } 616 617 // If newDirection is equal to "stopped", throw a TypeError. 618 if (aDirection == RTCRtpTransceiverDirection::Stopped) { 619 aRv.ThrowTypeError("Cannot use \"stopped\" in setDirection!"); 620 return; 621 } 622 623 // Set transceiver.[[Direction]] to newDirection. 624 SetDirectionInternal(aDirection); 625 626 // Update the negotiation-needed flag for connection. 627 mPc->UpdateNegotiationNeeded(); 628 } 629 630 void RTCRtpTransceiver::SetDirectionInternal( 631 RTCRtpTransceiverDirection aDirection) { 632 // We do not update the direction on the JsepTransceiver until sync 633 mDirection = aDirection; 634 } 635 636 bool RTCRtpTransceiver::ShouldRemove() const { return mShouldRemove; } 637 638 bool RTCRtpTransceiver::CanSendDTMF() const { 639 // Spec says: "If connection's RTCPeerConnectionState is not "connected" 640 // return false." We don't support that right now. This is supposed to be 641 // true once ICE is complete, and _all_ DTLS handshakes are also complete. We 642 // don't really have access to the state of _all_ of our DTLS states either. 643 // Our pipeline _does_ know whether SRTP/SRTCP is ready, which happens 644 // immediately after our transport finishes DTLS (unless there was an error), 645 // so this is pretty close. 646 // TODO (bug 1265827): Base this on RTCPeerConnectionState instead. 647 // TODO (bug 1623193): Tighten this up 648 if (!IsSending() || !mSender->GetTrack() || Stopping()) { 649 return false; 650 } 651 652 // Ok, it looks like the connection is up and sending. Did we negotiate 653 // telephone-event? 654 const JsepTrackNegotiatedDetails* details = 655 mJsepTransceiver.mSendTrack.GetNegotiatedDetails(); 656 if (NS_WARN_IF(!details || !details->GetEncodingCount())) { 657 // What? 658 return false; 659 } 660 661 for (size_t i = 0; i < details->GetEncodingCount(); ++i) { 662 const auto& encoding = details->GetEncoding(i); 663 for (const auto& codec : encoding.GetCodecs()) { 664 if (codec->mName == "telephone-event") { 665 return true; 666 } 667 } 668 } 669 670 return false; 671 } 672 673 JSObject* RTCRtpTransceiver::WrapObject(JSContext* aCx, 674 JS::Handle<JSObject*> aGivenProto) { 675 return dom::RTCRtpTransceiver_Binding::Wrap(aCx, this, aGivenProto); 676 } 677 678 nsPIDOMWindowInner* RTCRtpTransceiver::GetParentObject() const { 679 return mWindow; 680 } 681 682 static void JsepCodecDescToAudioCodecConfig( 683 const JsepAudioCodecDescription& aCodec, Maybe<AudioCodecConfig>* aConfig) { 684 uint16_t pt; 685 686 // TODO(bug 1761272): Getting the pt for a JsepAudioCodecDescription should be 687 // infallible. 688 if (NS_WARN_IF(!aCodec.GetPtAsInt(&pt))) { 689 MOZ_MTLOG(ML_ERROR, "Invalid payload type: " << aCodec.mDefaultPt); 690 MOZ_ASSERT(false); 691 return; 692 } 693 694 // libwebrtc crashes if we attempt to configure a mono recv codec 695 bool sendMono = aCodec.mForceMono && aCodec.mDirection == sdp::kSend; 696 697 *aConfig = Some(AudioCodecConfig( 698 pt, aCodec.mName, static_cast<int>(aCodec.mClock), 699 sendMono ? 1 : static_cast<int>(aCodec.mChannels), aCodec.mFECEnabled)); 700 (*aConfig)->mMaxPlaybackRate = static_cast<int>(aCodec.mMaxPlaybackRate); 701 (*aConfig)->mDtmfEnabled = aCodec.mDtmfEnabled; 702 (*aConfig)->mDTXEnabled = aCodec.mDTXEnabled; 703 (*aConfig)->mMaxAverageBitrate = aCodec.mMaxAverageBitrate; 704 (*aConfig)->mFrameSizeMs = aCodec.mFrameSizeMs; 705 (*aConfig)->mMinFrameSizeMs = aCodec.mMinFrameSizeMs; 706 (*aConfig)->mMaxFrameSizeMs = aCodec.mMaxFrameSizeMs; 707 (*aConfig)->mCbrEnabled = aCodec.mCbrEnabled; 708 } 709 710 // TODO: This and the next function probably should move to JsepTransceiver 711 Maybe<const std::vector<UniquePtr<JsepCodecDescription>>&> 712 RTCRtpTransceiver::GetNegotiatedSendCodecs() const { 713 if (!mJsepTransceiver.mSendTrack.GetActive()) { 714 return Nothing(); 715 } 716 717 const auto* details = mJsepTransceiver.mSendTrack.GetNegotiatedDetails(); 718 if (!details) { 719 return Nothing(); 720 } 721 722 if (details->GetEncodingCount() == 0) { 723 return Nothing(); 724 } 725 726 return SomeRef(details->GetEncoding(0).GetCodecs()); 727 } 728 729 Maybe<const std::vector<UniquePtr<JsepCodecDescription>>&> 730 RTCRtpTransceiver::GetNegotiatedRecvCodecs() const { 731 if (!mJsepTransceiver.mRecvTrack.GetActive()) { 732 return Nothing(); 733 } 734 735 const auto* details = mJsepTransceiver.mRecvTrack.GetNegotiatedDetails(); 736 if (!details) { 737 return Nothing(); 738 } 739 740 if (details->GetEncodingCount() == 0) { 741 return Nothing(); 742 } 743 744 return SomeRef(details->GetEncoding(0).GetCodecs()); 745 } 746 747 // TODO: Maybe move this someplace else? 748 /*static*/ 749 void RTCRtpTransceiver::NegotiatedDetailsToAudioCodecConfigs( 750 const JsepTrackNegotiatedDetails& aDetails, 751 std::vector<AudioCodecConfig>* aConfigs) { 752 Maybe<AudioCodecConfig> telephoneEvent; 753 754 const std::decay_t<decltype(aDetails.GetEncoding(0).GetCodecs())> empty; 755 const auto& codecs = 756 aDetails.GetEncodingCount() ? aDetails.GetEncoding(0).GetCodecs() : empty; 757 for (const auto& codec : codecs) { 758 if (NS_WARN_IF(codec->Type() != SdpMediaSection::kAudio)) { 759 MOZ_ASSERT(false, "Codec is not audio! This is a JSEP bug."); 760 return; 761 } 762 Maybe<AudioCodecConfig> config; 763 const JsepAudioCodecDescription& audio = 764 static_cast<const JsepAudioCodecDescription&>(*codec); 765 JsepCodecDescToAudioCodecConfig(audio, &config); 766 if (config->mName == "telephone-event") { 767 telephoneEvent = std::move(config); 768 } else { 769 aConfigs->push_back(std::move(*config)); 770 } 771 } 772 773 // Put telephone event at the back, because webrtc.org crashes if we don't 774 // If we need to do even more sorting, we should use std::sort. 775 if (telephoneEvent) { 776 aConfigs->push_back(std::move(*telephoneEvent)); 777 } 778 } 779 780 auto RTCRtpTransceiver::GetActivePayloadTypes() const 781 -> RefPtr<ActivePayloadTypesPromise> { 782 if (!mConduit) { 783 return ActivePayloadTypesPromise::CreateAndResolve(PayloadTypes(), 784 __func__); 785 } 786 787 if (!mCallWrapper) { 788 return ActivePayloadTypesPromise::CreateAndResolve(PayloadTypes(), 789 __func__); 790 } 791 792 return InvokeAsync(mCallWrapper->mCallThread, __func__, 793 [conduit = mConduit]() { 794 PayloadTypes pts; 795 pts.mSendPayloadType = conduit->ActiveSendPayloadType(); 796 pts.mRecvPayloadType = conduit->ActiveRecvPayloadType(); 797 return ActivePayloadTypesPromise::CreateAndResolve( 798 std::move(pts), __func__); 799 }); 800 } 801 802 static auto JsepCodecDescToVideoCodecConfig( 803 const JsepVideoCodecDescription& aCodec) -> Maybe<VideoCodecConfig> { 804 uint16_t pt; 805 806 Maybe<VideoCodecConfig> config; 807 // TODO(bug 1761272): Getting the pt for a JsepVideoCodecDescription should be 808 // infallible. 809 // Bug 1920249, yes please. 810 if (NS_WARN_IF(!aCodec.GetPtAsInt(&pt))) { 811 MOZ_MTLOG(ML_ERROR, "Invalid payload type: " << aCodec.mDefaultPt); 812 MOZ_ASSERT(false); 813 return Nothing(); 814 } 815 816 UniquePtr<VideoCodecConfigH264> h264Config; 817 818 if (aCodec.mName == "H264") { 819 h264Config = MakeUnique<VideoCodecConfigH264>(); 820 size_t spropSize = sizeof(h264Config->sprop_parameter_sets); 821 strncpy(h264Config->sprop_parameter_sets, 822 aCodec.mSpropParameterSets.c_str(), spropSize); 823 h264Config->sprop_parameter_sets[spropSize - 1] = '\0'; 824 h264Config->packetization_mode = 825 static_cast<int>(aCodec.mPacketizationMode); 826 h264Config->profile_level_id = static_cast<int>(aCodec.mProfileLevelId); 827 h264Config->tias_bw = 0; // TODO(bug 1403206) 828 config = Some(VideoCodecConfig::CreateH264Config(pt, aCodec.mConstraints, 829 *h264Config)); 830 } 831 832 if (aCodec.mName == "AV1") { 833 config = Some(VideoCodecConfig::CreateAv1Config(pt, aCodec.mConstraints, 834 aCodec.mAv1Config)); 835 } 836 837 if (config.isNothing()) { 838 // TODO bug 1920249, let's just pass in the JsepCodecConfig 839 config = Some(VideoCodecConfig(pt, aCodec.mName, aCodec.mConstraints)); 840 } 841 842 config->mAckFbTypes = aCodec.mAckFbTypes; 843 config->mNackFbTypes = aCodec.mNackFbTypes; 844 config->mCcmFbTypes = aCodec.mCcmFbTypes; 845 config->mRembFbSet = aCodec.RtcpFbRembIsSet(); 846 config->mFECFbSet = aCodec.mFECEnabled; 847 config->mTransportCCFbSet = aCodec.RtcpFbTransportCCIsSet(); 848 if (aCodec.mFECEnabled) { 849 uint16_t pt; 850 if (SdpHelper::GetPtAsInt(aCodec.mREDPayloadType, &pt)) { 851 config->mREDPayloadType = pt; 852 } 853 if (SdpHelper::GetPtAsInt(aCodec.mULPFECPayloadType, &pt)) { 854 config->mULPFECPayloadType = pt; 855 } 856 if (SdpHelper::GetPtAsInt(aCodec.mREDRTXPayloadType, &pt)) { 857 config->mREDRTXPayloadType = pt; 858 } 859 } 860 if (aCodec.mRtxEnabled) { 861 uint16_t pt; 862 if (SdpHelper::GetPtAsInt(aCodec.mRtxPayloadType, &pt)) { 863 config->mRTXPayloadType = pt; 864 } 865 } 866 return config; 867 } 868 869 // TODO: Maybe move this someplace else? 870 /*static*/ 871 void RTCRtpTransceiver::NegotiatedDetailsToVideoCodecConfigs( 872 const JsepTrackNegotiatedDetails& aDetails, 873 std::vector<VideoCodecConfig>* aConfigs) { 874 const std::decay_t<decltype(aDetails.GetEncoding(0).GetCodecs())> empty; 875 const auto& codecs = 876 aDetails.GetEncodingCount() ? aDetails.GetEncoding(0).GetCodecs() : empty; 877 for (const auto& codec : codecs) { 878 if (NS_WARN_IF(codec->Type() != SdpMediaSection::kVideo)) { 879 MOZ_ASSERT(false, "Codec is not video! This is a JSEP bug."); 880 return; 881 } 882 const JsepVideoCodecDescription& video = 883 static_cast<const JsepVideoCodecDescription&>(*codec); 884 885 JsepCodecDescToVideoCodecConfig(video).apply([&](VideoCodecConfig config) { 886 config.mTias = aDetails.GetTias(); 887 for (size_t i = 0; i < aDetails.GetEncodingCount(); ++i) { 888 const JsepTrackEncoding& jsepEncoding(aDetails.GetEncoding(i)); 889 if (jsepEncoding.HasFormat(video.mDefaultPt)) { 890 VideoCodecConfig::Encoding encoding; 891 encoding.rid = jsepEncoding.mRid; 892 config.mEncodings.push_back(encoding); 893 } 894 } 895 896 aConfigs->push_back(std::move(config)); 897 }); 898 } 899 } 900 901 /* static */ 902 void RTCRtpTransceiver::ToDomRtpCodec(const JsepCodecDescription& aCodec, 903 RTCRtpCodec* aDomCodec) { 904 MOZ_ASSERT(aCodec.Type() == SdpMediaSection::kAudio || 905 aCodec.Type() == SdpMediaSection::kVideo); 906 if (aCodec.Type() == SdpMediaSection::kAudio) { 907 aDomCodec->mChannels.Construct(aCodec.mChannels); 908 } 909 aDomCodec->mClockRate = aCodec.mClock; 910 std::string mimeType = 911 aCodec.Type() == SdpMediaSection::kAudio ? "audio/" : "video/"; 912 mimeType += aCodec.mName; 913 aDomCodec->mMimeType = NS_ConvertASCIItoUTF16(mimeType); 914 915 if (aCodec.mSdpFmtpLine) { 916 // The RTCRtpParameters.codecs case; just use what we parsed out of the SDP 917 if (!aCodec.mSdpFmtpLine->empty()) { 918 aDomCodec->mSdpFmtpLine.Construct( 919 NS_ConvertASCIItoUTF16(aCodec.mSdpFmtpLine->c_str())); 920 } 921 } else { 922 // The getCapabilities case; serialize what we would put in an offer. 923 UniquePtr<SdpFmtpAttributeList::Parameters> params; 924 aCodec.ApplyConfigToFmtp(params); 925 926 if (params != nullptr) { 927 std::ostringstream paramsString; 928 params->Serialize(paramsString); 929 if (!paramsString.str().empty()) { 930 nsTString<char16_t> fmtp; 931 fmtp.AssignASCII(paramsString.str()); 932 aDomCodec->mSdpFmtpLine.Construct(fmtp); 933 } 934 } 935 } 936 } 937 938 /* static */ 939 void RTCRtpTransceiver::ToDomRtpCodecParameters( 940 const JsepCodecDescription& aCodec, 941 RTCRtpCodecParameters* aDomCodecParameters) { 942 ToDomRtpCodec(aCodec, aDomCodecParameters); 943 uint16_t pt; 944 if (SdpHelper::GetPtAsInt(aCodec.mDefaultPt, &pt)) { 945 aDomCodecParameters->mPayloadType = pt; 946 } 947 } 948 949 /* static */ 950 void RTCRtpTransceiver::ToDomRtpCodecRtx( 951 const JsepVideoCodecDescription& aCodec, RTCRtpCodec* aDomCodec) { 952 MOZ_ASSERT(aCodec.Type() == SdpMediaSection::kVideo); 953 aDomCodec->mClockRate = aCodec.mClock; 954 aDomCodec->mMimeType = NS_ConvertASCIItoUTF16("video/rtx"); 955 std::ostringstream apt; 956 apt << "apt=" << aCodec.mDefaultPt; 957 aDomCodec->mSdpFmtpLine.Construct(NS_ConvertASCIItoUTF16(apt.str().c_str())); 958 } 959 960 /* static */ 961 void RTCRtpTransceiver::ToDomRtpCodecParametersRtx( 962 const JsepVideoCodecDescription& aCodec, 963 RTCRtpCodecParameters* aDomCodecParameters) { 964 ToDomRtpCodecRtx(aCodec, aDomCodecParameters); 965 uint16_t pt; 966 if (SdpHelper::GetPtAsInt(aCodec.mRtxPayloadType, &pt)) { 967 aDomCodecParameters->mPayloadType = pt; 968 } 969 } 970 971 void RTCRtpTransceiver::Stop(ErrorResult& aRv) { 972 if (mPc->IsClosed()) { 973 aRv.ThrowInvalidStateError("Peer connection is closed"); 974 return; 975 } 976 977 if (mStopping) { 978 return; 979 } 980 981 StopTransceiving(); 982 mPc->UpdateNegotiationNeeded(); 983 } 984 985 void RTCRtpTransceiver::SetCodecPreferences( 986 const nsTArray<RTCRtpCodec>& aCodecs, ErrorResult& aRv) { 987 nsTArray<RTCRtpCodec> aCodecsFiltered; 988 OverrideRtxPreference rtxOverride = 989 OverrideRtxPreference::OverrideWithDisabled; 990 ; 991 bool useableCodecs = false; 992 993 // kind = transciever's kind. 994 nsAutoString kind; 995 GetKind(kind); 996 997 if (!aCodecs.IsEmpty()) { 998 struct { 999 bool Equals(const RTCRtpCodec& aA, const RTCRtpCodec& aB) const { 1000 return ((aA.mMimeType.Equals(aB.mMimeType, 1001 nsCaseInsensitiveStringComparator)) && 1002 (aA.mClockRate == aB.mClockRate) && 1003 (aA.mChannels == aB.mChannels) && 1004 (aA.mSdpFmtpLine == aB.mSdpFmtpLine)); 1005 } 1006 } CodecComparator; 1007 1008 // Remove any duplicate values in codecs, ensuring that the first occurrence 1009 // of each value remains in place. 1010 for (const auto& codec : aCodecs) { 1011 if (!std::any_of(aCodecsFiltered.begin(), aCodecsFiltered.end(), 1012 [&codec, CodecComparator](RTCRtpCodec& alreadyInserted) { 1013 return CodecComparator.Equals(alreadyInserted, codec); 1014 })) { 1015 aCodecsFiltered.AppendElement(codec); 1016 } 1017 1018 // Ensure a usable codec was supplied and if RTX is still preferred. 1019 if (!useableCodecs && !codec.mMimeType.EqualsLiteral("video/ulpfec") && 1020 !codec.mMimeType.EqualsLiteral("video/red") && 1021 !codec.mMimeType.EqualsLiteral("video/rtx")) { 1022 useableCodecs = true; 1023 } 1024 if (codec.mMimeType.EqualsLiteral("video/rtx")) { 1025 rtxOverride = OverrideRtxPreference::OverrideWithEnabled; 1026 } 1027 } 1028 1029 // If codecs are not in the codecCapabilities of receiver capabilities 1030 // throw InvalidModificationError 1031 dom::Nullable<dom::RTCRtpCapabilities> codecCapabilities; 1032 PeerConnectionImpl::GetCapabilities(kind, codecCapabilities, 1033 sdp::Direction::kRecv); 1034 1035 for (const auto& codec : aCodecsFiltered) { 1036 if (!std::any_of(codecCapabilities.Value().mCodecs.begin(), 1037 codecCapabilities.Value().mCodecs.end(), 1038 [&codec, CodecComparator](RTCRtpCodec& recvCap) { 1039 return CodecComparator.Equals(codec, recvCap); 1040 })) { 1041 aRv.ThrowInvalidModificationError( 1042 nsPrintfCString("Codec %s not in capabilities", 1043 NS_ConvertUTF16toUTF8(codec.mMimeType).get())); 1044 return; 1045 } 1046 } 1047 1048 // If only RTX, RED, or FEC codecs throw InvalidModificationError 1049 if (!useableCodecs) { 1050 aRv.ThrowInvalidModificationError("No useable codecs supplied"); 1051 return; 1052 } 1053 } 1054 // If we passed an empty list, we should restore the default list, including 1055 // RTX 1056 1057 mPreferredCodecs.clear(); 1058 std::vector<UniquePtr<JsepCodecDescription>> defaultCodecs; 1059 1060 if (kind.EqualsLiteral("video")) { 1061 PeerConnectionImpl::GetDefaultVideoCodecs(defaultCodecs, rtxOverride); 1062 } else if (kind.EqualsLiteral("audio")) { 1063 PeerConnectionImpl::GetDefaultAudioCodecs(defaultCodecs); 1064 } 1065 1066 if (!aCodecsFiltered.IsEmpty()) { 1067 mPreferredCodecsInUse = true; 1068 1069 std::vector<std::pair<UniquePtr<JsepCodecDescription>, std::string>> 1070 defaultCodecsAndParams; 1071 for (auto& codec : defaultCodecs) { 1072 UniquePtr<SdpFmtpAttributeList::Parameters> params; 1073 codec->ApplyConfigToFmtp(params); 1074 std::ostringstream paramsString; 1075 if (params != nullptr) { 1076 params->Serialize(paramsString); 1077 } 1078 defaultCodecsAndParams.emplace_back(std::move(codec), paramsString.str()); 1079 } 1080 1081 // Take the array of RTCRtpCodec and convert it to a vector of 1082 // JsepCodecDescription in order to pass to the receive track and populate 1083 // codecs. 1084 for (auto& inputCodec : aCodecsFiltered) { 1085 auto mimeType = NS_ConvertUTF16toUTF8(inputCodec.mMimeType); 1086 for (auto& [defaultCodec, precomputedParamsString] : 1087 defaultCodecsAndParams) { 1088 bool channelsMatch = 1089 (!inputCodec.mChannels.WasPassed() && !defaultCodec->mChannels) || 1090 (inputCodec.mChannels.WasPassed() && 1091 (inputCodec.mChannels.Value() == defaultCodec->mChannels)); 1092 bool sdpFmtpLinesMatch = 1093 (precomputedParamsString.empty() && 1094 !inputCodec.mSdpFmtpLine.WasPassed()) || 1095 ((!precomputedParamsString.empty() && 1096 inputCodec.mSdpFmtpLine.WasPassed()) && 1097 NS_ConvertUTF16toUTF8(inputCodec.mSdpFmtpLine.Value()) 1098 .EqualsASCII(precomputedParamsString.c_str())); 1099 1100 if ((mimeType.Find(defaultCodec->mName) != kNotFound) && 1101 (inputCodec.mClockRate == defaultCodec->mClock) && channelsMatch && 1102 sdpFmtpLinesMatch) { 1103 mPreferredCodecs.emplace_back(defaultCodec->Clone()); 1104 break; 1105 } 1106 } 1107 } 1108 } else { 1109 mPreferredCodecs.swap(defaultCodecs); 1110 mPreferredCodecsInUse = false; 1111 } 1112 } 1113 1114 void RTCRtpTransceiver::StopTransceiving() { 1115 if (mStopping) { 1116 MOZ_ASSERT(false); 1117 return; 1118 } 1119 mStopping = true; 1120 // This is the "Stop sending and receiving" algorithm from webrtc-pc 1121 mSender->Stop(); 1122 mReceiver->Stop(); 1123 mDirection = RTCRtpTransceiverDirection::Inactive; 1124 } 1125 1126 void RTCRtpTransceiver::StopImpl() { 1127 // This is the "stop the RTCRtpTransceiver" algorithm from webrtc-pc 1128 if (!mStopping) { 1129 StopTransceiving(); 1130 } 1131 1132 if (mCallWrapper) { 1133 auto conduit = std::move(mConduit); 1134 (conduit ? conduit->Shutdown() 1135 : GenericPromise::CreateAndResolve(true, __func__)) 1136 ->Then(GetMainThreadSerialEventTarget(), __func__, 1137 [sender = mSender, receiver = mReceiver]() mutable { 1138 // Shutdown pipelines when conduits are guaranteed shut down, 1139 // so that all packets sent from conduits can be delivered. 1140 sender->Shutdown(); 1141 receiver->Shutdown(); 1142 }); 1143 mCallWrapper = nullptr; 1144 } 1145 mStopped = true; 1146 mCurrentDirection.SetNull(); 1147 1148 mSender->Stop(); 1149 mReceiver->Stop(); 1150 1151 mHasTransport = false; 1152 1153 auto self = nsMainThreadPtrHandle<RTCRtpTransceiver>( 1154 new nsMainThreadPtrHolder<RTCRtpTransceiver>( 1155 "RTCRtpTransceiver::StopImpl::self", this, false)); 1156 mStsThread->Dispatch(NS_NewRunnableFunction( 1157 __func__, [self] { self->mTransportHandler = nullptr; })); 1158 } 1159 1160 bool RTCRtpTransceiver::IsVideo() const { return mIsVideo; } 1161 1162 bool RTCRtpTransceiver::IsSending() const { 1163 return mCurrentDirection == Nullable(RTCRtpTransceiverDirection::Sendonly) || 1164 mCurrentDirection == Nullable(RTCRtpTransceiverDirection::Sendrecv); 1165 } 1166 1167 bool RTCRtpTransceiver::IsReceiving() const { 1168 return mCurrentDirection == Nullable(RTCRtpTransceiverDirection::Recvonly) || 1169 mCurrentDirection == Nullable(RTCRtpTransceiverDirection::Sendrecv); 1170 } 1171 1172 void RTCRtpTransceiver::ChainToDomPromiseWithCodecStats( 1173 nsTArray<RefPtr<RTCStatsPromise>> aStats, 1174 const RefPtr<dom::Promise>& aDomPromise) { 1175 nsTArray<RTCCodecStats> codecStats = 1176 mPc->GetCodecStats(mPc->GetTimestampMaker().GetNow().ToDom()); 1177 1178 AutoTArray< 1179 std::tuple<RTCRtpTransceiver*, RefPtr<RTCStatsPromise::AllPromiseType>>, 1180 1> 1181 statsPromises; 1182 statsPromises.AppendElement(std::make_tuple( 1183 this, RTCStatsPromise::All(GetMainThreadSerialEventTarget(), aStats))); 1184 1185 ApplyCodecStats(std::move(codecStats), std::move(statsPromises)) 1186 ->Then( 1187 GetMainThreadSerialEventTarget(), __func__, 1188 [aDomPromise, window = mWindow, 1189 idGen = mIdGenerator](UniquePtr<RTCStatsCollection> aStats) mutable { 1190 // Rewrite ids and merge stats collections into the final report. 1191 AutoTArray<UniquePtr<RTCStatsCollection>, 1> stats; 1192 stats.AppendElement(std::move(aStats)); 1193 1194 RTCStatsCollection opaqueStats; 1195 idGen->RewriteIds(std::move(stats), &opaqueStats); 1196 1197 RefPtr<RTCStatsReport> report(new RTCStatsReport(window)); 1198 report->Incorporate(opaqueStats); 1199 1200 aDomPromise->MaybeResolve(std::move(report)); 1201 }, 1202 [aDomPromise](nsresult aError) { 1203 aDomPromise->MaybeReject(NS_ERROR_FAILURE); 1204 }); 1205 } 1206 1207 RefPtr<RTCStatsPromise> RTCRtpTransceiver::ApplyCodecStats( 1208 nsTArray<RTCCodecStats> aCodecStats, 1209 nsTArray< 1210 std::tuple<RTCRtpTransceiver*, RefPtr<RTCStatsPromise::AllPromiseType>>> 1211 aTransceiverStatsPromises) { 1212 MOZ_ASSERT(NS_IsMainThread()); 1213 // The process here is roughly: 1214 // - Gather all inputs to the codec filtering process, including: 1215 // - Each transceiver's transportIds 1216 // - Each transceiver's active payload types (resolved) 1217 // - Each transceiver's resolved stats 1218 // 1219 // Waiting (async) for multiple promises of different types is not supported 1220 // by the MozPromise API (bug 1752318), so we are a bit finicky here. We 1221 // create media::Refcountables of the types we want to resolve, and let 1222 // these be shared across Then-functions through RefPtrs. 1223 // 1224 // - For each active payload type in a transceiver: 1225 // - Register the codec stats for this payload type and transport if we 1226 // haven't already done so 1227 // - If it was a send payload type, assign the codec stats id for this 1228 // payload type and transport to the transceiver's outbound-rtp and 1229 // remote-inbound-rtp stats as codecId 1230 // - If it was a recv payload type, assign the codec stats id for this 1231 // payload type and transport to the transceiver's inbound-rtp and 1232 // remote-outbound-rtp stats as codecId 1233 // 1234 // - Flatten all transceiver stats collections into one, and set the 1235 // registered codec stats on it 1236 1237 // Wrap codec stats in a Refcountable<> to allow sharing across promise 1238 // handlers. 1239 auto codecStats = MakeRefPtr<media::Refcountable<nsTArray<RTCCodecStats>>>(); 1240 *codecStats = std::move(aCodecStats); 1241 1242 struct IdComparator { 1243 bool operator()(const RTCCodecStats& aA, const RTCCodecStats& aB) const { 1244 return aA.mId.Value() < aB.mId.Value(); 1245 } 1246 }; 1247 1248 // Stores distinct codec stats by id; to avoid dupes within a transport. 1249 auto finalCodecStats = 1250 MakeRefPtr<media::Refcountable<std::set<RTCCodecStats, IdComparator>>>(); 1251 1252 // All the transceiver rtp stream stats in a single array. These stats will, 1253 // when resolved, contain codecIds. 1254 nsTArray<RefPtr<RTCStatsPromise>> promises( 1255 aTransceiverStatsPromises.Length()); 1256 1257 for (const auto& [transceiver, allPromise] : aTransceiverStatsPromises) { 1258 // Per transceiver, gather up what we need to assign codecId to this 1259 // transceiver's rtp stream stats. Register codec stats while we're at it. 1260 auto payloadTypes = 1261 MakeRefPtr<media::Refcountable<RTCRtpTransceiver::PayloadTypes>>(); 1262 promises.AppendElement( 1263 transceiver->GetActivePayloadTypes() 1264 ->Then( 1265 GetMainThreadSerialEventTarget(), __func__, 1266 [payloadTypes, allPromise = allPromise]( 1267 RTCRtpTransceiver::PayloadTypes aPayloadTypes) { 1268 // Forward active payload types to the next Then-handler. 1269 *payloadTypes = std::move(aPayloadTypes); 1270 return allPromise; 1271 }, 1272 [] { 1273 MOZ_CRASH("Unexpected reject"); 1274 return RTCStatsPromise::AllPromiseType::CreateAndReject( 1275 NS_ERROR_UNEXPECTED, __func__); 1276 }) 1277 ->Then( 1278 GetMainThreadSerialEventTarget(), __func__, 1279 [codecStats, finalCodecStats, payloadTypes, 1280 transportId = 1281 NS_ConvertASCIItoUTF16(transceiver->GetTransportId())]( 1282 nsTArray<UniquePtr<RTCStatsCollection>> 1283 aTransceiverStats) mutable { 1284 // We have all the data we need to register codec stats and 1285 // assign codecIds for this transceiver's rtp stream stats. 1286 1287 auto report = MakeUnique<RTCStatsCollection>(); 1288 FlattenStats(std::move(aTransceiverStats), report.get()); 1289 1290 // Find the codec stats we are looking for, based on the 1291 // transportId and the active payload types. 1292 Maybe<RTCCodecStats&> sendCodec; 1293 Maybe<RTCCodecStats&> recvCodec; 1294 for (auto& codec : *codecStats) { 1295 if (payloadTypes->mSendPayloadType.isSome() == 1296 sendCodec.isSome() && 1297 payloadTypes->mRecvPayloadType.isSome() == 1298 recvCodec.isSome()) { 1299 // We have found all the codec stats we were looking for. 1300 break; 1301 } 1302 if (codec.mTransportId != transportId) { 1303 continue; 1304 } 1305 if (payloadTypes->mSendPayloadType && 1306 *payloadTypes->mSendPayloadType == 1307 static_cast<int>(codec.mPayloadType) && 1308 (!codec.mCodecType.WasPassed() || 1309 codec.mCodecType.Value() == RTCCodecType::Encode)) { 1310 MOZ_ASSERT(!sendCodec, 1311 "At most one send codec stat per transceiver"); 1312 sendCodec = SomeRef(codec); 1313 } 1314 if (payloadTypes->mRecvPayloadType && 1315 *payloadTypes->mRecvPayloadType == 1316 static_cast<int>(codec.mPayloadType) && 1317 (!codec.mCodecType.WasPassed() || 1318 codec.mCodecType.Value() == RTCCodecType::Decode)) { 1319 MOZ_ASSERT(!recvCodec, 1320 "At most one recv codec stat per transceiver"); 1321 recvCodec = SomeRef(codec); 1322 } 1323 } 1324 1325 // Register and assign codecIds for the found codec stats. 1326 if (sendCodec) { 1327 finalCodecStats->insert(*sendCodec); 1328 for (auto& stat : report->mOutboundRtpStreamStats) { 1329 stat.mCodecId.Construct(sendCodec->mId.Value()); 1330 } 1331 for (auto& stat : report->mRemoteInboundRtpStreamStats) { 1332 stat.mCodecId.Construct(sendCodec->mId.Value()); 1333 } 1334 } 1335 if (recvCodec) { 1336 finalCodecStats->insert(*recvCodec); 1337 for (auto& stat : report->mInboundRtpStreamStats) { 1338 stat.mCodecId.Construct(recvCodec->mId.Value()); 1339 } 1340 for (auto& stat : report->mRemoteOutboundRtpStreamStats) { 1341 stat.mCodecId.Construct(recvCodec->mId.Value()); 1342 } 1343 } 1344 1345 return RTCStatsPromise::CreateAndResolve(std::move(report), 1346 __func__); 1347 }, 1348 [] { 1349 MOZ_CRASH("Unexpected reject"); 1350 return RTCStatsPromise::CreateAndReject(NS_ERROR_UNEXPECTED, 1351 __func__); 1352 })); 1353 } 1354 1355 return RTCStatsPromise::All(GetMainThreadSerialEventTarget(), promises) 1356 ->Then( 1357 GetMainThreadSerialEventTarget(), __func__, 1358 [finalCodecStats = std::move(finalCodecStats)]( 1359 nsTArray<UniquePtr<RTCStatsCollection>> aStats) mutable { 1360 auto finalStats = MakeUnique<RTCStatsCollection>(); 1361 FlattenStats(std::move(aStats), finalStats.get()); 1362 MOZ_ASSERT(finalStats->mCodecStats.IsEmpty()); 1363 if (!finalStats->mCodecStats.SetCapacity(finalCodecStats->size(), 1364 fallible)) { 1365 mozalloc_handle_oom(0); 1366 } 1367 while (!finalCodecStats->empty()) { 1368 auto node = finalCodecStats->extract(finalCodecStats->begin()); 1369 if (!finalStats->mCodecStats.AppendElement( 1370 std::move(node.value()), fallible)) { 1371 mozalloc_handle_oom(0); 1372 } 1373 } 1374 return RTCStatsPromise::CreateAndResolve(std::move(finalStats), 1375 __func__); 1376 }, 1377 [] { 1378 MOZ_CRASH("Unexpected reject"); 1379 return RTCStatsPromise::CreateAndReject(NS_ERROR_UNEXPECTED, 1380 __func__); 1381 }); 1382 } 1383 1384 } // namespace mozilla