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" |
43 | # include "qandroidscreencapture_p.h" |
44 | extern "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 | |
74 | QT_BEGIN_NAMESPACE |
75 | |
76 | static Q_LOGGING_CATEGORY(qLcFFmpeg, "qt.multimedia.ffmpeg" ); |
77 | |
78 | bool thread_local FFmpegLogsEnabledInThread = true; |
79 | static bool UseCustomFFmpegLogger = false; |
80 | |
81 | static 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 | |
107 | static 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 | |
117 | static 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 | |
140 | static 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 | |
162 | QFFmpegMediaIntegration::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 | |
179 | QMaybe<QPlatformAudioDecoder *> QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder) |
180 | { |
181 | return new QFFmpegAudioDecoder(decoder); |
182 | } |
183 | |
184 | QMaybe<std::unique_ptr<QPlatformAudioResampler>> |
185 | QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat, |
186 | const QAudioFormat &outputFormat) |
187 | { |
188 | return { std::make_unique<QFFmpegResampler>(args: inputFormat, args: outputFormat) }; |
189 | } |
190 | |
191 | QMaybe<QPlatformMediaCaptureSession *> QFFmpegMediaIntegration::createCaptureSession() |
192 | { |
193 | return new QFFmpegMediaCaptureSession(); |
194 | } |
195 | |
196 | QMaybe<QPlatformMediaPlayer *> QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player) |
197 | { |
198 | return new QFFmpegMediaPlayer(player); |
199 | } |
200 | |
201 | QMaybe<QPlatformCamera *> QFFmpegMediaIntegration::createCamera(QCamera *camera) |
202 | { |
203 | #ifdef Q_OS_DARWIN |
204 | return new QAVFCamera(camera); |
205 | #elif defined(Q_OS_ANDROID) |
206 | return new QAndroidCamera(camera); |
207 | #elif QT_CONFIG(linux_v4l) |
208 | return new QV4L2Camera(camera); |
209 | #elif defined(Q_OS_WINDOWS) |
210 | return new QWindowsCamera(camera); |
211 | #else |
212 | Q_UNUSED(camera); |
213 | return nullptr;//new QFFmpegCamera(camera); |
214 | #endif |
215 | } |
216 | |
217 | QPlatformSurfaceCapture *QFFmpegMediaIntegration::createScreenCapture(QScreenCapture *) |
218 | { |
219 | static const QString screenCaptureBackend = |
220 | QString::fromLocal8Bit(ba: qgetenv(varName: "QT_SCREEN_CAPTURE_BACKEND" )).toLower(); |
221 | |
222 | if (!screenCaptureBackend.isEmpty()) { |
223 | if (auto screenCapture = createScreenCaptureByBackend(backend: screenCaptureBackend)) |
224 | return screenCapture; |
225 | |
226 | qWarning() << "Not supported QT_SCREEN_CAPTURE_BACKEND:" << screenCaptureBackend; |
227 | } |
228 | |
229 | #if QT_CONFIG(xlib) |
230 | if (QX11SurfaceCapture::isSupported()) |
231 | return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{}); |
232 | #endif |
233 | |
234 | #if QT_CONFIG(pipewire_screencapture) |
235 | if (QtPipeWire::QPipeWireCapture::isSupported()) |
236 | return new QtPipeWire::QPipeWireCapture(QPlatformSurfaceCapture::ScreenSource{}); |
237 | #endif |
238 | |
239 | #if QT_CONFIG(eglfs) |
240 | if (QEglfsScreenCapture::isSupported()) |
241 | return new QEglfsScreenCapture; |
242 | #endif |
243 | |
244 | #if defined(Q_OS_WINDOWS) |
245 | return new QFFmpegScreenCaptureDxgi; |
246 | #elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well |
247 | return new QAVFScreenCapture; |
248 | #elif defined(Q_OS_ANDROID) |
249 | return new QAndroidScreenCapture; |
250 | #else |
251 | return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{}); |
252 | #endif |
253 | } |
254 | |
255 | QPlatformSurfaceCapture *QFFmpegMediaIntegration::createWindowCapture(QWindowCapture *) |
256 | { |
257 | static const QString windowCaptureBackend = |
258 | QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WINDOW_CAPTURE_BACKEND" )).toLower(); |
259 | |
260 | if (!windowCaptureBackend.isEmpty()) { |
261 | if (auto windowCapture = createWindowCaptureByBackend(backend: windowCaptureBackend)) |
262 | return windowCapture; |
263 | |
264 | qWarning() << "Not supported QT_WINDOW_CAPTURE_BACKEND:" << windowCaptureBackend; |
265 | } |
266 | |
267 | #if QT_CONFIG(xlib) |
268 | if (QX11SurfaceCapture::isSupported()) |
269 | return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{}); |
270 | #endif |
271 | |
272 | #if defined(Q_OS_WINDOWS) |
273 | # if QT_CONFIG(cpp_winrt) |
274 | if (QFFmpegWindowCaptureUwp::isSupported()) |
275 | return new QFFmpegWindowCaptureUwp; |
276 | # endif |
277 | |
278 | return new QGdiWindowCapture; |
279 | #elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well |
280 | return new QCGWindowCapture; |
281 | #else |
282 | return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{}); |
283 | #endif |
284 | } |
285 | |
286 | QMaybe<QPlatformMediaRecorder *> QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder) |
287 | { |
288 | return new QFFmpegMediaRecorder(recorder); |
289 | } |
290 | |
291 | QMaybe<QPlatformImageCapture *> QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture) |
292 | { |
293 | #if defined(Q_OS_ANDROID) |
294 | return new QAndroidImageCapture(imageCapture); |
295 | #else |
296 | return new QFFmpegImageCapture(imageCapture); |
297 | #endif |
298 | } |
299 | |
300 | QMaybe<QPlatformVideoSink *> QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink) |
301 | { |
302 | return new QFFmpegVideoSink(sink); |
303 | } |
304 | |
305 | QMaybe<QPlatformAudioInput *> QFFmpegMediaIntegration::createAudioInput(QAudioInput *input) |
306 | { |
307 | return new QFFmpegAudioInput(input); |
308 | } |
309 | |
310 | QVideoFrame QFFmpegMediaIntegration::convertVideoFrame(QVideoFrame &srcFrame, |
311 | const QVideoFrameFormat &destFormat) |
312 | { |
313 | return convertFrame(src&: srcFrame, dstFormat: destFormat); |
314 | } |
315 | |
316 | QPlatformMediaFormatInfo *QFFmpegMediaIntegration::createFormatInfo() |
317 | { |
318 | return new QFFmpegMediaFormatInfo; |
319 | } |
320 | |
321 | QPlatformVideoDevices *QFFmpegMediaIntegration::createVideoDevices() |
322 | { |
323 | #if defined(Q_OS_ANDROID) |
324 | return new QAndroidVideoDevices(this); |
325 | #elif QT_CONFIG(linux_v4l) |
326 | return new QV4L2CameraDevices(this); |
327 | #elif defined Q_OS_DARWIN |
328 | return new QAVFVideoDevices( |
329 | this, |
330 | &QFFmpeg::isCVFormatSupported); |
331 | #elif defined(Q_OS_WINDOWS) |
332 | return new QWindowsVideoDevices(this); |
333 | #else |
334 | return nullptr; |
335 | #endif |
336 | } |
337 | |
338 | QPlatformCapturableWindows *QFFmpegMediaIntegration::createCapturableWindows() |
339 | { |
340 | #if QT_CONFIG(xlib) |
341 | if (QX11SurfaceCapture::isSupported()) |
342 | return new QX11CapturableWindows; |
343 | #elif defined Q_OS_MACOS |
344 | return new QCGCapturableWindows; |
345 | #elif defined(Q_OS_WINDOWS) |
346 | return new QWinCapturableWindows; |
347 | #endif |
348 | return nullptr; |
349 | } |
350 | |
351 | #ifdef Q_OS_ANDROID |
352 | |
353 | Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) |
354 | { |
355 | static bool initialized = false; |
356 | if (initialized) |
357 | return JNI_VERSION_1_6; |
358 | initialized = true; |
359 | |
360 | QT_USE_NAMESPACE |
361 | void *environment; |
362 | if (vm->GetEnv(&environment, JNI_VERSION_1_6)) |
363 | return JNI_ERR; |
364 | |
365 | // setting our javavm into ffmpeg. |
366 | if (av_jni_set_java_vm(vm, nullptr)) |
367 | return JNI_ERR; |
368 | |
369 | if (!QAndroidCamera::registerNativeMethods() |
370 | ||!QAndroidScreenCapture::registerNativeMethods()) { |
371 | return JNI_ERR; |
372 | } |
373 | |
374 | return JNI_VERSION_1_6; |
375 | } |
376 | #endif |
377 | |
378 | QT_END_NAMESPACE |
379 | |