Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 #ifndef MOZILLA_STREAMBUFFER_H_
7 #define MOZILLA_STREAMBUFFER_H_
9 #include "MediaSegment.h"
10 #include "nsAutoPtr.h"
12 namespace mozilla {
14 /**
15 * Media time relative to the start of a StreamBuffer.
16 */
17 typedef MediaTime StreamTime;
18 const StreamTime STREAM_TIME_MAX = MEDIA_TIME_MAX;
20 /**
21 * Track rate in Hz. Maximum 1 << MEDIA_TIME_FRAC_BITS Hz. This ensures
22 * calculations below don't overflow.
23 */
24 typedef int32_t TrackRate;
25 const TrackRate TRACK_RATE_MAX = 1 << MEDIA_TIME_FRAC_BITS;
27 /**
28 * Unique ID for track within a StreamBuffer. Tracks from different
29 * StreamBuffers may have the same ID; this matters when appending StreamBuffers,
30 * since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
31 */
32 typedef int32_t TrackID;
33 const TrackID TRACK_NONE = 0;
35 inline TrackTicks TimeToTicksRoundUp(TrackRate aRate, StreamTime aTime)
36 {
37 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
38 NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
39 return (aTime*aRate + (1 << MEDIA_TIME_FRAC_BITS) - 1) >> MEDIA_TIME_FRAC_BITS;
40 }
42 inline TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime)
43 {
44 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
45 NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
46 return (aTime*aRate) >> MEDIA_TIME_FRAC_BITS;
47 }
49 inline StreamTime TicksToTimeRoundUp(TrackRate aRate, TrackTicks aTicks)
50 {
51 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
52 NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
53 return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate - 1)/aRate;
54 }
56 inline StreamTime TicksToTimeRound(TrackRate aRate, TrackTicks aTicks)
57 {
58 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
59 NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
60 return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate/2)/aRate;
61 }
63 inline StreamTime TicksToTimeRoundDown(TrackRate aRate, TrackTicks aTicks)
64 {
65 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
66 NS_WARN_IF_FALSE(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
67 return (aTicks << MEDIA_TIME_FRAC_BITS)/aRate;
68 }
70 /**
71 * This object contains the decoded data for a stream's tracks.
72 * A StreamBuffer can be appended to. Logically a StreamBuffer only gets longer,
73 * but we also have the ability to "forget" data before a certain time that
74 * we know won't be used again. (We prune a whole number of seconds internally.)
75 *
76 * StreamBuffers should only be used from one thread at a time.
77 *
78 * A StreamBuffer has a set of tracks that can be of arbitrary types ---
79 * the data for each track is a MediaSegment. The set of tracks can vary
80 * over the timeline of the StreamBuffer.
81 */
82 class StreamBuffer {
83 public:
84 /**
85 * Every track has a start time --- when it started in the StreamBuffer.
86 * It has an end flag; when false, no end point is known; when true,
87 * the track ends when the data we have for the track runs out.
88 * Tracks have a unique ID assigned at creation. This allows us to identify
89 * the same track across StreamBuffers. A StreamBuffer should never have
90 * two tracks with the same ID (even if they don't overlap in time).
91 * TODO Tracks can also be enabled and disabled over time.
92 * TODO Add TimeVarying<TrackTicks,bool> mEnabled.
93 * Takes ownership of aSegment.
94 */
95 class Track {
96 public:
97 Track(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
98 : mStart(aStart),
99 mSegment(aSegment),
100 mRate(aRate),
101 mID(aID),
102 mEnded(false)
103 {
104 MOZ_COUNT_CTOR(Track);
106 NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
107 NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Invalid rate");
108 NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position");
109 }
110 ~Track()
111 {
112 MOZ_COUNT_DTOR(Track);
113 }
114 template <class T> T* Get() const
115 {
116 if (mSegment->GetType() == T::StaticType()) {
117 return static_cast<T*>(mSegment.get());
118 }
119 return nullptr;
120 }
121 MediaSegment* GetSegment() const { return mSegment; }
122 TrackRate GetRate() const { return mRate; }
123 TrackID GetID() const { return mID; }
124 bool IsEnded() const { return mEnded; }
125 TrackTicks GetStart() const { return mStart; }
126 TrackTicks GetEnd() const { return mSegment->GetDuration(); }
127 StreamTime GetEndTimeRoundDown() const
128 {
129 return mozilla::TicksToTimeRoundDown(mRate, mSegment->GetDuration());
130 }
131 StreamTime GetStartTimeRoundDown() const
132 {
133 return mozilla::TicksToTimeRoundDown(mRate, mStart);
134 }
135 TrackTicks TimeToTicksRoundDown(StreamTime aTime) const
136 {
137 return mozilla::TimeToTicksRoundDown(mRate, aTime);
138 }
139 StreamTime TicksToTimeRoundDown(TrackTicks aTicks) const
140 {
141 return mozilla::TicksToTimeRoundDown(mRate, aTicks);
142 }
143 MediaSegment::Type GetType() const { return mSegment->GetType(); }
145 void SetEnded() { mEnded = true; }
146 void AppendFrom(Track* aTrack)
147 {
148 NS_ASSERTION(!mEnded, "Can't append to ended track");
149 NS_ASSERTION(aTrack->mID == mID, "IDs must match");
150 NS_ASSERTION(aTrack->mStart == 0, "Source track must start at zero");
151 NS_ASSERTION(aTrack->mSegment->GetType() == GetType(), "Track types must match");
152 NS_ASSERTION(aTrack->mRate == mRate, "Track rates must match");
154 mSegment->AppendFrom(aTrack->mSegment);
155 mEnded = aTrack->mEnded;
156 }
157 MediaSegment* RemoveSegment()
158 {
159 return mSegment.forget();
160 }
161 void ForgetUpTo(TrackTicks aTime)
162 {
163 mSegment->ForgetUpTo(aTime);
164 }
166 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
167 {
168 size_t amount = aMallocSizeOf(this);
169 if (mSegment) {
170 amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
171 }
172 return amount;
173 }
175 protected:
176 friend class StreamBuffer;
178 // Start offset is in ticks at rate mRate
179 TrackTicks mStart;
180 // The segment data starts at the start of the owning StreamBuffer, i.e.,
181 // there's mStart silence/no video at the beginning.
182 nsAutoPtr<MediaSegment> mSegment;
183 TrackRate mRate; // rate in ticks per second
184 // Unique ID
185 TrackID mID;
186 // True when the track ends with the data in mSegment
187 bool mEnded;
188 };
190 class CompareTracksByID {
191 public:
192 bool Equals(Track* aA, Track* aB) const {
193 return aA->GetID() == aB->GetID();
194 }
195 bool LessThan(Track* aA, Track* aB) const {
196 return aA->GetID() < aB->GetID();
197 }
198 };
200 StreamBuffer()
201 : mTracksKnownTime(0), mForgottenTime(0)
202 {
203 MOZ_COUNT_CTOR(StreamBuffer);
204 }
205 ~StreamBuffer()
206 {
207 MOZ_COUNT_DTOR(StreamBuffer);
208 }
210 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
211 {
212 size_t amount = 0;
213 amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
214 for (size_t i = 0; i < mTracks.Length(); i++) {
215 amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
216 }
217 return amount;
218 }
220 /**
221 * Takes ownership of aSegment. Don't do this while iterating, or while
222 * holding a Track reference.
223 * aSegment must have aStart worth of null data.
224 */
225 Track& AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
226 {
227 NS_ASSERTION(TimeToTicksRoundDown(aRate, mTracksKnownTime) <= aStart,
228 "Start time too early");
229 NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
231 return **mTracks.InsertElementSorted(new Track(aID, aRate, aStart, aSegment),
232 CompareTracksByID());
233 }
234 void AdvanceKnownTracksTime(StreamTime aKnownTime)
235 {
236 NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier");
237 mTracksKnownTime = aKnownTime;
238 }
240 /**
241 * The end time for the StreamBuffer 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;
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;
253 #ifdef DEBUG
254 void DumpTrackInfo() const;
255 #endif
257 Track* FindTrack(TrackID aID);
259 class TrackIter {
260 public:
261 /**
262 * Iterate through the tracks of aBuffer in order of ID.
263 */
264 TrackIter(const StreamBuffer& aBuffer) :
265 mBuffer(&aBuffer.mTracks), mIndex(0), mMatchType(false) {}
266 /**
267 * Iterate through the tracks of aBuffer with type aType, in order of ID.
268 */
269 TrackIter(const StreamBuffer& aBuffer, MediaSegment::Type aType) :
270 mBuffer(&aBuffer.mTracks), mIndex(0), mType(aType), mMatchType(true) { FindMatch(); }
271 bool IsEnded() { return mIndex >= mBuffer->Length(); }
272 void Next()
273 {
274 ++mIndex;
275 FindMatch();
276 }
277 Track* get() { return mBuffer->ElementAt(mIndex); }
278 Track& operator*() { return *mBuffer->ElementAt(mIndex); }
279 Track* operator->() { return mBuffer->ElementAt(mIndex); }
280 private:
281 void FindMatch()
282 {
283 if (!mMatchType)
284 return;
285 while (mIndex < mBuffer->Length() &&
286 mBuffer->ElementAt(mIndex)->GetType() != mType) {
287 ++mIndex;
288 }
289 }
291 const nsTArray<nsAutoPtr<Track> >* mBuffer;
292 uint32_t mIndex;
293 MediaSegment::Type mType;
294 bool mMatchType;
295 };
296 friend class TrackIter;
298 /**
299 * Forget stream data before aTime; they will no longer be needed.
300 * Also can forget entire tracks that have ended at or before aTime.
301 * Can't be used to forget beyond GetEnd().
302 */
303 void ForgetUpTo(StreamTime aTime);
304 /**
305 * Returns the latest time passed to ForgetUpTo.
306 */
307 StreamTime GetForgottenDuration()
308 {
309 return mForgottenTime;
310 }
312 protected:
313 // Any new tracks added will start at or after this time. In other words, the track
314 // list is complete and correct for all times less than this time.
315 StreamTime mTracksKnownTime;
316 StreamTime mForgottenTime;
317 // All known tracks for this StreamBuffer
318 nsTArray<nsAutoPtr<Track> > mTracks;
319 };
321 }
323 #endif /* MOZILLA_STREAMBUFFER_H_ */