summaryrefslogtreecommitdiffstats
path: root/dom/media/ChannelMediaResource.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ChannelMediaResource.h')
-rw-r--r--dom/media/ChannelMediaResource.h273
1 files changed, 273 insertions, 0 deletions
diff --git a/dom/media/ChannelMediaResource.h b/dom/media/ChannelMediaResource.h
new file mode 100644
index 0000000000..892dca1de0
--- /dev/null
+++ b/dom/media/ChannelMediaResource.h
@@ -0,0 +1,273 @@
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_media_ChannelMediaResource_h
+#define mozilla_dom_media_ChannelMediaResource_h
+
+#include "BaseMediaResource.h"
+#include "MediaCache.h"
+#include "mozilla/Mutex.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIThreadRetargetableStreamListener.h"
+
+class nsIHttpChannel;
+
+namespace mozilla {
+
+/**
+ * This class is responsible for managing the suspend count and report suspend
+ * status of channel.
+ **/
+class ChannelSuspendAgent {
+ public:
+ explicit ChannelSuspendAgent(MediaCacheStream& aCacheStream)
+ : mCacheStream(aCacheStream) {}
+
+ // True when the channel has been suspended or needs to be suspended.
+ bool IsSuspended();
+
+ // Return true when the channel is logically suspended, i.e. the suspend
+ // count goes from 0 to 1.
+ bool Suspend();
+
+ // Return true only when the suspend count is equal to zero.
+ bool Resume();
+
+ // Tell the agent to manage the suspend status of the channel.
+ void Delegate(nsIChannel* aChannel);
+ // Stop the management of the suspend status of the channel.
+ void Revoke();
+
+ private:
+ // Only suspends channel but not changes the suspend count.
+ void SuspendInternal();
+
+ nsIChannel* mChannel = nullptr;
+ MediaCacheStream& mCacheStream;
+ uint32_t mSuspendCount = 0;
+ bool mIsChannelSuspended = false;
+};
+
+DDLoggedTypeDeclNameAndBase(ChannelMediaResource, BaseMediaResource);
+
+/**
+ * This is the MediaResource implementation that wraps Necko channels.
+ * Much of its functionality is actually delegated to MediaCache via
+ * an underlying MediaCacheStream.
+ *
+ * All synchronization is performed by MediaCacheStream; all off-main-
+ * thread operations are delegated directly to that object.
+ */
+class ChannelMediaResource
+ : public BaseMediaResource,
+ public DecoderDoctorLifeLogger<ChannelMediaResource> {
+ // Store information shared among resources. Main thread only.
+ struct SharedInfo {
+ NS_INLINE_DECL_REFCOUNTING(SharedInfo);
+
+ nsTArray<ChannelMediaResource*> mResources;
+ // Null if there is not yet any data from any origin.
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ // Meaningful only when mPrincipal is non-null,
+ // unaffected by intermediate cross-origin redirects.
+ bool mFinalResponsesAreOpaque = false;
+
+ bool mHadCrossOriginRedirects = false;
+
+ private:
+ ~SharedInfo() = default;
+ };
+ RefPtr<SharedInfo> mSharedInfo;
+
+ public:
+ ChannelMediaResource(MediaResourceCallback* aDecoder, nsIChannel* aChannel,
+ nsIURI* aURI, int64_t aStreamLength,
+ bool aIsPrivateBrowsing = false);
+ ~ChannelMediaResource();
+
+ // These are called on the main thread by MediaCache. These must
+ // not block or grab locks, because the media cache is holding its lock.
+ // Notify that data is available from the cache. This can happen even
+ // if this stream didn't read any data, since another stream might have
+ // received data for the same resource.
+ void CacheClientNotifyDataReceived();
+ // Notify that we reached the end of the stream. This can happen even
+ // if this stream didn't read any data, since another stream might have
+ // received data for the same resource.
+ void CacheClientNotifyDataEnded(nsresult aStatus);
+ // Notify that the principal for the cached resource changed.
+ void CacheClientNotifyPrincipalChanged();
+ // Notify the decoder that the cache suspended status changed.
+ void CacheClientNotifySuspendedStatusChanged(bool aSuspended);
+
+ // These are called on the main thread by MediaCache. These shouldn't block,
+ // but they may grab locks --- the media cache is not holding its lock
+ // when these are called.
+ // Start a new load at the given aOffset. The old load is cancelled
+ // and no more data from the old load will be notified via
+ // MediaCacheStream::NotifyDataReceived/Ended.
+ void CacheClientSeek(int64_t aOffset, bool aResume);
+ // Suspend the current load since data is currently not wanted
+ void CacheClientSuspend();
+ // Resume the current load since data is wanted again
+ void CacheClientResume();
+
+ bool IsSuspended();
+
+ void ThrottleReadahead(bool bThrottle) override;
+
+ // Main thread
+ nsresult Open(nsIStreamListener** aStreamListener) override;
+ RefPtr<GenericPromise> Close() override;
+ void Suspend(bool aCloseImmediately) override;
+ void Resume() override;
+ already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
+ bool HadCrossOriginRedirects() override;
+ bool CanClone() override;
+ already_AddRefed<BaseMediaResource> CloneData(
+ MediaResourceCallback* aDecoder) override;
+ nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
+ uint32_t aCount) override;
+
+ // Other thread
+ void SetReadMode(MediaCacheStream::ReadMode aMode) override;
+ void SetPlaybackRate(uint32_t aBytesPerSecond) override;
+ nsresult ReadAt(int64_t offset, char* aBuffer, uint32_t aCount,
+ uint32_t* aBytes) override;
+ // Data stored in IO&lock-encumbered MediaCacheStream, caching recommended.
+ bool ShouldCacheReads() override { return true; }
+
+ // Any thread
+ void Pin() override;
+ void Unpin() override;
+ double GetDownloadRate(bool* aIsReliable) override;
+ int64_t GetLength() override;
+ int64_t GetNextCachedData(int64_t aOffset) override;
+ int64_t GetCachedDataEnd(int64_t aOffset) override;
+ bool IsDataCachedToEndOfResource(int64_t aOffset) override;
+ bool IsTransportSeekable() override;
+ bool IsLiveStream() const override { return mIsLiveStream; }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
+ // Might be useful to track in the future:
+ // - mListener (seems minor)
+ size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+ size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
+
+ return size;
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ void GetDebugInfo(dom::MediaResourceDebugInfo& aInfo) override;
+
+ class Listener final : public nsIStreamListener,
+ public nsIInterfaceRequestor,
+ public nsIChannelEventSink,
+ public nsIThreadRetargetableStreamListener {
+ ~Listener() = default;
+
+ public:
+ Listener(ChannelMediaResource* aResource, int64_t aOffset, uint32_t aLoadID)
+ : mMutex("Listener.mMutex"),
+ mResource(aResource),
+ mOffset(aOffset),
+ mLoadID(aLoadID) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSICHANNELEVENTSINK
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
+
+ void Revoke();
+
+ private:
+ Mutex mMutex MOZ_UNANNOTATED;
+ // mResource should only be modified on the main thread with the lock.
+ // So it can be read without lock on the main thread or on other threads
+ // with the lock.
+ RefPtr<ChannelMediaResource> mResource;
+
+ const int64_t mOffset;
+ const uint32_t mLoadID;
+ };
+ friend class Listener;
+
+ nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
+
+ protected:
+ nsresult Seek(int64_t aOffset, bool aResume);
+
+ // These are called on the main thread by Listener.
+ nsresult OnStartRequest(nsIRequest* aRequest, int64_t aRequestOffset);
+ nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
+ nsresult OnDataAvailable(uint32_t aLoadID, nsIInputStream* aStream,
+ uint32_t aCount);
+ nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
+ uint32_t aFlags, int64_t aOffset);
+
+ // Use only before MediaDecoder shutdown. Main thread only.
+ dom::HTMLMediaElement* MediaElement() const;
+ // Opens the channel, using an HTTP byte range request to start at aOffset
+ // if possible. Main thread only.
+ nsresult OpenChannel(int64_t aOffset);
+ nsresult RecreateChannel();
+ // Add headers to HTTP request. Main thread only.
+ nsresult SetupChannelHeaders(int64_t aOffset);
+ // Closes the channel. Main thread only.
+ void CloseChannel();
+ // Update the principal for the resource. Main thread only.
+ void UpdatePrincipal();
+
+ // Parses 'Content-Range' header and returns results via parameters.
+ // Returns error if header is not available, values are not parse-able or
+ // values are out of range.
+ nsresult ParseContentRangeHeader(nsIHttpChannel* aHttpChan,
+ int64_t& aRangeStart, int64_t& aRangeEnd,
+ int64_t& aRangeTotal) const;
+
+ // Calculates the length of the resource using HTTP headers, if this
+ // is an HTTP channel. Returns -1 on failure, or for non HTTP channels.
+ int64_t CalculateStreamLength() const;
+
+ struct Closure {
+ uint32_t mLoadID;
+ ChannelMediaResource* mResource;
+ };
+
+ static nsresult CopySegmentToCache(nsIInputStream* aInStream, void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset, uint32_t aCount,
+ uint32_t* aWriteCount);
+
+ // Main thread access only
+ // True if Close() has been called.
+ bool mClosed = false;
+ // The last reported seekability state for the underlying channel
+ bool mIsTransportSeekable = false;
+ // Length of the content first reported.
+ int64_t mFirstReadLength = -1;
+ RefPtr<Listener> mListener;
+ // A mono-increasing integer to uniquely identify the channel we are loading.
+ uint32_t mLoadID = 0;
+ bool mIsLiveStream = false;
+
+ // Any thread access
+ MediaCacheStream mCacheStream;
+
+ ChannelSuspendAgent mSuspendAgent;
+
+ // The size of the stream if known at construction time (such as with blob)
+ const int64_t mKnownStreamLength;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_dom_media_ChannelMediaResource_h