diff options
Diffstat (limited to 'dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp')
-rw-r--r-- | dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp b/dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp new file mode 100644 index 0000000000..c0718739e9 --- /dev/null +++ b/dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp @@ -0,0 +1,151 @@ +/* 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 "MediaConduitInterface.h" + +#include "nsTArray.h" +#include "mozilla/Assertions.h" +#include "MainThreadUtils.h" +#include "SystemTime.h" + +#include "system_wrappers/include/clock.h" + +namespace mozilla { + +void MediaSessionConduit::GetRtpSources( + nsTArray<dom::RTCRtpSourceEntry>& outSources) const { + MOZ_ASSERT(NS_IsMainThread()); + if (mSourcesUpdateNeeded) { + UpdateRtpSources(GetUpstreamRtpSources()); + OnSourcesUpdated(); + } + outSources.Clear(); + for (auto& [key, entry] : mSourcesCache) { + (void)key; + outSources.AppendElement(entry); + } + + struct TimestampComparator { + bool LessThan(const dom::RTCRtpSourceEntry& aLhs, + const dom::RTCRtpSourceEntry& aRhs) const { + // Sort descending! + return aLhs.mTimestamp > aRhs.mTimestamp; + } + + bool Equals(const dom::RTCRtpSourceEntry& aLhs, + const dom::RTCRtpSourceEntry& aRhs) const { + return aLhs.mTimestamp == aRhs.mTimestamp; + } + }; + + // *sigh* We have to re-sort this by JS timestamp; we can run into cases + // where the libwebrtc timestamps are not in exactly the same order as JS + // timestamps due to clock differences (wibbly-wobbly, timey-wimey stuff) + outSources.Sort(TimestampComparator()); +} + +static double rtpToDomAudioLevel(uint8_t aAudioLevel) { + if (aAudioLevel == 127) { + // Spec indicates that a value of 127 should be set to 0 + return 0; + } + + // All other values are calculated as 10^(-rfc_level/20) + return std::pow(10, -aAudioLevel / 20.0); +} + +void MediaSessionConduit::UpdateRtpSources( + const std::vector<webrtc::RtpSource>& aSources) const { + MOZ_ASSERT(NS_IsMainThread()); + // Empty out the cache; we'll copy things back as needed + auto cache = std::move(mSourcesCache); + + for (const auto& source : aSources) { + SourceKey key(source); + auto it = cache.find(key); + if (it != cache.end()) { + // This source entry was already in the cache, and should continue to be + // present in exactly the same form as before. This means we do _not_ + // want to perform the timestamp adjustment again, since it might yield a + // slightly different result. This is why we copy this entry from the old + // cache instead of simply rebuilding it, and is also why we key the + // cache based on timestamp (keying the cache based on timestamp also + // gets us the ordering we want, conveniently). + mSourcesCache[key] = it->second; + continue; + } + + // This is something we did not already have in the cache. + dom::RTCRtpSourceEntry domEntry; + domEntry.mSource = source.source_id(); + switch (source.source_type()) { + case webrtc::RtpSourceType::SSRC: + domEntry.mSourceType = dom::RTCRtpSourceEntryType::Synchronization; + break; + case webrtc::RtpSourceType::CSRC: + domEntry.mSourceType = dom::RTCRtpSourceEntryType::Contributing; + break; + default: + MOZ_CRASH("Unexpected RTCRtpSourceEntryType"); + } + + if (source.audio_level()) { + domEntry.mAudioLevel.Construct(rtpToDomAudioLevel(*source.audio_level())); + } + + // These timestamps are always **rounded** to milliseconds. That means they + // can jump up to half a millisecond into the future. We compensate for that + // here so that things seem consistent to js. + domEntry.mTimestamp = GetTimestampMaker().ReduceRealtimePrecision( + webrtc::Timestamp::Millis(source.timestamp_ms()) - + webrtc::TimeDelta::Micros(500)); + domEntry.mRtpTimestamp = source.rtp_timestamp(); + mSourcesCache[key] = domEntry; + } +} + +void MediaSessionConduit::OnSourcesUpdated() const { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mSourcesUpdateNeeded); + mSourcesUpdateNeeded = false; + // Reset the updateNeeded flag and clear the cache in a direct task, i.e., + // as soon as the current task has finished. + AbstractThread::GetCurrent()->TailDispatcher().AddDirectTask( + NS_NewRunnableFunction( + __func__, [this, self = RefPtr<const MediaSessionConduit>(this)] { + mSourcesUpdateNeeded = true; + mSourcesCache.clear(); + })); +} + +void MediaSessionConduit::InsertAudioLevelForContributingSource( + const uint32_t aCsrcSource, const int64_t aTimestamp, + const uint32_t aRtpTimestamp, const bool aHasAudioLevel, + const uint8_t aAudioLevel) { + MOZ_ASSERT(NS_IsMainThread()); + + if (mSourcesUpdateNeeded) { + OnSourcesUpdated(); + } + + dom::RTCRtpSourceEntry domEntry; + domEntry.mSource = aCsrcSource; + domEntry.mSourceType = dom::RTCRtpSourceEntryType::Contributing; + domEntry.mTimestamp = aTimestamp; + domEntry.mRtpTimestamp = aRtpTimestamp; + if (aHasAudioLevel) { + domEntry.mAudioLevel.Construct(rtpToDomAudioLevel(aAudioLevel)); + } + + webrtc::Timestamp libwebrtcNow = GetTimestampMaker().GetNowRealtime(); + double jsNow = GetTimestampMaker().ReduceRealtimePrecision(libwebrtcNow); + double ago = jsNow - aTimestamp; + webrtc::Timestamp convertedTimestamp = + libwebrtcNow - webrtc::TimeDelta::Millis(ago); + + SourceKey key(convertedTimestamp.ms<uint32_t>(), aCsrcSource); + mSourcesCache[key] = domEntry; +} + +} // namespace mozilla |