天天看点

基于WebRTC搭建直播系统源码

直播系统源码可以说是近年来最火的互联网项目,各大直播系统源码如雨后春笋般先后兴起,转眼间主播这一行业也成为最赚钱的代名词。那我们就来从0开始搭建一个直播系统源码吧。

WebRTC

WebRTC,名称源自网页实时通信(Web Real-Time Communication)。是一个支持网页浏览器进行实时语音对话或视频对话的技术,谷歌于2010年收购获得。2011年5月开放了工程的源代码,成为下一代视频通话的标准。

优点

WebRTC作为一个面向网页浏览器的实时语音视频技术,主要有以下几个优点:

具有良好的通用性,几乎在任何平台都可以正常使用。

其使用的Interactive Connectivity Establishment(ICE)能让各个设备之间自动匹配当前最好的通讯方式,这是很多别的技术都不具备的。

具备全双工的能力,即双向通讯(P2P),不仅可作为单向直播使用还能完成电子视频会议的双向音视频对话。

为Google旗下,具有良好的发展前景,最重要的:开源。

开始使用

导包

这里没有直接使用官方的原生库进行编译,因为太麻烦了,网上已经有组织提供了编译好的版本可以供我们直接使用,对库的提供者不是很了解,不知道是属于什么性质的,但是几乎市面上所有的Android端都是采用该库所以应该没有什么问题。

快速开始

以下步骤都是经过分类和优化后整理出来的,加上注释,大多都能读懂,因此就不详细解释了。如有不清楚的地方可以留言问我。

初始化,Peer连接工厂类:

//初始化 关键
PeerConnectionFactory.initializeAndroidGlobals(context, true, true, true)
factory = PeerConnectionFactory()
           

获得Media,简单理解就是需要传输的音视频流:

//获取视频源
val videoCapture = VideoCapturerAndroid.create(CameraEnumerationAndroid.getNameOfBackFacingDevice())
val videoSource = factory.createVideoSource(videoCapture, MediaConstraints())
//获取音频源
val audioSource = factory.createAudioSource(MediaConstraints())
//获取封装MediaTrack
val videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource)
val audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource)
//封装媒体流
localMs = factory.createLocalMediaStream("ARDAMS")
localMs?.addTrack(videoTrack)
localMs?.addTrack(audioTrack)
//预览
preview?.invoke(localMs!!)
           

配置ICE,进行网络连接:

//Ice NAT穿透
val iceList = ArrayList<PeerConnection.IceServer>()
iceList.add(PeerConnection.IceServer("stun:23.21.150.121"))
iceList.add(PeerConnection.IceServer("stun:stun.l.google.com:19302"))
//媒体限制
val constraints = MediaConstraints()
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
constraints.optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
pc = factory.createPeerConnection(iceList, constraints, Observer())
pc?.addStream(localMs)
           

这里使用的是stun方式,ICE逻辑判断逻辑很复杂,主要分为两种方式:STUN、TURN。

STUN是将本地地址转换成外部可访问的公网地址,说白了有点像内网穿透的意思。

TURN则是需要自己搭建,由公共的中介服务器分配地址。

创建Offer:

WebRTC模型中Peer之间需要进行信令交换,而承载信令的载体就是Offer/Answer,这里因为是实现的直播系统源码推流端,因此创建的是Offer,如果是观看端则需要创建Answer。

信令交换:

private inner class Observer : SdpObserver, PeerConnection.Observer {
//创建offer成功
  override fun onCreateSuccess(p0: SessionDescription?) {
    log("offer:${p0?.description}")
    pc?.setLocalDescription(Observer(), p0)
    val jsonObject = JSONObject()
    jsonObject.put("id", "presenter")
    jsonObject.put("sdpOffer", p0?.description)
    SocketUtils.sendMsg(jsonObject.toString())
    log("json:$jsonObject")
  }

//创建offer失败
  override fun onCreateFailure(p0: String?) {
    log("error: create offer $p0")
  }

//当网络可用即Ice穿透成功
  override fun onIceCandidate(p0: IceCandidate?) {
    val jsonObject = JSONObject()
    jsonObject.put("id", "onIceCandidate")
    val ice = JSONObject()
    ice.put("sdpMid", p0?.sdpMid)
    ice.put("sdpMLineIndex", p0?.sdpMLineIndex)
    ice.put("usernameFragment", p0?.sdp?.substringAfter("ufrag ")?.substring(0, 4))
    ice.put("candidate", p0?.sdp)
    jsonObject.put("candidate", ice)
    SocketUtils.sendMsg(jsonObject.toString())
    log("iceCandidate:$p0")
    log("json:$jsonObject")
  }
}
           

通过Socket获得:

private fun setRemoteSdp(sdp: String) {
      val answer = SessionDescription(SessionDescription.Type.ANSWER, sdp)
      pc?.setRemoteDescription(Observer(), answer)
  }

  private fun setRemoteIce(ice: EventIceCandidate) {
      ice.candidate?.let {
          pc?.addIceCandidate(IceCandidate(
                  it.sdpMid,
                  it.sdpMLineIndex,
                  it.candidate
          ))
      }
  }
           

显示画面

gl_view.preserveEGLContextOnPause = true
  gl_view.keepScreenOn = true
  VideoRendererGui.setView(gl_view) {
      RtcClient(this).preview = { localMs ->
          // local and remote render
          val localRender = VideoRendererGui.createGui(
                  LOCAL_X_CONNECTED, LOCAL_Y_CONNECTED,
                  LOCAL_WIDTH_CONNECTED, LOCAL_HEIGHT_CONNECTED,
                  RendererCommon.ScalingType.SCALE_ASPECT_FILL,
                  false)
          localMs.videoTracks[0].addRenderer(localRender)
      }
  }
           

主要流程

直播端

初始化,Peer连接工厂类。

获取Media,创建GL渲染器,进入显示就绪状态。

配置ICE并监听,当Candidate收集完成发送给远程端。

创建Offer,当Offer创建完成发送给远程端。

接收远程端发送的ICE信息,并设置给PeerConnection对象。

接收远程端返回的Answer,并设置给PeerConnection对象。

建立连接,开始直播。

观看端

初始化,Peer连接工厂类。

获取Media,创建GL渲染器,进入显示就绪状态。

配置ICE并监听,当Candidate收集完成发送给远程端。

接收远程端发送的ICE信息,并设置给PeerConnection对象。

接收远程端发送的Offer,并设置给PeerConnection对象。

创建Answer,当Answer创建完成发送给远程端。

建立连接,开始直播。

以上就是基于WebRTC搭建直播系统源码的全部内容了。

继续阅读