Bug 1324552 - Implement AudioContext baseLatency and outputLatency attributes. r=pehrsons,baku

Differential Revision: https://phabricator.services.mozilla.com/D37280

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Paul Adenot 2019-07-12 11:28:03 +00:00
Родитель 2a3e21430e
Коммит 8c692f2f23
12 изменённых файлов: 67 добавлений и 29 удалений

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

@ -1132,6 +1132,17 @@ void AudioCallbackDriver::CompleteAudioContextOperations(
}
}
TimeDuration AudioCallbackDriver::AudioOutputLatency() {
uint32_t latency_frames;
int rv = cubeb_stream_get_latency(mAudioStream, &latency_frames);
if (rv || mSampleRate == 0) {
return TimeDuration::FromSeconds(0.0);
}
return TimeDuration::FromSeconds(static_cast<double>(latency_frames) /
mSampleRate);
}
void AudioCallbackDriver::FallbackToSystemClockDriver() {
MOZ_ASSERT(!ThreadRunning());
GraphImpl()->GetMonitor().AssertCurrentThreadOwns();

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

@ -433,6 +433,9 @@ class AudioCallbackDriver : public GraphDriver,
void CompleteAudioContextOperations(AsyncCubebOperation aOperation);
// Returns the output latency for the current audio output stream.
TimeDuration AudioOutputLatency();
private:
/* Remove Mixer callbacks when switching */
void RemoveMixerCallback();

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

@ -957,6 +957,10 @@ void MediaStreamGraphImpl::DeviceChanged() {
MediaStreamGraphImpl* mGraphImpl;
};
// Reset the latency, it will get fetched again next time it's queried.
MOZ_ASSERT(NS_IsMainThread());
mAudioOutputLatency = 0.0;
AppendMessage(MakeUnique<Message>(this));
}
@ -3192,7 +3196,8 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested,
mCanRunMessagesSynchronously(false)
#endif
,
mMainThreadGraphTime(0, "MediaStreamGraphImpl::mMainThreadGraphTime") {
mMainThreadGraphTime(0, "MediaStreamGraphImpl::mMainThreadGraphTime"),
mAudioOutputLatency(0.0) {
if (mRealtime) {
if (aDriverRequested == AUDIO_THREAD_DRIVER) {
// Always start with zero input channels, and no particular preferences
@ -3796,6 +3801,29 @@ void MediaStreamGraph::ApplyAudioContextOperation(
aDestinationStream, aStreams, aOperation, aPromise, aFlags));
}
double MediaStreamGraph::AudioOutputLatency() {
return static_cast<MediaStreamGraphImpl*>(this)->AudioOutputLatency();
}
double MediaStreamGraphImpl::AudioOutputLatency() {
MOZ_ASSERT(NS_IsMainThread());
if (mAudioOutputLatency != 0.0) {
return mAudioOutputLatency;
}
MonitorAutoLock lock(mMonitor);
if (CurrentDriver()->AsAudioCallbackDriver()) {
mAudioOutputLatency = CurrentDriver()
->AsAudioCallbackDriver()
->AudioOutputLatency()
.ToSeconds();
} else {
// Failure mode: return 0.0 if running on a normal thread.
mAudioOutputLatency = 0.0;
}
return mAudioOutputLatency;
}
bool MediaStreamGraph::IsNonRealtime() const {
return !static_cast<const MediaStreamGraphImpl*>(this)->mRealtime;
}

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

@ -1272,6 +1272,8 @@ class MediaStreamGraph {
*/
TrackRate GraphRate() const { return mSampleRate; }
double AudioOutputLatency();
void RegisterCaptureStreamForWindow(uint64_t aWindowId,
ProcessedMediaStream* aCaptureStream);
void UnregisterCaptureStreamForWindow(uint64_t aWindowId);

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

@ -453,6 +453,8 @@ class MediaStreamGraphImpl : public MediaStreamGraph,
uint32_t AudioOutputChannelCount() const { return mOutputChannels; }
double AudioOutputLatency();
/**
* The audio input channel count for a MediaStreamGraph is the max of all the
* channel counts requested by the listeners. The max channel count is
@ -950,6 +952,12 @@ class MediaStreamGraphImpl : public MediaStreamGraph,
* Read by stable state runnable on main thread. Protected by mMonitor.
*/
GraphTime mNextMainThreadGraphTime = 0;
/**
* Cached audio output latency, in seconds. Main thread only. This is reset
* whenever the audio device running this MediaStreamGraph changes.
*/
double mAudioOutputLatency;
};
} // namespace mozilla

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

@ -523,6 +523,8 @@ AudioListener* AudioContext::Listener() {
return mListener;
}
double AudioContext::OutputLatency() { return Graph()->AudioOutputLatency(); }
Worklet* AudioContext::GetAudioWorklet(ErrorResult& aRv) {
if (!mWorklet) {
mWorklet = AudioWorkletImpl::CreateWorklet(this, aRv);

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

@ -192,6 +192,14 @@ class AudioContext final : public DOMEventTargetHelper,
AudioContextState State() const { return mAudioContextState; }
double BaseLatency() const {
// Gecko does not do any buffering between rendering the audio and sending
// it to the audio subsystem.
return 0.0;
}
double OutputLatency();
Worklet* GetAudioWorklet(ErrorResult& aRv);
bool IsRunning() const;

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

@ -18,8 +18,9 @@ dictionary AudioContextOptions {
Constructor(optional AudioContextOptions contextOptions = {})]
interface AudioContext : BaseAudioContext {
// Bug 1324545: readonly attribute double outputLatency;
// Bug 1324545: AudioTimestamp getOutputTimestamp ();
readonly attribute double baseLatency;
readonly attribute double outputLatency;
[Throws]
Promise<void> suspend();

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

@ -27,7 +27,6 @@ interface BaseAudioContext : EventTarget {
readonly attribute AudioContextState state;
[Throws, SameObject, SecureContext, Pref="dom.audioworklet.enabled"]
readonly attribute AudioWorklet audioWorklet;
// Bug 1324552: readonly attribute double baseLatency;
[Throws]
Promise<void> resume();

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

@ -74,9 +74,6 @@
[AudioListener interface: context.listener must inherit property "forwardY" with the proper type]
expected: FAIL
[AudioContext interface: context must inherit property "baseLatency" with the proper type]
expected: FAIL
[MediaElementAudioSourceNode interface: attribute mediaElement]
expected: FAIL
@ -98,21 +95,12 @@
[AudioParamMap must be primary interface of worklet_node.parameters]
expected: FAIL
[AudioContext interface: context must inherit property "outputLatency" with the proper type]
expected: FAIL
[OfflineAudioContext interface: operation suspend(double)]
expected: FAIL
[AudioContext interface: attribute baseLatency]
expected: FAIL
[AudioParam interface: new AudioBufferSourceNode(context).playbackRate must inherit property "cancelAndHoldAtTime(double)" with the proper type]
expected: FAIL
[AudioContext interface: attribute outputLatency]
expected: FAIL
[AudioListener interface: attribute upX]
expected: FAIL

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

@ -1,16 +1,4 @@
[audiocontextoptions.html]
[X default baseLatency is not greater than 0. Got undefined.]
expected: FAIL
[X balanced baseLatency is not greater than or equal to undefined. Got undefined.]
expected: FAIL
[X playback baseLatency is not greater than or equal to undefined. Got undefined.]
expected: FAIL
[< [test-audiocontextoptions-latencyHint-basic\] 3 out of 9 assertions were failed.]
expected: FAIL
[X double-constructor baseLatency small is not less than or equal to undefined. Got undefined.]
expected: FAIL

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

@ -35,7 +35,7 @@
`context.sampleRate (${context.sampleRate} Hz)`).beGreaterThan(0);
defaultLatency = context.baseLatency;
should(defaultLatency, 'default baseLatency').beGreaterThan(0);
should(defaultLatency, 'default baseLatency').beGreaterThanOrEqualTo(0);
// Verify that an AudioContext can be created with the expected
// latency types.
@ -130,7 +130,7 @@
should(context1.baseLatency, 'high latency context baseLatency')
.beEqualTo(context2.baseLatency);
should(context1.baseLatency, 'high latency context baseLatency')
.beGreaterThan(interactiveLatency);
.beGreaterThanOrEqualTo(interactiveLatency);
closingPromises.push(context1.close());
closingPromises.push(context2.close());