如何在Android端实现轻量级RTSP服务(类似于IPC)

为什么要设计轻量级RTSP服务

首先声明一点,本blog提到的轻量级RTSP服务,类似于网络摄像头(IPC),而非传统意义的接受外部推流的RTSP服务器。

轻量级RTSP服务解决的核心痛点:避免用户单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头|屏幕、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,视频编码支持H.264/H.265,音频对外输出AAC,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

如何设计轻量级RTSP服务接口

轻量级RTSP服务接口,通过服务和发布RTSP结合的方式,可在Android端快速实现类似于IPC功能,详细接口也可参看大牛直播SDK提供的官方DEMO模块(地址)。

1. InitRtspServer()

顾名思义,初始化RTSP服务,和UnInitRTSPServer()接口配套使用,不管启动几个RTSP服务,Init和UnInit接口仅需调一次即可。

	/** Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)** @param ctx: get by this.getApplicationContext()** @return {0} if successful*/public native int InitRtspServer(Object ctx);

2.UnInitRtspServer()

如InitRtspServer()所述,不再赘述,接口设计如下:

	/** UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)** @return {0} if successful*/public native int UnInitRtspServer();

3. OpenRtspServer()

创建个RTSP Server实例,返回实例句柄。

	/** 创建一个rtsp server** @param reserve:保留参数传0** @return rtsp server 句柄*/public native long OpenRtspServer(int reserve);

4. SetRtspServerPort()

设置RTSP服务的监听端口,一般来说,可以设置如554、1554等。

	/** 设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口** @param rtsp_server_handle: rtsp server 句柄** @param port: 端口号,可以设置为554,或者是1024到65535之间,其他值返回失败** @return {0} if successful*/public native int SetRtspServerPort(long rtsp_server_handle, int port);

5. SetRtspServerUserNamePassword()

接口目的是为了设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置。

	/** 设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置** @param rtsp_server_handle: rtsp server 句柄** @param user_name: 用户名(必须是英文)* * @param password:密码(必须是英文)** @return {0} if successful*/public native int SetRtspServerUserNamePassword(long rtsp_server_handle, String user_name, String password);

6. SetRtspServerMulticast()

设置rtsp server 组播, 如果server设置成组播就不能单播,组播和单播只能选一个, 一般来说单播网络设备支持的好,wifi组播很多路由器不支持。一般不建议设置组播。

	/** 设置rtsp server 组播, 如果server设置成组播就不能单播,组播和单播只能选一个, 一般来说单播网络设备支持的好,wifi组播很多路由器不支持** @param rtsp_server_handle: rtsp server 句柄** @param is_multicast: 是否组播, 1为组播, 0为单播, 其他值接口返回错误, 默认是单播** @return {0} if successful*/public native int SetRtspServerMulticast(long rtsp_server_handle, int is_multicast);

7. SetRtspServerMulticastAddress()

设置rtsp server 组播组播地址。

	/** 设置rtsp server 组播组播地址** @param rtsp_server_handle: rtsp server 句柄** @param multicast_address: 组播地址** 如果设置的不是组播地址, 将返回错误* 组播地址范围说明: [224.0.0.0, 224.0.0.255] 为组播预留地址, 不能设置. 可设置范围为[224.0.1.0, 239.255.255.255], 其中SSM地址范围为[232.0.0.0, 232.255.255.255]**  @return {0} if successful*/public native int SetRtspServerMulticastAddress(long rtsp_server_handle, String multicast_address);

8. GetRtspServerClientSessionNumbers()

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

	/** 获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用** @param rtsp_server_handle: rtsp server 句柄** @return {当前rtsp server会话数}*/public native int GetRtspServerClientSessionNumbers(long rtsp_server_handle);

9. StartRtspServer()

启动RTSP服务。

	/** 启动rtsp server** @param rtsp_server_handle: rtsp server 句柄** @param reserve: 保留参数传0** @return {0} if successful*/public native int StartRtspServer(long rtsp_server_handle, int reserve);

10. StopRtspServer()

停止RTSP服务。

	/** 停止rtsp server** @param rtsp_server_handle: rtsp server 句柄** @return {0} if successful*/public native int StopRtspServer(long rtsp_server_handle);

如何调用接口

1. 先调InitRtspServer()接口

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

2. 启动、停止RTSP服务

    //启动/停止RTSP服务class ButtonRtspServiceListener implements 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端口失败! 请检查端口是否重复或者端口不在范围内!");}//String user_name = "admin";//String password = "12345";//libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password);//一般来说单播网络设备支持的好,wifi组播很多路由器不支持,默认单播模式;如需使用组播模式,确保设备支持后,打开注释代码测试即可/*boolean is_enable_multicast = true;if(is_enable_multicast){int is_multicast = 1;libPublisher.SetRtspServerMulticast(rtsp_handle_, is_multicast);boolean is_enable_ssm_multicast = true;String multicast_address = "";if(is_enable_ssm_multicast){multicast_address = MakeSSMMulticastAddress();}else{multicast_address = MakeMulticastAddress();}Log.i(TAG, "is_enable_ssm_multicast:" + is_enable_ssm_multicast + " multiAddr: " + multicast_address);libPublisher.SetRtspServerMulticastAddress(rtsp_handle_, multicast_address);}*/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;}}}

3. 发布、停止RTSP流

    //发布/停止RTSP流class ButtonRtspPublisherListener implements OnClickListener {public void onClick(View v) {if (isRTSPPublisherRunning) {stopRtspPublisher();if (!isPushingRtmp && !isRecording && !isPushingRtsp) {ConfigControlEnable(true);}btnRtspPublisher.setText("发布RTSP流");btnGetRtspSessionNumbers.setEnabled(false);btnRtspService.setEnabled(true);isRTSPPublisherRunning = false;return;}Log.i(TAG, "onClick start rtsp publisher..");if (!isPushingRtmp && !isRecording && !isPushingRtsp) {InitAndSetConfig();}if (publisherHandle == 0) {Log.e(TAG, "Start rtsp publisher, publisherHandle is null..");return;}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流接口失败!");return;}if (!isPushingRtmp && !isRecording && !isPushingRtsp) {if (pushType == 0 || pushType == 1) {CheckInitAudioRecorder();    //enable pure video publisher..}ConfigControlEnable(false);}btnRtspPublisher.setText("停止RTSP流");btnGetRtspSessionNumbers.setEnabled(true);btnRtspService.setEnabled(false);isRTSPPublisherRunning = true;}};

4. 获取RTSP会话数

    //当前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);}}};

5. UnInitRtspServer()

			libPublisher.UnInitRtspServer();      //如已启用内置服务功能(InitRtspServer),调用UnInitRtspServer, 注意,即便是启动多个RTSP服务,也只需调用UnInitRtspServer一次

6. 生成组播地址

    private String MakeMulticastAddress(){// 239.0.1.0 ~ 239.255.255.255long begin = 0xEF000100;long end = 0xEFFFFFFF;long count = end - begin;Random random = new Random();long addr_host = begin + (random.nextInt((int)count));return DigitToIpAddr(addr_host);}private String MakeSSMMulticastAddress(){// 232.0.1.0 ~ 232.255.255.255long begin = 0xE8000100;long end = 0xE8FFFFFF;long count = end - begin;Random random = new Random();long addr_host = begin + (random.nextInt((int)count));return DigitToIpAddr(addr_host);}private String DigitToIpAddr(long ip) {return ((ip >> 24) & 0xFF) + "."+ ((ip >> 16) & 0xFF) + "."+ ((ip >> 8) & 0xFF) + "."+ (ip & 0xFF);}

总结

为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,我们在推送端发布了轻量级RTSP服务SDK,简单来说,之前推送端SDK支持的功能,内置轻量级RTSP服务SDK后,功能继续支持。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨

背景 随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求。目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务器部署非常复杂,可…

微信公众号怎么发送模板消息 微信公众平台模板消息免费发送的技巧

想要发免费的模板消息,该怎么发布?下面我们就来看看详细的教程。 1、首先我们需要在微信公众号里面开通模板消息功能,没有开通的需要去申请,不然就用不了,如下图所示。 微信公众号怎么发送模板消息?微信公众平台模板消息免费发…

如何理解面向过程和面向对象?

一句话理解面向对象 有人说:“如果上帝是程序员,他怎么创造世界上的所有动物。”,理解这个问题就理解了面向对像。 面向过程和面向对象区别? 面向过程的思路:什么事都自己做;分析解决问题所需的步骤&…

如何实现Android平台GB28181前端设备接入

技术背景 在实现Android平台GB28181前端设备接入之前,我们几年前就有了非常成熟的RTMP推送、RTSP推送和轻量级RTSP服务等模块,特别是RTMP推送,行业内应用非常广泛,好多开发者可能会问,既然有了以上模块,干…

foxmail怎么加入黑名单 foxmail导入黑名单邮箱地址的教程

1、首先,先进入到了foxmail的窗口的界面当中,进行点击菜单中工具,弹出了下拉菜单中,进行选中为“系统工具” foxmail怎么加入黑名单? foxmail导入黑名单邮箱地址的教程 2、进入到了的系统的设置的界面中,进行选中反…

Android前端音视频数据接入GB28181平台意义

技术背景 在我们研发Android平台GB28181前端音视频接入模块之前,业内听到最多的是,如何用Android或者Windows端,在没有国标IPC设备的前提下,模拟GB28181的信令和媒体流交互流程,实现GB28181整体方案的测试&#xff1f…

QQ浏览器怎样在首页显示优先推荐的网站

QQ浏览器怎样在首页显示优先推荐的网站?QQ浏览器显示优先推荐的网站的方法 1,在手机桌面上找到QQ浏览器的图标,点击打开。 QQ浏览器怎样在首页显示优先推荐的网站?QQ浏览器显示优先推荐的网站的方法[多图] 2,点击下…

push_back还是emplace_back?

背景和区别 emplace_back() 是 C11 之后,vector容器中添加的新方法,和 push_back()一样,都是在容器末尾添加一个新的元素,相对于push_back函数,它减少了一次类的构造。不同的是emplace_back() 在效率上相比较于 push_…

std::tuple还是struct?

std::tuple是C11提供的新模板类,可以翻译为“元组”,可把多个不同类型的变量组合成一个对象。std::tuple可看做std::pair的泛化实现,std::pair包含两个元素,std::tuple 可以同时包含多个元素,它拥有 struct 的表现&…

微软发动图明示新一代操作系统Windows 11

可能感到关注度不够,微软再次出面预热所谓的新一代Windows操作系统,阿拉伯数字“11”图形赫然出现在预热动图中,可以说暗示得不要再明显了。 值得注意的是,动图中11的两个1中间还有一位正打坐的小人,配合微软关注大脑…

std::atomic和std::mutex区别

​std::atomic介绍​ ​模板类std::atomic是C11提供的原子操作类型&#xff0c;头文件 #include<atomic>。​在多线程调用下&#xff0c;利用std::atomic可实现数据结构的无锁设计。​​ ​和互斥量的不同之处在于&#xff0c;std::atomic原子操作&#xff0c;主要是保…

C++ std::remove/std::remove_if/erase用法探讨

​std::remove 不会改变输入vector/string的长度。其过程相当于去除指定的字符&#xff0c;剩余字符往前靠。后面的和原始字符保持一致。​ 需要注意的是&#xff0c;remove函数是通过覆盖移去的&#xff0c;如果容器最后一个值刚好是需要删除的&#xff0c;则它无法覆盖掉容器…

深度技术Win11 64位最新旗舰版镜像V2021.08

深度技术Win11 64位最新旗舰版镜像V2021.08是微软最新版本的电脑操作系统&#xff0c;系统稳定性的进一步优化和提升&#xff0c;可以更好的获得整个纯版本系统的稳定性。支持系统智能激活服务&#xff0c;用户可以快速激活系统&#xff0c;提高系统使用率&#xff0c;有需要的…

再谈NULL和nullptr(C++11)区别

在谈NULL和nullptr区别之前&#xff0c;我们先看段代码&#xff1a; #include "stdafx.h" #include <iostream>using namespace std; void func(void *p) {cout << "p is pointer " << p << endl; } void func(int num) {cout &l…

C/C++如何快速区分指针数组|数组指针|函数指针|指针函数

如何区分这些概念&#xff0c;主要还是看后面两个字&#xff0c;中文表达模式“​表语定性名词​”&#xff0c;​所以关键的都是后面的这个名词​&#xff1a; ​指针数组​&#xff1a;一个数组&#xff0c;数组元素是指针&#xff0c;如&#xff1a; int* p[20]; ​数组指…

Foxmail记事插入的表格怎么设置单元格边距

Foxmail在使用记事时候&#xff0c;插入了表格&#xff0c;该怎么设置单元格的边距呢?下面我们就来看看详细的教程。 Foxmail记事插入的表格怎么设置单元格边距? 1、下载并安装Foxmail邮件客户端。 Foxmail记事插入的表格怎么设置单元格边距? 2、打开Foxmail邮件客户端&…

C++11新特性探索:原始字符串字面值(raw string literal)

原始字符串字面值(raw string literal)是C11引入的新特性。 原始字符串简单来说&#xff0c;“原生的、不加处理的”&#xff0c;字符表示的就是自己&#xff08;所见即所得&#xff09;&#xff0c;引号、斜杠无需 “\” 转义&#xff0c;比如常用的目录表示&#xff0c;引入…

C++11新特性探究:显式override和final

C中&#xff0c;我们一般可以以基类声明纯虚函数&#xff0c;然后让派生类继承并重写这个虚函数&#xff0c;用​override表示显示覆盖基类方法&#xff0c;但一直没有提供一种方法来阻止派生类继承基类的虚函数。 C11标准引入了final说明符&#xff0c;很好的解决了上面的问题…

微信公众号小程序与服务号和订阅号有什么区别

微信中有小程序&#xff0c;有微信订阅号、微信服务号&#xff0c;这些功能之间有什么区别?下面我们就来详细介绍一下。 一、适合的场景 都是搭建在微信平台&#xff0c;功能、主要用途有些区别&#xff0c;使用不同的场景 微信公众号小程序与服务号和订阅号有什么区别? …

Android国标接入终端实现GB28181实时位置(MobilePosition)上报

技术背景 在实现本文提到的Android平台国标GB28181接入终端的实时位置上报之前&#xff0c;之前已经完成了Android终端GB28181常规功能接入&#xff0c;采集到实时音视频数据&#xff0c;编码PS打包后&#xff0c;按需传到GB28281服务平台&#xff0c;媒体流支持最新GB28181-2…