基础能力使用
在快速开始中,我们完成了qplayer2播放器的创建\播放\销毁,本文介绍如何使用qplayer2的播放能力
播放
创建播放数据
以下配置的mediaModel是一个有2个清晰度的播放数据,包含2个清晰度1080 和 720 ,同时包含2个srt字幕文件,起播的时候会自动播放1080P的链接和加载中文的字幕
//创建MediaModel的构造器
builder = QMediaModelBuilder()
//配置视频元素
builder.addStreamElement(
"", //userType 预留字段可以填空
QURLType.QAUDIO_AND_VIDEO, //资源的类型,指该元素是包含音频还是视频还是都包含
1080, //清晰度数值标记
"http://demo-videos.qnsdk.com/qiniu-1080p.mp4", //视频的地址
true //是否是默认元素,默认元素是指,起播的时候默认会播放这个元素
)
builder.addStreamElement(
"", //userType 预留字段可以填空
QURLType.QAUDIO_AND_VIDEO, //资源的类型,指该元素是包含音频还是视频还是都包含
720, //清晰度数值标记
"http://demo-videos.qnsdk.com/qiniu-720p.mp4", //视频的地址
false //是否是默认元素,默认元素是指,起播的时候默认会播放这个元素
)
builder.addSubtitleElement("中文", //字幕名称(唯一的key)
"http://demo-videos.qnsdk.com/qiniu-2023-chinese.srt", //srt字幕地址
true) //是否是默认字幕 默认元素是指,起播的时候默认加载这个字幕
builder.addSubtitleElement("英文",
"http://demo-videos.qnsdk.com/qiniu-2023-english.srt",
false)
val mediaModel = builder.build(false) //此处的参数指代该资源是否是一个直播资源,是直播的话就是true,不是直播就是false
以下配置的mediaModel是一个音频和视频分成2路url的的播放数据 ,起播的时候会同时播放这两个元素
builder = QMediaModelBuilder()
builder.addStreamElement(
"", //userType 预留字段可以填空
QURLType.QAUDIO, //资源的类型,这里的url对应的资源只有音频所以这里用QAUDIO
100, //清晰度数值标记为100
"http://demo-videos.qnsdk.com/only-audio.m4s", //资源地址
true //是否是默认元素,默认元素是指,起播的时候默认会播放这个元素
)
builder.addStreamElement(
"", //userType 预留字段可以填空
QURLType.QVIDEO, //资源的类型,这里的url对应的资源只有视频所以这里用QVIDEO
1080, //清晰度数值标记为1080
"http://demo-videos.qnsdk.com/only-video-1080p-60fps.m4s", //资源地址
true //是否是默认元素,此处也是默认播放,所以也是true
)
val mediaModel = builder.build(false) //此处的参数指代该资源是否是一个直播资源,是直播的话就是true,不是直播就是false
addStreamElement方法的所有参数
参数名 | 参数含义 |
---|---|
userType | 用户自定义Type,目前预留,可以填空字符串 |
urlType | 媒体的资源属性 只包含视频/只包含音频/同时有视频音频 |
quality | 清晰度 |
url | 资源地址 |
isSelected | 是否起播时播放该流 |
backupUrl | 备用地址 |
referer | http/https 协议的地址 支持该属性 用于防盗链referer参数 |
videoRenderType | 视频的渲染类型,默认是QVideoRenderType.PLANE, 如果是VR视频此处为QVideoRenderType.PANORAMA_EQUIRECT_ANGULAR |
hlsDrmKey | 私有DRM解密的key,用在私有hls加密视频播放的场景中 |
mp4DrmKey | mp4 drm 的解密 key 用于 CENC-AES-CTR 加密的视频播放场景中 |
mp4QNDrmComKey | mp4 七牛私有 drm 的解密 comkey 用于七牛私有 mp4 加密的视频播放场景中 |
mp4QNDrmFileKey | mp4 七牛私有 drm 的解密 filekey 用于七牛私有 mp4 加密的视频播放场景中 |
MP4 加密
目前 QPlayer2 仅支持显式的 CENC-AES-CTR 加密,暂不支持隐式加密。CENC-AES-CTR 的初始化向量(IV) 由加密工具随机生成并存储在加密后的 MP4 文件中,QPlayer2 读取 MP4 文件的 IV 和QmediaModel 存储的 mp4DrmKey 进行解密。
配置DRM加密视频
//七牛私有HLS解密密钥
builder = QMediaModelBuilder()
url = "http://80rjmu.com1.z0.glb.clouddn.com/ncyY3ioGAakS-Lg252IscrXNxyo=/Fgny-rvibYqoFP-lPkI53JfmoIx5"
builder.addStreamElement("", QURLType.QAUDIO_AND_VIDEO, 0, url, true, "", "", QVideoRenderType.PLANE, "testtesttesttest")
mVideoList.add(Pair(url, builder.build(false)))
//公有MP4解密密钥
builder = QMediaModelBuilder()
url = "https://sdk-release.qnsdk.com/CENC_AES-CTR.mp4"
builder.addStreamElement("", QURLType.QAUDIO_AND_VIDEO, 0, url, true, "", "", QVideoRenderType.PLANE, "", "c7e16c4403654b85847037383f0c2db3")
mVideoList.add(Pair(url, builder.build(false)))
//私有MP4解密密钥
builder = QMediaModelBuilder()
url = "https://kodo.koafun.com/drm-16884581530881688458153078-new.mp4"
builder.addStreamElement("", QURLType.QAUDIO_AND_VIDEO, 0, url, true, "", "", QVideoRenderType.PLANE, "", "", "comnew", "filenew")
mVideoList.add(Pair(url, builder.build(false)))
对 MP4 文件进行 CENC-AES-CTR 加密
可以选择 ffmpeg 对一个 MP4 进行加密。加密命令如下:
ffmpeg -i test2.mp4 -vcodec copy -acodec copy -encryption_scheme cenc-aes-ctr -encryption_key c7e16c4403654b85847037383f0c2db3 -encryption_kid a7e61c373e219033c21091fa607bf3b8 encrypted_IV_test.mp4
命令说明
test2.mp4 为输入文件
encrypted_IV_test.mp4 为输出文件
-vcodec copy 和 -acodec copy :指定视频流和音频流的编码方式为原始流的拷贝,即不进行重新编码,保持与原始文件相同的编码格式。
-encryption_scheme cenc-aes-ctr :设置加密方案为 CENC-AES-CTR,即采用 CTR 模式进行加密。
-encryption_key c7e16c4403654b85847037383f0c2db3 指定加密所使用的密钥,即解密所使用的 key。这里的c7e16c4403654b85847037383f0c2db3 是一个示例密钥,您可以替换为您自己的密钥,格式要求为:16字节的16进制数据。
-encryption_kid a7e61c373e219033c21091fa607bf3b8:指定加密所使用的密钥标识符(KID)。这里的 a7e61c373e219033c21091fa607bf3b8 是一个示例 KID,您可以替换为您自己的密钥标识符, 格式要求为:16字节的16进制数据。
加密后可通过ffplay 4.4 版本进行播放验证,播放正常即可。命令如下:
ffplay encrypted_IV_test.mp4 -decryption_key c7e16c4403654b85847037383f0c2db3
QPlayer2 对加密后 MP4 文件进行播放
builder = QMediaModelBuilder()
//配置视频元素
builder.addStreamElement(
"", //userType 预留字段可以填空
QURLType.QAUDIO_AND_VIDEO, //资源的类型,指该元素是包含音频还是视频还是都包含
1080, //清晰度数值标记
"https://sdk-release.qnsdk.com/CENC_AES-CTR.mp", //视频的地址
true //是否是默认元素,默认元素是指,起播的时候默认会播放这个元素
"c7e16c4403654b85847037383f0c2db3" //mp4 解密使用的秘钥
)
val mediaModel = builder.build(false) //此处的参数指代该资源是否是一个直播资源,是直播的话就是true,不是直播就是false //预留字段,可以为
视频URL类型枚举
QAUDIO_AND_VIDEO(0), //包含视频和音频
QAUDIO(1), //仅包含音频
QVIDEO(2), //仅包含视频
NONE(5); //无
播放媒体资源
上一步中我们创建了一些mediaModel,接下来我们将播放他们
mQPlayerView.playerControlHandler.playMediaModel(
mediaModel, //要播放的媒体资源
0 //起播的位置,如果是直播这里必须为0,不然有不可预见的问题发生
)
播放错误监听
当视频起播时,如果遇到网络等问题原因,导致没有和服务器建立链接,那么会导致视频播放失败,总共会有3次重试机会,3次都失败的情况下会回调onOpenFailed方法
当视频已经起播后,在中途遇到网络等问题原因,进入buffer, 那么内核会一直重试,直到再次连接上服务端继续播放,或者用户通过接口停止播放该视频,在重试的过程中
onReconnectStart/onReconnectEnd会被回调,那么可以监控回调的次数来做业务层的结束播放的策略,当然在正常播放的情况下,这两个方法也是会被回调的,所以
最好结合buffer的回调数据一起使用。
val listener = object: QIPlayerMediaNetworkListener {
/***
* 开始重连
*
* @param userType 重连stream element的userType
* @param urlType 重连stream element的urlType
* @param url 重连的url
* @param retryTime 已重试的次数
*/
override fun onReconnectStart(userType: String, urlType: QURLType, url: String, retryTime: Int) {
}
/***
* 重连结束
*
* @param userType 重连stream element的userType
* @param urlType 重连stream element的urlType
* @param url 重连的url
* @param retryTime 已重试的次数
* @param error 错误码
*/
override fun onReconnectEnd(userType: String, urlType: QURLType, url: String, retryTime: Int, error: OpenError) {
}
/***
* 打开失败
*
* @param userType 重连stream element的userType
* @param urlType 重连stream element的urlType
* @param url 重连的url
* @param error 错误码
*/
override fun onOpenFailed(userType: String, urlType: QURLType, url: String, error: OpenError) {
//当非用户中断时,才展示错误面板
if (error != QIPlayerMediaNetworkListener.OpenError.INTERRUPT) {
//展示错误面板
}
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerMediaNetworkListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerMediaNetworkListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerMediaNetworkListener()
播放错误码枚举
UNKOWN(10000),
NONE(0),
IOERROR(-5),
INTERRUPT(-1414092869),
URL_INVALID(-875574520),
FORMAT_INVALID(-1094995529);
SRT字幕
要使用SRT字幕需要满足四个条件
- 视频源配置了字幕(配置方式参阅播放条目)
- 字幕功能开启
mQPlayerView.playerControlHandler.setSubtitleEnable(true)
- 有生效的字幕(以下方式二选一)
- 在视频源配置了
isDefault=True
的字幕启动播放 - 通过接口设置生效字幕
mQPlayerView.playerControlHandler..setSubtitle(subtitleName)
- 通过字幕回调监听,每当时间轴触发了新的字幕条目,则该监听会回调,并将最新的一条字幕文案传递给监听接口,用户可以以自己的展示方式来展示这条字幕条目
字幕回调监听
val listener = object: QIPlayerSubtitleListener {
//text:最新的字幕条目
override fun on_subtitle_text_change(text: String) {
//更新字幕文案展示
}
//name:字幕的名称
override fun on_subtitle_name_change(name: String) {
//字幕变更回调
}
override fun on_subtitle_close() {
//字幕能力关闭
}
//name:字幕的名称
//result:加载的结果
override fun on_subtitle_loaded(name: String, result: Boolean) {
//字幕加载结束
}
//name:字幕的名称
//result:解析的结果
override fun on_subtitle_decoded(name: String, result: Boolean) {
//字幕解析结束
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerSubtitleListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerSubtitleListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerSubtitleListeners()
预加载
播放预加载实例用于点播(一般用在短视频场景中),不能用在直播上,播放预加载实例能提升首帧的速度。
预加载的加载时机策略由业务层自行实现
预加载的简单使用
//创建预加载实例,一般是提前创建
mediaItem = QMediaItemContext(mediaModel, logLevel, localStorageDir, startPos)
//开始预加载
mediaItem.playMediaControlHandler.start()
//在需要播放时调用播放预加载实例接口
mQPlayerView.playerControlHandler.playMediaItem(mediaItem)
//播放完成后
停止预加载实例
mediaItem.playMediaControlHandler.stop()
预加载实例状态枚举
NONE(100) //初始状态
PREPARE(101) //开始加载前,准备工作状态
LOADING(102) //开始加载
PAUSED(103) //暂停加载
STOPED(104) //停止加载, 无法恢复
ERROR(105) //加载出错
PREPARE_USE(106) //准备使用播放
USED(107) //已经使用
DISCARD(108) //废弃
预加载实例状态监听
val listener = object: QIMediaItemStateChangeListener {
//状态变更回调时回调,返回变更后的状态
override fun onStateChanged(state: QMediaItemState) {
}
}
//添加监听
mediaItem.playMediaControlHandler.addMediaItemStateChangeListener(listener)
//移除监听
mediaItem.playMediaControlHandler.removeMediaItemStateChangeListener(listener)
//移除所有监听
mediaItem.playMediaControlHandler.removeAllPlayMediaStateChangeListener()
预加载的状态枚举
NONE(100),//初始状态
PREPARE(101),
LOADING(102),
PAUSED(103),
STOPED(104),
ERROR(105),
PREPARE_USE(106),
USED(107),
DISCARD(108);
设置日志等级
mQPlayerView.playerControlHandler.setLogLevel(QLogLevel.LOG_INFO)
暂停播放
mQPlayerView.playerControlHandler.pauseRender()
恢复播放
mQPlayerView.playerControlHandler.resumeRender()
停止当前视频播放
mQPlayerView.playerControlHandler.stop()
鉴权
鉴权功能介绍见鉴权相关文档
强制网络鉴权
调用该接口时,会强制进行网络鉴权
mQPlayerView.playerControlHandler.forceAuthenticationFromNetwork()
鉴权监听
val listener = object: QIPlayerAuthenticationListener {
/**
* 鉴权失败回调,同时,会停止当前的视频播放,且状态切换成 @see[QPlayerState.ERROR]
*/
fun on_authentication_failed(error_type: QAuthenticationErrorType) {
}
/**
* 鉴权成功回调
*/
fun on_authentication_success() {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerAuthenticationListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerAuthenticationListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerAuthentications()
鉴权错误码枚举
NONE(0), //鉴权出错
NO_BASE_AUTH(1), //缺少基础功能权限
NO_VR_AUTH(2), //缺少VR功能权限
NO_BLIND_AUTH(3),//缺少色盲功能权限
NO_SEI_AUTH(4), //缺少SEI功能权限
NO_SRT_AUTH(5); //缺少SRT功能权限
切换视频进度
mQPlayerView.playerControlHandler.seek(2000) //参数为要定位的position 单位毫秒
Seek监听
一般情况下 Seek都会成功,如果网络差的情况下,会无限loading(除非调用stop才会停止),直到seek到指定的位置为止。但是如果视频源本身是格式不正确的,比如pts错乱等问题,才会导致seek失败,所以在seek失败回调中,做好提示工作就好,不要尝试再次seek
val listener = object: QIPlayerSeekListener {
/*seek失败回调*/
override fun onSeekFailed() {
}
/*seek成功回调*/
override fun onSeekSuccess() {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerSeekListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerSeekListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerSeekListener()
视频解码方式
目前支持的解码方式有:软解、硬解、混解、自动,一般情况下,使用自动,内核会根据手机的硬件情况,要播放的视频资源编码格式等信息,挑选
合适的解码方式进行解码,各个解码方式的优劣情况如下
软解: 解码较慢,但是兼容性好,适用于所有手机
硬解:解码性能高,但是有一定兼容性,比如某些视频资源的编码格式,硬解芯片不支持。并且相对于软解,首帧的速度慢。
混解:通过软解和硬解相结合的方式,提升首帧速度,加快了解码性能,但是在能耗上略有劣势。
设置优先选择哪种解码方式
//下一次播放生效
//最终的解码方式可能不是设置的,在设置的解码方式不能正确解码时,内核会进行降级,选择其他能正确解码的方式
mQPlayerView.playerControlHandler.setDecodeType(QPlayerSetting.QPlayerDecoder.QPLAYER_DECODER_SETTING_AUTO)
优先选择的解码方式枚举
//自动选择
QPLAYER_DECODER_SETTING_AUTO(0),
//硬解优先
QPLAYER_DECODER_SETTING_HARDWARE_PRIORITY(1),
//软解优先
QPLAYER_DECODER_SETTING_SOFT_PRIORITY(2),
//混解优先
QPLAYER_DECODER_SETTING_FIRST_FRAME_ACCEL_PRIORITY(3),
播放器视频解码监听相关
val listener = object: QIPlayerVideoDecodeListener {
//setDecodeType 设置了优先使用哪种解码方式,经过内核处理后,该回调将正真的视频解码方式返回
override fun onVideoDecodeByType(type: QPlayerDecodeType) {
}
// 如果当前视频编码 所在设备或者sdk不支持 则回调该方法
// codecId 视频的编码id
override fun notSupportCodecFormat(codecId: Int) {
}
/**
* 通知当前视频解码失败,
* @param retry 底层是否重试
*/
override fun onDecodeFailed(retry: Boolean) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerVideoDecodeTypeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerVideoDecodeTypeListener(listener)
//移除所有播放器视频解码监听
mQPlayerView.playerControlHandler.removeAllPlayerVideoDecodeTypeListener()
解码方式枚举
NONE(0),
//软解
SOFTWARE(1),
//surface 硬解
HARDWARE_SURFACE(10),
//buffer 硬解
HARDWARE_BUFFER(11),
//混解
FIRST_FRAME_ACCEL(2);
设置起播方式
//下一次播放生效
mQPlayerView.playerControlHandler.setStartAction(QPlayerSetting.QPlayerStart.QPLAYER_START_SETTING_PLAYING)
解码方式枚举
//起播播放
QPLAYER_START_SETTING_PLAYING(0),
//起播暂停在首帧
QPLAYER_START_SETTING_PAUSE(1),
设置seek方式
//下一次seek即生效
mQPlayerView.playerControlHandler.setSeekMode(QPlayerSetting.QPlayerSeek.QPLAYER_SEEK_SETTING_NORMAL)
seek方式枚举
//关键帧seek,每次seek 都seek到离目标位置向前的最近的一个关键帧,耗时少
QPLAYER_SEEK_SETTING_NORMAL(0),
//精准seek,耗时比关键帧seek多,且耗时和视频的gop间隔的大小成正比
QPLAYER_SEEK_SETTING_ACCURATE(1),
获取当前播放状态
mQPlayerView.playerControlHandler.currentPlayerState
获取当前进度
mQPlayerView.playerControlHandler.currentPosition
获取视频时长
mQPlayerView.playerControlHandler.duration
获取当前下载速度
mQPlayerView.playerControlHandler.downloadSpeed
获取当前缓冲进度
mQPlayerView.playerControlHandler.bufferPositon
播放器状态
在整个播放器实例生命周期中,播放会在不同的状态中切换,用户可以通过监听播放器状态,实现上层业务逻辑,
比如在视频播放完时,展示其他视频的推荐页,那么只需监听播放器状态回调,待回调的状态是COMPLETED,则展示推荐页
播放器状态枚举
NONE(0),//初始状态
INIT(1),//播放器开始创建各种对象
PREPARE(2),//开始播放一个新视频的时候先进入这个状态,进行一些准备工作
PLAYING(4),//播放中
PAUSED_RENDER(6),//暂停
COMPLETED(7),//播放完成
SEEKING(8), //SEEK
STOPPED(9),//播放停止
ERROR(10),//播放出错
END(11),//播放器释放各种对象完成
MEDIA_ITEM_PREPARE(12), //预加载的Prepare状态,开始播放一个新视频的时候先进入这个状态,进行一些准备工作
RELEASE(13); //播放器结束,且释放各类资源
播放器状态变更监听相关
val listener = object: QIPlayerStateChangeListener {
//状态变更回调时回调,返回变更后的状态
override fun onStateChanged(state: QPlayerState) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerStateChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerStateChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerStateChangeListener()
播放速度
播放速度的调整范围建议在0.25-2.0之间,这样会有一个比较好的效果
设置播放速度
//即时播放生效
mQPlayerView.playerControlHandler.setSpeed(1.25f) //参数为倍速值
获取当前速度
mQPlayerView.playerControlHandler.speed
速度变更监听相关
val listener = object: QIPlayerSpeedListener {
//播放倍速变更回调
override fun onSpeedChanged(speed: Float) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerSpeedChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerSpeedChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerSpeedChangeListener()
清晰度
QPlayer2提供的清晰度能力是无缝切换清晰度方案,在切换过程中,画面是连续的画面,不会出现类似跳帧的情况。
使用无缝切换清晰度功能,必须在mediaModel中配置多路清晰度,如果使用非immediately方式(即异步方式)时,同时只能有一次切换会话存在,会话结束后,才能切下一次
切换视频清晰度
//使用该方法时,mediaModel中需要存在多个StreamElement
mQPlayerView.playerControlHandler.switchQuality(
"", //要切换的StreamElement的userType
QURLType.QAUDIO_AND_VIDEO, //要切换的StreamElement的urltype
1080, //要切换的StreamElement的清晰度数值标记
false //是否immediately方式,true:立即切换过程中会出现卡一下,一般用于直播,false:非立即切换下,切换过程无缝切换,只能用于点播。
)
获取当前的清晰度
//使用该方法时,mediaModel中需要存在多个StreamElement,返回清晰度数值标记
mQPlayerView.playerControlHandler.getCurrentQuality(
"", //要查询的StreamElement的userType
QURLType.QAUDIO_AND_VIDEO //要查询的StreamElement的urltype
)
获取当前正在切换的清晰度
//获取指定 userType urlType 流正在切换的清晰度(非immediately方式)
mQPlayerView.playerControlHandler.getSwitchingQuality(
"", //要查询的StreamElement的userType
QURLType.QAUDIO_AND_VIDEO //要查询的StreamElement的urltype
)
播放器清晰度相关监听
val listener = object: QIPlayerQualityListener {
/**
* 开始清晰度切换
* @param userType 开始切换清晰度的stream element的userType
* @param urlType 开始切换清晰度的stream element的 urlType @see[QURLType]
* @param newQuality 要切到哪路清晰度
* @param oldQuality 切换前的清晰度
*/
override fun onQualitySwitchStart(userType: String, urlType: QURLType, newQuality: Int, oldQuality: Int) {
}
/**
* 清晰度切换完成
* @param userType 开始切换清晰度的stream element的userType
* @param urlType 开始切换清晰度的stream element的urlType @see[QURLType]
* @param newQuality 要切到哪路清晰度
* @param oldQuality 切换前的清晰度
*/
override fun onQualitySwitchComplete(userType: String, urlType: QURLType, newQuality: Int, oldQuality: Int) {
}
/**
* 清晰度切换取消
* @param userType 开始切换清晰度的stream element的userType
* @param urlType 开始切换清晰度的stream element的 urlType @see[QURLType]
* @param newQuality 要切到哪路清晰度
* @param oldQuality 切换前的清晰度
*/
override fun onQualitySwitchCanceled(userType: String, urlType: QURLType, newQuality: Int, oldQuality: Int) {
}
/**
* 清晰度切换失败
* @param userType 开始切换清晰度的url流的userType
* @param urlType 开始切换清晰度的url流的 urlType @see[QURLType]
* @param newQuality 要切到哪路清晰度
* @param oldQuality 切换前的清晰度
*/
override fun onQualitySwitchFailed(userType: String, urlType: QURLType, newQuality: Int, oldQuality: Int) {
}
/**
* 目前仅支持同时有一个清晰度切换,如果前一个还未切换完,再次发起切换 会回调这个函数
* @param userType 开始切换清晰度的url流的userType
* @param urlType 开始切换清晰度的url流的 urlType @see[QURLType]
* @param newQuality 要切到哪路清晰度
* **/
override fun onQualitySwitchRetryLater(userType: String, urlType: QURLType, newQuality: Int) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerQualityListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerQualityListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerQualityListener()
直播支持多路清晰度切换
无缝切换清晰度方案需要配置以下三个条件
- 首先切换的模式要选择立即切换即:immediately=true
- 服务端配置PTS透传,
- 对应的清晰度要做主动转码设置
如果无法满足以上3个条件,那么建议用最基础的能力来实现清晰度切换,即每个清晰度都是一个media model,切换时,调用playMediaModel来播放新的清晰度流。
播放进度监听
只有在点播的时候 duration 和 progress 才有意义,直播的话这两个值没有参考意义
val listener = object: QIPlayerProgressListener {
/**
* 进度变更回调
* @param duration 总时长 单位毫秒
* @param progress 当前进度 单位毫秒
*/
override fun onProgressChanged(duration: Long, progress: Long) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerProgressChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerProgressChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerProgressChangeListener()
实时帧率
获取当前fps
mQPlayerView.playerControlHandler.fps
实时帧率监听
val listener = object: QIPlayerFPSListener {
/**
* fps 改变的回调
* @param fps 帧率
*/
override fun onFPSChanged(fps: Int) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerFPSChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerFPSChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerFPSChangeListener()
像素格式或者音频sample格式不支持的监听
val listener = object: QIPlayerFormatListener {
/**
* 当前有format 不支持,所以视频没法播放
*/
override fun onFormatNotSupport() {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerFormatListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerFormatListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerFormatListeners()
缓冲拉流监听
val listener = object: QIPlayerDownloadListener {
/**
* 拉流速率改变
* @param speed 单位 b/s(比特每秒)
* @param bufferProgress 缓冲的进度
*/
override onDownloadChanged(speed: Long, bufferProgress: Long) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerDownloadChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerDownloadChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerDownloadChangeListener()
播放器操作不允许执行监听
在某些状态下,一些操作将不被执行,通过该监听回调上来通知
val listener = object: QIPlayerCommandNotAllowListener {
/**
* 操作不被允许
* @param commandName 操作名称
* @param state 操作被检测时播放器的状态
*/
override fun onCommandNotAllow(commandName: String, state: QPlayerState) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerCommandNotAllowListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerCommandNotAllowListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerCommandNotAllowListener()
buffering 监听
val listener = object: QIPlayerBufferingListener {
/**
* 开始buffering
*/
override fun onBufferingStart() {
//展示buffering UI
}
/**
* 结束buffering
*/
override fun onBufferingEnd() {
//隐藏buffering UI
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerBufferingChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerBufferingChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerBufferingChangeListener()
码率
获取当前码率
mQPlayerView.playerControlHandler.biteRate
实时码率监听
val listener = object: QIPlayerBiteRateListener {
/**
* 码率变换回调
* @param biteRate 比特率, 单位:bps
*/
override fun onBiteRateChanged(biteRate: Long) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerBiteRateChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerBiteRateChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerBiteRateChangeListener()
SEI 数据回调
用户自定义数据可以带在H264或者H265的编码帧里,用于实现一些基于时间轴的业务场景。
开启该功能后,如果视频资源内带有SEI数据 会通过监听接口抛给业务层。
该功能需要单独开通,具体可以联系技术支持咨询。
开启 SEI
mQPlayerView.playerControlHandler.setSEIEnable(true)
SEI 数据监听
val listener = object: QIPlayerSEIDataListener {
/**
* SEI 数据回调,且回调时机为SEI数据所在帧的时间
* @param data SEI 数据
*/
override fun onSEIData(data: ByteArray) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerSEIDataListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerSEIDataListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerSEIDataListeners()
音频数据上抛相关监听
开启音频数据监听
mQPlayerView.playerControlHandler.setAudioDataCallbackEnable(true)
音频数据监听
val listener = object: QIPlayerAudioDataListener {
/**
* 音频数据回调
* @param sampleRate 采样率
* @param format 音频位宽
* @param channelNum 声道数
* @param channelLayout 声道布局
* @param data 音频数据
*/
override fun onAudioData(sampleRate: Int, format: QSampleFormat, channelNum: Int, channelLayout:QChannelLayout, data: ByteArray){
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerAudioDataListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerAudioDataListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerAudioDataListeners()
视频数据上抛相关监听
开启视频数据监听
mQPlayerView.playerControlHandler.setVideoDataCallbackEnable(true)
音频数据监听
val listener = object: QIPlayerVideoDataListener {
/***
* 视频数据回调
* @param width 视频宽度
* @param height 视频高度
* @param type 视频数据类型
* @param data 视频数据
*/
override fun onVideoData(width: Int, height: Int, type: QVideoDataType, data: ByteArray) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerVideoDataListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerVideoDataListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerVideoDataListeners()
渲染相关监听
val listener = object: QIPlayerRenderListener {
//首帧耗时回调 从play 开始到首帧渲染出来的总耗时 单位毫秒
override fun onFirstFrameRendered(elapsedTime: Long) {
}
}
//添加监听
mQPlayerView.playerRenderHandler.addPlayerRenderListener(listener)
//移除监听
mQPlayerView.playerRenderHandler.removePlayerRenderListener(listener)
//移除所有监听
mQPlayerView.playerRenderHandler.removeAllPlayerRenderListener()
设置视频渲染比例
mQPlayerView.playerRenderHandler.setRenderRatio(QPlayerSetting.QPlayerRenderRatio.QPLAYER_RATIO_SETTING_AUTO)
视频比例枚举
//自动
QPLAYER_RATIO_SETTING_AUTO(1),
//拉升
QPLAYER_RATIO_SETTING_STRETCH(2),
//铺满
QPLAYER_RATIO_SETTING_FULL_SCREEN(3),
//16:9
QPLAYER_RATIO_SETTING_16_9(4),
//4:3
QPLAYER_RATIO_SETTING_4_3(5),
设置色觉优化
mQPlayerView.playerControlHandler.setBlindType(QPlayerSetting.QPlayerBlind.QPLAYER_BLIND_SETTING_NONE)
色觉优化枚举
//无
QPLAYER_BLIND_SETTING_NONE(0),
//红色盲
QPLAYER_BLIND_SETTING_RED(1),
//绿色盲
QPLAYER_BLIND_SETTING_GREEN(2),
//蓝色盲
QPLAYER_BLIND_SETTING_BLUE(3),
VR视频播放
在播放VR视频时,需要在mediaModel中配置vr视频的资源 并且将streamElement的videoRenderType设置为QVideoRenderType.PANORAMA_EQUIRECT_ANGULAR
然后通过设置vr视频视角改变视频朝向
该功能需要单独开通,具体可以联系技术支持咨询。
/***
* 设置VR视频的旋转角度
* @param rotateX 横向角度 (360度制)
* @param rotateY 纵向角度 (360度制)
* @return true 设置成功 false 设置失败
*/
mQPlayerView.playerRenderHandler.setPanoramaViewRotate(mCurrentRotationY, -mCurrentRotationX)
视频类型枚举
NONE(-1),
//普通视频
PLANE(0),
//ANGULAR类 VR视频
PANORAMA_EQUIRECT_ANGULAR(1),
截图
调用截图接口,就会回调截图时的video画面,并以jpeg的格式返回
mQPlayerView.playerControlHandler.shootVideo()
截图监听
val listener = object: QIPlayerShootVideoListener {
/**
* 截图成功
*/
override fun onShootSuccessful(
image: ByteArray, //图片的二进制数组
width: Int, //图片的宽
height: Int, //图片的高
type: QIPlayerShootVideoListener.ShootVideoType //图片的类型(jpeg)
) {
//保存截图
//展示截图
}
/**
* 截图失败回调
*/
override fun onShootFailed() {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerShootVideoListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerShootVideoListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerShootVideoListeners()
静音
控制播放器静音,且该静音非系统的声音设置,仅仅只是把播放器设置为静音
/**
@param isMute true: 静音 false:非静音
*/
mQPlayerView.playerControlHandler.setMute(true)
静音监听
val listener = object: QIPlayerAudioListener {
//静音改变回调
override fun onMuteChanged(is_mute: Boolean) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerAudioListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerAudioListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerAudioListener()
视频宽高改变监听
在视频首帧渲染时,会调用该回调,告诉上层当前的视频宽高是多少,如果在这个这个视频播放的生命周期中,宽高改变,那么还会再次调用该回调
val listener = object: QIPlayerVideoFrameSizeChangeListener {
//宽:width 高:height
onVideoFrameSizeChanged(width: Int, height: Int) {
}
}
//添加监听
mQPlayerView.playerControlHandler.addPlayerVideoFrameSizeChangeListener(listener)
//移除监听
mQPlayerView.playerControlHandler.removePlayerVideoFrameSizeChangeListener(listener)
//移除所有监听
mQPlayerView.playerControlHandler.removeAllPlayerVideoFrameSizeChangeListener()