mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
b=449315 Support WAV format in <audio> element. r+sr=roc
This commit is contained in:
parent
70d7909b47
commit
f7e186f1bd
@ -159,6 +159,8 @@ MOZ_MORKREADER = @MOZ_MORKREADER@
|
||||
MOZ_NO_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
|
||||
MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
|
||||
MOZ_OGG = @MOZ_OGG@
|
||||
MOZ_SYDNEYAUDIO = @MOZ_SYDNEYAUDIO@
|
||||
MOZ_WAVE = @MOZ_WAVE@
|
||||
MOZ_MEDIA = @MOZ_MEDIA@
|
||||
NS_PRINTING = @NS_PRINTING@
|
||||
MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
|
||||
|
41
configure.in
41
configure.in
@ -4308,6 +4308,8 @@ MOZ_NO_INSPECTOR_APIS=
|
||||
MOZ_NO_XPCOM_OBSOLETE=
|
||||
MOZ_NO_FAST_LOAD=
|
||||
MOZ_OGG=1
|
||||
MOZ_SYDNEYAUDIO=
|
||||
MOZ_WAVE=1
|
||||
MOZ_MEDIA=
|
||||
MOZ_OJI=1
|
||||
MOZ_PERMISSIONS=1
|
||||
@ -5280,7 +5282,7 @@ if test -n "$MOZ_NO_FAST_LOAD"; then
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable Ogg Codecs
|
||||
dnl = Disable Ogg Codecs
|
||||
dnl ========================================================
|
||||
MOZ_ARG_DISABLE_BOOL(ogg,
|
||||
[ --disable-ogg Disable Ogg Codec support],
|
||||
@ -5291,23 +5293,56 @@ AC_SUBST(MOZ_OGG)
|
||||
|
||||
if test -n "$MOZ_OGG"; then
|
||||
AC_DEFINE(MOZ_OGG)
|
||||
MOZ_SYDNEYAUDIO=1
|
||||
MOZ_MEDIA=1
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable Wave decoder support
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(wave,
|
||||
[ --disable-wave Disable Wave decoder support],
|
||||
MOZ_WAVE=,
|
||||
MOZ_WAVE=1)
|
||||
|
||||
AC_SUBST(MOZ_WAVE)
|
||||
|
||||
if test -n "$MOZ_WAVE"; then
|
||||
AC_DEFINE(MOZ_WAVE)
|
||||
MOZ_SYDNEYAUDIO=1
|
||||
MOZ_MEDIA=1
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Handle dependent SYDNEYAUDIO and MEDIA defines
|
||||
dnl ========================================================
|
||||
|
||||
AC_SUBST(MOZ_SYDNEYAUDIO)
|
||||
|
||||
if test -n "$MOZ_SYDNEYAUDIO"; then
|
||||
AC_DEFINE(MOZ_SYDNEYAUDIO)
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_MEDIA)
|
||||
|
||||
if test -n "$MOZ_MEDIA"; then
|
||||
AC_DEFINE(MOZ_MEDIA)
|
||||
fi
|
||||
|
||||
dnl If using Ogg with Linux, ensure that the alsa library is available
|
||||
if test -n "$MOZ_OGG"; then
|
||||
dnl ========================================================
|
||||
dnl = Check alsa availability on Linux if using sydneyaudio
|
||||
dnl ========================================================
|
||||
|
||||
dnl If using sydneyaudio with Linux, ensure that the alsa library is available
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
if test -n "$MOZ_SYDNEYAUDIO"; then
|
||||
case "$target_os" in
|
||||
linux*)
|
||||
AC_CHECK_LIB(asound, snd_pcm_open,,AC_MSG_ERROR([Ogg support on Linux requires the alsa library]))
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl Permissions System
|
||||
|
@ -72,6 +72,9 @@
|
||||
#ifdef MOZ_OGG
|
||||
#include "nsOggDecoder.h"
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
#include "nsWaveDecoder.h"
|
||||
#endif
|
||||
|
||||
class nsAsyncEventRunner : public nsRunnable
|
||||
{
|
||||
@ -487,12 +490,34 @@ static PRBool IsOggType(const nsACString& aType)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WAVE
|
||||
static const char gWaveTypes[][16] = {
|
||||
"audio/x-wav",
|
||||
"audio/wav",
|
||||
"audio/wave",
|
||||
"audio/x-pn-wav"
|
||||
};
|
||||
|
||||
static PRBool IsWaveType(const nsACString& aType)
|
||||
{
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); ++i) {
|
||||
if (aType.EqualsASCII(gWaveTypes[i]))
|
||||
return PR_TRUE;
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
PRBool nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType)
|
||||
{
|
||||
#ifdef MOZ_OGG
|
||||
if (IsOggType(nsDependentCString(aMIMEType)))
|
||||
return PR_TRUE;
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(nsDependentCString(aMIMEType)))
|
||||
return PR_TRUE;
|
||||
#endif
|
||||
return PR_FALSE;
|
||||
}
|
||||
@ -509,6 +534,13 @@ void nsHTMLMediaElement::InitMediaTypes()
|
||||
"@mozilla.org/content/document-loader-factory;1",
|
||||
PR_FALSE, PR_TRUE, nsnull);
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); i++) {
|
||||
catMan->AddCategoryEntry("Gecko-Content-Viewers", gWaveTypes[i],
|
||||
"@mozilla.org/content/document-loader-factory;1",
|
||||
PR_FALSE, PR_TRUE, nsnull);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -523,6 +555,11 @@ void nsHTMLMediaElement::ShutdownMediaTypes()
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); i++) {
|
||||
catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gOggTypes[i], PR_FALSE);
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); i++) {
|
||||
catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gWaveTypes[i], PR_FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -536,6 +573,14 @@ PRBool nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
|
||||
mDecoder = nsnull;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(aType)) {
|
||||
mDecoder = new nsWaveDecoder();
|
||||
if (mDecoder && !mDecoder->Init()) {
|
||||
mDecoder = nsnull;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return mDecoder != nsnull;
|
||||
}
|
||||
|
@ -47,14 +47,30 @@ EXPORTS = \
|
||||
nsMediaDecoder.h \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_OGG
|
||||
ifdef MOZ_MEDIA
|
||||
EXPORTS += \
|
||||
nsAudioStream.h \
|
||||
nsChannelReader.h \
|
||||
nsChannelToPipeListener.h \
|
||||
nsMediaStream.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
EXPORTS += \
|
||||
nsAudioStream.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_OGG
|
||||
EXPORTS += \
|
||||
nsChannelReader.h \
|
||||
nsOggDecoder.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WAVE
|
||||
EXPORTS += \
|
||||
nsWaveDecoder.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -35,6 +35,9 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#if !defined(nsAudioStream_h_)
|
||||
#define nsAudioStream_h_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "prlog.h"
|
||||
|
||||
@ -64,7 +67,12 @@ class nsAudioStream
|
||||
// Write sound data to the audio hardware. aBuf is an array of floats of
|
||||
// length aCount. aCount should be evenly divisible by the number of
|
||||
// channels in this audio stream.
|
||||
void Write(float* aBuf, PRUint32 count);
|
||||
void Write(const float* aBuf, PRUint32 aCount);
|
||||
|
||||
// Write sound data to the audio hardware. aBuf is an array of shorts in
|
||||
// signed 16-bit little endian format of length aCount. Acount should be
|
||||
// evenly divisible by the number of channels in this audio stream.
|
||||
void Write(const short* aBuf, PRUint32 aCount);
|
||||
|
||||
// Return the number of sound samples that can be written to the audio device
|
||||
// without blocking.
|
||||
@ -78,10 +86,33 @@ class nsAudioStream
|
||||
// 0 (meaning muted) to 1 (meaning full volume).
|
||||
void SetVolume(float aVolume);
|
||||
|
||||
// Block until buffered audio data has been consumed.
|
||||
void Drain();
|
||||
|
||||
// Pause sound playback.
|
||||
void Pause();
|
||||
|
||||
// Resume sound playback.
|
||||
void Resume();
|
||||
|
||||
// Return the position (in seconds) of the audio sample currently being
|
||||
// played by the audio hardware.
|
||||
double GetTime();
|
||||
|
||||
private:
|
||||
double mVolume;
|
||||
void* mAudioHandle;
|
||||
int mRate;
|
||||
int mChannels;
|
||||
PRPackedBool mMute;
|
||||
|
||||
// The byte position in the audio buffer where playback was last paused.
|
||||
PRInt64 mSavedPauseBytes;
|
||||
PRInt64 mPauseBytes;
|
||||
|
||||
float mStartTime;
|
||||
float mPauseTime;
|
||||
PRInt64 mSamplesBuffered;
|
||||
|
||||
PRPackedBool mPaused;
|
||||
};
|
||||
#endif
|
||||
|
@ -127,6 +127,9 @@ class nsMediaDecoder : public nsIObserver
|
||||
// Called when the video file has completed downloading.
|
||||
virtual void ResourceLoaded() = 0;
|
||||
|
||||
// Called if the media file encounters a network error.
|
||||
virtual void NetworkError() = 0;
|
||||
|
||||
// Call from any thread safely. Return PR_TRUE if we are currently
|
||||
// seeking in the media resource.
|
||||
virtual PRBool IsSeeking() const = 0;
|
||||
|
@ -331,6 +331,10 @@ class nsOggDecoder : public nsMediaDecoder
|
||||
// Call on the main thread only.
|
||||
void ResourceLoaded();
|
||||
|
||||
// Called if the media file encounters a network error.
|
||||
// Call on the main thread only.
|
||||
void NetworkError();
|
||||
|
||||
// Call from any thread safely. Return PR_TRUE if we are currently
|
||||
// seeking in the media resource.
|
||||
virtual PRBool IsSeeking() const;
|
||||
|
270
content/media/video/public/nsWaveDecoder.h
Normal file
270
content/media/video/public/nsWaveDecoder.h
Normal file
@ -0,0 +1,270 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: ML 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Matthew Gregan <kinetik@flim.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#if !defined(nsWaveDecoder_h_)
|
||||
#define nsWaveDecoder_h_
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMediaDecoder.h"
|
||||
#include "nsMediaStream.h"
|
||||
|
||||
/*
|
||||
nsWaveDecoder provides an implementation of the abstract nsMediaDecoder
|
||||
class that supports parsing and playback of Waveform Audio (WAVE) chunks
|
||||
embedded in Resource Interchange File Format (RIFF) bitstreams as
|
||||
specified by the Multimedia Programming Interface and Data Specification
|
||||
1.0.
|
||||
|
||||
Each decoder instance starts one thread (the playback thread). A single
|
||||
nsWaveStateMachine event is dispatched to this thread to start the
|
||||
thread's state machine running. The Run method of the event is a loop
|
||||
that executes the current state. The state can be changed by the state
|
||||
machine, or from the main thread via threadsafe methods on the event.
|
||||
During playback, the playback thread reads data from the network and
|
||||
writes it to the audio backend, attempting to keep the backend's audio
|
||||
buffers full. It is also responsible for seeking, buffering, and
|
||||
pausing/resuming audio.
|
||||
|
||||
The decoder also owns an nsMediaStream instance that provides a threadsafe
|
||||
blocking interface to read from network channels. The state machine is
|
||||
the primary user of this stream and holds a weak (raw) pointer to it as
|
||||
the thread, state machine, and stream's lifetimes are all managed by the
|
||||
decoder.
|
||||
|
||||
nsWaveStateMachine has the following states:
|
||||
|
||||
LOADING_METADATA
|
||||
RIFF/WAVE chunks are being read from the stream, the metadata describing
|
||||
the audio data is parsed.
|
||||
|
||||
BUFFERING
|
||||
Playback is paused while waiting for additional data.
|
||||
|
||||
PLAYING
|
||||
If data is available in the stream and the audio backend can consume
|
||||
more data, it is read from the stream and written to the audio backend.
|
||||
Sleep until approximately half of the backend's buffers have drained.
|
||||
|
||||
SEEKING
|
||||
Decoder is seeking to a specified time in the media.
|
||||
|
||||
PAUSED
|
||||
Pause the audio backend, then wait for a state transition.
|
||||
|
||||
ENDED
|
||||
Expected PCM data (or stream EOF) reached, wait for the audio backend to
|
||||
play any buffered data, then wait for shutdown.
|
||||
|
||||
ERROR
|
||||
Metadata loading/parsing failed, wait for shutdown.
|
||||
|
||||
SHUTDOWN
|
||||
Close the audio backend and return from the run loop.
|
||||
|
||||
State transitions within the state machine are:
|
||||
|
||||
LOADING_METADATA -> PLAYING
|
||||
-> PAUSED
|
||||
-> ERROR
|
||||
|
||||
BUFFERING -> PLAYING
|
||||
-> PAUSED
|
||||
|
||||
PLAYING -> BUFFERING
|
||||
-> ENDED
|
||||
|
||||
SEEKING -> PLAYING
|
||||
-> PAUSED
|
||||
|
||||
PAUSED -> waits for caller to play, seek, or shutdown
|
||||
|
||||
ENDED -> waits for caller to shutdown
|
||||
|
||||
ERROR -> waits for caller to shutdown
|
||||
|
||||
SHUTDOWN -> exits state machine
|
||||
|
||||
In addition, the following methods cause state transitions:
|
||||
|
||||
Shutdown(), Play(), Pause(), Seek(float)
|
||||
|
||||
The decoder implementation is currently limited to Linear PCM encoded
|
||||
audio data with one or two channels of 16-bit samples at sample rates from
|
||||
100 Hz to 96 kHz. The sample size and number of channels (and, to some
|
||||
extent, the sample format) is limited by what the audio backend
|
||||
(sydneyaudio via nsAudioStream) currently supports. sydneyaudio's API
|
||||
suggests support of 8-bit, µ-Law, and A-Law samples, but this support is
|
||||
not currently implemented. The supported sample rate is artificially
|
||||
limited to arbitrarily selected sane values. Support for additional
|
||||
channels (and other new features) would require extending nsWaveDecoder to
|
||||
support parsing the newer WAVE_FORMAT_EXTENSIBLE chunk format.
|
||||
*/
|
||||
|
||||
class nsWaveStateMachine;
|
||||
|
||||
class nsWaveDecoder : public nsMediaDecoder
|
||||
{
|
||||
friend class nsWaveStateMachine;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
public:
|
||||
nsWaveDecoder();
|
||||
~nsWaveDecoder();
|
||||
|
||||
virtual void GetCurrentURI(nsIURI** aURI);
|
||||
virtual nsIPrincipal* GetCurrentPrincipal();
|
||||
|
||||
// Return the current playback position in the media in seconds.
|
||||
virtual float GetCurrentTime();
|
||||
|
||||
// Return the total playback length of the media in seconds.
|
||||
virtual float GetDuration();
|
||||
|
||||
// Get the current audio playback volume; result in range [0.0, 1.0].
|
||||
virtual float GetVolume();
|
||||
|
||||
// Set the audio playback volume; must be in range [0.0, 1.0].
|
||||
virtual void SetVolume(float aVolume);
|
||||
|
||||
virtual nsresult Play();
|
||||
virtual void Stop();
|
||||
virtual void Pause();
|
||||
|
||||
// Set the current time of the media to aTime. This may cause mStream to
|
||||
// create a new channel to fetch data from the appropriate position in the
|
||||
// stream.
|
||||
virtual nsresult Seek(float aTime);
|
||||
|
||||
// Report whether the decoder is currently seeking.
|
||||
virtual PRBool IsSeeking() const;
|
||||
|
||||
// Start downloading the media at the specified URI. The media's metadata
|
||||
// will be parsed and made available as the load progresses.
|
||||
virtual nsresult Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStreamListener);
|
||||
|
||||
// Called by mStream (and possibly the nsChannelToPipeListener used
|
||||
// internally by mStream) when the stream has completed loading.
|
||||
virtual void ResourceLoaded();
|
||||
|
||||
// Called by mStream (and possibly the nsChannelToPipeListener used
|
||||
// internally by mStream) if the stream encounters a network error.
|
||||
virtual void NetworkError();
|
||||
|
||||
// Element is notifying us that the requested playback rate has changed.
|
||||
virtual nsresult PlaybackRateChanged();
|
||||
|
||||
// Getter/setter for mContentLength.
|
||||
virtual PRInt64 GetTotalBytes();
|
||||
virtual void SetTotalBytes(PRInt64 aBytes);
|
||||
|
||||
// Getter/setter for mSeekable.
|
||||
virtual void SetSeekable(PRBool aSeekable);
|
||||
virtual PRBool GetSeekable();
|
||||
|
||||
// Getter/setter for mBytesDownloaded.
|
||||
virtual PRUint64 GetBytesLoaded();
|
||||
virtual void UpdateBytesDownloaded(PRUint64 aBytes);
|
||||
|
||||
// Must be called by the owning object before disposing the decoder.
|
||||
virtual void Shutdown();
|
||||
|
||||
private:
|
||||
// Notifies the nsHTMLMediaElement that buffering has started.
|
||||
void BufferingStarted();
|
||||
|
||||
// Notifies the element that buffering has stopped.
|
||||
void BufferingStopped();
|
||||
|
||||
// Notifies the element that seeking has started.
|
||||
void SeekingStarted();
|
||||
|
||||
// Notifies the element that seeking has completed.
|
||||
void SeekingStopped();
|
||||
|
||||
// Notifies the element that metadata loading has completed. Only fired
|
||||
// if metadata is valid.
|
||||
void MetadataLoaded();
|
||||
|
||||
// Notifies the element that playback has completed.
|
||||
void PlaybackEnded();
|
||||
|
||||
// Notifies the element that metadata loading has failed.
|
||||
void MediaErrorDecode();
|
||||
|
||||
void RegisterShutdownObserver();
|
||||
void UnregisterShutdownObserver();
|
||||
|
||||
// Length of the current resource, or -1 if not available.
|
||||
PRInt64 mContentLength;
|
||||
|
||||
// Total bytes downloaded by mStream so far.
|
||||
PRUint64 mBytesDownloaded;
|
||||
|
||||
// Volume that the audio backend will be initialized with.
|
||||
float mInitialVolume;
|
||||
|
||||
// URI of the current resource.
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
|
||||
// Thread that handles audio playback, including data download.
|
||||
nsCOMPtr<nsIThread> mPlaybackThread;
|
||||
|
||||
// State machine that runs on mPlaybackThread. Methods on this object are
|
||||
// safe to call from any thread.
|
||||
nsCOMPtr<nsWaveStateMachine> mPlaybackStateMachine;
|
||||
|
||||
// Threadsafe wrapper around channels that provides seeking based on the
|
||||
// underlying channel type.
|
||||
nsAutoPtr<nsMediaStream> mStream;
|
||||
|
||||
// Copy of the current time and duration when the state machine was
|
||||
// disposed. Used to respond to time and duration queries with sensible
|
||||
// values after playback has ended.
|
||||
float mEndedCurrentTime;
|
||||
float mEndedDuration;
|
||||
|
||||
// True if we have registered a shutdown observer.
|
||||
PRPackedBool mNotifyOnShutdown;
|
||||
|
||||
// True if the media resource is seekable.
|
||||
PRPackedBool mSeekable;
|
||||
};
|
||||
|
||||
#endif
|
@ -82,16 +82,32 @@ CPPSRCS = \
|
||||
nsMediaDecoder.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_OGG
|
||||
ifdef MOZ_MEDIA
|
||||
CPPSRCS += \
|
||||
nsAudioStream.cpp \
|
||||
nsChannelReader.cpp \
|
||||
nsChannelToPipeListener.cpp \
|
||||
nsMediaStream.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
CPPSRCS += \
|
||||
nsAudioStream.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_OGG
|
||||
CPPSRCS += \
|
||||
nsChannelReader.cpp \
|
||||
nsOggDecoder.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WAVE
|
||||
CPPSRCS += \
|
||||
nsWaveDecoder.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -49,6 +49,13 @@ extern "C" {
|
||||
PRLogModuleInfo* gAudioStreamLog = nsnull;
|
||||
#endif
|
||||
|
||||
#define FAKE_BUFFER_SIZE 176400
|
||||
|
||||
static float CurrentTimeInSeconds()
|
||||
{
|
||||
return PR_IntervalToMilliseconds(PR_IntervalNow()) / 1000.0;
|
||||
}
|
||||
|
||||
void nsAudioStream::InitLibrary()
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
@ -64,7 +71,12 @@ nsAudioStream::nsAudioStream() :
|
||||
mVolume(1.0),
|
||||
mAudioHandle(0),
|
||||
mRate(0),
|
||||
mChannels(0)
|
||||
mChannels(0),
|
||||
mSavedPauseBytes(0),
|
||||
mPauseBytes(0),
|
||||
mPauseTime(0.0),
|
||||
mSamplesBuffered(0),
|
||||
mPaused(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
@ -72,6 +84,7 @@ void nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate)
|
||||
{
|
||||
mRate = aRate;
|
||||
mChannels = aNumChannels;
|
||||
mStartTime = CurrentTimeInSeconds();
|
||||
if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
|
||||
NULL,
|
||||
SA_MODE_WRONLY,
|
||||
@ -100,8 +113,10 @@ void nsAudioStream::Shutdown()
|
||||
mAudioHandle = nsnull;
|
||||
}
|
||||
|
||||
void nsAudioStream::Write(float* aBuf, PRUint32 aCount)
|
||||
void nsAudioStream::Write(const float* aBuf, PRUint32 aCount)
|
||||
{
|
||||
mSamplesBuffered += aCount;
|
||||
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
@ -126,14 +141,37 @@ void nsAudioStream::Write(float* aBuf, PRUint32 aCount)
|
||||
if (sa_stream_write(reinterpret_cast<sa_stream_t*>(mAudioHandle), s_data.get(), aCount * sizeof(short)) != SA_SUCCESS) {
|
||||
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nsAudioStream::Write(const short* aBuf, PRUint32 aCount)
|
||||
{
|
||||
mSamplesBuffered += aCount;
|
||||
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
nsAutoArrayPtr<short> s_data(new short[aCount]);
|
||||
|
||||
if (s_data) {
|
||||
for (PRUint32 i = 0; i < aCount; ++i) {
|
||||
s_data[i] = aBuf[i] * mVolume;
|
||||
}
|
||||
|
||||
if (sa_stream_write(reinterpret_cast<sa_stream_t*>(mAudioHandle), s_data.get(), aCount * sizeof(short)) != SA_SUCCESS) {
|
||||
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 nsAudioStream::Available()
|
||||
{
|
||||
// If the audio backend failed to open, lie and say we'll accept some
|
||||
// data.
|
||||
if (!mAudioHandle)
|
||||
return 0;
|
||||
return FAKE_BUFFER_SIZE;
|
||||
|
||||
size_t s = 0;
|
||||
sa_stream_get_write_size(reinterpret_cast<sa_stream_t*>(mAudioHandle), &s);
|
||||
@ -149,3 +187,77 @@ void nsAudioStream::SetVolume(float aVolume)
|
||||
{
|
||||
mVolume = aVolume;
|
||||
}
|
||||
|
||||
void nsAudioStream::Drain()
|
||||
{
|
||||
if (!mAudioHandle) {
|
||||
PRUint32 drainTime = (float(mSamplesBuffered) / mRate / mChannels - GetTime()) * 1000.0;
|
||||
PR_Sleep(PR_MillisecondsToInterval(drainTime));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sa_stream_drain(reinterpret_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
|
||||
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void nsAudioStream::Pause()
|
||||
{
|
||||
if (mPaused)
|
||||
return;
|
||||
|
||||
// Save the elapsed playback time. Used to offset the wall-clock time
|
||||
// when resuming.
|
||||
mPauseTime = CurrentTimeInSeconds() - mStartTime;
|
||||
|
||||
mPaused = PR_TRUE;
|
||||
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
int64_t bytes = 0;
|
||||
#if !defined(WIN32)
|
||||
sa_stream_get_position(reinterpret_cast<sa_stream_t*>(mAudioHandle), SA_POSITION_WRITE_SOFTWARE, &bytes);
|
||||
#endif
|
||||
mSavedPauseBytes = bytes;
|
||||
|
||||
sa_stream_pause(reinterpret_cast<sa_stream_t*>(mAudioHandle));
|
||||
}
|
||||
|
||||
void nsAudioStream::Resume()
|
||||
{
|
||||
if (!mPaused)
|
||||
return;
|
||||
|
||||
// Reset the start time to the current time offset backwards by the
|
||||
// elapsed time saved when the stream paused.
|
||||
mStartTime = CurrentTimeInSeconds() - mPauseTime;
|
||||
|
||||
mPaused = PR_FALSE;
|
||||
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
sa_stream_resume(reinterpret_cast<sa_stream_t*>(mAudioHandle));
|
||||
|
||||
#if !defined(WIN32)
|
||||
mPauseBytes += mSavedPauseBytes;
|
||||
#endif
|
||||
}
|
||||
|
||||
double nsAudioStream::GetTime()
|
||||
{
|
||||
// If the audio backend failed to open, emulate the current playback
|
||||
// position using the system clock.
|
||||
if (!mAudioHandle)
|
||||
return mPaused ? mPauseTime : CurrentTimeInSeconds() - mStartTime;
|
||||
|
||||
int64_t bytes = 0;
|
||||
#if defined(WIN32)
|
||||
sa_stream_get_position(reinterpret_cast<sa_stream_t*>(mAudioHandle), SA_POSITION_WRITE_HARDWARE, &bytes);
|
||||
#else
|
||||
sa_stream_get_position(reinterpret_cast<sa_stream_t*>(mAudioHandle), SA_POSITION_WRITE_SOFTWARE, &bytes);
|
||||
#endif
|
||||
return double(bytes + mPauseBytes) / (sizeof(short) * mChannels * mRate);
|
||||
}
|
||||
|
@ -135,8 +135,12 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
|
||||
nsresult nsChannelToPipeListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
|
||||
{
|
||||
mOutput = nsnull;
|
||||
if (mDecoder) {
|
||||
mDecoder->ResourceLoaded();
|
||||
if (aStatus != NS_BINDING_ABORTED && mDecoder) {
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
mDecoder->ResourceLoaded();
|
||||
} else {
|
||||
mDecoder->NetworkError();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ class nsDefaultStreamStrategy : public nsStreamStrategy
|
||||
{
|
||||
public:
|
||||
nsDefaultStreamStrategy(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
|
||||
nsStreamStrategy(aDecoder, aChannel, aURI)
|
||||
nsStreamStrategy(aDecoder, aChannel, aURI),
|
||||
mPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -87,6 +88,10 @@ private:
|
||||
// Input stream for the media data currently downloaded
|
||||
// and stored in the pipe. This can be used from any thread.
|
||||
nsCOMPtr<nsIInputStream> mPipeInput;
|
||||
|
||||
// Current seek position. Need to compute this manually because
|
||||
// the underlying channel may not offer this information.
|
||||
PRInt64 mPosition;
|
||||
};
|
||||
|
||||
nsresult nsDefaultStreamStrategy::Open(nsIStreamListener** aStreamListener)
|
||||
@ -112,6 +117,8 @@ nsresult nsDefaultStreamStrategy::Open(nsIStreamListener** aStreamListener)
|
||||
rv = mListener->GetInputStream(getter_AddRefs(mPipeInput));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mPosition = 0;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -137,7 +144,14 @@ nsresult nsDefaultStreamStrategy::Read(char* aBuffer, PRUint32 aCount, PRUint32*
|
||||
// stream. This allows calling from any thread as the pipe is
|
||||
// threadsafe.
|
||||
nsAutoLock lock(mLock);
|
||||
return mPipeInput ? mPipeInput->Read(aBuffer, aCount, aBytes) : NS_ERROR_FAILURE;
|
||||
if (!mPipeInput)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv = mPipeInput->Read(aBuffer, aCount, aBytes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mPosition += *aBytes;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsDefaultStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
@ -148,8 +162,7 @@ nsresult nsDefaultStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
|
||||
PRInt64 nsDefaultStreamStrategy::Tell()
|
||||
{
|
||||
// Default streams cannot be seeked
|
||||
return 0;
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
PRUint32 nsDefaultStreamStrategy::Available()
|
||||
|
@ -966,7 +966,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
mReader->Available() < mBufferingBytes) {
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("Buffering data until %d bytes available or %d milliseconds",
|
||||
(long)(mBufferingBytes - mReader->Available()),
|
||||
mBufferingBytes - mReader->Available(),
|
||||
BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart))));
|
||||
mon.Wait(PR_MillisecondsToInterval(1000));
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
@ -1336,6 +1336,13 @@ void nsOggDecoder::ResourceLoaded()
|
||||
StopProgress();
|
||||
}
|
||||
|
||||
void nsOggDecoder::NetworkError()
|
||||
{
|
||||
if (mElement)
|
||||
mElement->NetworkError();
|
||||
Stop();
|
||||
}
|
||||
|
||||
PRBool nsOggDecoder::IsSeeking() const
|
||||
{
|
||||
return mPlayState == PLAY_STATE_SEEKING;
|
||||
|
1268
content/media/video/src/nsWaveDecoder.cpp
Normal file
1268
content/media/video/src/nsWaveDecoder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -66,9 +66,11 @@ _TEST_FILES = test_autoplay.html \
|
||||
test_timeupdate2.html \
|
||||
test_timeupdate3.html \
|
||||
test_volume.html \
|
||||
test_wav_ended1.html \
|
||||
320x240.ogg \
|
||||
bug461281.ogg \
|
||||
seek.ogg \
|
||||
r11025_s16_c1.wav \
|
||||
# test_bug448534.html \
|
||||
$(NULL)
|
||||
|
||||
|
BIN
content/media/video/test/r11025_s16_c1.wav
Normal file
BIN
content/media/video/test/r11025_s16_c1.wav
Normal file
Binary file not shown.
46
content/media/video/test/test_wav_ended1.html
Normal file
46
content/media/video/test/test_wav_ended1.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Media test: ended</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<audio id='v'
|
||||
onloadedmetadata='return startTest();'
|
||||
onended='return playbackEnded();'>
|
||||
<source type='audio/x-wav' src='r11025_s16_c1.wav'>
|
||||
</audio>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
// Test if the ended event works correctly.
|
||||
var v = document.getElementById('v');
|
||||
var endPassed = false;
|
||||
var completed = false;
|
||||
|
||||
function startTest() {
|
||||
if (completed)
|
||||
return false;
|
||||
|
||||
v.play();
|
||||
return false;
|
||||
}
|
||||
|
||||
function playbackEnded() {
|
||||
if (completed)
|
||||
return false
|
||||
|
||||
completed = true;
|
||||
ok(v.currentTime >= 0.9 && v.currentTime <= 1.1,
|
||||
"Checking currentTime at end: " + v.currentTime);
|
||||
ok(v.ended, "Checking playback has ended");
|
||||
SimpleTest.finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -153,10 +153,15 @@ SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/media/libogg/src/$(LIB_PREFIX)ogg.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/media/liboggplay/src/liboggplay/$(LIB_PREFIX)oggplay.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/media/liboggz/src/liboggz/$(LIB_PREFIX)oggz.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/media/libsydneyaudio/src/$(LIB_PREFIX)sydneyaudio.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/media/libtheora/lib/$(LIB_PREFIX)theora.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/media/libvorbis/lib/$(LIB_PREFIX)vorbis.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/media/libsydneyaudio/src/$(LIB_PREFIX)sydneyaudio.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
LOCAL_INCLUDES += -I$(DEPTH)/content/html/content/src
|
||||
endif
|
||||
|
||||
@ -258,7 +263,7 @@ ifneq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT)))
|
||||
EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS)
|
||||
endif
|
||||
|
||||
ifdef MOZ_OGG
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
OS_LIBS += -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
|
||||
endif
|
||||
|
@ -115,7 +115,7 @@ PRBool NS_SVGEnabled();
|
||||
#include "nsHTMLMediaElement.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_OGG
|
||||
#ifdef MOZ_SYDNEYAUDIO
|
||||
#include "nsAudioStream.h"
|
||||
#endif
|
||||
|
||||
@ -257,7 +257,7 @@ nsLayoutStatics::Initialize()
|
||||
nsHTMLMediaElement::InitMediaTypes();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_OGG
|
||||
#ifdef MOZ_SYDNEYAUDIO
|
||||
nsAudioStream::InitLibrary();
|
||||
#endif
|
||||
|
||||
@ -342,7 +342,7 @@ nsLayoutStatics::Shutdown()
|
||||
#ifdef MOZ_MEDIA
|
||||
nsHTMLMediaElement::ShutdownMediaTypes();
|
||||
#endif
|
||||
#ifdef MOZ_OGG
|
||||
#ifdef MOZ_SYDNEYAUDIO
|
||||
nsAudioStream::ShutdownLibrary();
|
||||
#endif
|
||||
|
||||
|
@ -206,7 +206,7 @@ EXTRA_DSO_LDOPTS += \
|
||||
$(TK_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_OGG
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
EXTRA_DSO_LDOPTS += \
|
||||
-framework CoreAudio \
|
||||
-framework AudioToolbox \
|
||||
|
@ -1115,6 +1115,11 @@ if [ "$MOZ_OGG" ]; then
|
||||
$MAKEFILES_libogg
|
||||
$MAKEFILES_libfishsound
|
||||
$MAKEFILES_liboggplay
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$MOZ_SYDNEYAUDIO" ]; then
|
||||
add_makefiles "
|
||||
$MAKEFILES_libsydneyaudio
|
||||
"
|
||||
fi
|
||||
|
@ -124,12 +124,17 @@ tier_gecko_dirs += \
|
||||
media/libogg \
|
||||
media/liboggplay \
|
||||
media/liboggz \
|
||||
media/libsydneyaudio \
|
||||
media/libtheora \
|
||||
media/libvorbis \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_SYDNEYAUDIO
|
||||
tier_gecko_dirs += \
|
||||
media/libsydneyaudio \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
tier_gecko_dirs += \
|
||||
uriloader \
|
||||
modules/libimg \
|
||||
|
@ -537,7 +537,8 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
|
||||
{ TEXT_CSS, "css", "Style Sheet", MAC_TYPE('TEXT'), MAC_TYPE('ttxt') },
|
||||
{ "audio/ogg", "oga", "Ogg Audio", 0, 0 },
|
||||
{ "video/ogg", "ogv", "Ogg Video", 0, 0 },
|
||||
{ "audio/ogg", "ogg", "Ogg Audio", 0, 0 }
|
||||
{ "audio/ogg", "ogg", "Ogg Audio", 0, 0 },
|
||||
{ "audio/x-wav", "wav", "Waveform Audio", 0, 0 }
|
||||
};
|
||||
|
||||
#undef MAC_TYPE
|
||||
|
Loading…
Reference in New Issue
Block a user