/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 | | |