зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2a3e21430e
Коммит
8c692f2f23
|
@ -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());
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче