You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Qt5.2的QML界面中读取树莓派摄像头的MotionJpeg流?

嘿,这个需求我熟!在Qt 5.2 + QML环境下读取并显示树莓派的MotionJPEG流其实没那么复杂,核心思路就是用Qt的网络模块拉取流数据,解码每一帧JPEG图像,再把图像数据传递给QML界面展示。下面是我整理的详细实现步骤和可直接运行的示例代码:

一、先确认树莓派的流可用性

首先得确保你的树莓派已经配置好能输出MotionJPEG流——比如用raspivid命令或者motion软件搭建。通常流地址类似http://<树莓派IP>:8080/stream.mjpg,先在浏览器里打开这个地址测试,能看到实时画面再往下走,避免后续排查问题走弯路。

二、核心实现思路

Qt 5.2里我们可以这么做:

  • 用QNetworkAccessManager发起HTTP请求拉取流数据
  • 用QImageReader解码流里的单帧JPEG图像
  • 通过Q_PROPERTY把解码后的QImage暴露给QML
  • 用QML的Image组件实时刷新显示图像
三、完整示例代码

1. C++后端流处理类(MjpegStreamer)

先写头文件MjpegStreamer.h:

#ifndef MJPEGSTREAMER_H
#define MJPEGSTREAMER_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QImageReader>
#include <QImage>

class MjpegStreamer : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QImage currentImage READ currentImage NOTIFY currentImageChanged)
public:
    explicit MjpegStreamer(QObject *parent = nullptr);

    QImage currentImage() const;

public slots:
    void startStreaming(const QString &url);
    void stopStreaming();

private slots:
    void onReadyRead();
    void onReplyFinished();

private:
    QNetworkAccessManager *m_networkManager;
    QNetworkReply *m_reply;
    QImageReader m_imageReader;
    QImage m_currentImage;
    QByteArray m_buffer;
};

#endif // MJPEGSTREAMER_H

再写实现文件MjpegStreamer.cpp:

#include "MjpegStreamer.h"
#include <QDebug>

MjpegStreamer::MjpegStreamer(QObject *parent) : QObject(parent),
    m_networkManager(new QNetworkAccessManager(this)),
    m_reply(nullptr)
{
    m_imageReader.setAutoDetectImageFormat(false);
    m_imageReader.setFormat("jpg");
}

QImage MjpegStreamer::currentImage() const
{
    return m_currentImage;
}

void MjpegStreamer::startStreaming(const QString &url)
{
    if (m_reply) {
        stopStreaming();
    }

    QNetworkRequest request(QUrl(url));
    m_reply = m_networkManager->get(request);
    connect(m_reply, &QNetworkReply::readyRead, this, &MjpegStreamer::onReadyRead);
    connect(m_reply, &QNetworkReply::finished, this, &MjpegStreamer::onReplyFinished);
}

void MjpegStreamer::stopStreaming()
{
    if (m_reply) {
        m_reply->abort();
        m_reply->deleteLater();
        m_reply = nullptr;
    }
    m_buffer.clear();
}

void MjpegStreamer::onReadyRead()
{
    if (!m_reply) return;

    m_buffer.append(m_reply->readAll());

    // 注意:不同流的边界分隔符可能不同,需要根据实际流的HTTP头调整
    const QByteArray boundary = "\r\n--";
    int startIndex = 0;
    int endIndex = m_buffer.indexOf(boundary, startIndex);

    while (endIndex != -1) {
        QByteArray frameData = m_buffer.mid(startIndex, endIndex - startIndex);

        // 跳过HTTP头,提取纯JPEG数据
        int jpegStart = frameData.indexOf("\r\n\r\n") + 4;
        if (jpegStart != -1 && jpegStart < frameData.size()) {
            QByteArray jpegData = frameData.mid(jpegStart);
            m_imageReader.setDevice(new QBuffer(&jpegData));
            if (m_imageReader.read(&m_currentImage)) {
                emit currentImageChanged();
            } else {
                qDebug() << "解码JPEG帧失败:" << m_imageReader.errorString();
            }
            delete m_imageReader.device();
        }

        startIndex = endIndex + boundary.size();
        endIndex = m_buffer.indexOf(boundary, startIndex);
    }

    // 保留未处理的剩余数据,用于拼接下一帧
    m_buffer = m_buffer.mid(startIndex);
}

void MjpegStreamer::onReplyFinished()
{
    qDebug() << "流已断开或结束";
    stopStreaming();
}

2. 主函数注册QML类型(main.cpp)

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "MjpegStreamer.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    // 把自定义类注册到QML环境
    qmlRegisterType<MjpegStreamer>("com.example.mjpeg", 1, 0, "MjpegStreamer");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

3. QML界面(main.qml)

import QtQuick 2.1
import QtQuick.Window 2.1
import com.example.mjpeg 1.0

Window {
    visible: true
    width: 800
    height: 600
    title: "树莓派摄像头流"

    MjpegStreamer {
        id: streamer
    }

    Image {
        anchors.fill: parent
        source: streamer.currentImage
        fillMode: Image.PreserveAspectFit
    }

    Component.onCompleted: {
        // 替换成你的树莓派流地址
        streamer.startStreaming("http://192.168.1.100:8080/stream.mjpg")
    }
}
四、关键注意事项
  • 边界分隔符调整:不同的MotionJPEG流可能用不同的边界符,你可以查看流的HTTP响应头里的Content-Type字段(格式类似multipart/x-mixed-replace; boundary=xxx),把代码里的boundary改成对应的值。
  • Qt 5.2兼容性:上面的代码全用了Qt 5.2原生支持的API,没有依赖高版本特性,可以直接编译运行。
  • 性能优化:如果流帧率很高,建议把解码逻辑移到子线程里,避免阻塞UI线程——可以用QThread把MjpegStreamer的处理逻辑拆分,注意线程间通信的安全性。
  • 权限问题:如果树莓派的流需要认证,可以在QNetworkRequest里添加用户名和密码:request.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg(username).arg(password).toUtf8()).toBase64());

内容的提问来源于stack exchange,提问作者X6Entrepreneur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:43:36