content/media/MediaCache.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:b053ec4fab9a
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

mercurial