由于手机端浏览器播放云游戏流效果不太理想,所以考虑开发专有手机端,目前两个想法,第一用私有协议实现视频传输推流,第二基于android WebRTC。第二由我来负责,特此把开发学习过程中写的小demo记录下。
参考链接:https://www.jianshu.com/p/eb5fd116e6c8
原文链接:https://blog.csdn.net/weixin_44259356/article/details/101449173
基本思路和参考链接想法一样,只是语言换成了kotlion,有部分的函数调用略有区别,使用摄像头采集流显示在首栏,然后推流到第二栏,核心代码如下:
val eglBaseContext = EglBase.create().eglBaseContext
// create PeerConnectionFactory
PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions
.builder(this)
.createInitializationOptions()
)
val options = PeerConnectionFactory.Options()
val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(eglBaseContext, true, true)
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options)
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory()
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext)
// create VideoCapturer
val videoCapturer = createCameraCapturer(true)
val videoSource = peerConnectionFactory.createVideoSource(videoCapturer!!.isScreencast)
videoCapturer.initialize(
surfaceTextureHelper,
applicationContext,
videoSource.capturerObserver
)
videoCapturer.startCapture(480, 640, 30)
localView.setMirror(true)
localView.init(eglBaseContext, null)
// create VideoTrack
val videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource)
// // display in localView
videoTrack.addSink(localView);
val remoteSurfaceTextureHelper =
SurfaceTextureHelper.create("RemoteCaptureThread", eglBaseContext)
// create VideoCapturer
val remoteVideoCapturer = createCameraCapturer(false)
val remoteVideoSource =
peerConnectionFactory.createVideoSource(remoteVideoCapturer!!.isScreencast)
remoteVideoCapturer.initialize(
remoteSurfaceTextureHelper,
applicationContext, remoteVideoSource.capturerObserver
)
remoteVideoCapturer.startCapture(480, 640, 30)
remoteView.setMirror(false)
remoteView.init(eglBaseContext, null)
// create VideoTrack
val remoteVideoTrack = peerConnectionFactory.createVideoTrack("102", remoteVideoSource)
// // display in remoteView
// remoteVideoTrack.addSink(remoteView);
mediaStreamLocal = peerConnectionFactory.createLocalMediaStream("mediaStreamLocal")
mediaStreamLocal.addTrack(videoTrack)
mediaStreamRemote = peerConnectionFactory.createLocalMediaStream("mediaStreamRemote")
mediaStreamRemote.addTrack(remoteVideoTrack)
call(mediaStreamLocal, mediaStreamRemote)
}
private fun call(localMediaStream: MediaStream, remoteMediaStream: MediaStream) {
val iceServers = ArrayList<PeerConnection.IceServer>()
peerConnectionLocal= peerConnectionFactory.createPeerConnection(
iceServers,
object : PeerConnectionAdapter("localconnection") {
override fun onIceCandidate(iceCandidate: IceCandidate) {
super.onIceCandidate(iceCandidate)
peerConnectionRemote.addIceCandidate(iceCandidate)
}
override fun onAddStream(mediaStream: MediaStream) {
super.onAddStream(mediaStream)
val remoteVideoTrack = mediaStream.videoTracks[0]
runOnUiThread {
// remoteVideoTrack.addSink(localView)
}
}
})!!
peerConnectionRemote = peerConnectionFactory.createPeerConnection(
iceServers,
object : PeerConnectionAdapter("remoteconnection") {
override fun onIceCandidate(iceCandidate: IceCandidate) {
super.onIceCandidate(iceCandidate)
peerConnectionLocal.addIceCandidate(iceCandidate)
}
override fun onAddStream(mediaStream: MediaStream) {
super.onAddStream(mediaStream)
val localVideoTrack = mediaStream.videoTracks[0]
runOnUiThread {
localVideoTrack.addSink(remoteView)
}
}
})!!
peerConnectionLocal.addStream(localMediaStream)
peerConnectionLocal.createOffer(object : SdpAdapter("local offer sdp") {
override fun onCreateSuccess(sessionDescription: SessionDescription) {
super.onCreateSuccess(sessionDescription)
// todo crashed here
peerConnectionLocal.setLocalDescription(
SdpAdapter("local set local"),
sessionDescription
)
peerConnectionRemote.addStream(remoteMediaStream)
peerConnectionRemote.setRemoteDescription(
SdpAdapter("remote set remote"),
sessionDescription
)
peerConnectionRemote.createAnswer(object : SdpAdapter("remote answer sdp") {
override fun onCreateSuccess(sdp: SessionDescription) {
super.onCreateSuccess(sdp)
peerConnectionRemote.setLocalDescription(
SdpAdapter("remote set local"),
sdp
)
peerConnectionLocal.setRemoteDescription(
SdpAdapter("local set remote"),
sdp
)
}
}, MediaConstraints())
}
}, MediaConstraints())
}
package com.example.myapplication
import android.util.Log
import org.webrtc.*
open class PeerConnectionAdapter(tag: String) : PeerConnection.Observer {
private val tag: String = "chao $tag"
private fun log(s: String) {
Log.d(tag, s)
}
override fun onSignalingChange(signalingState: PeerConnection.SignalingState) {
log("onSignalingChange $signalingState")
}
override fun onIceConnectionChange(iceConnectionState: PeerConnection.IceConnectionState) {
log("onIceConnectionChange $iceConnectionState")
}
override fun onIceConnectionReceivingChange(b: Boolean) {
log("onIceConnectionReceivingChange $b")
}
override fun onIceGatheringChange(iceGatheringState: PeerConnection.IceGatheringState) {
log("onIceGatheringChange $iceGatheringState")
}
override fun onIceCandidate(iceCandidate: IceCandidate) {
log("onIceCandidate $iceCandidate")
}
override fun onIceCandidatesRemoved(iceCandidates: Array<IceCandidate>) {
log("onIceCandidatesRemoved $iceCandidates")
}
override fun onAddStream(mediaStream: MediaStream) {
log("onAddStream $mediaStream")
}
override fun onRemoveStream(mediaStream: MediaStream) {
log("onRemoveStream $mediaStream")
}
override fun onDataChannel(dataChannel: DataChannel) {
log("onDataChannel $dataChannel")
}
override fun onRenegotiationNeeded() {
log("onRenegotiationNeeded ")
}
override fun onAddTrack(rtpReceiver: RtpReceiver, mediaStreams: Array<MediaStream>) {
log("onAddTrack $mediaStreams")
}
}
package com.example.myapplication
import android.util.Log
import org.webrtc.SdpObserver
import org.webrtc.SessionDescription
open class SdpAdapter(tag: String) : SdpObserver {
private val tag: String = "chao $tag"
private fun log(s: String) {
Log.d(tag, s)
}
override fun onCreateSuccess(sessionDescription: SessionDescription) {
log("onCreateSuccess $sessionDescription")
}
override fun onSetSuccess() {
log("onSetSuccess ")
}
override fun onCreateFailure(s: String) {
log("onCreateFailure $s")
}
override fun onSetFailure(s: String) {
log("onSetFailure $s")
}
}