Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpegmediaintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtFFmpegMediaPluginImpl/private/qffmpegmediaintegration_p.h>
5
6#include <QtFFmpegMediaPluginImpl/private/qffmpegaudiodecoder_p.h>
7#include <QtFFmpegMediaPluginImpl/private/qffmpegaudioinput_p.h>
8#include <QtFFmpegMediaPluginImpl/private/qffmpegconverter_p.h>
9#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
10#include <QtFFmpegMediaPluginImpl/private/qffmpegimagecapture_p.h>
11#include <QtFFmpegMediaPluginImpl/private/qffmpegmediacapturesession_p.h>
12#include <QtFFmpegMediaPluginImpl/private/qffmpegmediaformatinfo_p.h>
13#include <QtFFmpegMediaPluginImpl/private/qffmpegmediaplayer_p.h>
14#include <QtFFmpegMediaPluginImpl/private/qffmpegmediarecorder_p.h>
15#include <QtFFmpegMediaPluginImpl/private/qffmpegresampler_p.h>
16#include <QtFFmpegMediaPluginImpl/private/qffmpegvideosink_p.h>
17#include <QtFFmpegMediaPluginImpl/private/qgrabwindowsurfacecapture_p.h>
18
19#include <QtMultimedia/private/qplatformmediaplugin_p.h>
20#include <QtMultimedia/qcameradevice.h>
21
22#ifdef Q_OS_DARWIN
23#include <QtFFmpegMediaPluginImpl/private/qffmpegdarwinintegrationfactory_p.h>
24#include <QtMultimedia/private/qdarwinintegrationfactory_p.h>
25#endif
26
27#ifdef Q_OS_WINDOWS
28# include <QtMultimedia/private/qwindowsresampler_p.h>
29# include <QtMultimedia/private/qwindowsvideodevices_p.h>
30# include "qwindowscamera_p.h"
31# include "qffmpegscreencapture_dxgi_p.h"
32# include "qwincapturablewindows_p.h"
33# include "qgdiwindowcapture_p.h"
34#endif
35
36#ifdef Q_OS_LINUX
37# include <QtMultimedia/private/qplatformlibcameraintegration_p.h>
38#endif
39
40#ifdef Q_OS_ANDROID
41#include <QtFFmpegMediaPluginImpl/private/qandroidcamera_p.h>
42#include <QtFFmpegMediaPluginImpl/private/qandroidimagecapture_p.h>
43#include <QtFFmpegMediaPluginImpl/private/qandroidscreencapture_p.h>
44#include <QtFFmpegMediaPluginImpl/private/qandroidvideodevices_p.h>
45#include <jni.h>
46extern "C" {
47# include <libavutil/log.h>
48# include <libavcodec/jni.h>
49}
50#endif
51
52#if QT_CONFIG(linux_v4l)
53# include <QtMultimedia/private/qv4l2camera_p.h>
54# include <QtMultimedia/private/qv4l2cameradevices_p.h>
55#endif
56
57#if QT_CONFIG(cpp_winrt)
58#include "qffmpegwindowcapture_uwp_p.h"
59#endif
60
61#if QT_CONFIG(xlib)
62#include "qx11surfacecapture_p.h"
63#include "qx11capturablewindows_p.h"
64#endif
65
66#if QT_CONFIG(pipewire_screencapture)
67# include <QtMultimedia/private/qpipewire_screencapture_p.h>
68#endif
69
70#if QT_CONFIG(pipewire)
71# include <QtMultimedia/private/qpipewire_camera_p.h>
72# include <QtMultimedia/private/qpipewire_cameradevices_p.h>
73#endif
74
75#if QT_CONFIG(eglfs)
76#include "qeglfsscreencapture_p.h"
77#endif
78
79#include <QtCore/qloggingcategory.h>
80
82
83Q_STATIC_LOGGING_CATEGORY(qLcFFmpeg, "qt.multimedia.ffmpeg");
84
85using namespace Qt::Literals;
86
87bool thread_local FFmpegLogsEnabledInThread = true;
88
89namespace {
90
91bool UseCustomFFmpegLogger = false;
92
93void qffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl)
94{
96 return;
97
98 if (!UseCustomFFmpegLogger)
99 return av_log_default_callback(ptr, level, fmt, vl);
100
101 // filter logs above the chosen level and AV_LOG_QUIET (negative level)
102 if (level < 0 || level > av_log_get_level())
103 return;
104
105 QString message = QStringLiteral("FFmpeg log: %1").arg(QString::vasprintf(fmt, vl));
106 if (message.endsWith(u"\n"))
107 message.removeLast();
108
109 if (level == AV_LOG_DEBUG || level == AV_LOG_TRACE)
110 qDebug() << message;
111 else if (level == AV_LOG_VERBOSE || level == AV_LOG_INFO)
112 qInfo() << message;
113 else if (level == AV_LOG_WARNING)
114 qWarning() << message;
115 else if (level == AV_LOG_ERROR || level == AV_LOG_FATAL || level == AV_LOG_PANIC)
116 qCritical() << message;
117}
118
119void setupFFmpegLogger()
120{
121 if (qEnvironmentVariableIsSet("QT_FFMPEG_DEBUG")) {
122 av_log_set_level(AV_LOG_DEBUG);
123 UseCustomFFmpegLogger = true;
124 }
125
126 av_log_set_callback(&qffmpegLogCallback);
127}
128
129#ifdef Q_OS_LINUX
130
131enum class CameraBackend {
132 V4L2,
133 PipeWire,
134 Libcamera,
135};
136
137std::optional<CameraBackend> requestedBackend()
138{
139 const QByteArray requested = qgetenv("QT_CAMERA_BACKEND").toLower();
140 if (requested.isEmpty())
141 return std::nullopt;
142 if (requested == "v4l2"_ba)
143 return CameraBackend::V4L2;
144 if (requested == "libcamera"_ba)
145 return CameraBackend::Libcamera;
146# if QT_CONFIG(pipewire)
147 if (requested == "pipewire"_ba)
148 return CameraBackend::PipeWire;
149# endif
150 qWarning() << "Unknown QT_CAMERA_BACKEND:" << requested << "... Using default.";
151 return std::nullopt;
152}
153
154[[maybe_unused]] CameraBackend selectedCameraBackend()
155{
156 static const CameraBackend backend = [] {
157 std::optional<CameraBackend> requested = requestedBackend();
158
159# if QT_CONFIG(pipewire)
160 if (requested == CameraBackend::PipeWire) {
161 if (QtPipeWire::QCameraDevices::isSupported())
162 return CameraBackend::PipeWire;
163
164 if (requested == CameraBackend::PipeWire)
165 qWarning() << "QT_CAMERA_BACKEND=pipewire requested but PipeWire is not supported "
166 "on this system.";
167 }
168# endif
169 if (requested == CameraBackend::Libcamera) {
170 if (QLibCamera::libCameraIntegration())
171 return CameraBackend::Libcamera;
172 qWarning() << "QT_CAMERA_BACKEND=libcamera requested but libcamera is not supported on "
173 "this system.";
174 }
175 if (!requested || requested == CameraBackend::V4L2)
176 return CameraBackend::V4L2;
177
178 return CameraBackend::V4L2;
179 }();
180
181 return backend;
182}
183
184#endif
185
186QPlatformSurfaceCapture *createScreenCaptureByBackend(const QString &backend)
187{
188 if (backend == u"grabwindow")
189 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
190
191#if QT_CONFIG(eglfs)
192 if (backend == u"eglfs")
193 return new QEglfsScreenCapture;
194#endif
195
196#if QT_CONFIG(pipewire_screencapture)
197 if (backend == u"pipewire") {
198 if (auto capture = QtPipeWire::QPipeWireCapture::create())
199 return capture.release();
200 }
201#endif
202
203#if QT_CONFIG(xlib)
204 if (backend == u"x11")
205 return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
206#elif defined(Q_OS_WINDOWS)
207 if (backend == u"dxgi")
208 return new QFFmpegScreenCaptureDxgi;
209#elif defined(Q_OS_MACOS)
210 if (backend == u"avf")
211 return QFFmpeg::makeQSckScreenCapture().release();
212#endif
213 return nullptr;
214}
215
216QPlatformSurfaceCapture *createWindowCaptureByBackend(const QString &backend)
217{
218 if (backend == u"grabwindow")
219 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
220
221#if QT_CONFIG(xlib)
222 if (backend == u"x11")
223 return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
224#elif defined(Q_OS_WINDOWS)
225 if (backend == u"gdi")
226 return new QGdiWindowCapture;
227#if QT_CONFIG(cpp_winrt)
228 if (backend == u"uwp")
229 return new QFFmpegWindowCaptureUwp;
230#endif
231#elif defined(Q_OS_MACOS)
232 if (backend == u"cg")
233 return QFFmpeg::makeQSckWindowCapture().release();
234#endif
235 return nullptr;
236}
237
238} // namespace
239
240QFFmpegMediaIntegration::QFFmpegMediaIntegration()
241 : QPlatformMediaIntegration(QLatin1String("ffmpeg"))
242{
243 setupFFmpegLogger();
244
245 qCInfo(qLcFFmpeg) << "Using Qt multimedia with FFmpeg version" << av_version_info()
246 << avutil_license();
247
248 qCDebug(qLcFFmpeg) << "Available HW decoding frameworks:";
249 for (auto type : QFFmpeg::HWAccel::decodingDeviceTypes())
250 qCDebug(qLcFFmpeg) << " " << type;
251
252 qCDebug(qLcFFmpeg) << "Available HW encoding frameworks:";
253 for (auto type : QFFmpeg::HWAccel::encodingDeviceTypes())
254 qCDebug(qLcFFmpeg) << " " << type;
255}
256
257q23::expected<QPlatformAudioDecoder *, QString>
258QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
259{
260 return new QFFmpegAudioDecoder(decoder);
261}
262
263q23::expected<std::unique_ptr<QPlatformAudioResampler>, QString>
264QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat,
265 const QAudioFormat &outputFormat)
266{
267 auto ffmpegResampler = QFFmpegResampler::createFromInputFormat(inputFormat, outputFormat);
268 if (ffmpegResampler)
269 return ffmpegResampler;
270
271#ifdef Q_OS_WINDOWS
272 auto windowsResampler = std::make_unique<QWindowsResampler>();
273 if (windowsResampler->setup(inputFormat, outputFormat))
274 return windowsResampler;
275#endif
276
277 return q23::unexpected{ notAvailable };
278}
279
280q23::expected<QPlatformMediaCaptureSession *, QString>
281QFFmpegMediaIntegration::createCaptureSession()
282{
283 return new QFFmpegMediaCaptureSession();
284}
285
286q23::expected<QPlatformMediaPlayer *, QString>
287QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
288{
289 return new QFFmpegMediaPlayer(player);
290}
291
292q23::expected<QPlatformCamera *, QString> QFFmpegMediaIntegration::createCamera(QCamera *camera)
293{
294 Q_ASSERT(camera);
295
296#ifdef Q_OS_DARWIN
297 return QFFmpeg::makeQAvfCamera(*camera).release();
298#elif defined(Q_OS_ANDROID)
299 return new QFFmpeg::QAndroidCamera(camera);
300#elif defined(Q_OS_WINDOWS)
301 return new QFFmpeg::QWindowsCamera(camera);
302#elif defined(Q_OS_LINUX)
303 auto backend = selectedCameraBackend();
304 switch (backend) {
305# if QT_CONFIG(pipewire)
306 case CameraBackend::PipeWire:
307 return new QtPipeWire::QPipeWireCamera(camera);
308# endif
309 case CameraBackend::V4L2:
310 return new QtV4L2::QV4L2Camera(camera);
311 case CameraBackend::Libcamera:
312 return QLibCamera::libCameraIntegration()->createCamera(camera);
313 default:
314 return q23::unexpected{ notAvailable };
315 }
316#else
317 Q_UNUSED(camera);
318 return q23::unexpected{ notAvailable };
319#endif
320}
321
322QPlatformSurfaceCapture *QFFmpegMediaIntegration::createScreenCapture(QScreenCapture *)
323{
324 static const QString screenCaptureBackend =
325 QString::fromLocal8Bit(qgetenv("QT_SCREEN_CAPTURE_BACKEND")).toLower();
326
327 if (!screenCaptureBackend.isEmpty()) {
328 if (auto screenCapture = createScreenCaptureByBackend(screenCaptureBackend))
329 return screenCapture;
330
331 qWarning() << "Not supported QT_SCREEN_CAPTURE_BACKEND:" << screenCaptureBackend;
332 }
333
334#if QT_CONFIG(xlib)
335 if (QX11SurfaceCapture::isSupported())
336 return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
337#endif
338
339#if QT_CONFIG(pipewire_screencapture)
340 if (auto capture = QtPipeWire::QPipeWireCapture::create())
341 return capture.release();
342#endif
343
344#if QT_CONFIG(eglfs)
345 if (QEglfsScreenCapture::isSupported())
346 return new QEglfsScreenCapture;
347#endif
348
349#if defined(Q_OS_WINDOWS)
350 return new QFFmpegScreenCaptureDxgi;
351#elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well
352 return QFFmpeg::makeQSckScreenCapture().release();
353#elif defined(Q_OS_ANDROID)
354 return new QAndroidScreenCapture;
355#else
356 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
357#endif
358}
359
360QPlatformSurfaceCapture *QFFmpegMediaIntegration::createWindowCapture(QWindowCapture *)
361{
362 static const QString windowCaptureBackend =
363 QString::fromLocal8Bit(qgetenv("QT_WINDOW_CAPTURE_BACKEND")).toLower();
364
365 if (!windowCaptureBackend.isEmpty()) {
366 if (auto windowCapture = createWindowCaptureByBackend(windowCaptureBackend))
367 return windowCapture;
368
369 qWarning() << "Not supported QT_WINDOW_CAPTURE_BACKEND:" << windowCaptureBackend;
370 }
371
372#if QT_CONFIG(xlib)
373 if (QX11SurfaceCapture::isSupported())
374 return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
375#endif
376
377#if defined(Q_OS_WINDOWS)
378# if QT_CONFIG(cpp_winrt)
379 if (QFFmpegWindowCaptureUwp::isSupported())
380 return new QFFmpegWindowCaptureUwp;
381# endif
382
383 return new QGdiWindowCapture;
384#elif defined(Q_OS_MACOS)
385 return QFFmpeg::makeQSckWindowCapture().release();
386#else
387 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
388#endif
389}
390
391q23::expected<QPlatformMediaRecorder *, QString>
392QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder)
393{
394 return new QFFmpegMediaRecorder(recorder);
395}
396
397q23::expected<QPlatformImageCapture *, QString>
398QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
399{
400 Q_ASSERT(imageCapture);
401#if defined(Q_OS_ANDROID)
402 return new QFFmpeg::QAndroidImageCapture(imageCapture);
403#elif defined(Q_OS_DARWIN)
404 return QFFmpeg::makeQAvfImageCapture(*imageCapture).release();
405#else
406 return new QFFmpegImageCapture(imageCapture);
407#endif
408}
409
410q23::expected<QPlatformVideoSink *, QString>
411QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink)
412{
413 return new QFFmpegVideoSink(sink);
414}
415
416q23::expected<QPlatformAudioInput *, QString>
417QFFmpegMediaIntegration::createAudioInput(QAudioInput *input)
418{
419 return new QFFmpegAudioInput(input);
420}
421
422QVideoFrame QFFmpegMediaIntegration::convertVideoFrame(QVideoFrame &srcFrame,
423 const QVideoFrameFormat &destFormat)
424{
425 return convertFrame(srcFrame, destFormat);
426}
427
428QPlatformMediaFormatInfo *QFFmpegMediaIntegration::createFormatInfo()
429{
430 return new QFFmpegMediaFormatInfo;
431}
432
433QPlatformVideoDevices *QFFmpegMediaIntegration::createVideoDevices()
434{
435#if defined(Q_OS_ANDROID)
436 return new QFFmpeg::QAndroidVideoDevices(this);
437#elif defined Q_OS_DARWIN
438 return makeQAvfVideoDevices(
439 *this,
440 &QFFmpeg::isCVFormatSupported).release();
441#elif defined(Q_OS_WINDOWS)
442 return new QWindowsVideoDevices(this);
443#elif defined(Q_OS_LINUX)
444 auto backend = selectedCameraBackend();
445 switch (backend) {
446# if QT_CONFIG(pipewire)
447 case CameraBackend::PipeWire:
448 return new QtPipeWire::QCameraDevices(this);
449# endif
450 case CameraBackend::V4L2:
451 return new QtV4L2::QV4L2CameraDevices(this);
452 case CameraBackend::Libcamera:
453 return QLibCamera::libCameraIntegration()->createVideoDevices(this);
454 default:
455 return nullptr;
456 }
457#else
458 return nullptr;
459#endif
460}
461
462QPlatformCapturableWindows *QFFmpegMediaIntegration::createCapturableWindows()
463{
464#if QT_CONFIG(xlib)
465 if (QX11SurfaceCapture::isSupported())
466 return new QX11CapturableWindows;
467#elif defined Q_OS_MACOS
468 return QFFmpeg::makeQSckCapturableWindows().release();
469#elif defined(Q_OS_WINDOWS)
470 return new QWinCapturableWindows;
471#endif
472 return nullptr;
473}
474
475QT_END_NAMESPACE
476
477#ifdef Q_OS_ANDROID
478
479extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
480{
481 static bool initialized = false;
482 if (initialized)
483 return JNI_VERSION_1_6;
484 initialized = true;
485
486 void *environment;
487 if (vm->GetEnv(&environment, JNI_VERSION_1_6))
488 return JNI_ERR;
489
490 // setting our javavm into ffmpeg.
491 if (av_jni_set_java_vm(vm, nullptr))
492 return JNI_ERR;
493
494 if (!QFFmpeg::QAndroidCamera::registerNativeMethods()
495 || !QAndroidScreenCapture::registerNativeMethods()
496 || !QFFmpeg::QAndroidVideoDevices::registerNativeMethods()) {
497 return JNI_ERR;
498 }
499
500 return JNI_VERSION_1_6;
501}
502#endif
bool FFmpegLogsEnabledInThread
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)