有奖捉虫:云通信与企业服务文档专题,速来> HOT

简介

说明:
由于各平台命名习惯不同,以下结构图命名可能稍有差异,具体见各平台实现。

播放器架构图





多媒体核心(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() {
@Override
public void onStatus(int status) {
Log.d(TAG, "onStatus " + status);
}
});

//时间回调
xxxPlayer.setTimeListener(new ITimeListener() {
@Override
public void onTime(long currentTime) {
Log.d(TAG, "onTime " + currentTime);
}
});

//错误回调
xxxPlayer.setErrorListener(new IErrorListener() {
@Override
public void onError(int error) {
Log.d(TAG, "onError " + error);
}
});

//用户数据回调
xxxPlayer.setUserDataListener(new IUserDataListener() {
@Override
public void onReceive(ByteBuffer data) {

}
});

添加播放器渲染图层

1. 布局添加IoTVideoView
<com.tencentcs.iotvideo.iotvideoplayer.IoTVideoView
android: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() {
@Override
public 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() {
@Override
public void onStartRecord() {
Toast.makeText(mViewOwner.getContext(),"开始录像",Toast.LENGTH_LONG).show();
}

@Override
public void onPositionUpdated(long videoDuration, long audioDuration) {
LogUtils.i(TAG,"onPositionUpdated,videoDuration:" + videoDuration + "; audioDuration:" + audioDuration);
}

@Override
public 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.timeInMillis

selectCalendar.set(Calendar.HOUR_OF_DAY, 23)
selectCalendar.set(Calendar.MINUTE, 59)
selectCalendar.set(Calendar.SECOND, 59)
selectCalendar.set(Calendar.MILLISECOND, 999)
endTime = selectCalendar.timeInMillis

LogUtils.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() {
@Override
public void init(AVHeader header) {

}

@Override
public int send_packet(AVData inPacket) {
//TODO
//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃
}

@Override
public int receive_frame(AVData outFrame) {
//TODO
//return >=0 成功,-1 稍后再试,其他值 失败
}

@Override
public void release() {

}
});

//设置视频解码
mMonitorPlayer.setVideoDecoder(new IVideoDecoder() {
@Override
public void init(AVHeader header) {

}

@Override
public int send_packet(AVData inPacket) {
//TODO
//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃
}

@Override
public int receive_frame(AVData outFrame) {
//TODO
//return >=0 成功,-1 稍后再试,其他值 失败
}

@Override
public void release() {

}
});

自定义编码器

//视频编码器
mMonitorPlayer.setVideoEncoder(new IVideoEncoder() {
@Override
public void init(AVHeader header) {

}

@Override
public int send_frame(AVData inFrame) {
//TODO
//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃
}

@Override
public int receive_packet(AVData outPacket) {
//TODO
//return >=0 成功,-1 稍后再试,其他值 失败
}

@Override
public void release() {

}
});

//音频编码器
mMonitorPlayer.setAudioEncoder(new IAudioEncoder() {
@Override
public void init(AVHeader header) {

}

@Override
public int send_frame(AVData inFrame) {
//TODO
//return >=0 成功; -1 稍后再试,这一帧不会丢弃,下一次会再次回调这一帧数据; 其他值 失败,这一帧数据会丢弃
}

@Override
public int receive_packet(AVData outPacket) {
//TODO
//return >=0 成功,-1 稍后再试,其他值 失败
}

@Override
public void release() {

}
});

自定义音视频渲染

//设置视频自定义渲染
mMonitorPlayer.setVideoRender(new IVideoRender() {
@Override
public void onInit(AVHeader header) {
//音视频头
}
@Override
public void onFrameUpdate(AVData data) {
//每帧视频YUV数据
}

@Override
public void onRelease() {

}
});


mMonitorPlayer.setAudioRender(new IAudioRender() {
@Override
public void onInit(AVHeader header) {
//音视频头
}
@Override
public void onFrameUpdate(AVData data) {
//每帧音频PCM数据
}
@Override
public 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();