简介
说明:
由于各平台命名习惯不同,以下结构图命名可能稍有差异,具体见各平台实现。
播放器架构图
多媒体核心(IoT Video(Consumer Version) Player)
IoT Video(Consumer Version) Player 是整个多媒体模块的核心,主要负责以下流程控制:
音视频通道建立。
音视频流的推拉。
协议解析。
封装和解封装。
音视频编解码。
音视频同步。
音视频渲染。
音视频录制。
播放状态控制。
其中,音视频编解码和音视频渲染流程允许开发者自定义实现(播放器已内置实现,不推荐自定义实现)。
监控播放器(MonitorPlayer)
MonitorPlayer 是基于 IoT Video(Consumer Version) Player 封装的监控播放器,主要增加以下功能:
语音对讲。
音视频通话播放器(LivePlayer)
LivePlayer 是基于 IoT Video(Consumer Version) Player 封装的音视频通话播放器,主要增加以下功能:
语音对讲。
双向视频。
回放播放器(PlaybackPlayer)
PlaybackPlayer 是基于 IoT Video(Consumer Version) Player 封装的回放播放器,主要增加以下功能:
暂停/恢复。
跳至指定位置播放。
播放器功能对比
功能 | 监控播放器 | 回放播放器 | 音视频通话 |
视频播放 | ✓ | ✓ | ✓ |
音频播放 | ✓ | ✓ | ✓ |
暂停/恢复 | x | ✓ | x |
跳至指定位置播放 | x | ✓ | x |
总时长 | x | ✓ | x |
当前播放进度 | x | ✓ | x |
播放器状态变更通知 | ✓ | ✓ | ✓ |
静音 | ✓ | ✓ | ✓ |
画面缩放模式设置 | ✓ | ✓ | ✓ |
播放器截图 | ✓ | ✓ | ✓ |
边播边录 | ✓ | ✓ | ✓ |
对讲 | ✓ | ✓ | ✓ |
分辨率切换 | ✓ | x | x |
双向视频 | x | x | ✓ |
使用示例
创建播放器实例
注意:
以下使用
xxxxPlayer
泛指支持该功能的播放器。//监控播放器MonitorPlayer monitorPlayer = new MonitorPlayer();monitorPlayer.setDataResource(mDeviceId);//双向音视频通话播放器LivePlayer livePlayer = new LivePlayer();livePlayer.setDataResource(mDeviceId);//回放播放器PlaybackPlayer playbackPlayer = new PlaybackPlayer();playbackPlayer.setDataResource(mDeviceId);
设置播放器回调
//状态回调xxxPlayer.setStatusListener(new IStatusListener() {@Overridepublic void onStatus(int status) {Log.d(TAG, "onStatus " + status);}});//时间回调xxxPlayer.setTimeListener(new ITimeListener() {@Overridepublic void onTime(long currentTime) {Log.d(TAG, "onTime " + currentTime);}});//错误回调xxxPlayer.setErrorListener(new IErrorListener() {@Overridepublic void onError(int error) {Log.d(TAG, "onError " + error);}});//用户数据回调xxxPlayer.setUserDataListener(new IUserDataListener() {@Overridepublic void onReceive(ByteBuffer data) {}});
添加播放器渲染图层
1. 布局添加IoTVideoView<com.tencentcs.iotvideo.iotvideoplayer.IoTVideoViewandroid:id="@+id/video_view"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintDimensionRatio="H,16:9"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />2. 设置渲染IoTVideoView mVideoView = findViewById(R.id.video_view);xxxPlayer.setVideoView(mVideoView);
预连接
xxxxPlayer.prepare()
开始播放
xxxxPlayer.play()
停止播放
xxxxPlayer.stop()
释放
xxxxPlayer.release()
截图
String dateStringParse = mSimpleDateFormat.format(new Date());mMonitorPlayer.snapShot(new File(PIC_PATH, dateStringParse + ".jpeg").getAbsolutePath(),new ISnapShotListener() {@Overridepublic void onResult(int code, String path) {Toast.makeText(this, "code:" + code + " path:" + path, Toast.LENGTH_LONG).show();}});
录像
//开始录像String dateStringParse = mSimpleDateFormat.format(new Date());mMonitorPlayer.startRecord(mRecordPath, mSimpleDateFormat.format(new Date()) + ".mp4",new IRecordListener() {@Overridepublic void onStartRecord() {Toast.makeText(mViewOwner.getContext(),"开始录像",Toast.LENGTH_LONG).show();}@Overridepublic void onPositionUpdated(long videoDuration, long audioDuration) {LogUtils.i(TAG,"onPositionUpdated,videoDuration:" + videoDuration + "; audioDuration:" + audioDuration);}@Overridepublic void onResult(int code, String path) {LogUtils.i(TAG,"onResult,code:" + code + "; path:" + path);}});//结束录像mMonitorPlayer.stopRecord();
开启/关闭语音对讲(只支持 MonitorPlayer/LivePlayer)
//开始对讲xxxxPlayer.startTalk()//结束对讲xxxxPlayer.stopTalk()
开启/切换/关闭摄像头(只支持 LivePlayer)
//打开摄像头livePlayer.openCamera()//切换摄像头livePlayer.switchCamera()//关闭摄像头livePlayer.closeCamera()
添加摄像头预览图层(只支持 LivePlayer)
//开始预览livePlayer.startPreview(surface);//结束预览livePlayer.stopPreview();
跳转到指定时间播放(只支持 PlaybackPlayer)
方式一
耗时较方式二耗时会更久,具体操作步骤如下:
1. 先调用 stop 断开当前播放器与设备的链接。
2. 通过播放器的 setDataResource 接口重新指定开始播放的时间。
3. 调用 play 重新建立连接
方式二(推荐)
当 PlaybackPlayer 播放器实例的播放状态为 STATE_PLAY 时,如果需要跳转到指定时间开始播放,可以使用播放器的 seek 方法。
接口
//路径:com.tencentcs.iotvideo.iotvideoplayer.player.PlaybackPlayer/*** 指定时间播放* @param seekTime 时间* @param data 播放节点信息*/public void seek(final long seekTime, final PlaybackNode data);
demo 示例代码
if (mPlaybackPlayer.isPlaying) {mPlaybackPlayer.seek(it.startTime, it)}
暂停/恢复播放(只支持 PlaybackPlayer)
//暂停playbackPlayer.pause()//恢复playbackPlayer.resume()
查询 SD 卡录像日期列表(只支持 PlaybackPlayer)
接口
//路径:com.tencentcs.iotvideo.iotvideoplayer.player.PlaybackPlayer/*** 获取设备端sd卡中存在录像的日期列表** @param deviceId 设备ID* @param startTime 查询的起始时间(单位毫秒)* @param endTime 查询的结束时间(单位毫秒)* @param pageIndex 查询页* @param countPerPage 每页大小* @param listener 回调*/public static void getExistRecordDateList(String deviceId, long startTime, long endTime, int pageIndex, int countPerPage, IResultListener<PlaybackExistDateMessage> listener);
demo 示例代码
var endTime:Long = System.currentTimeMillis();var startTime:Long = endTime - 24 * 60 * 60 * 1000 * 30L;PlaybackPlayer.getExistRecordDateList(mDeviceId,startTime, endTime,0,1000,object : IResultListener<PlaybackExistDateMessage>{override fun onStart() {Snackbar.make(tv_get_playback_previous,"开始查询存在录像日期列表...",Snackbar.LENGTH_LONG).show()}override fun onSuccess(msg: PlaybackExistDateMessage?) {LogUtils.i(TAG,"getExistRecordDateList onSuccess:${msg.toString()}")runOnUiThread {if (msg?.type == -1 && msg?.error == -1 && msg?.id.toInt() == -1) {LogUtils.i(TAG, "录像存在日期列表为空")}else{showExistDateListDialog(msg!!.dateListString)}}}override fun onError(errorCode: Int, errorMsg: String?) {LogUtils.i(TAG,"getExistRecordDateList onError,errorCode:$errorCode ; errorMsg:$errorMsg")}})
获取 SD 卡回放详情列表(只支持 PlaybackPlayer)
接口
/*** 获取回放详情列表*开始时间-结束时间的时间差不允许大于一个月* @param deviceId 设备ID* @param startTime 查询的起始时间(单位毫秒)* @param endTime 查询的结束时间(单位毫秒)* @param pageIndex 查询页* @param countPerPage 每页大小* @param recordType 筛选类型, null为不筛选* @param listener 回调*/public static void getPlaybackListV2(String deviceId, long startTime, long endTime, int pageIndex, int countPerPage, String recordType, IResultListener<playbackmessage> listener);
demo 示例代码
val startTime:Long;val endTime:Long;val recordType = ""selectCalendar.set(Calendar.HOUR_OF_DAY, 0)selectCalendar.set(Calendar.MINUTE, 0)selectCalendar.set(Calendar.SECOND, 0)selectCalendar.set(Calendar.MILLISECOND, 1)startTime = selectCalendar.timeInMillisselectCalendar.set(Calendar.HOUR_OF_DAY, 23)selectCalendar.set(Calendar.MINUTE, 59)selectCalendar.set(Calendar.SECOND, 59)selectCalendar.set(Calendar.MILLISECOND, 999)endTime = selectCalendar.timeInMillisLogUtils.i(TAG,"startTime:$startTime; endTime:$endTime")PlaybackPlayer.getPlaybackListV2(mDeviceId, startTime, endTime,pageIndex, 20, recordType, object : IResultListener<PlaybackMessage> {override fun onStart() {LogUtils.d(TAG, "请求中...")playback_status.text = "正在获取回放列表${pageIndex + 1}..."}override fun onSuccess(msg: PlaybackMessage?) {if (msg?.type == -1 && msg?.error == -1 && msg?.id.toInt() == -1) {playback_status.text = "回放列表为空"LogUtils.i(TAG, "回放列表为空")return}mCurrentPageIndex = msg?.currentPage!!mPageCount = msg?.pageCount!!val logStr = "获取成功 : 当前页 ${mCurrentPageIndex + 1}, 总页数 $mPageCount"LogUtils.d(TAG, logStr)LogUtils.d(TAG, "获取成功 ${msg.toString()}")runOnUiThread {playback_status.text = "获取回放列表成功"data.clear()msg.playbackList?.let {data.addAll(it)mAdapter.notifyDataSetChanged()}Snackbar.make(view, logStr, Snackbar.LENGTH_LONG).show()}}override fun onError(errorCode: Int, errorMsg: String?) {val logStr = "getPlaybackList error code $errorCode, $errorMsg"runOnUiThread {playback_status.text = "获取回放列表失败 $errorCode, $errorMsg"LogUtils.d(TAG, logStr)Snackbar.make(view, logStr, Snackbar.LENGTH_LONG).show()}}})
高级功能
自定义解码器
//设置音频解码mMonitorPlayer.setAudioDecoder(new IAudioDecoder() {@Overridepublic void init(AVHeader header) {}@Overridepublic int send_packet(AVData inPacket) {//TODO//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃}@Overridepublic int receive_frame(AVData outFrame) {//TODO//return >=0 成功,-1 稍后再试,其他值 失败}@Overridepublic void release() {}});//设置视频解码mMonitorPlayer.setVideoDecoder(new IVideoDecoder() {@Overridepublic void init(AVHeader header) {}@Overridepublic int send_packet(AVData inPacket) {//TODO//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃}@Overridepublic int receive_frame(AVData outFrame) {//TODO//return >=0 成功,-1 稍后再试,其他值 失败}@Overridepublic void release() {}});
自定义编码器
//视频编码器mMonitorPlayer.setVideoEncoder(new IVideoEncoder() {@Overridepublic void init(AVHeader header) {}@Overridepublic int send_frame(AVData inFrame) {//TODO//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃}@Overridepublic int receive_packet(AVData outPacket) {//TODO//return >=0 成功,-1 稍后再试,其他值 失败}@Overridepublic void release() {}});//音频编码器mMonitorPlayer.setAudioEncoder(new IAudioEncoder() {@Overridepublic void init(AVHeader header) {}@Overridepublic int send_frame(AVData inFrame) {//TODO//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃}@Overridepublic int receive_packet(AVData outPacket) {//TODO//return >=0 成功,-1 稍后再试,其他值 失败}@Overridepublic void release() {}});
自定义音视频渲染
//设置视频自定义渲染mMonitorPlayer.setVideoRender(new IVideoRender() {@Overridepublic void onInit(AVHeader header) {//音视频头}@Overridepublic void onFrameUpdate(AVData data) {//每帧视频YUV数据}@Overridepublic void onRelease() {}});mMonitorPlayer.setAudioRender(new IAudioRender() {@Overridepublic void onInit(AVHeader header) {//音视频头}@Overridepublic void onFrameUpdate(AVData data) {//每帧音频PCM数据}@Overridepublic void onRelease() {}});
透传长连接
监控、回放等播放器主要都是用于多媒体传输和解码,在此提供了一种特殊的“播放器”,用于传输消息,单条消息的数据量不能超过63K。使用方法如下:
//创建长连接mConnection = new TransmissionConnection();mConnection.setDataResource(deviceId);//设置回调mConnection.setPreparedListener(mPreparedListener);mConnection.setStatusListener(mStatusListener);mConnection.setErrorListener(mErrorListener);mConnection.setUserDataListener(mUserDataListener);//进行连接mConnection.play();//发送消息mConnection.sendUserData(data);//断开连接mConnection.stop();