MediaConduitInterface.cpp (5299B)
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 "MediaConduitInterface.h" 6 7 #include "MainThreadUtils.h" 8 #include "SystemTime.h" 9 #include "mozilla/Assertions.h" 10 #include "nsTArray.h" 11 #include "system_wrappers/include/clock.h" 12 13 namespace mozilla { 14 15 void MediaSessionConduit::GetRtpSources( 16 nsTArray<dom::RTCRtpSourceEntry>& outSources) const { 17 MOZ_ASSERT(NS_IsMainThread()); 18 if (mSourcesUpdateNeeded) { 19 UpdateRtpSources(GetUpstreamRtpSources()); 20 OnSourcesUpdated(); 21 } 22 outSources.Clear(); 23 for (auto& [key, entry] : mSourcesCache) { 24 (void)key; 25 outSources.AppendElement(entry); 26 } 27 28 struct TimestampComparator { 29 bool LessThan(const dom::RTCRtpSourceEntry& aLhs, 30 const dom::RTCRtpSourceEntry& aRhs) const { 31 // Sort descending! 32 return aLhs.mTimestamp > aRhs.mTimestamp; 33 } 34 35 bool Equals(const dom::RTCRtpSourceEntry& aLhs, 36 const dom::RTCRtpSourceEntry& aRhs) const { 37 return aLhs.mTimestamp == aRhs.mTimestamp; 38 } 39 }; 40 41 // *sigh* We have to re-sort this by JS timestamp; we can run into cases 42 // where the libwebrtc timestamps are not in exactly the same order as JS 43 // timestamps due to clock differences (wibbly-wobbly, timey-wimey stuff) 44 outSources.Sort(TimestampComparator()); 45 } 46 47 static double rtpToDomAudioLevel(uint8_t aAudioLevel) { 48 if (aAudioLevel == 127) { 49 // Spec indicates that a value of 127 should be set to 0 50 return 0; 51 } 52 53 // All other values are calculated as 10^(-rfc_level/20) 54 return std::pow(10, -aAudioLevel / 20.0); 55 } 56 57 void MediaSessionConduit::UpdateRtpSources( 58 const std::vector<webrtc::RtpSource>& aSources) const { 59 MOZ_ASSERT(NS_IsMainThread()); 60 // Empty out the cache; we'll copy things back as needed 61 auto cache = std::move(mSourcesCache); 62 63 for (const auto& source : aSources) { 64 SourceKey key(source); 65 auto it = cache.find(key); 66 if (it != cache.end()) { 67 // This source entry was already in the cache, and should continue to be 68 // present in exactly the same form as before. This means we do _not_ 69 // want to perform the timestamp adjustment again, since it might yield a 70 // slightly different result. This is why we copy this entry from the old 71 // cache instead of simply rebuilding it, and is also why we key the 72 // cache based on timestamp (keying the cache based on timestamp also 73 // gets us the ordering we want, conveniently). 74 mSourcesCache[key] = it->second; 75 continue; 76 } 77 78 // This is something we did not already have in the cache. 79 dom::RTCRtpSourceEntry domEntry; 80 domEntry.mSource = source.source_id(); 81 switch (source.source_type()) { 82 case webrtc::RtpSourceType::SSRC: 83 domEntry.mSourceType = dom::RTCRtpSourceEntryType::Synchronization; 84 break; 85 case webrtc::RtpSourceType::CSRC: 86 domEntry.mSourceType = dom::RTCRtpSourceEntryType::Contributing; 87 break; 88 default: 89 MOZ_CRASH("Unexpected RTCRtpSourceEntryType"); 90 } 91 92 if (source.audio_level()) { 93 domEntry.mAudioLevel.Construct(rtpToDomAudioLevel(*source.audio_level())); 94 } 95 96 // These timestamps are always **rounded** to milliseconds. That means they 97 // can jump up to half a millisecond into the future. We compensate for that 98 // here so that things seem consistent to js. 99 domEntry.mTimestamp = 100 dom::RTCStatsTimestamp::FromRealtime( 101 GetTimestampMaker(), 102 webrtc::Timestamp::Millis(source.timestamp().ms()) - 103 webrtc::TimeDelta::Micros(500)) 104 .ToDom(); 105 domEntry.mRtpTimestamp = source.rtp_timestamp(); 106 mSourcesCache[key] = domEntry; 107 } 108 } 109 110 void MediaSessionConduit::OnSourcesUpdated() const { 111 MOZ_ASSERT(NS_IsMainThread()); 112 MOZ_ASSERT(mSourcesUpdateNeeded); 113 mSourcesUpdateNeeded = false; 114 // Reset the updateNeeded flag and clear the cache in a direct task, i.e., 115 // as soon as the current task has finished. 116 AbstractThread::GetCurrent()->TailDispatcher().AddDirectTask( 117 NS_NewRunnableFunction( 118 __func__, [this, self = RefPtr<const MediaSessionConduit>(this)] { 119 mSourcesUpdateNeeded = true; 120 mSourcesCache.clear(); 121 })); 122 } 123 124 void MediaSessionConduit::InsertAudioLevelForContributingSource( 125 const uint32_t aCsrcSource, const int64_t aTimestamp, 126 const uint32_t aRtpTimestamp, const bool aHasAudioLevel, 127 const uint8_t aAudioLevel) { 128 MOZ_ASSERT(NS_IsMainThread()); 129 130 if (mSourcesUpdateNeeded) { 131 OnSourcesUpdated(); 132 } 133 134 dom::RTCRtpSourceEntry domEntry; 135 domEntry.mSource = aCsrcSource; 136 domEntry.mSourceType = dom::RTCRtpSourceEntryType::Contributing; 137 domEntry.mTimestamp = aTimestamp; 138 domEntry.mRtpTimestamp = aRtpTimestamp; 139 if (aHasAudioLevel) { 140 domEntry.mAudioLevel.Construct(rtpToDomAudioLevel(aAudioLevel)); 141 } 142 143 auto now = GetTimestampMaker().GetNow(); 144 webrtc::Timestamp convertedTimestamp = 145 now.ToRealtime() - webrtc::TimeDelta::Millis(now.ToDom() - aTimestamp); 146 147 SourceKey key(convertedTimestamp.ms<uint32_t>(), aCsrcSource); 148 mSourcesCache[key] = domEntry; 149 } 150 151 } // namespace mozilla