Coverage Report

Created: 2018-09-25 14:53

/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