Follow-on fix for bug 457825. Use sheet principal for agent and user sheets. r=dbaron...
[wine-gecko.git] / content / media / video / src / nsWaveDecoder.cpp
blob6cb77e8d212bf526e86a90628233c30afd8142d7
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, 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
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).
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
258 // the media.
259 PRUint32 mWaveLength;
261 // Start offset of the PCM data in the media stream. Extends mWaveLength
262 // bytes.
263 PRUint32 mWavePCMOffset;
266 All member variables following this comment are accessed by both
267 threads and must be synchronized via mMonitor.
269 PRMonitor* mMonitor;
271 // The state to enter when the state machine loop iterates next.
272 State mState;
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.
278 State mNextState;
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
285 // during shutdown.
286 float mTimeOffset;
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),
302 mStream(aStream),
303 mBufferingWait(aBufferWaitTime),
304 mBufferingBytes(0),
305 mBufferingStart(0),
306 mAudioBufferSize(0),
307 mSampleRate(0),
308 mChannels(0),
309 mSampleSize(0),
310 mSampleFormat(nsAudioStream::FORMAT_S16_LE),
311 mWaveLength(0),
312 mWavePCMOffset(0),
313 mMonitor(nsnull),
314 mState(STATE_LOADING_METADATA),
315 mNextState(STATE_PAUSED),
316 mInitialVolume(aInitialVolume),
317 mTimeOffset(0.0),
318 mExpectMoreData(PR_TRUE),
319 mMetadataValid(PR_FALSE)
321 mMonitor = nsAutoMonitor::NewMonitor("nsWaveStateMachine");
324 nsWaveStateMachine::~nsWaveStateMachine()
326 nsAutoMonitor::DestroyMonitor(mMonitor);
329 void
330 nsWaveStateMachine::Shutdown()
332 ChangeState(STATE_SHUTDOWN);
335 void
336 nsWaveStateMachine::Play()
338 nsAutoMonitor monitor(mMonitor);
339 if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING) {
340 mNextState = STATE_PLAYING;
341 } else {
342 ChangeState(STATE_PLAYING);
346 float
347 nsWaveStateMachine::GetVolume()
349 float volume = mInitialVolume;
350 if (mAudioStream) {
351 volume = mAudioStream->GetVolume();
353 return volume;
356 void
357 nsWaveStateMachine::SetVolume(float aVolume)
359 nsAutoMonitor monitor(mMonitor);
360 mInitialVolume = aVolume;
361 if (mAudioStream) {
362 mAudioStream->SetVolume(aVolume);
366 void
367 nsWaveStateMachine::Pause()
369 nsAutoMonitor monitor(mMonitor);
370 if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING) {
371 mNextState = STATE_PAUSED;
372 } else {
373 ChangeState(STATE_PAUSED);
377 void
378 nsWaveStateMachine::Seek(float aTime)
380 nsAutoMonitor monitor(mMonitor);
381 mNextState = mState;
382 mTimeOffset = aTime;
383 ChangeState(STATE_SEEKING);
386 float
387 nsWaveStateMachine::GetDuration()
389 nsAutoMonitor monitor(mMonitor);
390 if (mMetadataValid) {
391 return BytesToTime(mWaveLength);
393 return std::numeric_limits<float>::quiet_NaN();
396 float
397 nsWaveStateMachine::GetCurrentTime()
399 nsAutoMonitor monitor(mMonitor);
400 double time = 0.0;
401 if (mAudioStream) {
402 time = mAudioStream->GetTime();
404 return float(time + mTimeOffset);
407 PRBool
408 nsWaveStateMachine::IsSeeking()
410 nsAutoMonitor monitor(mMonitor);
411 return mState == STATE_SEEKING;
414 void
415 nsWaveStateMachine::StreamEnded()
417 nsAutoMonitor monitor(mMonitor);
418 mExpectMoreData = PR_FALSE;
421 NS_IMETHODIMP
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);
429 for (;;) {
430 switch (mState) {
431 case STATE_LOADING_METADATA:
433 monitor.Exit();
434 PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
435 monitor.Enter();
437 if (mState == STATE_LOADING_METADATA) {
438 nsCOMPtr<nsIRunnable> event;
439 State newState;
441 if (loaded) {
442 mMetadataValid = PR_TRUE;
443 event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MetadataLoaded);
444 newState = mNextState;
445 } else {
446 event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MediaErrorDecode);
447 newState = STATE_ERROR;
450 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
451 ChangeState(newState);
454 break;
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));
465 } else {
466 ChangeState(mNextState);
467 nsCOMPtr<nsIRunnable> event =
468 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, BufferingStopped);
469 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
472 break;
474 case STATE_PLAYING:
475 if (!mAudioStream) {
476 OpenAudioStream();
477 } else {
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);
497 } else {
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))));
504 if (len) {
505 nsAutoArrayPtr<char> buf(new char[len]);
506 PRUint32 got = 0;
508 monitor.Exit();
509 if (NS_FAILED(mStream->Read(buf.get(), len, &got))) {
510 NS_WARNING("Stream read failed");
513 if (got == 0) {
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
520 // buffered.
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();
526 if (endDelta < 0) {
527 // Read past the end of PCM data. Adjust len to avoid playing
528 // back trailing data.
529 len -= -endDelta;
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);
542 monitor.Enter();
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)));
556 break;
558 case STATE_SEEKING:
560 CloseAudioStream();
562 monitor.Exit();
563 nsCOMPtr<nsIRunnable> startEvent =
564 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, SeekingStarted);
565 NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
566 monitor.Enter();
568 if (mState == STATE_SHUTDOWN) {
569 break;
572 PRInt64 position = RoundDownToSample(TimeToBytes(mTimeOffset)) + mWavePCMOffset;
574 monitor.Exit();
575 nsresult rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, position);
576 if (NS_FAILED(rv)) {
577 NS_WARNING("Seek failed");
579 monitor.Enter();
581 if (mState == STATE_SHUTDOWN) {
582 break;
585 monitor.Exit();
586 nsCOMPtr<nsIRunnable> stopEvent =
587 NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, SeekingStopped);
588 NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
589 monitor.Enter();
591 if (mState != STATE_SHUTDOWN) {
592 ChangeState(mNextState);
595 break;
597 case STATE_PAUSED:
598 if (mAudioStream) {
599 mAudioStream->Pause();
601 monitor.Wait();
602 break;
604 case STATE_ENDED:
605 if (mAudioStream) {
606 monitor.Exit();
607 mAudioStream->Drain();
608 monitor.Enter();
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
614 // stream time.
615 CloseAudioStream();
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) {
624 monitor.Wait();
626 break;
628 case STATE_ERROR:
629 monitor.Wait();
630 if (mState != STATE_SHUTDOWN) {
631 NS_WARNING("Invalid state transition");
632 ChangeState(STATE_ERROR);
634 break;
636 case STATE_SHUTDOWN:
637 if (mAudioStream) {
638 mTimeOffset = mAudioStream->GetTime();
640 CloseAudioStream();
641 return NS_OK;
645 return NS_OK;
648 void
649 nsWaveStateMachine::ChangeState(State aState)
651 nsAutoMonitor monitor(mMonitor);
652 mState = aState;
653 monitor.NotifyAll();
656 void
657 nsWaveStateMachine::OpenAudioStream()
659 mAudioStream = new nsAudioStream();
660 if (!mAudioStream) {
661 LOG(PR_LOG_ERROR, ("Could not create audio stream"));
662 } else {
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);
671 void
672 nsWaveStateMachine::CloseAudioStream()
674 if (mAudioStream) {
675 mAudioStream->Shutdown();
676 mAudioStream = nsnull;
680 static PRUint32
681 ReadUint32BE(const char** aBuffer)
683 PRUint32 result =
684 PRUint8((*aBuffer)[0]) << 24 |
685 PRUint8((*aBuffer)[1]) << 16 |
686 PRUint8((*aBuffer)[2]) << 8 |
687 PRUint8((*aBuffer)[3]);
688 *aBuffer += sizeof(PRUint32);
689 return result;
692 static PRUint32
693 ReadUint32LE(const char** aBuffer)
695 PRUint32 result =
696 PRUint8((*aBuffer)[3]) << 24 |
697 PRUint8((*aBuffer)[2]) << 16 |
698 PRUint8((*aBuffer)[1]) << 8 |
699 PRUint8((*aBuffer)[0]);
700 *aBuffer += sizeof(PRUint32);
701 return result;
704 static PRUint16
705 ReadUint16LE(const char** aBuffer)
707 PRUint16 result =
708 PRUint8((*aBuffer)[1]) << 8 |
709 PRUint8((*aBuffer)[0]) << 0;
710 *aBuffer += sizeof(PRUint16);
711 return result;
714 static PRBool
715 ReadAll(nsMediaStream* aStream, char* aBuf, PRUint32 aSize)
717 PRUint32 got = 0;
718 do {
719 PRUint32 read = 0;
720 if (NS_FAILED(aStream->Read(aBuf + got, aSize - got, &read))) {
721 NS_WARNING("Stream read failed");
722 return PR_FALSE;
724 got += read;
725 } while (got != aSize);
726 return PR_TRUE;
729 PRBool
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))) {
739 return PR_FALSE;
742 if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
743 NS_WARNING("Stream data not in RIFF format");
744 return PR_FALSE;
747 // Skip over RIFF size field.
748 p += 4;
750 if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
751 NS_WARNING("Expected WAVE chunk");
752 return PR_FALSE;
755 return PR_TRUE;
758 PRBool
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))) {
770 return PR_FALSE;
773 if (ReadUint32BE(&p) != FRMT_CHUNK_MAGIC) {
774 NS_WARNING("Expected format chunk");
775 return PR_FALSE;
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");
782 return PR_FALSE;
785 channels = ReadUint16LE(&p);
786 rate = ReadUint32LE(&p);
788 // Skip over average bytes per second field.
789 p += 4;
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
799 // "format" chunk.
800 if (fmtsize > WAVE_FORMAT_CHUNK_SIZE) {
801 char extLength[2];
802 const char* p = extLength;
804 if (!ReadAll(mStream, extLength, sizeof(extLength))) {
805 return PR_FALSE;
808 PRUint16 extra = ReadUint16LE(&p);
809 if (fmtsize - (WAVE_FORMAT_CHUNK_SIZE + 2) != extra) {
810 NS_WARNING("Invalid extended format chunk size");
811 return PR_FALSE;
813 extra += extra % 2;
815 if (extra > 0) {
816 nsAutoArrayPtr<char> chunkExtension(new char[extra]);
817 if (!ReadAll(mStream, chunkExtension.get(), extra)) {
818 return PR_FALSE;
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");
835 return PR_FALSE;
838 nsAutoMonitor monitor(mMonitor);
839 mSampleRate = rate;
840 mChannels = channels;
841 mSampleSize = sampleSize;
842 if (sampleFormat == 8) {
843 mSampleFormat = nsAudioStream::FORMAT_U8;
844 } else {
845 mSampleFormat = nsAudioStream::FORMAT_S16_LE;
847 return PR_TRUE;
850 PRBool
851 nsWaveStateMachine::FindDataOffset()
853 PRUint32 length;
854 PRInt64 offset;
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.
862 for (;;) {
863 char chunkHeader[8];
864 const char* p = chunkHeader;
866 if (!ReadAll(mStream, chunkHeader, sizeof(chunkHeader))) {
867 return PR_FALSE;
870 PRUint32 magic = ReadUint32BE(&p);
872 if (magic == DATA_CHUNK_MAGIC) {
873 length = ReadUint32LE(&p);
874 break;
877 if (magic == FRMT_CHUNK_MAGIC) {
878 LOG(PR_LOG_ERROR, ("Invalid WAVE: expected \"data\" chunk but found \"format\" chunk"));
879 return PR_FALSE;
882 PRUint32 size = ReadUint32LE(&p);
883 size += size % 2;
885 nsAutoArrayPtr<char> chunk(new char[size]);
886 if (!ReadAll(mStream, chunk.get(), size)) {
887 return PR_FALSE;
891 offset = mStream->Tell();
892 if (!offset) {
893 NS_WARNING("PCM data offset not found");
894 return PR_FALSE;
897 if (offset < 0 || offset > PR_UINT32_MAX) {
898 NS_WARNING("offset out of range");
899 return PR_FALSE;
902 nsAutoMonitor monitor(mMonitor);
903 mWaveLength = length;
904 mWavePCMOffset = PRUint32(offset);
905 return PR_TRUE;
908 NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder, nsIObserver)
910 nsWaveDecoder::nsWaveDecoder()
911 : mBytesDownloaded(0),
912 mInitialVolume(1.0),
913 mStream(nsnull),
914 mEndedCurrentTime(0.0),
915 mEndedDuration(std::numeric_limits<float>::quiet_NaN()),
916 mNotifyOnShutdown(PR_FALSE),
917 mSeekable(PR_TRUE)
919 MOZ_COUNT_CTOR(nsWaveDecoder);
922 nsWaveDecoder::~nsWaveDecoder()
924 MOZ_COUNT_DTOR(nsWaveDecoder);
927 void
928 nsWaveDecoder::GetCurrentURI(nsIURI** aURI)
930 NS_IF_ADDREF(*aURI = mURI);
933 nsIPrincipal*
934 nsWaveDecoder::GetCurrentPrincipal()
936 if (!mStream) {
937 return nsnull;
939 return mStream->GetCurrentPrincipal();
942 float
943 nsWaveDecoder::GetCurrentTime()
945 if (mPlaybackStateMachine) {
946 return mPlaybackStateMachine->GetCurrentTime();
948 return mEndedCurrentTime;
951 nsresult
952 nsWaveDecoder::Seek(float aTime)
954 if (mPlaybackStateMachine && aTime >= 0.0) {
955 mPlaybackStateMachine->Seek(aTime);
956 return NS_OK;
958 return NS_ERROR_FAILURE;
961 nsresult
962 nsWaveDecoder::PlaybackRateChanged()
964 return NS_ERROR_NOT_IMPLEMENTED;
967 float
968 nsWaveDecoder::GetDuration()
970 if (mPlaybackStateMachine) {
971 return mPlaybackStateMachine->GetDuration();
973 return mEndedDuration;
976 void
977 nsWaveDecoder::Pause()
979 if (mPlaybackStateMachine) {
980 mPlaybackStateMachine->Pause();
984 float
985 nsWaveDecoder::GetVolume()
987 if (!mPlaybackStateMachine) {
988 return mInitialVolume;
990 return mPlaybackStateMachine->GetVolume();
993 void
994 nsWaveDecoder::SetVolume(float aVolume)
996 mInitialVolume = aVolume;
997 if (mPlaybackStateMachine) {
998 mPlaybackStateMachine->SetVolume(aVolume);
1002 nsresult
1003 nsWaveDecoder::Play()
1005 if (!mPlaybackStateMachine) {
1006 Load(mURI, nsnull, nsnull);
1009 if (mPlaybackStateMachine) {
1010 mPlaybackStateMachine->Play();
1011 return NS_OK;
1014 return NS_ERROR_FAILURE;
1017 void
1018 nsWaveDecoder::Stop()
1020 StopProgress();
1022 if (mPlaybackStateMachine) {
1023 mPlaybackStateMachine->Shutdown();
1026 if (mStream) {
1027 mStream->Cancel();
1030 if (mPlaybackThread) {
1031 mEndedCurrentTime = mPlaybackStateMachine->GetCurrentTime();
1032 mEndedDuration = mPlaybackStateMachine->GetDuration();
1033 mPlaybackThread->Shutdown();
1036 mPlaybackThread = nsnull;
1037 mPlaybackStateMachine = nsnull;
1038 mStream = nsnull;
1040 UnregisterShutdownObserver();
1043 nsresult
1044 nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStreamListener)
1046 if (aStreamListener) {
1047 *aStreamListener = nsnull;
1050 if (aURI) {
1051 NS_ASSERTION(!aStreamListener, "No listener should be requested here");
1052 mURI = aURI;
1053 } else {
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);
1061 StartProgress();
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,
1075 mInitialVolume);
1076 rv = mPlaybackThread->Dispatch(mPlaybackStateMachine, NS_DISPATCH_NORMAL);
1077 NS_ENSURE_SUCCESS(rv, rv);
1079 return NS_OK;
1082 void
1083 nsWaveDecoder::MetadataLoaded()
1085 if (mElement) {
1086 mElement->MetadataLoaded();
1087 mElement->FirstFrameLoaded();
1091 void
1092 nsWaveDecoder::PlaybackEnded()
1094 Stop();
1095 if (mElement) {
1096 mElement->PlaybackEnded();
1100 void
1101 nsWaveDecoder::ResourceLoaded()
1103 if (mElement) {
1104 mElement->ResourceLoaded();
1106 if (mPlaybackStateMachine) {
1107 mPlaybackStateMachine->StreamEnded();
1109 StopProgress();
1112 void
1113 nsWaveDecoder::NetworkError()
1115 if (mElement) {
1116 mElement->NetworkError();
1118 if (mPlaybackStateMachine) {
1119 mPlaybackStateMachine->StreamEnded();
1121 Stop();
1124 PRBool
1125 nsWaveDecoder::IsSeeking() const
1127 if (mPlaybackStateMachine) {
1128 return mPlaybackStateMachine->IsSeeking();
1130 return PR_FALSE;
1133 PRUint64
1134 nsWaveDecoder::GetBytesLoaded()
1136 return mBytesDownloaded;
1139 PRInt64
1140 nsWaveDecoder::GetTotalBytes()
1142 return mContentLength;
1145 void
1146 nsWaveDecoder::SetTotalBytes(PRInt64 aBytes)
1148 mContentLength = aBytes;
1151 void
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
1165 public:
1166 nsWaveDecoderShutdown(nsWaveDecoder* aDecoder)
1167 : mDecoder(aDecoder)
1171 NS_IMETHOD Run()
1173 mDecoder->Stop();
1174 return NS_OK;
1177 private:
1178 nsRefPtr<nsWaveDecoder> mDecoder;
1181 void
1182 nsWaveDecoder::Shutdown()
1184 nsMediaDecoder::Shutdown();
1186 nsCOMPtr<nsIRunnable> event = new nsWaveDecoderShutdown(this);
1187 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1190 nsresult
1191 nsWaveDecoder::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
1193 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
1194 Shutdown();
1196 return NS_OK;
1199 void
1200 nsWaveDecoder::BufferingStarted()
1202 if (mElement) {
1203 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE);
1207 void
1208 nsWaveDecoder::BufferingStopped()
1210 if (mElement) {
1211 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME);
1215 void
1216 nsWaveDecoder::SeekingStarted()
1218 if (mElement) {
1219 mElement->SeekStarted();
1223 void
1224 nsWaveDecoder::SeekingStopped()
1226 if (mElement) {
1227 mElement->SeekCompleted();
1231 void
1232 nsWaveDecoder::RegisterShutdownObserver()
1234 if (!mNotifyOnShutdown) {
1235 nsCOMPtr<nsIObserverService> observerService =
1236 do_GetService("@mozilla.org/observer-service;1");
1237 if (observerService) {
1238 mNotifyOnShutdown =
1239 NS_SUCCEEDED(observerService->AddObserver(this,
1240 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
1241 PR_FALSE));
1242 } else {
1243 NS_WARNING("Could not get an observer service. Audio playback may not shutdown cleanly.");
1248 void
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);
1261 void
1262 nsWaveDecoder::MediaErrorDecode()
1264 #if 0
1265 if (mElement) {
1266 mElement->MediaErrorDecode();
1268 #else
1269 NS_WARNING("MediaErrorDecode fired, but not implemented.");
1270 #endif
1273 void
1274 nsWaveDecoder::SetSeekable(PRBool aSeekable)
1276 mSeekable = aSeekable;
1279 PRBool
1280 nsWaveDecoder::GetSeekable()
1282 return mSeekable;