在qt中开发不考虑跨平台,可以使用vlc-qt-sdk,是vlc-sdk的qt封装好的,更适合开发,但是官方,没有适配arm版本的macos,官方平台只有适配x86-macos的预编译版本与windows版本。如果要通过源码编译,但是源码中也是没有支持arm版本的macos。所以只能使用vlc官方的sdk开发包。
VLC-SDK获取
vlc-sdk下载链接为http://download.videolan.org/pub/videolan/vlc/last/
可以选择对应的版本下载
这里先选择windows版本的vlc
<这里少一张windows的照片>
下载解压打开后,里面所需要的只有plugins目录、sdk目录与对应的dll文件
复制这两个文件夹文件到qt开发目录中(最好新建一个文件夹专门存放windows架构下的vlc sdk)
对应的在macos中,在安装好dmg文件后,
在访达的应用程序中,找到vlc,右击显示包内容
进入Macos,里面可以看到我们需要的文件有include目录、lib目录与plugins目录
将其打包为一个文件夹,放到qt的工程文件目录下,
这里把两个系统对应的库文件分别做成两个文件夹,注意选择相同版本号的sdk
VLC SDK使用
为了实现跨平台作用,我们需要在不同平台下使用对应的sdk进行编译
这里在pt的pro文件中,添加如下
macx: INCLUDEPATH += $$PWD/Thirdparty/vlc_sdk/3.0.21-macosarm64/include
macx: LIBS += -L$$PWD/Thirdparty/vlc_sdk/3.0.21-macosarm64/lib -lvlc
win32: INCLUDEPATH += $$PWD/Thirdparty/vlc_sdk/3.0.21-win64/include
win32: LIBS += -L$$PWD/Thirdparty/vlc_sdk/3.0.21-win64/lib -lvlc
这里我写了一个QWidget子类用于展示视频窗口
在头文件中
#ifndef RTSPVIDEOWINDOW_H
#define RTSPVIDEOWINDOW_H
#include <QWidget>
#include <vlc/vlc.h> //包含该头文件
namespace Ui {
class rtspvideowindow;
}
class rtspvideowindow : public QWidget
{
Q_OBJECT
public:
explicit rtspvideowindow(Communication *comm, QWidget *parent = nullptr);
~rtspvideowindow();
void play();
void stop();
signals:
void playbackError();
void playbackStarted();
private slots:
void on_refresh_btn_clicked();
private:
static void handleVLCEvent(const libvlc_event_t* event, void* userData);
void processVLCEvent(const libvlc_event_t* event);
void setupEvents();
bool get_url();
Ui::rtspvideowindow *ui;
libvlc_instance_t* vlcInstance; //vlc实例
libvlc_media_player_t* mediaPlayer; //vlc播放器实例
QString currentUrl; //当前rtsp直播流地址
bool vlc_ready; //vlc ready
};
#endif // RTSPVIDEOWINDOW_H
构造函数实现
rtspvideowindow::rtspvideowindow(Communication *comm, QWidget *parent)
: QWidget(parent)
, ui(new Ui::rtspvideowindow)
, vlcInstance(nullptr)
, mediaPlayer(nullptr)
, vlc_ready(false)
{
ui->setupUi(this);
setWindowTitle("rtsp");
ui->stackedWidget->setCurrentIndex(0);
/**
* 区别不同架构下对于vlc插件的要求
* 在macos下,需要设置系统环境变量,指出plugins的路径
* 在windows下,需要把dll文件与plugins文件夹,放在qt最终的执行文件同一个目录下
*/
#ifdef Q_OS_MACOS
setenv("VLC_PLUGIN_PATH", "/Users/tonydiao/Documents/qt_project/3_24_2025/Thirdparty/vlc_sdk/3.0.21-macosarm64/plugins", 1);
ui->network_buffer_spinBox->setMinimum(0);
ui->network_buffer_spinBox->setValue(10);
#elif defined(Q_OS_WIN64)
ui->network_buffer_spinBox->setMinimum(50);
ui->network_buffer_spinBox->setValue(100);
#endif
qDebug() << ui->network_buffer_spinBox->value();
QString network_caching_ms = QString::number(ui->network_buffer_spinBox->value());
network_caching_ms.push_front("--network-caching=");
qDebug() << network_caching_ms;
const char* const vlc_args[] = {
network_caching_ms.toUtf8().constData(),
"--clock-synchro=1",
"--drop-late-frames",
"--cr-average=10",
"--clock-jitter=30"
"--no-audio",
};
vlcInstance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args); //初始化vlc实例
if (!vlcInstance) {
QMessageBox::critical(this, "rtsp video", "Cann't Init VLC lib!");
vlc_ready = false;
qWarning() << "Failed to create VLC instance!";
return;
}
vlc_ready = true;
}
play核心函数
void rtspvideowindow::play()
{
if(!vlc_ready){
QMessageBox::warning(this, "rtsp video", "VLC lib Inital fail, Cann't play");
return;
}
stop();
if(!get_url()){
qDebug() << "fail to get url, cann't play";
return;
}
qDebug() << "rtsp url: " << currentUrl;
libvlc_media_t* media = libvlc_media_new_location(vlcInstance, currentUrl.toUtf8().constData()); //输出rtsp地址,初始化media
if (!media) {
qWarning() << "Failed to create media!";
emit playbackError();
return;
}
QString network_caching_ms = QString::number(ui->network_buffer_spinBox->value());
qDebug() << "network cash ms: " << network_caching_ms;
network_caching_ms.push_front(":network-caching="); //给多长的network delay buffer 越大画面延迟越大 但是流程度越高
libvlc_media_add_option(media, network_caching_ms.toUtf8().constData()); //添加vlc配置选项
// libvlc_media_add_option(media, ":file-caching=0"); // 关闭本地文件缓存
libvlc_media_add_option(media, ":rtsp-tcp"); // tcp传输视频流
mediaPlayer = libvlc_media_player_new_from_media(media);
libvlc_media_release(media);
if (!mediaPlayer) {
qWarning() << "Failed to create media player!";
emit playbackError();
return;
}
/**
* 区别不同系统下不同的调用方式,
* win下对应窗口句柄调用方式
* mac下调用nsobject方式
* 这里media_container为一个widget UI对象(我是在desinger中生成的)
**/
#ifdef Q_OS_WIN
libvlc_media_player_set_hwnd(mediaPlayer, (void*)ui->media_container->winId());
#elif defined(Q_OS_MAC)
libvlc_media_player_set_nsobject(mediaPlayer, (void*)ui->media_container->winId());
#endif
setupEvents();
int ret = libvlc_media_player_play(mediaPlayer);
if (ret != 0) {
qWarning() << "Failed to play media!";
emit playbackError();
}
}
VLC运行
/**
* 区别不同架构下对于vlc插件的要求
* 在macos下,需要设置系统环境变量,指出plugins的路径
* 在windows下,需要把dll文件与plugins文件夹,放在qt最终的执行文件同一个目录下
*/
这里的Not Connect是通过一个stackedWidget,把一个label与video_widget叠放起来,根据当前vlc返回的状态实时转换。
总结
调用rtsp视频流,或者其他连接的视频流所用到的核心函数如下
// 1. 初始化vlc实例,如果初始化失败,一般是plugins文件夹没有放对位置
const char* const vlc_args[] = { // vlc自定义配置
。。。。
};
vlcInstance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
if (!vlcInstance) {
QMessageBox::critical(this, "rtsp video", "Cann't Init VLC lib!");
vlc_ready = false;
qWarning() << "Failed to create VLC instance!";
return;
}
// 2. 初始化media实例,输入视频流地址
libvlc_media_t* media = libvlc_media_new_location(vlcInstance, currentUrl.toUtf8().constData());
if (!media) {
qWarning() << "Failed to create media!";
emit playbackError();
return;
}
//这里也可以通过播放本地视频测试
libvlc_media_t* media = libvlc_media_new_path(vlcInstance, videoPath) // videoPath为对应的视频路径
// 3. 配置输出widget
#ifdef Q_OS_WIN
libvlc_media_player_set_hwnd(mediaPlayer, (void*)ui->media_container->winId());
#elif defined(Q_OS_MAC)
libvlc_media_player_set_nsobject(mediaPlayer, (void*)ui->media_container->winId());
#endif
// 4. 启动输出
int ret = libvlc_media_player_play(mediaPlayer);
if (ret != 0) {
qWarning() << "Failed to play media!";
emit playbackError();
}
VLC 网络视频流低延迟配置——RTSP为例
经过不断的测试,vlc中进行低延迟视频流配置如下
const char* const vlc_args[] = {
"--network-caching=10",
"--clock-synchro=1",
"--drop-late-frames",
"--cr-average=10",
"--clock-jitter=30"
"--no-audio",
};
参数--network-caching 为网络延时buffer大小,根据你的视频流与网络情况配置,caching越大,延迟越高,但视频越流畅;反之,延迟越低,但视频会受网络波动卡顿
参数--clock-synchro 为开启时钟同步,如果不开启时钟同步,慢慢的会有一个随时间逐渐变大的延迟(重新连接视频流才会重置)
参数--drop-late-frames为丢弃过时帧,如果某一针受网络波动影响很大,自动丢弃
参数--cr-average为平均时钟引用计数器,当输入源的时钟波动较大,cr-average为一个时间窗口值,将输入的cr-average数量个时间戳进行平均,从而降低时钟波动,但是会增加画面延迟
参数--clock-jitter为时钟抖动补偿值ms,如果输入源的时间戳偏差超过clock-jitter值,就进行音视频同步;实际测下来该值越大,延迟越大
具体表现请参考具体视频流的配置情况,如果视频流的I帧间隔很小、网络环境好,可以适当拉低network-caching, cr-average, clock-jitter