[mirotalksfu] - #33 recording all audioTracks

هذا الالتزام موجود في:
Miroslav Pejic
2023-09-29 20:17:26 +02:00
الأصل 37a0015a38
التزام 73b1a47754
3 ملفات معدلة مع 115 إضافات و17 حذوفات

82
public/js/Helpers.js Normal file
عرض الملف

@@ -0,0 +1,82 @@
'use strict';
class MixedAudioRecorder {
constructor(useGainNode = true) {
this.useGainNode = useGainNode;
this.gainNode = null;
this.audioSources = [];
this.audioSource = null;
this.audioDestination = null;
this.audioContext = this.createAudioContext();
}
createAudioContext() {
if (window.AudioContext) {
return new AudioContext();
} else if (window.webkitAudioContext) {
return new webkitAudioContext();
} else if (window.mozAudioContext) {
return new mozAudioContext();
} else {
throw new Error('Web Audio API is not supported in this browser');
}
}
getMixedAudioStream(audioStreams) {
this.audioSources = [];
if (this.useGainNode) {
this.gainNode = this.audioContext.createGain();
this.gainNode.connect(this.audioContext.destination);
this.gainNode.gain.value = 0;
}
audioStreams.forEach((stream) => {
if (!stream.getTracks().filter((t) => t.kind === 'audio').length) {
return;
}
let audioSource = this.audioContext.createMediaStreamSource(stream);
if (this.useGainNode) {
audioSource.connect(this.gainNode);
}
this.audioSources.push(audioSource);
});
this.audioDestination = this.audioContext.createMediaStreamDestination();
this.audioSources.forEach((source) => {
source.connect(this.audioDestination);
});
return this.audioDestination.stream;
}
stopMixedAudioStream() {
if (this.useGainNode) {
this.gainNode.disconnect();
this.gainNode = null;
}
if (this.audioSources.length) {
this.audioSources.forEach((source) => {
source.disconnect();
});
this.audioSources = [];
}
if (this.audioDestination) {
this.audioDestination.disconnect();
this.audioDestination = null;
}
if (this.audioContext) {
this.audioContext.close();
this.audioContext = null;
}
}
}
// Usage
// const audioRecorder = new MixedAudioRecorder();
// To start recording, call audioRecorder.getMixedAudioStream(audioStreams);
// To stop recording, call audioRecorder.stopMixedAudioStream();
// Credits: https://github.com/muaz-khan/MultiStreamsMixer

عرض الملف

@@ -116,7 +116,9 @@ const _EVENTS = {
hostOnlyRecordingOff: 'hostOnlyRecordingOff', hostOnlyRecordingOff: 'hostOnlyRecordingOff',
}; };
// Recording
let recordedBlobs; let recordedBlobs;
class RoomClient { class RoomClient {
constructor( constructor(
localAudioEl, localAudioEl,
@@ -204,6 +206,8 @@ class RoomClient {
this.fileSharingInput = '*'; this.fileSharingInput = '*';
this.chunkSize = 1024 * 16; // 16kb/s this.chunkSize = 1024 * 16; // 16kb/s
this.audioRecorder = null;
// Encodings // Encodings
this.forceVP8 = false; // Force VP8 codec for webcam and screen sharing this.forceVP8 = false; // Force VP8 codec for webcam and screen sharing
this.forceVP9 = false; // Force VP9 codec for webcam and screen sharing this.forceVP9 = false; // Force VP9 codec for webcam and screen sharing
@@ -373,10 +377,10 @@ class RoomClient {
} catch (error) { } catch (error) {
if (error.name === 'UnsupportedError') { if (error.name === 'UnsupportedError') {
console.error('Browser not supported'); console.error('Browser not supported');
this.userLog('error', 'Browser not supported', 'center'); this.userLog('error', 'Browser not supported', 'center', 6000);
} }
console.error('Browser not supported: ', error); console.error('Browser not supported: ', error);
this.userLog('error', 'Browser not supported: ' + error, 'center'); this.userLog('error', 'Browser not supported: ' + error, 'center', 6000);
} }
await device.load({ await device.load({
routerRtpCapabilities, routerRtpCapabilities,
@@ -2139,14 +2143,14 @@ class RoomClient {
if (err.name === 'SecurityError') if (err.name === 'SecurityError')
errorMessage = `You need to use HTTPS for selecting audio output device: ${err}`; errorMessage = `You need to use HTTPS for selecting audio output device: ${err}`;
console.error('Attach SinkId error: ', errorMessage); console.error('Attach SinkId error: ', errorMessage);
this.userLog('error', errorMessage, 'top-end'); this.userLog('error', errorMessage, 'top-end', 6000);
speakerSelect.selectedIndex = 0; speakerSelect.selectedIndex = 0;
lS.setLocalStorageDevices(lS.MEDIA_TYPE.speaker, 0, speakerSelect.value); lS.setLocalStorageDevices(lS.MEDIA_TYPE.speaker, 0, speakerSelect.value);
}); });
} else { } else {
let error = `Browser seems doesn't support output device selection.`; let error = `Browser seems doesn't support output device selection.`;
console.warn(error); console.warn(error);
this.userLog('error', error, 'top-end'); this.userLog('error', error, 'top-end', 6000);
} }
} }
@@ -3093,7 +3097,7 @@ class RoomClient {
this.userLog('success', 'Message copied!', 'top-end', 1000); this.userLog('success', 'Message copied!', 'top-end', 1000);
}) })
.catch((err) => { .catch((err) => {
this.userLog('error', err, 'top-end', 2000); this.userLog('error', err, 'top-end', 6000);
}); });
} }
@@ -3299,20 +3303,21 @@ class RoomClient {
console.log('MediaRecorder supported options', options); console.log('MediaRecorder supported options', options);
options = { mimeType: options[0] }; options = { mimeType: options[0] };
try { try {
// get all participants audio tracks this.audioRecorder = new MixedAudioRecorder();
const audioTracks = this.getAudioTracksFromAudioElements(); const audioStreams = this.getAudioStreamFromAudioElements();
// const audioMixerStreams = this.audioRecorder.getMixedAudioStream([audioStreams, this.localAudioStream]);
const audioMixerTracks = audioMixerStreams.getTracks();
if (this.isMobileDevice) { if (this.isMobileDevice) {
const videoTracks = this.localVideoStream.getTracks(); const videoTracks = this.localVideoStream.getTracks();
console.log('INIT CAM RECORDING', { console.log('INIT CAM RECORDING', {
localAudioStream: this.localAudioStream, localAudioStream: this.localAudioStream,
localVideoStream: this.localVideoStream, localVideoStream: this.localVideoStream,
videoTracks: videoTracks, videoTracks: videoTracks,
audioTracks: audioTracks, audioMixerTracks: audioMixerTracks,
}); });
// on mobile devices recording camera + all audio tracks // on mobile devices recording camera + all audio tracks
let newStream = new MediaStream([...videoTracks, ...audioTracks]); let newStream = new MediaStream([...videoTracks, ...audioMixerTracks]);
// TODO not recording all audio tracks
console.log('New Cam Media Stream ---> ', newStream.getTracks()); console.log('New Cam Media Stream ---> ', newStream.getTracks());
this.mediaRecorder = new MediaRecorder(newStream, options); this.mediaRecorder = new MediaRecorder(newStream, options);
console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options); console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options);
@@ -3332,10 +3337,9 @@ class RoomClient {
localAudioStream: this.localAudioStream, localAudioStream: this.localAudioStream,
localVideoStream: this.localVideoStream, localVideoStream: this.localVideoStream,
screenTracks: screenTracks, screenTracks: screenTracks,
audioTracks: audioTracks, audioMixerTracks: audioMixerTracks,
}); });
this.recScreenStream = new MediaStream([...screenTracks, ...audioTracks]); this.recScreenStream = new MediaStream([...screenTracks, ...audioMixerTracks]);
// TODO not recording all audio tracks
console.log('New Screen/Window Media Stream ---> ', this.recScreenStream.getTracks()); console.log('New Screen/Window Media Stream ---> ', this.recScreenStream.getTracks());
this.mediaRecorder = new MediaRecorder(this.recScreenStream, options); this.mediaRecorder = new MediaRecorder(this.recScreenStream, options);
console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options); console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options);
@@ -3347,15 +3351,25 @@ class RoomClient {
}) })
.catch((err) => { .catch((err) => {
console.error('Error Unable to recording the screen + audio', err); console.error('Error Unable to recording the screen + audio', err);
this.userLog('error', 'Unable to recording the screen + audio reason: ' + err, 'top-end'); this.userLog('error', 'Unable to recording the screen + audio reason: ' + err, 'top-end', 6000);
}); });
} }
} catch (err) { } catch (err) {
console.error('Exception while creating MediaRecorder: ', err); console.error('Exception while creating MediaRecorder: ', err);
return this.userLog('error', "Can't start stream recording reason: " + err, 'top-end'); return this.userLog('error', "Can't start stream recording reason: " + err, 'top-end', 6000);
} }
} }
hasAudioTrack(mediaStream) {
const audioTracks = mediaStream.getAudioTracks();
return audioTracks.length > 0;
}
hasVideoTrack(mediaStream) {
const videoTracks = mediaStream.getVideoTracks();
return videoTracks.length > 0;
}
getAudioTracksFromAudioElements() { getAudioTracksFromAudioElements() {
const audioElements = document.querySelectorAll('audio'); const audioElements = document.querySelectorAll('audio');
const audioTracks = []; const audioTracks = [];
@@ -3482,6 +3496,7 @@ class RoomClient {
} }
if (this.isMobileDevice) this.getId('swapCameraButton').className = ''; if (this.isMobileDevice) this.getId('swapCameraButton').className = '';
this.event(_EVENTS.stopRec); this.event(_EVENTS.stopRec);
this.audioRecorder.stopMixedAudioStream();
this.recordingAction('Stop recording'); this.recordingAction('Stop recording');
this.sound('recStop'); this.sound('recStop');
} }
@@ -3898,7 +3913,7 @@ class RoomClient {
this.socket.emit('shareVideoAction', data); this.socket.emit('shareVideoAction', data);
this.openVideo(data); this.openVideo(data);
} else { } else {
this.userLog('error', 'Not valid video URL', 'top-end'); this.userLog('error', 'Not valid video URL', 'top-end', 6000);
} }
} }
}); });

عرض الملف

@@ -76,6 +76,7 @@
<script defer src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script> <script defer src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script>
<script defer src="../js/LocalStorage.js"></script> <script defer src="../js/LocalStorage.js"></script>
<script defer src="../js/Rules.js"></script> <script defer src="../js/Rules.js"></script>
<script defer src="../js/Helpers.js"></script>
<script defer src="../js/Room.js"></script> <script defer src="../js/Room.js"></script>
<script defer src="../js/RoomClient.js"></script> <script defer src="../js/RoomClient.js"></script>
<script defer src="../js/SpeechRec.js"></script> <script defer src="../js/SpeechRec.js"></script>