Android平台RTMP推送端实现外部数据对接推送和录像
背景
好多开发者在做Android平台RTMP推送对接的同时,除了编码前的数据外,还有些外部编码数据推送诉求,他们希望外部的编码音视频数据不止可以实现RTMP推送,还可以同时在推送端实时录制下来,本文以我们(官方)Android平台RTMP直播推送模块为例,介绍下外部数据对接流程和数据录制流程。
对接流程
开始推送
private boolean StartPush()
{
if (isPushing)
return false;
//relayStreamUrl = "rtmp://192.168.1.77/hls/stream1";
if (relayStreamUrl == null) {
Log.e(TAG, "StartPush URL is null...");
return false;
}
if (!OpenPushHandle())
return false;
if ( libPublisher.SmartPublisherSetURL(publisherHandle, relayStreamUrl) != 0 )
{
Log.e(TAG, "StartPush failed!");
}
int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);
if( startRet != 0)
{
Log.e(TAG, "Failed to call StartPublisher!");
if(isRTSPPublisherRunning)
{
libPublisher.SmartPublisherClose(publisherHandle);
publisherHandle = 0;
}
return false;
}
isPushing = true;
return true;
}
OpenPushHandle()实现
注意,如果对接外部编码后的音视频数据的话,调用SmartPublisherOpen()接口时,记得audio_opt和video_opt均传2。
private boolean OpenPushHandle()
{
if(publisherHandle != 0)
{
return true;
}
int audio_opt = 2;
int video_opt = 2;
int videoWidth = 640;
int videoHeight = 480;
publisherHandle = libPublisher.SmartPublisherOpen(myContext, audio_opt, video_opt,
videoWidth, videoHeight);
if (publisherHandle == 0 )
{
Log.e(TAG, "OpenPushHandle failed!");
return false;
}
Log.i(TAG, "publisherHandle=" + publisherHandle);
libPublisher.SetSmartPublisherEventCallbackV2(publisherHandle, new EventHandePublisherV2());
return true;
}
停止推送
public void StopPush()
{
if (!isPushing)
return;
isPushing = false;
libPublisher.SmartPublisherStopPublisher(publisherHandle);
if(!isRTSPPublisherRunning && !isRTSPServiceRunning)
{
libPublisher.SmartPublisherClose(publisherHandle);
publisherHandle = 0;
}
}
实时音视频数据投递
如果需要同时录制外部编码后的音视频数据,分别用以下接口完成数据投递:
涉及到的sps、pps或者audio的一些配置信息,上层很容易拿到,传递下去即可。
/**
* 设置编码后视频数据(H.264),如需录制编码后的数据,用此接口,且设置实际宽高
*
* @param codec_id, H.264对应 1
*
* @param data 编码后的video数据
*
*@param offset data的偏移
*
* @param size data length
*
* @param is_key_frame 是否I帧, if with key frame, please set 1, otherwise, set 0.
*
* @param timestamp video timestamp
*
* @param pts Presentation Time Stamp, 显示时间戳
*
* @param width, height: 编码后视频宽高
*
* @return {0} if successful
*/
public native int SmartPublisherPostVideoEncodedDataV3(long handle, int codec_id,
ByteBuffer data, int offset, int size,
int is_key_frame, long timestamp, long pts,
byte[] sps, int sps_len,
byte[] pps, int pps_len,
int width, int height);
/**
* 设置音频数据(AAC/PCMA/PCMU/SPEEX)
*
* @param codec_id:
*
* NT_MEDIA_CODEC_ID_AUDIO_BASE = 0x10000,
* NT_MEDIA_CODEC_ID_PCMA = NT_MEDIA_CODEC_ID_AUDIO_BASE,
* NT_MEDIA_CODEC_ID_PCMU,
* NT_MEDIA_CODEC_ID_AAC,
* NT_MEDIA_CODEC_ID_SPEEX,
* NT_MEDIA_CODEC_ID_SPEEX_NB,
* NT_MEDIA_CODEC_ID_SPEEX_WB,
* NT_MEDIA_CODEC_ID_SPEEX_UWB,
*
* @param data audio数据
*
* @param offset data的偏移
*
* @param size data length
*
* @param is_key_frame 是否I帧, if with key frame, please set 1, otherwise, set 0, audio忽略
*
* @param timestamp video timestamp
*
* @param parameter_info 用于AAC special config信息填充
*
* @param parameter_info_size parameter info size
*
* @param sample_rate 采样率,如果需要录像的话必须传正确的值
*
*@param channels 通道数, 如果需要录像的话必须传正确的值, 一般是1或者2
*
* @return {0} if successful
*/
public native int SmartPublisherPostAudioEncodedDataV3(long handle, int codec_id,
ByteBuffer data, int offset, int size,
int is_key_frame, long timestamp,
byte[] parameter_info, int parameter_info_size,
int sample_rate, int channels);
开始录像
private boolean StartRecorder()
{
if (!OpenPullHandle())
return false;
ConfigRecorderFuntion();
int iRecRet = libPlayer
.SmartPlayerStartRecorder(playerHandle);
if (iRecRet != 0) {
Log.e(TAG, "StartRecorder failed!");
if ( !isPulling &&!isPlaying && !isPushing && !isRTSPPublisherRunning)
{
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
return false;
}
isRecording = true;
return true;
}
停止录像
private void StopRecorder()
{
if ( !isRecording )
return;
isRecording = false;
libPlayer.SmartPlayerStopRecorder(playerHandle);
if ( !isPlaying && !isPulling && !isPushing && !isRTSPPublisherRunning)
{
libPlayer.SmartPlayerClose(playerHandle);
playerHandle = 0;
}
}
总结
外部数据对接的话,需要确保传递的音视频数据编码信息正常,相关的时间戳能对得上,从而确保好的用户体验。
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-05-18 公网可用的RTMP、RTSP测试地址(更新于2021年3月)
2021-05-18 rtmp/rtsp/hls公网真正可用的测试地址
2021-05-18 Windows平台RTMP|RTSP播放器实现画面全屏功能
2021-05-18 Windows平台RTMP|RTSP播放器为什么要兼容GDI绘制
2021-05-18 Windows平台RTMP推送|轻量级RTSP服务实现本地摄像头|屏幕|叠加数据预览
2021-05-18 Android对接实现内网无纸化会议|智慧教室|实时同屏功能
2021-05-18 QT实现低延迟的RTSP、RTMP播放器