生活物联网平台提供Android版本的设备端Link Visual SDK,您可以基于该SDK开发Link Visual视频设备的直播、点播、语音对讲、抓图等功能。
Android设备端Link Visual SDK依赖如下。
获取SDK
请您根据引入依赖的方法来获取Android版本的设备端Link Visual SDK,并在proguard-rules.pro文件中排除不需要被混淆的类和方法。
- 引入依赖
// 1. 根build.gradle添加对aliyun maven仓库的引用
allprojects {
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
}
// 2. app build.gradle中添加依赖
implementation 'com.aliyun.iotx:linkvisual-ipc:1.4.4'
- 混淆配置
# keep linkvisual
-keep class com.aliyun.iotx.linkvisualipc.** { *; }
初始化SDK
请在Link SDK初始化完毕后再初始化Link Visual SDK,初始化时需要传入设备证书信息。
LinkKit.getInstance().init(this, params, new ILinkKitConnectListener() {
@Override
public void onError(AError error) {
Log.d(TAG,
"onError() called with: error = [" + (error == null ? "null" : (error.getCode() + error.getMsg()))
+ "]");
}
@Override
public void onInitDone(Object data) {
Log.d(TAG, "onInitDone() called with: data = [" + JSON.toJSONString(data) + "]");
// 初始化SDK
IPCDev.getInstance().init(context, your_productKey, your_deviceName, your_deviceSecret);
}
其中your_productname、your_devicename、your_devicesecret需要替换为您自己的设备证书信息。
Link Visual SDK需借助Link kit的能力来完成消息监听和处理。注册监听的流程如下。
- 在设备服务中注册异步服务调用监听器。
//注册异步服务调用监听器
LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(),
itResRequestHandler);
//异步服务调用监听器
private ITResRequestHandler itResRequestHandler = new ITResRequestHandler() {
@Override
public void onProcess(String identify, Object result, ITResResponseCallback
itResResponseCallback) {
Log.d(TAG,
"ITResRequestHandler onProcess() called with: identify = [" + identify + "], result = ["
+ JSON.toJSONString(result) + "], itResResponseCallback = ["
+ itResResponseCallback + "]");
/**
* 添加SDK对异步服务调用的监听
*/
IPCDev.getInstance().notifyAsyncTopicReceived(identify, result, itResResponseCallback);
}
@Override
public void onSuccess(Object o, OutputParams outputParams) {
Log.d(TAG,
"onSuccess() called with: o = [" + JSON.toJSONString(o) + "], outputParams = [" + JSON
.toJSONString(outputParams) + "]");
}
@Override
public void onFail(Object o, ErrorInfo errorInfo) {
Log.d(TAG, "onFail() called with: o = [" + JSON.toJSONString(o) + "], errorInfo = [" + JSON
.toJSONString(errorInfo) + "]");
}
};
- 注册同步服务调用的监听器。
/**
* 注册同步服务调用的监听器
*/
LinkKit.getInstance().registerOnPushListener(connectNotifyListener);
//同步服务调用监听器
private IConnectNotifyListener connectNotifyListener = new IConnectNotifyListener() {
@Override
public void onNotify(String connectId, String topic, AMessage aMessage) {
/**
* 添加SDK对同步服务调用的监听
*/
IPCDev.getInstance().notifySyncTopicReceived(connectId, topic, aMessage);
if (CONNECT_ID.equals(connectId) && !TextUtils.isEmpty(topic) &&
topic.startsWith("/sys/" + productKey + "/" + deviceName + "/rrpc/request")) {
Log.d(TAG, "IConnectNotifyListener onNotify() called with: connectId = [" + connectId + "], topic = ["
+ topic + "], aMessage = ["
+ new String((byte[])aMessage.data) + "]");
}
}
@Override
public boolean shouldHandle(String connectId, String topic) {
return true;
}
@Override
public void onConnectStateChange(String connectId, ConnectState connectState) {
}
};
开发直播功能
直播通过RTMP推流,视频支持H264、H265,音频支持G711a以及AAC_LC格式。您可以按以下步骤开发直播功能的推流。
- 注册直播事件监听器和流错误监听器。
Link Visual SDK收到服务端下发的开始推流指令后,会通过事先注册的直播流事件监听器(OnLiveStreamListener)来通知何时开始或结束推流、强制I帧等。
该流程开发如下所示。
- 设置直播流事件监听。
// 设置直播流事件监听
IPCDev.getInstance().getIpcStreamManager().setOnLiveStreamListener(MainActivity.this);
// 设置流错误监听
IPCDev.getInstance().getIpcStreamManager().setOnStreamErrorListener(MainActivity.this);
- 接收服务端发送的开始推直播流请求。
public interface OnLiveStreamListener {
/**
* 收到开始推直播流请求
*
* @param streamId 流ID
* @param streamType 码流类型: 0为主码流, 1为辅码流
* @param preTimeInS 预先录制时间,单位S
*/
void onStartPushLiveStreaming(final int streamId, final int streamType, final int preTimeInS);
/**
* 收到停止推流请求
*
* @param streamId 流ID
*/
void onStopPushStreaming(final int streamId);
/**
* 收到强制I帧请求
* 需立即构造一个I帧并发送
* @param streamId 流ID
*/
void onForceIFrame(int streamId);
}
推流中发生的错误也将通过流错误监听器来通知。请不要在回调接口中执行阻塞任务。
public interface OnStreamErrorListener {
/**
* 流异常时回调
* @param streamId
* @param error 参考StreamError定义
*/
void onError(int streamId, StreamError error);
}
流错误码如下所示。
错误码 |
标志符 |
错误描述 |
1 |
StreamError.ERROR_STREAM_CREATE_FAILED |
创建流实例失败。 |
2 |
StreamError.ERROR_STREAM_START_FAILED |
开流失败。 |
3 |
StreamError.ERROR_STREAM_STOP_FAILED |
停止流失败。 |
4 |
StreamError.ERROR_STREAM_SEND_VIDEO_FAILED |
发送视频数据失败。 |
5 |
StreamError.ERROR_STREAM_SEND_AUDIO_FAILED |
发送音频数据失败。 |
6 |
StreamError.ERROR_STREAM_INVALID_PARAMS |
无效的流参数。 |
RTMP错误码如下所示。
错误码 |
标志符 |
描述 |
-1 |
RTMP_ILLEGAL_INPUT |
输入不合法,请检查输入参数。 |
-2 |
RTMP_MALLOC_FAILED |
内存分配失败。 |
-3 |
RTMP_CONNECT_FAILED |
RTMP建连失败。 |
-4 |
RTMP_IS_DISCONNECTED |
RTMP连接未建立。 |
-5 |
RTMP_UNSUPPORT_FORMAT |
不支持的音视频格式。 |
-6 |
RTMP_SEND_FAILED |
RTMP数据包发送失败。 |
-7 |
RTMP_READ_MESSAGE_FAILED |
RTMP消息读取失败。 |
-8 |
RTMP_READ_TIMESTAMP_ERROR |
输入时间戳错误。 |
- 处理开始直播推流请求。
- 当服务端下发推流请求时,回调
OnLiveStreamListener.onStartPushLiveStreaming(int streamId, int streamType, int preTimeInS)
方法来通知设备端需要开始采流并推流。
一般需要开启摄像头和录音机进行采流,对摄像头采集的数据调用MediaCodec进行H264编码,对录音机采集的数据进行G711a编码,提前设置对应格式的音视频参数,分别调用发送音视频的接口来持续发送采集到编码后的数据。
@Override
public void onStartPushLiveStreaming(int streamId, int streamType, int preTimeInS) {
this.streamId = streamId;
try {
// 构造视频参数
VideoStreamParams videoStreamParams = new VideoStreamParams();
// 直播流该参数始终为0
videoStreamParams.setDurationInS(0);
videoStreamParams.setVideoFormat(VideoStreamParams.VIDEO_FORMAT_H264);
// 构造音频参数
AudioStreamParams audioStreamParams = new AudioStreamParams();
audioStreamParams.setAudioChannel(AudioStreamParams.AUDIO_CHANNEL_MONO);
audioStreamParams.setAudioFormat(AudioStreamParams.AUDIO_FORMAT_G711A);
audioStreamParams.setAudioEncoding(AudioStreamParams.AUDIO_ENCODING_16BIT);
audioStreamParams.setAudioSampleRate(AudioStreamParams.AUDIO_SAMPLE_RATE_8000);
// 设置推流参数
IPCDev.getInstance().getIpcStreamManager().setStreamParams(streamId, videoStreamParams, audioStreamParams);
// TODO 开始采流、编码并发送音视频数据
} catch (NoSuchStreamException e) {
e.printStackTrace();
}
}
- 发送音视频数据接口(IPCStreamManager)。
/**
* 发送音频帧数据
*
* @param streamId 流ID
* @param directByteBuffer 源数据
* @param length 数据长度
* @param timeStampInMs 音频帧时间戳,单位ms
*/
void sendAudioData(int streamId, ByteBuffer directByteBuffer, int length, long timeStampInMs) throws NoSuchStreamException
/**
* 发送视频帧数据
*
* @param streamId 流ID
* @param directByteBuffer 源数据
* @param length 数据长度
* @param isIFrame 是否为I帧
* @param timeStampInMs 视频帧时间戳,单位ms
*/
void sendVideoData(int streamId, ByteBuffer directByteBuffer, int length, boolean isIFrame, long timeStampInMs) throws NoSuchStreamException
/**
* 发送音频帧数据
*
* @param streamId 流ID
* @param data 源数据
* @param offset 偏移量
* @param length 数据长度
* @param timeStampInMs 音频帧时间戳,单位ms
* @deprecated 使用 {@link #sendAudioData(int, ByteBuffer, int, long)}来替换
*/
@Deprecated
void sendAudioData(int streamId, byte[] data, int offset, int length, long timeStampInMs) throws NoSuchStreamException
/**
* 发送视频帧数据
*
* @param streamId 流ID
* @param data 源数据
* @param offset 偏移量
* @param length 数据长度
* @param isIFrame 是否为I帧
* @param timeStampInMs 视频帧时间戳,单位ms
* @deprecated 使用 {@link #sendVideoData(int, ByteBuffer, int, boolean, long)}来替换
*/
@Deprecated
public void sendVideoData(int streamId, byte[] data, int offset, int length, boolean isIFrame, long timeStampInMs) throws NoSuchStreamException
- 打印I帧的前256个字节,并查看结果。
H264、H265的帧结构会有一定的要求,可以打印I帧的前256个字节查看,打印代码如下。
for (int i = 0; i < ((buffer_size > 256)?256:buffer_size); i++) {
printf("%02x ", buffer[i]);
if ((i + 1) % 30 == 0) {
printf("\n");
}
}
printf("\n");
H264、H265的帧结构的说明如下。
- H264
H264要求I帧为:帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR
,其中,0x000001或者0x00000001是帧分隔符,0x67是SPS的开始,0x68是PPS的开始,0x65是IDR的开始。如下图所示。

- H265
H265要求I帧为:帧分隔符+VPS+帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR
,其中,0x000001或者0x00000001是帧分隔符,0x40是VPS的开始,0x42是SPS的开始,0x44是PPS的开始,0x26是IDR的开始。如下图所示。

- 处理结束推流请求。
当服务端下发停止推流请求时,回调
OnLiveStreamListener.onStopPushLiveStreaming()
方法来通知设备端停止推流。
一般需要停止摄像头数据和录音机数据的采集,并调用IPCStreamManager的stopStreaming(int streamId)
方法。
/**
* 收到停止推流请求
*
* @param streamId 流ID
*/
@Override
public void onStopPushStreaming(int streamId) {
// TODO 停止音视频数据的发送
try {
// 调用停止推流接口
IPCDev.getInstance().getIpcStreamManager().stopStreaming(streamId);
} catch (NoSuchStreamException e) {
e.printStackTrace();
}
}
- 处理流错误。
推流过程中需要处理流错误,通过OnStreamErrorListener.onError(int streamId, StreamError error)
来接收和处理。
开发点播功能
点播通过RTMP推流,视频支持H264/H265,音频支持G711a以及AAC_LC格式。请根据以下步骤来开发点播功能的推流。
- 注册点播事件监听器和流错误监听器。
SDK收到服务端下发的开始推流指令后,会通过事先注册的点播流事件监听器(OnVodStreamListener)来通知何时开始或结束推流/暂停/恢复/seek等。
推流中发生的错误也将通过流错误监听器来通知。请不要在回调接口中执行阻塞任务。
- 处理查询设备端录像列表请求。
App端发起查询设备端录像列表的请求,设备端会收到
同步服务调用(rrpc/request),收到查询设备录像列表请求,响应该请求将当查询范围内的文件列表返回给App端。
@Override
public void onNotify(String connectId, String topic, AMessage aMessage) {
Log.d(TAG, "onNotify() called with: connectId = [" + connectId + "], topic = [" + topic + "], aMessage = ["
+ new String((byte[]) aMessage.data) + "]");
/**
* 添加SDK的监听。
*/
IPCDev.getInstance().notifySyncTopicReceived(connectId, topic, aMessage);
// 处理同步服务调用
if (CONNECT_ID.equals(connectId) && !TextUtils.isEmpty(topic) &&
topic.contains("rrpc")) {
Log.d(TAG, "IConnectNotifyListener onNotify() called with: connectId = [" + connectId + "], topic = ["
+ topic + "], aMessage = ["
+ new String((byte[]) aMessage.data) + "]");
int code = 200;
String data = "{}";
JSONObject json = JSON.parseObject(new String((byte[]) aMessage.data));
if (json != null) {
String method = json.getString("method");
JSONObject params = json.getJSONObject("params");
switch (method) {
// 查询设备录像列表请求
case "thing.service.QueryRecordList":
int beginTime = params.getIntValue("BeginTime");
int endTime = params.getIntValue("EndTime");
int querySize = params.getIntValue("QuerySize");
int type = params.getIntValue("Type");
appendLog("收到查询设备录像列表的请求: beginTime=" + beginTime +
"\tendTime=" + endTime + "\tquerySize=" + querySize + "\ttype=" + type);
JSONArray resultArray = new JSONArray();
JSONObject item1 = new JSONObject();
item1.put("FileName", Base64.encode("file1".getBytes(), Base64.DEFAULT));
item1.put("BeginTime", System.currentTimeMillis() / 1000 - 200);
item1.put("EndTime", System.currentTimeMillis() / 1000 - 100);
item1.put("Size", 1024000);
item1.put("Type", 0);
resultArray.add(item1);
JSONObject item2 = new JSONObject();
item2.put("FileName", Base64.encode("file2".getBytes(), Base64.DEFAULT));
item2.put("BeginTime", System.currentTimeMillis() / 1000 - 100);
item2.put("EndTime", System.currentTimeMillis() / 1000);
item2.put("Size", 1024000);
item2.put("Type", 0);
resultArray.add(item2);
JSONObject result = new JSONObject();
result.put("RecordList", resultArray);
code = 200;
data = result.toJSONString();
break;
default:
break;
}
}
MqttPublishRequest request = new MqttPublishRequest();
request.isRPC = false;
request.topic = topic.replace("request", "response");
String resId = topic.substring(topic.indexOf("rrpc/request/") + 13);
request.msgId = resId;
request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":" + code + ",\"data\":" + data + "}";
LinkKit.getInstance().publish(request, new IConnectSendListener() {
@Override
public void onResponse(ARequest aRequest, AResponse aResponse) {
appendLog("上报成功");
}
@Override
public void onFailure(ARequest aRequest, AError aError) {
appendLog("上报失败:" + aError.toString());
}
});
}
}
- 处理开始推流指令。
App端请求上一步返回的列表中某个设备录像文件后,服务端会下发推流指令,回调
OnVodStreamListener.onStartPushVodStreaming(int streamId, String fileName)
或
OnVodStreamListener.onStartPushVodStreaming(int streamId, int beginTimeUtc, int endTimeUtc)
方法来通知设备端需要将音视频数据推流。
- 按文件方式播放设备端录像的响应
@Override
public void onStartPushVodStreaming(int streamId, String fileName) {
appendLog("开始推点播流 " + streamId + " 文件名:" + new String(Base64.decode(fileName, Base64.NO_WRAP)));
try {
// 构造视频参数
VideoStreamParams videoStreamParams = new VideoStreamParams();
// 该视频文件的时长,单位S
videoStreamParams.setDurationInS(H264_DURATION_IN_S);
videoStreamParams.setVideoFormat(VideoStreamParams.VIDEO_FORMAT_H264);
// 构造音频参数
AudioStreamParams audioStreamParams = new AudioStreamParams();
audioStreamParams.setAudioChannel(AudioStreamParams.AUDIO_CHANNEL_MONO);
audioStreamParams.setAudioFormat(AudioStreamParams.AUDIO_FORMAT_G711A);
audioStreamParams.setAudioEncoding(AudioStreamParams.AUDIO_ENCODING_16BIT);
audioStreamParams.setAudioSampleRate(AudioStreamParams.AUDIO_SAMPLE_RATE_8000);
// 设置推流参数
IPCDev.getInstance().getIpcStreamManager().setStreamParams(streamId, videoStreamParams, audioStreamParams);
// TODO 读取fileName文件,调用发送音视频数据接口进行推流
// 文件推流完毕后应调用 IPCDev.getInstance().getIpcStreamManager().notifyVodComplete(streamId) 通知推流完成
} catch (NoSuchStreamException e) {
e.printStackTrace();
}
}
- 按时间方式播放设备端录像的响应
@Override
public void onStartPushVodStreaming(int streamId, int beginTimeUtc, int endTimeUtc) {
appendLog("开始推点播流 " + streamId + " beginTimeUtc: "+beginTimeUtc + " endTimeUtc:"+endTimeUtc);
//TODO 推流逻辑需要添加:
// 1. beginTimeUtc和endTimeUtc一般是一天的开始和结束时间
// 2. 当收到onStartPushVodStreaming回调后,应从beginTimeUtc开始向后最近的I帧开始推流,时间戳应使用对应帧的UTC时间
// 3. 若beginTimeUtc到endTimeUtc范围内没有录像或范围内推流已经完成了,则应调用 IPCDev.getInstance().getIpcStreamManager().notifyVodComplete(streamId) 通知推流完成
// 4. 只要是beginTimeUtc到endTimeUtc范围内有数据, 即使跨文件,推流应该持续不断
}
- 处理暂停或恢复指令。
需响应暂停或恢复推流指令(OnVodStreamListener),相应的暂停或恢复发送音视频数据。
/**
* 收到暂停推流请求
*
* @param streamId 流ID
*/
void onPausePushVodStreaming(int streamId);
/**
* 收到恢复推流的请求
*
* @param streamId 流ID
*/
void onResumePushVodStreaming(int streamId);
- 处理Seek指令。
当您需响应Seek指令(OnVodStreamListener)时,例如App端播放器进度条Seek到80秒时,对应会回调onSeekTo方法,请从该timeStampInS时间点最近的I帧开始继续推流。
/**
* 收到重新定位请求
*
* @param streamId 流ID
* @param timeStampInS 时间偏移量,相对于视频开始时间,单位为S
*/
void onSeekTo(int streamId, long timeStampInS);
- 处理停止推流指令。
当服务端下发停止推流请求时,通过回调
OnVodStreamListener.onStopPushLiveStreaming()
方法来通知设备端停止推流。
一般要停止调用发送音视频接口、关闭视频文件,并调用IPCStreamManager的stopStreaming(int streamId)
方法。
/**
* 收到停止推流请求
*
* @param streamId 流ID
*/
@Override
public void onStopPushStreaming(int streamId) {
// TODO 停止音视频数据的发送
try {
// 调用停止推流接口
IPCDev.getInstance().getIpcStreamManager().stopStreaming(streamId);
catch (NoSuchStreamException e) {
e.printStackTrace();
}
}
- 处理流错误。
推流过程中需要处理流错误,通过OnStreamErrorListener.onError(int streamId, StreamError error)
来接收和处理。
开发语音对讲功能
语音对讲支持单讲、双讲模式。
- 单讲:App端采集并发送音频数据到设备端进行播放。
- 双讲:App端和设备端都需要同时做采音和放音,设备端必须支持AEC,否则不建议使用该方案。
语音对讲支持的音频格式如下。
格式 |
采样率 |
编码 |
解码 |
G711A |
8Khz/16Khz |
✓ |
✓ |
G711U |
8Khz/16Khz |
✓ |
✓ |
说明 对讲提供V1、V2两套接口,除特殊说明外,请使用V2版本。
- V1版本:需要自行实现录音采集、播放以及回声消除。
- V2版本:支持单/双讲模式,内部已经实现录音采集、播放以及回声消除,支持设置采集音频增益大小。
- 注册语音对讲事件监听器和错误监听器。
SDK收到服务端下发的开始推流指令后,会通过事先注册的语音对讲事件监听器(OnLiveIntercomListener)来通知开始或结束语音对讲、接收对端的音频参数和语音数据。
推流中发生的错误也将通过流错误监听器来通知App。请不要在回调接口中执行阻塞任务。
- 选择使用的语音对讲版本。
// 设置使用对讲第二版本, 默认为此版本
IPCDev.getInstance().setLiveIntercomVersionBeforeInit(IPCDev.LiveIntercomVersion.VERSION_2);
- 选择对讲模式。
// 设置双向对讲模式,只对V2版本接口有效
IPCDev.getInstance().setLiveIntercomModeBeforeInit(IPCLiveIntercomV2.LiveIntercomMode.DoubleTalk);
- 设置监听。
// 设置语音对讲事件监听
IPCDev.getInstance().getIpcLiveIntercom().setOnLiveIntercomListener(MainActivity.this);
// 设置语音对讲错误回调
IPCDev.getInstance().getIpcLiveIntercom().setOnLiveIntercomErrorListener(MainActivity.this);
注册语音对讲事件监听器和错误监听器的完整代码如下。
public interface OnLiveIntercomListener {
/**
* 收到App端发起的开始语音对讲请求
*
* @return 返回当前设备端上行音频参数格式,如采样率、通道数、采样位宽、音频格式, 请确保对App端能支持该音频参数配置
*/
AudioParams onStartVoiceIntercom();
/**
* 收到结束语音对讲请求
*/
void onStopVoiceIntercom();
/**
* 收到App端音频参数,表示与App端的通道已建立,可以开始对讲
* @param audioParams App端的音频参数
*/
void onAudioParamsChange(AudioParams audioParams);
/**
* 收到App端发送的PCM数据,一般用来做UI展示,比如绘制音量大小
* @param buffer
* @param size
*/
void onAudioBufferReceive(byte[] buffer, int size);
}
public interface OnLiveIntercomErrorListener {
/**
* 语音对讲发生错误
* @param error 见{@link LiveIntercomError}
*/
void onError(LiveIntercomError error);
}
- 响应开始语音对讲请求(仅V1版本接口需要处理)。
- 启动录音机开始音频采集并将音频数据发送给对端 。
@Override
public AudioParams onStartVoiceIntercom() {
appendLog("收到开始语音对讲指令");
// 收到开始语音对讲请求, 启动录音机
simpleAudioRecord.setAudioRecordListener(new AudioRecordListener() {
@Override
public void onRecordStart() {
// 录音开始
appendLog("录音开始");
}
@Override
public void onRecordEnd() {
// 录音结束
appendLog("录音结束");
}
@Override
public void onBufferReceived(byte[] buffer, int offset, int size) {
// 收到录音机抛出的PCM数据, 调用发送接口发送给对端
IPCDev.getInstance().getIpcLiveIntercom().sendAudioBuffer(buffer, offset, size);
}
@Override
public void onError(int error, String message) {
appendLog("录音机错误:" + error + " " + message);
}
});
simpleAudioRecord.start();
// 通知使用G711A作为音频发送格式,内部会对PCM数据重新做编码
return AudioParams.AUDIOPARAM_MONO_8K_G711A;
}
- 发送音频数据接口(IPCLiveIntercom,仅V1版本接口需要处理)。
/**
* 发送音频数据<br>
* 等价{@link #sendAudioBuffer(byte[] data, int offset, int length, boolean enableEncode)} enableEncode=true
*/
void sendAudioBuffer(byte[] data, int offset, int length);
/**
* 发送音频数据<br>
*
* @param data 数据buffer
* @param offset 偏移量
* @param length 长度
* @param enableEncode true: 内部会依据{@link OnLiveIntercomListener#onStartVoiceIntercom()}返回的音频格式对送入的数据(必须为PCM)重新编码并发送
* false: 内部会直接发送音频数据
*/
void sendAudioBuffer(byte[] data, int offset, int length, boolean enableEncode);
- 处理声音播放(仅V1版本接口需要处理)。
当App端和设备端语音对讲通道建立后,设备端通过
onAudioParamsChange(AudioParams audioParams)
收到App端发过来的音频参数。依据该音频参数新建音频播放器,供后续接受的音频数据播放。
@Override
public void onAudioParamsChange(AudioParams audioParams) {
// 收到对端发送的音频参数
appendLog("收到客户端的音频参数: " + audioParams.toString());
// 初始化播放器
if (simpleStreamAudioTrack != null) {
simpleStreamAudioTrack.release();
audioTrackQueue.clear();
}
if (acousticEchoCanceler != null) {
acousticEchoCanceler.release();
}
if (noiseSuppressor != null) {
noiseSuppressor.release();
}
noiseSuppressor = NoiseSuppressor.create(simpleAudioRecord.getAudioSessionId());
if (noiseSuppressor != null) {
noiseSuppressor.setEnabled(true);
}
simpleStreamAudioTrack = new SimpleStreamAudioTrack(audioParams, AudioManager.STREAM_MUSIC, audioTrackQueue,
simpleAudioRecord.getAudioSessionId());
if (AcousticEchoCanceler.isAvailable()) {
acousticEchoCanceler = AcousticEchoCanceler.create(simpleAudioRecord.getAudioSessionId());
if (acousticEchoCanceler != null) {
appendLog("已开启回声消除");
acousticEchoCanceler.setEnabled(true);
}
}
simpleStreamAudioTrack.start();
}
@Override
public void onAudioBufferReceive(byte[] buffer, int size) {
// 收到对端发送的PCM数据
audioTrackQueue.add(buffer);
}
- 处理结束语音对讲指令(仅V1版本接口需要处理)。
@Override
public void onStopVoiceIntercom() {
appendLog("收到停止语音对讲指令");
// 收到停止语音对讲请求,停止录音
simpleAudioRecord.stop();
}
- 语音对讲错误处理。
@Override
public void onError(LiveIntercomError error) {
// 语音对讲发生错误
appendLog("语音对讲错误:" + error.getCode() + " msg:" + error.getMessage());
// 停止录音(仅V1版本接口需要处理)
simpleAudioRecord.stop();
// 停止播放(仅V1版本接口需要处理)
simpleStreamAudioTrack.stop();
}
语音对讲错误列表如下。
错误码 |
错误描述 |
LiveIntercomError.INVALID_AUDIO_PARAMS |
无效的设备端音频参数 |
LiveIntercomError.START_LIVE_INTERCOM_REQUEST_FAILED |
无效的语音对讲请求 |
LiveIntercomError.CONNECTION_STREAM_FAILED |
建立语音对讲流通道失败 |
LiveIntercomError.SEND_STREAM_DATA_FAILED |
发送音频数据失败 |
LiveIntercomError.RECEIVE_STREAM_DATA_FAILED |
接收音频数据失败 |
LiveIntercomError.INIT_RECORD_FAILED |
录音机初始化错误 |
LiveIntercomError.START_RECORD_FAILED |
录音机启动错误 |
LiveIntercomError.READ_RECORD_BUFFER_FAILED |
录音机数据读取错误 |
LiveIntercomError.INIT_AUDIO_PLAYER_FAILED |
音频播放器创建失败 |
开发抓图功能
抓图功能通过回调IUploadPicListener
来上传图片。回调信息包括上传地址、此次上传的图片ID、上传类型等。图片上传完成后,设备端通过IUploadPicProcessCallback
回调通知SDK上传任务完成。抓图功能主要分以下两种场景。
- App端发起抓图请求,设备抓图后上传至云端。
- 侦测事件触发,设备端自动抓图并上传图片至云端。
说明 您还需要确认拍照的物模型TriggerPicCapture里的UploadUrl
字符串长度,如果小于512字符,则需要修改到大于512字符,否则图片上传会受到影响。
- 抓图上传。
拍照上传任务的流程通常为:SDK通知设备拍照、设备拍照、设备上传图片、通知SDK上传结果。 详细流程如下。
- 设备端注册上传图片监听。
/**
* 上传图片监听
*/
public interface IUploadPicListener {
/**
* 触发上传
* @param uploadInfo 上传信息
* @param callback 上传结果回调
*/
void onUpload(UploadInfo uploadInfo, IUploadPicProcessCallback callback);
}
- 当App触发拍照,设备端通过
IUploadPicListener
的onUpload
获取到当前需要抓图并上传通知。
其中图片上传的URL从onUpload回调的uploadInfo参数中获取。
/**
* 上传图片
*/
public class UploadInfo {
/**
* 上传url
*/
private String uploadUrl;
/**
* 上传图片ID(用户生成的,只有事件上报时才有)
*/
private String picId;
/**
* 上传类型,0为拍照,1为事件图片上传。
*/
private int type;
}
- 设备端通过
IUploadPicProcessCallback
回调通知SDK上传结果。/**
* 上传图片结果回调
*/
public interface IUploadPicProcessCallback {
/**
* 上传任务成功
*/
void onSucceed();
/**
* 上传任务失败
*
* @param errorMsg 错误信息
*/
void onFailed(String errorMsg);
}
- 监听注册结果。
LinkKit SDK初始化成功后,设备端通过如下代码监听注册。
//注册上传图片监听,当App端触发拍照或者设备上报了事件以后,会在注册的回调中收到上传的回调。
IPCDev.getInstance().registerUploadPicListener(uploadPicListener);
/**
* 上传图片回调,回调中只有是事件照片上传的时候picId不为空且为上报事件时用户上传的ID。
* type为上传类型,0为App触发设备拍照,1为设备上传事件照片。
*/
private IUploadPicListener uploadPicListener = new IUploadPicListener() {
@Override
public void onUpload(final UploadInfo uploadInfo, final IUploadPicProcessCallback callback) {
final String url = uploadInfo.getUploadUrl();
final String picId = uploadInfo.getPicId();
final int type = uploadInfo.getType();
switch (type) {
//拍照上报
case 0:
//拍照并且上传
upLoadFile(url, pic, new CallBack(){
@Override
public void onSucceed() {
Log.d(TAG, "uploadImage onSucceed");
if (callback != null) {
callback.onSucceed();
}
}
@Override
public void onFailed(String error) {
Log.e(TAG, "uploadImage onFailed:" + error);
if (callback != null) {
callback.onFailed(error);
}
}
});
break;
//事件图片上报
case 1:
//上传图片
upLoadFile(url, pic, new CallBack(){
@Override
public void onSucceed() {
Log.d(TAG, "uploadImage onSucceed");
if (callback != null) {
callback.onSucceed();
}
}
@Override
public void onFailed(String error) {
Log.e(TAG, "uploadImage onFailed:" + error);
if (callback != null) {
callback.onFailed(error);
}
}
});
break;
default:
break;
}
}};
- 事件上报触发图片上传。
事件上报流程依次为:设备触发事件、事件上报、SDK通知设备上传图片、设备上传图片、通知SDK上传结果。 具体的流程如下。
- 设备端注册上传图片的监听。
- 当设备端触发事件,设备通过SDK提供的接口上报事件(需要上传图片ID)。
- SDK会通过
IUploadPicListener
的onUpload
通知设备上传图片。
- 设备端通过
onUpload
中的UploadInfo获取此次上传的URL、图片ID。
- 设备端自行通过URL上传图片。
- 设备端通过
IUploadPicProcessCallback
回调通知SDK上传结果。
上报事件接口实例如下。
//上报报警事件分成两步:1.上传报警,2.上传图片
//上报报警事件需要设备生成一个报警图片ID,这个报警图片ID要求是设备唯一的图片ID,建议用Unix时间戳。
// 不过这边需要确认一下拍照的物模型"TriggerPicCapture"里的"UploadUrl"的字符串长度,如果小于512请增加长度到512以上。
// 上报事件以后会收到IUploadPicListener(需要注册监听)内的onUpload回调,然后请将对应的图片上传到回调中url指定地址。
final String alarmPicId = String.valueOf(System.currentTimeMillis() / 1000);
IPCDev.getInstance().reportAlarmEvent(alarmPicId, 1, new ReportAlarmEventListener() {
@Override
public void onSucceed() {
Log.d(TAG, " report onSucceed alarmPicId:" + alarmPicId);
appendLog("上报事件成功");
}
@Override
public void onFailed(String msg) {
Log.e(TAG, " report onFailed e:" + msg);
appendLog("上报事件失败 e:" + msg);
}
});