一、简介

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