Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/StreamTracks.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#ifndef MOZILLA_STREAMTRACKS_H_
7
#define MOZILLA_STREAMTRACKS_H_
8
9
#include "MediaSegment.h"
10
#include "nsAutoPtr.h"
11
#include "TrackID.h"
12
13
namespace mozilla {
14
15
inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
16
                                            TrackRate aInRate,
17
                                            TrackTicks aTicks)
18
0
{
19
0
  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
20
0
  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
21
0
  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
22
0
  return (aTicks * aOutRate) / aInRate;
23
0
}
24
inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate,
25
                                          TrackRate aInRate, TrackTicks aTicks)
26
{
27
  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
28
  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
29
  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
30
  return (aTicks * aOutRate + aInRate - 1) / aInRate;
31
}
32
33
/**
34
 * This object contains the decoded data for a stream's tracks.
35
 * A StreamTracks can be appended to. Logically a StreamTracks only gets longer,
36
 * but we also have the ability to "forget" data before a certain time that
37
 * we know won't be used again. (We prune a whole number of seconds internally.)
38
 *
39
 * StreamTrackss should only be used from one thread at a time.
40
 *
41
 * A StreamTracks has a set of tracks that can be of arbitrary types ---
42
 * the data for each track is a MediaSegment. The set of tracks can vary
43
 * over the timeline of the StreamTracks.
44
 */
45
class StreamTracks
46
{
47
public:
48
  /**
49
   * Every track has a start time --- when it started in the StreamTracks.
50
   * It has an end flag; when false, no end point is known; when true,
51
   * the track ends when the data we have for the track runs out.
52
   * Tracks have a unique ID assigned at creation. This allows us to identify
53
   * the same track across StreamTrackss. A StreamTracks should never have
54
   * two tracks with the same ID (even if they don't overlap in time).
55
   * TODO Tracks can also be enabled and disabled over time.
56
   * Takes ownership of aSegment.
57
   */
58
  class Track final
59
  {
60
    Track(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
61
      : mStart(aStart),
62
        mSegment(aSegment),
63
        mID(aID),
64
        mEnded(false)
65
0
    {
66
0
      MOZ_COUNT_CTOR(Track);
67
0
68
0
      NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
69
0
      NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position");
70
0
    }
71
72
  public:
73
    ~Track()
74
0
    {
75
0
      MOZ_COUNT_DTOR(Track);
76
0
    }
77
78
    template <class T> T* Get() const
79
0
    {
80
0
      if (mSegment->GetType() == T::StaticType()) {
81
0
        return static_cast<T*>(mSegment.get());
82
0
      }
83
0
      return nullptr;
84
0
    }
85
86
0
    MediaSegment* GetSegment() const { return mSegment; }
87
0
    TrackID GetID() const { return mID; }
88
0
    bool IsEnded() const { return mEnded; }
89
    StreamTime GetStart() const { return mStart; }
90
0
    StreamTime GetEnd() const { return mSegment->GetDuration(); }
91
0
    MediaSegment::Type GetType() const { return mSegment->GetType(); }
92
93
0
    void SetEnded() { mEnded = true; }
94
    void AppendFrom(Track* aTrack)
95
    {
96
      NS_ASSERTION(!mEnded, "Can't append to ended track");
97
      NS_ASSERTION(aTrack->mID == mID, "IDs must match");
98
      NS_ASSERTION(aTrack->mStart == 0, "Source track must start at zero");
99
      NS_ASSERTION(aTrack->mSegment->GetType() == GetType(), "Track types must match");
100
101
      mSegment->AppendFrom(aTrack->mSegment);
102
      mEnded = aTrack->mEnded;
103
    }
104
    MediaSegment* RemoveSegment()
105
    {
106
      return mSegment.forget();
107
    }
108
    void ForgetUpTo(StreamTime aTime)
109
0
    {
110
0
      mSegment->ForgetUpTo(aTime);
111
0
    }
112
    void FlushAfter(StreamTime aNewEnd)
113
    {
114
      // Forget everything after a given endpoint
115
      // a specified amount
116
      mSegment->FlushAfter(aNewEnd);
117
    }
118
119
    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
120
0
    {
121
0
      size_t amount = aMallocSizeOf(this);
122
0
      if (mSegment) {
123
0
        amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
124
0
      }
125
0
      return amount;
126
0
    }
127
128
  private:
129
    friend class StreamTracks;
130
131
    // Start offset is in ticks at rate mRate
132
    StreamTime mStart;
133
    // The segment data starts at the start of the owning StreamTracks, i.e.,
134
    // there's mStart silence/no video at the beginning.
135
    nsAutoPtr<MediaSegment> mSegment;
136
    // Unique ID
137
    TrackID mID;
138
    // True when the track ends with the data in mSegment
139
    bool mEnded;
140
  };
141
142
  class MOZ_STACK_CLASS CompareTracksByID final
143
  {
144
  public:
145
0
    bool Equals(Track* aA, Track* aB) const {
146
0
      return aA->GetID() == aB->GetID();
147
0
    }
148
0
    bool LessThan(Track* aA, Track* aB) const {
149
0
      return aA->GetID() < aB->GetID();
150
0
    }
151
  };
152
153
  StreamTracks()
154
    : mGraphRate(0)
155
    , mTracksKnownTime(0)
156
    , mForgottenTime(0)
157
    , mTracksDirty(false)
158
#ifdef DEBUG
159
    , mGraphRateIsSet(false)
160
#endif
161
0
  {
162
0
    MOZ_COUNT_CTOR(StreamTracks);
163
0
  }
164
  ~StreamTracks()
165
0
  {
166
0
    MOZ_COUNT_DTOR(StreamTracks);
167
0
  }
168
169
  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
170
0
  {
171
0
    size_t amount = 0;
172
0
    amount += mTracks.ShallowSizeOfExcludingThis(aMallocSizeOf);
173
0
    for (size_t i = 0; i < mTracks.Length(); i++) {
174
0
      amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
175
0
    }
176
0
    return amount;
177
0
  }
178
179
  /**
180
   * Initialize the graph rate for use in calculating StreamTimes from track
181
   * ticks.  Called when a MediaStream's graph pointer is initialized.
182
   */
183
  void InitGraphRate(TrackRate aGraphRate)
184
0
  {
185
0
    mGraphRate = aGraphRate;
186
#if DEBUG
187
    MOZ_ASSERT(!mGraphRateIsSet);
188
    mGraphRateIsSet = true;
189
#endif
190
  }
191
192
  TrackRate GraphRate() const
193
  {
194
    MOZ_ASSERT(mGraphRateIsSet);
195
    return mGraphRate;
196
  }
197
198
  /**
199
   * Takes ownership of aSegment. Don't do this while iterating, or while
200
   * holding a Track reference.
201
   * aSegment must have aStart worth of null data.
202
   */
203
  Track& AddTrack(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
204
0
  {
205
0
    NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
206
0
207
0
    Track* track = new Track(aID, aStart, aSegment);
208
0
    mTracks.InsertElementSorted(track, CompareTracksByID());
209
0
    mTracksDirty = true;
210
0
211
0
    if (mTracksKnownTime == STREAM_TIME_MAX) {
212
0
      // There exists code like
213
0
      // http://mxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp?rev=96b197deb91e&mark=1292-1297#1292
214
0
      NS_WARNING("Adding track to StreamTracks that should have no more tracks");
215
0
    } else {
216
0
      NS_ASSERTION(mTracksKnownTime <= aStart, "Start time too early");
217
0
    }
218
0
    return *track;
219
0
  }
220
221
  void AdvanceKnownTracksTime(StreamTime aKnownTime)
222
0
  {
223
0
    NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier");
224
0
    mTracksKnownTime = aKnownTime;
225
0
  }
226
227
  /**
228
   * The end time for the StreamTracks is the latest time for which we have
229
   * data for all tracks that haven't ended by that time.
230
   */
231
  StreamTime GetEnd() const;
232
233
  /**
234
   * Returns the earliest time >= 0 at which all tracks have ended
235
   * and all their data has been played out and no new tracks can be added,
236
   * or STREAM_TIME_MAX if there is no such time.
237
   */
238
  StreamTime GetAllTracksEnd() const;
239
240
#ifdef DEBUG
241
  void DumpTrackInfo() const;
242
#endif
243
244
  Track* FindTrack(TrackID aID) const;
245
246
  class MOZ_STACK_CLASS TrackIter final
247
  {
248
  public:
249
    /**
250
     * Iterate through the tracks of aBuffer in order of ID.
251
     */
252
    explicit TrackIter(const StreamTracks& aBuffer) :
253
      mBuffer(&aBuffer.mTracks),
254
      mIndex(0),
255
      mType(static_cast<MediaSegment::Type>(0)),
256
      mMatchType(false)
257
0
      {
258
0
      }
259
    /**
260
     * Iterate through the tracks of aBuffer with type aType, in order of ID.
261
     */
262
    TrackIter(const StreamTracks& aBuffer, MediaSegment::Type aType) :
263
0
      mBuffer(&aBuffer.mTracks), mIndex(0), mType(aType), mMatchType(true) { FindMatch(); }
264
0
    bool IsEnded() const { return mIndex >= mBuffer->Length(); }
265
    void Next()
266
0
    {
267
0
      ++mIndex;
268
0
      FindMatch();
269
0
    }
270
0
    Track* get() const { return mBuffer->ElementAt(mIndex); }
271
0
    Track& operator*() { return *mBuffer->ElementAt(mIndex); }
272
0
    Track* operator->() { return mBuffer->ElementAt(mIndex); }
273
  private:
274
    void FindMatch()
275
0
    {
276
0
      if (!mMatchType)
277
0
        return;
278
0
      while (mIndex < mBuffer->Length() &&
279
0
             mBuffer->ElementAt(mIndex)->GetType() != mType) {
280
0
        ++mIndex;
281
0
      }
282
0
    }
283
284
    const nsTArray<nsAutoPtr<Track> >* mBuffer;
285
    uint32_t mIndex;
286
    MediaSegment::Type mType;
287
    bool mMatchType;
288
  };
289
  friend class TrackIter;
290
291
  /**
292
   * Forget stream data before aTime; they will no longer be needed.
293
   * Also can forget entire tracks that have ended at or before aTime.
294
   * Can't be used to forget beyond GetEnd().
295
   */
296
  void ForgetUpTo(StreamTime aTime);
297
  /**
298
   * Clears out all Tracks and the data they are holding.
299
   * MediaStreamGraph calls this during forced shutdown.
300
   */
301
  void Clear();
302
  /**
303
   * Returns the latest time passed to ForgetUpTo.
304
   */
305
  StreamTime GetForgottenDuration() const
306
0
  {
307
0
    return mForgottenTime;
308
0
  }
309
310
  bool GetAndResetTracksDirty()
311
0
  {
312
0
    if (!mTracksDirty) {
313
0
      return false;
314
0
    }
315
0
316
0
    mTracksDirty = false;
317
0
    return true;
318
0
  }
319
320
protected:
321
  TrackRate mGraphRate; // StreamTime per second
322
  // Any new tracks added will start at or after this time. In other words, the track
323
  // list is complete and correct for all times less than this time.
324
  StreamTime mTracksKnownTime;
325
  StreamTime mForgottenTime;
326
327
private:
328
  // All known tracks for this StreamTracks
329
  nsTArray<nsAutoPtr<Track>> mTracks;
330
  bool mTracksDirty;
331
332
#ifdef DEBUG
333
  bool mGraphRateIsSet;
334
#endif
335
};
336
337
} // namespace mozilla
338
339
#endif /* MOZILLA_STREAMTRACKS_H_ */
340