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