зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1860954 name GraphDriver audio streams with document title r=padenot
Differential Revision: https://phabricator.services.mozilla.com/D191837
This commit is contained in:
Родитель
3dc85f36e6
Коммит
04a0140bc4
|
@ -44,10 +44,17 @@ GraphDriver::GraphDriver(GraphInterface* aGraphInterface,
|
|||
mSampleRate(aSampleRate),
|
||||
mPreviousDriver(aPreviousDriver) {}
|
||||
|
||||
void GraphDriver::SetState(GraphTime aIterationEnd,
|
||||
void GraphDriver::SetStreamName(const nsACString& aStreamName) {
|
||||
MOZ_ASSERT(InIteration() || (!ThreadRunning() && NS_IsMainThread()));
|
||||
mStreamName = aStreamName;
|
||||
}
|
||||
|
||||
void GraphDriver::SetState(const nsACString& aStreamName,
|
||||
GraphTime aIterationEnd,
|
||||
GraphTime aStateComputedTime) {
|
||||
MOZ_ASSERT(InIteration() || !ThreadRunning());
|
||||
|
||||
mStreamName = aStreamName;
|
||||
mIterationEnd = aIterationEnd;
|
||||
mStateComputedTime = aStateComputedTime;
|
||||
}
|
||||
|
@ -219,7 +226,7 @@ void ThreadedDriver::RunThread() {
|
|||
if (GraphDriver* nextDriver = result.NextDriver()) {
|
||||
LOG(LogLevel::Debug, ("%p: Switching to AudioCallbackDriver", Graph()));
|
||||
result.Switched();
|
||||
nextDriver->SetState(mIterationEnd, mStateComputedTime);
|
||||
nextDriver->SetState(mStreamName, mIterationEnd, mStateComputedTime);
|
||||
nextDriver->Start();
|
||||
break;
|
||||
}
|
||||
|
@ -288,14 +295,17 @@ MediaTime OfflineClockDriver::GetIntervalForIteration() {
|
|||
}
|
||||
|
||||
AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver,
|
||||
AsyncCubebOperation aOperation)
|
||||
AsyncCubebOperation aOperation,
|
||||
const nsACString& aName)
|
||||
: Runnable("AsyncCubebTask"),
|
||||
mDriver(aDriver),
|
||||
mOperation(aOperation),
|
||||
mName(aName),
|
||||
mShutdownGrip(aDriver->Graph()) {
|
||||
MOZ_ASSERT(mDriver->mAudioStreamState ==
|
||||
AudioCallbackDriver::AudioStreamState::Pending ||
|
||||
aOperation == AsyncCubebOperation::SHUTDOWN,
|
||||
MOZ_ASSERT((aOperation == AsyncCubebOperation::SHUTDOWN) == aName.IsVoid());
|
||||
MOZ_ASSERT(aOperation != AsyncCubebOperation::INIT ||
|
||||
mDriver->mAudioStreamState ==
|
||||
AudioCallbackDriver::AudioStreamState::Pending,
|
||||
"Replacing active stream!");
|
||||
}
|
||||
|
||||
|
@ -309,7 +319,13 @@ AsyncCubebTask::Run() {
|
|||
case AsyncCubebOperation::INIT: {
|
||||
LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::INIT driver=%p",
|
||||
mDriver->Graph(), mDriver.get()));
|
||||
mDriver->Init();
|
||||
mDriver->Init(mName);
|
||||
break;
|
||||
}
|
||||
case AsyncCubebOperation::NAME_CHANGE: {
|
||||
LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::NAME_CHANGE driver=%p",
|
||||
mDriver->Graph(), mDriver.get()));
|
||||
mDriver->SetCubebStreamName(mName);
|
||||
break;
|
||||
}
|
||||
case AsyncCubebOperation::SHUTDOWN: {
|
||||
|
@ -350,14 +366,15 @@ class AudioCallbackDriver::FallbackWrapper : public GraphInterface {
|
|||
public:
|
||||
FallbackWrapper(RefPtr<GraphInterface> aGraph,
|
||||
RefPtr<AudioCallbackDriver> aOwner, uint32_t aSampleRate,
|
||||
GraphTime aIterationEnd, GraphTime aStateComputedTime)
|
||||
const nsACString& aStreamName, GraphTime aIterationEnd,
|
||||
GraphTime aStateComputedTime)
|
||||
: mGraph(std::move(aGraph)),
|
||||
mOwner(std::move(aOwner)),
|
||||
mFallbackDriver(
|
||||
MakeRefPtr<SystemClockDriver>(this, nullptr, aSampleRate)),
|
||||
mIterationEnd(aIterationEnd),
|
||||
mStateComputedTime(aStateComputedTime) {
|
||||
mFallbackDriver->SetState(mIterationEnd, mStateComputedTime);
|
||||
mFallbackDriver->SetState(aStreamName, mIterationEnd, mStateComputedTime);
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
@ -368,6 +385,9 @@ class AudioCallbackDriver::FallbackWrapper : public GraphInterface {
|
|||
RefPtr<SystemClockDriver> driver = mFallbackDriver;
|
||||
driver->Shutdown();
|
||||
}
|
||||
void SetStreamName(const nsACString& aStreamName) {
|
||||
mFallbackDriver->SetStreamName(aStreamName);
|
||||
}
|
||||
void EnsureNextIteration() { mFallbackDriver->EnsureNextIteration(); }
|
||||
#ifdef DEBUG
|
||||
bool InIteration() { return mFallbackDriver->InIteration(); }
|
||||
|
@ -446,7 +466,8 @@ class AudioCallbackDriver::FallbackWrapper : public GraphInterface {
|
|||
("%p: Switching from fallback to other driver.",
|
||||
mOwner.get()));
|
||||
result.Switched();
|
||||
nextDriver->SetState(mIterationEnd, mStateComputedTime);
|
||||
nextDriver->SetState(mOwner->mStreamName, mIterationEnd,
|
||||
mStateComputedTime);
|
||||
nextDriver->Start();
|
||||
} else if (result.IsStop()) {
|
||||
LOG(LogLevel::Debug,
|
||||
|
@ -546,7 +567,7 @@ bool IsMacbookOrMacbookAir() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void AudioCallbackDriver::Init() {
|
||||
void AudioCallbackDriver::Init(const nsCString& aStreamName) {
|
||||
TRACE("AudioCallbackDriver::Init");
|
||||
MOZ_ASSERT(OnCubebOperationThread());
|
||||
MOZ_ASSERT(mAudioStreamState == AudioStreamState::Pending);
|
||||
|
@ -645,12 +666,14 @@ void AudioCallbackDriver::Init() {
|
|||
}
|
||||
|
||||
cubeb_stream* stream = nullptr;
|
||||
const char* streamName =
|
||||
aStreamName.IsEmpty() ? "AudioCallbackDriver" : aStreamName.get();
|
||||
bool inputWanted = mInputChannelCount > 0;
|
||||
CubebUtils::AudioDeviceID outputId = mOutputDeviceID;
|
||||
CubebUtils::AudioDeviceID inputId = mInputDeviceID;
|
||||
|
||||
if (CubebUtils::CubebStreamInit(
|
||||
cubebContext, &stream, "AudioCallbackDriver", inputId,
|
||||
cubebContext, &stream, streamName, inputId,
|
||||
inputWanted ? &input : nullptr,
|
||||
forcedOutputDeviceId ? forcedOutputDeviceId : outputId, &output,
|
||||
latencyFrames, DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
|
||||
|
@ -698,6 +721,12 @@ void AudioCallbackDriver::Init() {
|
|||
LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", Graph()));
|
||||
}
|
||||
|
||||
void AudioCallbackDriver::SetCubebStreamName(const nsCString& aStreamName) {
|
||||
MOZ_ASSERT(OnCubebOperationThread());
|
||||
MOZ_ASSERT(mAudioStream);
|
||||
cubeb_stream_set_name(mAudioStream, aStreamName.get());
|
||||
}
|
||||
|
||||
void AudioCallbackDriver::Start() {
|
||||
MOZ_ASSERT(!IsStarted());
|
||||
MOZ_ASSERT(mAudioStreamState == AudioStreamState::None);
|
||||
|
@ -727,8 +756,8 @@ void AudioCallbackDriver::Start() {
|
|||
|
||||
LOG(LogLevel::Debug, ("Starting new audio driver off main thread, "
|
||||
"to ensure it runs after previous shutdown."));
|
||||
RefPtr<AsyncCubebTask> initEvent =
|
||||
new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::INIT);
|
||||
RefPtr<AsyncCubebTask> initEvent = new AsyncCubebTask(
|
||||
AsAudioCallbackDriver(), AsyncCubebOperation::INIT, mStreamName);
|
||||
initEvent->Dispatch();
|
||||
}
|
||||
|
||||
|
@ -782,6 +811,31 @@ void AudioCallbackDriver::Shutdown() {
|
|||
"AudioCallbackDriver::Shutdown"_ns);
|
||||
}
|
||||
|
||||
void AudioCallbackDriver::SetStreamName(const nsACString& aStreamName) {
|
||||
MOZ_ASSERT(InIteration() || !ThreadRunning());
|
||||
if (aStreamName == mStreamName) {
|
||||
return;
|
||||
}
|
||||
// Record the stream name, which will be passed onto the next driver, if
|
||||
// any, either from this driver or the fallback driver.
|
||||
GraphDriver::SetStreamName(aStreamName);
|
||||
{
|
||||
auto fallbackLock = mFallback.Lock();
|
||||
FallbackWrapper* fallback = fallbackLock.ref().get();
|
||||
if (fallback) {
|
||||
MOZ_ASSERT(fallback->OnThread());
|
||||
fallback->SetStreamName(aStreamName);
|
||||
}
|
||||
}
|
||||
AudioStreamState streamState = mAudioStreamState;
|
||||
if (streamState != AudioStreamState::None &&
|
||||
streamState != AudioStreamState::Stopping) {
|
||||
RefPtr<AsyncCubebTask> nameChange = new AsyncCubebTask(
|
||||
AsAudioCallbackDriver(), AsyncCubebOperation::NAME_CHANGE, mStreamName);
|
||||
nameChange->Dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
long AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream, void* aUser,
|
||||
const void* aInputBuffer,
|
||||
|
@ -994,7 +1048,7 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
|
|||
}
|
||||
result.Switched();
|
||||
mAudioStreamState = AudioStreamState::Stopping;
|
||||
nextDriver->SetState(mIterationEnd, mStateComputedTime);
|
||||
nextDriver->SetState(mStreamName, mIterationEnd, mStateComputedTime);
|
||||
nextDriver->Start();
|
||||
if (!mSandboxed) {
|
||||
CallbackThreadRegistry::Get()->Unregister(mAudioThreadId);
|
||||
|
@ -1212,8 +1266,9 @@ void AudioCallbackDriver::FallbackToSystemClockDriver() {
|
|||
mNextReInitBackoffStep =
|
||||
TimeDuration::FromMilliseconds(AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS);
|
||||
mNextReInitAttempt = TimeStamp::Now() + mNextReInitBackoffStep;
|
||||
auto fallback = MakeRefPtr<FallbackWrapper>(
|
||||
Graph(), this, mSampleRate, mIterationEnd, mStateComputedTime);
|
||||
auto fallback =
|
||||
MakeRefPtr<FallbackWrapper>(Graph(), this, mSampleRate, mStreamName,
|
||||
mIterationEnd, mStateComputedTime);
|
||||
{
|
||||
auto driver = mFallback.Lock();
|
||||
driver.ref() = fallback;
|
||||
|
|
|
@ -277,6 +277,9 @@ class GraphDriver {
|
|||
virtual void Start() = 0;
|
||||
/* Shutdown GraphDriver */
|
||||
MOZ_CAN_RUN_SCRIPT virtual void Shutdown() = 0;
|
||||
/* Set the UTF-8 name for system audio streams.
|
||||
* Graph thread, or main thread if the graph is not running. */
|
||||
virtual void SetStreamName(const nsACString& aStreamName);
|
||||
/* Rate at which the GraphDriver runs, in ms. This can either be user
|
||||
* controlled (because we are using a {System,Offline}ClockDriver, and decide
|
||||
* how often we want to wakeup/how much we want to process per iteration), or
|
||||
|
@ -315,7 +318,8 @@ class GraphDriver {
|
|||
* Set the state of the driver so it can start at the right point in time,
|
||||
* after switching from another driver.
|
||||
*/
|
||||
void SetState(GraphTime aIterationEnd, GraphTime aStateComputedTime);
|
||||
void SetState(const nsACString& aStreamName, GraphTime aIterationEnd,
|
||||
GraphTime aStateComputedTime);
|
||||
|
||||
GraphInterface* Graph() const { return mGraphInterface; }
|
||||
|
||||
|
@ -345,6 +349,8 @@ class GraphDriver {
|
|||
}
|
||||
|
||||
protected:
|
||||
// The UTF-8 name for system audio streams. Graph thread.
|
||||
nsCString mStreamName;
|
||||
// Time of the end of this graph iteration.
|
||||
GraphTime mIterationEnd = 0;
|
||||
// Time until which the graph has processed data.
|
||||
|
@ -532,7 +538,7 @@ struct TrackAndPromiseForOperation {
|
|||
MozPromiseHolder<MediaTrackGraph::AudioContextOperationPromise> mHolder;
|
||||
};
|
||||
|
||||
enum class AsyncCubebOperation { INIT, SHUTDOWN };
|
||||
enum class AsyncCubebOperation { INIT, NAME_CHANGE, SHUTDOWN };
|
||||
enum class AudioInputType { Unknown, Voice };
|
||||
|
||||
/**
|
||||
|
@ -573,6 +579,7 @@ class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver {
|
|||
|
||||
void Start() override;
|
||||
MOZ_CAN_RUN_SCRIPT void Shutdown() override;
|
||||
void SetStreamName(const nsACString& aStreamName) override;
|
||||
|
||||
/* Static wrapper function cubeb calls back. */
|
||||
static long DataCallback_s(cubeb_stream* aStream, void* aUser,
|
||||
|
@ -663,7 +670,8 @@ class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver {
|
|||
/* Start the cubeb stream */
|
||||
bool StartStream();
|
||||
friend class AsyncCubebTask;
|
||||
void Init();
|
||||
void Init(const nsCString& aStreamName);
|
||||
void SetCubebStreamName(const nsCString& aStreamName);
|
||||
void Stop();
|
||||
/**
|
||||
* Fall back to a SystemClockDriver using a normal thread. If needed,
|
||||
|
@ -787,7 +795,9 @@ class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver {
|
|||
|
||||
class AsyncCubebTask : public Runnable {
|
||||
public:
|
||||
AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation);
|
||||
// aName is not required for AsyncCubebOperation::SHUTDOWN
|
||||
AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation,
|
||||
const nsACString& aName = VoidCString());
|
||||
|
||||
nsresult Dispatch(uint32_t aFlags = NS_DISPATCH_NORMAL) {
|
||||
return mDriver->mInitShutdownThread->Dispatch(this, aFlags);
|
||||
|
@ -807,6 +817,7 @@ class AsyncCubebTask : public Runnable {
|
|||
|
||||
RefPtr<AudioCallbackDriver> mDriver;
|
||||
AsyncCubebOperation mOperation;
|
||||
nsCString mName;
|
||||
RefPtr<GraphInterface> mShutdownGrip;
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "CrossGraphPort.h"
|
||||
#include "VideoSegment.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindowInner.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "prerror.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#endif // MOZ_WEBRTC
|
||||
#include "MediaTrackListener.h"
|
||||
#include "mozilla/dom/BaseAudioContextBinding.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/WorkletThread.h"
|
||||
#include "mozilla/media/MediaUtils.h"
|
||||
#include <algorithm>
|
||||
|
@ -1680,6 +1682,40 @@ MediaTrackGraphImpl::Notify(nsITimer* aTimer) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsCString GetDocumentTitle(uint64_t aWindowID) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCString title;
|
||||
auto* win = nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
|
||||
if (!win) {
|
||||
return title;
|
||||
}
|
||||
Document* doc = win->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return title;
|
||||
}
|
||||
nsAutoString titleUTF16;
|
||||
doc->GetTitle(titleUTF16);
|
||||
CopyUTF16toUTF8(titleUTF16, title);
|
||||
return title;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaTrackGraphImpl::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(strcmp(aTopic, "document-title-changed") == 0);
|
||||
nsCString streamName = GetDocumentTitle(mWindowID);
|
||||
LOG(LogLevel::Debug, ("%p: document title: %s", this, streamName.get()));
|
||||
if (streamName.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
QueueControlMessageWithNoShutdown(
|
||||
[self = RefPtr{this}, this, streamName = std::move(streamName)] {
|
||||
CurrentDriver()->SetStreamName(streamName);
|
||||
});
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool MediaTrackGraphImpl::AddShutdownBlocker() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mShutdownBlocker);
|
||||
|
@ -3254,6 +3290,9 @@ MediaTrackGraphImpl::MediaTrackGraphImpl(
|
|||
} else {
|
||||
mDriver = new SystemClockDriver(this, nullptr, mSampleRate);
|
||||
}
|
||||
nsCString streamName = GetDocumentTitle(aWindowID);
|
||||
LOG(LogLevel::Debug, ("%p: document title: %s", this, streamName.get()));
|
||||
mDriver->SetStreamName(streamName);
|
||||
} else {
|
||||
mDriver =
|
||||
new OfflineClockDriver(this, mSampleRate, MEDIA_GRAPH_TARGET_PERIOD_MS);
|
||||
|
@ -3341,6 +3380,12 @@ MediaTrackGraphImpl* MediaTrackGraphImpl::GetInstance(
|
|||
aOutputDeviceID, aMainThread);
|
||||
MOZ_ALWAYS_TRUE(graphs->add(addPtr, graph));
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(graph, "document-title-changed", false);
|
||||
}
|
||||
|
||||
LOG(LogLevel::Debug, ("Starting up MediaTrackGraph %p for window 0x%" PRIx64,
|
||||
graph, aWindowID));
|
||||
|
||||
|
@ -3385,8 +3430,8 @@ void MediaTrackGraph::ForceShutDown() {
|
|||
graph->ForceShutDown();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(MediaTrackGraphImpl, nsIMemoryReporter, nsIThreadObserver,
|
||||
nsITimerCallback, nsINamed)
|
||||
NS_IMPL_ISUPPORTS(MediaTrackGraphImpl, nsIMemoryReporter, nsIObserver,
|
||||
nsIThreadObserver, nsITimerCallback, nsINamed)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaTrackGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
|
@ -3580,6 +3625,12 @@ void MediaTrackGraphImpl::RemoveTrack(MediaTrack* aTrack) {
|
|||
graphs->lookup({mWindowID, mSampleRate, mOutputDeviceID});
|
||||
MOZ_ASSERT(*p == this);
|
||||
graphs->remove(p);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->RemoveObserver(this, "document-title-changed");
|
||||
}
|
||||
}
|
||||
// The graph thread will shut itself down soon, but won't be able to do
|
||||
// that if JS continues to run.
|
||||
|
|
|
@ -107,6 +107,7 @@ class MessageBlock {
|
|||
class MediaTrackGraphImpl : public MediaTrackGraph,
|
||||
public GraphInterface,
|
||||
public nsIMemoryReporter,
|
||||
public nsIObserver,
|
||||
public nsIThreadObserver,
|
||||
public nsITimerCallback,
|
||||
public nsINamed {
|
||||
|
@ -115,6 +116,7 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
|
|||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSINAMED
|
||||
|
@ -186,6 +188,17 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
|
|||
* during RunInStableState; the messages will run on the graph thread.
|
||||
*/
|
||||
virtual void AppendMessage(UniquePtr<ControlMessageInterface> aMessage);
|
||||
/**
|
||||
* Append to the message queue a control message to execute a given lambda
|
||||
* function with no parameters. The lambda will be executed on the graph
|
||||
* thread. The lambda will not be executed if the graph has been forced to
|
||||
* shut down.
|
||||
**/
|
||||
template <typename Function>
|
||||
void QueueControlMessageWithNoShutdown(Function&& aFunction) {
|
||||
AppendMessage(WrapUnique(new MediaTrack::ControlMessageWithNoShutdown(
|
||||
std::forward<Function>(aFunction))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a runnable from any thread to the correct main thread for this
|
||||
|
|
|
@ -141,14 +141,12 @@ void cubeb_mock_destroy(cubeb* context) {
|
|||
MockCubeb::AsMock(context)->Destroy();
|
||||
}
|
||||
|
||||
MockCubebStream::MockCubebStream(cubeb* aContext, cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams,
|
||||
cubeb_data_callback aDataCallback,
|
||||
cubeb_state_callback aStateCallback,
|
||||
void* aUserPtr, SmartMockCubebStream* aSelf,
|
||||
RunningMode aRunningMode, bool aFrozenStart)
|
||||
MockCubebStream::MockCubebStream(
|
||||
cubeb* aContext, char const* aStreamName, cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams, cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams, cubeb_data_callback aDataCallback,
|
||||
cubeb_state_callback aStateCallback, void* aUserPtr,
|
||||
SmartMockCubebStream* aSelf, RunningMode aRunningMode, bool aFrozenStart)
|
||||
: context(aContext),
|
||||
mUserPtr(aUserPtr),
|
||||
mRunningMode(aRunningMode),
|
||||
|
@ -159,6 +157,7 @@ MockCubebStream::MockCubebStream(cubeb* aContext, cubeb_devid aInputDevice,
|
|||
mFrozenStart(aFrozenStart),
|
||||
mDataCallback(aDataCallback),
|
||||
mStateCallback(aStateCallback),
|
||||
mName(aStreamName),
|
||||
mInputDeviceID(aInputDevice),
|
||||
mOutputDeviceID(aOutputDevice),
|
||||
mAudioGenerator(aInputStreamParams ? aInputStreamParams->channels
|
||||
|
@ -252,6 +251,12 @@ void MockCubebStream::Destroy() {
|
|||
MockCubeb::AsMock(context)->StreamDestroy(this);
|
||||
}
|
||||
|
||||
int MockCubebStream::SetName(char const* aName) {
|
||||
mName = aName;
|
||||
mNameSetEvent.Notify(mName);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int MockCubebStream::RegisterDeviceChangedCallback(
|
||||
cubeb_device_changed_callback aDeviceChangedCallback) {
|
||||
if (mDeviceChangedCallback && aDeviceChangedCallback) {
|
||||
|
@ -326,6 +331,10 @@ void MockCubebStream::SetInputRecordingEnabled(bool aEnabled) {
|
|||
mInputRecordingEnabled = aEnabled;
|
||||
}
|
||||
|
||||
MediaEventSource<nsCString>& MockCubebStream::NameSetEvent() {
|
||||
return mNameSetEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<cubeb_state>& MockCubebStream::StateEvent() {
|
||||
return mStateEvent;
|
||||
}
|
||||
|
@ -612,7 +621,7 @@ void MockCubeb::UnforceAudioThread() {
|
|||
}
|
||||
|
||||
int MockCubeb::StreamInit(cubeb* aContext, cubeb_stream** aStream,
|
||||
cubeb_devid aInputDevice,
|
||||
char const* aStreamName, cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams,
|
||||
|
@ -624,7 +633,7 @@ int MockCubeb::StreamInit(cubeb* aContext, cubeb_stream** aStream,
|
|||
}
|
||||
|
||||
auto mockStream = MakeRefPtr<SmartMockCubebStream>(
|
||||
aContext, aInputDevice, aInputStreamParams, aOutputDevice,
|
||||
aContext, aStreamName, aInputDevice, aInputStreamParams, aOutputDevice,
|
||||
aOutputStreamParams, aDataCallback, aStateCallback, aUserPtr,
|
||||
mRunningMode, mStreamStartFreezeEnabled);
|
||||
*aStream = mockStream->AsCubebStream();
|
||||
|
|
|
@ -152,7 +152,8 @@ class MockCubebStream {
|
|||
enum class KeepProcessing { No, Yes };
|
||||
enum class RunningMode { Automatic, Manual };
|
||||
|
||||
MockCubebStream(cubeb* aContext, cubeb_devid aInputDevice,
|
||||
MockCubebStream(cubeb* aContext, char const* aStreamName,
|
||||
cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams,
|
||||
|
@ -167,12 +168,14 @@ class MockCubebStream {
|
|||
int Stop();
|
||||
uint64_t Position();
|
||||
void Destroy();
|
||||
int SetName(char const* aName);
|
||||
int RegisterDeviceChangedCallback(
|
||||
cubeb_device_changed_callback aDeviceChangedCallback);
|
||||
|
||||
cubeb_stream* AsCubebStream();
|
||||
static MockCubebStream* AsMock(cubeb_stream* aStream);
|
||||
|
||||
char const* StreamName() const { return mName.get(); }
|
||||
cubeb_devid GetInputDeviceID() const;
|
||||
cubeb_devid GetOutputDeviceID() const;
|
||||
|
||||
|
@ -202,6 +205,7 @@ class MockCubebStream {
|
|||
// only works once.
|
||||
nsTArray<AudioDataValue>&& TakeRecordedInput();
|
||||
|
||||
MediaEventSource<nsCString>& NameSetEvent();
|
||||
MediaEventSource<cubeb_state>& StateEvent();
|
||||
MediaEventSource<uint32_t>& FramesProcessedEvent();
|
||||
MediaEventSource<uint32_t>& FramesVerifiedEvent();
|
||||
|
@ -249,6 +253,8 @@ class MockCubebStream {
|
|||
cubeb_state_callback mStateCallback = nullptr;
|
||||
// The device changed callback
|
||||
cubeb_device_changed_callback mDeviceChangedCallback = nullptr;
|
||||
// A name for this stream
|
||||
nsCString mName;
|
||||
// The stream params
|
||||
cubeb_stream_params mOutputParams = {};
|
||||
cubeb_stream_params mInputParams = {};
|
||||
|
@ -265,6 +271,7 @@ class MockCubebStream {
|
|||
AudioGenerator<AudioDataValue> mAudioGenerator;
|
||||
AudioVerifier<AudioDataValue> mAudioVerifier;
|
||||
|
||||
MediaEventProducer<nsCString> mNameSetEvent;
|
||||
MediaEventProducer<cubeb_state> mStateEvent;
|
||||
MediaEventProducer<uint32_t> mFramesProcessedEvent;
|
||||
MediaEventProducer<uint32_t> mFramesVerifiedEvent;
|
||||
|
@ -285,14 +292,15 @@ class SmartMockCubebStream
|
|||
public SupportsThreadSafeWeakPtr<SmartMockCubebStream> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(SmartMockCubebStream)
|
||||
SmartMockCubebStream(cubeb* aContext, cubeb_devid aInputDevice,
|
||||
SmartMockCubebStream(cubeb* aContext, char const* aStreamName,
|
||||
cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams,
|
||||
cubeb_data_callback aDataCallback,
|
||||
cubeb_state_callback aStateCallback, void* aUserPtr,
|
||||
RunningMode aRunningMode, bool aFrozenStart)
|
||||
: MockCubebStream(aContext, aInputDevice, aInputStreamParams,
|
||||
: MockCubebStream(aContext, aStreamName, aInputDevice, aInputStreamParams,
|
||||
aOutputDevice, aOutputStreamParams, aDataCallback,
|
||||
aStateCallback, aUserPtr, this, aRunningMode,
|
||||
aFrozenStart) {}
|
||||
|
@ -387,7 +395,7 @@ class MockCubeb {
|
|||
void UnforceAudioThread();
|
||||
|
||||
int StreamInit(cubeb* aContext, cubeb_stream** aStream,
|
||||
cubeb_devid aInputDevice,
|
||||
char const* aStreamName, cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
cubeb_devid aOutputDevice,
|
||||
cubeb_stream_params* aOutputStreamParams,
|
||||
|
@ -483,8 +491,9 @@ int cubeb_mock_stream_init(
|
|||
unsigned int latency, cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback, void* user_ptr) {
|
||||
return MockCubeb::AsMock(context)->StreamInit(
|
||||
context, stream, input_device, input_stream_params, output_device,
|
||||
output_stream_params, data_callback, state_callback, user_ptr);
|
||||
context, stream, stream_name, input_device, input_stream_params,
|
||||
output_device, output_stream_params, data_callback, state_callback,
|
||||
user_ptr);
|
||||
}
|
||||
|
||||
int cubeb_mock_stream_start(cubeb_stream* stream) {
|
||||
|
@ -524,6 +533,7 @@ static int cubeb_mock_stream_set_volume(cubeb_stream* stream, float volume) {
|
|||
|
||||
static int cubeb_mock_stream_set_name(cubeb_stream* stream,
|
||||
char const* stream_name) {
|
||||
return MockCubebStream::AsMock(stream)->SetName(stream_name);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -219,6 +219,44 @@ TEST(TestAudioTrackGraph, SetOutputDeviceID)
|
|||
WaitFor(cubeb->StreamDestroyEvent());
|
||||
}
|
||||
|
||||
TEST(TestAudioTrackGraph, StreamName)
|
||||
{
|
||||
MockCubeb* cubeb = new MockCubeb();
|
||||
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
|
||||
|
||||
// Initialize a graph with a system thread driver to check that the stream
|
||||
// name survives the driver switch.
|
||||
MediaTrackGraphImpl* graph = MediaTrackGraphImpl::GetInstance(
|
||||
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
|
||||
CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
|
||||
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
|
||||
GetMainThreadSerialEventTarget());
|
||||
nsLiteralCString name1("name1");
|
||||
graph->CurrentDriver()->SetStreamName(name1);
|
||||
|
||||
// Dummy track to start the graph rolling and switch to an
|
||||
// AudioCallbackDriver.
|
||||
RefPtr<SourceMediaTrack> dummySource;
|
||||
DispatchFunction(
|
||||
[&] { dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); });
|
||||
|
||||
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
|
||||
EXPECT_STREQ(stream->StreamName(), name1.get());
|
||||
|
||||
// Test a name change on an existing stream.
|
||||
nsLiteralCString name2("name2");
|
||||
DispatchFunction([&] {
|
||||
graph->QueueControlMessageWithNoShutdown(
|
||||
[&] { graph->CurrentDriver()->SetStreamName(name2); });
|
||||
});
|
||||
nsCString name = WaitFor(stream->NameSetEvent());
|
||||
EXPECT_EQ(name, name2);
|
||||
|
||||
// Test has finished. Destroy the track to shutdown the MTG.
|
||||
DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
|
||||
WaitFor(cubeb->StreamDestroyEvent());
|
||||
}
|
||||
|
||||
TEST(TestAudioTrackGraph, NotifyDeviceStarted)
|
||||
{
|
||||
MockCubeb* cubeb = new MockCubeb();
|
||||
|
|
Загрузка…
Ссылка в новой задаче