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

如何从QCamera/QVideoWidget获取帧字节数组?present方法未调用排查

问题解答

1. present方法未被调用的排查与修复

(1)未将自定义视频表面关联到QCamera

摄像头不会自动把帧投递到你的自定义表面,必须显式将QMyAbstractVideoSurface实例绑定到QCamera:

QCamera *camera = new QCamera(this);
QMyAbstractVideoSurface *mySurface = new QMyAbstractVideoSurface(this);
camera->setVideoSurface(mySurface);
camera->start();

(2)start方法未做格式校验

你的start方法直接调用父类实现,但未先确认当前格式是否被表面支持。若格式不匹配,表面启动失败,present自然不会被触发。修改start方法:

bool QMyAbstractVideoSurface::start(const QVideoSurfaceFormat& format) {
    if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) {
        return QAbstractVideoSurface::start(format);
    }
    return false;
}

(3)QCamera未正确初始化或启动

确保已选择有效摄像头设备,且调用了camera->start()。若摄像头因权限、设备占用等原因启动失败,不会有帧投递。

(4)忽略视频缓冲的HandleType

你的supportedPixelFormats方法忽略了handleType参数,部分平台的摄像头可能使用特定类型缓冲(如纹理缓冲),若表面未对应支持,也会导致无法接收帧。可针对不同类型返回对应格式:

QList<QVideoFrame::PixelFormat> QMyAbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const {
    if (handleType == QAbstractVideoBuffer::NoHandle) {
        return QList<QVideoFrame::PixelFormat>()
            << QVideoFrame::Format_ARGB32
            << QVideoFrame::Format_RGB24
            << QVideoFrame::Format_YUV420P;
            // 按需添加其他格式
    } else {
        return {}; // 不支持的handleType返回空列表
    }
}

2. 与QCameraViewfinder的配合方式

QCameraViewfinder自带内部视频表面用于显示画面,若要同时显示画面并处理帧,有两种常用方案:

方案一:自定义表面处理后转发给Viewfinder

  1. 保存Viewfinder的内部表面
  2. 在自定义present方法中处理帧后,将帧转发给Viewfinder表面

示例代码:

// 自定义表面类添加成员变量保存Viewfinder表面
class QMyAbstractVideoSurface : public QAbstractVideoSurface {
    Q_OBJECT
private:
    QAbstractVideoSurface *m_viewfinderSurface = nullptr;
public:
    void setViewfinderSurface(QAbstractVideoSurface *surface) {
        m_viewfinderSurface = surface;
    }
    // 其他方法...
};

// present方法处理并转发帧
bool QMyAbstractVideoSurface::present(const QVideoFrame& frame) {
    // 处理帧逻辑(如编码、TCP传输)
    QVideoFrame cloneFrame(frame);
    if (cloneFrame.map(QAbstractVideoBuffer::ReadOnly)) {
        QByteArray data((char*)cloneFrame.bits(), cloneFrame.mappedBytes());
        // 这里添加数据处理、TCP发送逻辑
        cloneFrame.unmap();
    }

    // 转发给Viewfinder显示
    if (m_viewfinderSurface) {
        m_viewfinderSurface->present(frame);
    }
    return true;
}

// 初始化关联
QCameraViewfinder *viewfinder = new QCameraViewfinder(this);
QMyAbstractVideoSurface *mySurface = new QMyAbstractVideoSurface(this);
mySurface->setViewfinderSurface(viewfinder->videoSurface());

QCamera *camera = new QCamera(this);
camera->setVideoSurface(mySurface);
camera->start();

方案二:使用视频过滤器拦截帧

通过QAbstractVideoFilter拦截摄像头帧进行处理,Viewfinder负责显示,这种方式更简洁:

// 自定义视频过滤器
class MyVideoFilter : public QAbstractVideoFilter {
    Q_OBJECT
public:
    QVideoFilterRunnable *createFilterRunnable() override {
        return new MyFilterRunnable(this);
    }
};

class MyFilterRunnable : public QVideoFilterRunnable {
public:
    QVideoFrame run(QVideoFrame *input, const QVideoSurfaceFormat &surfaceFormat, RunFlags flags) override {
        // 处理帧逻辑
        if (input->map(QAbstractVideoBuffer::ReadOnly)) {
            QByteArray data((char*)input->bits(), input->mappedBytes());
            // 添加数据处理、TCP发送逻辑
            input->unmap();
        }
        return *input; // 返回原帧给Viewfinder显示
    }
};

// 使用方式
QCamera *camera = new QCamera(this);
QCameraViewfinder *viewfinder = new QCameraViewfinder(this);
MyVideoFilter *filter = new MyVideoFilter(this);

camera->setViewfinder(viewfinder);
camera->setVideoFilter(filter);
camera->start();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:15:33