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