webrtc浏览器端直播推流示例代码
代码语言:html
所属分类:多媒体
代码描述:webrtc浏览器端直播推流示例代码,直播服务器可以安装mediamtx,地址https://github.com/bluenviron/mediamtx
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <style> html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; } body { display: flex; flex-direction: column; } #video { height: 100%; background: black; flex-grow: 1; min-height: 0; } #controls { height: 200px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } #device { flex-direction: column; } #device > div { margin: 10px 0; display: flex; gap: 20px; justify-content: center; } select { width: 200px; } </style> </head> <body> <video id="video" muted controls autoplay playsinline></video> <div id="controls"> <div id="initializing" style="display: block;"> initializing </div> <div id="device" style="display: none;"> <div id="device_line"> video device: <select id="video_device"> <option value="none">none</option> </select> audio device: <select id="audio_device"> <option value="none">none</option> </select> </div> <div id="codec_line"> video codec: <select id="video_codec"> </select> audio codec: <select id="audio_codec"> </select> </div> <div id="bitrate_line"> video bitrate (kbps): <input id="video_bitrate" type="text" value="10000" /> audio bitrate (kbps): <input id="audio_bitrate" type="text" value="32" /> <div> <input id="audio_voice" type="checkbox" checked> <label for="audio_voice">optimize for voice</label> </div> </div> <div id="submit_line"> <button id="publish_confirm">publish</button> </div> </div> <div id="transmitting" style="display: none;"> publishing </div> <div id="error" style="display: none;"> <span id="error-message"></span> </div> </div> <script> const INITIALIZING = 0; const DEVICE = 1; const TRANSMITTING = 2; const ERROR = 3; let state = INITIALIZING; let errorMessage = ''; const render = () => { for (const el of ['initializing', 'device', 'transmitting', 'error']) { document.getElementById(el).style.display = 'none'; } switch (state) { case DEVICE: document.getElementById('device').style.display = 'flex'; break; case TRANSMITTING: document.getElementById('transmitting').style.display = 'flex'; break; case ERROR: document.getElementById('error').style.display = 'flex'; document.getElementById('error-message').innerHTML = 'error: ' + errorMessage; break; } }; const restartPause = 2000; const unquoteCredential = (v) => ( JSON.parse(`"${v}"`) ); const linkToIceServers = (links) => ( (links !== null) ? links.split(', ').map((link) => { const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i); const ret = { urls: [m[1]], }; if (m[3] !== undefined) { ret.username = unquoteCredential(m[3]); ret.credential = unquoteCredential(m[4]); ret.credentialType = "password"; } return ret; }) : [] ); const parseOffer = (offer) => { const ret = { iceUfrag: '', icePwd: '', medias: [], }; for (const line of offer.split('\r\n')) { if (line.startsWith('m=')) { ret.medias.push(line.slice('m='.length)); } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) { ret.iceUfrag = line.slice('a=ice-ufrag:'.length); } else if (ret.icePwd === '' && line.startsWith('a=ice-pwd:')) { ret.icePwd = line.slice('a=ice-pwd:'.length); } } return ret; }; const generateSdpFragment = (offerData, candidates) => { const candidatesByMedia = {}; for (const candidate of candidates) { const mid = candidate.sdpMLineIndex; if (candidatesByMedia[mid] === undefined) { candidatesByMedia[mid] = []; } candidatesByMedia[mid].push(candidate); } let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n' + 'a=ice-pwd:' + offerData.icePwd + '\r\n'; let mid = 0; for (const media of offerData.medias) { if (candidatesByMedia[mid] !== undefined) { frag += 'm=' + media + '\r\n' + 'a=mid:' + mid + '\r\n'; for (const candidate of candidatesByMedia[mid]) { frag += 'a=' + candidate.candidate + '\r\n'; } } mid++; } return frag; }; const setCodec = (section, codec) => { const lines = section.split('\r\n'); const lines2 = []; const payloadFormats = []; for (const line of lines) { if (!line.startsWith('a=rtpmap:')) { lines2.push(line); } else { if (line.toLowerCase().includes(codec)) { payloadFormats.push(line.slice('a=rtpmap:'.length).split(' ')[0]); lines2.push(line); } } } const lines3 = []; for (const line of lines2) { if (line.startsWith('a=fmtp:')) { if (payloadFormats.includes(line.slice('a=fmtp:'.length).split(' ')[0])) { lines3.push(line); } } else if (line.startsWith('a=rtcp-fb:')) { if (payloadFormats.includes(line.slice('a=rtcp-fb:'.length).split(' ')[0])) { lines3.push(line); } } else { lines3.push(line); } } return lines3.join('\r\n'); }; const setVideoBitrate = (section, bitrate) => { let lines = section.split('\r\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('c=')) { lines = [...lines.slice(0, i+1), 'b=TIAS:' + (parseInt(bitrate) * 1024).toString(), ...lines.slice(i+1)]; break } } return lines.join('\r\n'); }; const setAudioBitrate = (section, bitrate, voice) => { let opusPayloadFormat = ''; let lines = section.split('\r\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('a=rtpmap:') && lines[i].toLowerCase().includes('opus/')) { opusPayloadFormat = lines[i].slice('a=rtpmap:'.length).split(' ')[0]; break; } } if (opusPayloadFormat === '') { return section; } for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('a=fmtp:' + opusPayloadFormat + ' ')) { if (voice) { lines[i] = 'a=fmtp:' + opusPayloadFormat + ' minptime=10;useinbandfec=1;maxaveragebitrate=' + (parseInt(bitrate) * 1024).toString(); } else { lines[i] = 'a=fmtp:' + opusPayloadFormat + ' maxplaybackrate=48000;stereo=1;sprop-stereo=1;maxaveragebitrate' + (parseInt(bitrate) * 1024).toString(); } } } return lines.join('\r\n'); }; const editAnswer = (answer, videoCodec, audioCodec, videoBitrate, audioBitrate, audioVoice) => { const sections = answer.split('m='); for (let i = 0; i < sections.length; i++) { const section = sections[i]; if (section.startsWith('video')) { sections[i] = setVideoBitrate(setCodec(section, videoCodec), videoBitrate); } else if (section.startsWith('audio')) { sections[i] = setAudioBitrate(setCodec(section, audioCodec), audioBitrate, audioVoice); } } return sections.join('m='); }; class Transmitter { constructor(stream) { this.stream = stream; this.pc = null; this.restartTimeout = null; this.eTag = ''; this.queuedCandidates = []; this.start(); } start() { //获取ice服务器,这里可以选择支持webrtc的直播流服务器地址 console.log("requesting ICE servers"); fetch(new URL('whip', window.location.href) + window.location.search, { method: 'OPTIONS', }) .then((res) => this.onIceServers(res)) .catch((err) => { console.log('error: ' + err); this.scheduleRestart(); .........完整代码请登录后点击上方下载按钮下载查看
网友评论0