Android技术分享| 实现视频连麦直播
视频连麦产品端核心步骤分析
- 游客申请连麦/取消申请
- 主播同意/拒绝申请
- 音视频发布取消
- 支持很多观众观看
- 支持多人连麦
- 低延时
- IM 弹幕
视频连麦技术端调研
emmm,大致可以分为视频采集、编码,传输,解码,渲染%¥#@¥%…………%……¥……%¥%……¥%%……%#¥%#%¥&%&……%……&%……¥?卒 
所以经过调研,发现市场上已经有很多成熟的商业解决方案,并且成本很低就可以接入使用。
对比了很久,最后采用 anyRTC 公司的两个 SDK 来完成视频连麦中的音视频通许和信令传输。
信令交互

首先利用 RTM SDK,主播和游客端都分别加入同一个频道,这样就能实现频道内的成员心令交互。
实现申请连麦/取消申请连麦的话,只需要和主播端约定好信令格式即可,代码如下。
游客
//申请连麦
fun applyLine() {
RtmManager.instance.sendPeerMessage(hostId,
"{\"cmd\": \"apply\", \"avatar\": \"$userAvatar\", \"userName\": \"$userNickname\"}"
)
}
//取消申请连麦
fun cancelApply() {
RtmManager.instance.sendPeerMessage( hostId,"{\"cmd\": \"cancelApply\"}"
)
}
//收到信令回调通知
override fun onP2PMessageReceived(var1: RtmMessage?, var2: String?) {
val params = JSONObject(var1.text)
when (params.get("cmd")) {
"acceptLine" -> {
//主播同意
}
"rejectLine" -> {
//主播拒绝
}
}
}
主播
//拒绝游客连麦
fun rejectLine(uid: String) {
RtmManager.instance.sendPeerMessage(uid, "{\"cmd\": \"rejectLine\"}"
)
}
//同意主播连麦
fun acceptLine(uid: String) {
RtmManager.instance.sendPeerMessage(uid, "{\"cmd\": \"acceptLine\"}"
)
}
override fun onP2PMessageReceived(var1: RtmMessage?, var2: String?) {
val params = JSONObject(var1.text)
when (params.get("cmd")) {
"apply" -> {
//收到游客申请连麦
}
"cancelApply" -> {
//游客取消申请连麦
}
}
}
以上,就完成了最简单的信令交互,实际业务场景中,会有更多更复杂的,只需商定好,通过 RTM 发送交互就行。
音视频推流

游客只观看主播的时候,是不需要发布音视频的,所以在加入房间后,将自己的身份设置为游客(CLIENT_ROLE_AUDIENCE)即可。申请连麦并且主播同意后,再将身份设置为(CLIENT_ROLE_BROADCASTER)就会自动发布音视频。这样就能与直播互动了。
anyRTC 音视频 SDK 有2种直播模式,分别如下:
1.RTC
RTC(Real Time Communication)实时音视频通信,最大的特点就是延迟极低,基本都是基于 WebRTC 标准,使用私有协议进行推流的。
主播
进入视频页面,打开自己的视频并加入房间。
//初始化 SDK 引擎
RtcManager.instance.initRtc(ctx)
//打开视频模块
RtcManager.instance.enableVideo()
//打开本地摄像头设置视图显示
RtcManager.instance.setupLocalVideo(VideoCanvas(textureView))
//加入房间
RtcManager.instance.joinChannel(rtcToken, roomId, userId, isHost)
加入房间成功之后,如果有人申请连麦并上麦后,会收到 onUserJoin 回调,只需要在合适的回调中显示移除上麦人的视图即可。
//在这显示上麦的人的视频
override fun onUserJoined(uid: String?, elapsed: Int) {
// add remote user
}
//移除他人视频
override fun onUserOffline(uid: String?, reason: Int) {
//remove remote user
}
游客
主播同意上麦申请后,应将自己的身份设置为 CLIENT_ROLE_BROADCASTER,并将自己的视频模块打开并添加到视图中。
//转换身份
RtcManager.instance.setClientRole(Constants.CLIENT_ROLE_BROADCASTER)
//添加自己的视频
val clientTexture = RtcEngine.CreateRendererView(this)
addVideoView(
binding.rlHostView,
clientTexture,
yourself = true,
setupLocalVideo = true
)
binding.apply.text = "下麦"
使用 RTC 模式实现非常的简单,只需要关注几个重要的回调即可。
2.RTMP+CDN
RTMP (Real Time Messaging Protocol)是基于 TCP 的流媒体传输协议,最大的特点是与 CDN 的强绑定,需要借助 CDN 的负载均衡系统将内容推送到接近用户的边缘节点,使用户就近取得所需内容,提高用户访问的响应速度和成功率,解决因分布、带宽、服务器性能带来的访问延迟问题。更多适用于站点加速、点播、短视频等场景。
使用该模式比 RTC 模式代码要写的多一点,因为主播短要设置上麦人的视频合流参数,观众需要添加一个 anyRTC 内置的播放器用来拉 rtmp 流。
rtmp会比RTC 延时要高一些,但是连麦不会,因为连麦后还是走的 RTC 模式,因此,连麦的人和主播之间的延时会很小。其他观众拉流的话,并不会察觉到主播和上麦者之间有什么延迟。
主播推流到 CDN
//加入频道成功
override fun joinChannelSuccess() {
//设置合流参数
publishPushLiveTranscoding()
//添加推流地址
RtcManager.instance.addPublishStreamUrl(cdnUrl, true)
}
private fun publishPushLiveTranscoding(
transcodingArr: List<LiveTranscoding.TranscodingUser>,
canvasInfo: CDNStreamLayoutInfo
) {
rtcEngine?.setLiveTranscoding(LiveTranscoding().apply {
width = canvasInfo.width
height = canvasInfo.height
}.apply { transcodingArr.forEach { addUser(it) } })
}
//游客上麦
override fun onUserJoin(uid: String) {
//更新合流布局
updatePushLiveTranscoding()
}
//游客下麦
override fun onUserOffline(uid: String) {
//更新合流布局
updatePushLiveTranscoding()
}
这里需要注意的是合流布局参数的设置,具体可以查看文末 github仓库代码。
游客拉流播放
//创建播放器并播放主播的流
RtcManager.instance.initMediaPlayer().apply {
open(cdnUrl, 0)
setView(textureView)
play()
}
游客上下麦还是和上麦的流程一致,变的只是界面上的效果。具体可以参考Demo
效果图展示

Demo 代码地址
Android技术分享| 实现视频连麦直播的更多相关文章
- fir.im Weekly - 8 个不能错过的 iOS / Android 技术分享
本期 fir.im Weekly 收集了 2 月下旬新鲜出炉的 iOS /Android 技术分享.源码等,iOS 中图片技术的解压缩.逆向实战.iOS SDK 实践,Android架构思考.Andr ...
- Android技术分享-文字转语音并朗读
Android技术分享-文字转语音并朗读 最近在做一个项目,其中有一个功能是需要将文本转换成语音并播放出来.下面我将我的做法分享一下. 非常令人开心的是,Android系统目前已经集成了TTS,提供了 ...
- Android技术分享收集
Android高工必备技能! 我的 Android 开发实战经验总结 微信Android客户端架构演进之路 微信Android版智能心跳方案 流量优化: WebP 探寻之路 HTTP 协议缓存机制详解 ...
- Android中直播视频技术探究之---视频直播服务端环境搭建(Nginx+RTMP)
一.前言 前面介绍了Android中视频直播中的一个重要类ByteBuffer,不了解的同学可以 点击查看 到这里开始,我们开始动手开发了,因为我们后续肯定是需要直播视频功能,然后把视频推流到服务端, ...
- 从无到有开发连麦直播技术<转>
转贴地址:http://blog.csdn.net/heisedelangzi/article/details/52400333 从无到有开发连麦直播技术点整理-AnyRTC 直播关键字 采集.前处理 ...
- 腾讯技术分享:微信小程序音视频技术背后的故事
1.引言 微信小程序自2017年1月9日正式对外公布以来,越来越受到关注和重视,小程序上的各种技术体验也越来越丰富.而音视频作为高速移动网络时代下增长最快的应用形式之一,在微信小程序中也当然不能错过. ...
- 腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践
1.概述 本文来自腾讯视频云终端技术总监rexchang(常青)技术分享,内容分别介绍了微信小程序视音视频和WebRTC的技术特征.差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebR ...
- UWP 手绘视频创作工具技术分享系列 - Ink & Surface Dial
本篇作为技术分享系列的第四篇,详细讲一下手绘视频中 Surface Pen 和 Surface Dial 的使用场景. 先放一张微软官方商城的图,Surface 的使用中结合了 Surface Pen ...
- UWP 手绘视频创作工具技术分享系列 - 手绘视频与视频的结合
本篇作为技术分享系列的第三篇,详细讲一下手绘视频中结合视频的处理方式. 随着近几年短视频和直播行业的兴起,视频成为了人们表达情绪和交流的一种重要方式,人们对于视频的创作.编辑和分享有了更多的需求.而视 ...
随机推荐
- Map类型的Json格式
示例代码: Map<String, Object> map = new HashMap<>();// boolean 类型 map.put("boolean" ...
- 解析 Nebula Graph 子图设计及实践
本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践. 前言 在先前的 Query Engine 源码解析中,我们介绍了 2.0 ...
- 【.NET 与树莓派】TM1638 模块的按键扫描
上一篇水文中,老周马马虎虎地介绍 TM1638 的数码管驱动,这个模块除了驱动 LED 数码管,还有一个功能:按键扫描.记得前面的水文中老周写过一个 16 个按键的模块.那个是我们自己写代码去完成键扫 ...
- jquery combo.select. 下拉选择插件
演示地址:http://www.dowebok.com/demo/179/index5.html 引入js.css 即可使用,效果如图所示: 这个插件的好处是可以在输入框里面输入数据 自动检索内容. ...
- Redis启动正常,一段时间后报错,连不上redis
Redis报错 1.redis在最终目标上移动临时数据库文件时出错 错误:redis:Error moving temp DB file temp-13792.rdb on the final des ...
- 【译】GO语言:管理多个错误
原文:https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1 关于开发者使用Go遇到的最大挑 ...
- XML:使用cxf调用WebService接口时报错:编码GBK的不可映射字符(设置UTF-8字符集)
调用代码如下 JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); Client client = dcf. ...
- SpringCloud的Ribbon自定义负载均衡算法
1.Ribbon默认使用RoundRobinRule策略轮询选择server 策略名 策略声明 策略描述 实现说明 BestAvailableRule public class BestAvailab ...
- 【转载】每天一个linux命令(11):nl命令
转载至:http://www.cnblogs.com/peida/archive/2012/11/01/2749048.html nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内 ...
- 你会用哪些JavaScript循环遍历
总结JavaScript中的循环遍历定义一个数组和对象 const arr = ['a', 'b', 'c', 'd', 'e', 'f']; const obj = { a: 1, b: 2, c: ...