tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaResource.h (13427B)


      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 "DecoderDoctorLogger.h"
     10 #  include "Intervals.h"
     11 #  include "MediaData.h"
     12 #  include "mozilla/Attributes.h"
     13 #  include "mozilla/UniquePtr.h"
     14 #  include "nsISeekableStream.h"
     15 #  include "nsThreadUtils.h"
     16 
     17 namespace mozilla {
     18 
     19 // Represents a section of contiguous media, with a start and end offset.
     20 // Used to denote ranges of data which are cached.
     21 
     22 typedef media::Interval<int64_t> MediaByteRange;
     23 typedef media::IntervalSet<int64_t> MediaByteRangeSet;
     24 
     25 DDLoggedTypeDeclName(MediaResource);
     26 
     27 /**
     28 * Provides a thread-safe, seek/read interface to resources
     29 * loaded from a URI. Uses MediaCache to cache data received over
     30 * Necko's async channel API, thus resolving the mismatch between clients
     31 * that need efficient random access to the data and protocols that do not
     32 * support efficient random access, such as HTTP.
     33 *
     34 * Instances of this class must be created on the main thread.
     35 * Most methods must be called on the main thread only. Read, Seek and
     36 * Tell must only be called on non-main threads. In the case of the Ogg
     37 * Decoder they are called on the Decode thread for example. You must
     38 * ensure that no threads are calling these methods once Close is called.
     39 *
     40 * Instances of this class are reference counted. Use nsRefPtr for
     41 * managing the lifetime of instances of this class.
     42 *
     43 * The generic implementation of this class is ChannelMediaResource, which can
     44 * handle any URI for which Necko supports AsyncOpen.
     45 * The 'file:' protocol can be implemented efficiently with direct random
     46 * access, so the FileMediaResource implementation class bypasses the cache.
     47 * For cross-process blob URL, CloneableWithRangeMediaResource is used.
     48 * MediaResource::Create automatically chooses the best implementation class.
     49 */
     50 class MediaResource : public DecoderDoctorLifeLogger<MediaResource> {
     51 public:
     52  // Our refcounting is threadsafe, and when our refcount drops to zero
     53  // we dispatch an event to the main thread to delete the MediaResource.
     54  // Note that this means it's safe for references to this object to be
     55  // released on a non main thread, but the destructor will always run on
     56  // the main thread.
     57  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
     58      MediaResource)
     59 
     60  // Close the resource, stop any listeners, channels, etc.
     61  // Cancels any currently blocking Read request and forces that request to
     62  // return an error. This must be called (and resolve) before the MediaResource
     63  // is deleted.
     64  virtual RefPtr<GenericPromise> Close() {
     65    return GenericPromise::CreateAndResolve(true, __func__);
     66  }
     67 
     68  // These methods are called off the main thread.
     69  // Read up to aCount bytes from the stream. The read starts at
     70  // aOffset in the stream, seeking to that location initially if
     71  // it is not the current stream offset.
     72  // On success, *aBytes returns the number of bytes that were actually read.
     73  // This can be less than aCount on end of stream or when remaining bytes will
     74  // not be available because a network error has occurred.
     75  // This will block until the data is available or the stream is closed.
     76  // For most resources, this will block until the data is available or the
     77  // stream is closed.
     78  // The exception is SourceBufferResource, which will return
     79  // NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA if aCount bytes are not available to
     80  // read.
     81  virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
     82                          uint32_t* aBytes) = 0;
     83  // Indicate whether caching data in advance of reads is worth it.
     84  // E.g. Caching lockless and memory-based MediaResource subclasses would be a
     85  // waste, but caching lock/IO-bound resources means reducing the impact of
     86  // each read.
     87  virtual bool ShouldCacheReads() = 0;
     88 
     89  // These can be called on any thread.
     90  // Cached blocks associated with this stream will not be evicted
     91  // while the stream is pinned.
     92  virtual void Pin() = 0;
     93  virtual void Unpin() = 0;
     94  // Get the length of the stream in bytes. Returns -1 if not known.
     95  // This can change over time; after a seek operation, a misbehaving
     96  // server may give us a resource of a different length to what it had
     97  // reported previously --- or it may just lie in its Content-Length
     98  // header and give us more or less data than it reported. We will adjust
     99  // the result of GetLength to reflect the data that's actually arriving.
    100  virtual int64_t GetLength() = 0;
    101  // Returns the offset of the first byte of cached data at or after aOffset,
    102  // or -1 if there is no such cached data.
    103  virtual int64_t GetNextCachedData(int64_t aOffset) = 0;
    104  // Returns the end of the bytes starting at the given offset which are in
    105  // cache. Returns aOffset itself if there are zero bytes available there.
    106  virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0;
    107  // Returns true if all the data from aOffset to the end of the stream
    108  // is in cache. If the end of the stream is not known, we return false.
    109  virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0;
    110  // Reads only data which is cached in the media cache. If you try to read
    111  // any data which overlaps uncached data, or if aCount bytes otherwise can't
    112  // be read, this function will return failure. This function be called from
    113  // any thread, and it is the only read operation which is safe to call on
    114  // the main thread, since it's guaranteed to be non blocking.
    115  virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
    116                                 uint32_t aCount) = 0;
    117 
    118  /**
    119   * Fills aRanges with MediaByteRanges representing the data which is cached
    120   * in the media cache. Stream should be pinned during call and while
    121   * aRanges is being used.
    122   */
    123  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0;
    124 
    125 protected:
    126  virtual ~MediaResource() = default;
    127 };
    128 
    129 /**
    130 * RAII class that handles pinning and unpinning for MediaResource and derived.
    131 * This should be used when making calculations that involve potentially-cached
    132 * MediaResource data, so that the state of the world can't change out from
    133 * under us.
    134 */
    135 template <class T>
    136 class MOZ_RAII AutoPinned {
    137 public:
    138  explicit AutoPinned(T* aResource) : mResource(aResource) {
    139    MOZ_ASSERT(mResource);
    140    mResource->Pin();
    141  }
    142 
    143  ~AutoPinned() { mResource->Unpin(); }
    144 
    145  operator T*() const { return mResource; }
    146  T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
    147 
    148 private:
    149  T* mResource;
    150 };
    151 
    152 DDLoggedTypeDeclName(MediaResourceIndex);
    153 
    154 /*
    155 * MediaResourceIndex provides a way to access MediaResource objects.
    156 * Read, Seek and Tell must only be called on non-main threads.
    157 * In the case of the Ogg Decoder they are called on the Decode thread for
    158 * example. You must ensure that no threads are calling these methods once
    159 * the MediaResource has been Closed.
    160 */
    161 class MediaResourceIndex : public DecoderDoctorLifeLogger<MediaResourceIndex> {
    162 public:
    163  explicit MediaResourceIndex(MediaResource* aResource);
    164 
    165  // Read up to aCount bytes from the stream. The buffer must have
    166  // enough room for at least aCount bytes. Stores the number of
    167  // actual bytes read in *aBytes. This can be less than aCount on end
    168  // of stream or error.
    169  nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
    170  // Seek to the given bytes offset in the stream. aWhence can be
    171  // one of:
    172  //   nsISeekableStream::NS_SEEK_SET
    173  //   nsISeekableStream::NS_SEEK_CUR
    174  //   nsISeekableStream::NS_SEEK_END
    175  //
    176  // In the Http strategy case the cancel will cause the http
    177  // channel's listener to close the pipe, forcing an i/o error on any
    178  // blocked read. This will allow the decode thread to complete the
    179  // event.
    180  //
    181  // In the case of a seek in progress, the byte range request creates
    182  // a new listener. This is done on the main thread via seek
    183  // synchronously dispatching an event. This avoids the issue of us
    184  // closing the listener but an outstanding byte range request
    185  // creating a new one. They run on the same thread so no explicit
    186  // synchronisation is required. The byte range request checks for
    187  // the cancel flag and does not create a new channel or listener if
    188  // we are cancelling.
    189  //
    190  // The default strategy does not do any seeking - the only issue is
    191  // a blocked read which it handles by causing the listener to close
    192  // the pipe, as per the http case.
    193  //
    194  // The file strategy doesn't block for any great length of time so
    195  // is fine for a no-op cancel.
    196  nsresult Seek(int32_t aWhence, int64_t aOffset);
    197  // Report the current offset in bytes from the start of the stream.
    198  int64_t Tell() const { return mOffset; }
    199 
    200  // Return the underlying MediaResource.
    201  MediaResource* GetResource() const { return mResource; }
    202 
    203  // Read up to aCount bytes from the stream. The read starts at
    204  // aOffset in the stream, seeking to that location initially if
    205  // it is not the current stream offset.
    206  // ReadAt only returns fewer bytes than
    207  // requested if end of stream or an error is encountered. There is no need to
    208  // call it again to get more data.
    209  // If the resource has cached data past the end of the request, it will be
    210  // used to fill a local cache, which should speed up consecutive ReadAt's
    211  // (mostly by avoiding using the resource's IOs and locks.)
    212  // *aBytes will contain the number of bytes copied, even if an error occurred.
    213  // ReadAt doesn't have an impact on the offset returned by Tell().
    214  nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
    215                  uint32_t* aBytes);
    216 
    217  // Same as ReadAt, but doesn't try to cache around the read.
    218  // Useful if you know that you will not read again from the same area.
    219  nsresult UncachedReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
    220                          uint32_t* aBytes) const;
    221 
    222  // Similar to ReadAt, but doesn't try to cache around the read.
    223  // Useful if you know that you will not read again from the same area.
    224  // Will attempt to read aRequestedCount+aExtraCount, repeatedly calling
    225  // MediaResource/ ReadAt()'s until a read returns 0 bytes (so we may actually
    226  // get less than aRequestedCount bytes), or until we get at least
    227  // aRequestedCount bytes (so we may not get any/all of the aExtraCount bytes.)
    228  nsresult UncachedRangedReadAt(int64_t aOffset, char* aBuffer,
    229                                uint32_t aRequestedCount, uint32_t aExtraCount,
    230                                uint32_t* aBytes) const;
    231 
    232  // This method returns nullptr if anything fails.
    233  // Otherwise, it returns an owned buffer.
    234  // MediaReadAt may return fewer bytes than requested if end of stream is
    235  // encountered. There is no need to call it again to get more data.
    236  // Note this method will not update mOffset.
    237  already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset,
    238                                                uint32_t aCount) const;
    239 
    240  already_AddRefed<MediaByteBuffer> CachedMediaReadAt(int64_t aOffset,
    241                                                      uint32_t aCount) const;
    242 
    243  // Get the length of the stream in bytes. Returns -1 if not known.
    244  // This can change over time; after a seek operation, a misbehaving
    245  // server may give us a resource of a different length to what it had
    246  // reported previously --- or it may just lie in its Content-Length
    247  // header and give us more or less data than it reported. We will adjust
    248  // the result of GetLength to reflect the data that's actually arriving.
    249  int64_t GetLength() const;
    250 
    251 private:
    252  // If the resource has cached data past the requested range, try to grab it
    253  // into our local cache.
    254  // If there is no cached data, or attempting to read it fails, fallback on
    255  // a (potentially-blocking) read of just what was requested, so that we don't
    256  // get unexpected side-effects by trying to read more than intended.
    257  nsresult CacheOrReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
    258                         uint32_t* aBytes);
    259 
    260  // Maps a file offset to a mCachedBlock index.
    261  uint32_t IndexInCache(int64_t aOffsetInFile) const;
    262 
    263  // Starting file offset of the cache block that contains a given file offset.
    264  int64_t CacheOffsetContaining(int64_t aOffsetInFile) const;
    265 
    266  RefPtr<MediaResource> mResource;
    267  int64_t mOffset;
    268 
    269  // Local cache used by ReadAt().
    270  // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
    271  // data of length mCachedBytes, starting at offset `mCachedOffset` in the
    272  // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
    273  //
    274  // resource: |------------------------------------------------------|
    275  //                                          <----------> mCacheBlockSize
    276  //           <---------------------------------> mCachedOffset
    277  //                                             <--> mCachedBytes
    278  // mCachedBlock:                            |..----....|
    279  //  CacheOffsetContaining(mCachedOffset)    <--> IndexInCache(mCachedOffset)
    280  //           <------------------------------>
    281  const uint32_t mCacheBlockSize;
    282  int64_t mCachedOffset;
    283  uint32_t mCachedBytes;
    284  UniquePtr<char[]> mCachedBlock;
    285 };
    286 
    287 }  // namespace mozilla
    288 
    289 #endif