Skip to content

Commit b10e0c8

Browse files
authored
Merge branch 'gh-pages' into video-replay
2 parents 501f971 + 555d697 commit b10e0c8

File tree

5 files changed

+114
-93
lines changed

5 files changed

+114
-93
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ module.exports = {
3535
// From Streams specification
3636
"TransformStream": true,
3737
// From WebCodec specification
38+
"AudioData": true,
39+
"AudioEncoder": true,
40+
"AudioDecoder": true,
3841
"VideoFrame": true,
3942
"VideoEncoder": true,
4043
"VideoDecoder": true,

src/content/insertable-streams/audio-processing/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC
4848
<button type="button" id="stopButton" disabled>Stop</button>
4949
<div>
5050
<p class="warning">Warning: if you're not using headphones, pressing Start will cause feedback.</p>
51+
<div id="errorMsg" class="warning"></div>
5152

5253
<p>View the console to see logging. The <code>audio</code>, <code>processor</code>,
5354
<code>generator</code>, <code>transformer</code>, <code>stream</code> and

src/content/insertable-streams/audio-processing/js/main.js

Lines changed: 21 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,14 @@ let stopButton;
4444
// Transformation chain elements
4545
let processor;
4646
let generator;
47-
let transformer;
4847

4948
// Stream from getUserMedia
5049
let stream;
5150
// Output from the transform
5251
let processedStream;
5352

54-
// Adjust this value to increase/decrease the amount of filtering.
55-
// eslint-disable-next-line prefer-const
56-
let cutoff = 100;
57-
58-
// An AbortController used to stop the transform.
59-
let abortController;
53+
// Worker for processing
54+
let worker;
6055

6156
// Initialize on page load.
6257
async function init() {
@@ -73,85 +68,35 @@ const constraints = window.constraints = {
7368
video: false
7469
};
7570

76-
// Returns a low-pass transform function for use with TransformStream.
77-
function lowPassFilter() {
78-
const format = 'f32-planar';
79-
let lastValuePerChannel = undefined;
80-
return (data, controller) => {
81-
const rc = 1.0 / (cutoff * 2 * Math.PI);
82-
const dt = 1.0 / data.sampleRate;
83-
const alpha = dt / (rc + dt);
84-
const nChannels = data.numberOfChannels;
85-
if (!lastValuePerChannel) {
86-
console.log(`Audio stream has ${nChannels} channels.`);
87-
lastValuePerChannel = Array(nChannels).fill(0);
88-
}
89-
const buffer = new Float32Array(data.numberOfFrames * nChannels);
90-
for (let c = 0; c < nChannels; c++) {
91-
const offset = data.numberOfFrames * c;
92-
const samples = buffer.subarray(offset, offset + data.numberOfFrames);
93-
data.copyTo(samples, {planeIndex: c, format});
94-
let lastValue = lastValuePerChannel[c];
95-
96-
// Apply low-pass filter to samples.
97-
for (let i = 0; i < samples.length; ++i) {
98-
lastValue = lastValue + alpha * (samples[i] - lastValue);
99-
samples[i] = lastValue;
100-
}
101-
102-
lastValuePerChannel[c] = lastValue;
103-
}
104-
controller.enqueue(new AudioData({
105-
format,
106-
sampleRate: data.sampleRate,
107-
numberOfFrames: data.numberOfFrames,
108-
numberOfChannels: nChannels,
109-
timestamp: data.timestamp,
110-
data: buffer
111-
}));
112-
};
113-
}
114-
11571
async function start() {
11672
startButton.disabled = true;
11773
try {
11874
stream = await navigator.mediaDevices.getUserMedia(constraints);
75+
const audioTracks = stream.getAudioTracks();
76+
console.log('Using audio device: ' + audioTracks[0].label);
77+
stream.oninactive = () => {
78+
console.log('Stream ended');
79+
};
80+
81+
processor = new MediaStreamTrackProcessor(audioTracks[0]);
82+
generator = new MediaStreamTrackGenerator('audio');
83+
const source = processor.readable;
84+
const sink = generator.writable;
85+
worker = new Worker('js/worker.js');
86+
worker.postMessage({source: source, sink: sink}, [source, sink]);
87+
88+
processedStream = new MediaStream();
89+
processedStream.addTrack(generator);
90+
audio.srcObject = processedStream;
91+
stopButton.disabled = false;
92+
await audio.play();
11993
} catch (error) {
12094
const errorMessage =
12195
'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' +
12296
error.name;
12397
document.getElementById('errorMsg').innerText = errorMessage;
12498
console.log(errorMessage);
12599
}
126-
const audioTracks = stream.getAudioTracks();
127-
console.log('Using audio device: ' + audioTracks[0].label);
128-
stream.oninactive = () => {
129-
console.log('Stream ended');
130-
};
131-
132-
processor = new MediaStreamTrackProcessor(audioTracks[0]);
133-
generator = new MediaStreamTrackGenerator('audio');
134-
const source = processor.readable;
135-
const sink = generator.writable;
136-
transformer = new TransformStream({transform: lowPassFilter()});
137-
abortController = new AbortController();
138-
const signal = abortController.signal;
139-
const promise = source.pipeThrough(transformer, {signal}).pipeTo(sink);
140-
promise.catch((e) => {
141-
if (signal.aborted) {
142-
console.log('Shutting down streams after abort.');
143-
} else {
144-
console.error('Error from stream transform:', e);
145-
}
146-
source.cancel(e);
147-
sink.abort(e);
148-
});
149-
150-
processedStream = new MediaStream();
151-
processedStream.addTrack(generator);
152-
audio.srcObject = processedStream;
153-
stopButton.disabled = false;
154-
await audio.play();
155100
}
156101

157102
async function stop() {
@@ -161,8 +106,7 @@ async function stop() {
161106
stream.getTracks().forEach(track => {
162107
track.stop();
163108
});
164-
abortController.abort();
165-
abortController = null;
109+
worker.postMessage({command: 'abort'});
166110
startButton.disabled = false;
167111
}
168112

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a BSD-style license
5+
* that can be found in the LICENSE file in the root of the source
6+
* tree.
7+
*/
8+
9+
// Adjust this value to increase/decrease the amount of filtering.
10+
// eslint-disable-next-line prefer-const
11+
let cutoff = 100;
12+
13+
// Returns a low-pass transform function for use with TransformStream.
14+
function lowPassFilter() {
15+
const format = 'f32-planar';
16+
let lastValuePerChannel = undefined;
17+
return (data, controller) => {
18+
const rc = 1.0 / (cutoff * 2 * Math.PI);
19+
const dt = 1.0 / data.sampleRate;
20+
const alpha = dt / (rc + dt);
21+
const nChannels = data.numberOfChannels;
22+
if (!lastValuePerChannel) {
23+
console.log(`Audio stream has ${nChannels} channels.`);
24+
lastValuePerChannel = Array(nChannels).fill(0);
25+
}
26+
const buffer = new Float32Array(data.numberOfFrames * nChannels);
27+
for (let c = 0; c < nChannels; c++) {
28+
const offset = data.numberOfFrames * c;
29+
const samples = buffer.subarray(offset, offset + data.numberOfFrames);
30+
data.copyTo(samples, {planeIndex: c, format});
31+
let lastValue = lastValuePerChannel[c];
32+
33+
// Apply low-pass filter to samples.
34+
for (let i = 0; i < samples.length; ++i) {
35+
lastValue = lastValue + alpha * (samples[i] - lastValue);
36+
samples[i] = lastValue;
37+
}
38+
39+
lastValuePerChannel[c] = lastValue;
40+
}
41+
controller.enqueue(new AudioData({
42+
format,
43+
sampleRate: data.sampleRate,
44+
numberOfFrames: data.numberOfFrames,
45+
numberOfChannels: nChannels,
46+
timestamp: data.timestamp,
47+
data: buffer
48+
}));
49+
};
50+
}
51+
52+
let abortController;
53+
54+
onmessage = async (event) => {
55+
if (event.data.command == 'abort') {
56+
abortController.abort();
57+
abortController = null;
58+
} else {
59+
const source = event.data.source;
60+
const sink = event.data.sink;
61+
const transformer = new TransformStream({transform: lowPassFilter()});
62+
abortController = new AbortController();
63+
const signal = abortController.signal;
64+
const promise = source.pipeThrough(transformer, {signal}).pipeTo(sink);
65+
promise.catch((e) => {
66+
if (signal.aborted) {
67+
console.log('Shutting down streams after abort.');
68+
} else {
69+
console.error('Error from stream transform:', e);
70+
}
71+
source.cancel(e);
72+
sink.abort(e);
73+
});
74+
}
75+
};

src/content/peerconnection/bandwidth/js/main.js

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,14 @@ function onSetSessionDescriptionError(error) {
200200
bandwidthSelector.onchange = () => {
201201
bandwidthSelector.disabled = true;
202202
const bandwidth = bandwidthSelector.options[bandwidthSelector.selectedIndex].value;
203+
setBandwidth(bandwidth)
204+
.then(() => {
205+
bandwidthSelector.disabled = false;
206+
});
207+
};
203208

204-
// In Chrome, use RTCRtpSender.setParameters to change bandwidth without
209+
function setBandwidth(bandwidthInKbps) {
210+
// In modern browsers, use RTCRtpSender.setParameters to change bandwidth without
205211
// (local) renegotiation. Note that this will be within the envelope of
206212
// the initial maximum bandwidth negotiated via SDP.
207213
if ((adapter.browserDetails.browser === 'chrome' ||
@@ -215,36 +221,28 @@ bandwidthSelector.onchange = () => {
215221
if (!parameters.encodings) {
216222
parameters.encodings = [{}];
217223
}
218-
if (bandwidth === 'unlimited') {
224+
if (bandwidthInKbps === 'unlimited') {
219225
delete parameters.encodings[0].maxBitrate;
220226
} else {
221-
parameters.encodings[0].maxBitrate = bandwidth * 1000;
227+
parameters.encodings[0].maxBitrate = bandwidthInKbps * 1000;
222228
}
223-
sender.setParameters(parameters)
224-
.then(() => {
225-
bandwidthSelector.disabled = false;
226-
})
227-
.catch(e => console.error(e));
228-
return;
229+
return sender.setParameters(parameters);
229230
}
230-
// Fallback to the SDP munging with local renegotiation way of limiting
231+
// Fallback to the SDP changes with local renegotiation as way of limiting
231232
// the bandwidth.
232-
pc1.createOffer()
233+
return pc1.createOffer()
233234
.then(offer => pc1.setLocalDescription(offer))
234235
.then(() => {
235236
const desc = {
236237
type: pc1.remoteDescription.type,
237-
sdp: bandwidth === 'unlimited' ?
238+
sdp: bandwidthInKbps === 'unlimited' ?
238239
removeBandwidthRestriction(pc1.remoteDescription.sdp) :
239-
updateBandwidthRestriction(pc1.remoteDescription.sdp, bandwidth)
240+
updateBandwidthRestriction(pc1.remoteDescription.sdp, bandwidthInKbps)
240241
};
241242
console.log('Applying bandwidth restriction to setRemoteDescription:\n' +
242243
desc.sdp);
243244
return pc1.setRemoteDescription(desc);
244245
})
245-
.then(() => {
246-
bandwidthSelector.disabled = false;
247-
})
248246
.catch(onSetSessionDescriptionError);
249247
};
250248

0 commit comments

Comments
 (0)