content/media/StreamBuffer.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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

mercurial