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: ML 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) 2008
20 * the Initial Developer. All Rights Reserved.
23 * Matthew Gregan <kinetik@flim.org>
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 ***** */
41 #include "nsIDOMHTMLMediaElement.h"
42 #include "nsIDocument.h"
44 #include "nsIObserver.h"
45 #include "nsIObserverService.h"
46 #include "nsISeekableStream.h"
47 #include "nsAudioStream.h"
48 #include "nsAutoLock.h"
49 #include "nsHTMLMediaElement.h"
50 #include "nsNetUtil.h"
51 #include "nsThreadUtils.h"
52 #include "nsWaveDecoder.h"
54 // Maximum number of seconds to wait when buffering.
55 #define BUFFERING_TIMEOUT 3
57 // The number of seconds of buffer data before buffering happens
58 // based on current playback rate.
59 #define BUFFERING_SECONDS_LOW_WATER_MARK 1
61 // Magic values that identify RIFF chunks we're interested in.
62 #define RIFF_CHUNK_MAGIC 0x52494646
63 #define WAVE_CHUNK_MAGIC 0x57415645
64 #define FRMT_CHUNK_MAGIC 0x666d7420
65 #define DATA_CHUNK_MAGIC 0x64617461
67 // Size of RIFF chunk header. 4 byte chunk header type and 4 byte size field.
68 #define RIFF_CHUNK_HEADER_SIZE 8
70 // Size of RIFF header. RIFF chunk and 4 byte RIFF type.
71 #define RIFF_INITIAL_SIZE (RIFF_CHUNK_HEADER_SIZE + 4)
73 // Size of required part of format chunk. Actual format chunks may be
74 // extended (for non-PCM encodings), but we skip any extended data.
75 #define WAVE_FORMAT_CHUNK_SIZE 16
77 // Size of format chunk including RIFF chunk header.
78 #define WAVE_FORMAT_SIZE (RIFF_CHUNK_HEADER_SIZE + WAVE_FORMAT_CHUNK_SIZE)
80 // PCM encoding type from format chunk. Linear PCM is the only encoding
81 // supported by nsAudioStream.
82 #define WAVE_FORMAT_ENCODING_PCM 1
85 A single nsWaveStateMachine instance is owned by the decoder, created
86 on-demand at load time. Upon creation, the decoder immediately
87 dispatches the state machine event to the decode thread to begin
88 execution. Once running, metadata loading begins immediately. If this
89 completes successfully, the state machine will move into a paused state
90 awaiting further commands. The state machine provides a small set of
91 threadsafe methods enabling the main thread to play, pause, seek, and
94 An weak (raw) pointer to the decoder's nsMediaStream is used by the state
95 machine to read data, seek, and query stream information. The decoder is
96 responsible for creating and opening the stream, and may also cancel it.
97 Every other stream operation is performed on the playback thread by the
98 state machine. A cancel from the main thread will force any in-flight
99 stream operations to abort.
101 class nsWaveStateMachine
: public nsRunnable
105 STATE_LOADING_METADATA
,
115 nsWaveStateMachine(nsWaveDecoder
* aDecoder
, nsMediaStream
* aStream
,
116 PRUint32 aBufferWaitTime
, float aInitialVolume
);
117 ~nsWaveStateMachine();
119 // Return current audio volume from the audio backend. Result in range
120 // [0.0, 1.0]. Threadsafe.
123 // Set specified volume. aVolume must be in range [0.0, 1.0].
125 void SetVolume(float aVolume
);
128 The following four member functions initiate the appropriate state
129 transition suggested by the function name. Threadsafe.
133 void Seek(float aTime
);
136 // Returns the playback length of the audio data in seconds, calculated
137 // from the length extracted from the metadata. Returns NaN if called
138 // before metadata validation has completed. Threadsafe.
141 // Returns the current playback position in the audio stream in seconds.
143 float GetCurrentTime();
145 // Returns true if the state machine is seeking. Threadsafe.
148 // Called by the decoder to indicate that the media stream has closed.
151 // Main state machine loop. Runs forever, until shutdown state is reached.
155 // Change the current state and wake the playback thread if it is waiting
156 // on mMonitor. Used by public member functions called from both threads,
157 // so must hold mMonitor. Threadsafe.
158 void ChangeState(State aState
);
160 // Create and initialize audio stream using current audio parameters.
161 void OpenAudioStream();
163 // Shut down and dispose audio stream.
164 void CloseAudioStream();
166 // Read RIFF_INITIAL_SIZE from the beginning of the stream and verify that
167 // the stream data is a RIFF bitstream containing WAVE data.
168 PRBool
LoadRIFFChunk();
170 // Scan forward in the stream looking for the WAVE format chunk. If
171 // found, parse and validate required metadata, then use it to set
172 // mSampleRate, mChannels, and mSampleSize.
173 PRBool
LoadFormatChunk();
175 // Scan forward in the stream looking for the start of the PCM data. If
176 // found, record the data length and offset in mWaveLength and
178 PRBool
FindDataOffset();
180 // Returns the number of seconds that aBytes represents based on the
181 // current audio parameters. e.g. 176400 bytes is 1 second at 16-bit
183 float BytesToTime(PRUint32 aBytes
) const
185 NS_ABORT_IF_FALSE(mMetadataValid
, "Requires valid metadata");
186 return float(aBytes
) / mSampleRate
/ mSampleSize
;
189 // Returns the number of bytes that aTime represents based on the current
190 // audio parameters. e.g. 1 second is 176400 bytes at 16-bit stereo
192 PRUint32
TimeToBytes(float aTime
) const
194 NS_ABORT_IF_FALSE(mMetadataValid
, "Requires valid metadata");
195 return PRUint32(aTime
* mSampleRate
* mSampleSize
);
198 // Rounds aBytes down to the nearest complete sample. Assumes beginning
199 // of byte range is already sample aligned by caller.
200 PRUint32
RoundDownToSample(PRUint32 aBytes
) const
202 NS_ABORT_IF_FALSE(mMetadataValid
, "Requires valid metadata");
203 return aBytes
- (aBytes
% mSampleSize
);
206 // Weak (raw) pointer to our decoder instance. The decoder manages the
207 // lifetime of the state machine object, so it is guaranteed that the
208 // state machine will not outlive the decoder. The decoder is not
209 // threadsafe, so this pointer must only be used to create runnable events
210 // targeted at the main thread.
211 nsWaveDecoder
* mDecoder
;
213 // Weak (raw) pointer to a media stream. The decoder manages the lifetime
214 // of the stream, so it is guaranteed that the stream will live as long as
215 // the state machine. The stream is threadsafe, but is only used on the
216 // playback thread except for create, open, and cancel, which are called
217 // from the main thread.
218 nsMediaStream
* mStream
;
220 // Our audio stream. Created on demand when entering playback state. It
221 // is destroyed when seeking begins and will not be reinitialized until
222 // playback resumes, so it is possible for this to be null.
223 nsAutoPtr
<nsAudioStream
> mAudioStream
;
225 // Maximum time in milliseconds to spend waiting for data during buffering.
226 PRUint32 mBufferingWait
;
228 // Maximum number of bytes to wait for during buffering.
229 PRUint32 mBufferingBytes
;
231 // Machine time that buffering began, used with mBufferingWait to time out
233 PRIntervalTime mBufferingStart
;
235 // Maximum number of bytes mAudioStream buffers internally. Used to
236 // calculate next wakeup time after refilling audio buffers.
237 PRUint32 mAudioBufferSize
;
240 Metadata extracted from the WAVE header. Used to initialize the audio
241 stream, and for byte<->time domain conversions.
244 // Number of samples per second. Limited to range [100, 96000] in LoadFormatChunk.
245 PRUint32 mSampleRate
;
247 // Number of channels. Limited to range [1, 2] in LoadFormatChunk.
250 // Size of a single sample segment, which includes a sample for each
251 // channel (interleaved). Limited to 2 or 4 due to requirement for 16-bit
252 // samples and the limit on number of channels.
253 PRUint32 mSampleSize
;
255 // Size of PCM data stored in the WAVE as reported by the data chunk in
257 PRUint32 mWaveLength
;
259 // Start offset of the PCM data in the media stream. Extends mWaveLength
261 PRUint32 mWavePCMOffset
;
264 All member variables following this comment are accessed by both
265 threads and must be synchronized via mMonitor.
269 // The state to enter when the state machine loop iterates next.
272 // A queued state transition. This is used to record the next state
273 // transition when play or pause is requested during seeking or metadata
274 // loading to ensure a completed metadata load or seek returns to the most
275 // recently requested state on completion.
278 // Volume that the audio backend will be initialized with.
279 float mInitialVolume
;
281 // Time position (in seconds) to offset current time from audio stream.
282 // Set by calling Seek(float) when seeking, and when the stream is closed
286 // Set when StreamEnded has fired to indicate that we should not expect
287 // any more data from mStream than what is already buffered (i.e. what
288 // Available() reports).
289 PRPackedBool mExpectMoreData
;
291 // True once metadata has been parsed and validated. Users of mSampleRate,
292 // mChannels, mSampleSize, mWaveLength, mWavePCMOffset must check this
293 // flag before assuming the values are valid.
294 PRPackedBool mMetadataValid
;
297 nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder
* aDecoder
, nsMediaStream
* aStream
,
298 PRUint32 aBufferWaitTime
, float aInitialVolume
)
299 : mDecoder(aDecoder
),
301 mBufferingWait(aBufferWaitTime
),
311 mState(STATE_LOADING_METADATA
),
312 mNextState(STATE_PAUSED
),
313 mInitialVolume(aInitialVolume
),
315 mExpectMoreData(PR_TRUE
),
316 mMetadataValid(PR_FALSE
)
318 mMonitor
= nsAutoMonitor::NewMonitor("nsWaveStateMachine");
321 nsWaveStateMachine::~nsWaveStateMachine()
323 nsAutoMonitor::DestroyMonitor(mMonitor
);
327 nsWaveStateMachine::Shutdown()
329 ChangeState(STATE_SHUTDOWN
);
333 nsWaveStateMachine::Play()
335 nsAutoMonitor
monitor(mMonitor
);
336 if (mState
== STATE_LOADING_METADATA
|| mState
== STATE_SEEKING
) {
337 mNextState
= STATE_PLAYING
;
339 ChangeState(STATE_PLAYING
);
344 nsWaveStateMachine::GetVolume()
346 float volume
= mInitialVolume
;
348 volume
= mAudioStream
->GetVolume();
354 nsWaveStateMachine::SetVolume(float aVolume
)
356 nsAutoMonitor
monitor(mMonitor
);
357 mInitialVolume
= aVolume
;
359 mAudioStream
->SetVolume(aVolume
);
364 nsWaveStateMachine::Pause()
366 nsAutoMonitor
monitor(mMonitor
);
367 if (mState
== STATE_LOADING_METADATA
|| mState
== STATE_SEEKING
) {
368 mNextState
= STATE_PAUSED
;
370 ChangeState(STATE_PAUSED
);
375 nsWaveStateMachine::Seek(float aTime
)
377 nsAutoMonitor
monitor(mMonitor
);
380 ChangeState(STATE_SEEKING
);
384 nsWaveStateMachine::GetDuration()
386 nsAutoMonitor
monitor(mMonitor
);
387 if (mMetadataValid
) {
388 return BytesToTime(mWaveLength
);
390 return std::numeric_limits
<float>::quiet_NaN();
394 nsWaveStateMachine::GetCurrentTime()
396 nsAutoMonitor
monitor(mMonitor
);
399 time
= mAudioStream
->GetTime();
401 return float(time
+ mTimeOffset
);
405 nsWaveStateMachine::IsSeeking()
407 nsAutoMonitor
monitor(mMonitor
);
408 return mState
== STATE_SEEKING
;
412 nsWaveStateMachine::StreamEnded()
414 nsAutoMonitor
monitor(mMonitor
);
415 mExpectMoreData
= PR_FALSE
;
419 nsWaveStateMachine::Run()
421 // Monitor is held by this thread almost permanently, but must be manually
422 // dropped during long operations to prevent the main thread from blocking
423 // when calling methods on the state machine object.
424 nsAutoMonitor
monitor(mMonitor
);
428 case STATE_LOADING_METADATA
:
431 PRBool loaded
= LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
434 if (mState
== STATE_LOADING_METADATA
) {
435 nsCOMPtr
<nsIRunnable
> event
;
439 mMetadataValid
= PR_TRUE
;
440 event
= NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, MetadataLoaded
);
441 newState
= mNextState
;
443 event
= NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, MediaErrorDecode
);
444 newState
= STATE_ERROR
;
447 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
448 ChangeState(newState
);
453 case STATE_BUFFERING
:
454 if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart
) < mBufferingWait
) &&
455 mStream
->DownloadRate() >= 0 &&
456 mStream
->Available() < mBufferingBytes
) {
457 LOG(PR_LOG_DEBUG
, ("Buffering data until %d bytes or %d milliseconds (rate %f)\n",
458 mBufferingBytes
- mStream
->Available(),
459 mBufferingWait
- (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart
)),
460 mStream
->DownloadRate()));
461 monitor
.Wait(PR_MillisecondsToInterval(1000));
463 ChangeState(mNextState
);
464 nsCOMPtr
<nsIRunnable
> event
=
465 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, BufferingStopped
);
466 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
475 mAudioStream
->Resume();
478 if (mStream
->DownloadRate() >= 0 &&
479 mStream
->Available() < mStream
->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK
) {
480 nsCOMPtr
<nsIRunnable
> event
=
481 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, BufferingStarted
);
482 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
484 // Buffer until mBufferingWait milliseconds of data is available.
485 mBufferingBytes
= TimeToBytes(float(mBufferingWait
) / 1000.0);
486 mBufferingStart
= PR_IntervalNow();
487 ChangeState(STATE_BUFFERING
);
490 if (!mExpectMoreData
&& mStream
->Available() < mSampleSize
) {
491 // Media stream has ended and there is less data available than a
492 // single sample so end playback.
493 ChangeState(STATE_ENDED
);
495 // Assuming enough data is available from the network, we aim to
496 // completely fill the audio backend's buffers with data. This
497 // allows us plenty of time to wake up and refill the buffers
498 // without an underrun occurring.
499 PRUint32 len
= RoundDownToSample(NS_MIN(mStream
->Available(),
500 PRUint32(mAudioStream
->Available() * sizeof(short))));
502 nsAutoArrayPtr
<char> buf(new char[len
]);
506 if (NS_FAILED(mStream
->Read(buf
.get(), len
, &got
))) {
507 NS_WARNING("Stream read failed");
511 ChangeState(STATE_ENDED
);
514 // If we got less data than requested, go ahead and write what we
515 // got to the audio hardware. It's unlikely that this can happen
516 // since we never attempt to read more data than what is already
518 len
= RoundDownToSample(got
);
520 // Calculate difference between the current media stream position
521 // and the expected end of the PCM data.
522 PRInt64 endDelta
= mWavePCMOffset
+ mWaveLength
- mStream
->Tell();
524 // Read past the end of PCM data. Adjust len to avoid playing
525 // back trailing data.
527 if (RoundDownToSample(len
) != len
) {
528 NS_WARNING("PCM data does not end with complete sample");
529 len
= RoundDownToSample(len
);
531 ChangeState(STATE_ENDED
);
534 mAudioStream
->Write(reinterpret_cast<short*>(buf
.get()), len
/ sizeof(short));
538 // To avoid waking up too frequently to top up these buffers,
539 // calculate the duration of the currently buffered data and sleep
540 // until most of the buffered data has been consumed. We can't
541 // sleep for the entire duration because we might not wake up in
542 // time to refill the buffers, causing an underrun. To avoid this,
543 // wake up when approximately half the buffered data has been
544 // consumed. This could be made smarter, but at least avoids waking
545 // up frequently to perform small buffer refills.
546 float nextWakeup
= BytesToTime(mAudioBufferSize
- mAudioStream
->Available() * sizeof(short)) * 1000.0 / 2.0;
547 monitor
.Wait(PR_MillisecondsToInterval(PRUint32(nextWakeup
)));
556 nsCOMPtr
<nsIRunnable
> startEvent
=
557 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, SeekingStarted
);
558 NS_DispatchToMainThread(startEvent
, NS_DISPATCH_SYNC
);
561 if (mState
== STATE_SHUTDOWN
) {
565 PRInt64 position
= RoundDownToSample(TimeToBytes(mTimeOffset
)) + mWavePCMOffset
;
568 nsresult rv
= mStream
->Seek(nsISeekableStream::NS_SEEK_SET
, position
);
570 NS_WARNING("Seek failed");
574 if (mState
== STATE_SHUTDOWN
) {
579 nsCOMPtr
<nsIRunnable
> stopEvent
=
580 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, SeekingStopped
);
581 NS_DispatchToMainThread(stopEvent
, NS_DISPATCH_SYNC
);
584 if (mState
!= STATE_SHUTDOWN
) {
585 ChangeState(mNextState
);
592 mAudioStream
->Pause();
600 mAudioStream
->Drain();
604 if (mState
!= STATE_SHUTDOWN
) {
605 nsCOMPtr
<nsIRunnable
> event
=
606 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, PlaybackEnded
);
607 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
610 while (mState
!= STATE_SHUTDOWN
) {
617 if (mState
!= STATE_SHUTDOWN
) {
618 NS_WARNING("Invalid state transition");
619 ChangeState(STATE_ERROR
);
625 mTimeOffset
= mAudioStream
->GetTime();
636 nsWaveStateMachine::ChangeState(State aState
)
638 nsAutoMonitor
monitor(mMonitor
);
644 nsWaveStateMachine::OpenAudioStream()
646 mAudioStream
= new nsAudioStream();
648 LOG(PR_LOG_ERROR
, ("Could not create audio stream"));
650 mAudioStream
->Init(mChannels
, mSampleRate
);
651 mAudioStream
->SetVolume(mInitialVolume
);
652 mAudioBufferSize
= mAudioStream
->Available() * sizeof(short);
657 nsWaveStateMachine::CloseAudioStream()
660 mAudioStream
->Shutdown();
661 mAudioStream
= nsnull
;
666 ReadUint32BE(const char** aBuffer
)
669 PRUint8((*aBuffer
)[0]) << 24 |
670 PRUint8((*aBuffer
)[1]) << 16 |
671 PRUint8((*aBuffer
)[2]) << 8 |
672 PRUint8((*aBuffer
)[3]);
673 *aBuffer
+= sizeof(PRUint32
);
678 ReadUint32LE(const char** aBuffer
)
681 PRUint8((*aBuffer
)[3]) << 24 |
682 PRUint8((*aBuffer
)[2]) << 16 |
683 PRUint8((*aBuffer
)[1]) << 8 |
684 PRUint8((*aBuffer
)[0]);
685 *aBuffer
+= sizeof(PRUint32
);
690 ReadUint16LE(const char** aBuffer
)
693 PRUint8((*aBuffer
)[1]) << 8 |
694 PRUint8((*aBuffer
)[0]) << 0;
695 *aBuffer
+= sizeof(PRUint16
);
700 ReadAll(nsMediaStream
* aStream
, char* aBuf
, PRUint32 aSize
)
705 if (NS_FAILED(aStream
->Read(aBuf
+ got
, aSize
- got
, &read
))) {
706 NS_WARNING("Stream read failed");
710 } while (got
!= aSize
);
715 nsWaveStateMachine::LoadRIFFChunk()
717 char riffHeader
[RIFF_INITIAL_SIZE
];
718 const char* p
= riffHeader
;
720 NS_ABORT_IF_FALSE(mStream
->Tell() == 0,
721 "LoadRIFFChunk called when stream in invalid state");
723 if (!ReadAll(mStream
, riffHeader
, sizeof(riffHeader
))) {
727 if (ReadUint32BE(&p
) != RIFF_CHUNK_MAGIC
) {
728 NS_WARNING("Stream data not in RIFF format");
732 // Skip over RIFF size field.
735 if (ReadUint32BE(&p
) != WAVE_CHUNK_MAGIC
) {
736 NS_WARNING("Expected WAVE chunk");
744 nsWaveStateMachine::LoadFormatChunk()
746 PRUint32 rate
, channels
, sampleSize
;
747 char waveFormat
[WAVE_FORMAT_SIZE
];
748 const char* p
= waveFormat
;
750 // RIFF chunks are always word (two byte) aligned.
751 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
752 "LoadFormatChunk called with unaligned stream");
754 if (!ReadAll(mStream
, waveFormat
, sizeof(waveFormat
))) {
758 if (ReadUint32BE(&p
) != FRMT_CHUNK_MAGIC
) {
759 NS_WARNING("Expected format chunk");
763 PRUint32 fmtsize
= ReadUint32LE(&p
);
765 if (ReadUint16LE(&p
) != WAVE_FORMAT_ENCODING_PCM
) {
766 NS_WARNING("WAVE is not uncompressed PCM, compressed encodings are not supported");
770 channels
= ReadUint16LE(&p
);
771 rate
= ReadUint32LE(&p
);
773 // Skip over average bytes per second field.
776 sampleSize
= ReadUint16LE(&p
);
778 // We only support 16-bit audio for now, since that's all that the in-tree
779 // libsydney supports.
780 if (ReadUint16LE(&p
) != 16) {
781 NS_WARNING("WAVE is not 16-bit, other bit rates are not supported");
785 // PCM encoded WAVEs are not expected to have an extended "format" chunk,
786 // but I have found WAVEs that have a extended "format" chunk with an
787 // extension size of 0 bytes. Be polite and handle this rather than
788 // considering the file invalid. This code skips any extension of the
790 if (fmtsize
> WAVE_FORMAT_CHUNK_SIZE
) {
792 const char* p
= extLength
;
794 if (!ReadAll(mStream
, extLength
, sizeof(extLength
))) {
798 PRUint16 extra
= ReadUint16LE(&p
);
799 if (fmtsize
- WAVE_FORMAT_CHUNK_SIZE
+ 2 != extra
) {
800 NS_WARNING("Invalid extended format chunk size");
806 nsAutoArrayPtr
<char> chunkExtension(new char[extra
]);
807 if (!ReadAll(mStream
, chunkExtension
.get(), extra
)) {
813 // RIFF chunks are always word (two byte) aligned.
814 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
815 "LoadFormatChunk left stream unaligned");
817 // Make sure metadata is fairly sane. The rate check is fairly arbitrary,
818 // but the channels/sampleSize check is intentionally limited to 16-bit
819 // mono or stereo because that's what the audio backend currently
821 if (rate
< 100 || rate
> 96000 ||
822 channels
< 1 || channels
> 2 ||
823 sampleSize
< 2 || sampleSize
> 4) {
824 NS_WARNING("Invalid WAVE metadata");
828 nsAutoMonitor
monitor(mMonitor
);
830 mChannels
= channels
;
831 mSampleSize
= sampleSize
;
836 nsWaveStateMachine::FindDataOffset()
841 // RIFF chunks are always word (two byte) aligned.
842 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
843 "FindDataOffset called with unaligned stream");
845 // The "data" chunk may not directly follow the "format" chunk, so skip
846 // over any intermediate chunks.
849 const char* p
= chunkHeader
;
851 if (!ReadAll(mStream
, chunkHeader
, sizeof(chunkHeader
))) {
855 PRUint32 magic
= ReadUint32BE(&p
);
857 if (magic
== DATA_CHUNK_MAGIC
) {
858 length
= ReadUint32LE(&p
);
862 if (magic
== FRMT_CHUNK_MAGIC
) {
863 LOG(PR_LOG_ERROR
, ("Invalid WAVE: expected \"data\" chunk but found \"format\" chunk"));
867 PRUint32 size
= ReadUint32LE(&p
);
870 nsAutoArrayPtr
<char> chunk(new char[size
]);
871 if (!ReadAll(mStream
, chunk
.get(), size
)) {
876 offset
= mStream
->Tell();
878 NS_WARNING("PCM data offset not found");
882 if (offset
< 0 || offset
> PR_UINT32_MAX
) {
883 NS_WARNING("offset out of range");
887 nsAutoMonitor
monitor(mMonitor
);
888 mWaveLength
= length
;
889 mWavePCMOffset
= PRUint32(offset
);
893 NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder
, nsIObserver
)
895 nsWaveDecoder::nsWaveDecoder()
896 : mBytesDownloaded(0),
899 mEndedCurrentTime(0.0),
900 mEndedDuration(std::numeric_limits
<float>::quiet_NaN()),
901 mNotifyOnShutdown(PR_FALSE
),
904 MOZ_COUNT_CTOR(nsWaveDecoder
);
907 nsWaveDecoder::~nsWaveDecoder()
909 MOZ_COUNT_DTOR(nsWaveDecoder
);
913 nsWaveDecoder::GetCurrentURI(nsIURI
** aURI
)
915 NS_IF_ADDREF(*aURI
= mURI
);
919 nsWaveDecoder::GetCurrentPrincipal()
924 return mStream
->GetCurrentPrincipal();
928 nsWaveDecoder::GetCurrentTime()
930 if (mPlaybackStateMachine
) {
931 return mPlaybackStateMachine
->GetCurrentTime();
933 return mEndedCurrentTime
;
937 nsWaveDecoder::Seek(float aTime
)
939 if (mPlaybackStateMachine
&& aTime
>= 0.0) {
940 mPlaybackStateMachine
->Seek(aTime
);
943 return NS_ERROR_FAILURE
;
947 nsWaveDecoder::PlaybackRateChanged()
949 return NS_ERROR_NOT_IMPLEMENTED
;
953 nsWaveDecoder::GetDuration()
955 if (mPlaybackStateMachine
) {
956 return mPlaybackStateMachine
->GetDuration();
958 return mEndedDuration
;
962 nsWaveDecoder::Pause()
964 if (mPlaybackStateMachine
) {
965 mPlaybackStateMachine
->Pause();
970 nsWaveDecoder::GetVolume()
972 if (!mPlaybackStateMachine
) {
973 return mInitialVolume
;
975 return mPlaybackStateMachine
->GetVolume();
979 nsWaveDecoder::SetVolume(float aVolume
)
981 mInitialVolume
= aVolume
;
982 if (mPlaybackStateMachine
) {
983 mPlaybackStateMachine
->SetVolume(aVolume
);
988 nsWaveDecoder::Play()
990 if (!mPlaybackStateMachine
) {
991 Load(mURI
, nsnull
, nsnull
);
994 if (mPlaybackStateMachine
) {
995 mPlaybackStateMachine
->Play();
999 return NS_ERROR_FAILURE
;
1003 nsWaveDecoder::Stop()
1007 if (mPlaybackStateMachine
) {
1008 mPlaybackStateMachine
->Shutdown();
1015 if (mPlaybackThread
) {
1016 mEndedCurrentTime
= mPlaybackStateMachine
->GetCurrentTime();
1017 mEndedDuration
= mPlaybackStateMachine
->GetDuration();
1018 mPlaybackThread
->Shutdown();
1021 mPlaybackThread
= nsnull
;
1022 mPlaybackStateMachine
= nsnull
;
1025 UnregisterShutdownObserver();
1029 nsWaveDecoder::Load(nsIURI
* aURI
, nsIChannel
* aChannel
, nsIStreamListener
** aStreamListener
)
1031 if (aStreamListener
) {
1032 *aStreamListener
= nsnull
;
1036 NS_ASSERTION(!aStreamListener
, "No listener should be requested here");
1039 NS_ASSERTION(aChannel
, "Either a URI or a channel is required");
1040 NS_ASSERTION(aStreamListener
, "A listener should be requested here");
1042 nsresult rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(mURI
));
1043 NS_ENSURE_SUCCESS(rv
, rv
);
1047 RegisterShutdownObserver();
1049 mStream
= new nsMediaStream();
1050 NS_ENSURE_TRUE(mStream
, NS_ERROR_OUT_OF_MEMORY
);
1052 nsresult rv
= mStream
->Open(this, aURI
, aChannel
, aStreamListener
);
1053 NS_ENSURE_SUCCESS(rv
, rv
);
1055 rv
= NS_NewThread(getter_AddRefs(mPlaybackThread
));
1056 NS_ENSURE_SUCCESS(rv
, rv
);
1058 mPlaybackStateMachine
= new nsWaveStateMachine(this, mStream
.get(),
1059 BUFFERING_TIMEOUT
* 1000,
1061 rv
= mPlaybackThread
->Dispatch(mPlaybackStateMachine
, NS_DISPATCH_NORMAL
);
1062 NS_ENSURE_SUCCESS(rv
, rv
);
1068 nsWaveDecoder::MetadataLoaded()
1071 mElement
->MetadataLoaded();
1072 mElement
->FirstFrameLoaded();
1077 nsWaveDecoder::PlaybackEnded()
1081 mElement
->PlaybackEnded();
1086 nsWaveDecoder::ResourceLoaded()
1089 mElement
->ResourceLoaded();
1091 if (mPlaybackStateMachine
) {
1092 mPlaybackStateMachine
->StreamEnded();
1098 nsWaveDecoder::NetworkError()
1101 mElement
->NetworkError();
1103 if (mPlaybackStateMachine
) {
1104 mPlaybackStateMachine
->StreamEnded();
1110 nsWaveDecoder::IsSeeking() const
1112 if (mPlaybackStateMachine
) {
1113 return mPlaybackStateMachine
->IsSeeking();
1119 nsWaveDecoder::GetBytesLoaded()
1121 return mBytesDownloaded
;
1125 nsWaveDecoder::GetTotalBytes()
1127 return mContentLength
;
1131 nsWaveDecoder::SetTotalBytes(PRInt64 aBytes
)
1133 mContentLength
= aBytes
;
1137 nsWaveDecoder::UpdateBytesDownloaded(PRUint64 aBytes
)
1139 mBytesDownloaded
= aBytes
;
1142 // An event that gets posted to the main thread, when the media element is
1143 // being destroyed, to destroy the decoder. Since the decoder shutdown can
1144 // block and post events this cannot be done inside destructor calls. So
1145 // this event is posted asynchronously to the main thread to perform the
1146 // shutdown. It keeps a strong reference to the decoder to ensure it does
1147 // not get deleted when the element is deleted.
1148 class nsWaveDecoderShutdown
: public nsRunnable
1151 nsWaveDecoderShutdown(nsWaveDecoder
* aDecoder
)
1152 : mDecoder(aDecoder
)
1163 nsRefPtr
<nsWaveDecoder
> mDecoder
;
1167 nsWaveDecoder::Shutdown()
1169 nsMediaDecoder::Shutdown();
1171 nsCOMPtr
<nsIRunnable
> event
= new nsWaveDecoderShutdown(this);
1172 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
1176 nsWaveDecoder::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aData
)
1178 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
1185 nsWaveDecoder::BufferingStarted()
1188 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE
);
1193 nsWaveDecoder::BufferingStopped()
1196 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME
);
1201 nsWaveDecoder::SeekingStarted()
1204 mElement
->SeekStarted();
1209 nsWaveDecoder::SeekingStopped()
1212 mElement
->SeekCompleted();
1217 nsWaveDecoder::RegisterShutdownObserver()
1219 if (!mNotifyOnShutdown
) {
1220 nsCOMPtr
<nsIObserverService
> observerService
=
1221 do_GetService("@mozilla.org/observer-service;1");
1222 if (observerService
) {
1224 NS_SUCCEEDED(observerService
->AddObserver(this,
1225 NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
1228 NS_WARNING("Could not get an observer service. Audio playback may not shutdown cleanly.");
1234 nsWaveDecoder::UnregisterShutdownObserver()
1236 if (mNotifyOnShutdown
) {
1237 nsCOMPtr
<nsIObserverService
> observerService
=
1238 do_GetService("@mozilla.org/observer-service;1");
1239 if (observerService
) {
1240 mNotifyOnShutdown
= PR_FALSE
;
1241 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
1247 nsWaveDecoder::MediaErrorDecode()
1251 mElement
->MediaErrorDecode();
1254 NS_WARNING("MediaErrorDecode fired, but not implemented.");
1259 nsWaveDecoder::SetSeekable(PRBool aSeekable
)
1261 mSeekable
= aSeekable
;
1265 nsWaveDecoder::GetSeekable()