Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / content / media / video / src / nsMediaStream.cpp
blobaf56d251b17ebf4b24c3c87aea4c57b8a8725623
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
14 * License.
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.
22 * Contributor(s):
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 ***** */
38 #include "nsDebug.h"
39 #include "nsMediaStream.h"
40 #include "nsMediaDecoder.h"
41 #include "nsNetUtil.h"
42 #include "nsAutoLock.h"
43 #include "nsThreadUtils.h"
44 #include "nsIFile.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
62 public:
63 nsDefaultStreamStrategy(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
64 nsStreamStrategy(aDecoder, aChannel, aURI),
65 mPosition(0)
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();
81 private:
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
85 // main thread only.
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.
94 PRInt64 mPosition;
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);
112 } else {
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);
120 mPosition = 0;
122 return NS_OK;
125 nsresult nsDefaultStreamStrategy::Close()
127 nsAutoLock lock(mLock);
128 if (mChannel) {
129 mChannel->Cancel(NS_BINDING_ABORTED);
130 mChannel = nsnull;
132 if (mPipeInput) {
133 mPipeInput->Close();
134 mPipeInput = nsnull;
136 mListener = nsnull;
137 return NS_OK;
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
144 // threadsafe.
145 nsAutoLock lock(mLock);
146 if (!mPipeInput)
147 return NS_ERROR_FAILURE;
149 nsresult rv = mPipeInput->Read(aBuffer, aCount, aBytes);
150 NS_ENSURE_SUCCESS(rv, rv);
151 mPosition += *aBytes;
153 return NS_OK;
156 nsresult nsDefaultStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
158 // Default streams cannot be seeked
159 return NS_ERROR_FAILURE;
162 PRInt64 nsDefaultStreamStrategy::Tell()
164 return mPosition;
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
171 // threadsafe.
172 nsAutoLock lock(mLock);
173 if (!mPipeInput)
174 return 0;
176 PRUint32 count = 0;
177 mPipeInput->Available(&count);
178 return count;
181 float nsDefaultStreamStrategy::DownloadRate()
183 nsAutoLock lock(mLock);
184 return mListener ? mListener->BytesPerSecond() : NS_MEDIA_UNKNOWN_RATE;
187 void nsDefaultStreamStrategy::Cancel()
189 if (mListener)
190 mListener->Cancel();
193 nsIPrincipal* nsDefaultStreamStrategy::GetCurrentPrincipal()
195 if (!mListener)
196 return nsnull;
198 return mListener->GetCurrentPrincipal();
201 class nsFileStreamStrategy : public nsStreamStrategy
203 public:
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();
220 private:
221 // Seekable stream interface to file. This can be used from any
222 // thread.
223 nsCOMPtr<nsISeekableStream> mSeekable;
225 // Input stream for the media data. This can be used from any
226 // thread.
227 nsCOMPtr<nsIInputStream> mInput;
229 // Security Principal
230 nsCOMPtr<nsIPrincipal> mPrincipal;
233 nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
235 if (aStreamListener) {
236 *aStreamListener = nsnull;
239 nsresult rv;
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));
245 if (!fc)
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);
253 } else {
254 rv = mChannel->Open(getter_AddRefs(mInput));
256 NS_ENSURE_SUCCESS(rv, rv);
258 mSeekable = do_QueryInterface(mInput);
259 if (!mSeekable) {
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
268 // supported here.
269 PRUint32 size;
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");
278 if (secMan) {
279 rv = secMan->GetChannelPrincipal(mChannel,
280 getter_AddRefs(mPrincipal));
281 if (NS_FAILED(rv)) {
282 return rv;
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);
292 return NS_OK;
295 nsresult nsFileStreamStrategy::Close()
297 nsAutoLock lock(mLock);
298 if (mChannel) {
299 mChannel->Cancel(NS_BINDING_ABORTED);
300 mChannel = nsnull;
301 mInput = nsnull;
302 mSeekable = nsnull;
305 return NS_OK;
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);
323 if (!mSeekable)
324 return 0;
326 PRInt64 offset = 0;
327 mSeekable->Tell(&offset);
328 return offset;
331 PRUint32 nsFileStreamStrategy::Available()
333 nsAutoLock lock(mLock);
334 if (!mInput)
335 return 0;
337 PRUint32 count = 0;
338 mInput->Available(&count);
339 return count;
342 float nsFileStreamStrategy::DownloadRate()
344 return NS_MEDIA_UNKNOWN_RATE;
347 nsIPrincipal* nsFileStreamStrategy::GetCurrentPrincipal()
349 return mPrincipal;
352 class nsHttpStreamStrategy : public nsStreamStrategy
354 public:
355 nsHttpStreamStrategy(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
356 nsStreamStrategy(aDecoder, aChannel, aURI),
357 mPosition(0),
358 mAtEOF(PR_FALSE),
359 mCancelled(PR_FALSE)
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);
386 private:
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
390 // main thread only.
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).
402 PRInt64 mPosition;
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).
407 PRPackedBool mAtEOF;
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);
419 mChannel = aChannel;
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);
439 } else {
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);
444 if (hc) {
445 hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
446 NS_LITERAL_CSTRING("bytes=0-"),
447 PR_FALSE);
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);
457 mPosition = 0;
459 return NS_OK;
462 nsresult nsHttpStreamStrategy::Close()
464 nsAutoLock lock(mLock);
465 if (mChannel) {
466 mChannel->Cancel(NS_BINDING_ABORTED);
467 mChannel = nsnull;
469 if (mPipeInput) {
470 mPipeInput->Close();
471 mPipeInput = nsnull;
473 mListener = nsnull;
474 return NS_OK;
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
481 // threadsafe.
482 nsAutoLock lock(mLock);
483 if (!mPipeInput)
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;
492 return rv;
495 class nsByteRangeEvent : public nsRunnable
497 public:
498 nsByteRangeEvent(nsHttpStreamStrategy* aStrategy,
499 nsMediaDecoder* aDecoder,
500 nsIURI* aURI,
501 PRInt64 aOffset) :
502 mStrategy(aStrategy),
503 mDecoder(aDecoder),
504 mURI(aURI),
505 mOffset(aOffset),
506 mResult(NS_OK)
508 MOZ_COUNT_CTOR(nsByteRangeEvent);
511 ~nsByteRangeEvent()
513 MOZ_COUNT_DTOR(nsByteRangeEvent);
516 nsresult GetResult()
518 return mResult;
521 NS_IMETHOD Run() {
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;
530 return NS_OK;
533 mStrategy->Close();
534 mResult = NS_NewChannel(getter_AddRefs(mChannel),
535 mURI,
536 nsnull,
537 nsnull,
538 nsnull,
539 nsIRequest::LOAD_NORMAL);
540 NS_ENSURE_SUCCESS(mResult, mResult);
541 nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
542 if (hc) {
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);
562 return NS_OK;
565 private:
566 nsHttpStreamStrategy* mStrategy;
567 nsMediaDecoder* mDecoder;
568 nsIURI* mURI;
569 nsCOMPtr<nsIChannel> mChannel;
570 nsCOMPtr<nsChannelToPipeListener> mListener;
571 nsCOMPtr<nsIInputStream> mStream;
572 PRInt64 mOffset;
573 nsresult mResult;
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;
592 mAtEOF = PR_TRUE;
593 return NS_OK;
595 else {
596 mAtEOF = PR_FALSE;
599 // Handle cases of aWhence not being NS_SEEK_SET by converting to
600 // NS_SEEK_SET
601 switch (aWhence) {
602 case nsISeekableStream::NS_SEEK_END: {
603 PRInt32 length;
604 mChannel->GetContentLength(&length);
605 if (length == -1)
606 return NS_ERROR_FAILURE;
608 aOffset -= length;
609 aWhence = nsISeekableStream::NS_SEEK_SET;
610 break;
612 case nsISeekableStream::NS_SEEK_CUR: {
613 aOffset += mPosition;
614 aWhence = nsISeekableStream::NS_SEEK_SET;
615 break;
617 default:
618 // Do nothing, we are NS_SEEK_SET
619 break;
622 // If we are already at the correct position, do nothing
623 if (aOffset == mPosition) {
624 return NS_OK;
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
629 // range request.
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]);
643 if (!data)
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;
650 PRUint32 bytes = 0;
651 do {
652 nsresult rv = mPipeInput->Read(data.get(),
653 (bytesAhead-bytesRead),
654 &bytes);
655 NS_ENSURE_SUCCESS(rv, rv);
656 NS_ENSURE_TRUE(bytes != 0, NS_ERROR_FAILURE); // Tried to read past EOF.
657 mPosition += bytes;
658 bytesRead += bytes;
659 } while (bytesRead != bytesAhead);
660 return rv;
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)) {
673 mPosition = aOffset;
676 return 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
690 // threadsafe.
691 nsAutoLock lock(mLock);
692 if (!mPipeInput)
693 return 0;
695 PRUint32 count = 0;
696 mPipeInput->Available(&count);
697 return count;
700 float nsHttpStreamStrategy::DownloadRate()
702 nsAutoLock lock(mLock);
703 if (!mListener)
704 return NS_MEDIA_UNKNOWN_RATE;
705 return mListener->BytesPerSecond();
708 void nsHttpStreamStrategy::Cancel()
710 mCancelled = PR_TRUE;
711 if (mListener)
712 mListener->Cancel();
715 PRBool nsHttpStreamStrategy::IsCancelled() const
717 return mCancelled;
720 nsIPrincipal* nsHttpStreamStrategy::GetCurrentPrincipal()
722 if (!mListener)
723 return nsnull;
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;
748 if (aChannel) {
749 channel = aChannel;
750 } else {
751 nsresult rv = NS_NewChannel(getter_AddRefs(channel),
752 aURI,
753 nsnull,
754 nsnull,
755 nsnull,
756 nsIRequest::LOAD_NORMAL);
757 NS_ENSURE_SUCCESS(rv, rv);
760 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(channel);
761 nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
762 if (hc)
763 mStreamStrategy = new nsHttpStreamStrategy(aDecoder, channel, aURI);
764 else if (fc)
765 mStreamStrategy = new nsFileStreamStrategy(aDecoder, channel, aURI);
766 else
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;
787 return rv;
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
825 // event.
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();