| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <title>RTCRtpEncodingParameters codec opus stereo</title> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script> | |
| 'use strict'; | |
| const kNumChannelsMono = 1; | |
| const kNumChannelsStereo = 2; | |
| const kAudioProcessingEnabled = true; | |
| const kAudioProcessingDisabled = false; | |
| // In some implementations (e.g. Chrome), whether or not audio processing is | |
| // used may affect the number of channels we get. | |
| // To isolate this factor from the test, we constrain both channels and APM. | |
| function getAudioConstraints(numChannels, audioProcessing) { | |
| return { | |
| channelCount: {exact: numChannels}, | |
| autoGainControl: audioProcessing, | |
| echoCancellation: audioProcessing, | |
| noiseSuppression: audioProcessing | |
| }; | |
| } | |
| // Polls getSettings() until `channelCount` is known. | |
| // Remote tracks don't populate their `channelCount` until media is received. | |
| async function pollChannelCount(t, track) { | |
| while (true) { | |
| if (track.getSettings().channelCount != undefined) { | |
| break; | |
| } | |
| await new Promise(r => t.step_timeout(r, 50)); | |
| } | |
| return track.getSettings().channelCount; | |
| } | |
| promise_test(async t => { | |
| const pc1 = new RTCPeerConnection(); | |
| t.add_cleanup(() => pc1.close()); | |
| const pc2 = new RTCPeerConnection(); | |
| t.add_cleanup(() => pc2.close()); | |
| pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate); | |
| pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate); | |
| // Open microphone as mono. | |
| const stream = await navigator.mediaDevices.getUserMedia( | |
| {audio: getAudioConstraints(kNumChannelsMono, kAudioProcessingEnabled)}); | |
| const [track] = stream.getTracks(); | |
| t.add_cleanup(() => track.stop()); | |
| // Prerequisite of the test. | |
| assert_equals(track.getSettings().channelCount, 1, | |
| 'Can open track in mono'); | |
| // Negotiate opus. | |
| const transceiver = pc1.addTransceiver(track); | |
| transceiver.setCodecPreferences( | |
| RTCRtpSender.getCapabilities('audio').codecs.filter( | |
| codec => codec.mimeType == 'audio/opus')); | |
| await pc1.setLocalDescription(); | |
| await pc2.setRemoteDescription(pc1.localDescription); | |
| await pc2.setLocalDescription(); | |
| await pc1.setRemoteDescription(pc2.localDescription); | |
| const [receiver] = pc2.getReceivers(); | |
| const remoteTrack = receiver.track; | |
| // Attaching the track to an audio element is needed for media to flow, | |
| // otherwise the `channelCount` is never known. | |
| const audio = document.createElement('audio'); | |
| audio.srcObject = new MediaStream(); | |
| audio.srcObject.addTrack(remoteTrack); | |
| // The stereo opus decoder outputs stereo regardless of the signal on the | |
| // wire. | |
| assert_equals(await pollChannelCount(t, remoteTrack), 2, | |
| 'Remote track is stereo (despite local track being mono)'); | |
| }, 'Sending and receiving a mono track with opus (stereo decoder upmix)'); | |
| promise_test(async t => { | |
| const pc1 = new RTCPeerConnection(); | |
| t.add_cleanup(() => pc1.close()); | |
| const pc2 = new RTCPeerConnection(); | |
| t.add_cleanup(() => pc2.close()); | |
| pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate); | |
| pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate); | |
| // Open microphone as stereo. | |
| const stream = await navigator.mediaDevices.getUserMedia( | |
| {audio: getAudioConstraints(kNumChannelsStereo, kAudioProcessingDisabled)}); | |
| const [track] = stream.getTracks(); | |
| t.add_cleanup(() => track.stop()); | |
| // Prerequisite of the test. | |
| assert_equals(track.getSettings().channelCount, 2, | |
| 'Can open track in stereo'); | |
| // Negotiate opus. | |
| const transceiver = pc1.addTransceiver(track); | |
| transceiver.setCodecPreferences( | |
| RTCRtpSender.getCapabilities('audio').codecs.filter( | |
| codec => codec.mimeType == 'audio/opus')); | |
| await pc1.setLocalDescription(); | |
| await pc2.setRemoteDescription(pc1.localDescription); | |
| await pc2.setLocalDescription(); | |
| await pc1.setRemoteDescription(pc2.localDescription); | |
| const [receiver] = pc2.getReceivers(); | |
| const remoteTrack = receiver.track; | |
| // Attaching the track to an audio element is needed for media to flow, | |
| // otherwise the `channelCount` is never known. | |
| const audio = document.createElement('audio'); | |
| audio.srcObject = new MediaStream(); | |
| audio.srcObject.addTrack(remoteTrack); | |
| assert_equals(await pollChannelCount(t, remoteTrack), 2, | |
| 'Remote track is also stereo'); | |
| }, 'Sending and receiving a stereo track with opus'); | |
| </script> |