summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/libwebrtcglue/RtpSourceObserver.h
blob: 4fbf2fafe9a7a9016630d79f19aaa94c5e8381c9 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

#ifndef AUDIOLEVELOBSERVER_H
#define AUDIOLEVELOBSERVER_H

#include <vector>
#include <map>

#include "nsISupportsImpl.h"
#include "mozilla/dom/RTCRtpSourcesBinding.h"
#include "webrtc/common_types.h"
#include "jsapi/RTCStatsReport.h"

// Unit Test class
namespace test {
class RtpSourcesTest;
}

namespace mozilla {

/* Observes reception of RTP packets and tabulates data about the
 * most recent arival times by source (csrc or ssrc) and audio level information
 *  * csrc-audio-level RTP header extension
 *  * ssrc-audio-level RTP header extension
 */
class RtpSourceObserver {
 public:
  explicit RtpSourceObserver(
      const dom::RTCStatsTimestampMaker& aTimestampMaker);

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RtpSourceObserver)

  void OnRtpPacket(const webrtc::RTPHeader& aHeader, const uint32_t aJitter);

  /*
   * Get the most recent 10 second window of CSRC and SSRC sources.
   * @param outLevels will be popluted with source entries
   * Note: this takes jitter into account when calculating the window so
   * the window is actually [time - jitter - 10 sec .. time - jitter]
   */
  void GetRtpSources(nsTArray<dom::RTCRtpSourceEntry>& outSources) const;

 private:
  virtual ~RtpSourceObserver() = default;

  struct RtpSourceEntry {
    RtpSourceEntry() = default;
    void Update(const int64_t aTimestamp, const uint32_t aRtpTimestamp,
                const bool aHasAudioLevel, const uint8_t aAudioLevel) {
      predictedPlayoutTime = aTimestamp;
      rtpTimestamp = aRtpTimestamp;
      // Audio level range is 0 - 127 inclusive
      hasAudioLevel = aHasAudioLevel && !(aAudioLevel & 0x80);
      audioLevel = aAudioLevel;
    }
    // Sets the audio level nullable according to the linear scale
    // outlined in the webrtc-pc spec.
    double ToLinearAudioLevel() const;
    // Time this information was received + jitter
    int64_t predictedPlayoutTime = 0;
    // The original RTP timestamp in the received packet
    uint32_t rtpTimestamp = 0;
    bool hasAudioLevel = false;
    uint8_t audioLevel = 0;
  };

  /* Why this is needed:
   * We are supposed to only report source stats for packets that have already
   * been rendered. Unfortunately, we only know when these packets are
   * _received_ right now. So, we need to make a guess at when each packet will
   * be rendered, and hide its statistics until the clock reaches that estimate.
   */
  /* Maintains a history of packets for reporting with getContributingSources
   * and getSynchronizationSources. It is expected that entries will not always
   * be observed in chronological order, and that the correct entry for a query
   * not be the most recently added item. Many times the query time is expected
   * to fall within [now - Jitter window .. now + Jitter Window]. A full history
   * is kept within the jitter window, and only the most recent to fall out of
   * the window is stored for the full 10 seconds. This value is only likely to
   * be returned when the stream is stopped or paused.
   *  x-axis: time (non-linear scale)
   *  let J = now + Jitter Window
   *  let T = now - Jitter Window
   *  now - 10 seconds                             T      now       J
   *  |-----------------Z--------------------------|-AB--CDEFG-HI--J|
   *                    ^Latest evicted               ^Jitter buffer entries
   *  Ex Query Time  ^Q0                  ^Q1          ^Q2 ^Q3     ^Q4
   *  Query result:
   *  Q0: Nothing
   *  Q1: Z
   *  Q2: B
   *  Q3: E
   *  Q4: I
   */
  class RtpSourceHistory {
   public:
    RtpSourceHistory() = default;
    // Finds the closest entry to a time, and passes that value to a closure
    // Note: the pointer is invalidated by any operation on the history
    // Note: the pointer is owned by the RtpSourceHistory
    const RtpSourceEntry* FindClosestNotAfter(int64_t aTime) const;
    // Inserts data into the history, may silently drop data if it is too old
    void Insert(const int64_t aTimeNow, const int64_t aTimestamp,
                const uint32_t aRtpTimestamp, const bool aHasAudioLevel,
                const uint8_t aAudioLevel);
    // Removes aged out from the jitter window
    void Prune(const int64_t aTimeNow);
    // Set Source
    void SetSource(uint32_t aSource, dom::RTCRtpSourceEntryType aType);

   private:
    // Finds a place to insert data and returns a reference to it
    RtpSourceObserver::RtpSourceEntry& Insert(const int64_t aTimeNow,
                                              const int64_t aTimestamp);
    // Is the history buffer empty?
    bool Empty() const { return !mDetailedHistory.size(); }
    // Is there an evicted entry
    bool HasEvicted() const { return mHasEvictedEntry; }

    // Minimum amount of time (ms) to store a complete packet history
    constexpr static int64_t kMinJitterWindow = 1000;
    // Size of the history window (ms)
    constexpr static int64_t kHistoryWindow = 10000;
    // This is 2 x the maximum observed jitter or the min which ever is higher
    int64_t mMaxJitterWindow = kMinJitterWindow;
    // The least old entry to be kicked from the buffer.
    RtpSourceEntry mLatestEviction;
    // Is there an evicted entry?
    bool mHasEvictedEntry = false;
    std::map<int64_t, RtpSourceEntry> mDetailedHistory;
    // Entry before history
    RtpSourceEntry mPrehistory;
    // Unit test
    friend test::RtpSourcesTest;
  };

  // Do not copy or assign
  RtpSourceObserver(const RtpSourceObserver&) = delete;
  RtpSourceObserver& operator=(RtpSourceObserver const&) = delete;
  // Returns a key for a source and a type
  static uint64_t GetKey(const uint32_t id,
                         const dom::RTCRtpSourceEntryType aType) {
    return (aType == dom::RTCRtpSourceEntryType::Synchronization)
               ? (static_cast<uint64_t>(id) |
                  (static_cast<uint64_t>(0x1) << 32))
               : (static_cast<uint64_t>(id));
  }
  // Returns the source from a key
  static uint32_t GetSourceFromKey(const uint64_t aKey) {
    return static_cast<uint32_t>(aKey & ~(static_cast<uint64_t>(0x1) << 32));
  }
  // Returns the type from a key
  static dom::RTCRtpSourceEntryType GetTypeFromKey(const uint64_t aKey) {
    return (aKey & (static_cast<uint64_t>(0x1) << 32))
               ? dom::RTCRtpSourceEntryType::Synchronization
               : dom::RTCRtpSourceEntryType::Contributing;
  }
  // Map CSRC to RtpSourceEntry
  std::map<uint64_t, RtpSourceHistory> mRtpSources;
  // 2 x the largest observed
  int64_t mMaxJitterWindow;
  dom::RTCStatsTimestampMaker mTimestampMaker;

  // Unit test
  friend test::RtpSourcesTest;

  // Testing only
  // Inserts additional csrc audio levels for mochitests
  friend void InsertAudioLevelForContributingSource(
      RtpSourceObserver& observer, const uint32_t aCsrcSource,
      const int64_t aTimestamp, const uint32_t aRtpTimestamp,
      const bool aHasAudioLevel, const uint8_t aAudioLevel);
};
}  // namespace mozilla
#undef NG
#endif  // AUDIOLEVELOBSERVER_H