当前位置: 首页 > news >正文

文化传媒主播公司 东莞网站建设北京知名seo公司精准互联

文化传媒主播公司 东莞网站建设,北京知名seo公司精准互联,如何优化网页,单位网站建设的必要性一、WebRTC 使用入门 WebRTC#xff08;全称 Web Real-Time Communication#xff09;#xff0c;即网页即时通信。 是一个支持网页浏览器进行实时语音对话或视频对话的技术方案。从前端技术开发的视角来看#xff0c;是一组可调用的API标准。 WebRTC API 官网#xff1…一、WebRTC 使用入门 WebRTC全称 Web Real-Time Communication即网页即时通信。 是一个支持网页浏览器进行实时语音对话或视频对话的技术方案。从前端技术开发的视角来看是一组可调用的API标准。 WebRTC API 官网WebRTC samples WebRTC 标准概括介绍了两种不同的技术媒体捕获设备和点对点连接。 媒体捕获设备包括摄像机和麦克风还包括屏幕捕获设备。对于摄像头和麦克风我们使用 navigator.mediaDevices.getUserMedia() 来捕获 MediaStreams。对于屏幕录制我们改为使用 navigator.mediaDevices.getDisplayMedia()。 点对点连接由 RTCPeerConnection 接口处理。这是在 WebRTC 中两个对等方之间建立和控制连接的中心点。 WebRPC 需要做以下的几件事: - 获取音频,视频或者其他数据- 获取网络信息比如IP地址,端口,并与其他的WebRTC客户端进行交换,穿过NAT合防火墙进行连接.- 处理信号以便发起请求报告错误或者关闭会话- 交换客户端支持的媒体信息,比如分辨率,解码器- 传输音频视频流或者数据 webrtc工作流程 媒体设备使用入门 针对 Web 开发时WebRTC 标准提供了用于访问连接到计算机或智能手机的相机和麦克风的 API。这些设备通常称为媒体设备可以通过实现 MediaDevices 接口的 navigator.mediaDevices 对象使用 JavaScript 进行访问。通过此对象我们可以枚举所有已连接的设备监听设备的变化设备连接或断开连接时以及打开设备以检索媒体流见下文。 其最常见的方式是通过 getUserMedia() 函数该函数会返回一个解析为匹配媒体设备的 MediaStream 的 promise。此函数采用单个 MediaStreamConstraints 对象用于指定我们的要求。例如要简单地打开默认麦克风和摄像头请执行以下操作。 // 使用promise const constraints {video: true,audio: true } navigator.mediaDevices.getUserMedia(constraints).then(stream {console.log(Got MediaStream:, stream);}).catch(error {console.error(Error accessing media devices., error);});// 使用await/async const openMediaDevices async (constraints) {return await navigator.mediaDevices.getUserMedia(constraints); }try {const stream openMediaDevices({video:true,audio:true});console.log(Got MediaStream:, stream); } catch(error) {console.error(Error accessing media devices., error); } 调用 getUserMedia() 将触发权限请求。如果用户接受该权限系统会使用包含一个视频和一个音轨的 MediaStream 解析该 promise。如果权限遭拒系统会抛出 PermissionDeniedError。如果没有连接任何匹配的设备则会抛出 NotFoundError。 查询媒体设备 在更复杂的应用中我们很可能需要检查所有连接的摄像头和麦克风并向用户提供相应的反馈。这可以通过调用 enumerateDevices() 函数来实现。这将返回一个 promise它可以解析为描述每个已知媒体设备的 MediaDevicesInfo 数组。我们可以用它来呈现界面让用户选择他们喜欢的那个。每个 MediaDevicesInfo 都包含一个名为 kind 的属性其值为 audioinput、audiooutput 或 videoinput指示它是哪种类型的媒体设备。 // promise function getConnectedDevices(type, callback) {navigator.mediaDevices.enumerateDevices().then(devices {const filtered devices.filter(device device.kind type);callback(filtered);}); }getConnectedDevices(videoinput, cameras console.log(Cameras found, cameras));// async await async function getConnectedDevices(type) {const devices await navigator.mediaDevices.enumerateDevices();return devices.filter(device device.kind type) }const videoCameras getConnectedDevices(videoinput); console.log(Cameras found:, videoCameras); 监听设备更改 大多数计算机都支持在运行时插入各种设备。它可能是通过 USB 连接的摄像头、蓝牙耳机或一组外部扬声器。为了正确支持这一点Web 应用应监听媒体设备的变化。这可以通过为 devicechange 事件的 navigator.mediaDevices 添加监听器来实现。 // Updates the select element with the provided set of cameras function updateCameraList(cameras) {const listElement document.querySelector(select#availableCameras);listElement.innerHTML ;cameras.map(camera {const cameraOption document.createElement(option);cameraOption.label camera.label;cameraOption.value camera.deviceId;}).forEach(cameraOption listElement.add(cameraOption)); }// Fetch an array of devices of a certain type async function getConnectedDevices(type) {const devices await navigator.mediaDevices.enumerateDevices();return devices.filter(device device.kind type) }// Get the initial set of cameras connected const videoCameras getConnectedDevices(videoinput); updateCameraList(videoCameras);// Listen for changes to media devices and update the list accordingly navigator.mediaDevices.addEventListener(devicechange, event {const newCameraList getConnectedDevices(video);updateCameraList(newCameraList); }); 媒体限制 如果约束对象必须实现 MediaStreamConstraints 接口并将其作为参数传递给 getUserMedia()我们就可以打开符合特定要求的媒体设备。此要求可以非常宽泛音频和/或视频也可以非常具体最低相机分辨率或确切设备 ID。建议使用 getUserMedia() API 的应用先检查现有设备然后使用 deviceId 限制条件指定与设备完全匹配的限制条件。如果可能设备还会根据限制条件进行配置。我们可以对麦克风启用回声消除功能也可以从摄像头设置视频的特定或最小宽度和高度。 async function getConnectedDevices(type) {const devices await navigator.mediaDevices.enumerateDevices();return devices.filter(device device.kind type) }// Open camera with at least minWidth and minHeight capabilities async function openCamera(cameraId, minWidth, minHeight) {const constraints {audio: {echoCancellation: true},video: {deviceId: cameraId,width: {min: minWidth},height: {min: minHeight}}}return await navigator.mediaDevices.getUserMedia(constraints); }const cameras getConnectedDevices(videoinput); if (cameras cameras.length 0) {// Open first available video camera with a resolution of 1280x720 pixelsconst stream openCamera(cameras[0].deviceId, 1280, 720); } 本地播放(拉流) 媒体设备打开后如果有 MediaStream我们可以将其分配给视频或音频元素以在本地播放流。 async function playVideoFromCamera() {try {const constraints {video: true, audio: true};const stream await navigator.mediaDevices.getUserMedia(constraints);const videoElement document.querySelector(video#localVideo);videoElement.srcObject stream;} catch(error) {console.error(Error opening video camera., error);} } 与 getUserMedia() 一起使用的典型视频元素所需的 HTML 通常具有 autoplay 和 playsinline 属性。autoplay 属性将使分配给元素的新数据流自动播放。playsinline 属性允许视频在特定移动浏览器中内嵌播放而不仅仅是全屏播放。此外我们还建议对直播使用 controlsfalse除非用户应能够暂停这些直播。 html headtitleLocal video playback/video/head bodyvideo idlocalVideo autoplay playsinline controlsfalse/ /body /html 二、媒体捕获和约束 WebRTC 的媒体部分介绍了如何使用能够捕捉视频和音频的硬件例如相机和麦克风以及媒体流的工作原理。此外还介绍了显示媒体这是应用可执行屏幕捕获的方式。 媒体设备 您可以通过 navigator.mediaDevices 对象访问和管理浏览器支持的所有摄像头和麦克风。应用可以检索已连接设备的最新列表并监听变化因为许多相机和微型麦克风可通过 USB 连接并且可以在应用生命周期内连接和断开连接。由于媒体设备的状态可能会随时发生变化因此建议应用注册设备更改以便正确处理更改。 采集音视频 访问媒体设备时建议您提供尽可能详细的限制条件。虽然可以通过简单的约束条件打开默认摄像头和麦克风但其提供的媒体流可能明显优于应用的最佳流。 具体的约束条件在 MediaTrackConstraint 对象中定义一个针对音频另一个针对视频。此对象中的特性类型为 ConstraintLong、ConstraintBoolean、ConstraintDouble 或 ConstraintDOMString。这些对象可以是特定值例如数字、布尔值或字符串、范围具有最小值和最大值的 LongRange 或 DoubleRange或具有 ideal 或 exact 定义的对象。对于特定值浏览器将尝试选择尽可能接近的值。对于某个范围将使用该范围内的最佳值。指定 exact 后系统将仅返回与约束条件完全匹配的媒体流。 // Camera with a resolution as close to 640x480 as possible {video: {width: 640,height: 480} } // Camera with a resolution in the range 640x480 to 1024x768 {video: {width: {min: 640,max: 1024},height: {min: 480,max: 768}} } // Camera with the exact resolution of 1024x768 {video: {width: {exact: 1024},height: {exact: 768}} } 为了确定某个媒体流的特定轨道的实际配置我们可以调用 MediaStreamTrack.getSettings()它会返回当前应用的 MediaTrackSettings。 此外也可以通过对媒体轨道上调用 applyConstraints() 来更新已打开的媒体设备上的轨道约束条件。这样应用无需重新关闭现有音频流即可重新配置媒体设备。 显示媒体 想要能够截取和录制屏幕的应用必须使用 Display Media API。函数 getDisplayMedia()属于 navigator.mediaDevices 的一部分与 getUserMedia() 类似用于打开显示内容或部分内容如窗口。返回的 MediaStream 与使用 getUserMedia() 时相同。 getDisplayMedia() 的约束条件与常规视频或音频输入资源的限制不同。 {video: {cursor: always | motion | never,displaySurface: application | browser | monitor | window} } 上述代码片段展示了屏幕录制的特殊限制的工作原理。请注意并非所有支持显示媒体支持的浏览器都支持这些属性。 帧率降噪功能配置 frameRate可以配置视频帧率 width设置视频宽度ideal代表理想宽度 height设置视频高度ideal代表理想高度 aspectRatio代表宽高比 对于音频则是开启回音消除、降噪、自动增益等操作 const mediaStreamContrains {video: {frameRate: {min: 20},width: {min: 640, ideal: 1280},height: {min: 360, ideal: 720},aspectRatio: 16/9},audio: {echoCancellation: true, // 开启回音消除noiseSuppression: true, // 降噪autoGainControl: true // 自动增益} };var promise navigator.mediaDevices.getUserMedia(mediaStreamContrains); 采集视频数据 采集摄像头的内容并在浏览器上播放 需要注意的是一定要在https协议或者本地localhost域名下才可以调用 我们通过调用 getUserMedia 方法将视频数据加载到 video 标签中进行播放 如果video标签想要播放流媒体数据需要将数据挂在到 srcObject属性上该属性和普通的 src 属性互斥 如果是第一次请求 Camera浏览器会向用户弹出提示窗口让用户决定是否可以访问摄像头 如果用户允许访问且设备可用则调用 gotLocalMediaStream 方法 获取浏览器设备信息 以手机为例它一般会包括前置摄像头和后置摄像头麦克风、相机、耳机等。我们可以根据自己的需要选择打开不同的设备 WebRTC 是否提供了的 enumerateDevices 接口可以查询自己机子上都有哪些音视频设备 deviceInfo中有三个比较重要的属性 deviceID设备的唯一标识 label设备名称用户已被授予访问媒体设备的权限要想授予权限需要使用 HTTPS 请求否则 label 字段始终为空。 kind设备种类可用于识别出是音频设备还是视频设备是输入设备还是输出设备 // 判断浏览器是否支持这些 API if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {console.log(enumerateDevices() not supported.);return; }// 枚举 cameras and microphones. navigator.mediaDevices.enumerateDevices().then(function (deviceInfos) {// 打印出每一个设备的信息deviceInfos.forEach(function (deviceInfo) {console.log(deviceInfo.kind : deviceInfo.label id deviceInfo.deviceId);});}).catch(function (err) {console.log(err.name : err.message);}); 方法 getUserMedia 的配置参数 facingMode: ‘user’ , ‘environment’ 代表前后置。sampleRate指定采样率。sampleSize每个采样点大小的位数volume从0静音到1最大取值echoCancellation是否使用回声消除来尝试去除通过麦克风回传到扬声器的音频autoGainControl是否要修改麦克风的输入音量noiseSuppression是否尝试去除音频信号中的背景噪声latency以秒为单位控制开始处理声音和下一步可以使用数据之间的时间不是很确定为什么要设更高的延迟但是音频编解码器的延时确实有所不同。channelCount规定了单声道的时候为1立体声的时候为2。   数据流和轨道 MediaStream 表示媒体内容流由音频和视频轨道 (MediaStreamTrack) 组成。您可以通过调用 MediaStream.getTracks() 从 MediaStream 检索所有轨道该方法会返回一组 MediaStreamTrack 对象。 媒体流跟踪 MediaStreamTrack 具有的 kind 属性为 audio 或 video用于表示其表示的媒体类型。您可以通过切换其 enabled 属性将各个轨道静音。轨道具有布尔属性 remote它会指示它来自 RTCPeerConnection 而来自远程对等设备。 对等连接 点对点连接是 WebRTC 规范的一部分该规范旨在对点一台计算机上的两台应用进行连接以使用点对点协议进行通信。对等设备之间的通信可以是视频、音频或任意二进制数据适用于支持 RTCDataChannel API 的客户端。为了发现两个对等端如何连接两个客户端都需要提供 ICE Server 配置。这是 STUN 或 TURN 服务器其作用是向每个客户端提供 ICE 候选对象然后这些客户端将被传输到远程对等方。这种转移 ICE 候选对象的方式通常称为信号。 信令 WebRTC 规范包含用于与 ICE互联网连接建立服务器通信的 API但**信令组件**并不属于该组件。需要发出信号才能让两个对等网络共享它们之间的连接方式。这通常可以通过基于 HTTP 的常规 Web API即 REST 服务或其他 RPC 机制解决在此过程中网络应用可在发起对等连接之前中继必要的信息。 // Set up an asynchronous communication channel that will be // used during the peer connection setup const signalingChannel new SignalingChannel(remoteClientId); signalingChannel.addEventListener(message, message {// New message from remote client received });// Send an asynchronous message to the remote client signalingChannel.send(Hello!); 信令可以通过许多不同的方式实现WebRTC 规范不偏好任何特定的解决方案。(前端程序员,可以使用nodejs,websocket技术实现) 启动对等连接 每个对等连接都由一个 RTCPeerConnection 对象处理。此类的构造函数接受单个 RTCConfiguration 对象作为其参数。此对象定义对等连接的设置方式**应包含关于要使用的 ICE 服务器的信息**。 每个对等连接都由一个RTCPeerconnection对象处理。此类的构造函数将单个RTCConfiguration对象作为其参数。此对象定义了对等连接的设置方式并应包含有关要使用的ICE服务器的信息。 一旦创建了RTCPeerConnection连接我们需要创建**SDP提供**或**应答**这取决于我们是主叫对等体还是接收对等体。一旦创建了SDP提供或应答就必须通过不同的信道将其发送到远程对等端。将SDP对象传递给远程对等方称为**信令**不在Web RTC规范的范围内。 为了从调用端启动对等连接设置我们创建了一个RTCPeerconnection对象然后调用createOffer()来创建一个RTCSessionDescription对象。使用setLocalDescription()将此会话描述设置为本地描述然后通过我们的信令信道发送到接收方。我们还为我们的信号通道设置了一个监听器以便在从接收端接收到对我们提供的会话描述的回答时使用。 async function makeCall() {const configuration {iceServers: [{urls: stun:stun.l.google.com:19302}]}const peerConnection new RTCPeerConnection(configuration);signalingChannel.addEventListener(message, async message {if (message.answer) {const remoteDesc new RTCSessionDescription(message.answer);await peerConnection.setRemoteDescription(remoteDesc);}});const offer await peerConnection.createOffer();await peerConnection.setLocalDescription(offer);signalingChannel.send({offer: offer}); } RTCPeerConnection.createOffer():RTCPeerConnection接口的 createOffer() 方法启动创建一个[SDP](https://developer.mozilla.org/zh-CN/docs/Glossary/SDP) offer目的是启动一个新的 WebRTC 去连接远程端点。SDP offer 包含有关已附加到 WebRTC 会话浏览器支持的编解码器和选项的所有[MediaStreamTrack](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStreamTrack)s 信息以及[ICE](https://developer.mozilla.org/zh-CN/docs/Glossary/ICE) 代理目的是通过信令信道发送给潜在远程端点以请求连接或更新现有连接的配置。返回值是一个[Promise (en-US)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)创建 offer 后将使用包含新创建的要约的[RTCSessionDescription](https://developer.mozilla.org/zh-CN/docs/Web/API/RTCSessionDescription)对象来解析该返回值。 在接收端我们会等待传入的回应然后再创建 RTCPeerConnection 实例。完成后我们使用 setRemoteDescription() 设置收到的回应。接下来我们调用 createAnswer() 为收到的优惠创建答案。系统会使用 setLocalDescription() 将此答案设置为本地说明然后通过我们的信令服务器将其发送至发起调用的一方。 const peerConnection new RTCPeerConnection(configuration); signalingChannel.addEventListener(message, async message {if (message.offer) {peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));const answer await peerConnection.createAnswer();await peerConnection.setLocalDescription(answer);signalingChannel.send({answer: answer});} }); 两个对等方同时设置了本地和远程会话说明之后他们就会了解远程对等方的功能。这并不意味着对等设备之间的连接已准备就绪。为此我们需要在每个对等端收集 ICE 候选项并通过信令通道传输给另一个对等方。 ICE 创建 RTCPeerConnection 对象后底层框架会使用提供的 ICE 服务器收集连接建立的候选对象ICE 候选对象。RTCPeerConnection 上的事件 icegatheringstatechange 会指示 ICE 收集的状态为new、gathering 或 complete。 虽然对等设备可以等待 ICE 收集完成但通常要高效地使用“滚动冰”技术并在发现每个 ICE 候选设备后将其传输到远程对等设备。这将大大缩短对等连接的设置时间并允许视频通话以更低的延迟开始。 要收集 ICE 候选对象只需为 icecandidate 事件添加监听器即可。针对该监听器发出的 RTCPeerConnectionIceEvent 将包含 candidate 属性该属性表示应发送到远程对等端的新候选音频请参阅信号。 // Listen for local ICE candidates on the local RTCPeerConnection peerConnection.addEventListener(icecandidate, event {if (event.candidate) {signalingChannel.send({new-ice-candidate: event.candidate});} });// Listen for remote ICE candidates and add them to the local RTCPeerConnection signalingChannel.addEventListener(message, async message {if (message.iceCandidate) {try {await peerConnection.addIceCandidate(message.iceCandidate);} catch (e) {console.error(Error adding received ice candidate, e);}} }); 已建立连接 收到 ICE 候选对象后我们的对等连接状态最终会变为已连接状态。为了检测这一点我们在 RTCPeerConnection 中添加一个监听器用于监听 connectionstatechange 事件。 // Listen for connectionstatechange on the local RTCPeerConnection peerConnection.addEventListener(connectionstatechange, event {if (peerConnection.connectionState connected) {// Peers connected!} }); 远程数据流使用入门 RTCPeerConnection 连接到远程对等设备后就可以在它们之间流式传输音频和视频。此时我们会将从 getUserMedia() 收到的数据流连接到 RTCPeerConnection。媒体流包含至少一个媒体轨道当我们想将媒体传输到远程对等设备时它们会分别添加到 RTCPeerConnection 中。 const localStream await getUserMedia({vide: true, audio: true}); const peerConnection new RTCPeerConnection(iceConfig); localStream.getTracks().forEach(track {peerConnection.addTrack(track, localStream); }); 轨道可以在连接到远程对等方之前添加到 RTCPeerConnection因此最好尽早执行此设置而不是等待连接完成。 添加远程轨道 为了接收由另一个对等方添加的远程轨道我们会在本地 RTCPeerConnection 上注册一个监听器用于监听 track 事件。RTCTrackEvent 包含一个 MediaStream 对象数组这些对象与对等项的相应本地数据流具有相同的 MediaStream.id 值。在我们的示例中每个轨道仅与单个数据流相关联。 const remoteVideo document.querySelector(#remoteVideo);peerConnection.addEventListener(track, async (event) {const [remoteStream] event.streams;remoteVideo.srcObject remoteStream; }); 数据通道 WebRTC 标准还涵盖用于通过 RTCPeerConnection 发送任意数据的 API。可通过对 RTCPeerConnection 对象调用 createDataChannel() 来完成此操作该方法会返回 RTCDataChannel 对象。 const peerConnection new RTCPeerConnection(configuration); const dataChannel peerConnection.createDataChannel(); 远程对等端可以通过监听 RTCPeerConnection 对象的 datachannel 事件来接收数据通道。收到的事件是 RTCDataChannelEvent 类型包含一个 channel 属性该属性表示在对等方之间连接的 RTCDataChannel。 const peerConnection new RTCPeerConnection(configuration); peerConnection.addEventListener(datachannel, event {const dataChannel event.channel; }); 打开和关闭事件 在使用数据通道发送数据之前客户端需要等到数据通道打开后才能使用它。具体方法是监听 open 事件。同样当任意一侧关闭频道时也会发生 close 事件。 const messageBox document.querySelector(#messageBox); const sendButton document.querySelector(#sendButton); const peerConnection new RTCPeerConnection(configuration); const dataChannel peerConnection.createDataChannel();// Enable textarea and button when opened dataChannel.addEventListener(open, event {messageBox.disabled false;messageBox.focus();sendButton.disabled false; });// Disable input when closed dataChannel.addEventListener(close, event {messageBox.disabled false;sendButton.disabled false; }); 信息 如需在 RTCDataChannel 上发送消息请使用要发送的数据调用 send() 函数。此函数的 data 参数可以是字符串、Blob、ArrayBuffer 或 ArrayBufferView。 const messageBox document.querySelector(#messageBox); const sendButton document.querySelector(#sendButton);// Send a simple text message when we click the button sendButton.addEventListener(click, event {const message messageBox.textContent;dataChannel.send(message); }) 远程对等端将通过监听 message 事件来接收 RTCDataChannel 上发送的消息。 const incomingMessages document.querySelector(#incomingMessages);const peerConnection new RTCPeerConnection(configuration); const dataChannel peerConnection.createDataChannel();// Append new messages to the box of incoming messages dataChannel.addEventListener(message, event {const message event.data;incomingMessages.textContent message \n; }); 代码流程实例 webrtc.js export default {data() {return {stream: null,}},methods: {// 推流async pullPlayer() {try {// 1.获取本地音视频流// 调用 getUserMedia API 获取音视频流this.stream await navigator.mediaDevices.getUserMedia({audio: true,video: true,})// 拉流this.localVideo document.getElementById(localVideo)console.log(Received local stream, this.stream, this.localVideo.srcObject)// this.desc 11111this.localVideo.srcObject this.streamthis.localStream this.stream} catch (e) {console.log(getUserMedia() error: ${e})}},// 创建RTCPeerConnection连接发送方createRTCPeer() {this.configuration {iceServers: [{urls: stun:stun.l.google.com:19302,},],}// 源连接this.pc1 new RTCPeerConnection(this.configuration)// 监听返回的 Candidate// 当ice准备好后加到目标源中this.pc1.addEventListener(icecandidate, e this.onIceCandidate(this.pc1, e))this.pc1.addEventListener(iceconnectionstatechange, e this.onIceStateChange(this.pc1, e))//把localStream的音视频放到源中this.getTracksStreams()},accceptRTCPeer() {//目标this.pc2 new RTCPeerConnection(this.configuration)// 当ice准备好后加到目标源中this.pc2.addEventListener(icecandidate, e this.onIceCandidate(this.pc2, e))this.pc2.addEventListener(iceconnectionstatechange, e this.onIceStateChange(this.pc2, e))//等待源发来的流this.pc2.addEventListener(track, this.gotRemoteStream)},//把localStream的音视频放到源中getTracksStreams() {// 遍历本地流的所有轨道this.localStream.getTracks().forEach(track this.pc1.addTrack(track, this.localStream))},// 添加 iceCandidate 时调用的方法async onIceCandidate(pc, event) {try {// 源发来的ice加入到目标中await this.getOtherPc(pc).addIceCandidate(event.candidate)// 添加成功this.onAddIceCandidateSuccess(pc)} catch (e) {// 添加失败this.onAddIceCandidateError(pc, e)}console.log(${this.getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : (null)},)},// 拉流将发送来的轨道数据赋值gotRemoteStream(e) {if (this.remoteVideo.srcObject ! e.streams[0]) {// getUserMedia 获得流后将音视频流展示并保存到 localStreamthis.remoteVideo.srcObject e.streams[0]console.log(pc2 received remote stream, e.streams[0])}},// 判断是发送方还是接收方getOtherPc(pc) {return pc this.pc1 ? this.pc2 : this.pc1},getName(pc) {return pc this.pc1 ? pc1 : pc2},onIceStateChange(pc, event) {if (pc) {console.log(${this.getName(pc)} ICE state: ${pc.iceConnectionState})console.log(ICE state change event: , event)}},// 创建和设置连接描述async createOffers() {try {console.log(pc1 createOffer start)this.offerOptions {offerToReceiveAudio: 1,offerToReceiveVideo: 1,}// 交换媒体描述信息const offer await this.pc1.createOffer(this.offerOptions)await this.onCreateOfferSuccess(offer)} catch (e) {this.onCreateSessionDescriptionError(e)}},async onCreateOfferSuccess(desc) {// 发送端创建连接描述try {// 本地设置描述并将它发送给远端// 将 offer 保存到本地await this.pc1.setLocalDescription(desc)this.onSetLocalSuccess(this.pc1)} catch (e) {this.onSetSessionDescriptionError()}// 接收端创建连接描述try {// 远端将本地给它的描述设置为远端描述// 远端将 offer 保存await this.pc2.setRemoteDescription(desc)this.onSetRemoteSuccess(this.pc2)} catch (e) {this.onSetSessionDescriptionError()}// 目标 拿到源的连接描述后给自己并生成自己的连接描述try {// 远端创建应答 answerconst answer await this.pc2.createAnswer()await this.onCreateAnswerSuccess(answer)} catch (e) {this.onCreateSessionDescriptionError(e)}},// 本地描述创建成功onSetLocalSuccess(pc) {console.log(${this.getName(pc)} setLocalDescription complete)},// 本地描述创建失败onSetSessionDescriptionError(error) {console.log(Failed to set session description: ${error.toString()})},// 接收描述创建成功onSetRemoteSuccess(pc) {console.log(${this.getName(pc)} setRemoteDescription complete)},// 接收描述创建失败onCreateSessionDescriptionError(error) {console.log(Failed to create session description: ${error.toString()})},// 接收端生成自己的连接描述async onCreateAnswerSuccess(desc) {try {// 远端设置本地描述并将它发给本地// 远端保存 answerawait this.pc2.setLocalDescription(desc)this.onSetLocalSuccess(this.pc2)} catch (e) {this.onSetSessionDescriptionError(e)}console.log(pc1 setRemoteDescription start)try {// 本地将远端的应答描述设置为远端描述// 本地保存 answerawait this.pc1.setRemoteDescription(desc)this.onSetRemoteSuccess(this.pc1)} catch (e) {this.onSetSessionDescriptionError(e)}},// 接收端创建本地描述成功onSetLocalSuccess(pc) {console.log(${this.getName(pc)} setLocalDescription complete)},// 3.端与端建立连接handleConnection(event) {// 获取到触发 icecandidate 事件的 RTCPeerConnection 对象// 获取到具体的Candidateconst peerConnection event.targetconst iceCandidate event.candidateif (iceCandidate) {// 创建 RTCIceCandidate 对象const newIceCandidate new RTCIceCandidate(iceCandidate)// 得到对端的 RTCPeerConnectionconst otherPeer getOtherPeer(peerConnection)// 将本地获得的 Candidate 添加到远端的 RTCPeerConnection 对象中// 为了简单这里并没有通过信令服务器来发送 Candidate直接通过 addIceCandidate 来达到互换 Candidate 信息的目的otherPeer.addIceCandidate(newIceCandidate).then(() {handleConnectionSuccess(peerConnection)}).catch(error {handleConnectionFailure(peerConnection, error)})}}, // 4.显示远端媒体流gotRemoteMediaStream(event) {if (remoteVideo.srcObject ! event.streams[0]) {remoteVideo.srcObject event.streams[0]remoteStream event.streams[0]console.log(remote 开始接受远端流)}},}, }player.vue templatediv classvideo-windowvideo idlocalVideo playsinline autoplay muted/videovideo idremoteVideo playsinline autoplay/videodiv classboxbutton idstartButton clickstartStart/buttonbutton idcallButton clickcallCall/buttonbutton idhangupButton clickhangupHang Up/button/divdiv{{ desc }}/divbutton classbtn clickToIndex1001/button/div /template script import wx from weixin-js-sdk import webrtc from ../mixins/webrtc export default {data() {return {startButton: null,callButton: null,hangupButton: null,localVideo: null,remoteVideo: null,startTime: null,pc1: null,pc2: null,desc: ,}},mixins: [webrtc],created() {this.roomId this.$route.query.roomId ? this.$route.query.roomId : 001},mounted() {this.startButton document.getElementById(startButton)this.callButton document.getElementById(callButton)this.hangupButton document.getElementById(hangupButton)this.callButton.disabled truethis.hangupButton.disabled truethis.remoteVideo document.getElementById(remoteVideo)},methods: {async start() {try {await this.pullPlayer()this.startButton.disabled truethis.callButton.disabled false} catch (error) {this.desc ethis.startButton.disabled false}},// 拉流async call() {this.callButton.disabled truethis.hangupButton.disabled falseconsole.log(Starting call)this.startTime window.performance.now()// 视频轨道const videoTracks this.localStream.getVideoTracks()// 音频轨道const audioTracks this.localStream.getAudioTracks()// 判断视频轨道是否有值if (videoTracks.length 0) {console.log(Using video device: ${videoTracks[0].label})}// 判断音频轨道是否有值if (audioTracks.length 0) {console.log(Using audio device: ${audioTracks[0].label})}await this.createRTCPeer()await this.accceptRTCPeer()await this.createOffers()},// 断链hangup() {console.log(Ending call)this.pc1.close()this.pc2.close()this.pc1 nullthis.pc2 nullthis.hangupButton.disabled truethis.callButton.disabled false},ToIndex() {wx.miniProgram.navigateTo({url: /pages/index/index, //小程序地址})},ToIndex1() {this.$router.push({path: index,})},}, } /script style scoped .video-window {width: 100%;height: 100%; } /* .video-window video {width: 100%;height: 100%;margin: 12px 12px 0; } */ video {width: calc(100% - 48px);height: 200px;background: #000;margin: 24px 24px 0; } .btn {padding: 12px;position: absolute;top: 12px;right: 12px; } .box {width: 100%;height: 42px;text-align: center; } .box button {padding: 8px 12px;margin: 12px 4px; } /styleWEBRTC能力测试 TRTC 能力检测 页面准备 视频播放的媒介是 H5 提供的 Video音视频和 Audio纯音频。   body !-- 音视频 -- !-- 本地视频流 muted: 本地视频流的video必须置为静音muted)否则会出现啸叫/回声等问题 Mac / iPhone / iPad 需要用js设置muted属性 autoplay必须为激活状态 playsinline保证在ios safari中不全屏播放 -- video idlocalVideo muted autoplay playsinline/video !-- 远端视频流 -- video idremoteVideo autoplay playsinline/video !-- 纯音频 -- !-- 本地音频流 / 这种场景下localaudio 其实没有播放的必要了可以用来调试 -- !-- audio idlocalAudioMedia muted autoplay/audio -- !-- 远端音频流 -- !-- audio idremoteAudioMedia autoplay /audio -- script srchttps://sqimg.qq.com/expert_qq/webrtc/3.0/WebRTCAPI.min.js/script /body H5 支持的平台 操作系统平台浏览器/webview版本要求备注iOSSafari ( 只支持Safari )11.1.2由于苹果 Safari 仍有偶现的 bug产品化方案建议先规避待苹果解决后再使用对于iOS可以考虑使用我们的小程序解决方案AndroidTBS 微信和手机QQ的默认Webview43600微信和手机QQ默认内置的浏览器内核为TBS。TBS 介绍AndroidChrome60需要支持 H264MacChrome47MacSafari11Windows(PC)Chrome52Windows(PC)QQ浏览器10.2 function checkTBSVersion(ua) { //ua Mozilla/5.0 (Linux; Android 7.1.1; vivo X9 Build/NMF26F; wv) //AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 //Mobile MQQBrowser/6.2 TBS/043501 Safari/537.36 //MicroMessenger/6.5.13.1100 NetType/WIFI Language/zh_CN;var list ua.split( );for (var i 0; i list.length; i) {var item list[i];if (item.indexOf(TBS) ! -1 || item.indexOf(tbs) ! -1) {var versionStr item.split(/)[1];var version parseInt(versionStr) || 0;if (version 43600) {alert(您的TBS版本号( versionStr )过低不支持WebRTC请升级!);}}} }
http://www.huolong8.cn/news/56365/

相关文章:

  • dede手机网站模板下载WordPress批量删除无用标签
  • 中企动力 网站价格免费搭建单页网站
  • 如何建设一个双语的网站烦恼可以做网站吗
  • 导购类网站模板wordpress首页缓存
  • 广西seo网站母婴类网站 网站建设方案书 备案
  • 苏州哪里做网站青岛济南网站建设优化
  • 建设银行官方网站手机版下载类似淘宝的网站怎么做的
  • 基因数据库网站建设seo工具不包括
  • 怎么样学做网站简单h5
  • 专业房地产网站建设网站建设月流量
  • 做外贸最好的网站建设如何在wordpress中添加背景音乐
  • 北京市住房城乡建设厅网站首页百度知道推广软件
  • 龙岩网站推广h5响应式网站源码下载
  • 石家庄最新大事建设和优化网站的步骤
  • 椒江做网站wordpress编辑器不行
  • 锒川市住房和城乡建设局网站公告oppo网站开发设计
  • 商家产品展示网站源码加拿大购物网站排名
  • 网站访客qq号码获取企业网站建设和运营
  • 网站建设公司十大部门门户网站建设请示
  • 建设工程施工许可证在哪个网站办python做一个简单的网页
  • 怎么找人帮做网站WordPress 布局 企业网站
  • 网站注册备案之后怎么做网站php怎么创建网站
  • 响应式网站开发pdf网站备案查询姓名
  • 海珠区pc端网站建设如何做网站拓扑结构图
  • 免费推广店铺的网站怎么用源码搭建网站
  • 潍坊中脉网站建设广东网站建设系统
  • 用wordpress建立的网站网络推广方式有哪几种
  • 英语教学网站建设意见项目营销推广方案
  • 长沙的网站建设公司哪家好搜索更多网页内容
  • dw做网站是静态还是动态凡科网站建设网页怎么建