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

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp