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 | extern "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 | |
71 | QT_BEGIN_NAMESPACE |
72 | |
73 | class QFFmpegMediaPlugin : public QPlatformMediaPlugin |
74 | { |
75 | Q_OBJECT |
76 | Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "ffmpeg.json" ) |
77 | |
78 | public: |
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 | |
91 | bool thread_local FFmpegLogsEnabledInThread = true; |
92 | static bool UseCustomFFmpegLogger = false; |
93 | |
94 | static 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 | |
120 | static 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 | |
130 | static 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 | |
153 | static 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 | |
175 | QFFmpegMediaIntegration::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 | |
191 | QMaybe<QPlatformAudioDecoder *> QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder) |
192 | { |
193 | return new QFFmpegAudioDecoder(decoder); |
194 | } |
195 | |
196 | QMaybe<std::unique_ptr<QPlatformAudioResampler>> |
197 | QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat, |
198 | const QAudioFormat &outputFormat) |
199 | { |
200 | return { std::make_unique<QFFmpegResampler>(args: inputFormat, args: outputFormat) }; |
201 | } |
202 | |
203 | QMaybe<QPlatformMediaCaptureSession *> QFFmpegMediaIntegration::createCaptureSession() |
204 | { |
205 | return new QFFmpegMediaCaptureSession(); |
206 | } |
207 | |
208 | QMaybe<QPlatformMediaPlayer *> QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player) |
209 | { |
210 | return new QFFmpegMediaPlayer(player); |
211 | } |
212 | |
213 | QMaybe<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 | |
229 | QPlatformSurfaceCapture *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 | |
264 | QPlatformSurfaceCapture *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 | |
294 | QMaybe<QPlatformMediaRecorder *> QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder) |
295 | { |
296 | return new QFFmpegMediaRecorder(recorder); |
297 | } |
298 | |
299 | QMaybe<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 | |
308 | QMaybe<QPlatformVideoSink *> QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink) |
309 | { |
310 | return new QFFmpegVideoSink(sink); |
311 | } |
312 | |
313 | QMaybe<QPlatformAudioInput *> QFFmpegMediaIntegration::createAudioInput(QAudioInput *input) |
314 | { |
315 | return new QFFmpegAudioInput(input); |
316 | } |
317 | |
318 | QVideoFrame QFFmpegMediaIntegration::convertVideoFrame(QVideoFrame &srcFrame, |
319 | const QVideoFrameFormat &destFormat) |
320 | { |
321 | return convertFrame(src&: srcFrame, dstFormat: destFormat); |
322 | } |
323 | |
324 | QPlatformMediaFormatInfo *QFFmpegMediaIntegration::createFormatInfo() |
325 | { |
326 | return new QFFmpegMediaFormatInfo; |
327 | } |
328 | |
329 | QPlatformVideoDevices *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 | |
344 | QPlatformCapturableWindows *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 | |
359 | Q_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 | |
382 | QT_END_NAMESPACE |
383 | |
384 | #include "qffmpegmediaintegration.moc" |
385 | |