JSFiddle - React, Tailwind, and code Playground
HTML
<audio playsinline id="playout" autoplay></audio>
JavaScript
// from https://github.com/otalk/sdp
var SDPUtils = {};
// Splits SDP into lines, dealing with both CRLF and LF.
SDPUtils.splitLines = function(blob) {
return blob.trim().split('\n').map(function(line) {
return line.trim();
});
};
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
SDPUtils.splitSections = function(blob) {
var parts = blob.split('\nm=');
return parts.map(function(part, index) {
return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
});
};
// Returns lines that start with a certain prefix.
SDPUtils.matchPrefix = function(blob, prefix) {
return SDPUtils.splitLines(blob).filter(function(line) {
return line.indexOf(prefix) === 0;
});
};
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
pc1.onicecandidate = (e) => e.candidate && e.candidate.sdpMLineIndex === 0 && pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = (e) => e.candidate && e.candidate.sdpMLineIndex === 0 && pc1.addIceCandidate(e.candidate);
pc2.ontrack = (e) => {
document.getElementById('playout').srcObject = new MediaStream(e.streams[0].getAudioTracks());
};
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
pc1.addTrack(stream.getTracks()[0], stream);
return pc1.createOffer();
})
.then(offer => pc1.setLocalDescription(offer))
.then(() => {
// fake a video m-line.
let sdp = pc1.localDescription.sdp;
const iceDtls = SDPUtils.splitLines(sdp).filter(line => {
return line.startsWith('a=fingerprint:') || line.startsWith('a=ice-ufrag:') || line.startsWith('a=ice-pwd');
});
const msid = SDPUtils.matchPrefix(sdp, 'a=msid:')[0].split(' ');
console.log(msid);
sdp += 'm=video 9 UDP/TLS/RTP/SAVPF 100\r\n'
+ 'c=IN IP4 0.0.0.0\r\n'
+ iceDtls[0] + '\r\n'
+ iceDtls[1] + '\r\n'
+ iceDtls[2] + '\r\n'
+ 'a=setup:actpass\r\n'
+ 'a=mid:1\r\n'
+ 'a=sendonly\r\n'
+ 'a=rtcp-mux\r\n'
+ 'a=rtpmap:100 VP8/90000\r\n'
+ msid[0] + ' ' +...