ChannelMediaResource.h (10114B)
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 #ifndef mozilla_dom_media_ChannelMediaResource_h 7 #define mozilla_dom_media_ChannelMediaResource_h 8 9 #include "BaseMediaResource.h" 10 #include "MediaCache.h" 11 #include "mozilla/EventTargetAndLockCapability.h" 12 #include "mozilla/Mutex.h" 13 #include "nsIChannelEventSink.h" 14 #include "nsIInterfaceRequestor.h" 15 #include "nsIThreadRetargetableStreamListener.h" 16 17 class nsIHttpChannel; 18 19 namespace mozilla { 20 21 /** 22 * This class is responsible for managing the suspend count and report suspend 23 * status of channel. 24 **/ 25 class ChannelSuspendAgent { 26 public: 27 explicit ChannelSuspendAgent(MediaCacheStream& aCacheStream) 28 : mCacheStream(aCacheStream) {} 29 30 // True when the channel has been suspended or needs to be suspended. 31 bool IsSuspended(); 32 33 // Return true when the channel is logically suspended, i.e. the suspend 34 // count goes from 0 to 1. 35 bool Suspend(); 36 37 // Return true only when the suspend count is equal to zero. 38 bool Resume(); 39 40 // Tell the agent to manage the suspend status of the channel. 41 void Delegate(nsIChannel* aChannel); 42 // Stop the management of the suspend status of the channel. 43 void Revoke(); 44 45 private: 46 // Only suspends channel but not changes the suspend count. 47 void SuspendInternal(); 48 49 nsIChannel* mChannel = nullptr; 50 MediaCacheStream& mCacheStream; 51 uint32_t mSuspendCount = 0; 52 bool mIsChannelSuspended = false; 53 }; 54 55 DDLoggedTypeDeclNameAndBase(ChannelMediaResource, BaseMediaResource); 56 57 /** 58 * This is the MediaResource implementation that wraps Necko channels. 59 * Much of its functionality is actually delegated to MediaCache via 60 * an underlying MediaCacheStream. 61 * 62 * All synchronization is performed by MediaCacheStream; all off-main- 63 * thread operations are delegated directly to that object. 64 */ 65 class ChannelMediaResource 66 : public BaseMediaResource, 67 public DecoderDoctorLifeLogger<ChannelMediaResource> { 68 // Store information shared among resources. Main thread only. 69 struct SharedInfo { 70 NS_INLINE_DECL_REFCOUNTING(SharedInfo); 71 72 nsTArray<ChannelMediaResource*> mResources; 73 // Null if there is not yet any data from any origin. 74 nsCOMPtr<nsIPrincipal> mPrincipal; 75 // Meaningful only when mPrincipal is non-null, 76 // unaffected by intermediate cross-origin redirects. 77 bool mFinalResponsesAreOpaque = false; 78 79 bool mHadCrossOriginRedirects = false; 80 81 private: 82 ~SharedInfo() = default; 83 }; 84 RefPtr<SharedInfo> mSharedInfo; 85 86 public: 87 ChannelMediaResource(MediaResourceCallback* aDecoder, nsIChannel* aChannel, 88 nsIURI* aURI, int64_t aStreamLength, 89 bool aIsPrivateBrowsing = false); 90 ~ChannelMediaResource(); 91 92 // These are called on the main thread by MediaCache. These must 93 // not block or grab locks, because the media cache is holding its lock. 94 // Notify that data is available from the cache. This can happen even 95 // if this stream didn't read any data, since another stream might have 96 // received data for the same resource. 97 void CacheClientNotifyDataReceived(); 98 // Notify that we reached the end of the stream. This can happen even 99 // if this stream didn't read any data, since another stream might have 100 // received data for the same resource. 101 void CacheClientNotifyDataEnded(nsresult aStatus); 102 // Notify that the principal for the cached resource changed. 103 void CacheClientNotifyPrincipalChanged(); 104 // Notify the decoder that the cache suspended status changed. 105 void CacheClientNotifySuspendedStatusChanged(bool aSuspended); 106 107 // These are called on the main thread by MediaCache. These shouldn't block, 108 // but they may grab locks --- the media cache is not holding its lock 109 // when these are called. 110 // Start a new load at the given aOffset. The old load is cancelled 111 // and no more data from the old load will be notified via 112 // MediaCacheStream::NotifyDataReceived/Ended. 113 void CacheClientSeek(int64_t aOffset, bool aResume); 114 // Suspend the current load since data is currently not wanted 115 void CacheClientSuspend(); 116 // Resume the current load since data is wanted again 117 void CacheClientResume(); 118 119 bool IsSuspended(); 120 121 void ThrottleReadahead(bool bThrottle) override; 122 123 // Main thread 124 nsresult Open(nsIStreamListener** aStreamListener) override; 125 RefPtr<GenericPromise> Close() override; 126 void Suspend(bool aCloseImmediately) override; 127 void Resume() override; 128 already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override; 129 bool HadCrossOriginRedirects() override; 130 bool CanClone() override; 131 already_AddRefed<BaseMediaResource> CloneData( 132 MediaResourceCallback* aDecoder) override; 133 nsresult ReadFromCache(char* aBuffer, int64_t aOffset, 134 uint32_t aCount) override; 135 136 // Other thread 137 void SetReadMode(MediaCacheStream::ReadMode aMode) override; 138 void SetPlaybackRate(uint32_t aBytesPerSecond) override; 139 nsresult ReadAt(int64_t offset, char* aBuffer, uint32_t aCount, 140 uint32_t* aBytes) override; 141 // Data stored in IO&lock-encumbered MediaCacheStream, caching recommended. 142 bool ShouldCacheReads() override { return true; } 143 144 // Any thread 145 void Pin() override; 146 void Unpin() override; 147 double GetDownloadRate(bool* aIsReliable) override; 148 int64_t GetLength() override; 149 int64_t GetNextCachedData(int64_t aOffset) override; 150 int64_t GetCachedDataEnd(int64_t aOffset) override; 151 bool IsDataCachedToEndOfResource(int64_t aOffset) override; 152 bool IsTransportSeekable() override; 153 bool IsLiveStream() const override { return mIsLiveStream; } 154 155 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { 156 // Might be useful to track in the future: 157 // - mListener (seems minor) 158 size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf); 159 size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf); 160 161 return size; 162 } 163 164 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { 165 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 166 } 167 168 void GetDebugInfo(dom::MediaResourceDebugInfo& aInfo) override; 169 170 class Listener final : public nsIInterfaceRequestor, 171 public nsIChannelEventSink, 172 public nsIThreadRetargetableStreamListener { 173 ~Listener() = default; 174 175 public: 176 Listener(ChannelMediaResource* aResource, int64_t aOffset, uint32_t aLoadID) 177 : mLock("Listener.mLock"), 178 mResource(aResource), 179 mOffset(aOffset), 180 mLoadID(aLoadID) {} 181 182 NS_DECL_THREADSAFE_ISUPPORTS 183 NS_DECL_NSIREQUESTOBSERVER 184 NS_DECL_NSISTREAMLISTENER 185 NS_DECL_NSICHANNELEVENTSINK 186 NS_DECL_NSIINTERFACEREQUESTOR 187 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 188 189 void Revoke(); 190 191 private: 192 MainThreadAndLockCapability<Mutex> mLock; 193 // mResource should only be modified on the main thread with the lock. 194 // So it can be read without lock on the main thread or on other threads 195 // with the lock. 196 RefPtr<ChannelMediaResource> mResource MOZ_GUARDED_BY(mLock); 197 198 const int64_t mOffset; 199 const uint32_t mLoadID; 200 }; 201 friend class Listener; 202 203 nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override; 204 205 protected: 206 nsresult Seek(int64_t aOffset, bool aResume); 207 208 // These are called on the main thread by Listener. 209 nsresult OnStartRequest(nsIRequest* aRequest, int64_t aRequestOffset); 210 nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus); 211 nsresult OnDataAvailable(uint32_t aLoadID, nsIInputStream* aStream, 212 uint32_t aCount); 213 nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, 214 uint32_t aFlags, int64_t aOffset); 215 216 // Use only before MediaDecoder shutdown. Main thread only. 217 dom::HTMLMediaElement* MediaElement() const; 218 // Opens the channel, using an HTTP byte range request to start at aOffset 219 // if possible. Main thread only. 220 nsresult OpenChannel(int64_t aOffset); 221 nsresult RecreateChannel(); 222 // Add headers to HTTP request. Main thread only. 223 nsresult SetupChannelHeaders(int64_t aOffset); 224 // Closes the channel. Main thread only. 225 void CloseChannel(); 226 // Update the principal for the resource. Main thread only. 227 void UpdatePrincipal(); 228 229 // Parses 'Content-Range' header and returns results via parameters. 230 // Returns error if header is not available, values are not parse-able or 231 // values are out of range. 232 nsresult ParseContentRangeHeader(nsIHttpChannel* aHttpChan, 233 int64_t& aRangeStart, int64_t& aRangeEnd, 234 int64_t& aRangeTotal) const; 235 236 // Calculates the length of the resource using HTTP headers, if this 237 // is an HTTP channel. Returns -1 on failure, or for non HTTP channels. 238 int64_t CalculateStreamLength() const; 239 240 struct Closure { 241 uint32_t mLoadID; 242 ChannelMediaResource* mResource; 243 }; 244 245 static nsresult CopySegmentToCache(nsIInputStream* aInStream, void* aClosure, 246 const char* aFromSegment, 247 uint32_t aToOffset, uint32_t aCount, 248 uint32_t* aWriteCount); 249 250 // Main thread access only 251 // True if Close() has been called. 252 bool mClosed = false; 253 // The last reported seekability state for the underlying channel 254 bool mIsTransportSeekable = false; 255 // Length of the content first reported. 256 int64_t mFirstReadLength = -1; 257 RefPtr<Listener> mListener; 258 // A mono-increasing integer to uniquely identify the channel we are loading. 259 uint32_t mLoadID = 0; 260 bool mIsLiveStream = false; 261 262 // Any thread access 263 MediaCacheStream mCacheStream; 264 265 ChannelSuspendAgent mSuspendAgent; 266 267 // The size of the stream if known at construction time (such as with blob) 268 const int64_t mKnownStreamLength; 269 }; 270 271 } // namespace mozilla 272 273 #endif // mozilla_dom_media_ChannelMediaResource_h