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