diff options
Diffstat (limited to 'dom/media/ChannelMediaResource.h')
-rw-r--r-- | dom/media/ChannelMediaResource.h | 273 |
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 |