本文可以了解到
移动端视频直播相关的基础知识,以及如何利用腾讯云直播 SDK 搭建自己的直播系统。
前言
1. 视频时代已经来临
当今的互联网,视频已经成为一股洪流,冲刷着每一个人。
2020 年,由于新型冠状病毒疫情的爆发,视频直播互动更是一飞冲天,在网购、游戏、教育、金融等等方面都呈现爆发式发展。
可见音视频相关的技术,已经是我们不得不去了解的内容了。
2. 视频开发包括哪些内容
移动端的音视频开发一般有:短视频和视频直播互动。
在本人的【Android 音视频开发打怪升级】系列文章中,主要讲解的就是「短视频开发」相关的知识。
而「直播」涉及到的技术,更加庞大,可以说是「短视频开发」的一个超集,因为它不仅涉及到本地视频的编辑,还涉及到直播服务器的架设,以及面对不稳定网络的优化等等,搭建一个直播系统并非易事。
那么你肯定要问了,既然很难,那我直接用第三方的 SDK
就可以了,干嘛还要学习这些晦涩难懂的视频知识?
3. 为什么学习直播基础知识
如果不了解基本知识,直接接入第三方的 SDK
可以吗?当然可以!
但俗话说得好:工欲善其事,必先利其器!
如果不了解其中原理,在未来遇到问题时,要么难以有效地解决,要么需要付出比现在成倍的努力。
一、直播基础知识
最原始的直播系统其实并没有想象的那么复杂,无非就是主播端将音视频数据推送到服务器,观众端则从服务器拉取数据播放。
1. 基础流程
通过下面这个数据流程图,能清晰地看到整个直播的过程。
最简单的直播系统
可以看到,「主播客户端」处理的事情,其实就是短视频开发中最重要的内容:
流程 | 详细操作 |
---|---|
音视频数据采集 | 通过摄像头和麦克风采集 |
音视频滤镜 | 通过 OpenGL 和 SoundTouch 等工具实现音视频编辑 |
音视频编码 | 通过系统硬编码 或 FFmpeg 软编码,将数据编码为 H264 和 AAC |
数据封装打包 | 将编码好的数据封装成指定的格式 |
唯一不一样的地方,短视频会将封装好的数据保存到本地,直播则是通过 推流协议
将数据推送到服务器。
关于
H264
、AAC
、封装格式
等知识,不清楚的可以查看这篇文章【音视频基础知识】。
- 推流
推流,是直播中的一个术语,意思是将流媒体数据推送到服务器。如何推流,关键就在于使用的推流协议。
- 拉流
拉流,指的是「观众端」流媒体数据的拉取,同样也需要通过约定的拉流协议来拉取。
2. 直播协议
直播协议包含了上面提到的 「推流」和「拉流」协议。
主要有一下三种:
- RTMP
RTMP 全名:Real Time Messaging Protocol,实时消息传送协议。是 Adobe 公司开发的,用于 Flash Player 和服务器间之间传输音视频数据。RTMP
是基于 TCP
开发的,属于应用层的协议,默认端口为 1935
。
RTMP 主要特点是实时性好,延时比较低(1~3s)。既可以用来推流,也可以用来拉流。
但是由于其基于 TCP
长链接协议,默认端口非公共端口,可能会被防火墙拦截。
RTMP 视频数据封装格式为 flv
。
- HTTP-FLV
HTTP-FLV,从其命名可以大概看出工作模式。即:将音视频数据封装为 flv
格式,通过 HTTP
长链接协议传输。既然是基于 HTTP
,其默认端口就是 80
,可以直接穿透防火墙。
其传输方式和 RTMP
一样,只是将协议更换为 HTTP
,所以实时性也比较好。
由于 HTTP-FLV
的特点,非常适合用于 App
直播拉流。
- HLS
HLS:HTTP Live Streaming。是苹果公司推出的基于 HTTP
的流媒体传输协议,视频封装格式是 ts
,在 iOS 和 Mac 支持比较好。
与 RTMP
和 HTTP-FLV
不同的是,HSL
是切片传输,它会将视频切为一个个小的 ts
文件,并将切片信息记录在 .m3u8
文件中。
拉流客户端根据 .m3u8
中的 ts
索引信息,按顺序下载播放。
由于其切片的特点,会导致比较大的延迟,在实时性要求比较高的情况下,效果不好。
以上,就是在直播系统中经常使用到的三大协议。
综合以上特点,推流的时候,经常使用的是
RTMP
协议;拉流播放的时候,经常使用的是HTTP-FLV
和HLS
协议。
3. 直播服务器
有了协议,那么客户端和服务器就可以通讯了,怎么样搭建服务器呢?
服务器:一般都是 Nginx
协议拓展模块。
协议拓展模块:
i. nginx-rtmp-module
,不支持 http-flv;
ii. nginx-http-flv-module
,兼容nginx-rtmp-module,并支持 http-flv 直播;
iii. srs(simple-rtmp-server)
,支持三种协议。
搭建服务器也是一个比较繁琐的过程,涉及到的知识也很多,本文不再深入,网上也有很多相关资料可查看。
二、直播中的重难点
在直播中,有几个非常重要的地方,会直接影响直播效果,导致用户流失。
1. 首屏时间
首屏时间,即从观众打开直播,到看到画面呈现出来的时间。影响这个时间的是 H264
编码中的一个概念: GOP
。
- GOP
全称:Group of Picture。即一组帧组成的一个序列。
在 H264
中,分别有 I帧、P帧、B帧 三种帧类型。
GOP
就是由一个 I帧
和多个 P帧
或 B帧
组成的一组相近的画面 。
GOP
解码器可以直接解码 I帧
,但是 P帧
和 B帧
必须依赖 I帧
,或者前后的 P 或 B
才能解码。
首次连上直播间时,需要抛弃掉 P
和 B
帧,等待 I帧
。
所以,影响首屏时间最重要的因素就是 I帧
,也就是两个 GOP
之间的间隔时间。
GOP 间隔的设置并非越小越好,太小则两个
I帧
之间的P/B帧
越少,压缩率越低,画面质量越差,需要做好权衡。
2. 稳定性问题
我们知道网络是不稳定的,经常会出现网速慢,甚至断网的问题,所以稳定性优化也是非常重要的。
比如以下几个方面:
- 码率控制
同样分辨率下,码率越高,视频越清晰,同时需要的带宽也越大。相反,码率越低,视频越模糊,数据越小。
- 弱网优化
根据不同的网速切换不同的码率进行播放等。
- 断线重连
网络断开时的重联机制。
3. 全局负载均衡
随着业务的发展,如果主播和观众的数量越来越多以后,系统可能会面临高并发情景,按照上面介绍的最简单的系统,可能就扛不住了。
直播卡顿,甚至系统奔溃,将直接影响公司的声誉。解决这种情况的一个好办法就是使用 CDN
。
- CDN 内容分发
解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播。
关于 CDN 可参考: CDN是什么?使用CDN有什么优势?
加入 CDN 后,整个直播系统架构如下:
CDN
三、其他
除了以上提到的内容,当今的直播系统还要包括以下内容:录制
、转码
、鉴黄
、截屏
、权鉴防盗
、回声消除
、连麦
等等,整套下来,需要非常多的知识储备,以及大量的时间精力,才能完成。
大部分公司其实很难完成,而国内大厂们则利用自己强大的研发能力,提供了各自的 SDK ,大大降低了直播门槛。
实际上,一些知名的直播公司,也是利用了第三方的 SDK 来搭建自己的直播系统的,比如斗鱼、龙珠直播、now直播等等,都是利用了腾讯云直播平台搭建的。
下面,就来看看如何使用腾讯云直播 SDK 来搭建直播系统。
四、使用腾讯云直播 SDK ,搭建直播系统
先通过腾讯云直播的架构图,了解一下腾讯云直播提供了哪些功能。
图片上传失败...(image-61e3ca-1587690635760)
可以看到,腾讯云直播中提供了最基础的 推流
,拉流
,录制
,转码
,点播
,以及 推流加速
和 CDN加速
。除了这些,还有 控制台
,以及更加灵活的 API
控制接口等等。
基本上,一个直播系统该有的功能基本都具有了。
1. 开通云直播服务
要使用腾讯云直播功能,需要先注册腾讯云账号,完成注册后,进入控制台,然后搜索「云直播」,打开对应的页面,点击【申请开通】即可开通云直播服务。
开通后,会赠送 20GB 国内播放流量,可以用来测试。
- 申请移动端 License
想要在 Android/iOS 手机上使用云直播,需要先申请对应的 License 。当然了,这是需要付费才能使用的。
不过腾讯提供了一个免费的测试版 License ,可以免费使用 28 天。这里使用测试版的 License 进行演示。具体如下:
进入「云直播」控制台后,点击左侧的「直播 SDK」-「License」。
点击「立即申请」,填写 App 名字
、Android 包名
、iOS Bundle ID
。
申请License
填好以后,点击「确定」,得到 Key
和 LiscenseUrl
,这两个值在后面接入 SDK 的时候将会用到。
获取Key和LiscenseUrl
- 添加自有域名
虽然腾讯云直播已经搭建好了直播服务器,但是
工信部
规定,直播需要备案域名,所以如果没有服务器和域名的话,需要先购买服务器,注册域名,然后对域名申请备案。
需要注意的是:云直播需要两个不同的域名,一个用作推流,一个用于播放。
腾讯云已经为我们提供了一个 推流域名
**,所以我们其实只需要提供一个域名用于播放就可以了。**
添加流程如下:
「云直播控制台」- 「域名管理」-「添加域名」
添加域名
添加完成后,会生成域名对应的 CNAME
(CNAME 很关键,下面详细讲解)。
生成CNAME
到这里域名就配置完毕了。
- 配置CNAME
如果你一个 “纯粹” 的移动端开发者,不怎么了解服务端的话,你肯定会觉得很奇幻:刚刚配置的播放域名有什么用呢?它是怎么和腾讯云的服务器进行关联的呢?
关键的地方就在这个 CNAME
上。
我们知道,手机/PC 在访问一个域名的时候,会向 DNS 发起请求,普通情况下,DNS 会将域名解析成 IP 地址,然后返回给 手机/PC ,接着通过这个 IP 发起真正的请求。
但是,实际上并非总是这样的,我们可以给一个 域名A
配置一个 别名
,这个别名也是一个 域名B
。
当客户端对 A域名
发起请求时,DNS 会找到 A ,说:“给我 IP 吧”,这时候 A 会告诉 DNS : “我没有,你去找 B域名
要吧”。
DNS 转向 B ,并且获取到了 B 的 IP 地址,然后返回给客户端,客户端对 B 的 IP 发起了请求。
这时候的 A域名
就变成了类似一个中转站的东西,对 A域名
的请求,实际上访问的是 B域名
。
CNAME 就是 别名
的意思。也就是说,上面腾讯云生成的 CNAME
域名,是不能直接访问的,还需要把它和我们自己备案的 播放域名
进行关联。
当我们访问自己的 播放域名
的时候,实际上是访问了腾讯云的直播服务器。
弄清楚了这层关系以后,配置就很简单了。
至于怎么关联,各大云服务提供商都会提供 CNAME
的配置,按照云服务提供商的说明进行配置就可以了。
这也说明了,腾讯云直播可以绑定任意的云服务,你可以使用腾讯云的,也可以使用阿里云的。只要备案好,并配置 CNAME 即可。
2. 集成 SDK
完成了以上配置以后,一个基础版的直播服务就开通了。接下来,只需在客户端集成 SDK 就可以实现推流和拉流播放了。
以 Android
端为例,说明整个集成过程。
- 依赖配置
腾讯云提供了多种集成方式,jar/aar/gradle 依赖,推荐使用 gradle 依赖,方便简单,在 app/build.gradle 添加:
代码语言:javascript复制dependencies {
implementation 'com.tencent.liteavsdk:LiteAVSDK_Smart:latest.release'
}
配置 so 架构,腾讯云直播支持 armeabi 、 armeabi-v7a 和 arm64-v8a,你可以选择这几个架构都适配,也可以选择其中的一个架构,在 app/build.gradle 添加:
代码语言:javascript复制defaultConfig {
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
}
}
- 配置打包参数
在 app/build.gradle 中配置
代码语言:javascript复制android {
//省略其他...
packagingOptions {
pickFirst '**/libc _shared.so'
doNotStrip "*/armeabi/libYTCommon.so"
doNotStrip "*/armeabi-v7a/libYTCommon.so"
doNotStrip "*/x86/libYTCommon.so"
doNotStrip "*/arm64-v8a/libYTCommon.so"
}
//......
}
- 配置权限
在 AndroidManifest.xml 中配置
代码语言:javascript复制<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-feature android:name="android.hardware.Camera"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
- 初始化SDK
在 Application 类中配置 License 的 Key 和 LicenseUrl
代码语言:javascript复制class App : Application() {
override fun onCreate() {
super.onCreate()
// 替换成控制台获取到的 licence url
val licenceURL = "http://license.vod2.myzijiebao.com/license/v1/xxxxx"
// 替换成控制台获取到的 licence key
val licenceKey = "e90e0872aaeb7797xxxxxxxxx"
TXLiveBase.getInstance().setLicence(this, licenceURL, licenceKey)
}
}
3. 推流与拉流
实现推流和拉流非常简单,腾讯云已经把功能都封装好了。
推流:摄像头/麦克风数据采集、编码、视频渲染、推送数据。 拉流:拉取数据、解码、渲染播放。
我们要做的就是简单地调用一下 API 。
3.1 实现数据推流
直播 SDK 提供了一个 View 用于视频的播放显示:TXCloudVideoView
。把它放到 xml
中即可,SDK 会根据 TXCloudVideoView 的宽高和视频的宽高,做对应的自适应缩放。
代码语言:javascript复制// activity_push.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@ id/tx_cloud_video_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="推流"
android:onClick="startPushing"/>
</androidx.constraintlayout.widget.ConstraintLayout>
接着,在 Activity 中初始化推流工具,并把 TXCloudVideoView
绑定到 SDK ,实现摄像头预览。
class VideoPushActivity : AppCompatActivity() {
private lateinit var mLivePusher: TXLivePusher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_push)
initPusher()
}
private fun initPusher() {
val mLivePushConfig = TXLivePushConfig()
mLivePusher = TXLivePusher(this)
// 一般情况下不需要修改 config 的默认配置
mLivePusher.config = mLivePushConfig
mLivePusher.startCameraPreview(tx_cloud_video_view)
}
fun startPushing(v: View) {
val rtmpURL = "rtmp://2157.livepush.myzijiebao.com/live/demo?txSecret=39193bda8a439ba8852baf9bce1fde39&txTime=5E8DF4FF"
val ret = mLivePusher.startPusher(rtmpURL.trim())
if (ret == -5) {
Log.i("VideoPushActivity", "startRTMPPush: license 校验失败")
}
}
override fun onDestroy() {
super.onDestroy()
mLivePusher.stopPusher()
mLivePusher.stopCameraPreview(true)
}
}
是不是非常简单,初始化完毕后,只要把推流地址给到推流工具,启动推流就搞定了。
当然了,你还可以做更多的配置,比如
- 通过
mLivePusher.setPushListener(this)
来监听推送状态和网络状态,详见【官方文档】; - 通过
TXLivePushConfig
来配置旋转角度、设置水印、帧率、码率等等,非常丰富的参数配置,详见【官方文档】; - 通过
mLivePusher.getBeautyManager()
获取到美颜工具TXBeautyManager
可用于配置美颜相关的内容,不过这个方法是需要另外付费的。
最后,要注意的是,在退出推流页面的时候,需要在 onDestroy
中停止推流,并关闭、释放摄像头。
3.2 实现拉流播放
接下来,看看如何播放上面的推流视频。
视频的播放显示,依然是通过 TXCloudVideoView
。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@ id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"
android:onClick="startPlaying"/>
</androidx.constraintlayout.widget.ConstraintLayout>
同样的,在 Activity
中把 TXCloudVideoView
绑定给拉流工具。
// VideoPlayerActivity
class VideoPlayerActivity : AppCompatActivity() {
private lateinit var mLivePlayer: TXLivePlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initPlayer()
}
private fun initPlayer() {
//创建 player 对象
mLivePlayer = TXLivePlayer(this)
//关联 player 对象与界面 view
mLivePlayer.setPlayerView(video_view)
}
fun startPlaying(v: View) {
val flvUrl = "http://2157.liveplay.myzijiebao.com/live/demo.flv"
mLivePlayer.startPlay(flvUrl, TXLivePlayer.PLAY_TYPE_LIVE_FLV)
}
}
拉流播放的过程也是非常简单,配置好拉流地址,启动播放就可以了。
需要注意的是,在 App 端,目前只支持 RMPT
和 FLV
两种播放格式,在启动播放的时候,需要指定对应的格式。
mLivePlayer.startPlay(flvUrl, TXLivePlayer.PLAY_TYPE_LIVE_FLV)
或
mLivePlayer.startPlay(flvUrl, TXLivePlayer.PLAY_TYPE_LIVE_RTMP)
当然了,你也可以通过一些配置来达到自己的播放需求。比如:
mLivePlayer.setRenderRotation(TXLiveConstants.RENDER_ROTATION_LANDSCAPE)
设置横屏播放;mLivePlayer.setRenderMode(int mode)
设置画面拉升模式:平铺模式/自适应模式;mLivePlayer.setPlayListener(this)
监听播放状态和网络状态。
详见【官方文档】
3.3 生成推拉流地址
- 地址格式
从上面推拉流的地址可以看出地址的组合格式,具体如下图:
推拉流地址格式
⚠️注意
只要符合上图规则的 URL 地址,腾讯云直播就认为是正确的,就可以正常推流和拉流。
前面四个参数很简单,根据自己的需求配置就可以了。
关键在于最后面的 权鉴
参数。
- 权鉴
权鉴的用途是:防盗推/播!
腾讯云直播默认情况下,推流
已经设置了权鉴,拉流
是没有设置权鉴的。
⚠️ 注意
权鉴不是必填参数,也就是说,如果不启动权鉴,这个参数是可以去掉的,一样可以推拉流。 但是,没有权鉴的情况下,人家只要知道了服务器域名,就可以按照地址格式,拼接出合规的地址,直接就可以推拉流了。 言外之意就是,你的服务器被人盗了。
而直播 SDK 的计费方式恰恰就是根据播放流量来计费的,如果被人家随便拉流播放的话,必然会造成损失。
代码语言:javascript复制// 无权鉴推流地址
rtmp://2157.livepush.myzijiebao.com/live/demo
// 有权鉴推流地址
rtmp://2157.livepush.myzijiebao.com/live/demo?txSecret=39193bda8a439ba8852baf9bce1fde39&txTime=5E8DF4FF
- 开启权鉴
开启权鉴的方式也很简单,可以通过控制台或调用 API 设置。
⚠️ 注意
一般控制台设置只在测试的时候使用,生产环境下建议是通过自己的服务端调用 API 来配置权鉴 key ,并保持动态更新。
这里只是测试,我们就用控制台来配置。
「云直播控制台」-「域名管理」-「在要开启权鉴播放域名上,点击管理
」-「访问控制」-「编辑」-「开启权鉴」-「设置主key、备key、过期时间」
开启播放权鉴
其中:
主key
很重要,用来参与推拉流地址中权鉴的计算。
备用key
主要用于当 主key
泄漏以后,可用 备用key
来生成地址,并且更新 主key
。
⚠️ 注意
权鉴的 key 千万不能泄露,只有自己和腾讯云知道,这样双方才能对权鉴中 MD5 的加密值进行验证。泄漏以后要赶紧更新。
- 权鉴计算
从地址格式拼接图已经知道,权鉴的格式是:
代码语言:javascript复制txSecrect=Md5(key StreamName txTime)&txTime=xxxx
txTime:到期时间的 UNIX 时间戳。
比如过期时间为
2020-04-28 23:59:59
,时间戳为 1586361599 。时间戳可以进一步转换为 16 进制,比如 1586361599 的 16 进制为 5E8DF4FF。
因此,
代码语言:javascript复制txTime = 1586361599
或
txTime = 5E8DF4FF
⚠️ 注意
拉流地址的 txTime
的计算有所不同,它的值等于【设置的时间 权鉴key
设置的过期时间】。
例如设置的过期时间为 2020-04-28 00:00:00
,权鉴key
的过期时间是 10s ,那么拉流地址真实的过期时间为:2020-04-28 00:00:10
(UNIX时间戳十六进制为 5EA7018A)。
假设 权鉴key
为 123456 ,StreamName
为 demo,那么
txSecrect = MD5(12345demo5EA7018A) = 08472000d6fb5b5b2f2dc61583900287
搞清楚了地址的规则和权鉴的原理,就可以获取推拉流相应的地址了。
有两种方式:
- 云直播控制台生成(主要用于测试)
- 自己拼接生成(用于生产,根据需求来生成)
- 控制台生成方式
在「云直播控制台」-「辅助工具」-「地址生成器」,可选择生成推流或者拉流地址,如下:
生成地址
其中,
AppName
:应用名,默认为live,根据自己的需求配置。
StreamName
:直播通道名字,根据需求填写,比如主播的用户ID。
过期时间
:直播通道的有效时间,过了这个时间,则无法再推送或者拉流。
点击「生成地址」,控制台自动生成好推流和拉流地址和权鉴,如下:
生成推流地址
生成拉流地址
- 手动拼接方式
我们已经知道了地址的规则,那么自己来拼接也是可以的,而且在生产环境中也只能通过这种方法,因为我们不可能每次开启直播的时候,都要去控制台生成一个地址。
所以,只要按照上面讲解的规则生成地址就可以了,并且地址的生成应该放到我们自己系统的服务器中,而不是 App 中,这样才能避免泄漏权鉴key。
请求流程如下:
App 请求推拉流地址
至此,一个简单的直播系统就搭建完成了。
如果你想要实现一些高级的效果,比如滤镜、添加水印,但是你又不想付费,那也是有办法的,通过 SDK 自定义渲染接口,就可以实现,前提是你有 OpenGL 基础。
下面就通过一个灰色滤镜和添加水印,简单介绍一下如何自定义渲染。
4. 自定义视频渲染
目前视频渲染基本都是使用 OpenGL 实现的,腾讯云直播也不例外。
先来看下 SDK 提供的 推流
自定义渲染接口。
TXLivePusher
可以设置一个回调接口 VideoCustomProcessListener
,其接口定义如下:
public interface VideoCustomProcessListener {
/**
* @param textureId: 原视频纹理 ID
* @param width: 视频宽
* @param height: 视频高
* return 新视频画面纹理 ID
*/
int onTextureCustomProcess(int textureId, int width, int height);
void onDetectFacePoints(float[] points);
void onTextureDestoryed();
}
重点是第一个接口 onTextureCustomProcess
,第一个参数是一个 OpenGL 的纹理 ID,这个纹理保存了原视频画面,另外两个参数是画面的宽和高。
返回值也是一个纹理 ID,这个纹理就是经过处理后的视频画面的纹理 ID 。
如果直接把第一个参数 textureId 返回,则渲染的画面就是原始的视频画面。
- 自定义灰色滤镜
根据上面的接口,既然已经有了原视频画面的纹理,那么对这个纹理进行处理就是很简单的事了。但仅仅处理是不够,还需要返回一个新的纹理,要怎么才能得到一个新的纹理呢?
如果你学过 OpenGL ,以及 OpenGL FBO(帧缓存对象),那就很容易实现了。
OpenGL FBO 提供了缓冲技术,不需要将画面显示出来,可以将画面绘制到一个新的纹理上。详细介绍可以看【OpenGL FBO 数据缓冲区】
限于文章篇幅,以及 OpenGL 相关知识的了解程度,这里只简单将接口实现贴出来,要了解详细的代码,可以参考本文的【Demo源码:GreyFilter】
代码语言:javascript复制class VideoPushActivity : AppCompatActivity(), TXLivePusher.VideoCustomProcessListener {
// 省略无关代码
// ......
private fun initPusher() {
val mLivePushConfig = TXLivePushConfig()
mLivePusher = TXLivePusher(this)
// 一般情况下不需要修改 config 的默认配置
mLivePusher.config = mLivePushConfig
mLivePusher.startCameraPreview(tx_cloud_video_view)
// 开启自定义渲染
mLivePusher.setVideoProcessListener(this)
}
private var greyFilter: GreyFilter? = null
override fun onTextureCustomProcess(textureId: Int, width: Int, height: Int): Int {
if (greyFilter == null) {
greyFilter = GreyFilter()
greyFilter?.setTextureID(textureId)
greyFilter?.setVideoSize(width, height)
}
greyFilter?.draw()
return if(greyFilter?.getFboTextureID() == -1) textureId else greyFilter?.getFboTextureID()!!
}
// ......
}
原理很简单:利用 OpenGL FBO 技术,创建一个新的纹理单元 B
,接着将 SDK 传递给我们的纹理单元 A
中的画面处理成灰色,并绘制到 FBO 上,新的纹理单元 B
就有了灰色滤镜处理过的画面,然后将 B
返回给 SDK 内部进行渲染绘制。
- 添加水印
添加水印也很简单,水印其实就是在原视频的基础上,绘制多一个 Logo 图片叠加在上面。
那么只需要在 FBO 上面,先绘制原视频数据,再绘制水印即可,唯一要注意的是,需要做好水印的位置和大小的适配。
具体实现参考【Demo源码:WaterMarkDrawer】
五、总结
腾讯云直播 SDK 提供了比较全面的功能,基本上涵盖了直播系统中该有的功能点,整一套使用下来,也比较顺畅易用(仅仅体验使用,深入使用可能会遇到其他的一些问题),主要有以下特点:
- 系统搭建非常简单,SDK 提供的接口很简洁友好,基本上是傻瓜式接入;
- 提供的功能点齐全,从采集-本地处理-推流,到云端转码、录制、鉴黄等,再到拉流,还有直播连麦等高级功能,提供了一体式解决方案;
- 支持推拉流地址自定义,拓展方便;
- 提供自定义渲染接口,对于有自定义能力的小伙伴来说非常实用。
当然也有美中不足的地方:
- 拉流自定义渲染接口只提供了原始数据,没有提供 OpenGL 纹理渲染,处理起来比较麻烦;
- 推流自定义渲染接口必须要返回一个新的纹理,不能直接通过 OpenGL 渲染,需要借助 FBO ,加大了开销;
- 推拉流都可以设置 SurfaceView 作为渲染窗口,但是被绑定到 SDK 的 OpenGL 线程中,没有暴露渲染上下文,无法定义自己的 OpenGL 渲染环境。文档中也没有深入的说明,目前不知其用途。
综上,直播涉及到的知识确实非常多,投入足够多的精力也并非不可实现,但对于小公司或者没有足够研发投入的公司来说,使用第三方的 SDK 或许是更好的选择,毕竟他们都是顶住了千万级流量的,专业性,稳定性和可靠性都比较好。