WebRTC服务端点对点播放媒体资源

在通常情况下,我们遇到视频或者音频播放均采用视频文件加载到前端,然后用对应的组件播放,如果涉及到付费视频则一般采用直播HLS拉流点播的方式;如果你听过WebRTC这项技术那么恭喜你视频点播方式又可以增加一种更加巧妙的方式了。如果你不了解 WebRTC 那么我简要的概括下:

WebRTC 是一种可以将不同客户端的媒体信息通过点对点的方式传输到对方客户端,一般使用场景:浏览器端的视频语音通话等。虽然始于 Web 端,但不仅仅局限于 Web 端,随着WebRTC这项技术的成熟,JavaPythongolangRust等均有对应的客户端API

WebRTC服务端点对点播放媒体资源

本文则选择两种WebRTC的客户端API去实现我们标题的目的:服务端资源点播。对WebRTC不熟悉的同学可以先去打打基础,学习下这门技术,对于前端后端而言均不难,很容易入手

首先我们来讲讲利用 WebRTC 点播资源的原理

  • 建立基础的WebRTC通信。即完成基础握手流程,建立 RTC 通信关联关系。
  • 服务端加载指定资源,并添加到 RTC 关联的媒体Track
  • 网页端监听远程的媒体信息,并在页面展示。
  • 点播完成后断开 RTC 连接,服务端移除对应的内存临时信息。对WebRTC比较熟悉的同学这里可能想到用 WebRTC的 DataChannel传输数据,不是不可以哈,但是我们本文利用现成的媒体轨道不是更方便。

实战

1.建立基础的WebRTC通信。即完成基础握手流程,建立 RTC 通信关联关系。

这里的代码为前端WebRTC的基础API,然后创建Offer SDP->添加到本地描述->SDP 发送到服务端->服务端收到后创建应答的信令即:answer SDP->返回给web客户端后,客户端将其添加到远程描述

async playerVideo(){
            //清除DOM种历史媒体信息
            this.clearMedia()
            let that = this
            if(!that.fileName){
                    return;
            }
            //建立WebRTC 核心关联实例
            pc = await new PeerConnection();
            // 这里重点,请注意 是发送 还是接收 还是双向的RTP数据包
            pc.addTransceiver("audio", {direction: "recvonly"});
            pc.addTransceiver("video", {direction: "recvonly"});
            //创建offer sdp基础信令添加本地描述
            await pc.setLocalDescription(await pc.createOffer())
            //监听收集的ICE候选信息
            pc.onicecandidate = (event) => {
              if (event.candidate) {
                // console.log("ICE 候选信息",event.candidate)
              } else {
                /* 在此次协商中,没有更多的候选了 */
                console.log("在此次协商中,没有更多的候选了")
              }
            }
            //监听ICE采集过程 采集完成后和服务端的WebRTC交换信令
            pc.onicegatheringstatechange = async ev => {
              let connection = ev.target;
              console.log(connection.iceGatheringState)
              switch(connection.iceGatheringState) {
                case "gathering":
                  /* 候选人收集已经开始 */
                  break;
                case "complete":
                  /* 候选人收集完成 */
                    var offer = pc.localDescription;
                    let sdp = offer.sdp
                    let type = offer.type
                    //和服务端交换信令的同时 将对应要点播的视频信息传过去
                    let result = await this.playVideosSdp(sdp,type,that.fileName)
                    //获取服务端的SDP信息
                    await pc.setRemoteDescription(new RTCSessionDescription({type: 'answer', sdp: result['sdp']}))
                    break;
              }
            }
            pc.ontrack = (evt) => {
                console.log("远程媒体",evt.track)
                that.setDomVideoTrick("localmeidastream",evt.track)
            }

2.服务端加载指定资源,并添加到 RTC 关联的媒体Track。

这里我们采用 Python 作为服务端,用到的WebRTC相关的库为 aiortc; 请注意代码中的注释,核心:pc.addTransceiver和pc.addTrack()

from aiortc import RTCPeerConnection,RTCIceCandidate, RTCSessionDescription
from aiortc.contrib.media import MediaPlayer, MediaRelay
from aiortc.rtcrtpsender import RTCRtpSender
from aiortc.contrib.signaling import BYE, add_signaling_arguments, create_signaling
from aiortc.contrib.media import MediaPlayer, MediaRelay
from aiortc.rtcrtpsender import RTCRtpSender
import os,uuid
ROOT = os.path.dirname(__file__)
pcs = set()


async def on_ice_candidate(candidate:RTCIceCandidate):
 print(f"ICE candidate:======================\n{candidate.to_sdp()}")

async def receiveOfferSdpPlayVideo(sdp,fileName):
 offer = RTCSessionDescription(sdp=sdp, type='offer')
 pc = RTCPeerConnection()

 # 重点配置哦 看这里
 pc.addTransceiver("audio", direction='sendonly');
 pc.addTransceiver("video", direction='sendonly');
 pcs.add(pc)


 pc.onicecandidate = on_ice_candidate
 @pc.on("icegatheringstatechange")
 async def on_icegatheringstatechange():
  print(f"ICE gathering state =========== {pc.iceGatheringState}")

 @pc.on("iceconnectionstatechange")
 async def on_iceconnectionstatechange():
  print(f"ICE connection state ========= {pc.iceConnectionState}")

    # 连接状态监听 如果失败或者断开则移除 对应的pc实例
 @pc.on("connectionstatechange")
 async def on_connectionstatechange():
  c_state = pc.connectionState
  print("Connection state is =========", c_state)
  if c_state == "failed":
   await pc.close()
   pcs.discard(pc)
  if c_state == "closed":
   pcs.discard(pc)

 # 这里就是加载本地视频的 你只需要告诉在服务端的哪个文件夹下 叫什么名字即可
 player = MediaPlayer(os.path.join(ROOT, "./media/"+fileName))
 audio_sender = pc.addTrack(player.audio)
 video_sender = pc.addTrack(player.video)
 # remote desc 这些流程就是WebRTC的基础流程
 await pc.setRemoteDescription(offer)
 answer = await pc.createAnswer()
 await pc.setLocalDescription(answer)
 return {"sdp": answer.sdp, "type": answer.type}

3.网页端监听远程的媒体信息,并在页面展示。

setDomVideoTrick(domId,trick){
    let video = document.getElementById(domId)
    let stream = video.srcObject
    if(stream){
        stream.addTrack(trick)
    }else {
        stream = new MediaStream()
        stream.addTrack(trick)
        video.srcObject = stream
        // video.controls = false;
        video.autoplay = true;
        video.muted = true
        video.setAttribute('playsinline','')
    }
    video.setAttribute('playsinline','')
},

完整源码地址:

源码地址: https://github.com/wangsrGit119/suke-webrtc-ext

参考资料:WebRTC 实现网页会议直播系统系列文章小册: https://juejin.cn/book/7168418382318927880

作者:suke | 来源:公众号——技术源share

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论