Source code

Revision control

Copy as Markdown

Other Tools

/* 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 nsIInterfaceRequestor,
public nsIChannelEventSink,
public nsIThreadRetargetableStreamListener,
public SingleWriterLockOwner {
~Listener() = default;
public:
Listener(ChannelMediaResource* aResource, int64_t aOffset, uint32_t aLoadID)
: mMutex("Listener.mMutex", this),
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();
bool OnWritingThread() const override { return NS_IsMainThread(); }
private:
MutexSingleWriter mMutex;
// 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 MOZ_GUARDED_BY(mMutex);
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