ffmpeg pulls RTSP stream and forwards RTMP server

overall design

1. Streaming: Get the audio and video data through the data callback interface of RTSP|RTMP live broadcast SDK;

2. Retweet: Use the encoded data input interface of the RTMP live push SDK to pass the callback data to the RTMP live push module to realize the forwarding of RTSP|RTMP data streams to the RTMP server;

3. Recording: If you need to record, use the RTSP|RTMP live broadcast SDK to pull the audio and video data and directly store the MP4 file;

4. Snapshot: If you need a real-time snapshot, after pulling the stream, decode and call the player-side snapshot interface to generate a snapshot, because the snapshot involves video data decoding.

Demo mainly implements the following functional point display:

1. Set the URL for RTMP and RTSP streaming;

2. Set the RTMP URL for retweet;

3. Real-time playback | During the recording process, real-time mute and take snapshots;

4. Real-time playback;

5. Real-time recording;

6. The pulled streaming data is retweeted in real time, corresponding to "Start streaming";

7. Inject the pulled stream data into the lightweight RTSP service. After starting the service, publish the RTSP stream and provide an accessible RTSP URL to the outside world.

Note: The above four playback, recording, RTMP retweet, and lightweight RTSP injection services can work independently, and related functions can be started or stopped at any time without affecting each other.

Related code implementation

Start streaming

The purpose of streaming is mainly to start the data callback. Note: streaming does not directly play out the window, but only gets the data. If you need to preview the streaming data locally, you can click "Start Playing".

Note: Before "Start Pushing Streaming" and "Publish RTSP Streaming", you must first "Start Pulling Streaming" to get the audio and video data.

private boolean StartPull()
	{
		if ( isPulling )
			return false;

		if (!OpenPullHandle())
			return false;

		libPlayer.SmartPlayerSetAudioDataCallback(playerHandle, new PlayerAudioDataCallback());
		libPlayer.SmartPlayerSetVideoDataCallback(playerHandle, new PlayerVideoDataCallback());

		int is_pull_trans_code  = 1;
		libPlayer.SmartPlayerSetPullStreamAudioTranscodeAAC(playerHandle, is_pull_trans_code);

		int startRet = libPlayer.SmartPlayerStartPullStream(playerHandle);

		if (startRet != 0) {
			Log.e(TAG, "Failed to start pull stream!");

			if(!isPlaying && !isRecording && isPushing && !isRTSPPublisherRunning)
			{
				libPlayer.SmartPlayerClose(playerHandle);
				playerHandle = 0;
			}

			return false;
		}

		isPulling = true;
		return true;
	}

Switching to the OpenPullHandle() package here actually means starting to investigate the Player's Open() interface, obtaining the player handle, and then setting up the basic data interface, such as event callback, buffer time, TCP/UDP mode, streaming URL, etc.;


private boolean OpenPullHandle()
	{
		if (playerHandle != 0) {
			return true;
		}

		playbackUrl = "rtsp://admin:[email protected]:554/h265/ch1/main/av_stream";

		if (playbackUrl == null) {
			Log.e(TAG, "playback URL with NULL...");
			return false;
		}

		playerHandle = libPlayer.SmartPlayerOpen(myContext);

		if (playerHandle == 0) {
			Log.e(TAG, "playerHandle is nil..");
			return false;
		}

		libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,
				new EventHandePlayerV2());

		libPlayer.SmartPlayerSetBuffer(playerHandle, playBuffer);

		// set report download speed
		// libPlayer.SmartPlayerSetReportDownloadSpeed(playerHandle, 1, 5);

		//设置RTSP超时时间
		int rtsp_timeout = 12;
		libPlayer.SmartPlayerSetRTSPTimeout(playerHandle, rtsp_timeout);

		//设置RTSP TCP/UDP模式自动切换
		int is_auto_switch_tcp_udp

Stop streaming

private void StopPull()
	{
		if ( !isPulling )
			return;

		libPlayer.SmartPlayerStopPullStream(playerHandle);

		if ( !isPlaying && !isRecording && !isPushing && !isRTSPPublisherRunning)
		{
			libPlayer.SmartPlayerClose(playerHandle);
			playerHandle = 0;
		}

		isPulling = false;
	}

Start playing


private boolean StartPlay()
	{
		if (!OpenPullHandle())
			return false;

		// 如果第二个参数设置为null,则播放纯音频
		libPlayer.SmartPlayerSetSurface(playerHandle, sSurfaceView);

		libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);

		// External Render test
		// libPlayer.SmartPlayerSetExternalRender(playerHandle, new
		// RGBAExternalRender());
		// libPlayer.SmartPlayerSetExternalRender(playerHandle, new
		// I420ExternalRender());

		libPlayer.SmartPlayerSetFastStartup(playerHandle, isFastStartup ? 1 : 0);

		libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 1);

		if (isMute) {
			libPlayer.SmartPlayerSetMute(playerHandle, isMute ? 1
					: 0);
		}

		if (isHardwareDecoder)
		{
			int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);

			int isSupportH264HwDecoder = libPlayer
					.SetSmartPlayerVideoHWDecoder(playerHandle, 1);

			Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
		}

		libPlayer.SmartPlayerSetLowLatencyMode(playerHandle, isLowLatency ? 1
				: 0);

		libPlayer.SmartPlayerSetRotation(playerHandle, rotate_degrees);

		int iPlaybackRet = libPlayer
				.SmartPlayerStartPlay(playerHandle);

		if (iPlaybackRet != 0) {
			Log.e(TAG, "StartPlay failed!");

			if ( !isPulling && !isRecording && !isPushing && !isRTSPPublisherRunning)
			{
				libPlayer.SmartPlayerClose(playerHandle);
				playerHandle = 0;
			}

			return false;
		}

		isPlaying = true;
		return true;
	}

Stop play

private void StopPlay()
	{
		if ( !isPlaying )
			return;

		isPlaying = false;

		libPlayer.SmartPlayerStopPlay(playerHandle);

		if ( !isPulling && !isRecording && !isPushing && !isRTSPPublisherRunning)
		{
			libPlayer.SmartPlayerClose(playerHandle);
			playerHandle = 0;
		}
	}

Start recording


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;
		}
	}

Start streaming



private boolean StartPush()
	{
		if (isPushing)
			return false;

		relayStreamUrl = "rtmp://192.168.0.211:1935/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;
	}

Start pushing and adjust the OpenPushHandle() package. The specific code is as follows:

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

Stop pushing

public void StopPush()
	{
		if (!isPushing)
			return;

		isPushing = false;

		libPublisher.SmartPublisherStopPublisher(publisherHandle);

		if(!isRTSPPublisherRunning && !isRTSPServiceRunning)
		{
			libPublisher.SmartPublisherClose(publisherHandle);
			publisherHandle = 0;
		}
	}

Start RTSP service



//启动/停止RTSP服务
	class ButtonRtspServiceListener implements OnClickListener {
		public void onClick(View v) {
			if (isRTSPServiceRunning) {
				stopRtspService();

				btnRtspService.setText("启动RTSP服务");
				btnRtspPublisher.setEnabled(false);

				isRTSPServiceRunning = false;
				return;
			}

			if(!OpenPushHandle())
			{
				return;
			}

			Log.i(TAG, "onClick start rtsp service..");

			rtsp_handle_ = libPublisher.OpenRtspServer(0);

			if (rtsp_handle_ == 0) {
				Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性");
			} else {
				int port = 8554;
				if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {
					libPublisher.CloseRtspServer(rtsp_handle_);
					rtsp_handle_ = 0;
					Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
				}

				//String user_name = "admin";
				//String password = "12345";
				//libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password);

				//一般来说单播网络设备支持的好,wifi组播

Stop RTSP service

//停止RTSP服务
	private void stopRtspService() {
		if(!isRTSPServiceRunning)
			return;

		if (libPublisher != null && rtsp_handle_ != 0) {
			libPublisher.StopRtspServer(rtsp_handle_);
			libPublisher.CloseRtspServer(rtsp_handle_);
			rtsp_handle_ = 0;
		}

		if(!isPushing)
		{
			libPublisher.SmartPublisherClose(publisherHandle);
			publisherHandle = 0;
		}

		isRTSPServiceRunning = false;
	}

Start publishing RTSP



private boolean StartRtspStream()
	{
		if (isRTSPPublisherRunning)
			return false;

		String rtsp_stream_name = "stream1";
		libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);
		libPublisher.ClearRtspStreamServer(publisherHandle);

		libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);

		if (libPublisher.StartRtspStream(publisherHandle, 0) != 0)
		{
			Log.e(TAG, "调用发布rtsp流接口失败!");

			if (!isPushing)
			{
				libPublisher.SmartPublisherClose(publisherHandle);
				publisherHandle = 0;
			}

			return false;
		}

		isRTSPPublisherRunning = true;
		return true;
	}

Stop publishing RTSP stream

//停止发布RTSP流
	private void stopRtspPublisher()
	{
		if(!isRTSPPublisherRunning)
			return;

		if (libPublisher != null) {
			libPublisher.StopRtspStream(publisherHandle);
		}

		if (!isPushing && !isRTSPServiceRunning)
		{
			libPublisher.SmartPublisherClose(publisherHandle);
			publisherHandle = 0;
		}

		isRTSPPublisherRunning = false;
	}

Get the number of RTSP connection sessions

//当前RTSP会话数弹出框
	private void PopRtspSessionNumberDialog(int session_numbers) {
		final EditText inputUrlTxt = new EditText(this);
		inputUrlTxt.setFocusable(true);
		inputUrlTxt.setEnabled(false);

String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers

;
		inputUrlTxt.setText(session_numbers_tag);

		AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
		builderUrl
				.setTitle("内置RTSP服务

")
				.setView(inputUrlTxt).setNegativeButton("确定", null);
		builderUrl.show();
	}
//获取RTSP会话数
	class ButtonGetRtspSessionNumbersListener implements OnClickListener {
		public void onClick(View v) {
			if (libPublisher != null && rtsp_handle_ != 0) {
				int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);

				Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);

				PopRtspSessionNumberDialog(session_numbers);
			}
		}
	};




Guess you like

Origin blog.csdn.net/xiehuanbin/article/details/133160650