Qt6 Android应用后台恢复后QVideoFrame::map崩溃问题求助
Qt6 Android应用后台恢复后调用
QVideoFrame::map崩溃问题解决 问题背景
基于Qt6.5.0开发的Quick Android应用,使用Camera组件采集视频并通过全屏VideoOutput显示。点击"Take frame"按钮时,后端backend的setVideoSink方法会从QVideoSink获取QVideoFrame副本,调用map(只读模式)处理数据后unmap。应用前台运行正常,但进入后台再恢复后,调用map方法时崩溃。
崩溃日志
E libEGL : call to OpenGL ES API with no current context (logged once per thread) W libappCameraProcessor_arm64-v8a.so: Failed to compile shader: W libappCameraProcessor_arm64-v8a.so: Source was: W libappCameraProcessor_arm64-v8a.so: #version 100 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: W libappCameraProcessor_arm64-v8a.so: struct buf W libappCameraProcessor_arm64-v8a.so: { W libappCameraProcessor_arm64-v8a.so: mat4 matrix; W libappCameraProcessor_arm64-v8a.so: mat4 colorMatrix; W libappCameraProcessor_arm64-v8a.so: float opacity; W libappCameraProcessor_arm64-v8a.so: float width; W libappCameraProcessor_arm64-v8a.so: float masteringWhite; W libappCameraProcessor_arm64-v8a.so: float maxLum; W libappCameraProcessor_arm64-v8a.so: }; 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: W libappCameraProcessor_arm64-v8a.so: uniform buf ubuf; 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: W libappCameraProcessor_arm64-v8a.so: varying vec2 texCoord; W libappCameraProcessor_arm64-v8a.so: attribute vec2 vertexTexCoord; W libappCameraProcessor_arm64-v8a.so: attribute vec4 vertexPosition; 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: W libappCameraProcessor_arm64-v8a.so: void main() W libappCameraProcessor_arm64-v8a.so: { W libappCameraProcessor_arm64-v8a.so: texCoord = vertexTexCoord; W libappCameraProcessor_arm64-v8a.so: gl_Position = ubuf.matrix * vertexPosition; W libappCameraProcessor_arm64-v8a.so: } 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: 06-16 13:32:36.205 18373 18394 W libappCameraProcessor_arm64-v8a.so: W CameraProcesso: 0xebadde09 skipped times: 0 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x78 in tid 18394 (qtMainLoopThrea), pid 18373 (CameraProcessor)
原代码
Main.qml
import QtQuick.Window import QtMultimedia import QtQuick.Controls import Backend Window { width: 1920 height: 1024 visible: true Camera { id: userCamera focusMode: Camera.FocusModeAuto } CaptureSession { id: viewFinderCaptureSession videoOutput: viewfinder camera: userCamera } VideoOutput { id: viewfinder anchors.fill: parent } Backend{ id: backend objectName: "backend" } Component.onCompleted: userCamera.start() Button { id: processButton onClicked: backend.videSink = viewfinder.videoSink width: 200 height: 50 text: "Take frame" x: 20 y: 20 } }
backend.h
#include <QObject> #include <QVideoSink> #include <QVideoFrame> #include <QVariant> class backend : public QObject { Q_OBJECT public: backend(QObject* _parent = nullptr); Q_INVOKABLE void setVideoSink(QVideoSink* _videoSink); void disconnectSink(); };
backend.cpp
#include "backend.h" #include <QDebug> backend::backend(QObject* _parent): QObject(_parent) { // noting to do... } void backend::setVideoSink(QVideoSink* _videoSink) { auto myFrame = _videoSink->videoFrame(); qDebug() << "map frame; is valid: " << myFrame.isValid() << "isMapped: " << myFrame.isMapped(); myFrame.map(QVideoFrame::MapMode::ReadOnly); // TODO here comes some work ... myFrame.unmap(); }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "backend.h" int main(int argc, char *argv[]) { qmlRegisterType< backend >( "Backend", 1, 0, "Backend" ); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.loadFromModule("CameraProcessor", "Main"); QObject::connect(&app, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState _state){ qDebug() << "State changed to: " << _state; }); return app.exec(); }
问题原因
- OpenGL上下文丢失:Android应用进入后台时,系统会回收OpenGL ES上下文;恢复后,
QVideoFrame可能仍绑定已失效的旧上下文,调用map时触发无上下文的OpenGL调用,导致崩溃。 - Camera资源未正确重启:后台状态下Camera会被系统暂停,恢复后未重新初始化,导致获取的
QVideoFrame无效或关联资源已释放。 - 未校验帧有效性:代码直接获取帧并执行
map,未处理帧无效、上下文缺失的异常场景。
修复思路
- 监听应用状态,后台时停止Camera并解除CaptureSession关联,恢复时重新绑定并启动Camera,确保资源重置。
- 改用
QVideoSink::videoFrameChanged信号获取最新帧,而非直接读取当前帧,避免拿到过期或无效帧。 - 操作帧前校验帧有效性,针对GPU内存帧检查当前OpenGL上下文是否存在。
修复示例代码
1. 修改Main.qml,添加应用状态监听
import QtQuick.Window import QtMultimedia import QtQuick.Controls import QtQuick 2.15 import Backend Window { id: mainWindow width: 1920 height: 1024 visible: true property var app: Qt.application Camera { id: userCamera focusMode: Camera.FocusModeAuto } CaptureSession { id: viewFinderCaptureSession videoOutput: viewfinder camera: userCamera } VideoOutput { id: viewfinder anchors.fill: parent } Backend{ id: backend objectName: "backend" } Component.onCompleted: userCamera.start() Button { id: processButton onClicked: backend.videoSink = viewfinder.videoSink width: 200 height: 50 text: "Take frame" x: 20 y: 20 } // 监听应用状态,处理Camera启停 Connections { target: app function onApplicationStateChanged(state) { if (state === Qt.ApplicationSuspended) { userCamera.stop() viewFinderCaptureSession.camera = null } else if (state === Qt.ApplicationActive) { viewFinderCaptureSession.camera = userCamera userCamera.start() } } } }
2. 修改backend.h,添加属性和信号槽
#include <QObject> #include <QVideoSink> #include <QVideoFrame> class backend : public QObject { Q_OBJECT Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged) public: explicit backend(QObject* parent = nullptr); QVideoSink* videoSink() const; void setVideoSink(QVideoSink* sink); signals: void videoSinkChanged(); private slots: void onVideoFrameChanged(const QVideoFrame& frame); private: QVideoSink* m_videoSink = nullptr; };
3. 修改backend.cpp,添加帧校验和上下文检查
#include "backend.h" #include <QDebug> #include <QOpenGLContext> backend::backend(QObject* parent): QObject(parent) {} QVideoSink* backend::videoSink() const { return m_videoSink; } void backend::setVideoSink(QVideoSink* sink) { if (m_videoSink == sink) return; // 断开旧的信号连接 if (m_videoSink) { disconnect(m_videoSink, &QVideoSink::videoFrameChanged, this, &backend::onVideoFrameChanged); } m_videoSink = sink; if (m_videoSink) { // 连接新的帧变化信号 connect(m_videoSink, &QVideoSink::videoFrameChanged, this, &backend::onVideoFrameChanged); } emit videoSinkChanged(); } void backend::onVideoFrameChanged(const QVideoFrame& frame) { // 校验帧有效性 if (!frame.isValid()) { qDebug() << "Received invalid video frame"; return; } // 针对OpenGL纹理帧,检查上下文是否存在 if (frame.handleType() == QVideoFrame::OpenGLTextureHandle && !QOpenGLContext::currentContext()) { qDebug() << "No active OpenGL context, skip frame mapping"; return; } QVideoFrame frameCopy = frame; if (frameCopy.map(QVideoFrame::ReadOnly)) { // 处理帧数据的逻辑写在这里 qDebug() << "Frame mapped successfully: width=" << frameCopy.width() << ", height=" << frameCopy.height(); frameCopy.unmap(); } else { qDebug() << "Failed to map video frame"; } }
4. main.cpp无需修改,保持原状态即可
内容的提问来源于stack exchange,提问作者mtb
相关产品推荐
相关产品推荐

