2021-12-18 18:09:46 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "DeviceInputTrack.h"
|
|
|
|
|
|
|
|
#include "MediaTrackGraphImpl.h"
|
|
|
|
#include "Tracing.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
#ifdef LOG_INTERNAL
|
|
|
|
# undef LOG_INTERNAL
|
|
|
|
#endif // LOG_INTERNAL
|
|
|
|
#define LOG_INTERNAL(level, msg, ...) \
|
|
|
|
MOZ_LOG(gMediaTrackGraphLog, LogLevel::level, (msg, ##__VA_ARGS__))
|
|
|
|
|
|
|
|
#ifdef LOG
|
|
|
|
# undef LOG
|
|
|
|
#endif // LOG
|
|
|
|
#define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
|
|
|
|
|
2022-01-28 01:29:35 +03:00
|
|
|
#ifdef LOGE
|
|
|
|
# undef LOGE
|
|
|
|
#endif // LOGE
|
|
|
|
#define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
// This can only be called in graph thread since mGraph->CurrentDriver() is
|
|
|
|
// graph thread only
|
|
|
|
#ifdef TRACK_GRAPH_LOG_INTERNAL
|
|
|
|
# undef TRACK_GRAPH_LOG_INTERNAL
|
|
|
|
#endif // TRACK_GRAPH_LOG_INTERNAL
|
|
|
|
#define TRACK_GRAPH_LOG_INTERNAL(level, msg, ...) \
|
2022-04-18 21:45:33 +03:00
|
|
|
LOG_INTERNAL(level, "(Graph %p, Driver %p) DeviceInputTrack %p, " msg, \
|
2021-12-18 18:09:46 +03:00
|
|
|
this->mGraph, this->mGraph->CurrentDriver(), this, \
|
|
|
|
##__VA_ARGS__)
|
|
|
|
|
|
|
|
#ifdef TRACK_GRAPH_LOG
|
|
|
|
# undef TRACK_GRAPH_LOG
|
|
|
|
#endif // TRACK_GRAPH_LOG
|
|
|
|
#define TRACK_GRAPH_LOG(msg, ...) \
|
|
|
|
TRACK_GRAPH_LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
#ifdef TRACK_GRAPH_LOGV
|
|
|
|
# undef TRACK_GRAPH_LOGV
|
|
|
|
#endif // TRACK_GRAPH_LOGV
|
|
|
|
#define TRACK_GRAPH_LOGV(msg, ...) \
|
|
|
|
TRACK_GRAPH_LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
/* static */
|
2022-04-18 21:45:33 +03:00
|
|
|
Result<RefPtr<DeviceInputTrack>, nsresult> DeviceInputTrack::OpenAudio(
|
2022-01-28 01:29:33 +03:00
|
|
|
MediaTrackGraphImpl* aGraph, CubebUtils::AudioDeviceID aDeviceId,
|
2022-01-28 01:29:35 +03:00
|
|
|
const PrincipalHandle& aPrincipalHandle, AudioDataListener* aListener) {
|
2021-12-18 18:09:46 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
2022-04-18 21:45:33 +03:00
|
|
|
RefPtr<DeviceInputTrack> track = aGraph->GetNativeInputTrack();
|
2022-01-28 01:29:35 +03:00
|
|
|
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);
|
|
|
|
|
|
|
|
track->mUserCount += 1;
|
2022-04-18 21:45:33 +03:00
|
|
|
LOG("DeviceInputTrack %p (device %p) in MTG %p has %d users now", track.get(),
|
2022-01-28 01:29:35 +03:00
|
|
|
track->mDeviceId, aGraph, track->mUserCount);
|
|
|
|
if (track->mUserCount > 1) {
|
|
|
|
track->ReevaluateInputDevice();
|
|
|
|
}
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
return track;
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:29:35 +03:00
|
|
|
/* static */
|
2022-04-18 21:45:33 +03:00
|
|
|
void DeviceInputTrack::CloseAudio(RefPtr<DeviceInputTrack>&& aTrack,
|
2022-01-28 01:29:35 +03:00
|
|
|
AudioDataListener* aListener) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(aTrack);
|
|
|
|
MOZ_ASSERT(aTrack->mUserCount > 0);
|
|
|
|
|
|
|
|
aTrack->RemoveDataListener(aListener);
|
|
|
|
aTrack->mUserCount -= 1;
|
2022-04-18 21:45:33 +03:00
|
|
|
LOG("DeviceInputTrack %p (device %p) in MTG %p has %d users now",
|
2022-01-28 01:29:35 +03:00
|
|
|
aTrack.get(), aTrack->mDeviceId, aTrack->GraphImpl(), aTrack->mUserCount);
|
|
|
|
if (aTrack->mUserCount == 0) {
|
|
|
|
aTrack->GraphImpl()->CloseAudioInput(aTrack);
|
|
|
|
aTrack->Destroy();
|
|
|
|
} else {
|
|
|
|
aTrack->ReevaluateInputDevice();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-18 21:45:33 +03:00
|
|
|
DeviceInputTrack::DeviceInputTrack(TrackRate aSampleRate,
|
2022-01-28 01:29:33 +03:00
|
|
|
CubebUtils::AudioDeviceID aDeviceId,
|
2022-01-28 01:29:32 +03:00
|
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
|
|
: ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO, new AudioSegment()),
|
2022-01-28 01:29:33 +03:00
|
|
|
mDeviceId(aDeviceId),
|
2022-01-28 01:29:32 +03:00
|
|
|
mPrincipalHandle(aPrincipalHandle),
|
|
|
|
mUserCount(0) {}
|
|
|
|
|
2022-04-18 21:45:33 +03:00
|
|
|
uint32_t DeviceInputTrack::MaxRequestedInputChannels() const {
|
|
|
|
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
|
|
|
|
uint32_t maxInputChannels = 0;
|
|
|
|
for (const auto& listener : mListeners) {
|
|
|
|
maxInputChannels = std::max(maxInputChannels,
|
|
|
|
listener->RequestedInputChannelCount(mGraph));
|
|
|
|
}
|
|
|
|
return maxInputChannels;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeviceInputTrack::HasVoiceInput() const {
|
|
|
|
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
|
|
|
|
for (const auto& listener : mListeners) {
|
|
|
|
if (listener->IsVoiceInput(mGraph)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceInputTrack::DeviceChanged(MediaTrackGraphImpl* aGraph) const {
|
|
|
|
MOZ_ASSERT(aGraph->OnGraphThreadOrNotRunning());
|
|
|
|
MOZ_ASSERT(aGraph == mGraph,
|
|
|
|
"Receive device changed signal from another graph");
|
|
|
|
TRACK_GRAPH_LOG("DeviceChanged");
|
|
|
|
for (const auto& listener : mListeners) {
|
|
|
|
listener->DeviceChanged(aGraph);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceInputTrack::ReevaluateInputDevice() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
class Message : public ControlMessage {
|
|
|
|
public:
|
|
|
|
explicit Message(MediaTrackGraphImpl* aGraph)
|
|
|
|
: ControlMessage(nullptr), mGraph(aGraph) {}
|
|
|
|
void Run() override {
|
|
|
|
TRACE("DeviceInputTrack::ReevaluateInputDevice ControlMessage");
|
|
|
|
mGraph->ReevaluateInputDevice();
|
|
|
|
}
|
|
|
|
MediaTrackGraphImpl* mGraph;
|
|
|
|
};
|
|
|
|
mGraph->AppendMessage(MakeUnique<Message>(mGraph));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceInputTrack::AddDataListener(AudioDataListener* aListener) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
class Message : public ControlMessage {
|
|
|
|
public:
|
|
|
|
Message(DeviceInputTrack* aInputTrack, AudioDataListener* aListener)
|
|
|
|
: ControlMessage(nullptr),
|
|
|
|
mInputTrack(aInputTrack),
|
|
|
|
mListener(aListener) {}
|
|
|
|
void Run() override {
|
|
|
|
TRACE("DeviceInputTrack::AddDataListener ControlMessage");
|
|
|
|
MOZ_ASSERT(!mInputTrack->mListeners.Contains(mListener.get()),
|
|
|
|
"Don't add a listener twice.");
|
|
|
|
mInputTrack->mListeners.AppendElement(mListener.get());
|
|
|
|
}
|
|
|
|
RefPtr<DeviceInputTrack> mInputTrack;
|
|
|
|
RefPtr<AudioDataListener> mListener;
|
|
|
|
};
|
|
|
|
|
|
|
|
mGraph->AppendMessage(MakeUnique<Message>(this, aListener));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceInputTrack::RemoveDataListener(AudioDataListener* aListener) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
class Message : public ControlMessage {
|
|
|
|
public:
|
|
|
|
Message(DeviceInputTrack* aInputTrack, AudioDataListener* aListener)
|
|
|
|
: ControlMessage(nullptr),
|
|
|
|
mInputTrack(aInputTrack),
|
|
|
|
mListener(aListener) {}
|
|
|
|
void Run() override {
|
|
|
|
TRACE("DeviceInputTrack::RemoveDataListener ControlMessage");
|
|
|
|
DebugOnly<bool> wasPresent =
|
|
|
|
mInputTrack->mListeners.RemoveElement(mListener.get());
|
|
|
|
MOZ_ASSERT(wasPresent, "Remove an unknown listener");
|
|
|
|
mListener->Disconnect(mInputTrack->GraphImpl());
|
|
|
|
}
|
|
|
|
RefPtr<DeviceInputTrack> mInputTrack;
|
|
|
|
RefPtr<AudioDataListener> mListener;
|
|
|
|
};
|
|
|
|
|
|
|
|
mGraph->AppendMessage(MakeUnique<Message>(this, aListener));
|
|
|
|
}
|
|
|
|
|
|
|
|
NativeInputTrack::NativeInputTrack(TrackRate aSampleRate,
|
|
|
|
CubebUtils::AudioDeviceID aDeviceId,
|
|
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
|
|
: DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle),
|
|
|
|
mIsBufferingAppended(false),
|
|
|
|
mInputChannels(0) {}
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
void NativeInputTrack::DestroyImpl() {
|
|
|
|
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
|
2021-12-18 18:09:46 +03:00
|
|
|
mPendingData.Clear();
|
2021-12-18 18:09:46 +03:00
|
|
|
ProcessedMediaTrack::DestroyImpl();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
|
|
|
|
uint32_t aFlags) {
|
2022-02-10 02:22:37 +03:00
|
|
|
MOZ_ASSERT(mGraph->OnGraphThread());
|
2021-12-18 18:09:46 +03:00
|
|
|
TRACE_COMMENT("NativeInputTrack::ProcessInput", "%p", this);
|
|
|
|
|
2022-04-18 21:45:33 +03:00
|
|
|
TRACK_GRAPH_LOGV("(Native) ProcessInput from %" PRId64 " to %" PRId64
|
2021-12-18 18:09:46 +03:00
|
|
|
", needs %" PRId64 " frames",
|
|
|
|
aFrom, aTo, aTo - aFrom);
|
|
|
|
|
|
|
|
TrackTime from = GraphTimeToTrackTime(aFrom);
|
|
|
|
TrackTime to = GraphTimeToTrackTime(aTo);
|
|
|
|
if (from >= to) {
|
2021-12-18 18:09:46 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
MOZ_ASSERT_IF(!mIsBufferingAppended, mPendingData.IsEmpty());
|
2021-12-18 18:09:46 +03:00
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
TrackTime need = to - from;
|
|
|
|
TrackTime dataNeed = std::min(mPendingData.GetDuration(), need);
|
|
|
|
TrackTime silenceNeed = std::max(need - dataNeed, (TrackTime)0);
|
2021-12-18 18:09:46 +03:00
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
MOZ_ASSERT_IF(dataNeed > 0, silenceNeed == 0);
|
2021-12-18 18:09:46 +03:00
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
GetData<AudioSegment>()->AppendSlice(mPendingData, 0, dataNeed);
|
|
|
|
mPendingData.RemoveLeading(dataNeed);
|
|
|
|
GetData<AudioSegment>()->AppendNullData(silenceNeed);
|
2021-12-18 18:09:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t NativeInputTrack::NumberOfChannels() const {
|
|
|
|
MOZ_ASSERT(mGraph->OnGraphThreadOrNotRunning());
|
|
|
|
return mInputChannels;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeInputTrack::NotifyInputStopped(MediaTrackGraphImpl* aGraph) {
|
|
|
|
MOZ_ASSERT(aGraph->OnGraphThreadOrNotRunning());
|
|
|
|
MOZ_ASSERT(aGraph == mGraph,
|
|
|
|
"Receive input stopped signal from another graph");
|
2022-04-18 21:45:33 +03:00
|
|
|
TRACK_GRAPH_LOG("(Native) NotifyInputStopped");
|
2021-12-18 18:09:46 +03:00
|
|
|
mInputChannels = 0;
|
2021-12-18 18:09:46 +03:00
|
|
|
mIsBufferingAppended = false;
|
|
|
|
mPendingData.Clear();
|
2021-12-18 18:09:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void NativeInputTrack::NotifyInputData(MediaTrackGraphImpl* aGraph,
|
|
|
|
const AudioDataValue* aBuffer,
|
|
|
|
size_t aFrames, TrackRate aRate,
|
|
|
|
uint32_t aChannels,
|
|
|
|
uint32_t aAlreadyBuffered) {
|
2022-02-10 02:22:37 +03:00
|
|
|
MOZ_ASSERT(aGraph->OnGraphThread());
|
2021-12-18 18:09:46 +03:00
|
|
|
MOZ_ASSERT(aGraph == mGraph, "Receive input data from another graph");
|
2021-12-18 18:09:46 +03:00
|
|
|
TRACK_GRAPH_LOGV(
|
|
|
|
"NotifyInputData: frames=%zu, rate=%d, channel=%u, alreadyBuffered=%u",
|
|
|
|
aFrames, aRate, aChannels, aAlreadyBuffered);
|
|
|
|
|
|
|
|
if (!mIsBufferingAppended) {
|
|
|
|
// First time we see live frames getting added. Use what's already buffered
|
|
|
|
// in the driver's scratch buffer as a starting point.
|
|
|
|
MOZ_ASSERT(mPendingData.IsEmpty());
|
|
|
|
constexpr TrackTime buffering = WEBAUDIO_BLOCK_SIZE;
|
|
|
|
const TrackTime remaining =
|
|
|
|
buffering - static_cast<TrackTime>(aAlreadyBuffered);
|
|
|
|
mPendingData.AppendNullData(remaining);
|
|
|
|
mIsBufferingAppended = true;
|
|
|
|
TRACK_GRAPH_LOG("Set mIsBufferingAppended by appending %" PRId64 " frames.",
|
|
|
|
remaining);
|
|
|
|
}
|
2021-12-18 18:09:46 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT(aChannels);
|
|
|
|
if (!mInputChannels) {
|
|
|
|
mInputChannels = aChannels;
|
|
|
|
}
|
2021-12-18 18:09:46 +03:00
|
|
|
mPendingData.AppendFromInterleavedBuffer(aBuffer, aFrames, aChannels,
|
2022-01-05 02:14:08 +03:00
|
|
|
mPrincipalHandle);
|
2021-12-18 18:09:46 +03:00
|
|
|
}
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
#undef LOG_INTERNAL
|
|
|
|
#undef LOG
|
2022-01-28 01:29:35 +03:00
|
|
|
#undef LOGE
|
2021-12-18 18:09:46 +03:00
|
|
|
#undef TRACK_GRAPH_LOG_INTERNAL
|
|
|
|
#undef TRACK_GRAPH_LOG
|
|
|
|
#undef TRACK_GRAPH_LOGV
|
|
|
|
|
2021-12-18 18:09:46 +03:00
|
|
|
} // namespace mozilla
|