-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Video: Replace OpenGL qml6glsink with appsink Metal path on macOS #14228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import QtQuick | ||
| import QtMultimedia | ||
|
|
||
| import QGroundControl | ||
|
|
||
| // macOS Metal rendering path for GStreamer video. | ||
| // Frames are pushed from the C++ GstAppSinkAdapter to this VideoOutput's QVideoSink. | ||
| VideoOutput { | ||
| objectName: "videoContent" | ||
| fillMode: VideoOutput.PreserveAspectFit | ||
|
|
||
| Connections { | ||
| target: QGroundControl.videoManager | ||
| function onImageFileChanged(filename) { | ||
| grabToImage(function(result) { | ||
| if (!result.saveToFile(filename)) { | ||
| console.error('Error capturing video frame'); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -239,10 +239,11 @@ void QGCApplication::init() | |
| bool QGCApplication::_initVideo() | ||
| { | ||
| #ifdef QGC_GST_STREAMING | ||
| // GStreamer video playback requires OpenGL. On platforms where OpenGL | ||
| // is unavailable (e.g. recent macOS with only Metal), fall back to the | ||
| // default graphics API — video streaming won't work but the rest of | ||
| // QGC remains functional. | ||
| // GStreamer video rendering backend selection: | ||
| // - Windows D3D11: native RHI, no OpenGL needed. | ||
| // - macOS: appsink → QVideoSink → Metal RHI VideoOutput, no OpenGL needed. | ||
| // - Linux/other: qml6glsink requires OpenGL. Probe for a working GL context | ||
| // and fall back to the default graphics API if unavailable. | ||
| // | ||
| // The offscreen platform (used in CI boot tests) never provides a real | ||
| // GL context, so skip the probe there — just set OpenGL API to exercise | ||
|
|
@@ -255,15 +256,17 @@ bool QGCApplication::_initVideo() | |
| QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); | ||
| } | ||
| qCDebug(QGCApplicationLog) << "D3D11 video sink available, using default graphics API"; | ||
| #elif defined(Q_OS_MACOS) | ||
| // macOS Metal rendering path: appsink → QVideoSink → VideoOutput. | ||
| // Do NOT force OpenGL — let Qt use the default Metal RHI backend. | ||
| // The appsink path in qgcvideosinkbin avoids the GL-dependent qml6glsink. | ||
| if (isOffscreen) { | ||
| QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); | ||
| } else { | ||
| qCDebug(QGCApplicationLog) << "macOS: using default RHI backend (Metal) for appsink video path"; | ||
| } | ||
|
Comment on lines
+260
to
+267
|
||
| #else | ||
| const bool skipGLProbe = isOffscreen | ||
| #if defined(Q_OS_MACOS) | ||
| // macOS still provides OpenGL (deprecated but functional). The | ||
| // QOpenGLContext::create() probe is unreliable without a native | ||
| // surface, so skip it and force OpenGL for qml6glsink. | ||
| || true | ||
| #endif | ||
| ; | ||
| const bool skipGLProbe = isOffscreen; | ||
|
|
||
| if (skipGLProbe) { | ||
| QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); | ||
|
|
@@ -276,7 +279,7 @@ bool QGCApplication::_initVideo() | |
| << "Using default graphics API (Metal/Vulkan)."; | ||
| } | ||
| } | ||
| #endif // QGC_GST_D3D11_SINK | ||
| #endif // QGC_GST_D3D11_SINK / Q_OS_MACOS | ||
| #endif | ||
|
|
||
| QGCCorePlugin::instance(); // CorePlugin must be initialized before VideoManager for Video Cleanup | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,11 @@ | |
| #include "UVCReceiver.h" | ||
| #ifdef QGC_GST_STREAMING | ||
| #include "GStreamerHelpers.h" | ||
| #include "GStreamer.h" | ||
| #endif | ||
| #if defined(QGC_GST_STREAMING) && defined(Q_OS_MACOS) | ||
| #include <QtMultimedia/QVideoSink> | ||
| #include <QtMultimediaQuick/private/qquickvideooutput_p.h> | ||
| #endif | ||
|
|
||
| #include <QtConcurrent/QtConcurrent> | ||
|
|
@@ -501,6 +506,15 @@ bool VideoManager::gstreamerD3D11Sink() | |
| #endif | ||
| } | ||
|
|
||
| bool VideoManager::gstreamerAppleSink() | ||
| { | ||
| #if defined(QGC_GST_STREAMING) && defined(Q_OS_MACOS) && !defined(QGC_GST_D3D11_SINK) | ||
| return true; | ||
| #else | ||
| return false; | ||
| #endif | ||
| } | ||
|
Comment on lines
+509
to
+516
|
||
|
|
||
| bool VideoManager::uvcEnabled() | ||
| { | ||
| return UVCReceiver::enabled(); | ||
|
|
@@ -888,6 +902,22 @@ void VideoManager::_initVideoReceiver(VideoReceiver *receiver, QQuickWindow *win | |
| } | ||
| receiver->setSink(sink); | ||
|
|
||
| #if defined(QGC_GST_STREAMING) && defined(Q_OS_MACOS) && !defined(QGC_GST_D3D11_SINK) | ||
| // macOS Metal path: connect appsink inside the sinkbin to the QVideoSink | ||
| // belonging to the QML VideoOutput widget. | ||
| if (sink && widget) { | ||
| auto *videoOutput = qobject_cast<QQuickVideoOutput *>(widget); | ||
| if (videoOutput) { | ||
| QVideoSink *videoSink = videoOutput->videoSink(); | ||
| if (!GStreamer::setupAppleSinkAdapter(sink, videoSink, receiver)) { | ||
| qCWarning(VideoManagerLog) << "setupAppleSinkAdapter failed" << receiver->name(); | ||
| } | ||
| } else { | ||
| qCWarning(VideoManagerLog) << "Widget is not a VideoOutput, cannot connect appsink" << receiver->name(); | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| (void) connect(receiver, &VideoReceiver::onStartComplete, this, [this, receiver](VideoReceiver::STATUS status) { | ||
| qCDebug(VideoManagerLog) << "Video" << receiver->name() << "Start complete, status:" << status; | ||
| switch (status) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE VideoItemStub.h) | |||||||||||||||||||||
| if(QGC_ENABLE_GST_VIDEOSTREAMING) | ||||||||||||||||||||||
| find_package(QGCGStreamer | ||||||||||||||||||||||
| REQUIRED | ||||||||||||||||||||||
| COMPONENTS Core Base Video Gl GlPrototypes Rtsp | ||||||||||||||||||||||
| COMPONENTS Core Base Video Gl GlPrototypes Rtsp App | ||||||||||||||||||||||
| OPTIONAL_COMPONENTS GlEgl GlWayland GlX11 | ||||||||||||||||||||||
|
Comment on lines
5
to
8
|
||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
Comment on lines
+7
to
10
|
||||||||||||||||||||||
| COMPONENTS Core Base Video Gl GlPrototypes Rtsp App | |
| OPTIONAL_COMPONENTS GlEgl GlWayland GlX11 | |
| ) | |
| COMPONENTS Core Base Video Gl GlPrototypes Rtsp | |
| OPTIONAL_COMPONENTS App GlEgl GlWayland GlX11 | |
| ) | |
| if(APPLE AND NOT QGCGStreamer_App_FOUND) | |
| message(FATAL_ERROR "QGCGStreamer App component (gst-app) is required on Apple platforms") | |
| endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The component selection is driven by
videoManager.gstreamerAppleSink, which is currently compile-time and always true on macOS. If the GStreamer sinkbin can't initialize the appsink path (or falls back to GL), this will still loadFlightDisplayViewMetal(VideoOutput) instead of the GL-backedQGCVideoBackground, and video rendering will break. Consider selecting based on a runtime backend indicator (or ensure the appsink path cannot fail without disabling streaming).