Line data Source code
1 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #if !defined(MediaResource_h_)
7 : #define MediaResource_h_
8 :
9 : #include "mozilla/Mutex.h"
10 : #include "nsIChannel.h"
11 : #include "nsIURI.h"
12 : #include "nsISeekableStream.h"
13 : #include "nsIStreamingProtocolController.h"
14 : #include "nsIStreamListener.h"
15 : #include "nsIChannelEventSink.h"
16 : #include "nsIInterfaceRequestor.h"
17 : #include "Intervals.h"
18 : #include "MediaCache.h"
19 : #include "MediaContainerType.h"
20 : #include "MediaData.h"
21 : #include "MediaPrefs.h"
22 : #include "MediaResourceCallback.h"
23 : #include "mozilla/Atomics.h"
24 : #include "mozilla/Attributes.h"
25 : #include "mozilla/TimeStamp.h"
26 : #include "mozilla/UniquePtr.h"
27 : #include "nsThreadUtils.h"
28 : #include <algorithm>
29 :
30 : // For HTTP seeking, if number of bytes needing to be
31 : // seeked forward is less than this value then a read is
32 : // done rather than a byte range request.
33 : //
34 : // If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes
35 : // a delay of 200ms, then in that 200ms we could have simply read ahead 2MB. So
36 : // setting SEEK_VS_READ_THRESHOLD to 1MB sounds reasonable.
37 : static const int64_t SEEK_VS_READ_THRESHOLD = 1 * 1024 * 1024;
38 :
39 : static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;
40 :
41 : // Number of bytes we have accumulated before we assume the connection download
42 : // rate can be reliably calculated. 57 Segments at IW=3 allows slow start to
43 : // reach a CWND of 30 (See bug 831998)
44 : static const int64_t RELIABLE_DATA_THRESHOLD = 57 * 1460;
45 :
46 : class nsIHttpChannel;
47 : class nsIPrincipal;
48 :
49 : namespace mozilla {
50 :
51 : class MediaChannelStatistics;
52 :
53 : /**
54 : * This class is useful for estimating rates of data passing through
55 : * some channel. The idea is that activity on the channel "starts"
56 : * and "stops" over time. At certain times data passes through the
57 : * channel (usually while the channel is active; data passing through
58 : * an inactive channel is ignored). The GetRate() function computes
59 : * an estimate of the "current rate" of the channel, which is some
60 : * kind of average of the data passing through over the time the
61 : * channel is active.
62 : *
63 : * All methods take "now" as a parameter so the user of this class can
64 : * control the timeline used.
65 : */
66 : class MediaChannelStatistics {
67 : public:
68 0 : MediaChannelStatistics() = default;
69 :
70 : MediaChannelStatistics(const MediaChannelStatistics&) = default;
71 :
72 : void Reset() {
73 : mLastStartTime = TimeStamp();
74 : mAccumulatedTime = TimeDuration(0);
75 : mAccumulatedBytes = 0;
76 : mIsStarted = false;
77 : }
78 0 : void Start() {
79 0 : if (mIsStarted)
80 0 : return;
81 0 : mLastStartTime = TimeStamp::Now();
82 0 : mIsStarted = true;
83 : }
84 0 : void Stop() {
85 0 : if (!mIsStarted)
86 0 : return;
87 0 : mAccumulatedTime += TimeStamp::Now() - mLastStartTime;
88 0 : mIsStarted = false;
89 : }
90 0 : void AddBytes(int64_t aBytes) {
91 0 : if (!mIsStarted) {
92 : // ignore this data, it may be related to seeking or some other
93 : // operation we don't care about
94 0 : return;
95 : }
96 0 : mAccumulatedBytes += aBytes;
97 : }
98 0 : double GetRateAtLastStop(bool* aReliable) {
99 0 : double seconds = mAccumulatedTime.ToSeconds();
100 0 : *aReliable = (seconds >= 1.0) ||
101 0 : (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
102 0 : if (seconds <= 0.0)
103 0 : return 0.0;
104 0 : return static_cast<double>(mAccumulatedBytes)/seconds;
105 : }
106 0 : double GetRate(bool* aReliable) {
107 0 : TimeDuration time = mAccumulatedTime;
108 0 : if (mIsStarted) {
109 0 : time += TimeStamp::Now() - mLastStartTime;
110 : }
111 0 : double seconds = time.ToSeconds();
112 0 : *aReliable = (seconds >= 3.0) ||
113 0 : (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
114 0 : if (seconds <= 0.0)
115 0 : return 0.0;
116 0 : return static_cast<double>(mAccumulatedBytes)/seconds;
117 : }
118 : private:
119 : int64_t mAccumulatedBytes = 0;
120 : TimeDuration mAccumulatedTime;
121 : TimeStamp mLastStartTime;
122 : bool mIsStarted = false;
123 : };
124 :
125 : // Represents a section of contiguous media, with a start and end offset.
126 : // Used to denote ranges of data which are cached.
127 :
128 : typedef media::Interval<int64_t> MediaByteRange;
129 : typedef media::IntervalSet<int64_t> MediaByteRangeSet;
130 :
131 : /**
132 : * Provides a thread-safe, seek/read interface to resources
133 : * loaded from a URI. Uses MediaCache to cache data received over
134 : * Necko's async channel API, thus resolving the mismatch between clients
135 : * that need efficient random access to the data and protocols that do not
136 : * support efficient random access, such as HTTP.
137 : *
138 : * Instances of this class must be created on the main thread.
139 : * Most methods must be called on the main thread only. Read, Seek and
140 : * Tell must only be called on non-main threads. In the case of the Ogg
141 : * Decoder they are called on the Decode thread for example. You must
142 : * ensure that no threads are calling these methods once Close is called.
143 : *
144 : * Instances of this class are reference counted. Use nsRefPtr for
145 : * managing the lifetime of instances of this class.
146 : *
147 : * The generic implementation of this class is ChannelMediaResource, which can
148 : * handle any URI for which Necko supports AsyncOpen.
149 : * The 'file:' protocol can be implemented efficiently with direct random
150 : * access, so the FileMediaResource implementation class bypasses the cache.
151 : * MediaResource::Create automatically chooses the best implementation class.
152 : */
153 0 : class MediaResource : public nsISupports
154 : {
155 : public:
156 : // Our refcounting is threadsafe, and when our refcount drops to zero
157 : // we dispatch an event to the main thread to delete the MediaResource.
158 : // Note that this means it's safe for references to this object to be
159 : // released on a non main thread, but the destructor will always run on
160 : // the main thread.
161 : NS_DECL_THREADSAFE_ISUPPORTS
162 :
163 : // Close the resource, stop any listeners, channels, etc.
164 : // Cancels any currently blocking Read request and forces that request to
165 : // return an error.
166 : virtual nsresult Close() = 0;
167 : // Suspend any downloads that are in progress.
168 : // If aCloseImmediately is set, resources should be released immediately
169 : // since we don't expect to resume again any time soon. Otherwise we
170 : // may resume again soon so resources should be held for a little
171 : // while.
172 : virtual void Suspend(bool aCloseImmediately) = 0;
173 : // Resume any downloads that have been suspended.
174 : virtual void Resume() = 0;
175 : // Get the current principal for the channel
176 : virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
177 : // If this returns false, then we shouldn't try to clone this MediaResource
178 : // because its underlying resources are not suitable for reuse (e.g.
179 : // because the underlying connection has been lost, or this resource
180 : // just can't be safely cloned). If this returns true, CloneData could
181 : // still fail. If this returns false, CloneData should not be called.
182 0 : virtual bool CanClone() { return false; }
183 : // Create a new stream of the same type that refers to the same URI
184 : // with a new channel. Any cached data associated with the original
185 : // stream should be accessible in the new stream too.
186 0 : virtual already_AddRefed<MediaResource> CloneData(
187 : MediaResourceCallback* aCallback)
188 : {
189 0 : return nullptr;
190 : }
191 :
192 : // These methods are called off the main thread.
193 : // The mode is initially MODE_PLAYBACK.
194 : virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
195 : // This is the client's estimate of the playback rate assuming
196 : // the media plays continuously. The cache can't guess this itself
197 : // because it doesn't know when the decoder was paused, buffering, etc.
198 : virtual void SetPlaybackRate(uint32_t aBytesPerSecond) = 0;
199 : // Read up to aCount bytes from the stream. The read starts at
200 : // aOffset in the stream, seeking to that location initially if
201 : // it is not the current stream offset. The remaining arguments,
202 : // results and requirements are the same as per the Read method.
203 : virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
204 : uint32_t aCount, uint32_t* aBytes) = 0;
205 : // Indicate whether caching data in advance of reads is worth it.
206 : // E.g. Caching lockless and memory-based MediaResource subclasses would be a
207 : // waste, but caching lock/IO-bound resources means reducing the impact of
208 : // each read.
209 : virtual bool ShouldCacheReads() = 0;
210 :
211 0 : already_AddRefed<MediaByteBuffer> CachedReadAt(int64_t aOffset, uint32_t aCount)
212 : {
213 0 : RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
214 0 : bool ok = bytes->SetLength(aCount, fallible);
215 0 : NS_ENSURE_TRUE(ok, nullptr);
216 0 : char* curr = reinterpret_cast<char*>(bytes->Elements());
217 0 : nsresult rv = ReadFromCache(curr, aOffset, aCount);
218 0 : NS_ENSURE_SUCCESS(rv, nullptr);
219 0 : return bytes.forget();
220 : }
221 :
222 : // Pass true to limit the amount of readahead data (specified by
223 : // "media.cache_readahead_limit") or false to read as much as the
224 : // cache size allows.
225 0 : virtual void ThrottleReadahead(bool bThrottle) { }
226 :
227 : // Report the current offset in bytes from the start of the stream.
228 : // This is used to approximate where we currently are in the playback of a
229 : // media.
230 : // A call to ReadAt will update this position.
231 : virtual int64_t Tell() = 0;
232 : // Moves any existing channel loads into or out of background. Background
233 : // loads don't block the load event. This also determines whether or not any
234 : // new loads initiated (for example to seek) will be in the background.
235 0 : virtual void SetLoadInBackground(bool aLoadInBackground) {}
236 : // Ensures that the value returned by IsSuspendedByCache below is up to date
237 : // (i.e. the cache has examined this stream at least once).
238 0 : virtual void EnsureCacheUpToDate() {}
239 :
240 : // These can be called on any thread.
241 : // Cached blocks associated with this stream will not be evicted
242 : // while the stream is pinned.
243 : virtual void Pin() = 0;
244 : virtual void Unpin() = 0;
245 : // Get the estimated download rate in bytes per second (assuming no
246 : // pausing of the channel is requested by Gecko).
247 : // *aIsReliable is set to true if we think the estimate is useful.
248 : virtual double GetDownloadRate(bool* aIsReliable) = 0;
249 : // Get the length of the stream in bytes. Returns -1 if not known.
250 : // This can change over time; after a seek operation, a misbehaving
251 : // server may give us a resource of a different length to what it had
252 : // reported previously --- or it may just lie in its Content-Length
253 : // header and give us more or less data than it reported. We will adjust
254 : // the result of GetLength to reflect the data that's actually arriving.
255 : virtual int64_t GetLength() = 0;
256 : // Returns the offset of the first byte of cached data at or after aOffset,
257 : // or -1 if there is no such cached data.
258 : virtual int64_t GetNextCachedData(int64_t aOffset) = 0;
259 : // Returns the end of the bytes starting at the given offset which are in
260 : // cache. Returns aOffset itself if there are zero bytes available there.
261 : virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0;
262 : // Returns true if all the data from aOffset to the end of the stream
263 : // is in cache. If the end of the stream is not known, we return false.
264 : virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0;
265 : // Returns true if we are expecting any more data to arrive
266 : // sometime in the not-too-distant future, either from the network or from
267 : // an appendBuffer call on a MediaSource element.
268 0 : virtual bool IsExpectingMoreData()
269 : {
270 : // MediaDecoder::mDecoderPosition is roughly the same as Tell() which
271 : // returns a position updated by latest Read() or ReadAt().
272 0 : return !IsDataCachedToEndOfResource(Tell()) && !IsSuspended();
273 : }
274 : // Returns true if this stream is suspended by the cache because the
275 : // cache is full. If true then the decoder should try to start consuming
276 : // data, otherwise we may not be able to make progress.
277 : // MediaDecoder::NotifySuspendedStatusChanged is called when this
278 : // changes.
279 : // For resources using the media cache, this returns true only when all
280 : // streams for the same resource are all suspended.
281 : virtual bool IsSuspendedByCache() = 0;
282 : // Returns true if this stream has been suspended.
283 : virtual bool IsSuspended() = 0;
284 : // Reads only data which is cached in the media cache. If you try to read
285 : // any data which overlaps uncached data, or if aCount bytes otherwise can't
286 : // be read, this function will return failure. This function be called from
287 : // any thread, and it is the only read operation which is safe to call on
288 : // the main thread, since it's guaranteed to be non blocking.
289 : virtual nsresult ReadFromCache(char* aBuffer,
290 : int64_t aOffset,
291 : uint32_t aCount) = 0;
292 : // Returns true if the resource can be seeked to unbuffered ranges, i.e.
293 : // for an HTTP network stream this returns true if HTTP1.1 Byte Range
294 : // requests are supported by the connection/server.
295 : virtual bool IsTransportSeekable() = 0;
296 :
297 : /**
298 : * Create a resource, reading data from the channel. Call on main thread only.
299 : * The caller must follow up by calling resource->Open().
300 : */
301 : static already_AddRefed<MediaResource>
302 : Create(MediaResourceCallback* aCallback,
303 : nsIChannel* aChannel, bool aIsPrivateBrowsing);
304 :
305 : /**
306 : * Open the stream. This creates a stream listener and returns it in
307 : * aStreamListener; this listener needs to be notified of incoming data.
308 : */
309 : virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
310 :
311 : /**
312 : * Fills aRanges with MediaByteRanges representing the data which is cached
313 : * in the media cache. Stream should be pinned during call and while
314 : * aRanges is being used.
315 : */
316 : virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0;
317 :
318 : // Returns true if the resource is a live stream.
319 0 : virtual bool IsLiveStream()
320 : {
321 0 : return GetLength() == -1;
322 : }
323 :
324 0 : virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
325 0 : return 0;
326 : }
327 :
328 0 : virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
329 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
330 : }
331 :
332 : protected:
333 0 : virtual ~MediaResource() {};
334 :
335 : private:
336 : void Destroy();
337 : };
338 :
339 : class BaseMediaResource : public MediaResource {
340 : public:
341 : void SetLoadInBackground(bool aLoadInBackground) override;
342 :
343 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
344 : {
345 : // Might be useful to track in the future:
346 : // - mChannel
347 : // - mURI (possibly owned, looks like just a ref from mChannel)
348 : // Not owned:
349 : // - mCallback
350 0 : size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
351 0 : return size;
352 : }
353 :
354 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
355 : {
356 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
357 : }
358 :
359 : protected:
360 0 : BaseMediaResource(MediaResourceCallback* aCallback,
361 : nsIChannel* aChannel,
362 : nsIURI* aURI)
363 0 : : mCallback(aCallback)
364 : , mChannel(aChannel)
365 : , mURI(aURI)
366 0 : , mLoadInBackground(false)
367 : {
368 0 : }
369 0 : virtual ~BaseMediaResource()
370 0 : {
371 0 : }
372 :
373 : // Set the request's load flags to aFlags. If the request is part of a
374 : // load group, the request is removed from the group, the flags are set, and
375 : // then the request is added back to the load group.
376 : void ModifyLoadFlags(nsLoadFlags aFlags);
377 :
378 : // Dispatches an event to call MediaDecoder::NotifyBytesConsumed(aNumBytes, aOffset)
379 : // on the main thread. This is called automatically after every read.
380 : void DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset);
381 :
382 : RefPtr<MediaResourceCallback> mCallback;
383 :
384 : // Channel used to download the media data. Must be accessed
385 : // from the main thread only.
386 : nsCOMPtr<nsIChannel> mChannel;
387 :
388 : // URI in case the stream needs to be re-opened. Access from
389 : // main thread only.
390 : nsCOMPtr<nsIURI> mURI;
391 :
392 : // True if SetLoadInBackground() has been called with
393 : // aLoadInBackground = true, i.e. when the document load event is not
394 : // blocked by this resource, and all channel loads will be in the
395 : // background.
396 : bool mLoadInBackground;
397 : };
398 :
399 :
400 : /**
401 : * This class is responsible for managing the suspend count and report suspend
402 : * status of channel.
403 : **/
404 : class ChannelSuspendAgent {
405 : public:
406 0 : explicit ChannelSuspendAgent(nsIChannel* aChannel)
407 0 : : mChannel(aChannel),
408 : mSuspendCount(0),
409 0 : mIsChannelSuspended(false)
410 0 : {}
411 :
412 : // True when the channel has been suspended or needs to be suspended.
413 : bool IsSuspended();
414 :
415 : // Return true when the channel is logically suspended, i.e. the suspend
416 : // count goes from 0 to 1.
417 : bool Suspend();
418 :
419 : // Return true only when the suspend count is equal to zero.
420 : bool Resume();
421 :
422 : // Call after opening channel, set channel and check whether the channel
423 : // needs to be suspended.
424 : void NotifyChannelOpened(nsIChannel* aChannel);
425 :
426 : // Call before closing channel, reset the channel internal status if needed.
427 : void NotifyChannelClosing();
428 :
429 : // Check whether we need to suspend the channel.
430 : void UpdateSuspendedStatusIfNeeded();
431 : private:
432 : // Only suspends channel but not changes the suspend count.
433 : void SuspendInternal();
434 :
435 : nsIChannel* mChannel;
436 : Atomic<uint32_t> mSuspendCount;
437 : bool mIsChannelSuspended;
438 : };
439 :
440 : /**
441 : * This is the MediaResource implementation that wraps Necko channels.
442 : * Much of its functionality is actually delegated to MediaCache via
443 : * an underlying MediaCacheStream.
444 : *
445 : * All synchronization is performed by MediaCacheStream; all off-main-
446 : * thread operations are delegated directly to that object.
447 : */
448 : class ChannelMediaResource : public BaseMediaResource
449 : {
450 : public:
451 : ChannelMediaResource(MediaResourceCallback* aDecoder,
452 : nsIChannel* aChannel,
453 : nsIURI* aURI,
454 : bool aIsPrivateBrowsing);
455 : ChannelMediaResource(MediaResourceCallback* aDecoder,
456 : nsIChannel* aChannel,
457 : nsIURI* aURI,
458 : const MediaChannelStatistics& aStatistics);
459 : ~ChannelMediaResource();
460 :
461 : // These are called on the main thread by MediaCache. These must
462 : // not block or grab locks, because the media cache is holding its lock.
463 : // Notify that data is available from the cache. This can happen even
464 : // if this stream didn't read any data, since another stream might have
465 : // received data for the same resource.
466 : void CacheClientNotifyDataReceived();
467 : // Notify that we reached the end of the stream. This can happen even
468 : // if this stream didn't read any data, since another stream might have
469 : // received data for the same resource.
470 : void CacheClientNotifyDataEnded(nsresult aStatus);
471 : // Notify that the principal for the cached resource changed.
472 : void CacheClientNotifyPrincipalChanged();
473 : // Notify the decoder that the cache suspended status changed.
474 : void CacheClientNotifySuspendedStatusChanged();
475 :
476 : // These are called on the main thread by MediaCache. These shouldn't block,
477 : // but they may grab locks --- the media cache is not holding its lock
478 : // when these are called.
479 : // Start a new load at the given aOffset. The old load is cancelled
480 : // and no more data from the old load will be notified via
481 : // MediaCacheStream::NotifyDataReceived/Ended.
482 : // This can fail.
483 : nsresult CacheClientSeek(int64_t aOffset, bool aResume);
484 : // Suspend the current load since data is currently not wanted
485 : nsresult CacheClientSuspend();
486 : // Resume the current load since data is wanted again
487 : nsresult CacheClientResume();
488 :
489 : void ThrottleReadahead(bool bThrottle) override;
490 :
491 : // Main thread
492 : nsresult Open(nsIStreamListener** aStreamListener) override;
493 : nsresult Close() override;
494 : void Suspend(bool aCloseImmediately) override;
495 : void Resume() override;
496 : already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
497 : // Return true if the stream has been closed.
498 : bool IsClosed() const { return mCacheStream.IsClosed(); }
499 : bool CanClone() override;
500 : already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aDecoder) override;
501 : nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
502 : void EnsureCacheUpToDate() override;
503 :
504 : // Other thread
505 : void SetReadMode(MediaCacheStream::ReadMode aMode) override;
506 : void SetPlaybackRate(uint32_t aBytesPerSecond) override;
507 : nsresult ReadAt(int64_t offset, char* aBuffer,
508 : uint32_t aCount, uint32_t* aBytes) override;
509 : // Data stored in IO&lock-encumbered MediaCacheStream, caching recommended.
510 0 : bool ShouldCacheReads() override { return true; }
511 : int64_t Tell() override;
512 :
513 : // Any thread
514 : void Pin() override;
515 : void Unpin() override;
516 : double GetDownloadRate(bool* aIsReliable) override;
517 : int64_t GetLength() override;
518 : int64_t GetNextCachedData(int64_t aOffset) override;
519 : int64_t GetCachedDataEnd(int64_t aOffset) override;
520 : bool IsDataCachedToEndOfResource(int64_t aOffset) override;
521 : bool IsSuspendedByCache() override;
522 : bool IsSuspended() override;
523 : bool IsTransportSeekable() override;
524 :
525 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
526 : // Might be useful to track in the future:
527 : // - mListener (seems minor)
528 : // - mChannelStatistics (seems minor)
529 : // - mDataReceivedEvent (seems minor)
530 0 : size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
531 0 : size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
532 :
533 0 : return size;
534 : }
535 :
536 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
537 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
538 : }
539 :
540 : class Listener final : public nsIStreamListener,
541 : public nsIInterfaceRequestor,
542 : public nsIChannelEventSink
543 : {
544 0 : ~Listener() {}
545 : public:
546 0 : explicit Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
547 :
548 : NS_DECL_ISUPPORTS
549 : NS_DECL_NSIREQUESTOBSERVER
550 : NS_DECL_NSISTREAMLISTENER
551 : NS_DECL_NSICHANNELEVENTSINK
552 : NS_DECL_NSIINTERFACEREQUESTOR
553 :
554 0 : void Revoke() { mResource = nullptr; }
555 :
556 : private:
557 : RefPtr<ChannelMediaResource> mResource;
558 : };
559 : friend class Listener;
560 :
561 : nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
562 :
563 : protected:
564 : // These are called on the main thread by Listener.
565 : nsresult OnStartRequest(nsIRequest* aRequest);
566 : nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
567 : nsresult OnDataAvailable(nsIRequest* aRequest,
568 : nsIInputStream* aStream,
569 : uint32_t aCount);
570 : nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
571 :
572 : // Opens the channel, using an HTTP byte range request to start at mOffset
573 : // if possible. Main thread only.
574 : nsresult OpenChannel(nsIStreamListener** aStreamListener);
575 : nsresult RecreateChannel();
576 : // Add headers to HTTP request. Main thread only.
577 : nsresult SetupChannelHeaders();
578 : // Closes the channel. Main thread only.
579 : void CloseChannel();
580 :
581 : // Parses 'Content-Range' header and returns results via parameters.
582 : // Returns error if header is not available, values are not parse-able or
583 : // values are out of range.
584 : nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
585 : int64_t& aRangeStart,
586 : int64_t& aRangeEnd,
587 : int64_t& aRangeTotal);
588 :
589 : void DoNotifyDataReceived();
590 :
591 : static nsresult CopySegmentToCache(nsIInputStream* aInStream,
592 : void* aClosure,
593 : const char* aFromSegment,
594 : uint32_t aToOffset,
595 : uint32_t aCount,
596 : uint32_t* aWriteCount);
597 :
598 : nsresult CopySegmentToCache(nsIPrincipal* aPrincipal,
599 : const char* aFromSegment,
600 : uint32_t aCount,
601 : uint32_t* aWriteCount);
602 :
603 : // Main thread access only
604 : int64_t mOffset;
605 : RefPtr<Listener> mListener;
606 : // A data received event for the decoder that has been dispatched but has
607 : // not yet been processed.
608 : nsRevocableEventPtr<nsRunnableMethod<ChannelMediaResource, void, false> > mDataReceivedEvent;
609 : // When this flag is set, if we get a network error we should silently
610 : // reopen the stream.
611 : bool mReopenOnError;
612 : // When this flag is set, we should not report the next close of the
613 : // channel.
614 : bool mIgnoreClose;
615 :
616 : // Any thread access
617 : MediaCacheStream mCacheStream;
618 :
619 : // This lock protects mChannelStatistics
620 : Mutex mLock;
621 : MediaChannelStatistics mChannelStatistics;
622 :
623 : // True if we couldn't suspend the stream and we therefore don't want
624 : // to resume later. This is usually due to the channel not being in the
625 : // isPending state at the time of the suspend request.
626 : bool mIgnoreResume;
627 :
628 : ChannelSuspendAgent mSuspendAgent;
629 : };
630 :
631 : /**
632 : * RAII class that handles pinning and unpinning for MediaResource and derived.
633 : * This should be used when making calculations that involve potentially-cached
634 : * MediaResource data, so that the state of the world can't change out from under
635 : * us.
636 : */
637 : template<class T>
638 : class MOZ_RAII AutoPinned {
639 : public:
640 0 : explicit AutoPinned(T* aResource MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mResource(aResource) {
641 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
642 0 : MOZ_ASSERT(mResource);
643 0 : mResource->Pin();
644 0 : }
645 :
646 0 : ~AutoPinned() {
647 0 : mResource->Unpin();
648 0 : }
649 :
650 0 : operator T*() const { return mResource; }
651 0 : T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
652 :
653 : private:
654 : T* mResource;
655 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
656 : };
657 :
658 : /*
659 : * MediaResourceIndex provides a way to access MediaResource objects.
660 : * Read, Seek and Tell must only be called on non-main threads.
661 : * In the case of the Ogg Decoder they are called on the Decode thread for
662 : * example. You must ensure that no threads are calling these methods once
663 : * the MediaResource has been Closed.
664 : */
665 :
666 0 : class MediaResourceIndex
667 : {
668 : public:
669 0 : explicit MediaResourceIndex(MediaResource* aResource)
670 0 : : mResource(aResource)
671 : , mOffset(0)
672 0 : , mCacheBlockSize(aResource->ShouldCacheReads()
673 0 : ? SelectCacheSize(MediaPrefs::MediaResourceIndexCache())
674 : : 0 )
675 : , mCachedOffset(0)
676 : , mCachedBytes(0)
677 0 : , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize))
678 0 : {}
679 :
680 : // Read up to aCount bytes from the stream. The buffer must have
681 : // enough room for at least aCount bytes. Stores the number of
682 : // actual bytes read in aBytes (0 on end of file).
683 : // May read less than aCount bytes if the number of
684 : // available bytes is less than aCount. Always check *aBytes after
685 : // read, and call again if necessary.
686 : nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
687 : // Seek to the given bytes offset in the stream. aWhence can be
688 : // one of:
689 : // NS_SEEK_SET
690 : // NS_SEEK_CUR
691 : // NS_SEEK_END
692 : //
693 : // In the Http strategy case the cancel will cause the http
694 : // channel's listener to close the pipe, forcing an i/o error on any
695 : // blocked read. This will allow the decode thread to complete the
696 : // event.
697 : //
698 : // In the case of a seek in progress, the byte range request creates
699 : // a new listener. This is done on the main thread via seek
700 : // synchronously dispatching an event. This avoids the issue of us
701 : // closing the listener but an outstanding byte range request
702 : // creating a new one. They run on the same thread so no explicit
703 : // synchronisation is required. The byte range request checks for
704 : // the cancel flag and does not create a new channel or listener if
705 : // we are cancelling.
706 : //
707 : // The default strategy does not do any seeking - the only issue is
708 : // a blocked read which it handles by causing the listener to close
709 : // the pipe, as per the http case.
710 : //
711 : // The file strategy doesn't block for any great length of time so
712 : // is fine for a no-op cancel.
713 : nsresult Seek(int32_t aWhence, int64_t aOffset);
714 : // Report the current offset in bytes from the start of the stream.
715 0 : int64_t Tell() const { return mOffset; }
716 :
717 : // Return the underlying MediaResource.
718 0 : MediaResource* GetResource() const { return mResource; }
719 :
720 : // Read up to aCount bytes from the stream. The read starts at
721 : // aOffset in the stream, seeking to that location initially if
722 : // it is not the current stream offset.
723 : // Unlike MediaResource::ReadAt, ReadAt only returns fewer bytes than
724 : // requested if end of stream or an error is encountered. There is no need to
725 : // call it again to get more data.
726 : // If the resource has cached data past the end of the request, it will be
727 : // used to fill a local cache, which should speed up consecutive ReadAt's
728 : // (mostly by avoiding using the resource's IOs and locks.)
729 : // *aBytes will contain the number of bytes copied, even if an error occurred.
730 : // ReadAt doesn't have an impact on the offset returned by Tell().
731 : nsresult ReadAt(int64_t aOffset,
732 : char* aBuffer,
733 : uint32_t aCount,
734 : uint32_t* aBytes);
735 :
736 : // Same as ReadAt, but doesn't try to cache around the read.
737 : // Useful if you know that you will not read again from the same area.
738 : nsresult UncachedReadAt(int64_t aOffset,
739 : char* aBuffer,
740 : uint32_t aCount,
741 : uint32_t* aBytes) const;
742 :
743 : // Similar to ReadAt, but doesn't try to cache around the read.
744 : // Useful if you know that you will not read again from the same area.
745 : // Will attempt to read aRequestedCount+aExtraCount, repeatedly calling
746 : // MediaResource/ ReadAt()'s until a read returns 0 bytes (so we may actually
747 : // get less than aRequestedCount bytes), or until we get at least
748 : // aRequestedCount bytes (so we may not get any/all of the aExtraCount bytes.)
749 : nsresult UncachedRangedReadAt(int64_t aOffset,
750 : char* aBuffer,
751 : uint32_t aRequestedCount,
752 : uint32_t aExtraCount,
753 : uint32_t* aBytes) const;
754 :
755 : // This method returns nullptr if anything fails.
756 : // Otherwise, it returns an owned buffer.
757 : // MediaReadAt may return fewer bytes than requested if end of stream is
758 : // encountered. There is no need to call it again to get more data.
759 : // Note this method will not update mOffset.
760 0 : already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) const
761 : {
762 0 : RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
763 0 : bool ok = bytes->SetLength(aCount, fallible);
764 0 : NS_ENSURE_TRUE(ok, nullptr);
765 0 : char* curr = reinterpret_cast<char*>(bytes->Elements());
766 0 : const char* start = curr;
767 0 : while (aCount > 0) {
768 : uint32_t bytesRead;
769 0 : nsresult rv = mResource->ReadAt(aOffset, curr, aCount, &bytesRead);
770 0 : NS_ENSURE_SUCCESS(rv, nullptr);
771 0 : if (!bytesRead) {
772 0 : break;
773 : }
774 0 : aOffset += bytesRead;
775 0 : aCount -= bytesRead;
776 0 : curr += bytesRead;
777 : }
778 0 : bytes->SetLength(curr - start);
779 0 : return bytes.forget();
780 : }
781 : // Get the length of the stream in bytes. Returns -1 if not known.
782 : // This can change over time; after a seek operation, a misbehaving
783 : // server may give us a resource of a different length to what it had
784 : // reported previously --- or it may just lie in its Content-Length
785 : // header and give us more or less data than it reported. We will adjust
786 : // the result of GetLength to reflect the data that's actually arriving.
787 0 : int64_t GetLength() const { return mResource->GetLength(); }
788 :
789 : private:
790 : // If the resource has cached data past the requested range, try to grab it
791 : // into our local cache.
792 : // If there is no cached data, or attempting to read it fails, fallback on
793 : // a (potentially-blocking) read of just what was requested, so that we don't
794 : // get unexpected side-effects by trying to read more than intended.
795 : nsresult CacheOrReadAt(int64_t aOffset,
796 : char* aBuffer,
797 : uint32_t aCount,
798 : uint32_t* aBytes);
799 :
800 : // Select the next power of 2 (in range 32B-128KB, or 0 -> no cache)
801 0 : static uint32_t SelectCacheSize(uint32_t aHint)
802 : {
803 0 : if (aHint == 0) {
804 0 : return 0;
805 : }
806 0 : if (aHint <= 32) {
807 0 : return 32;
808 : }
809 0 : if (aHint > 64*1024) {
810 0 : return 128*1024;
811 : }
812 : // 32-bit next power of 2, from:
813 : // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
814 0 : aHint--;
815 0 : aHint |= aHint >> 1;
816 0 : aHint |= aHint >> 2;
817 0 : aHint |= aHint >> 4;
818 0 : aHint |= aHint >> 8;
819 0 : aHint |= aHint >> 16;
820 0 : aHint++;
821 0 : return aHint;
822 : }
823 :
824 : // Maps a file offset to a mCachedBlock index.
825 0 : uint32_t IndexInCache(int64_t aOffsetInFile) const
826 : {
827 0 : const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1);
828 0 : MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize);
829 0 : return index;
830 : }
831 :
832 : // Starting file offset of the cache block that contains a given file offset.
833 0 : int64_t CacheOffsetContaining(int64_t aOffsetInFile) const
834 : {
835 0 : const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1);
836 0 : MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile));
837 0 : return offset;
838 : }
839 :
840 : RefPtr<MediaResource> mResource;
841 : int64_t mOffset;
842 :
843 : // Local cache used by ReadAt().
844 : // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
845 : // data of length mCachedBytes, starting at offset `mCachedOffset` in the
846 : // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
847 : //
848 : // resource: |------------------------------------------------------|
849 : // <----------> mCacheBlockSize
850 : // <---------------------------------> mCachedOffset
851 : // <--> mCachedBytes
852 : // mCachedBlock: |..----....|
853 : // CacheOffsetContaining(mCachedOffset) <--> IndexInCache(mCachedOffset)
854 : // <------------------------------>
855 : const uint32_t mCacheBlockSize;
856 : int64_t mCachedOffset;
857 : uint32_t mCachedBytes;
858 : UniquePtr<char[]> mCachedBlock;
859 : };
860 :
861 : } // namespace mozilla
862 :
863 : #endif
|