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