1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/StreamBuffer.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,324 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef MOZILLA_STREAMBUFFER_H_ 1.10 +#define MOZILLA_STREAMBUFFER_H_ 1.11 + 1.12 +#include "MediaSegment.h" 1.13 +#include "nsAutoPtr.h" 1.14 + 1.15 +namespace mozilla { 1.16 + 1.17 +/** 1.18 + * Media time relative to the start of a StreamBuffer. 1.19 + */ 1.20 +typedef MediaTime StreamTime; 1.21 +const StreamTime STREAM_TIME_MAX = MEDIA_TIME_MAX; 1.22 + 1.23 +/** 1.24 + * Track rate in Hz. Maximum 1 << MEDIA_TIME_FRAC_BITS Hz. This ensures 1.25 + * calculations below don't overflow. 1.26 + */ 1.27 +typedef int32_t TrackRate; 1.28 +const TrackRate TRACK_RATE_MAX = 1 << MEDIA_TIME_FRAC_BITS; 1.29 + 1.30 +/** 1.31 + * Unique ID for track within a StreamBuffer. Tracks from different 1.32 + * StreamBuffers may have the same ID; this matters when appending StreamBuffers, 1.33 + * since tracks with the same ID are matched. Only IDs greater than 0 are allowed. 1.34 + */ 1.35 +typedef int32_t TrackID; 1.36 +const TrackID TRACK_NONE = 0; 1.37 + 1.38 +inline TrackTicks TimeToTicksRoundUp(TrackRate aRate, StreamTime aTime) 1.39 +{ 1.40 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate"); 1.41 + NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time"); 1.42 + return (aTime*aRate + (1 << MEDIA_TIME_FRAC_BITS) - 1) >> MEDIA_TIME_FRAC_BITS; 1.43 +} 1.44 + 1.45 +inline TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime) 1.46 +{ 1.47 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate"); 1.48 + NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time"); 1.49 + return (aTime*aRate) >> MEDIA_TIME_FRAC_BITS; 1.50 +} 1.51 + 1.52 +inline StreamTime TicksToTimeRoundUp(TrackRate aRate, TrackTicks aTicks) 1.53 +{ 1.54 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate"); 1.55 + NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples"); 1.56 + return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate - 1)/aRate; 1.57 +} 1.58 + 1.59 +inline StreamTime TicksToTimeRound(TrackRate aRate, TrackTicks aTicks) 1.60 +{ 1.61 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate"); 1.62 + NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples"); 1.63 + return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate/2)/aRate; 1.64 +} 1.65 + 1.66 +inline StreamTime TicksToTimeRoundDown(TrackRate aRate, TrackTicks aTicks) 1.67 +{ 1.68 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate"); 1.69 + NS_WARN_IF_FALSE(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples"); 1.70 + return (aTicks << MEDIA_TIME_FRAC_BITS)/aRate; 1.71 +} 1.72 + 1.73 +/** 1.74 + * This object contains the decoded data for a stream's tracks. 1.75 + * A StreamBuffer can be appended to. Logically a StreamBuffer only gets longer, 1.76 + * but we also have the ability to "forget" data before a certain time that 1.77 + * we know won't be used again. (We prune a whole number of seconds internally.) 1.78 + * 1.79 + * StreamBuffers should only be used from one thread at a time. 1.80 + * 1.81 + * A StreamBuffer has a set of tracks that can be of arbitrary types --- 1.82 + * the data for each track is a MediaSegment. The set of tracks can vary 1.83 + * over the timeline of the StreamBuffer. 1.84 + */ 1.85 +class StreamBuffer { 1.86 +public: 1.87 + /** 1.88 + * Every track has a start time --- when it started in the StreamBuffer. 1.89 + * It has an end flag; when false, no end point is known; when true, 1.90 + * the track ends when the data we have for the track runs out. 1.91 + * Tracks have a unique ID assigned at creation. This allows us to identify 1.92 + * the same track across StreamBuffers. A StreamBuffer should never have 1.93 + * two tracks with the same ID (even if they don't overlap in time). 1.94 + * TODO Tracks can also be enabled and disabled over time. 1.95 + * TODO Add TimeVarying<TrackTicks,bool> mEnabled. 1.96 + * Takes ownership of aSegment. 1.97 + */ 1.98 + class Track { 1.99 + public: 1.100 + Track(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment) 1.101 + : mStart(aStart), 1.102 + mSegment(aSegment), 1.103 + mRate(aRate), 1.104 + mID(aID), 1.105 + mEnded(false) 1.106 + { 1.107 + MOZ_COUNT_CTOR(Track); 1.108 + 1.109 + NS_ASSERTION(aID > TRACK_NONE, "Bad track ID"); 1.110 + NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Invalid rate"); 1.111 + NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position"); 1.112 + } 1.113 + ~Track() 1.114 + { 1.115 + MOZ_COUNT_DTOR(Track); 1.116 + } 1.117 + template <class T> T* Get() const 1.118 + { 1.119 + if (mSegment->GetType() == T::StaticType()) { 1.120 + return static_cast<T*>(mSegment.get()); 1.121 + } 1.122 + return nullptr; 1.123 + } 1.124 + MediaSegment* GetSegment() const { return mSegment; } 1.125 + TrackRate GetRate() const { return mRate; } 1.126 + TrackID GetID() const { return mID; } 1.127 + bool IsEnded() const { return mEnded; } 1.128 + TrackTicks GetStart() const { return mStart; } 1.129 + TrackTicks GetEnd() const { return mSegment->GetDuration(); } 1.130 + StreamTime GetEndTimeRoundDown() const 1.131 + { 1.132 + return mozilla::TicksToTimeRoundDown(mRate, mSegment->GetDuration()); 1.133 + } 1.134 + StreamTime GetStartTimeRoundDown() const 1.135 + { 1.136 + return mozilla::TicksToTimeRoundDown(mRate, mStart); 1.137 + } 1.138 + TrackTicks TimeToTicksRoundDown(StreamTime aTime) const 1.139 + { 1.140 + return mozilla::TimeToTicksRoundDown(mRate, aTime); 1.141 + } 1.142 + StreamTime TicksToTimeRoundDown(TrackTicks aTicks) const 1.143 + { 1.144 + return mozilla::TicksToTimeRoundDown(mRate, aTicks); 1.145 + } 1.146 + MediaSegment::Type GetType() const { return mSegment->GetType(); } 1.147 + 1.148 + void SetEnded() { mEnded = true; } 1.149 + void AppendFrom(Track* aTrack) 1.150 + { 1.151 + NS_ASSERTION(!mEnded, "Can't append to ended track"); 1.152 + NS_ASSERTION(aTrack->mID == mID, "IDs must match"); 1.153 + NS_ASSERTION(aTrack->mStart == 0, "Source track must start at zero"); 1.154 + NS_ASSERTION(aTrack->mSegment->GetType() == GetType(), "Track types must match"); 1.155 + NS_ASSERTION(aTrack->mRate == mRate, "Track rates must match"); 1.156 + 1.157 + mSegment->AppendFrom(aTrack->mSegment); 1.158 + mEnded = aTrack->mEnded; 1.159 + } 1.160 + MediaSegment* RemoveSegment() 1.161 + { 1.162 + return mSegment.forget(); 1.163 + } 1.164 + void ForgetUpTo(TrackTicks aTime) 1.165 + { 1.166 + mSegment->ForgetUpTo(aTime); 1.167 + } 1.168 + 1.169 + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.170 + { 1.171 + size_t amount = aMallocSizeOf(this); 1.172 + if (mSegment) { 1.173 + amount += mSegment->SizeOfIncludingThis(aMallocSizeOf); 1.174 + } 1.175 + return amount; 1.176 + } 1.177 + 1.178 + protected: 1.179 + friend class StreamBuffer; 1.180 + 1.181 + // Start offset is in ticks at rate mRate 1.182 + TrackTicks mStart; 1.183 + // The segment data starts at the start of the owning StreamBuffer, i.e., 1.184 + // there's mStart silence/no video at the beginning. 1.185 + nsAutoPtr<MediaSegment> mSegment; 1.186 + TrackRate mRate; // rate in ticks per second 1.187 + // Unique ID 1.188 + TrackID mID; 1.189 + // True when the track ends with the data in mSegment 1.190 + bool mEnded; 1.191 + }; 1.192 + 1.193 + class CompareTracksByID { 1.194 + public: 1.195 + bool Equals(Track* aA, Track* aB) const { 1.196 + return aA->GetID() == aB->GetID(); 1.197 + } 1.198 + bool LessThan(Track* aA, Track* aB) const { 1.199 + return aA->GetID() < aB->GetID(); 1.200 + } 1.201 + }; 1.202 + 1.203 + StreamBuffer() 1.204 + : mTracksKnownTime(0), mForgottenTime(0) 1.205 + { 1.206 + MOZ_COUNT_CTOR(StreamBuffer); 1.207 + } 1.208 + ~StreamBuffer() 1.209 + { 1.210 + MOZ_COUNT_DTOR(StreamBuffer); 1.211 + } 1.212 + 1.213 + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.214 + { 1.215 + size_t amount = 0; 1.216 + amount += mTracks.SizeOfExcludingThis(aMallocSizeOf); 1.217 + for (size_t i = 0; i < mTracks.Length(); i++) { 1.218 + amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf); 1.219 + } 1.220 + return amount; 1.221 + } 1.222 + 1.223 + /** 1.224 + * Takes ownership of aSegment. Don't do this while iterating, or while 1.225 + * holding a Track reference. 1.226 + * aSegment must have aStart worth of null data. 1.227 + */ 1.228 + Track& AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment) 1.229 + { 1.230 + NS_ASSERTION(TimeToTicksRoundDown(aRate, mTracksKnownTime) <= aStart, 1.231 + "Start time too early"); 1.232 + NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists"); 1.233 + 1.234 + return **mTracks.InsertElementSorted(new Track(aID, aRate, aStart, aSegment), 1.235 + CompareTracksByID()); 1.236 + } 1.237 + void AdvanceKnownTracksTime(StreamTime aKnownTime) 1.238 + { 1.239 + NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier"); 1.240 + mTracksKnownTime = aKnownTime; 1.241 + } 1.242 + 1.243 + /** 1.244 + * The end time for the StreamBuffer is the latest time for which we have 1.245 + * data for all tracks that haven't ended by that time. 1.246 + */ 1.247 + StreamTime GetEnd() const; 1.248 + 1.249 + /** 1.250 + * Returns the earliest time >= 0 at which all tracks have ended 1.251 + * and all their data has been played out and no new tracks can be added, 1.252 + * or STREAM_TIME_MAX if there is no such time. 1.253 + */ 1.254 + StreamTime GetAllTracksEnd() const; 1.255 + 1.256 +#ifdef DEBUG 1.257 + void DumpTrackInfo() const; 1.258 +#endif 1.259 + 1.260 + Track* FindTrack(TrackID aID); 1.261 + 1.262 + class TrackIter { 1.263 + public: 1.264 + /** 1.265 + * Iterate through the tracks of aBuffer in order of ID. 1.266 + */ 1.267 + TrackIter(const StreamBuffer& aBuffer) : 1.268 + mBuffer(&aBuffer.mTracks), mIndex(0), mMatchType(false) {} 1.269 + /** 1.270 + * Iterate through the tracks of aBuffer with type aType, in order of ID. 1.271 + */ 1.272 + TrackIter(const StreamBuffer& aBuffer, MediaSegment::Type aType) : 1.273 + mBuffer(&aBuffer.mTracks), mIndex(0), mType(aType), mMatchType(true) { FindMatch(); } 1.274 + bool IsEnded() { return mIndex >= mBuffer->Length(); } 1.275 + void Next() 1.276 + { 1.277 + ++mIndex; 1.278 + FindMatch(); 1.279 + } 1.280 + Track* get() { return mBuffer->ElementAt(mIndex); } 1.281 + Track& operator*() { return *mBuffer->ElementAt(mIndex); } 1.282 + Track* operator->() { return mBuffer->ElementAt(mIndex); } 1.283 + private: 1.284 + void FindMatch() 1.285 + { 1.286 + if (!mMatchType) 1.287 + return; 1.288 + while (mIndex < mBuffer->Length() && 1.289 + mBuffer->ElementAt(mIndex)->GetType() != mType) { 1.290 + ++mIndex; 1.291 + } 1.292 + } 1.293 + 1.294 + const nsTArray<nsAutoPtr<Track> >* mBuffer; 1.295 + uint32_t mIndex; 1.296 + MediaSegment::Type mType; 1.297 + bool mMatchType; 1.298 + }; 1.299 + friend class TrackIter; 1.300 + 1.301 + /** 1.302 + * Forget stream data before aTime; they will no longer be needed. 1.303 + * Also can forget entire tracks that have ended at or before aTime. 1.304 + * Can't be used to forget beyond GetEnd(). 1.305 + */ 1.306 + void ForgetUpTo(StreamTime aTime); 1.307 + /** 1.308 + * Returns the latest time passed to ForgetUpTo. 1.309 + */ 1.310 + StreamTime GetForgottenDuration() 1.311 + { 1.312 + return mForgottenTime; 1.313 + } 1.314 + 1.315 +protected: 1.316 + // Any new tracks added will start at or after this time. In other words, the track 1.317 + // list is complete and correct for all times less than this time. 1.318 + StreamTime mTracksKnownTime; 1.319 + StreamTime mForgottenTime; 1.320 + // All known tracks for this StreamBuffer 1.321 + nsTArray<nsAutoPtr<Track> > mTracks; 1.322 +}; 1.323 + 1.324 +} 1.325 + 1.326 +#endif /* MOZILLA_STREAMBUFFER_H_ */ 1.327 +