summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaResource.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaResource.h')
-rw-r--r--dom/media/MediaResource.h283
1 files changed, 283 insertions, 0 deletions
diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h
new file mode 100644
index 0000000000..223340b876
--- /dev/null
+++ b/dom/media/MediaResource.h
@@ -0,0 +1,283 @@
+/* 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/. */
+
+#if !defined(MediaResource_h_)
+# define MediaResource_h_
+
+# include "DecoderDoctorLogger.h"
+# include "Intervals.h"
+# include "MediaData.h"
+# include "mozilla/Attributes.h"
+# include "mozilla/UniquePtr.h"
+# include "nsISeekableStream.h"
+# include "nsThreadUtils.h"
+
+namespace mozilla {
+
+// Represents a section of contiguous media, with a start and end offset.
+// Used to denote ranges of data which are cached.
+
+typedef media::Interval<int64_t> MediaByteRange;
+typedef media::IntervalSet<int64_t> MediaByteRangeSet;
+
+DDLoggedTypeDeclName(MediaResource);
+
+/**
+ * Provides a thread-safe, seek/read interface to resources
+ * loaded from a URI. Uses MediaCache to cache data received over
+ * Necko's async channel API, thus resolving the mismatch between clients
+ * that need efficient random access to the data and protocols that do not
+ * support efficient random access, such as HTTP.
+ *
+ * Instances of this class must be created on the main thread.
+ * Most methods must be called on the main thread only. Read, Seek and
+ * Tell must only be called on non-main threads. In the case of the Ogg
+ * Decoder they are called on the Decode thread for example. You must
+ * ensure that no threads are calling these methods once Close is called.
+ *
+ * Instances of this class are reference counted. Use nsRefPtr for
+ * managing the lifetime of instances of this class.
+ *
+ * The generic implementation of this class is ChannelMediaResource, which can
+ * handle any URI for which Necko supports AsyncOpen.
+ * The 'file:' protocol can be implemented efficiently with direct random
+ * access, so the FileMediaResource implementation class bypasses the cache.
+ * For cross-process blob URL, CloneableWithRangeMediaResource is used.
+ * MediaResource::Create automatically chooses the best implementation class.
+ */
+class MediaResource : public DecoderDoctorLifeLogger<MediaResource> {
+ public:
+ // Our refcounting is threadsafe, and when our refcount drops to zero
+ // we dispatch an event to the main thread to delete the MediaResource.
+ // Note that this means it's safe for references to this object to be
+ // released on a non main thread, but the destructor will always run on
+ // the main thread.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ MediaResource)
+
+ // Close the resource, stop any listeners, channels, etc.
+ // Cancels any currently blocking Read request and forces that request to
+ // return an error. This must be called (and resolve) before the MediaResource
+ // is deleted.
+ virtual RefPtr<GenericPromise> Close() {
+ return GenericPromise::CreateAndResolve(true, __func__);
+ }
+
+ // These methods are called off the main thread.
+ // Read up to aCount bytes from the stream. The read starts at
+ // aOffset in the stream, seeking to that location initially if
+ // it is not the current stream offset. The remaining arguments,
+ // results and requirements are the same as per the Read method.
+ virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
+ uint32_t* aBytes) = 0;
+ // Indicate whether caching data in advance of reads is worth it.
+ // E.g. Caching lockless and memory-based MediaResource subclasses would be a
+ // waste, but caching lock/IO-bound resources means reducing the impact of
+ // each read.
+ virtual bool ShouldCacheReads() = 0;
+
+ // These can be called on any thread.
+ // Cached blocks associated with this stream will not be evicted
+ // while the stream is pinned.
+ virtual void Pin() = 0;
+ virtual void Unpin() = 0;
+ // Get the length of the stream in bytes. Returns -1 if not known.
+ // This can change over time; after a seek operation, a misbehaving
+ // server may give us a resource of a different length to what it had
+ // reported previously --- or it may just lie in its Content-Length
+ // header and give us more or less data than it reported. We will adjust
+ // the result of GetLength to reflect the data that's actually arriving.
+ virtual int64_t GetLength() = 0;
+ // Returns the offset of the first byte of cached data at or after aOffset,
+ // or -1 if there is no such cached data.
+ virtual int64_t GetNextCachedData(int64_t aOffset) = 0;
+ // Returns the end of the bytes starting at the given offset which are in
+ // cache. Returns aOffset itself if there are zero bytes available there.
+ virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0;
+ // Returns true if all the data from aOffset to the end of the stream
+ // is in cache. If the end of the stream is not known, we return false.
+ virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0;
+ // Reads only data which is cached in the media cache. If you try to read
+ // any data which overlaps uncached data, or if aCount bytes otherwise can't
+ // be read, this function will return failure. This function be called from
+ // any thread, and it is the only read operation which is safe to call on
+ // the main thread, since it's guaranteed to be non blocking.
+ virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
+ uint32_t aCount) = 0;
+
+ /**
+ * Fills aRanges with MediaByteRanges representing the data which is cached
+ * in the media cache. Stream should be pinned during call and while
+ * aRanges is being used.
+ */
+ virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0;
+
+ protected:
+ virtual ~MediaResource() = default;
+};
+
+/**
+ * RAII class that handles pinning and unpinning for MediaResource and derived.
+ * This should be used when making calculations that involve potentially-cached
+ * MediaResource data, so that the state of the world can't change out from
+ * under us.
+ */
+template <class T>
+class MOZ_RAII AutoPinned {
+ public:
+ explicit AutoPinned(T* aResource) : mResource(aResource) {
+ MOZ_ASSERT(mResource);
+ mResource->Pin();
+ }
+
+ ~AutoPinned() { mResource->Unpin(); }
+
+ operator T*() const { return mResource; }
+ T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
+
+ private:
+ T* mResource;
+};
+
+DDLoggedTypeDeclName(MediaResourceIndex);
+
+/*
+ * MediaResourceIndex provides a way to access MediaResource objects.
+ * Read, Seek and Tell must only be called on non-main threads.
+ * In the case of the Ogg Decoder they are called on the Decode thread for
+ * example. You must ensure that no threads are calling these methods once
+ * the MediaResource has been Closed.
+ */
+class MediaResourceIndex : public DecoderDoctorLifeLogger<MediaResourceIndex> {
+ public:
+ explicit MediaResourceIndex(MediaResource* aResource);
+
+ // Read up to aCount bytes from the stream. The buffer must have
+ // enough room for at least aCount bytes. Stores the number of
+ // actual bytes read in aBytes (0 on end of file).
+ // May read less than aCount bytes if the number of
+ // available bytes is less than aCount. Always check *aBytes after
+ // read, and call again if necessary.
+ nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
+ // Seek to the given bytes offset in the stream. aWhence can be
+ // one of:
+ // nsISeekableStream::NS_SEEK_SET
+ // nsISeekableStream::NS_SEEK_CUR
+ // nsISeekableStream::NS_SEEK_END
+ //
+ // In the Http strategy case the cancel will cause the http
+ // channel's listener to close the pipe, forcing an i/o error on any
+ // blocked read. This will allow the decode thread to complete the
+ // event.
+ //
+ // In the case of a seek in progress, the byte range request creates
+ // a new listener. This is done on the main thread via seek
+ // synchronously dispatching an event. This avoids the issue of us
+ // closing the listener but an outstanding byte range request
+ // creating a new one. They run on the same thread so no explicit
+ // synchronisation is required. The byte range request checks for
+ // the cancel flag and does not create a new channel or listener if
+ // we are cancelling.
+ //
+ // The default strategy does not do any seeking - the only issue is
+ // a blocked read which it handles by causing the listener to close
+ // the pipe, as per the http case.
+ //
+ // The file strategy doesn't block for any great length of time so
+ // is fine for a no-op cancel.
+ nsresult Seek(int32_t aWhence, int64_t aOffset);
+ // Report the current offset in bytes from the start of the stream.
+ int64_t Tell() const { return mOffset; }
+
+ // Return the underlying MediaResource.
+ MediaResource* GetResource() const { return mResource; }
+
+ // Read up to aCount bytes from the stream. The read starts at
+ // aOffset in the stream, seeking to that location initially if
+ // it is not the current stream offset.
+ // Unlike MediaResource::ReadAt, ReadAt only returns fewer bytes than
+ // requested if end of stream or an error is encountered. There is no need to
+ // call it again to get more data.
+ // If the resource has cached data past the end of the request, it will be
+ // used to fill a local cache, which should speed up consecutive ReadAt's
+ // (mostly by avoiding using the resource's IOs and locks.)
+ // *aBytes will contain the number of bytes copied, even if an error occurred.
+ // ReadAt doesn't have an impact on the offset returned by Tell().
+ nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
+ uint32_t* aBytes);
+
+ // Same as ReadAt, but doesn't try to cache around the read.
+ // Useful if you know that you will not read again from the same area.
+ nsresult UncachedReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
+ uint32_t* aBytes) const;
+
+ // Similar to ReadAt, but doesn't try to cache around the read.
+ // Useful if you know that you will not read again from the same area.
+ // Will attempt to read aRequestedCount+aExtraCount, repeatedly calling
+ // MediaResource/ ReadAt()'s until a read returns 0 bytes (so we may actually
+ // get less than aRequestedCount bytes), or until we get at least
+ // aRequestedCount bytes (so we may not get any/all of the aExtraCount bytes.)
+ nsresult UncachedRangedReadAt(int64_t aOffset, char* aBuffer,
+ uint32_t aRequestedCount, uint32_t aExtraCount,
+ uint32_t* aBytes) const;
+
+ // This method returns nullptr if anything fails.
+ // Otherwise, it returns an owned buffer.
+ // MediaReadAt may return fewer bytes than requested if end of stream is
+ // encountered. There is no need to call it again to get more data.
+ // Note this method will not update mOffset.
+ already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset,
+ uint32_t aCount) const;
+
+ already_AddRefed<MediaByteBuffer> CachedMediaReadAt(int64_t aOffset,
+ uint32_t aCount) const;
+
+ // Get the length of the stream in bytes. Returns -1 if not known.
+ // This can change over time; after a seek operation, a misbehaving
+ // server may give us a resource of a different length to what it had
+ // reported previously --- or it may just lie in its Content-Length
+ // header and give us more or less data than it reported. We will adjust
+ // the result of GetLength to reflect the data that's actually arriving.
+ int64_t GetLength() const;
+
+ private:
+ // If the resource has cached data past the requested range, try to grab it
+ // into our local cache.
+ // If there is no cached data, or attempting to read it fails, fallback on
+ // a (potentially-blocking) read of just what was requested, so that we don't
+ // get unexpected side-effects by trying to read more than intended.
+ nsresult CacheOrReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
+ uint32_t* aBytes);
+
+ // Maps a file offset to a mCachedBlock index.
+ uint32_t IndexInCache(int64_t aOffsetInFile) const;
+
+ // Starting file offset of the cache block that contains a given file offset.
+ int64_t CacheOffsetContaining(int64_t aOffsetInFile) const;
+
+ RefPtr<MediaResource> mResource;
+ int64_t mOffset;
+
+ // Local cache used by ReadAt().
+ // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
+ // data of length mCachedBytes, starting at offset `mCachedOffset` in the
+ // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
+ //
+ // resource: |------------------------------------------------------|
+ // <----------> mCacheBlockSize
+ // <---------------------------------> mCachedOffset
+ // <--> mCachedBytes
+ // mCachedBlock: |..----....|
+ // CacheOffsetContaining(mCachedOffset) <--> IndexInCache(mCachedOffset)
+ // <------------------------------>
+ const uint32_t mCacheBlockSize;
+ int64_t mCachedOffset;
+ uint32_t mCachedBytes;
+ UniquePtr<char[]> mCachedBlock;
+};
+
+} // namespace mozilla
+
+#endif