一、简介
WebRTC概念
WebRTC是由Google主导的,由一组规范、协定和JavaScript API组成,用于实现浏览器之间(端到端之间)的音频、视频及数据共享。WebRTC不须要装置任何插件,通过简略的JavaScript API就能够使得实时通信变成一种规范性能。
为什么应用webrtc
当初各大浏览器以及终曾经逐步加大对WebRTC技术的反对。下图是webrtc官网给出的当初曾经提供反对了的浏览器和平台。
二、H5播放webrtc
webrtc播放通过一直摸索,基本上没有现行的库来间接播放一个webrtc的url的库,很大一部分的思路是通过websocket+webrtc来实现数据传输和播放的,也有很多应用EMScript将c代码编解码库编译成js代码来应用。
不过庆幸的是,我找了一个库是反对srs的webrtc流的,它就是开源的jswebrtc,应用代码如下:
通过html播放
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebRTCPlayer</title> <style type="text/css"> html, body { background-color: #111; text-align: center; } </style></head><body> <div class="jswebrtc" data-url="webrtc://192.168.12.187/live/1"></div> <script type="text/javascript" src="jswebrtc.min.js"></script></body></html>
通过js播放
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebRTCPlayer</title></head><body> <video id="video-webrtc" controls></video> <script type="text/javascript" src="jswebrtc.min.js"></script> <script type="text/javascript"> var video = document.getElementById('video-webrtc'); var url = 'webrtc://192.168.12.187/live/1'; var player = new JSWebrtc.Player(url, { video: video, autoplay: true, onPlay: (obj) => { console.log("start play") } }); </script></body></html>
播放成果如下:
这里要阐明的是srs尽管提供了webrtc协定转换,然而webrtc是基于udp的,可能是udp丢包过于重大,srs并没有解决好,所以画面如果有动画,基本上是动画的中央会有花屏!!不过实时性的确不错,能够与rtmp实时协定相差无几。
三、扩大
webrtc是一种标准协议,不像只反对IE的OCX(ActiveX)插件或广泛浏览器都反对的flash插件(重点是google的chrome浏览器在2020年12月份之后不再反对flash插件),所以后续的无插件实时视频播放的重点就落在了webrtc上,所以从久远来讲它的特点(无插件、规范通用协定)使得webrtc被宽泛和短暂应用。
既然webrtc如此重要,在安防或互联网直播畛域就少不了视频采集或视频公布性能,所以就防止不了采集本地摄像头的音视频性能,如下就是我要讲的如何通过webrtc协定采集本地音视频,H5代码如下:
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="description" content="WebRTC code samples"><meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"><meta itemprop="description" content="Client-side WebRTC code samples"><meta itemprop="name" content="WebRTC code samples"><meta name="mobile-web-app-capable" content="yes"><meta id="theme-color" name="theme-color" content="#ffffff"><base target="_blank"><title>MediaStream Recording</title></head><body> <div id="container"> <h1> <span>WebRTC实例-媒体录制器</span> </h1> <video id="gum" playsinline autoplay muted></video> <video id="recorded" playsinline loop></video> <div> <button id="start">关上摄像头</button> <button id="record" disabled class="off">开始录像</button> <button id="play" disabled>播放</button> <button id="download" disabled>下载</button> </div> <div> <h4>媒体流束缚选项:</h4> <p> 打消回声: <input type="checkbox" id="echoCancellation"> </p> </div> <div> <span id="errorMsg"></span> </div> </div> <script async>'use strict';const mediaSource = new MediaSource();mediaSource.addEventListener('sourceopen', handleSourceOpen, false);let mediaRecorder;let recordedBlobs;let sourceBuffer;const errorMsgElement = document.querySelector('span#errorMsg');const recordedVideo = document.querySelector('video#recorded');const recordButton = document.querySelector('button#record');recordButton.addEventListener('click', () => { if (recordButton.className === 'off') { startRecording(); } else { stopRecording(); recordButton.textContent = '开始录像'; recordButton.className = 'off'; playButton.disabled = false; downloadButton.disabled = false; }});const playButton = document.querySelector('button#play');playButton.addEventListener('click', () => { const superBuffer = new Blob(recordedBlobs, {type: 'video/webm'}); recordedVideo.src = null; recordedVideo.srcObject = null; recordedVideo.src = window.URL.createObjectURL(superBuffer); recordedVideo.controls = true; recordedVideo.play();});const downloadButton = document.querySelector('button#download');downloadButton.addEventListener('click', () => { const blob = new Blob(recordedBlobs, {type: 'video/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);});function handleSourceOpen(event) { console.log('MediaSource opened'); sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"'); console.log('Source buffer: ', sourceBuffer);}function handleDataAvailable(event) { if (event.data && event.data.size > 0) { recordedBlobs.push(event.data); }}function startRecording() { recordedBlobs = []; let options = {mimeType: 'video/webm;codecs=vp9'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.error(`${options.mimeType} is not Supported`); errorMsgElement.innerHTML = `${options.mimeType} is not Supported`; options = {mimeType: 'video/webm;codecs=vp8'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.error(`${options.mimeType} is not Supported`); errorMsgElement.innerHTML = `${options.mimeType} is not Supported`; options = {mimeType: 'video/webm'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.error(`${options.mimeType} is not Supported`); errorMsgElement.innerHTML = `${options.mimeType} is not Supported`; options = {mimeType: ''}; } } } try { mediaRecorder = new MediaRecorder(window.stream, options); } catch (e) { console.error('Exception while creating MediaRecorder:', e); errorMsgElement.innerHTML = `Exception while creating MediaRecorder: ${JSON.stringify(e)}`; return; } console.log('Created MediaRecorder', mediaRecorder, 'with options', options); recordButton.textContent = '进行录像'; recordButton.className = 'on'; playButton.disabled = true; downloadButton.disabled = true; mediaRecorder.onstop = (event) => { console.log('Recorder stopped: ', event); }; mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.start(10); // collect 10ms of data console.log('MediaRecorder started', mediaRecorder);}function stopRecording() { mediaRecorder.stop(); console.log('Recorded Blobs: ', recordedBlobs);}function handleSuccess(stream) { recordButton.disabled = false; console.log('getUserMedia() got stream:', stream); window.stream = stream; const gumVideo = document.querySelector('video#gum'); gumVideo.srcObject = stream;}async function init(constraints) { try { const stream = await navigator.mediaDevices.getUserMedia(constraints); handleSuccess(stream); } catch (e) { console.error('navigator.getUserMedia error:', e); errorMsgElement.innerHTML = `navigator.getUserMedia error:${e.toString()}`; }}document.querySelector('button#start').addEventListener('click', async () => { const hasEchoCancellation = document.querySelector('#echoCancellation').checked; const constraints = { audio: { echoCancellation: {exact: hasEchoCancellation} }, video: { width: 1280, height: 720 } }; console.log('Using media constraints:', constraints); await init(constraints);});</script> <style>.hidden { display: none;}.highlight { background-color: #eee; font-size: 1.2em; margin: 0 0 30px 0; padding: 0.2em 1.5em;}.warning { color: red; font-weight: 400;}@media screen and (min-width: 1000px) { /* hack! to detect non-touch devices */ div#links a { line-height: 0.8em; }}audio { max-width: 100%;}body { font-family: 'Roboto', sans-serif; font-weight: 300; margin: 0; padding: 1em; word-break: break-word;}button { background-color: #d84a38; border: none; border-radius: 2px; color: white; font-family: 'Roboto', sans-serif; font-size: 0.8em; margin: 0 0 1em 0; padding: 0.5em 0.7em 0.6em 0.7em;}button:active { background-color: #2fcf5f;}button:hover { background-color: #cf402f;}button[disabled] { color: #ccc;}button[disabled]:hover { background-color: #d84a38;}canvas { background-color: #ccc; max-width: 100%; width: 100%;}code { font-family: 'Roboto', sans-serif; font-weight: 400;}div#container { margin: 0 auto 0 auto; max-width: 60em; padding: 1em 1.5em 1.3em 1.5em;}div#links { padding: 0.5em 0 0 0;}h1 { border-bottom: 1px solid #ccc; font-family: 'Roboto', sans-serif; font-weight: 500; margin: 0 0 0.8em 0; padding: 0 0 0.2em 0;}h2 { color: #444; font-weight: 500;}h3 { border-top: 1px solid #eee; color: #666; font-weight: 500; margin: 10px 0 10px 0; white-space: nowrap;}li { margin: 0 0 0.4em 0;}html { overflow-y: scroll;}img { border: none; max-width: 100%;}input[type=radio] { position: relative; top: -1px;}p#data { border-top: 1px dotted #666; font-family: Courier New, monospace; line-height: 1.3em; max-height: 1000px; overflow-y: auto; padding: 1em 0 0 0;}section p:last-of-type { margin: 0;}section { border-bottom: 1px solid #eee; margin: 0 0 30px 0; padding: 0 0 20px 0;}section:last-of-type { border-bottom: none; padding: 0 0 1em 0;}select { margin: 0 1em 1em 0; position: relative; top: -1px;}video { background: #222; margin: 0 0 20px 0; - -width: 100%; width: var(- -width); height: calc(var(- -width)* 0.75);}@media screen and (max-width: 450px) { h1 { font-size: 20px; }}button { margin: 0 3px 10px 0; padding-left: 2px; padding-right: 2px; width: 99px;}button:last-of-type { margin: 0;}p.borderBelow { margin: 0 0 20px 0; padding: 0 0 20px 0;}video { vertical-align: top; - -width: 25vw; width: var(- -width); height: calc(var(- -width)* 0.5625);}video:last-of-type { margin: 0 0 20px 0;}video#gumVideo { margin: 0 20px 20px 0;}</style></body></html>
留神:HTTPS加密通信能力获取getUserMedia(),否则报错:
TypeError: Cannot read property 'getUserMedia' of undefined
咱们能够将以上代码拆散和简化成html和js文件,那么index.html的采集渲染代码如下:
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>学习webrtc</title> </head> <body> <video autoplay></video> <script src="main.js"></script> </body> </html>
main.js采集代码如下:
//判断是否反对调用设施api,因为浏览器不同所以判断形式不同哦 function hasUserMedia() { return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);}if (hasUserMedia()) { //alert("浏览器反对") navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; navigator.getUserMedia({ video : true,//开启视频 audio : false //先敞开音频,因为会有回响,当前两台电脑通信不会有响声 }, function(stream) {//将视频流交给video var video = document.querySelector("video"); //video.src=window.URL.createObjectURL(stream); try { video.srcObject = stream; } catch (error) { video.src = window.URL.createObjectURL(stream); } }, function(err) { console.log("capturing", err) });} else { alert("浏览器暂不反对")}
通过以上代码,咱们就能够采集本地的摄像头播放本地的采集的视频和音频了!
源码获取、单干、技术交换请获取如下联系方式:
QQ交换群:961179337
微信账号:lixiang6153
公众号:IT技术快餐
电子邮箱:lixx2048@163.com