content/media/StreamBuffer.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial