/src/mozilla-central/dom/media/MediaCache.h
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/Result.h" |
13 | | #include "mozilla/UniquePtr.h" |
14 | | #include "nsCOMPtr.h" |
15 | | #include "nsHashKeys.h" |
16 | | #include "nsTArray.h" |
17 | | #include "nsTHashtable.h" |
18 | | |
19 | | #include "MediaChannelStatistics.h" |
20 | | |
21 | | class nsIEventTarget; |
22 | | class nsIPrincipal; |
23 | | |
24 | | namespace mozilla { |
25 | | // defined in MediaResource.h |
26 | | class ChannelMediaResource; |
27 | | typedef media::IntervalSet<int64_t> MediaByteRangeSet; |
28 | | class MediaResource; |
29 | | class MonitorAutoLock; |
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 | | { |
195 | | using AutoLock = MonitorAutoLock; |
196 | | |
197 | | public: |
198 | | // This needs to be a power of two |
199 | | static const int64_t BLOCK_SIZE = 32768; |
200 | | |
201 | | enum ReadMode { |
202 | | MODE_METADATA, |
203 | | MODE_PLAYBACK |
204 | | }; |
205 | | |
206 | | // aClient provides the underlying transport that cache will use to read |
207 | | // data for this stream. |
208 | | MediaCacheStream(ChannelMediaResource* aClient, bool aIsPrivateBrowsing); |
209 | | ~MediaCacheStream(); |
210 | | |
211 | | // Set up this stream with the cache. Can fail on OOM. |
212 | | // aContentLength is the content length if known, otherwise -1. |
213 | | // Exactly one of InitAsClone or Init must be called before any other method |
214 | | // on this class. Does nothing if already initialized. |
215 | | nsresult Init(int64_t aContentLength); |
216 | | |
217 | | // Set up this stream with the cache, assuming it's for the same data |
218 | | // as the aOriginal stream. |
219 | | // Exactly one of InitAsClone or Init must be called before any other method |
220 | | // on this class. |
221 | | void InitAsClone(MediaCacheStream* aOriginal); |
222 | | |
223 | | nsIEventTarget* OwnerThread() const; |
224 | | |
225 | | // These are called on the main thread. |
226 | | // This must be called (and return) before the ChannelMediaResource |
227 | | // used to create this MediaCacheStream is deleted. |
228 | | void Close(); |
229 | | // This returns true when the stream has been closed. |
230 | 0 | bool IsClosed(AutoLock&) const { return mClosed; } |
231 | | // Returns true when this stream is can be shared by a new resource load. |
232 | | // Called on the main thread only. |
233 | 0 | bool IsAvailableForSharing() const { return !mIsPrivateBrowsing; } |
234 | | |
235 | | // These callbacks are called on the main thread by the client |
236 | | // when data has been received via the channel. |
237 | | |
238 | | // Notifies the cache that a load has begun. We pass the offset |
239 | | // because in some cases the offset might not be what the cache |
240 | | // requested. In particular we might unexpectedly start providing |
241 | | // data at offset 0. This need not be called if the offset is the |
242 | | // offset that the cache requested in |
243 | | // ChannelMediaResource::CacheClientSeek. This can be called at any |
244 | | // time by the client, not just after a CacheClientSeek. |
245 | | // |
246 | | // aSeekable tells us whether the stream is seekable or not. Non-seekable |
247 | | // streams will always pass 0 for aOffset to CacheClientSeek. This should only |
248 | | // be called while the stream is at channel offset 0. Seekability can |
249 | | // change during the lifetime of the MediaCacheStream --- every time |
250 | | // we do an HTTP load the seekability may be different (and sometimes |
251 | | // is, in practice, due to the effects of caching proxies). |
252 | | // |
253 | | // aLength tells the cache what the server said the data length is going to |
254 | | // be. The actual data length may be greater (we receive more data than |
255 | | // specified) or smaller (the stream ends before we reach the given |
256 | | // length), because servers can lie. The server's reported data length |
257 | | // *and* the actual data length can even vary over time because a |
258 | | // misbehaving server may feed us a different stream after each seek |
259 | | // operation. So this is really just a hint. The cache may however |
260 | | // stop reading (suspend the channel) when it thinks we've read all the |
261 | | // data available based on an incorrect reported length. Seeks relative |
262 | | // EOF also depend on the reported length if we haven't managed to |
263 | | // read the whole stream yet. |
264 | | void NotifyDataStarted(uint32_t aLoadID, |
265 | | int64_t aOffset, |
266 | | bool aSeekable, |
267 | | int64_t aLength); |
268 | | // Notifies the cache that data has been received. The stream already |
269 | | // knows the offset because data is received in sequence and |
270 | | // the starting offset is known via NotifyDataStarted or because |
271 | | // the cache requested the offset in |
272 | | // ChannelMediaResource::CacheClientSeek, or because it defaulted to 0. |
273 | | void NotifyDataReceived(uint32_t aLoadID, |
274 | | uint32_t aCount, |
275 | | const uint8_t* aData); |
276 | | |
277 | | // Set the load ID so the following NotifyDataEnded() call can work properly. |
278 | | // Used in some rare cases where NotifyDataEnded() is called without the |
279 | | // preceding NotifyDataStarted(). |
280 | | void NotifyLoadID(uint32_t aLoadID); |
281 | | |
282 | | // Notifies the cache that the channel has closed with the given status. |
283 | | void NotifyDataEnded(uint32_t aLoadID, nsresult aStatus); |
284 | | |
285 | | // Notifies the stream that the suspend status of the client has changed. |
286 | | // Main thread only. |
287 | | void NotifyClientSuspended(bool aSuspended); |
288 | | |
289 | | // Notifies the stream to resume download at the current offset. |
290 | | void NotifyResume(); |
291 | | |
292 | | // These methods can be called on any thread. |
293 | | // Cached blocks associated with this stream will not be evicted |
294 | | // while the stream is pinned. |
295 | | void Pin(); |
296 | | void Unpin(); |
297 | | // See comments above for NotifyDataStarted about how the length |
298 | | // can vary over time. Returns -1 if no length is known. Returns the |
299 | | // reported length if we haven't got any better information. If |
300 | | // the stream ended normally we return the length we actually got. |
301 | | // If we've successfully read data beyond the originally reported length, |
302 | | // we return the end of the data we've read. |
303 | | int64_t GetLength() const; |
304 | | // Return the length and offset where next channel data will write to. Main |
305 | | // thread only. |
306 | | // This method should be removed as part of bug 1464045. |
307 | | struct LengthAndOffset |
308 | | { |
309 | | int64_t mLength; |
310 | | int64_t mOffset; |
311 | | }; |
312 | | LengthAndOffset GetLengthAndOffset() const; |
313 | | // Returns the unique resource ID. Call only on the main thread or while |
314 | | // holding the media cache lock. |
315 | 0 | int64_t GetResourceID() { return mResourceID; } |
316 | | // Returns the end of the bytes starting at the given offset |
317 | | // which are in cache. |
318 | | int64_t GetCachedDataEnd(int64_t aOffset); |
319 | | // Returns the offset of the first byte of cached data at or after aOffset, |
320 | | // or -1 if there is no such cached data. |
321 | | int64_t GetNextCachedData(int64_t aOffset); |
322 | | // Fills aRanges with the ByteRanges representing the data which is currently |
323 | | // cached. Locks the media cache while running, to prevent any ranges |
324 | | // growing. The stream should be pinned while this runs and while its results |
325 | | // are used, to ensure no data is evicted. |
326 | | nsresult GetCachedRanges(MediaByteRangeSet& aRanges); |
327 | | |
328 | | double GetDownloadRate(bool* aIsReliable); |
329 | | |
330 | | // Reads from buffered data only. Will fail if not all data to be read is |
331 | | // in the cache. Will not mark blocks as read. Can be called from the main |
332 | | // thread. It's the caller's responsibility to wrap the call in a pin/unpin, |
333 | | // and also to check that the range they want is cached before calling this. |
334 | | nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount); |
335 | | |
336 | | // IsDataCachedToEndOfStream returns true if all the data from |
337 | | // aOffset to the end of the stream (the server-reported end, if the |
338 | | // real end is not known) is in cache. If we know nothing about the |
339 | | // end of the stream, this returns false. |
340 | | bool IsDataCachedToEndOfStream(int64_t aOffset); |
341 | | // The mode is initially MODE_PLAYBACK. |
342 | | void SetReadMode(ReadMode aMode); |
343 | | // This is the client's estimate of the playback rate assuming |
344 | | // the media plays continuously. The cache can't guess this itself |
345 | | // because it doesn't know when the decoder was paused, buffering, etc. |
346 | | // Do not pass zero. |
347 | | void SetPlaybackRate(uint32_t aBytesPerSecond); |
348 | | |
349 | | // Returns true when all streams for this resource are suspended or their |
350 | | // channel has ended. |
351 | | bool AreAllStreamsForResourceSuspended(AutoLock&); |
352 | | |
353 | | // These methods must be called on a different thread from the main |
354 | | // thread. They should always be called on the same thread for a given |
355 | | // stream. |
356 | | // *aBytes gets the number of bytes that were actually read. This can |
357 | | // be less than aCount. If the first byte of data is not in the cache, |
358 | | // this will block until the data is available or the stream is |
359 | | // closed, otherwise it won't block. |
360 | | nsresult Read(AutoLock&, char* aBuffer, uint32_t aCount, uint32_t* aBytes); |
361 | | // Seeks to aOffset in the stream then performs a Read operation. See |
362 | | // 'Read' for argument and return details. |
363 | | nsresult ReadAt(int64_t aOffset, char* aBuffer, |
364 | | uint32_t aCount, uint32_t* aBytes); |
365 | | |
366 | | void ThrottleReadahead(bool bThrottle); |
367 | | |
368 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; |
369 | | |
370 | | nsCString GetDebugInfo(); |
371 | | |
372 | | private: |
373 | | friend class MediaCache; |
374 | | |
375 | | /** |
376 | | * A doubly-linked list of blocks. Add/Remove/Get methods are all |
377 | | * constant time. We declare this here so that a stream can contain a |
378 | | * BlockList of its read-ahead blocks. Blocks are referred to by index |
379 | | * into the MediaCache::mIndex array. |
380 | | * |
381 | | * Blocks can belong to more than one list at the same time, because |
382 | | * the next/prev pointers are not stored in the block. |
383 | | */ |
384 | | class BlockList { |
385 | | public: |
386 | 0 | BlockList() : mFirstBlock(-1), mCount(0) {} |
387 | 0 | ~BlockList() { |
388 | 0 | NS_ASSERTION(mFirstBlock == -1 && mCount == 0, |
389 | 0 | "Destroying non-empty block list"); |
390 | 0 | } |
391 | | void AddFirstBlock(int32_t aBlock); |
392 | | void AddAfter(int32_t aBlock, int32_t aBefore); |
393 | | void RemoveBlock(int32_t aBlock); |
394 | | // Returns the first block in the list, or -1 if empty |
395 | 0 | int32_t GetFirstBlock() const { return mFirstBlock; } |
396 | | // Returns the last block in the list, or -1 if empty |
397 | | int32_t GetLastBlock() const; |
398 | | // Returns the next block in the list after aBlock or -1 if |
399 | | // aBlock is the last block |
400 | | int32_t GetNextBlock(int32_t aBlock) const; |
401 | | // Returns the previous block in the list before aBlock or -1 if |
402 | | // aBlock is the first block |
403 | | int32_t GetPrevBlock(int32_t aBlock) const; |
404 | 0 | bool IsEmpty() const { return mFirstBlock < 0; } |
405 | 0 | int32_t GetCount() const { return mCount; } |
406 | | // The contents of aBlockIndex1 and aBlockIndex2 have been swapped |
407 | | void NotifyBlockSwapped(int32_t aBlockIndex1, int32_t aBlockIndex2); |
408 | | #ifdef DEBUG |
409 | | // Verify linked-list invariants |
410 | | void Verify(); |
411 | | #else |
412 | 0 | void Verify() {} |
413 | | #endif |
414 | | |
415 | | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
416 | | |
417 | | private: |
418 | | struct Entry : public nsUint32HashKey { |
419 | | explicit Entry(KeyTypePointer aKey) |
420 | | : nsUint32HashKey(aKey) |
421 | | , mNextBlock(0) |
422 | | , mPrevBlock(0) |
423 | 0 | { |
424 | 0 | } |
425 | | Entry(const Entry& toCopy) : nsUint32HashKey(&toCopy.GetKey()), |
426 | 0 | mNextBlock(toCopy.mNextBlock), mPrevBlock(toCopy.mPrevBlock) {} |
427 | | |
428 | | int32_t mNextBlock; |
429 | | int32_t mPrevBlock; |
430 | | }; |
431 | | nsTHashtable<Entry> mEntries; |
432 | | |
433 | | // The index of the first block in the list, or -1 if the list is empty. |
434 | | int32_t mFirstBlock; |
435 | | // The number of blocks in the list. |
436 | | int32_t mCount; |
437 | | }; |
438 | | |
439 | | // Read data from the partial block and return the number of bytes read |
440 | | // successfully. 0 if aOffset is not an offset in the partial block or there |
441 | | // is nothing to read. |
442 | | uint32_t ReadPartialBlock(AutoLock&, int64_t aOffset, Span<char> aBuffer); |
443 | | |
444 | | // Read data from the cache block specified by aOffset. Return the number of |
445 | | // bytes read successfully or an error code if any failure. |
446 | | Result<uint32_t, nsresult> ReadBlockFromCache(AutoLock&, |
447 | | int64_t aOffset, |
448 | | Span<char> aBuffer, |
449 | | bool aNoteBlockUsage = false); |
450 | | |
451 | | // Non-main thread only. |
452 | | nsresult Seek(AutoLock&, int64_t aOffset); |
453 | | |
454 | | // Returns the end of the bytes starting at the given offset |
455 | | // which are in cache. |
456 | | // This method assumes that the cache monitor is held and can be called on |
457 | | // any thread. |
458 | | int64_t GetCachedDataEndInternal(AutoLock&, int64_t aOffset); |
459 | | // Returns the offset of the first byte of cached data at or after aOffset, |
460 | | // or -1 if there is no such cached data. |
461 | | // This method assumes that the cache monitor is held and can be called on |
462 | | // any thread. |
463 | | int64_t GetNextCachedDataInternal(AutoLock&, int64_t aOffset); |
464 | | // Used by |NotifyDataEnded| to write |mPartialBlock| to disk. |
465 | | // If |aNotifyAll| is true, this function will wake up readers who may be |
466 | | // waiting on the media cache monitor. Called on the main thread only. |
467 | | void FlushPartialBlockInternal(AutoLock&, bool aNotify); |
468 | | |
469 | | void NotifyDataStartedInternal(uint32_t aLoadID, |
470 | | int64_t aOffset, |
471 | | bool aSeekable, |
472 | | int64_t aLength); |
473 | | |
474 | | void NotifyDataEndedInternal(uint32_t aLoadID, nsresult aStatus); |
475 | | |
476 | | void UpdateDownloadStatistics(AutoLock&); |
477 | | |
478 | | void CloseInternal(AutoLock&); |
479 | | void InitAsCloneInternal(MediaCacheStream* aOriginal); |
480 | | |
481 | | // Instance of MediaCache to use with this MediaCacheStream. |
482 | | RefPtr<MediaCache> mMediaCache; |
483 | | |
484 | | ChannelMediaResource* const mClient; |
485 | | |
486 | | // The following fields must be written holding the cache's monitor and |
487 | | // only on the main thread, thus can be read either on the main thread |
488 | | // or while holding the cache's monitor. |
489 | | |
490 | | // Set to true when the stream has been closed either explicitly or |
491 | | // due to an internal cache error |
492 | | bool mClosed = false; |
493 | | // This is a unique ID representing the resource we're loading. |
494 | | // All streams with the same mResourceID are loading the same |
495 | | // underlying resource and should share data. |
496 | | // Initialized to 0 as invalid. Will be allocated a valid ID (always positive) |
497 | | // from the cache. |
498 | | int64_t mResourceID = 0; |
499 | | // The last reported seekability state for the underlying channel |
500 | | bool mIsTransportSeekable; |
501 | | // True if the cache has suspended our channel because the cache is |
502 | | // full and the priority of the data that would be received is lower |
503 | | // than the priority of the data already in the cache |
504 | | bool mCacheSuspended; |
505 | | // True if the channel ended and we haven't seeked it again. |
506 | | bool mChannelEnded; |
507 | | |
508 | | // The following fields are protected by the cache's monitor and can be written |
509 | | // by any thread. |
510 | | |
511 | | // The reported or discovered length of the data, or -1 if nothing is known |
512 | | int64_t mStreamLength = -1; |
513 | | // The offset where the next data from the channel will arrive |
514 | | int64_t mChannelOffset = 0; |
515 | | // The offset where the reader is positioned in the stream |
516 | | int64_t mStreamOffset; |
517 | | // For each block in the stream data, maps to the cache entry for the |
518 | | // block, or -1 if the block is not cached. |
519 | | nsTArray<int32_t> mBlocks; |
520 | | // The list of read-ahead blocks, ordered by stream offset; the first |
521 | | // block is the earliest in the stream (so the last block will be the |
522 | | // least valuable). |
523 | | BlockList mReadaheadBlocks; |
524 | | // The list of metadata blocks; the first block is the most recently used |
525 | | BlockList mMetadataBlocks; |
526 | | // The list of played-back blocks; the first block is the most recently used |
527 | | BlockList mPlayedBlocks; |
528 | | // The last reported estimate of the decoder's playback rate |
529 | | uint32_t mPlaybackBytesPerSecond; |
530 | | // The number of times this stream has been Pinned without a |
531 | | // corresponding Unpin |
532 | | uint32_t mPinCount; |
533 | | // True if CacheClientNotifyDataEnded has been called for this stream. |
534 | | bool mDidNotifyDataEnded = false; |
535 | | // The status used when we did CacheClientNotifyDataEnded. Only valid |
536 | | // when mDidNotifyDataEnded is true. |
537 | | nsresult mNotifyDataEndedStatus; |
538 | | // The last reported read mode |
539 | | ReadMode mCurrentMode = MODE_METADATA; |
540 | | // True if some data in mPartialBlockBuffer has been read as metadata |
541 | | bool mMetadataInPartialBlockBuffer; |
542 | | // The load ID of the current channel. Used to check whether the data is |
543 | | // coming from an old channel and should be discarded. |
544 | | uint32_t mLoadID = 0; |
545 | | // The seek target initiated by MediaCache. -1 if no seek is going on. |
546 | | int64_t mSeekTarget = -1; |
547 | | |
548 | | bool mThrottleReadahead = false; |
549 | | |
550 | | // Data received for the block containing mChannelOffset. Data needs |
551 | | // to wait here so we can write back a complete block. The first |
552 | | // mChannelOffset%BLOCK_SIZE bytes have been filled in with good data, |
553 | | // the rest are garbage. |
554 | | // Heap allocate this buffer since the exact power-of-2 will cause allocation |
555 | | // slop when combined with the rest of the object members. |
556 | | // This partial buffer should always be read/write within the cache's monitor. |
557 | | const UniquePtr<uint8_t[]> mPartialBlockBuffer = |
558 | | MakeUnique<uint8_t[]>(BLOCK_SIZE); |
559 | | |
560 | | // True if associated with a private browsing window. |
561 | | const bool mIsPrivateBrowsing; |
562 | | |
563 | | // True if the client is suspended. Accessed on the owner thread only. |
564 | | bool mClientSuspended = false; |
565 | | |
566 | | MediaChannelStatistics mDownloadStatistics; |
567 | | }; |
568 | | |
569 | | } // namespace mozilla |
570 | | |
571 | | #endif |