技术背景

Android上启动一个轻量级RTSP服务,让Android终端像网络摄像头一样提供个外部可供RTSP拉流的服务,在内网小并发又不希望部署单独流媒体服务的场景下非常适用,在Android终端实现这样的流媒体服务,决定了,只能是轻量级的服务。可以通过集成第三方库或编写自定义的RTSP服务器代码来实现这一功能。

今天我们介绍两种方案,一种是GStreamer,另外一种,大牛直播SDK的SmartRtspServer。

方案比较

GStreamer

1. GStreamer特点

在Android平台上使用GStreamer来启动RTSP服务涉及几个关键步骤,包括配置GStreamer环境、编写GStreamer管道(pipeline)以及集成到Android应用中。

  • 模块化:GStreamer的模块化设计使得开发者可以根据需要选择和组合不同的元素,构建出满足特定需求的媒体处理流程。
  • 可扩展:GStreamer支持用户自定义元素,开发者可以根据需要编写新的元素来扩展GStreamer的功能。
  • 高性能:GStreamer支持多线程和异步处理,能够在多核处理器上高效地处理大量数据。
  • 跨平台:GStreamer可以在多种操作系统上运行,包括Linux、Windows和macOS等。
  • 支持多种媒体格式:GStreamer支持广泛的音频和视频格式,包括常见的编解码器和容器格式。

2. GStreamer管道代码示例

GStreamer管道定义了媒体数据的处理流程。对于RTSP服务,你需要创建一个能够捕获媒体(如摄像头视频)、编码它,并通过RTSP服务器发送的管道,GStreamer的特点。

一个基本的RTSP服务器管道示例代码如下:

GstElement *pipeline, *src, *enc, *sink;  
  
/* 初始化GStreamer */  
gst_init(NULL, NULL);  
  
/* 创建管道 */  
pipeline = gst_parse_launch("videotestsrc ! videoconvert ! x264enc ! rtph264pay name=pay0 pt=96", NULL);  
  
/* 设置RTSP服务器的地址和端口 */  
g_object_set(G_OBJECT(pay0), "server-port", 8554, NULL);  
  
/* 启动管道 */  
gst_element_set_state(pipeline, GST_STATE_PLAYING);  
  
/* ...(其他代码,如处理错误、清理等)... */

3. 集成到Android应用

有了GStreamer管道的代码,你需要将其集成到你的Android应用中:

  • 在Android的ActivityService中调用JNI函数来初始化GStreamer并设置管道。
  • 处理Android摄像头的权限和初始化。
  • 使用appsrc元素(如果适用)从Android摄像头捕获视频帧,并将其推送到GStreamer管道中。
  • 管理GStreamer的生命周期,确保在Android应用的生命周期事件(如onPauseonResumeonDestroy)中正确地停止和启动GStreamer。

SmartRtspServer

下面介绍的是大牛直播SDK的SmartRtspServer,不同于GStreamer,SmartRtspServer功能更完善,稳定性和商业度更高,实现逻辑如下:

Android平台轻量级RTSP服务之GStreamer还是SmartRtspServer_安卓启动rtsp服务

  • 配置摄像头:首先,可使用Android的Camera2 API或CameraX库来捕获视频帧,考虑到好的体验和目前市面上的版本,都已经是5.0以后,一般建议使用Camera2采集;
  • 视频编码:将捕获到的视频帧编码为适合网络传输的格式,如H.264或H.265,音频的话,采集到的麦克风数据,可以编码成AAC或者PCMA;
  • 实现RTSP服务器:自研实现轻量级RTSP服务逻辑,支持设置RTSP服务器的参数,如端口号、流名称等。同时,配置服务器以从摄像头麦克风接收视音频流,并将其封装为RTSP流;
  • 启动服务器:启动RTSP服务器,使其开始监听并响应RTSP客户端的请求,发布RTSP流,对外提供RTSP拉流能力;
  • 查看RTSP会话数:轻量级RTSP服务,需要有支持查看RTSP会话数的能力。

功能设计

  •  [视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  • 协议:RTSP;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  • [音视频]支持纯音频/纯视频/音视频;
  • [摄像头]支持采集过程中,前后摄像头实时切换;
  • 支持帧率、关键帧间隔(GOP)、码率(bit-rate)设置;
  • [实时水印]支持动态文字水印、png水印;
  • [实时快照]支持实时快照;
  • [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  • [外部编码前视频数据对接]支持YUV数据对接;
  • [外部编码前音频数据对接]支持PCM对接;
  • [外部编码后视频数据对接]支持外部H.264、H.265数据对接;
  • [外部编码后音频数据对接]外部AAC数据对接;
  • [扩展录像功能]支持和录像SDK组合使用,录像相关功能。
  • 支持RTSP端口设置;
  • 支持RTSP鉴权用户名、密码设置;
  • 支持获取当前RTSP服务会话连接数;
  • 支持Android 5.1及以上版本。

接口设计

Android内置轻量级RTSP服务模块接口设计

调用描述

接口

接口描述

SmartRTSPServerSDK

初始化RTSP Server

InitRtspServer

Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)

创建一个rtsp server

OpenRtspServer

创建一个rtsp server,返回rtsp server句柄

设置端口

SetRtspServerPort

设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口

设置鉴权用户名、密码

SetRtspServerUserNamePassword

设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置

获取rtsp server当前会话数

GetRtspServerClientSessionNumbers

获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用

启动rtsp server

StartRtspServer

启动rtsp server

停止rtsp server

StopRtspServer

停止rtsp server

关闭rtsp server

CloseRtspServer

关闭rtsp server

UnInit rtsp server

UnInitRtspServer

UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)

SmartRTSPServerSDK供Publisher调用的接口

设置rtsp的流名称

SetRtspStreamName

设置rtsp的流名称

给要发布的rtsp流设置rtsp server

AddRtspStreamServer

给要发布的rtsp流设置rtsp server, 一个流可以发布到多个rtsp server上,rtsp server的创建启动请参考OpenRtspServer和StartRtspServer接口

清除设置的rtsp server

ClearRtspStreamServer

清除设置的rtsp server

启动rtsp流

StartRtspStream

启动rtsp流

停止rtsp流

StopRtspStream

停止rtsp流

调用示例

以SmartRtspServer采集摄像头为例,先初始化RTSP Server:

/*
 * MainActivity.java
 * Author: daniusdk.com
 * WeChat:xinsheng120
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	...

	context_ = this.getApplicationContext();
	
	libPublisher = new SmartPublisherJniV2();

	libPublisher.InitRtspServer(context_);      //和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用
}

启动、停止RTSP服务:

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

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

			isRTSPServiceRunning = false;
			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端口失败! 请检查端口是否重复或者端口不在范围内!");
			}

			if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {
				Log.i(TAG, "启动rtsp server 成功!");
			} else {
				libPublisher.CloseRtspServer(rtsp_handle_);
				rtsp_handle_ = 0;
				Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
			}

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

			isRTSPServiceRunning = true;
		}
	}
}

stopRtspService()实现如下:

//停止RTSP服务
private void stopRtspService() {
	if(!isRTSPServiceRunning)
	{
		return;
	}
	if (libPublisher != null && rtsp_handle_ != 0) {
		libPublisher.StopRtspServer(rtsp_handle_);
		libPublisher.CloseRtspServer(rtsp_handle_);
		rtsp_handle_ = 0;
	}
}

发布、停止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..");

		InitAndSetConfig();

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

		stream_publisher_.AddRtspStreamServer(rtsp_handle_);

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

		startAudioRecorder();
		startLayerPostThread();

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

stopRtspPublisher()实现如下:

//停止发布RTSP流
private void stopRtspPublisher() {
	stream_publisher_.StopRtspStream();
	stream_publisher_.try_release();

	if (!stream_publisher_.is_publishing())
		stopAudioRecorder();
}

总结

Android平台实现内网环境轻量级RTSP服务,共享摄像头或麦克风数据,如果自身技术栈完备,可以考虑基于GStreamer实现,顺便也积累了流媒体相关的能力,如果商业化产品,对质量和功能性能要求非常高,可以用现成的SmartRtspServer,集成复杂度低,更稳定可靠,以上是二者的比较,感兴趣的开发者,可以单独跟我沟通探讨。