暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

音视频资源保护|WebRTC服务端点对点播放媒体资源

技术源share 2023-02-24
462

概述

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

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

演示

GIF-1677224665612.gif

本文则选择两种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','')
},

复制

完整源码

源码地址[1]

最后

  • 如果有问题请留言,大家一起探讨。
  • WebRTC 实现网页会议直播系统系列文章小册[2]

参考资料

[1]

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

[2]

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


文章转载自技术源share,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论