1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla code.
18 * The Initial Developer of the Original Code is the Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Chris Double <chris.double@double.co.nz>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsMediaStream.h"
40 #include "nsMediaDecoder.h"
41 #include "nsNetUtil.h"
42 #include "nsAutoLock.h"
43 #include "nsThreadUtils.h"
45 #include "nsIFileChannel.h"
46 #include "nsIHttpChannel.h"
47 #include "nsISeekableStream.h"
48 #include "nsIInputStream.h"
49 #include "nsIOutputStream.h"
50 #include "nsIRequestObserver.h"
51 #include "nsIStreamListener.h"
52 #include "nsIScriptSecurityManager.h"
53 #include "nsChannelToPipeListener.h"
55 // For HTTP seeking, if number of bytes needing to be
56 // seeked forward is less than this value then a read is
57 // done rather than a byte range request.
58 #define SEEK_VS_READ_THRESHOLD (32*1024)
60 class nsDefaultStreamStrategy
: public nsStreamStrategy
63 nsDefaultStreamStrategy(nsMediaDecoder
* aDecoder
, nsIChannel
* aChannel
, nsIURI
* aURI
) :
64 nsStreamStrategy(aDecoder
, aChannel
, aURI
),
69 // These methods have the same thread calling requirements
70 // as those with the same name in nsMediaStream
71 virtual nsresult
Open(nsIStreamListener
** aStreamListener
);
72 virtual nsresult
Close();
73 virtual nsresult
Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
);
74 virtual nsresult
Seek(PRInt32 aWhence
, PRInt64 aOffset
);
75 virtual PRInt64
Tell();
76 virtual PRUint32
Available();
77 virtual float DownloadRate();
78 virtual void Cancel();
79 virtual nsIPrincipal
* GetCurrentPrincipal();
82 // Listener attached to channel to constantly download the
83 // media data asynchronously and store it in the pipe. The
84 // data is obtainable via the mPipeInput member. Use on
86 nsCOMPtr
<nsChannelToPipeListener
> mListener
;
88 // Input stream for the media data currently downloaded
89 // and stored in the pipe. This can be used from any thread.
90 nsCOMPtr
<nsIInputStream
> mPipeInput
;
92 // Current seek position. Need to compute this manually because
93 // the underlying channel may not offer this information.
97 nsresult
nsDefaultStreamStrategy::Open(nsIStreamListener
** aStreamListener
)
99 if (aStreamListener
) {
100 *aStreamListener
= nsnull
;
103 mListener
= new nsChannelToPipeListener(mDecoder
);
104 NS_ENSURE_TRUE(mListener
, NS_ERROR_OUT_OF_MEMORY
);
106 nsresult rv
= mListener
->Init();
107 NS_ENSURE_SUCCESS(rv
, rv
);
109 if (aStreamListener
) {
110 *aStreamListener
= mListener
;
111 NS_ADDREF(mListener
);
113 rv
= mChannel
->AsyncOpen(mListener
, nsnull
);
114 NS_ENSURE_SUCCESS(rv
, rv
);
117 rv
= mListener
->GetInputStream(getter_AddRefs(mPipeInput
));
118 NS_ENSURE_SUCCESS(rv
, rv
);
125 nsresult
nsDefaultStreamStrategy::Close()
127 nsAutoLock
lock(mLock
);
129 mChannel
->Cancel(NS_BINDING_ABORTED
);
140 nsresult
nsDefaultStreamStrategy::Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
)
142 // The read request pulls from the pipe, not the channels input
143 // stream. This allows calling from any thread as the pipe is
145 nsAutoLock
lock(mLock
);
147 return NS_ERROR_FAILURE
;
149 nsresult rv
= mPipeInput
->Read(aBuffer
, aCount
, aBytes
);
150 NS_ENSURE_SUCCESS(rv
, rv
);
151 mPosition
+= *aBytes
;
156 nsresult
nsDefaultStreamStrategy::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
158 // Default streams cannot be seeked
159 return NS_ERROR_FAILURE
;
162 PRInt64
nsDefaultStreamStrategy::Tell()
167 PRUint32
nsDefaultStreamStrategy::Available()
169 // The request pulls from the pipe, not the channels input
170 // stream. This allows calling from any thread as the pipe is
172 nsAutoLock
lock(mLock
);
177 mPipeInput
->Available(&count
);
181 float nsDefaultStreamStrategy::DownloadRate()
183 nsAutoLock
lock(mLock
);
184 return mListener
? mListener
->BytesPerSecond() : NS_MEDIA_UNKNOWN_RATE
;
187 void nsDefaultStreamStrategy::Cancel()
193 nsIPrincipal
* nsDefaultStreamStrategy::GetCurrentPrincipal()
198 return mListener
->GetCurrentPrincipal();
201 class nsFileStreamStrategy
: public nsStreamStrategy
204 nsFileStreamStrategy(nsMediaDecoder
* aDecoder
, nsIChannel
* aChannel
, nsIURI
* aURI
) :
205 nsStreamStrategy(aDecoder
, aChannel
, aURI
)
209 // These methods have the same thread calling requirements
210 // as those with the same name in nsMediaStream
211 virtual nsresult
Open(nsIStreamListener
** aStreamListener
);
212 virtual nsresult
Close();
213 virtual nsresult
Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
);
214 virtual nsresult
Seek(PRInt32 aWhence
, PRInt64 aOffset
);
215 virtual PRInt64
Tell();
216 virtual PRUint32
Available();
217 virtual float DownloadRate();
218 virtual nsIPrincipal
* GetCurrentPrincipal();
221 // Seekable stream interface to file. This can be used from any
223 nsCOMPtr
<nsISeekableStream
> mSeekable
;
225 // Input stream for the media data. This can be used from any
227 nsCOMPtr
<nsIInputStream
> mInput
;
229 // Security Principal
230 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
233 nsresult
nsFileStreamStrategy::Open(nsIStreamListener
** aStreamListener
)
235 if (aStreamListener
) {
236 *aStreamListener
= nsnull
;
240 if (aStreamListener
) {
241 // The channel is already open. We need a synchronous stream that
242 // implements nsISeekableStream, so we have to find the underlying
243 // file and reopen it
244 nsCOMPtr
<nsIFileChannel
> fc(do_QueryInterface(mChannel
));
246 return NS_ERROR_UNEXPECTED
;
248 nsCOMPtr
<nsIFile
> file
;
249 rv
= fc
->GetFile(getter_AddRefs(file
));
250 NS_ENSURE_SUCCESS(rv
, rv
);
252 rv
= NS_NewLocalFileInputStream(getter_AddRefs(mInput
), file
);
254 rv
= mChannel
->Open(getter_AddRefs(mInput
));
256 NS_ENSURE_SUCCESS(rv
, rv
);
258 mSeekable
= do_QueryInterface(mInput
);
260 // XXX The file may just be a .url or similar
261 // shortcut that points to a Web site. We need to fix this by
262 // doing an async open and waiting until we locate the real resource,
263 // then using that (if it's still a file!).
264 return NS_ERROR_FAILURE
;
267 // Get the file size and inform the decoder. Only files up to 4GB are
270 rv
= mInput
->Available(&size
);
271 if (NS_SUCCEEDED(rv
)) {
272 mDecoder
->SetTotalBytes(size
);
275 /* Get our principal */
276 nsCOMPtr
<nsIScriptSecurityManager
> secMan
=
277 do_GetService("@mozilla.org/scriptsecuritymanager;1");
279 rv
= secMan
->GetChannelPrincipal(mChannel
,
280 getter_AddRefs(mPrincipal
));
286 // For a file stream the resource is considered loaded since there
287 // is no buffering delays, etc reading.
288 nsCOMPtr
<nsIRunnable
> event
=
289 NS_NEW_RUNNABLE_METHOD(nsMediaDecoder
, mDecoder
, ResourceLoaded
);
290 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
295 nsresult
nsFileStreamStrategy::Close()
297 nsAutoLock
lock(mLock
);
299 mChannel
->Cancel(NS_BINDING_ABORTED
);
308 nsresult
nsFileStreamStrategy::Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
)
310 nsAutoLock
lock(mLock
);
311 return mInput
? mInput
->Read(aBuffer
, aCount
, aBytes
) : NS_ERROR_FAILURE
;
314 nsresult
nsFileStreamStrategy::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
316 nsAutoLock
lock(mLock
);
317 return mSeekable
? mSeekable
->Seek(aWhence
, aOffset
) : NS_ERROR_FAILURE
;
320 PRInt64
nsFileStreamStrategy::Tell()
322 nsAutoLock
lock(mLock
);
327 mSeekable
->Tell(&offset
);
331 PRUint32
nsFileStreamStrategy::Available()
333 nsAutoLock
lock(mLock
);
338 mInput
->Available(&count
);
342 float nsFileStreamStrategy::DownloadRate()
344 return NS_MEDIA_UNKNOWN_RATE
;
347 nsIPrincipal
* nsFileStreamStrategy::GetCurrentPrincipal()
352 class nsHttpStreamStrategy
: public nsStreamStrategy
355 nsHttpStreamStrategy(nsMediaDecoder
* aDecoder
, nsIChannel
* aChannel
, nsIURI
* aURI
) :
356 nsStreamStrategy(aDecoder
, aChannel
, aURI
),
363 // These methods have the same thread calling requirements
364 // as those with the same name in nsMediaStream
365 virtual nsresult
Open(nsIStreamListener
** aListener
);
366 virtual nsresult
Close();
367 virtual nsresult
Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
);
368 virtual nsresult
Seek(PRInt32 aWhence
, PRInt64 aOffset
);
369 virtual PRInt64
Tell();
370 virtual PRUint32
Available();
371 virtual float DownloadRate();
372 virtual void Cancel();
373 virtual nsIPrincipal
* GetCurrentPrincipal();
375 // Return PR_TRUE if the stream has been cancelled.
376 PRBool
IsCancelled() const;
378 // This must be called on the main thread only, and at a
379 // time when the strategy is not reading from the current
380 // channel/stream. It's primary purpose is to be called from
381 // a Seek to reset to the new byte range request http channel.
382 void Reset(nsIChannel
* aChannel
,
383 nsChannelToPipeListener
* aListener
,
384 nsIInputStream
* aStream
);
387 // Listener attached to channel to constantly download the
388 // media data asynchronously and store it in the pipe. The
389 // data is obtainable via the mPipeInput member. Use on
391 nsCOMPtr
<nsChannelToPipeListener
> mListener
;
393 // Input stream for the media data currently downloaded
394 // and stored in the pipe. This can be used from any thread.
395 nsCOMPtr
<nsIInputStream
> mPipeInput
;
397 // Current seek position. Need to compute this manually due to
398 // seeking with byte range requests meaning the position in the pipe
399 // is not valid. This is initially set on the main thread during the
400 // Open call. After that it is read and written by a single thread
401 // only (the thread that calls the read/seek operations).
404 // PR_TRUE if we are positioned at the end of the file.
405 // This is written and read from a single thread only (the thread that
406 // calls the read/seek operations).
409 // PR_TRUE if the media stream requested this strategy is cancelled.
410 // This is read and written on the main thread only.
411 PRPackedBool mCancelled
;
414 void nsHttpStreamStrategy::Reset(nsIChannel
* aChannel
,
415 nsChannelToPipeListener
* aListener
,
416 nsIInputStream
* aStream
)
418 nsAutoLock
lock(mLock
);
420 mListener
= aListener
;
421 mPipeInput
= aStream
;
424 nsresult
nsHttpStreamStrategy::Open(nsIStreamListener
**aStreamListener
)
426 if (aStreamListener
) {
427 *aStreamListener
= nsnull
;
430 mListener
= new nsChannelToPipeListener(mDecoder
);
431 NS_ENSURE_TRUE(mListener
, NS_ERROR_OUT_OF_MEMORY
);
433 nsresult rv
= mListener
->Init();
434 NS_ENSURE_SUCCESS(rv
, rv
);
436 if (aStreamListener
) {
437 *aStreamListener
= mListener
;
438 NS_ADDREF(*aStreamListener
);
440 // Use a byte range request from the start of the resource.
441 // This enables us to detect if the stream supports byte range
442 // requests, and therefore seeking, early.
443 nsCOMPtr
<nsIHttpChannel
> hc
= do_QueryInterface(mChannel
);
445 hc
->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
446 NS_LITERAL_CSTRING("bytes=0-"),
450 rv
= mChannel
->AsyncOpen(mListener
, nsnull
);
451 NS_ENSURE_SUCCESS(rv
, rv
);
454 rv
= mListener
->GetInputStream(getter_AddRefs(mPipeInput
));
455 NS_ENSURE_SUCCESS(rv
, rv
);
462 nsresult
nsHttpStreamStrategy::Close()
464 nsAutoLock
lock(mLock
);
466 mChannel
->Cancel(NS_BINDING_ABORTED
);
477 nsresult
nsHttpStreamStrategy::Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
)
479 // The read request pulls from the pipe, not the channels input
480 // stream. This allows calling from any thread as the pipe is
482 nsAutoLock
lock(mLock
);
484 return NS_ERROR_FAILURE
;
486 // If Cancel() is called then the read will fail with an error so we
487 // can bail out of the blocking call.
488 nsresult rv
= mPipeInput
->Read(aBuffer
, aCount
, aBytes
);
489 NS_ENSURE_SUCCESS(rv
, rv
);
490 mPosition
+= *aBytes
;
495 class nsByteRangeEvent
: public nsRunnable
498 nsByteRangeEvent(nsHttpStreamStrategy
* aStrategy
,
499 nsMediaDecoder
* aDecoder
,
502 mStrategy(aStrategy
),
508 MOZ_COUNT_CTOR(nsByteRangeEvent
);
513 MOZ_COUNT_DTOR(nsByteRangeEvent
);
522 // This event runs in the main thread. The same
523 // thread as that which can block waiting for the
524 // decode event to complete when the stream
525 // is cancelled. Check to see if we are cancelled
526 // in case this event is processed after the cancel flag
527 // which would otherwise cause the listener to be recreated.
528 if (mStrategy
->IsCancelled()) {
529 mResult
= NS_ERROR_FAILURE
;
534 mResult
= NS_NewChannel(getter_AddRefs(mChannel
),
539 nsIRequest::LOAD_NORMAL
);
540 NS_ENSURE_SUCCESS(mResult
, mResult
);
541 nsCOMPtr
<nsIHttpChannel
> hc
= do_QueryInterface(mChannel
);
543 nsCAutoString
rangeString("bytes=");
544 rangeString
.AppendInt(mOffset
);
545 rangeString
.Append("-");
546 hc
->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString
, PR_FALSE
);
549 mListener
= new nsChannelToPipeListener(mDecoder
, PR_TRUE
);
550 NS_ENSURE_TRUE(mListener
, NS_ERROR_OUT_OF_MEMORY
);
552 mResult
= mListener
->Init();
553 NS_ENSURE_SUCCESS(mResult
, mResult
);
555 mResult
= mChannel
->AsyncOpen(mListener
, nsnull
);
556 NS_ENSURE_SUCCESS(mResult
, mResult
);
558 mResult
= mListener
->GetInputStream(getter_AddRefs(mStream
));
559 NS_ENSURE_SUCCESS(mResult
, mResult
);
561 mStrategy
->Reset(mChannel
, mListener
, mStream
);
566 nsHttpStreamStrategy
* mStrategy
;
567 nsMediaDecoder
* mDecoder
;
569 nsCOMPtr
<nsIChannel
> mChannel
;
570 nsCOMPtr
<nsChannelToPipeListener
> mListener
;
571 nsCOMPtr
<nsIInputStream
> mStream
;
576 nsresult
nsHttpStreamStrategy::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
579 nsAutoLock
lock(mLock
);
580 if (!mChannel
|| !mPipeInput
)
581 return NS_ERROR_FAILURE
;
583 // When seeking liboggz will first seek to the end of the file to
584 // obtain the length of the file. It immediately does a 'tell' to
585 // get the position and reseeks somewhere else. This traps the seek
586 // to end of file and sets mAtEOF. Tell() looks for this flag being
587 // set and returns the content length.
588 if(aWhence
== nsISeekableStream::NS_SEEK_END
&& aOffset
== 0) {
589 if (mDecoder
->GetTotalBytes() == -1)
590 return NS_ERROR_FAILURE
;
599 // Handle cases of aWhence not being NS_SEEK_SET by converting to
602 case nsISeekableStream::NS_SEEK_END
: {
604 mChannel
->GetContentLength(&length
);
606 return NS_ERROR_FAILURE
;
609 aWhence
= nsISeekableStream::NS_SEEK_SET
;
612 case nsISeekableStream::NS_SEEK_CUR
: {
613 aOffset
+= mPosition
;
614 aWhence
= nsISeekableStream::NS_SEEK_SET
;
618 // Do nothing, we are NS_SEEK_SET
622 // If we are already at the correct position, do nothing
623 if (aOffset
== mPosition
) {
627 // If we are seeking to a byterange that we already have buffered in
628 // the listener then move to that and avoid the need to send a byte
630 PRInt32 bytesAhead
= aOffset
- mPosition
;
631 PRUint32 available
= 0;
632 nsresult rv
= mPipeInput
->Available(&available
);
633 PRInt32 diff
= available
- PRUint32(bytesAhead
);
635 // If we have enough buffered data to satisfy the seek request
636 // just do a read and discard the data.
637 // If the seek request is for a small amount ahead (defined by
638 // SEEK_VS_READ_THRESHOLD) then also perform a read and discard
639 // instead of doing a byte range request. This improves the speed
640 // of the seeks quite a bit.
641 if (NS_SUCCEEDED(rv
) && bytesAhead
> 0 && diff
> -SEEK_VS_READ_THRESHOLD
) {
642 nsAutoArrayPtr
<char> data(new char[bytesAhead
]);
644 return NS_ERROR_OUT_OF_MEMORY
;
646 // Read until the read cursor reaches new seek point. If Cancel() is
647 // called then the read will fail with an error so we can bail out of
648 // the blocking call.
649 PRUint32 bytesRead
= 0;
652 nsresult rv
= mPipeInput
->Read(data
.get(),
653 (bytesAhead
-bytesRead
),
655 NS_ENSURE_SUCCESS(rv
, rv
);
656 NS_ENSURE_TRUE(bytes
!= 0, NS_ERROR_FAILURE
); // Tried to read past EOF.
659 } while (bytesRead
!= bytesAhead
);
664 // Don't acquire mLock in this scope as we do a synchronous call to the main thread
665 // which would deadlock if that thread is calling Close().
666 nsCOMPtr
<nsByteRangeEvent
> event
= new nsByteRangeEvent(this, mDecoder
, mURI
, aOffset
);
667 NS_DispatchToMainThread(event
, NS_DISPATCH_SYNC
);
669 // If the sync request fails, or a call to Cancel() is made during the request,
670 // don't update the position and return the error.
671 nsresult rv
= event
->GetResult();
672 if (NS_SUCCEEDED(rv
)) {
679 PRInt64
nsHttpStreamStrategy::Tell()
681 // Handle the case of a seek to EOF by liboggz
682 // (See Seek for details)
683 return mAtEOF
? mDecoder
->GetTotalBytes() : mPosition
;
686 PRUint32
nsHttpStreamStrategy::Available()
688 // The request pulls from the pipe, not the channels input
689 // stream. This allows calling from any thread as the pipe is
691 nsAutoLock
lock(mLock
);
696 mPipeInput
->Available(&count
);
700 float nsHttpStreamStrategy::DownloadRate()
702 nsAutoLock
lock(mLock
);
704 return NS_MEDIA_UNKNOWN_RATE
;
705 return mListener
->BytesPerSecond();
708 void nsHttpStreamStrategy::Cancel()
710 mCancelled
= PR_TRUE
;
715 PRBool
nsHttpStreamStrategy::IsCancelled() const
720 nsIPrincipal
* nsHttpStreamStrategy::GetCurrentPrincipal()
725 return mListener
->GetCurrentPrincipal();
728 nsMediaStream::nsMediaStream() :
729 mPlaybackRateCount(0)
731 NS_ASSERTION(NS_IsMainThread(),
732 "nsMediaStream created on non-main thread");
733 MOZ_COUNT_CTOR(nsMediaStream
);
736 nsMediaStream::~nsMediaStream()
738 MOZ_COUNT_DTOR(nsMediaStream
);
741 nsresult
nsMediaStream::Open(nsMediaDecoder
* aDecoder
, nsIURI
* aURI
,
742 nsIChannel
* aChannel
, nsIStreamListener
** aListener
)
744 NS_ASSERTION(NS_IsMainThread(),
745 "nsMediaStream::Open called on non-main thread");
747 nsCOMPtr
<nsIChannel
> channel
;
751 nsresult rv
= NS_NewChannel(getter_AddRefs(channel
),
756 nsIRequest::LOAD_NORMAL
);
757 NS_ENSURE_SUCCESS(rv
, rv
);
760 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(channel
);
761 nsCOMPtr
<nsIHttpChannel
> hc
= do_QueryInterface(channel
);
763 mStreamStrategy
= new nsHttpStreamStrategy(aDecoder
, channel
, aURI
);
765 mStreamStrategy
= new nsFileStreamStrategy(aDecoder
, channel
, aURI
);
767 mStreamStrategy
= new nsDefaultStreamStrategy(aDecoder
, channel
, aURI
);
769 mPlaybackRateCount
= 0;
770 mPlaybackRateStart
= PR_IntervalNow();
772 return mStreamStrategy
->Open(aListener
);
775 nsresult
nsMediaStream::Close()
777 NS_ASSERTION(NS_IsMainThread(),
778 "nsMediaStream::Close called on non-main thread");
780 return mStreamStrategy
->Close();
783 nsresult
nsMediaStream::Read(char* aBuffer
, PRUint32 aCount
, PRUint32
* aBytes
)
785 nsresult rv
= mStreamStrategy
->Read(aBuffer
, aCount
, aBytes
);
786 mPlaybackRateCount
+= *aBytes
;
790 nsresult
nsMediaStream::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
792 return mStreamStrategy
->Seek(aWhence
, aOffset
);
795 PRInt64
nsMediaStream::Tell()
797 return mStreamStrategy
->Tell();
800 PRUint32
nsMediaStream::Available()
802 return mStreamStrategy
->Available();
805 float nsMediaStream::DownloadRate()
807 return mStreamStrategy
->DownloadRate();
810 float nsMediaStream::PlaybackRate()
812 PRIntervalTime now
= PR_IntervalNow();
813 PRUint32 interval
= PR_IntervalToMilliseconds(now
- mPlaybackRateStart
);
814 return static_cast<float>(mPlaybackRateCount
) * 1000 / interval
;
817 void nsMediaStream::Cancel()
819 NS_ASSERTION(NS_IsMainThread(),
820 "nsMediaStream::Cancel called on non-main thread");
822 // In the Http strategy case the cancel will cause the http
823 // channel's listener to close the pipe, forcing an i/o error on any
824 // blocked read. This will allow the decode thread to complete the
827 // In the case of a seek in progress, the byte range request creates
828 // a new listener. This is done on the main thread via seek
829 // synchronously dispatching an event. This avoids the issue of us
830 // closing the listener but an outstanding byte range request
831 // creating a new one. They run on the same thread so no explicit
832 // synchronisation is required. The byte range request checks for
833 // the cancel flag and does not create a new channel or listener if
834 // we are cancelling.
836 // The default strategy does not do any seeking - the only issue is
837 // a blocked read which it handles by causing the listener to close
838 // the pipe, as per the http case.
840 // The file strategy doesn't block for any great length of time so
841 // is fine for a no-op cancel.
842 mStreamStrategy
->Cancel();
845 nsIPrincipal
* nsMediaStream::GetCurrentPrincipal()
847 return mStreamStrategy
->GetCurrentPrincipal();