tor-browser

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

MediaCache.h (25911B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef MediaCache_h_
      8 #define MediaCache_h_
      9 
     10 #include "DecoderDoctorLogger.h"
     11 #include "Intervals.h"
     12 #include "MediaChannelStatistics.h"
     13 #include "mozilla/Monitor.h"
     14 #include "mozilla/Result.h"
     15 #include "mozilla/UniquePtr.h"
     16 #include "mozilla/dom/MediaDebugInfoBinding.h"
     17 #include "nsCOMPtr.h"
     18 #include "nsHashKeys.h"
     19 #include "nsTArray.h"
     20 #include "nsTHashtable.h"
     21 
     22 class nsIEventTarget;
     23 class nsIPrincipal;
     24 
     25 namespace mozilla {
     26 // defined in MediaResource.h
     27 class ChannelMediaResource;
     28 typedef media::IntervalSet<int64_t> MediaByteRangeSet;
     29 class MediaResource;
     30 
     31 /**
     32 * Media applications want fast, "on demand" random access to media data,
     33 * for pausing, seeking, etc. But we are primarily interested
     34 * in transporting media data using HTTP over the Internet, which has
     35 * high latency to open a connection, requires a new connection for every
     36 * seek, may not even support seeking on some connections (especially
     37 * live streams), and uses a push model --- data comes from the server
     38 * and you don't have much control over the rate. Also, transferring data
     39 * over the Internet can be slow and/or unpredictable, so we want to read
     40 * ahead to buffer and cache as much data as possible.
     41 *
     42 * The job of the media cache is to resolve this impedance mismatch.
     43 * The media cache reads data from Necko channels into file-backed storage,
     44 * and offers a random-access file-like API to the stream data
     45 * (MediaCacheStream). Along the way it solves several problems:
     46 * -- The cache intelligently reads ahead to prefetch data that may be
     47 * needed in the future
     48 * -- The size of the cache is bounded so that we don't fill up
     49 * storage with read-ahead data
     50 * -- Cache replacement is managed globally so that the most valuable
     51 * data (across all streams) is retained
     52 * -- The cache can suspend Necko channels temporarily when their data is
     53 * not wanted (yet)
     54 * -- The cache translates file-like seek requests to HTTP seeks,
     55 * including optimizations like not triggering a new seek if it would
     56 * be faster to just keep reading until we reach the seek point. The
     57 * "seek to EOF" idiom to determine file size is also handled efficiently
     58 * (seeking to EOF and then seeking back to the previous offset does not
     59 * trigger any Necko activity)
     60 * -- The cache also handles the case where the server does not support
     61 * seeking
     62 * -- Necko can only send data to the main thread, but MediaCacheStream
     63 * can distribute data to any thread
     64 * -- The cache exposes APIs so clients can detect what data is
     65 * currently held
     66 *
     67 * Note that although HTTP is the most important transport and we only
     68 * support transport-level seeking via HTTP byte-ranges, the media cache
     69 * works with any kind of Necko channels and provides random access to
     70 * cached data even for, e.g., FTP streams.
     71 *
     72 * The media cache is not persistent. It does not currently allow
     73 * data from one load to be used by other loads, either within the same
     74 * browser session or across browser sessions. The media cache file
     75 * is marked "delete on close" so it will automatically disappear in the
     76 * event of a browser crash or shutdown.
     77 *
     78 * The media cache is block-based. Streams are divided into blocks of a
     79 * fixed size (currently 4K) and we cache blocks. A single cache contains
     80 * blocks for all streams.
     81 *
     82 * The cache size is controlled by the media.cache_size preference
     83 * (which is in KB). The default size is 500MB.
     84 *
     85 * The replacement policy predicts a "time of next use" for each block
     86 * in the cache. When we need to free a block, the block with the latest
     87 * "time of next use" will be evicted. Blocks are divided into
     88 * different classes, each class having its own predictor:
     89 * FREE_BLOCK: these blocks are effectively infinitely far in the future;
     90 * a free block will always be chosen for replacement before other classes
     91 * of blocks.
     92 * METADATA_BLOCK: these are blocks that contain data that has been read
     93 * by the decoder in "metadata mode", e.g. while the decoder is searching
     94 * the stream during a seek operation. These blocks are managed with an
     95 * LRU policy; the "time of next use" is predicted to be as far in the
     96 * future as the last use was in the past.
     97 * PLAYED_BLOCK: these are blocks that have not been read in "metadata
     98 * mode", and contain data behind the current decoder read point. (They
     99 * may not actually have been read by the decoder, if the decoder seeked
    100 * forward.) These blocks are managed with an LRU policy except that we add
    101 * REPLAY_DELAY seconds of penalty to their predicted "time of next use",
    102 * to reflect the uncertainty about whether replay will actually happen
    103 * or not.
    104 * READAHEAD_BLOCK: these are blocks that have not been read in
    105 * "metadata mode" and that are entirely ahead of the current decoder
    106 * read point. (They may actually have been read by the decoder in the
    107 * past if the decoder has since seeked backward.) We predict the
    108 * time of next use for these blocks by assuming steady playback and
    109 * dividing the number of bytes between the block and the current decoder
    110 * read point by the decoder's estimate of its playback rate in bytes
    111 * per second. This ensures that the blocks farthest ahead are considered
    112 * least valuable.
    113 * For efficient prediction of the "latest time of next use", we maintain
    114 * linked lists of blocks in each class, ordering blocks by time of
    115 * next use. READAHEAD_BLOCKS have one linked list per stream, since their
    116 * time of next use depends on stream parameters, but the other lists
    117 * are global.
    118 *
    119 * A block containing a current decoder read point can contain data
    120 * both behind and ahead of the read point. It will be classified as a
    121 * PLAYED_BLOCK but we will give it special treatment so it is never
    122 * evicted --- it actually contains the highest-priority readahead data
    123 * as well as played data.
    124 *
    125 * "Time of next use" estimates are also used for flow control. When
    126 * reading ahead we can predict the time of next use for the data that
    127 * will be read. If the predicted time of next use is later then the
    128 * prediction for all currently cached blocks, and the cache is full, then
    129 * we should suspend reading from the Necko channel.
    130 *
    131 * Unfortunately suspending the Necko channel can't immediately stop the
    132 * flow of data from the server. First our desire to suspend has to be
    133 * transmitted to the server (in practice, Necko stops reading from the
    134 * socket, which causes the kernel to shrink its advertised TCP receive
    135 * window size to zero). Then the server can stop sending the data, but
    136 * we will receive data roughly corresponding to the product of the link
    137 * bandwidth multiplied by the round-trip latency. We deal with this by
    138 * letting the cache overflow temporarily and then trimming it back by
    139 * moving overflowing blocks back into the body of the cache, replacing
    140 * less valuable blocks as they become available. We try to avoid simply
    141 * discarding overflowing readahead data.
    142 *
    143 * All changes to the actual contents of the cache happen on the main
    144 * thread, since that's where Necko's notifications happen.
    145 *
    146 * The media cache maintains at most one Necko channel for each stream.
    147 * (In the future it might be advantageous to relax this, e.g. so that a
    148 * seek to near the end of the file can happen without disturbing
    149 * the loading of data from the beginning of the file.) The Necko channel
    150 * is managed through ChannelMediaResource; MediaCache does not
    151 * depend on Necko directly.
    152 *
    153 * Every time something changes that might affect whether we want to
    154 * read from a Necko channel, or whether we want to seek on the Necko
    155 * channel --- such as data arriving or data being consumed by the
    156 * decoder --- we asynchronously trigger MediaCache::Update on the main
    157 * thread. That method implements most cache policy. It evaluates for
    158 * each stream whether we want to suspend or resume the stream and what
    159 * offset we should seek to, if any. It is also responsible for trimming
    160 * back the cache size to its desired limit by moving overflowing blocks
    161 * into the main part of the cache.
    162 *
    163 * Streams can be opened in non-seekable mode. In non-seekable mode,
    164 * the cache will only call ChannelMediaResource::CacheClientSeek with
    165 * a 0 offset. The cache tries hard not to discard readahead data
    166 * for non-seekable streams, since that could trigger a potentially
    167 * disastrous re-read of the entire stream. It's up to cache clients
    168 * to try to avoid requesting seeks on such streams.
    169 *
    170 * MediaCache has a single internal monitor for all synchronization.
    171 * This is treated as the lowest level monitor in the media code. So,
    172 * we must not acquire any MediaDecoder locks or MediaResource locks
    173 * while holding the MediaCache lock. But it's OK to hold those locks
    174 * and then get the MediaCache lock.
    175 *
    176 * MediaCache associates a principal with each stream. CacheClientSeek
    177 * can trigger new HTTP requests; due to redirects to other domains,
    178 * each HTTP load can return data with a different principal. This
    179 * principal must be passed to NotifyDataReceived, and MediaCache
    180 * will detect when different principals are associated with data in the
    181 * same stream, and replace them with a null principal.
    182 */
    183 class MediaCache;
    184 
    185 DDLoggedTypeDeclName(MediaCacheStream);
    186 
    187 /**
    188 * If the cache fails to initialize then Init will fail, so nonstatic
    189 * methods of this class can assume gMediaCache is non-null.
    190 *
    191 * This class can be directly embedded as a value.
    192 */
    193 class MediaCacheStream : public DecoderDoctorLifeLogger<MediaCacheStream> {
    194  using AutoLock = MonitorAutoLock;
    195 
    196 public:
    197  // This needs to be a power of two
    198  static constexpr int64_t BLOCK_SIZE = 32768;
    199 
    200  enum ReadMode { MODE_METADATA, MODE_PLAYBACK };
    201 
    202  // aClient provides the underlying transport that cache will use to read
    203  // data for this stream.
    204  MediaCacheStream(ChannelMediaResource* aClient, bool aIsPrivateBrowsing);
    205  ~MediaCacheStream();
    206 
    207  // Set up this stream with the cache. Can fail on OOM.
    208  // aContentLength is the content length if known, otherwise -1.
    209  // Exactly one of InitAsClone or Init must be called before any other method
    210  // on this class. Does nothing if already initialized.
    211  nsresult Init(int64_t aContentLength);
    212 
    213  // Set up this stream with the cache, assuming it's for the same data
    214  // as the aOriginal stream.
    215  // Exactly one of InitAsClone or Init must be called before any other method
    216  // on this class.
    217  void InitAsClone(MediaCacheStream* aOriginal);
    218 
    219  nsISerialEventTarget* OwnerThread() const;
    220 
    221  // These are called on the main thread.
    222  // This must be called (and resolve) before the ChannelMediaResource
    223  // used to create this MediaCacheStream is deleted.
    224  RefPtr<GenericPromise> Close();
    225  // This returns true when the stream has been closed.
    226  bool IsClosed(AutoLock&) const { return mClosed; }
    227  // Returns true when this stream is can be shared by a new resource load.
    228  // Called on the main thread only.
    229  bool IsAvailableForSharing() const { return !mIsPrivateBrowsing; }
    230 
    231  // These callbacks are called on the main thread by the client
    232  // when data has been received via the channel.
    233 
    234  // Notifies the cache that a load has begun. We pass the offset
    235  // because in some cases the offset might not be what the cache
    236  // requested. In particular we might unexpectedly start providing
    237  // data at offset 0. This need not be called if the offset is the
    238  // offset that the cache requested in
    239  // ChannelMediaResource::CacheClientSeek. This can be called at any
    240  // time by the client, not just after a CacheClientSeek.
    241  //
    242  // aSeekable tells us whether the stream is seekable or not. Non-seekable
    243  // streams will always pass 0 for aOffset to CacheClientSeek. This should only
    244  // be called while the stream is at channel offset 0. Seekability can
    245  // change during the lifetime of the MediaCacheStream --- every time
    246  // we do an HTTP load the seekability may be different (and sometimes
    247  // is, in practice, due to the effects of caching proxies).
    248  //
    249  // aLength tells the cache what the server said the data length is going to
    250  // be. The actual data length may be greater (we receive more data than
    251  // specified) or smaller (the stream ends before we reach the given
    252  // length), because servers can lie. The server's reported data length
    253  // *and* the actual data length can even vary over time because a
    254  // misbehaving server may feed us a different stream after each seek
    255  // operation. So this is really just a hint. The cache may however
    256  // stop reading (suspend the channel) when it thinks we've read all the
    257  // data available based on an incorrect reported length. Seeks relative
    258  // EOF also depend on the reported length if we haven't managed to
    259  // read the whole stream yet.
    260  void NotifyDataStarted(uint32_t aLoadID, int64_t aOffset, bool aSeekable,
    261                         int64_t aLength);
    262  // Notifies the cache that data has been received. The stream already
    263  // knows the offset because data is received in sequence and
    264  // the starting offset is known via NotifyDataStarted or because
    265  // the cache requested the offset in
    266  // ChannelMediaResource::CacheClientSeek, or because it defaulted to 0.
    267  void NotifyDataReceived(uint32_t aLoadID, uint32_t aCount,
    268                          const uint8_t* aData);
    269 
    270  // Set the load ID so the following NotifyDataEnded() call can work properly.
    271  // Used in some rare cases where NotifyDataEnded() is called without the
    272  // preceding NotifyDataStarted().
    273  void NotifyLoadID(uint32_t aLoadID);
    274 
    275  // Notifies the cache that the channel has closed with the given status.
    276  void NotifyDataEnded(uint32_t aLoadID, nsresult aStatus);
    277 
    278  // Notifies the stream that the suspend status of the client has changed.
    279  // Main thread only.
    280  void NotifyClientSuspended(bool aSuspended);
    281 
    282  // Notifies the stream to resume download at the current offset.
    283  void NotifyResume();
    284 
    285  // These methods can be called on any thread.
    286  // Cached blocks associated with this stream will not be evicted
    287  // while the stream is pinned.
    288  void Pin();
    289  void Unpin();
    290  // See comments above for NotifyDataStarted about how the length
    291  // can vary over time. Returns -1 if no length is known. Returns the
    292  // reported length if we haven't got any better information. If
    293  // the stream ended normally we return the length we actually got.
    294  // If we've successfully read data beyond the originally reported length,
    295  // we return the end of the data we've read.
    296  int64_t GetLength() const;
    297  // Return the length and offset where next channel data will write to. Main
    298  // thread only.
    299  // This method should be removed as part of bug 1464045.
    300  struct LengthAndOffset {
    301    int64_t mLength;
    302    int64_t mOffset;
    303  };
    304  LengthAndOffset GetLengthAndOffset() const;
    305  // Returns the unique resource ID. Call only on the main thread or while
    306  // holding the media cache lock.
    307  int64_t GetResourceID() { return mResourceID; }
    308  // Returns the end of the bytes starting at the given offset
    309  // which are in cache.
    310  int64_t GetCachedDataEnd(int64_t aOffset);
    311  // Returns the offset of the first byte of cached data at or after aOffset,
    312  // or -1 if there is no such cached data.
    313  int64_t GetNextCachedData(int64_t aOffset);
    314  // Fills aRanges with the ByteRanges representing the data which is currently
    315  // cached. Locks the media cache while running, to prevent any ranges
    316  // growing. The stream should be pinned while this runs and while its results
    317  // are used, to ensure no data is evicted.
    318  nsresult GetCachedRanges(MediaByteRangeSet& aRanges);
    319 
    320  double GetDownloadRate(bool* aIsReliable);
    321 
    322  // Reads from buffered data only. Will fail if not all data to be read is
    323  // in the cache. Will not mark blocks as read. Can be called from the main
    324  // thread. It's the caller's responsibility to wrap the call in a pin/unpin,
    325  // and also to check that the range they want is cached before calling this.
    326  nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
    327 
    328  // IsDataCachedToEndOfStream returns true if all the data from
    329  // aOffset to the end of the stream (the server-reported end, if the
    330  // real end is not known) is in cache. If we know nothing about the
    331  // end of the stream, this returns false.
    332  bool IsDataCachedToEndOfStream(int64_t aOffset);
    333  // The mode is initially MODE_METADATA.
    334  void SetReadMode(ReadMode aMode);
    335  // This is the client's estimate of the playback rate assuming
    336  // the media plays continuously. The cache can't guess this itself
    337  // because it doesn't know when the decoder was paused, buffering, etc.
    338  // Do not pass zero.
    339  void SetPlaybackRate(uint32_t aBytesPerSecond);
    340 
    341  // Returns true when all streams for this resource are suspended or their
    342  // channel has ended.
    343  bool AreAllStreamsForResourceSuspended(AutoLock&);
    344 
    345  // These methods must be called on a different thread from the main
    346  // thread. They should always be called on the same thread for a given
    347  // stream.
    348  // On success, *aBytes returns the number of bytes that were actually read.
    349  // This can be less than aCount on end of stream or when remaining bytes will
    350  // not be available because a network error has occurred.
    351  // This will block until the data is available or the stream is closed.
    352  nsresult Read(AutoLock&, char* aBuffer, uint32_t aCount, uint32_t* aBytes);
    353  // Seeks to aOffset in the stream then performs a Read operation. See
    354  // 'Read' for argument and return details.
    355  nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
    356                  uint32_t* aBytes);
    357 
    358  void ThrottleReadahead(bool bThrottle);
    359 
    360  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
    361 
    362  void GetDebugInfo(dom::MediaCacheStreamDebugInfo& aInfo);
    363 
    364 private:
    365  friend class MediaCache;
    366 
    367  /**
    368   * A doubly-linked list of blocks. Add/Remove/Get methods are all
    369   * constant time. We declare this here so that a stream can contain a
    370   * BlockList of its read-ahead blocks. Blocks are referred to by index
    371   * into the MediaCache::mIndex array.
    372   *
    373   * Blocks can belong to more than one list at the same time, because
    374   * the next/prev pointers are not stored in the block.
    375   */
    376  class BlockList {
    377   public:
    378    BlockList() : mFirstBlock(-1), mCount(0) {}
    379    ~BlockList() {
    380      NS_ASSERTION(mFirstBlock == -1 && mCount == 0,
    381                   "Destroying non-empty block list");
    382    }
    383    void AddFirstBlock(int32_t aBlock);
    384    void AddAfter(int32_t aBlock, int32_t aBefore);
    385    void RemoveBlock(int32_t aBlock);
    386    // Returns the first block in the list, or -1 if empty
    387    int32_t GetFirstBlock() const { return mFirstBlock; }
    388    // Returns the last block in the list, or -1 if empty
    389    int32_t GetLastBlock() const;
    390    // Returns the next block in the list after aBlock or -1 if
    391    // aBlock is the last block
    392    int32_t GetNextBlock(int32_t aBlock) const;
    393    // Returns the previous block in the list before aBlock or -1 if
    394    // aBlock is the first block
    395    int32_t GetPrevBlock(int32_t aBlock) const;
    396    bool IsEmpty() const { return mFirstBlock < 0; }
    397    int32_t GetCount() const { return mCount; }
    398    // The contents of aBlockIndex1 and aBlockIndex2 have been swapped
    399    void NotifyBlockSwapped(int32_t aBlockIndex1, int32_t aBlockIndex2);
    400 #ifdef DEBUG
    401    // Verify linked-list invariants
    402    void Verify();
    403 #else
    404    void Verify() {}
    405 #endif
    406 
    407    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
    408 
    409   private:
    410    struct Entry : public nsUint32HashKey {
    411      explicit Entry(KeyTypePointer aKey)
    412          : nsUint32HashKey(aKey), mNextBlock(0), mPrevBlock(0) {}
    413      Entry(const Entry& toCopy)
    414          : nsUint32HashKey(&toCopy.GetKey()),
    415            mNextBlock(toCopy.mNextBlock),
    416            mPrevBlock(toCopy.mPrevBlock) {}
    417 
    418      int32_t mNextBlock;
    419      int32_t mPrevBlock;
    420    };
    421    nsTHashtable<Entry> mEntries;
    422 
    423    // The index of the first block in the list, or -1 if the list is empty.
    424    int32_t mFirstBlock;
    425    // The number of blocks in the list.
    426    int32_t mCount;
    427  };
    428 
    429  // Read data from the partial block and return the number of bytes read
    430  // successfully. 0 if aOffset is not an offset in the partial block or there
    431  // is nothing to read.
    432  uint32_t ReadPartialBlock(AutoLock&, int64_t aOffset, Span<char> aBuffer);
    433 
    434  // Read data from the cache block specified by aOffset. Return the number of
    435  // bytes read successfully or an error code if any failure.
    436  Result<uint32_t, nsresult> ReadBlockFromCache(AutoLock&, int64_t aOffset,
    437                                                Span<char> aBuffer,
    438                                                bool aNoteBlockUsage = false);
    439 
    440  // Non-main thread only.
    441  nsresult Seek(AutoLock&, int64_t aOffset);
    442 
    443  // Returns the end of the bytes starting at the given offset
    444  // which are in cache.
    445  // This method assumes that the cache monitor is held and can be called on
    446  // any thread.
    447  int64_t GetCachedDataEndInternal(AutoLock&, int64_t aOffset);
    448  // Returns the offset of the first byte of cached data at or after aOffset,
    449  // or -1 if there is no such cached data.
    450  // This method assumes that the cache monitor is held and can be called on
    451  // any thread.
    452  int64_t GetNextCachedDataInternal(AutoLock&, int64_t aOffset);
    453  // Used by |NotifyDataEnded| to write |mPartialBlock| to disk.
    454  // This function will wake up readers who may be
    455  // waiting on the media cache monitor. Called on the media cache thread only.
    456  void FlushPartialBlockInternal(AutoLock&);
    457 
    458  void NotifyDataStartedInternal(uint32_t aLoadID, int64_t aOffset,
    459                                 bool aSeekable, int64_t aLength);
    460 
    461  void NotifyDataEndedInternal(uint32_t aLoadID, nsresult aStatus);
    462 
    463  void UpdateDownloadStatistics(AutoLock&);
    464 
    465  void CloseInternal(AutoLock&);
    466  void InitAsCloneInternal(MediaCacheStream* aOriginal);
    467 
    468  // Instance of MediaCache to use with this MediaCacheStream.
    469  RefPtr<MediaCache> mMediaCache;
    470 
    471  ChannelMediaResource* const mClient;
    472 
    473  // The following fields must be written holding the cache's monitor and
    474  // only on the main thread, thus can be read either on the main thread
    475  // or while holding the cache's monitor.
    476 
    477  // Set to true when the stream has been closed either explicitly or
    478  // due to an internal cache error
    479  bool mClosed = false;
    480  // This is a unique ID representing the resource we're loading.
    481  // All streams with the same mResourceID are loading the same
    482  // underlying resource and should share data.
    483  // Initialized to 0 as invalid. Will be allocated a valid ID (always positive)
    484  // from the cache.
    485  int64_t mResourceID = 0;
    486  // The last reported seekability state for the underlying channel
    487  bool mIsTransportSeekable;
    488  // True if the cache has suspended our channel because the cache is
    489  // full and the priority of the data that would be received is lower
    490  // than the priority of the data already in the cache
    491  bool mCacheSuspended;
    492  // True if the channel ended and we haven't seeked it again.
    493  bool mChannelEnded;
    494 
    495  // The following fields are protected by the cache's monitor and can be
    496  // written by any thread.
    497 
    498  // The reported or discovered length of the data, or -1 if nothing is known
    499  int64_t mStreamLength = -1;
    500  // The offset where the next data from the channel will arrive
    501  int64_t mChannelOffset = 0;
    502  // The offset where the reader is positioned in the stream
    503  int64_t mStreamOffset;
    504  // For each block in the stream data, maps to the cache entry for the
    505  // block, or -1 if the block is not cached.
    506  nsTArray<int32_t> mBlocks;
    507  // The list of read-ahead blocks, ordered by stream offset; the first
    508  // block is the earliest in the stream (so the last block will be the
    509  // least valuable).
    510  BlockList mReadaheadBlocks;
    511  // The list of metadata blocks; the first block is the most recently used
    512  BlockList mMetadataBlocks;
    513  // The list of played-back blocks; the first block is the most recently used
    514  BlockList mPlayedBlocks;
    515  // The last reported estimate of the decoder's playback rate
    516  uint32_t mPlaybackBytesPerSecond;
    517  // The number of times this stream has been Pinned without a
    518  // corresponding Unpin
    519  uint32_t mPinCount;
    520  // True if CacheClientNotifyDataEnded has been called for this stream.
    521  bool mDidNotifyDataEnded = false;
    522  // The status used when we did CacheClientNotifyDataEnded. Only valid
    523  // when mDidNotifyDataEnded is true.
    524  nsresult mNotifyDataEndedStatus;
    525  // The last reported read mode
    526  ReadMode mCurrentMode = MODE_METADATA;
    527  // The load ID of the current channel. Used to check whether the data is
    528  // coming from an old channel and should be discarded.
    529  uint32_t mLoadID = 0;
    530  // The seek target initiated by MediaCache. -1 if no seek is going on.
    531  int64_t mSeekTarget = -1;
    532 
    533  bool mThrottleReadahead = false;
    534 
    535  // Data received for the block containing mChannelOffset. Data needs
    536  // to wait here so we can write back a complete block. The first
    537  // mChannelOffset%BLOCK_SIZE bytes have been filled in with good data,
    538  // the rest are garbage.
    539  // Heap allocate this buffer since the exact power-of-2 will cause allocation
    540  // slop when combined with the rest of the object members.
    541  // This partial buffer should always be read/write within the cache's monitor.
    542  const UniquePtr<uint8_t[]> mPartialBlockBuffer =
    543      MakeUnique<uint8_t[]>(BLOCK_SIZE);
    544 
    545  // True if associated with a private browsing window.
    546  const bool mIsPrivateBrowsing;
    547 
    548  // True if the client is suspended. Accessed on the owner thread only.
    549  bool mClientSuspended = false;
    550 
    551  MediaChannelStatistics mDownloadStatistics;
    552 };
    553 
    554 }  // namespace mozilla
    555 
    556 #endif