QT实现低延迟的RTSP、RTMP播放器

article/2025/11/6 19:18:42

 

好多开发者在QT环境下实现RTMP或RTSP播放时,首先考虑到的是集成VLC,集成后,却发现VLC在延迟、断网重连、稳定性等各个方面不尽人意,无法满足上线环境需求。本文以调用大牛直播SDK(官方)的Windows平台播放端SDK为例,介绍下如何在QT下实现低延迟的RTMP|RTSP播放器,废话不多说,先上图:

大牛直播SDK有MFC的demo,所以在QT上实现播放轻车熟路,如果需要多窗口播放,也可以参考转发的demo,转发的那个4窗口预览的demo做了二次封装,调用更方便。

窗体布局不再赘述,就是个普通的6窗口布局,不得不说,QT在窗体布局这块,相对MFC真的太方便了。

考虑到大多场景下,开发者有多路播放诉求,针对这种情况,我们对player做个简单的封装:

开始播放:

bool player_wrapper::StartPlay(const std::string& url, bool is_rtsp_tcp_mode, bool is_mute)
{if (is_playing_)return false;if (!OpenPlayerHandle(url, is_rtsp_tcp_mode, is_mute))return false;player_api_->SetBuffer(player_handle_, 100);player_api_->SetMute(player_handle_, is_mute ? 1 : 0);player_api_->SetRtspAutoSwitchTcpUdp(player_handle_, true);player_api_->SetRtspTimeout(player_handle_, 10);player_api_->SetRenderWindow(player_handle_, render_wnd_);player_api_->SetRenderScaleMode(player_handle_, 1);auto ret = player_api_->StartPlay(player_handle_);if (NT_ERC_OK != ret){if (!is_recording_){player_api_->Close(player_handle_);player_handle_ = NULL;}return false;}is_playing_ = true;return true;
}

开始播放封装,调用了OpenPlayerHandle(),检查系统是不是支持特定机型硬解码,通过调用Open()接口,获取播放实例,然后进行播放前的参数设置,比如网络状态event回调、视频宽高回调、设置buffer time、RTSP的TCP-UDP模式,默认播放音量等,具体实现如下:

bool player_wrapper::OpenPlayerHandle(const std::string& url, bool is_rtsp_tcp_mode, bool is_mute)
{if (player_handle_ != NULL)return true;if (url.empty())return false;bool is_support_h264_hardware_decoder_ = NT_ERC_OK == player_api_->IsSupportH264HardwareDecoder();bool is_support_h265_hardware_decoder_ = NT_ERC_OK == player_api_->IsSupportH265HardwareDecoder();NT_HANDLE player_handle = NULL;Q_ASSERT(player_api_ != NULL);if (NT_ERC_OK != player_api_->Open(&player_handle, render_wnd_, 0, NULL)){return false;}Q_ASSERT(player_handle != NULL);player_api_->SetEventCallBack(player_handle, this, &NT_Player_SDKEventHandle);player_api_->SetVideoSizeCallBack(player_handle, this, SP_SDKVideoSizeHandle);player_api_->SetH264HardwareDecoder(player_handle, is_support_h264_hardware_decoder_ ? 1 : 0, 0);player_api_->SetH265HardwareDecoder(player_handle, is_support_h265_hardware_decoder_ ? 1 : 0, 0);player_api_->SetBuffer(player_handle, 0);player_api_->SetFastStartup(player_handle, 1);player_api_->SetRTSPTcpMode(player_handle, is_rtsp_tcp_mode ? 1 : 0);player_api_->SetMute(player_handle, is_mute ? 1 : 0);int audio_volume = 100;player_api_->SetAudioVolume(player_handle, audio_volume);if (NT_ERC_OK != player_api_->SetURL(player_handle, url.c_str())){if (!is_recording_){player_api_->Close(player_handle_);player_handle_ = NULL;}return false;}player_handle_ = player_handle;return true;
}

停止播放:

void player_wrapper::StopPlay()
{if (!is_playing_)return;player_api_->StopPlay(player_handle_);if (!is_recording_){player_api_->Close(player_handle_);player_handle_ = NULL;}is_playing_ = false;
}

Event回调:

extern "C" NT_VOID NT_CALLBACK NT_Player_SDKEventHandle(NT_HANDLE handle, NT_PVOID user_data,NT_UINT32 event_id,NT_INT64  param1,NT_INT64  param2,NT_UINT64 param3,NT_PCSTR  param4,NT_PCSTR  param5,NT_PVOID  param6)
{if (user_data == NULL)return;auto wrapper = reinterpret_cast<player_wrapper*>(user_data);if (wrapper == NULL)return;wrapper->OnPlayerStatus(event_id, param1);
}
void player_wrapper::OnPlayerStatus(NT_UINT32 event_id, NT_INT64 param1)
{if (player_handle_ == NULL)return;if (!is_playing_ && !is_recording_){return;}if (NT_SP_E_EVENT_ID_RTSP_STATUS_CODE == event_id){int status_code = (int)param1;if (401 == status_code){//HandleVerification();}return;}if (NT_SP_E_EVENT_ID_CONNECTING == event_id|| NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id|| NT_SP_E_EVENT_ID_CONNECTED == event_id|| NT_SP_E_EVENT_ID_DISCONNECTED == event_id|| NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){if (NT_SP_E_EVENT_ID_CONNECTING == event_id){OutputDebugStringA("connection status: connecting\r\n");}else if (NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id){OutputDebugStringA("connection status: connection failed\r\n");}else if (NT_SP_E_EVENT_ID_CONNECTED == event_id){OutputDebugStringA("connection status: connected\r\n");}else if (NT_SP_E_EVENT_ID_DISCONNECTED == event_id){OutputDebugStringA("connection status: disconnected\r\n");}else if (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){OutputDebugStringA("connection status: no mediadata received\r\n");}}std::unique_lock<std::recursive_mutex> lock(player_handle_mutex_);player_status_ = event_id;
}

调用封装后的播放接口,记得多实例播放环境下,Init()和UnInit()接口仅需要调用一次,测试URL可自行设置。

void frmMain::startplay()
{for (int i = 0; i < widgets.size(); ++i){if (!plays[i]){plays[i] = std::make_shared<player_wrapper>(&player_api_, (HWND)NULL,(HWND)widgets.at(i)->winId());}auto& play = plays[i];if (!play->IsPlaying()){QString play_url = "rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1&subtype=0";if (!play->StartPlay(play_url.toStdString(), true, false)){QMessageBox::information(NULL, "播放失败!", play_url);}}}
}
void frmMain::stopplay()
{for (int i = 0; i < widgets.size(); ++i){if (!plays[i]){plays[i] = std::make_shared<player_wrapper>(&player_api_, (HWND)NULL,(HWND)widgets.at(i)->winId());}auto& play = plays[i];if (play->IsPlaying()){play->StopPlay();}widgets.at(i)->setText(QString("通道 %1").arg(i + 1));widgets.at(i)->update();}
}

窗体大小发生改变时:


void frmMain::resizeEvent(QResizeEvent* event)
{for (int i = 0; i < widgets.size(); ++i){if (!plays[i]){plays[i] = std::make_shared<player_wrapper>(&player_api_, (HWND)NULL,(HWND)widgets.at(i)->winId());}auto& play = plays[i];if (play->IsPlaying()){play->OnWindowSize(widgets.at(i)->width(), widgets.at(i)->height());}}
}

以上是QT环境下集成个低延迟的RTMP、RTSP播放的基本流程,感兴趣的开发者可酌情参考。相对MFC,QT环境下在实现更酷炫和实用的逻辑显然体验更好一些。


http://chatgpt.dhexx.cn/article/0h6x1NVa.shtml

相关文章

【技术分享】如何实现功能完备性能优异的RTMP、RTSP播放器?

技术背景 这几年&#xff0c;我们对接了太多有RTSP或RTMP直播播放器诉求的开发者&#xff0c;他们当中除了寻求完整的解决方案的&#xff0c;还有些是技术探讨&#xff0c;希望能借鉴我们播放端的开发思路或功能特性&#xff0c;完善自己的产品。 忙里偷闲&#xff0c;今天我…

高稳定、低延时、高并发RTMP播放器流媒体音视频播放器EasyPlayer-RTMP-iOS器如何将核心代码打包成静态库

背景分析 RTMP是Real Time Messaging Protocol&#xff08;实时消息传输协议&#xff09;的首字母缩写。该协议基于TCP&#xff0c;是一个协议族&#xff0c;包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议&#xff0c;主要用…

Windows平台RTMP、RTSP播放器录像模块精细化控制

技术背景 上篇文章&#xff0c;我们介绍了Unity平台RTMP、RTSP播放器录像功能&#xff0c;这里&#xff0c;我们详细的介绍下&#xff0c;做个RTSP或RTMP拉流端录像模块有哪些需要考虑的技术点&#xff1f; 在我们常规的考量&#xff0c;RTMP或RTSP流录制&#xff0c;无非就是…

RTMP视频播放器

感谢作者tcking、Bilibili&#xff0c;本项目借鉴了GiraffePlayer项目&#xff0c;项目一开始的灵感来源于GiraffePlayer项目&#xff0c;后期做纯粹做了视频播放器的界面的定制&#xff0c;基于ijkplayer项目进行的播放器界面UI封装。 简介 当前项目是基于ijkplayer项目进行…

从零开发一款Android RTMP播放器

1. 背景介绍 15年移动端直播应用火起来的时候&#xff0c;主要的直播协议是RTMP&#xff0c;多媒体服务以Adobe的AMS、wowza、Red5、crtmpserver、nginx rtmp module等&#xff0c;后面过长RTMP服务SRS开始流行。Android端播放器主要以开始以EXOPlayer播放HLS&#xff0c;但是…

rtsp+rtmp多路网页播放

一、前言 之前博主有写过 一篇博文&#xff0c;讲的是使用videojs在谷歌浏览器网页上播放rtmp流媒体&#xff0c;具体可参考我之前的博客&#xff1a;videojshlsrtmp网页播放 - 蛋片鸡 - 博客园 最近又开始研究了一下网页播放流媒体&#xff0c;在这里我主要补充一些播放rtmp、…

低延时极简RTMP播放器

RtmpPlaySdk简介 近期将项目上RTMP播放相关功能进行打包整理&#xff0c;实现了一款低延时的极简接口RTMP播放器&#xff08;Windows版和Android版&#xff09;。市面上的RTMP播放器较多&#xff0c;有开源的ijkplayer及其衍生品&#xff0c;也有收费的功能繁多的播放器&#…

rtmp 点播系统之播放器篇

rtmp (Real Time Media Protocal) ,是实时流媒体协议,由Adobe公司提出,属于半开放的协议。此协议基于flash平台的音视频点播协议。 音视的点播系统分为两部分,分别为客户端和服务端。在本文中我会先讲解如何实现一个简单的rtmp播放器。至于rtmp服务器的实现,会在下文中分析…

pgsql 使用技巧

1.CASCADE 级联删除&#xff0c;如果表或模式或数据库有序列、分区相关 依赖时&#xff0c;需要修改表或模式或数据库&#xff0c;则使用它 DROP SCHEMA viid_facestatic CASCADE 2.pgsql隐藏字段ctid&#xff0c;一般用于去重 3. pg 表自连接使用场景 3.1 有一张卡口表 求…

pgsql

这里写目录标题 pgpool安装pgsql流复制备份与恢复客户端验证服务器设置及操作服务器配置监控数据活动 PostgreSQL是以加州大学伯克利分校计算机系开发的 POSTGRES, Version 4.2为基础的对象关系型数据库管理系统(ORDBMS)。 PostgreSQL是最初伯克利代码的一个开放源码的继承者。…

pgsql基本操作

pgsql基本操作 1. 修改postgresql.conf postgresql.conf存放位置在/etc/postgresql/9.x/main下&#xff0c;这里的x取决于你安装PostgreSQL的版本号&#xff0c;编辑或添加下面一行&#xff0c;使PostgreSQL可以接受来自任意IP的连接请求。 listen_addresses *2. 修改pg_hb…

pgsql常用sql和函数

常用pgsql -- 列出所有schema select * from information_schema.schemata; -- Schema下所有表 select * from pg_tables where schemaname query_db and tablename in(port,device,res_carry_business,hardware,shelf,device_hardware_relation); -- Schema下所有索引 select…

PGSQL大小写敏感总结

PGSQL大小写敏感总结 由于PGSQL使用会出现大小写敏感的问题&#xff0c;所以在设置字段名字的时候&#xff0c;如果字段名需要大写需要加上""号来表示&#xff0c;该字段需要大写 公司业务需要我使用PGSQL创建自增主键&#xff0c;我在使用 nextval() 绑定创建的序列…

PGSQL常用操作

0. 启动pgsl数据库 pg_ctl -D /xx/pgdata start 回到顶部 1. 查看pgsl版本 pg_ctl --version 回到顶部 1. 命令行登录数据库 1 psql -U username -d dbname -h hostip -p port 回到顶部 2. 列出所有数据库 \l 回到顶部 3. 切换数据库 1 \c dbname 回到顶部 …

PL/pgSQL

1.简介 L/pgSQL是一种用于PostgreSQL数据库系统的可载入的过程语言。 可以被用来创建函数和触发器过程对SQL语言增加控制结构可以执行复杂计算继承所有用户定义类型、函数和操作符可以被定义为受服务器信任便于使用 [1]使用PL/pgSQL的优点 SQL 是一种查询语言&#xff0c;可…

Windows 10 安装配置连接PostgreSQL教程

Windows 10 安装配置连接PostgreSQL教程 1.1 PostgreSQL 下载1.2 配置环境变量1.3 PostgreSQL 初始化1.4 创建postgres用户1.5 启动postgresql1.6 客户端连接测试1.6.1 SQL Shell (psql)命令行连接测试1.6.2 客户端 pdAdmin 4 连接测试1.6.3 客户端Navicat Permium 连接测试 1.…

Spring原理-IOC控制反转

spring相关文章 Spring原理-IOC控制反转 Spring框架七大核心模块 Spring Beans原理–bean生命周期 一、Spring概述 1、 定义 Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发复杂性。它是一个分层的JavaSE/Java…

依赖倒置和控制反转

依赖倒置 定义 依赖反转原则&#xff08;Dependency inversion principle&#xff0c;DIP&#xff09;是指一种特定的解耦形式&#xff0c;使得高层次的类不依赖于低层次的类的实现细节&#xff0c;依赖关系被颠倒&#xff08;反转&#xff09;&#xff0c;从而使得低层次类依…

Inversion of Control (IOC)控制反转 有什么好处

要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想&#xff1a;依赖倒置原则&#xff08;Dependency Inversion Principle &#xff09;。 什么是依赖倒置原则&#xff1f;假设我们设计一辆汽车&#xff1a;先设计轮子&#xff0c;然后根据…

Spring学习:IOC控制反转

一、Spring概述&#xff1a; Spring是一个开源框架&#xff0c;其存在的根本使命就是简化JAVA开发。为了降低JAVA开发的复杂性&#xff0c;Spring采取了以下四种关键策略&#xff1a; 基于POJO的最轻量级和最小侵入性编程&#xff1b;通过依赖注入和面向接口实现松耦合&#x…