b=449315 Support WAV format in <audio> element. r+sr=roc
[wine-gecko.git] / content / media / video / src / nsWaveDecoder.cpp
blob6d45e9f71919508697f986982b69c3a60ef35d35
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
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) 2008
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
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 ***** */
38 #include "limits"
39 #include "prlog.h"
40 #include "prmem.h"
41 #include "nsIDOMHTMLMediaElement.h"
42 #include "nsIDocument.h"
43 #include "nsIFrame.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
92 query parameters.
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
103 public:
104 enum State {
105 STATE_LOADING_METADATA,
106 STATE_BUFFERING,
107 STATE_PLAYING,
108 STATE_SEEKING,
109 STATE_PAUSED,
110 STATE_ENDED,
111 STATE_ERROR,
112 STATE_SHUTDOWN
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.
121 float GetVolume();
123 // Set specified volume. aVolume must be in range [0.0, 1.0].
124 // Threadsafe.
125 void SetVolume(float aVolume);
128 The following four member functions initiate the appropriate state
129 transition suggested by the function name. Threadsafe.
131 void Play();
132 void Pause();
133 void Seek(float aTime);
134 void Shutdown();
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.
139 float GetDuration();
141 // Returns the current playback position in the audio stream in seconds.
142 // Threadsafe.
143 float GetCurrentTime();
145 // Returns true if the state machine is seeking. Threadsafe.
146 PRBool IsSeeking();
148 // Called by the decoder to indicate that the media stream has closed.
149 void StreamEnded();
151 // Main state machine loop. Runs forever, until shutdown state is reached.
152 NS_IMETHOD Run();
154 private:
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
177 // mWavePCMOffset.
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
182 // stereo 44.1kHz.
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
191 // 44.1kHz.
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
232 // buffering.
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.
248 PRUint32 mChannels;
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
256 // the media.
257 PRUint32 mWaveLength;
259 // Start offset of the PCM data in the media stream. Extends mWaveLength
260 // bytes.
261 PRUint32 mWavePCMOffset;
264 All member variables following this comment are accessed by both
265 threads and must be synchronized via mMonitor.
267 PRMonitor* mMonitor;
269 // The state to enter when the state machine loop iterates next.
270 State mState;
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.
276 State mNextState;
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
283 // during shutdown.
284 float mTimeOffset;
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),
300 mStream(aStream),
301 mBufferingWait(aBufferWaitTime),
302 mBufferingBytes(0),
303 mBufferingStart(0),
304 mAudioBufferSize(0),
305 mSampleRate(0),
306 mChannels(0),
307 mSampleSize(0),
308 mWaveLength(0),
309 mWavePCMOffset(0),
310 mMonitor(nsnull),
311 mState(STATE_LOADING_METADATA),
312 mNextState(STATE_PAUSED),
313 mInitialVolume(aInitialVolume),
314 mTimeOffset(0.0),
315 mExpectMoreData(PR_TRUE),
316 mMetadataValid(PR_FALSE)
318 mMonitor = nsAutoMonitor::NewMonitor("nsWaveStateMachine");
321 nsWaveStateMachine::~nsWaveStateMachine()
323 nsAutoMonitor::DestroyMonitor(mMonitor);
326 void
327 nsWaveStateMachine::Shutdown()
329 ChangeState(STATE_SHUTDOWN);
332 void
333 nsWaveStateMachine::Play()
335 nsAutoMonitor monitor(mMonitor);
336 if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING) {
337 mNextState = STATE_PLAYING;
338 } else {
339 ChangeState(STATE_PLAYING);
343 float
344 nsWaveStateMachine::GetVolume()
346 float volume = mInitialVolume;
347 if (mAudioStream) {
348 volume = mAudioStream->GetVolume();
350 return volume;
353 void
354 nsWaveStateMachine::SetVolume(float aVolume)
356 nsAutoMonitor monitor(mMonitor);
357 mInitialVolume = aVolume;
358 if (mAudioStream) {
359 mAudioStream->SetVolume(aVolume);
363 void
364 nsWaveStateMachine::Pause()
366 nsAutoMonitor monitor(mMonitor);
367 if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING) {
368 mNextState = STATE_PAUSED;
369 } else {
370 ChangeState(STATE_PAUSED);
374 void
375 nsWaveStateMachine::Seek(float aTime)
377 nsAutoMonitor monitor(mMonitor);
378 mNextState = mState;
379 mTimeOffset = aTime;
380 ChangeState(STATE_SEEKING);
383 float
384 nsWaveStateMachine::GetDuration()
386 nsAutoMonitor monitor(mMonitor);
387 if (mMetadataValid) {
388 return BytesToTime(mWaveLength);
390 return std::numeric_limits<float>::quiet_NaN();
393 float
394 nsWaveStateMachine::GetCurrentTime()
396 nsAutoMonitor monitor(mMonitor);
397 double time = 0.0;
398 if (mAudioStream) {
399 time = mAudioStream->GetTime();
401 return float(time + mTimeOffset);
404 PRBool
405 nsWaveStateMachine::IsSeeking()
407 nsAutoMonitor monitor(mMonitor);
408 return mState == STATE_SEEKING;
411 void
412 nsWaveStateMachine::StreamEnded()
414 nsAutoMonitor monitor(mMonitor);
415 mExpectMoreData = PR_FALSE;
418 NS_IMETHODIMP
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);
426 for (;;) {
427 switch (mState) {
428 case STATE_LOADING_METADATA:
430 monitor.Exit();
431 PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
432 monitor.Enter();
434 if (mState == STATE_LOADING_METADATA) {
435 nsCOMPtr<nsIRunnable> event;
436 State newState;
438 if (loaded) {
439 mMetadataValid = PR_TRUE;
440 event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MetadataLoaded);
441 newState = mNextState;
442 } else {
443 event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MediaErrorDecode);
444 newState = STATE_ERROR;
447 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
448 ChangeState(newState);
451 break;
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));
462 } else {
463 ChangeState(mNextState);
464 nsCOMPtr<nsIRunnable> event =
465 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, BufferingStopped);
466 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
469 break;
471 case STATE_PLAYING:
472 if (!mAudioStream) {
473 OpenAudioStream();
474 } else {
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);
494 } else {
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))));
501 if (len) {
502 nsAutoArrayPtr<char> buf(new char[len]);
503 PRUint32 got = 0;
505 monitor.Exit();
506 if (NS_FAILED(mStream->Read(buf.get(), len, &got))) {
507 NS_WARNING("Stream read failed");
510 if (got == 0) {
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
517 // buffered.
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();
523 if (endDelta < 0) {
524 // Read past the end of PCM data. Adjust len to avoid playing
525 // back trailing data.
526 len -= -endDelta;
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));
535 monitor.Enter();
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)));
549 break;
551 case STATE_SEEKING:
553 CloseAudioStream();
555 monitor.Exit();
556 nsCOMPtr<nsIRunnable> startEvent =
557 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, SeekingStarted);
558 NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
559 monitor.Enter();
561 if (mState == STATE_SHUTDOWN) {
562 break;
565 PRInt64 position = RoundDownToSample(TimeToBytes(mTimeOffset)) + mWavePCMOffset;
567 monitor.Exit();
568 nsresult rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, position);
569 if (NS_FAILED(rv)) {
570 NS_WARNING("Seek failed");
572 monitor.Enter();
574 if (mState == STATE_SHUTDOWN) {
575 break;
578 monitor.Exit();
579 nsCOMPtr<nsIRunnable> stopEvent =
580 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, SeekingStopped);
581 NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
582 monitor.Enter();
584 if (mState != STATE_SHUTDOWN) {
585 ChangeState(mNextState);
588 break;
590 case STATE_PAUSED:
591 if (mAudioStream) {
592 mAudioStream->Pause();
594 monitor.Wait();
595 break;
597 case STATE_ENDED:
598 if (mAudioStream) {
599 monitor.Exit();
600 mAudioStream->Drain();
601 monitor.Enter();
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) {
611 monitor.Wait();
613 break;
615 case STATE_ERROR:
616 monitor.Wait();
617 if (mState != STATE_SHUTDOWN) {
618 NS_WARNING("Invalid state transition");
619 ChangeState(STATE_ERROR);
621 break;
623 case STATE_SHUTDOWN:
624 if (mAudioStream) {
625 mTimeOffset = mAudioStream->GetTime();
627 CloseAudioStream();
628 return NS_OK;
632 return NS_OK;
635 void
636 nsWaveStateMachine::ChangeState(State aState)
638 nsAutoMonitor monitor(mMonitor);
639 mState = aState;
640 monitor.NotifyAll();
643 void
644 nsWaveStateMachine::OpenAudioStream()
646 mAudioStream = new nsAudioStream();
647 if (!mAudioStream) {
648 LOG(PR_LOG_ERROR, ("Could not create audio stream"));
649 } else {
650 mAudioStream->Init(mChannels, mSampleRate);
651 mAudioStream->SetVolume(mInitialVolume);
652 mAudioBufferSize = mAudioStream->Available() * sizeof(short);
656 void
657 nsWaveStateMachine::CloseAudioStream()
659 if (mAudioStream) {
660 mAudioStream->Shutdown();
661 mAudioStream = nsnull;
665 static PRUint32
666 ReadUint32BE(const char** aBuffer)
668 PRUint32 result =
669 PRUint8((*aBuffer)[0]) << 24 |
670 PRUint8((*aBuffer)[1]) << 16 |
671 PRUint8((*aBuffer)[2]) << 8 |
672 PRUint8((*aBuffer)[3]);
673 *aBuffer += sizeof(PRUint32);
674 return result;
677 static PRUint32
678 ReadUint32LE(const char** aBuffer)
680 PRUint32 result =
681 PRUint8((*aBuffer)[3]) << 24 |
682 PRUint8((*aBuffer)[2]) << 16 |
683 PRUint8((*aBuffer)[1]) << 8 |
684 PRUint8((*aBuffer)[0]);
685 *aBuffer += sizeof(PRUint32);
686 return result;
689 static PRUint16
690 ReadUint16LE(const char** aBuffer)
692 PRUint16 result =
693 PRUint8((*aBuffer)[1]) << 8 |
694 PRUint8((*aBuffer)[0]) << 0;
695 *aBuffer += sizeof(PRUint16);
696 return result;
699 static PRBool
700 ReadAll(nsMediaStream* aStream, char* aBuf, PRUint32 aSize)
702 PRUint32 got = 0;
703 do {
704 PRUint32 read = 0;
705 if (NS_FAILED(aStream->Read(aBuf + got, aSize - got, &read))) {
706 NS_WARNING("Stream read failed");
707 return PR_FALSE;
709 got += read;
710 } while (got != aSize);
711 return PR_TRUE;
714 PRBool
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))) {
724 return PR_FALSE;
727 if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
728 NS_WARNING("Stream data not in RIFF format");
729 return PR_FALSE;
732 // Skip over RIFF size field.
733 p += 4;
735 if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
736 NS_WARNING("Expected WAVE chunk");
737 return PR_FALSE;
740 return PR_TRUE;
743 PRBool
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))) {
755 return PR_FALSE;
758 if (ReadUint32BE(&p) != FRMT_CHUNK_MAGIC) {
759 NS_WARNING("Expected format chunk");
760 return PR_FALSE;
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");
767 return PR_FALSE;
770 channels = ReadUint16LE(&p);
771 rate = ReadUint32LE(&p);
773 // Skip over average bytes per second field.
774 p += 4;
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");
782 return PR_FALSE;
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
789 // "format" chunk.
790 if (fmtsize > WAVE_FORMAT_CHUNK_SIZE) {
791 char extLength[2];
792 const char* p = extLength;
794 if (!ReadAll(mStream, extLength, sizeof(extLength))) {
795 return PR_FALSE;
798 PRUint16 extra = ReadUint16LE(&p);
799 if (fmtsize - WAVE_FORMAT_CHUNK_SIZE + 2 != extra) {
800 NS_WARNING("Invalid extended format chunk size");
801 return PR_FALSE;
803 extra += extra % 2;
805 if (extra > 0) {
806 nsAutoArrayPtr<char> chunkExtension(new char[extra]);
807 if (!ReadAll(mStream, chunkExtension.get(), extra)) {
808 return PR_FALSE;
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
820 // supports.
821 if (rate < 100 || rate > 96000 ||
822 channels < 1 || channels > 2 ||
823 sampleSize < 2 || sampleSize > 4) {
824 NS_WARNING("Invalid WAVE metadata");
825 return PR_FALSE;
828 nsAutoMonitor monitor(mMonitor);
829 mSampleRate = rate;
830 mChannels = channels;
831 mSampleSize = sampleSize;
832 return PR_TRUE;
835 PRBool
836 nsWaveStateMachine::FindDataOffset()
838 PRUint32 length;
839 PRInt64 offset;
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.
847 for (;;) {
848 char chunkHeader[8];
849 const char* p = chunkHeader;
851 if (!ReadAll(mStream, chunkHeader, sizeof(chunkHeader))) {
852 return PR_FALSE;
855 PRUint32 magic = ReadUint32BE(&p);
857 if (magic == DATA_CHUNK_MAGIC) {
858 length = ReadUint32LE(&p);
859 break;
862 if (magic == FRMT_CHUNK_MAGIC) {
863 LOG(PR_LOG_ERROR, ("Invalid WAVE: expected \"data\" chunk but found \"format\" chunk"));
864 return PR_FALSE;
867 PRUint32 size = ReadUint32LE(&p);
868 size += size % 2;
870 nsAutoArrayPtr<char> chunk(new char[size]);
871 if (!ReadAll(mStream, chunk.get(), size)) {
872 return PR_FALSE;
876 offset = mStream->Tell();
877 if (!offset) {
878 NS_WARNING("PCM data offset not found");
879 return PR_FALSE;
882 if (offset < 0 || offset > PR_UINT32_MAX) {
883 NS_WARNING("offset out of range");
884 return PR_FALSE;
887 nsAutoMonitor monitor(mMonitor);
888 mWaveLength = length;
889 mWavePCMOffset = PRUint32(offset);
890 return PR_TRUE;
893 NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder, nsIObserver)
895 nsWaveDecoder::nsWaveDecoder()
896 : mBytesDownloaded(0),
897 mInitialVolume(1.0),
898 mStream(nsnull),
899 mEndedCurrentTime(0.0),
900 mEndedDuration(std::numeric_limits<float>::quiet_NaN()),
901 mNotifyOnShutdown(PR_FALSE),
902 mSeekable(PR_TRUE)
904 MOZ_COUNT_CTOR(nsWaveDecoder);
907 nsWaveDecoder::~nsWaveDecoder()
909 MOZ_COUNT_DTOR(nsWaveDecoder);
912 void
913 nsWaveDecoder::GetCurrentURI(nsIURI** aURI)
915 NS_IF_ADDREF(*aURI = mURI);
918 nsIPrincipal*
919 nsWaveDecoder::GetCurrentPrincipal()
921 if (!mStream) {
922 return nsnull;
924 return mStream->GetCurrentPrincipal();
927 float
928 nsWaveDecoder::GetCurrentTime()
930 if (mPlaybackStateMachine) {
931 return mPlaybackStateMachine->GetCurrentTime();
933 return mEndedCurrentTime;
936 nsresult
937 nsWaveDecoder::Seek(float aTime)
939 if (mPlaybackStateMachine && aTime >= 0.0) {
940 mPlaybackStateMachine->Seek(aTime);
941 return NS_OK;
943 return NS_ERROR_FAILURE;
946 nsresult
947 nsWaveDecoder::PlaybackRateChanged()
949 return NS_ERROR_NOT_IMPLEMENTED;
952 float
953 nsWaveDecoder::GetDuration()
955 if (mPlaybackStateMachine) {
956 return mPlaybackStateMachine->GetDuration();
958 return mEndedDuration;
961 void
962 nsWaveDecoder::Pause()
964 if (mPlaybackStateMachine) {
965 mPlaybackStateMachine->Pause();
969 float
970 nsWaveDecoder::GetVolume()
972 if (!mPlaybackStateMachine) {
973 return mInitialVolume;
975 return mPlaybackStateMachine->GetVolume();
978 void
979 nsWaveDecoder::SetVolume(float aVolume)
981 mInitialVolume = aVolume;
982 if (mPlaybackStateMachine) {
983 mPlaybackStateMachine->SetVolume(aVolume);
987 nsresult
988 nsWaveDecoder::Play()
990 if (!mPlaybackStateMachine) {
991 Load(mURI, nsnull, nsnull);
994 if (mPlaybackStateMachine) {
995 mPlaybackStateMachine->Play();
996 return NS_OK;
999 return NS_ERROR_FAILURE;
1002 void
1003 nsWaveDecoder::Stop()
1005 StopProgress();
1007 if (mPlaybackStateMachine) {
1008 mPlaybackStateMachine->Shutdown();
1011 if (mStream) {
1012 mStream->Cancel();
1015 if (mPlaybackThread) {
1016 mEndedCurrentTime = mPlaybackStateMachine->GetCurrentTime();
1017 mEndedDuration = mPlaybackStateMachine->GetDuration();
1018 mPlaybackThread->Shutdown();
1021 mPlaybackThread = nsnull;
1022 mPlaybackStateMachine = nsnull;
1023 mStream = nsnull;
1025 UnregisterShutdownObserver();
1028 nsresult
1029 nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStreamListener)
1031 if (aStreamListener) {
1032 *aStreamListener = nsnull;
1035 if (aURI) {
1036 NS_ASSERTION(!aStreamListener, "No listener should be requested here");
1037 mURI = aURI;
1038 } else {
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);
1046 StartProgress();
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,
1060 mInitialVolume);
1061 rv = mPlaybackThread->Dispatch(mPlaybackStateMachine, NS_DISPATCH_NORMAL);
1062 NS_ENSURE_SUCCESS(rv, rv);
1064 return NS_OK;
1067 void
1068 nsWaveDecoder::MetadataLoaded()
1070 if (mElement) {
1071 mElement->MetadataLoaded();
1072 mElement->FirstFrameLoaded();
1076 void
1077 nsWaveDecoder::PlaybackEnded()
1079 Stop();
1080 if (mElement) {
1081 mElement->PlaybackEnded();
1085 void
1086 nsWaveDecoder::ResourceLoaded()
1088 if (mElement) {
1089 mElement->ResourceLoaded();
1091 if (mPlaybackStateMachine) {
1092 mPlaybackStateMachine->StreamEnded();
1094 StopProgress();
1097 void
1098 nsWaveDecoder::NetworkError()
1100 if (mElement) {
1101 mElement->NetworkError();
1103 if (mPlaybackStateMachine) {
1104 mPlaybackStateMachine->StreamEnded();
1106 Stop();
1109 PRBool
1110 nsWaveDecoder::IsSeeking() const
1112 if (mPlaybackStateMachine) {
1113 return mPlaybackStateMachine->IsSeeking();
1115 return PR_FALSE;
1118 PRUint64
1119 nsWaveDecoder::GetBytesLoaded()
1121 return mBytesDownloaded;
1124 PRInt64
1125 nsWaveDecoder::GetTotalBytes()
1127 return mContentLength;
1130 void
1131 nsWaveDecoder::SetTotalBytes(PRInt64 aBytes)
1133 mContentLength = aBytes;
1136 void
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
1150 public:
1151 nsWaveDecoderShutdown(nsWaveDecoder* aDecoder)
1152 : mDecoder(aDecoder)
1156 NS_IMETHOD Run()
1158 mDecoder->Stop();
1159 return NS_OK;
1162 private:
1163 nsRefPtr<nsWaveDecoder> mDecoder;
1166 void
1167 nsWaveDecoder::Shutdown()
1169 nsMediaDecoder::Shutdown();
1171 nsCOMPtr<nsIRunnable> event = new nsWaveDecoderShutdown(this);
1172 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1175 nsresult
1176 nsWaveDecoder::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
1178 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
1179 Shutdown();
1181 return NS_OK;
1184 void
1185 nsWaveDecoder::BufferingStarted()
1187 if (mElement) {
1188 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE);
1192 void
1193 nsWaveDecoder::BufferingStopped()
1195 if (mElement) {
1196 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME);
1200 void
1201 nsWaveDecoder::SeekingStarted()
1203 if (mElement) {
1204 mElement->SeekStarted();
1208 void
1209 nsWaveDecoder::SeekingStopped()
1211 if (mElement) {
1212 mElement->SeekCompleted();
1216 void
1217 nsWaveDecoder::RegisterShutdownObserver()
1219 if (!mNotifyOnShutdown) {
1220 nsCOMPtr<nsIObserverService> observerService =
1221 do_GetService("@mozilla.org/observer-service;1");
1222 if (observerService) {
1223 mNotifyOnShutdown =
1224 NS_SUCCEEDED(observerService->AddObserver(this,
1225 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
1226 PR_FALSE));
1227 } else {
1228 NS_WARNING("Could not get an observer service. Audio playback may not shutdown cleanly.");
1233 void
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);
1246 void
1247 nsWaveDecoder::MediaErrorDecode()
1249 #if 0
1250 if (mElement) {
1251 mElement->MediaErrorDecode();
1253 #else
1254 NS_WARNING("MediaErrorDecode fired, but not implemented.");
1255 #endif
1258 void
1259 nsWaveDecoder::SetSeekable(PRBool aSeekable)
1261 mSeekable = aSeekable;
1264 PRBool
1265 nsWaveDecoder::GetSeekable()
1267 return mSeekable;