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 "qv4l2camera_p.h"
49#include "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(eglfs)
66#include "qeglfsscreencapture_p.h"
67#endif
68
69#include <QtCore/qloggingcategory.h>
70
72
73Q_STATIC_LOGGING_CATEGORY(qLcFFmpeg, "qt.multimedia.ffmpeg");
74
75bool thread_local FFmpegLogsEnabledInThread = true;
76static bool UseCustomFFmpegLogger = false;
77
78static void qffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl)
79{
81 return;
82
84 return av_log_default_callback(ptr, level, fmt, vl);
85
86 // filter logs above the chosen level and AV_LOG_QUIET (negative level)
87 if (level < 0 || level > av_log_get_level())
88 return;
89
90 QString message = QStringLiteral("FFmpeg log: %1").arg(QString::vasprintf(fmt, vl));
91 if (message.endsWith(u"\n"))
92 message.removeLast();
93
94 if (level == AV_LOG_DEBUG || level == AV_LOG_TRACE)
95 qDebug() << message;
96 else if (level == AV_LOG_VERBOSE || level == AV_LOG_INFO)
97 qInfo() << message;
98 else if (level == AV_LOG_WARNING)
99 qWarning() << message;
100 else if (level == AV_LOG_ERROR || level == AV_LOG_FATAL || level == AV_LOG_PANIC)
101 qCritical() << message;
102}
103
104static void setupFFmpegLogger()
105{
106 if (qEnvironmentVariableIsSet("QT_FFMPEG_DEBUG")) {
107 av_log_set_level(AV_LOG_DEBUG);
109 }
110
111 av_log_set_callback(&qffmpegLogCallback);
112}
113
115{
116 if (backend == u"grabwindow")
117 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
118
119#if QT_CONFIG(eglfs)
120 if (backend == u"eglfs")
121 return new QEglfsScreenCapture;
122#endif
123
124#if QT_CONFIG(xlib)
125 if (backend == u"x11")
126 return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
127#elif defined(Q_OS_WINDOWS)
128 if (backend == u"dxgi")
129 return new QFFmpegScreenCaptureDxgi;
130#elif defined(Q_OS_MACOS)
131 if (backend == u"avf")
132 return QFFmpeg::makeQAvfScreenCapture().release();
133#endif
134 return nullptr;
135}
136
138{
139 if (backend == u"grabwindow")
140 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
141
142#if QT_CONFIG(xlib)
143 if (backend == u"x11")
144 return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
145#elif defined(Q_OS_WINDOWS)
146 if (backend == u"gdi")
147 return new QGdiWindowCapture;
148#if QT_CONFIG(cpp_winrt)
149 if (backend == u"uwp")
150 return new QFFmpegWindowCaptureUwp;
151#endif
152#elif defined(Q_OS_MACOS)
153 if (backend == u"cg")
154 return QFFmpeg::makeQCgWindowCapture().release();
155#endif
156 return nullptr;
157}
158
159QFFmpegMediaIntegration::QFFmpegMediaIntegration()
160 : QPlatformMediaIntegration(QLatin1String("ffmpeg"))
161{
162 setupFFmpegLogger();
163
164 qCInfo(qLcFFmpeg) << "Using Qt multimedia with FFmpeg version" << av_version_info()
165 << avutil_license();
166
167 qCDebug(qLcFFmpeg) << "Available HW decoding frameworks:";
168 for (auto type : QFFmpeg::HWAccel::decodingDeviceTypes())
169 qCDebug(qLcFFmpeg) << " " << av_hwdevice_get_type_name(type);
170
171 qCDebug(qLcFFmpeg) << "Available HW encoding frameworks:";
172 for (auto type : QFFmpeg::HWAccel::encodingDeviceTypes())
173 qCDebug(qLcFFmpeg) << " " << av_hwdevice_get_type_name(type);
174}
175
176q23::expected<QPlatformAudioDecoder *, QString>
177QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
178{
179 return new QFFmpegAudioDecoder(decoder);
180}
181
182q23::expected<std::unique_ptr<QPlatformAudioResampler>, QString>
183QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat,
184 const QAudioFormat &outputFormat)
185{
186 auto ffmpegResampler = QFFmpegResampler::createFromInputFormat(inputFormat, outputFormat);
187 if (ffmpegResampler)
188 return ffmpegResampler;
189
190#ifdef Q_OS_WINDOWS
191 auto windowsResampler = std::make_unique<QWindowsResampler>();
192 if (windowsResampler->setup(inputFormat, outputFormat))
193 return windowsResampler;
194#endif
195
196 return q23::unexpected{ notAvailable };
197}
198
199q23::expected<QPlatformMediaCaptureSession *, QString>
200QFFmpegMediaIntegration::createCaptureSession()
201{
202 return new QFFmpegMediaCaptureSession();
203}
204
205q23::expected<QPlatformMediaPlayer *, QString>
206QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
207{
208 return new QFFmpegMediaPlayer(player);
209}
210
211q23::expected<QPlatformCamera *, QString> QFFmpegMediaIntegration::createCamera(QCamera *camera)
212{
213 Q_ASSERT(camera);
214#ifdef Q_OS_DARWIN
215 return QFFmpeg::makeQAvfCamera(*camera).release();
216#elif defined(Q_OS_ANDROID)
217 return new QFFmpeg::QAndroidCamera(camera);
218#elif QT_CONFIG(linux_v4l)
219 return new QV4L2Camera(camera);
220#elif defined(Q_OS_WINDOWS)
221 return new QFFmpeg::QWindowsCamera(camera);
222#else
223 Q_UNUSED(camera);
224 return q23::unexpected{ notAvailable };
225#endif
226}
227
228QPlatformSurfaceCapture *QFFmpegMediaIntegration::createScreenCapture(QScreenCapture *)
229{
230 static const QString screenCaptureBackend =
231 QString::fromLocal8Bit(qgetenv("QT_SCREEN_CAPTURE_BACKEND")).toLower();
232
233 if (!screenCaptureBackend.isEmpty()) {
234 if (auto screenCapture = createScreenCaptureByBackend(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_screencapture)
246 if (QtPipeWire::QPipeWireCapture::isSupported())
247 return new QtPipeWire::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 QFFmpeg::makeQAvfScreenCapture().release();
259#elif defined(Q_OS_ANDROID)
260 return new QAndroidScreenCapture;
261#else
262 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
263#endif
264}
265
266QPlatformSurfaceCapture *QFFmpegMediaIntegration::createWindowCapture(QWindowCapture *)
267{
268 static const QString windowCaptureBackend =
269 QString::fromLocal8Bit(qgetenv("QT_WINDOW_CAPTURE_BACKEND")).toLower();
270
271 if (!windowCaptureBackend.isEmpty()) {
272 if (auto windowCapture = createWindowCaptureByBackend(windowCaptureBackend))
273 return windowCapture;
274
275 qWarning() << "Not supported QT_WINDOW_CAPTURE_BACKEND:" << windowCaptureBackend;
276 }
277
278#if QT_CONFIG(xlib)
279 if (QX11SurfaceCapture::isSupported())
280 return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
281#endif
282
283#if defined(Q_OS_WINDOWS)
284# if QT_CONFIG(cpp_winrt)
285 if (QFFmpegWindowCaptureUwp::isSupported())
286 return new QFFmpegWindowCaptureUwp;
287# endif
288
289 return new QGdiWindowCapture;
290#elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well
291 return QFFmpeg::makeQCgWindowCapture().release();
292#else
293 return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
294#endif
295}
296
297q23::expected<QPlatformMediaRecorder *, QString>
298QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder)
299{
300 return new QFFmpegMediaRecorder(recorder);
301}
302
303q23::expected<QPlatformImageCapture *, QString>
304QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
305{
306 Q_ASSERT(imageCapture);
307#if defined(Q_OS_ANDROID)
308 return new QFFmpeg::QAndroidImageCapture(imageCapture);
309#elif defined(Q_OS_DARWIN)
310 return QFFmpeg::makeQAvfImageCapture(*imageCapture).release();
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(srcFrame, 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 makeQAvfVideoDevices(
347 *this,
348 &QFFmpeg::isCVFormatSupported).release();
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 QFFmpeg::makeQCgCapturableWindows().release();
363#elif defined(Q_OS_WINDOWS)
364 return new QWinCapturableWindows;
365#endif
366 return nullptr;
367}
368
369QT_END_NAMESPACE
370
371#ifdef Q_OS_ANDROID
372
373extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
374{
375 static bool initialized = false;
376 if (initialized)
377 return JNI_VERSION_1_6;
378 initialized = true;
379
380 QT_USE_NAMESPACE
381 void *environment;
382 if (vm->GetEnv(&environment, JNI_VERSION_1_6))
383 return JNI_ERR;
384
385 // setting our javavm into ffmpeg.
386 if (av_jni_set_java_vm(vm, nullptr))
387 return JNI_ERR;
388
389 if (!QFFmpeg::QAndroidCamera::registerNativeMethods()
390 ||!QAndroidScreenCapture::registerNativeMethods()) {
391 return JNI_ERR;
392 }
393
394 return JNI_VERSION_1_6;
395}
396#endif
static QPlatformSurfaceCapture * createScreenCaptureByBackend(QString backend)
static QPlatformSurfaceCapture * createWindowCaptureByBackend(QString backend)
static bool UseCustomFFmpegLogger
static void qffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl)
static void setupFFmpegLogger()
QT_BEGIN_NAMESPACE bool FFmpegLogsEnabledInThread
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)