产品
基础服务
视频云
人工智能
物联网
区块链
第三方服务
存储服务
云存储
网络加速
CDN
PCDN
动态加速(DCDN)
全站加速(WSA)
奇盾
企业应用
云短信
SSL证书
电子签章
支付系统
帐号服务平台
视频应用
云直播
云点播
音视频通信RTC
媒体处理
视频工具
剪辑SDK
播放SDK
云剪辑
推流SDK
内容安全
图片审核
视频审核
文本审核
音频审核
OCR识别
卡证识别
通用文本识别
车牌识别
人脸与人体识别
人脸实名认证
人脸识别
人体识别
图像技术
以图定位
AI开发平台
机器学习平台 Prophet Pro
AI能力定制平台 Prophet Lite
监控服务
云监控
物联网平台
设备接入
设备管理
规则引擎
应用开发
边缘计算
区块链通用服务
QBaaS
开测平台
兼容性测试
真机租用
运营
消息推送
企业应用
企业直播-小虎盟
企业培训/在线教育/知识付费
教学直播-易讲
视频技术与传统教室融合
智能监控-小安
云边融合, AI使能的智能视频监控
解决方案
通用解决方案
行业解决方案
视频
通用直播解决方案
短视频解决方案
公共语音房聊天室解决方案
物联网
儿童手表音视频通话
云端NVR解决方案
数据上云
通用存储解决方案
IoT设备
摄像机
门锁
安防传感
网关
个护健康
账号体系
360用户帐号体系解决方案
智慧城市
智慧社区
智慧交通
电商
电商视频解决方案
电商平台收款解决方案
教育
在线教育解决方案
互动课堂解决方案
全屋智能
智慧公寓
智慧酒店
家庭智能
智慧安防
医疗
健康看护
智能制造
工业物联网
游戏
游戏音视频解决方案
帮助支持
关于我们
技术社区
控制台
登录
注册
SDK管理
云直播
产品文档
常见问题
API文档
云点播
产品文档
快速入门
API文档
CDN
产品文档
API文档
云存储
产品文档
SDK手册
API文档
互动直播
产品文档
API文档
操作指南
SDK管理
播放SDK
上传SDK
剪辑SDK
媒体处理
产品文档
API文档
云监控
产品文档
API文档
操作指南
帐号服务平台
整体介绍
SDK文档
支付平台
产品列表
产品文档
渠道接入解析
内容审核
产品文档
API文档
OCR识别
产品文档
API文档
人脸与人体识别
产品文档
API文档
SSL证书
产品说明
购买指南
电子签章
产品介绍
接入流程
云短信
接口文档
物联网平台
产品简介
快速入门
自定义设置->def_doc_id。")">小虎盟
Android互动直播
开发文档
版本说明
Windows互动直播
开发文档
版本说明
iOS互动直播
开发文档
Android推流
开发文档
版本说明
iOS推流
iOS推流SDK开发文档
Android上传SDK
Android上传SDK接入文档
Android上传SDK版本说明
IOS上传SDK
iOS上传sdk开发文档
IOS上传SDK版本说明
Net SDK
iOS
开发文档
版本说明
Android
开发文档
版本说明
IoT SDK
iOS
开发文档
版本说明
Android
开发文档
版本说明
Web
开发文档
版本说明
固件端
开发文档
WEB播放SDK
WEB播放器介绍
跨域请求媒体源失败
QHWW-Player
拍摄SDK
iOS
开发文档
版本说明
Android
开发文档
版本说明
剪辑SDK
iOS
版本说明
开发文档
Android
开发文档
版本说明
投屏SDK
iOS
开发文档
Android
开发文档
播放SDK
iOS
开发文档
版本说明
Android
开发文档
版本说明
Web 上传SDK
首页
>
开发者中心
>
SDK管理
>
播放SDK
>
Android
>
开发文档
# Android播放SDK开发文档 ### Android播放SDK简单介绍 智汇云以SDK形式提供视频直播点播播放器,可以帮助开发者快速实现视频播放能力。SDK包含jar包、so库、demo及开发文档。智汇云SDK支持插件机制,可以缩小嵌入客户端的大小。 ### 功能说明 系统属性 | 系统特性 | 支持内容 | | -------- | ------------ | | 系统版本 | 4.0+ | | 流媒体协议 | RTMP、http(s)、http(m3u8) | | 硬件特性 | armv5、armv7、x86、arm64 | 支持协议 | 直播协议 | 协议优势 | 缺陷 | | -------- | ------------ | ------------ | | RTMP | 延时相对较低 | 大并发时消耗性能较大且防火墙不友好 | | http(FLV)| 延时比较低 | 对防火墙友好,但移动端支持不友好 | | http(M3u8)| 在移动端支持最好,延时比较大,一般在10-20s左右 |延时比较大 | 点播协议 | 协议优势 | 缺陷 | | -------- | ------------ | ------------ | | http(M3u8)| 移动端支持比较好 | 多个小文件分发,系统维度难度相对大 | | http(FLV)| 一般直播转回放可以用 | 移动端需要用SDK播放 | | http(mp4)| 在移动端支持相对好 | 对播放器要求比较高,容错性比较差 | 解码库说明 类型 | 体积 | 支持协议 | 支持容器 | 适用场景 ---|---|---|---|---|--- 系统播放器 | 无体积 |http |以系统支持为准,不支持flv,解码器少|个别视频播放, 精简版 | 小 | http/https/rtmp/hls | mp4/flv/m3u8/ts | 在乎体积大小,点播播放和直播flv场景 录制版 | 中 | http/https/rtmp/hls | mp4/flv/m3u8/ts |在乎体积大小,附带边播边录功能 全解码版 | 大 | http/https/rtmp/hls | mp4/flv/m3u8/ts/avi/rm/rmvb/mkv/mov/... | 体积较大,但可以播放绝大多数视频 含录制全解码 | 大 | http/https/rtmp/hls | mp4/flv/m3u8/ts/avi/rm/rmvb/mkv/mov/... | 能播放市面上绝大多数视频,且还能边播边录制到本地 功能列表 | 系统特性 | 支持内容 | |--------------|----------------------------------------------------| | 解码方式 | 支持软硬解码切换 | | 基础播放功能 | 开始、暂停、停止、快进、快退、静音等基础播放器功能 | | 播放屏幕 | 全屏、非全屏 | | 播放截图 | 支持从视频流中截图 | |画面填充|直冲多种画面的预览模式包括:原始尺寸、视频屏幕、全屏铺满、16:9、4:3等| |获取文件meta信息|支持获取视频文件属性信息| | 解码标准 | 以用户选择为准 | | 视频格式 | 以用户选择为准 | | 多实例播放 | 多实例支持 | | 倍速播放 | 支持 | | 视频缓存支持 |支持,支持视频下载 | |播放器秒开|在1s之内快速打开视频播放| |是否追帧|支持自由设置| |缓冲策略|支持自由配置 |边播放边录制|支持| |云引擎配置|支持云引擎配置参数| |多码率播放|支持无缝切换多码率播放,支持网络自适应| |渲染后置处理|支持业务自定义后置处理方式,自由接管渲染模式| |画质设置|支持亮度、饱和度、锐利度设置| |镜像播放|支持镜像播放| |统计信息,如实时帧率、码率|帧率、码率实时信息回调给客户,方便客户做不同网络状况下的业务| ### 业务流程图  ### SDK集成 #### 下载SDK sdk下载链接:[https://zyun.360.cn/developer](https://zyun.360.cn/developer) - jar > qhvc_tools_sdk.jar > qhvc_player_sdk.jar - so > armeabi-v7a/libtranscore.so > armeabi-v7a/libqhvc_breakpad.so > armeabi-v7a/libjplayer.so > armeabi-v7a/libviewer.so > armeabi-v7a/libffmpeg.so demo下载链接:[https://github.com/360livecloud/android_demo.git](https://github.com/360livecloud/android_demo.git) #### 配置说明 将qhvc_tools_sdk.jar、qhvc_player_sdk.jar放到工程libs目录下,在build.gradle中配置: ``` dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') } ``` 将libffmpeg.so、libtranscore.so、libqhvc_breakpad.so、libjplayer.so、libviewer.so放到工程src/main/jniLibs/armeabi-v7a/目录下,在build.gradle中配置: ``` android { defaultConfig { ndk { // 设置支持的CPU架构,目前只支持armeabi-v7a、arm64-v8a abiFilters 'armeabi-v7a' } } } ``` 注: 1. 若qhvc_tools_sdk.jar、libtranscore.so已在接入其他模块时引入,请保留高版本,版本号可通过`Stats.getVersion()`获取。 2. 若需减小APK体积,请参考#插件化支持# #### 权限配置 ``` <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ``` #### 混淆规则 ``` -keep class com.qihoo.livecloud.** { <fields>; <methods>; } -keep class com.qihoo.videocloud.** { <fields>; <methods>; } -keep class net.qihoo.videocloud.** { <fields>; <methods>; } -keep class com.qihoo.bugreport.** { <fields>; <methods>; } -keep class com.qihoo.jiagu.** { <fields>; <methods>; } -keep class com.yunfan.net.** { <fields>; <methods>; } ``` #### SDK初始化 需要调用`QHVCSdk.getInstance().init()`,接口说明如下: appId(也叫businessId)是为业务分配的业务ID(appId获取地址:登录[视频云官网](https://zyun.360.cn/)-控制台-SDK-应用管理-新建应用)【见下图】;version为业务端APP的版本号;machineId为设备唯一标识;uid为业务方用户标识,只要保证唯一即可。  [点此查看应用ID](https://zyun.360.cn/console/sdk/application) > 对appId和channelID这两个参数的引入,在设计之初目的是为了每个业务有独立的ID,通过appId来获取云端的一些参数控制,特别是视频缓冲策略、P2P行为、硬解码黑白名单以及解码库类型;channelID更多的为数据统计打点的唯一标注,可以通过云点播和云直播的服务标识来获取,以此获取播放数据反馈。UserID的设计是为了业务方和视频云唯一的沟通桥梁,只要保持唯一即可,一般为用户ID的加密字符串,通过UserID可以排查到一个用户的级别的失败日志; ``` QHVCSdkConfig.Builder builder = new QHVCSdkConfig.Builder(this) .setAppId("appId") .setAppVersion("version") .setMachineId("machineId") .setUserId("uid"); QHVCSdk.getInstance().init(builder.build()); ``` #### APPID合法性验证 需要调用 `public int validityCheck(String appId, String authorization, long authTime, int randomNum, final QHVCAppAuth.ResultCallback<String> callback)`,接口说明如下: 合法性验证需要用到appId, Access Key(简称AK)和Secret Key(简称SK)。AK、SK获取地址: 登录[视频云官网](https://zyun.360.cn/)-用户中心-秘钥管理【见下图】。 只有appId、Access Key以及Secret Key相匹配,才能通过鉴权,否则将无法正常使用SDK。  [点此查看秘钥](https://zyun.360.cn/console/usercenter/accesskey) ==注意:此接口必须在启动APP后120秒内调用,否则将无法正常使用SDK。== appId使用和SDK初始化时同样的内容,callback可以用来监听合法性验证结果。 计算authorization时需要使用AK、SK以及当前系统的时间戳和随机数。 **为了保证AK、SK的安全,请业务接入时,务必把AK和SK存储在服务端,并且把authorization的计算也放在服务端来做。** 计算authorization的示例代码如下: ``` private void videocloudValidCheck() { int randomNum = new Random(100000000).nextInt(); //随机数 long authTime = System.currentTimeMillis() / 1000; //时间戳 (unix时间戳(10位)) //TODO 为了保证ak、sk的安全,请业务接入时,务必把AK和SK存储在服务端,并且把authorization的计算也放在服务端来做。 String authorization = getServerAuthorization(appId, AK, SK, randomNum, authTime); QHVCSdk.getInstance().validityCheck(appId, authorization, authTime, randomNum, new QHVCAppAuth.ResultCallback<String>() { @Override public void onSuccess(String data) { Logger.i(TAG, "validityCheck onSuccess, data: " + data); } @Override public void onFailed(int errCode, String errMsg) { Logger.e(TAG, "validityCheck onFailed, errCode: " + errCode + ", errMsg: " + errMsg); } }); } private String getServerAuthorization(String appID, String ak, String sk, int randNum, long authTime) { HashMap<String, String> mapParams = new HashMap<>(); mapParams.put("appid", appID); String strParams = getParams(mapParams); String paramSign = makeParamSign(strParams, randNum); String authString = ak + "\n" + authTime + "\n" + randNum + "\n" + paramSign; String encryptString = null; try { encryptString = encryptHMAC(authString, sk); } catch (Exception e) { e.printStackTrace(); } Logger.i(TAG, "apiAuth(), encryptString: " + encryptString); return ak + ":" + encryptString; } private String getParams(Map<String, String> mapParams) { String strParams; if (mapParams == null || mapParams.isEmpty()) { strParams = ""; } else { StringBuilder sbParams = new StringBuilder(); Object[] key_arr = mapParams.keySet().toArray(); Arrays.sort(key_arr);/*key升序排列*/ for (Object key : key_arr) { String value = mapParams.get(key); if (!TextUtils.isEmpty(value)) { sbParams.append("&").append(key).append("=").append(value); } } strParams = sbParams.substring(1); } return strParams; } private String makeParamSign(String param, int randNum) { return MD5.encryptMD5(MD5.encryptMD5(param) + randNum); } public String encryptHMAC(String data, String key) throws Exception { final String KEY_MAC = "HmacSHA1"; SecretKey secretKey = new SecretKeySpec(key.getBytes(), KEY_MAC); Mac mac = Mac.getInstance(KEY_MAC); mac.init(secretKey); byte[] rawHmac = mac.doFinal(data.getBytes()); return Base64.encodeToString(rawHmac, Base64.DEFAULT); } ``` 相关错误码: ``` int AUTH_OK = 1; //鉴权成功 int AUTH_FAILED = -11001; //鉴权未通过(鉴权失败) int AUTH_NOT_INVOKED = -11002; //未调用鉴权接口 ``` #### 调试 为便于接入时定位问题,可以在开发版本中打开logcat日志,播放相关TAG为QHVCPlayer。 ``` DebugUtils debugUtils = new DebugUtils(); debugUtils.setWriteLogs(true) .setPlayerLogLevel(Constants.ELogLevel.LOG_LEVEL_DEBUG) .setTransportLogLevel(Constants.ELogLevel.LOG_LEVEL_DEBUG); QHVCSdkConfig.Builder builder = new QHVCSdkConfig.Builder(this) .setAppId("appId") .setAppVersion("version") .setMachineId("machineId") .setUserId("uid"), .setDebugUtils(debugUtils); QHVCSdk.getInstance().init(builder.build()); ``` 除SDK初始化时可指定是否开启日志外,业务方也可以调用以下接口随时开启或关闭日志。 ``` DebugUtils debugUtils = new DebugUtils(); debugUtils.setWriteLogs(true) .setPlayerLogLevel(Constants.ELogLevel.LOG_LEVEL_DEBUG) .setTransportLogLevel(Constants.ELogLevel.LOG_LEVEL_DEBUG); /** * 更新日志配置 * * @param debugUtils 日志配置 */ QHVCSdk.getInstance().updateDebugUtils(debugUtils); ``` ### 接口说明 #### 直播 参考: demo\src\main\java\com\qihoo\videocloud\player\live\LiveActivity.java ##### 创建渲染View ``` <com.qihoo.videocloud.view.QHVCTextureView android:id="@+id/playView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` ``` public class LiveActivity extends Activity { private IQHVCPlayerAdvanced qhvcPlayer; private QHVCTextureView playView; @Override protected void onCreate(Bundle savedInstanceState) { playView = (QHVCTextureView) findViewById(R.id.playView); } } ``` ##### 播放视频 ``` private void startPlay() { qhvcPlayer = new QHVCPlayer(this); playView.onPlay(); playView.setPlayer(qhvcPlayer); qhvcPlayer.setDisplay(playView); qhvcPlayer.setDataSource(IQHVCPlayer.PLAYTYPE_LIVE, url, null); qhvcPlayer.setOnPreparedListener(new IQHVCPlayer.OnPreparedListener() { @Override public void onPrepared() { qhvcPlayer.start(); } }); qhvcPlayer.setOnInfoListener(new IQHVCPlayer.OnInfoListener() { @Override public void onInfo(int handle, int what, int extra) { if (what == IQHVCPlayer.INFO_DEVICE_RENDER_QUERY_SURFACE) { if (playView != null) { if (qhvcPlayer != null && !qhvcPlayer.isPaused()) { playView.render_proc(PlayerCallback.DEVICE_RENDER_QUERY_SURFACE, 0/*不使用此变量*/); } } } else if (what == IQHVCPlayer.INFO_RENDER_RESET_SURFACE) { if (playView != null) { playView.pauseSurface(); } } } }); qhvcPlayer.prepareAsync(); } ``` ##### 停止播放 ``` private void stopPlay() { playView.stopRender(); qhvcPlayer.stop(); qhvcPlayer.release(); qhvcPlayer = null; } ``` #### 点播 参考:demo\src\main\java\com\qihoo\videocloud\player\vod\VodActivity.java 与直播方式基本一致,仅在设置数据源时略有不同: ``` qhvcPlayer.setDataSource(IQHVCPlayer.PLAYTYPE_VOD, url, null); ``` #### 其他 接口使用方式可参考Android系统播放器MediaPlayer。 ### 接口详解 #### 基础API ``` /** * 设置播放器渲染View * * @param display: 目前支持GL2VideoView、TexturePlayView、GLTextureView、LiveCloudPlayView */ void setDisplay(ILiveCloudDisplay display); ``` ``` /** * 设置视频源 * * @param playType 播放类型 {@link #PLAYTYPE_LIVE}, {@link #PLAYTYPE_VOD} * @param url 视频源地址 * @param channelId 业务id * @throws IOException * @throws IllegalArgumentException * @throws IllegalStateException */ void setDataSource(@PlayType int playType, @NonNull String url, @NonNull String channelId) throws IOException, IllegalArgumentException, IllegalStateException; /** * 设置视频源 * * @param playType 播放类型 {@link #PLAYTYPE_LIVE}, {@link #PLAYTYPE_VOD} * @param url url * @param channelId 业务id * @param options 播放器可选属性 {@link IQHVCPlayerAdvanced.OptionKey} * @throws IOException * @throws IllegalArgumentException * @throws IllegalStateException */ void setDataSource(@PlayType int playType, @NonNull String url, @NonNull String channelId, @Nullable Map<String, Object> options) throws IOException, IllegalArgumentException, IllegalStateException; ``` ``` /** * 准备播放,异步方法<br> * 注: 调用此方法开始走调度流程。<br><br> * <p> * 成功: 回调OnPreparedListener的onPrepared()接口 - 调度成功,应用层可调用start(0)开始播放<br> * 失败: 回调onError(ERROR_PREPARE_FAILED, ERROR_EXTRA_PREPARE_DISPATCH_FAILED)<br> */ void prepareAsync() throws IllegalStateException; ``` ``` /** * 开始播放 */ void start() throws IllegalStateException; /** * 是否正在播放 */ boolean isPlaying(); /** * 拖动播放,目标时间点单位:ms(毫秒)<br> * 目前只支持回看拖动<br> * * @param millis 要拖动到的目标时间 */ void seekTo(int millis) throws IllegalStateException; /** * 暂停播放 */ void pause() throws IllegalStateException; /** * 视频是否处于暂停状态 */ boolean isPaused(); /** * 结束播放 */ void stop() throws IllegalStateException; /** * 播放器资源回收 */ void release(); ``` ``` /** * 获取当前已播放时间,单位:ms(毫秒)<br> * 注: 只支持点播(回看)<br> * * @return 当前已播放时间 */ int getCurrentPosition(); /** * 获取视频文件总时长,单位:ms(毫秒)<br> * 注: 只支持点播(回看)<br> * * @return 视频文件总时长 */ int getDuration(); /** * 获取视频信息 * * @return */ Map<String, Object> getMediaInformation(); ``` ``` /** * 设置是否静音播放 * * @param mute <br>true: 静音播放,<br>false:正常播放, 默认是false */ void setMute(boolean mute) throws IllegalStateException; /** * 是否是静音状态 * * @return ture - 当前是静音状态 */ boolean isMute(); /** * 设置音量 * * @param volume 音量范围 0.0~1.0(1.0最大) */ void setVolume(float volume) throws IllegalStateException; /** * 获取播放器当前音量 * * @return 音量范围 0.0~1.0(1.0最大) <0 无效 * @throws IllegalStateException */ float getVolume() throws IllegalStateException; ``` ``` /** * 设置播放时是否屏幕常亮 * * @param screenOn <br>true: 屏幕常亮, <br>false:系统默认, 默认是true。 */ void setScreenOnWhilePlaying(boolean screenOn); ``` ``` interface OnPreparedListener { /** * Called when the media file is ready for playback. */ void onPrepared(); } /** * Register a callback to be invoked when the media source is ready for playback. * * @param listener */ void setOnPreparedListener(OnPreparedListener listener); /** * Interface definition of a callback to be invoked indicating * the completion of a seek operation. */ interface OnSeekCompleteListener { /** * Called to indicate the completion of a seek operation. * * @param handle playerId. the player that issued the seek operation */ void onSeekComplete(int handle); } /** * Register a callback to be invoked when a seek operation has been completed. * * @param listener */ void setOnSeekCompleteListener(OnSeekCompleteListener listener); /** * Interface definition of a callback to be invoked when there * has been an error during an asynchronous operation (other errors * will throw exceptions at method call time). */ interface OnErrorListener { /** * Called to indicate an error. * * @param handle playerId. the player the error pertains to * @param what the type of error that has occurred: * @param extra an extra code, specific to the error. Typically * implementation dependent. * @return True if the method handled the error, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the OnCompletionListener to be called. */ boolean onError(int handle, int what, int extra); } /** * Register a callback to be invoked when an error has happened during an asynchronous operation. * * @param listener */ void setOnErrorListener(OnErrorListener listener); /** * Interface definition for a callback to be invoked when playback of * a media source has completed. */ interface OnCompletionListener { /** * Called when the end of a media source is reached during playback. * * @param handle playerId. the player that reached the end of the file */ void onCompletion(int handle); } /** * Register a callback to be invoked when the end of a media source has been reached during playback. * * @param listener */ void setOnCompletionListener(OnCompletionListener listener); /** * Interface definition of a callback to be invoked to communicate some * info and/or warning about the media or its playback. */ interface OnInfoListener { /** * Called to indicate an info or a warning. * * @param handle playerId. * @param what the type of info or warning. * @param extra an extra code, specific to the info. Typically * implementation dependent. * @return True if the method handled the info, false if it didn't. */ void onInfo(int handle, int what, int extra); } /** * Register a callback to be invoked when an info/warning is available. * * @param listener */ void setOnInfoListener(OnInfoListener listener); /** * Interface definition of a callback to be invoked when the * video size is first known or updated */ interface OnVideoSizeChangedListener { /** * Called to indicate the video size * <p> * The video size (width and height) could be 0 if there was no video, * no display surface was set, or the value was not determined yet. * * @param handle the player associated with this callback * @param width the width of the video * @param height the height of the video */ void onVideoSizeChanged(int handle, int width, int height); } /** * Register a callback to be invoked when the video size is known or updated. * * @param listener */ void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener); /** * Interface definition of a callback to be invoked indicating buffering * status of a media resource being streamed over the network. */ interface OnBufferingUpdateListener { /** * Called to update status in buffering a media stream received through * progressive HTTP download. The received buffering percentage * indicates how much of the content has been buffered or played. * For example a buffering update of 80 percent when half the content * has already been played indicates that the next 30 percent of the * content to play has been buffered. * * @param handle handle * @param percent the percentage (0-100) of the content * that has been buffered or played thus far */ void onBufferingUpdate(int handle, int percent); } /** * Register a callback to be invoked when the status of a network stream's buffer has changed. * * @param listener */ void setOnBufferingUpdateListener(OnBufferingUpdateListener listener); /** * 缓冲事件 * <p> * 播放器根据缓冲策略,缓冲一定量的数据才会开始播放。 */ interface OnBufferingEventListener { /** * 播放器开始缓冲 */ void onBufferingStart(int handle); /** * 缓冲进度 * * @param handle handle * @param progress 缓冲进度 */ void onBufferingProgress(int handle, int progress); /** * 播放器结束缓冲 * <p> * 在播放状态下,缓冲结束,自动开始播放。 */ void onBufferingStop(int handle); } void setOnBufferingEventListener(OnBufferingEventListener listener); interface onProgressChangeListener { /** * 播放进度回调, 只有点播(回看)才会回调 * * @param total 总长度, 单位:毫秒 * @param progress 当前播放位置, 单位: 毫秒 */ void onProgressChange(int handle, int total, int progress); } void setOnProgressChangeListener(onProgressChangeListener listener); ``` #### 高级API ``` /** * 为播放器设置渲染SurfaceTexture * * @param from * @param texture {@link SurfaceTexture} 播放器渲染的texture */ public void setSurface(final String from, final SurfaceTexture texture); /** * 为播放器渲染的surface设置Viewport * * @param x x位置 * @param y y位置 * @param width 宽度 * @param height 高度 */ public void setSurfaceViewport(int x, int y, int width, int height); /** * 获取播放器句柄 */ int getPlayerId(); ``` ``` /** * 设置视频源 * * @param playType 播放类型 {@link #PLAYTYPE_LIVE}, {@link #PLAYTYPE_VOD} * @param sn sn (需要走调度流程) * @param channelId 业务id * @param sign 鉴权签名 * @param options 播放器可选属性 {@link OptionKey} * @throws IOException * @throws IllegalArgumentException * @throws IllegalStateException */ void setDataSource(@PlayType int playType, @NonNull String sn, @NonNull String channelId, @NonNull String sign, @Nullable Map<String, Object> options) throws IOException, IllegalArgumentException, IllegalStateException; ``` ``` /** * 获取当前使用的解码模式 * * @return 解码模式 {@link #LIVECLOUD_SOFT_DECODE_MODE}, {@link #LIVECLOUD_SMART_DECODE_MODE} */ @DecodeMode int getDecoderMode(); /** * 设置播放器是否渲染图像 * * @param isRender: <br>true: 不渲染图像,只播放声音 * <br>false:播放声音和图像 */ void disableRender(boolean isRender) throws IllegalStateException; ``` ``` /** * 精确seekTo * * @param millis 目标时间点,单位:毫秒 * @param accurate 是否进行精准seek;accurate=false等同于void seekTo(long msec); */ void seekTo(int millis, boolean accurate) throws IllegalStateException; /** * 倍速播放 * * @param rate 播放速度,取值1~n(建议n<=5) */ void setPlayBackRate(float rate) throws IllegalStateException; /** * 停止播放 * * @param reason <br>停止播放的原因<br> * @link #USER_CLOSE}{@link #PLAY_OVER} {@link #PLAY_ERROR} {@link #PLAY_INCOMING_CALL}{@link #PLAY_OTHER} */ void stop(@StopReason int reason) throws IllegalStateException; ``` ``` interface OnPlayerNetStatsListener { /** * 流量计算回调函数 * * @param handle * @param dvbps 播放器下行视频每秒字节数 * @param dabps 播放器下行音频每秒字节数 * @param dvfps 播放器下行视频每秒帧数 * @param dafps 播放器下行音频每秒帧数 * @param fps 当前fps * @param bitrate 当前bitrate * @param param1 保留 * @param param2 保留 * @param param3 保留 */ void onPlayerNetStats(int handle, long dvbps, long dabps, long dvfps, long dafps, long fps, long bitrate, long param1, long param2, long param3); } /** * 流量统计信息回调(暂时只支持直播) * * @param listener */ void setOnPlayerNetStatsListener(OnPlayerNetStatsListener listener); interface OnAudioPCMListener { /** * pcm 数据回调 * * @param handle player handle * @param id 帧数据id * @param buffer 帧数据 * @param timestamp time stamp * @param channels 声道数 * @param sampleRate 采样率 * @param bitsPerSample 采样位数 */ void onAudioPCM(int handle, int id, ByteBuffer buffer, long timestamp, int channels, int sampleRate, int bitsPerSample); } /** * 设置音频数据回调(只支持直播) * * @param listener */ void setOnAudioPCMListener(OnAudioPCMListener listener); ``` ### 附加功能 #### 截图 ``` /** * 截图 (同步方法,会阻塞线程) * * @param path 存储路径 * @return */ boolean snapshot(String path); ``` #### 视频录制 ``` /** * 开始录制 * <p> * 注意:不支持暂停时录制<br> * 录制mp4: fmt使用 {@link IQHVCPlayerAdvanced#RECORDER_FORMAT_MP4}, Config使用VideoRecordConfig * 录制gif: fmt使用 {@link IQHVCPlayerAdvanced#RECORDER_FORMAT_GIF}, Config使用GifRecordConfig * * @param filePath 录像存储路径(确保有读写权限) * @param fmt 存储格式 {@link RecorderFormat} <br> * @param config 配置 * @param listener callback * @return 0:成功 <0: 失败 */ int startRecorder(String filePath, @RecorderFormat String fmt, RecordConfig config, OnRecordListener listener); /** * 结束录制(异步接口) * * @return 0: 调用成功 * <0: 调用失败 */ int stopRecorder(); ``` #### 多码率支持 ``` /** * 切换分辨率<br> * 此接口是异步的,从切换分辨率到切换完成 有5秒延迟 * * @param index 数据源索引,{@link #setDataSource(String[] source, int index, int playType)}设置的source的索引 * @param listener 状态回调 */ void switchResolution(int index, QHVCSwitchResolutionListener listener); /** * 取消分辨率切换 * * @param reason 取消切换原因 */ void switchResolutionStop(String reason); /** * 自动切换分辨率 * * @param isAdapt 是否根据网络状况自动切换分辨率 * @param listener 状态回调 */ void setResolutionAdapt(boolean isAdapt, QHVCSwitchResolutionListener listener); /** * 切分辨率错误 */ interface QHVCSwitchResolutionError { /** * 不支持的状态 * <p> * - 仅支持在播放状态下切换<br> * - 一次切换结束后,才能进行下一次切换<br> */ int NOT_SUPPORT_STATUS = -1; /** * 切换失败 */ int FAILED = -2; } /** * 切换分辨率回调 */ interface QHVCSwitchResolutionListener { /** * 切换准备 */ void onPrepare(); /** * 切换中 */ void onStart(); /** * 切换分辨率成功 * * @param index 当前播放source index * @param url 当前播放url */ void onSuccess(int index, String url); /** * 切换失败 * * @param errorCode 错误码 {@link QHVCSwitchResolutionError} * @param errorMsg 错误信息 */ void onError(int errorCode, String errorMsg); } ``` ### 插件化支持 为减小APK体积,播放SDK支持插件化方式接入,业务方仅需接入JAR包即可,不需要捆包带SO文件,最终APK体积预计增加500KB。 > 播放器插件下载并加载成功前,播放SDK会使用系统播放器保障基本的播放需求,请使用`QHVCPlayerBuilder.getInstance().getPlayer(context)`创建播放器实例,不要使用`new QHVCPlayer()`。系统播放器功能有限,请业务方根据当前创建的播放器类型处理业务需求。判断是否是系统播放器,请调用`isSystemMediaPlayer()`接口。 ``` /** * 创建播放器实例 * * @param context context * @return 若智汇云播放器插件已下载并加载,则使用智汇云播放器;否则使用系统播放器 */ QHVCPlayerBuilder.getInstance().getPlayer(Context context); ``` 插件化基本流程:  ``` /** * 设置是否自带插件,如果使用插件化方式接入,该接口必须第一个被调用,必须早于QHVCPlayerBuilder.getInstance().getPlayer(context)接口 * * @param defaultPluginInstalled true自带,false不自带 */ QHVCPlayerPlugin.getInstance().setDefaultPluginInstalled(boolean defaultPluginInstalled); ``` ``` /** * 安装插件 * * @param context context * @param listener 下载监听接口,若业务方需要自行实现插件下载功能,需要实现该接口 * @param callback 回调接口 * @return 错误码,{@link LiveCloudPluginConstant#ERROR_INSTALL_RUNNING}表示插件正在后台安装或升级, * {@link LiveCloudPluginConstant#ERROR_SUCCESS}表示插件开始安装或升级 */ QHVCPlayerPlugin.getInstance().checkInstallPlugin(Context context, final PluginDownloadListener listener, final PluginCallback callback); ``` ``` /** * 升级插件,若新插件已下载,执行本地升级逻辑;否则后台静默下载新插件,待APP下次启动时再次执行本地升级逻辑<br> * 注意:<br> * 1.该接口需要在{@link #setDefaultPluginInstalled(boolean)}接口后且{@link #loadPlugin()}接口前调用<br> * 2.为避免插件下载占用网络带宽进而影响当前业务,建议设置{@link PluginDownloadListener}参数自行选择时机下载插件<br> * 3.如无特殊需求,业务方无需主动调用该接口,播放器插件会在后台自动升级 * * @param context context * @param listener 下载监听接口,若业务方需要自行实现插件下载功能,需要实现该接口 * @return 错误码,参见{@link LiveCloudPluginConstant#ERROR_UNKNOWN}等 */ QHVCPlayerPlugin.getInstance().checkUpdatePlugin(Context context, final PluginDownloadListener listener); /** * 设置插件后台静默升级下载监听接口<br> * 注意:插件会后台自动升级,业务方可设置该监听接口以便管理下载时机。 * * @param listener 下载监听接口,若业务方需要自行实现插件下载功能,需要实现该接口 */ QHVCPlayerPlugin.getInstance().setPluginDownloadListener(PluginDownloadListener listener); ``` ``` /** * 加载已安装的插件 * * @return 错误码,参见{@link #ERROR_UNKNOWN}等 */ QHVCPlayerPlugin.getInstance().loadPlugin(); ``` ``` /** * 移除插件,该接口必须在加载插件前调用方可生效 * * @return 错误码,参见{@link LocalServerPlugin#ERROR_UNKNOWN}等 */ QHVCPlayerPlugin.getInstance().removePlugin(); ``` 示例: ``` public void initPlayerProxy(Context context) { final QHVCPlayerPlugin liveCloudPlugin = QHVCPlayerPlugin.getInstance(); //设置接入时不带插件,使用插件化方案时必须第一个调用该接口 liveCloudPlugin.setDefaultPluginInstalled(false); if (liveCloudPlugin.isDefaultPluginInstalled()) { initPlayer(context); } else if (liveCloudPlugin.isPluginInstalled()) { //插件已下载,必须加载后才能使用 int result = liveCloudPlugin.loadPlugin(); if (result == QHVCPlayerPlugin.ERROR_SUCCESS) { initPlayer(context); } } else { liveCloudPlugin.checkInstallOrUpdatePlugin(context, new QHVCPlayerPlugin.PluginCallback() { @Override public void onStart(Context context) { } @Override public void onProgress(Context context, int progress) { } @Override public void onComplete(Context context, boolean background, int result) { if (result == QHVCPlayerPlugin.ERROR_SUCCESS) { //插件下载完成,必须加载后才能使用 int loadResult = liveCloudPlugin.loadPlugin(); if (loadResult == QHVCPlayerPlugin.ERROR_SUCCESS) { initPlayer(context); } } } @Override public void onCancel(Context context) { } }); } } private void initPlayer(Context context) { //此时调用QHVCPlayerBuilder.getInstance().getPlayer(Context context)接口返回的即为智汇云播放器,否则为系统播放器 } ``` ### 多类型播放器支持 智汇云SDK支持多种类型的的播放器,分别为: 类型 | 体积 | 支持协议 | 支持容器 | 编码器 ---|---|---|---|---|--- 系统播放器 | 0 | 精简版 | 小 | http/rtmp/hls | mp4/flv/m3u8/ts | 无 录制版 | 中 | http/rtmp/hls | mp4/flv/m3u8/ts | H264/AAC 多解码器版 | 大 | http/rtmp/hls | mp4/flv/m3u8/ts/avi/rm/rmvb/mkv/mov/... | 无 多解码器+录制版 | 大 | http/rtmp/hls | mp4/flv/m3u8/ts/avi/rm/rmvb/mkv/mov/... | H264/AAC 业务方可以在接入SDK时捆包自带播放器,也可以使用插件化方式动态下载指定类型的播放器,该接口需要在`loadPlugin()`前调用,建议与`setDefaultPluginInstalled(boolean defaultPluginInstalled)`一起调用。 ``` /** * 设置播放器插件类型,默认为标准版本<br> * 注意:该接口需要在{@link #loadPlugin()}接口前调用方可生效 * * @param playerPluginType 播放器插件类型,参见{@link #TYPE_NORMAL}等 */ QHVCPlayerPlugin.getInstance().setPlayerPluginType(String playerPluginType); ``` ### 视频渲染自定义 播放器视频渲染通过OpenGL实现,业务方可通过自定义顶点着色器、片段着色器实现自己的渲染效果,也可以在视频渲染基础上绘制其他内容。参考示例代码[下载](http://rs-beijing.oss.yunpan.360.cn/Object.getFile/livecloudsdk/Q3VzdG9tUGxheWVyUmVuZGVyLmphdmE=)。 ``` /** * 设置视频渲染高级接口,必须在{@link IQHVCPlayer#setDisplay(ILiveCloudDisplay)}前调用 * * @param surfaceRenderListener 视频渲染高级接口 */ QHVCPlayer.setSurfaceRenderListener(SurfaceRenderListener surfaceRenderListener); ``` ``` /** * 视频渲染高级接口,业务方可通过自定义顶点着色器及片段着色器修改默认的渲染 * <p> * <p>顶点着色器: * <pre> * private final static String VERTEX_SHADER = "" + "uniform mat4 uTextureMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTextureCoord = (uTextureMatrix * aTextureCoord).xy;\n" + "}"; * </pre> * <p>片段着色器: * <pre> * private final static String FRAGMENT_SHADER_OES = "" + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES sTexture;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}"; * </pre> */ public interface SurfaceRenderListener { /** * 顶点着色器-默认顶点着色器 */ String VERTEX_DEFAULT_SHADER = BaseGLRender.VERTEX_SHADER_MATRIX_TEXTURE; /** * 顶点着色器-顶点坐标<br> * attribute vec4 aPosition; */ String VERTEX_ATTRIBUTE_POSITION = "aPosition"; /** * 顶点着色器-纹理坐标<br> * attribute vec4 aTextureCoord; */ String VERTEX_ATTRIBUTE_TEXTURE_COORD = "aTextureCoord"; /** * 顶点着色器-纹理变换矩阵<br> * uniform mat4 uTextureMatrix */ String VERTEX_UNIFORM_TEXTURE_MATRIX = "uTextureMatrix"; /** * 顶点着色器-变换后的纹理坐标,用于片段着色器<br> * varying vec2 vTextureCoord; */ String VERTEX_VARYING_TEXTURE_COORD = "vTextureCoord"; /** * 片段着色器-默认片段着色器 */ String FRAGMENT_DEFAULT_SHADER = BaseGLRender.FRAGMENT_SHADER_OES; /** * 片段着色器-纹理单元<br> * uniform samplerExternalOES sTexture; */ String FRAGMENT_UNIFORM_TEXTURE = "sTexture"; /** * 创建着色器程序 * * @param program 着色器程序 */ void onCreateProgram(int program); /** * 删除着色器程序 * * @param program 着色器程序 */ void onDeleteProgram(int program); /** * 创建着色器 * * @param type 着色器类型,参见{@link GLES20#GL_VERTEX_SHADER}、{@link GLES20#GL_FRAGMENT_SHADER} * @return 顶点着色器脚本代码(业务方需调用glCompileShader),为空时使用智汇云默认顶点着色器 */ int onCreateShader(int type); /** * 删除顶点着色器 * * @param shader 顶点着色器 */ void onDeleteShader(int shader); /** * 渲染<br> * 注意:业务方必须主动调用<code>GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCoords.capacity() / 2);</code> * * @param program 着色器程序 * @param vertexCoords 顶点坐标,默认值{1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f} * @param textureCoords 纹理坐标,默认值{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f} * @param textureMatrix 纹理变换矩阵,Mat4x4 * @param textureUnit 纹理单元,默认值{@link GLES20#GL_TEXTURE0} * @param textureTarget 纹理绑定目标,默认值{@link GLES11Ext#GL_TEXTURE_EXTERNAL_OES} * @param texture 纹理名称 */ void onDraw(int program, FloatBuffer vertexCoords, FloatBuffer textureCoords, float[] textureMatrix, int textureUnit, int textureTarget, int texture); } ``` ### 错误码说明 | 状态码 | 含义 | |--------|--------------------| | 1000 | 未知错误 | | 1001 | prepare失败 | | 1002 | 开始播放就失败 | | 1003 | 不支持的视频格式 | | 1004 | 文件不能打开 | | 1005 | 播放器插件加载失败 | | 1006 | 无效参数 | | 1007 | 当前播放器状态无效 | | 1008 | IO异常 | | 1009 | 安全异常 | | 1010 | 不支持的操作 | | 1011 | 鉴权失败 | | 状态码 | 详细错误信息 | |--------|---------------------------------------| | 10001 | 详细信息为空 | | 10002 | 调度返回的url为null | | 10003 | startplay时,setsession失败 | | 10004 | 回放状态下,文件没有打开,或者url失效 | | 10005 | jplayer open失败 | | 10006 | 调度sn失败 | | 10007 | 拿不到媒体信息、或者打不开连接 | | 10008 | 手机不支持硬解码 | | 10009 | 连接断开或者失败 | | 10010 | 录像出现错误 | | 10011 | 无效的录像文件 | | 1011 | 不支持的视频格式 |
即刻开始使用
只需完成注册与实名认证,即可体验我们的贴心服务
立即注册
请您联系我们
邮箱
g-zyun@360.cn
电话
010-56821952
给我们留言
您的姓名
手机号
公司名称(选填)
相关产品
留言内容
需求描述
产品建议
其他
提交
登录后才可以留言哦
立即登录
去注册账号