背景
好多开发者在做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;}}
总结
外部数据对接的话,需要确保传递的音视频数据编码信息正常,相关的时间戳能对得上,从而确保好的用户体验。