summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/libwebrtcglue/MediaConduitInterface.cpp
blob: 7e337953b5077b00e18e1f345f12ba9038ece676 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* 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 =
        dom::RTCStatsTimestamp::FromRealtime(
            GetTimestampMaker(),
            webrtc::Timestamp::Millis(source.timestamp().ms()) -
                webrtc::TimeDelta::Micros(500))
            .ToDom();
    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));
  }

  auto now = GetTimestampMaker().GetNow();
  webrtc::Timestamp convertedTimestamp =
      now.ToRealtime() - webrtc::TimeDelta::Millis(now.ToDom() - aTimestamp);

  SourceKey key(convertedTimestamp.ms<uint32_t>(), aCsrcSource);
  mSourcesCache[key] = domEntry;
}

}  // namespace mozilla