mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1404977 - Part 8 - Tell the MSG the MediaEngineAudioSource are now independent and that we can have multiple of them, cleanup the MSG-side API for managing them. r=pehrsons
The MSG now can feed microphone data to all its input listeners. This paves the way for multiple input device, if we feel it's needed at some point, but does not implement it. The method for adding/removing inputs are also cleaned up. MozReview-Commit-ID: 9OX4Da6Gjq2 --HG-- extra : rebase_source : 043c486e53f9220ae61fd788ed86064ba723f1a4
This commit is contained in:
parent
9b7c6c96da
commit
064d4d9b47
@ -770,8 +770,9 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream)
|
||||
}
|
||||
output.AppendNullData(toWrite);
|
||||
LOG(LogLevel::Verbose,
|
||||
("MediaStream %p writing %" PRId64 " padding slsamples for %f to "
|
||||
("%p MediaStream %p writing %" PRId64 " padding slsamples for %f to "
|
||||
"%f (samples %" PRId64 " to %" PRId64 ")",
|
||||
this,
|
||||
aStream,
|
||||
toWrite,
|
||||
MediaTimeToSeconds(t),
|
||||
@ -789,50 +790,36 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream)
|
||||
// Need unique id for stream & track - and we want it to match the inserter
|
||||
output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
|
||||
mMixer,
|
||||
AudioChannelCount(),
|
||||
AudioOutputChannelCount(),
|
||||
mSampleRate);
|
||||
}
|
||||
return ticksWritten;
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::OpenAudioInputImpl(int aID,
|
||||
AudioDataListener *aListener)
|
||||
MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(OnGraphThread());
|
||||
// Bug 1238038 Need support for multiple mics at once
|
||||
if (mInputDeviceUsers.Count() > 0 &&
|
||||
!mInputDeviceUsers.Get(aListener, nullptr)) {
|
||||
NS_ASSERTION(false, "Input from multiple mics not yet supported; bug 1238038");
|
||||
// Need to support separate input-only AudioCallback drivers; they'll
|
||||
// call us back on "other" threads. We will need to echo-cancel them, though.
|
||||
// Only allow one device per MSG (hence, per document), but allow opening a
|
||||
// device multiple times
|
||||
nsTArray<RefPtr<AudioDataListener>>& listeners = mInputDeviceUsers.GetOrInsert(aID);
|
||||
if (listeners.IsEmpty() && mInputDeviceUsers.Count() > 1) {
|
||||
// We don't support opening multiple input device in a graph for now.
|
||||
listeners.RemoveElement(aID);
|
||||
return;
|
||||
}
|
||||
mInputWanted = true;
|
||||
|
||||
// Add to count of users for this ID.
|
||||
// XXX Since we can't rely on IDs staying valid (ugh), use the listener as
|
||||
// a stand-in for the ID. Fix as part of support for multiple-captures
|
||||
// (Bug 1238038)
|
||||
uint32_t count = 0;
|
||||
mInputDeviceUsers.Get(aListener, &count); // ok if this fails
|
||||
count++;
|
||||
mInputDeviceUsers.Put(aListener, count); // creates a new entry in the hash if needed
|
||||
MOZ_ASSERT(!listeners.Contains(aListener), "Don't add a listener twice.");
|
||||
|
||||
if (count == 1) { // first open for this listener
|
||||
// aID is a cubeb_devid, and we assume that opaque ptr is valid until
|
||||
// we close cubeb.
|
||||
listeners.AppendElement(aListener);
|
||||
|
||||
if (listeners.Length() == 1) { // first open for this device
|
||||
mInputDeviceID = aID;
|
||||
mAudioInputs.AppendElement(aListener); // always monitor speaker data
|
||||
|
||||
// Switch Drivers since we're adding input (to input-only or full-duplex)
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
|
||||
AudioCallbackDriver* driver = new AudioCallbackDriver(this);
|
||||
driver->SetMicrophoneActive(true);
|
||||
LOG(
|
||||
LogLevel::Debug,
|
||||
("OpenAudioInput: starting new AudioCallbackDriver(input) %p", driver));
|
||||
AudioCallbackDriver* driver = new AudioCallbackDriver(this, AudioInputChannelCount());
|
||||
LOG(
|
||||
LogLevel::Debug,
|
||||
("OpenAudioInput: starting new AudioCallbackDriver(input) %p", driver));
|
||||
@ -840,15 +827,14 @@ MediaStreamGraphImpl::OpenAudioInputImpl(int aID,
|
||||
CurrentDriver()->SwitchAtNextIteration(driver);
|
||||
} else {
|
||||
LOG(LogLevel::Error, ("OpenAudioInput in shutdown!"));
|
||||
LOG(LogLevel::Debug, ("OpenAudioInput in shutdown!"));
|
||||
NS_ASSERTION(false, "Can't open cubeb inputs in shutdown");
|
||||
MOZ_ASSERT_UNREACHABLE("Can't open cubeb inputs in shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaStreamGraphImpl::OpenAudioInput(int aID,
|
||||
AudioDataListener *aListener)
|
||||
MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener)
|
||||
{
|
||||
// So, so, so annoying. Can't AppendMessage except on Mainthread
|
||||
if (!NS_IsMainThread()) {
|
||||
@ -862,15 +848,15 @@ MediaStreamGraphImpl::OpenAudioInput(int aID,
|
||||
}
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStreamGraphImpl *aGraph, int aID,
|
||||
AudioDataListener *aListener) :
|
||||
Message(MediaStreamGraphImpl *aGraph, CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener) :
|
||||
ControlMessage(nullptr), mGraph(aGraph), mID(aID), mListener(aListener) {}
|
||||
void Run() override
|
||||
{
|
||||
mGraph->OpenAudioInputImpl(mID, mListener);
|
||||
}
|
||||
MediaStreamGraphImpl *mGraph;
|
||||
int mID;
|
||||
CubebUtils::AudioDeviceID mID;
|
||||
RefPtr<AudioDataListener> mListener;
|
||||
};
|
||||
// XXX Check not destroyed!
|
||||
@ -879,24 +865,31 @@ MediaStreamGraphImpl::OpenAudioInput(int aID,
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
|
||||
MediaStreamGraphImpl::CloseAudioInputImpl(Maybe<CubebUtils::AudioDeviceID>& aID, AudioDataListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(OnGraphThread());
|
||||
uint32_t count;
|
||||
DebugOnly<bool> result = mInputDeviceUsers.Get(aListener, &count);
|
||||
MOZ_ASSERT(result);
|
||||
if (--count > 0) {
|
||||
mInputDeviceUsers.Put(aListener, count);
|
||||
return; // still in use
|
||||
MOZ_ASSERT(OnGraphThreadOrNotRunning());
|
||||
// It is possible to not know the ID here, find it first.
|
||||
if (aID.isNothing()) {
|
||||
for (auto iter = mInputDeviceUsers.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Data().Contains(aListener)) {
|
||||
aID = Some(iter.Key());
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(aID.isSome(), "Closing an audio input that was not opened.");
|
||||
}
|
||||
mInputDeviceUsers.Remove(aListener);
|
||||
mInputDeviceID = -1;
|
||||
mInputWanted = false;
|
||||
AudioCallbackDriver *driver = CurrentDriver()->AsAudioCallbackDriver();
|
||||
if (driver) {
|
||||
driver->RemoveInputListener(aListener);
|
||||
|
||||
nsTArray<RefPtr<AudioDataListener>>* listeners = mInputDeviceUsers.GetValue(aID.value());
|
||||
|
||||
MOZ_ASSERT(listeners);
|
||||
DebugOnly<bool> wasPresent = listeners->RemoveElement(aListener);
|
||||
MOZ_ASSERT(wasPresent);
|
||||
if (!listeners->IsEmpty()) {
|
||||
// There is still a consumer for this audio input device
|
||||
return;
|
||||
}
|
||||
mAudioInputs.RemoveElement(aListener);
|
||||
|
||||
mInputDeviceID = nullptr; // reset to default
|
||||
mInputDeviceUsers.Remove(aID.value());
|
||||
|
||||
// Switch Drivers since we're adding or removing an input (to nothing/system or output only)
|
||||
bool audioTrackPresent = AudioTrackPresent();
|
||||
@ -906,9 +899,9 @@ MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
|
||||
GraphDriver* driver;
|
||||
if (audioTrackPresent) {
|
||||
// We still have audio output
|
||||
LOG(LogLevel::Debug, ("CloseInput: output present (AudioCallback)"));
|
||||
LOG(LogLevel::Debug, ("%p: CloseInput: output present (AudioCallback)", this));
|
||||
|
||||
driver = new AudioCallbackDriver(this);
|
||||
driver = new AudioCallbackDriver(this, AudioInputChannelCount());
|
||||
CurrentDriver()->SwitchAtNextIteration(driver);
|
||||
} else if (CurrentDriver()->AsAudioCallbackDriver()) {
|
||||
LOG(LogLevel::Debug,
|
||||
@ -921,41 +914,118 @@ MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::CloseAudioInput(AudioDataListener *aListener)
|
||||
MediaStreamGraphImpl::CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID, AudioDataListener* aListener)
|
||||
{
|
||||
// So, so, so annoying. Can't AppendMessage except on Mainthread
|
||||
if (!NS_IsMainThread()) {
|
||||
RefPtr<nsIRunnable> runnable =
|
||||
WrapRunnable(this,
|
||||
&MediaStreamGraphImpl::CloseAudioInput,
|
||||
aID,
|
||||
RefPtr<AudioDataListener>(aListener));
|
||||
mAbstractMainThread->Dispatch(runnable.forget());
|
||||
return;
|
||||
}
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStreamGraphImpl *aGraph, AudioDataListener *aListener) :
|
||||
ControlMessage(nullptr), mGraph(aGraph), mListener(aListener) {}
|
||||
Message(MediaStreamGraphImpl *aGraph,
|
||||
Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener)
|
||||
: ControlMessage(nullptr),
|
||||
mGraph(aGraph),
|
||||
mID(aID),
|
||||
mListener(aListener)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
mGraph->CloseAudioInputImpl(mListener);
|
||||
mGraph->CloseAudioInputImpl(mID, mListener);
|
||||
}
|
||||
MediaStreamGraphImpl *mGraph;
|
||||
Maybe<CubebUtils::AudioDeviceID> mID;
|
||||
RefPtr<AudioDataListener> mListener;
|
||||
};
|
||||
this->AppendMessage(MakeUnique<Message>(this, aListener));
|
||||
this->AppendMessage(MakeUnique<Message>(this, aID, aListener));
|
||||
}
|
||||
|
||||
// All AudioInput listeners get the same speaker data (at least for now).
|
||||
void
|
||||
MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels)
|
||||
MediaStreamGraphImpl::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels)
|
||||
{
|
||||
for (auto& listener : mAudioInputs) {
|
||||
if (!mInputDeviceID) {
|
||||
return;
|
||||
}
|
||||
// When/if we decide to support multiple input devices per graph, this needs
|
||||
// to loop over them.
|
||||
nsTArray<RefPtr<AudioDataListener>>* listeners = mInputDeviceUsers.GetValue(mInputDeviceID);
|
||||
MOZ_ASSERT(listeners);
|
||||
for (auto& listener : *listeners) {
|
||||
listener->NotifyOutputData(this, aBuffer, aFrames, aRate, aChannels);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
// Either we have an audio input device, or we just removed the audio input
|
||||
// this iteration, and we're switching back to an output-only driver next
|
||||
// iteration.
|
||||
MOZ_ASSERT(mInputDeviceID || CurrentDriver()->Switching());
|
||||
}
|
||||
#endif
|
||||
nsTArray<RefPtr<AudioDataListener>>* listeners = mInputDeviceUsers.GetValue(mInputDeviceID);
|
||||
MOZ_ASSERT(listeners);
|
||||
for (auto& listener : *listeners) {
|
||||
listener->NotifyInputData(this, aBuffer, aFrames, aRate, aChannels);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamGraphImpl::DeviceChanged()
|
||||
{
|
||||
MOZ_ASSERT(!OnGraphThread());
|
||||
if (!mInputDeviceID) {
|
||||
return;
|
||||
}
|
||||
nsTArray<RefPtr<AudioDataListener>>* listeners = mInputDeviceUsers.GetValue(mInputDeviceID);
|
||||
for (auto& listener : *listeners) {
|
||||
listener->DeviceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamGraphImpl::ReevaluateInputDevice()
|
||||
{
|
||||
MOZ_ASSERT(OnGraphThread());
|
||||
bool needToSwitch = false;
|
||||
|
||||
if (CurrentDriver()->AsAudioCallbackDriver()) {
|
||||
AudioCallbackDriver* audioCallbackDriver = CurrentDriver()->AsAudioCallbackDriver();
|
||||
if (audioCallbackDriver->InputChannelCount() != AudioInputChannelCount()) {
|
||||
needToSwitch = true;
|
||||
}
|
||||
} else {
|
||||
// We're already in the process of switching to a audio callback driver,
|
||||
// which will happen at the next iteration.
|
||||
// However, maybe it's not the correct number of channels. Re-query the
|
||||
// correct channel amount at this time.
|
||||
#ifdef DEBUG
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(CurrentDriver()->Switching());
|
||||
#endif
|
||||
needToSwitch = true;
|
||||
}
|
||||
if (needToSwitch) {
|
||||
AudioCallbackDriver* newDriver = new AudioCallbackDriver(this, AudioInputChannelCount());
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
CurrentDriver()->SwitchAtNextIteration(newDriver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaStreamGraph::OnGraphThreadOrNotRunning() const
|
||||
{
|
||||
@ -2707,22 +2777,22 @@ SourceMediaStream::SourceMediaStream()
|
||||
}
|
||||
|
||||
nsresult
|
||||
SourceMediaStream::OpenAudioInput(int aID,
|
||||
SourceMediaStream::OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener *aListener)
|
||||
{
|
||||
if (GraphImpl()) {
|
||||
mInputListener = aListener;
|
||||
return GraphImpl()->OpenAudioInput(aID, aListener);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
MOZ_ASSERT(GraphImpl());
|
||||
mInputListener = aListener;
|
||||
return GraphImpl()->OpenAudioInput(aID, aListener);
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::CloseAudioInput()
|
||||
SourceMediaStream::CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(mInputListener == aListener);
|
||||
// Destroy() may have run already and cleared this
|
||||
if (GraphImpl() && mInputListener) {
|
||||
GraphImpl()->CloseAudioInput(mInputListener);
|
||||
GraphImpl()->CloseAudioInput(aID, aListener);
|
||||
}
|
||||
mInputListener = nullptr;
|
||||
}
|
||||
@ -2730,7 +2800,8 @@ SourceMediaStream::CloseAudioInput()
|
||||
void
|
||||
SourceMediaStream::DestroyImpl()
|
||||
{
|
||||
CloseAudioInput();
|
||||
Maybe<CubebUtils::AudioDeviceID> id = Nothing();
|
||||
CloseAudioInput(id, mInputListener);
|
||||
|
||||
GraphImpl()->AssertOnGraphThreadOrNotRunning();
|
||||
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
|
||||
@ -2967,7 +3038,8 @@ SourceMediaStream::FinishAddTracks()
|
||||
MutexAutoLock lock(mMutex);
|
||||
mUpdateTracks.AppendElements(std::move(mPendingTracks));
|
||||
LOG(LogLevel::Debug,
|
||||
("FinishAddTracks: %lu/%lu",
|
||||
("%p: FinishAddTracks: %lu/%lu",
|
||||
GraphImpl(),
|
||||
(long)mPendingTracks.Length(),
|
||||
(long)mUpdateTracks.Length()));
|
||||
if (GraphImpl()) {
|
||||
@ -3316,45 +3388,6 @@ SourceMediaStream::HasPendingAudioTrack()
|
||||
return audioTrackPresent;
|
||||
}
|
||||
|
||||
bool
|
||||
SourceMediaStream::OpenNewAudioCallbackDriver(AudioDataListener * aListener)
|
||||
{
|
||||
// Can't AppendMessage except on Mainthread. This is an ungly trick
|
||||
// to bounce the message in mainthread and then in MSG thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
RefPtr<nsIRunnable> runnable =
|
||||
WrapRunnable(this,
|
||||
&SourceMediaStream::OpenNewAudioCallbackDriver,
|
||||
aListener);
|
||||
GraphImpl()->mAbstractMainThread->Dispatch(runnable.forget());
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioCallbackDriver* nextDriver = new AudioCallbackDriver(GraphImpl());
|
||||
nextDriver->SetInputListener(aListener);
|
||||
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStream* aStream, AudioCallbackDriver* aNextDriver)
|
||||
: ControlMessage(aStream)
|
||||
, mNextDriver(aNextDriver)
|
||||
{MOZ_ASSERT(mNextDriver);}
|
||||
void Run() override
|
||||
{
|
||||
MediaStreamGraphImpl* graphImpl = mNextDriver->GraphImpl();
|
||||
MonitorAutoLock mon(graphImpl->GetMonitor());
|
||||
MOZ_ASSERT(graphImpl->LifecycleStateRef() ==
|
||||
MediaStreamGraphImpl::LifecycleState::LIFECYCLE_RUNNING);
|
||||
graphImpl->CurrentDriver()->SwitchAtNextIteration(mNextDriver);
|
||||
}
|
||||
AudioCallbackDriver* mNextDriver;
|
||||
};
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, nextDriver));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MediaInputPort::Init()
|
||||
{
|
||||
@ -3611,10 +3644,8 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested,
|
||||
: MediaStreamGraph(aSampleRate)
|
||||
, mFirstCycleBreaker(0)
|
||||
, mPortCount(0)
|
||||
, mInputWanted(false)
|
||||
, mInputDeviceID(-1)
|
||||
, mOutputWanted(true)
|
||||
, mOutputDeviceID(-1)
|
||||
, mInputDeviceID(nullptr)
|
||||
, mOutputDeviceID(nullptr)
|
||||
, mNeedAnotherIteration(false)
|
||||
, mGraphDriverAsleep(false)
|
||||
, mMonitor("MediaStreamGraphImpl")
|
||||
@ -3792,6 +3823,12 @@ MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
|
||||
MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
|
||||
|
||||
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
|
||||
|
||||
if (!graph->mNonRealtimeProcessing) {
|
||||
// Start the graph, but don't produce anything
|
||||
graph->StartNonRealtimeProcessing(0);
|
||||
}
|
||||
|
||||
graph->ForceShutDown(nullptr);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "StreamTracks.h"
|
||||
#include "VideoSegment.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "nsAutoPtr.h"
|
||||
@ -708,10 +709,11 @@ public:
|
||||
// last stream referencing an input goes away, so it can close the cubeb
|
||||
// input. Also note: callable on any thread (though it bounces through
|
||||
// MainThread to set the command if needed).
|
||||
nsresult OpenAudioInput(int aID,
|
||||
AudioDataListener *aListener);
|
||||
nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener);
|
||||
// Note: also implied when Destroy() happens
|
||||
void CloseAudioInput();
|
||||
void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener);
|
||||
|
||||
// MediaStreamGraph thread only
|
||||
void DestroyImpl() override;
|
||||
@ -838,8 +840,6 @@ public:
|
||||
return mStreamTracksStartTimeStamp;
|
||||
}
|
||||
|
||||
bool OpenNewAudioCallbackDriver(AudioDataListener *aListener);
|
||||
|
||||
// XXX need a Reset API
|
||||
|
||||
friend class MediaStreamGraphImpl;
|
||||
@ -1324,13 +1324,10 @@ public:
|
||||
// Idempotent
|
||||
static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
|
||||
|
||||
virtual nsresult OpenAudioInput(int aID,
|
||||
AudioDataListener *aListener)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
virtual void CloseAudioInput(AudioDataListener *aListener) {}
|
||||
|
||||
virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener) = 0;
|
||||
virtual void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener) = 0;
|
||||
// Control API.
|
||||
/**
|
||||
* Create a stream that a media decoder (or some other source of
|
||||
@ -1408,13 +1405,6 @@ public:
|
||||
already_AddRefed<MediaInputPort> ConnectToCaptureStream(
|
||||
uint64_t aWindowId, MediaStream* aMediaStream);
|
||||
|
||||
/**
|
||||
* Data going to the speakers from the GraphDriver's DataCallback
|
||||
* to notify any listeners (for echo cancellation).
|
||||
*/
|
||||
void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels);
|
||||
|
||||
void AssertOnGraphThreadOrNotRunning() const
|
||||
{
|
||||
MOZ_ASSERT(OnGraphThreadOrNotRunning());
|
||||
@ -1445,11 +1435,6 @@ protected:
|
||||
* at construction.
|
||||
*/
|
||||
TrackRate mSampleRate;
|
||||
|
||||
/**
|
||||
* CloseAudioInput is async, so hold a reference here.
|
||||
*/
|
||||
nsTArray<RefPtr<AudioDataListener>> mAudioInputs;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsINamed.h"
|
||||
#include "nsIRunnable.h"
|
||||
@ -378,17 +378,41 @@ public:
|
||||
* to the audio output stream. Returns the number of frames played.
|
||||
*/
|
||||
StreamTime PlayAudio(MediaStream* aStream);
|
||||
/**
|
||||
* No more data will be forthcoming for aStream. The stream will end
|
||||
* at the current buffer end point. The StreamTracks's tracks must be
|
||||
* explicitly set to finished by the caller.
|
||||
*/
|
||||
void OpenAudioInputImpl(int aID,
|
||||
AudioDataListener *aListener);
|
||||
virtual nsresult OpenAudioInput(int aID,
|
||||
AudioDataListener *aListener) override;
|
||||
void CloseAudioInputImpl(AudioDataListener *aListener);
|
||||
virtual void CloseAudioInput(AudioDataListener *aListener) override;
|
||||
/* Runs off a message on the graph thread when something requests audio from
|
||||
* an input audio device of ID aID, and delivers the input audio frames to
|
||||
* aListener. */
|
||||
void OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener);
|
||||
/* Called on the main thread when something requests audio from an input
|
||||
* audio device aID. */
|
||||
virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
|
||||
AudioDataListener* aListener) override;
|
||||
/* Runs off a message on the graph when input audio from aID is not needed
|
||||
* anymore, for a particular stream. It can be that other streams still need
|
||||
* audio from this audio input device. */
|
||||
void CloseAudioInputImpl(Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener);
|
||||
/* Called on the main thread when input audio from aID is not needed
|
||||
* anymore, for a particular stream. It can be that other streams still need
|
||||
* audio from this audio input device. */
|
||||
virtual void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID,
|
||||
AudioDataListener* aListener) override;
|
||||
/* Called on the graph thread when the input device settings should be
|
||||
* reevaluated, for example, if the channel count of the input stream should
|
||||
* be changed . */
|
||||
void ReevaluateInputDevice();
|
||||
/* Called on the graph thread when there is new output data for listeners.
|
||||
* This is the mixed audio output of this MediaStreamGraph. */
|
||||
void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels);
|
||||
/* Called on the graph thread when there is new input data for listeners. This
|
||||
* is the raw audio input for this MediaStreamGraph. */
|
||||
void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels);
|
||||
/* Called every time there are changes to input/output audio devices like
|
||||
* plug/unplug etc. Depending on the platform, this can be called on
|
||||
* different thread. This function is called with the MSG lock held. */
|
||||
void DeviceChanged();
|
||||
|
||||
/**
|
||||
* Compute how much stream data we would like to buffer for aStream.
|
||||
@ -427,11 +451,46 @@ public:
|
||||
mStreamOrderDirty = true;
|
||||
}
|
||||
|
||||
uint32_t AudioChannelCount() const
|
||||
uint32_t AudioOutputChannelCount() const
|
||||
{
|
||||
return mOutputChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* The audio input channel count for a MediaStreamGraph is the max of all the
|
||||
* channel counts requested by the listeners. The max channel count is
|
||||
* delivered to the listeners themselves, and they take care of downmixing.
|
||||
*/
|
||||
uint32_t AudioInputChannelCount()
|
||||
{
|
||||
MOZ_ASSERT(OnGraphThreadOrNotRunning());
|
||||
|
||||
if (!mInputDeviceID) {
|
||||
#ifndef ANDROID
|
||||
MOZ_ASSERT(mInputDeviceUsers.Count() == 0,
|
||||
"If running on a platform other than android,"
|
||||
"an explicit device id should be present");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
uint32_t maxInputChannels = 0;
|
||||
// When/if we decide to support multiple input device per graph, this needs
|
||||
// loop over them.
|
||||
nsTArray<RefPtr<AudioDataListener>>* listeners =
|
||||
mInputDeviceUsers.GetValue(mInputDeviceID);
|
||||
MOZ_ASSERT(listeners);
|
||||
for (const auto& listener : *listeners) {
|
||||
maxInputChannels =
|
||||
std::max(maxInputChannels, listener->InputChannelCount());
|
||||
}
|
||||
return maxInputChannels;
|
||||
}
|
||||
|
||||
CubebUtils::AudioDeviceID InputDeviceID()
|
||||
{
|
||||
return mInputDeviceID;
|
||||
}
|
||||
|
||||
double MediaTimeToSeconds(GraphTime aTime) const
|
||||
{
|
||||
NS_ASSERTION(aTime > -STREAM_TIME_MAX && aTime <= STREAM_TIME_MAX,
|
||||
@ -626,16 +685,22 @@ public:
|
||||
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
|
||||
* Devices to use for cubeb input & output, or nullptr for default device.
|
||||
* A MediaStreamGraph always has an output (even if silent).
|
||||
* If `mInputDeviceUsers.Count() != 0`, this MediaStreamGraph wants audio
|
||||
* input.
|
||||
*
|
||||
* In any case, the number of channels to use can be queried (on the graph
|
||||
* thread) by AudioInputChannelCount() and AudioOutputChannelCount().
|
||||
*/
|
||||
bool mInputWanted;
|
||||
int mInputDeviceID;
|
||||
bool mOutputWanted;
|
||||
int mOutputDeviceID;
|
||||
// Maps AudioDataListeners to a usecount of streams using the listener
|
||||
// so we can know when it's no longer in use.
|
||||
nsDataHashtable<nsPtrHashKey<AudioDataListener>, uint32_t> mInputDeviceUsers;
|
||||
CubebUtils::AudioDeviceID mInputDeviceID;
|
||||
CubebUtils::AudioDeviceID mOutputDeviceID;
|
||||
// Maps AudioDeviceID to an array of their users (that are listeners). This is
|
||||
// used to deliver audio input frames and to notify the listeners that the
|
||||
// audio device that delivers the audio frames has changed.
|
||||
// This is only touched on the graph thread.
|
||||
nsDataHashtable<nsVoidPtrHashKey,
|
||||
nsTArray<RefPtr<AudioDataListener>>> mInputDeviceUsers;
|
||||
|
||||
// True if the graph needs another iteration after the current iteration.
|
||||
Atomic<bool> mNeedAnotherIteration;
|
||||
|
Loading…
Reference in New Issue
Block a user