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

news2024/10/6 2:20:18

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


感谢查阅
玉念聿辉:编辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/514501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三分钟教你如何定义自己的ChatGPT

三分钟教你如何定义自己的ChatGPT 成品预览材料准备MyChatGPT自定义AI 成品预览 材料准备 总共有两种方式&#xff1a; 一种是使用自己的OpenAI账号&#xff0c;这种方式是可控性比较强&#xff0c;同时也会有很多问题&#xff0c;比如你需要准备国外的手机号和Visa卡&#x…

目标检测论文总结

文章目录 1.目标检测论文123456789101112131415161718192021**22****25**2627 总结改进思路 1.目标检测论文 EI https://www.engineeringvillage.com/search/quick.url 其他 A YOLOv3-based Deep Learning Application Research for Condition Monitoring of Rail Thermite …

极客公园对话 Zilliz 星爵:大模型时代,需要新的「存储基建」

大模型在以「日更」进展的同时&#xff0c;不知不觉也带来一股焦虑情绪&#xff1a;估值 130 亿美元的 AI 写作工具 Grammarly 在 ChatGPT 发布后网站用户直线下降&#xff1b;AI 聊天机器人独角兽公司 Character.AI 的自建大模型在 ChatGPT 进步之下&#xff0c;被质疑能否形成…

外观、装饰、策略模式代码详解-软件设计(七十二)

真题详解&#xff08;索引长度计算&#xff09;-软件设计&#xff08;七十一)https://blog.csdn.net/ke1ying/article/details/130590260 外观模式 解析&#xff1a; public String getName()public void dispose(Patient patient)new ConcreteOatient(“name”)Facadenew Fa…

UPF问题解决

UPF配置文件内容解析 NWI Network Instance of the interface 结果调查&#xff0c;对upf网元配置文件进行了如下修改 将 - IF_2_NWIaccess.oai.org改为 - IF_2_NWIaccess3.oai.org 将 - IF_2_NWIaccess.oai.org改为 - IF_2_NWIaccess3.oai.org 然后两核心网接入了两个基站启…

Apache Doris 2.0 冷热分离快速体验

概述 对于任何一种数据库类软件来说&#xff0c;无论其基于传统数据库模型还是基于分布式结构&#xff0c;作为核心的永远是数据本身。而数据的生命周期&#xff0c;则体现在CRUD操作&#xff08;创建、查询、更新、删除&#xff09;上。任何一条数据从其生成的时刻开始&#…

程序设计语言与语言处理程序基础

目录 第七章、程序设计语言与语言处理程序基础1、编译与解释2、文法3、正规式 4、有限自动机5、表达式 6、传值与传址 7、多种程序语言特点 第七章、程序设计语言与语言处理程序基础 1、编译与解释 编译器是将整个高级语言程序一次性转化成目标机器的机器代码&#xff0c;编译…

Xcode安装ipa

iOS APP上架App Store其中一个步骤就是要把ipa文件上传到App Store&#xff01;​ 下面进行步骤介绍&#xff01;​ 利用Appuploader这个软件&#xff0c;可以在Windows、Linux或Mac系统中申请ios和上传IPA到App Store Connect。​ 非常的方便&#xff0c;没有Mac也可以用Ap…

手游反外挂方案解析

据中国音数协游戏工委发布的《2023年1—3月游戏产业报告》显示&#xff0c;2023年1—3月&#xff0c;中国移动游戏市场实际销售收入为486.94亿元&#xff0c;占游戏市场整体实际销售收入的72.12% &#xff0c;虽然数据同比去年略有下滑&#xff0c;但足以证明&#xff0c;移动游…

【逆向】动态链接库

文章目录 动态链接库1. 动态链接库的定义2. 动态库的由来&#xff1a;3. Dll与ExE程序区别4. DLL导出5. DLL导入6. 静态库 动态链接库 1. 动态链接库的定义 动态链接库英文DLL&#xff0c;是Dynamic Link Libarary的缩写。Dll中包含若干公用的代码、数据等&#xff0c;可供其他…

如何搭建在线产品手册

在现代社会&#xff0c;随着科技的发展&#xff0c;越来越多的企业将目光投向互联网&#xff0c;并将自己的产品推向了线上。而对于这些线上产品&#xff0c;拥有一份完备的、易用、高质量的在线产品手册显得尤为重要。 那么如何才能搭建一份高质量且易用的在线产品手册呢&…

《Android性能优化》学习笔记—启动优化

为什么要做App的启动优化&#xff1f; 网页端存在的一个定律叫8秒定律&#xff1a;即指用户访问一个网站时&#xff0c;如果等待打开的时间超过8秒&#xff0c;超过70%的用户将会放弃等待。 同样的&#xff0c;移动端也有一个8秒定律&#xff1a;如果一个App的启动时间超过8秒…

UNIAPP实战项目笔记66 当前用户更改购物车商品数量的前端和后端交互

UNIAPP实战项目笔记66 当前用户更改购物车商品数量的前端和后端交互 思路 前端改变数量的时候将数据发送到后端 后端接收到数据后更改数据库中的数据 案例截图 代码 前端代码 cart.js export default{state:{list:[/* {id:1,name:"332经济法能聚聚会技能大赛 经济法能聚…

vmware15+ubuntu+AS

一、VMware Workstation 与 Device/Credential Guard 不兼容 安装VMware15后&#xff0c;在运行启动ubuntu时一直提示与Device/Credential Guard不兼容 1、WINR打开运行&#xff0c;输入services.msc&#xff1b; 2、服务中找 HV主机服务&#xff0c;双击打开设置改为禁用&am…

【Python入门篇】——Python中判断语句(if elif else语句,判断语句的嵌套与实战案例)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

Day3--C高级3

一.编写一个名为myfirstshell.sh的脚本&#xff0c;它包括以下内容。 1、包含一段注释&#xff0c;列出您的姓名、脚本的名称和编写这个脚本的目的 2、和当前用户说“hello 用户名” 3、显示您的机器名 hostname 4、显示上一级目录中的所有文件的列表 5、显示变量PATH和HO…

【云原生】Kubernetes二进制--多节点Master集群高可用

多节点Master集群高可用 一、Kubernetes多Master集群高可用方案1、实现高可用方法2、多节点Master高可用的部署 二、多节点Master部署1、配置master022、修改配置文件kube-apiserver中的IP3、在 master02 节点上启动各服务并设置开机自启 三、负载均衡部署1、配置nginx的官方在…

Google I/O 2023 大会上发布了一些令人兴奋的技术和产品,让我们一起来看看吧!

文章目录 Google I/O 2023 的主要内容- **Android 14**&#xff1a;- **Google Pixel 7**&#xff1a;- **Google Assistant**&#xff1a;- **Google Lens**&#xff1a;- **Google Cloud**&#xff1a; Google I/O 2023 大会四大主题 回顾&#xff1a;跨移动、网络、AI 和云A…

以太坊钱包私钥爆破产业链和攻击案例

一:产业链频道&#xff1a;小飞机搜索"BRUTE_FORCE_CRYPTO_WALLET" 2、github项目(有成熟的工具)GitHub - Houzich/CUDA-GPU-Brute-Force-Mnemonic-Old-Electrum-V1: CUDA-GPU-Brute-Force-Mnemonic-Old-Electrum-V1 3、揭秘以太坊 Vanity 生成器 Profanity 私钥破解…

C++学习路线-自用

C学习路线 做目录索引用&#xff0c;后续更新 初步想在学习完成后做对应的link 1、summary 参考网址 https://mp.weixin.qq.com/s/tXilzUzN7cDhnc3ztw4Vlw https://blog.csdn.net/qq_43564374/article/details/109409256 https://zhuanlan.zhihu.com/p/130364187 学习方式 看书…