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, mSampleSize, and mSampleFormat.
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).
252 PRUint32 mSampleSize
;
254 // The sample format of the PCM data.
255 nsAudioStream::SampleFormat mSampleFormat
;
257 // Size of PCM data stored in the WAVE as reported by the data chunk in
259 PRUint32 mWaveLength
;
261 // Start offset of the PCM data in the media stream. Extends mWaveLength
263 PRUint32 mWavePCMOffset
;
266 All member variables following this comment are accessed by both
267 threads and must be synchronized via mMonitor.
271 // The state to enter when the state machine loop iterates next.
274 // A queued state transition. This is used to record the next state
275 // transition when play or pause is requested during seeking or metadata
276 // loading to ensure a completed metadata load or seek returns to the most
277 // recently requested state on completion.
280 // Volume that the audio backend will be initialized with.
281 float mInitialVolume
;
283 // Time position (in seconds) to offset current time from audio stream.
284 // Set by calling Seek(float) when seeking, and when the stream is closed
288 // Set when StreamEnded has fired to indicate that we should not expect
289 // any more data from mStream than what is already buffered (i.e. what
290 // Available() reports).
291 PRPackedBool mExpectMoreData
;
293 // True once metadata has been parsed and validated. Users of mSampleRate,
294 // mChannels, mSampleSize, mSampleFormat, mWaveLength, mWavePCMOffset must
295 // check this flag before assuming the values are valid.
296 PRPackedBool mMetadataValid
;
299 nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder
* aDecoder
, nsMediaStream
* aStream
,
300 PRUint32 aBufferWaitTime
, float aInitialVolume
)
301 : mDecoder(aDecoder
),
303 mBufferingWait(aBufferWaitTime
),
310 mSampleFormat(nsAudioStream::FORMAT_S16_LE
),
314 mState(STATE_LOADING_METADATA
),
315 mNextState(STATE_PAUSED
),
316 mInitialVolume(aInitialVolume
),
318 mExpectMoreData(PR_TRUE
),
319 mMetadataValid(PR_FALSE
)
321 mMonitor
= nsAutoMonitor::NewMonitor("nsWaveStateMachine");
324 nsWaveStateMachine::~nsWaveStateMachine()
326 nsAutoMonitor::DestroyMonitor(mMonitor
);
330 nsWaveStateMachine::Shutdown()
332 ChangeState(STATE_SHUTDOWN
);
336 nsWaveStateMachine::Play()
338 nsAutoMonitor
monitor(mMonitor
);
339 if (mState
== STATE_LOADING_METADATA
|| mState
== STATE_SEEKING
) {
340 mNextState
= STATE_PLAYING
;
342 ChangeState(STATE_PLAYING
);
347 nsWaveStateMachine::GetVolume()
349 float volume
= mInitialVolume
;
351 volume
= mAudioStream
->GetVolume();
357 nsWaveStateMachine::SetVolume(float aVolume
)
359 nsAutoMonitor
monitor(mMonitor
);
360 mInitialVolume
= aVolume
;
362 mAudioStream
->SetVolume(aVolume
);
367 nsWaveStateMachine::Pause()
369 nsAutoMonitor
monitor(mMonitor
);
370 if (mState
== STATE_LOADING_METADATA
|| mState
== STATE_SEEKING
) {
371 mNextState
= STATE_PAUSED
;
373 ChangeState(STATE_PAUSED
);
378 nsWaveStateMachine::Seek(float aTime
)
380 nsAutoMonitor
monitor(mMonitor
);
383 ChangeState(STATE_SEEKING
);
387 nsWaveStateMachine::GetDuration()
389 nsAutoMonitor
monitor(mMonitor
);
390 if (mMetadataValid
) {
391 return BytesToTime(mWaveLength
);
393 return std::numeric_limits
<float>::quiet_NaN();
397 nsWaveStateMachine::GetCurrentTime()
399 nsAutoMonitor
monitor(mMonitor
);
402 time
= mAudioStream
->GetTime();
404 return float(time
+ mTimeOffset
);
408 nsWaveStateMachine::IsSeeking()
410 nsAutoMonitor
monitor(mMonitor
);
411 return mState
== STATE_SEEKING
;
415 nsWaveStateMachine::StreamEnded()
417 nsAutoMonitor
monitor(mMonitor
);
418 mExpectMoreData
= PR_FALSE
;
422 nsWaveStateMachine::Run()
424 // Monitor is held by this thread almost permanently, but must be manually
425 // dropped during long operations to prevent the main thread from blocking
426 // when calling methods on the state machine object.
427 nsAutoMonitor
monitor(mMonitor
);
431 case STATE_LOADING_METADATA
:
434 PRBool loaded
= LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
437 if (mState
== STATE_LOADING_METADATA
) {
438 nsCOMPtr
<nsIRunnable
> event
;
442 mMetadataValid
= PR_TRUE
;
443 event
= NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, MetadataLoaded
);
444 newState
= mNextState
;
446 event
= NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, MediaErrorDecode
);
447 newState
= STATE_ERROR
;
450 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
451 ChangeState(newState
);
456 case STATE_BUFFERING
:
457 if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart
) < mBufferingWait
) &&
458 mStream
->DownloadRate() >= 0 &&
459 mStream
->Available() < mBufferingBytes
) {
460 LOG(PR_LOG_DEBUG
, ("Buffering data until %d bytes or %d milliseconds (rate %f)\n",
461 mBufferingBytes
- mStream
->Available(),
462 mBufferingWait
- (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart
)),
463 mStream
->DownloadRate()));
464 monitor
.Wait(PR_MillisecondsToInterval(1000));
466 ChangeState(mNextState
);
467 nsCOMPtr
<nsIRunnable
> event
=
468 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, BufferingStopped
);
469 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
478 mAudioStream
->Resume();
481 if (mStream
->DownloadRate() >= 0 &&
482 mStream
->Available() < mStream
->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK
) {
483 nsCOMPtr
<nsIRunnable
> event
=
484 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, BufferingStarted
);
485 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
487 // Buffer until mBufferingWait milliseconds of data is available.
488 mBufferingBytes
= TimeToBytes(float(mBufferingWait
) / 1000.0);
489 mBufferingStart
= PR_IntervalNow();
490 ChangeState(STATE_BUFFERING
);
493 if (!mExpectMoreData
&& mStream
->Available() < mSampleSize
) {
494 // Media stream has ended and there is less data available than a
495 // single sample so end playback.
496 ChangeState(STATE_ENDED
);
498 // Assuming enough data is available from the network, we aim to
499 // completely fill the audio backend's buffers with data. This
500 // allows us plenty of time to wake up and refill the buffers
501 // without an underrun occurring.
502 PRUint32 len
= RoundDownToSample(NS_MIN(mStream
->Available(),
503 PRUint32(mAudioStream
->Available() * sizeof(short))));
505 nsAutoArrayPtr
<char> buf(new char[len
]);
509 if (NS_FAILED(mStream
->Read(buf
.get(), len
, &got
))) {
510 NS_WARNING("Stream read failed");
514 ChangeState(STATE_ENDED
);
517 // If we got less data than requested, go ahead and write what we
518 // got to the audio hardware. It's unlikely that this can happen
519 // since we never attempt to read more data than what is already
521 len
= RoundDownToSample(got
);
523 // Calculate difference between the current media stream position
524 // and the expected end of the PCM data.
525 PRInt64 endDelta
= mWavePCMOffset
+ mWaveLength
- mStream
->Tell();
527 // Read past the end of PCM data. Adjust len to avoid playing
528 // back trailing data.
530 if (RoundDownToSample(len
) != len
) {
531 NS_WARNING("PCM data does not end with complete sample");
532 len
= RoundDownToSample(len
);
534 ChangeState(STATE_ENDED
);
537 PRUint32 lengthInSamples
= len
;
538 if (mSampleFormat
== nsAudioStream::FORMAT_S16_LE
) {
539 lengthInSamples
/= sizeof(short);
541 mAudioStream
->Write(buf
.get(), lengthInSamples
);
545 // To avoid waking up too frequently to top up these buffers,
546 // calculate the duration of the currently buffered data and sleep
547 // until most of the buffered data has been consumed. We can't
548 // sleep for the entire duration because we might not wake up in
549 // time to refill the buffers, causing an underrun. To avoid this,
550 // wake up when approximately half the buffered data has been
551 // consumed. This could be made smarter, but at least avoids waking
552 // up frequently to perform small buffer refills.
553 float nextWakeup
= BytesToTime(mAudioBufferSize
- mAudioStream
->Available() * sizeof(short)) * 1000.0 / 2.0;
554 monitor
.Wait(PR_MillisecondsToInterval(PRUint32(nextWakeup
)));
563 nsCOMPtr
<nsIRunnable
> startEvent
=
564 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, SeekingStarted
);
565 NS_DispatchToMainThread(startEvent
, NS_DISPATCH_SYNC
);
568 if (mState
== STATE_SHUTDOWN
) {
572 PRInt64 position
= RoundDownToSample(TimeToBytes(mTimeOffset
)) + mWavePCMOffset
;
575 nsresult rv
= mStream
->Seek(nsISeekableStream::NS_SEEK_SET
, position
);
577 NS_WARNING("Seek failed");
581 if (mState
== STATE_SHUTDOWN
) {
586 nsCOMPtr
<nsIRunnable
> stopEvent
=
587 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, SeekingStopped
);
588 NS_DispatchToMainThread(stopEvent
, NS_DISPATCH_SYNC
);
591 if (mState
!= STATE_SHUTDOWN
) {
592 ChangeState(mNextState
);
599 mAudioStream
->Pause();
607 mAudioStream
->Drain();
609 mTimeOffset
= mAudioStream
->GetTime();
612 // Dispose the audio stream early (before SHUTDOWN) so that
613 // GetCurrentTime no longer attempts to query the audio backend for
617 if (mState
!= STATE_SHUTDOWN
) {
618 nsCOMPtr
<nsIRunnable
> event
=
619 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder
, mDecoder
, PlaybackEnded
);
620 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
623 while (mState
!= STATE_SHUTDOWN
) {
630 if (mState
!= STATE_SHUTDOWN
) {
631 NS_WARNING("Invalid state transition");
632 ChangeState(STATE_ERROR
);
638 mTimeOffset
= mAudioStream
->GetTime();
649 nsWaveStateMachine::ChangeState(State aState
)
651 nsAutoMonitor
monitor(mMonitor
);
657 nsWaveStateMachine::OpenAudioStream()
659 mAudioStream
= new nsAudioStream();
661 LOG(PR_LOG_ERROR
, ("Could not create audio stream"));
663 NS_ABORT_IF_FALSE(mMetadataValid
,
664 "Attempting to initialize audio stream with invalid metadata");
665 mAudioStream
->Init(mChannels
, mSampleRate
, mSampleFormat
);
666 mAudioStream
->SetVolume(mInitialVolume
);
667 mAudioBufferSize
= mAudioStream
->Available() * sizeof(short);
672 nsWaveStateMachine::CloseAudioStream()
675 mAudioStream
->Shutdown();
676 mAudioStream
= nsnull
;
681 ReadUint32BE(const char** aBuffer
)
684 PRUint8((*aBuffer
)[0]) << 24 |
685 PRUint8((*aBuffer
)[1]) << 16 |
686 PRUint8((*aBuffer
)[2]) << 8 |
687 PRUint8((*aBuffer
)[3]);
688 *aBuffer
+= sizeof(PRUint32
);
693 ReadUint32LE(const char** aBuffer
)
696 PRUint8((*aBuffer
)[3]) << 24 |
697 PRUint8((*aBuffer
)[2]) << 16 |
698 PRUint8((*aBuffer
)[1]) << 8 |
699 PRUint8((*aBuffer
)[0]);
700 *aBuffer
+= sizeof(PRUint32
);
705 ReadUint16LE(const char** aBuffer
)
708 PRUint8((*aBuffer
)[1]) << 8 |
709 PRUint8((*aBuffer
)[0]) << 0;
710 *aBuffer
+= sizeof(PRUint16
);
715 ReadAll(nsMediaStream
* aStream
, char* aBuf
, PRUint32 aSize
)
720 if (NS_FAILED(aStream
->Read(aBuf
+ got
, aSize
- got
, &read
))) {
721 NS_WARNING("Stream read failed");
725 } while (got
!= aSize
);
730 nsWaveStateMachine::LoadRIFFChunk()
732 char riffHeader
[RIFF_INITIAL_SIZE
];
733 const char* p
= riffHeader
;
735 NS_ABORT_IF_FALSE(mStream
->Tell() == 0,
736 "LoadRIFFChunk called when stream in invalid state");
738 if (!ReadAll(mStream
, riffHeader
, sizeof(riffHeader
))) {
742 if (ReadUint32BE(&p
) != RIFF_CHUNK_MAGIC
) {
743 NS_WARNING("Stream data not in RIFF format");
747 // Skip over RIFF size field.
750 if (ReadUint32BE(&p
) != WAVE_CHUNK_MAGIC
) {
751 NS_WARNING("Expected WAVE chunk");
759 nsWaveStateMachine::LoadFormatChunk()
761 PRUint32 rate
, channels
, sampleSize
, sampleFormat
;
762 char waveFormat
[WAVE_FORMAT_SIZE
];
763 const char* p
= waveFormat
;
765 // RIFF chunks are always word (two byte) aligned.
766 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
767 "LoadFormatChunk called with unaligned stream");
769 if (!ReadAll(mStream
, waveFormat
, sizeof(waveFormat
))) {
773 if (ReadUint32BE(&p
) != FRMT_CHUNK_MAGIC
) {
774 NS_WARNING("Expected format chunk");
778 PRUint32 fmtsize
= ReadUint32LE(&p
);
780 if (ReadUint16LE(&p
) != WAVE_FORMAT_ENCODING_PCM
) {
781 NS_WARNING("WAVE is not uncompressed PCM, compressed encodings are not supported");
785 channels
= ReadUint16LE(&p
);
786 rate
= ReadUint32LE(&p
);
788 // Skip over average bytes per second field.
791 sampleSize
= ReadUint16LE(&p
);
793 sampleFormat
= ReadUint16LE(&p
);
795 // PCM encoded WAVEs are not expected to have an extended "format" chunk,
796 // but I have found WAVEs that have a extended "format" chunk with an
797 // extension size of 0 bytes. Be polite and handle this rather than
798 // considering the file invalid. This code skips any extension of the
800 if (fmtsize
> WAVE_FORMAT_CHUNK_SIZE
) {
802 const char* p
= extLength
;
804 if (!ReadAll(mStream
, extLength
, sizeof(extLength
))) {
808 PRUint16 extra
= ReadUint16LE(&p
);
809 if (fmtsize
- (WAVE_FORMAT_CHUNK_SIZE
+ 2) != extra
) {
810 NS_WARNING("Invalid extended format chunk size");
816 nsAutoArrayPtr
<char> chunkExtension(new char[extra
]);
817 if (!ReadAll(mStream
, chunkExtension
.get(), extra
)) {
823 // RIFF chunks are always word (two byte) aligned.
824 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
825 "LoadFormatChunk left stream unaligned");
827 // Make sure metadata is fairly sane. The rate check is fairly arbitrary,
828 // but the channels check is intentionally limited to mono or stereo
829 // because that's what the audio backend currently supports.
830 if (rate
< 100 || rate
> 96000 ||
831 channels
< 1 || channels
> 2 ||
832 (sampleSize
!= 1 && sampleSize
!= 2 && sampleSize
!= 4) ||
833 (sampleFormat
!= 8 && sampleFormat
!= 16)) {
834 NS_WARNING("Invalid WAVE metadata");
838 nsAutoMonitor
monitor(mMonitor
);
840 mChannels
= channels
;
841 mSampleSize
= sampleSize
;
842 if (sampleFormat
== 8) {
843 mSampleFormat
= nsAudioStream::FORMAT_U8
;
845 mSampleFormat
= nsAudioStream::FORMAT_S16_LE
;
851 nsWaveStateMachine::FindDataOffset()
856 // RIFF chunks are always word (two byte) aligned.
857 NS_ABORT_IF_FALSE(mStream
->Tell() % 2 == 0,
858 "FindDataOffset called with unaligned stream");
860 // The "data" chunk may not directly follow the "format" chunk, so skip
861 // over any intermediate chunks.
864 const char* p
= chunkHeader
;
866 if (!ReadAll(mStream
, chunkHeader
, sizeof(chunkHeader
))) {
870 PRUint32 magic
= ReadUint32BE(&p
);
872 if (magic
== DATA_CHUNK_MAGIC
) {
873 length
= ReadUint32LE(&p
);
877 if (magic
== FRMT_CHUNK_MAGIC
) {
878 LOG(PR_LOG_ERROR
, ("Invalid WAVE: expected \"data\" chunk but found \"format\" chunk"));
882 PRUint32 size
= ReadUint32LE(&p
);
885 nsAutoArrayPtr
<char> chunk(new char[size
]);
886 if (!ReadAll(mStream
, chunk
.get(), size
)) {
891 offset
= mStream
->Tell();
893 NS_WARNING("PCM data offset not found");
897 if (offset
< 0 || offset
> PR_UINT32_MAX
) {
898 NS_WARNING("offset out of range");
902 nsAutoMonitor
monitor(mMonitor
);
903 mWaveLength
= length
;
904 mWavePCMOffset
= PRUint32(offset
);
908 NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder
, nsIObserver
)
910 nsWaveDecoder::nsWaveDecoder()
911 : mBytesDownloaded(0),
914 mEndedCurrentTime(0.0),
915 mEndedDuration(std::numeric_limits
<float>::quiet_NaN()),
916 mNotifyOnShutdown(PR_FALSE
),
919 MOZ_COUNT_CTOR(nsWaveDecoder
);
922 nsWaveDecoder::~nsWaveDecoder()
924 MOZ_COUNT_DTOR(nsWaveDecoder
);
928 nsWaveDecoder::GetCurrentURI(nsIURI
** aURI
)
930 NS_IF_ADDREF(*aURI
= mURI
);
934 nsWaveDecoder::GetCurrentPrincipal()
939 return mStream
->GetCurrentPrincipal();
943 nsWaveDecoder::GetCurrentTime()
945 if (mPlaybackStateMachine
) {
946 return mPlaybackStateMachine
->GetCurrentTime();
948 return mEndedCurrentTime
;
952 nsWaveDecoder::Seek(float aTime
)
954 if (mPlaybackStateMachine
&& aTime
>= 0.0) {
955 mPlaybackStateMachine
->Seek(aTime
);
958 return NS_ERROR_FAILURE
;
962 nsWaveDecoder::PlaybackRateChanged()
964 return NS_ERROR_NOT_IMPLEMENTED
;
968 nsWaveDecoder::GetDuration()
970 if (mPlaybackStateMachine
) {
971 return mPlaybackStateMachine
->GetDuration();
973 return mEndedDuration
;
977 nsWaveDecoder::Pause()
979 if (mPlaybackStateMachine
) {
980 mPlaybackStateMachine
->Pause();
985 nsWaveDecoder::GetVolume()
987 if (!mPlaybackStateMachine
) {
988 return mInitialVolume
;
990 return mPlaybackStateMachine
->GetVolume();
994 nsWaveDecoder::SetVolume(float aVolume
)
996 mInitialVolume
= aVolume
;
997 if (mPlaybackStateMachine
) {
998 mPlaybackStateMachine
->SetVolume(aVolume
);
1003 nsWaveDecoder::Play()
1005 if (!mPlaybackStateMachine
) {
1006 Load(mURI
, nsnull
, nsnull
);
1009 if (mPlaybackStateMachine
) {
1010 mPlaybackStateMachine
->Play();
1014 return NS_ERROR_FAILURE
;
1018 nsWaveDecoder::Stop()
1022 if (mPlaybackStateMachine
) {
1023 mPlaybackStateMachine
->Shutdown();
1030 if (mPlaybackThread
) {
1031 mEndedCurrentTime
= mPlaybackStateMachine
->GetCurrentTime();
1032 mEndedDuration
= mPlaybackStateMachine
->GetDuration();
1033 mPlaybackThread
->Shutdown();
1036 mPlaybackThread
= nsnull
;
1037 mPlaybackStateMachine
= nsnull
;
1040 UnregisterShutdownObserver();
1044 nsWaveDecoder::Load(nsIURI
* aURI
, nsIChannel
* aChannel
, nsIStreamListener
** aStreamListener
)
1046 if (aStreamListener
) {
1047 *aStreamListener
= nsnull
;
1051 NS_ASSERTION(!aStreamListener
, "No listener should be requested here");
1054 NS_ASSERTION(aChannel
, "Either a URI or a channel is required");
1055 NS_ASSERTION(aStreamListener
, "A listener should be requested here");
1057 nsresult rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(mURI
));
1058 NS_ENSURE_SUCCESS(rv
, rv
);
1062 RegisterShutdownObserver();
1064 mStream
= new nsMediaStream();
1065 NS_ENSURE_TRUE(mStream
, NS_ERROR_OUT_OF_MEMORY
);
1067 nsresult rv
= mStream
->Open(this, aURI
, aChannel
, aStreamListener
);
1068 NS_ENSURE_SUCCESS(rv
, rv
);
1070 rv
= NS_NewThread(getter_AddRefs(mPlaybackThread
));
1071 NS_ENSURE_SUCCESS(rv
, rv
);
1073 mPlaybackStateMachine
= new nsWaveStateMachine(this, mStream
.get(),
1074 BUFFERING_TIMEOUT
* 1000,
1076 rv
= mPlaybackThread
->Dispatch(mPlaybackStateMachine
, NS_DISPATCH_NORMAL
);
1077 NS_ENSURE_SUCCESS(rv
, rv
);
1083 nsWaveDecoder::MetadataLoaded()
1086 mElement
->MetadataLoaded();
1087 mElement
->FirstFrameLoaded();
1092 nsWaveDecoder::PlaybackEnded()
1096 mElement
->PlaybackEnded();
1101 nsWaveDecoder::ResourceLoaded()
1104 mElement
->ResourceLoaded();
1106 if (mPlaybackStateMachine
) {
1107 mPlaybackStateMachine
->StreamEnded();
1113 nsWaveDecoder::NetworkError()
1116 mElement
->NetworkError();
1118 if (mPlaybackStateMachine
) {
1119 mPlaybackStateMachine
->StreamEnded();
1125 nsWaveDecoder::IsSeeking() const
1127 if (mPlaybackStateMachine
) {
1128 return mPlaybackStateMachine
->IsSeeking();
1134 nsWaveDecoder::GetBytesLoaded()
1136 return mBytesDownloaded
;
1140 nsWaveDecoder::GetTotalBytes()
1142 return mContentLength
;
1146 nsWaveDecoder::SetTotalBytes(PRInt64 aBytes
)
1148 mContentLength
= aBytes
;
1152 nsWaveDecoder::UpdateBytesDownloaded(PRUint64 aBytes
)
1154 mBytesDownloaded
= aBytes
;
1157 // An event that gets posted to the main thread, when the media element is
1158 // being destroyed, to destroy the decoder. Since the decoder shutdown can
1159 // block and post events this cannot be done inside destructor calls. So
1160 // this event is posted asynchronously to the main thread to perform the
1161 // shutdown. It keeps a strong reference to the decoder to ensure it does
1162 // not get deleted when the element is deleted.
1163 class nsWaveDecoderShutdown
: public nsRunnable
1166 nsWaveDecoderShutdown(nsWaveDecoder
* aDecoder
)
1167 : mDecoder(aDecoder
)
1178 nsRefPtr
<nsWaveDecoder
> mDecoder
;
1182 nsWaveDecoder::Shutdown()
1184 nsMediaDecoder::Shutdown();
1186 nsCOMPtr
<nsIRunnable
> event
= new nsWaveDecoderShutdown(this);
1187 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
1191 nsWaveDecoder::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aData
)
1193 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
1200 nsWaveDecoder::BufferingStarted()
1203 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE
);
1208 nsWaveDecoder::BufferingStopped()
1211 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME
);
1216 nsWaveDecoder::SeekingStarted()
1219 mElement
->SeekStarted();
1224 nsWaveDecoder::SeekingStopped()
1227 mElement
->SeekCompleted();
1232 nsWaveDecoder::RegisterShutdownObserver()
1234 if (!mNotifyOnShutdown
) {
1235 nsCOMPtr
<nsIObserverService
> observerService
=
1236 do_GetService("@mozilla.org/observer-service;1");
1237 if (observerService
) {
1239 NS_SUCCEEDED(observerService
->AddObserver(this,
1240 NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
1243 NS_WARNING("Could not get an observer service. Audio playback may not shutdown cleanly.");
1249 nsWaveDecoder::UnregisterShutdownObserver()
1251 if (mNotifyOnShutdown
) {
1252 nsCOMPtr
<nsIObserverService
> observerService
=
1253 do_GetService("@mozilla.org/observer-service;1");
1254 if (observerService
) {
1255 mNotifyOnShutdown
= PR_FALSE
;
1256 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
1262 nsWaveDecoder::MediaErrorDecode()
1266 mElement
->MediaErrorDecode();
1269 NS_WARNING("MediaErrorDecode fired, but not implemented.");
1274 nsWaveDecoder::SetSeekable(PRBool aSeekable
)
1276 mSeekable
= aSeekable
;
1280 nsWaveDecoder::GetSeekable()