mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 942657 - Devirtualize AudioStream. r=doublec,gps
This commit is contained in:
parent
ded7b4c4c5
commit
93237d768e
4
config/external/moz.build
vendored
4
config/external/moz.build
vendored
@ -33,14 +33,12 @@ if CONFIG['MOZ_VP8'] and not CONFIG['MOZ_NATIVE_LIBVPX']:
|
|||||||
if CONFIG['MOZ_OGG']:
|
if CONFIG['MOZ_OGG']:
|
||||||
external_dirs += ['media/libogg', 'media/libtheora']
|
external_dirs += ['media/libogg', 'media/libtheora']
|
||||||
|
|
||||||
if CONFIG['MOZ_CUBEB']:
|
|
||||||
external_dirs += ['media/libcubeb']
|
|
||||||
|
|
||||||
if not CONFIG['MOZ_NATIVE_PNG']:
|
if not CONFIG['MOZ_NATIVE_PNG']:
|
||||||
external_dirs += ['media/libpng']
|
external_dirs += ['media/libpng']
|
||||||
|
|
||||||
external_dirs += [
|
external_dirs += [
|
||||||
'media/kiss_fft',
|
'media/kiss_fft',
|
||||||
|
'media/libcubeb',
|
||||||
'media/libspeex_resampler',
|
'media/libspeex_resampler',
|
||||||
'media/libsoundtouch',
|
'media/libsoundtouch',
|
||||||
]
|
]
|
||||||
|
29
configure.in
29
configure.in
@ -3938,7 +3938,6 @@ MOZ_JSDEBUGGER=1
|
|||||||
MOZ_AUTH_EXTENSION=1
|
MOZ_AUTH_EXTENSION=1
|
||||||
MOZ_OGG=1
|
MOZ_OGG=1
|
||||||
MOZ_RAW=
|
MOZ_RAW=
|
||||||
MOZ_CUBEB=
|
|
||||||
MOZ_VORBIS=
|
MOZ_VORBIS=
|
||||||
MOZ_TREMOR=
|
MOZ_TREMOR=
|
||||||
MOZ_WAVE=1
|
MOZ_WAVE=1
|
||||||
@ -5199,7 +5198,6 @@ MOZ_ARG_DISABLE_BOOL(ogg,
|
|||||||
|
|
||||||
if test -n "$MOZ_OGG"; then
|
if test -n "$MOZ_OGG"; then
|
||||||
AC_DEFINE(MOZ_OGG)
|
AC_DEFINE(MOZ_OGG)
|
||||||
MOZ_CUBEB=1
|
|
||||||
|
|
||||||
dnl Checks for __attribute__(aligned()) directive
|
dnl Checks for __attribute__(aligned()) directive
|
||||||
AC_CACHE_CHECK([__attribute__ ((aligned ())) support],
|
AC_CACHE_CHECK([__attribute__ ((aligned ())) support],
|
||||||
@ -5259,7 +5257,6 @@ MOZ_ARG_DISABLE_BOOL(directshow,
|
|||||||
|
|
||||||
if test -n "$MOZ_DIRECTSHOW"; then
|
if test -n "$MOZ_DIRECTSHOW"; then
|
||||||
AC_DEFINE(MOZ_DIRECTSHOW)
|
AC_DEFINE(MOZ_DIRECTSHOW)
|
||||||
MOZ_CUBEB=1
|
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
@ -5279,7 +5276,6 @@ MOZ_ARG_DISABLE_BOOL(wmf,
|
|||||||
|
|
||||||
if test -n "$MOZ_WMF"; then
|
if test -n "$MOZ_WMF"; then
|
||||||
AC_DEFINE(MOZ_WMF)
|
AC_DEFINE(MOZ_WMF)
|
||||||
MOZ_CUBEB=1
|
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
@ -5410,7 +5406,6 @@ AC_SUBST(MOZ_LIBVPX_CFLAGS)
|
|||||||
AC_SUBST(MOZ_LIBVPX_LIBS)
|
AC_SUBST(MOZ_LIBVPX_LIBS)
|
||||||
|
|
||||||
if test "$MOZ_WEBM" -o "$MOZ_OGG"; then
|
if test "$MOZ_WEBM" -o "$MOZ_OGG"; then
|
||||||
MOZ_CUBEB=1
|
|
||||||
if test "$MOZ_SAMPLE_TYPE_FLOAT32"; then
|
if test "$MOZ_SAMPLE_TYPE_FLOAT32"; then
|
||||||
MOZ_VORBIS=1
|
MOZ_VORBIS=1
|
||||||
else
|
else
|
||||||
@ -5512,17 +5507,12 @@ MOZ_ARG_DISABLE_BOOL(wave,
|
|||||||
|
|
||||||
if test -n "$MOZ_WAVE"; then
|
if test -n "$MOZ_WAVE"; then
|
||||||
AC_DEFINE(MOZ_WAVE)
|
AC_DEFINE(MOZ_WAVE)
|
||||||
MOZ_CUBEB=1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
dnl = Handle dependent CUBEB and MEDIA defines
|
dnl = Handle dependent MEDIA defines
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
|
|
||||||
if test -n "$MOZ_CUBEB"; then
|
|
||||||
AC_DEFINE(MOZ_CUBEB)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$MOZ_OPUS" -a -z "$MOZ_OGG"; then
|
if test -n "$MOZ_OPUS" -a -z "$MOZ_OGG"; then
|
||||||
AC_MSG_ERROR([MOZ_OPUS requires MOZ_OGG which is disabled.])
|
AC_MSG_ERROR([MOZ_OPUS requires MOZ_OGG which is disabled.])
|
||||||
fi
|
fi
|
||||||
@ -5552,12 +5542,12 @@ if test -n "$MOZ_OPUS"; then
|
|||||||
AC_DEFINE(MOZ_OPUS)
|
AC_DEFINE(MOZ_OPUS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dnl ====================================================
|
dnl ==================================
|
||||||
dnl = Check alsa availability on Linux if using libcubeb
|
dnl = Check alsa availability on Linux
|
||||||
dnl ====================================================
|
dnl ==================================
|
||||||
|
|
||||||
dnl If using libcubeb with Linux, ensure that the alsa library is available
|
dnl If using Linux, ensure that the alsa library is available
|
||||||
if test -n "$MOZ_CUBEB" -a "$OS_TARGET" = "Linux"; then
|
if test "$OS_TARGET" = "Linux"; then
|
||||||
MOZ_ALSA=1
|
MOZ_ALSA=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -5567,7 +5557,6 @@ MOZ_ARG_ENABLE_BOOL(alsa,
|
|||||||
MOZ_ALSA=)
|
MOZ_ALSA=)
|
||||||
|
|
||||||
if test -n "$MOZ_ALSA"; then
|
if test -n "$MOZ_ALSA"; then
|
||||||
AC_DEFINE(MOZ_CUBEB)
|
|
||||||
PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
|
PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
|
||||||
[echo "$MOZ_ALSA_PKG_ERRORS"
|
[echo "$MOZ_ALSA_PKG_ERRORS"
|
||||||
AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux. Disable with --disable-ogg --disable-wave --disable-webm. (On Ubuntu, you might try installing the package libasound2-dev.)])])
|
AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux. Disable with --disable-ogg --disable-wave --disable-webm. (On Ubuntu, you might try installing the package libasound2-dev.)])])
|
||||||
@ -5581,8 +5570,8 @@ dnl ========================================================
|
|||||||
dnl = Disable PulseAudio
|
dnl = Disable PulseAudio
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
|
|
||||||
dnl If using libcubeb with Linux, ensure that the PA library is available
|
dnl If using Linux, ensure that the PA library is available
|
||||||
if test -n "$MOZ_CUBEB" -a "$OS_TARGET" = "Linux" -a -z "$MOZ_B2G"; then
|
if test "$OS_TARGET" = "Linux" -a -z "$MOZ_B2G"; then
|
||||||
MOZ_PULSEAUDIO=1
|
MOZ_PULSEAUDIO=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -5592,7 +5581,6 @@ MOZ_ARG_DISABLE_BOOL(pulseaudio,
|
|||||||
MOZ_PULSEAUDIO=1)
|
MOZ_PULSEAUDIO=1)
|
||||||
|
|
||||||
if test -n "$MOZ_PULSEAUDIO"; then
|
if test -n "$MOZ_PULSEAUDIO"; then
|
||||||
AC_DEFINE(MOZ_CUBEB)
|
|
||||||
if test -z "$gonkdir"; then
|
if test -z "$gonkdir"; then
|
||||||
PKG_CHECK_MODULES(MOZ_PULSEAUDIO, libpulse, ,
|
PKG_CHECK_MODULES(MOZ_PULSEAUDIO, libpulse, ,
|
||||||
[echo "$MOZ_PULSEAUDIO_PKG_ERRORS"
|
[echo "$MOZ_PULSEAUDIO_PKG_ERRORS"
|
||||||
@ -8655,7 +8643,6 @@ AC_SUBST(MOZ_NSS_PATCH)
|
|||||||
AC_SUBST(MOZ_APP_COMPONENT_LIBS)
|
AC_SUBST(MOZ_APP_COMPONENT_LIBS)
|
||||||
AC_SUBST(MOZ_APP_EXTRA_LIBS)
|
AC_SUBST(MOZ_APP_EXTRA_LIBS)
|
||||||
|
|
||||||
AC_SUBST(MOZ_CUBEB)
|
|
||||||
AC_SUBST(MOZ_WAVE)
|
AC_SUBST(MOZ_WAVE)
|
||||||
AC_SUBST(MOZ_VORBIS)
|
AC_SUBST(MOZ_VORBIS)
|
||||||
AC_SUBST(MOZ_TREMOR)
|
AC_SUBST(MOZ_TREMOR)
|
||||||
|
@ -111,7 +111,7 @@ HTMLAudioElement::MozSetup(uint32_t aChannels, uint32_t aRate, ErrorResult& aRv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mAudioStream = AudioStream::AllocateStream();
|
mAudioStream = new AudioStream();
|
||||||
aRv = mAudioStream->Init(aChannels, aRate, mAudioChannelType, AudioStream::HighLatency);
|
aRv = mAudioStream->Init(aChannels, aRate, mAudioChannelType, AudioStream::HighLatency);
|
||||||
if (aRv.Failed()) {
|
if (aRv.Failed()) {
|
||||||
mAudioStream->Shutdown();
|
mAudioStream->Shutdown();
|
||||||
|
@ -16,104 +16,91 @@
|
|||||||
#include "soundtouch/SoundTouch.h"
|
#include "soundtouch/SoundTouch.h"
|
||||||
#include "Latency.h"
|
#include "Latency.h"
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
#include "nsAutoRef.h"
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
PRLogModuleInfo* gAudioStreamLog = nullptr;
|
PRLogModuleInfo* gAudioStreamLog = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PREF_VOLUME_SCALE "media.volume_scale"
|
|
||||||
#define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
|
|
||||||
|
|
||||||
static Mutex* gAudioPrefsLock = nullptr;
|
|
||||||
static double gVolumeScale;
|
|
||||||
static uint32_t gCubebLatency;
|
|
||||||
static bool gCubebLatencyPrefSet;
|
|
||||||
static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
|
|
||||||
|
|
||||||
StaticMutex AudioStream::mMutex;
|
|
||||||
uint32_t AudioStream::mPreferredSampleRate = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When MOZ_DUMP_AUDIO is set in the environment (to anything),
|
* When MOZ_DUMP_AUDIO is set in the environment (to anything),
|
||||||
* we'll drop a series of files in the current working directory named
|
* we'll drop a series of files in the current working directory named
|
||||||
* dumped-audio-<nnn>.wav, one per nsBufferedAudioStream created, containing
|
* dumped-audio-<nnn>.wav, one per AudioStream created, containing
|
||||||
* the audio for the stream including any skips due to underruns.
|
* the audio for the stream including any skips due to underruns.
|
||||||
*/
|
*/
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
static int gDumpedAudioCount = 0;
|
static int gDumpedAudioCount = 0;
|
||||||
#endif
|
|
||||||
|
|
||||||
static int PrefChanged(const char* aPref, void* aClosure)
|
#define PREF_VOLUME_SCALE "media.volume_scale"
|
||||||
|
#define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
|
||||||
|
|
||||||
|
static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
|
||||||
|
|
||||||
|
StaticMutex AudioStream::sMutex;
|
||||||
|
cubeb* AudioStream::sCubebContext;
|
||||||
|
uint32_t AudioStream::sPreferredSampleRate;
|
||||||
|
double AudioStream::sVolumeScale;
|
||||||
|
uint32_t AudioStream::sCubebLatency;
|
||||||
|
bool AudioStream::sCubebLatencyPrefSet;
|
||||||
|
|
||||||
|
/*static*/ int AudioStream::PrefChanged(const char* aPref, void* aClosure)
|
||||||
{
|
{
|
||||||
if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
|
if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
|
||||||
nsAdoptingString value = Preferences::GetString(aPref);
|
nsAdoptingString value = Preferences::GetString(aPref);
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
if (value.IsEmpty()) {
|
if (value.IsEmpty()) {
|
||||||
gVolumeScale = 1.0;
|
sVolumeScale = 1.0;
|
||||||
} else {
|
} else {
|
||||||
NS_ConvertUTF16toUTF8 utf8(value);
|
NS_ConvertUTF16toUTF8 utf8(value);
|
||||||
gVolumeScale = std::max<double>(0, PR_strtod(utf8.get(), nullptr));
|
sVolumeScale = std::max<double>(0, PR_strtod(utf8.get(), nullptr));
|
||||||
}
|
}
|
||||||
} else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
|
} else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
|
||||||
// Arbitrary default stream latency of 100ms. The higher this
|
// Arbitrary default stream latency of 100ms. The higher this
|
||||||
// value, the longer stream volume changes will take to become
|
// value, the longer stream volume changes will take to become
|
||||||
// audible.
|
// audible.
|
||||||
gCubebLatencyPrefSet = Preferences::HasUserValue(aPref);
|
sCubebLatencyPrefSet = Preferences::HasUserValue(aPref);
|
||||||
uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
|
uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
gCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
|
sCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB)
|
/*static*/ double AudioStream::GetVolumeScale()
|
||||||
static double GetVolumeScale()
|
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
return gVolumeScale;
|
return sVolumeScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
static cubeb* gCubebContext;
|
/*static*/ cubeb* AudioStream::GetCubebContext()
|
||||||
|
|
||||||
static cubeb* GetCubebContext()
|
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
if (gCubebContext ||
|
return GetCubebContextUnlocked();
|
||||||
cubeb_init(&gCubebContext, "AudioStream") == CUBEB_OK) {
|
}
|
||||||
return gCubebContext;
|
|
||||||
|
/*static*/ cubeb* AudioStream::GetCubebContextUnlocked()
|
||||||
|
{
|
||||||
|
sMutex.AssertCurrentThreadOwns();
|
||||||
|
if (sCubebContext ||
|
||||||
|
cubeb_init(&sCubebContext, "AudioStream") == CUBEB_OK) {
|
||||||
|
return sCubebContext;
|
||||||
}
|
}
|
||||||
NS_WARNING("cubeb_init failed");
|
NS_WARNING("cubeb_init failed");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t GetCubebLatency()
|
/*static*/ uint32_t AudioStream::GetCubebLatency()
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
return gCubebLatency;
|
return sCubebLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CubebLatencyPrefSet()
|
/*static*/ bool AudioStream::CubebLatencyPrefSet()
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(*gAudioPrefsLock);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
return gCubebLatencyPrefSet;
|
return sCubebLatencyPrefSet;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB) && defined(__ANDROID__) && defined(MOZ_B2G)
|
#if defined(__ANDROID__) && defined(MOZ_B2G)
|
||||||
static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannelType aType)
|
static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannelType aType)
|
||||||
{
|
{
|
||||||
switch(aType) {
|
switch(aType) {
|
||||||
@ -139,52 +126,58 @@ static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannelType aType)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioStream::AudioStream()
|
AudioStream::AudioStream()
|
||||||
: mInRate(0),
|
: mMonitor("AudioStream")
|
||||||
mOutRate(0),
|
, mInRate(0)
|
||||||
mChannels(0),
|
, mOutRate(0)
|
||||||
mWritten(0),
|
, mChannels(0)
|
||||||
mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST()),
|
, mWritten(0)
|
||||||
mLatencyRequest(HighLatency),
|
, mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST())
|
||||||
mReadPoint(0)
|
, mLatencyRequest(HighLatency)
|
||||||
{}
|
, mReadPoint(0)
|
||||||
|
, mLostFrames(0)
|
||||||
void AudioStream::InitLibrary()
|
, mDumpFile(nullptr)
|
||||||
|
, mVolume(1.0)
|
||||||
|
, mBytesPerFrame(0)
|
||||||
|
, mState(INITIALIZED)
|
||||||
{
|
{
|
||||||
#ifdef PR_LOGGING
|
// keep a ref in case we shut down later than nsLayoutStatics
|
||||||
gAudioStreamLog = PR_NewLogModule("AudioStream");
|
mLatencyLog = AsyncLatencyLogger::Get(true);
|
||||||
#endif
|
|
||||||
gAudioPrefsLock = new Mutex("AudioStream::gAudioPrefsLock");
|
|
||||||
PrefChanged(PREF_VOLUME_SCALE, nullptr);
|
|
||||||
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
PrefChanged(PREF_CUBEB_LATENCY, nullptr);
|
|
||||||
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioStream::ShutdownLibrary()
|
|
||||||
{
|
|
||||||
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
|
|
||||||
#endif
|
|
||||||
delete gAudioPrefsLock;
|
|
||||||
gAudioPrefsLock = nullptr;
|
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
if (gCubebContext) {
|
|
||||||
cubeb_destroy(gCubebContext);
|
|
||||||
gCubebContext = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioStream::~AudioStream()
|
AudioStream::~AudioStream()
|
||||||
{
|
{
|
||||||
|
Shutdown();
|
||||||
|
if (mDumpFile) {
|
||||||
|
fclose(mDumpFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ void AudioStream::InitLibrary()
|
||||||
|
{
|
||||||
|
#ifdef PR_LOGGING
|
||||||
|
gAudioStreamLog = PR_NewLogModule("AudioStream");
|
||||||
|
#endif
|
||||||
|
PrefChanged(PREF_VOLUME_SCALE, nullptr);
|
||||||
|
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
|
||||||
|
PrefChanged(PREF_CUBEB_LATENCY, nullptr);
|
||||||
|
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ void AudioStream::ShutdownLibrary()
|
||||||
|
{
|
||||||
|
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
|
||||||
|
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
|
||||||
|
|
||||||
|
StaticMutexAutoLock lock(sMutex);
|
||||||
|
if (sCubebContext) {
|
||||||
|
cubeb_destroy(sCubebContext);
|
||||||
|
sCubebContext = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult AudioStream::EnsureTimeStretcherInitialized()
|
nsresult AudioStream::EnsureTimeStretcherInitialized()
|
||||||
{
|
{
|
||||||
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (!mTimeStretcher) {
|
if (!mTimeStretcher) {
|
||||||
// SoundTouch does not support a number of channels > 2
|
// SoundTouch does not support a number of channels > 2
|
||||||
if (mChannels > 2) {
|
if (mChannels > 2) {
|
||||||
@ -254,224 +247,43 @@ int64_t AudioStream::GetWritten()
|
|||||||
return mWritten;
|
return mWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB)
|
/*static*/ int AudioStream::MaxNumberOfChannels()
|
||||||
class nsCircularByteBuffer
|
|
||||||
{
|
{
|
||||||
public:
|
cubeb* cubebContext = GetCubebContext();
|
||||||
nsCircularByteBuffer()
|
|
||||||
: mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Set the capacity of the buffer in bytes. Must be called before any
|
|
||||||
// call to append or pop elements.
|
|
||||||
void SetCapacity(uint32_t aCapacity) {
|
|
||||||
NS_ABORT_IF_FALSE(!mBuffer, "Buffer allocated.");
|
|
||||||
mCapacity = aCapacity;
|
|
||||||
mBuffer = new uint8_t[mCapacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Length() {
|
|
||||||
return mCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Capacity() {
|
|
||||||
return mCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Available() {
|
|
||||||
return Capacity() - Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append aLength bytes from aSrc to the buffer. Caller must check that
|
|
||||||
// sufficient space is available.
|
|
||||||
void AppendElements(const uint8_t* aSrc, uint32_t aLength) {
|
|
||||||
NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
|
|
||||||
NS_ABORT_IF_FALSE(aLength <= Available(), "Buffer full.");
|
|
||||||
|
|
||||||
uint32_t end = (mStart + mCount) % mCapacity;
|
|
||||||
|
|
||||||
uint32_t toCopy = std::min(mCapacity - end, aLength);
|
|
||||||
memcpy(&mBuffer[end], aSrc, toCopy);
|
|
||||||
memcpy(&mBuffer[0], aSrc + toCopy, aLength - toCopy);
|
|
||||||
mCount += aLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove aSize bytes from the buffer. Caller must check returned size in
|
|
||||||
// aSize{1,2} before using the pointer returned in aData{1,2}. Caller
|
|
||||||
// must not specify an aSize larger than Length().
|
|
||||||
void PopElements(uint32_t aSize, void** aData1, uint32_t* aSize1,
|
|
||||||
void** aData2, uint32_t* aSize2) {
|
|
||||||
NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
|
|
||||||
NS_ABORT_IF_FALSE(aSize <= Length(), "Request too large.");
|
|
||||||
|
|
||||||
*aData1 = &mBuffer[mStart];
|
|
||||||
*aSize1 = std::min(mCapacity - mStart, aSize);
|
|
||||||
*aData2 = &mBuffer[0];
|
|
||||||
*aSize2 = aSize - *aSize1;
|
|
||||||
mCount -= *aSize1 + *aSize2;
|
|
||||||
mStart += *aSize1 + *aSize2;
|
|
||||||
mStart %= mCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsAutoArrayPtr<uint8_t> mBuffer;
|
|
||||||
uint32_t mCapacity;
|
|
||||||
uint32_t mStart;
|
|
||||||
uint32_t mCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BufferedAudioStream : public AudioStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BufferedAudioStream();
|
|
||||||
~BufferedAudioStream();
|
|
||||||
|
|
||||||
nsresult Init(int32_t aNumChannels, int32_t aRate,
|
|
||||||
const dom::AudioChannelType aAudioChannelType,
|
|
||||||
AudioStream::LatencyRequest aLatencyRequest);
|
|
||||||
void Shutdown();
|
|
||||||
nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr);
|
|
||||||
uint32_t Available();
|
|
||||||
void SetVolume(double aVolume);
|
|
||||||
void Drain();
|
|
||||||
void Start();
|
|
||||||
void Pause();
|
|
||||||
void Resume();
|
|
||||||
int64_t GetPosition();
|
|
||||||
int64_t GetPositionInFrames();
|
|
||||||
int64_t GetPositionInFramesInternal();
|
|
||||||
int64_t GetLatencyInFrames();
|
|
||||||
bool IsPaused();
|
|
||||||
void GetBufferInsertTime(int64_t &aTimeMs);
|
|
||||||
// This method acquires the monitor and forward the call to the base
|
|
||||||
// class, to prevent a race on |mTimeStretcher|, in
|
|
||||||
// |AudioStream::EnsureTimeStretcherInitialized|.
|
|
||||||
nsresult EnsureTimeStretcherInitialized();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
|
|
||||||
{
|
|
||||||
return static_cast<BufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
|
|
||||||
{
|
|
||||||
static_cast<BufferedAudioStream*>(aThis)->StateCallback(aState);
|
|
||||||
}
|
|
||||||
|
|
||||||
long DataCallback(void* aBuffer, long aFrames);
|
|
||||||
void StateCallback(cubeb_state aState);
|
|
||||||
|
|
||||||
// aTime is the time in ms the samples were inserted into MediaStreamGraph
|
|
||||||
long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
|
|
||||||
long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
|
|
||||||
long GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t &aTime);
|
|
||||||
|
|
||||||
// Shared implementation of underflow adjusted position calculation.
|
|
||||||
// Caller must own the monitor.
|
|
||||||
int64_t GetPositionInFramesUnlocked();
|
|
||||||
|
|
||||||
void StartUnlocked();
|
|
||||||
|
|
||||||
// The monitor is held to protect all access to member variables. Write()
|
|
||||||
// waits while mBuffer is full; DataCallback() notifies as it consumes
|
|
||||||
// data from mBuffer. Drain() waits while mState is DRAINING;
|
|
||||||
// StateCallback() notifies when mState is DRAINED.
|
|
||||||
Monitor mMonitor;
|
|
||||||
|
|
||||||
// Sum of silent frames written when DataCallback requests more frames
|
|
||||||
// than are available in mBuffer.
|
|
||||||
uint64_t mLostFrames;
|
|
||||||
|
|
||||||
// Output file for dumping audio
|
|
||||||
FILE* mDumpFile;
|
|
||||||
|
|
||||||
// Temporary audio buffer. Filled by Write() and consumed by
|
|
||||||
// DataCallback(). Once mBuffer is full, Write() blocks until sufficient
|
|
||||||
// space becomes available in mBuffer. mBuffer is sized in bytes, not
|
|
||||||
// frames.
|
|
||||||
nsCircularByteBuffer mBuffer;
|
|
||||||
|
|
||||||
// Software volume level. Applied during the servicing of DataCallback().
|
|
||||||
double mVolume;
|
|
||||||
|
|
||||||
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
|
|
||||||
// nsAutoRef's destructor.
|
|
||||||
nsAutoRef<cubeb_stream> mCubebStream;
|
|
||||||
|
|
||||||
uint32_t mBytesPerFrame;
|
|
||||||
|
|
||||||
uint32_t BytesToFrames(uint32_t aBytes) {
|
|
||||||
NS_ASSERTION(aBytes % mBytesPerFrame == 0,
|
|
||||||
"Byte count not aligned on frames size.");
|
|
||||||
return aBytes / mBytesPerFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t FramesToBytes(uint32_t aFrames) {
|
|
||||||
return aFrames * mBytesPerFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum StreamState {
|
|
||||||
INITIALIZED, // Initialized, playback has not begun.
|
|
||||||
STARTED, // Started by a call to Write() (iff INITIALIZED) or Resume().
|
|
||||||
STOPPED, // Stopped by a call to Pause().
|
|
||||||
DRAINING, // Drain requested. DataCallback will indicate end of stream
|
|
||||||
// once the remaining contents of mBuffer are requested by
|
|
||||||
// cubeb, after which StateCallback will indicate drain
|
|
||||||
// completion.
|
|
||||||
DRAINED, // StateCallback has indicated that the drain is complete.
|
|
||||||
ERRORED // Stream disabled due to an internal error.
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamState mState;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AudioStream* AudioStream::AllocateStream()
|
|
||||||
{
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
return new BufferedAudioStream();
|
|
||||||
#endif
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioStream::MaxNumberOfChannels()
|
|
||||||
{
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
uint32_t maxNumberOfChannels;
|
uint32_t maxNumberOfChannels;
|
||||||
|
if (cubebContext &&
|
||||||
if (cubeb_get_max_channel_count(GetCubebContext(),
|
cubeb_get_max_channel_count(cubebContext,
|
||||||
&maxNumberOfChannels) == CUBEB_OK) {
|
&maxNumberOfChannels) == CUBEB_OK) {
|
||||||
return static_cast<int>(maxNumberOfChannels);
|
return static_cast<int>(maxNumberOfChannels);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioStream::PreferredSampleRate()
|
/*static*/ int AudioStream::PreferredSampleRate()
|
||||||
{
|
{
|
||||||
StaticMutexAutoLock lock(AudioStream::mMutex);
|
const int fallbackSampleRate = 44100;
|
||||||
|
StaticMutexAutoLock lock(sMutex);
|
||||||
|
if (sPreferredSampleRate != 0) {
|
||||||
|
return sPreferredSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb* cubebContext = GetCubebContextUnlocked();
|
||||||
|
if (!cubebContext) {
|
||||||
|
sPreferredSampleRate = fallbackSampleRate;
|
||||||
|
}
|
||||||
// Get the preferred samplerate for this platform, or fallback to something
|
// Get the preferred samplerate for this platform, or fallback to something
|
||||||
// sensible if we fail. We cache the value, because this might be accessed
|
// sensible if we fail. We cache the value, because this might be accessed
|
||||||
// often, and the complexity of the function call below depends on the
|
// often, and the complexity of the function call below depends on the
|
||||||
// backend used.
|
// backend used.
|
||||||
const int fallbackSampleRate = 44100;
|
if (cubeb_get_preferred_sample_rate(cubebContext,
|
||||||
if (mPreferredSampleRate == 0) {
|
&sPreferredSampleRate) != CUBEB_OK) {
|
||||||
#if defined(MOZ_CUBEB)
|
sPreferredSampleRate = fallbackSampleRate;
|
||||||
if (cubeb_get_preferred_sample_rate(GetCubebContext(),
|
|
||||||
&mPreferredSampleRate) == CUBEB_OK) {
|
|
||||||
return mPreferredSampleRate;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
mPreferredSampleRate = fallbackSampleRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mPreferredSampleRate;
|
return sPreferredSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MOZ_CUBEB)
|
|
||||||
static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
|
static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
|
||||||
{
|
{
|
||||||
aDest[0] = aValue & 0xFF;
|
aDest[0] = aValue & 0xFF;
|
||||||
@ -541,33 +353,10 @@ WriteDumpFile(FILE* aDumpFile, AudioStream* aStream, uint32_t aFrames,
|
|||||||
fflush(aDumpFile);
|
fflush(aDumpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedAudioStream::BufferedAudioStream()
|
|
||||||
: mMonitor("BufferedAudioStream"), mLostFrames(0), mDumpFile(nullptr),
|
|
||||||
mVolume(1.0), mBytesPerFrame(0), mState(INITIALIZED)
|
|
||||||
{
|
|
||||||
// keep a ref in case we shut down later than nsLayoutStatics
|
|
||||||
mLatencyLog = AsyncLatencyLogger::Get(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedAudioStream::~BufferedAudioStream()
|
|
||||||
{
|
|
||||||
Shutdown();
|
|
||||||
if (mDumpFile) {
|
|
||||||
fclose(mDumpFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
BufferedAudioStream::EnsureTimeStretcherInitialized()
|
AudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
||||||
{
|
const dom::AudioChannelType aAudioChannelType,
|
||||||
MonitorAutoLock mon(mMonitor);
|
LatencyRequest aLatencyRequest)
|
||||||
return AudioStream::EnsureTimeStretcherInitialized();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|
||||||
const dom::AudioChannelType aAudioChannelType,
|
|
||||||
AudioStream::LatencyRequest aLatencyRequest)
|
|
||||||
{
|
{
|
||||||
cubeb* cubebContext = GetCubebContext();
|
cubeb* cubebContext = GetCubebContext();
|
||||||
|
|
||||||
@ -610,7 +399,7 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||||||
// for low latency playback, try to get the lowest latency possible.
|
// for low latency playback, try to get the lowest latency possible.
|
||||||
// Otherwise, for normal streams, use 100ms.
|
// Otherwise, for normal streams, use 100ms.
|
||||||
uint32_t latency;
|
uint32_t latency;
|
||||||
if (aLatencyRequest == AudioStream::LowLatency && !CubebLatencyPrefSet()) {
|
if (aLatencyRequest == LowLatency && !CubebLatencyPrefSet()) {
|
||||||
if (cubeb_get_min_latency(cubebContext, params, &latency) != CUBEB_OK) {
|
if (cubeb_get_min_latency(cubebContext, params, &latency) != CUBEB_OK) {
|
||||||
latency = GetCubebLatency();
|
latency = GetCubebLatency();
|
||||||
}
|
}
|
||||||
@ -620,7 +409,7 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||||||
|
|
||||||
{
|
{
|
||||||
cubeb_stream* stream;
|
cubeb_stream* stream;
|
||||||
if (cubeb_stream_init(cubebContext, &stream, "BufferedAudioStream", params,
|
if (cubeb_stream_init(cubebContext, &stream, "AudioStream", params,
|
||||||
latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
|
latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
|
||||||
mCubebStream.own(stream);
|
mCubebStream.own(stream);
|
||||||
}
|
}
|
||||||
@ -639,8 +428,8 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||||||
|
|
||||||
// Start the stream right away when low latency has been requested. This means
|
// Start the stream right away when low latency has been requested. This means
|
||||||
// that the DataCallback will feed silence to cubeb, until the first frames
|
// that the DataCallback will feed silence to cubeb, until the first frames
|
||||||
// are writtent to this BufferedAudioStream.
|
// are writtent to this AudioStream.
|
||||||
if (mLatencyRequest == AudioStream::LowLatency) {
|
if (mLatencyRequest == LowLatency) {
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,7 +437,7 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::Shutdown()
|
AudioStream::Shutdown()
|
||||||
{
|
{
|
||||||
if (mState == STARTED) {
|
if (mState == STARTED) {
|
||||||
Pause();
|
Pause();
|
||||||
@ -660,7 +449,7 @@ BufferedAudioStream::Shutdown()
|
|||||||
|
|
||||||
// aTime is the time in ms the samples were inserted into MediaStreamGraph
|
// aTime is the time in ms the samples were inserted into MediaStreamGraph
|
||||||
nsresult
|
nsresult
|
||||||
BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime)
|
AudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime)
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (!mCubebStream || mState == ERRORED) {
|
if (!mCubebStream || mState == ERRORED) {
|
||||||
@ -715,7 +504,7 @@ BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
BufferedAudioStream::Available()
|
AudioStream::Available()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
|
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
|
||||||
@ -723,7 +512,7 @@ BufferedAudioStream::Available()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::SetVolume(double aVolume)
|
AudioStream::SetVolume(double aVolume)
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
|
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
|
||||||
@ -731,7 +520,7 @@ BufferedAudioStream::SetVolume(double aVolume)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::Drain()
|
AudioStream::Drain()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (mState != STARTED) {
|
if (mState != STARTED) {
|
||||||
@ -745,14 +534,14 @@ BufferedAudioStream::Drain()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::Start()
|
AudioStream::Start()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
StartUnlocked();
|
StartUnlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::StartUnlocked()
|
AudioStream::StartUnlocked()
|
||||||
{
|
{
|
||||||
mMonitor.AssertCurrentThreadOwns();
|
mMonitor.AssertCurrentThreadOwns();
|
||||||
if (!mCubebStream || mState != INITIALIZED) {
|
if (!mCubebStream || mState != INITIALIZED) {
|
||||||
@ -771,7 +560,7 @@ BufferedAudioStream::StartUnlocked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::Pause()
|
AudioStream::Pause()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (!mCubebStream || mState != STARTED) {
|
if (!mCubebStream || mState != STARTED) {
|
||||||
@ -789,7 +578,7 @@ BufferedAudioStream::Pause()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::Resume()
|
AudioStream::Resume()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (!mCubebStream || mState != STOPPED) {
|
if (!mCubebStream || mState != STOPPED) {
|
||||||
@ -807,7 +596,7 @@ BufferedAudioStream::Resume()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
BufferedAudioStream::GetPosition()
|
AudioStream::GetPosition()
|
||||||
{
|
{
|
||||||
return mAudioClock.GetPosition();
|
return mAudioClock.GetPosition();
|
||||||
}
|
}
|
||||||
@ -817,7 +606,7 @@ BufferedAudioStream::GetPosition()
|
|||||||
#pragma optimize("", off)
|
#pragma optimize("", off)
|
||||||
#endif
|
#endif
|
||||||
int64_t
|
int64_t
|
||||||
BufferedAudioStream::GetPositionInFrames()
|
AudioStream::GetPositionInFrames()
|
||||||
{
|
{
|
||||||
return mAudioClock.GetPositionInFrames();
|
return mAudioClock.GetPositionInFrames();
|
||||||
}
|
}
|
||||||
@ -826,14 +615,14 @@ BufferedAudioStream::GetPositionInFrames()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
BufferedAudioStream::GetPositionInFramesInternal()
|
AudioStream::GetPositionInFramesInternal()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
return GetPositionInFramesUnlocked();
|
return GetPositionInFramesUnlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
BufferedAudioStream::GetPositionInFramesUnlocked()
|
AudioStream::GetPositionInFramesUnlocked()
|
||||||
{
|
{
|
||||||
mMonitor.AssertCurrentThreadOwns();
|
mMonitor.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
@ -859,10 +648,10 @@ BufferedAudioStream::GetPositionInFramesUnlocked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
BufferedAudioStream::GetLatencyInFrames()
|
AudioStream::GetLatencyInFrames()
|
||||||
{
|
{
|
||||||
uint32_t latency;
|
uint32_t latency;
|
||||||
if(cubeb_stream_get_latency(mCubebStream, &latency)) {
|
if (cubeb_stream_get_latency(mCubebStream, &latency)) {
|
||||||
NS_WARNING("Could not get cubeb latency.");
|
NS_WARNING("Could not get cubeb latency.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -870,14 +659,14 @@ BufferedAudioStream::GetLatencyInFrames()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BufferedAudioStream::IsPaused()
|
AudioStream::IsPaused()
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
return mState == STOPPED;
|
return mState == STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::GetBufferInsertTime(int64_t &aTimeMs)
|
AudioStream::GetBufferInsertTime(int64_t &aTimeMs)
|
||||||
{
|
{
|
||||||
if (mInserts.Length() > 0) {
|
if (mInserts.Length() > 0) {
|
||||||
// Find the right block, but don't leave the array empty
|
// Find the right block, but don't leave the array empty
|
||||||
@ -894,7 +683,7 @@ BufferedAudioStream::GetBufferInsertTime(int64_t &aTimeMs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
long
|
long
|
||||||
BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs)
|
AudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs)
|
||||||
{
|
{
|
||||||
uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
|
uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
|
||||||
|
|
||||||
@ -925,7 +714,7 @@ BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeM
|
|||||||
// Get unprocessed samples, and pad the beginning of the buffer with silence if
|
// Get unprocessed samples, and pad the beginning of the buffer with silence if
|
||||||
// there is not enough data.
|
// there is not enough data.
|
||||||
long
|
long
|
||||||
BufferedAudioStream::GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t& aTimeMs)
|
AudioStream::GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t& aTimeMs)
|
||||||
{
|
{
|
||||||
uint32_t toPopBytes = FramesToBytes(aFrames);
|
uint32_t toPopBytes = FramesToBytes(aFrames);
|
||||||
uint32_t available = std::min(toPopBytes, mBuffer.Length());
|
uint32_t available = std::min(toPopBytes, mBuffer.Length());
|
||||||
@ -949,12 +738,12 @@ BufferedAudioStream::GetUnprocessedWithSilencePadding(void* aBuffer, long aFrame
|
|||||||
}
|
}
|
||||||
|
|
||||||
long
|
long
|
||||||
BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
|
AudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
|
||||||
{
|
{
|
||||||
long processedFrames = 0;
|
long processedFrames = 0;
|
||||||
|
|
||||||
// We need to call the non-locking version, because we already have the lock.
|
// We need to call the non-locking version, because we already have the lock.
|
||||||
if (AudioStream::EnsureTimeStretcherInitialized() != NS_OK) {
|
if (EnsureTimeStretcherInitialized() != NS_OK) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,7 +779,7 @@ BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTim
|
|||||||
}
|
}
|
||||||
|
|
||||||
long
|
long
|
||||||
BufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
|
AudioStream::DataCallback(void* aBuffer, long aFrames)
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
|
uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
|
||||||
@ -1006,7 +795,7 @@ BufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
|
|||||||
// underrun at the beginning of the buffer, so the first buffer is not cut
|
// underrun at the beginning of the buffer, so the first buffer is not cut
|
||||||
// in half by the silence inserted to compensate for the underrun.
|
// in half by the silence inserted to compensate for the underrun.
|
||||||
if (mInRate == mOutRate) {
|
if (mInRate == mOutRate) {
|
||||||
if (mLatencyRequest == AudioStream::LowLatency && !mWritten) {
|
if (mLatencyRequest == LowLatency && !mWritten) {
|
||||||
servicedFrames = GetUnprocessedWithSilencePadding(output, aFrames, insertTime);
|
servicedFrames = GetUnprocessedWithSilencePadding(output, aFrames, insertTime);
|
||||||
} else {
|
} else {
|
||||||
servicedFrames = GetUnprocessed(output, aFrames, insertTime);
|
servicedFrames = GetUnprocessed(output, aFrames, insertTime);
|
||||||
@ -1060,7 +849,7 @@ BufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BufferedAudioStream::StateCallback(cubeb_state aState)
|
AudioStream::StateCallback(cubeb_state aState)
|
||||||
{
|
{
|
||||||
MonitorAutoLock mon(mMonitor);
|
MonitorAutoLock mon(mMonitor);
|
||||||
if (aState == CUBEB_STATE_DRAINED) {
|
if (aState == CUBEB_STATE_DRAINED) {
|
||||||
@ -1071,8 +860,6 @@ BufferedAudioStream::StateCallback(cubeb_state aState)
|
|||||||
mon.NotifyAll();
|
mon.NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AudioClock::AudioClock(AudioStream* aStream)
|
AudioClock::AudioClock(AudioStream* aStream)
|
||||||
:mAudioStream(aStream),
|
:mAudioStream(aStream),
|
||||||
mOldOutRate(0),
|
mOldOutRate(0),
|
||||||
|
@ -9,10 +9,20 @@
|
|||||||
#include "AudioSampleFormat.h"
|
#include "AudioSampleFormat.h"
|
||||||
#include "AudioChannelCommon.h"
|
#include "AudioChannelCommon.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
#include "nsAutoRef.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "Latency.h"
|
#include "Latency.h"
|
||||||
#include "mozilla/StaticMutex.h"
|
#include "mozilla/StaticMutex.h"
|
||||||
|
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
|
||||||
|
};
|
||||||
|
|
||||||
namespace soundtouch {
|
namespace soundtouch {
|
||||||
class SoundTouch;
|
class SoundTouch;
|
||||||
}
|
}
|
||||||
@ -23,84 +33,141 @@ class AudioStream;
|
|||||||
|
|
||||||
class AudioClock
|
class AudioClock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioClock(mozilla::AudioStream* aStream);
|
AudioClock(AudioStream* aStream);
|
||||||
// Initialize the clock with the current AudioStream. Need to be called
|
// Initialize the clock with the current AudioStream. Need to be called
|
||||||
// before querying the clock. Called on the audio thread.
|
// before querying the clock. Called on the audio thread.
|
||||||
void Init();
|
void Init();
|
||||||
// Update the number of samples that has been written in the audio backend.
|
// Update the number of samples that has been written in the audio backend.
|
||||||
// Called on the state machine thread.
|
// Called on the state machine thread.
|
||||||
void UpdateWritePosition(uint32_t aCount);
|
void UpdateWritePosition(uint32_t aCount);
|
||||||
// Get the read position of the stream, in microseconds.
|
// Get the read position of the stream, in microseconds.
|
||||||
// Called on the state machine thead.
|
// Called on the state machine thead.
|
||||||
uint64_t GetPosition();
|
uint64_t GetPosition();
|
||||||
// Get the read position of the stream, in frames.
|
// Get the read position of the stream, in frames.
|
||||||
// Called on the state machine thead.
|
// Called on the state machine thead.
|
||||||
uint64_t GetPositionInFrames();
|
uint64_t GetPositionInFrames();
|
||||||
// Set the playback rate.
|
// Set the playback rate.
|
||||||
// Called on the audio thread.
|
// Called on the audio thread.
|
||||||
void SetPlaybackRate(double aPlaybackRate);
|
void SetPlaybackRate(double aPlaybackRate);
|
||||||
// Get the current playback rate.
|
// Get the current playback rate.
|
||||||
// Called on the audio thread.
|
// Called on the audio thread.
|
||||||
double GetPlaybackRate();
|
double GetPlaybackRate();
|
||||||
// Set if we are preserving the pitch.
|
// Set if we are preserving the pitch.
|
||||||
// Called on the audio thread.
|
// Called on the audio thread.
|
||||||
void SetPreservesPitch(bool aPreservesPitch);
|
void SetPreservesPitch(bool aPreservesPitch);
|
||||||
// Get the current pitch preservation state.
|
// Get the current pitch preservation state.
|
||||||
// Called on the audio thread.
|
// Called on the audio thread.
|
||||||
bool GetPreservesPitch();
|
bool GetPreservesPitch();
|
||||||
// Get the number of frames written to the backend.
|
// Get the number of frames written to the backend.
|
||||||
int64_t GetWritten();
|
int64_t GetWritten();
|
||||||
private:
|
private:
|
||||||
// This AudioStream holds a strong reference to this AudioClock. This
|
// This AudioStream holds a strong reference to this AudioClock. This
|
||||||
// pointer is garanteed to always be valid.
|
// pointer is garanteed to always be valid.
|
||||||
AudioStream* mAudioStream;
|
AudioStream* mAudioStream;
|
||||||
// The old output rate, to compensate audio latency for the period inbetween
|
// The old output rate, to compensate audio latency for the period inbetween
|
||||||
// the moment resampled buffers are pushed to the hardware and the moment the
|
// the moment resampled buffers are pushed to the hardware and the moment the
|
||||||
// clock should take the new rate into account for A/V sync.
|
// clock should take the new rate into account for A/V sync.
|
||||||
int mOldOutRate;
|
int mOldOutRate;
|
||||||
// Position at which the last playback rate change occured
|
// Position at which the last playback rate change occured
|
||||||
int64_t mBasePosition;
|
int64_t mBasePosition;
|
||||||
// Offset, in frames, at which the last playback rate change occured
|
// Offset, in frames, at which the last playback rate change occured
|
||||||
int64_t mBaseOffset;
|
int64_t mBaseOffset;
|
||||||
// Old base offset (number of samples), used when changing rate to compute the
|
// Old base offset (number of samples), used when changing rate to compute the
|
||||||
// position in the stream.
|
// position in the stream.
|
||||||
int64_t mOldBaseOffset;
|
int64_t mOldBaseOffset;
|
||||||
// Old base position (number of microseconds), when changing rate. This is the
|
// Old base position (number of microseconds), when changing rate. This is the
|
||||||
// time in the media, not wall clock position.
|
// time in the media, not wall clock position.
|
||||||
int64_t mOldBasePosition;
|
int64_t mOldBasePosition;
|
||||||
// Write position at which the playbackRate change occured.
|
// Write position at which the playbackRate change occured.
|
||||||
int64_t mPlaybackRateChangeOffset;
|
int64_t mPlaybackRateChangeOffset;
|
||||||
// The previous position reached in the media, used when compensating
|
// The previous position reached in the media, used when compensating
|
||||||
// latency, to have the position at which the playbackRate change occured.
|
// latency, to have the position at which the playbackRate change occured.
|
||||||
int64_t mPreviousPosition;
|
int64_t mPreviousPosition;
|
||||||
// Number of samples effectivelly written in backend, i.e. write position.
|
// Number of samples effectivelly written in backend, i.e. write position.
|
||||||
int64_t mWritten;
|
int64_t mWritten;
|
||||||
// Output rate in Hz (characteristic of the playback rate)
|
// Output rate in Hz (characteristic of the playback rate)
|
||||||
int mOutRate;
|
int mOutRate;
|
||||||
// Input rate in Hz (characteristic of the media being played)
|
// Input rate in Hz (characteristic of the media being played)
|
||||||
int mInRate;
|
int mInRate;
|
||||||
// True if the we are timestretching, false if we are resampling.
|
// True if the we are timestretching, false if we are resampling.
|
||||||
bool mPreservesPitch;
|
bool mPreservesPitch;
|
||||||
// True if we are playing at the old playbackRate after it has been changed.
|
// True if we are playing at the old playbackRate after it has been changed.
|
||||||
bool mCompensatingLatency;
|
bool mCompensatingLatency;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CircularByteBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CircularByteBuffer()
|
||||||
|
: mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Set the capacity of the buffer in bytes. Must be called before any
|
||||||
|
// call to append or pop elements.
|
||||||
|
void SetCapacity(uint32_t aCapacity) {
|
||||||
|
NS_ABORT_IF_FALSE(!mBuffer, "Buffer allocated.");
|
||||||
|
mCapacity = aCapacity;
|
||||||
|
mBuffer = new uint8_t[mCapacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Length() {
|
||||||
|
return mCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Capacity() {
|
||||||
|
return mCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Available() {
|
||||||
|
return Capacity() - Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append aLength bytes from aSrc to the buffer. Caller must check that
|
||||||
|
// sufficient space is available.
|
||||||
|
void AppendElements(const uint8_t* aSrc, uint32_t aLength) {
|
||||||
|
NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
|
||||||
|
NS_ABORT_IF_FALSE(aLength <= Available(), "Buffer full.");
|
||||||
|
|
||||||
|
uint32_t end = (mStart + mCount) % mCapacity;
|
||||||
|
|
||||||
|
uint32_t toCopy = std::min(mCapacity - end, aLength);
|
||||||
|
memcpy(&mBuffer[end], aSrc, toCopy);
|
||||||
|
memcpy(&mBuffer[0], aSrc + toCopy, aLength - toCopy);
|
||||||
|
mCount += aLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove aSize bytes from the buffer. Caller must check returned size in
|
||||||
|
// aSize{1,2} before using the pointer returned in aData{1,2}. Caller
|
||||||
|
// must not specify an aSize larger than Length().
|
||||||
|
void PopElements(uint32_t aSize, void** aData1, uint32_t* aSize1,
|
||||||
|
void** aData2, uint32_t* aSize2) {
|
||||||
|
NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
|
||||||
|
NS_ABORT_IF_FALSE(aSize <= Length(), "Request too large.");
|
||||||
|
|
||||||
|
*aData1 = &mBuffer[mStart];
|
||||||
|
*aSize1 = std::min(mCapacity - mStart, aSize);
|
||||||
|
*aData2 = &mBuffer[0];
|
||||||
|
*aSize2 = aSize - *aSize1;
|
||||||
|
mCount -= *aSize1 + *aSize2;
|
||||||
|
mStart += *aSize1 + *aSize2;
|
||||||
|
mStart %= mCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsAutoArrayPtr<uint8_t> mBuffer;
|
||||||
|
uint32_t mCapacity;
|
||||||
|
uint32_t mStart;
|
||||||
|
uint32_t mCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Access to a single instance of this class must be synchronized by
|
// Access to a single instance of this class must be synchronized by
|
||||||
// callers, or made from a single thread. One exception is that access to
|
// callers, or made from a single thread. One exception is that access to
|
||||||
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
|
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
|
||||||
// is thread-safe without external synchronization.
|
// is thread-safe without external synchronization.
|
||||||
class AudioStream
|
class AudioStream MOZ_FINAL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum LatencyRequest {
|
|
||||||
HighLatency,
|
|
||||||
LowLatency
|
|
||||||
};
|
|
||||||
AudioStream();
|
|
||||||
|
|
||||||
virtual ~AudioStream();
|
|
||||||
|
|
||||||
// Initialize Audio Library. Some Audio backends require initializing the
|
// Initialize Audio Library. Some Audio backends require initializing the
|
||||||
// library before using it.
|
// library before using it.
|
||||||
static void InitLibrary();
|
static void InitLibrary();
|
||||||
@ -109,11 +176,6 @@ public:
|
|||||||
// library after using it.
|
// library after using it.
|
||||||
static void ShutdownLibrary();
|
static void ShutdownLibrary();
|
||||||
|
|
||||||
// AllocateStream will return either a local stream or a remoted stream
|
|
||||||
// depending on where you call it from. If you call this from a child process,
|
|
||||||
// you may receive an implementation which forwards to a compositing process.
|
|
||||||
static AudioStream* AllocateStream();
|
|
||||||
|
|
||||||
// Returns the maximum number of channels supported by the audio hardware.
|
// Returns the maximum number of channels supported by the audio hardware.
|
||||||
static int MaxNumberOfChannels();
|
static int MaxNumberOfChannels();
|
||||||
|
|
||||||
@ -121,79 +183,122 @@ public:
|
|||||||
// samplerate the hardware/mixer supports.
|
// samplerate the hardware/mixer supports.
|
||||||
static int PreferredSampleRate();
|
static int PreferredSampleRate();
|
||||||
|
|
||||||
|
AudioStream();
|
||||||
|
~AudioStream();
|
||||||
|
|
||||||
|
enum LatencyRequest {
|
||||||
|
HighLatency,
|
||||||
|
LowLatency
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the audio stream. aNumChannels is the number of audio
|
// Initialize the audio stream. aNumChannels is the number of audio
|
||||||
// channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
|
// channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
|
||||||
// (22050Hz, 44100Hz, etc).
|
// (22050Hz, 44100Hz, etc).
|
||||||
virtual nsresult Init(int32_t aNumChannels, int32_t aRate,
|
nsresult Init(int32_t aNumChannels, int32_t aRate,
|
||||||
const dom::AudioChannelType aAudioStreamType,
|
const dom::AudioChannelType aAudioStreamType,
|
||||||
LatencyRequest aLatencyRequest) = 0;
|
LatencyRequest aLatencyRequest);
|
||||||
|
|
||||||
// Closes the stream. All future use of the stream is an error.
|
// Closes the stream. All future use of the stream is an error.
|
||||||
virtual void Shutdown() = 0;
|
void Shutdown();
|
||||||
|
|
||||||
// Write audio data to the audio hardware. aBuf is an array of AudioDataValues
|
// Write audio data to the audio hardware. aBuf is an array of AudioDataValues
|
||||||
// AudioDataValue of length aFrames*mChannels. If aFrames is larger
|
// AudioDataValue of length aFrames*mChannels. If aFrames is larger
|
||||||
// than the result of Available(), the write will block until sufficient
|
// than the result of Available(), the write will block until sufficient
|
||||||
// buffer space is available. aTime is the time in ms associated with the first sample
|
// buffer space is available. aTime is the time in ms associated with the first sample
|
||||||
// for latency calculations
|
// for latency calculations
|
||||||
virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr) = 0;
|
nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp* aTime = nullptr);
|
||||||
|
|
||||||
// Return the number of audio frames that can be written without blocking.
|
// Return the number of audio frames that can be written without blocking.
|
||||||
virtual uint32_t Available() = 0;
|
uint32_t Available();
|
||||||
|
|
||||||
// Set the current volume of the audio playback. This is a value from
|
// Set the current volume of the audio playback. This is a value from
|
||||||
// 0 (meaning muted) to 1 (meaning full volume). Thread-safe.
|
// 0 (meaning muted) to 1 (meaning full volume). Thread-safe.
|
||||||
virtual void SetVolume(double aVolume) = 0;
|
void SetVolume(double aVolume);
|
||||||
|
|
||||||
// Block until buffered audio data has been consumed.
|
// Block until buffered audio data has been consumed.
|
||||||
virtual void Drain() = 0;
|
void Drain();
|
||||||
|
|
||||||
// Start the stream.
|
// Start the stream.
|
||||||
virtual void Start() = 0;
|
void Start();
|
||||||
|
|
||||||
// Return the number of frames written so far in the stream. This allow the
|
// Return the number of frames written so far in the stream. This allow the
|
||||||
// caller to check if it is safe to start the stream, if needed.
|
// caller to check if it is safe to start the stream, if needed.
|
||||||
virtual int64_t GetWritten();
|
int64_t GetWritten();
|
||||||
|
|
||||||
// Pause audio playback.
|
// Pause audio playback.
|
||||||
virtual void Pause() = 0;
|
void Pause();
|
||||||
|
|
||||||
// Resume audio playback.
|
// Resume audio playback.
|
||||||
virtual void Resume() = 0;
|
void Resume();
|
||||||
|
|
||||||
// Return the position in microseconds of the audio frame being played by
|
// Return the position in microseconds of the audio frame being played by
|
||||||
// the audio hardware, compensated for playback rate change. Thread-safe.
|
// the audio hardware, compensated for playback rate change. Thread-safe.
|
||||||
virtual int64_t GetPosition() = 0;
|
int64_t GetPosition();
|
||||||
|
|
||||||
// Return the position, measured in audio frames played since the stream
|
// Return the position, measured in audio frames played since the stream
|
||||||
// was opened, of the audio hardware. Thread-safe.
|
// was opened, of the audio hardware. Thread-safe.
|
||||||
virtual int64_t GetPositionInFrames() = 0;
|
int64_t GetPositionInFrames();
|
||||||
|
|
||||||
// Return the position, measured in audio framed played since the stream was
|
// Return the position, measured in audio framed played since the stream was
|
||||||
// opened, of the audio hardware, not adjusted for the changes of playback
|
// opened, of the audio hardware, not adjusted for the changes of playback
|
||||||
// rate.
|
// rate.
|
||||||
virtual int64_t GetPositionInFramesInternal() = 0;
|
int64_t GetPositionInFramesInternal();
|
||||||
|
|
||||||
// Returns true when the audio stream is paused.
|
// Returns true when the audio stream is paused.
|
||||||
virtual bool IsPaused() = 0;
|
bool IsPaused();
|
||||||
|
|
||||||
int GetRate() { return mOutRate; }
|
int GetRate() { return mOutRate; }
|
||||||
int GetChannels() { return mChannels; }
|
int GetChannels() { return mChannels; }
|
||||||
|
|
||||||
// This should be called before attempting to use the time stretcher.
|
// This should be called before attempting to use the time stretcher.
|
||||||
virtual nsresult EnsureTimeStretcherInitialized();
|
nsresult EnsureTimeStretcherInitialized();
|
||||||
// Set playback rate as a multiple of the intrinsic playback rate. This is to
|
// Set playback rate as a multiple of the intrinsic playback rate. This is to
|
||||||
// be called only with aPlaybackRate > 0.0.
|
// be called only with aPlaybackRate > 0.0.
|
||||||
virtual nsresult SetPlaybackRate(double aPlaybackRate);
|
nsresult SetPlaybackRate(double aPlaybackRate);
|
||||||
// Switch between resampling (if false) and time stretching (if true, default).
|
// Switch between resampling (if false) and time stretching (if true, default).
|
||||||
virtual nsresult SetPreservesPitch(bool aPreservesPitch);
|
nsresult SetPreservesPitch(bool aPreservesPitch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int PrefChanged(const char* aPref, void* aClosure);
|
||||||
|
static double GetVolumeScale();
|
||||||
|
static cubeb* GetCubebContext();
|
||||||
|
static cubeb* GetCubebContextUnlocked();
|
||||||
|
static uint32_t GetCubebLatency();
|
||||||
|
static bool CubebLatencyPrefSet();
|
||||||
|
|
||||||
|
static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
|
||||||
|
{
|
||||||
|
return static_cast<AudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
|
||||||
|
{
|
||||||
|
static_cast<AudioStream*>(aThis)->StateCallback(aState);
|
||||||
|
}
|
||||||
|
|
||||||
|
long DataCallback(void* aBuffer, long aFrames);
|
||||||
|
void StateCallback(cubeb_state aState);
|
||||||
|
|
||||||
|
// aTime is the time in ms the samples were inserted into MediaStreamGraph
|
||||||
|
long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
|
||||||
|
long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
|
||||||
|
long GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t &aTime);
|
||||||
|
|
||||||
|
// Shared implementation of underflow adjusted position calculation.
|
||||||
|
// Caller must own the monitor.
|
||||||
|
int64_t GetPositionInFramesUnlocked();
|
||||||
|
|
||||||
|
int64_t GetLatencyInFrames();
|
||||||
|
void GetBufferInsertTime(int64_t &aTimeMs);
|
||||||
|
|
||||||
|
void StartUnlocked();
|
||||||
|
|
||||||
|
// The monitor is held to protect all access to member variables. Write()
|
||||||
|
// waits while mBuffer is full; DataCallback() notifies as it consumes
|
||||||
|
// data from mBuffer. Drain() waits while mState is DRAINING;
|
||||||
|
// StateCallback() notifies when mState is DRAINED.
|
||||||
|
Monitor mMonitor;
|
||||||
|
|
||||||
protected:
|
|
||||||
// This mutex protects the mPreferedSamplerate member below.
|
|
||||||
static StaticMutex mMutex;
|
|
||||||
// Prefered samplerate, in Hz (characteristic of the
|
|
||||||
// hardware/mixer/platform/API used).
|
|
||||||
static uint32_t mPreferredSampleRate;
|
|
||||||
// Input rate in Hz (characteristic of the media being played)
|
// Input rate in Hz (characteristic of the media being played)
|
||||||
int mInRate;
|
int mInRate;
|
||||||
// Output rate in Hz (characteristic of the playback rate)
|
// Output rate in Hz (characteristic of the playback rate)
|
||||||
@ -218,7 +323,65 @@ protected:
|
|||||||
int64_t mTimeMs;
|
int64_t mTimeMs;
|
||||||
int64_t mFrames;
|
int64_t mFrames;
|
||||||
};
|
};
|
||||||
nsAutoTArray<Inserts,8> mInserts;
|
nsAutoTArray<Inserts, 8> mInserts;
|
||||||
|
|
||||||
|
// Sum of silent frames written when DataCallback requests more frames
|
||||||
|
// than are available in mBuffer.
|
||||||
|
uint64_t mLostFrames;
|
||||||
|
|
||||||
|
// Output file for dumping audio
|
||||||
|
FILE* mDumpFile;
|
||||||
|
|
||||||
|
// Temporary audio buffer. Filled by Write() and consumed by
|
||||||
|
// DataCallback(). Once mBuffer is full, Write() blocks until sufficient
|
||||||
|
// space becomes available in mBuffer. mBuffer is sized in bytes, not
|
||||||
|
// frames.
|
||||||
|
CircularByteBuffer mBuffer;
|
||||||
|
|
||||||
|
// Software volume level. Applied during the servicing of DataCallback().
|
||||||
|
double mVolume;
|
||||||
|
|
||||||
|
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
|
||||||
|
// nsAutoRef's destructor.
|
||||||
|
nsAutoRef<cubeb_stream> mCubebStream;
|
||||||
|
|
||||||
|
uint32_t mBytesPerFrame;
|
||||||
|
|
||||||
|
uint32_t BytesToFrames(uint32_t aBytes) {
|
||||||
|
NS_ASSERTION(aBytes % mBytesPerFrame == 0,
|
||||||
|
"Byte count not aligned on frames size.");
|
||||||
|
return aBytes / mBytesPerFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FramesToBytes(uint32_t aFrames) {
|
||||||
|
return aFrames * mBytesPerFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StreamState {
|
||||||
|
INITIALIZED, // Initialized, playback has not begun.
|
||||||
|
STARTED, // Started by a call to Write() (iff INITIALIZED) or Resume().
|
||||||
|
STOPPED, // Stopped by a call to Pause().
|
||||||
|
DRAINING, // Drain requested. DataCallback will indicate end of stream
|
||||||
|
// once the remaining contents of mBuffer are requested by
|
||||||
|
// cubeb, after which StateCallback will indicate drain
|
||||||
|
// completion.
|
||||||
|
DRAINED, // StateCallback has indicated that the drain is complete.
|
||||||
|
ERRORED // Stream disabled due to an internal error.
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamState mState;
|
||||||
|
|
||||||
|
// This mutex protects the static members below.
|
||||||
|
static StaticMutex sMutex;
|
||||||
|
static cubeb* sCubebContext;
|
||||||
|
|
||||||
|
// Prefered samplerate, in Hz (characteristic of the
|
||||||
|
// hardware/mixer/platform/API used).
|
||||||
|
static uint32_t sPreferredSampleRate;
|
||||||
|
|
||||||
|
static double sVolumeScale;
|
||||||
|
static uint32_t sCubebLatency;
|
||||||
|
static bool sCubebLatencyPrefSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -1056,7 +1056,7 @@ void MediaDecoderStateMachine::AudioLoop()
|
|||||||
// AudioStream initialization can block for extended periods in unusual
|
// AudioStream initialization can block for extended periods in unusual
|
||||||
// circumstances, so we take care to drop the decoder monitor while
|
// circumstances, so we take care to drop the decoder monitor while
|
||||||
// initializing.
|
// initializing.
|
||||||
nsAutoPtr<AudioStream> audioStream(AudioStream::AllocateStream());
|
nsAutoPtr<AudioStream> audioStream(new AudioStream());
|
||||||
audioStream->Init(channels, rate, audioChannelType, AudioStream::HighLatency);
|
audioStream->Init(channels, rate, audioChannelType, AudioStream::HighLatency);
|
||||||
audioStream->SetVolume(volume);
|
audioStream->SetVolume(volume);
|
||||||
if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
|
if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
|
||||||
|
@ -778,7 +778,7 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
|
|||||||
aStream->mAudioOutputStreams.AppendElement();
|
aStream->mAudioOutputStreams.AppendElement();
|
||||||
audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
|
audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
|
||||||
audioOutputStream->mBlockedAudioTime = 0;
|
audioOutputStream->mBlockedAudioTime = 0;
|
||||||
audioOutputStream->mStream = AudioStream::AllocateStream();
|
audioOutputStream->mStream = new AudioStream();
|
||||||
// XXX for now, allocate stereo output. But we need to fix this to
|
// XXX for now, allocate stereo output. But we need to fix this to
|
||||||
// match the system's ideal channel configuration.
|
// match the system's ideal channel configuration.
|
||||||
audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
|
audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
|
||||||
|
@ -17,10 +17,5 @@ ifeq (WINNT,$(OS_TARGET))
|
|||||||
symbols.def: symbols.def.in $(GLOBAL_DEPS)
|
symbols.def: symbols.def.in $(GLOBAL_DEPS)
|
||||||
$(call py_action,preprocessor,$(ACDEFINES) $< -o $@)
|
$(call py_action,preprocessor,$(ACDEFINES) $< -o $@)
|
||||||
|
|
||||||
OS_LIBS += $(call EXPAND_LIBNAME, msimg32)
|
OS_LIBS += $(call EXPAND_LIBNAME, msimg32 winmm)
|
||||||
|
|
||||||
ifdef MOZ_CUBEB
|
|
||||||
OS_LIBS += $(call EXPAND_LIBNAME, winmm)
|
|
||||||
endif
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
@ -117,7 +117,6 @@ speex_resampler_get_output_latency
|
|||||||
speex_resampler_skip_zeros
|
speex_resampler_skip_zeros
|
||||||
speex_resampler_reset_mem
|
speex_resampler_reset_mem
|
||||||
speex_resampler_strerror
|
speex_resampler_strerror
|
||||||
#ifdef MOZ_CUBEB
|
|
||||||
cubeb_destroy
|
cubeb_destroy
|
||||||
cubeb_init
|
cubeb_init
|
||||||
cubeb_get_max_channel_count
|
cubeb_get_max_channel_count
|
||||||
@ -129,7 +128,6 @@ cubeb_stream_init
|
|||||||
cubeb_stream_start
|
cubeb_stream_start
|
||||||
cubeb_stream_stop
|
cubeb_stream_stop
|
||||||
cubeb_stream_get_latency
|
cubeb_stream_get_latency
|
||||||
#endif
|
|
||||||
#ifdef MOZ_OGG
|
#ifdef MOZ_OGG
|
||||||
th_comment_clear
|
th_comment_clear
|
||||||
th_comment_init
|
th_comment_init
|
||||||
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
|||||||
|
|
||||||
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
|
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
|
||||||
|
|
||||||
The git commit ID used was 8c78a282aa0320e997436d6832024efe1527ca1c.
|
The git commit ID used was e92a27c96c0efd33acf983e4c873376ff4cae3d8.
|
||||||
|
@ -159,7 +159,7 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
|
|||||||
int
|
int
|
||||||
cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms)
|
cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms)
|
||||||
{
|
{
|
||||||
if (!latency_ms) {
|
if (!context || !latency_ms) {
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
return context->ops->get_min_latency(context, params, latency_ms);
|
return context->ops->get_min_latency(context, params, latency_ms);
|
||||||
@ -168,7 +168,7 @@ cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * la
|
|||||||
int
|
int
|
||||||
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
|
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
|
||||||
{
|
{
|
||||||
if (!rate) {
|
if (!context || !rate) {
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
return context->ops->get_preferred_sample_rate(context, rate);
|
return context->ops->get_preferred_sample_rate(context, rate);
|
||||||
|
@ -17,13 +17,11 @@ LIBS = \
|
|||||||
$(DEPTH)/netwerk/srtp/src/$(LIB_PREFIX)nksrtp_s.$(LIB_SUFFIX) \
|
$(DEPTH)/netwerk/srtp/src/$(LIB_PREFIX)nksrtp_s.$(LIB_SUFFIX) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
ifdef MOZ_CUBEB
|
|
||||||
ifdef MOZ_ALSA
|
ifdef MOZ_ALSA
|
||||||
LIBS += \
|
LIBS += \
|
||||||
$(MOZ_ALSA_LIBS) \
|
$(MOZ_ALSA_LIBS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(OS_TARGET),Android)
|
ifeq ($(OS_TARGET),Android)
|
||||||
LIBS += \
|
LIBS += \
|
||||||
|
@ -180,11 +180,9 @@ endif
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifdef MOZ_CUBEB
|
|
||||||
ifdef MOZ_ALSA
|
ifdef MOZ_ALSA
|
||||||
EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
|
EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef HAVE_CLOCK_MONOTONIC
|
ifdef HAVE_CLOCK_MONOTONIC
|
||||||
EXTRA_DSO_LDOPTS += $(REALTIME_LIBS)
|
EXTRA_DSO_LDOPTS += $(REALTIME_LIBS)
|
||||||
@ -247,10 +245,8 @@ OS_LIBS += \
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq (OpenBSD,$(OS_ARCH))
|
ifeq (OpenBSD,$(OS_ARCH))
|
||||||
ifdef MOZ_CUBEB
|
|
||||||
EXTRA_DSO_LDOPTS += -lsndio
|
EXTRA_DSO_LDOPTS += -lsndio
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef MOZ_ENABLE_DBUS
|
ifdef MOZ_ENABLE_DBUS
|
||||||
EXTRA_DSO_LDOPTS += $(MOZ_DBUS_GLIB_LIBS)
|
EXTRA_DSO_LDOPTS += $(MOZ_DBUS_GLIB_LIBS)
|
||||||
|
Loading…
Reference in New Issue
Block a user