Discussion on several situations of RTMP multi-instance push on Android platform

Discussion on several situations of RTMP multi-instance push on Android platform

Many developers mentioned how to implement Android platform, multi-instance push, multi-instance push, there are several understandings:

  1. Multi-channel encoding, multiple instances are pushed to different RTMP URLs (for example, the Android capture board is connected to 2 channels at the same time);
  2. With the same encoding, multiple instances are pushed to different RTMP URLs (for example, pushed to different RTMP servers on the intranet and extranet);
  3. Multiple instances of H.264/AAC data after partial-channel encoding and partial-channel docking are pushed to different RTMP URLs (hybrid push).

At present, most of the designs on the market are not flexible enough. The following takes "Android callback encoded audio and video data" as an example to push all the original encoded RTMP data out, and then the encoded data is called back to the upper layer, and then Start a new Publisher instance and push it to the new RTMP address (of course it is just a business display, which can actually be used to connect to third-party systems, such as GB28181 or other services):

The specific process is as follows:

  1. Set audio and video callback

Corresponding interface:

/** * Set Audio Encoded Data Callback. * * @param audio_encoded_data_callback: Audio Encoded Data Callback. * * @return {0} if successful */ public native int SmartPublisherSetAudioEncodedDataCallback ( long handle, Object audio_encoded_data_callback) ; /** * Set Video Encoded Data Callback. * * @param video_encoded_data_callback: Video Encoded Data Callback. * * @return {0} if successful */ public native int SmartPublisherSetVideoEncodedDataCallback ( long handle, Object video_encoded_data_callback) ; Set callback: libPublisher.SmartPublisherSetAudioEncodedDataCallback(publisherHandle, new PublisherAudioEncodedDataCallback()); libPublisher.SmartPublisherSetVideoEncodedDataCallback(publisherHandle, new PublisherVideoEncodedDataCallback()); Copy code
  1. Implement PublisherAudioEncodedDataCallback and PublisherVideoEncodedDataCallback:
class PublisherAudioEncodedDataCallback implements NTAudioDataCallback { private int audio_buffer_size = 0 ; private int param_info_size = 0 ; private ByteBuffer audio_buffer_ = null ; private ByteBuffer parameter_info_ = null ; @Override public ByteBuffer getAudioByteBuffer ( int size) { //Log.i("getAudioByteBuffer", "size: "+ size); if (size < 1 ) { return null ; } if (size <= audio_buffer_size && audio_buffer_ != null ) { return audio_buffer_; } audio_buffer_size = size + 512 ; audio_buffer_size = (audio_buffer_size + 0xf ) & (~ 0xf ); audio_buffer_ = ByteBuffer.allocateDirect(audio_buffer_size); //Log.i("getAudioByteBuffer", "size: "+ size +" buffer_size:" + audio_buffer_size); return audio_buffer_; } @Override public ByteBuffer getAudioParameterInfo ( int size) { //Log.i("getAudioParameterInfo", "size: "+ size); if (size < 1 ) { return null ; } if (size <= param_info_size && parameter_info_ != null ) { return parameter_info_; } param_info_size = size + 32 ; param_info_size = (param_info_size + 0xf ) & (~ 0xf ); parameter_info_ = ByteBuffer.allocateDirect(param_info_size); //Log.i("getAudioParameterInfo", "size: "+ size +" buffer_size:" + param_info_size); return parameter_info_; } public void onAudioDataCallback ( int ret, int audio_codec_id, int sample_size, int is_key_frame, long timestamp, int sample_rate, int channel, int parameter_info_size, long reserve) { Log.i( "onAudioDataCallback" , "ret: " + ret + ", audio_codec_id:" + audio_codec_id + ", sample_size: " + sample_size + ", timestamp:" + timestamp + ",sample_rate:" + sample_rate + ",chn : " + channel + ", parameter_info_size:" + parameter_info_size); if (audio_buffer_ == null ) return ; audio_buffer_.rewind(); if (ret == 0 && publisherHandle2 != 0 ) { libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle2, audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size); } } } class PublisherVideoEncodedDataCallback implements NTVideoDataCallback { private int video_buffer_size = 0 ; private ByteBuffer video_buffer_ = null ; @Override public ByteBuffer getVideoByteBuffer ( int size) { //Log.i("getVideoByteBuffer", "size: "+ size); if (size < 1 ) { return null ; } if (size <= video_buffer_size && video_buffer_ != null ) { return video_buffer_; } video_buffer_size = size + 1024 ; video_buffer_size = (video_buffer_size + 0xf ) & (~ 0xf ); video_buffer_ = ByteBuffer.allocateDirect(video_buffer_size); //Log.i("getVideoByteBuffer", "size: "+ size +" buffer_size:" + video_buffer_size); return video_buffer_; } public void onVideoDataCallback ( int ret, int video_codec_id , int sample_size, int is_key_frame, long timestamp, int width, int height, long presentation_timestamp) { Log.i ( "onVideoDataCallback" , "RET:" + RET + ", video_codec_id:" + video_codec_id + ", SAMPLE_SIZE:" + SAMPLE_SIZE + ", is_key_frame:" + is_key_frame + ", timestamp:" + timestamp + ", width : " + width + ", height:" + height + ",presentation_timestamp:" + presentation_timestamp); if (video_buffer_ == null ) return ; video_buffer_.rewind(); if (ret == 0 && publisherHandle2 != 0 ) { libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle2, video_codec_id, video_buffer_, sample_size, is_key_frame, timestamp, presentation_timestamp); } } } Copy code
  1. Provide start callback data and stop callback data interface:
/** * Start output Encoded Data (for callback of encoded audio and video data) * * @return {0} if successful */ public native int SmartPublisherStartOutputEncodedData ( long handle) ; /** * Stop output Encoded Data * * @return {0} if successful */ public native int SmartPublisherStopOutputEncodedData ( long handle) ; copy the code
  1. Examples of calling the upper demo:
class ButtonEncodedDataCallbackListener the implements OnClickListener to { public void the onClick (View V) { IF (isEncodedDatacallbackRunning) { stopEncodedDataCallback(); if (!isPushing && !isRTSPPublisherRunning && !isRecording) { ConfigControlEnable( true ); } btnEncodedDataCallback.setText( "Start encoding data callback" ); isEncodedDatacallbackRunning = false ; if (publisherHandle2 != 0 ) { libPublisher.SmartPublisherStopPublisher(publisherHandle2); libPublisher.SmartPublisherClose(publisherHandle2); publisherHandle2 = 0 ; } return ; } Log.i(TAG, "onClick start encoded data callback.." ); if (libPublisher == null ) return ; if (!isPushing && !isRTSPPublisherRunning && !isRecording) { InitAndSetConfig(); } libPublisher.SmartPublisherSetAudioEncodedDataCallback(publisherHandle, new PublisherAudioEncodedDataCallback()); libPublisher.SmartPublisherSetVideoEncodedDataCallback(publisherHandle, new PublisherVideoEncodedDataCallback()); int startRet = libPublisher.SmartPublisherStartOutputEncodedData(publisherHandle); if (startRet != 0 ) { isEncodedDatacallbackRunning = false ; Log.e(TAG, "Failed to start encoded data callback." ); return ; } if (!isPushing && !isRTSPPublisherRunning && !isRecording) { if (pushType == 0 || pushType == 1 ) { CheckInitAudioRecorder(); //enable pure video publisher.. } ConfigControlEnable( false ); } btnEncodedDataCallback.setText( "Stop encoding data callback" ); isEncodedDatacallbackRunning = true ; int audio_opt = 2 ; int video_opt = 2 ; publisherHandle2 = libPublisher.SmartPublisherOpen(myContext, audio_opt, video_opt, videoWidth, videoHeight); if (publisherHandle2 == 0 ) { Log.e(TAG, "sdk open failed!" ); return ; } String relayUrl = "rtmp://player.daniulive.com:1935/hls/stream8888" ; libPublisher.SmartPublisherSetURL(publisherHandle2, relayUrl); libPublisher.SmartPublisherStartPublisher(publisherHandle2); } } ; //Data callback after stopping encoding private void stopEncodedDataCallback () { if (!isEncodedDatacallbackRunning) { return ; } if (!isPushing && !isRTSPPublisherRunning && !isRecording) { if (audioRecord_ != null ) { Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording.." ); audioRecord_.Stop(); if (audioRecordCallback_ != null ) { audioRecord_.RemoveCallback(audioRecordCallback_); audioRecordCallback_ = null ; } audioRecord_ = null ; } } if (libPublisher != null ) { libPublisher.SmartPublisherStopOutputEncodedData(publisherHandle); } if (!isPushing && !isRTSPPublisherRunning && !isRecording) { if (publisherHandle != 0 ) { if (libPublisher != null ) { libPublisher.SmartPublisherClose(publisherHandle); publisherHandle = 0 ; } } } } Copy code

In the demo above, in order to demonstrate the effect of multiple instances, a new push instance (corresponding to the new publisherHandle) has been opened. The audio and video encoded data, through the new instance, calls the encoded audio and video data interface, and continues to push RTMP out. So as to achieve the purpose of multi-instance push.