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