Created
September 4, 2020 03:20
-
-
Save kwindla/1e8bd83e0a964a21d8913da178df54d4 to your computer and use it in GitHub Desktop.
Daily.co video call API local audio track recorder example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>record local audio</title> | |
<script src="./daily-js-pluot-core/dist/daily-iframe.js"></script> | |
</head> | |
<body onload="join()"> | |
<div id="buttons" style="width: 50%; float: left"> | |
</div> | |
<div id="videos" style="width: 50%; float: right"> | |
</div> | |
<script> | |
const ROOM_URL = 'https://DAILY-CO-TEAM-NAME.daily.co/ROOM_NAME'; | |
const AUDIO_CHUNK_SIZE_IN_SECONDS = 2; | |
async function join() { | |
try { | |
window.callObject = DailyIframe.createCallObject({ url: ROOM_URL }); | |
// for recording of local audio | |
window.callObject.on("track-started", localAudioTrackStarted); | |
window.callObject.on("track-stopped", localAudioTrackStopped); | |
// for normal display/playback of video audio tracks in UI | |
window.callObject.on("track-started", trackStarted); | |
window.callObject.on("track-stopped", trackStopped); | |
window.callObject.on("left-meeting", leftMeeting); | |
await callObject.join(); | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
async function localAudioTrackStarted(e) { | |
if (!(e && | |
e.participant && | |
e.participant.local && | |
e.track && | |
e.track.kind === 'audio')) { | |
return; | |
} | |
console.log('starting local audio recorder', e); | |
window.audioRecorder = new ExampleRecorder({ track: e.track }); | |
} | |
async function localAudioTrackStopped(e) { | |
if (!(e && | |
e.participant && | |
e.participant.local && | |
e.track && | |
e.track.kind === 'audio')) { | |
return; | |
} | |
window.audioRecorder.stopRecording(); | |
window.audioRecorder = null; | |
} | |
class ExampleRecorder { | |
constructor({ track }) { | |
this.startRecording(track); | |
} | |
async startRecording(track) { | |
if (!track || track.kind !== 'audio') { | |
console.log('need an audio track in order to record'); | |
return; | |
} | |
try { | |
this.mediaRecorder = new MediaRecorder( | |
new MediaStream([ track ]), | |
{ | |
mimeType: "audio/webm; codecs=opus", | |
audioBitsPerSecond: 60 * 1000, | |
} | |
); | |
this.mediaRecorder.ondataavailable = this.onData.bind(this); | |
this.mediaRecorder.onstop = this.onStop.bind(this); | |
this.mediaRecorder.onerror = this.errorHandler.bind(this); | |
this.mediaRecorder.start(AUDIO_CHUNK_SIZE_IN_SECONDS * 1000); | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
async stopRecording() { | |
this.mediaRecorder.stop(); | |
} | |
async onData(blobEvent) { | |
let data = await blobEvent.data.arrayBuffer(); | |
console.log('new data to handle ... DO SOMETHING WITH THE DATA HERE', | |
data); | |
} | |
async onStop() { | |
console.log('recorder stopped ... clean up if necessary'); | |
} | |
errorHandler(e) { | |
console.error('recorder error', e); | |
} | |
} | |
// | |
// ---- general utility functions to display video and playback audio ---- | |
// | |
function trackStarted(e) { | |
let vidsContainer = document.getElementById("videos"); | |
if (e.track && e.track.kind === "video") { | |
let isScreenTrack = e.track === e.participant.screenVideoTrack; | |
let vid = findVideoForParticipant(e.participant.session_id, isScreenTrack); | |
if (!vid) { | |
vid = document.createElement("video"); | |
vid.session_id = e.participant.session_id; | |
vid.is_screen_track = isScreenTrack; | |
vid.style.width = "100%"; | |
vid.autoplay = true; | |
vid.muted = true; | |
vid.playsInline = true; | |
vidsContainer.appendChild(vid); | |
} | |
vid.srcObject = new MediaStream([e.track]); | |
} else if (e.track && e.track.kind === "audio") { | |
let aud = findAudioForParticipant(e.participant.session_id); | |
if (!aud) { | |
aud = document.createElement("audio"); | |
aud.session_id = e.participant.session_id; | |
if (e.participant && e.participant.local) { | |
console.log("local audio track ... not playing locally"); | |
aud.muted = true; | |
} else { | |
aud.autoplay = true; | |
} | |
vidsContainer.appendChild(aud); | |
} | |
aud.srcObject = new MediaStream([e.track]); | |
} | |
} | |
function trackStopped(e) { | |
let el = | |
findVideoForTrack(e.track && e.track.id) || | |
findAudioForTrack(e.track && e.track.id); | |
if (el) { | |
el.remove(); | |
} | |
} | |
function findVideoForParticipant(session_id, isScreenTrack) { | |
for (const vid of document.getElementsByTagName("video")) { | |
if ( | |
vid.session_id === session_id && | |
vid.is_screen_track === isScreenTrack | |
) { | |
return vid; | |
} | |
} | |
} | |
function findVideoForTrack(trackId) { | |
for (const vid of document.getElementsByTagName("video")) { | |
if ( | |
vid.srcObject && | |
vid.srcObject.getTracks().find((t) => t.id === trackId) | |
) { | |
return vid; | |
} | |
} | |
} | |
function findAudioForParticipant(session_id) { | |
for (const aud of document.getElementsByTagName("audio")) { | |
if (aud.session_id === session_id) { | |
return aud; | |
} | |
} | |
} | |
function findAudioForTrack(trackId) { | |
for (const aud of document.getElementsByTagName("audio")) { | |
if ( | |
aud.srcObject && | |
aud.srcObject.getTracks().find((t) => t.id === trackId) | |
) { | |
return aud; | |
} | |
} | |
} | |
function leftMeeting(e) { | |
document.getElementById("videos").innerHTML = ""; | |
} | |
async function aTimeout(ms) { | |
return new Promise((resolve) => setTimeout(() => resolve(), ms)); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment