Bug 1221587: patch 8 - use cubeb devids to select input devices r?padenot
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -8,16 +8,18 @@
#define CubebUtils_h_
#include "cubeb/cubeb.h"
#include "mozilla/dom/AudioChannelBinding.h"
namespace mozilla {
namespace CubebUtils {
+typedef cubeb_devid AudioDeviceID;
+
// Initialize Audio Library. Some Audio backends require initializing the
// library before using it.
void InitLibrary();
// Shutdown Audio Library. Some Audio backends require shutting down the
// library after using it.
void ShutdownLibrary();
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -554,61 +554,64 @@ AudioCallbackDriver::AudioCallbackDriver
AudioCallbackDriver::~AudioCallbackDriver()
{
MOZ_ASSERT(mPromisesForOperation.IsEmpty());
}
void
AudioCallbackDriver::Init()
{
- cubeb_stream_params out_params;
- cubeb_stream_params in_params;
+ cubeb_stream_params output;
+ cubeb_stream_params input;
uint32_t latency;
MOZ_ASSERT(!NS_IsMainThread(),
"This is blocking and should never run on the main thread.");
- out_params.devid = nullptr; // XXX take from config for the graph
- mSampleRate = out_params.rate = CubebUtils::PreferredSampleRate();
+ output.devid = mGraphImpl->mOutputDeviceID;
+ mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
#if defined(__ANDROID__)
#if defined(MOZ_B2G)
- out_params.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
+ output.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
#else
- out_params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+ output.stream_type = CUBEB_STREAM_TYPE_MUSIC;
#endif
- if (out_params.stream_type == CUBEB_STREAM_TYPE_MAX) {
+ if (output.stream_type == CUBEB_STREAM_TYPE_MAX) {
NS_WARNING("Bad stream type");
return;
}
#else
(void)mAudioChannel;
#endif
- out_params.channels = mGraphImpl->AudioChannelCount();
+ output.channels = mGraphImpl->AudioChannelCount();
if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
- out_params.format = CUBEB_SAMPLE_S16NE;
+ output.format = CUBEB_SAMPLE_S16NE;
} 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.");
return;
}
- in_params = out_params;
- in_params.channels = 1; // change to support optional stereo capture
+ input = output;
+ input.channels = 1; // change to support optional stereo capture
+ input.devid = mGraphImpl->mInputDeviceID;
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.
// XXX Add support for adding/removing an input listener later.
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) {
mAudioStream.own(stream);
} else {
NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
// Fall back to a driver using a normal thread.
MonitorAutoLock lock(GraphImpl()->GetMonitor());
SetNextDriver(new SystemClockDriver(GraphImpl()));
NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -919,56 +919,66 @@ MediaStreamGraphImpl::PlayVideo(MediaStr
// If the stream has finished and the timestamps of all frames have expired
// then no more updates are required.
if (aStream->mFinished && !haveMultipleImages) {
aStream->mLastPlayedVideoFrame.SetNull();
}
}
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()) {
CurrentDriver()->SetInputListener(aListener);
} else {
// XXX Switch to callback driver
}
mAudioInputs.AppendElement(aListener); // always monitor speaker data
}
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
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(WrapRunnable(this,
&MediaStreamGraphImpl::OpenAudioInput,
- aName, aListener)); // XXX Fix! string need to copied
+ aID, aListener)); // XXX Fix! string need to copied
return NS_OK;
}
class Message : public ControlMessage {
public:
- Message(MediaStreamGraphImpl *aGraph, char *aName, AudioDataListener *aListener) :
- ControlMessage(nullptr), mGraph(aGraph), mName(aName), mListener(aListener) {}
+ Message(MediaStreamGraphImpl *aGraph, CubebUtils::AudioDeviceID aID,
+ AudioDataListener *aListener) :
+ ControlMessage(nullptr), mGraph(aGraph), mID(aID), mListener(aListener) {}
virtual void Run()
{
- mGraph->OpenAudioInputImpl(mName, mListener);
+ mGraph->OpenAudioInputImpl(mID, mListener);
}
MediaStreamGraphImpl *mGraph;
- char *mName; // XXX needs to copy
+ CubebUtils::AudioDeviceID mID;
RefPtr<AudioDataListener> mListener;
};
- this->AppendMessage(new Message(this, aName, aListener));
+ this->AppendMessage(new Message(this, aID, aListener));
return NS_OK;
}
void
MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
{
+ mInputDeviceID = nullptr;
+ mInputWanted = false;
CurrentDriver()->RemoveInputListener(aListener);
+ // XXX Switch Drivers
mAudioInputs.RemoveElement(aListener);
}
void
MediaStreamGraphImpl::CloseAudioInput(AudioDataListener *aListener)
{
// XXX So, so, so annoying. Can't AppendMessage except on Mainthread
if (!NS_IsMainThread()) {
@@ -2706,16 +2716,20 @@ ProcessedMediaStream::DestroyImpl()
// SetStreamOrderDirty(), for other reasons.
}
MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested,
TrackRate aSampleRate,
dom::AudioChannel aChannel)
: MediaStreamGraph(aSampleRate)
, mPortCount(0)
+ , mInputWanted(false)
+ , mInputDeviceID(nullptr)
+ , mOutputWanted(true)
+ , mOutputDeviceID(nullptr)
, mNeedAnotherIteration(false)
, mGraphDriverAsleep(false)
, mMonitor("MediaStreamGraphImpl")
, mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
, mEndTime(GRAPH_TIME_MAX)
, mForceShutDown(false)
, mPostedRunInStableStateEvent(false)
, mDetectedNotRunning(false)
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -1203,17 +1203,18 @@ public:
};
// Main thread only
static MediaStreamGraph* GetInstance(GraphDriverType aGraphDriverRequested,
dom::AudioChannel aChannel);
static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate);
// Idempotent
static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
- virtual nsresult OpenAudioInput(char *aName, AudioDataListener *aListener) {
+ virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
+ AudioDataListener *aListener) {
return NS_ERROR_FAILURE;
}
virtual void CloseAudioInput(AudioDataListener *aListener) {}
// Control API.
/**
* Create a stream that a media decoder (or some other source of
* media data, such as a camera) can write to.
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -345,18 +345,20 @@ public:
* Set the correct current video frame for stream aStream.
*/
void PlayVideo(MediaStream* aStream);
/**
* No more data will be forthcoming for aStream. The stream will end
* at the current buffer end point. The StreamBuffer's tracks must be
* explicitly set to finished by the caller.
*/
- void OpenAudioInputImpl(char *aName, AudioDataListener *aListener);
- virtual nsresult OpenAudioInput(char *aName, AudioDataListener *aListener) override;
+ void OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
+ AudioDataListener *aListener);
+ virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
+ AudioDataListener *aListener) override;
void CloseAudioInputImpl(AudioDataListener *aListener);
virtual void CloseAudioInput(AudioDataListener *aListener) override;
void FinishStream(MediaStream* aStream);
/**
* Compute how much stream data we would like to buffer for aStream.
*/
StreamTime GetDesiredBufferEnd(MediaStream* aStream);
@@ -577,16 +579,25 @@ public:
* Date of the last time we updated the main thread with the graph state.
*/
TimeStamp mLastMainThreadUpdate;
/**
* Number of active MediaInputPorts
*/
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.
Atomic<bool> mNeedAnotherIteration;
// GraphDriver may need a WakeUp() if something changes
Atomic<bool> mGraphDriverAsleep;
// mMonitor guards the data below.
// MediaStreamGraph normally does its work without holding mMonitor, so it is
// not safe to just grab mMonitor from some thread and start monkeying with
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -39,16 +39,18 @@ GetUserMediaLog()
#include "MediaEngineGonkVideoSource.h"
#endif
#undef LOG
#define LOG(args) MOZ_LOG(GetUserMediaLog(), mozilla::LogLevel::Debug, args)
namespace mozilla {
+cubeb_device_collection* AudioInputCubeb::mDevices = nullptr;
+
MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
: mMutex("mozilla::MediaEngineWebRTC"),
mVoiceEngine(nullptr),
mAudioInput(nullptr),
mAudioEngineInit(false)
{
#ifndef MOZ_B2G_CAMERA
nsCOMPtr<nsIComponentRegistrar> compMgr;
@@ -314,17 +316,24 @@ MediaEngineWebRTC::EnumerateAudioDevices
}
RefPtr<MediaEngineAudioSource> aSource;
NS_ConvertUTF8toUTF16 uuid(uniqueId);
if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) {
// We've already seen this device, just append.
aASources->AppendElement(aSource.get());
} 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);
mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
aASources->AppendElement(aSource);
}
}
}
void
@@ -354,16 +363,17 @@ MediaEngineWebRTC::Shutdown()
if (mVoiceEngine) {
mVoiceEngine->SetTraceCallback(nullptr);
webrtc::VoiceEngine::Delete(mVoiceEngine);
}
mVoiceEngine = nullptr;
mozilla::camera::Shutdown();
+ AudioInputCubeb::CleanupGlobalData();
if (mThread) {
mThread->Shutdown();
mThread = nullptr;
}
}
}
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -154,90 +154,123 @@ protected:
webrtc::VoiceEngine* mVoiceEngine;
};
class AudioInputCubeb : public AudioInput
{
public:
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;
+ }
+ }
virtual int GetNumOfRecordingDevices(int& aDevices)
{
- // devices = cubeb_get_num_devices(...)
- if (CUBEB_OK != cubeb_enumerate_devices(CubebUtils::GetCubebContext(),
- CUBEB_DEVICE_TYPE_INPUT,
- &mDevices)) {
- 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
+ if (!mDevices) {
+ if (CUBEB_OK != cubeb_enumerate_devices(CubebUtils::GetCubebContext(),
+ CUBEB_DEVICE_TYPE_INPUT,
+ &mDevices)) {
+ return 0;
}
}
+ // 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;
}
+ 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
+ }
+
virtual int GetRecordingDeviceName(int aIndex, char aStrNameUTF8[128],
char aStrGuidUTF8[128])
{
- if (!mDevices) {
+ int32_t devindex = DeviceIndex(aIndex);
+ if (!mDevices || devindex < 0) {
return 1;
}
- int devindex = aIndex == -1 ? 0 : aIndex;
PR_snprintf(aStrNameUTF8, 128, "%s%s", aIndex == -1 ? "default: " : "",
mDevices->device[devindex]->friendly_name);
aStrGuidUTF8[0] = '\0';
return 0;
}
virtual int GetRecordingDeviceStatus(bool& aIsAvailable)
{
// With cubeb, we only expose devices of type CUBEB_DEVICE_TYPE_INPUT
aIsAvailable = true;
return 0;
}
virtual void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
{
+ MOZ_ASSERT(mDevices);
+
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
if (ptrVoERender) {
ptrVoERender->SetExternalRecordingStatus(true);
}
- aGraph->OpenAudioInput(nullptr, aListener);
+ aGraph->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
}
virtual void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
{
aGraph->CloseAudioInput(aListener);
}
virtual int SetRecordingDevice(int aIndex)
{
- // Relevant with devid support
- return 1;
+ int32_t devindex = DeviceIndex(aIndex);
+ if (!mDevices || devindex < 0) {
+ return 1;
+ }
+ mSelectedDevice = devindex;
+ return 0;
}
protected:
- virtual ~AudioInputCubeb()
- {
- if (mDevices) {
- cubeb_device_collection_destroy(mDevices);
- mDevices = nullptr;
- }
- }
+ virtual ~AudioInputCubeb() {}
private:
- cubeb_device_collection* mDevices;
+ nsTArray<int> mDeviceIndexes;
+ int mSelectedDevice;
+ static cubeb_device_collection *mDevices;
};
class AudioInputWebRTC : public AudioInput
{
public:
AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {}
virtual int GetNumOfRecordingDevices(int& aDevices)