iOS平台如何实现低延迟RTSP转RTMP推送?

技术背景

好多开发者都知道我们有Windows、Android、Linux平台的RTSP转RTMP推送模块,实际上,iOS平台我们也有,并在2016年就已发布。我们都知道,一个好的RTSP转RTMP推送模块,需要足够稳定的前提下,还要低延迟、灵活、有状态反馈机制、资源占用低,方便进行多路转发。

技术设计

1. 拉流:通过RTSP直播播放SDK的数据回调接口,拿到音视频数据;

2. 转推:通过RTMP直播推送SDK的编码后数据输入接口,把回调上来的数据,传给RTMP直播推送模块,实现RTSP数据流到RTMP服务器的转发;

3. 录像:如果需要录像,借助RTSP直播播放SDK,拉到音视频数据后,直接存储MP4文件即可;

4. 快照:如果需要实时快照,拉流后,解码调用播放端快照接口,生成快照,因为快照涉及到video数据解码,如无必要,可不必开启,不然会额外消耗性能。

5. 拉流预览:如需预览拉流数据,只要调用播放端的播放接口,即可实现拉流数据预览;

6. 数据转AAC后转发:考虑到好多监控设备出来的音频可能是PCMA/PCMU的,如需要更通用的音频格式,可以转AAC后,在通过RTMP推送;

7. 转推RTMP实时静音:只需要在传audio数据的地方,加个判断即可;

8. 拉流速度反馈:通过RTSP播放端的实时码率反馈event,拿到实时带宽占用即可;

9. 整体网络状态反馈:考虑到有些摄像头可能会临时或异常关闭,RTMP服务器亦是,可以通过推拉流的event回调状态,查看那整体网络情况,如此界定:是拉不到流,还是推不到RTMP服务器。

接口实现

再说iOS平台RTSP转RTMP技术实现细节,先说拉流:

调用InitPlayer的时候,我们可以设置audio转AAC在推送:

//  ViewController.m
//  SmartiOSRelayDemo
//
//  Author: daniulive.com
-(bool)InitPlayer
{NSLog(@"InitPlayer++");if(is_inited_player_){NSLog(@"InitPlayer: has inited before..");return true;}_smart_player_sdk = [[SmartPlayerSDK alloc] init];if (_smart_player_sdk ==nil ) {NSLog(@"SmartPlayerSDK init failed..");return false;}if (playback_url_.length == 0) {NSLog(@"_streamUrl with nil..");return false;}if (_smart_player_sdk.delegate == nil){_smart_player_sdk.delegate = self;NSLog(@"SmartPlayerSDK _player.delegate:%@", _smart_player_sdk);}NSInteger initRet = [_smart_player_sdk SmartPlayerInitPlayer];if ( initRet != DANIULIVE_RETURN_OK ){NSLog(@"SmartPlayerSDK call SmartPlayerInitPlayer failed, ret=%ld", (long)initRet);return false;}[_smart_player_sdk SmartPlayerSetPlayURL:playback_url_];//超低延迟模式is_low_latency_mode_ = YES;[_smart_player_sdk SmartPlayerSetLowLatencyMode:(NSInteger)is_low_latency_mode_];buffer_time_ = 0;if(buffer_time_ >= 0){[_smart_player_sdk SmartPlayerSetBuffer:buffer_time_];}is_fast_startup_ = YES;[_smart_player_sdk SmartPlayerSetFastStartup:(NSInteger)is_fast_startup_];NSLog(@"[relayDemo]is_fast_startup_:%d, buffer_time_:%ld", is_fast_startup_, (long)buffer_time_);[_smart_player_sdk SmartPlayerSetRTSPTcpMode:is_rtsp_tcp_mode_];NSInteger rtsp_timeout = 10;    //RTSP超时时间设置[_smart_player_sdk SmartPlayerSetRTSPTimeout:rtsp_timeout];NSInteger is_auto_switch_tcp_udp = 1;       //RTSP TCP/UDP模式自动切换设置[_smart_player_sdk SmartPlayerSetRTSPAutoSwitchTcpUdp:is_auto_switch_tcp_udp];NSInteger image_flag = 1;[_smart_player_sdk SmartPlayerSaveImageFlag:image_flag];//如需查看实时流量信息,可打开以下接口//NSInteger is_report = 1;//NSInteger report_interval = 1;//[_player SmartPlayerSetReportDownloadSpeed:is_report report_interval:report_interval];NSInteger is_rec_trans_code = 1;[_smart_player_sdk SmartPlayerSetRecorderAudioTranscodeAAC:is_rec_trans_code];NSInteger is_pull_trans_code = 1;[_smart_player_sdk SmartPlayerSetPullStreamAudioTranscodeAAC:is_pull_trans_code];is_inited_player_ = YES;NSLog(@"InitPlayer--");return true;
}

对应的设置接口设计如下:

NSInteger is_pull_trans_code = 1;
[_smart_player_sdk SmartPlayerSetPullStreamAudioTranscodeAAC:is_pull_trans_code];

设置音视频数据callback如下,音视频数据回调设置,需要在调用SmartPlayerStartPullStream()接口之前完成:

-(void)StartStreamDataCallback
{//_smart_player_sdk.pullStreamVideoDataBlock = nil; //如不需要回调视频数据if(is_stream_data_callback_started){NSLog(@"StartStreamDataCallback: has inited before..");return;}if(_smart_player_sdk == nil){NSLog(@"StartStreamDataCallback failed, _smart_player_sdk is null..");return;}__weak __typeof(self) weakSelf = self;_smart_player_sdk.pullStreamVideoDataBlock = ^(int video_codec_id, unsigned char *data, int size, int is_key_frame, unsigned long long timestamp, int width, int height, unsigned char *parameter_info, int parameter_info_size, unsigned long long presentation_timestamp){//NSLog(@"[pullStreamVideoDataBlock]videoCodecID:%d, is_key_frame:%d, size:%d, width:%d, height:%d, ts:%lld",//      video_codec_id, is_key_frame, size, width, height, timestamp);[weakSelf OnPostVideoEncodedData:video_codec_id data:data size:size is_key_frame:is_key_frame timestamp:timestamp pts:presentation_timestamp];};//_smart_player_sdk.pullStreamAudioDataBlock = nil; //如不需要回调音频数据_smart_player_sdk.pullStreamAudioDataBlock = ^(int audio_codec_id, unsigned char *data, int size, int is_key_frame, unsigned long long timestamp, int sample_rate, int channel, unsigned char *parameter_info, int parameter_info_size, unsigned long long reserve){//NSLog(@"[pullStreamAudioDataBlock]audioCodecID:%x, is_key_frame:%d, size:%d, parameter_info_size:%d",//      audio_codec_id, is_key_frame, size, parameter_info_size);[weakSelf OnPostAudioEncodedData:audio_codec_id data:data size:size is_key_frame:is_key_frame timestamp:timestamp parameter_info:parameter_info parameter_info_size:parameter_info_size];};//设置拉流视频数据回调bool isEnablePSVideoDataBlock = true;[_smart_player_sdk SmartPlayerSetPullStreamVideoDataBlock:isEnablePSVideoDataBlock];//设置拉流音频数据回调bool isEnablePSAudioDataBlock = true;[_smart_player_sdk SmartPlayerSetPullStreamAudioDataBlock:isEnablePSAudioDataBlock];if([_smart_player_sdk SmartPlayerStartPullStream] != DANIULIVE_RETURN_OK){NSLog(@"Call SmartPlayerStartPullStream failed..");}is_stream_data_callback_started = YES;
}

获取到的RTSP编码后的音视频数据投递到推送模块:

//如需转发video数据
- (void)OnPostVideoEncodedData:(NSInteger)codec_id data:(unsigned char*)data size:(NSInteger)size is_key_frame:(NSInteger)is_key_frame timestamp:(unsigned long long)timestamp pts:(unsigned long long)pts
{if((is_pulling_ || isRTSPPublisherRunning) && _smart_publisher_sdk != nil ){[_smart_publisher_sdk SmartPublisherPostVideoEncodedData:codec_id data:data size:size is_key_frame:is_key_frame timestamp:timestamp pts:pts];}
}//如需转发audio数据
- (void)OnPostAudioEncodedData:(NSInteger)codec_id data:(unsigned char*)data size:(NSInteger)size is_key_frame:(NSInteger)is_key_frame timestamp:(unsigned long long)timestamp parameter_info:(unsigned char*)parameter_info parameter_info_size:(NSInteger)parameter_info_size
{if((is_pulling_ || isRTSPPublisherRunning) && _smart_publisher_sdk != nil ){[_smart_publisher_sdk SmartPublisherPostAudioEncodedData:codec_id data:data size:size is_key_frame:is_key_frame timestamp:timestamp parameter_info:parameter_info parameter_info_size:parameter_info_size];}
}

如果转推RTMP,转推的部分实现如下,需要注意的是,转推的时候,audio_opt和video_opt设置2即可:

//推送端接口封装
-(bool)InitPublisher
{NSLog(@"InitPublisher++");if(is_inited_publisher_){NSLog(@"InitPublisher: has inited before..");return true;}if(_smart_publisher_sdk != nil){NSLog(@"InitPublisher, publisher() has inited before..");return true;}_smart_publisher_sdk = [[SmartPublisherSDK alloc] init];if (_smart_publisher_sdk == nil ){NSLog(@"_smart_publisher_sdk with nil..");return false;}if(_smart_publisher_sdk.delegate == nil){_smart_publisher_sdk.delegate = self;}NSInteger audio_opt = 2;NSInteger video_opt = 2;if([_smart_publisher_sdk SmartPublisherInit:audio_opt video_opt:video_opt] != DANIULIVE_RETURN_OK){NSLog(@"Call SmartPublisherInit failed..");_smart_publisher_sdk = nil;return false;}is_inited_publisher_ = YES;NSLog(@"InitPublisher--");return true;
}-(bool)StartPushRTMP
{NSLog(@"StartPushRTMP++");if ( _smart_publisher_sdk == nil ){NSLog(@"StartPushRTMP, publisher SDK with nil");return false;}NSInteger errorCode = [_smart_publisher_sdk SmartPublisherStartPublisher:relay_url_];NSLog(@"rtmp pusher url: %@", relay_url_);if(errorCode != DANIULIVE_RETURN_OK){NSLog(@"Call SmartPublisherStartPublisher failed..ret:%ld", (long)errorCode);return false;}NSLog(@"StartPushRTMP--");return true;
}-(bool)StopPushRTMP
{NSLog(@"StopPushRTMP++");if ( _smart_publisher_sdk == nil ){NSLog(@"StopPushRTMP, publiher SDK with nil");return false;}[_smart_publisher_sdk SmartPublisherStopPublisher];NSLog(@"StopPushRTMP--");return true;
}

总结

以上是iOS平台RTSP转RTMP推送模块大概设计思路,如果需要录像,可以调用录像接口,也可以实现实时快照,或者转推轻量级RTSP服务,感兴趣的开发者,可以单独跟我交流。

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

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

相关文章

Unity 接口、抽象类、具体类对象的配合使用案例

文章目录 示例1:接口(Interface)示例2:抽象类(Abstract Class)示例3:结合使用接口与抽象类示例4:多接口实现示例5:抽象类与接口结合 在Unity中使用C#编程时,接…

制作耳机壳的UV树脂耳机壳UV胶和塑料材质有什么不同?

制作耳机壳的UV树脂和塑料材质在以下几个方面存在区别: 硬度与耐磨性:UV树脂具有较高的硬度和耐磨性,能够有效保护耳机内部零件,延长耳机使用寿命。而塑料材质相对较软,容易受到磨损。透明度与光泽度:UV树…

【SpringBoot】application配置(5)

type-aliases-package: com.rabbiter.cm.domaintype-aliases-package: 这个配置用于指定mybatis的别名,别名是一个简化的方式,让你在Mapper xml 文件中引用java类型,而不需要使用使用完整的类名。例如,如果你在 com.rabbiter.cm.d…

pymunk初步:设置重力

文章目录 官方示例可视化 官方示例 pymunk是一款2D物理引擎,在游戏开发中十分有用。安装过程无坑 pip install pymunk下面举出官网的一个案例,来简述pymunk的使用流程 import pymunkspace pymunk.Space() space.gravity 0,-981body pymunk.Body() …

邮件群发系统怎么用呢?专业的邮件群发器?

邮件群发系统哪个比较好?邮件营销系统的效果怎么样? 在现代商业活动中,邮件群发系统已成为企业营销的重要工具。那么,邮件群发系统究竟如何使用呢?接下来,蜂邮EDM将为您详细解析邮件群发系统的使用方法。 …

股市反转数据分析

20240206是一个很好的股市反转数据分析的样本。因为之前的1月份2月前3个交易日也就是2月1日,2月2日和2月5日基本都是大跌。数据记录如下: 指数名称指数代码收盘价 [日期] 20231229 [单位] 元收盘价 [日期] 20240205 [单位] 元区间涨跌幅上证指数000001.…

深度学习图像分类相关概念简析+个人举例3(CNN相关补充,附详细举例代码1)

【1】激活函数(Activation Function):在深度学习(CNN)中,激活函数用于引入非线性性质,帮助模型学习复杂的关系。常见的激活函数有ReLU、Sigmoid和Tanh等。 (1)ReLU激活函…

Godot 游戏引擎个人评价和2024年规划(无代码)

文章目录 前言Godot C# .net core 开发简单评价Godot相关网址可行性 Godot(GDScirpt) Vs CocosGodot VS UnityUnity 的裁员Unity的股票Unity的历史遗留问题:Mono和.net core.net core的开发者,微软 个人的独立游戏Steam平台分成说明独立游戏的选题美术风…

C# CAD交互界面-自定义面板集-添加快捷命令(五)

运行环境 vs2022 c# cad2016 调试成功 一、引用 using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Windows; using System; using System.Drawing; using System.Windows.Forms; 二、代码说明 [CommandMethod("Cre…

游戏开发-会飞的小鸟(已完结,附源码)

游戏开发-会飞的小鸟(已完结,附源码) 你将学到的课程链接详细介绍 你将学到的 掌握Java编程的基本技能开发出自己的“会飞的小鸟”游戏对面向对象编程有深刻的理解学会运用常见算法和数据结构解决问题能够独立调试和优化自己的代码 课程链接…

python二维数组初始化的一个极其隐蔽的bug(浅拷贝)

初始化一个三行三列的矩阵 m n 3初始化方式1 a [[0 for i in range(m)] for j in range(n)]初始化方式2 b [] row [0 for i in range(0,m)] for i in range(0,n):b.append(row)分别输出两个初始化的结果 for row in a:print(row) for row in b:print(row)当前的输出为…

标准库 STM32+EC11编码器+I2C ssd1306多级菜单例程

标准库 STM32EC11编码器I2C ssd1306多级菜单例程 📌原创项目来源于:https://github.com/AdamLoong/Embedded_Menu_Simple📍相关功能演示观看:https://space.bilibili.com/74495335 单片机多级菜单v1.2 👉本次采用的是原…

springboot162基于SpringBoot的体育馆管理系统的设计与实现

体育馆管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本体育馆管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕…

算法练习-二叉树的节点个数【完全/普通二叉树】(思路+流程图+代码)

难度参考 难度:中等 分类:二叉树 难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨…

Java on VS Code 2024年1月更新|JDK 21支持!测试覆盖率功能最新体验!

作者:Nick Zhu - Senior Program Manager, Developer Division At Microsoft 排版:Alan Wang 大家好,欢迎来到 Visual Studio Code for Java 2024年的第一期更新!提前祝愿大家春节快乐!在本博客中,我们将有…

开源软件:引领技术创新、商业模式与安全的融合

序 在信息技术日新月异的今天,开源软件以其独特的魅力和影响力,正逐渐成为软件产业的新常态。开源软件的低成本、高度可协作性和透明度等特点,不仅吸引了无数企业和个人用户的青睐,更为软件行业带来了前所未有的繁荣景象。 一、…

复旦微 zynq amp cpu0 唤醒启动cpu1

1 配置多核amp工程,参考上一篇文章 https://blog.csdn.net/yangchenglin927/article/details/136057534 2 在cpu0的main函数中增加唤醒代码 active_cpu1(); /** helloworld.c: simple test application** This application configures UART 16550 to baud rate 96…

Flink SQL Client 安装各类 Connector、Format 组件的方法汇总(持续更新中....)

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,…

数据结构——D/二叉树

🌈个人主页:慢了半拍 🔥 创作专栏:《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》 🏆我的格言:一切只是时间问题。 ​ 1.树概念及结构 1.1树的概念 树是一种非线性的…

10.0 Zookeeper 权限控制 ACL

zookeeper 的 ACL(Access Control List,访问控制表)权限在生产环境是特别重要的,所以本章节特别介绍一下。 ACL 权限可以针对节点设置相关读写等权限,保障数据安全性。 permissions 可以指定不同的权限范围及角色。 …