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

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