qt+gstreamer实现播放功能

一 概述

本文章实现了在qt环境下,使用gstreamer接口实现了播放功能。包括本地文件播放和网络流播放。该文章展示了两种方式实现播放功能,1-命令行方式 2-代码方式。

二 命令行方式

1.播放本地文件

使用 uridecodebin插件 播放

gst-launch-1.0.exe uridecodebin uri=file:///D:/video/11.264 ! videoconvert ! video/x-raw,format=RGB ! autovideosink

使用 filesrc插件 播放

gst-launch-1.0.exe filesrc location=D:/video/dzq.mp4 ! qtdemux name=demuxer ! decodebin ! queue ! videoconvert ! video/x-raw,format=RGB ! autovideosink

2.播放网络流

使用 uridecodebin插件 播放

gst-launch-1.0.exe uridecodebin uri="rtsp://admin:Admin12345@192.168.250.42:1554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1" ! videoconvert ! video/x-raw,format=RGB ! autovideosink

三 代码方式

1.播放本地文件

cpp 复制代码
bool GstPipelineManager::playLocalFile(const QString filename)
{
    stop();
    QString sourceDesc = QString("filesrc location=%1").arg(filename);
#if 0
    sourceDesc = QString("uridecodebin uri=file:///%1").arg(filename);
#endif
    QString suffix = ".264";
    if(filename.mid(filename.size()-4) == ".264"){
        suffix = ".264";
    }else if(filename.mid(filename.size()-4) == ".265"){
        suffix = ".265";
    }else if(filename.mid(filename.size()-4) == ".mp4"){
        suffix = ".mp4";
    }
    return buildPlayPipelineEx(sourceDesc,suffix);
}
cpp 复制代码
bool GstPipelineManager::buildPlayPipelineEx(const QString &sourceDesc, QString suffix)
{
    GError *error = nullptr;

    // 构建播放管道: Source -> Convert -> Appsink (用于Qt绘制)
    QString pipelineStr = QString("%1 ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true sync=false")
                              .arg(sourceDesc);
#if 1
    if(suffix == ".264"){
        pipelineStr = QString("%1 ! h264parse ! avdec_h264 ! queue ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true  max-buffers=1 drop=true")
                .arg(sourceDesc);
    }else if(suffix == ".265"){
        pipelineStr = QString("%1 ! h265parse ! avdec_h265 ! queue ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true  max-buffers=1 drop=true")
                .arg(sourceDesc);
    }else if(suffix == ".mp4"){
        //emit-signals=true sync=false max-buffers=1 drop=true
        pipelineStr = QString("%1 ! qtdemux name=demuxer "
                              "! decodebin ! queue ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true  max-buffers=1 drop=true "
                              )
                .arg(sourceDesc);
    }else {
        emit errorOccurred("不支持的文件格式");
        return false;
    }
#else
    pipelineStr = QString(
        "%1 ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink  max-buffers=1 drop=true"
    ).arg(sourceDesc);
#endif
    qDebug() << "pipelineStr:" << pipelineStr;
    pipeline = gst_parse_launch(pipelineStr.toStdString().data(), &error);
    if (error) {
        emit errorOccurred(QString("Pipeline Error: %1").arg(error->message));
        g_error_free(error);
        return false;
    }

    videoSink = gst_bin_get_by_name(GST_BIN(pipeline), "videosink");
    if (!videoSink) {
        emit errorOccurred("Failed to get videosink");
        return false;
    }

    // 设置 appsink 回调以获取帧数据
    GstAppSinkCallbacks callbacks = {nullptr, nullptr, onNewSampleFromAppsinkEx};
    gst_app_sink_set_callbacks(GST_APP_SINK(videoSink), &callbacks, this, nullptr);

    // 启动管道
    GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        emit errorOccurred("Failed to start pipeline");
        GstBus *bus = gst_element_get_bus(pipeline);
        GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR);
        if (msg != NULL) {
            GError *err = NULL;
            gchar *debug_info = NULL;
            gst_message_parse_error(msg, &err, &debug_info);
            g_printerr("Error from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
            g_printerr("Debugging info: %s\n", debug_info ? debug_info : "none");
        }
        gst_object_unref(videoSink);
        gst_object_unref(pipeline);
        pipeline = nullptr;
        videoSink = nullptr;
        return false;
    }

    // 必须等启动完成才能出画面
//    gst_element_get_state(pipeline, nullptr, nullptr, GST_CLOCK_TIME_NONE);
    emit stateChanged("Playing");
    return true;
}

2.播放网络流

cpp 复制代码
bool GstPipelineManager::playNetworkStream(const QString &url) {
    stop();
    // 使用 uridecodebin 自动处理 RTSP/RTMP 等协议
    QString sourceDesc = QString("uridecodebin uri=%1").arg(url);
    return buildPlayPipeline(sourceDesc, false);
}
cpp 复制代码
bool GstPipelineManager::buildPlayPipeline(const QString &sourceDesc, bool isCamera) {
    GError *error = nullptr;
    
    // 构建播放管道: Source -> Convert -> Appsink (用于Qt绘制)
    QString pipelineStr = QString("%1 ! videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true sync=false")
                              .arg(sourceDesc);

    qDebug() << "pipelineStr:" << pipelineStr;
    pipeline = gst_parse_launch(pipelineStr.toUtf8().constData(), &error);
    if (error) {
        emit errorOccurred(QString("Pipeline Error: %1").arg(error->message));
        g_error_free(error);
        return false;
    }

    videoSink = gst_bin_get_by_name(GST_BIN(pipeline), "videosink");
    if (!videoSink) {
        emit errorOccurred("Failed to get videosink");
        return false;
    }


    // 设置 appsink 回调以获取帧数据
    GstAppSinkCallbacks callbacks = {nullptr, nullptr, onNewSampleFromAppsink};
    gst_app_sink_set_callbacks(GST_APP_SINK(videoSink), &callbacks, this, nullptr);

    gst_element_set_state(pipeline, GST_STATE_PLAYING);
    emit stateChanged("Playing");
    return true;
}
cpp 复制代码
GstFlowReturn GstPipelineManager::onNewSampleFromAppsink(GstAppSink *appsink, gpointer user_data) {
    GstPipelineManager *self = static_cast<GstPipelineManager*>(user_data);
    if (!self->frameCallback) return GST_FLOW_ERROR;

    GstSample *sample = gst_app_sink_pull_sample(appsink);
    if (!sample) return GST_FLOW_ERROR;

    GstBuffer *buffer = gst_sample_get_buffer(sample);
    GstCaps *caps = gst_sample_get_caps(sample);
    
    if (buffer && caps) {
        GstMapInfo map;
        if (gst_buffer_map(buffer, &map, GST_MAP_READ)) {
            GstStructure *s = gst_caps_get_structure(caps, 0);
            int width, height;
            if (gst_structure_get_int(s, "width", &width) && gst_structure_get_int(s, "height", &height)) {
                self->frameCallback(map.data, width, height, self->callbackUserData);
            }
            gst_buffer_unmap(buffer, &map);
        }
    }
    gst_sample_unref(sample);

    return GST_FLOW_OK;
}

四 效果图

播放网络流

播放本地文件

相关推荐
云小逸13 小时前
【 VS2013 集成 Qt5.7.1 踩坑记录:moc/uic/rcc 报“系统找不到指定的路径”怎么解决?】
开发语言·windows·qt
努力努力再努力wz14 小时前
【Qt入门系列】深入理解信号与槽:从事件响应到自定义信号机制
c语言·开发语言·数据结构·数据库·c++·qt·mysql
mengzhi啊15 小时前
串口数据监控软件开发总结
qt
小短腿的代码世界15 小时前
Qwt实时FFT频谱分析深度解析:从信号采集到可视化渲染的完整架构设计
前端·qt·架构·交互
Hua-Jay15 小时前
OpenCV联合C++/Qt 学习笔记(二十)----Harri角点检测、Shi-Tomas角点检测及亚像素级别角点位置优化
c++·笔记·qt·opencv·学习·计算机视觉
十五年专注C++开发15 小时前
QFluentKit: 一个基于 Qt Widgets 的 Fluent Design 风格 UI 组件库
开发语言·c++·qt·ui·qfluentkit
Hua-Jay15 小时前
OpenCV联合C++/Qt 学习笔记(十九)----图像分割
c++·笔记·qt·opencv·学习
小短腿的代码世界16 小时前
Qt位置服务深度解析:从GPS定位到地理围栏的完整架构设计
开发语言·qt
史迪仔011216 小时前
[QML] Qt5/6图像色彩空间处理
开发语言·前端·c++·qt
小短腿的代码世界16 小时前
传感器暗战:Qt Sensors如何让桌面应用“感知“物理世界?
开发语言·qt