您现在的位置是:首页 >技术教程 >Android WebRTC+SRS/ZLM视频通话(5):Android使用WebRTC从SRS/ZLMediaKit拉流网站首页技术教程

Android WebRTC+SRS/ZLM视频通话(5):Android使用WebRTC从SRS/ZLMediaKit拉流

玉念聿辉 2024-06-17 00:01:02
简介Android WebRTC+SRS/ZLM视频通话(5):Android使用WebRTC从SRS/ZLMediaKit拉流

Android WebRTC+SRS/ZLM视频通话(5):Android使用WebRTC从SRS/ZLMediaKit拉流

来自奔三人员的焦虑日志

接着上一章内容,继续来记录Android是如何使用WebRTC从SRS/ZLMediaKit拉流播放。WebRTC是一种实现实时音视频通信的技术,而SRS(SRS Streaming Cluster)和ZLMediaKit则是两种常用的流媒体服务。 Android 平台上,可以使用 WebRTC 从 SRS/ZLMediaKit 中拉取流并进行播放。

WebRTC推拉流的区别

推流:WebRTC 中的推流通常指将本地音视频流发送到远端。推流涉及到本地设备上的音视频采集、编码、传输等过程。

拉流:WebRTC 中的拉流通常指从远端获取音视频流并进行播放。拉流涉及到 RTCPeerConnection 对象的建立和音视频解码等过程。拉流可以用于实现实时音视频播放、音视频录制等场景。

总的来说,WebRTC 的推拉流都是通过 PeerConnection 对象进行实现,具体的实现细节会有一些不同。其中推流涉及到本地设备的音视频采集和编码等过程,而拉流则是接收远端的音视频流进行解码和播放。

注意:这里暂不考虑NAT可穿透服务器,详情可了解STUN/TURN 服务器的搭建和使用

别看一堆文字就头大,实际上也就是两行代码的事,注意初始化时PeerConnection!!.addTransceiver方法的参数设置即可。去掉视频、音频采集等代码,剩下的就是交换sdp的API地址变一下就好。

		//拉流
        if (!isPublish) {
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
            )
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
            )
        }
        //推流
        else {
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY)
            )
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY)
            )
         }

这里我已经封装成工具类,详情看WebRTCUtil

页面代码

跟推流类似,我们新增一个预览页面、播放地址文本和一个开始拉流按钮,具体看下面代码:

					//拉流地址
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Box(
                            modifier = Modifier.weight(1f)
                        ) {
                            Text(text = playUrl.value)
                        }
                        Box(
                            modifier = Modifier
                                .clickable {
                                    doPlay()//开始播放
                                }
                                .width(80.dp)
                                .background(
                                    Color.White,
                                    RoundedCornerShape(5.dp)
                                )
                                .border(
                                    1.dp,
                                    Color(0xFF000000),
                                    shape = RoundedCornerShape(5.dp)
                                )
                                .padding(5.dp),
                            contentAlignment = Alignment.Center
                        ) {
                            Text(text = "拉流")
                        }
                    }
                    //拉流部分
                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(300.dp)
                    ) {
                        surfaceViewRenderer2 = mSurfaceViewRenderer2(mEglBase, webRtcUtil2!!)
                        AndroidView({ surfaceViewRenderer2!! }) { videoView ->
                            CoroutineScope(Dispatchers.Main).launch {
                                //根据视频大小缩放surfaceViewRenderer控件
                                var screenSize = "480-640"
                                var screenSizeD = 720 / 1280.0
                                val screenSizeS: Array<String> =
                                    screenSize.split("-").toTypedArray()
                                screenSizeD =
                                    screenSizeS[0].toInt() / (screenSizeS[1].toInt() * 1.0)
                                var finalScreenSizeD = screenSizeD
                                var vto = videoView.viewTreeObserver
                                vto.addOnPreDrawListener {
                                    var width: Int = videoView.measuredWidth
                                    var height: Int = (finalScreenSizeD * width).toInt()
                                    //获取到宽度和高度后,可用于计算
                                    var layoutParams = videoView.layoutParams
                                    layoutParams.height = height
                                    videoView.layoutParams = layoutParams
                                    true
                                }
                            }
                        }
                    }
@Composable
fun mSurfaceViewRenderer2(mEglBase: EglBase, webRtcUtil2: WebRTCUtil): SurfaceViewRenderer {
    val context = LocalContext.current
    val surfaceViewRenderer = remember {
        SurfaceViewRenderer(context).apply {
            id = R.id.surface_view_2
        }
    }
    //Makes MapView follow the lifecycle of this composable
    val lifecycleObserver = rememberMapLifecycleObserver(surfaceViewRenderer, mEglBase, webRtcUtil2)
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(lifecycle) {
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }
    return surfaceViewRenderer
}
	private var webRtcUtil2: WebRTCUtil? = null
    private var playUrl =
        mutableStateOf("https://192.168.1.172/index/api/webrtc?app=live&stream=test&type=play")
    private var surfaceViewRenderer2: SurfaceViewRenderer? = null
    /**
     * 开始播放
     */
    private fun doPlay() {
        if (webRtcUtil2 != null) {
            webRtcUtil2!!.destroy()
        }
        webRtcUtil2 = WebRTCUtil(this@MainActivity)
        webRtcUtil2!!.create(
            mEglBase,
            surfaceViewRenderer2,
            isPublish = false,
            isShowCamera = true,
            playUrl = playUrl.value,
            callBack = object : WebRTCUtil.WebRtcCallBack {
                override fun onSuccess() {}
                override fun onFail() {}
            })
    }

主要新增这几个,详情可到Gitee拉取完整Demo

运行效果

在这里插入图片描述

第五章到这里就结束了,下节继续记录Android如何往SRS推拉流,占用您的垃圾时间了,实在对不住

THE END


感谢查阅
玉念聿辉:编辑
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。