зеркало из https://github.com/mozilla/gecko-dev.git
634 строки
21 KiB
C++
634 строки
21 KiB
C++
/* -*- 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 "Tracing.h"
|
|
|
|
namespace mozilla {
|
|
|
|
#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__)
|
|
|
|
#ifdef LOGE
|
|
# undef LOGE
|
|
#endif // LOGE
|
|
#define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
|
|
|
|
// 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, ...) \
|
|
LOG_INTERNAL(level, "(Graph %p, Driver %p) DeviceInputTrack %p, " msg, \
|
|
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__)
|
|
|
|
#ifdef TRACK_GRAPH_LOGE
|
|
# undef TRACK_GRAPH_LOGE
|
|
#endif // TRACK_GRAPH_LOGE
|
|
#define TRACK_GRAPH_LOGE(msg, ...) \
|
|
TRACK_GRAPH_LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
|
|
|
|
#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(Graph());
|
|
MOZ_ASSERT(aListener);
|
|
MOZ_ASSERT(!mListener);
|
|
MOZ_ASSERT(!mDeviceInputTrack);
|
|
MOZ_ASSERT(mDeviceId.isNothing());
|
|
MOZ_ASSERT(!mDeviceInputTrack,
|
|
"Must disconnect a device input before connecting a new one");
|
|
|
|
mListener = aListener;
|
|
mDeviceId.emplace(aId);
|
|
|
|
mDeviceInputTrack =
|
|
DeviceInputTrack::OpenAudio(Graph(), 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(Graph());
|
|
|
|
if (!mListener) {
|
|
MOZ_ASSERT(mDeviceId.isNothing());
|
|
MOZ_ASSERT(!mDeviceInputTrack);
|
|
return;
|
|
}
|
|
|
|
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 {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return mDeviceInputTrack && mDeviceInputTrack->AsNativeInputTrack();
|
|
}
|
|
|
|
bool DeviceInputConsumerTrack::ConnectToNonNativeDevice() const {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return mDeviceInputTrack && mDeviceInputTrack->AsNonNativeInputTrack();
|
|
}
|
|
|
|
void DeviceInputConsumerTrack::GetInputSourceData(AudioSegment& aOutput,
|
|
const MediaInputPort* aPort,
|
|
GraphTime aFrom,
|
|
GraphTime aTo) const {
|
|
AssertOnGraphThread();
|
|
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 */
|
|
NotNull<RefPtr<DeviceInputTrack>> DeviceInputTrack::OpenAudio(
|
|
MediaTrackGraph* aGraph, CubebUtils::AudioDeviceID aDeviceId,
|
|
const PrincipalHandle& aPrincipalHandle,
|
|
DeviceInputConsumerTrack* aConsumer) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aConsumer);
|
|
MOZ_ASSERT(aGraph == aConsumer->Graph());
|
|
|
|
RefPtr<DeviceInputTrack> track =
|
|
aGraph->GetDeviceInputTrackMainThread(aDeviceId);
|
|
if (track) {
|
|
MOZ_ASSERT(!track->mConsumerTracks.IsEmpty());
|
|
track->AddDataListener(aConsumer->GetAudioDataListener());
|
|
} else {
|
|
// 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->Graph(),
|
|
track->mConsumerTracks.Length());
|
|
if (track->mConsumerTracks.IsEmpty()) {
|
|
track->Graph()->CloseAudioInput(track);
|
|
track->Destroy();
|
|
} else {
|
|
track->ReevaluateInputDevice();
|
|
}
|
|
}
|
|
|
|
const nsTArray<RefPtr<DeviceInputConsumerTrack>>&
|
|
DeviceInputTrack::GetConsumerTracks() const {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return mConsumerTracks;
|
|
}
|
|
|
|
DeviceInputTrack::DeviceInputTrack(TrackRate aSampleRate,
|
|
CubebUtils::AudioDeviceID aDeviceId,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
: ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO, new AudioSegment()),
|
|
mDeviceId(aDeviceId),
|
|
mPrincipalHandle(aPrincipalHandle) {}
|
|
|
|
uint32_t DeviceInputTrack::MaxRequestedInputChannels() const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
uint32_t maxInputChannels = 0;
|
|
for (const auto& listener : mListeners) {
|
|
maxInputChannels = std::max(maxInputChannels,
|
|
listener->RequestedInputChannelCount(mGraph));
|
|
}
|
|
return maxInputChannels;
|
|
}
|
|
|
|
bool DeviceInputTrack::HasVoiceInput() const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
for (const auto& listener : mListeners) {
|
|
if (listener->IsVoiceInput(mGraph)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DeviceInputTrack::DeviceChanged(MediaTrackGraph* aGraph) const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
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());
|
|
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this] {
|
|
TRACE("DeviceInputTrack::ReevaluateInputDevice ControlMessage");
|
|
Graph()->ReevaluateInputDevice(mDeviceId);
|
|
});
|
|
}
|
|
|
|
void DeviceInputTrack::AddDataListener(AudioDataListener* aListener) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
QueueControlMessageWithNoShutdown(
|
|
[self = RefPtr{this}, this, listener = RefPtr{aListener}] {
|
|
TRACE("DeviceInputTrack::AddDataListener ControlMessage");
|
|
MOZ_ASSERT(!mListeners.Contains(listener.get()),
|
|
"Don't add a listener twice.");
|
|
mListeners.AppendElement(listener.get());
|
|
});
|
|
}
|
|
|
|
void DeviceInputTrack::RemoveDataListener(AudioDataListener* aListener) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
QueueControlMessageWithNoShutdown(
|
|
[self = RefPtr{this}, this, listener = RefPtr{aListener}] {
|
|
TRACE("DeviceInputTrack::RemoveDataListener ControlMessage");
|
|
DebugOnly<bool> wasPresent = mListeners.RemoveElement(listener.get());
|
|
MOZ_ASSERT(wasPresent, "Remove an unknown listener");
|
|
listener->Disconnect(Graph());
|
|
});
|
|
}
|
|
|
|
NativeInputTrack::NativeInputTrack(TrackRate aSampleRate,
|
|
CubebUtils::AudioDeviceID aDeviceId,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
: DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle),
|
|
mIsBufferingAppended(false),
|
|
mInputChannels(0) {}
|
|
|
|
void NativeInputTrack::DestroyImpl() {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
mPendingData.Clear();
|
|
ProcessedMediaTrack::DestroyImpl();
|
|
}
|
|
|
|
void NativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
|
|
uint32_t aFlags) {
|
|
AssertOnGraphThread();
|
|
TRACE_COMMENT("NativeInputTrack::ProcessInput", "%p", this);
|
|
|
|
TRACK_GRAPH_LOGV("(Native) ProcessInput from %" PRId64 " to %" PRId64
|
|
", needs %" PRId64 " frames",
|
|
aFrom, aTo, aTo - aFrom);
|
|
|
|
TrackTime from = GraphTimeToTrackTime(aFrom);
|
|
TrackTime to = GraphTimeToTrackTime(aTo);
|
|
if (from >= to) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT_IF(!mIsBufferingAppended, mPendingData.IsEmpty());
|
|
|
|
TrackTime need = to - from;
|
|
TrackTime dataNeed = std::min(mPendingData.GetDuration(), need);
|
|
TrackTime silenceNeed = std::max(need - dataNeed, (TrackTime)0);
|
|
|
|
MOZ_ASSERT_IF(dataNeed > 0, silenceNeed == 0);
|
|
|
|
GetData<AudioSegment>()->AppendSlice(mPendingData, 0, dataNeed);
|
|
mPendingData.RemoveLeading(dataNeed);
|
|
GetData<AudioSegment>()->AppendNullData(silenceNeed);
|
|
}
|
|
|
|
uint32_t NativeInputTrack::NumberOfChannels() const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
return mInputChannels;
|
|
}
|
|
|
|
void NativeInputTrack::NotifyInputStopped(MediaTrackGraph* aGraph) {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
MOZ_ASSERT(aGraph == mGraph,
|
|
"Receive input stopped signal from another graph");
|
|
TRACK_GRAPH_LOG("(Native) NotifyInputStopped");
|
|
mInputChannels = 0;
|
|
mIsBufferingAppended = false;
|
|
mPendingData.Clear();
|
|
}
|
|
|
|
void NativeInputTrack::NotifyInputData(MediaTrackGraph* aGraph,
|
|
const AudioDataValue* aBuffer,
|
|
size_t aFrames, TrackRate aRate,
|
|
uint32_t aChannels,
|
|
uint32_t aAlreadyBuffered) {
|
|
AssertOnGraphThread();
|
|
MOZ_ASSERT(aGraph == mGraph, "Receive input data from another graph");
|
|
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);
|
|
}
|
|
|
|
MOZ_ASSERT(aChannels);
|
|
if (!mInputChannels) {
|
|
mInputChannels = aChannels;
|
|
}
|
|
mPendingData.AppendFromInterleavedBuffer(aBuffer, aFrames, aChannels,
|
|
mPrincipalHandle);
|
|
}
|
|
|
|
NonNativeInputTrack::NonNativeInputTrack(
|
|
TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
: DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle),
|
|
mAudioSource(nullptr),
|
|
mSourceIdNumber(0) {}
|
|
|
|
void NonNativeInputTrack::DestroyImpl() {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
if (mAudioSource) {
|
|
mAudioSource->Stop();
|
|
mAudioSource = nullptr;
|
|
}
|
|
ProcessedMediaTrack::DestroyImpl();
|
|
}
|
|
|
|
void NonNativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
|
|
uint32_t aFlags) {
|
|
AssertOnGraphThread();
|
|
TRACE_COMMENT("NonNativeInputTrack::ProcessInput", "%p", this);
|
|
|
|
TRACK_GRAPH_LOGV("(NonNative) ProcessInput from %" PRId64 " to %" PRId64
|
|
", needs %" PRId64 " frames",
|
|
aFrom, aTo, aTo - aFrom);
|
|
|
|
TrackTime from = GraphTimeToTrackTime(aFrom);
|
|
TrackTime to = GraphTimeToTrackTime(aTo);
|
|
if (from >= to) {
|
|
return;
|
|
}
|
|
|
|
TrackTime delta = to - from;
|
|
if (!mAudioSource) {
|
|
GetData<AudioSegment>()->AppendNullData(delta);
|
|
return;
|
|
}
|
|
|
|
AudioInputSource::Consumer consumer = AudioInputSource::Consumer::Same;
|
|
// GraphRunner keeps the same thread.
|
|
MOZ_ASSERT(!HasGraphThreadChanged());
|
|
|
|
AudioSegment data = mAudioSource->GetAudioSegment(delta, consumer);
|
|
MOZ_ASSERT(data.GetDuration() == delta);
|
|
GetData<AudioSegment>()->AppendFrom(&data);
|
|
}
|
|
|
|
uint32_t NonNativeInputTrack::NumberOfChannels() const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
return mAudioSource ? mAudioSource->mChannelCount : 0;
|
|
}
|
|
|
|
void NonNativeInputTrack::StartAudio(
|
|
RefPtr<AudioInputSource>&& aAudioInputSource) {
|
|
AssertOnGraphThread();
|
|
MOZ_ASSERT(aAudioInputSource->mPrincipalHandle == mPrincipalHandle);
|
|
MOZ_ASSERT(aAudioInputSource->mDeviceId == mDeviceId);
|
|
|
|
TRACK_GRAPH_LOG("StartAudio with source %p", aAudioInputSource.get());
|
|
#ifdef DEBUG
|
|
mGraphThreadId = std::this_thread::get_id();
|
|
#endif
|
|
mAudioSource = std::move(aAudioInputSource);
|
|
mAudioSource->Start();
|
|
}
|
|
|
|
void NonNativeInputTrack::StopAudio() {
|
|
AssertOnGraphThread();
|
|
|
|
TRACK_GRAPH_LOG("StopAudio from source %p", mAudioSource.get());
|
|
if (!mAudioSource) {
|
|
return;
|
|
}
|
|
mAudioSource->Stop();
|
|
mAudioSource = nullptr;
|
|
#ifdef DEBUG
|
|
mGraphThreadId = std::thread::id();
|
|
#endif
|
|
}
|
|
|
|
AudioInputType NonNativeInputTrack::DevicePreference() const {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
return mAudioSource && mAudioSource->mIsVoice ? AudioInputType::Voice
|
|
: AudioInputType::Unknown;
|
|
}
|
|
|
|
void NonNativeInputTrack::NotifyDeviceChanged(uint32_t aSourceId) {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
|
|
// No need to forward the notification if the audio input has been stopped or
|
|
// restarted by it users.
|
|
if (!mAudioSource || mAudioSource->mId != aSourceId) {
|
|
TRACK_GRAPH_LOG("(NonNative) NotifyDeviceChanged: No need to forward");
|
|
return;
|
|
}
|
|
|
|
TRACK_GRAPH_LOG("(NonNative) NotifyDeviceChanged");
|
|
// Forward the notification.
|
|
DeviceInputTrack::DeviceChanged(mGraph);
|
|
}
|
|
|
|
void NonNativeInputTrack::NotifyInputStopped(uint32_t aSourceId) {
|
|
AssertOnGraphThreadOrNotRunning();
|
|
|
|
// No need to forward the notification if the audio input has been stopped or
|
|
// restarted by it users.
|
|
if (!mAudioSource || mAudioSource->mId != aSourceId) {
|
|
TRACK_GRAPH_LOG("(NonNative) NotifyInputStopped: No need to forward");
|
|
return;
|
|
}
|
|
|
|
TRACK_GRAPH_LOGE(
|
|
"(NonNative) NotifyInputStopped: audio unexpectedly stopped");
|
|
// Destory the underlying audio stream if it's stopped unexpectedly.
|
|
mAudioSource->Stop();
|
|
}
|
|
|
|
AudioInputSource::Id NonNativeInputTrack::GenerateSourceId() {
|
|
AssertOnGraphThread();
|
|
return mSourceIdNumber++;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
bool NonNativeInputTrack::HasGraphThreadChanged() {
|
|
AssertOnGraphThread();
|
|
|
|
std::thread::id currentId = std::this_thread::get_id();
|
|
if (mGraphThreadId == currentId) {
|
|
return false;
|
|
}
|
|
mGraphThreadId = currentId;
|
|
return true;
|
|
}
|
|
#endif // DEBUG
|
|
|
|
AudioInputSourceListener::AudioInputSourceListener(NonNativeInputTrack* aOwner)
|
|
: mOwner(aOwner) {}
|
|
|
|
void AudioInputSourceListener::AudioDeviceChanged(
|
|
AudioInputSource::Id aSourceId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mOwner);
|
|
|
|
if (mOwner->IsDestroyed()) {
|
|
LOG("NonNativeInputTrack %p has been destroyed. No need to forward the "
|
|
"audio device-changed notification",
|
|
mOwner.get());
|
|
return;
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mOwner->Graph());
|
|
mOwner->QueueControlMessageWithNoShutdown([inputTrack = mOwner, aSourceId] {
|
|
TRACE("NonNativeInputTrack::AudioDeviceChanged ControlMessage");
|
|
inputTrack->NotifyDeviceChanged(aSourceId);
|
|
});
|
|
}
|
|
|
|
void AudioInputSourceListener::AudioStateCallback(
|
|
AudioInputSource::Id aSourceId,
|
|
AudioInputSource::EventListener::State aState) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mOwner);
|
|
|
|
const char* state =
|
|
aState == AudioInputSource::EventListener::State::Started ? "started"
|
|
: aState == AudioInputSource::EventListener::State::Stopped ? "stopped"
|
|
: aState == AudioInputSource::EventListener::State::Drained ? "drained"
|
|
: "error";
|
|
|
|
if (mOwner->IsDestroyed()) {
|
|
LOG("NonNativeInputTrack %p has been destroyed. No need to forward the "
|
|
"audio state-changed(%s) notification",
|
|
mOwner.get(), state);
|
|
return;
|
|
}
|
|
|
|
if (aState == AudioInputSource::EventListener::State::Started) {
|
|
LOG("We can ignore %s notification for NonNativeInputTrack %p", state,
|
|
mOwner.get());
|
|
return;
|
|
}
|
|
|
|
LOG("Notify audio stopped due to entering %s state", state);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mOwner->Graph());
|
|
mOwner->QueueControlMessageWithNoShutdown([inputTrack = mOwner, aSourceId] {
|
|
TRACE("NonNativeInputTrack::AudioStateCallback ControlMessage");
|
|
inputTrack->NotifyInputStopped(aSourceId);
|
|
});
|
|
}
|
|
|
|
#undef LOG_INTERNAL
|
|
#undef LOG
|
|
#undef LOGE
|
|
#undef TRACK_GRAPH_LOG_INTERNAL
|
|
#undef TRACK_GRAPH_LOG
|
|
#undef TRACK_GRAPH_LOGV
|
|
#undef TRACK_GRAPH_LOGE
|
|
#undef CONSUMER_GRAPH_LOG_INTERNAL
|
|
#undef CONSUMER_GRAPH_LOGV
|
|
|
|
} // namespace mozilla
|