在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

山和山不相遇,人与人要相逢