Android版本设备端LinkVisual SDK提供直播功能,本文介绍实现直播功能的过程。
下文简称设备端Android版本LinkVisual SDK为LinkVisual SDK。
前提条件
背景信息
直播功能通过RTMP协议推流。其支持的视频编码格式和音频编码格式如下:
视频编码格式:H.264和H.265。
音频编码格式:G711a、G711u和AAC_LC。
操作步骤
步骤一:注册直播事件监听器和流错误监听器。
LinkVisual 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 预先录制时间,单位秒 */ 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); }
步骤二:处理开始直播推流请求。
当服务端下发推流请求时:通过回调
OnLiveStreamListener.onStartPushLiveStreaming(int streamId, int streamType, int preTimeInS)
方法,通知设备端需要开始采流并推流。处理开始直播推流请求时,一般需要同时开启IPC设备和录音机进行采流。
若开启,请调用
MediaCodec
方法对IPC设备采集的数据进行H264编码,对录音机采集的数据进行音频编码,并设置对应格式的音视频参数。若不开启,请跳过此步骤。
@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 音频帧时间戳,单位为毫秒 */ void sendAudioData(int streamId, ByteBuffer directByteBuffer, int length, long timeStampInMs) throws NoSuchStreamException /** * 发送视频帧数据 * * @param streamId 流ID * @param directByteBuffer 源数据 * @param length 数据长度 * @param isIFrame 是否为I帧 * @param timeStampInMs 视频帧时间戳,单位为毫秒 */ 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 音频帧时间戳,单位为毫秒 * @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 视频帧时间戳,单位为毫秒 * @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个字节,并查看结果。
视频播放时对H.264和H.265的帧结构有如下要求,您可参考下面的代码,打印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");
H.264的I帧格式要求为
帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR
。以下图为例,帧分隔符为0x000001或者0x00000001;序列参数集SPS(Sequence Parameter Set)起始数据为0x67;图像参数集PPS(Picture Parameter Set)起始数据为0x68;即时解码器刷新IDR(Instantaneous Decoding Refresh)起始数据为0x65。
H.265的I帧格式要求为
帧分隔符+VPS+帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR
。以下图为例,帧分隔符为0x000001或者0x00000001;视频参数集VPS(Video Parameter Set)起始数据为0x40;SPS起始数据为0x42;PPS起始数据为0x44;IDR起始数据为0x26。
步骤三:处理结束推流请求。
服务端下发停止推流请求时,回调
OnLiveStreamListener.onStopPushLiveStreaming()
方法通知设备端停止推流。处理停止推流时,一般情况下需要同时停止采集IPC设备数据和录音机数据。
若需要,请调用
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)
方法接收和处理流错误。错误码详细信息,请参考本文下方的错误码。
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 | 建立RTMP链接失败。 | 请检查网络是否正常然后重试。 |
3 | StreamError.ERROR_STREAM_STOP_FAILED | 停止流失败。 | 因引入了无效的StreamId而引发的错误,该错误可忽略。 |
4 | StreamError.ERROR_STREAM_SEND_VIDEO_FAILED | 发送视频数据失败。 | 请根据RTMP错误码判断具体的出错原因。RTMP错误码的详细信息,请参考本文下方的RTMP错误码。 |
5 | StreamError.ERROR_STREAM_SEND_AUDIO_FAILED | 发送音频数据失败。 | 请根据RTMP错误码判断具体的出错原因。RTMP错误码的详细信息,请参考本文下方的RTMP错误码。 |
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数据包发送失败。 | 与服务端断开连接后再调用send接口会导致报该错误,可忽略。 |
-7 | RTMP_READ_MESSAGE_FAILED | RTMP消息读取失败。 | 该错误通常因服务端断开导致,可忽略。 |
-8 | RTMP_READ_TIMESTAMP_ERROR | 输入时间戳错误。 | 直播推流时需保证时间戳未出现回退。请检查时间戳是否合法后重试。 |