Bug 1221587: use cubeb devids to select input devices r=padenot

--HG--
extra : commitid : AH6Lt4KfNaF
This commit is contained in:
Randell Jesup 2016-01-21 11:51:36 -05:00
Родитель be7d8f1d36
Коммит 46878bf96f
7 изменённых файлов: 128 добавлений и 54 удалений

Просмотреть файл

@ -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