news 2026/10/23 4:57:15

Qt Onboard开发套件实战(3): 多媒体与摄像头集成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt Onboard开发套件实战(3): 多媒体与摄像头集成

前言

边缘AI应用往往离不开多媒体能力,像摄像头采集图像、麦克风采集语音、扬声器播放TTS结果。在FactoryPulse方案中,Qt Onboard针对高通跃龙IQ-9075做了多项多媒体定制优化:

  • 音频:用ALSA替换PulseAudio,支持USB音频驱动,降低资源占用
  • 视频:增加GStreamer插件,支持QML中的VideoOutput集成
  • 摄像头:新增定制化USB摄像头驱动,满足Edge Impulse平台要求

本文带你实战玩转Qt Onboard的多媒体子系统,从视频播放、音频播放到摄像头采集,全部走一遍。

1、多媒体子系统架构

先看整体架构图,理清各层关系:

架构图说明:

  • Qt应用层:Qt Multimedia模块(MediaPlayer、VideoOutput、Camera)
  • 多媒体框架层:GStreamer 1.x(视频/流媒体)+ ALSA(音频,替换PulseAudio)
  • 内核驱动与硬件层:V4L2子系统、USB摄像头驱动、CSI接口、VPU硬件编解码器 + ALSA SoC驱动、USB音频、I2S/PCM接口、Codec芯片

关键定制点:FactoryPulse镜像中用ALSA直接替换了PulseAudio,原因有两个:一是嵌入式场景下资源更紧张,ALSA比PulseAudio更轻量;二是USB音频设备在ALSA下延迟更低、更稳定。

2、多媒体处理流程图

下面这张Mermaid图展示了视频采集、播放和音频处理的完整管线:

媒体播放 Media Playback

音频管线 Audio Pipeline

视频管线 Video Pipeline

USB/CSI 摄像头

V4L2 子系统

GStreamer v4l2src

视频处理
ivapostproc

硬件解码 VPU

Qt VideoOutput

HDMI 显示输出

USB 麦克风

ALSA 驱动

GStreamer alsasrc

音频处理

Qt AudioInput

应用层处理

Qt AudioOutput

GStreamer alsasink

ALSA 驱动

USB 扬声器/耳机

Qt MediaPlayer

GStreamer playbin

demux 解复用

视频解码 VPU

音频解码

3、实战:视频播放(GStreamer + Qt Multimedia)

3.1 环境确认

首先登录到IQ-9075开发板,确认GStreamer环境:

sshroot@<设备IP># 检查GStreamer版本gst-launch-1.0--version# 输出: gst-launch-1.0 version 1.22.x# 检查可用的插件gst-inspect-1.0|grep-E"v4l2|alsa|vpudec|qtwidget"# 检查Qt Multimedia是否可用ls/usr/lib/qt6/plugins/multimedia/

💡 你应该能看到gstreamermediaplugin.so,这是Qt Multimedia的GStreamer后端插件。

3.2 命令行测试GStreamer

在写代码之前,先用命令行测试GStreamer管线是否正常:

# 测试视频测试源 + 显示gst-launch-1.0 videotestsrc!videoconvert!qtv4l2sink# 测试本地视频文件播放(如果有的话)gst-launch-1.0 filesrclocation=/usr/share/sample.mp4!\qtdemux!h264parse!vpudec!videoconvert!qtv4l2sink# 测试音频播放gst-launch-1.0 audiotestsrc!audioconvert!alsasink

如果能看到测试视频画面或听到测试音频,说明GStreamer环境正常。

3.3 Qt Quick视频播放器实战

创建一个Qt Quick项目,实现一个简单的视频播放器。

步骤1:创建项目
文件 → 新建 → Qt Quick Application (Empty) 项目名称:VideoPlayerDemo
步骤2:修改CMakeLists.txt

添加 Multimedia 模块依赖:

cmake_minimum_required(VERSION 3.16) project(VideoPlayerDemo VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 6.11 REQUIRED COMPONENTS Quick Multimedia) qt_standard_project_setup(REQUIRES 6.5) qt_add_executable(appVideoPlayerDemo main.cpp ) qt_add_qml_module(appVideoPlayerDemo URI VideoPlayerDemo VERSION 1.0 QML_FILES Main.qml ) target_link_libraries(appVideoPlayerDemo PRIVATE Qt6::Quick Qt6::Multimedia ) include(GNUInstallDirs) install(TARGETS appVideoPlayerDemo BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
步骤3:编写Main.qml
import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Layouts import QtMultimedia Window { width: 800 height: 500 visible: true title: "Qt Onboard 视频播放器" color: "#0F172A" // 媒体播放器 MediaPlayer { id: mediaPlayer source: "file:///usr/share/sample.mp4" videoOutput: videoOutput onError: { console.log("播放错误:", errorString) } } ColumnLayout { anchors.fill: parent spacing: 0 // 视频输出区域 VideoOutput { id: videoOutput Layout.fillWidth: true Layout.fillHeight: true fillMode: VideoOutput.PreserveAspectFit } // 控制栏 Rectangle { Layout.fillWidth: true Layout.preferredHeight: 60 color: "#1E293B" border.color: "#334155" RowLayout { anchors.fill: parent anchors.leftMargin: 15 anchors.rightMargin: 15 spacing: 12 // 播放/暂停按钮 Button { text: mediaPlayer.playbackState === MediaPlayer.PlayingState ? "⏸ 暂停" : "▶ 播放" Layout.preferredWidth: 100 Layout.preferredHeight: 36 onClicked: { if (mediaPlayer.playbackState === MediaPlayer.PlayingState) mediaPlayer.pause() else mediaPlayer.play() } } // 进度条 Slider { id: progressBar Layout.fillWidth: true from: 0 to: mediaPlayer.duration value: mediaPlayer.position onMoved: mediaPlayer.seek(value) } // 时间显示 Label { text: formatTime(mediaPlayer.position) + " / " + formatTime(mediaPlayer.duration) color: "#94A3B8" Layout.preferredWidth: 110 font.pixelSize: 12 } // 音量控制 Slider { Layout.preferredWidth: 100 from: 0 to: 100 value: mediaPlayer.volume * 100 onMoved: mediaPlayer.volume = value / 100 } Label { text: "🔊" font.pixelSize: 16 } } } } function formatTime(ms) { let totalSec = Math.floor(ms / 1000); let min = Math.floor(totalSec / 60); let sec = totalSec % 60; return min.toString().padStart(2, '0') + ":" + sec.toString().padStart(2, '0'); } Component.onCompleted: { console.log("视频播放器就绪"); // 自动播放 mediaPlayer.play(); } }
步骤4:准备测试视频

将一个H.264编码的MP4视频文件复制到开发板:

# 在电脑上scptest_video.mp4 root@192.168.1.100:/usr/share/sample.mp4

⚠️注意:IQ-9075支持硬件解码H.264/H.265等格式,建议使用硬件支持的编码格式以获得最佳性能。

步骤5:编译部署运行

选择QtOnboard Kit,点击运行。你应该能在HDMI显示器上看到视频播放界面,支持播放/暂停、进度条拖拽、音量调节。

🔍观察:在应用输出面板查看播放状态,如果GStreamer管线正常建立,你会看到类似Pipeline is PREROLLED的日志。

4、实战:音频播放与录制(ALSA)

4.1 为什么用ALSA而不是PulseAudio?

在FactoryPulse定制镜像中,Qt Multimedia的音频后端从PulseAudio切换到了ALSA,原因如下:

特性PulseAudioALSA (直接使用)
资源占用较高(需要守护进程)低(直接操作硬件)
延迟较高更低
混音能力强(多应用同时播放)有限(硬件混音)
适用场景桌面Linux嵌入式/工业场景

对于嵌入式边缘AI应用,低延迟、低资源占用比多应用混音更重要,所以选择ALSA更合适。

4.2 命令行测试ALSA音频

# 查看音频设备aplay-l# 输出类似:# card 0: audiocodec [audiocodec], device 0: ...# card 1: USB [USB Audio Device], device 0: USB Audio ...# 查看录音设备arecord-l# 播放测试音频aplay /usr/share/sounds/alsa/Front_Left.wav# 录音测试(10秒)arecord-Dplughw:1,0-d10-fS16_LE-r44100-c2test.wav# 播放录制的音频aplay test.wav

💡USB音频设备:USB麦克风和USB扬声器通常会识别为card 1,而板载Codec是card 0。使用plughw:1,0指定USB设备。

4.3 Qt Quick音频播放器

import QtQuick import QtQuick.Controls import QtQuick.Layouts import QtMultimedia Window { width: 400 height: 300 visible: true title: "ALSA 音频播放器" color: "#0F172A" MediaPlayer { id: audioPlayer source: "file:///usr/share/sounds/alsa/Front_Left.wav" audioOutput: AudioOutput {} } ColumnLayout { anchors.centerIn: parent spacing: 20 Text { text: "🎵 ALSA 音频测试" font.pixelSize: 22 color: "#F8FAFC" Layout.alignment: Qt.AlignHCenter } Row { Layout.alignment: Qt.AlignHCenter spacing: 10 Button { text: "▶ 播放" onClicked: audioPlayer.play() } Button { text: "⏸ 暂停" onClicked: audioPlayer.pause() } Button { text: "⏹ 停止" onClicked: audioPlayer.stop() } } Text { text: "状态: " + (audioPlayer.playbackState === MediaPlayer.PlayingState ? "播放中" : "已停止") color: "#94A3B8" Layout.alignment: Qt.AlignHCenter } } }

5、实战:USB摄像头采集

5.1 确认摄像头设备

将USB摄像头插入IQ-9075的USB 3.0接口,然后检查设备:

# 检查V4L2设备ls/dev/video*# 输出: /dev/video0 /dev/video1# 查看摄像头能力v4l2-ctl--device=/dev/video0--all# 列出支持的格式和分辨率v4l2-ctl--device=/dev/video0 --list-formats-ext

🔍FactoryPulse定制:原文提到"按照Edge Impulse平台的要求,增加了适用于定制化USB摄像头的全新驱动程序"。这意味着Qt Onboard的FactoryPulse镜像中内置了特定USB摄像头的驱动支持,确保与Edge Impulse的AI模型兼容。

5.2 命令行测试摄像头

# 使用GStreamer采集摄像头画面并显示gst-launch-1.0 v4l2srcdevice=/dev/video0!\videoconvert!\video/x-raw,width=640,height=480!\qtv4l2sink# 录制摄像头视频gst-launch-1.0 v4l2srcdevice=/dev/video0 num-buffers=300!\videoconvert!x264enc!mp4mux!\filesinklocation=camera_test.mp4

5.3 Qt Quick摄像头预览应用

import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Layouts import QtMultimedia Window { width: 720 height: 560 visible: true title: "USB 摄像头采集" color: "#0F172A" // 摄像头 Camera { id: camera cameraDevice: MediaDevices.defaultVideoInput } // 捕获会话 CaptureSession { id: captureSession camera: camera videoOutput: videoOutput } ColumnLayout { anchors.fill: parent spacing: 10 // 视频预览 VideoOutput { id: videoOutput Layout.fillWidth: true Layout.preferredHeight: 420 fillMode: VideoOutput.PreserveAspectFit } // 控制栏 RowLayout { Layout.alignment: Qt.AlignHCenter spacing: 10 Button { text: camera.active ? "⏹ 停止" : "▶ 开始预览" onClicked: camera.start() } Button { text: "📸 拍照" onClicked: { // 捕获静态图片 let imgCapture = imageCapture imgCapture.capture() } } ComboBox { id: resolutionCombo Layout.preferredWidth: 150 model: ["640x480", "1280x720", "1920x1080"] onActivated: { let sizes = ["640x480", "1280x720", "1920x1080"] let res = sizes[currentIndex].split("x") camera.setResolution(Qt.size(parseInt(res[0]), parseInt(res[1]))) } } } // 摄像头信息 Text { text: "设备: " + camera.cameraDevice.description + " | 分辨率: " + camera.resolution.width + "x" + camera.resolution.height color: "#94A3B8" font.pixelSize: 11 Layout.alignment: Qt.AlignHCenter } } ImageCapture { id: imageCapture } Component.onCompleted: { console.log("摄像头设备列表:", MediaDevices.videoInputs.length) camera.start() } }

💡性能优化提示:在IQ-9075上,摄像头采集建议使用硬件加速路径。GStreamer的ivapostproc和ivacompositor插件可以利用硬件进行颜色转换和缩放,降低CPU占用。

6、实战进阶:音视频同步录制

做一个更完整的例子,同时采集摄像头画面和麦克风声音,录制为MP4文件。

关键代码片段

// main.cpp - 主程序#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QtMultimedia>intmain(intargc,char*argv[]){QGuiApplicationapp(argc,argv);QQmlApplicationEngine engine;qmlRegisterType<QCamera>("QtMultimedia",6,0,"Camera");qmlRegisterType<QMediaCaptureSession>("QtMultimedia",6,0,"CaptureSession");qmlRegisterType<QImageCapture>("QtMultimedia",6,0,"ImageCapture");qmlRegisterType<QMediaRecorder>("QtMultimedia",6,0,"MediaRecorder");qmlRegisterType<QAudioInput>("QtMultimedia",6,0,"AudioInput");engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));returnapp.exec();}
// Main.qml 中的录制部分 MediaRecorder { id: recorder captureSession: captureSession outputLocation: "file:///tmp/recording.mp4" quality: MediaRecorder.HighQuality } Button { text: recorder.recorderState === MediaRecorder.RecordingState ? "⏹ 停止录制" : "⏺ 开始录制" onClicked: { if (recorder.recorderState === MediaRecorder.RecordingState) recorder.stop() else recorder.record() } }

7、常见问题与排错

Q1:VideoOutput黑屏,没有画面

排查步骤:

  1. 检查Qt Multimedia插件是否存在:ls /usr/lib/qt6/plugins/multimedia/
  2. 检查GStreamer Qt插件:gst-inspect-1.0 qt
  3. 用命令行测试GStreamer管线是否正常
  4. 检查环境变量:
    exportQT_QPA_PLATFORM=eglfsexportGST_DEBUG=2# 查看GStreamer调试信息

Q2:USB摄像头不识别

排查步骤:

  1. 检查USB设备是否枚举:lsusb
  2. 检查V4L2设备:ls /dev/video*
  3. 查看内核日志:dmesg | grep -i usb
  4. 确认摄像头驱动是否加载:lsmod | grep uvcvideo

⚠️FactoryPulse镜像说明:如果你的摄像头是特殊型号,可能需要在Yocto中添加对应的驱动。但Qt Onboard的FactoryPulse定制镜像已经包含了常用USB摄像头的驱动。

Q3:ALSA音频播放没有声音

排查步骤:

  1. 确认音频设备:aplay -l
  2. 指定设备播放:aplay -D plughw:1,0 test.wav
  3. 检查音量:alsamixer(确保没有静音)
  4. 查看Qt Multimedia音频后端:
    qDebug()<<QMediaDevices::audioOutputs();

Q4:视频CPU占用过高

优化方法:

  1. 使用硬件解码:确保使用vpudec而不是软件解码
  2. 使用零拷贝:尽量减少内存拷贝
  3. 降低分辨率/帧率:如果不需要1080p,用720p
  4. 使用GStreamer硬件加速插件:ivapostproc、ivacompositor

8、性能对比与最佳实践

8.1 视频播放性能对比(IQ-9075上1080p H.264)

解码方式CPU占用帧率延迟
软件解码 (avdec_h264)~85%~25fps高
硬件解码 (vpudec)~15%60fps+低

✅结论:一定要使用硬件解码插件vpudec,性能差距非常大!

8.2 音频延迟对比

音频后端典型延迟资源占用
PulseAudio50-100ms较高
ALSA (直接)10-30ms低

✅结论:对实时性要求高的场景(如语音交互、AI对话),ALSA直接访问是更好的选择。

8.3 最佳实践总结

  1. 视频优先用硬件加速:VPU解码、IVA后处理
  2. 音频优先用ALSA:嵌入式场景延迟更低
  3. 摄像头用V4L2:标准接口,兼容性好
  4. 资源监控:使用top、gst-top监控性能
  5. 分辨率按需选择:不要盲目追求最高分辨率

9、总结

本篇我们深入实战了Qt Onboard的多媒体子系统,核心要点回顾:

功能技术栈关键组件
视频播放GStreamer + VPUvpudec、ivapostproc
音频播放ALSAalsasink、USB Audio
摄像头采集V4L2 + GStreamerv4l2src、uvcvideo
Qt集成Qt MultimediaMediaPlayer、Camera、VideoOutput

本篇核心收获:

  1. ✅ 理解多媒体子系统的三层架构
  2. ✅ 掌握GStreamer命令行调试技巧
  3. ✅ 学会使用Qt Multimedia实现视频播放
  4. ✅ 理解ALSA替换PulseAudio的原因与优势
  5. ✅ 掌握USB摄像头采集与预览方法
  6. ✅ 了解硬件加速的重要性与性能优化方向

在下一篇,也是本系列的最后一篇中,我们将进入边缘AI应用的实战,讲解如何在Qt Onboard中集成Genie大模型库和ONNX运行时,构建完整的FactoryPulse式边缘AI应用,这也是Qt Onboard最核心的价值所在。

系列索引:(一)环境搭建全攻略 · (二)Qt Creator交叉编译与一键部署实战 · (三)多媒体与摄像头集成· (四)边缘AI应用

如果本文对你有帮助,欢迎点赞、收藏、关注!有问题也欢迎在评论区交流~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 4:56:29

Linux Gstreamer深度解析之gst_audio_meta_get_info调用流程与实战(八十九)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》作者 博主新书推荐&#xff1a;《Android系统多媒体进阶实战》&#x1f680; Android Audio工程师专栏地址&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; Android多媒体专栏地址&a…

作者头像 李华
网站建设 2026/10/23 4:56:07

VS Code Agent技巧,再也不要复制粘贴了

我在 VS Code 里跟 AI Agent 聊代码&#xff0c;聊到一半&#xff0c;它给我的某个函数里有一段写得不太对。我想针对那一小段追问&#xff1a;“这里为什么不用缓存&#xff1f;”。于是我做了什么呢&#xff1f; 我用鼠标选中那段代码&#xff0c;按 CtrlC&#xff0c;打开对…

作者头像 李华
网站建设 2026/10/23 4:54:34

企业GEO优化需求差异与解决方案全解析

1. 企业规模与GEO优化需求差异解析不同体量的企业在GEO优化需求上存在显著差异&#xff0c;这直接决定了服务商的选择策略。我们以员工规模为基准划分四类企业时&#xff0c;会发现其技术需求和预算分配呈现阶梯式变化。**微型企业&#xff08;1-10人&#xff09;**通常面临的最…

作者头像 李华
网站建设 2026/10/23 4:53:03

LFM脉冲压缩雷达仿真:从信号生成到距离像的完整实现

简介&#xff1a;本资源是一套面向雷达信号处理初学者与Matlab实践者的线性调频&#xff08;LFM&#xff09;脉冲压缩雷达完整仿真教学包&#xff0c;聚焦于理解匹配滤波原理、提升距离分辨力与信噪比等核心雷达性能指标。资源包含21个文件&#xff0c;涵盖10张关键原理图&…

作者头像 李华
网站建设 2026/10/23 4:52:30

BrushNet 图像修复实践指南:用最小示例跑通文本引导修复

BrushNet 图像修复实践指南&#xff1a;用最小示例跑通文本引导修复 【免费下载链接】BrushNet [ECCV 2024] The official implementation of paper "BrushNet: A Plug-and-Play Image Inpainting Model with Decomposed Dual-Branch Diffusion" 项目地址: https:/…

作者头像 李华
网站建设 2026/10/23 4:51:23

Flutter应用更新全流程指南与实战经验

1. Flutter应用更新概述作为一款跨平台移动应用框架&#xff0c;Flutter凭借其高效的开发体验和出色的性能表现&#xff0c;已经成为众多开发者的首选。当我们在Google Play商店成功发布Flutter应用后&#xff0c;后续的版本更新迭代是不可避免的。不同于首次发布&#xff0c;应…

作者头像 李华