LCOV - code coverage report
Current view: top level - dom/media - MediaResource.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 141 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 47 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       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
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #if !defined(MediaResource_h_)
       7             : #define MediaResource_h_
       8             : 
       9             : #include "mozilla/Mutex.h"
      10             : #include "nsIChannel.h"
      11             : #include "nsIURI.h"
      12             : #include "nsISeekableStream.h"
      13             : #include "nsIStreamingProtocolController.h"
      14             : #include "nsIStreamListener.h"
      15             : #include "nsIChannelEventSink.h"
      16             : #include "nsIInterfaceRequestor.h"
      17             : #include "Intervals.h"
      18             : #include "MediaCache.h"
      19             : #include "MediaContainerType.h"
      20             : #include "MediaData.h"
      21             : #include "MediaPrefs.h"
      22             : #include "MediaResourceCallback.h"
      23             : #include "mozilla/Atomics.h"
      24             : #include "mozilla/Attributes.h"
      25             : #include "mozilla/TimeStamp.h"
      26             : #include "mozilla/UniquePtr.h"
      27             : #include "nsThreadUtils.h"
      28             : #include <algorithm>
      29             : 
      30             : // For HTTP seeking, if number of bytes needing to be
      31             : // seeked forward is less than this value then a read is
      32             : // done rather than a byte range request.
      33             : //
      34             : // If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes
      35             : // a delay of 200ms, then in that 200ms we could have simply read ahead 2MB. So
      36             : // setting SEEK_VS_READ_THRESHOLD to 1MB sounds reasonable.
      37             : static const int64_t SEEK_VS_READ_THRESHOLD = 1 * 1024 * 1024;
      38             : 
      39             : static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;
      40             : 
      41             : // Number of bytes we have accumulated before we assume the connection download
      42             : // rate can be reliably calculated. 57 Segments at IW=3 allows slow start to
      43             : // reach a CWND of 30 (See bug 831998)
      44             : static const int64_t RELIABLE_DATA_THRESHOLD = 57 * 1460;
      45             : 
      46             : class nsIHttpChannel;
      47             : class nsIPrincipal;
      48             : 
      49             : namespace mozilla {
      50             : 
      51             : class MediaChannelStatistics;
      52             : 
      53             : /**
      54             :  * This class is useful for estimating rates of data passing through
      55             :  * some channel. The idea is that activity on the channel "starts"
      56             :  * and "stops" over time. At certain times data passes through the
      57             :  * channel (usually while the channel is active; data passing through
      58             :  * an inactive channel is ignored). The GetRate() function computes
      59             :  * an estimate of the "current rate" of the channel, which is some
      60             :  * kind of average of the data passing through over the time the
      61             :  * channel is active.
      62             :  *
      63             :  * All methods take "now" as a parameter so the user of this class can
      64             :  * control the timeline used.
      65             :  */
      66             : class MediaChannelStatistics {
      67             : public:
      68           0 :   MediaChannelStatistics() = default;
      69             : 
      70             :   MediaChannelStatistics(const MediaChannelStatistics&) = default;
      71             : 
      72             :   void Reset() {
      73             :     mLastStartTime = TimeStamp();
      74             :     mAccumulatedTime = TimeDuration(0);
      75             :     mAccumulatedBytes = 0;
      76             :     mIsStarted = false;
      77             :   }
      78           0 :   void Start() {
      79           0 :     if (mIsStarted)
      80           0 :       return;
      81           0 :     mLastStartTime = TimeStamp::Now();
      82           0 :     mIsStarted = true;
      83             :   }
      84           0 :   void Stop() {
      85           0 :     if (!mIsStarted)
      86           0 :       return;
      87           0 :     mAccumulatedTime += TimeStamp::Now() - mLastStartTime;
      88           0 :     mIsStarted = false;
      89             :   }
      90           0 :   void AddBytes(int64_t aBytes) {
      91           0 :     if (!mIsStarted) {
      92             :       // ignore this data, it may be related to seeking or some other
      93             :       // operation we don't care about
      94           0 :       return;
      95             :     }
      96           0 :     mAccumulatedBytes += aBytes;
      97             :   }
      98           0 :   double GetRateAtLastStop(bool* aReliable) {
      99           0 :     double seconds = mAccumulatedTime.ToSeconds();
     100           0 :     *aReliable = (seconds >= 1.0) ||
     101           0 :                  (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
     102           0 :     if (seconds <= 0.0)
     103           0 :       return 0.0;
     104           0 :     return static_cast<double>(mAccumulatedBytes)/seconds;
     105             :   }
     106           0 :   double GetRate(bool* aReliable) {
     107           0 :     TimeDuration time = mAccumulatedTime;
     108           0 :     if (mIsStarted) {
     109           0 :       time += TimeStamp::Now() - mLastStartTime;
     110             :     }
     111           0 :     double seconds = time.ToSeconds();
     112           0 :     *aReliable = (seconds >= 3.0) ||
     113           0 :                  (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
     114           0 :     if (seconds <= 0.0)
     115           0 :       return 0.0;
     116           0 :     return static_cast<double>(mAccumulatedBytes)/seconds;
     117             :   }
     118             : private:
     119             :   int64_t mAccumulatedBytes = 0;
     120             :   TimeDuration mAccumulatedTime;
     121             :   TimeStamp mLastStartTime;
     122             :   bool mIsStarted = false;
     123             : };
     124             : 
     125             : // Represents a section of contiguous media, with a start and end offset.
     126             : // Used to denote ranges of data which are cached.
     127             : 
     128             : typedef media::Interval<int64_t> MediaByteRange;
     129             : typedef media::IntervalSet<int64_t> MediaByteRangeSet;
     130             : 
     131             : /**
     132             :  * Provides a thread-safe, seek/read interface to resources
     133             :  * loaded from a URI. Uses MediaCache to cache data received over
     134             :  * Necko's async channel API, thus resolving the mismatch between clients
     135             :  * that need efficient random access to the data and protocols that do not
     136             :  * support efficient random access, such as HTTP.
     137             :  *
     138             :  * Instances of this class must be created on the main thread.
     139             :  * Most methods must be called on the main thread only. Read, Seek and
     140             :  * Tell must only be called on non-main threads. In the case of the Ogg
     141             :  * Decoder they are called on the Decode thread for example. You must
     142             :  * ensure that no threads are calling these methods once Close is called.
     143             :  *
     144             :  * Instances of this class are reference counted. Use nsRefPtr for
     145             :  * managing the lifetime of instances of this class.
     146             :  *
     147             :  * The generic implementation of this class is ChannelMediaResource, which can
     148             :  * handle any URI for which Necko supports AsyncOpen.
     149             :  * The 'file:' protocol can be implemented efficiently with direct random
     150             :  * access, so the FileMediaResource implementation class bypasses the cache.
     151             :  * MediaResource::Create automatically chooses the best implementation class.
     152             :  */
     153           0 : class MediaResource : public nsISupports
     154             : {
     155             : public:
     156             :   // Our refcounting is threadsafe, and when our refcount drops to zero
     157             :   // we dispatch an event to the main thread to delete the MediaResource.
     158             :   // Note that this means it's safe for references to this object to be
     159             :   // released on a non main thread, but the destructor will always run on
     160             :   // the main thread.
     161             :   NS_DECL_THREADSAFE_ISUPPORTS
     162             : 
     163             :   // Close the resource, stop any listeners, channels, etc.
     164             :   // Cancels any currently blocking Read request and forces that request to
     165             :   // return an error.
     166             :   virtual nsresult Close() = 0;
     167             :   // Suspend any downloads that are in progress.
     168             :   // If aCloseImmediately is set, resources should be released immediately
     169             :   // since we don't expect to resume again any time soon. Otherwise we
     170             :   // may resume again soon so resources should be held for a little
     171             :   // while.
     172             :   virtual void Suspend(bool aCloseImmediately) = 0;
     173             :   // Resume any downloads that have been suspended.
     174             :   virtual void Resume() = 0;
     175             :   // Get the current principal for the channel
     176             :   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
     177             :   // If this returns false, then we shouldn't try to clone this MediaResource
     178             :   // because its underlying resources are not suitable for reuse (e.g.
     179             :   // because the underlying connection has been lost, or this resource
     180             :   // just can't be safely cloned). If this returns true, CloneData could
     181             :   // still fail. If this returns false, CloneData should not be called.
     182           0 :   virtual bool CanClone() { return false; }
     183             :   // Create a new stream of the same type that refers to the same URI
     184             :   // with a new channel. Any cached data associated with the original
     185             :   // stream should be accessible in the new stream too.
     186           0 :   virtual already_AddRefed<MediaResource> CloneData(
     187             :     MediaResourceCallback* aCallback)
     188             :   {
     189           0 :     return nullptr;
     190             :   }
     191             : 
     192             :   // These methods are called off the main thread.
     193             :   // The mode is initially MODE_PLAYBACK.
     194             :   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
     195             :   // This is the client's estimate of the playback rate assuming
     196             :   // the media plays continuously. The cache can't guess this itself
     197             :   // because it doesn't know when the decoder was paused, buffering, etc.
     198             :   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) = 0;
     199             :   // Read up to aCount bytes from the stream. The read starts at
     200             :   // aOffset in the stream, seeking to that location initially if
     201             :   // it is not the current stream offset. The remaining arguments,
     202             :   // results and requirements are the same as per the Read method.
     203             :   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
     204             :                           uint32_t aCount, uint32_t* aBytes) = 0;
     205             :   // Indicate whether caching data in advance of reads is worth it.
     206             :   // E.g. Caching lockless and memory-based MediaResource subclasses would be a
     207             :   // waste, but caching lock/IO-bound resources means reducing the impact of
     208             :   // each read.
     209             :   virtual bool ShouldCacheReads() = 0;
     210             : 
     211           0 :   already_AddRefed<MediaByteBuffer> CachedReadAt(int64_t aOffset, uint32_t aCount)
     212             :   {
     213           0 :     RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
     214           0 :     bool ok = bytes->SetLength(aCount, fallible);
     215           0 :     NS_ENSURE_TRUE(ok, nullptr);
     216           0 :     char* curr = reinterpret_cast<char*>(bytes->Elements());
     217           0 :     nsresult rv = ReadFromCache(curr, aOffset, aCount);
     218           0 :     NS_ENSURE_SUCCESS(rv, nullptr);
     219           0 :     return bytes.forget();
     220             :   }
     221             : 
     222             :   // Pass true to limit the amount of readahead data (specified by
     223             :   // "media.cache_readahead_limit") or false to read as much as the
     224             :   // cache size allows.
     225           0 :   virtual void ThrottleReadahead(bool bThrottle) { }
     226             : 
     227             :   // Report the current offset in bytes from the start of the stream.
     228             :   // This is used to approximate where we currently are in the playback of a
     229             :   // media.
     230             :   // A call to ReadAt will update this position.
     231             :   virtual int64_t Tell() = 0;
     232             :   // Moves any existing channel loads into or out of background. Background
     233             :   // loads don't block the load event. This also determines whether or not any
     234             :   // new loads initiated (for example to seek) will be in the background.
     235           0 :   virtual void SetLoadInBackground(bool aLoadInBackground) {}
     236             :   // Ensures that the value returned by IsSuspendedByCache below is up to date
     237             :   // (i.e. the cache has examined this stream at least once).
     238           0 :   virtual void EnsureCacheUpToDate() {}
     239             : 
     240             :   // These can be called on any thread.
     241             :   // Cached blocks associated with this stream will not be evicted
     242             :   // while the stream is pinned.
     243             :   virtual void Pin() = 0;
     244             :   virtual void Unpin() = 0;
     245             :   // Get the estimated download rate in bytes per second (assuming no
     246             :   // pausing of the channel is requested by Gecko).
     247             :   // *aIsReliable is set to true if we think the estimate is useful.
     248             :   virtual double GetDownloadRate(bool* aIsReliable) = 0;
     249             :   // Get the length of the stream in bytes. Returns -1 if not known.
     250             :   // This can change over time; after a seek operation, a misbehaving
     251             :   // server may give us a resource of a different length to what it had
     252             :   // reported previously --- or it may just lie in its Content-Length
     253             :   // header and give us more or less data than it reported. We will adjust
     254             :   // the result of GetLength to reflect the data that's actually arriving.
     255             :   virtual int64_t GetLength() = 0;
     256             :   // Returns the offset of the first byte of cached data at or after aOffset,
     257             :   // or -1 if there is no such cached data.
     258             :   virtual int64_t GetNextCachedData(int64_t aOffset) = 0;
     259             :   // Returns the end of the bytes starting at the given offset which are in
     260             :   // cache. Returns aOffset itself if there are zero bytes available there.
     261             :   virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0;
     262             :   // Returns true if all the data from aOffset to the end of the stream
     263             :   // is in cache. If the end of the stream is not known, we return false.
     264             :   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0;
     265             :   // Returns true if we are expecting any more data to arrive
     266             :   // sometime in the not-too-distant future, either from the network or from
     267             :   // an appendBuffer call on a MediaSource element.
     268           0 :   virtual bool IsExpectingMoreData()
     269             :   {
     270             :     // MediaDecoder::mDecoderPosition is roughly the same as Tell() which
     271             :     // returns a position updated by latest Read() or ReadAt().
     272           0 :     return !IsDataCachedToEndOfResource(Tell()) && !IsSuspended();
     273             :   }
     274             :   // Returns true if this stream is suspended by the cache because the
     275             :   // cache is full. If true then the decoder should try to start consuming
     276             :   // data, otherwise we may not be able to make progress.
     277             :   // MediaDecoder::NotifySuspendedStatusChanged is called when this
     278             :   // changes.
     279             :   // For resources using the media cache, this returns true only when all
     280             :   // streams for the same resource are all suspended.
     281             :   virtual bool IsSuspendedByCache() = 0;
     282             :   // Returns true if this stream has been suspended.
     283             :   virtual bool IsSuspended() = 0;
     284             :   // Reads only data which is cached in the media cache. If you try to read
     285             :   // any data which overlaps uncached data, or if aCount bytes otherwise can't
     286             :   // be read, this function will return failure. This function be called from
     287             :   // any thread, and it is the only read operation which is safe to call on
     288             :   // the main thread, since it's guaranteed to be non blocking.
     289             :   virtual nsresult ReadFromCache(char* aBuffer,
     290             :                                  int64_t aOffset,
     291             :                                  uint32_t aCount) = 0;
     292             :   // Returns true if the resource can be seeked to unbuffered ranges, i.e.
     293             :   // for an HTTP network stream this returns true if HTTP1.1 Byte Range
     294             :   // requests are supported by the connection/server.
     295             :   virtual bool IsTransportSeekable() = 0;
     296             : 
     297             :   /**
     298             :    * Create a resource, reading data from the channel. Call on main thread only.
     299             :    * The caller must follow up by calling resource->Open().
     300             :    */
     301             :   static already_AddRefed<MediaResource>
     302             :   Create(MediaResourceCallback* aCallback,
     303             :          nsIChannel* aChannel, bool aIsPrivateBrowsing);
     304             : 
     305             :   /**
     306             :    * Open the stream. This creates a stream listener and returns it in
     307             :    * aStreamListener; this listener needs to be notified of incoming data.
     308             :    */
     309             :   virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
     310             : 
     311             :   /**
     312             :    * Fills aRanges with MediaByteRanges representing the data which is cached
     313             :    * in the media cache. Stream should be pinned during call and while
     314             :    * aRanges is being used.
     315             :    */
     316             :   virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0;
     317             : 
     318             :   // Returns true if the resource is a live stream.
     319           0 :   virtual bool IsLiveStream()
     320             :   {
     321           0 :     return GetLength() == -1;
     322             :   }
     323             : 
     324           0 :   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
     325           0 :     return 0;
     326             :   }
     327             : 
     328           0 :   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     329           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     330             :   }
     331             : 
     332             : protected:
     333           0 :   virtual ~MediaResource() {};
     334             : 
     335             : private:
     336             :   void Destroy();
     337             : };
     338             : 
     339             : class BaseMediaResource : public MediaResource {
     340             : public:
     341             :   void SetLoadInBackground(bool aLoadInBackground) override;
     342             : 
     343           0 :   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
     344             :   {
     345             :     // Might be useful to track in the future:
     346             :     // - mChannel
     347             :     // - mURI (possibly owned, looks like just a ref from mChannel)
     348             :     // Not owned:
     349             :     // - mCallback
     350           0 :     size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
     351           0 :     return size;
     352             :   }
     353             : 
     354           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
     355             :   {
     356           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     357             :   }
     358             : 
     359             : protected:
     360           0 :   BaseMediaResource(MediaResourceCallback* aCallback,
     361             :                     nsIChannel* aChannel,
     362             :                     nsIURI* aURI)
     363           0 :     : mCallback(aCallback)
     364             :     , mChannel(aChannel)
     365             :     , mURI(aURI)
     366           0 :     , mLoadInBackground(false)
     367             :   {
     368           0 :   }
     369           0 :   virtual ~BaseMediaResource()
     370           0 :   {
     371           0 :   }
     372             : 
     373             :   // Set the request's load flags to aFlags.  If the request is part of a
     374             :   // load group, the request is removed from the group, the flags are set, and
     375             :   // then the request is added back to the load group.
     376             :   void ModifyLoadFlags(nsLoadFlags aFlags);
     377             : 
     378             :   // Dispatches an event to call MediaDecoder::NotifyBytesConsumed(aNumBytes, aOffset)
     379             :   // on the main thread. This is called automatically after every read.
     380             :   void DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset);
     381             : 
     382             :   RefPtr<MediaResourceCallback> mCallback;
     383             : 
     384             :   // Channel used to download the media data. Must be accessed
     385             :   // from the main thread only.
     386             :   nsCOMPtr<nsIChannel> mChannel;
     387             : 
     388             :   // URI in case the stream needs to be re-opened. Access from
     389             :   // main thread only.
     390             :   nsCOMPtr<nsIURI> mURI;
     391             : 
     392             :   // True if SetLoadInBackground() has been called with
     393             :   // aLoadInBackground = true, i.e. when the document load event is not
     394             :   // blocked by this resource, and all channel loads will be in the
     395             :   // background.
     396             :   bool mLoadInBackground;
     397             : };
     398             : 
     399             : 
     400             : /**
     401             :  * This class is responsible for managing the suspend count and report suspend
     402             :  * status of channel.
     403             :  **/
     404             : class ChannelSuspendAgent {
     405             : public:
     406           0 :   explicit ChannelSuspendAgent(nsIChannel* aChannel)
     407           0 :   : mChannel(aChannel),
     408             :     mSuspendCount(0),
     409           0 :     mIsChannelSuspended(false)
     410           0 :   {}
     411             : 
     412             :   // True when the channel has been suspended or needs to be suspended.
     413             :   bool IsSuspended();
     414             : 
     415             :   // Return true when the channel is logically suspended, i.e. the suspend
     416             :   // count goes from 0 to 1.
     417             :   bool Suspend();
     418             : 
     419             :   // Return true only when the suspend count is equal to zero.
     420             :   bool Resume();
     421             : 
     422             :   // Call after opening channel, set channel and check whether the channel
     423             :   // needs to be suspended.
     424             :   void NotifyChannelOpened(nsIChannel* aChannel);
     425             : 
     426             :   // Call before closing channel, reset the channel internal status if needed.
     427             :   void NotifyChannelClosing();
     428             : 
     429             :   // Check whether we need to suspend the channel.
     430             :   void UpdateSuspendedStatusIfNeeded();
     431             : private:
     432             :   // Only suspends channel but not changes the suspend count.
     433             :   void SuspendInternal();
     434             : 
     435             :   nsIChannel* mChannel;
     436             :   Atomic<uint32_t> mSuspendCount;
     437             :   bool mIsChannelSuspended;
     438             : };
     439             : 
     440             : /**
     441             :  * This is the MediaResource implementation that wraps Necko channels.
     442             :  * Much of its functionality is actually delegated to MediaCache via
     443             :  * an underlying MediaCacheStream.
     444             :  *
     445             :  * All synchronization is performed by MediaCacheStream; all off-main-
     446             :  * thread operations are delegated directly to that object.
     447             :  */
     448             : class ChannelMediaResource : public BaseMediaResource
     449             : {
     450             : public:
     451             :   ChannelMediaResource(MediaResourceCallback* aDecoder,
     452             :                        nsIChannel* aChannel,
     453             :                        nsIURI* aURI,
     454             :                        bool aIsPrivateBrowsing);
     455             :   ChannelMediaResource(MediaResourceCallback* aDecoder,
     456             :                        nsIChannel* aChannel,
     457             :                        nsIURI* aURI,
     458             :                        const MediaChannelStatistics& aStatistics);
     459             :   ~ChannelMediaResource();
     460             : 
     461             :   // These are called on the main thread by MediaCache. These must
     462             :   // not block or grab locks, because the media cache is holding its lock.
     463             :   // Notify that data is available from the cache. This can happen even
     464             :   // if this stream didn't read any data, since another stream might have
     465             :   // received data for the same resource.
     466             :   void CacheClientNotifyDataReceived();
     467             :   // Notify that we reached the end of the stream. This can happen even
     468             :   // if this stream didn't read any data, since another stream might have
     469             :   // received data for the same resource.
     470             :   void CacheClientNotifyDataEnded(nsresult aStatus);
     471             :   // Notify that the principal for the cached resource changed.
     472             :   void CacheClientNotifyPrincipalChanged();
     473             :   // Notify the decoder that the cache suspended status changed.
     474             :   void CacheClientNotifySuspendedStatusChanged();
     475             : 
     476             :   // These are called on the main thread by MediaCache. These shouldn't block,
     477             :   // but they may grab locks --- the media cache is not holding its lock
     478             :   // when these are called.
     479             :   // Start a new load at the given aOffset. The old load is cancelled
     480             :   // and no more data from the old load will be notified via
     481             :   // MediaCacheStream::NotifyDataReceived/Ended.
     482             :   // This can fail.
     483             :   nsresult CacheClientSeek(int64_t aOffset, bool aResume);
     484             :   // Suspend the current load since data is currently not wanted
     485             :   nsresult CacheClientSuspend();
     486             :   // Resume the current load since data is wanted again
     487             :   nsresult CacheClientResume();
     488             : 
     489             :   void ThrottleReadahead(bool bThrottle) override;
     490             : 
     491             :   // Main thread
     492             :   nsresult Open(nsIStreamListener** aStreamListener) override;
     493             :   nsresult Close() override;
     494             :   void     Suspend(bool aCloseImmediately) override;
     495             :   void     Resume() override;
     496             :   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
     497             :   // Return true if the stream has been closed.
     498             :   bool     IsClosed() const { return mCacheStream.IsClosed(); }
     499             :   bool     CanClone() override;
     500             :   already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aDecoder) override;
     501             :   nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
     502             :   void     EnsureCacheUpToDate() override;
     503             : 
     504             :   // Other thread
     505             :   void     SetReadMode(MediaCacheStream::ReadMode aMode) override;
     506             :   void     SetPlaybackRate(uint32_t aBytesPerSecond) override;
     507             :   nsresult ReadAt(int64_t offset, char* aBuffer,
     508             :                   uint32_t aCount, uint32_t* aBytes) override;
     509             :   // Data stored in IO&lock-encumbered MediaCacheStream, caching recommended.
     510           0 :   bool ShouldCacheReads() override { return true; }
     511             :   int64_t Tell() override;
     512             : 
     513             :   // Any thread
     514             :   void    Pin() override;
     515             :   void    Unpin() override;
     516             :   double  GetDownloadRate(bool* aIsReliable) override;
     517             :   int64_t GetLength() override;
     518             :   int64_t GetNextCachedData(int64_t aOffset) override;
     519             :   int64_t GetCachedDataEnd(int64_t aOffset) override;
     520             :   bool    IsDataCachedToEndOfResource(int64_t aOffset) override;
     521             :   bool    IsSuspendedByCache() override;
     522             :   bool    IsSuspended() override;
     523             :   bool    IsTransportSeekable() override;
     524             : 
     525           0 :   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
     526             :     // Might be useful to track in the future:
     527             :     //   - mListener (seems minor)
     528             :     //   - mChannelStatistics (seems minor)
     529             :     //   - mDataReceivedEvent (seems minor)
     530           0 :     size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
     531           0 :     size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
     532             : 
     533           0 :     return size;
     534             :   }
     535             : 
     536           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
     537           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     538             :   }
     539             : 
     540             :   class Listener final : public nsIStreamListener,
     541             :                          public nsIInterfaceRequestor,
     542             :                          public nsIChannelEventSink
     543             :   {
     544           0 :     ~Listener() {}
     545             :   public:
     546           0 :     explicit Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
     547             : 
     548             :     NS_DECL_ISUPPORTS
     549             :     NS_DECL_NSIREQUESTOBSERVER
     550             :     NS_DECL_NSISTREAMLISTENER
     551             :     NS_DECL_NSICHANNELEVENTSINK
     552             :     NS_DECL_NSIINTERFACEREQUESTOR
     553             : 
     554           0 :     void Revoke() { mResource = nullptr; }
     555             : 
     556             :   private:
     557             :     RefPtr<ChannelMediaResource> mResource;
     558             :   };
     559             :   friend class Listener;
     560             : 
     561             :   nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
     562             : 
     563             : protected:
     564             :   // These are called on the main thread by Listener.
     565             :   nsresult OnStartRequest(nsIRequest* aRequest);
     566             :   nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
     567             :   nsresult OnDataAvailable(nsIRequest* aRequest,
     568             :                            nsIInputStream* aStream,
     569             :                            uint32_t aCount);
     570             :   nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
     571             : 
     572             :   // Opens the channel, using an HTTP byte range request to start at mOffset
     573             :   // if possible. Main thread only.
     574             :   nsresult OpenChannel(nsIStreamListener** aStreamListener);
     575             :   nsresult RecreateChannel();
     576             :   // Add headers to HTTP request. Main thread only.
     577             :   nsresult SetupChannelHeaders();
     578             :   // Closes the channel. Main thread only.
     579             :   void CloseChannel();
     580             : 
     581             :   // Parses 'Content-Range' header and returns results via parameters.
     582             :   // Returns error if header is not available, values are not parse-able or
     583             :   // values are out of range.
     584             :   nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
     585             :                                    int64_t& aRangeStart,
     586             :                                    int64_t& aRangeEnd,
     587             :                                    int64_t& aRangeTotal);
     588             : 
     589             :   void DoNotifyDataReceived();
     590             : 
     591             :   static nsresult CopySegmentToCache(nsIInputStream* aInStream,
     592             :                                      void* aClosure,
     593             :                                      const char* aFromSegment,
     594             :                                      uint32_t aToOffset,
     595             :                                      uint32_t aCount,
     596             :                                      uint32_t* aWriteCount);
     597             : 
     598             :   nsresult CopySegmentToCache(nsIPrincipal* aPrincipal,
     599             :                               const char* aFromSegment,
     600             :                               uint32_t aCount,
     601             :                               uint32_t* aWriteCount);
     602             : 
     603             :   // Main thread access only
     604             :   int64_t            mOffset;
     605             :   RefPtr<Listener> mListener;
     606             :   // A data received event for the decoder that has been dispatched but has
     607             :   // not yet been processed.
     608             :   nsRevocableEventPtr<nsRunnableMethod<ChannelMediaResource, void, false> > mDataReceivedEvent;
     609             :   // When this flag is set, if we get a network error we should silently
     610             :   // reopen the stream.
     611             :   bool               mReopenOnError;
     612             :   // When this flag is set, we should not report the next close of the
     613             :   // channel.
     614             :   bool               mIgnoreClose;
     615             : 
     616             :   // Any thread access
     617             :   MediaCacheStream mCacheStream;
     618             : 
     619             :   // This lock protects mChannelStatistics
     620             :   Mutex               mLock;
     621             :   MediaChannelStatistics mChannelStatistics;
     622             : 
     623             :   // True if we couldn't suspend the stream and we therefore don't want
     624             :   // to resume later. This is usually due to the channel not being in the
     625             :   // isPending state at the time of the suspend request.
     626             :   bool mIgnoreResume;
     627             : 
     628             :   ChannelSuspendAgent mSuspendAgent;
     629             : };
     630             : 
     631             : /**
     632             :  * RAII class that handles pinning and unpinning for MediaResource and derived.
     633             :  * This should be used when making calculations that involve potentially-cached
     634             :  * MediaResource data, so that the state of the world can't change out from under
     635             :  * us.
     636             :  */
     637             : template<class T>
     638             : class MOZ_RAII AutoPinned {
     639             :  public:
     640           0 :   explicit AutoPinned(T* aResource MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mResource(aResource) {
     641           0 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     642           0 :     MOZ_ASSERT(mResource);
     643           0 :     mResource->Pin();
     644           0 :   }
     645             : 
     646           0 :   ~AutoPinned() {
     647           0 :     mResource->Unpin();
     648           0 :   }
     649             : 
     650           0 :   operator T*() const { return mResource; }
     651           0 :   T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
     652             : 
     653             : private:
     654             :   T* mResource;
     655             :   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     656             : };
     657             : 
     658             : /*
     659             :  * MediaResourceIndex provides a way to access MediaResource objects.
     660             :  * Read, Seek and Tell must only be called on non-main threads.
     661             :  * In the case of the Ogg Decoder they are called on the Decode thread for
     662             :  * example. You must ensure that no threads are calling these methods once
     663             :  * the MediaResource has been Closed.
     664             :  */
     665             : 
     666           0 : class MediaResourceIndex
     667             : {
     668             : public:
     669           0 :   explicit MediaResourceIndex(MediaResource* aResource)
     670           0 :     : mResource(aResource)
     671             :     , mOffset(0)
     672           0 :     , mCacheBlockSize(aResource->ShouldCacheReads()
     673           0 :                       ? SelectCacheSize(MediaPrefs::MediaResourceIndexCache())
     674             :                       : 0 )
     675             :     , mCachedOffset(0)
     676             :     , mCachedBytes(0)
     677           0 :     , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize))
     678           0 :   {}
     679             : 
     680             :   // Read up to aCount bytes from the stream. The buffer must have
     681             :   // enough room for at least aCount bytes. Stores the number of
     682             :   // actual bytes read in aBytes (0 on end of file).
     683             :   // May read less than aCount bytes if the number of
     684             :   // available bytes is less than aCount. Always check *aBytes after
     685             :   // read, and call again if necessary.
     686             :   nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
     687             :   // Seek to the given bytes offset in the stream. aWhence can be
     688             :   // one of:
     689             :   //   NS_SEEK_SET
     690             :   //   NS_SEEK_CUR
     691             :   //   NS_SEEK_END
     692             :   //
     693             :   // In the Http strategy case the cancel will cause the http
     694             :   // channel's listener to close the pipe, forcing an i/o error on any
     695             :   // blocked read. This will allow the decode thread to complete the
     696             :   // event.
     697             :   //
     698             :   // In the case of a seek in progress, the byte range request creates
     699             :   // a new listener. This is done on the main thread via seek
     700             :   // synchronously dispatching an event. This avoids the issue of us
     701             :   // closing the listener but an outstanding byte range request
     702             :   // creating a new one. They run on the same thread so no explicit
     703             :   // synchronisation is required. The byte range request checks for
     704             :   // the cancel flag and does not create a new channel or listener if
     705             :   // we are cancelling.
     706             :   //
     707             :   // The default strategy does not do any seeking - the only issue is
     708             :   // a blocked read which it handles by causing the listener to close
     709             :   // the pipe, as per the http case.
     710             :   //
     711             :   // The file strategy doesn't block for any great length of time so
     712             :   // is fine for a no-op cancel.
     713             :   nsresult Seek(int32_t aWhence, int64_t aOffset);
     714             :   // Report the current offset in bytes from the start of the stream.
     715           0 :   int64_t Tell() const { return mOffset; }
     716             : 
     717             :   // Return the underlying MediaResource.
     718           0 :   MediaResource* GetResource() const { return mResource; }
     719             : 
     720             :   // Read up to aCount bytes from the stream. The read starts at
     721             :   // aOffset in the stream, seeking to that location initially if
     722             :   // it is not the current stream offset.
     723             :   // Unlike MediaResource::ReadAt, ReadAt only returns fewer bytes than
     724             :   // requested if end of stream or an error is encountered. There is no need to
     725             :   // call it again to get more data.
     726             :   // If the resource has cached data past the end of the request, it will be
     727             :   // used to fill a local cache, which should speed up consecutive ReadAt's
     728             :   // (mostly by avoiding using the resource's IOs and locks.)
     729             :   // *aBytes will contain the number of bytes copied, even if an error occurred.
     730             :   // ReadAt doesn't have an impact on the offset returned by Tell().
     731             :   nsresult ReadAt(int64_t aOffset,
     732             :                   char* aBuffer,
     733             :                   uint32_t aCount,
     734             :                   uint32_t* aBytes);
     735             : 
     736             :   // Same as ReadAt, but doesn't try to cache around the read.
     737             :   // Useful if you know that you will not read again from the same area.
     738             :   nsresult UncachedReadAt(int64_t aOffset,
     739             :                           char* aBuffer,
     740             :                           uint32_t aCount,
     741             :                           uint32_t* aBytes) const;
     742             : 
     743             :   // Similar to ReadAt, but doesn't try to cache around the read.
     744             :   // Useful if you know that you will not read again from the same area.
     745             :   // Will attempt to read aRequestedCount+aExtraCount, repeatedly calling
     746             :   // MediaResource/ ReadAt()'s until a read returns 0 bytes (so we may actually
     747             :   // get less than aRequestedCount bytes), or until we get at least
     748             :   // aRequestedCount bytes (so we may not get any/all of the aExtraCount bytes.)
     749             :   nsresult UncachedRangedReadAt(int64_t aOffset,
     750             :                                 char* aBuffer,
     751             :                                 uint32_t aRequestedCount,
     752             :                                 uint32_t aExtraCount,
     753             :                                 uint32_t* aBytes) const;
     754             : 
     755             :   // This method returns nullptr if anything fails.
     756             :   // Otherwise, it returns an owned buffer.
     757             :   // MediaReadAt may return fewer bytes than requested if end of stream is
     758             :   // encountered. There is no need to call it again to get more data.
     759             :   // Note this method will not update mOffset.
     760           0 :   already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) const
     761             :   {
     762           0 :     RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
     763           0 :     bool ok = bytes->SetLength(aCount, fallible);
     764           0 :     NS_ENSURE_TRUE(ok, nullptr);
     765           0 :     char* curr = reinterpret_cast<char*>(bytes->Elements());
     766           0 :     const char* start = curr;
     767           0 :     while (aCount > 0) {
     768             :       uint32_t bytesRead;
     769           0 :       nsresult rv = mResource->ReadAt(aOffset, curr, aCount, &bytesRead);
     770           0 :       NS_ENSURE_SUCCESS(rv, nullptr);
     771           0 :       if (!bytesRead) {
     772           0 :         break;
     773             :       }
     774           0 :       aOffset += bytesRead;
     775           0 :       aCount -= bytesRead;
     776           0 :       curr += bytesRead;
     777             :     }
     778           0 :     bytes->SetLength(curr - start);
     779           0 :     return bytes.forget();
     780             :   }
     781             :   // Get the length of the stream in bytes. Returns -1 if not known.
     782             :   // This can change over time; after a seek operation, a misbehaving
     783             :   // server may give us a resource of a different length to what it had
     784             :   // reported previously --- or it may just lie in its Content-Length
     785             :   // header and give us more or less data than it reported. We will adjust
     786             :   // the result of GetLength to reflect the data that's actually arriving.
     787           0 :   int64_t GetLength() const { return mResource->GetLength(); }
     788             : 
     789             : private:
     790             :   // If the resource has cached data past the requested range, try to grab it
     791             :   // into our local cache.
     792             :   // If there is no cached data, or attempting to read it fails, fallback on
     793             :   // a (potentially-blocking) read of just what was requested, so that we don't
     794             :   // get unexpected side-effects by trying to read more than intended.
     795             :   nsresult CacheOrReadAt(int64_t aOffset,
     796             :                          char* aBuffer,
     797             :                          uint32_t aCount,
     798             :                          uint32_t* aBytes);
     799             : 
     800             :   // Select the next power of 2 (in range 32B-128KB, or 0 -> no cache)
     801           0 :   static uint32_t SelectCacheSize(uint32_t aHint)
     802             :   {
     803           0 :     if (aHint == 0) {
     804           0 :       return 0;
     805             :     }
     806           0 :     if (aHint <= 32) {
     807           0 :       return 32;
     808             :     }
     809           0 :     if (aHint > 64*1024) {
     810           0 :       return 128*1024;
     811             :     }
     812             :     // 32-bit next power of 2, from:
     813             :     // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
     814           0 :     aHint--;
     815           0 :     aHint |= aHint >> 1;
     816           0 :     aHint |= aHint >> 2;
     817           0 :     aHint |= aHint >> 4;
     818           0 :     aHint |= aHint >> 8;
     819           0 :     aHint |= aHint >> 16;
     820           0 :     aHint++;
     821           0 :     return aHint;
     822             :   }
     823             : 
     824             :   // Maps a file offset to a mCachedBlock index.
     825           0 :   uint32_t IndexInCache(int64_t aOffsetInFile) const
     826             :   {
     827           0 :     const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1);
     828           0 :     MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize);
     829           0 :     return index;
     830             :   }
     831             : 
     832             :   // Starting file offset of the cache block that contains a given file offset.
     833           0 :   int64_t CacheOffsetContaining(int64_t aOffsetInFile) const
     834             :   {
     835           0 :     const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1);
     836           0 :     MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile));
     837           0 :     return offset;
     838             :   }
     839             : 
     840             :   RefPtr<MediaResource> mResource;
     841             :   int64_t mOffset;
     842             : 
     843             :   // Local cache used by ReadAt().
     844             :   // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
     845             :   // data of length mCachedBytes, starting at offset `mCachedOffset` in the
     846             :   // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
     847             :   //
     848             :   // resource: |------------------------------------------------------|
     849             :   //                                          <----------> mCacheBlockSize
     850             :   //           <---------------------------------> mCachedOffset
     851             :   //                                             <--> mCachedBytes
     852             :   // mCachedBlock:                            |..----....|
     853             :   //  CacheOffsetContaining(mCachedOffset)    <--> IndexInCache(mCachedOffset)
     854             :   //           <------------------------------>
     855             :   const uint32_t mCacheBlockSize;
     856             :   int64_t mCachedOffset;
     857             :   uint32_t mCachedBytes;
     858             :   UniquePtr<char[]> mCachedBlock;
     859             : };
     860             : 
     861             : } // namespace mozilla
     862             : 
     863             : #endif

Generated by: LCOV version 1.13