mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 989921 - Allow the MediaStreamGraph mixer to send data back to multiple consumers. r=jesup
This commit is contained in:
parent
601228fc42
commit
6b08a02243
@ -9,13 +9,17 @@
|
|||||||
#include "AudioSampleFormat.h"
|
#include "AudioSampleFormat.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
|
#include "mozilla/LinkedList.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
typedef void(*MixerFunc)(AudioDataValue* aMixedBuffer,
|
|
||||||
AudioSampleFormat aFormat,
|
struct MixerCallbackReceiver {
|
||||||
uint32_t aChannels,
|
virtual void MixerCallback(AudioDataValue* aMixedBuffer,
|
||||||
uint32_t aFrames,
|
AudioSampleFormat aFormat,
|
||||||
uint32_t aSampleRate);
|
uint32_t aChannels,
|
||||||
|
uint32_t aFrames,
|
||||||
|
uint32_t aSampleRate) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class mixes multiple streams of audio together to output a single audio
|
* This class mixes multiple streams of audio together to output a single audio
|
||||||
@ -32,21 +36,29 @@ typedef void(*MixerFunc)(AudioDataValue* aMixedBuffer,
|
|||||||
class AudioMixer
|
class AudioMixer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioMixer(MixerFunc aCallback)
|
AudioMixer()
|
||||||
: mCallback(aCallback),
|
: mFrames(0),
|
||||||
mFrames(0),
|
|
||||||
mChannels(0),
|
mChannels(0),
|
||||||
mSampleRate(0)
|
mSampleRate(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
~AudioMixer()
|
||||||
|
{
|
||||||
|
mCallbacks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the data from the mixer. This is supposed to be called when all the
|
/* Get the data from the mixer. This is supposed to be called when all the
|
||||||
* tracks have been mixed in. The caller should not hold onto the data. */
|
* tracks have been mixed in. The caller should not hold onto the data. */
|
||||||
void FinishMixing() {
|
void FinishMixing() {
|
||||||
mCallback(mMixedAudio.Elements(),
|
MOZ_ASSERT(mChannels && mFrames && mSampleRate, "Mix not called for this cycle?");
|
||||||
AudioSampleTypeToFormat<AudioDataValue>::Format,
|
for (MixerCallback* cb = mCallbacks.getFirst();
|
||||||
mChannels,
|
cb != nullptr; cb = cb->getNext()) {
|
||||||
mFrames,
|
cb->mReceiver->MixerCallback(mMixedAudio.Elements(),
|
||||||
mSampleRate);
|
AudioSampleTypeToFormat<AudioDataValue>::Format,
|
||||||
|
mChannels,
|
||||||
|
mFrames,
|
||||||
|
mSampleRate);
|
||||||
|
}
|
||||||
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
|
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
|
||||||
mSampleRate = mChannels = mFrames = 0;
|
mSampleRate = mChannels = mFrames = 0;
|
||||||
}
|
}
|
||||||
@ -71,6 +83,21 @@ public:
|
|||||||
mMixedAudio[i] += aSamples[i];
|
mMixedAudio[i] += aSamples[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddCallback(MixerCallbackReceiver* aReceiver) {
|
||||||
|
mCallbacks.insertBack(new MixerCallback(aReceiver));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveCallback(MixerCallbackReceiver* aReceiver) {
|
||||||
|
for (MixerCallback* cb = mCallbacks.getFirst();
|
||||||
|
cb != nullptr; cb = cb->getNext()) {
|
||||||
|
if (cb->mReceiver == aReceiver) {
|
||||||
|
cb->remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
void EnsureCapacityAndSilence() {
|
void EnsureCapacityAndSilence() {
|
||||||
if (mFrames * mChannels > mMixedAudio.Length()) {
|
if (mFrames * mChannels > mMixedAudio.Length()) {
|
||||||
@ -79,8 +106,17 @@ private:
|
|||||||
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
|
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MixerCallback : public LinkedListElement<MixerCallback>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MixerCallback(MixerCallbackReceiver* aReceiver)
|
||||||
|
: mReceiver(aReceiver)
|
||||||
|
{ }
|
||||||
|
MixerCallbackReceiver* mReceiver;
|
||||||
|
};
|
||||||
|
|
||||||
/* Function that is called when the mixing is done. */
|
/* Function that is called when the mixing is done. */
|
||||||
MixerFunc mCallback;
|
LinkedList<MixerCallback> mCallbacks;
|
||||||
/* Number of frames for this mixing block. */
|
/* Number of frames for this mixing block. */
|
||||||
uint32_t mFrames;
|
uint32_t mFrames;
|
||||||
/* Number of channels for this mixing block. */
|
/* Number of channels for this mixing block. */
|
||||||
|
@ -586,24 +586,6 @@ MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream
|
|||||||
*mStreams.AppendElement() = stream.forget();
|
*mStreams.AppendElement() = stream.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
|
|
||||||
AudioSampleFormat aFormat,
|
|
||||||
uint32_t aChannels,
|
|
||||||
uint32_t aFrames,
|
|
||||||
uint32_t aSampleRate)
|
|
||||||
{
|
|
||||||
// Need an api to register mixer callbacks, bug 989921
|
|
||||||
#ifdef MOZ_WEBRTC
|
|
||||||
if (aFrames > 0 && aChannels > 0) {
|
|
||||||
// XXX need Observer base class and registration API
|
|
||||||
if (gFarendObserver) {
|
|
||||||
gFarendObserver->InsertFarEnd(aMixedBuffer, aFrames, false,
|
|
||||||
aSampleRate, aChannels, aFormat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaStreamGraphImpl::UpdateStreamOrder()
|
MediaStreamGraphImpl::UpdateStreamOrder()
|
||||||
{
|
{
|
||||||
@ -631,8 +613,12 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mMixer && shouldMix) {
|
if (!mMixer && shouldMix) {
|
||||||
mMixer = new AudioMixer(AudioMixerCallback);
|
mMixer = new AudioMixer();
|
||||||
|
if (gFarendObserver) {
|
||||||
|
mMixer->AddCallback(gFarendObserver);
|
||||||
|
}
|
||||||
} else if (mMixer && !shouldMix) {
|
} else if (mMixer && !shouldMix) {
|
||||||
|
mMixer->RemoveCallback(gFarendObserver);
|
||||||
mMixer = nullptr;
|
mMixer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include "MainThreadUtils.h"
|
#include "MainThreadUtils.h"
|
||||||
#include "nsAutoRef.h"
|
#include "nsAutoRef.h"
|
||||||
#include "speex/speex_resampler.h"
|
#include "speex/speex_resampler.h"
|
||||||
#include "AudioMixer.h"
|
|
||||||
#include "mozilla/dom/AudioChannelBinding.h"
|
#include "mozilla/dom/AudioChannelBinding.h"
|
||||||
|
|
||||||
class nsIRunnable;
|
class nsIRunnable;
|
||||||
|
@ -9,25 +9,28 @@
|
|||||||
using mozilla::AudioDataValue;
|
using mozilla::AudioDataValue;
|
||||||
using mozilla::AudioSampleFormat;
|
using mozilla::AudioSampleFormat;
|
||||||
|
|
||||||
|
struct MixerConsumer : public mozilla::MixerCallbackReceiver
|
||||||
|
{
|
||||||
/* In this test, the different audio stream and channels are always created to
|
/* In this test, the different audio stream and channels are always created to
|
||||||
* cancel each other. */
|
* cancel each other. */
|
||||||
void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate)
|
void MixerCallback(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate)
|
||||||
{
|
{
|
||||||
bool silent = true;
|
bool silent = true;
|
||||||
for (uint32_t i = 0; i < aChannels * aFrames; i++) {
|
for (uint32_t i = 0; i < aChannels * aFrames; i++) {
|
||||||
if (aData[i] != 0.0) {
|
if (aData[i] != 0.0) {
|
||||||
if (aFormat == mozilla::AUDIO_FORMAT_S16) {
|
if (aFormat == mozilla::AUDIO_FORMAT_S16) {
|
||||||
fprintf(stderr, "Sample at %d is not silent: %d\n", i, (short)aData[i]);
|
fprintf(stderr, "Sample at %d is not silent: %d\n", i, (short)aData[i]);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Sample at %d is not silent: %f\n", i, (float)aData[i]);
|
fprintf(stderr, "Sample at %d is not silent: %f\n", i, (float)aData[i]);
|
||||||
|
}
|
||||||
|
silent = false;
|
||||||
}
|
}
|
||||||
silent = false;
|
}
|
||||||
|
if (!silent) {
|
||||||
|
MOZ_CRASH();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!silent) {
|
};
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function to give us the maximum and minimum value that don't clip,
|
/* Helper function to give us the maximum and minimum value that don't clip,
|
||||||
* for a given sample format (integer or floating-point). */
|
* for a given sample format (integer or floating-point). */
|
||||||
@ -68,6 +71,7 @@ void FillBuffer(AudioDataValue* aBuffer, uint32_t aLength, AudioDataValue aValue
|
|||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
const uint32_t CHANNEL_LENGTH = 256;
|
const uint32_t CHANNEL_LENGTH = 256;
|
||||||
const uint32_t AUDIO_RATE = 44100;
|
const uint32_t AUDIO_RATE = 44100;
|
||||||
|
MixerConsumer consumer;
|
||||||
AudioDataValue a[CHANNEL_LENGTH * 2];
|
AudioDataValue a[CHANNEL_LENGTH * 2];
|
||||||
AudioDataValue b[CHANNEL_LENGTH * 2];
|
AudioDataValue b[CHANNEL_LENGTH * 2];
|
||||||
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
|
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
|
||||||
@ -77,7 +81,8 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
int iterations = 2;
|
int iterations = 2;
|
||||||
mozilla::AudioMixer mixer(MixingDone);
|
mozilla::AudioMixer mixer;
|
||||||
|
mixer.AddCallback(&consumer);
|
||||||
|
|
||||||
fprintf(stderr, "Test AudioMixer constant buffer length.\n");
|
fprintf(stderr, "Test AudioMixer constant buffer length.\n");
|
||||||
|
|
||||||
@ -89,7 +94,8 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
mozilla::AudioMixer mixer(MixingDone);
|
mozilla::AudioMixer mixer;
|
||||||
|
mixer.AddCallback(&consumer);
|
||||||
|
|
||||||
fprintf(stderr, "Test AudioMixer variable buffer length.\n");
|
fprintf(stderr, "Test AudioMixer variable buffer length.\n");
|
||||||
|
|
||||||
@ -120,7 +126,9 @@ int main(int argc, char* argv[]) {
|
|||||||
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
|
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
|
||||||
|
|
||||||
{
|
{
|
||||||
mozilla::AudioMixer mixer(MixingDone);
|
mozilla::AudioMixer mixer;
|
||||||
|
mixer.AddCallback(&consumer);
|
||||||
|
|
||||||
fprintf(stderr, "Test AudioMixer variable channel count.\n");
|
fprintf(stderr, "Test AudioMixer variable channel count.\n");
|
||||||
|
|
||||||
mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
|
mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
|
||||||
@ -135,7 +143,8 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
mozilla::AudioMixer mixer(MixingDone);
|
mozilla::AudioMixer mixer;
|
||||||
|
mixer.AddCallback(&consumer);
|
||||||
fprintf(stderr, "Test AudioMixer variable stream count.\n");
|
fprintf(stderr, "Test AudioMixer variable stream count.\n");
|
||||||
|
|
||||||
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
|
mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#define AUDIOOUTPUTOBSERVER_H_
|
#define AUDIOOUTPUTOBSERVER_H_
|
||||||
|
|
||||||
#include "mozilla/StaticPtr.h"
|
#include "mozilla/StaticPtr.h"
|
||||||
|
#include "AudioMixer.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
class SingleRwFifo;
|
class SingleRwFifo;
|
||||||
@ -20,12 +21,18 @@ typedef struct FarEndAudioChunk_ {
|
|||||||
} FarEndAudioChunk;
|
} FarEndAudioChunk;
|
||||||
|
|
||||||
// XXX Really a singleton currently
|
// XXX Really a singleton currently
|
||||||
class AudioOutputObserver // : public MSGOutputObserver
|
class AudioOutputObserver : public MixerCallbackReceiver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioOutputObserver();
|
AudioOutputObserver();
|
||||||
virtual ~AudioOutputObserver();
|
virtual ~AudioOutputObserver();
|
||||||
|
|
||||||
|
void MixerCallback(AudioDataValue* aMixedBuffer,
|
||||||
|
AudioSampleFormat aFormat,
|
||||||
|
uint32_t aChannels,
|
||||||
|
uint32_t aFrames,
|
||||||
|
uint32_t aSampleRate) MOZ_OVERRIDE;
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
void InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
|
void InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
|
||||||
int aFreq, int aChannels, AudioSampleFormat aFormat);
|
int aFreq, int aChannels, AudioSampleFormat aFormat);
|
||||||
|
@ -86,6 +86,17 @@ AudioOutputObserver::Size()
|
|||||||
return mPlayoutFifo->size();
|
return mPlayoutFifo->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputObserver::MixerCallback(AudioDataValue* aMixedBuffer,
|
||||||
|
AudioSampleFormat aFormat,
|
||||||
|
uint32_t aChannels,
|
||||||
|
uint32_t aFrames,
|
||||||
|
uint32_t aSampleRate)
|
||||||
|
{
|
||||||
|
gFarendObserver->InsertFarEnd(aMixedBuffer, aFrames, false,
|
||||||
|
aSampleRate, aChannels, aFormat);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void
|
void
|
||||||
AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
|
AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
|
||||||
|
Loading…
Reference in New Issue
Block a user