summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/webrtc/jsapi/RTCRtpReceiver.cpp619
1 files changed, 619 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp b/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp
new file mode 100644
index 0000000000..972b81adba
--- /dev/null
+++ b/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp
@@ -0,0 +1,619 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RTCRtpReceiver.h"
+#include "transport/logging.h"
+#include "mozilla/dom/MediaStreamTrack.h"
+#include "mozilla/dom/Promise.h"
+#include "transportbridge/MediaPipeline.h"
+#include "nsPIDOMWindow.h"
+#include "PrincipalHandle.h"
+#include "nsIPrincipal.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/NullPrincipal.h"
+#include "MediaTrackGraph.h"
+#include "RemoteTrackSource.h"
+#include "libwebrtcglue/RtpRtcpConfig.h"
+#include "nsString.h"
+#include "mozilla/dom/AudioStreamTrack.h"
+#include "mozilla/dom/VideoStreamTrack.h"
+#include "MediaTransportHandler.h"
+#include "jsep/JsepTransceiver.h"
+#include "mozilla/dom/RTCRtpReceiverBinding.h"
+#include "mozilla/dom/RTCRtpSourcesBinding.h"
+#include "RTCStatsReport.h"
+#include "mozilla/Preferences.h"
+#include "TransceiverImpl.h"
+#include "libwebrtcglue/AudioConduit.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCRtpReceiver, mWindow, mTrack)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCRtpReceiver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCRtpReceiver)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCRtpReceiver)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+LazyLogModule gReceiverLog("RTCRtpReceiver");
+
+static PrincipalHandle GetPrincipalHandle(nsPIDOMWindowInner* aWindow,
+ bool aPrivacyNeeded) {
+ // Set the principal used for creating the tracks. This makes the track
+ // data (audio/video samples) accessible to the receiving page. We're
+ // only certain that privacy hasn't been requested if we're connected.
+ nsCOMPtr<nsIScriptObjectPrincipal> winPrincipal = do_QueryInterface(aWindow);
+ RefPtr<nsIPrincipal> principal = winPrincipal->GetPrincipal();
+ if (NS_WARN_IF(!principal)) {
+ principal = NullPrincipal::CreateWithoutOriginAttributes();
+ } else if (aPrivacyNeeded) {
+ principal = NullPrincipal::CreateWithInheritedAttributes(principal);
+ }
+ return MakePrincipalHandle(principal);
+}
+
+static already_AddRefed<dom::MediaStreamTrack> CreateTrack(
+ nsPIDOMWindowInner* aWindow, bool aAudio,
+ const nsCOMPtr<nsIPrincipal>& aPrincipal) {
+ MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
+ aAudio ? MediaTrackGraph::AUDIO_THREAD_DRIVER
+ : MediaTrackGraph::SYSTEM_THREAD_DRIVER,
+ aWindow, MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
+ MediaTrackGraph::DEFAULT_OUTPUT_DEVICE);
+
+ RefPtr<MediaStreamTrack> track;
+ RefPtr<RemoteTrackSource> trackSource;
+ if (aAudio) {
+ RefPtr<SourceMediaTrack> source =
+ graph->CreateSourceTrack(MediaSegment::AUDIO);
+ trackSource = new RemoteTrackSource(source, aPrincipal, u"remote audio"_ns);
+ track = new AudioStreamTrack(aWindow, source, trackSource);
+ } else {
+ RefPtr<SourceMediaTrack> source =
+ graph->CreateSourceTrack(MediaSegment::VIDEO);
+ trackSource = new RemoteTrackSource(source, aPrincipal, u"remote video"_ns);
+ track = new VideoStreamTrack(aWindow, source, trackSource);
+ }
+
+ // Spec says remote tracks start out muted.
+ trackSource->SetMuted(true);
+
+ return track.forget();
+}
+
+RTCRtpReceiver::RTCRtpReceiver(nsPIDOMWindowInner* aWindow, bool aPrivacyNeeded,
+ const std::string& aPCHandle,
+ MediaTransportHandler* aTransportHandler,
+ JsepTransceiver* aJsepTransceiver,
+ nsISerialEventTarget* aMainThread,
+ nsISerialEventTarget* aStsThread,
+ MediaSessionConduit* aConduit,
+ TransceiverImpl* aTransceiverImpl)
+ : mWindow(aWindow),
+ mPCHandle(aPCHandle),
+ mJsepTransceiver(aJsepTransceiver),
+ mMainThread(aMainThread),
+ mStsThread(aStsThread),
+ mTransportHandler(aTransportHandler),
+ mTransceiverImpl(aTransceiverImpl) {
+ PrincipalHandle principalHandle = GetPrincipalHandle(aWindow, aPrivacyNeeded);
+ mTrack = CreateTrack(aWindow, aConduit->type() == MediaSessionConduit::AUDIO,
+ principalHandle.get());
+ // Until Bug 1232234 is fixed, we'll get extra RTCP BYES during renegotiation,
+ // so we'll disable muting on RTCP BYE and timeout for now.
+ if (Preferences::GetBool("media.peerconnection.mute_on_bye_or_timeout",
+ false)) {
+ aConduit->SetRtcpEventObserver(this);
+ }
+ if (aConduit->type() == MediaSessionConduit::AUDIO) {
+ mPipeline = new MediaPipelineReceiveAudio(
+ mPCHandle, aTransportHandler, mMainThread.get(), mStsThread.get(),
+ static_cast<AudioSessionConduit*>(aConduit), mTrack, principalHandle);
+ } else {
+ mPipeline = new MediaPipelineReceiveVideo(
+ mPCHandle, aTransportHandler, mMainThread.get(), mStsThread.get(),
+ static_cast<VideoSessionConduit*>(aConduit), mTrack, principalHandle);
+ }
+}
+
+RTCRtpReceiver::~RTCRtpReceiver() { MOZ_ASSERT(!mPipeline); }
+
+JSObject* RTCRtpReceiver::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return RTCRtpReceiver_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+RTCDtlsTransport* RTCRtpReceiver::GetTransport() const {
+ if (!mTransceiverImpl) {
+ return nullptr;
+ }
+ return mTransceiverImpl->GetDtlsTransport();
+}
+
+already_AddRefed<Promise> RTCRtpReceiver::GetStats() {
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
+ ErrorResult rv;
+ RefPtr<Promise> promise = Promise::Create(global, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.StealNSResult();
+ return nullptr;
+ }
+
+ // Two promises; one for RTP/RTCP stats, another for ICE stats.
+ auto promises = GetStatsInternal();
+ RTCStatsPromise::All(mMainThread, promises)
+ ->Then(
+ mMainThread, __func__,
+ [promise, window = mWindow](
+ const nsTArray<UniquePtr<RTCStatsCollection>>& aStats) {
+ RefPtr<RTCStatsReport> report(new RTCStatsReport(window));
+ for (const auto& stats : aStats) {
+ report->Incorporate(*stats);
+ }
+ promise->MaybeResolve(std::move(report));
+ },
+ [promise](nsresult aError) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ });
+
+ return promise.forget();
+}
+
+static UniquePtr<dom::RTCStatsCollection> GetReceiverStats_s(
+ const RefPtr<MediaPipelineReceive>& aPipeline,
+ const nsString& aRecvTrackId) {
+ UniquePtr<dom::RTCStatsCollection> report(new dom::RTCStatsCollection);
+ auto asVideo = aPipeline->Conduit()->AsVideoSessionConduit();
+
+ nsString kind = asVideo.isNothing() ? u"audio"_ns : u"video"_ns;
+ nsString idstr = kind + u"_"_ns;
+ idstr.AppendInt(static_cast<uint32_t>(aPipeline->Level()));
+
+ // Add bandwidth estimation stats
+ aPipeline->Conduit()->GetBandwidthEstimation().apply([&](auto& bw) {
+ bw.mTrackIdentifier = aRecvTrackId;
+ if (!report->mBandwidthEstimations.AppendElement(bw, fallible)) {
+ mozalloc_handle_oom(0);
+ }
+ });
+
+ Maybe<uint32_t> ssrc;
+ unsigned int ssrcval;
+ if (aPipeline->Conduit()->GetRemoteSSRC(&ssrcval)) {
+ ssrc = Some(ssrcval);
+ }
+
+ // Add frame history
+ asVideo.apply([&](const auto& conduit) {
+ if (conduit->AddFrameHistory(&report->mVideoFrameHistories)) {
+ auto& history = report->mVideoFrameHistories.LastElement();
+ history.mTrackIdentifier = aRecvTrackId;
+ }
+ });
+
+ // TODO(@@NG):ssrcs handle Conduits having multiple stats at the same level
+ // This is pending spec work
+ // Gather pipeline stats.
+ nsString localId = u"inbound_rtp_"_ns + idstr;
+ nsString remoteId;
+
+ // First, fill in remote stat with rtcp sender data, if present.
+ uint32_t packetsSent;
+ uint64_t bytesSent;
+ DOMHighResTimeStamp remoteTimestamp;
+ Maybe<DOMHighResTimeStamp> timestamp =
+ aPipeline->Conduit()->LastRtcpReceived();
+ if (timestamp.isSome() && aPipeline->Conduit()->GetRTCPSenderReport(
+ &packetsSent, &bytesSent, &remoteTimestamp)) {
+ RTCRemoteOutboundRtpStreamStats s;
+ remoteId = u"inbound_rtcp_"_ns + idstr;
+ s.mTimestamp.Construct(*timestamp);
+ s.mId.Construct(remoteId);
+ s.mType.Construct(RTCStatsType::Remote_outbound_rtp);
+ ssrc.apply([&s](uint32_t aSsrc) { s.mSsrc.Construct(aSsrc); });
+ s.mMediaType.Construct(kind); // mediaType is the old name for kind.
+ s.mKind.Construct(kind);
+ s.mLocalId.Construct(localId);
+ s.mPacketsSent.Construct(packetsSent);
+ s.mBytesSent.Construct(bytesSent);
+ s.mRemoteTimestamp.Construct(remoteTimestamp);
+ if (!report->mRemoteOutboundRtpStreamStats.AppendElement(s, fallible)) {
+ mozalloc_handle_oom(0);
+ }
+ }
+
+ // Then, fill in local side (with cross-link to remote only if present)
+ RTCInboundRtpStreamStats s;
+ // TODO(bug 1496533): Should we use the time of the most-recently received
+ // RTP packet? If so, what do we use if we haven't received any RTP? Now?
+ s.mTimestamp.Construct(aPipeline->GetNow());
+ s.mId.Construct(localId);
+ s.mType.Construct(RTCStatsType::Inbound_rtp);
+ ssrc.apply([&s](uint32_t aSsrc) { s.mSsrc.Construct(aSsrc); });
+ s.mMediaType.Construct(kind); // mediaType is the old name for kind.
+ s.mKind.Construct(kind);
+ unsigned int jitterMs, packetsLost;
+ if (aPipeline->Conduit()->GetRTPReceiverStats(&jitterMs, &packetsLost)) {
+ s.mJitter.Construct(double(jitterMs) / 1000);
+ s.mPacketsLost.Construct(packetsLost);
+ }
+ if (remoteId.Length()) {
+ s.mRemoteId.Construct(remoteId);
+ }
+ s.mPacketsReceived.Construct(aPipeline->RtpPacketsReceived());
+ s.mBytesReceived.Construct(aPipeline->RtpBytesReceived());
+
+ // Fill in packet type statistics
+ webrtc::RtcpPacketTypeCounter counters;
+ if (aPipeline->Conduit()->GetRecvPacketTypeStats(&counters)) {
+ s.mNackCount.Construct(counters.nack_packets);
+ // Fill in video only packet type stats
+ if (asVideo) {
+ s.mFirCount.Construct(counters.fir_packets);
+ s.mPliCount.Construct(counters.pli_packets);
+ }
+ }
+ // Lastly, fill in video decoder stats if this is video
+ asVideo.apply([&s](auto conduit) {
+ double framerateMean;
+ double framerateStdDev;
+ double bitrateMean;
+ double bitrateStdDev;
+ uint32_t discardedPackets;
+ uint32_t framesDecoded;
+ if (conduit->GetVideoDecoderStats(&framerateMean, &framerateStdDev,
+ &bitrateMean, &bitrateStdDev,
+ &discardedPackets, &framesDecoded)) {
+ s.mFramerateMean.Construct(framerateMean);
+ s.mFramerateStdDev.Construct(framerateStdDev);
+ s.mBitrateMean.Construct(bitrateMean);
+ s.mBitrateStdDev.Construct(bitrateStdDev);
+ s.mDiscardedPackets.Construct(discardedPackets);
+ s.mFramesDecoded.Construct(framesDecoded);
+ }
+ });
+ if (!report->mInboundRtpStreamStats.AppendElement(s, fallible)) {
+ mozalloc_handle_oom(0);
+ }
+
+ // Fill in Contributing Source statistics
+ aPipeline->GetContributingSourceStats(localId,
+ report->mRtpContributingSourceStats);
+
+ return report;
+}
+
+nsTArray<RefPtr<RTCStatsPromise>> RTCRtpReceiver::GetStatsInternal() {
+ nsTArray<RefPtr<RTCStatsPromise>> promises;
+ if (mPipeline && mHaveStartedReceiving) {
+ nsString recvTrackId;
+ MOZ_ASSERT(mTrack);
+ if (mTrack) {
+ mTrack->GetId(recvTrackId);
+ }
+ promises.AppendElement(InvokeAsync(
+ mStsThread, __func__, [pipeline = mPipeline, recvTrackId]() {
+ return RTCStatsPromise::CreateAndResolve(
+ GetReceiverStats_s(pipeline, recvTrackId), __func__);
+ }));
+
+ if (mJsepTransceiver->mTransport.mComponents) {
+ promises.AppendElement(mTransportHandler->GetIceStats(
+ mJsepTransceiver->mTransport.mTransportId, mPipeline->GetNow()));
+ }
+ }
+ return promises;
+}
+
+void RTCRtpReceiver::GetContributingSources(
+ nsTArray<RTCRtpContributingSource>& aSources) {
+ // Duplicate code...
+ if (mPipeline && mPipeline->Conduit()) {
+ RefPtr<AudioSessionConduit> conduit(
+ static_cast<AudioSessionConduit*>(mPipeline->Conduit()));
+ nsTArray<dom::RTCRtpSourceEntry> sources;
+ conduit->GetRtpSources(sources);
+ sources.RemoveElementsBy([](const dom::RTCRtpSourceEntry& aEntry) {
+ return aEntry.mSourceType != dom::RTCRtpSourceEntryType::Contributing;
+ });
+ aSources.ReplaceElementsAt(0, aSources.Length(), sources.Elements(),
+ sources.Length());
+ }
+}
+
+void RTCRtpReceiver::GetSynchronizationSources(
+ nsTArray<dom::RTCRtpSynchronizationSource>& aSources) {
+ // Duplicate code...
+ if (mPipeline && mPipeline->Conduit()) {
+ RefPtr<AudioSessionConduit> conduit(
+ static_cast<AudioSessionConduit*>(mPipeline->Conduit()));
+ nsTArray<dom::RTCRtpSourceEntry> sources;
+ conduit->GetRtpSources(sources);
+ sources.RemoveElementsBy([](const dom::RTCRtpSourceEntry& aEntry) {
+ return aEntry.mSourceType != dom::RTCRtpSourceEntryType::Synchronization;
+ });
+ aSources.ReplaceElementsAt(0, aSources.Length(), sources.Elements(),
+ sources.Length());
+ }
+}
+
+nsPIDOMWindowInner* RTCRtpReceiver::GetParentObject() const { return mWindow; }
+
+void RTCRtpReceiver::Shutdown() {
+ ASSERT_ON_THREAD(mMainThread);
+ if (mPipeline) {
+ mPipeline->Shutdown_m();
+ mPipeline->Conduit()->SetRtcpEventObserver(nullptr);
+ mPipeline = nullptr;
+ }
+ mTransceiverImpl = nullptr;
+}
+
+void RTCRtpReceiver::UpdateTransport() {
+ ASSERT_ON_THREAD(mMainThread);
+ if (!mHaveSetupTransport) {
+ mPipeline->SetLevel(mJsepTransceiver->GetLevel());
+ mHaveSetupTransport = true;
+ }
+
+ UniquePtr<MediaPipelineFilter> filter;
+
+ auto const& details = mJsepTransceiver->mRecvTrack.GetNegotiatedDetails();
+ if (mJsepTransceiver->HasBundleLevel() && details) {
+ std::vector<webrtc::RtpExtension> extmaps;
+ details->ForEachRTPHeaderExtension(
+ [&extmaps](const SdpExtmapAttributeList::Extmap& extmap) {
+ extmaps.emplace_back(extmap.extensionname, extmap.entry);
+ });
+ filter = MakeUnique<MediaPipelineFilter>(extmaps);
+
+ // Add remote SSRCs so we can distinguish which RTP packets actually
+ // belong to this pipeline (also RTCP sender reports).
+ for (uint32_t ssrc : mJsepTransceiver->mRecvTrack.GetSsrcs()) {
+ filter->AddRemoteSSRC(ssrc);
+ }
+ for (uint32_t ssrc : mJsepTransceiver->mRecvTrack.GetRtxSsrcs()) {
+ filter->AddRemoteSSRC(ssrc);
+ }
+ auto mid = Maybe<std::string>();
+ if (GetMid() != "") {
+ mid = Some(GetMid());
+ }
+ filter->SetRemoteMediaStreamId(mid);
+
+ // Add unique payload types as a last-ditch fallback
+ auto uniquePts = mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()
+ ->GetUniquePayloadTypes();
+ for (unsigned char& uniquePt : uniquePts) {
+ filter->AddUniquePT(uniquePt);
+ }
+ }
+
+ mPipeline->UpdateTransport_m(mJsepTransceiver->mTransport.mTransportId,
+ std::move(filter));
+}
+
+nsresult RTCRtpReceiver::UpdateConduit() {
+ if (mPipeline->Conduit()->type() == MediaSessionConduit::VIDEO) {
+ return UpdateVideoConduit();
+ }
+ return UpdateAudioConduit();
+}
+
+nsresult RTCRtpReceiver::UpdateVideoConduit() {
+ RefPtr<VideoSessionConduit> conduit =
+ static_cast<VideoSessionConduit*>(mPipeline->Conduit());
+
+ // NOTE(pkerr) - this is new behavior. Needed because the
+ // CreateVideoReceiveStream method of the Call API will assert (in debug)
+ // and fail if a value is not provided for the remote_ssrc that will be used
+ // by the far-end sender.
+ if (!mJsepTransceiver->mRecvTrack.GetSsrcs().empty()) {
+ MOZ_LOG(gReceiverLog, LogLevel::Debug,
+ ("%s[%s]: %s Setting remote SSRC %u", mPCHandle.c_str(),
+ GetMid().c_str(), __FUNCTION__,
+ mJsepTransceiver->mRecvTrack.GetSsrcs().front()));
+ uint32_t rtxSsrc = mJsepTransceiver->mRecvTrack.GetRtxSsrcs().empty()
+ ? 0
+ : mJsepTransceiver->mRecvTrack.GetRtxSsrcs().front();
+ conduit->SetRemoteSSRC(mJsepTransceiver->mRecvTrack.GetSsrcs().front(),
+ rtxSsrc);
+ }
+
+ // TODO (bug 1423041) once we pay attention to receiving MID's in RTP
+ // packets (see bug 1405495) we could make this depending on the presence of
+ // MID in the RTP packets instead of relying on the signaling.
+ if (mJsepTransceiver->HasBundleLevel() &&
+ (!mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() ||
+ !mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()->GetExt(
+ webrtc::RtpExtension::kMIdUri))) {
+ mStsThread->Dispatch(
+ NewRunnableMethod("VideoSessionConduit::DisableSsrcChanges", conduit,
+ &VideoSessionConduit::DisableSsrcChanges));
+ }
+
+ if (mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() &&
+ mJsepTransceiver->mRecvTrack.GetActive()) {
+ const auto& details(*mJsepTransceiver->mRecvTrack.GetNegotiatedDetails());
+
+ TransceiverImpl::UpdateConduitRtpExtmap(
+ *conduit, details, MediaSessionConduitLocalDirection::kRecv);
+
+ std::vector<UniquePtr<VideoCodecConfig>> configs;
+ nsresult rv = TransceiverImpl::NegotiatedDetailsToVideoCodecConfigs(
+ details, &configs);
+
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gReceiverLog, LogLevel::Error,
+ ("%s[%s]: %s Failed to convert "
+ "JsepCodecDescriptions to VideoCodecConfigs (recv).",
+ mPCHandle.c_str(), GetMid().c_str(), __FUNCTION__));
+ return rv;
+ }
+
+ auto error =
+ conduit->ConfigureRecvMediaCodecs(configs, details.GetRtpRtcpConfig());
+
+ if (error) {
+ MOZ_LOG(gReceiverLog, LogLevel::Error,
+ ("%s[%s]: %s "
+ "ConfigureRecvMediaCodecs failed: %u",
+ mPCHandle.c_str(), GetMid().c_str(), __FUNCTION__, error));
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult RTCRtpReceiver::UpdateAudioConduit() {
+ RefPtr<AudioSessionConduit> conduit =
+ static_cast<AudioSessionConduit*>(mPipeline->Conduit());
+
+ if (!mJsepTransceiver->mRecvTrack.GetSsrcs().empty()) {
+ MOZ_LOG(gReceiverLog, LogLevel::Debug,
+ ("%s[%s]: %s Setting remote SSRC %u", mPCHandle.c_str(),
+ GetMid().c_str(), __FUNCTION__,
+ mJsepTransceiver->mRecvTrack.GetSsrcs().front()));
+ uint32_t rtxSsrc = mJsepTransceiver->mRecvTrack.GetRtxSsrcs().empty()
+ ? 0
+ : mJsepTransceiver->mRecvTrack.GetRtxSsrcs().front();
+ conduit->SetRemoteSSRC(mJsepTransceiver->mRecvTrack.GetSsrcs().front(),
+ rtxSsrc);
+ }
+
+ if (mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() &&
+ mJsepTransceiver->mRecvTrack.GetActive()) {
+ const auto& details(*mJsepTransceiver->mRecvTrack.GetNegotiatedDetails());
+ std::vector<UniquePtr<AudioCodecConfig>> configs;
+ nsresult rv = TransceiverImpl::NegotiatedDetailsToAudioCodecConfigs(
+ details, &configs);
+
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gReceiverLog, LogLevel::Error,
+ ("%s[%s]: %s Failed to convert "
+ "JsepCodecDescriptions to AudioCodecConfigs (recv).",
+ mPCHandle.c_str(), GetMid().c_str(), __FUNCTION__));
+ return rv;
+ }
+
+ // Ensure conduit knows about extensions prior to creating streams
+ TransceiverImpl::UpdateConduitRtpExtmap(
+ *conduit, details, MediaSessionConduitLocalDirection::kRecv);
+
+ auto error = conduit->ConfigureRecvMediaCodecs(configs);
+
+ if (error) {
+ MOZ_LOG(gReceiverLog, LogLevel::Error,
+ ("%s[%s]: %s "
+ "ConfigureRecvMediaCodecs failed: %u",
+ mPCHandle.c_str(), GetMid().c_str(), __FUNCTION__, error));
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+void RTCRtpReceiver::Stop() {
+ if (mPipeline) {
+ mPipeline->Stop();
+ }
+}
+
+void RTCRtpReceiver::Start() {
+ mPipeline->Start();
+ mHaveStartedReceiving = true;
+}
+
+bool RTCRtpReceiver::HasTrack(const dom::MediaStreamTrack* aTrack) const {
+ return !aTrack || (mTrack == aTrack);
+}
+
+void RTCRtpReceiver::UpdateStreams(StreamAssociationChanges* aChanges) {
+ // We don't sort and use set_difference, because we need to report the
+ // added/removed streams in the order that they appear in the SDP.
+ std::set<std::string> newIds(
+ mJsepTransceiver->mRecvTrack.GetStreamIds().begin(),
+ mJsepTransceiver->mRecvTrack.GetStreamIds().end());
+ MOZ_ASSERT(mJsepTransceiver->mRecvTrack.GetRemoteSetSendBit() ||
+ newIds.empty());
+ bool needsTrackEvent = false;
+ for (const auto& id : mStreamIds) {
+ if (!newIds.count(id)) {
+ aChanges->mStreamAssociationsRemoved.push_back({mTrack, id});
+ }
+ }
+
+ std::set<std::string> oldIds(mStreamIds.begin(), mStreamIds.end());
+ for (const auto& id : mJsepTransceiver->mRecvTrack.GetStreamIds()) {
+ if (!oldIds.count(id)) {
+ needsTrackEvent = true;
+ aChanges->mStreamAssociationsAdded.push_back({mTrack, id});
+ }
+ }
+
+ mStreamIds = mJsepTransceiver->mRecvTrack.GetStreamIds();
+
+ if (mRemoteSetSendBit != mJsepTransceiver->mRecvTrack.GetRemoteSetSendBit()) {
+ mRemoteSetSendBit = mJsepTransceiver->mRecvTrack.GetRemoteSetSendBit();
+ if (mRemoteSetSendBit) {
+ needsTrackEvent = true;
+ } else {
+ aChanges->mTracksToMute.push_back(mTrack);
+ }
+ }
+
+ if (needsTrackEvent) {
+ aChanges->mTrackEvents.push_back({this, mStreamIds});
+ }
+}
+
+void RTCRtpReceiver::MozAddRIDExtension(unsigned short aExtensionId) {
+ if (mPipeline) {
+ mPipeline->AddRIDExtension_m(aExtensionId);
+ }
+}
+
+void RTCRtpReceiver::MozAddRIDFilter(const nsAString& aRid) {
+ if (mPipeline) {
+ mPipeline->AddRIDFilter_m(NS_ConvertUTF16toUTF8(aRid).get());
+ }
+}
+
+// test-only: adds fake CSRCs and audio data
+void RTCRtpReceiver::MozInsertAudioLevelForContributingSource(
+ const uint32_t aSource, const DOMHighResTimeStamp aTimestamp,
+ const uint32_t aRtpTimestamp, const bool aHasLevel, const uint8_t aLevel) {
+ if (!mPipeline || mPipeline->IsVideo() || !mPipeline->Conduit()) {
+ return;
+ }
+ WebrtcAudioConduit* audio_conduit =
+ static_cast<WebrtcAudioConduit*>(mPipeline->Conduit());
+ audio_conduit->InsertAudioLevelForContributingSource(
+ aSource, aTimestamp, aRtpTimestamp, aHasLevel, aLevel);
+}
+
+void RTCRtpReceiver::OnRtcpBye() { SetReceiveTrackMuted(true); }
+
+void RTCRtpReceiver::OnRtcpTimeout() { SetReceiveTrackMuted(true); }
+
+void RTCRtpReceiver::SetReceiveTrackMuted(bool aMuted) {
+ if (mTrack) {
+ // This sets the muted state for mTrack and all its clones.
+ static_cast<RemoteTrackSource&>(mTrack->GetSource()).SetMuted(aMuted);
+ }
+}
+
+std::string RTCRtpReceiver::GetMid() const {
+ if (mJsepTransceiver->IsAssociated()) {
+ return mJsepTransceiver->GetMid();
+ }
+ return std::string();
+}
+
+} // namespace mozilla::dom
+
+#undef LOGTAG