Closed guest271314 closed 2 years ago
When {sampleRate: 22050}
is set at AudioContext
constructor the result can be inconsistent with regard to MediaStreamTrack.getSettings()
.
Without setting a sampleRate
Assertion failed: [2]
MediaStreamTrack constraints not applied: OverconstrainedError Cannot satisfy constraints
MediaStreamTrack contraints: {}
MediaStreamTrack.channelCount before starting OscillatorNode: 2
MediaStreamTrack.channelCount after starting OscillatorNode: 2
Assertion failed: [
1,
1,
1
]
compare first run with {sampleRate: 22050}
Assertion failed: [2]
MediaStreamTrack constraints not applied: OverconstrainedError Cannot satisfy constraints
MediaStreamTrack contraints: {}
MediaStreamTrack.channelCount before starting OscillatorNode: 2
MediaStreamTrack.channelCount after starting OscillatorNode: 1
Assertion failed: [
1,
1,
1
]
run again at console
with {sampleRate: 22050}
without reloading the document
Assertion failed: [2]
MediaStreamTrack constraints not applied: OverconstrainedError Cannot satisfy constraints
MediaStreamTrack contraints: {}
MediaStreamTrack.channelCount before starting OscillatorNode: 2
MediaStreamTrack.channelCount after starting OscillatorNode: 2
Assertion failed: [
1,
1,
1
]
Part of the issue is incorrect and inconsistent return value of MediaStreamTrack.getSettings()
(Firefox returns empty objects for getSettings()
), which when MediaStreamAudioDestinationNode
and OscillatorNode
which connects to the former are both constructed with {channelCount: 1}
option should result in MediaStramTrack.getSettings().channelCount1
not ever being 2
, however, in practice that is not the case (if an application is relying on the initial value of getSettings().channelCount
to be correct and consistent).
<!DOCTYPE html>
<html>
<head>
<title>
MediaStreamTrack.getSettings() returns incorrect, inconsistent
channelCount
</title>
</head>
<body>
<h1>Click several times, observe output at console</h1>
<script>
document.querySelector('h1').onclick = async () => {
const ac = new AudioContext();
const msd = new MediaStreamAudioDestinationNode(ac, {
channelCount: 1,
channelCountMode: 'explicit',
channelInterpretation: 'discrete',
});
const osc = new OscillatorNode(ac, {
channelCount: 1,
channelCountMode: 'explicit',
channelInterpretation: 'discrete',
});
osc.connect(msd);
osc.start(ac.currentTime);
console.log(
'console.log',
osc.channelCount,
msd.stream.getAudioTracks()[0].getSettings().channelCount
); // 1 2, should be 1 1
setTimeout(
() =>
console.log(
'console.log in setTimeout()',
osc.channelCount,
msd.stream.getAudioTracks()[0].getSettings().channelCount
),
0
); // could be 1 1 or 1 2
await scheduler.postTask(() =>
console.log(
'console.log in scheduler.postTask()',
osc.channelCount,
msd.stream.getAudioTracks()[0].getSettings().channelCount
)
); // could be 1 1 or 1 2
await ac.close();
};
</script>
</body>
</html>
Chromium bug https://bugs.chromium.org/p/chromium/issues/detail?id=1193248
The specification for how channel count and mode and interpretation work is unclear. I don't actually know what the expectations are.
The plain language channelCount
strongly implies that the user can directly set channel count for the underlying MediaStreamTrack
. 'explicit'
cannot means anything other than that, relevant to the channelCount
(I am not sure what else 'explicit'
could be refering to other than channelCount
)
determines how channels will be counted when up-mixing and down-mixing connections to any inputs to the node
here we are "down-mixing" the evidently default 2 channel to 1 channel, explicitly.
is the exact value as specified by the channelCount
Re channelInterpretation
Up-mix by filling channels until they run out then zero out remaining channels. Down-mix by filling as many channels as possible, then dropping remaining channels.
by setting 'discrete'
I am expecting that if, by some undisclosed, or known internal algorithm, if the MediaStreamTrack
still has 2 channels after the above that all except for 1
channel, the API runs the "dropping remaining channels" part of the algortithm(s).
WG Discussion:
Good catch, there lots of unknowns in the area:
MediaStream{,Track}
sHTMLMediaElement
with a number of channels in the media file that changes, when used with MediaElementAudioSourceNode
.
- Is changing the channelCount of a MediaStreamTrack that is not a microphone intended? What should be the result? This is in the MediaCapture and Streams: https://w3c.github.io/mediacapture-main/. For a mic, it's useful to force mono for stereo mic (or the opposite), for example, but for others, I'm not sure. The Web Audio API itself can downmix or upmix and that's well defined.
Yes. It does not matter what the underlying source of the track is.
With regards to actual audio input or API initially used to get the track. For example, when we pass mediaStream
from getUserMedia()
to MediaStreamAudioSourceNode
we must be able to set constraints on the track to whatever we want.
If the Web Audio API can down-mix then that is what must consistently occur when we set channelCount
to 1
at an audio node constructor, or when using applyConstraints({channelCount: 1})
, however that throws OverConstrained
error at both Chromium and Firefox.
- When this changes, should we change the channel count of our objects that wrap those
MediaStream{,Track}
s
Yes.
- Same issue comes from
HTMLMediaElement
with a number of channels in the media file that changes, when used withMediaElementAudioSourceNode
.
Yes. Change the implementation MUST change the channel count internally, and those changes MUST be observable at getSettings()
and getConstraints()
and other methods that return settings, and constraints of the MediaStreamTrack
, else all of those options and constraint setting and gettings methods are, as evident, inconsistent and incorrect, and useless.
AudioWG virtual F2F:
@padenot See https://github.com/w3c/mediacapture-main/issues/775.
TPAC 2022:
So the problem is that the result of query on MediaStreamTrack.getSettings().channelCount
value does not match the channel count of MediaStreamAudioDestinationNode.
This should be handled by the construction of MediaStreamTrack
:
https://w3c.github.io/mediacapture-main/#dfn-create-a-mediastreamtrack
If you see problems in getting a correct value, please consider file an issue against browsers.
Describe the issue
Setting
channelCount
to1
atMediaStreamAudioDestinatioNode
constructor has no effect, or is not being recognized atMediaStreamTrack.getSettings().channelCount
value - untilMediaStreamTrackProcessor.readable.read()
is called where input is an audio node with 1 channel.applyConstraints({channelCount: 1})
throws, however thechannelCount
is dynamically changed at some point to1
.Firefox returns empty JavaScript plain objects for
getSettings()
andgetConstraints()
.Where Is It
MediaStreamAudioDestinationNode
MediaStreamAudioSourceNode
How can this be true when
applyConstraints()
is used?This is not true in the following code where the
channelCount
is expected to be set to1
, is not set to1
byMediaStreamAudioDestinationNode
constructoroptions
and reading a frame fromMediaStreamTrackProcessor.readable.read()
channelCount
MediaStreamAudioDestinationNode
is not listed as having known restrictions on thechannelCount
value being set and returned.For the above reasons changes to the specification
channelCount
set atMediaStreamAudioDestinationNode
constructor MUST setMediaStreamTrack
channelCount
constraintchannelCount
set atMediaStreamAudioDestinationNode
constructor MUST be reflected atMediaStreamAudioDestinationNode.MediaStreamTrack.getSettings()
MediaStreamAudioDestinationNode.MediaStreamTrack.applyConstraints({channelCount: channelCount})
MUST apply the constraint, once the constraint is applied andPromise
fulfilled be reflected atMediaStreamAudioDestinationNode.MediaStreamTrack.getConstraints()
The above changes will ensure that the
channelCount
set on aMediaStreamAudioDestinationNode
by user is set by implementations on theMediaStreamTrack
, observable via the appropriate methods that provide statistics and at the actualMediaStreamTrack
channel count settings.Additional Information