зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1221587: use cubeb devids to select input devices r=padenot
--HG-- extra : commitid : AH6Lt4KfNaF
This commit is contained in:
Родитель
be7d8f1d36
Коммит
46878bf96f
|
@ -13,6 +13,8 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace CubebUtils {
|
namespace CubebUtils {
|
||||||
|
|
||||||
|
typedef cubeb_devid AudioDeviceID;
|
||||||
|
|
||||||
// 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.
|
||||||
void InitLibrary();
|
void InitLibrary();
|
||||||
|
|
|
@ -559,23 +559,23 @@ AudioCallbackDriver::~AudioCallbackDriver()
|
||||||
void
|
void
|
||||||
AudioCallbackDriver::Init()
|
AudioCallbackDriver::Init()
|
||||||
{
|
{
|
||||||
cubeb_stream_params out_params;
|
cubeb_stream_params output;
|
||||||
cubeb_stream_params in_params;
|
cubeb_stream_params input;
|
||||||
uint32_t latency;
|
uint32_t latency;
|
||||||
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread(),
|
MOZ_ASSERT(!NS_IsMainThread(),
|
||||||
"This is blocking and should never run on the main thread.");
|
"This is blocking and should never run on the main thread.");
|
||||||
|
|
||||||
out_params.devid = nullptr; // XXX take from config for the graph
|
output.devid = mGraphImpl->mOutputDeviceID;
|
||||||
mSampleRate = out_params.rate = CubebUtils::PreferredSampleRate();
|
mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#if defined(MOZ_B2G)
|
#if defined(MOZ_B2G)
|
||||||
out_params.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
|
output.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
|
||||||
#else
|
#else
|
||||||
out_params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
output.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
||||||
#endif
|
#endif
|
||||||
if (out_params.stream_type == CUBEB_STREAM_TYPE_MAX) {
|
if (output.stream_type == CUBEB_STREAM_TYPE_MAX) {
|
||||||
NS_WARNING("Bad stream type");
|
NS_WARNING("Bad stream type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -583,27 +583,30 @@ AudioCallbackDriver::Init()
|
||||||
(void)mAudioChannel;
|
(void)mAudioChannel;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
out_params.channels = mGraphImpl->AudioChannelCount();
|
output.channels = mGraphImpl->AudioChannelCount();
|
||||||
if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
|
if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
|
||||||
out_params.format = CUBEB_SAMPLE_S16NE;
|
output.format = CUBEB_SAMPLE_S16NE;
|
||||||
} else {
|
} else {
|
||||||
out_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
output.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cubeb_get_min_latency(CubebUtils::GetCubebContext(), out_params, &latency) != CUBEB_OK) {
|
if (cubeb_get_min_latency(CubebUtils::GetCubebContext(), output, &latency) != CUBEB_OK) {
|
||||||
NS_WARNING("Could not get minimal latency from cubeb.");
|
NS_WARNING("Could not get minimal latency from cubeb.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
in_params = out_params;
|
input = output;
|
||||||
in_params.channels = 1; // change to support optional stereo capture
|
input.channels = 1; // change to support optional stereo capture
|
||||||
|
input.devid = mGraphImpl->mInputDeviceID;
|
||||||
|
|
||||||
cubeb_stream* stream;
|
cubeb_stream* stream;
|
||||||
// XXX Only pass input in_params if we have an input listener. Always
|
// XXX Only pass input input if we have an input listener. Always
|
||||||
// set up output because it's easier, and it will just get silence.
|
// set up output because it's easier, and it will just get silence.
|
||||||
// XXX Add support for adding/removing an input listener later.
|
// XXX Add support for adding/removing an input listener later.
|
||||||
if (cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
|
if (cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
|
||||||
"AudioCallbackDriver", &out_params, &in_params, latency,
|
"AudioCallbackDriver",
|
||||||
|
mGraphImpl->mInputWanted ? &input : nullptr,
|
||||||
|
mGraphImpl->mOutputWanted ? &output : nullptr, latency,
|
||||||
DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
|
DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
|
||||||
mAudioStream.own(stream);
|
mAudioStream.own(stream);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -924,8 +924,13 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaStreamGraphImpl::OpenAudioInputImpl(char *aName, AudioDataListener *aListener)
|
MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
|
||||||
|
AudioDataListener *aListener)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(!mInputWanted);
|
||||||
|
mInputWanted = true;
|
||||||
|
mInputDeviceID = aID;
|
||||||
|
// XXX Switch Drivers
|
||||||
if (CurrentDriver()->AsAudioCallbackDriver()) {
|
if (CurrentDriver()->AsAudioCallbackDriver()) {
|
||||||
CurrentDriver()->SetInputListener(aListener);
|
CurrentDriver()->SetInputListener(aListener);
|
||||||
} else {
|
} else {
|
||||||
|
@ -935,35 +940,40 @@ MediaStreamGraphImpl::OpenAudioInputImpl(char *aName, AudioDataListener *aListen
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MediaStreamGraphImpl::OpenAudioInput(char *aName, AudioDataListener *aListener)
|
MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||||
|
AudioDataListener *aListener)
|
||||||
{
|
{
|
||||||
// XXX So, so, so annoying. Can't AppendMessage except on Mainthread
|
// XXX So, so, so annoying. Can't AppendMessage except on Mainthread
|
||||||
if (!NS_IsMainThread()) {
|
if (!NS_IsMainThread()) {
|
||||||
NS_DispatchToMainThread(WrapRunnable(this,
|
NS_DispatchToMainThread(WrapRunnable(this,
|
||||||
&MediaStreamGraphImpl::OpenAudioInput,
|
&MediaStreamGraphImpl::OpenAudioInput,
|
||||||
aName, aListener)); // XXX Fix! string need to copied
|
aID, aListener)); // XXX Fix! string need to copied
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
class Message : public ControlMessage {
|
class Message : public ControlMessage {
|
||||||
public:
|
public:
|
||||||
Message(MediaStreamGraphImpl *aGraph, char *aName, AudioDataListener *aListener) :
|
Message(MediaStreamGraphImpl *aGraph, CubebUtils::AudioDeviceID aID,
|
||||||
ControlMessage(nullptr), mGraph(aGraph), mName(aName), mListener(aListener) {}
|
AudioDataListener *aListener) :
|
||||||
|
ControlMessage(nullptr), mGraph(aGraph), mID(aID), mListener(aListener) {}
|
||||||
virtual void Run()
|
virtual void Run()
|
||||||
{
|
{
|
||||||
mGraph->OpenAudioInputImpl(mName, mListener);
|
mGraph->OpenAudioInputImpl(mID, mListener);
|
||||||
}
|
}
|
||||||
MediaStreamGraphImpl *mGraph;
|
MediaStreamGraphImpl *mGraph;
|
||||||
char *mName; // XXX needs to copy
|
CubebUtils::AudioDeviceID mID;
|
||||||
RefPtr<AudioDataListener> mListener;
|
RefPtr<AudioDataListener> mListener;
|
||||||
};
|
};
|
||||||
this->AppendMessage(new Message(this, aName, aListener));
|
this->AppendMessage(new Message(this, aID, aListener));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
|
MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
|
||||||
{
|
{
|
||||||
|
mInputDeviceID = nullptr;
|
||||||
|
mInputWanted = false;
|
||||||
CurrentDriver()->RemoveInputListener(aListener);
|
CurrentDriver()->RemoveInputListener(aListener);
|
||||||
|
// XXX Switch Drivers
|
||||||
mAudioInputs.RemoveElement(aListener);
|
mAudioInputs.RemoveElement(aListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2711,6 +2721,10 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested,
|
||||||
dom::AudioChannel aChannel)
|
dom::AudioChannel aChannel)
|
||||||
: MediaStreamGraph(aSampleRate)
|
: MediaStreamGraph(aSampleRate)
|
||||||
, mPortCount(0)
|
, mPortCount(0)
|
||||||
|
, mInputWanted(false)
|
||||||
|
, mInputDeviceID(nullptr)
|
||||||
|
, mOutputWanted(true)
|
||||||
|
, mOutputDeviceID(nullptr)
|
||||||
, mNeedAnotherIteration(false)
|
, mNeedAnotherIteration(false)
|
||||||
, mGraphDriverAsleep(false)
|
, mGraphDriverAsleep(false)
|
||||||
, mMonitor("MediaStreamGraphImpl")
|
, mMonitor("MediaStreamGraphImpl")
|
||||||
|
|
|
@ -1208,7 +1208,8 @@ public:
|
||||||
// Idempotent
|
// Idempotent
|
||||||
static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
|
static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
|
||||||
|
|
||||||
virtual nsresult OpenAudioInput(char *aName, AudioDataListener *aListener) {
|
virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||||
|
AudioDataListener *aListener) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
virtual void CloseAudioInput(AudioDataListener *aListener) {}
|
virtual void CloseAudioInput(AudioDataListener *aListener) {}
|
||||||
|
|
|
@ -350,8 +350,10 @@ public:
|
||||||
* at the current buffer end point. The StreamBuffer's tracks must be
|
* at the current buffer end point. The StreamBuffer's tracks must be
|
||||||
* explicitly set to finished by the caller.
|
* explicitly set to finished by the caller.
|
||||||
*/
|
*/
|
||||||
void OpenAudioInputImpl(char *aName, AudioDataListener *aListener);
|
void OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
|
||||||
virtual nsresult OpenAudioInput(char *aName, AudioDataListener *aListener) override;
|
AudioDataListener *aListener);
|
||||||
|
virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||||
|
AudioDataListener *aListener) override;
|
||||||
void CloseAudioInputImpl(AudioDataListener *aListener);
|
void CloseAudioInputImpl(AudioDataListener *aListener);
|
||||||
virtual void CloseAudioInput(AudioDataListener *aListener) override;
|
virtual void CloseAudioInput(AudioDataListener *aListener) override;
|
||||||
|
|
||||||
|
@ -582,6 +584,15 @@ public:
|
||||||
*/
|
*/
|
||||||
int32_t mPortCount;
|
int32_t mPortCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Devices to use for cubeb input & output, or NULL for no input (void*),
|
||||||
|
* and boolean to control if we want input/output
|
||||||
|
*/
|
||||||
|
bool mInputWanted;
|
||||||
|
CubebUtils::AudioDeviceID mInputDeviceID;
|
||||||
|
bool mOutputWanted;
|
||||||
|
CubebUtils::AudioDeviceID mOutputDeviceID;
|
||||||
|
|
||||||
// True if the graph needs another iteration after the current iteration.
|
// True if the graph needs another iteration after the current iteration.
|
||||||
Atomic<bool> mNeedAnotherIteration;
|
Atomic<bool> mNeedAnotherIteration;
|
||||||
// GraphDriver may need a WakeUp() if something changes
|
// GraphDriver may need a WakeUp() if something changes
|
||||||
|
|
|
@ -44,6 +44,8 @@ GetUserMediaLog()
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
cubeb_device_collection* AudioInputCubeb::mDevices = nullptr;
|
||||||
|
|
||||||
MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
|
MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
|
||||||
: mMutex("mozilla::MediaEngineWebRTC"),
|
: mMutex("mozilla::MediaEngineWebRTC"),
|
||||||
mVoiceEngine(nullptr),
|
mVoiceEngine(nullptr),
|
||||||
|
@ -326,7 +328,14 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
|
||||||
// We've already seen this device, just append.
|
// We've already seen this device, just append.
|
||||||
aASources->AppendElement(aSource.get());
|
aASources->AppendElement(aSource.get());
|
||||||
} else {
|
} else {
|
||||||
aSource = new MediaEngineWebRTCMicrophoneSource(mThread, mVoiceEngine, mAudioInput,
|
AudioInput* audioinput = mAudioInput;
|
||||||
|
if (true /*platform_supports_full_duplex*/) {
|
||||||
|
// For cubeb, it has state (the selected ID
|
||||||
|
// XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this
|
||||||
|
// XXX Small window where the device list/index could change!
|
||||||
|
audioinput = new mozilla::AudioInputCubeb(mVoiceEngine);
|
||||||
|
}
|
||||||
|
aSource = new MediaEngineWebRTCMicrophoneSource(mThread, mVoiceEngine, audioinput,
|
||||||
i, deviceName, uniqueId);
|
i, deviceName, uniqueId);
|
||||||
mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
|
mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
|
||||||
aASources->AppendElement(aSource);
|
aASources->AppendElement(aSource);
|
||||||
|
@ -366,6 +375,7 @@ MediaEngineWebRTC::Shutdown()
|
||||||
mVoiceEngine = nullptr;
|
mVoiceEngine = nullptr;
|
||||||
|
|
||||||
mozilla::camera::Shutdown();
|
mozilla::camera::Shutdown();
|
||||||
|
AudioInputCubeb::CleanupGlobalData();
|
||||||
|
|
||||||
if (mThread) {
|
if (mThread) {
|
||||||
mThread->Shutdown();
|
mThread->Shutdown();
|
||||||
|
|
|
@ -159,35 +159,66 @@ class AudioInputCubeb final : public AudioInput
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine) :
|
explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine) :
|
||||||
AudioInput(aVoiceEngine), mDevices(nullptr) {}
|
AudioInput(aVoiceEngine), mSelectedDevice(0)
|
||||||
|
{
|
||||||
|
// Force calculation of the indexes. We could keep them global
|
||||||
|
// too... cleanup would be annoying
|
||||||
|
int devices;
|
||||||
|
GetNumOfRecordingDevices(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CleanupGlobalData()
|
||||||
|
{
|
||||||
|
if (mDevices) {
|
||||||
|
// This doesn't require anything more than support for free()
|
||||||
|
cubeb_device_collection_destroy(mDevices);
|
||||||
|
mDevices = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int GetNumOfRecordingDevices(int& aDevices)
|
int GetNumOfRecordingDevices(int& aDevices)
|
||||||
{
|
{
|
||||||
// devices = cubeb_get_num_devices(...)
|
if (!mDevices) {
|
||||||
if (CUBEB_OK != cubeb_enumerate_devices(CubebUtils::GetCubebContext(),
|
if (CUBEB_OK != cubeb_enumerate_devices(CubebUtils::GetCubebContext(),
|
||||||
CUBEB_DEVICE_TYPE_INPUT,
|
CUBEB_DEVICE_TYPE_INPUT,
|
||||||
&mDevices)) {
|
&mDevices)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
aDevices = 0;
|
|
||||||
for (uint32_t i = 0; i < mDevices->count; i++) {
|
|
||||||
if (mDevices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
|
|
||||||
mDevices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED)
|
|
||||||
{
|
|
||||||
aDevices++;
|
|
||||||
// XXX to support device changes, we need to identify by name/UUID not index
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Calculate translation once (per AudioInput)
|
||||||
|
if (mDeviceIndexes.Length() == 0) {
|
||||||
|
for (uint32_t i = 0; i < mDevices->count; i++) {
|
||||||
|
if (mDevices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
|
||||||
|
(mDevices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED ||
|
||||||
|
mDevices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED))
|
||||||
|
{
|
||||||
|
mDeviceIndexes.AppendElement(i);
|
||||||
|
// XXX to support device changes, we need to identify by name/devid not index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aDevices = mDeviceIndexes.Length();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t DeviceIndex(int aIndex)
|
||||||
|
{
|
||||||
|
if (aIndex == -1) {
|
||||||
|
aIndex = 0; // -1 = system default
|
||||||
|
}
|
||||||
|
if (aIndex >= (int) mDeviceIndexes.Length()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return mDeviceIndexes[aIndex]; // translate to mDevices index
|
||||||
|
}
|
||||||
|
|
||||||
int GetRecordingDeviceName(int aIndex, char aStrNameUTF8[128],
|
int GetRecordingDeviceName(int aIndex, char aStrNameUTF8[128],
|
||||||
char aStrGuidUTF8[128])
|
char aStrGuidUTF8[128])
|
||||||
{
|
{
|
||||||
if (!mDevices) {
|
int32_t devindex = DeviceIndex(aIndex);
|
||||||
|
if (!mDevices || devindex < 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int devindex = aIndex == -1 ? 0 : aIndex;
|
|
||||||
PR_snprintf(aStrNameUTF8, 128, "%s%s", aIndex == -1 ? "default: " : "",
|
PR_snprintf(aStrNameUTF8, 128, "%s%s", aIndex == -1 ? "default: " : "",
|
||||||
mDevices->device[devindex]->friendly_name);
|
mDevices->device[devindex]->friendly_name);
|
||||||
aStrGuidUTF8[0] = '\0';
|
aStrGuidUTF8[0] = '\0';
|
||||||
|
@ -203,12 +234,14 @@ public:
|
||||||
|
|
||||||
void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
|
void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(mDevices);
|
||||||
|
|
||||||
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
|
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
|
||||||
ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
|
ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
|
||||||
if (ptrVoERender) {
|
if (ptrVoERender) {
|
||||||
ptrVoERender->SetExternalRecordingStatus(true);
|
ptrVoERender->SetExternalRecordingStatus(true);
|
||||||
}
|
}
|
||||||
aGraph->OpenAudioInput(nullptr, aListener);
|
aGraph->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
|
void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
|
||||||
|
@ -218,21 +251,21 @@ public:
|
||||||
|
|
||||||
int SetRecordingDevice(int aIndex)
|
int SetRecordingDevice(int aIndex)
|
||||||
{
|
{
|
||||||
// Relevant with devid support
|
int32_t devindex = DeviceIndex(aIndex);
|
||||||
return 1;
|
if (!mDevices || devindex < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
mSelectedDevice = devindex;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~AudioInputCubeb() {
|
~AudioInputCubeb() {}
|
||||||
{
|
|
||||||
if (mDevices) {
|
|
||||||
cubeb_device_collection_destroy(mDevices);
|
|
||||||
mDevices = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cubeb_device_collection* mDevices;
|
nsTArray<int> mDeviceIndexes;
|
||||||
|
int mSelectedDevice;
|
||||||
|
static cubeb_device_collection *mDevices;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioInputWebRTC final : public AudioInput
|
class AudioInputWebRTC final : public AudioInput
|
||||||
|
|
Загрузка…
Ссылка в новой задаче