MediaResource.h (13427B)
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 "DecoderDoctorLogger.h" 10 # include "Intervals.h" 11 # include "MediaData.h" 12 # include "mozilla/Attributes.h" 13 # include "mozilla/UniquePtr.h" 14 # include "nsISeekableStream.h" 15 # include "nsThreadUtils.h" 16 17 namespace mozilla { 18 19 // Represents a section of contiguous media, with a start and end offset. 20 // Used to denote ranges of data which are cached. 21 22 typedef media::Interval<int64_t> MediaByteRange; 23 typedef media::IntervalSet<int64_t> MediaByteRangeSet; 24 25 DDLoggedTypeDeclName(MediaResource); 26 27 /** 28 * Provides a thread-safe, seek/read interface to resources 29 * loaded from a URI. Uses MediaCache to cache data received over 30 * Necko's async channel API, thus resolving the mismatch between clients 31 * that need efficient random access to the data and protocols that do not 32 * support efficient random access, such as HTTP. 33 * 34 * Instances of this class must be created on the main thread. 35 * Most methods must be called on the main thread only. Read, Seek and 36 * Tell must only be called on non-main threads. In the case of the Ogg 37 * Decoder they are called on the Decode thread for example. You must 38 * ensure that no threads are calling these methods once Close is called. 39 * 40 * Instances of this class are reference counted. Use nsRefPtr for 41 * managing the lifetime of instances of this class. 42 * 43 * The generic implementation of this class is ChannelMediaResource, which can 44 * handle any URI for which Necko supports AsyncOpen. 45 * The 'file:' protocol can be implemented efficiently with direct random 46 * access, so the FileMediaResource implementation class bypasses the cache. 47 * For cross-process blob URL, CloneableWithRangeMediaResource is used. 48 * MediaResource::Create automatically chooses the best implementation class. 49 */ 50 class MediaResource : public DecoderDoctorLifeLogger<MediaResource> { 51 public: 52 // Our refcounting is threadsafe, and when our refcount drops to zero 53 // we dispatch an event to the main thread to delete the MediaResource. 54 // Note that this means it's safe for references to this object to be 55 // released on a non main thread, but the destructor will always run on 56 // the main thread. 57 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD( 58 MediaResource) 59 60 // Close the resource, stop any listeners, channels, etc. 61 // Cancels any currently blocking Read request and forces that request to 62 // return an error. This must be called (and resolve) before the MediaResource 63 // is deleted. 64 virtual RefPtr<GenericPromise> Close() { 65 return GenericPromise::CreateAndResolve(true, __func__); 66 } 67 68 // These methods are called off the main thread. 69 // Read up to aCount bytes from the stream. The read starts at 70 // aOffset in the stream, seeking to that location initially if 71 // it is not the current stream offset. 72 // On success, *aBytes returns the number of bytes that were actually read. 73 // This can be less than aCount on end of stream or when remaining bytes will 74 // not be available because a network error has occurred. 75 // This will block until the data is available or the stream is closed. 76 // For most resources, this will block until the data is available or the 77 // stream is closed. 78 // The exception is SourceBufferResource, which will return 79 // NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA if aCount bytes are not available to 80 // read. 81 virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, 82 uint32_t* aBytes) = 0; 83 // Indicate whether caching data in advance of reads is worth it. 84 // E.g. Caching lockless and memory-based MediaResource subclasses would be a 85 // waste, but caching lock/IO-bound resources means reducing the impact of 86 // each read. 87 virtual bool ShouldCacheReads() = 0; 88 89 // These can be called on any thread. 90 // Cached blocks associated with this stream will not be evicted 91 // while the stream is pinned. 92 virtual void Pin() = 0; 93 virtual void Unpin() = 0; 94 // Get the length of the stream in bytes. Returns -1 if not known. 95 // This can change over time; after a seek operation, a misbehaving 96 // server may give us a resource of a different length to what it had 97 // reported previously --- or it may just lie in its Content-Length 98 // header and give us more or less data than it reported. We will adjust 99 // the result of GetLength to reflect the data that's actually arriving. 100 virtual int64_t GetLength() = 0; 101 // Returns the offset of the first byte of cached data at or after aOffset, 102 // or -1 if there is no such cached data. 103 virtual int64_t GetNextCachedData(int64_t aOffset) = 0; 104 // Returns the end of the bytes starting at the given offset which are in 105 // cache. Returns aOffset itself if there are zero bytes available there. 106 virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0; 107 // Returns true if all the data from aOffset to the end of the stream 108 // is in cache. If the end of the stream is not known, we return false. 109 virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0; 110 // Reads only data which is cached in the media cache. If you try to read 111 // any data which overlaps uncached data, or if aCount bytes otherwise can't 112 // be read, this function will return failure. This function be called from 113 // any thread, and it is the only read operation which is safe to call on 114 // the main thread, since it's guaranteed to be non blocking. 115 virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, 116 uint32_t aCount) = 0; 117 118 /** 119 * Fills aRanges with MediaByteRanges representing the data which is cached 120 * in the media cache. Stream should be pinned during call and while 121 * aRanges is being used. 122 */ 123 virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0; 124 125 protected: 126 virtual ~MediaResource() = default; 127 }; 128 129 /** 130 * RAII class that handles pinning and unpinning for MediaResource and derived. 131 * This should be used when making calculations that involve potentially-cached 132 * MediaResource data, so that the state of the world can't change out from 133 * under us. 134 */ 135 template <class T> 136 class MOZ_RAII AutoPinned { 137 public: 138 explicit AutoPinned(T* aResource) : mResource(aResource) { 139 MOZ_ASSERT(mResource); 140 mResource->Pin(); 141 } 142 143 ~AutoPinned() { mResource->Unpin(); } 144 145 operator T*() const { return mResource; } 146 T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; } 147 148 private: 149 T* mResource; 150 }; 151 152 DDLoggedTypeDeclName(MediaResourceIndex); 153 154 /* 155 * MediaResourceIndex provides a way to access MediaResource objects. 156 * Read, Seek and Tell must only be called on non-main threads. 157 * In the case of the Ogg Decoder they are called on the Decode thread for 158 * example. You must ensure that no threads are calling these methods once 159 * the MediaResource has been Closed. 160 */ 161 class MediaResourceIndex : public DecoderDoctorLifeLogger<MediaResourceIndex> { 162 public: 163 explicit MediaResourceIndex(MediaResource* aResource); 164 165 // Read up to aCount bytes from the stream. The buffer must have 166 // enough room for at least aCount bytes. Stores the number of 167 // actual bytes read in *aBytes. This can be less than aCount on end 168 // of stream or error. 169 nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes); 170 // Seek to the given bytes offset in the stream. aWhence can be 171 // one of: 172 // nsISeekableStream::NS_SEEK_SET 173 // nsISeekableStream::NS_SEEK_CUR 174 // nsISeekableStream::NS_SEEK_END 175 // 176 // In the Http strategy case the cancel will cause the http 177 // channel's listener to close the pipe, forcing an i/o error on any 178 // blocked read. This will allow the decode thread to complete the 179 // event. 180 // 181 // In the case of a seek in progress, the byte range request creates 182 // a new listener. This is done on the main thread via seek 183 // synchronously dispatching an event. This avoids the issue of us 184 // closing the listener but an outstanding byte range request 185 // creating a new one. They run on the same thread so no explicit 186 // synchronisation is required. The byte range request checks for 187 // the cancel flag and does not create a new channel or listener if 188 // we are cancelling. 189 // 190 // The default strategy does not do any seeking - the only issue is 191 // a blocked read which it handles by causing the listener to close 192 // the pipe, as per the http case. 193 // 194 // The file strategy doesn't block for any great length of time so 195 // is fine for a no-op cancel. 196 nsresult Seek(int32_t aWhence, int64_t aOffset); 197 // Report the current offset in bytes from the start of the stream. 198 int64_t Tell() const { return mOffset; } 199 200 // Return the underlying MediaResource. 201 MediaResource* GetResource() const { return mResource; } 202 203 // Read up to aCount bytes from the stream. The read starts at 204 // aOffset in the stream, seeking to that location initially if 205 // it is not the current stream offset. 206 // ReadAt only returns fewer bytes than 207 // requested if end of stream or an error is encountered. There is no need to 208 // call it again to get more data. 209 // If the resource has cached data past the end of the request, it will be 210 // used to fill a local cache, which should speed up consecutive ReadAt's 211 // (mostly by avoiding using the resource's IOs and locks.) 212 // *aBytes will contain the number of bytes copied, even if an error occurred. 213 // ReadAt doesn't have an impact on the offset returned by Tell(). 214 nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, 215 uint32_t* aBytes); 216 217 // Same as ReadAt, but doesn't try to cache around the read. 218 // Useful if you know that you will not read again from the same area. 219 nsresult UncachedReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, 220 uint32_t* aBytes) const; 221 222 // Similar to ReadAt, but doesn't try to cache around the read. 223 // Useful if you know that you will not read again from the same area. 224 // Will attempt to read aRequestedCount+aExtraCount, repeatedly calling 225 // MediaResource/ ReadAt()'s until a read returns 0 bytes (so we may actually 226 // get less than aRequestedCount bytes), or until we get at least 227 // aRequestedCount bytes (so we may not get any/all of the aExtraCount bytes.) 228 nsresult UncachedRangedReadAt(int64_t aOffset, char* aBuffer, 229 uint32_t aRequestedCount, uint32_t aExtraCount, 230 uint32_t* aBytes) const; 231 232 // This method returns nullptr if anything fails. 233 // Otherwise, it returns an owned buffer. 234 // MediaReadAt may return fewer bytes than requested if end of stream is 235 // encountered. There is no need to call it again to get more data. 236 // Note this method will not update mOffset. 237 already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, 238 uint32_t aCount) const; 239 240 already_AddRefed<MediaByteBuffer> CachedMediaReadAt(int64_t aOffset, 241 uint32_t aCount) const; 242 243 // Get the length of the stream in bytes. Returns -1 if not known. 244 // This can change over time; after a seek operation, a misbehaving 245 // server may give us a resource of a different length to what it had 246 // reported previously --- or it may just lie in its Content-Length 247 // header and give us more or less data than it reported. We will adjust 248 // the result of GetLength to reflect the data that's actually arriving. 249 int64_t GetLength() const; 250 251 private: 252 // If the resource has cached data past the requested range, try to grab it 253 // into our local cache. 254 // If there is no cached data, or attempting to read it fails, fallback on 255 // a (potentially-blocking) read of just what was requested, so that we don't 256 // get unexpected side-effects by trying to read more than intended. 257 nsresult CacheOrReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, 258 uint32_t* aBytes); 259 260 // Maps a file offset to a mCachedBlock index. 261 uint32_t IndexInCache(int64_t aOffsetInFile) const; 262 263 // Starting file offset of the cache block that contains a given file offset. 264 int64_t CacheOffsetContaining(int64_t aOffsetInFile) const; 265 266 RefPtr<MediaResource> mResource; 267 int64_t mOffset; 268 269 // Local cache used by ReadAt(). 270 // mCachedBlock is valid when mCachedBytes != 0, in which case it contains 271 // data of length mCachedBytes, starting at offset `mCachedOffset` in the 272 // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock. 273 // 274 // resource: |------------------------------------------------------| 275 // <----------> mCacheBlockSize 276 // <---------------------------------> mCachedOffset 277 // <--> mCachedBytes 278 // mCachedBlock: |..----....| 279 // CacheOffsetContaining(mCachedOffset) <--> IndexInCache(mCachedOffset) 280 // <------------------------------> 281 const uint32_t mCacheBlockSize; 282 int64_t mCachedOffset; 283 uint32_t mCachedBytes; 284 UniquePtr<char[]> mCachedBlock; 285 }; 286 287 } // namespace mozilla 288 289 #endif