Bug 1238038 - Allow opening multiple devices r=padenot

This patch allows website users to open multiple microphones in the same
MediaTrackGraph.

Depends on D138189

Differential Revision: https://phabricator.services.mozilla.com/D138726
This commit is contained in:
Chun-Min Chang 2022-04-18 18:45:36 +00:00
Родитель 9fcad09de8
Коммит 2c01e7fdbf
13 изменённых файлов: 1871 добавлений и 462 удалений

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

@ -70,6 +70,9 @@ void AudioInputSource::Start() {
// operations to the task thread.
MOZ_ASSERT(mTaskThread);
// mSPSCQueue will have a new consumer.
mSPSCQueue.ResetConsumerThreadId();
LOG("AudioInputSource %p, start", this);
MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() mutable {
@ -116,7 +119,14 @@ void AudioInputSource::Stop() {
})));
}
AudioSegment AudioInputSource::GetAudioSegment(TrackTime aDuration) {
AudioSegment AudioInputSource::GetAudioSegment(TrackTime aDuration,
Consumer aConsumer) {
if (aConsumer == Consumer::Changed) {
// Reset queue's consumer to avoid hitting the assertion for checking the
// consistency of mSPSCQueue's mConsumerId in Dequeue.
mSPSCQueue.ResetConsumerThreadId();
}
AudioSegment raw;
while (mSPSCQueue.AvailableRead()) {
AudioChunk chunk;

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

@ -58,7 +58,11 @@ class AudioInputSource : public CubebInputStream::Listener {
// Stops producing audio data.
void Stop();
// Returns the AudioSegment with aDuration of data inside.
AudioSegment GetAudioSegment(TrackTime aDuration);
// The graph thread can change behind the scene, e.g., cubeb stream reinit due
// to default output device changed). When this happens, we need to notify
// mSPSCQueue to change its data consumer.
enum class Consumer { Same, Changed };
AudioSegment GetAudioSegment(TrackTime aDuration, Consumer aConsumer);
// CubebInputStream::Listener interface: These are used only for the
// underlying audio stream. No user should call these APIs.

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

@ -55,60 +55,222 @@ namespace mozilla {
#define TRACK_GRAPH_LOGE(msg, ...) \
TRACK_GRAPH_LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
/* static */
Result<RefPtr<DeviceInputTrack>, nsresult> DeviceInputTrack::OpenAudio(
MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aDeviceId,
const PrincipalHandle& aPrincipalHandle, AudioDataListener* aListener) {
#ifdef CONSUMER_GRAPH_LOG_INTERNAL
# undef CONSUMER_GRAPH_LOG_INTERNAL
#endif // CONSUMER_GRAPH_LOG_INTERNAL
#define CONSUMER_GRAPH_LOG_INTERNAL(level, msg, ...) \
LOG_INTERNAL( \
level, "(Graph %p, Driver %p) DeviceInputConsumerTrack %p, " msg, \
this->mGraph, this->mGraph->CurrentDriver(), this, ##__VA_ARGS__)
#ifdef CONSUMER_GRAPH_LOGV
# undef CONSUMER_GRAPH_LOGV
#endif // CONSUMER_GRAPH_LOGV
#define CONSUMER_GRAPH_LOGV(msg, ...) \
CONSUMER_GRAPH_LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
DeviceInputConsumerTrack::DeviceInputConsumerTrack(TrackRate aSampleRate)
: ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO,
new AudioSegment()) {}
void DeviceInputConsumerTrack::ConnectDeviceInput(
CubebUtils::AudioDeviceID aId, AudioDataListener* aListener,
const PrincipalHandle& aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GraphImpl());
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mListener);
MOZ_ASSERT(!mDeviceInputTrack);
MOZ_ASSERT(mDeviceId.isNothing());
RefPtr<DeviceInputTrack> track = aGraph->GetNativeInputTrack();
if (!track) {
track =
new NativeInputTrack(aGraph->GraphRate(), aDeviceId, aPrincipalHandle);
LOG("Create NativeInputTrack %p in MTG %p for device %p", track.get(),
aGraph, aDeviceId);
aGraph->AddTrack(track);
// Add the listener before opening the device so an open device always has a
// non-zero input channel count.
track->AddDataListener(aListener);
aGraph->OpenAudioInput(track);
} else if (track->mDeviceId != aDeviceId) {
// We only allows one device per MediaTrackGraph for now.
LOGE("Device %p is not native device", aDeviceId);
return Err(NS_ERROR_INVALID_ARG);
} else {
MOZ_ASSERT(track->mUserCount > 0);
track->AddDataListener(aListener);
}
MOZ_ASSERT(track->mDeviceId == aDeviceId);
mListener = aListener;
mDeviceId.emplace(aId);
track->mUserCount += 1;
LOG("DeviceInputTrack %p (device %p) in MTG %p has %d users now", track.get(),
track->mDeviceId, aGraph, track->mUserCount);
if (track->mUserCount > 1) {
track->ReevaluateInputDevice();
mDeviceInputTrack =
DeviceInputTrack::OpenAudio(GraphImpl(), aId, aPrincipal, this);
LOG("Open device %p (DeviceInputTrack %p) for consumer %p", aId,
mDeviceInputTrack.get(), this);
mPort = AllocateInputPort(mDeviceInputTrack.get());
}
void DeviceInputConsumerTrack::DisconnectDeviceInput() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GraphImpl());
if (!mListener) {
MOZ_ASSERT(mDeviceId.isNothing());
return;
}
return track;
MOZ_ASSERT(mPort);
MOZ_ASSERT(mDeviceInputTrack);
MOZ_ASSERT(mDeviceId.isSome());
LOG("Close device %p (DeviceInputTrack %p) for consumer %p ", *mDeviceId,
mDeviceInputTrack.get(), this);
mPort->Destroy();
DeviceInputTrack::CloseAudio(mDeviceInputTrack.forget(), this);
mListener = nullptr;
mDeviceId = Nothing();
}
Maybe<CubebUtils::AudioDeviceID> DeviceInputConsumerTrack::DeviceId() const {
MOZ_ASSERT(NS_IsMainThread());
return mDeviceId;
}
NotNull<AudioDataListener*> DeviceInputConsumerTrack::GetAudioDataListener()
const {
MOZ_ASSERT(NS_IsMainThread());
return WrapNotNull(mListener.get());
}
bool DeviceInputConsumerTrack::ConnectToNativeDevice() const {
return mPort &&
mPort->GetSource()->AsDeviceInputTrack()->AsNativeInputTrack();
}
bool DeviceInputConsumerTrack::ConnectToNonNativeDevice() const {
return mPort &&
mPort->GetSource()->AsDeviceInputTrack()->AsNonNativeInputTrack();
}
void DeviceInputConsumerTrack::GetInputSourceData(AudioSegment& aOutput,
const MediaInputPort* aPort,
GraphTime aFrom,
GraphTime aTo) const {
MOZ_ASSERT(mGraph->OnGraphThread());
MOZ_ASSERT(aOutput.IsEmpty());
MediaTrack* source = aPort->GetSource();
GraphTime next;
for (GraphTime t = aFrom; t < aTo; t = next) {
MediaInputPort::InputInterval interval =
MediaInputPort::GetNextInputInterval(aPort, t);
interval.mEnd = std::min(interval.mEnd, aTo);
const bool inputEnded =
source->Ended() &&
source->GetEnd() <=
source->GraphTimeToTrackTimeWithBlocking(interval.mStart);
TrackTime ticks = interval.mEnd - interval.mStart;
next = interval.mEnd;
if (interval.mStart >= interval.mEnd) {
break;
}
if (inputEnded) {
aOutput.AppendNullData(ticks);
CONSUMER_GRAPH_LOGV(
"Getting %" PRId64
" ticks of null data from input port source (ended input)",
ticks);
} else if (interval.mInputIsBlocked) {
aOutput.AppendNullData(ticks);
CONSUMER_GRAPH_LOGV(
"Getting %" PRId64
" ticks of null data from input port source (blocked input)",
ticks);
} else if (source->IsSuspended()) {
aOutput.AppendNullData(ticks);
CONSUMER_GRAPH_LOGV(
"Getting %" PRId64
" ticks of null data from input port source (source is suspended)",
ticks);
} else {
TrackTime start =
source->GraphTimeToTrackTimeWithBlocking(interval.mStart);
TrackTime end = source->GraphTimeToTrackTimeWithBlocking(interval.mEnd);
MOZ_ASSERT(source->GetData<AudioSegment>()->GetDuration() >= end);
aOutput.AppendSlice(*source->GetData<AudioSegment>(), start, end);
CONSUMER_GRAPH_LOGV("Getting %" PRId64
" ticks of real data from input port source %p",
end - start, source);
}
}
}
/* static */
void DeviceInputTrack::CloseAudio(RefPtr<DeviceInputTrack>&& aTrack,
AudioDataListener* aListener) {
NotNull<RefPtr<DeviceInputTrack>> DeviceInputTrack::OpenAudio(
MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aDeviceId,
const PrincipalHandle& aPrincipalHandle,
DeviceInputConsumerTrack* aConsumer) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTrack);
MOZ_ASSERT(aTrack->mUserCount > 0);
MOZ_ASSERT(aConsumer);
MOZ_ASSERT(aGraph == aConsumer->GraphImpl());
aTrack->RemoveDataListener(aListener);
aTrack->mUserCount -= 1;
LOG("DeviceInputTrack %p (device %p) in MTG %p has %d users now",
aTrack.get(), aTrack->mDeviceId, aTrack->GraphImpl(), aTrack->mUserCount);
if (aTrack->mUserCount == 0) {
aTrack->GraphImpl()->CloseAudioInput(aTrack);
aTrack->Destroy();
RefPtr<DeviceInputTrack> track =
aGraph->GetDeviceInputTrackMainThread(aDeviceId);
if (track) {
MOZ_ASSERT(!track->mConsumerTracks.IsEmpty());
track->AddDataListener(aConsumer->GetAudioDataListener());
} else {
aTrack->ReevaluateInputDevice();
// Create a NativeInputTrack or NonNativeInputTrack, depending on whether
// the given graph already has a native device or not.
if (aGraph->GetNativeInputTrackMainThread()) {
// A native device is already in use. This device will be a non-native
// device.
track = new NonNativeInputTrack(aGraph->GraphRate(), aDeviceId,
aPrincipalHandle);
} else {
// No native device is in use. This device will be the native device.
track = new NativeInputTrack(aGraph->GraphRate(), aDeviceId,
aPrincipalHandle);
}
LOG("Create %sNativeInputTrack %p in MTG %p for device %p",
(track->AsNativeInputTrack() ? "" : "Non"), track.get(), aGraph,
aDeviceId);
aGraph->AddTrack(track);
// Add the listener before opening the device so the device passed to
// OpenAudioInput always has a non-zero input channel count.
track->AddDataListener(aConsumer->GetAudioDataListener());
aGraph->OpenAudioInput(track);
}
MOZ_ASSERT(track->AsNativeInputTrack() || track->AsNonNativeInputTrack());
MOZ_ASSERT(track->mDeviceId == aDeviceId);
MOZ_ASSERT(!track->mConsumerTracks.Contains(aConsumer));
track->mConsumerTracks.AppendElement(aConsumer);
LOG("DeviceInputTrack %p (device %p: %snative) in MTG %p has %zu users now",
track.get(), track->mDeviceId,
(track->AsNativeInputTrack() ? "" : "non-"), aGraph,
track->mConsumerTracks.Length());
if (track->mConsumerTracks.Length() > 1) {
track->ReevaluateInputDevice();
}
return WrapNotNull(track);
}
/* static */
void DeviceInputTrack::CloseAudio(already_AddRefed<DeviceInputTrack> aTrack,
DeviceInputConsumerTrack* aConsumer) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<DeviceInputTrack> track = aTrack;
MOZ_ASSERT(track);
track->RemoveDataListener(aConsumer->GetAudioDataListener());
DebugOnly<bool> removed = track->mConsumerTracks.RemoveElement(aConsumer);
MOZ_ASSERT(removed);
LOG("DeviceInputTrack %p (device %p) in MTG %p has %zu users now",
track.get(), track->mDeviceId, track->GraphImpl(),
track->mConsumerTracks.Length());
if (track->mConsumerTracks.IsEmpty()) {
track->GraphImpl()->CloseAudioInput(track);
track->Destroy();
} else {
track->ReevaluateInputDevice();
}
}
const nsTArray<RefPtr<DeviceInputConsumerTrack>>&
DeviceInputTrack::GetConsumerTracks() const {
MOZ_ASSERT(NS_IsMainThread());
return mConsumerTracks;
}
DeviceInputTrack::DeviceInputTrack(TrackRate aSampleRate,
@ -116,8 +278,7 @@ DeviceInputTrack::DeviceInputTrack(TrackRate aSampleRate,
const PrincipalHandle& aPrincipalHandle)
: ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO, new AudioSegment()),
mDeviceId(aDeviceId),
mPrincipalHandle(aPrincipalHandle),
mUserCount(0) {}
mPrincipalHandle(aPrincipalHandle) {}
uint32_t DeviceInputTrack::MaxRequestedInputChannels() const {
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
@ -153,15 +314,15 @@ void DeviceInputTrack::ReevaluateInputDevice() {
MOZ_ASSERT(NS_IsMainThread());
class Message : public ControlMessage {
public:
explicit Message(MediaTrackGraphImpl* aGraph)
: ControlMessage(nullptr), mGraph(aGraph) {}
explicit Message(MediaTrack* aTrack, CubebUtils::AudioDeviceID aDeviceId)
: ControlMessage(aTrack), mDeviceId(aDeviceId) {}
void Run() override {
TRACE("DeviceInputTrack::ReevaluateInputDevice ControlMessage");
mGraph->ReevaluateInputDevice();
mTrack->GraphImpl()->ReevaluateInputDevice(mDeviceId);
}
MediaTrackGraphImpl* mGraph;
CubebUtils::AudioDeviceID mDeviceId;
};
mGraph->AppendMessage(MakeUnique<Message>(mGraph));
mGraph->AppendMessage(MakeUnique<Message>(this, mDeviceId));
}
void DeviceInputTrack::AddDataListener(AudioDataListener* aListener) {
@ -301,7 +462,9 @@ NonNativeInputTrack::NonNativeInputTrack(
TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId,
const PrincipalHandle& aPrincipalHandle)
: DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle),
mAudioSource(nullptr) {}
mAudioSource(nullptr),
mSourceIdNumber(0),
mGraphDriverThreadId(std::thread::id()) {}
void NonNativeInputTrack::DestroyImpl() {
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
@ -333,7 +496,18 @@ void NonNativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
return;
}
AudioSegment data = mAudioSource->GetAudioSegment(delta);
// GetAudioSegment only checks the given reader if DEBUG is defined.
AudioInputSource::Consumer consumer =
#ifdef DEBUG
// If we are on GraphRunner, we should always be on the same thread.
mGraph->mGraphRunner || !CheckGraphDriverChanged()
? AudioInputSource::Consumer::Same
: AudioInputSource::Consumer::Changed;
#else
AudioInputSource::Consumer::Same;
#endif
AudioSegment data = mAudioSource->GetAudioSegment(delta, consumer);
MOZ_ASSERT(data.GetDuration() == delta);
GetData<AudioSegment>()->AppendFrom(&data);
}
@ -402,6 +576,22 @@ void NonNativeInputTrack::NotifyInputStopped(uint32_t aSourceId) {
mAudioSource->Stop();
}
AudioInputSource::Id NonNativeInputTrack::GenerateSourceId() {
MOZ_ASSERT(mGraph->OnGraphThread());
return mSourceIdNumber++;
}
bool NonNativeInputTrack::CheckGraphDriverChanged() {
MOZ_ASSERT(mGraph->CurrentDriver()->OnThread());
std::thread::id currentId = std::this_thread::get_id();
if (mGraphDriverThreadId == currentId) {
return false;
}
mGraphDriverThreadId = currentId;
return true;
}
AudioInputSourceListener::AudioInputSourceListener(NonNativeInputTrack* aOwner)
: mOwner(aOwner) {}
@ -495,5 +685,7 @@ void AudioInputSourceListener::AudioStateCallback(
#undef TRACK_GRAPH_LOG
#undef TRACK_GRAPH_LOGV
#undef TRACK_GRAPH_LOGE
#undef CONSUMER_GRAPH_LOG_INTERNAL
#undef CONSUMER_GRAPH_LOGV
} // namespace mozilla

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

@ -7,38 +7,143 @@
#ifndef DOM_MEDIA_DEVICEINPUTTRACK_H_
#define DOM_MEDIA_DEVICEINPUTTRACK_H_
#include <thread>
#include "AudioDriftCorrection.h"
#include "AudioSegment.h"
#include "AudioInputSource.h"
#include "MediaTrackGraph.h"
#include "GraphDriver.h"
#include "mozilla/NotNull.h"
namespace mozilla {
class NativeInputTrack;
class NonNativeInputTrack;
// Any MediaTrack that needs the audio data from the certain device should
// inherit the this class and get the raw audio data on graph thread via
// GetInputSourceData(), after calling ConnectDeviceInput() and before
// DisconnectDeviceInput() on main thread. See more examples in
// TestAudioTrackGraph.cpp
//
// Example:
//
// class RawAudioDataTrack : public DeviceInputConsumerTrack {
// public:
// ...
//
// void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override
// {
// if (aFrom >= aTo) {
// return;
// }
//
// if (mInputs.IsEmpty()) {
// GetData<AudioSegment>()->AppendNullData(aTo - aFrom);
// } else {
// MOZ_ASSERT(mInputs.Length() == 1);
// AudioSegment data;
// DeviceInputConsumerTrack::GetInputSourceData(data, mInputs[0], aFrom,
// aTo);
// // You can do audio data processing before appending to mSegment here.
// GetData<AudioSegment>()->AppendFrom(&data);
// }
// };
//
// uint32_t NumberOfChannels() const override {
// if (mInputs.IsEmpty()) {
// return 0;
// }
// DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
// MOZ_ASSERT(t);
// return t->NumberOfChannels();
// }
//
// ...
//
// private:
// explicit RawAudioDataTrack(TrackRate aSampleRate)
// : DeviceInputConsumerTrack(aSampleRate) {}
// };
class DeviceInputConsumerTrack : public ProcessedMediaTrack {
public:
explicit DeviceInputConsumerTrack(TrackRate aSampleRate);
// Main Thread APIs:
void ConnectDeviceInput(CubebUtils::AudioDeviceID aId,
AudioDataListener* aListener,
const PrincipalHandle& aPrincipal);
void DisconnectDeviceInput();
Maybe<CubebUtils::AudioDeviceID> DeviceId() const;
NotNull<AudioDataListener*> GetAudioDataListener() const;
// Any thread:
bool ConnectToNativeDevice() const;
bool ConnectToNonNativeDevice() const;
DeviceInputConsumerTrack* AsDeviceInputConsumerTrack() override {
return this;
}
protected:
// Graph thread API:
// Get the data in [aFrom, aTo) from aPort->GetSource() to aOutput. aOutput
// needs to be empty.
void GetInputSourceData(AudioSegment& aOutput, const MediaInputPort* aPort,
GraphTime aFrom, GraphTime aTo) const;
// Main Thread variables:
RefPtr<MediaInputPort> mPort;
RefPtr<DeviceInputTrack> mDeviceInputTrack;
RefPtr<AudioDataListener> mListener;
Maybe<CubebUtils::AudioDeviceID> mDeviceId;
};
class DeviceInputTrack : public ProcessedMediaTrack {
public:
// Main Thread APIs:
// Any MediaTrack that needs the audio data from the certain device should
// inherit the DeviceInputConsumerTrack class and call GetInputSourceData to
// get the data instead of using the below APIs.
//
// The following two APIs can create and destroy a DeviceInputTrack reference
// on main thread, then open and close the underlying audio device accordingly
// on the graph thread. The user who wants to read the audio input from a
// certain device should use these APIs to obtain a DeviceInputTrack reference
// and release the reference when the user no longer needs the audio data.
//
// Currently, all the DeviceInputTrack is NativeInputTrack.
// Once the DeviceInputTrack is created on the main thread, the paired device
// will start producing data, so its users can read the data immediately on
// the graph thread, once they obtain the reference. The lifetime of
// DeviceInputTrack is managed by the MediaTrackGraph itself. When the
// DeviceInputTrack has no user any more, MediaTrackGraph will destroy it.
// This means, it occurs when the last reference has been released by the API
// below.
//
// There is only one NativeInputTrack per MediaTrackGraph and it will be
// created when the first user who requests the audio data. Once the
// NativeInputTrack is created, the paired device will start producing data,
// so its users can read the data immediately once they obtain the reference.
// Currently, we only allow one audio device per MediaTrackGraph. If the user
// requests a different device from the one running in the MediaTrackGraph,
// the API will return an error. The lifetime of NativeInputTrack is managed
// by the MediaTrackGraph. When the NativeInputTrack has no user any more,
// MediaTrackGraph will destroy it. In other words, it occurs when the last
// reference is returned.
// The DeviceInputTrack is either a NativeInputTrack, or a
// NonNativeInputTrack. We can have only one NativeInputTrack per
// MediaTrackGraph, but multiple NonNativeInputTrack per graph. The audio
// device paired with the NativeInputTrack is called "native device", and the
// device paired with the NonNativeInputTrack is called "non-native device".
// In other words, we can have only one native device per MediaTrackGraph, but
// many non-native devices per graph.
//
// The native device is the first input device created in the MediaTrackGraph.
// All other devices created after it will be non-native devices. Once the
// native device is destroyed, the first non-native device will be promoted to
// the new native device. The switch will be started by the MediaTrackGraph.
// The MediaTrackGraph will force DeviceInputTrack's users to re-configure
// their DeviceInputTrack connections with the APIs below to execute the
// switching.
//
// The native device is also the audio input device serving the
// AudioCallbackDriver, which drives the MediaTrackGraph periodically from
// audio callback thread. The audio data produced by the native device and
// non-native device is stored in NativeInputTrack and NonNativeInputTrack
// respectively, and then accessed by their users. The only difference between
// these audio data is that the data from the non-native device is
// clock-drift-corrected since the non-native device may run on a different
// clock than the native device's one.
//
// Example:
// // On main thread
@ -48,22 +153,24 @@ class DeviceInputTrack : public ProcessedMediaTrack {
// AudioSegmen* data = track->GetData<AudioSegment>();
// ...
// // On main thread
// DeviceInputTrack::CloseAudio(std::move(track), ...);
// DeviceInputTrack::CloseAudio(track.forget(), ...);
//
// Returns a reference of DeviceInputTrack, storing the input audio data from
// the given device, in the given MediaTrackGraph, if the MediaTrackGraph has
// no audio input device, or the given device is the same as the one currently
// running in the MediaTrackGraph. Otherwise, return an error. The paired
// audio device will be opened accordingly in the successful case. The
// DeviceInputTrack will access its user's audio settings via the attached
// AudioDataListener when it needs.
static Result<RefPtr<DeviceInputTrack>, nsresult> OpenAudio(
// the given device, in the given MediaTrackGraph. The paired audio device
// will be opened accordingly. The DeviceInputTrack will access its user's
// audio settings via the attached AudioDataListener, and delivers the
// notifications when it needs.
static NotNull<RefPtr<DeviceInputTrack>> OpenAudio(
MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aDeviceId,
const PrincipalHandle& aPrincipalHandle, AudioDataListener* aListener);
const PrincipalHandle& aPrincipalHandle,
DeviceInputConsumerTrack* aConsumer);
// Destroy the DeviceInputTrack reference obtained by the above API. The
// paired audio device will be closed accordingly.
static void CloseAudio(RefPtr<DeviceInputTrack>&& aTrack,
AudioDataListener* aListener);
static void CloseAudio(already_AddRefed<DeviceInputTrack> aTrack,
DeviceInputConsumerTrack* aConsumer);
// Main thread API:
const nsTArray<RefPtr<DeviceInputConsumerTrack>>& GetConsumerTracks() const;
// Graph thread APIs:
// Query audio settings from its users.
@ -93,8 +200,8 @@ class DeviceInputTrack : public ProcessedMediaTrack {
void RemoveDataListener(AudioDataListener* aListener);
// Only accessed on the main thread.
// When this becomes zero, this DeviceInputTrack is no longer needed.
int32_t mUserCount = 0;
// When this becomes empty, this DeviceInputTrack is no longer needed.
nsTArray<RefPtr<DeviceInputConsumerTrack>> mConsumerTracks;
// Only accessed on the graph thread.
nsTArray<RefPtr<AudioDataListener>> mListeners;
@ -157,12 +264,20 @@ class NonNativeInputTrack final : public DeviceInputTrack {
AudioInputType DevicePreference() const;
void NotifyDeviceChanged(AudioInputSource::Id aSourceId);
void NotifyInputStopped(AudioInputSource::Id aSourceId);
AudioInputSource::Id GenerateSourceId();
private:
~NonNativeInputTrack() = default;
// Graph driver thread only.
bool CheckGraphDriverChanged();
// Graph thread only.
RefPtr<AudioInputSource> mAudioSource;
AudioInputSource::Id mSourceIdNumber;
// Graph driver thread only.
std::thread::id mGraphDriverThreadId;
};
class AudioInputSourceListener : public AudioInputSource::EventListener {

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

@ -33,6 +33,7 @@
#include "VideoFrameContainer.h"
#include "mozilla/AbstractThread.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_media.h"
#include "transport/runnable_utils.h"
#include "VideoUtils.h"
#include "GraphRunner.h"
@ -303,7 +304,7 @@ bool MediaTrackGraphImpl::AudioTrackPresent() {
// We may not have audio input device when we only have AudioNodeTracks. But
// if audioTrackPresent is false, we must have no input device.
MOZ_DIAGNOSTIC_ASSERT_IF(!audioTrackPresent, !mNativeInputTrackOnGraph);
MOZ_DIAGNOSTIC_ASSERT_IF(!audioTrackPresent, !mNativeInputTrackGraphThread);
return audioTrackPresent;
}
@ -338,13 +339,21 @@ void MediaTrackGraphImpl::CheckDriver() {
return;
}
uint32_t inputChannelCount =
mNativeInputTrackGraphThread
? AudioInputChannelCount(mNativeInputTrackGraphThread->mDeviceId)
: 0;
AudioInputType inputPreference =
mNativeInputTrackGraphThread
? AudioInputDevicePreference(mNativeInputTrackGraphThread->mDeviceId)
: AudioInputType::Unknown;
uint32_t graphOutputChannelCount = AudioOutputChannelCount();
if (!audioCallbackDriver) {
if (graphOutputChannelCount > 0) {
AudioCallbackDriver* driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, graphOutputChannelCount,
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
inputChannelCount, mOutputDeviceID, mInputDeviceID, inputPreference);
SwitchAtNextIteration(driver);
}
return;
@ -359,8 +368,7 @@ void MediaTrackGraphImpl::CheckDriver() {
if (graphOutputChannelCount != audioCallbackDriver->OutputChannelCount()) {
AudioCallbackDriver* driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, graphOutputChannelCount,
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
inputChannelCount, mOutputDeviceID, mInputDeviceID, inputPreference);
SwitchAtNextIteration(driver);
}
}
@ -649,79 +657,149 @@ TrackTime MediaTrackGraphImpl::PlayAudio(AudioMixer* aMixer,
return ticksWritten;
}
NativeInputTrack* MediaTrackGraphImpl::GetNativeInputTrack() {
DeviceInputTrack* MediaTrackGraphImpl::GetDeviceInputTrackMainThread(
CubebUtils::AudioDeviceID aID) {
MOZ_ASSERT(NS_IsMainThread());
return mNativeInputTrackOnMain.get();
if (mNativeInputTrackMainThread &&
mNativeInputTrackMainThread->mDeviceId == aID) {
return mNativeInputTrackMainThread.get();
}
for (const RefPtr<NonNativeInputTrack>& t : mNonNativeInputTracksMainThread) {
if (t->mDeviceId == aID) {
return t.get();
}
}
return nullptr;
}
void MediaTrackGraphImpl::OpenAudioInputImpl(NativeInputTrack* aTrack) {
DeviceInputTrack* MediaTrackGraphImpl::GetDeviceInputTrackGraphThread(
CubebUtils::AudioDeviceID aID) {
MOZ_ASSERT(OnGraphThread());
if (mNativeInputTrackGraphThread &&
mNativeInputTrackGraphThread->mDeviceId == aID) {
return mNativeInputTrackGraphThread.get();
}
for (const RefPtr<NonNativeInputTrack>& t :
mNonNativeInputTracksGraphThread) {
if (t->mDeviceId == aID) {
return t.get();
}
}
return nullptr;
}
NativeInputTrack* MediaTrackGraphImpl::GetNativeInputTrackMainThread() {
MOZ_ASSERT(NS_IsMainThread());
return mNativeInputTrackMainThread.get();
}
void MediaTrackGraphImpl::OpenAudioInputImpl(DeviceInputTrack* aTrack) {
MOZ_ASSERT(OnGraphThread());
LOG(LogLevel::Debug,
("%p OpenAudioInputImpl: device %p", this, aTrack->mDeviceId));
if (mNativeInputTrackOnGraph) {
MOZ_ASSERT_UNREACHABLE(
"We cannot open device twice, and we don't support multiple inputs for "
"now");
return;
if (NativeInputTrack* native = aTrack->AsNativeInputTrack()) {
MOZ_ASSERT(!mNativeInputTrackGraphThread);
mNativeInputTrackGraphThread = native;
mInputDeviceID = aTrack->mDeviceId;
// Switch Drivers since we're adding input (to input-only or full-duplex)
AudioCallbackDriver* driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, AudioOutputChannelCount(),
AudioInputChannelCount(aTrack->mDeviceId), mOutputDeviceID,
mInputDeviceID, AudioInputDevicePreference(aTrack->mDeviceId));
LOG(LogLevel::Debug,
("%p OpenAudioInputImpl: starting new AudioCallbackDriver(input) %p",
this, driver));
SwitchAtNextIteration(driver);
} else {
NonNativeInputTrack* nonNative = aTrack->AsNonNativeInputTrack();
MOZ_ASSERT(nonNative);
class DeviceTrackComparator {
public:
bool Equals(const RefPtr<NonNativeInputTrack>& aTrack,
CubebUtils::AudioDeviceID aDeviceId) const {
return aTrack->mDeviceId == aDeviceId;
}
};
MOZ_ASSERT(!mNonNativeInputTracksGraphThread.Contains(
aTrack->mDeviceId, DeviceTrackComparator()));
mNonNativeInputTracksGraphThread.AppendElement(nonNative);
// Start non-native input right away.
nonNative->StartAudio(MakeRefPtr<AudioInputSource>(
MakeRefPtr<AudioInputSourceListener>(nonNative),
nonNative->GenerateSourceId(), nonNative->mDeviceId,
AudioInputChannelCount(nonNative->mDeviceId),
AudioInputDevicePreference(nonNative->mDeviceId) ==
AudioInputType::Voice,
nonNative->mPrincipalHandle, nonNative->mSampleRate, GraphRate(),
StaticPrefs::media_clockdrift_buffering()));
}
LOG(LogLevel::Debug,
("%p Open audio input on device %p", this, aTrack->mDeviceId));
mNativeInputTrackOnGraph = aTrack;
mInputDeviceID = aTrack->mDeviceId;
// Switch Drivers since we're adding input (to input-only or full-duplex)
AudioCallbackDriver* driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, AudioOutputChannelCount(),
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
LOG(LogLevel::Debug,
("%p OpenAudioInput: starting new AudioCallbackDriver(input) %p", this,
driver));
SwitchAtNextIteration(driver);
}
void MediaTrackGraphImpl::OpenAudioInput(DeviceInputTrack* aTrack) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTrack);
MOZ_ASSERT(aTrack->AsNativeInputTrack());
LOG(LogLevel::Debug, ("%p OpenInput: DeviceInputTrack %p for device %p", this,
aTrack, aTrack->mDeviceId));
class Message : public ControlMessage {
public:
Message(MediaTrackGraphImpl* aGraph, NativeInputTrack* aInputTrack)
Message(MediaTrackGraphImpl* aGraph, DeviceInputTrack* aInputTrack)
: ControlMessage(nullptr), mGraph(aGraph), mInputTrack(aInputTrack) {}
void Run() override {
TRACE("MTG::OpenAudioInputImpl ControlMessage");
mGraph->OpenAudioInputImpl(mInputTrack);
}
MediaTrackGraphImpl* mGraph;
NativeInputTrack* mInputTrack;
DeviceInputTrack* mInputTrack;
};
MOZ_ASSERT(!mNativeInputTrackOnMain);
mNativeInputTrackOnMain = aTrack->AsNativeInputTrack();
if (NativeInputTrack* native = aTrack->AsNativeInputTrack()) {
MOZ_ASSERT(!mNativeInputTrackMainThread);
mNativeInputTrackMainThread = native;
} else {
NonNativeInputTrack* nonNative = aTrack->AsNonNativeInputTrack();
MOZ_ASSERT(nonNative);
struct DeviceTrackComparator {
public:
bool Equals(const RefPtr<NonNativeInputTrack>& aTrack,
CubebUtils::AudioDeviceID aDeviceId) const {
return aTrack->mDeviceId == aDeviceId;
}
};
MOZ_ASSERT(!mNonNativeInputTracksMainThread.Contains(
aTrack->mDeviceId, DeviceTrackComparator()));
mNonNativeInputTracksMainThread.AppendElement(nonNative);
}
// XXX Check not destroyed!
this->AppendMessage(MakeUnique<Message>(this, aTrack->AsNativeInputTrack()));
this->AppendMessage(MakeUnique<Message>(this, aTrack));
}
void MediaTrackGraphImpl::CloseAudioInputImpl(CubebUtils::AudioDeviceID aID) {
void MediaTrackGraphImpl::CloseAudioInputImpl(DeviceInputTrack* aTrack) {
MOZ_ASSERT(OnGraphThread());
LOG(LogLevel::Debug, ("%p CloseAudioInputImpl: device %p", this, aID));
LOG(LogLevel::Debug,
("%p CloseAudioInputImpl: device %p", this, aTrack->mDeviceId));
if (!mNativeInputTrackOnGraph || mNativeInputTrackOnGraph->mDeviceId != aID) {
LOG(LogLevel::Debug, ("%p Device %p is not native device", this, aID));
if (NonNativeInputTrack* nonNative = aTrack->AsNonNativeInputTrack()) {
nonNative->StopAudio();
MOZ_ASSERT(mNonNativeInputTracksGraphThread.Contains(nonNative));
DebugOnly<bool> removed =
mNonNativeInputTracksGraphThread.RemoveElement(nonNative);
MOZ_ASSERT(removed);
return;
}
LOG(LogLevel::Debug, ("%p Close device %p", this, aID));
MOZ_ASSERT(aTrack->AsNativeInputTrack());
MOZ_ASSERT(aTrack->AsNativeInputTrack() ==
mNativeInputTrackGraphThread.get());
mNativeInputTrackOnGraph = nullptr; // reset to default
mNativeInputTrackGraphThread = nullptr; // reset to default
mInputDeviceID = nullptr; // reset to default
// Switch Drivers since we're adding or removing an input (to nothing/system
@ -736,8 +814,8 @@ void MediaTrackGraphImpl::CloseAudioInputImpl(CubebUtils::AudioDeviceID aID) {
driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, AudioOutputChannelCount(),
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
AudioInputChannelCount(aTrack->mDeviceId), mOutputDeviceID,
mInputDeviceID, AudioInputDevicePreference(aTrack->mDeviceId));
SwitchAtNextIteration(driver);
} else if (CurrentDriver()->AsAudioCallbackDriver()) {
LOG(LogLevel::Debug,
@ -757,10 +835,19 @@ void MediaTrackGraphImpl::RegisterAudioOutput(MediaTrack* aTrack, void* aKey) {
tkv->mVolume = 1.0;
if (!CurrentDriver()->AsAudioCallbackDriver() && !Switching()) {
uint32_t inputChannelCount =
mNativeInputTrackGraphThread
? AudioInputChannelCount(mNativeInputTrackGraphThread->mDeviceId)
: 0;
AudioInputType inputPreference =
mNativeInputTrackGraphThread
? AudioInputDevicePreference(
mNativeInputTrackGraphThread->mDeviceId)
: AudioInputType::Unknown;
AudioCallbackDriver* driver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, AudioOutputChannelCount(),
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
inputChannelCount, mOutputDeviceID, mInputDeviceID, inputPreference);
SwitchAtNextIteration(driver);
}
}
@ -786,34 +873,54 @@ void MediaTrackGraphImpl::UnregisterAudioOutput(MediaTrack* aTrack,
void MediaTrackGraphImpl::CloseAudioInput(DeviceInputTrack* aTrack) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTrack);
MOZ_ASSERT(aTrack->AsNativeInputTrack());
LOG(LogLevel::Debug, ("%p CloseInput: DeviceInputTrack %p for device %p",
this, aTrack, aTrack->mDeviceId));
class Message : public ControlMessage {
public:
Message(MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aID)
: ControlMessage(nullptr), mGraph(aGraph), mID(aID) {}
Message(MediaTrackGraphImpl* aGraph, DeviceInputTrack* aInputTrack)
: ControlMessage(nullptr), mGraph(aGraph), mInputTrack(aInputTrack) {}
void Run() override {
TRACE("MTG::CloseAudioInputImpl ControlMessage");
mGraph->CloseAudioInputImpl(mID);
mGraph->CloseAudioInputImpl(mInputTrack);
}
MediaTrackGraphImpl* mGraph;
CubebUtils::AudioDeviceID mID;
DeviceInputTrack* mInputTrack;
};
// NativeInputTrack is still alive (in mTracks) even we remove it here.
mNativeInputTrackOnMain = nullptr;
// DeviceInputTrack is still alive (in mTracks) even we remove it here, since
// aTrack->Destroy() is called after this. See DeviceInputTrack::CloseAudio
// for more details.
if (NativeInputTrack* native = aTrack->AsNativeInputTrack()) {
MOZ_ASSERT(mNativeInputTrackMainThread);
mNativeInputTrackMainThread = nullptr;
} else {
NonNativeInputTrack* nonNative = aTrack->AsNonNativeInputTrack();
MOZ_ASSERT(nonNative);
MOZ_ASSERT(mNonNativeInputTracksMainThread.Contains(nonNative));
DebugOnly<bool> removed =
mNonNativeInputTracksMainThread.RemoveElement(nonNative);
MOZ_ASSERT(removed);
}
this->AppendMessage(MakeUnique<Message>(this, aTrack->mDeviceId));
this->AppendMessage(MakeUnique<Message>(this, aTrack));
if (aTrack->AsNativeInputTrack()) {
LOG(LogLevel::Debug,
("%p Native input device %p is closed!", this, aTrack->mDeviceId));
SetNewNativeInput();
}
}
// All AudioInput listeners get the same speaker data (at least for now).
void MediaTrackGraphImpl::NotifyOutputData(AudioDataValue* aBuffer,
size_t aFrames, TrackRate aRate,
uint32_t aChannels) {
if (!mNativeInputTrackOnGraph) {
if (!mNativeInputTrackGraphThread) {
return;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
MOZ_ASSERT(mNativeInputTrackGraphThread->mDeviceId == mInputDeviceID);
#if defined(MOZ_WEBRTC)
for (const auto& track : mTracks) {
@ -825,11 +932,11 @@ void MediaTrackGraphImpl::NotifyOutputData(AudioDataValue* aBuffer,
}
void MediaTrackGraphImpl::NotifyInputStopped() {
if (!mNativeInputTrackOnGraph) {
if (!mNativeInputTrackGraphThread) {
return;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
mNativeInputTrackOnGraph->NotifyInputStopped(this);
MOZ_ASSERT(mNativeInputTrackGraphThread->mDeviceId == mInputDeviceID);
mNativeInputTrackGraphThread->NotifyInputStopped(this);
}
void MediaTrackGraphImpl::NotifyInputData(const AudioDataValue* aBuffer,
@ -839,22 +946,22 @@ void MediaTrackGraphImpl::NotifyInputData(const AudioDataValue* aBuffer,
// 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(mNativeInputTrackOnGraph || Switching());
if (!mNativeInputTrackOnGraph) {
MOZ_ASSERT(mNativeInputTrackGraphThread || Switching());
if (!mNativeInputTrackGraphThread) {
return;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
mNativeInputTrackOnGraph->NotifyInputData(this, aBuffer, aFrames, aRate,
aChannels, aAlreadyBuffered);
MOZ_ASSERT(mNativeInputTrackGraphThread->mDeviceId == mInputDeviceID);
mNativeInputTrackGraphThread->NotifyInputData(this, aBuffer, aFrames, aRate,
aChannels, aAlreadyBuffered);
}
void MediaTrackGraphImpl::DeviceChangedImpl() {
MOZ_ASSERT(OnGraphThread());
if (!mNativeInputTrackOnGraph) {
if (!mNativeInputTrackGraphThread) {
return;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
mNativeInputTrackOnGraph->DeviceChanged(this);
MOZ_ASSERT(mNativeInputTrackGraphThread->mDeviceId == mInputDeviceID);
mNativeInputTrackGraphThread->DeviceChanged(this);
}
void MediaTrackGraphImpl::SetMaxOutputChannelCount(uint32_t aMaxChannelCount) {
@ -938,22 +1045,66 @@ static const char* GetAudioInputTypeString(const AudioInputType& aType) {
return aType == AudioInputType::Voice ? "Voice" : "Unknown";
}
void MediaTrackGraphImpl::ReevaluateInputDevice() {
void MediaTrackGraphImpl::ReevaluateInputDevice(CubebUtils::AudioDeviceID aID) {
MOZ_ASSERT(OnGraphThread());
LOG(LogLevel::Debug, ("%p: ReevaluateInputDevice: device %p", this, aID));
DeviceInputTrack* track = GetDeviceInputTrackGraphThread(aID);
if (!track) {
LOG(LogLevel::Debug,
("%p: No DeviceInputTrack for this device. Ignore", this));
return;
}
bool needToSwitch = false;
if (NonNativeInputTrack* nonNative = track->AsNonNativeInputTrack()) {
if (nonNative->NumberOfChannels() != AudioInputChannelCount(aID)) {
LOG(LogLevel::Debug,
("%p: %u-channel non-native input device %p (track %p) is "
"re-configured to %d-channel",
this, nonNative->NumberOfChannels(), aID, track,
AudioInputChannelCount(aID)));
needToSwitch = true;
}
if (nonNative->DevicePreference() != AudioInputDevicePreference(aID)) {
LOG(LogLevel::Debug,
("%p: %s-type non-native input device %p (track %p) is re-configured "
"to %s-type",
this, GetAudioInputTypeString(nonNative->DevicePreference()), aID,
track, GetAudioInputTypeString(AudioInputDevicePreference(aID))));
needToSwitch = true;
}
if (needToSwitch) {
nonNative->StopAudio();
nonNative->StartAudio(MakeRefPtr<AudioInputSource>(
MakeRefPtr<AudioInputSourceListener>(nonNative),
nonNative->GenerateSourceId(), aID, AudioInputChannelCount(aID),
AudioInputDevicePreference(aID) == AudioInputType::Voice,
nonNative->mPrincipalHandle, nonNative->mSampleRate, GraphRate(),
StaticPrefs::media_clockdrift_buffering()));
}
return;
}
MOZ_ASSERT(track->AsNativeInputTrack());
if (AudioCallbackDriver* audioCallbackDriver =
CurrentDriver()->AsAudioCallbackDriver()) {
if (audioCallbackDriver->InputChannelCount() != AudioInputChannelCount()) {
if (audioCallbackDriver->InputChannelCount() !=
AudioInputChannelCount(aID)) {
LOG(LogLevel::Debug,
("%p: ReevaluateInputDevice: %u-channel AudioCallbackDriver %p is "
"re-configured to %d-channel",
this, audioCallbackDriver->InputChannelCount(), audioCallbackDriver,
AudioInputChannelCount()));
AudioInputChannelCount(aID)));
needToSwitch = true;
}
if (audioCallbackDriver->InputDevicePreference() !=
AudioInputDevicePreference()) {
AudioInputDevicePreference(aID)) {
LOG(LogLevel::Debug,
("%p: ReevaluateInputDevice: %s-type AudioCallbackDriver %p is "
"re-configured to %s-type",
@ -961,7 +1112,7 @@ void MediaTrackGraphImpl::ReevaluateInputDevice() {
GetAudioInputTypeString(
audioCallbackDriver->InputDevicePreference()),
audioCallbackDriver,
GetAudioInputTypeString(AudioInputDevicePreference())));
GetAudioInputTypeString(AudioInputDevicePreference(aID))));
needToSwitch = true;
}
} else if (Switching() && NextDriver()->AsAudioCallbackDriver()) {
@ -975,8 +1126,8 @@ void MediaTrackGraphImpl::ReevaluateInputDevice() {
if (needToSwitch) {
AudioCallbackDriver* newDriver = new AudioCallbackDriver(
this, CurrentDriver(), mSampleRate, AudioOutputChannelCount(),
AudioInputChannelCount(), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference());
AudioInputChannelCount(aID), mOutputDeviceID, mInputDeviceID,
AudioInputDevicePreference(aID));
SwitchAtNextIteration(newDriver);
}
}
@ -3887,25 +4038,69 @@ GraphTime MediaTrackGraph::ProcessedTime() const {
return static_cast<const MediaTrackGraphImpl*>(this)->mProcessedTime;
}
uint32_t MediaTrackGraphImpl::AudioInputChannelCount() {
uint32_t MediaTrackGraphImpl::AudioInputChannelCount(
CubebUtils::AudioDeviceID aID) {
MOZ_ASSERT(OnGraphThreadOrNotRunning());
if (!mNativeInputTrackOnGraph) {
return 0;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
return mNativeInputTrackOnGraph->MaxRequestedInputChannels();
DeviceInputTrack* t = GetDeviceInputTrackGraphThread(aID);
return t ? t->MaxRequestedInputChannels() : 0;
}
AudioInputType MediaTrackGraphImpl::AudioInputDevicePreference() {
AudioInputType MediaTrackGraphImpl::AudioInputDevicePreference(
CubebUtils::AudioDeviceID aID) {
MOZ_ASSERT(OnGraphThreadOrNotRunning());
DeviceInputTrack* t = GetDeviceInputTrackGraphThread(aID);
return t && t->HasVoiceInput() ? AudioInputType::Voice
: AudioInputType::Unknown;
}
if (!mNativeInputTrackOnGraph) {
return AudioInputType::Unknown;
void MediaTrackGraphImpl::SetNewNativeInput() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNativeInputTrackMainThread);
LOG(LogLevel::Debug, ("%p SetNewNativeInput", this));
if (mNonNativeInputTracksMainThread.IsEmpty()) {
LOG(LogLevel::Debug, ("%p No other devices opened. Do nothing", this));
return;
}
MOZ_ASSERT(mNativeInputTrackOnGraph->mDeviceId == mInputDeviceID);
return mNativeInputTrackOnGraph->HasVoiceInput() ? AudioInputType::Voice
: AudioInputType::Unknown;
const CubebUtils::AudioDeviceID deviceId =
mNonNativeInputTracksMainThread[0]->mDeviceId;
const PrincipalHandle principal =
mNonNativeInputTracksMainThread[0]->mPrincipalHandle;
LOG(LogLevel::Debug,
("%p Select device %p as the new native input device", this, deviceId));
struct TrackListener {
DeviceInputConsumerTrack* track;
// Keep its reference so it won't be dropped when after
// DisconnectDeviceInput().
RefPtr<AudioDataListener> listener;
};
nsTArray<TrackListener> pairs;
for (const auto& track :
mNonNativeInputTracksMainThread[0]->GetConsumerTracks()) {
pairs.AppendElement(
TrackListener{track.get(), track->GetAudioDataListener().get()});
}
for (TrackListener& pair : pairs) {
pair.track->DisconnectDeviceInput();
}
for (TrackListener& pair : pairs) {
pair.track->ConnectDeviceInput(deviceId, pair.listener.get(), principal);
LOG(LogLevel::Debug,
("%p: Reinitialize AudioProcessingTrack %p for device %p", this,
pair.track, deviceId));
}
LOG(LogLevel::Debug,
("%p Native input device is set to device %p now", this, deviceId));
MOZ_ASSERT(mNativeInputTrackMainThread);
}
// nsIThreadObserver methods

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

@ -97,6 +97,7 @@ class MediaTrack;
class MediaTrackGraph;
class MediaTrackGraphImpl;
class MediaTrackListener;
class DeviceInputConsumerTrack;
class DeviceInputTrack;
class ProcessedMediaTrack;
class SourceMediaTrack;
@ -360,6 +361,9 @@ class MediaTrack : public mozilla::LinkedListElement<MediaTrack> {
virtual CrossGraphTransmitter* AsCrossGraphTransmitter() { return nullptr; }
virtual CrossGraphReceiver* AsCrossGraphReceiver() { return nullptr; }
virtual DeviceInputTrack* AsDeviceInputTrack() { return nullptr; }
virtual DeviceInputConsumerTrack* AsDeviceInputConsumerTrack() {
return nullptr;
}
// These Impl methods perform the core functionality of the control methods
// above, on the media graph thread.

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

@ -399,16 +399,21 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
TrackTime PlayAudio(AudioMixer* aMixer, const TrackKeyAndVolume& aTkv,
GraphTime aPlayedTime);
/* Do not call this directly. For users who need to get a NativeInputTrack,
* use NativeInputTrack::OpenAudio() instead. This should only be used in
* NativeInputTrack to get the existing NativeInputTrack paired with the given
/* Do not call this directly. For users who need to get a DeviceInputTrack,
* use DeviceInputTrack::OpenAudio() instead. This should only be used in
* DeviceInputTrack to get the existing DeviceInputTrack paired with the given
* device in this graph. Main thread only.*/
NativeInputTrack* GetNativeInputTrack();
DeviceInputTrack* GetDeviceInputTrackMainThread(
CubebUtils::AudioDeviceID aID);
/* Do not call this directly. This should only be used in DeviceInputTrack to
* get the existing NativeInputTrackMain thread only.*/
NativeInputTrack* GetNativeInputTrackMainThread();
/* 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(NativeInputTrack* aTrack);
void OpenAudioInputImpl(DeviceInputTrack* aTrack);
/* Called on the main thread when something requests audio from an input
* audio device aID. */
virtual void OpenAudioInput(DeviceInputTrack* aTrack) override;
@ -416,7 +421,7 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
/* Runs off a message on the graph when input audio from aID is not needed
* anymore, for a particular track. It can be that other tracks still need
* audio from this audio input device. */
void CloseAudioInputImpl(CubebUtils::AudioDeviceID aID);
void CloseAudioInputImpl(DeviceInputTrack* aTrack);
/* Called on the main thread when input audio from aID is not needed
* anymore, for a particular track. It can be that other tracks still need
* audio from this audio input device. */
@ -432,7 +437,7 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
/* Called on the graph thread when the input device settings should be
* reevaluated, for example, if the channel count of the input track should
* be changed. */
void ReevaluateInputDevice();
void ReevaluateInputDevice(CubebUtils::AudioDeviceID aID);
/* Called on the graph thread when there is new output data for listeners.
* This is the mixed audio output of this MediaTrackGraph. */
@ -506,11 +511,9 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
* 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();
uint32_t AudioInputChannelCount(CubebUtils::AudioDeviceID aID);
AudioInputType AudioInputDevicePreference();
CubebUtils::AudioDeviceID InputDeviceID() { return mInputDeviceID; }
AudioInputType AudioInputDevicePreference(CubebUtils::AudioDeviceID aID);
double MediaTimeToSeconds(GraphTime aTime) const {
NS_ASSERTION(aTime > -TRACK_TIME_MAX && aTime <= TRACK_TIME_MAX,
@ -726,23 +729,18 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
/**
* Devices to use for cubeb input & output, or nullptr for default device.
* A MediaTrackGraph always has an output (even if silent).
* If `mNativeInputTrackOnGraph` is not NULL, this MediaTrackGraph wants audio
* input.
* If `mNativeInputTrackGraphThread` is not NULL, this MediaTrackGraph wants
* audio input.
*
* All mInputDeviceID access is on the graph thread except for reads via
* InputDeviceID(), which are racy but used only for comparison.
*
* In any case, the number of channels to use can be queried (on the graph
* thread) by AudioInputChannelCount() and AudioOutputChannelCount().
* All mInputDeviceID access is on the graph thread.
*/
std::atomic<CubebUtils::AudioDeviceID> mInputDeviceID;
CubebUtils::AudioDeviceID mInputDeviceID;
CubebUtils::AudioDeviceID mOutputDeviceID;
// Track the native input device in graph. Graph thread only.
// TODO: Once multiple input devices is supported,
// mNativeInputTrackOnGraph->mDeviceId could replace mInputDeviceID since no
// other thread will read mInputDeviceID.
RefPtr<NativeInputTrack> mNativeInputTrackOnGraph;
// Track the native and non-native input device in graph. Graph thread only.
// TODO: Replace mInputDeviceID by mNativeInputTrackGraphThread->mDeviceId.
RefPtr<NativeInputTrack> mNativeInputTrackGraphThread;
nsTArray<RefPtr<NonNativeInputTrack>> mNonNativeInputTracksGraphThread;
/**
* List of resume operations waiting for a switch to an AudioCallbackDriver.
@ -953,6 +951,15 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
private:
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
// Returns the DeviceInputTrack paired with the device of aID if it exists in
// the graph. Otherwise, return nullptr. Graph thread only.
DeviceInputTrack* GetDeviceInputTrackGraphThread(
CubebUtils::AudioDeviceID aID);
// Set a new native iput device when the current native input device is close.
// Main thread only.
void SetNewNativeInput();
/**
* This class uses manual memory management, and all pointers to it are raw
* pointers. However, in order for it to implement nsIMemoryReporter, it needs
@ -1016,9 +1023,10 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
uint32_t mMaxOutputChannelCount;
/*
* Hold the NativeInputTrack for a certain device.
* Hold the NativeInputTrack or NonNativeInputTrack for a certain device.
*/
RefPtr<NativeInputTrack> mNativeInputTrackOnMain;
RefPtr<NativeInputTrack> mNativeInputTrackMainThread;
nsTArray<RefPtr<NonNativeInputTrack>> mNonNativeInputTracksMainThread;
};
} // namespace mozilla

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

@ -127,7 +127,8 @@ TEST(TestAudioInputSource, DataOutputBeforeStartAndAfterStop)
// It's ok to call GetAudioSegment before starting
{
AudioSegment data = ais->GetAudioSegment(requestFrames);
AudioSegment data =
ais->GetAudioSegment(requestFrames, AudioInputSource::Consumer::Same);
EXPECT_EQ(data.GetDuration(), requestFrames);
EXPECT_TRUE(data.IsNull());
}
@ -161,7 +162,8 @@ TEST(TestAudioInputSource, DataOutputBeforeStartAndAfterStop)
size_t expectedSamples =
expectedSegment.WriteToInterleavedBuffer(expected, channels);
AudioSegment actualSegment = ais->GetAudioSegment(requestFrames);
AudioSegment actualSegment =
ais->GetAudioSegment(requestFrames, AudioInputSource::Consumer::Same);
EXPECT_EQ(actualSegment.GetDuration(), requestFrames);
nsTArray<AudioDataValue> actual;
size_t actualSamples =

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -79,6 +79,91 @@ class TestDeviceInputTrack : public testing::Test {
RefPtr<MockGraphImpl> mGraph;
};
TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
public:
static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
MOZ_ASSERT(NS_IsMainThread());
TestDeviceInputConsumerTrack* track =
new TestDeviceInputConsumerTrack(aGraph->GraphRate());
aGraph->AddTrack(track);
return track;
}
void Destroy() {
MOZ_ASSERT(NS_IsMainThread());
DisconnectDeviceInput();
DeviceInputConsumerTrack::Destroy();
}
void ProcessInput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags) override{/* Ignored */};
uint32_t NumberOfChannels() const override {
if (mInputs.IsEmpty()) {
return 0;
}
DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
MOZ_ASSERT(t);
return t->NumberOfChannels();
}
private:
explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate)
: DeviceInputConsumerTrack(aSampleRate) {}
};
class TestAudioDataListener : public AudioDataListener {
public:
TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
: mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
// Graph thread APIs: AudioDataListenerInterface implementations.
uint32_t RequestedInputChannelCount(MediaTrackGraphImpl* aGraph) override {
MOZ_ASSERT(aGraph->OnGraphThread());
return mChannelCount;
}
bool IsVoiceInput(MediaTrackGraphImpl* aGraph) const override {
return mIsVoice;
};
void DeviceChanged(MediaTrackGraphImpl* aGraph) override { /* Ignored */
}
void Disconnect(MediaTrackGraphImpl* aGraph) override{/* Ignored */};
private:
~TestAudioDataListener() = default;
// Graph thread-only.
uint32_t mChannelCount;
// Any thread.
const bool mIsVoice;
};
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
const CubebUtils::AudioDeviceID device1 = (void*)1;
RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
RefPtr<TestDeviceInputConsumerTrack> track1 =
TestDeviceInputConsumerTrack::Create(mGraph);
track1->ConnectDeviceInput(device1, listener1.get(), testPrincipal);
EXPECT_TRUE(track1->ConnectToNativeDevice());
EXPECT_FALSE(track1->ConnectToNonNativeDevice());
const CubebUtils::AudioDeviceID device2 = (void*)2;
RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
RefPtr<TestDeviceInputConsumerTrack> track2 =
TestDeviceInputConsumerTrack::Create(mGraph);
track2->ConnectDeviceInput(device2, listener2.get(), testPrincipal);
EXPECT_FALSE(track2->ConnectToNativeDevice());
EXPECT_TRUE(track2->ConnectToNonNativeDevice());
track2->Destroy();
mGraph->RemoveTrackGraphThread(track2);
track1->Destroy();
mGraph->RemoveTrackGraphThread(track1);
}
TEST_F(TestDeviceInputTrack, NativeInputTrackData) {
const uint32_t flags = 0;
const CubebUtils::AudioDeviceID deviceId = (void*)1;
@ -138,26 +223,6 @@ TEST_F(TestDeviceInputTrack, NativeInputTrackData) {
mGraph->RemoveTrackGraphThread(track);
}
TEST_F(TestDeviceInputTrack, OpenTwiceWithoutCloseFirst) {
const CubebUtils::AudioDeviceID deviceId1 = (void*)1;
const CubebUtils::AudioDeviceID deviceId2 = (void*)2;
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
auto r1 = NativeInputTrack::OpenAudio(mGraph.get(), deviceId1, testPrincipal,
nullptr);
ASSERT_TRUE(r1.isOk());
RefPtr<NativeInputTrack> track1 = r1.unwrap()->AsNativeInputTrack();
ASSERT_TRUE(track1);
auto r2 = NativeInputTrack::OpenAudio(mGraph.get(), deviceId2, testPrincipal,
nullptr);
EXPECT_TRUE(r2.isErr());
NativeInputTrack::CloseAudio(std::move(track1), nullptr);
}
class MockEventListener : public AudioInputSource::EventListener {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener, override);

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

@ -215,8 +215,9 @@ void MediaEngineWebRTCMicrophoneSource::ApplySettings(
aPrefs.mResidualEchoOn;
RefPtr<MediaEngineWebRTCMicrophoneSource> that = this;
CubebUtils::AudioDeviceID deviceID = mDeviceInfo->DeviceID();
NS_DispatchToMainThread(NS_NewRunnableFunction(
__func__, [this, that, track = mTrack, prefs = aPrefs,
__func__, [this, that, deviceID, track = mTrack, prefs = aPrefs,
audioProcessingConfig = mAudioProcessingConfig] {
mSettings->mEchoCancellation.Value() = prefs.mAecOn;
mSettings->mAutoGainControl.Value() = prefs.mAgcOn;
@ -224,16 +225,19 @@ void MediaEngineWebRTCMicrophoneSource::ApplySettings(
mSettings->mChannelCount.Value() = prefs.mChannels;
class Message : public ControlMessage {
CubebUtils::AudioDeviceID mDeviceID;
const RefPtr<AudioInputProcessing> mInputProcessing;
const AudioProcessing::Config mAudioProcessingConfig;
const bool mPassThrough;
const uint32_t mRequestedInputChannelCount;
public:
Message(MediaTrack* aTrack, AudioInputProcessing* aInputProcessing,
Message(MediaTrack* aTrack, CubebUtils::AudioDeviceID aDeviceID,
AudioInputProcessing* aInputProcessing,
const AudioProcessing::Config& aAudioProcessingConfig,
bool aPassThrough, uint32_t aRequestedInputChannelCount)
: ControlMessage(aTrack),
mDeviceID(aDeviceID),
mInputProcessing(aInputProcessing),
mAudioProcessingConfig(aAudioProcessingConfig),
mPassThrough(aPassThrough),
@ -245,7 +249,7 @@ void MediaEngineWebRTCMicrophoneSource::ApplySettings(
{
TRACE("SetRequestedInputChannelCount");
mInputProcessing->SetRequestedInputChannelCount(
mTrack->GraphImpl(), mRequestedInputChannelCount);
mTrack->GraphImpl(), mDeviceID, mRequestedInputChannelCount);
}
{
TRACE("SetPassThrough")
@ -262,9 +266,9 @@ void MediaEngineWebRTCMicrophoneSource::ApplySettings(
if (track->IsDestroyed()) {
return;
}
track->GraphImpl()->AppendMessage(
MakeUnique<Message>(track, mInputProcessing, audioProcessingConfig,
passThrough, prefs.mChannels));
track->GraphImpl()->AppendMessage(MakeUnique<Message>(
track, deviceID, mInputProcessing, audioProcessingConfig,
passThrough, prefs.mChannels));
}));
}
@ -405,20 +409,9 @@ nsresult MediaEngineWebRTCMicrophoneSource::Start() {
MOZ_ASSERT(mState == kAllocated || mState == kStopped);
// This check is unreliable due to potential in-flight device updates.
// Multiple input devices are reliably excluded in ConnectDeviceInputImpl(),
// but the check here provides some error reporting most of the
// time.
CubebUtils::AudioDeviceID deviceID = mDeviceInfo->DeviceID();
if (mTrack->GraphImpl()->InputDeviceID() &&
mTrack->GraphImpl()->InputDeviceID() != deviceID) {
// For now, we only allow opening a single audio input device per document,
// because we can only have one MTG per document.
return NS_ERROR_NOT_AVAILABLE;
}
ApplySettings(mCurrentPrefs);
CubebUtils::AudioDeviceID deviceID = mDeviceInfo->DeviceID();
NS_DispatchToMainThread(NS_NewRunnableFunction(
__func__, [inputProcessing = mInputProcessing, deviceID, track = mTrack,
principal = mPrincipal] {
@ -428,7 +421,7 @@ nsresult MediaEngineWebRTCMicrophoneSource::Start() {
track->GraphImpl()->AppendMessage(MakeUnique<StartStopMessage>(
track, inputProcessing, StartStopMessage::Start));
track->ConnectDeviceInput(deviceID, inputProcessing, principal);
track->ConnectDeviceInput(deviceID, inputProcessing.get(), principal);
}));
MOZ_ASSERT(mState != kReleased);
@ -521,10 +514,11 @@ uint32_t AudioInputProcessing::GetRequestedInputChannelCount() {
}
void AudioInputProcessing::SetRequestedInputChannelCount(
MediaTrackGraphImpl* aGraph, uint32_t aRequestedInputChannelCount) {
MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aDeviceId,
uint32_t aRequestedInputChannelCount) {
mRequestedInputChannelCount = aRequestedInputChannelCount;
aGraph->ReevaluateInputDevice();
aGraph->ReevaluateInputDevice(aDeviceId);
}
void AudioInputProcessing::Start(MediaTrackGraphImpl* aGraph) {
@ -1237,7 +1231,8 @@ void AudioProcessingTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
} else {
MOZ_ASSERT(mInputs.Length() == 1);
AudioSegment data;
GetInputSourceData(data, mInputs[0], aFrom, aTo);
DeviceInputConsumerTrack::GetInputSourceData(data, mInputs[0], aFrom,
aTo);
mInputProcessing->Process(GraphImpl(), aFrom, aTo, &data,
GetData<AudioSegment>());
}
@ -1249,61 +1244,6 @@ void AudioProcessingTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
}
}
void AudioProcessingTrack::GetInputSourceData(AudioSegment& aOutput,
const MediaInputPort* aPort,
GraphTime aFrom,
GraphTime aTo) const {
MOZ_ASSERT(mGraph->OnGraphThread());
MOZ_ASSERT(aOutput.IsEmpty());
MediaTrack* source = aPort->GetSource();
GraphTime next;
for (GraphTime t = aFrom; t < aTo; t = next) {
MediaInputPort::InputInterval interval =
MediaInputPort::GetNextInputInterval(aPort, t);
interval.mEnd = std::min(interval.mEnd, aTo);
const bool inputEnded =
source->Ended() &&
source->GetEnd() <=
source->GraphTimeToTrackTimeWithBlocking(interval.mStart);
TrackTime ticks = interval.mEnd - interval.mStart;
next = interval.mEnd;
if (interval.mStart >= interval.mEnd) {
break;
}
if (inputEnded) {
aOutput.AppendNullData(ticks);
LOG_FRAME("(Graph %p, Driver %p) AudioProcessingTrack %p Getting %" PRId64
" ticks of null data from input port source (ended input)",
mGraph, mGraph->CurrentDriver(), this, ticks);
} else if (interval.mInputIsBlocked) {
aOutput.AppendNullData(ticks);
LOG_FRAME("(Graph %p, Driver %p) AudioProcessingTrack %p Getting %" PRId64
" ticks of null data from input port source (blocked input)",
mGraph, mGraph->CurrentDriver(), this, ticks);
} else if (source->IsSuspended()) {
aOutput.AppendNullData(ticks);
LOG_FRAME(
"(Graph %p, Driver %p) AudioProcessingTrack %p Getting %" PRId64
" ticks of null data from input port source (source is suspended)",
mGraph, mGraph->CurrentDriver(), this, ticks);
} else {
TrackTime start =
source->GraphTimeToTrackTimeWithBlocking(interval.mStart);
TrackTime end = source->GraphTimeToTrackTimeWithBlocking(interval.mEnd);
MOZ_ASSERT(source->GetData<AudioSegment>()->GetDuration() >= end);
aOutput.AppendSlice(*source->GetData<AudioSegment>(), start, end);
LOG_FRAME("(Graph %p, Driver %p) AudioInputTrack %p Getting %" PRId64
" ticks of real data from input port source %p",
mGraph, mGraph->CurrentDriver(), this, end - start, source);
}
}
}
void AudioProcessingTrack::NotifyOutputData(MediaTrackGraphImpl* aGraph,
AudioDataValue* aBuffer,
size_t aFrames, TrackRate aRate,
@ -1322,54 +1262,6 @@ void AudioProcessingTrack::SetInputProcessingImpl(
mInputProcessing = std::move(aInputProcessing);
}
nsresult AudioProcessingTrack::ConnectDeviceInput(
CubebUtils::AudioDeviceID aId, AudioDataListener* aListener,
const PrincipalHandle& aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GraphImpl());
MOZ_ASSERT(!mInputListener);
MOZ_ASSERT(mDeviceId.isNothing());
mInputListener = aListener;
mDeviceId.emplace(aId);
auto r = DeviceInputTrack::OpenAudio(GraphImpl(), aId, aPrincipal,
mInputListener.get());
if (r.isErr()) {
NS_WARNING("Failed to open audio device.");
return r.unwrapErr();
}
mDeviceInputTrack = r.unwrap();
MOZ_ASSERT(mDeviceInputTrack);
LOG("Open device %p (InputTrack=%p) for Mic source %p", aId,
mDeviceInputTrack.get(), this);
mPort = AllocateInputPort(mDeviceInputTrack.get());
return NS_OK;
}
void AudioProcessingTrack::DisconnectDeviceInput() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GraphImpl());
if (!mInputListener) {
return;
}
MOZ_ASSERT(mPort);
MOZ_ASSERT(mDeviceId.isSome());
MOZ_ASSERT(mDeviceInputTrack);
LOG("Close device %p (InputTrack=%p) for Mic source %p ", *mDeviceId,
mDeviceInputTrack.get(), this);
mPort->Destroy();
DeviceInputTrack::CloseAudio(std::move(mDeviceInputTrack),
mInputListener.get());
mInputListener = nullptr;
mDeviceId = Nothing();
}
Maybe<CubebUtils::AudioDeviceID> AudioProcessingTrack::DeviceId() const {
MOZ_ASSERT(NS_IsMainThread());
return mDeviceId;
}
MediaEngineWebRTCAudioCaptureSource::MediaEngineWebRTCAudioCaptureSource(
const MediaDevice* aMediaDevice) {
MOZ_ASSERT(aMediaDevice->mMediaSource == MediaSourceEnum::AudioCapture);

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

@ -141,6 +141,7 @@ class AudioInputProcessing : public AudioDataListener {
void SetPassThrough(MediaTrackGraphImpl* aGraph, bool aPassThrough);
uint32_t GetRequestedInputChannelCount();
void SetRequestedInputChannelCount(MediaTrackGraphImpl* aGraph,
CubebUtils::AudioDeviceID aDeviceId,
uint32_t aRequestedInputChannelCount);
// This is true when all processing is disabled, we can skip
// packetization, resampling and other processing passes.
@ -218,43 +219,17 @@ class AudioInputProcessing : public AudioDataListener {
};
// MediaTrack subclass tailored for MediaEngineWebRTCMicrophoneSource.
class AudioProcessingTrack : public ProcessedMediaTrack {
class AudioProcessingTrack : public DeviceInputConsumerTrack {
// Only accessed on the graph thread.
RefPtr<AudioInputProcessing> mInputProcessing;
// Only accessed on the main thread. Link to the track producing raw audio
// input data. Graph thread should use mInputs to get the source
RefPtr<MediaInputPort> mPort;
// Only accessed on the main thread. This is the track producing raw audio
// input data. Graph thread should MediaInputPort::GetSource() to get this
RefPtr<DeviceInputTrack> mDeviceInputTrack;
// Only accessed on the main thread. Used for bookkeeping on main thread, such
// that DisconnectDeviceInput can be idempotent.
// XXX Should really be a CubebUtils::AudioDeviceID, but they aren't
// copyable (opaque pointers)
RefPtr<AudioDataListener> mInputListener;
// Only accessed on the main thread.
Maybe<CubebUtils::AudioDeviceID> mDeviceId;
explicit AudioProcessingTrack(TrackRate aSampleRate)
: ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO,
new AudioSegment()) {}
: DeviceInputConsumerTrack(aSampleRate) {}
~AudioProcessingTrack() = default;
public:
// Main Thread API
// Users of audio inputs go through the track so it can track when the
// last track referencing an input goes away, so it can close the cubeb
// input. Main thread only.
nsresult ConnectDeviceInput(CubebUtils::AudioDeviceID aId,
AudioDataListener* aListener,
const PrincipalHandle& aPrincipal);
void DisconnectDeviceInput();
Maybe<CubebUtils::AudioDeviceID> DeviceId() const;
void Destroy() override;
void SetInputProcessing(RefPtr<AudioInputProcessing> aInputProcessing);
static AudioProcessingTrack* Create(MediaTrackGraph* aGraph);
@ -268,10 +243,6 @@ class AudioProcessingTrack : public ProcessedMediaTrack {
"Must set mInputProcessing before exposing to content");
return mInputProcessing->GetRequestedInputChannelCount();
}
// Get the data in [aFrom, aTo) from aPort->GetSource() to aOutput. aOutput
// needs to be empty.
void GetInputSourceData(AudioSegment& aOutput, const MediaInputPort* aPort,
GraphTime aFrom, GraphTime aTo) const;
// Pass the graph's mixed audio output to mInputProcessing for processing as
// the reverse stream.
void NotifyOutputData(MediaTrackGraphImpl* aGraph, AudioDataValue* aBuffer,

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

@ -8489,7 +8489,7 @@
# ClockDrift desired buffering in milliseconds
- name: media.clockdrift.buffering
type: uint32_t
type: RelaxedAtomicUint32
mirror: always
value: 50