WebRTC实战(一)

WebRTC是当前实时通信领域的重要技术之一,具有广泛的应用前景。可以实现音频、视频和数据的实时传输。支持点对点通信、多方会议、屏幕共享等多种应用场景,同时具有高质量、低延迟、强安全性等特点,是开发实时通信应用的理想选择。可以应用于远程协作、在线教育、在线医疗、物联网等领域,具有广泛的商业应用前景。WebRTC技术包括了音视频的采集、编解码、网络传输、显示等功能,本文主要从API使用说明和注意细节方面介绍一下WebRTC的大致情况。

根据W3C的WebRTC 1.0: Real-time Communication Between Browsers规范,WebRTC的源码中定义了两套主要的C++接口,分别是MediaStreamPeerConnection相关的API,MediaStream 相关API定义在源码api\media_stream_interface.h中。主要涉及这4个概念:sourcesinkmeidatrackmediastream

MediaStream API

Media API中有两个重要组成:MediaStreamTrack以及MediaStreamMediaStreamTrack对象代表单一类型的媒体流,产生自客户端的media source,可以是音频或者视频,但只能是其中一种,是音频称作audio track,视频的话称作video track,这其实就是我们平时所说的音轨与视频轨。

一个track由source与sink组成。source给track提供数据。

MeidiaStream用于将多个MediaStreamTrack对象打包到一起。一个MediaStream可包含audio trackvideo track。类似我们平时的多媒体文件,可包含音频与视频。

一个MediaStream对象包含0或多个MediaStreamTrack对象。MediaStream中的所有MediaStreamTrack对象在渲染时必须同步。就像我们平时播放媒体文件时,音视频的同步。

简单点说,source 与sink构成一个track,多个track构成MediaStram。

source 与 sink

在MediaTrack的源码中,MediaTrack都是由对应的source和sink组成的。

浏览器中存在从source到sink的媒体管道,其中source负责生产媒体资源,包括多媒体文件,web资源等静态资源以及麦克风采集的音频,摄像头采集的视频等动态资源。而sink则负责消费source生产媒体资源,也就是通过,video等媒体标签进行展示,或者是通过RTCPeerConnection将source通过网络传递到远端。RTCPeerConnection可同时扮演source与sink的角色,作为sink,可以将获取的source降低码率,缩放,调整帧率等,然后传递到远端,作为source,将获取的远端码流传递到本地渲染。

检测获取音视频设备

使用MediaDevices 接口提供访问连接媒体输入的设备,如照相机和麦克风,以及屏幕共享等。可以用来取得任何硬件资源的媒体数据。提供访问链接媒体输入的设备,如摄像头、麦克风、屏幕共享等。它可以使你取得任何硬件资源的媒体数据。

通过MediaDevices的方法 enumerateDevices() 请求一个可用的媒体输入和输出设备的列表,例如麦克风,摄像机,耳机设备等。返回的 Promise 完成时,会带有一个描述设备的 MediaDeviceInfo的数组。

function getUserMedia() {
   return new Promise((resolve, reject) => {
       navigator.mediaDevices.enumerateDevices().then((devices) => {
           devices.forEach((devInfo) => {
             if (devInfo.kind === 'audioinput') {
               //音频输入设备
            } else if (devInfo.kind === 'audiooutput') {
               //音频输出设备
            } else if (devInfo.kind === 'videoinput') {
               //摄像头
            }
          });
           resolve(devices);
      });
  });
}

采集本地音视频数据

MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其他轨道类型。

它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promisereject回调一个 PermissionDeniedError 或者 NotFoundError

navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
 // 使用 stream
}).catch(function(err) {
 // 处理 error
});
视频约束

getUserMedia()函数接收一个参数MediaStreamConstraints,MediaStreamConstraints对象用于指定要请求的轨道类型(音频,视频或二者)以及(可选)每个轨道的任何要求。

约束项可以具有下面这两个属性中的一个或两个:

  • video—表示是否需要视频轨道
  • audio—表示是否需要音频轨道

如果仅指定是否包含音频和视频流的时候,可以这样

var constraints = {audio: true, video: true} 

设置分辨率

可以用video中的width和height属性从网络摄像头请求一定的分辨率,比如请求1280×720分辨率的视频流。直接设置的width和height会被视为“ideal”(理想)值,浏览器会尽可能的保持这个分辨率,但是也有可能会返回一个接近的分辨率,因为我们设置的分辨率可能摄像头不支持

var constraints = {
 audio: true,
 video: {width: 1280, height: 720}
}

可以用min,max和exact关键字来限制分辨率范围,从而得到最佳的分辨率

{
 audio: true,
 video: {
   width: { min: 1024, ideal: 1280, max: 1920 },
   height: { min: 576, ideal: 720, max: 1080 },
},
}

可以使用deviceId指定被用于捕捉流的设备ID。这个设备ID是唯一的,并且在同一个来源的会话中是相同的。需要首使用MediaDevices.enumerateDevices()来获取设备id。以下是一些常用的约束条件:

{
 audio: {
   channelCount: 2, //声道数
   sampleRate://采样率。
   sampleSize://每个采样点大小的位数
   volume://从0(静音)到1(最大)取值
   echoCancellation://是否使用回声消除
   autoGainControl://自动增益
   noiseSuppression://是否尝试去除音频信号中的背景噪声
   latency://延迟,以秒为单位,控制开始处理声音和下一步可以使用数据之间的时间
},
 video: {
width: { min: 1024, ideal: 1280, max: 1920 },
   height: { min: 576, ideal: 720, max: 1080 },
   facingMode: { exact: "environment"},//摄像头 user前置摄像头,environment后置摄像头
   deviceId: {exact: myDeviceId}, //设备id
   aspectRatio: { ideal: 1.7777777778 }, //宽高比
   frameRate: 15 //视频帧率
   resizeMode: false //是否进行裁剪
},
}

MediaDevices.getSupportedConstraints()的支持。这个函数会返回一个字典,列出用户代理支持的约束。

 var video = document.querySelector('#video');
//流约束
const constraints = {
 audio: true,
 video: { width: 1280, height: 720 },
};
//成功回调
function successCallback(mediaStream) {
   localStream = mediaStream;
   // 获取视频的track
   const videoTrack = mediaStream.getVideoTracks()[0];
   //拿到video的所有约束
   const videoConstraints = videoTrack.getSettings();
  // 获取音频的track
   const audioTrack = mediaStream.getAudioTracks()[0];
 
   video.src = mediaStream;
}
//失败回调
function errorCallback(error){
  console.log('navigator.getUserMedia error: ', error);
}
navigator.mediaDevices.getUserMedia(constraints, successCallback, errorCallback);
设置码率

在 通 话 过 程 中 实 时 更 改 视 频 码 率 , 主 要 涉 及 对RTCRtpEncodingPara-meters.maxBitrate 的修改,maxBitrate 能够接受的码率单位是 bps。

static async updateBitrate(videoTrack, bitrate) {
   bitrate = bitrate * 1024;
   pc.getSenders().forEach(sender => {
       if(sender.track.kind === 'audio') return;
       let param = sender.getParameters();
       param.encodings[0].maxBitrate = bitrate;
       sender.setParameters(param).then(() => {
           param = sender.getParameters();
           console.log(" * Video Sender Encodings * ");
           const senderParamsEncoding = param.encodings.map(encoding =>
           JSON.stringify(encoding)).join("\n");
           console.log(senderParamsEncoding);
      }).catch(error => {
       console.error("Set MaxBitrate error! " + error.name);
      });
  });
}
兼容性问题

有一部分老的浏览器不能兼容navigator.mediaDevices.getUserMedia来获取麦克风和摄像头,可用如下来获取

if (navigator.mediaDevices.getUserMedia) {
 //最标准的api
 navigator.mediaDevices.getUserMedia(constrains).then((e) => {
}).catch(reject);
} else if (navigator.webkitGetUserMedia) {
 //webkit内核浏览器
 navigator.webkitGetUserMedia(constrains).then((e) => {
}).catch(reject);
} else if (navigator.mozGetUserMedia) {
 //Firefox浏览器
 navagator.mozGetUserMedia(constrains).then((e) => {
    }).catch(reject);
} else if (navigator.getUserMedia) {
 //旧版API
 navigator.getUserMedia(constrains).then((e) => {                
}).catch(reject);
}

Safari浏览器 不支持多个 tab getUserMedia,否则前一个 tab 会停止采集,远端流也有可能出现黑屏无声。

屏幕共享

MediaDevices 接口的 getDisplayMedia() 方法提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口)在一个MediaStream里。用法和getUserMedia类似。

var config = {
 video: {frameRate:15,width:1920,height:1080},
 audio: true
};
navigator.mediaDevices.getDisplayMedia(config).then(stream => {
   videoElement.srcObject = stream;
}, error => {
   console.log("Unable to acquire screen capture", error);
});

Safari默认只能共享整个屏幕,不支持选择应用和浏览器标签页。

设备监听

为了在应用程序中监测媒体设备的变化,WebRTC提供了devicechange事件和ondevicec-hange事件句柄,与navigator.mediaDevices结合即可实时监控媒体设备的热插拔。

// 1. 使用addEventListener监听事件
navigator.mediaDevices.addEventListener('devicechange', (event) => {
updateDeviceList();
})

//2.使用ondevicechange事件句柄
navigator.mediaDevices.ondevicechange = (event) => {
updateDeviceList();
}
视频质量调整策略

webrtc 提供了三种策略:

  • MAINTAIN_FRAMERATE:保帧率,降分辨率,该模式的使用场景为视频模式。
  • MAINTAIN_RESOLUTION: 保分辨率降帧率,使用场景为屏幕共享或者文档模式,对清晰度要求较高的场景。
  • BALANCED: 平衡帧率与分辨率。 默认关闭,需要通过 WebRTC-Video-BalancedDegradation 开启。enum class DegradationPreference {
     // Don’t take any actions based on over-utilization signals. Not part of the
     // web API.
     DISABLED,
     // On over-use, request lower resolution, possibly causing down-scaling.
     MAINTAIN_FRAMERATE,
     // On over-use, request lower frame rate, possibly causing frame drops.
     MAINTAIN_RESOLUTION,
     // Try to strike a “pleasing” balance between frame rate or resolution.
     BALANCED,
    };

webrtc API层提供了自适应策略的设置接口,通过设置 MediaStreamTrack 的 contentHint 属性就可以了。在WebRtcVideoSendStream::GetDegradationPreference 函数中会将 ContentHint 转换成 DegradationPreference。

Audio Content Hints

  • “speech”:指定音频源为讲演,可以适当使用噪声消除或者提升源的可理解性等
  • “speech-recognition”:指定音频源为语音识别,可以适合提高输入信号的清晰度以进行转录并关闭用于人类消费的音频处理组件
  • “music”:指定音频源为音乐,可能意味着调整或关闭用于处理语音数据的音频处理组件,以防止音频失真

Video Content Hints

  • “motion”:指定为运动的时候,降低分辨率以保持帧率
  • “detail”:指定为细节的时候,降低帧率以保持分辨率,屏幕分享一般默认为细节模式
  • “text”:指定为文本,降低帧率以保持分辨率,当编码器的文本模式可用的时候,使用文本模式。此设置通常会针对生成的单个帧中的细节进行优化,而不是平滑播放,并且可能会利用针对文本渲染进行优化的编码器工具,对于属性值为“text”的视频轨道,如果编码编解码器为AV1,则激活“text”模式的编码工具。
static async setVideoTrackContentHints(stream, hint) {
 // "motion" 流畅优先。 "detail" 清晰优先
 const tracks = stream.getVideoTracks();
 tracks.forEach((track) => {
   if ("contentHint" in track) {
     track.contentHint = hint;
     if (track.contentHint !== hint) {
       console.error(`Invalid video track contentHint: "${hint}"`);
    }
  } else {
     console.error("MediaStreamTrack contentHint attribute not supported");
  }
});
}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇