content/media/MediaResource.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #if !defined(MediaResource_h_)
michael@0 7 #define MediaResource_h_
michael@0 8
michael@0 9 #include "mozilla/Mutex.h"
michael@0 10 #include "nsIChannel.h"
michael@0 11 #include "nsIURI.h"
michael@0 12 #include "nsIStreamingProtocolController.h"
michael@0 13 #include "nsIStreamListener.h"
michael@0 14 #include "nsIChannelEventSink.h"
michael@0 15 #include "nsIInterfaceRequestor.h"
michael@0 16 #include "MediaCache.h"
michael@0 17 #include "mozilla/Attributes.h"
michael@0 18 #include "mozilla/TimeStamp.h"
michael@0 19 #include "nsThreadUtils.h"
michael@0 20
michael@0 21 // For HTTP seeking, if number of bytes needing to be
michael@0 22 // seeked forward is less than this value then a read is
michael@0 23 // done rather than a byte range request.
michael@0 24 static const int64_t SEEK_VS_READ_THRESHOLD = 32*1024;
michael@0 25
michael@0 26 static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;
michael@0 27
michael@0 28 // Number of bytes we have accumulated before we assume the connection download
michael@0 29 // rate can be reliably calculated. 57 Segments at IW=3 allows slow start to
michael@0 30 // reach a CWND of 30 (See bug 831998)
michael@0 31 static const int64_t RELIABLE_DATA_THRESHOLD = 57 * 1460;
michael@0 32
michael@0 33 class nsIHttpChannel;
michael@0 34 class nsIPrincipal;
michael@0 35
michael@0 36 namespace mozilla {
michael@0 37
michael@0 38 class MediaDecoder;
michael@0 39
michael@0 40 /**
michael@0 41 * This class is useful for estimating rates of data passing through
michael@0 42 * some channel. The idea is that activity on the channel "starts"
michael@0 43 * and "stops" over time. At certain times data passes through the
michael@0 44 * channel (usually while the channel is active; data passing through
michael@0 45 * an inactive channel is ignored). The GetRate() function computes
michael@0 46 * an estimate of the "current rate" of the channel, which is some
michael@0 47 * kind of average of the data passing through over the time the
michael@0 48 * channel is active.
michael@0 49 *
michael@0 50 * All methods take "now" as a parameter so the user of this class can
michael@0 51 * control the timeline used.
michael@0 52 */
michael@0 53 class MediaChannelStatistics {
michael@0 54 public:
michael@0 55 MediaChannelStatistics() { Reset(); }
michael@0 56
michael@0 57 MediaChannelStatistics(MediaChannelStatistics * aCopyFrom)
michael@0 58 {
michael@0 59 MOZ_ASSERT(aCopyFrom);
michael@0 60 mAccumulatedBytes = aCopyFrom->mAccumulatedBytes;
michael@0 61 mAccumulatedTime = aCopyFrom->mAccumulatedTime;
michael@0 62 mLastStartTime = aCopyFrom->mLastStartTime;
michael@0 63 mIsStarted = aCopyFrom->mIsStarted;
michael@0 64 }
michael@0 65
michael@0 66 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaChannelStatistics)
michael@0 67
michael@0 68 void Reset() {
michael@0 69 mLastStartTime = TimeStamp();
michael@0 70 mAccumulatedTime = TimeDuration(0);
michael@0 71 mAccumulatedBytes = 0;
michael@0 72 mIsStarted = false;
michael@0 73 }
michael@0 74 void Start() {
michael@0 75 if (mIsStarted)
michael@0 76 return;
michael@0 77 mLastStartTime = TimeStamp::Now();
michael@0 78 mIsStarted = true;
michael@0 79 }
michael@0 80 void Stop() {
michael@0 81 if (!mIsStarted)
michael@0 82 return;
michael@0 83 mAccumulatedTime += TimeStamp::Now() - mLastStartTime;
michael@0 84 mIsStarted = false;
michael@0 85 }
michael@0 86 void AddBytes(int64_t aBytes) {
michael@0 87 if (!mIsStarted) {
michael@0 88 // ignore this data, it may be related to seeking or some other
michael@0 89 // operation we don't care about
michael@0 90 return;
michael@0 91 }
michael@0 92 mAccumulatedBytes += aBytes;
michael@0 93 }
michael@0 94 double GetRateAtLastStop(bool* aReliable) {
michael@0 95 double seconds = mAccumulatedTime.ToSeconds();
michael@0 96 *aReliable = (seconds >= 1.0) ||
michael@0 97 (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
michael@0 98 if (seconds <= 0.0)
michael@0 99 return 0.0;
michael@0 100 return static_cast<double>(mAccumulatedBytes)/seconds;
michael@0 101 }
michael@0 102 double GetRate(bool* aReliable) {
michael@0 103 TimeDuration time = mAccumulatedTime;
michael@0 104 if (mIsStarted) {
michael@0 105 time += TimeStamp::Now() - mLastStartTime;
michael@0 106 }
michael@0 107 double seconds = time.ToSeconds();
michael@0 108 *aReliable = (seconds >= 3.0) ||
michael@0 109 (mAccumulatedBytes >= RELIABLE_DATA_THRESHOLD);
michael@0 110 if (seconds <= 0.0)
michael@0 111 return 0.0;
michael@0 112 return static_cast<double>(mAccumulatedBytes)/seconds;
michael@0 113 }
michael@0 114 private:
michael@0 115 int64_t mAccumulatedBytes;
michael@0 116 TimeDuration mAccumulatedTime;
michael@0 117 TimeStamp mLastStartTime;
michael@0 118 bool mIsStarted;
michael@0 119 };
michael@0 120
michael@0 121 // Forward declaration for use in MediaByteRange.
michael@0 122 class TimestampedMediaByteRange;
michael@0 123
michael@0 124 // Represents a section of contiguous media, with a start and end offset.
michael@0 125 // Used to denote ranges of data which are cached.
michael@0 126 class MediaByteRange {
michael@0 127 public:
michael@0 128 MediaByteRange() : mStart(0), mEnd(0) {}
michael@0 129
michael@0 130 MediaByteRange(int64_t aStart, int64_t aEnd)
michael@0 131 : mStart(aStart), mEnd(aEnd)
michael@0 132 {
michael@0 133 NS_ASSERTION(mStart < mEnd, "Range should end after start!");
michael@0 134 }
michael@0 135
michael@0 136 MediaByteRange(TimestampedMediaByteRange& aByteRange);
michael@0 137
michael@0 138 bool IsNull() const {
michael@0 139 return mStart == 0 && mEnd == 0;
michael@0 140 }
michael@0 141
michael@0 142 // Clears byte range values.
michael@0 143 void Clear() {
michael@0 144 mStart = 0;
michael@0 145 mEnd = 0;
michael@0 146 }
michael@0 147
michael@0 148 int64_t mStart, mEnd;
michael@0 149 };
michael@0 150
michael@0 151 // Represents a section of contiguous media, with a start and end offset, and
michael@0 152 // a timestamp representing the start time.
michael@0 153 class TimestampedMediaByteRange : public MediaByteRange {
michael@0 154 public:
michael@0 155 TimestampedMediaByteRange() : MediaByteRange(), mStartTime(-1) {}
michael@0 156
michael@0 157 TimestampedMediaByteRange(int64_t aStart, int64_t aEnd, int64_t aStartTime)
michael@0 158 : MediaByteRange(aStart, aEnd), mStartTime(aStartTime)
michael@0 159 {
michael@0 160 NS_ASSERTION(aStartTime >= 0, "Start time should not be negative!");
michael@0 161 }
michael@0 162
michael@0 163 bool IsNull() const {
michael@0 164 return MediaByteRange::IsNull() && mStartTime == -1;
michael@0 165 }
michael@0 166
michael@0 167 // Clears byte range values.
michael@0 168 void Clear() {
michael@0 169 MediaByteRange::Clear();
michael@0 170 mStartTime = -1;
michael@0 171 }
michael@0 172
michael@0 173 // In usecs.
michael@0 174 int64_t mStartTime;
michael@0 175 };
michael@0 176
michael@0 177 inline MediaByteRange::MediaByteRange(TimestampedMediaByteRange& aByteRange)
michael@0 178 : mStart(aByteRange.mStart), mEnd(aByteRange.mEnd)
michael@0 179 {
michael@0 180 NS_ASSERTION(mStart < mEnd, "Range should end after start!");
michael@0 181 }
michael@0 182
michael@0 183 class RtspMediaResource;
michael@0 184
michael@0 185 /**
michael@0 186 * Provides a thread-safe, seek/read interface to resources
michael@0 187 * loaded from a URI. Uses MediaCache to cache data received over
michael@0 188 * Necko's async channel API, thus resolving the mismatch between clients
michael@0 189 * that need efficient random access to the data and protocols that do not
michael@0 190 * support efficient random access, such as HTTP.
michael@0 191 *
michael@0 192 * Instances of this class must be created on the main thread.
michael@0 193 * Most methods must be called on the main thread only. Read, Seek and
michael@0 194 * Tell must only be called on non-main threads. In the case of the Ogg
michael@0 195 * Decoder they are called on the Decode thread for example. You must
michael@0 196 * ensure that no threads are calling these methods once Close is called.
michael@0 197 *
michael@0 198 * Instances of this class are reference counted. Use nsRefPtr for
michael@0 199 * managing the lifetime of instances of this class.
michael@0 200 *
michael@0 201 * The generic implementation of this class is ChannelMediaResource, which can
michael@0 202 * handle any URI for which Necko supports AsyncOpen.
michael@0 203 * The 'file:' protocol can be implemented efficiently with direct random
michael@0 204 * access, so the FileMediaResource implementation class bypasses the cache.
michael@0 205 * MediaResource::Create automatically chooses the best implementation class.
michael@0 206 */
michael@0 207 class MediaResource : public nsISupports
michael@0 208 {
michael@0 209 public:
michael@0 210 // Our refcounting is threadsafe, and when our refcount drops to zero
michael@0 211 // we dispatch an event to the main thread to delete the MediaResource.
michael@0 212 // Note that this means it's safe for references to this object to be
michael@0 213 // released on a non main thread, but the destructor will always run on
michael@0 214 // the main thread.
michael@0 215 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 216
michael@0 217 // The following can be called on the main thread only:
michael@0 218 // Get the URI
michael@0 219 virtual nsIURI* URI() const { return nullptr; }
michael@0 220 // Close the resource, stop any listeners, channels, etc.
michael@0 221 // Cancels any currently blocking Read request and forces that request to
michael@0 222 // return an error.
michael@0 223 virtual nsresult Close() = 0;
michael@0 224 // Suspend any downloads that are in progress.
michael@0 225 // If aCloseImmediately is set, resources should be released immediately
michael@0 226 // since we don't expect to resume again any time soon. Otherwise we
michael@0 227 // may resume again soon so resources should be held for a little
michael@0 228 // while.
michael@0 229 virtual void Suspend(bool aCloseImmediately) = 0;
michael@0 230 // Resume any downloads that have been suspended.
michael@0 231 virtual void Resume() = 0;
michael@0 232 // Get the current principal for the channel
michael@0 233 virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
michael@0 234 // If this returns false, then we shouldn't try to clone this MediaResource
michael@0 235 // because its underlying resources are not suitable for reuse (e.g.
michael@0 236 // because the underlying connection has been lost, or this resource
michael@0 237 // just can't be safely cloned). If this returns true, CloneData could
michael@0 238 // still fail. If this returns false, CloneData should not be called.
michael@0 239 virtual bool CanClone() { return false; }
michael@0 240 // Create a new stream of the same type that refers to the same URI
michael@0 241 // with a new channel. Any cached data associated with the original
michael@0 242 // stream should be accessible in the new stream too.
michael@0 243 virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) = 0;
michael@0 244 // Set statistics to be recorded to the object passed in.
michael@0 245 virtual void RecordStatisticsTo(MediaChannelStatistics *aStatistics) { }
michael@0 246
michael@0 247 // These methods are called off the main thread.
michael@0 248 // The mode is initially MODE_PLAYBACK.
michael@0 249 virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
michael@0 250 // This is the client's estimate of the playback rate assuming
michael@0 251 // the media plays continuously. The cache can't guess this itself
michael@0 252 // because it doesn't know when the decoder was paused, buffering, etc.
michael@0 253 virtual void SetPlaybackRate(uint32_t aBytesPerSecond) = 0;
michael@0 254 // Read up to aCount bytes from the stream. The buffer must have
michael@0 255 // enough room for at least aCount bytes. Stores the number of
michael@0 256 // actual bytes read in aBytes (0 on end of file).
michael@0 257 // May read less than aCount bytes if the number of
michael@0 258 // available bytes is less than aCount. Always check *aBytes after
michael@0 259 // read, and call again if necessary.
michael@0 260 virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) = 0;
michael@0 261 // Read up to aCount bytes from the stream. The read starts at
michael@0 262 // aOffset in the stream, seeking to that location initially if
michael@0 263 // it is not the current stream offset. The remaining arguments,
michael@0 264 // results and requirements are the same as per the Read method.
michael@0 265 virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
michael@0 266 uint32_t aCount, uint32_t* aBytes) = 0;
michael@0 267 // Seek to the given bytes offset in the stream. aWhence can be
michael@0 268 // one of:
michael@0 269 // NS_SEEK_SET
michael@0 270 // NS_SEEK_CUR
michael@0 271 // NS_SEEK_END
michael@0 272 //
michael@0 273 // In the Http strategy case the cancel will cause the http
michael@0 274 // channel's listener to close the pipe, forcing an i/o error on any
michael@0 275 // blocked read. This will allow the decode thread to complete the
michael@0 276 // event.
michael@0 277 //
michael@0 278 // In the case of a seek in progress, the byte range request creates
michael@0 279 // a new listener. This is done on the main thread via seek
michael@0 280 // synchronously dispatching an event. This avoids the issue of us
michael@0 281 // closing the listener but an outstanding byte range request
michael@0 282 // creating a new one. They run on the same thread so no explicit
michael@0 283 // synchronisation is required. The byte range request checks for
michael@0 284 // the cancel flag and does not create a new channel or listener if
michael@0 285 // we are cancelling.
michael@0 286 //
michael@0 287 // The default strategy does not do any seeking - the only issue is
michael@0 288 // a blocked read which it handles by causing the listener to close
michael@0 289 // the pipe, as per the http case.
michael@0 290 //
michael@0 291 // The file strategy doesn't block for any great length of time so
michael@0 292 // is fine for a no-op cancel.
michael@0 293 virtual nsresult Seek(int32_t aWhence, int64_t aOffset) = 0;
michael@0 294 virtual void StartSeekingForMetadata() = 0;
michael@0 295 virtual void EndSeekingForMetadata() = 0;
michael@0 296 // Report the current offset in bytes from the start of the stream.
michael@0 297 virtual int64_t Tell() = 0;
michael@0 298 // Moves any existing channel loads into the background, so that they don't
michael@0 299 // block the load event. Any new loads initiated (for example to seek)
michael@0 300 // will also be in the background.
michael@0 301 virtual void MoveLoadsToBackground() {}
michael@0 302 // Ensures that the value returned by IsSuspendedByCache below is up to date
michael@0 303 // (i.e. the cache has examined this stream at least once).
michael@0 304 virtual void EnsureCacheUpToDate() {}
michael@0 305
michael@0 306 // These can be called on any thread.
michael@0 307 // Cached blocks associated with this stream will not be evicted
michael@0 308 // while the stream is pinned.
michael@0 309 virtual void Pin() = 0;
michael@0 310 virtual void Unpin() = 0;
michael@0 311 // Get the estimated download rate in bytes per second (assuming no
michael@0 312 // pausing of the channel is requested by Gecko).
michael@0 313 // *aIsReliable is set to true if we think the estimate is useful.
michael@0 314 virtual double GetDownloadRate(bool* aIsReliable) = 0;
michael@0 315 // Get the length of the stream in bytes. Returns -1 if not known.
michael@0 316 // This can change over time; after a seek operation, a misbehaving
michael@0 317 // server may give us a resource of a different length to what it had
michael@0 318 // reported previously --- or it may just lie in its Content-Length
michael@0 319 // header and give us more or less data than it reported. We will adjust
michael@0 320 // the result of GetLength to reflect the data that's actually arriving.
michael@0 321 virtual int64_t GetLength() = 0;
michael@0 322 // Returns the offset of the first byte of cached data at or after aOffset,
michael@0 323 // or -1 if there is no such cached data.
michael@0 324 virtual int64_t GetNextCachedData(int64_t aOffset) = 0;
michael@0 325 // Returns the end of the bytes starting at the given offset
michael@0 326 // which are in cache.
michael@0 327 virtual int64_t GetCachedDataEnd(int64_t aOffset) = 0;
michael@0 328 // Returns true if all the data from aOffset to the end of the stream
michael@0 329 // is in cache. If the end of the stream is not known, we return false.
michael@0 330 virtual bool IsDataCachedToEndOfResource(int64_t aOffset) = 0;
michael@0 331 // Returns true if this stream is suspended by the cache because the
michael@0 332 // cache is full. If true then the decoder should try to start consuming
michael@0 333 // data, otherwise we may not be able to make progress.
michael@0 334 // MediaDecoder::NotifySuspendedStatusChanged is called when this
michael@0 335 // changes.
michael@0 336 // For resources using the media cache, this returns true only when all
michael@0 337 // streams for the same resource are all suspended.
michael@0 338 virtual bool IsSuspendedByCache() = 0;
michael@0 339 // Returns true if this stream has been suspended.
michael@0 340 virtual bool IsSuspended() = 0;
michael@0 341 // Reads only data which is cached in the media cache. If you try to read
michael@0 342 // any data which overlaps uncached data, or if aCount bytes otherwise can't
michael@0 343 // be read, this function will return failure. This function be called from
michael@0 344 // any thread, and it is the only read operation which is safe to call on
michael@0 345 // the main thread, since it's guaranteed to be non blocking.
michael@0 346 virtual nsresult ReadFromCache(char* aBuffer,
michael@0 347 int64_t aOffset,
michael@0 348 uint32_t aCount) = 0;
michael@0 349 // Returns true if the resource can be seeked to unbuffered ranges, i.e.
michael@0 350 // for an HTTP network stream this returns true if HTTP1.1 Byte Range
michael@0 351 // requests are supported by the connection/server.
michael@0 352 virtual bool IsTransportSeekable() = 0;
michael@0 353
michael@0 354 /**
michael@0 355 * Create a resource, reading data from the channel. Call on main thread only.
michael@0 356 * The caller must follow up by calling resource->Open().
michael@0 357 */
michael@0 358 static already_AddRefed<MediaResource> Create(MediaDecoder* aDecoder, nsIChannel* aChannel);
michael@0 359
michael@0 360 /**
michael@0 361 * Open the stream. This creates a stream listener and returns it in
michael@0 362 * aStreamListener; this listener needs to be notified of incoming data.
michael@0 363 */
michael@0 364 virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
michael@0 365
michael@0 366 /**
michael@0 367 * Fills aRanges with MediaByteRanges representing the data which is cached
michael@0 368 * in the media cache. Stream should be pinned during call and while
michael@0 369 * aRanges is being used.
michael@0 370 */
michael@0 371 virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) = 0;
michael@0 372
michael@0 373 // Ensure that the media cache writes any data held in its partial block.
michael@0 374 // Called on the main thread only.
michael@0 375 virtual void FlushCache() { }
michael@0 376
michael@0 377 // Notify that the last data byte range was loaded.
michael@0 378 virtual void NotifyLastByteRange() { }
michael@0 379
michael@0 380 // Returns the content type of the resource. This is copied from the
michael@0 381 // nsIChannel when the MediaResource is created. Safe to call from
michael@0 382 // any thread.
michael@0 383 virtual const nsCString& GetContentType() const = 0;
michael@0 384
michael@0 385 // Get the RtspMediaResource pointer if this MediaResource really is a
michael@0 386 // RtspMediaResource. For calling Rtsp specific functions.
michael@0 387 virtual RtspMediaResource* GetRtspPointer() {
michael@0 388 return nullptr;
michael@0 389 }
michael@0 390
michael@0 391 // Return true if the stream is a live stream
michael@0 392 virtual bool IsRealTime() {
michael@0 393 return false;
michael@0 394 }
michael@0 395
michael@0 396 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
michael@0 397 return 0;
michael@0 398 }
michael@0 399
michael@0 400 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
michael@0 401 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 402 }
michael@0 403
michael@0 404 protected:
michael@0 405 virtual ~MediaResource() {};
michael@0 406
michael@0 407 private:
michael@0 408 void Destroy();
michael@0 409 };
michael@0 410
michael@0 411 class BaseMediaResource : public MediaResource {
michael@0 412 public:
michael@0 413 virtual nsIURI* URI() const { return mURI; }
michael@0 414 virtual void MoveLoadsToBackground();
michael@0 415
michael@0 416 virtual size_t SizeOfExcludingThis(
michael@0 417 MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 418 {
michael@0 419 // Might be useful to track in the future:
michael@0 420 // - mChannel
michael@0 421 // - mURI (possibly owned, looks like just a ref from mChannel)
michael@0 422 // Not owned:
michael@0 423 // - mDecoder
michael@0 424 size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
michael@0 425 size += mContentType.SizeOfIncludingThisIfUnshared(aMallocSizeOf);
michael@0 426
michael@0 427 return size;
michael@0 428 }
michael@0 429
michael@0 430 virtual size_t SizeOfIncludingThis(
michael@0 431 MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 432 {
michael@0 433 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 434 }
michael@0 435
michael@0 436 protected:
michael@0 437 BaseMediaResource(MediaDecoder* aDecoder,
michael@0 438 nsIChannel* aChannel,
michael@0 439 nsIURI* aURI,
michael@0 440 const nsACString& aContentType) :
michael@0 441 mDecoder(aDecoder),
michael@0 442 mChannel(aChannel),
michael@0 443 mURI(aURI),
michael@0 444 mContentType(aContentType),
michael@0 445 mLoadInBackground(false)
michael@0 446 {
michael@0 447 MOZ_COUNT_CTOR(BaseMediaResource);
michael@0 448 NS_ASSERTION(!mContentType.IsEmpty(), "Must know content type");
michael@0 449 }
michael@0 450 virtual ~BaseMediaResource()
michael@0 451 {
michael@0 452 MOZ_COUNT_DTOR(BaseMediaResource);
michael@0 453 }
michael@0 454
michael@0 455 virtual const nsCString& GetContentType() const MOZ_OVERRIDE
michael@0 456 {
michael@0 457 return mContentType;
michael@0 458 }
michael@0 459
michael@0 460 // Set the request's load flags to aFlags. If the request is part of a
michael@0 461 // load group, the request is removed from the group, the flags are set, and
michael@0 462 // then the request is added back to the load group.
michael@0 463 void ModifyLoadFlags(nsLoadFlags aFlags);
michael@0 464
michael@0 465 // Dispatches an event to call MediaDecoder::NotifyBytesConsumed(aNumBytes, aOffset)
michael@0 466 // on the main thread. This is called automatically after every read.
michael@0 467 void DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset);
michael@0 468
michael@0 469 // This is not an nsCOMPointer to prevent a circular reference
michael@0 470 // between the decoder to the media stream object. The stream never
michael@0 471 // outlives the lifetime of the decoder.
michael@0 472 MediaDecoder* mDecoder;
michael@0 473
michael@0 474 // Channel used to download the media data. Must be accessed
michael@0 475 // from the main thread only.
michael@0 476 nsCOMPtr<nsIChannel> mChannel;
michael@0 477
michael@0 478 // URI in case the stream needs to be re-opened. Access from
michael@0 479 // main thread only.
michael@0 480 nsCOMPtr<nsIURI> mURI;
michael@0 481
michael@0 482 // Content-Type of the channel. This is copied from the nsIChannel when the
michael@0 483 // MediaResource is created. This is constant, so accessing from any thread
michael@0 484 // is safe.
michael@0 485 const nsAutoCString mContentType;
michael@0 486
michael@0 487 // True if MoveLoadsToBackground() has been called, i.e. the load event
michael@0 488 // has been fired, and all channel loads will be in the background.
michael@0 489 bool mLoadInBackground;
michael@0 490 };
michael@0 491
michael@0 492 /**
michael@0 493 * This is the MediaResource implementation that wraps Necko channels.
michael@0 494 * Much of its functionality is actually delegated to MediaCache via
michael@0 495 * an underlying MediaCacheStream.
michael@0 496 *
michael@0 497 * All synchronization is performed by MediaCacheStream; all off-main-
michael@0 498 * thread operations are delegated directly to that object.
michael@0 499 */
michael@0 500 class ChannelMediaResource : public BaseMediaResource
michael@0 501 {
michael@0 502 public:
michael@0 503 ChannelMediaResource(MediaDecoder* aDecoder,
michael@0 504 nsIChannel* aChannel,
michael@0 505 nsIURI* aURI,
michael@0 506 const nsACString& aContentType);
michael@0 507 ~ChannelMediaResource();
michael@0 508
michael@0 509 // These are called on the main thread by MediaCache. These must
michael@0 510 // not block or grab locks, because the media cache is holding its lock.
michael@0 511 // Notify that data is available from the cache. This can happen even
michael@0 512 // if this stream didn't read any data, since another stream might have
michael@0 513 // received data for the same resource.
michael@0 514 void CacheClientNotifyDataReceived();
michael@0 515 // Notify that we reached the end of the stream. This can happen even
michael@0 516 // if this stream didn't read any data, since another stream might have
michael@0 517 // received data for the same resource.
michael@0 518 void CacheClientNotifyDataEnded(nsresult aStatus);
michael@0 519 // Notify that the principal for the cached resource changed.
michael@0 520 void CacheClientNotifyPrincipalChanged();
michael@0 521
michael@0 522 // These are called on the main thread by MediaCache. These shouldn't block,
michael@0 523 // but they may grab locks --- the media cache is not holding its lock
michael@0 524 // when these are called.
michael@0 525 // Start a new load at the given aOffset. The old load is cancelled
michael@0 526 // and no more data from the old load will be notified via
michael@0 527 // MediaCacheStream::NotifyDataReceived/Ended.
michael@0 528 // This can fail.
michael@0 529 nsresult CacheClientSeek(int64_t aOffset, bool aResume);
michael@0 530 // Suspend the current load since data is currently not wanted
michael@0 531 nsresult CacheClientSuspend();
michael@0 532 // Resume the current load since data is wanted again
michael@0 533 nsresult CacheClientResume();
michael@0 534
michael@0 535 // Ensure that the media cache writes any data held in its partial block.
michael@0 536 // Called on the main thread.
michael@0 537 virtual void FlushCache() MOZ_OVERRIDE;
michael@0 538
michael@0 539 // Notify that the last data byte range was loaded.
michael@0 540 virtual void NotifyLastByteRange() MOZ_OVERRIDE;
michael@0 541
michael@0 542 // Main thread
michael@0 543 virtual nsresult Open(nsIStreamListener** aStreamListener);
michael@0 544 virtual nsresult Close();
michael@0 545 virtual void Suspend(bool aCloseImmediately);
michael@0 546 virtual void Resume();
michael@0 547 virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
michael@0 548 // Return true if the stream has been closed.
michael@0 549 bool IsClosed() const { return mCacheStream.IsClosed(); }
michael@0 550 virtual bool CanClone();
michael@0 551 virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder);
michael@0 552 // Set statistics to be recorded to the object passed in. If not called,
michael@0 553 // |ChannelMediaResource| will create it's own statistics objects in |Open|.
michael@0 554 void RecordStatisticsTo(MediaChannelStatistics *aStatistics) MOZ_OVERRIDE {
michael@0 555 NS_ASSERTION(aStatistics, "Statistics param cannot be null!");
michael@0 556 MutexAutoLock lock(mLock);
michael@0 557 if (!mChannelStatistics) {
michael@0 558 mChannelStatistics = aStatistics;
michael@0 559 }
michael@0 560 }
michael@0 561 virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
michael@0 562 virtual void EnsureCacheUpToDate();
michael@0 563
michael@0 564 // Other thread
michael@0 565 virtual void SetReadMode(MediaCacheStream::ReadMode aMode);
michael@0 566 virtual void SetPlaybackRate(uint32_t aBytesPerSecond);
michael@0 567 virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
michael@0 568 virtual nsresult ReadAt(int64_t offset, char* aBuffer,
michael@0 569 uint32_t aCount, uint32_t* aBytes);
michael@0 570 virtual nsresult Seek(int32_t aWhence, int64_t aOffset);
michael@0 571 virtual void StartSeekingForMetadata();
michael@0 572 virtual void EndSeekingForMetadata();
michael@0 573 virtual int64_t Tell();
michael@0 574
michael@0 575 // Any thread
michael@0 576 virtual void Pin();
michael@0 577 virtual void Unpin();
michael@0 578 virtual double GetDownloadRate(bool* aIsReliable);
michael@0 579 virtual int64_t GetLength();
michael@0 580 virtual int64_t GetNextCachedData(int64_t aOffset);
michael@0 581 virtual int64_t GetCachedDataEnd(int64_t aOffset);
michael@0 582 virtual bool IsDataCachedToEndOfResource(int64_t aOffset);
michael@0 583 virtual bool IsSuspendedByCache();
michael@0 584 virtual bool IsSuspended();
michael@0 585 virtual bool IsTransportSeekable() MOZ_OVERRIDE;
michael@0 586
michael@0 587 virtual size_t SizeOfExcludingThis(
michael@0 588 MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
michael@0 589 // Might be useful to track in the future:
michael@0 590 // - mListener (seems minor)
michael@0 591 // - mChannelStatistics (seems minor)
michael@0 592 // owned if RecordStatisticsTo is not called
michael@0 593 // - mDataReceivedEvent (seems minor)
michael@0 594 size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
michael@0 595 size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
michael@0 596
michael@0 597 return size;
michael@0 598 }
michael@0 599
michael@0 600 virtual size_t SizeOfIncludingThis(
michael@0 601 MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
michael@0 602 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 603 }
michael@0 604
michael@0 605 class Listener MOZ_FINAL : public nsIStreamListener,
michael@0 606 public nsIInterfaceRequestor,
michael@0 607 public nsIChannelEventSink
michael@0 608 {
michael@0 609 public:
michael@0 610 Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
michael@0 611 ~Listener() {}
michael@0 612
michael@0 613 NS_DECL_ISUPPORTS
michael@0 614 NS_DECL_NSIREQUESTOBSERVER
michael@0 615 NS_DECL_NSISTREAMLISTENER
michael@0 616 NS_DECL_NSICHANNELEVENTSINK
michael@0 617 NS_DECL_NSIINTERFACEREQUESTOR
michael@0 618
michael@0 619 void Revoke() { mResource = nullptr; }
michael@0 620
michael@0 621 private:
michael@0 622 nsRefPtr<ChannelMediaResource> mResource;
michael@0 623 };
michael@0 624 friend class Listener;
michael@0 625
michael@0 626 nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
michael@0 627
michael@0 628 protected:
michael@0 629 // These are called on the main thread by Listener.
michael@0 630 nsresult OnStartRequest(nsIRequest* aRequest);
michael@0 631 nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
michael@0 632 nsresult OnDataAvailable(nsIRequest* aRequest,
michael@0 633 nsIInputStream* aStream,
michael@0 634 uint32_t aCount);
michael@0 635 nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
michael@0 636
michael@0 637 // Opens the channel, using an HTTP byte range request to start at mOffset
michael@0 638 // if possible. Main thread only.
michael@0 639 nsresult OpenChannel(nsIStreamListener** aStreamListener);
michael@0 640 nsresult RecreateChannel();
michael@0 641 // Add headers to HTTP request. Main thread only.
michael@0 642 void SetupChannelHeaders();
michael@0 643 // Closes the channel. Main thread only.
michael@0 644 void CloseChannel();
michael@0 645
michael@0 646 // Parses 'Content-Range' header and returns results via parameters.
michael@0 647 // Returns error if header is not available, values are not parse-able or
michael@0 648 // values are out of range.
michael@0 649 nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
michael@0 650 int64_t& aRangeStart,
michael@0 651 int64_t& aRangeEnd,
michael@0 652 int64_t& aRangeTotal);
michael@0 653
michael@0 654 void DoNotifyDataReceived();
michael@0 655
michael@0 656 static NS_METHOD CopySegmentToCache(nsIInputStream *aInStream,
michael@0 657 void *aClosure,
michael@0 658 const char *aFromSegment,
michael@0 659 uint32_t aToOffset,
michael@0 660 uint32_t aCount,
michael@0 661 uint32_t *aWriteCount);
michael@0 662
michael@0 663 // Suspend the channel only if the channels is currently downloading data.
michael@0 664 // If it isn't we set a flag, mIgnoreResume, so that PossiblyResume knows
michael@0 665 // whether to acutually resume or not.
michael@0 666 void PossiblySuspend();
michael@0 667
michael@0 668 // Resume from a suspend if we actually suspended (See PossiblySuspend).
michael@0 669 void PossiblyResume();
michael@0 670
michael@0 671 // Main thread access only
michael@0 672 int64_t mOffset;
michael@0 673 nsRefPtr<Listener> mListener;
michael@0 674 // A data received event for the decoder that has been dispatched but has
michael@0 675 // not yet been processed.
michael@0 676 nsRevocableEventPtr<nsRunnableMethod<ChannelMediaResource, void, false> > mDataReceivedEvent;
michael@0 677 uint32_t mSuspendCount;
michael@0 678 // When this flag is set, if we get a network error we should silently
michael@0 679 // reopen the stream.
michael@0 680 bool mReopenOnError;
michael@0 681 // When this flag is set, we should not report the next close of the
michael@0 682 // channel.
michael@0 683 bool mIgnoreClose;
michael@0 684
michael@0 685 // Any thread access
michael@0 686 MediaCacheStream mCacheStream;
michael@0 687
michael@0 688 // This lock protects mChannelStatistics
michael@0 689 Mutex mLock;
michael@0 690 nsRefPtr<MediaChannelStatistics> mChannelStatistics;
michael@0 691
michael@0 692 // True if we couldn't suspend the stream and we therefore don't want
michael@0 693 // to resume later. This is usually due to the channel not being in the
michael@0 694 // isPending state at the time of the suspend request.
michael@0 695 bool mIgnoreResume;
michael@0 696
michael@0 697 // True if we are seeking to get the real duration of the file.
michael@0 698 bool mSeekingForMetadata;
michael@0 699
michael@0 700 // Start and end offset of the bytes to be requested.
michael@0 701 MediaByteRange mByteRange;
michael@0 702
michael@0 703 // True if the stream can seek into unbuffered ranged, i.e. if the
michael@0 704 // connection supports byte range requests.
michael@0 705 bool mIsTransportSeekable;
michael@0 706 };
michael@0 707
michael@0 708 } // namespace mozilla
michael@0 709
michael@0 710 #endif

mercurial