diff --git a/examples/simpleclient/src/index.html b/examples/simpleclient/src/index.html
index 50a793781..214913a42 100644
--- a/examples/simpleclient/src/index.html
+++ b/examples/simpleclient/src/index.html
@@ -8,6 +8,9 @@
|
+ |
+ |
+ |
My endpoint id: |
diff --git a/examples/simpleclient/src/main.ts b/examples/simpleclient/src/main.ts
index 4d1f5e132..1626c646c 100644
--- a/examples/simpleclient/src/main.ts
+++ b/examples/simpleclient/src/main.ts
@@ -1,6 +1,9 @@
import { addSimulcastSdpLocalDescription } from './simulcast';
const joinButton: HTMLButtonElement = document.getElementById('join');
+const startRecordingButton: HTMLButtonElement = document.getElementById('start-recording');
+const stopRecordingButton: HTMLButtonElement = document.getElementById('stop-recording');
+const downloadRecordingButton: HTMLButtonElement = document.getElementById('download-recording');
const endpointIdLabel: HTMLLabelElement = document.getElementById('endpointId');
const dominantSpeakerLabel: HTMLLabelElement = document.getElementById('dominantSpeaker');
const audioElementsDiv: HTMLDivElement = document.getElementById('audioElements');
@@ -9,7 +12,10 @@ let peerConnection: RTCPeerConnection|undefined = undefined
let localMediaStream: MediaStream|undefined = undefined;
let localDataChannel: RTCDataChannel|undefined = undefined;
let endpointId: string|undefined = undefined;
-let remoteMediaStreams: Set = new Set();
+let mixedStreamDestinationNode : MediaStreamAudioDestinationNode|undefined = undefined;
+let remoteMediaStreams: Map = new Map();
+let recordedBlobs : Blob[] = [];
+let mediaRecorder : MediaRecorder|undefined = undefined;
const serverUrl = 'https://localhost:8081/conferences/';
@@ -84,6 +90,7 @@ async function onIceGatheringStateChange(event: Event) {
const result = await fetch(request);
console.log('Answer result ' + result.status);
+ startRecordingButton.style.display = "block";
}
}
}
@@ -94,7 +101,7 @@ function onTrack(event: RTCTrackEvent) {
continue;
}
- remoteMediaStreams.add(stream.id);
+ remoteMediaStreams.set(stream.id, stream);
console.log(`Added remote stream ${stream.id} audio ${stream.getAudioTracks().length} video ${stream.getVideoTracks().length}`);
if (stream.getAudioTracks().length !== 0) {
@@ -140,6 +147,90 @@ function onDataChannelMessage(event: MessageEvent) {
}
}
+function startRecording() {
+ if (mixedStreamDestinationNode !== undefined) {
+ console.log("Recording is already active");
+ return;
+ }
+
+ const mimeType = "audio/webm;codecs=opus";
+ if (!MediaRecorder.isTypeSupported(mimeType)) {
+ console.error(`MimeType is not supported: ${mimeType}`);
+ throw new Error(`MimeType is not supported: ${mimeType}`);
+ }
+
+ const recordingAudioContext = new AudioContext();
+ mixedStreamDestinationNode = recordingAudioContext.createMediaStreamDestination();
+ //for (const receiver of peerConnection.getReceivers()) {
+ // if (receiver.track && receiver.track.kind === "audio") {
+ // console.log(`Adding remote stream ${receiver.track.id} to the recording`)
+ // const s = recordingAudioContext.createMediaStreamSource(new MediaStream([receiver.track]));
+ // s.connect(mixedStreamDestinationNode);
+ // }
+ //}
+ //console.log(`Number of remote streams ${remoteMediaStreams.size}`);
+
+ //const sources = audioTracks.map(t => ac.createMediaStreamSource(new MediaStream([t])));
+
+ for (const remoteStream of remoteMediaStreams) {
+ if (remoteStream[1].getAudioTracks().length !== 0) {
+ console.log(`Adding remote stream ${remoteStream[0]} to the recording`)
+ const audioStream = recordingAudioContext.createMediaStreamSource(remoteStream[1]);
+ audioStream.connect(mixedStreamDestinationNode);
+ }
+ }
+
+ const localSource = recordingAudioContext.createMediaStreamSource(localMediaStream);
+ localSource.connect(mixedStreamDestinationNode);
+
+ const options = {mimeType};
+ mediaRecorder= new MediaRecorder(mixedStreamDestinationNode.stream, options);
+ mediaRecorder.ondataavailable = handleRecordingData;
+ mediaRecorder.onstop = (event) => {
+ console.log('Recorder stopped: ', event);
+ console.log('Recorded Blobs: ', recordedBlobs);
+ };
+ mediaRecorder.start();
+ console.log('MediaRecorder started', mediaRecorder);
+
+ startRecordingButton.style.display = "none";
+ stopRecordingButton.style.display = "block";
+ downloadRecordingButton.style.display = "none";
+}
+
+function stopRecording() {
+ if (mediaRecorder) {
+ mediaRecorder.stop();
+ startRecordingButton.style.display = "block";
+ stopRecordingButton.style.display = "none";
+ downloadRecordingButton.style.display = "block";
+ mediaRecorder = undefined;
+ }
+ }
+
+function handleRecordingData(event: BlobEvent) {
+ console.log('handleDataAvailable', event);
+ if (event.data && event.data.size > 0) {
+ console.log("Pushed new data to recorder");
+ recordedBlobs.push(event.data);
+ }
+}
+
+function downloadRecorder() {
+ const blob = new Blob(recordedBlobs, {type: 'audio/webm'});
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.style.display = 'none';
+ a.href = url;
+ a.download = 'test.webm';
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(() => {
+ document.body.removeChild(a);
+ window.URL.revokeObjectURL(url);
+ }, 100);
+}
+
async function joinClicked() {
console.log('Join clicked');
@@ -157,7 +248,7 @@ async function joinClicked() {
localDataChannel = peerConnection.createDataChannel("webrtc-datachannel", { ordered: true });
localDataChannel.onmessage = onDataChannelMessage;
-
+
const url = serverUrl + 'endpoints/';
const body = {};
@@ -180,6 +271,13 @@ async function joinClicked() {
function main() {
joinButton.onclick = joinClicked;
+ startRecordingButton.onclick = startRecording;
+ stopRecordingButton.onclick = stopRecording;
+ downloadRecordingButton.onclick = downloadRecorder;
+
+ startRecordingButton.style.display = "none";
+ stopRecordingButton.style.display = "none";
+ downloadRecordingButton.style.display = "none";
}
main();