首页 > 其他分享 >Android平台如何拉取RTSP|RTMP流并转发至轻量级RTSP服务?

Android平台如何拉取RTSP|RTMP流并转发至轻量级RTSP服务?

时间:2024-11-16 12:18:47浏览次数:3  
标签:publisher stream RTSP 拉取 _. 模块 轻量级

技术背景

好多了解我们模块的开发者都知道,我们有非常成熟的轻量级RTSP服务模块,可以采集摄像头或屏幕的数据,编码打包注入Android平台的轻量级RTSP服务模块,让Android设备端,充当个类似于网络摄像头的角色,对外提供个RTSP拉流的URL,实现内网环境下的无服务部署直播场景,这种在内网监控或智慧教室、无纸化场景等,非常实用。

技术实现

今天要探讨的是,如何把外部的RTSP|RTMP流,注入到轻量级RTSP服务?实际上,这块对大牛直播SDK来说,算不上新模块或技术,因为前些年已经实现了,对应的是我们的内网RTSP网关模块。

实现方式,和RTSP转RTMP推送有些类似,先把RTSP或RTMP流,拉取下来,回调编码后的H.264/H.265/AAC/PCMU/PCMA数据到上层。

上层模块,通过轻量级RTSP服务模块提供的编码后数据投递接口,实现数据源的对接。

先说拉取RTSP或RTMP流数据,并回调到上层:

/* SmartRelayDemo.java
 * Created by daniusdk.com
 * WeChat:xinsheng120
 */
class ButtonPullListener implements View.OnClickListener {
	public void onClick(View v) {
		if (stream_player_.is_pulling()) {
			Log.i(TAG, "Stop Pull..");

			boolean iRet = stream_player_.StopPull();

			if (!iRet) {
				Log.e(TAG, "Call StopPull failed..");
				return;
			}

			stream_player_.try_release();
			btnPullStream.setText("开始拉流");
		} else {
			Log.i(TAG, "Start playback stream++");

			if (!stream_player_.OpenPlayerHandle(playback_url_, play_buffer_, is_using_tcp_))
				return;

			if(audio_opt_ == 2)
			{
				libPlayer.SmartPlayerSetAudioDataCallback(stream_player_.get(), new PlayerAudioDataCallback(stream_publisher_));
			}
			if(video_opt_ == 2)
			{
				libPlayer.SmartPlayerSetVideoDataCallback(stream_player_.get(), new PlayerVideoDataCallback(stream_publisher_));
			}

			int is_pull_trans_code = 1;
			boolean iPlaybackRet = stream_player_.StartPull(is_pull_trans_code);
			if (!iPlaybackRet) {
				Log.e(TAG, "Call StartPlayer failed..");
				return;
			}

			btnPullStream.setText("停止拉流");
		}
	}
}

拉取到的视音频数据,投递到轻量级RTSP服务模块即可,先看视频数据回调处理:

	class PlayerVideoDataCallback implements NTVideoDataCallback
	{
		private WeakReference<LibPublisherWrapper> publisher_;
		private int video_buffer_size = 0;
		private ByteBuffer video_buffer_ = null;

		public PlayerVideoDataCallback(LibPublisherWrapper publisher) {
			if (publisher != null)
				publisher_ = new WeakReference<>(publisher);
		}

		@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 +
			//		",presentation_timestamp:" + presentation_timestamp);

			if ( video_buffer_ == null)
				return;

			LibPublisherWrapper publisher = publisher_.get();
			if (null == publisher)
				return;

			if (!publisher.is_publishing())
				return;

			video_buffer_.rewind();

			publisher.PostVideoEncodedData(video_codec_id, video_buffer_, sample_size, is_key_frame, timestamp, presentation_timestamp);

		}
	}

音频数据回调处理:

	class PlayerAudioDataCallback implements NTAudioDataCallback
	{
		private WeakReference<LibPublisherWrapper> publisher_;
		private int audio_buffer_size = 0;
		private int param_info_size = 0;

		private ByteBuffer audio_buffer_ = null;
		private ByteBuffer parameter_info_ = null;

		public PlayerAudioDataCallback(LibPublisherWrapper publisher) {
			if (publisher != null)
				publisher_ = new WeakReference<>(publisher);
		}

		@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);

			if ( audio_buffer_ == null)
				return;

			LibPublisherWrapper publisher = publisher_.get();
			if (null == publisher)
				return;

			if (!publisher.is_publishing())
				return;

			audio_buffer_.rewind();

			publisher.PostAudioEncodedData(audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size);
		}
	}

启动、停止RTSP服务:

	//启动/停止RTSP服务
	class ButtonRtspServiceListener implements View.OnClickListener {
		public void onClick(View v) {
			if (!rtsp_server_.empty()) {
				rtsp_server_.reset();
				btnRtspService.setText("启动RTSP服务");
				btnRtspPublisher.setEnabled(false);
				return;
			}

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

			int port = 8554;
			String user_name = null;
			String password = null;
			LibPublisherWrapper.RTSPServer.Handle server_handle = LibPublisherWrapper.RTSPServer.create_and_start_server(libPublisher,
					port, user_name, password);

			if (null == server_handle) {
				Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
				return;
			}

			rtsp_server_.reset(server_handle);

			btnRtspService.setText("停止RTSP服务");
			btnRtspPublisher.setEnabled(true);
		}
	}

发布RTSP流:

	//发布/停止RTSP流
	class ButtonRtspPublisherListener implements View.OnClickListener {
		public void onClick(View v) {
			if (stream_publisher_.is_rtsp_publishing()) {
				stopRtspPublisher();

				btnRtspPublisher.setText("发布RTSP流");
				btnGetRtspSessionNumbers.setEnabled(false);
				btnRtspService.setEnabled(true);
				return;
			}

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

			PusherInitAndSetConfig();

			String rtsp_stream_name = "stream1";
			stream_publisher_.SetRtspStreamName(rtsp_stream_name);
			stream_publisher_.ClearRtspStreamServer();

			stream_publisher_.AddRtspStreamServer(rtsp_server_.get_native());

			if (!stream_publisher_.StartRtspStream()) {
				stream_publisher_.try_release();
				Log.e(TAG, "调用发布rtsp流接口失败!");
				return;
			}

			startAudioRecorder();

			btnRtspPublisher.setText("停止RTSP流");
			btnGetRtspSessionNumbers.setEnabled(true);
			btnRtspService.setEnabled(false);
		}
	}

获取RTSP会话数:

	//获取RTSP会话数
	class ButtonGetRtspSessionNumbersListener implements View.OnClickListener {
		public void onClick(View v) {
			if (rtsp_server_.is_running()) {
				int session_numbers = rtsp_server_.get_client_session_number();

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

				PopRtspSessionNumberDialog(session_numbers);
			}
		}
	}

总结

Android平台内网RTSP网关模块,系内置轻量级RTSP服务模块扩展,完成外部RTSP/RTMP数据拉取并注入到轻量级RTSP服务模块工作,多个内网客户端直接访问内网轻量级RTSP服务获取公网数据,无需部署单独的服务器,支持RTSP/RTMP H.265数据接入。

内置轻量级RTSP服务模块和内置RTSP网关模块共同点:

内置轻量级RTSP服务模块和内置RTSP网关模块,核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,数据汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

内置轻量级RTSP服务模块和内置RTSP网关模块不同点:数据来源不同

1. 内置轻量级RTSP服务模块,数据源来自摄像头、屏幕、麦克风等编码前数据,或者本地编码后的对接数据;

2. 内置RTSP网关模块,实际上是RTSP/RTMP拉流模块+内置轻量级RTSP服务模块组合出来的。数据源来自RTSP或RTMP网络流,拉流模块完成编码后的音视频数据回调,然后,汇聚到内置轻量级RTSP服务模块。

标签:publisher,stream,RTSP,拉取,_.,模块,轻量级
From: https://blog.csdn.net/renhui1112/article/details/143768094

相关文章

  • 网页直播/点播播放器EasyPlayer.js RTSP播放器出现多路视频卡顿、内存开始飙升的原因
    EasyPlayer.jsRTSP播放器是TSINGSEE青犀流媒体组件系列中关注度较高的产品,经过多年的发展和迭代,目前已经有多个应用版本,包括RTSP版、RTMP版、Pro版以及js版,其中js版本作为网页播放器,受到了用户的广泛使用。1、问题说明在已经使用硬解码基础上,播放多路视频,会出现卡顿,内存开始飙......
  • git拉取代码报错invalid path解决,以及windows的一个坑
    错误日志:Cloninginto'overmind-efficiency'...remote:Enumeratingobjects:702,done.remote:Countingobjects:100%(702/702),done.remote:Compressingobjects:100%(286/286),done.remote:Total126341(delta287),reused581(delta221),pack-reu......
  • RTSP播放器EasyPlayer.js播放器PC电脑端播放视频时整个显示器会白屏的操作方法
    EasyPlayer.js播放器是TSINGSEE青犀流媒体组件系列中关注度较高的产品,经过多年的发展和迭代,目前已经有多个应用版本,包括RTSP版、RTMP版、Pro版以及js版,其中js版本作为网页播放器,受到了用户的广泛使用。在功能上,EasyPlayer支持直播、点播、录像、快照截图、MP4播放、多屏播放、倍......
  • 占用资源极低!这款轻量级 Nacos 性能炸裂!
    大家好,我是Java陈序员。在开发中,Nacos作为一款非常流行的微服务配置中心、注册中心,在构建微服务项目时往往会使用到它。但是对于个人开发者而言,云服务器资源有限,往往无法撑起Nacos服务的高内存使用!今天,给大家介绍一款轻量级的Nacos服务,功能平替,占用资源极低!关注微信公......
  • kubernetes镜像拉取失败解决方法 ErrImagePull
    被这个问题卡住了最少一个下午的时间。。。。不过就当熟悉k8s的命令了吧。。。只能这么安慰自己了最近在捣鼓k8s部署.netcore的后端(我是在windows上部署dockerdesktop来做测试的),在拉取我阿里云镜像仓库的私有镜像,搞来搞去搞了好久都没用dockpull registry.cn-hangzhou.aliyu......
  • ubuntu下配置vim插件,实现轻量级代码编辑器
    背景因为需要用虚拟机做实验,然后虚拟机配置的内存很小,如果使用vscode编辑器,内存占用太高,所以放弃,远程使用vscode通过sftp链接也会有很多bug,所以也放弃,鉴于以上。只能考虑使用vimvim的优点轻量级不需要gui的支持,可以在server和desktop版本之间不限制是使用vim插件安装cu......
  • 推荐一个Star超过2K的.Net轻量级的CMS开源项目
    推荐一个具有模块化和可扩展的架构的CMS开源项目。01项目简介PiranhaCMS是一个轻量级且跨平台的CMS库,专为.NET8设计。该项目提供多种模板,具备CMS基本功能,也有空模板方便从头开始构建新网站,甚至可以作为移动应用的后端。是一个完全解耦的CMS,意味着我们可以使用任何技术以任......
  • PostgreSQL流复制主从监控和自动故障转移的轻量级实现
    如何实现PostgreSQL的高可用,之前研究过repmgr以及pg_auto_failover,起作用都是起到主节点故障时,实现“自动故障转移”的目的。但是repmgr以及pg_auto_failover得缺点是对数据库侵入过多,需要在监控的数据库内部进行一系列的配置操作,同时需要启动第三方服务实现节点的可用性监控,这又......
  • RTSP --- 推流拉流测试
    安装 MediaMTX用以转发视频流安装ffmpeg用以推送或拉取视频流使用运行转发服务器$./mediamtx2024/11/1016:39:18INFMediaMTXv1.9.32024/11/1016:39:18INFconfigurationloadedfrom/home/chenjinhe/Desktop/newVolume/share/mediamtx_v1.9.3_linux_amd64/med......
  • YoloV10改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码
    论文介绍CARAFE模块概述:本文介绍了一种名为CARAFE(Content-AwareReAssemblyofFEatures)的模块,它是一种用于特征上采样的新方法。应用场景:CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程,特别适用于目标检测、实例分割、语义分割和图像修复等任务。目标:通过......