前言
边缘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图展示了视频采集、播放和音频处理的完整管线:
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,原因如下:
| 特性 | PulseAudio | ALSA (直接使用) |
|---|---|---|
| 资源占用 | 较高(需要守护进程) | 低(直接操作硬件) |
| 延迟 | 较高 | 更低 |
| 混音能力 | 强(多应用同时播放) | 有限(硬件混音) |
| 适用场景 | 桌面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.mp45.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黑屏,没有画面
排查步骤:
- 检查Qt Multimedia插件是否存在:
ls /usr/lib/qt6/plugins/multimedia/ - 检查GStreamer Qt插件:
gst-inspect-1.0 qt - 用命令行测试GStreamer管线是否正常
- 检查环境变量:
exportQT_QPA_PLATFORM=eglfsexportGST_DEBUG=2# 查看GStreamer调试信息
Q2:USB摄像头不识别
排查步骤:
- 检查USB设备是否枚举:
lsusb - 检查V4L2设备:
ls /dev/video* - 查看内核日志:
dmesg | grep -i usb - 确认摄像头驱动是否加载:
lsmod | grep uvcvideo
⚠️FactoryPulse镜像说明:如果你的摄像头是特殊型号,可能需要在Yocto中添加对应的驱动。但Qt Onboard的FactoryPulse定制镜像已经包含了常用USB摄像头的驱动。
Q3:ALSA音频播放没有声音
排查步骤:
- 确认音频设备:
aplay -l - 指定设备播放:
aplay -D plughw:1,0 test.wav - 检查音量:
alsamixer(确保没有静音) - 查看Qt Multimedia音频后端:
qDebug()<<QMediaDevices::audioOutputs();
Q4:视频CPU占用过高
优化方法:
- 使用硬件解码:确保使用
vpudec而不是软件解码 - 使用零拷贝:尽量减少内存拷贝
- 降低分辨率/帧率:如果不需要1080p,用720p
- 使用GStreamer硬件加速插件:
ivapostproc、ivacompositor
8、性能对比与最佳实践
8.1 视频播放性能对比(IQ-9075上1080p H.264)
| 解码方式 | CPU占用 | 帧率 | 延迟 |
|---|---|---|---|
| 软件解码 (avdec_h264) | ~85% | ~25fps | 高 |
| 硬件解码 (vpudec) | ~15% | 60fps+ | 低 |
✅结论:一定要使用硬件解码插件
vpudec,性能差距非常大!
8.2 音频延迟对比
| 音频后端 | 典型延迟 | 资源占用 |
|---|---|---|
| PulseAudio | 50-100ms | 较高 |
| ALSA (直接) | 10-30ms | 低 |
✅结论:对实时性要求高的场景(如语音交互、AI对话),ALSA直接访问是更好的选择。
8.3 最佳实践总结
- 视频优先用硬件加速:VPU解码、IVA后处理
- 音频优先用ALSA:嵌入式场景延迟更低
- 摄像头用V4L2:标准接口,兼容性好
- 资源监控:使用
top、gst-top监控性能 - 分辨率按需选择:不要盲目追求最高分辨率
9、总结
本篇我们深入实战了Qt Onboard的多媒体子系统,核心要点回顾:
| 功能 | 技术栈 | 关键组件 |
|---|---|---|
| 视频播放 | GStreamer + VPU | vpudec、ivapostproc |
| 音频播放 | ALSA | alsasink、USB Audio |
| 摄像头采集 | V4L2 + GStreamer | v4l2src、uvcvideo |
| Qt集成 | Qt Multimedia | MediaPlayer、Camera、VideoOutput |
本篇核心收获:
- ✅ 理解多媒体子系统的三层架构
- ✅ 掌握GStreamer命令行调试技巧
- ✅ 学会使用Qt Multimedia实现视频播放
- ✅ 理解ALSA替换PulseAudio的原因与优势
- ✅ 掌握USB摄像头采集与预览方法
- ✅ 了解硬件加速的重要性与性能优化方向
在下一篇,也是本系列的最后一篇中,我们将进入边缘AI应用的实战,讲解如何在Qt Onboard中集成Genie大模型库和ONNX运行时,构建完整的FactoryPulse式边缘AI应用,这也是Qt Onboard最核心的价值所在。
系列索引:(一)环境搭建全攻略 · (二)Qt Creator交叉编译与一键部署实战 · (三)多媒体与摄像头集成· (四)边缘AI应用
如果本文对你有帮助,欢迎点赞、收藏、关注!有问题也欢迎在评论区交流~