mirror of
https://github.com/feather-wallet/feather.git
synced 2024-10-31 01:18:01 +00:00
9203 lines
287 KiB
Diff
9203 lines
287 KiB
Diff
From f88adf2a411e79c8f1fe73806cbf591a12f055c0 Mon Sep 17 00:00:00 2001
|
|
From: tobtoht <tob@featherwallet.org>
|
|
Date: Tue, 27 Dec 2022 12:00:44 +0100
|
|
Subject: [PATCH] qtmultimedia fixes
|
|
|
|
---
|
|
cmake/FindWMF.cmake | 10 +-
|
|
src/multimedia/CMakeLists.txt | 59 -
|
|
src/multimedia/configure.cmake | 3 +-
|
|
src/multimedia/video/qvideoframeformat.cpp | 5 -
|
|
.../windows/qwindowsmediadevices.cpp | 2 +-
|
|
src/multimedia/windows/qwindowsresampler.cpp | 2 +-
|
|
.../darwin/camera/qavfcamerabase.mm | 1 +
|
|
.../multimedia/darwin/qdarwinintegration.mm | 3 +-
|
|
src/plugins/multimedia/ffmpeg/CMakeLists.txt | 87 +-
|
|
src/plugins/multimedia/ffmpeg/qavfcamera.mm | 422 ------
|
|
src/plugins/multimedia/ffmpeg/qavfcamera_p.h | 87 --
|
|
src/plugins/multimedia/ffmpeg/qffmpeg_p.h | 60 -
|
|
.../multimedia/ffmpeg/qffmpegaudiodecoder.cpp | 265 ----
|
|
.../multimedia/ffmpeg/qffmpegaudiodecoder_p.h | 69 -
|
|
.../multimedia/ffmpeg/qffmpegaudioinput.cpp | 189 ---
|
|
.../multimedia/ffmpeg/qffmpegaudioinput_p.h | 54 -
|
|
.../multimedia/ffmpeg/qffmpegclock.cpp | 193 ---
|
|
.../multimedia/ffmpeg/qffmpegclock_p.h | 113 --
|
|
.../multimedia/ffmpeg/qffmpegdecoder.cpp | 1272 -----------------
|
|
.../multimedia/ffmpeg/qffmpegdecoder_p.h | 501 -------
|
|
.../multimedia/ffmpeg/qffmpegencoder.cpp | 557 --------
|
|
.../multimedia/ffmpeg/qffmpegencoder_p.h | 197 ---
|
|
.../ffmpeg/qffmpegencoderoptions.cpp | 272 ----
|
|
.../ffmpeg/qffmpegencoderoptions_p.h | 32 -
|
|
.../multimedia/ffmpeg/qffmpeghwaccel.cpp | 372 -----
|
|
.../ffmpeg/qffmpeghwaccel_d3d11.cpp | 158 --
|
|
.../ffmpeg/qffmpeghwaccel_d3d11_p.h | 43 -
|
|
.../ffmpeg/qffmpeghwaccel_mediacodec.cpp | 70 -
|
|
.../ffmpeg/qffmpeghwaccel_mediacodec_p.h | 35 -
|
|
.../multimedia/ffmpeg/qffmpeghwaccel_p.h | 121 --
|
|
.../ffmpeg/qffmpeghwaccel_vaapi.cpp | 346 -----
|
|
.../ffmpeg/qffmpeghwaccel_vaapi_p.h | 48 -
|
|
.../ffmpeg/qffmpeghwaccel_videotoolbox.mm | 281 ----
|
|
.../ffmpeg/qffmpeghwaccel_videotoolbox_p.h | 63 -
|
|
.../ffmpeg/qffmpegmediacapturesession.cpp | 15 +-
|
|
.../ffmpeg/qffmpegmediacapturesession_p.h | 1 -
|
|
.../ffmpeg/qffmpegmediaformatinfo.cpp | 474 ------
|
|
.../ffmpeg/qffmpegmediaformatinfo_p.h | 18 -
|
|
.../ffmpeg/qffmpegmediaintegration.cpp | 31 -
|
|
.../ffmpeg/qffmpegmediaintegration_p.h | 8 -
|
|
.../ffmpeg/qffmpegmediametadata.cpp | 105 --
|
|
.../ffmpeg/qffmpegmediametadata_p.h | 5 -
|
|
.../multimedia/ffmpeg/qffmpegmediaplayer.cpp | 236 ---
|
|
.../multimedia/ffmpeg/qffmpegmediaplayer_p.h | 98 --
|
|
.../ffmpeg/qffmpegmediarecorder.cpp | 157 --
|
|
.../ffmpeg/qffmpegmediarecorder_p.h | 68 -
|
|
.../multimedia/ffmpeg/qffmpegresampler.cpp | 95 --
|
|
.../multimedia/ffmpeg/qffmpegresampler_p.h | 46 -
|
|
.../multimedia/ffmpeg/qffmpegthread.cpp | 57 -
|
|
.../multimedia/ffmpeg/qffmpegthread_p.h | 68 -
|
|
.../multimedia/ffmpeg/qffmpegvideobuffer.cpp | 356 -----
|
|
.../multimedia/ffmpeg/qffmpegvideobuffer_p.h | 72 -
|
|
.../ffmpeg/qffmpegvideoframeencoder.cpp | 374 -----
|
|
.../ffmpeg/qffmpegvideoframeencoder_p.h | 76 -
|
|
.../multimedia/ffmpeg/qffmpegvideosink.cpp | 14 -
|
|
.../multimedia/ffmpeg/qffmpegvideosink_p.h | 10 +-
|
|
src/plugins/multimedia/ffmpeg/qv4l2camera.cpp | 4 +-
|
|
.../multimedia/ffmpeg/qwindowscamera.cpp | 4 +-
|
|
.../multimedia/windows/common/mfmetadata_p.h | 2 +-
|
|
.../windows/decoder/mfaudiodecodercontrol.cpp | 2 +-
|
|
.../qwindowsmediadevicereader_p.h | 4 +-
|
|
.../mediacapture/qwindowsmediaencoder.cpp | 2 +-
|
|
.../windows/player/mfplayercontrol_p.h | 2 +-
|
|
.../windows/player/mfplayersession.cpp | 2 +-
|
|
.../multimedia/windows/player/mftvideo.cpp | 2 +-
|
|
.../windows/qwindowsvideodevices.cpp | 4 +-
|
|
66 files changed, 33 insertions(+), 8371 deletions(-)
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qavfcamera.mm
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qavfcamera_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeg_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegclock.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegclock_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp
|
|
delete mode 100644 src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h
|
|
|
|
diff --git a/cmake/FindWMF.cmake b/cmake/FindWMF.cmake
|
|
index 7c6923c1e..b69274be5 100644
|
|
--- a/cmake/FindWMF.cmake
|
|
+++ b/cmake/FindWMF.cmake
|
|
@@ -22,11 +22,11 @@ find_library(WMF_UUID_LIBRARY uuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
find_library(WMF_MSDMO_LIBRARY msdmo HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
find_library(WMF_OLE32_LIBRARY ole32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
find_library(WMF_OLEAUT32_LIBRARY oleaut32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
-find_library(WMF_MF_LIBRARY Mf HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
-find_library(WMF_MFUUID_LIBRARY Mfuuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
-find_library(WMF_MFPLAT_LIBRARY Mfplat HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
-find_library(WMF_MFCORE_LIBRARY Mfcore HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
-find_library(WMF_PROPSYS_LIBRARY Propsys HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
+find_library(WMF_MF_LIBRARY mf HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
+find_library(WMF_MFUUID_LIBRARY mfuuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
+find_library(WMF_MFPLAT_LIBRARY mfplat HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
+find_library(WMF_MFCORE_LIBRARY mfcore HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
+find_library(WMF_PROPSYS_LIBRARY propsys HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
|
|
|
|
|
set(WMF_LIBRARIES ${WMF_STRMIIDS_LIBRARY} ${WMF_AMSTRMID_LIBRARY} ${WMF_DMOGUIDS_LIBRARY} ${WMF_UUID_LIBRARY}
|
|
diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt
|
|
index cc0a86958..440449918 100644
|
|
--- a/src/multimedia/CMakeLists.txt
|
|
+++ b/src/multimedia/CMakeLists.txt
|
|
@@ -248,64 +248,5 @@ set(VIDEO_SHADERS
|
|
"shaders/nv12_bt2020_hlg.frag"
|
|
)
|
|
|
|
-qt_internal_add_shaders(Multimedia "shaders"
|
|
- SILENT
|
|
- BATCHABLE
|
|
- PRECOMPILE
|
|
- OPTIMIZED
|
|
- PREFIX
|
|
- "/qt-project.org/multimedia"
|
|
- FILES
|
|
- ${VIDEO_VERTEX_SHADERS}
|
|
- ${VIDEO_SHADERS}
|
|
-)
|
|
-
|
|
string(REPLACE ".frag" "_linear.frag.qsb" LINEAR_VIDEO_SHADERS "${VIDEO_SHADERS}")
|
|
|
|
-qt_internal_add_shaders(Multimedia "shaders_linear"
|
|
- SILENT
|
|
- BATCHABLE
|
|
- PRECOMPILE
|
|
- OPTIMIZED
|
|
- PREFIX
|
|
- "/qt-project.org/multimedia"
|
|
- FILES
|
|
- ${VIDEO_SHADERS}
|
|
- OUTPUTS
|
|
- ${LINEAR_VIDEO_SHADERS}
|
|
- DEFINES
|
|
- QMM_OUTPUTSURFACE_LINEAR
|
|
-)
|
|
-
|
|
-qt_internal_add_shaders(Multimedia "shaders_gl_macos"
|
|
- SILENT
|
|
- BATCHABLE
|
|
- PRECOMPILE
|
|
- OPTIMIZED
|
|
- PREFIX
|
|
- "/qt-project.org/multimedia"
|
|
- GLSL
|
|
- "120,150"
|
|
- NOHLSL
|
|
- NOMSL
|
|
- FILES
|
|
- "shaders/rectsampler.vert"
|
|
- "shaders/rectsampler_bgra.frag"
|
|
-)
|
|
-
|
|
-qt_internal_add_shaders(Multimedia "shaders_gl_macos_linear"
|
|
- SILENT
|
|
- BATCHABLE
|
|
- PRECOMPILE
|
|
- OPTIMIZED
|
|
- PREFIX
|
|
- "/qt-project.org/multimedia"
|
|
- GLSL
|
|
- "120,150"
|
|
- NOHLSL
|
|
- NOMSL
|
|
- FILES
|
|
- "shaders/rectsampler_bgra.frag"
|
|
- OUTPUTS
|
|
- "shaders/rectsampler_bgra_linear.frag.qsb"
|
|
-)
|
|
diff --git a/src/multimedia/configure.cmake b/src/multimedia/configure.cmake
|
|
index efcadfc5c..29b056003 100644
|
|
--- a/src/multimedia/configure.cmake
|
|
+++ b/src/multimedia/configure.cmake
|
|
@@ -21,7 +21,6 @@ qt_find_package(WrapPulseAudio PROVIDED_TARGETS WrapPulseAudio::WrapPulseAudio M
|
|
qt_find_package(WMF PROVIDED_TARGETS WMF::WMF MODULE_NAME multimedia QMAKE_LIB wmf)
|
|
qt_find_package(EGL)
|
|
|
|
-qt_find_package(FFmpeg OPTIONAL_COMPONENTS AVCODEC AVFORMAT AVUTIL AVDEVICE SWRESAMPLE SWSCALE PROVIDED_TARGETS FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil FFmpeg::avdevice FFmpeg::swresample FFmpeg::swscale MODULE_NAME multimedia QMAKE_LIB ffmpeg)
|
|
qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VA VAAPI::DRM MODULE_NAME multimedia QMAKE_LIB vaapi)
|
|
|
|
#### Tests
|
|
@@ -73,7 +72,7 @@ qt_feature("ffmpeg" PRIVATE
|
|
LABEL "FFmpeg"
|
|
ENABLE INPUT_ffmpeg STREQUAL 'yes'
|
|
DISABLE INPUT_ffmpeg STREQUAL 'no'
|
|
- CONDITION FFmpeg_FOUND AND (APPLE OR WIN32 OR ANDROID OR QNX OR QT_FEATURE_pulseaudio)
|
|
+ CONDITION UNIX OR WIN32
|
|
)
|
|
qt_feature("alsa" PUBLIC PRIVATE
|
|
LABEL "ALSA (experimental)"
|
|
diff --git a/src/multimedia/video/qvideoframeformat.cpp b/src/multimedia/video/qvideoframeformat.cpp
|
|
index 22ccc1ac5..9c90fa8b8 100644
|
|
--- a/src/multimedia/video/qvideoframeformat.cpp
|
|
+++ b/src/multimedia/video/qvideoframeformat.cpp
|
|
@@ -11,10 +11,6 @@
|
|
#include <qvariant.h>
|
|
#include <qmatrix4x4.h>
|
|
|
|
-static void initResource() {
|
|
- Q_INIT_RESOURCE(shaders);
|
|
-}
|
|
-
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
class QVideoFrameFormatPrivate : public QSharedData
|
|
@@ -348,7 +344,6 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFrameFormatPrivate);
|
|
QVideoFrameFormat::QVideoFrameFormat()
|
|
: d(new QVideoFrameFormatPrivate)
|
|
{
|
|
- initResource();
|
|
}
|
|
|
|
/*!
|
|
diff --git a/src/multimedia/windows/qwindowsmediadevices.cpp b/src/multimedia/windows/qwindowsmediadevices.cpp
|
|
index c91597102..8c2df5816 100644
|
|
--- a/src/multimedia/windows/qwindowsmediadevices.cpp
|
|
+++ b/src/multimedia/windows/qwindowsmediadevices.cpp
|
|
@@ -13,7 +13,7 @@
|
|
#include <mmddk.h>
|
|
#include <mfobjects.h>
|
|
#include <mfidl.h>
|
|
-#include <Mferror.h>
|
|
+#include <mferror.h>
|
|
#include <mmdeviceapi.h>
|
|
#include <qwindowsmfdefs_p.h>
|
|
|
|
diff --git a/src/multimedia/windows/qwindowsresampler.cpp b/src/multimedia/windows/qwindowsresampler.cpp
|
|
index 9e76c9159..f78628c91 100644
|
|
--- a/src/multimedia/windows/qwindowsresampler.cpp
|
|
+++ b/src/multimedia/windows/qwindowsresampler.cpp
|
|
@@ -7,7 +7,7 @@
|
|
#include <qloggingcategory.h>
|
|
#include <QUuid>
|
|
|
|
-#include <Wmcodecdsp.h>
|
|
+#include <wmcodecdsp.h>
|
|
#include <mftransform.h>
|
|
#include <mferror.h>
|
|
|
|
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
|
|
index a11290a8d..b40c1133e 100644
|
|
--- a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
|
|
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
|
|
@@ -7,6 +7,7 @@
|
|
#include <private/qcameradevice_p.h>
|
|
#include "qavfhelpers_p.h"
|
|
#include <private/qplatformmediaintegration_p.h>
|
|
+#include <QtCore/qset.h>
|
|
|
|
QT_USE_NAMESPACE
|
|
|
|
diff --git a/src/plugins/multimedia/darwin/qdarwinintegration.mm b/src/plugins/multimedia/darwin/qdarwinintegration.mm
|
|
index 5e26fe5c4..3e82655b0 100644
|
|
--- a/src/plugins/multimedia/darwin/qdarwinintegration.mm
|
|
+++ b/src/plugins/multimedia/darwin/qdarwinintegration.mm
|
|
@@ -39,8 +39,7 @@ public:
|
|
QDarwinIntegration::QDarwinIntegration()
|
|
{
|
|
#if defined(Q_OS_MACOS) && QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_11_0)
|
|
- if (__builtin_available(macOS 11.0, *))
|
|
- VTRegisterSupplementalVideoDecoderIfAvailable(kCMVideoCodecType_VP9);
|
|
+ VTRegisterSupplementalVideoDecoderIfAvailable(kCMVideoCodecType_VP9);
|
|
#endif
|
|
m_videoDevices = new QAVFVideoDevices(this);
|
|
}
|
|
diff --git a/src/plugins/multimedia/ffmpeg/CMakeLists.txt b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
|
|
index 5d6c0a8c3..6c83b9cb2 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/CMakeLists.txt
|
|
+++ b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
|
|
@@ -1,107 +1,32 @@
|
|
-qt_find_package(EGL)
|
|
-qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VA VAAPI::DRM MODULE_NAME multimedia QMAKE_LIB vaapi)
|
|
-
|
|
-qt_internal_find_apple_system_framework(FWCoreMedia CoreMedia) # special case
|
|
-qt_internal_find_apple_system_framework(FWCoreAudio CoreAudio) # special case
|
|
-qt_internal_find_apple_system_framework(FWAudioUnit AudioUnit) # special case
|
|
-qt_internal_find_apple_system_framework(FWVideoToolbox VideoToolbox) # special case
|
|
-qt_internal_find_apple_system_framework(FWAVFoundation AVFoundation) # special case
|
|
-
|
|
qt_internal_add_plugin(QFFmpegMediaPlugin
|
|
OUTPUT_NAME ffmpegmediaplugin
|
|
PLUGIN_TYPE multimedia
|
|
SOURCES
|
|
- qffmpeg_p.h
|
|
- qffmpegaudiodecoder.cpp qffmpegaudiodecoder_p.h
|
|
- qffmpegaudioinput.cpp qffmpegaudioinput_p.h
|
|
- qffmpegclock.cpp qffmpegclock_p.h
|
|
- qffmpegdecoder.cpp qffmpegdecoder_p.h
|
|
- qffmpeghwaccel.cpp qffmpeghwaccel_p.h
|
|
- qffmpegencoderoptions.cpp qffmpegencoderoptions_p.h
|
|
qffmpegmediametadata.cpp qffmpegmediametadata_p.h
|
|
- qffmpegmediaplayer.cpp qffmpegmediaplayer_p.h
|
|
qffmpegvideosink.cpp qffmpegvideosink_p.h
|
|
qffmpegmediaformatinfo.cpp qffmpegmediaformatinfo_p.h
|
|
qffmpegmediaintegration.cpp qffmpegmediaintegration_p.h
|
|
- qffmpegvideobuffer.cpp qffmpegvideobuffer_p.h
|
|
qffmpegimagecapture.cpp qffmpegimagecapture_p.h
|
|
qffmpegmediacapturesession.cpp qffmpegmediacapturesession_p.h
|
|
- qffmpegmediarecorder.cpp qffmpegmediarecorder_p.h
|
|
- qffmpegencoder.cpp qffmpegencoder_p.h
|
|
- qffmpegthread.cpp qffmpegthread_p.h
|
|
- qffmpegresampler.cpp qffmpegresampler_p.h
|
|
- qffmpegvideoframeencoder.cpp qffmpegvideoframeencoder_p.h
|
|
DEFINES
|
|
QT_COMPILING_FFMPEG
|
|
LIBRARIES
|
|
Qt::MultimediaPrivate
|
|
Qt::CorePrivate
|
|
- FFmpeg::avformat FFmpeg::avcodec FFmpeg::swresample FFmpeg::swscale FFmpeg::avutil
|
|
-)
|
|
-
|
|
-qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_ffmpeg AND QT_FEATURE_vaapi
|
|
- SOURCES
|
|
- qffmpeghwaccel_vaapi.cpp qffmpeghwaccel_vaapi_p.h
|
|
- LIBRARIES
|
|
- VAAPI::VAAPI
|
|
- EGL::EGL
|
|
)
|
|
|
|
-qt_internal_extend_target(QFFmpegMediaPlugin CONDITION APPLE
|
|
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_linux_v4l
|
|
SOURCES
|
|
- ../darwin/qavfhelpers.mm ../darwin/qavfhelpers_p.h
|
|
- ../darwin/camera/qavfcamerabase_p.h ../darwin/camera/qavfcamerabase.mm
|
|
- ../darwin/camera/avfcamerautility_p.h ../darwin/camera/avfcamerautility.mm
|
|
- qffmpeghwaccel_videotoolbox.mm qffmpeghwaccel_videotoolbox_p.h
|
|
- qavfcamera.mm qavfcamera_p.h
|
|
- INCLUDE_DIRECTORIES
|
|
- ../darwin
|
|
- ../darwin/camera
|
|
- LIBRARIES
|
|
- ${FWAudioToolbox}
|
|
- ${FWCoreAudio}
|
|
- ${FWCoreFoundation}
|
|
- ${FWCoreMedia}
|
|
- ${FWCoreVideo}
|
|
- ${FWVideoToolbox}
|
|
- AVFoundation::AVFoundation
|
|
+ qv4l2camera.cpp qv4l2camera_p.h
|
|
)
|
|
|
|
qt_internal_extend_target(QFFmpegMediaPlugin CONDITION WIN32
|
|
- SOURCES
|
|
+ SOURCES
|
|
../windows/qwindowsvideodevices.cpp ../windows/qwindowsvideodevices_p.h
|
|
qwindowscamera.cpp qwindowscamera_p.h
|
|
- qffmpeghwaccel_d3d11.cpp qffmpeghwaccel_d3d11_p.h
|
|
- INCLUDE_DIRECTORIES
|
|
+ INCLUDE_DIRECTORIES
|
|
../windows
|
|
- LIBRARIES
|
|
+ LIBRARIES
|
|
WMF::WMF
|
|
mfreadwrite
|
|
-)
|
|
-
|
|
-qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_linux_v4l
|
|
- SOURCES
|
|
- qv4l2camera.cpp qv4l2camera_p.h
|
|
-)
|
|
-
|
|
-if (ANDROID)
|
|
- qt_internal_extend_target(QFFmpegMediaPlugin
|
|
- SOURCES
|
|
- qffmpeghwaccel_mediacodec.cpp qffmpeghwaccel_mediacodec_p.h
|
|
- ../android/wrappers/jni/androidsurfacetexture_p.h
|
|
- ../android/wrappers/jni/androidsurfacetexture.cpp
|
|
- INCLUDE_DIRECTORIES
|
|
- ${FFMPEG_DIR}/include
|
|
- ../android/wrappers/jni/
|
|
- )
|
|
-
|
|
- set_property(TARGET QFFmpegMediaPlugin APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
|
|
- plugins/multimedia/libplugins_multimedia_ffmpegmediaplugin.so
|
|
- )
|
|
-
|
|
- set_property(TARGET QFFmpegMediaPlugin APPEND PROPERTY QT_ANDROID_PERMISSIONS
|
|
- android.permission.CAMERA android.permission.RECORD_AUDIO
|
|
- android.permission.BLUETOOTH
|
|
- android.permission.MODIFY_AUDIO_SETTINGS
|
|
- )
|
|
-endif()
|
|
+)
|
|
\ No newline at end of file
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qavfcamera.mm b/src/plugins/multimedia/ffmpeg/qavfcamera.mm
|
|
deleted file mode 100644
|
|
index 37dd4b262..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qavfcamera.mm
|
|
+++ /dev/null
|
|
@@ -1,422 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include <qavfcamera_p.h>
|
|
-#include <qpointer.h>
|
|
-#include <qmediacapturesession.h>
|
|
-#include <private/qplatformmediacapture_p.h>
|
|
-#include "avfcamerautility_p.h"
|
|
-#include "qavfhelpers_p.h"
|
|
-#include <qvideosink.h>
|
|
-#include <private/qrhi_p.h>
|
|
-#define AVMediaType XAVMediaType
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "qffmpegvideosink_p.h"
|
|
-extern "C" {
|
|
-#include <libavutil/hwcontext_videotoolbox.h>
|
|
-#include <libavutil/hwcontext.h>
|
|
-}
|
|
-#undef AVMediaType
|
|
-
|
|
-
|
|
-
|
|
-#import <AVFoundation/AVFoundation.h>
|
|
-#include <CoreVideo/CoreVideo.h>
|
|
-
|
|
-static void releaseHwFrame(void */*opaque*/, uint8_t *data)
|
|
-{
|
|
- CVPixelBufferRelease(CVPixelBufferRef(data));
|
|
-}
|
|
-
|
|
-// Make sure this is compatible with the layout used in ffmpeg's hwcontext_videotoolbox
|
|
-static AVFrame *allocHWFrame(AVBufferRef *hwContext, const CVPixelBufferRef &pixbuf)
|
|
-{
|
|
- AVHWFramesContext *ctx = (AVHWFramesContext*)hwContext->data;
|
|
- AVFrame *frame = av_frame_alloc();
|
|
- frame->hw_frames_ctx = av_buffer_ref(hwContext);
|
|
- frame->extended_data = frame->data;
|
|
-
|
|
- frame->buf[0] = av_buffer_create((uint8_t *)pixbuf, 1, releaseHwFrame, NULL, 0);
|
|
- frame->data[3] = (uint8_t *)pixbuf;
|
|
- CVPixelBufferRetain(pixbuf);
|
|
- frame->width = ctx->width;
|
|
- frame->height = ctx->height;
|
|
- frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
|
|
- if (frame->width != (int)CVPixelBufferGetWidth(pixbuf) ||
|
|
- frame->height != (int)CVPixelBufferGetHeight(pixbuf)) {
|
|
- // This can happen while changing camera format
|
|
- av_frame_free(&frame);
|
|
- return nullptr;
|
|
- }
|
|
- return frame;
|
|
-}
|
|
-
|
|
-static AVAuthorizationStatus m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
|
|
-
|
|
-@interface QAVFSampleBufferDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
|
|
-
|
|
-- (QAVFSampleBufferDelegate *) initWithCamera:(QAVFCamera *)renderer;
|
|
-
|
|
-- (void) captureOutput:(AVCaptureOutput *)captureOutput
|
|
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
|
- fromConnection:(AVCaptureConnection *)connection;
|
|
-
|
|
-- (void) setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel;
|
|
-
|
|
-@end
|
|
-
|
|
-@implementation QAVFSampleBufferDelegate
|
|
-{
|
|
-@private
|
|
- QAVFCamera *m_camera;
|
|
- AVBufferRef *hwFramesContext;
|
|
- std::unique_ptr<QFFmpeg::HWAccel> m_accel;
|
|
- qint64 startTime;
|
|
- qint64 baseTime;
|
|
-}
|
|
-
|
|
-- (QAVFSampleBufferDelegate *) initWithCamera:(QAVFCamera *)renderer
|
|
-{
|
|
- if (!(self = [super init]))
|
|
- return nil;
|
|
-
|
|
- m_camera = renderer;
|
|
- hwFramesContext = nullptr;
|
|
- startTime = 0;
|
|
- baseTime = 0;
|
|
- return self;
|
|
-}
|
|
-
|
|
-- (void)captureOutput:(AVCaptureOutput *)captureOutput
|
|
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
|
- fromConnection:(AVCaptureConnection *)connection
|
|
-{
|
|
- Q_UNUSED(connection);
|
|
- Q_UNUSED(captureOutput);
|
|
-
|
|
- // NB: on iOS captureOutput/connection can be nil (when recording a video -
|
|
- // avfmediaassetwriter).
|
|
-
|
|
- CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
|
-
|
|
- CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
|
|
- qint64 frameTime = time.timescale ? time.value*1000/time.timescale : 0;
|
|
- if (baseTime == 0) {
|
|
- // drop the first frame to get a valid frame start time
|
|
- baseTime = frameTime;
|
|
- startTime = 0;
|
|
- return;
|
|
- }
|
|
-
|
|
- if (!m_accel)
|
|
- return;
|
|
-
|
|
- AVFrame *avFrame = allocHWFrame(m_accel->hwFramesContextAsBuffer(), imageBuffer);
|
|
- if (!avFrame)
|
|
- return;
|
|
-
|
|
-#ifdef USE_SW_FRAMES
|
|
- auto *swFrame = av_frame_alloc();
|
|
- /* retrieve data from GPU to CPU */
|
|
- int ret = av_hwframe_transfer_data(swFrame, avFrame, 0);
|
|
- if (ret < 0) {
|
|
- qWarning() << "Error transferring the data to system memory\n";
|
|
- av_frame_unref(swFrame);
|
|
- } else {
|
|
- av_frame_unref(avFrame);
|
|
- avFrame = swFrame;
|
|
- }
|
|
-#endif
|
|
-
|
|
- QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(imageBuffer);
|
|
- if (!format.isValid()) {
|
|
- av_frame_unref(avFrame);
|
|
- return;
|
|
- }
|
|
-
|
|
- avFrame->pts = startTime;
|
|
-
|
|
- QFFmpegVideoBuffer *buffer = new QFFmpegVideoBuffer(avFrame);
|
|
- QVideoFrame frame(buffer, format);
|
|
- frame.setStartTime(startTime);
|
|
- frame.setEndTime(frameTime);
|
|
- startTime = frameTime;
|
|
-
|
|
- m_camera->syncHandleFrame(frame);
|
|
-}
|
|
-
|
|
-- (void) setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
|
|
-{
|
|
- m_accel = std::move(accel);
|
|
-}
|
|
-
|
|
-@end
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-QAVFCamera::QAVFCamera(QCamera *parent)
|
|
- : QAVFCameraBase(parent)
|
|
-{
|
|
- m_captureSession = [[AVCaptureSession alloc] init];
|
|
- m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc] initWithCamera:this];
|
|
-}
|
|
-
|
|
-QAVFCamera::~QAVFCamera()
|
|
-{
|
|
- [m_sampleBufferDelegate release];
|
|
- [m_videoInput release];
|
|
- [m_videoDataOutput release];
|
|
- [m_captureSession release];
|
|
-}
|
|
-
|
|
-void QAVFCamera::requestCameraPermissionIfNeeded()
|
|
-{
|
|
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
|
|
- return;
|
|
-
|
|
- switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
|
|
- {
|
|
- case AVAuthorizationStatusAuthorized:
|
|
- {
|
|
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
|
|
- break;
|
|
- }
|
|
- case AVAuthorizationStatusNotDetermined:
|
|
- {
|
|
- m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
|
|
- QPointer<QAVFCamera> guard(this);
|
|
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
|
- dispatch_async(dispatch_get_main_queue(), ^{
|
|
- if (guard)
|
|
- cameraAuthorizationChanged(granted);
|
|
- });
|
|
- }];
|
|
- break;
|
|
- }
|
|
- case AVAuthorizationStatusDenied:
|
|
- case AVAuthorizationStatusRestricted:
|
|
- {
|
|
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
|
|
- return;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void QAVFCamera::cameraAuthorizationChanged(bool authorized)
|
|
-{
|
|
- if (authorized) {
|
|
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
|
|
- } else {
|
|
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
|
|
- qWarning() << "User has denied access to camera";
|
|
- }
|
|
-}
|
|
-
|
|
-void QAVFCamera::updateVideoInput()
|
|
-{
|
|
- requestCameraPermissionIfNeeded();
|
|
- if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
|
|
- return;
|
|
-
|
|
- [m_captureSession beginConfiguration];
|
|
-
|
|
- attachVideoInputDevice();
|
|
-
|
|
- if (!m_videoDataOutput) {
|
|
- m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
|
|
-
|
|
- // Configure video output
|
|
- m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
|
|
- [m_videoDataOutput
|
|
- setSampleBufferDelegate:m_sampleBufferDelegate
|
|
- queue:m_delegateQueue];
|
|
-
|
|
- [m_captureSession addOutput:m_videoDataOutput];
|
|
- }
|
|
- [m_captureSession commitConfiguration];
|
|
- deviceOrientationChanged();
|
|
-}
|
|
-
|
|
-void QAVFCamera::deviceOrientationChanged(int angle)
|
|
-{
|
|
- AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
|
|
- if (connection == nil || !m_videoDataOutput)
|
|
- return;
|
|
-
|
|
- if (!connection.supportsVideoOrientation)
|
|
- return;
|
|
-
|
|
- if (angle < 0)
|
|
- angle = m_orientationHandler.currentOrientation();
|
|
-
|
|
- AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
|
|
- switch (angle) {
|
|
- default:
|
|
- break;
|
|
- case 90:
|
|
- orientation = AVCaptureVideoOrientationLandscapeRight;
|
|
- break;
|
|
- case 180:
|
|
- // this keeps the last orientation, don't do anything
|
|
- return;
|
|
- case 270:
|
|
- orientation = AVCaptureVideoOrientationLandscapeLeft;
|
|
- break;
|
|
- }
|
|
-
|
|
- connection.videoOrientation = orientation;
|
|
-}
|
|
-
|
|
-void QAVFCamera::attachVideoInputDevice()
|
|
-{
|
|
- if (m_videoInput) {
|
|
- [m_captureSession removeInput:m_videoInput];
|
|
- [m_videoInput release];
|
|
- m_videoInput = nullptr;
|
|
- }
|
|
-
|
|
- QByteArray deviceId = m_cameraDevice.id();
|
|
- if (deviceId.isEmpty())
|
|
- return;
|
|
-
|
|
- AVCaptureDevice *videoDevice = [AVCaptureDevice deviceWithUniqueID:
|
|
- [NSString stringWithUTF8String: deviceId.constData()]];
|
|
-
|
|
- if (!videoDevice)
|
|
- return;
|
|
-
|
|
- m_videoInput = [AVCaptureDeviceInput
|
|
- deviceInputWithDevice:videoDevice
|
|
- error:nil];
|
|
- if (m_videoInput && [m_captureSession canAddInput:m_videoInput]) {
|
|
- [m_videoInput retain];
|
|
- [m_captureSession addInput:m_videoInput];
|
|
- } else {
|
|
- qWarning() << "Failed to create video device input";
|
|
- }
|
|
-}
|
|
-
|
|
-AVCaptureDevice *QAVFCamera::device() const
|
|
-{
|
|
- return m_videoInput ? m_videoInput.device : nullptr;
|
|
-}
|
|
-
|
|
-bool QAVFCamera::isActive() const
|
|
-{
|
|
- return m_active;
|
|
-}
|
|
-
|
|
-void QAVFCamera::setActive(bool active)
|
|
-{
|
|
- if (m_active == active)
|
|
- return;
|
|
- requestCameraPermissionIfNeeded();
|
|
- if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
|
|
- return;
|
|
-
|
|
- m_active = active;
|
|
-
|
|
- if (active) {
|
|
- // According to the doc, the capture device must be locked before
|
|
- // startRunning to prevent the format we set to be overridden by the
|
|
- // session preset.
|
|
- [m_videoInput.device lockForConfiguration:nil];
|
|
- [m_captureSession startRunning];
|
|
- [m_videoInput.device unlockForConfiguration];
|
|
- } else {
|
|
- [m_captureSession stopRunning];
|
|
- }
|
|
-
|
|
- emit activeChanged(active);
|
|
-}
|
|
-
|
|
-void QAVFCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
|
|
-{
|
|
- m_session = session ? session->captureSession() : nullptr;
|
|
-}
|
|
-
|
|
-void QAVFCamera::setCamera(const QCameraDevice &camera)
|
|
-{
|
|
- if (m_cameraDevice == camera)
|
|
- return;
|
|
-
|
|
- m_cameraDevice = camera;
|
|
-
|
|
- requestCameraPermissionIfNeeded();
|
|
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
|
|
- updateVideoInput();
|
|
- setCameraFormat({});
|
|
-}
|
|
-
|
|
-bool QAVFCamera::setCameraFormat(const QCameraFormat &format)
|
|
-{
|
|
- if (m_cameraFormat == format && !format.isNull())
|
|
- return true;
|
|
-
|
|
- QAVFCameraBase::setCameraFormat(format);
|
|
- updateCameraFormat();
|
|
- return true;
|
|
-}
|
|
-
|
|
-void QAVFCamera::updateCameraFormat()
|
|
-{
|
|
- AVCaptureDevice *captureDevice = device();
|
|
- if (!captureDevice)
|
|
- return;
|
|
-
|
|
- uint avPixelFormat = 0;
|
|
- AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(captureDevice, m_cameraFormat);
|
|
- if (newFormat) {
|
|
- qt_set_active_format(captureDevice, newFormat, false);
|
|
- avPixelFormat = setPixelFormat(m_cameraFormat.pixelFormat());
|
|
- }
|
|
-
|
|
- auto hwAccel = QFFmpeg::HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
|
|
- if (hwAccel) {
|
|
- hwAccel->createFramesContext(av_map_videotoolbox_format_to_pixfmt(avPixelFormat),
|
|
- m_cameraFormat.resolution());
|
|
- hwPixelFormat = hwAccel->hwFormat();
|
|
- } else {
|
|
- hwPixelFormat = AV_PIX_FMT_NONE;
|
|
- }
|
|
- [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
|
|
-}
|
|
-
|
|
-uint QAVFCamera::setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat)
|
|
-{
|
|
- // Default to 32BGRA pixel formats on the viewfinder, in case the requested
|
|
- // format can't be used (shouldn't happen unless the developers sets a wrong camera
|
|
- // format on the camera).
|
|
- unsigned avPixelFormat = kCVPixelFormatType_32BGRA;
|
|
- if (!QAVFHelpers::toCVPixelFormat(pixelFormat, avPixelFormat))
|
|
- qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32";
|
|
-
|
|
- bool isSupported = false;
|
|
- NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes;
|
|
- for (NSNumber *currentPixelFormat in supportedPixelFormats)
|
|
- {
|
|
- if ([currentPixelFormat unsignedIntValue] == avPixelFormat) {
|
|
- isSupported = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- if (isSupported) {
|
|
- NSDictionary* outputSettings = @{
|
|
- (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:avPixelFormat],
|
|
- (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
|
|
- };
|
|
- m_videoDataOutput.videoSettings = outputSettings;
|
|
- } else {
|
|
- qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?";
|
|
- }
|
|
- return avPixelFormat;
|
|
-}
|
|
-
|
|
-void QAVFCamera::syncHandleFrame(const QVideoFrame &frame)
|
|
-{
|
|
- Q_EMIT newVideoFrame(frame);
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#include "moc_qavfcamera_p.cpp"
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qavfcamera_p.h b/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
|
|
deleted file mode 100644
|
|
index 40a53dc7c..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
|
|
+++ /dev/null
|
|
@@ -1,87 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#ifndef QAVFCAMERA_H
|
|
-#define QAVFCAMERA_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qavfcamerabase_p.h"
|
|
-#include <private/qplatformmediaintegration_p.h>
|
|
-#include <private/qvideooutputorientationhandler_p.h>
|
|
-#define AVMediaType XAVMediaType
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#undef AVMediaType
|
|
-
|
|
-#include <qfilesystemwatcher.h>
|
|
-#include <qsocketnotifier.h>
|
|
-#include <qmutex.h>
|
|
-
|
|
-#include <dispatch/dispatch.h>
|
|
-
|
|
-Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureSession);
|
|
-Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDeviceInput);
|
|
-Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoDataOutput);
|
|
-Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice);
|
|
-Q_FORWARD_DECLARE_OBJC_CLASS(QAVFSampleBufferDelegate);
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QFFmpegVideoSink;
|
|
-
|
|
-class QAVFCamera : public QAVFCameraBase
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
- explicit QAVFCamera(QCamera *parent);
|
|
- ~QAVFCamera();
|
|
-
|
|
- bool isActive() const override;
|
|
- void setActive(bool active) override;
|
|
-
|
|
- void setCaptureSession(QPlatformMediaCaptureSession *) override;
|
|
-
|
|
- void setCamera(const QCameraDevice &camera) override;
|
|
- bool setCameraFormat(const QCameraFormat &format) override;
|
|
-
|
|
- void syncHandleFrame(const QVideoFrame &frame);
|
|
-
|
|
- void deviceOrientationChanged(int angle = -1);
|
|
-
|
|
- std::optional<int> ffmpegHWPixelFormat() const override { return hwPixelFormat; }
|
|
-
|
|
-private:
|
|
- void requestCameraPermissionIfNeeded();
|
|
- void cameraAuthorizationChanged(bool authorized);
|
|
- void updateCameraFormat();
|
|
- void updateVideoInput();
|
|
- void attachVideoInputDevice();
|
|
- uint setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat);
|
|
-
|
|
- AVCaptureDevice *device() const;
|
|
-
|
|
- QMediaCaptureSession *m_session = nullptr;
|
|
- AVCaptureSession *m_captureSession = nullptr;
|
|
- AVCaptureDeviceInput *m_videoInput = nullptr;
|
|
- AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
|
|
- QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
|
|
- dispatch_queue_t m_delegateQueue;
|
|
- QVideoOutputOrientationHandler m_orientationHandler;
|
|
- AVPixelFormat hwPixelFormat = AV_PIX_FMT_NONE;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-
|
|
-#endif // QFFMPEGCAMERA_H
|
|
-
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
|
|
deleted file mode 100644
|
|
index 6a1d6ab38..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
|
|
+++ /dev/null
|
|
@@ -1,60 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEG_P_H
|
|
-#define QFFMPEG_P_H
|
|
-
|
|
-#include <private/qtmultimediaglobal_p.h>
|
|
-#include <qstring.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavformat/avformat.h>
|
|
-#include <libavcodec/avcodec.h>
|
|
-#include <libswresample/swresample.h>
|
|
-#include <libavutil/avutil.h>
|
|
-#include <libswscale/swscale.h>
|
|
-}
|
|
-
|
|
-#define QT_FFMPEG_OLD_CHANNEL_LAYOUT (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59,24,100))
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-inline std::optional<qint64> mul(qint64 a, AVRational b)
|
|
-{
|
|
- return b.den != 0 ? (a * b.num + b.den / 2) / b.den : std::optional<qint64>{};
|
|
-}
|
|
-
|
|
-inline std::optional<qreal> mul(qreal a, AVRational b)
|
|
-{
|
|
- return b.den != 0 ? a * qreal(b.num) / qreal(b.den) : std::optional<qreal>{};
|
|
-}
|
|
-
|
|
-inline std::optional<qint64> timeStampMs(qint64 ts, AVRational base)
|
|
-{
|
|
- return mul(1'000 * ts, base);
|
|
-}
|
|
-
|
|
-inline std::optional<qint64> timeStampUs(qint64 ts, AVRational base)
|
|
-{
|
|
- return mul(1'000'000 * ts, base);
|
|
-}
|
|
-
|
|
-inline std::optional<float> toFloat(AVRational r)
|
|
-{
|
|
- return r.den != 0 ? float(r.num) / float(r.den) : std::optional<float>{};
|
|
-}
|
|
-
|
|
-inline QString err2str(int errnum)
|
|
-{
|
|
- char buffer[AV_ERROR_MAX_STRING_SIZE + 1] = {};
|
|
- av_make_error_string(buffer, AV_ERROR_MAX_STRING_SIZE, errnum);
|
|
- return QString::fromLocal8Bit(buffer);
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-}
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
|
|
deleted file mode 100644
|
|
index 4dff93d12..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
|
|
+++ /dev/null
|
|
@@ -1,265 +0,0 @@
|
|
-// Copyright (C) 2020 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-//#define DEBUG_DECODER
|
|
-
|
|
-#include "qffmpegaudiodecoder_p.h"
|
|
-#include "qffmpegdecoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpegresampler_p.h"
|
|
-#include "qaudiobuffer.h"
|
|
-
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
|
|
-
|
|
-#define MAX_BUFFERS_IN_QUEUE 4
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-class SteppingAudioRenderer : public Renderer
|
|
-{
|
|
-public:
|
|
- SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format);
|
|
- ~SteppingAudioRenderer()
|
|
- {
|
|
- }
|
|
-
|
|
- void loop() override;
|
|
- AudioDecoder *m_decoder;
|
|
- QAudioFormat m_format;
|
|
- std::unique_ptr<Resampler> resampler;
|
|
- bool atEndEmitted = false;
|
|
-};
|
|
-
|
|
-class AudioDecoder : public Decoder
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- explicit AudioDecoder(QFFmpegAudioDecoder *audioDecoder) : Decoder(), audioDecoder(audioDecoder)
|
|
- {}
|
|
-
|
|
- void setup(const QAudioFormat &format)
|
|
- {
|
|
- connect(this, &AudioDecoder::newAudioBuffer, audioDecoder, &QFFmpegAudioDecoder::newAudioBuffer);
|
|
- connect(this, &AudioDecoder::isAtEnd, audioDecoder, &QFFmpegAudioDecoder::done);
|
|
- m_format = format;
|
|
- audioRenderer = new SteppingAudioRenderer(this, format);
|
|
- audioRenderer->start();
|
|
- auto *stream = demuxer->addStream(avStreamIndex(QPlatformMediaPlayer::AudioStream));
|
|
- audioRenderer->setStream(stream);
|
|
- }
|
|
-
|
|
- void nextBuffer()
|
|
- {
|
|
- audioRenderer->setPaused(false);
|
|
- }
|
|
-
|
|
-Q_SIGNALS:
|
|
- void newAudioBuffer(const QAudioBuffer &b);
|
|
- void isAtEnd();
|
|
-
|
|
-private:
|
|
- QFFmpegAudioDecoder *audioDecoder = nullptr;
|
|
- QAudioFormat m_format;
|
|
-};
|
|
-
|
|
-SteppingAudioRenderer::SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format)
|
|
- : Renderer(QPlatformMediaPlayer::AudioStream)
|
|
- , m_decoder(decoder)
|
|
- , m_format(format)
|
|
-{
|
|
-}
|
|
-
|
|
-
|
|
-void SteppingAudioRenderer::loop()
|
|
-{
|
|
- if (!streamDecoder) {
|
|
- qCDebug(qLcAudioDecoder) << "no stream";
|
|
- timeOut = -1; // Avoid CPU load before play()
|
|
- return;
|
|
- }
|
|
-
|
|
- Frame frame = streamDecoder->takeFrame();
|
|
- if (!frame.isValid()) {
|
|
- if (streamDecoder->isAtEnd()) {
|
|
- if (!atEndEmitted)
|
|
- emit m_decoder->isAtEnd();
|
|
- atEndEmitted = true;
|
|
- paused = true;
|
|
- doneStep();
|
|
- timeOut = -1;
|
|
- return;
|
|
- }
|
|
- timeOut = 10;
|
|
- streamDecoder->wake();
|
|
- return;
|
|
- }
|
|
- qCDebug(qLcAudioDecoder) << " got frame";
|
|
-
|
|
- doneStep();
|
|
-
|
|
- if (!resampler)
|
|
- resampler.reset(new Resampler(frame.codec(), m_format));
|
|
-
|
|
- auto buffer = resampler->resample(frame.avFrame());
|
|
- paused = true;
|
|
- timeOut = -1;
|
|
-
|
|
- emit m_decoder->newAudioBuffer(buffer);
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-
|
|
-QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
|
|
- : QPlatformAudioDecoder(parent)
|
|
-{
|
|
-}
|
|
-
|
|
-QFFmpegAudioDecoder::~QFFmpegAudioDecoder()
|
|
-{
|
|
- delete decoder;
|
|
-}
|
|
-
|
|
-QUrl QFFmpegAudioDecoder::source() const
|
|
-{
|
|
- return m_url;
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
|
|
-{
|
|
- stop();
|
|
- m_sourceDevice = nullptr;
|
|
-
|
|
- if (m_url == fileName)
|
|
- return;
|
|
- m_url = fileName;
|
|
-
|
|
- emit sourceChanged();
|
|
-}
|
|
-
|
|
-QIODevice *QFFmpegAudioDecoder::sourceDevice() const
|
|
-{
|
|
- return m_sourceDevice;
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
|
|
-{
|
|
- stop();
|
|
- m_url.clear();
|
|
- bool isSignalRequired = (m_sourceDevice != device);
|
|
- m_sourceDevice = device;
|
|
- if (isSignalRequired)
|
|
- sourceChanged();
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::start()
|
|
-{
|
|
- qCDebug(qLcAudioDecoder) << "start";
|
|
- delete decoder;
|
|
- decoder = new QFFmpeg::AudioDecoder(this);
|
|
- decoder->setMedia(m_url, m_sourceDevice);
|
|
- if (error() != QAudioDecoder::NoError)
|
|
- goto error;
|
|
-
|
|
- decoder->setup(m_audioFormat);
|
|
- if (error() != QAudioDecoder::NoError)
|
|
- goto error;
|
|
- decoder->play();
|
|
- if (error() != QAudioDecoder::NoError)
|
|
- goto error;
|
|
- decoder->nextBuffer();
|
|
- if (error() != QAudioDecoder::NoError)
|
|
- goto error;
|
|
-
|
|
- connect(decoder, &QFFmpeg::Decoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal);
|
|
- durationChanged(duration());
|
|
- setIsDecoding(true);
|
|
- return;
|
|
-
|
|
- error:
|
|
- durationChanged(-1);
|
|
- positionChanged(-1);
|
|
- delete decoder;
|
|
- decoder = nullptr;
|
|
-
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::stop()
|
|
-{
|
|
- qCDebug(qLcAudioDecoder) << ">>>>> stop";
|
|
- if (decoder) {
|
|
- decoder->stop();
|
|
- done();
|
|
- }
|
|
-}
|
|
-
|
|
-QAudioFormat QFFmpegAudioDecoder::audioFormat() const
|
|
-{
|
|
- return m_audioFormat;
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
|
|
-{
|
|
- if (m_audioFormat == format)
|
|
- return;
|
|
-
|
|
- m_audioFormat = format;
|
|
- formatChanged(m_audioFormat);
|
|
-}
|
|
-
|
|
-QAudioBuffer QFFmpegAudioDecoder::read()
|
|
-{
|
|
- auto b = m_audioBuffer;
|
|
- qCDebug(qLcAudioDecoder) << "reading buffer" << b.startTime();
|
|
- m_audioBuffer = {};
|
|
- bufferAvailableChanged(false);
|
|
- if (decoder)
|
|
- decoder->nextBuffer();
|
|
- return b;
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
|
|
-{
|
|
- qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
|
|
- m_audioBuffer = b;
|
|
- const qint64 pos = b.startTime();
|
|
- positionChanged(pos/1000);
|
|
- bufferAvailableChanged(b.isValid());
|
|
- bufferReady();
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::done()
|
|
-{
|
|
- qCDebug(qLcAudioDecoder) << ">>>>> DONE!";
|
|
- finished();
|
|
-}
|
|
-
|
|
-void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
|
|
-{
|
|
- // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
|
|
- // Map them.
|
|
- switch (QMediaPlayer::Error(err)) {
|
|
- case QMediaPlayer::NoError:
|
|
- error(QAudioDecoder::NoError, errorString);
|
|
- break;
|
|
- case QMediaPlayer::ResourceError:
|
|
- error(QAudioDecoder::ResourceError, errorString);
|
|
- break;
|
|
- case QMediaPlayer::FormatError:
|
|
- error(QAudioDecoder::FormatError, errorString);
|
|
- break;
|
|
- case QMediaPlayer::NetworkError:
|
|
- // fall through, Network error doesn't exist in QAudioDecoder
|
|
- case QMediaPlayer::AccessDeniedError:
|
|
- error(QAudioDecoder::AccessDeniedError, errorString);
|
|
- break;
|
|
- }
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#include "qffmpegaudiodecoder.moc"
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
|
|
deleted file mode 100644
|
|
index 0196f88a7..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
|
|
+++ /dev/null
|
|
@@ -1,69 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#ifndef QFFMPEGAUDIODECODER_H
|
|
-#define QFFMPEGAUDIODECODER_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "private/qplatformaudiodecoder_p.h"
|
|
-#include <qffmpeg_p.h>
|
|
-
|
|
-#include <qmutex.h>
|
|
-#include <qurl.h>
|
|
-#include <qqueue.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-class AudioDecoder;
|
|
-}
|
|
-
|
|
-class QFFmpegAudioDecoder : public QPlatformAudioDecoder
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
- QFFmpegAudioDecoder(QAudioDecoder *parent);
|
|
- virtual ~QFFmpegAudioDecoder();
|
|
-
|
|
- QUrl source() const override;
|
|
- void setSource(const QUrl &fileName) override;
|
|
-
|
|
- QIODevice *sourceDevice() const override;
|
|
- void setSourceDevice(QIODevice *device) override;
|
|
-
|
|
- void start() override;
|
|
- void stop() override;
|
|
-
|
|
- QAudioFormat audioFormat() const override;
|
|
- void setAudioFormat(const QAudioFormat &format) override;
|
|
-
|
|
- QAudioBuffer read() override;
|
|
-
|
|
-public Q_SLOTS:
|
|
- void newAudioBuffer(const QAudioBuffer &b);
|
|
- void done();
|
|
- void errorSignal(int err, const QString &errorString);
|
|
-
|
|
-private:
|
|
- QUrl m_url;
|
|
- QIODevice *m_sourceDevice = nullptr;
|
|
- QFFmpeg::AudioDecoder *decoder = nullptr;
|
|
- QAudioFormat m_audioFormat;
|
|
-
|
|
- QAudioBuffer m_audioBuffer;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif // QFFMPEGAUDIODECODER_H
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
|
|
deleted file mode 100644
|
|
index 5c769d524..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
|
|
+++ /dev/null
|
|
@@ -1,189 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include "qffmpegaudioinput_p.h"
|
|
-#include <qiodevice.h>
|
|
-#include <qaudiosource.h>
|
|
-#include <qaudiobuffer.h>
|
|
-#include <qdebug.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class AudioSourceIO : public QIODevice
|
|
-{
|
|
- Q_OBJECT
|
|
- public:
|
|
- AudioSourceIO(QFFmpegAudioInput *audioInput)
|
|
- : QIODevice()
|
|
- , input(audioInput)
|
|
- {
|
|
- m_muted = input->muted;
|
|
- m_volume = input->volume;
|
|
- updateVolume();
|
|
- open(QIODevice::WriteOnly);
|
|
- }
|
|
- ~AudioSourceIO()
|
|
- {
|
|
- delete m_src;
|
|
- }
|
|
-
|
|
- void setDevice(const QAudioDevice &device)
|
|
- {
|
|
- QMutexLocker locker(&mutex);
|
|
- if (m_device == device)
|
|
- return;
|
|
- m_device = device;
|
|
- QMetaObject::invokeMethod(this, "updateSource");
|
|
- }
|
|
- void setFrameSize(int s)
|
|
- {
|
|
- QMutexLocker locker(&mutex);
|
|
- frameSize = s;
|
|
- bufferSize = m_format.bytesForFrames(frameSize);
|
|
- }
|
|
- void setRunning(bool r) {
|
|
- QMutexLocker locker(&mutex);
|
|
- if (m_running == r)
|
|
- return;
|
|
- m_running = r;
|
|
- QMetaObject::invokeMethod(this, "updateRunning");
|
|
- }
|
|
-
|
|
- void setVolume(float vol) {
|
|
- QMutexLocker locker(&mutex);
|
|
- m_volume = vol;
|
|
- QMetaObject::invokeMethod(this, "updateVolume");
|
|
- }
|
|
- void setMuted(bool muted) {
|
|
- QMutexLocker locker(&mutex);
|
|
- m_muted = muted;
|
|
- QMetaObject::invokeMethod(this, "updateVolume");
|
|
- }
|
|
-
|
|
-
|
|
-protected:
|
|
- qint64 readData(char *, qint64) override
|
|
- {
|
|
- return 0;
|
|
- }
|
|
- qint64 writeData(const char *data, qint64 len) override
|
|
- {
|
|
- int l = len;
|
|
- while (len > 0) {
|
|
- int toAppend = qMin(len, bufferSize - pcm.size());
|
|
- pcm.append(data, toAppend);
|
|
- data += toAppend;
|
|
- len -= toAppend;
|
|
- if (pcm.size() == bufferSize)
|
|
- sendBuffer();
|
|
- }
|
|
-
|
|
- return l;
|
|
- }
|
|
-
|
|
-private Q_SLOTS:
|
|
- void updateSource() {
|
|
- QMutexLocker locker(&mutex);
|
|
- m_format = m_device.preferredFormat();
|
|
- if (m_src) {
|
|
- delete m_src;
|
|
- pcm.clear();
|
|
- }
|
|
- m_src = new QAudioSource(m_device, m_format);
|
|
- updateVolume();
|
|
- if (m_running)
|
|
- m_src->start(this);
|
|
- }
|
|
- void updateVolume()
|
|
- {
|
|
- if (m_src)
|
|
- m_src->setVolume(m_muted ? 0. : m_volume);
|
|
- }
|
|
- void updateRunning()
|
|
- {
|
|
- QMutexLocker locker(&mutex);
|
|
- if (m_running) {
|
|
- if (!m_src)
|
|
- updateSource();
|
|
- m_src->start(this);
|
|
- } else {
|
|
- m_src->stop();
|
|
- }
|
|
- }
|
|
-
|
|
-private:
|
|
-
|
|
- void sendBuffer()
|
|
- {
|
|
- QAudioFormat fmt = m_src->format();
|
|
- qint64 time = fmt.durationForBytes(processed);
|
|
- QAudioBuffer buffer(pcm, fmt, time);
|
|
- emit input->newAudioBuffer(buffer);
|
|
- processed += bufferSize;
|
|
- pcm.clear();
|
|
- }
|
|
-
|
|
- QMutex mutex;
|
|
- QAudioDevice m_device;
|
|
- float m_volume = 1.;
|
|
- bool m_muted = false;
|
|
- bool m_running = false;
|
|
-
|
|
- QFFmpegAudioInput *input = nullptr;
|
|
- QAudioSource *m_src = nullptr;
|
|
- QAudioFormat m_format;
|
|
- int frameSize = 0;
|
|
- int bufferSize = 0;
|
|
- qint64 processed = 0;
|
|
- QByteArray pcm;
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QFFmpegAudioInput::QFFmpegAudioInput(QAudioInput *qq)
|
|
- : QPlatformAudioInput(qq)
|
|
-{
|
|
- qRegisterMetaType<QAudioBuffer>();
|
|
-
|
|
- inputThread = new QThread;
|
|
- audioIO = new QFFmpeg::AudioSourceIO(this);
|
|
- audioIO->moveToThread(inputThread);
|
|
- inputThread->start();
|
|
-}
|
|
-
|
|
-QFFmpegAudioInput::~QFFmpegAudioInput()
|
|
-{
|
|
- inputThread->exit();
|
|
- inputThread->wait();
|
|
- delete inputThread;
|
|
-}
|
|
-
|
|
-void QFFmpegAudioInput::setAudioDevice(const QAudioDevice &device)
|
|
-{
|
|
- audioIO->setDevice(device);
|
|
-}
|
|
-
|
|
-void QFFmpegAudioInput::setMuted(bool muted)
|
|
-{
|
|
- audioIO->setMuted(muted);
|
|
-}
|
|
-
|
|
-void QFFmpegAudioInput::setVolume(float volume)
|
|
-{
|
|
- audioIO->setVolume(volume);
|
|
-}
|
|
-
|
|
-void QFFmpegAudioInput::setFrameSize(int s)
|
|
-{
|
|
- audioIO->setFrameSize(s);
|
|
-}
|
|
-
|
|
-void QFFmpegAudioInput::setRunning(bool b)
|
|
-{
|
|
- audioIO->setRunning(b);
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#include "qffmpegaudioinput.moc"
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
|
|
deleted file mode 100644
|
|
index f81549748..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
|
|
+++ /dev/null
|
|
@@ -1,54 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGAUDIOINPUT_H
|
|
-#define QFFMPEGAUDIOINPUT_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include <private/qplatformaudioinput_p.h>
|
|
-#include "qffmpegthread_p.h"
|
|
-#include <qaudioinput.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QAudioSource;
|
|
-class QAudioBuffer;
|
|
-namespace QFFmpeg {
|
|
-class AudioSourceIO;
|
|
-}
|
|
-
|
|
-class QFFmpegAudioInput : public QObject, public QPlatformAudioInput
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- QFFmpegAudioInput(QAudioInput *qq);
|
|
- ~QFFmpegAudioInput();
|
|
-
|
|
- void setAudioDevice(const QAudioDevice &/*device*/) override;
|
|
- void setMuted(bool /*muted*/) override;
|
|
- void setVolume(float /*volume*/) override;
|
|
-
|
|
- void setFrameSize(int s);
|
|
- void setRunning(bool b);
|
|
-
|
|
-Q_SIGNALS:
|
|
- void newAudioBuffer(const QAudioBuffer &buffer);
|
|
-
|
|
-private:
|
|
- QThread *inputThread = nullptr;
|
|
- QFFmpeg::AudioSourceIO *audioIO = nullptr;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-
|
|
-#endif // QPLATFORMAUDIOINPUT_H
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp b/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp
|
|
deleted file mode 100644
|
|
index a3ca04d93..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp
|
|
+++ /dev/null
|
|
@@ -1,193 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include <qffmpegclock_p.h>
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcClock, "qt.multimedia.ffmpeg.clock")
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-static bool compareClocks(const QFFmpeg::Clock *a, const QFFmpeg::Clock *b)
|
|
-{
|
|
- if (!b)
|
|
- return false;
|
|
-
|
|
- if (!a)
|
|
- return true;
|
|
-
|
|
- return a->type() < b->type();
|
|
-}
|
|
-
|
|
-QFFmpeg::Clock::Clock(ClockController *controller)
|
|
- : controller(controller)
|
|
-{
|
|
- Q_ASSERT(controller);
|
|
- controller->addClock(this);
|
|
-}
|
|
-
|
|
-QFFmpeg::Clock::~Clock()
|
|
-{
|
|
- if (controller)
|
|
- controller->removeClock(this);
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::Clock::currentTime() const
|
|
-{
|
|
- return controller ? controller->currentTime() : 0;
|
|
-}
|
|
-
|
|
-void QFFmpeg::Clock::syncTo(qint64 time)
|
|
-{
|
|
- qCDebug(qLcClock) << "syncTo" << time << isMaster();
|
|
-}
|
|
-
|
|
-void QFFmpeg::Clock::setPlaybackRate(float rate, qint64 currentTime)
|
|
-{
|
|
- qCDebug(qLcClock) << "Clock::setPlaybackRate" << rate;
|
|
- Q_UNUSED(rate)
|
|
- Q_UNUSED(currentTime)
|
|
-}
|
|
-
|
|
-void QFFmpeg::Clock::setPaused(bool paused)
|
|
-{
|
|
- qCDebug(qLcClock) << "Clock::setPaused" << paused;
|
|
- Q_UNUSED(paused)
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::Clock::timeUpdated(qint64 currentTime)
|
|
-{
|
|
- if (controller)
|
|
- return controller->timeUpdated(this, currentTime);
|
|
- return currentTime;
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::Clock::usecsTo(qint64 currentTime, qint64 displayTime)
|
|
-{
|
|
- if (!controller || controller->m_isPaused)
|
|
- return -1;
|
|
- const qint64 t = qRound64((displayTime - currentTime) / playbackRate());
|
|
- return t < 0 ? 0 : t;
|
|
-}
|
|
-
|
|
-QFFmpeg::Clock::Type QFFmpeg::Clock::type() const
|
|
-{
|
|
- return SystemClock;
|
|
-}
|
|
-
|
|
-QFFmpeg::ClockController::~ClockController()
|
|
-{
|
|
- for (auto *p : qAsConst(m_clocks))
|
|
- p->setController(nullptr);
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::ClockController::timeUpdated(Clock *clock, qint64 time)
|
|
-{
|
|
- QMutexLocker l(&m_mutex);
|
|
- if (!isMaster(clock)) {
|
|
- // If the clock isn't the master clock, simply return the current time
|
|
- // so we can make adjustments as needed
|
|
- return currentTimeNoLock();
|
|
- }
|
|
-
|
|
- // if the clock is the master, adjust our base timing
|
|
- m_baseTime = time;
|
|
- m_elapsedTimer.restart();
|
|
-
|
|
- return time;
|
|
-}
|
|
-
|
|
-void QFFmpeg::ClockController::addClock(Clock *clock)
|
|
-{
|
|
- qCDebug(qLcClock) << "addClock" << clock;
|
|
- Q_ASSERT(clock != nullptr);
|
|
-
|
|
- if (m_clocks.contains(clock))
|
|
- return;
|
|
-
|
|
- m_clocks.append(clock);
|
|
- m_master = std::max(m_master.loadAcquire(), clock, compareClocks);
|
|
-
|
|
- clock->syncTo(currentTime());
|
|
- clock->setPaused(m_isPaused);
|
|
-}
|
|
-
|
|
-void QFFmpeg::ClockController::removeClock(Clock *clock)
|
|
-{
|
|
- qCDebug(qLcClock) << "removeClock" << clock;
|
|
- m_clocks.removeAll(clock);
|
|
- if (m_master == clock) {
|
|
- // find a new master clock
|
|
- m_master = m_clocks.empty()
|
|
- ? nullptr
|
|
- : *std::max_element(m_clocks.begin(), m_clocks.end(), compareClocks);
|
|
- }
|
|
-}
|
|
-
|
|
-bool QFFmpeg::ClockController::isMaster(const Clock *clock) const
|
|
-{
|
|
- return m_master.loadAcquire() == clock;
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::ClockController::currentTimeNoLock() const
|
|
-{
|
|
- return m_isPaused ? m_baseTime : m_baseTime + m_elapsedTimer.elapsed() / m_playbackRate;
|
|
-}
|
|
-
|
|
-qint64 QFFmpeg::ClockController::currentTime() const
|
|
-{
|
|
- QMutexLocker l(&m_mutex);
|
|
- return currentTimeNoLock();
|
|
-}
|
|
-
|
|
-void QFFmpeg::ClockController::syncTo(qint64 usecs)
|
|
-{
|
|
- {
|
|
- QMutexLocker l(&m_mutex);
|
|
- qCDebug(qLcClock) << "syncTo" << usecs;
|
|
- m_baseTime = usecs;
|
|
- m_seekTime = usecs;
|
|
- m_elapsedTimer.restart();
|
|
- }
|
|
-
|
|
- for (auto *p : qAsConst(m_clocks))
|
|
- p->syncTo(usecs);
|
|
-}
|
|
-
|
|
-void QFFmpeg::ClockController::setPlaybackRate(float rate)
|
|
-{
|
|
- qint64 baseTime = 0;
|
|
- {
|
|
- qCDebug(qLcClock) << "setPlaybackRate" << rate;
|
|
-
|
|
- QMutexLocker l(&m_mutex);
|
|
-
|
|
- m_baseTime = baseTime = currentTimeNoLock();
|
|
- m_elapsedTimer.restart();
|
|
- m_playbackRate = rate;
|
|
- }
|
|
-
|
|
- for (auto *p : qAsConst(m_clocks))
|
|
- p->setPlaybackRate(rate, baseTime);
|
|
-}
|
|
-
|
|
-void QFFmpeg::ClockController::setPaused(bool paused)
|
|
-{
|
|
- {
|
|
- QMutexLocker l(&m_mutex);
|
|
- if (m_isPaused == paused)
|
|
- return;
|
|
- qCDebug(qLcClock) << "setPaused" << paused;
|
|
- m_isPaused = paused;
|
|
- if (m_isPaused) {
|
|
- m_baseTime = currentTimeNoLock();
|
|
- m_seekTime = m_baseTime;
|
|
- } else {
|
|
- m_elapsedTimer.restart();
|
|
- }
|
|
- }
|
|
-
|
|
- for (auto *p : qAsConst(m_clocks))
|
|
- p->setPaused(paused);
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h b/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h
|
|
deleted file mode 100644
|
|
index f8cc0bdf3..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h
|
|
+++ /dev/null
|
|
@@ -1,113 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGCLOCK_P_H
|
|
-#define QFFMPEGCLOCK_P_H
|
|
-
|
|
-#include "qffmpeg_p.h"
|
|
-
|
|
-#include <qatomic.h>
|
|
-#include <qelapsedtimer.h>
|
|
-#include <qlist.h>
|
|
-#include <qmutex.h>
|
|
-#include <qmetaobject.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class ClockController;
|
|
-
|
|
-// Clock runs in displayTime, ie. if playbackRate is not 1, it runs faster or slower
|
|
-// than a regular clock. All methods take displayTime
|
|
-// Exception: usecsTo() will return the real time that should pass until we will
|
|
-// hit the requested display time
|
|
-class Clock
|
|
-{
|
|
- ClockController *controller = nullptr;
|
|
-public:
|
|
- enum Type {
|
|
- SystemClock,
|
|
- AudioClock
|
|
- };
|
|
- Clock(ClockController *controller);
|
|
- virtual ~Clock();
|
|
- virtual Type type() const;
|
|
-
|
|
- float playbackRate() const;
|
|
- bool isMaster() const;
|
|
-
|
|
- // all times in usecs
|
|
- qint64 currentTime() const;
|
|
- qint64 seekTime() const;
|
|
- qint64 usecsTo(qint64 currentTime, qint64 displayTime);
|
|
-
|
|
-protected:
|
|
- virtual void syncTo(qint64 usecs);
|
|
- virtual void setPlaybackRate(float rate, qint64 currentTime);
|
|
- virtual void setPaused(bool paused);
|
|
-
|
|
- qint64 timeUpdated(qint64 currentTime);
|
|
-
|
|
-private:
|
|
- friend class ClockController;
|
|
- void setController(ClockController *c)
|
|
- {
|
|
- controller = c;
|
|
- }
|
|
-};
|
|
-
|
|
-class ClockController
|
|
-{
|
|
- mutable QMutex m_mutex;
|
|
- QList<Clock *> m_clocks;
|
|
- QAtomicPointer<Clock> m_master = nullptr;
|
|
-
|
|
- QElapsedTimer m_elapsedTimer;
|
|
- qint64 m_baseTime = 0;
|
|
- qint64 m_seekTime = 0;
|
|
- float m_playbackRate = 1.;
|
|
- bool m_isPaused = true;
|
|
-
|
|
- qint64 currentTimeNoLock() const;
|
|
-
|
|
- friend class Clock;
|
|
- qint64 timeUpdated(Clock *clock, qint64 time);
|
|
- void addClock(Clock *provider);
|
|
- void removeClock(Clock *provider);
|
|
- bool isMaster(const Clock *clock) const;
|
|
-
|
|
-public:
|
|
- ClockController() = default;
|
|
- ~ClockController();
|
|
-
|
|
-
|
|
- qint64 currentTime() const;
|
|
-
|
|
- void syncTo(qint64 usecs);
|
|
-
|
|
- void setPlaybackRate(float s);
|
|
- float playbackRate() const { return m_playbackRate; }
|
|
- void setPaused(bool paused);
|
|
-};
|
|
-
|
|
-inline float Clock::playbackRate() const
|
|
-{
|
|
- return controller ? controller->m_playbackRate : 1.;
|
|
-}
|
|
-
|
|
-inline bool Clock::isMaster() const
|
|
-{
|
|
- return controller && controller->isMaster(this);
|
|
-}
|
|
-
|
|
-inline qint64 Clock::seekTime() const
|
|
-{
|
|
- return controller ? controller->m_seekTime : 0;
|
|
-}
|
|
-
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp
|
|
deleted file mode 100644
|
|
index 89a95f5a3..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp
|
|
+++ /dev/null
|
|
@@ -1,1272 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegdecoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpegmediametadata_p.h"
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "private/qplatformaudiooutput_p.h"
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#include "qffmpegvideosink_p.h"
|
|
-#include "qvideosink.h"
|
|
-#include "qaudiosink.h"
|
|
-#include "qaudiooutput.h"
|
|
-#include "qffmpegaudiodecoder_p.h"
|
|
-#include "qffmpegresampler_p.h"
|
|
-
|
|
-#include <qlocale.h>
|
|
-#include <qtimer.h>
|
|
-
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/hwcontext.h>
|
|
-}
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-using namespace QFFmpeg;
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcDemuxer, "qt.multimedia.ffmpeg.demuxer")
|
|
-Q_LOGGING_CATEGORY(qLcDecoder, "qt.multimedia.ffmpeg.decoder")
|
|
-Q_LOGGING_CATEGORY(qLcVideoRenderer, "qt.multimedia.ffmpeg.videoRenderer")
|
|
-Q_LOGGING_CATEGORY(qLcAudioRenderer, "qt.multimedia.ffmpeg.audioRenderer")
|
|
-
|
|
-Codec::Data::Data(UniqueAVCodecContext &&context, AVStream *stream, std::unique_ptr<QFFmpeg::HWAccel> &&hwAccel)
|
|
- : context(std::move(context))
|
|
- , stream(stream)
|
|
- , hwAccel(std::move(hwAccel))
|
|
-{
|
|
-}
|
|
-
|
|
-Codec::Data::~Data()
|
|
-{
|
|
- avcodec_close(context.get());
|
|
-}
|
|
-
|
|
-QMaybe<Codec> Codec::create(AVStream *stream)
|
|
-{
|
|
- if (!stream)
|
|
- return { "Invalid stream" };
|
|
-
|
|
- const AVCodec *decoder =
|
|
- QFFmpeg::HWAccel::hardwareDecoderForCodecId(stream->codecpar->codec_id);
|
|
- if (!decoder)
|
|
- return { "Failed to find a valid FFmpeg decoder" };
|
|
-
|
|
- //avcodec_free_context
|
|
- UniqueAVCodecContext context(avcodec_alloc_context3(decoder));
|
|
- if (!context)
|
|
- return { "Failed to allocate a FFmpeg codec context" };
|
|
-
|
|
- if (context->codec_type != AVMEDIA_TYPE_AUDIO &&
|
|
- context->codec_type != AVMEDIA_TYPE_VIDEO &&
|
|
- context->codec_type != AVMEDIA_TYPE_SUBTITLE) {
|
|
- return { "Unknown codec type" };
|
|
- }
|
|
-
|
|
- int ret = avcodec_parameters_to_context(context.get(), stream->codecpar);
|
|
- if (ret < 0)
|
|
- return { "Failed to set FFmpeg codec parameters" };
|
|
-
|
|
- std::unique_ptr<QFFmpeg::HWAccel> hwAccel;
|
|
- if (decoder->type == AVMEDIA_TYPE_VIDEO) {
|
|
- hwAccel = QFFmpeg::HWAccel::create(decoder);
|
|
- if (hwAccel)
|
|
- context->hw_device_ctx = av_buffer_ref(hwAccel->hwDeviceContextAsBuffer());
|
|
- }
|
|
- // ### This still gives errors about wrong HW formats (as we accept all of them)
|
|
- // But it would be good to get so we can filter out pixel format we don't support natively
|
|
- context->get_format = QFFmpeg::getFormat;
|
|
-
|
|
- /* Init the decoder, with reference counting and threading */
|
|
- AVDictionary *opts = nullptr;
|
|
- av_dict_set(&opts, "refcounted_frames", "1", 0);
|
|
- av_dict_set(&opts, "threads", "auto", 0);
|
|
- ret = avcodec_open2(context.get(), decoder, &opts);
|
|
- if (ret < 0)
|
|
- return "Failed to open FFmpeg codec context " + err2str(ret);
|
|
-
|
|
- return Codec(new Data(std::move(context), stream, std::move(hwAccel)));
|
|
-}
|
|
-
|
|
-
|
|
-Demuxer::Demuxer(Decoder *decoder, AVFormatContext *context)
|
|
- : Thread()
|
|
- , decoder(decoder)
|
|
- , context(context)
|
|
-{
|
|
- QString objectName = QLatin1String("Demuxer");
|
|
- setObjectName(objectName);
|
|
-
|
|
- streamDecoders.resize(context->nb_streams);
|
|
-}
|
|
-
|
|
-Demuxer::~Demuxer()
|
|
-{
|
|
- if (context) {
|
|
- if (context->pb) {
|
|
- avio_context_free(&context->pb);
|
|
- context->pb = nullptr;
|
|
- }
|
|
- avformat_free_context(context);
|
|
- }
|
|
-}
|
|
-
|
|
-StreamDecoder *Demuxer::addStream(int streamIndex)
|
|
-{
|
|
- if (streamIndex < 0 || streamIndex >= (int)context->nb_streams)
|
|
- return nullptr;
|
|
-
|
|
- AVStream *avStream = context->streams[streamIndex];
|
|
- if (!avStream)
|
|
- return nullptr;
|
|
-
|
|
- QMutexLocker locker(&mutex);
|
|
- auto maybeCodec = Codec::create(avStream);
|
|
- if (!maybeCodec) {
|
|
- decoder->errorOccured(QMediaPlayer::FormatError, "Cannot open codec; " + maybeCodec.error());
|
|
- return nullptr;
|
|
- }
|
|
- auto *stream = new StreamDecoder(this, maybeCodec.value());
|
|
- Q_ASSERT(!streamDecoders.at(streamIndex));
|
|
- streamDecoders[streamIndex] = stream;
|
|
- stream->start();
|
|
- updateEnabledStreams();
|
|
- return stream;
|
|
-}
|
|
-
|
|
-void Demuxer::removeStream(int streamIndex)
|
|
-{
|
|
- if (streamIndex < 0)
|
|
- return;
|
|
- QMutexLocker locker(&mutex);
|
|
- Q_ASSERT(streamIndex < (int)context->nb_streams);
|
|
- Q_ASSERT(streamDecoders.at(streamIndex) != nullptr);
|
|
- streamDecoders[streamIndex] = nullptr;
|
|
- updateEnabledStreams();
|
|
-}
|
|
-
|
|
-void Demuxer::stopDecoding()
|
|
-{
|
|
- qCDebug(qLcDemuxer) << "StopDecoding";
|
|
- QMutexLocker locker(&mutex);
|
|
- sendFinalPacketToStreams();
|
|
-}
|
|
-int Demuxer::seek(qint64 pos)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
|
|
- if (d)
|
|
- d->mutex.lock();
|
|
- }
|
|
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
|
|
- if (d)
|
|
- d->flush();
|
|
- }
|
|
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
|
|
- if (d)
|
|
- d->mutex.unlock();
|
|
- }
|
|
- qint64 seekPos = pos*AV_TIME_BASE/1000000; // usecs to AV_TIME_BASE
|
|
- av_seek_frame(context, -1, seekPos, AVSEEK_FLAG_BACKWARD);
|
|
- last_pts = -1;
|
|
- loop();
|
|
- qCDebug(qLcDemuxer) << "Demuxer::seek" << pos << last_pts;
|
|
- return last_pts;
|
|
-}
|
|
-
|
|
-void Demuxer::updateEnabledStreams()
|
|
-{
|
|
- if (isStopped())
|
|
- return;
|
|
- for (uint i = 0; i < context->nb_streams; ++i) {
|
|
- AVDiscard discard = AVDISCARD_DEFAULT;
|
|
- if (!streamDecoders.at(i))
|
|
- discard = AVDISCARD_ALL;
|
|
- context->streams[i]->discard = discard;
|
|
- }
|
|
-}
|
|
-
|
|
-void Demuxer::sendFinalPacketToStreams()
|
|
-{
|
|
- if (m_isStopped.loadAcquire())
|
|
- return;
|
|
- for (auto *streamDecoder : qAsConst(streamDecoders)) {
|
|
- qCDebug(qLcDemuxer) << "Demuxer: sending last packet to stream" << streamDecoder;
|
|
- if (!streamDecoder)
|
|
- continue;
|
|
- streamDecoder->addPacket(nullptr);
|
|
- }
|
|
- m_isStopped.storeRelease(true);
|
|
-}
|
|
-
|
|
-void Demuxer::init()
|
|
-{
|
|
- qCDebug(qLcDemuxer) << "Demuxer started";
|
|
-}
|
|
-
|
|
-void Demuxer::cleanup()
|
|
-{
|
|
- qCDebug(qLcDemuxer) << "Demuxer::cleanup";
|
|
-#ifndef QT_NO_DEBUG
|
|
- for (auto *streamDecoder : qAsConst(streamDecoders)) {
|
|
- Q_ASSERT(!streamDecoder);
|
|
- }
|
|
-#endif
|
|
- avformat_close_input(&context);
|
|
- Thread::cleanup();
|
|
-}
|
|
-
|
|
-bool Demuxer::shouldWait() const
|
|
-{
|
|
- if (m_isStopped)
|
|
- return true;
|
|
-// qCDebug(qLcDemuxer) << "XXXX Demuxer::shouldWait" << this << data->seek_pos.loadRelaxed();
|
|
- // require a minimum of 200ms of data
|
|
- qint64 queueSize = 0;
|
|
- bool buffersFull = true;
|
|
- for (auto *d : streamDecoders) {
|
|
- if (!d)
|
|
- continue;
|
|
- if (d->queuedDuration() < 200)
|
|
- buffersFull = false;
|
|
- queueSize += d->queuedPacketSize();
|
|
- }
|
|
-// qCDebug(qLcDemuxer) << " queue size" << queueSize << MaxQueueSize;
|
|
- if (queueSize > MaxQueueSize)
|
|
- return true;
|
|
-// qCDebug(qLcDemuxer) << " waiting!";
|
|
- return buffersFull;
|
|
-
|
|
-}
|
|
-
|
|
-void Demuxer::loop()
|
|
-{
|
|
- AVPacket *packet = av_packet_alloc();
|
|
- if (av_read_frame(context, packet) < 0) {
|
|
- sendFinalPacketToStreams();
|
|
- av_packet_free(&packet);
|
|
- return;
|
|
- }
|
|
-
|
|
- if (last_pts < 0 && packet->pts != AV_NOPTS_VALUE) {
|
|
- auto *stream = context->streams[packet->stream_index];
|
|
- auto pts = timeStampMs(packet->pts, stream->time_base);
|
|
- if (pts)
|
|
- last_pts = *pts;
|
|
- }
|
|
-
|
|
- auto *streamDecoder = streamDecoders.at(packet->stream_index);
|
|
- if (!streamDecoder) {
|
|
- av_packet_free(&packet);
|
|
- return;
|
|
- }
|
|
- streamDecoder->addPacket(packet);
|
|
-}
|
|
-
|
|
-
|
|
-StreamDecoder::StreamDecoder(Demuxer *demuxer, const Codec &codec)
|
|
- : Thread()
|
|
- , demuxer(demuxer)
|
|
- , codec(codec)
|
|
-{
|
|
- QString objectName;
|
|
- switch (codec.context()->codec_type) {
|
|
- case AVMEDIA_TYPE_AUDIO:
|
|
- objectName = QLatin1String("AudioDecoderThread");
|
|
- // Queue size: 3 frames for video/subtitle, 9 for audio
|
|
- frameQueue.maxSize = 9;
|
|
- break;
|
|
- case AVMEDIA_TYPE_VIDEO:
|
|
- objectName = QLatin1String("VideoDecoderThread");
|
|
- break;
|
|
- case AVMEDIA_TYPE_SUBTITLE:
|
|
- objectName = QLatin1String("SubtitleDecoderThread");
|
|
- break;
|
|
- default:
|
|
- Q_UNREACHABLE();
|
|
- }
|
|
- setObjectName(objectName);
|
|
-}
|
|
-
|
|
-void StreamDecoder::addPacket(AVPacket *packet)
|
|
-{
|
|
- {
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
-// qCDebug(qLcDecoder) << "enqueuing packet of type" << type()
|
|
-// << "size" << packet->size
|
|
-// << "stream index" << packet->stream_index
|
|
-// << "pts" << codec.toMs(packet->pts)
|
|
-// << "duration" << codec.toMs(packet->duration);
|
|
- packetQueue.queue.enqueue(Packet(packet));
|
|
- if (packet) {
|
|
- packetQueue.size += packet->size;
|
|
- packetQueue.duration += codec.toMs(packet->duration);
|
|
- }
|
|
- eos.storeRelease(false);
|
|
- }
|
|
- wake();
|
|
-}
|
|
-
|
|
-void StreamDecoder::flush()
|
|
-{
|
|
- qCDebug(qLcDecoder) << ">>>> flushing stream decoder" << type();
|
|
- avcodec_flush_buffers(codec.context());
|
|
- {
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- packetQueue.queue.clear();
|
|
- packetQueue.size = 0;
|
|
- packetQueue.duration = 0;
|
|
- }
|
|
- {
|
|
- QMutexLocker locker(&frameQueue.mutex);
|
|
- frameQueue.queue.clear();
|
|
- }
|
|
- qCDebug(qLcDecoder) << ">>>> done flushing stream decoder" << type();
|
|
-}
|
|
-
|
|
-void StreamDecoder::setRenderer(Renderer *r)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- m_renderer = r;
|
|
- if (m_renderer)
|
|
- m_renderer->wake();
|
|
-}
|
|
-
|
|
-void StreamDecoder::killHelper()
|
|
-{
|
|
- m_renderer = nullptr;
|
|
- demuxer->removeStream(codec.streamIndex());
|
|
-}
|
|
-
|
|
-Packet StreamDecoder::peekPacket()
|
|
-{
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- if (packetQueue.queue.isEmpty()) {
|
|
- if (demuxer)
|
|
- demuxer->wake();
|
|
- return {};
|
|
- }
|
|
- auto packet = packetQueue.queue.first();
|
|
-
|
|
- if (demuxer)
|
|
- demuxer->wake();
|
|
- return packet;
|
|
-}
|
|
-
|
|
-Packet StreamDecoder::takePacket()
|
|
-{
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- if (packetQueue.queue.isEmpty()) {
|
|
- if (demuxer)
|
|
- demuxer->wake();
|
|
- return {};
|
|
- }
|
|
- auto packet = packetQueue.queue.dequeue();
|
|
- if (packet.avPacket()) {
|
|
- packetQueue.size -= packet.avPacket()->size;
|
|
- packetQueue.duration -= codec.toMs(packet.avPacket()->duration);
|
|
- }
|
|
-// qCDebug(qLcDecoder) << "<<<< dequeuing packet of type" << type()
|
|
-// << "size" << packet.avPacket()->size
|
|
-// << "stream index" << packet.avPacket()->stream_index
|
|
-// << "pts" << codec.toMs(packet.avPacket()->pts)
|
|
-// << "duration" << codec.toMs(packet.avPacket()->duration)
|
|
-// << "ts" << decoder->clockController.currentTime();
|
|
- if (demuxer)
|
|
- demuxer->wake();
|
|
- return packet;
|
|
-}
|
|
-
|
|
-void StreamDecoder::addFrame(const Frame &f)
|
|
-{
|
|
- Q_ASSERT(f.isValid());
|
|
- QMutexLocker locker(&frameQueue.mutex);
|
|
- frameQueue.queue.append(std::move(f));
|
|
- if (m_renderer)
|
|
- m_renderer->wake();
|
|
-}
|
|
-
|
|
-Frame StreamDecoder::takeFrame()
|
|
-{
|
|
- QMutexLocker locker(&frameQueue.mutex);
|
|
- // wake up the decoder so it delivers more frames
|
|
- if (frameQueue.queue.isEmpty()) {
|
|
- wake();
|
|
- return {};
|
|
- }
|
|
- auto f = frameQueue.queue.dequeue();
|
|
- wake();
|
|
- return f;
|
|
-}
|
|
-
|
|
-void StreamDecoder::init()
|
|
-{
|
|
- qCDebug(qLcDecoder) << "Starting decoder";
|
|
-}
|
|
-
|
|
-bool StreamDecoder::shouldWait() const
|
|
-{
|
|
- if (eos.loadAcquire() || (hasNoPackets() && decoderHasNoFrames) || hasEnoughFrames())
|
|
- return true;
|
|
- return false;
|
|
-}
|
|
-
|
|
-void StreamDecoder::loop()
|
|
-{
|
|
- if (codec.context()->codec->type == AVMEDIA_TYPE_SUBTITLE)
|
|
- decodeSubtitle();
|
|
- else
|
|
- decode();
|
|
-}
|
|
-
|
|
-void StreamDecoder::decode()
|
|
-{
|
|
- Q_ASSERT(codec.context());
|
|
-
|
|
- AVFrame *frame = av_frame_alloc();
|
|
-// if (type() == 0)
|
|
-// qDebug() << "receiving frame";
|
|
- int res = avcodec_receive_frame(codec.context(), frame);
|
|
-
|
|
- if (res >= 0) {
|
|
- qint64 pts;
|
|
- if (frame->pts != AV_NOPTS_VALUE)
|
|
- pts = codec.toUs(frame->pts);
|
|
- else
|
|
- pts = codec.toUs(frame->best_effort_timestamp);
|
|
- addFrame(Frame{frame, codec, pts});
|
|
- } else if (res == AVERROR(EOF) || res == AVERROR_EOF) {
|
|
- eos.storeRelease(true);
|
|
- av_frame_free(&frame);
|
|
- timeOut = -1;
|
|
- return;
|
|
- } else if (res != AVERROR(EAGAIN)) {
|
|
- qWarning() << "error in decoder" << res << err2str(res);
|
|
- av_frame_free(&frame);
|
|
- return;
|
|
- } else {
|
|
- // EAGAIN
|
|
- decoderHasNoFrames = true;
|
|
- av_frame_free(&frame);
|
|
- }
|
|
-
|
|
- Packet packet = peekPacket();
|
|
- if (!packet.isValid()) {
|
|
- timeOut = -1;
|
|
- return;
|
|
- }
|
|
-
|
|
- res = avcodec_send_packet(codec.context(), packet.avPacket());
|
|
- if (res != AVERROR(EAGAIN)) {
|
|
- takePacket();
|
|
- }
|
|
- decoderHasNoFrames = false;
|
|
-}
|
|
-
|
|
-void StreamDecoder::decodeSubtitle()
|
|
-{
|
|
- // qCDebug(qLcDecoder) << " decoding subtitle" << "has delay:" << (codec->codec->capabilities & AV_CODEC_CAP_DELAY);
|
|
- AVSubtitle subtitle;
|
|
- memset(&subtitle, 0, sizeof(subtitle));
|
|
- int gotSubtitle = 0;
|
|
- Packet packet = takePacket();
|
|
- if (!packet.isValid())
|
|
- return;
|
|
-
|
|
- int res = avcodec_decode_subtitle2(codec.context(), &subtitle, &gotSubtitle, packet.avPacket());
|
|
- // qCDebug(qLcDecoder) << " subtitle got:" << res << gotSubtitle << subtitle.format << Qt::hex << (quint64)subtitle.pts;
|
|
- if (res >= 0 && gotSubtitle) {
|
|
- // apparently the timestamps in the AVSubtitle structure are not always filled in
|
|
- // if they are missing, use the packets pts and duration values instead
|
|
- qint64 start, end;
|
|
- if (subtitle.pts == AV_NOPTS_VALUE) {
|
|
- start = codec.toUs(packet.avPacket()->pts);
|
|
- end = start + codec.toUs(packet.avPacket()->duration);
|
|
- } else {
|
|
- auto pts = timeStampUs(subtitle.pts, AVRational{1, AV_TIME_BASE});
|
|
- start = *pts + qint64(subtitle.start_display_time)*1000;
|
|
- end = *pts + qint64(subtitle.end_display_time)*1000;
|
|
- }
|
|
- // qCDebug(qLcDecoder) << " got subtitle (" << start << "--" << end << "):";
|
|
- QString text;
|
|
- for (uint i = 0; i < subtitle.num_rects; ++i) {
|
|
- const auto *r = subtitle.rects[i];
|
|
- // qCDebug(qLcDecoder) << " subtitletext:" << r->text << "/" << r->ass;
|
|
- if (i)
|
|
- text += QLatin1Char('\n');
|
|
- if (r->text)
|
|
- text += QString::fromUtf8(r->text);
|
|
- else {
|
|
- const char *ass = r->ass;
|
|
- int nCommas = 0;
|
|
- while (*ass) {
|
|
- if (nCommas == 9)
|
|
- break;
|
|
- if (*ass == ',')
|
|
- ++nCommas;
|
|
- ++ass;
|
|
- }
|
|
- text += QString::fromUtf8(ass);
|
|
- }
|
|
- }
|
|
- text.replace(QLatin1String("\\N"), QLatin1String("\n"));
|
|
- text.replace(QLatin1String("\\n"), QLatin1String("\n"));
|
|
- text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
|
|
- if (text.endsWith(QLatin1Char('\n')))
|
|
- text.chop(1);
|
|
-
|
|
-// qCDebug(qLcDecoder) << " >>> subtitle adding" << text << start << end;
|
|
- Frame sub{text, start, end - start};
|
|
- addFrame(sub);
|
|
- }
|
|
-}
|
|
-
|
|
-QPlatformMediaPlayer::TrackType StreamDecoder::type() const
|
|
-{
|
|
- switch (codec.stream()->codecpar->codec_type) {
|
|
- case AVMEDIA_TYPE_AUDIO:
|
|
- return QPlatformMediaPlayer::AudioStream;
|
|
- case AVMEDIA_TYPE_VIDEO:
|
|
- return QPlatformMediaPlayer::VideoStream;
|
|
- case AVMEDIA_TYPE_SUBTITLE:
|
|
- return QPlatformMediaPlayer::SubtitleStream;
|
|
- default:
|
|
- return QPlatformMediaPlayer::NTrackTypes;
|
|
- }
|
|
-}
|
|
-
|
|
-Renderer::Renderer(QPlatformMediaPlayer::TrackType type)
|
|
- : Thread()
|
|
- , type(type)
|
|
-{
|
|
- QString objectName;
|
|
- if (type == QPlatformMediaPlayer::AudioStream)
|
|
- objectName = QLatin1String("AudioRenderThread");
|
|
- else
|
|
- objectName = QLatin1String("VideoRenderThread");
|
|
- setObjectName(objectName);
|
|
-}
|
|
-
|
|
-void Renderer::setStream(StreamDecoder *stream)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- if (streamDecoder == stream)
|
|
- return;
|
|
- if (streamDecoder)
|
|
- streamDecoder->kill();
|
|
- streamDecoder = stream;
|
|
- if (streamDecoder)
|
|
- streamDecoder->setRenderer(this);
|
|
- streamChanged();
|
|
- wake();
|
|
-}
|
|
-
|
|
-void Renderer::killHelper()
|
|
-{
|
|
- if (streamDecoder)
|
|
- streamDecoder->kill();
|
|
- streamDecoder = nullptr;
|
|
-}
|
|
-
|
|
-bool Renderer::shouldWait() const
|
|
-{
|
|
- if (!streamDecoder)
|
|
- return true;
|
|
- if (!paused)
|
|
- return false;
|
|
- if (step)
|
|
- return false;
|
|
- return true;
|
|
-}
|
|
-
|
|
-
|
|
-void ClockedRenderer::setPaused(bool paused)
|
|
-{
|
|
- Clock::setPaused(paused);
|
|
- Renderer::setPaused(paused);
|
|
-}
|
|
-
|
|
-VideoRenderer::VideoRenderer(Decoder *decoder, QVideoSink *sink)
|
|
- : ClockedRenderer(decoder, QPlatformMediaPlayer::VideoStream)
|
|
- , sink(sink)
|
|
-{}
|
|
-
|
|
-void VideoRenderer::killHelper()
|
|
-{
|
|
- if (subtitleStreamDecoder)
|
|
- subtitleStreamDecoder->kill();
|
|
- subtitleStreamDecoder = nullptr;
|
|
- if (streamDecoder)
|
|
- streamDecoder->kill();
|
|
- streamDecoder = nullptr;
|
|
-}
|
|
-
|
|
-void VideoRenderer::setSubtitleStream(StreamDecoder *stream)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- qCDebug(qLcVideoRenderer) << "setting subtitle stream to" << stream;
|
|
- if (stream == subtitleStreamDecoder)
|
|
- return;
|
|
- if (subtitleStreamDecoder)
|
|
- subtitleStreamDecoder->kill();
|
|
- subtitleStreamDecoder = stream;
|
|
- if (subtitleStreamDecoder)
|
|
- subtitleStreamDecoder->setRenderer(this);
|
|
- sink->setSubtitleText({});
|
|
- wake();
|
|
-}
|
|
-
|
|
-void VideoRenderer::init()
|
|
-{
|
|
- qCDebug(qLcVideoRenderer) << "starting video renderer";
|
|
- ClockedRenderer::init();
|
|
-}
|
|
-
|
|
-void VideoRenderer::loop()
|
|
-{
|
|
- if (!streamDecoder) {
|
|
- timeOut = -1; // Avoid 100% CPU load before play()
|
|
- return;
|
|
- }
|
|
-
|
|
- Frame frame = streamDecoder->takeFrame();
|
|
- if (!frame.isValid()) {
|
|
- if (streamDecoder->isAtEnd()) {
|
|
- timeOut = -1;
|
|
- eos.storeRelease(true);
|
|
- mutex.unlock();
|
|
- emit atEnd();
|
|
- mutex.lock();
|
|
- return;
|
|
- }
|
|
- timeOut = 1;
|
|
-// qDebug() << "no valid frame" << timer.elapsed();
|
|
- return;
|
|
- }
|
|
- eos.storeRelease(false);
|
|
-// qCDebug(qLcVideoRenderer) << "received video frame" << frame.pts();
|
|
- if (frame.pts() < seekTime()) {
|
|
- qCDebug(qLcVideoRenderer) << " discarding" << frame.pts() << seekTime();
|
|
- return;
|
|
- }
|
|
-
|
|
- AVStream *stream = frame.codec()->stream();
|
|
- qint64 startTime = frame.pts();
|
|
- qint64 duration = (1000000*stream->avg_frame_rate.den + (stream->avg_frame_rate.num>>1))
|
|
- /stream->avg_frame_rate.num;
|
|
-
|
|
- if (sink) {
|
|
- qint64 startTime = frame.pts();
|
|
-// qDebug() << "RHI:" << accel.isNull() << accel.rhi() << sink->rhi();
|
|
-
|
|
- // in practice this only happens with mediacodec
|
|
- if (frame.codec()->hwAccel() && !frame.avFrame()->hw_frames_ctx) {
|
|
- HWAccel *hwaccel = frame.codec()->hwAccel();
|
|
- AVFrame *avframe = frame.avFrame();
|
|
- if (!hwaccel->hwFramesContext())
|
|
- hwaccel->createFramesContext(AVPixelFormat(avframe->format),
|
|
- { avframe->width, avframe->height });
|
|
-
|
|
- avframe->hw_frames_ctx = av_buffer_ref(hwaccel->hwFramesContextAsBuffer());
|
|
- }
|
|
-
|
|
- QFFmpegVideoBuffer *buffer = new QFFmpegVideoBuffer(frame.takeAVFrame());
|
|
- QVideoFrameFormat format(buffer->size(), buffer->pixelFormat());
|
|
- format.setColorSpace(buffer->colorSpace());
|
|
- format.setColorTransfer(buffer->colorTransfer());
|
|
- format.setColorRange(buffer->colorRange());
|
|
- format.setMaxLuminance(buffer->maxNits());
|
|
- QVideoFrame videoFrame(buffer, format);
|
|
- videoFrame.setStartTime(startTime);
|
|
- videoFrame.setEndTime(startTime + duration);
|
|
-// qDebug() << "Creating video frame" << startTime << (startTime + duration) << subtitleStreamDecoder;
|
|
-
|
|
- // add in subtitles
|
|
- const Frame *currentSubtitle = nullptr;
|
|
- if (subtitleStreamDecoder)
|
|
- currentSubtitle = subtitleStreamDecoder->lockAndPeekFrame();
|
|
-
|
|
- if (currentSubtitle && currentSubtitle->isValid()) {
|
|
-// qDebug() << "frame: subtitle" << currentSubtitle->text() << currentSubtitle->pts() << currentSubtitle->duration();
|
|
- qCDebug(qLcVideoRenderer) << " " << currentSubtitle->pts() << currentSubtitle->duration() << currentSubtitle->text();
|
|
- if (currentSubtitle->pts() <= startTime && currentSubtitle->end() > startTime) {
|
|
-// qCDebug(qLcVideoRenderer) << " setting text";
|
|
- sink->setSubtitleText(currentSubtitle->text());
|
|
- }
|
|
- if (currentSubtitle->end() < startTime) {
|
|
-// qCDebug(qLcVideoRenderer) << " removing subtitle item";
|
|
- sink->setSubtitleText({});
|
|
- subtitleStreamDecoder->removePeekedFrame();
|
|
- }
|
|
- } else {
|
|
- sink->setSubtitleText({});
|
|
- }
|
|
- if (subtitleStreamDecoder)
|
|
- subtitleStreamDecoder->unlockAndReleaseFrame();
|
|
-
|
|
-// qCDebug(qLcVideoRenderer) << " sending a video frame" << startTime << duration << decoder->baseTimer.elapsed();
|
|
- sink->setVideoFrame(videoFrame);
|
|
- doneStep();
|
|
- }
|
|
- const Frame *nextFrame = streamDecoder->lockAndPeekFrame();
|
|
- qint64 nextFrameTime = 0;
|
|
- if (nextFrame)
|
|
- nextFrameTime = nextFrame->pts();
|
|
- else
|
|
- nextFrameTime = startTime + duration;
|
|
- streamDecoder->unlockAndReleaseFrame();
|
|
- qint64 mtime = timeUpdated(startTime);
|
|
- timeOut = usecsTo(mtime, nextFrameTime) / 1000;
|
|
- // qCDebug(qLcVideoRenderer) << " next video frame in" << startTime << nextFrameTime <<
|
|
- // currentTime() << timeOut;
|
|
-}
|
|
-
|
|
-AudioRenderer::AudioRenderer(Decoder *decoder, QAudioOutput *output)
|
|
- : ClockedRenderer(decoder, QPlatformMediaPlayer::AudioStream)
|
|
- , output(output)
|
|
-{
|
|
- connect(output, &QAudioOutput::deviceChanged, this, &AudioRenderer::updateAudio);
|
|
- connect(output, &QAudioOutput::volumeChanged, this, &AudioRenderer::setSoundVolume);
|
|
-}
|
|
-
|
|
-void AudioRenderer::syncTo(qint64 usecs)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
-
|
|
- Clock::syncTo(usecs);
|
|
- audioBaseTime = usecs;
|
|
- processedBase = processedUSecs;
|
|
-}
|
|
-
|
|
-void AudioRenderer::setPlaybackRate(float rate, qint64 currentTime)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
-
|
|
- audioBaseTime = currentTime;
|
|
- processedBase = processedUSecs;
|
|
- Clock::setPlaybackRate(rate, currentTime);
|
|
- deviceChanged = true;
|
|
-}
|
|
-
|
|
-void AudioRenderer::updateOutput(const Codec *codec)
|
|
-{
|
|
- qCDebug(qLcAudioRenderer) << ">>>>>> updateOutput" << currentTime() << seekTime() << processedUSecs << isMaster();
|
|
- freeOutput();
|
|
- qCDebug(qLcAudioRenderer) << " " << currentTime() << seekTime() << processedUSecs;
|
|
-
|
|
- AVStream *audioStream = codec->stream();
|
|
-
|
|
- auto dev = output->device();
|
|
- format = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
|
|
- format.setChannelConfig(dev.channelConfiguration());
|
|
-
|
|
- initResempler(codec);
|
|
-
|
|
- audioSink = new QAudioSink(dev, format);
|
|
- audioSink->setVolume(output->volume());
|
|
-
|
|
- audioSink->setBufferSize(format.bytesForDuration(100000));
|
|
- audioDevice = audioSink->start();
|
|
-
|
|
- latencyUSecs = format.durationForBytes(audioSink->bufferSize()); // ### ideally get full latency
|
|
- qCDebug(qLcAudioRenderer) << " -> have an audio sink" << audioDevice;
|
|
-}
|
|
-
|
|
-void AudioRenderer::initResempler(const Codec *codec)
|
|
-{
|
|
- // init resampler. It's ok to always do this, as the resampler will be a no-op if
|
|
- // formats agree.
|
|
- AVSampleFormat requiredFormat = QFFmpegMediaFormatInfo::avSampleFormat(format.sampleFormat());
|
|
-
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- qCDebug(qLcAudioRenderer) << "init resampler" << requiredFormat
|
|
- << codec->stream()->codecpar->channels;
|
|
-#else
|
|
- qCDebug(qLcAudioRenderer) << "init resampler" << requiredFormat
|
|
- << codec->stream()->codecpar->ch_layout.nb_channels;
|
|
-#endif
|
|
-
|
|
- auto resamplerFormat = format;
|
|
- resamplerFormat.setSampleRate(qRound(format.sampleRate() / playbackRate()));
|
|
- resampler.reset(new Resampler(codec, resamplerFormat));
|
|
-}
|
|
-
|
|
-void AudioRenderer::freeOutput()
|
|
-{
|
|
- if (audioSink) {
|
|
- audioSink->reset();
|
|
- delete audioSink;
|
|
- audioSink = nullptr;
|
|
- audioDevice = nullptr;
|
|
- }
|
|
-
|
|
- bufferedData = {};
|
|
- bufferWritten = 0;
|
|
-
|
|
- audioBaseTime = currentTime();
|
|
- processedBase = 0;
|
|
- processedUSecs = writtenUSecs = 0;
|
|
-}
|
|
-
|
|
-void AudioRenderer::init()
|
|
-{
|
|
- qCDebug(qLcAudioRenderer) << "Starting audio renderer";
|
|
- ClockedRenderer::init();
|
|
-}
|
|
-
|
|
-void AudioRenderer::cleanup()
|
|
-{
|
|
- freeOutput();
|
|
-}
|
|
-
|
|
-void AudioRenderer::loop()
|
|
-{
|
|
- if (!streamDecoder) {
|
|
- timeOut = -1; // Avoid 100% CPU load before play()
|
|
- return;
|
|
- }
|
|
-
|
|
- if (deviceChanged)
|
|
- freeOutput();
|
|
- deviceChanged = false;
|
|
- doneStep();
|
|
-
|
|
- qint64 bytesWritten = 0;
|
|
- if (bufferedData.isValid()) {
|
|
- bytesWritten = audioDevice->write(bufferedData.constData<char>() + bufferWritten, bufferedData.byteCount() - bufferWritten);
|
|
- bufferWritten += bytesWritten;
|
|
- if (bufferWritten == bufferedData.byteCount()) {
|
|
- bufferedData = {};
|
|
- bufferWritten = 0;
|
|
- }
|
|
- processedUSecs = audioSink->processedUSecs();
|
|
- } else {
|
|
- Frame frame = streamDecoder->takeFrame();
|
|
- if (!frame.isValid()) {
|
|
- if (streamDecoder->isAtEnd()) {
|
|
- if (audioSink)
|
|
- processedUSecs = audioSink->processedUSecs();
|
|
- timeOut = -1;
|
|
- eos.storeRelease(true);
|
|
- mutex.unlock();
|
|
- emit atEnd();
|
|
- mutex.lock();
|
|
- return;
|
|
- }
|
|
- timeOut = 1;
|
|
- return;
|
|
- }
|
|
- eos.storeRelease(false);
|
|
- if (!audioSink)
|
|
- updateOutput(frame.codec());
|
|
-
|
|
- qint64 startTime = frame.pts();
|
|
- if (startTime < seekTime())
|
|
- return;
|
|
-
|
|
- if (!paused) {
|
|
- auto buffer = resampler->resample(frame.avFrame());
|
|
-
|
|
- if (output->isMuted())
|
|
- // This is somewhat inefficient, but it'll work
|
|
- memset(buffer.data<char>(), 0, buffer.byteCount());
|
|
-
|
|
- bytesWritten = audioDevice->write(buffer.constData<char>(), buffer.byteCount());
|
|
- if (bytesWritten < buffer.byteCount()) {
|
|
- bufferedData = buffer;
|
|
- bufferWritten = bytesWritten;
|
|
- }
|
|
-
|
|
- processedUSecs = audioSink->processedUSecs();
|
|
- }
|
|
- }
|
|
-
|
|
- qint64 duration = format.durationForBytes(bytesWritten);
|
|
- writtenUSecs += duration;
|
|
-
|
|
- timeOut = (writtenUSecs - processedUSecs - latencyUSecs)/1000;
|
|
- if (timeOut < 0)
|
|
- // Don't use a zero timeout if the sink didn't want any more data, rather wait for 10ms.
|
|
- timeOut = bytesWritten > 0 ? 0 : 10;
|
|
-
|
|
-// if (!bufferedData.isEmpty())
|
|
-// qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>> could not write all data" << (bufferedData.size() - bufferWritten);
|
|
-// qDebug() << "Audio: processed" << processedUSecs << "written" << writtenUSecs
|
|
-// << "delta" << (writtenUSecs - processedUSecs) << "timeOut" << timeOut;
|
|
-// qCDebug(qLcAudioRenderer) << " updating time to" << currentTimeNoLock();
|
|
- timeUpdated(audioBaseTime + qRound((processedUSecs - processedBase) * playbackRate()));
|
|
-}
|
|
-
|
|
-void AudioRenderer::streamChanged()
|
|
-{
|
|
- // mutex is already locked
|
|
- deviceChanged = true;
|
|
-}
|
|
-
|
|
-void AudioRenderer::updateAudio()
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- deviceChanged = true;
|
|
-}
|
|
-
|
|
-void AudioRenderer::setSoundVolume(float volume)
|
|
-{
|
|
- QMutexLocker locker(&mutex);
|
|
- if (audioSink)
|
|
- audioSink->setVolume(volume);
|
|
-}
|
|
-
|
|
-Decoder::Decoder()
|
|
-{
|
|
-}
|
|
-
|
|
-Decoder::~Decoder()
|
|
-{
|
|
- pause();
|
|
- if (videoRenderer)
|
|
- videoRenderer->kill();
|
|
- if (audioRenderer)
|
|
- audioRenderer->kill();
|
|
- if (demuxer)
|
|
- demuxer->kill();
|
|
-}
|
|
-
|
|
-static int read(void *opaque, uint8_t *buf, int buf_size)
|
|
-{
|
|
- auto *dev = static_cast<QIODevice *>(opaque);
|
|
- if (dev->atEnd())
|
|
- return AVERROR_EOF;
|
|
- return dev->read(reinterpret_cast<char *>(buf), buf_size);
|
|
-}
|
|
-
|
|
-static int64_t seek(void *opaque, int64_t offset, int whence)
|
|
-{
|
|
- QIODevice *dev = static_cast<QIODevice *>(opaque);
|
|
-
|
|
- if (dev->isSequential())
|
|
- return AVERROR(EINVAL);
|
|
-
|
|
- if (whence & AVSEEK_SIZE)
|
|
- return dev->size();
|
|
-
|
|
- whence &= ~AVSEEK_FORCE;
|
|
-
|
|
- if (whence == SEEK_CUR)
|
|
- offset += dev->pos();
|
|
- else if (whence == SEEK_END)
|
|
- offset += dev->size();
|
|
-
|
|
- if (!dev->seek(offset))
|
|
- return AVERROR(EINVAL);
|
|
- return offset;
|
|
-}
|
|
-
|
|
-static void insertVideoData(QMediaMetaData &metaData, AVStream *stream)
|
|
-{
|
|
- Q_ASSERT(stream);
|
|
- auto *codecPar = stream->codecpar;
|
|
- metaData.insert(QMediaMetaData::VideoBitRate, (int)codecPar->bit_rate);
|
|
- metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(QFFmpegMediaFormatInfo::videoCodecForAVCodecId(codecPar->codec_id)));
|
|
- metaData.insert(QMediaMetaData::Resolution, QSize(codecPar->width, codecPar->height));
|
|
- auto fr = toFloat(stream->avg_frame_rate);
|
|
- if (fr)
|
|
- metaData.insert(QMediaMetaData::VideoFrameRate, *fr);
|
|
-};
|
|
-
|
|
-static void insertAudioData(QMediaMetaData &metaData, AVStream *stream)
|
|
-{
|
|
- Q_ASSERT(stream);
|
|
- auto *codecPar = stream->codecpar;
|
|
- metaData.insert(QMediaMetaData::AudioBitRate, (int)codecPar->bit_rate);
|
|
- metaData.insert(QMediaMetaData::AudioCodec,
|
|
- QVariant::fromValue(QFFmpegMediaFormatInfo::audioCodecForAVCodecId(codecPar->codec_id)));
|
|
-};
|
|
-
|
|
-static int getDefaultStreamIndex(QList<Decoder::StreamInfo> &streams)
|
|
-{
|
|
- if (streams.empty())
|
|
- return -1;
|
|
- for (qsizetype i = 0; i < streams.size(); i++)
|
|
- if (streams[i].isDefault)
|
|
- return i;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void readStreams(const AVFormatContext *context,
|
|
- QList<Decoder::StreamInfo> (&map)[QPlatformMediaPlayer::NTrackTypes], qint64 &maxDuration)
|
|
-{
|
|
- maxDuration = 0;
|
|
-
|
|
- for (unsigned int i = 0; i < context->nb_streams; ++i) {
|
|
- auto *stream = context->streams[i];
|
|
- if (!stream)
|
|
- continue;
|
|
-
|
|
- auto *codecPar = stream->codecpar;
|
|
- if (!codecPar)
|
|
- continue;
|
|
-
|
|
- QMediaMetaData metaData = QFFmpegMetaData::fromAVMetaData(stream->metadata);
|
|
- bool isDefault = stream->disposition & AV_DISPOSITION_DEFAULT;
|
|
- QPlatformMediaPlayer::TrackType type = QPlatformMediaPlayer::VideoStream;
|
|
-
|
|
- switch (codecPar->codec_type) {
|
|
- case AVMEDIA_TYPE_UNKNOWN:
|
|
- case AVMEDIA_TYPE_DATA: ///< Opaque data information usually continuous
|
|
- case AVMEDIA_TYPE_ATTACHMENT: ///< Opaque data information usually sparse
|
|
- case AVMEDIA_TYPE_NB:
|
|
- continue;
|
|
- case AVMEDIA_TYPE_VIDEO:
|
|
- type = QPlatformMediaPlayer::VideoStream;
|
|
- insertVideoData(metaData, stream);
|
|
- break;
|
|
- case AVMEDIA_TYPE_AUDIO:
|
|
- type = QPlatformMediaPlayer::AudioStream;
|
|
- insertAudioData(metaData, stream);
|
|
- break;
|
|
- case AVMEDIA_TYPE_SUBTITLE:
|
|
- type = QPlatformMediaPlayer::SubtitleStream;
|
|
- break;
|
|
- }
|
|
-
|
|
- map[type].append({ (int)i, isDefault, metaData });
|
|
- auto maybeDuration = mul(1'000'000ll * stream->duration, stream->time_base);
|
|
- if (maybeDuration)
|
|
- maxDuration = qMax(maxDuration, *maybeDuration);
|
|
- }
|
|
-}
|
|
-
|
|
-void Decoder::setMedia(const QUrl &media, QIODevice *stream)
|
|
-{
|
|
- QByteArray url = media.toEncoded(QUrl::PreferLocalFile);
|
|
-
|
|
- AVFormatContext *context = nullptr;
|
|
- if (stream) {
|
|
- if (!stream->isOpen()) {
|
|
- if (!stream->open(QIODevice::ReadOnly)) {
|
|
- emit errorOccured(QMediaPlayer::ResourceError,
|
|
- QLatin1String("Could not open source device."));
|
|
- return;
|
|
- }
|
|
- }
|
|
- if (!stream->isSequential())
|
|
- stream->seek(0);
|
|
- context = avformat_alloc_context();
|
|
- constexpr int bufferSize = 32768;
|
|
- unsigned char *buffer = (unsigned char *)av_malloc(bufferSize);
|
|
- context->pb = avio_alloc_context(buffer, bufferSize, false, stream, ::read, nullptr, ::seek);
|
|
- }
|
|
-
|
|
- int ret = avformat_open_input(&context, url.constData(), nullptr, nullptr);
|
|
- if (ret < 0) {
|
|
- auto code = QMediaPlayer::ResourceError;
|
|
- if (ret == AVERROR(EACCES))
|
|
- code = QMediaPlayer::AccessDeniedError;
|
|
- else if (ret == AVERROR(EINVAL))
|
|
- code = QMediaPlayer::FormatError;
|
|
-
|
|
- emit errorOccured(code, QMediaPlayer::tr("Could not open file"));
|
|
- return;
|
|
- }
|
|
-
|
|
- ret = avformat_find_stream_info(context, nullptr);
|
|
- if (ret < 0) {
|
|
- emit errorOccured(QMediaPlayer::FormatError,
|
|
- QMediaPlayer::tr("Could not find stream information for media file"));
|
|
- avformat_free_context(context);
|
|
- return;
|
|
- }
|
|
-
|
|
-#ifndef QT_NO_DEBUG
|
|
- av_dump_format(context, 0, url.constData(), 0);
|
|
-#endif
|
|
-
|
|
- readStreams(context, m_streamMap, m_duration);
|
|
-
|
|
- m_requestedStreams[QPlatformMediaPlayer::VideoStream] = getDefaultStreamIndex(m_streamMap[QPlatformMediaPlayer::VideoStream]);
|
|
- m_requestedStreams[QPlatformMediaPlayer::AudioStream] = getDefaultStreamIndex(m_streamMap[QPlatformMediaPlayer::AudioStream]);
|
|
- m_requestedStreams[QPlatformMediaPlayer::SubtitleStream] = -1;
|
|
-
|
|
- m_metaData = QFFmpegMetaData::fromAVMetaData(context->metadata);
|
|
- m_metaData.insert(QMediaMetaData::FileFormat,
|
|
- QVariant::fromValue(QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(context->iformat)));
|
|
-
|
|
- if (m_requestedStreams[QPlatformMediaPlayer::VideoStream] >= 0)
|
|
- insertVideoData(m_metaData, context->streams[avStreamIndex(QPlatformMediaPlayer::VideoStream)]);
|
|
-
|
|
- if (m_requestedStreams[QPlatformMediaPlayer::AudioStream] >= 0)
|
|
- insertAudioData(m_metaData, context->streams[avStreamIndex(QPlatformMediaPlayer::AudioStream)]);
|
|
-
|
|
- m_isSeekable = !(context->ctx_flags & AVFMTCTX_UNSEEKABLE);
|
|
-
|
|
- demuxer = new Demuxer(this, context);
|
|
- demuxer->start();
|
|
-}
|
|
-
|
|
-int Decoder::activeTrack(QPlatformMediaPlayer::TrackType type)
|
|
-{
|
|
- return m_requestedStreams[type];
|
|
-}
|
|
-
|
|
-void Decoder::setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber)
|
|
-{
|
|
- if (streamNumber < 0 || streamNumber >= m_streamMap[type].size())
|
|
- streamNumber = -1;
|
|
- if (m_requestedStreams[type] == streamNumber)
|
|
- return;
|
|
- m_requestedStreams[type] = streamNumber;
|
|
- changeAVTrack(type);
|
|
-}
|
|
-
|
|
-void Decoder::setState(QMediaPlayer::PlaybackState state)
|
|
-{
|
|
- if (m_state == state)
|
|
- return;
|
|
-
|
|
- switch (state) {
|
|
- case QMediaPlayer::StoppedState:
|
|
- qCDebug(qLcDecoder) << "Decoder::stop";
|
|
- setPaused(true);
|
|
- if (demuxer)
|
|
- demuxer->stopDecoding();
|
|
- seek(0);
|
|
- if (videoSink)
|
|
- videoSink->setVideoFrame({});
|
|
- qCDebug(qLcDecoder) << "Decoder::stop: done";
|
|
- break;
|
|
- case QMediaPlayer::PausedState:
|
|
- qCDebug(qLcDecoder) << "Decoder::pause";
|
|
- setPaused(true);
|
|
- if (demuxer) {
|
|
- demuxer->startDecoding();
|
|
- demuxer->wake();
|
|
- if (m_state == QMediaPlayer::StoppedState)
|
|
- triggerStep();
|
|
- }
|
|
- break;
|
|
- case QMediaPlayer::PlayingState:
|
|
- qCDebug(qLcDecoder) << "Decoder::play";
|
|
- setPaused(false);
|
|
- if (demuxer)
|
|
- demuxer->startDecoding();
|
|
- break;
|
|
- }
|
|
- m_state = state;
|
|
-}
|
|
-
|
|
-void Decoder::setPaused(bool b)
|
|
-{
|
|
- clockController.setPaused(b);
|
|
-}
|
|
-
|
|
-void Decoder::triggerStep()
|
|
-{
|
|
- if (audioRenderer)
|
|
- audioRenderer->singleStep();
|
|
- if (videoRenderer)
|
|
- videoRenderer->singleStep();
|
|
-}
|
|
-
|
|
-void Decoder::setVideoSink(QVideoSink *sink)
|
|
-{
|
|
- qCDebug(qLcDecoder) << "setVideoSink" << sink;
|
|
- if (sink == videoSink)
|
|
- return;
|
|
- videoSink = sink;
|
|
- if (!videoSink || m_requestedStreams[QPlatformMediaPlayer::VideoStream] < 0) {
|
|
- if (videoRenderer) {
|
|
- videoRenderer->kill();
|
|
- videoRenderer = nullptr;
|
|
- }
|
|
- } else if (!videoRenderer) {
|
|
- videoRenderer = new VideoRenderer(this, sink);
|
|
- connect(videoRenderer, &Renderer::atEnd, this, &Decoder::streamAtEnd);
|
|
- videoRenderer->start();
|
|
- StreamDecoder *stream = demuxer->addStream(avStreamIndex(QPlatformMediaPlayer::VideoStream));
|
|
- videoRenderer->setStream(stream);
|
|
- stream = demuxer->addStream(avStreamIndex(QPlatformMediaPlayer::SubtitleStream));
|
|
- videoRenderer->setSubtitleStream(stream);
|
|
- }
|
|
-}
|
|
-
|
|
-void Decoder::setAudioSink(QPlatformAudioOutput *output)
|
|
-{
|
|
- if (audioOutput == output)
|
|
- return;
|
|
-
|
|
- qCDebug(qLcDecoder) << "setAudioSink" << audioOutput;
|
|
- audioOutput = output;
|
|
- if (!output || m_requestedStreams[QPlatformMediaPlayer::AudioStream] < 0) {
|
|
- if (audioRenderer) {
|
|
- audioRenderer->kill();
|
|
- audioRenderer = nullptr;
|
|
- }
|
|
- } else if (!audioRenderer) {
|
|
- audioRenderer = new AudioRenderer(this, output->q);
|
|
- connect(audioRenderer, &Renderer::atEnd, this, &Decoder::streamAtEnd);
|
|
- audioRenderer->start();
|
|
- auto *stream = demuxer->addStream(avStreamIndex(QPlatformMediaPlayer::AudioStream));
|
|
- audioRenderer->setStream(stream);
|
|
- }
|
|
-}
|
|
-
|
|
-void Decoder::changeAVTrack(QPlatformMediaPlayer::TrackType type)
|
|
-{
|
|
- if (!demuxer)
|
|
- return;
|
|
- qCDebug(qLcDecoder) << " applying to renderer.";
|
|
- if (m_state == QMediaPlayer::PlayingState)
|
|
- setPaused(true);
|
|
- auto *streamDecoder = demuxer->addStream(avStreamIndex(type));
|
|
- switch (type) {
|
|
- case QPlatformMediaPlayer::AudioStream:
|
|
- audioRenderer->setStream(streamDecoder);
|
|
- break;
|
|
- case QPlatformMediaPlayer::VideoStream:
|
|
- videoRenderer->setStream(streamDecoder);
|
|
- break;
|
|
- case QPlatformMediaPlayer::SubtitleStream:
|
|
- videoRenderer->setSubtitleStream(streamDecoder);
|
|
- break;
|
|
- default:
|
|
- Q_UNREACHABLE();
|
|
- }
|
|
- demuxer->seek(clockController.currentTime());
|
|
- if (m_state == QMediaPlayer::PlayingState)
|
|
- setPaused(false);
|
|
- else
|
|
- triggerStep();
|
|
-}
|
|
-
|
|
-void Decoder::seek(qint64 pos)
|
|
-{
|
|
- if (!demuxer)
|
|
- return;
|
|
- pos = qBound(0, pos, m_duration);
|
|
- demuxer->seek(pos);
|
|
- clockController.syncTo(pos);
|
|
- demuxer->wake();
|
|
- if (m_state == QMediaPlayer::PausedState)
|
|
- triggerStep();
|
|
-}
|
|
-
|
|
-void Decoder::setPlaybackRate(float rate)
|
|
-{
|
|
- clockController.setPlaybackRate(rate);
|
|
-}
|
|
-
|
|
-void Decoder::streamAtEnd()
|
|
-{
|
|
- if (audioRenderer && !audioRenderer->isAtEnd())
|
|
- return;
|
|
- if (videoRenderer && !videoRenderer->isAtEnd())
|
|
- return;
|
|
- pause();
|
|
-
|
|
- emit endOfStream();
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h
|
|
deleted file mode 100644
|
|
index 2ee61a68e..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h
|
|
+++ /dev/null
|
|
@@ -1,501 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGDECODER_P_H
|
|
-#define QFFMPEGDECODER_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpegthread_p.h"
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpegmediaplayer_p.h"
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#include "qffmpegclock_p.h"
|
|
-#include "qaudiobuffer.h"
|
|
-#include "qffmpegresampler_p.h"
|
|
-
|
|
-#include <private/qmultimediautils_p.h>
|
|
-#include <qshareddata.h>
|
|
-#include <qtimer.h>
|
|
-#include <qqueue.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QAudioSink;
|
|
-class QFFmpegAudioDecoder;
|
|
-class QFFmpegMediaPlayer;
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-class Resampler;
|
|
-
|
|
-// queue up max 16M of encoded data, that should always be enough
|
|
-// (it's around 2 secs of 4K HDR video, longer for almost all other formats)
|
|
-enum { MaxQueueSize = 16*1024*1024 };
|
|
-
|
|
-struct Packet
|
|
-{
|
|
- struct Data {
|
|
- Data(AVPacket *p)
|
|
- : packet(p)
|
|
- {}
|
|
- ~Data() {
|
|
- if (packet)
|
|
- av_packet_free(&packet);
|
|
- }
|
|
- QAtomicInt ref;
|
|
- AVPacket *packet = nullptr;
|
|
- };
|
|
- Packet() = default;
|
|
- Packet(AVPacket *p)
|
|
- : d(new Data(p))
|
|
- {}
|
|
-
|
|
- bool isValid() const { return !!d; }
|
|
- AVPacket *avPacket() const { return d->packet; }
|
|
-private:
|
|
- QExplicitlySharedDataPointer<Data> d;
|
|
-};
|
|
-
|
|
-struct Codec
|
|
-{
|
|
- struct AVCodecFreeContext { void operator()(AVCodecContext *ctx) { avcodec_free_context(&ctx); } };
|
|
- using UniqueAVCodecContext = std::unique_ptr<AVCodecContext, AVCodecFreeContext>;
|
|
- struct Data {
|
|
- Data(UniqueAVCodecContext &&context, AVStream *stream, std::unique_ptr<QFFmpeg::HWAccel> &&hwAccel);
|
|
- ~Data();
|
|
- QAtomicInt ref;
|
|
- UniqueAVCodecContext context;
|
|
- AVStream *stream = nullptr;
|
|
- std::unique_ptr<QFFmpeg::HWAccel> hwAccel;
|
|
- };
|
|
-
|
|
- static QMaybe<Codec> create(AVStream *);
|
|
-
|
|
- AVCodecContext *context() const { return d->context.get(); }
|
|
- AVStream *stream() const { return d->stream; }
|
|
- uint streamIndex() const { return d->stream->index; }
|
|
- HWAccel *hwAccel() const { return d->hwAccel.get(); }
|
|
- qint64 toMs(qint64 ts) const { return timeStampMs(ts, d->stream->time_base).value_or(0); }
|
|
- qint64 toUs(qint64 ts) const { return timeStampUs(ts, d->stream->time_base).value_or(0); }
|
|
-
|
|
-private:
|
|
- Codec(Data *data) : d(data) {}
|
|
- QExplicitlySharedDataPointer<Data> d;
|
|
-};
|
|
-
|
|
-
|
|
-struct Frame
|
|
-{
|
|
- struct Data {
|
|
- Data(AVFrame *f, const Codec &codec, qint64 pts)
|
|
- : codec(codec)
|
|
- , frame(f)
|
|
- , pts(pts)
|
|
- {}
|
|
- Data(const QString &text, qint64 pts, qint64 duration)
|
|
- : text(text), pts(pts), duration(duration)
|
|
- {}
|
|
- ~Data() {
|
|
- if (frame)
|
|
- av_frame_free(&frame);
|
|
- }
|
|
- QAtomicInt ref;
|
|
- std::optional<Codec> codec;
|
|
- AVFrame *frame = nullptr;
|
|
- QString text;
|
|
- qint64 pts = -1;
|
|
- qint64 duration = -1;
|
|
- };
|
|
- Frame() = default;
|
|
- Frame(AVFrame *f, const Codec &codec, qint64 pts)
|
|
- : d(new Data(f, codec, pts))
|
|
- {}
|
|
- Frame(const QString &text, qint64 pts, qint64 duration)
|
|
- : d(new Data(text, pts, duration))
|
|
- {}
|
|
- bool isValid() const { return !!d; }
|
|
-
|
|
- AVFrame *avFrame() const { return d->frame; }
|
|
- AVFrame *takeAVFrame() const {
|
|
- AVFrame *f = d->frame;
|
|
- d->frame = nullptr;
|
|
- return f;
|
|
- }
|
|
- const Codec *codec() const { return d->codec ? &d->codec.value() : nullptr; }
|
|
- qint64 pts() const { return d->pts; }
|
|
- qint64 duration() const { return d->duration; }
|
|
- qint64 end() const { return d->pts + d->duration; }
|
|
- QString text() const { return d->text; }
|
|
-private:
|
|
- QExplicitlySharedDataPointer<Data> d;
|
|
-};
|
|
-
|
|
-class Demuxer;
|
|
-class StreamDecoder;
|
|
-class Renderer;
|
|
-class AudioRenderer;
|
|
-class VideoRenderer;
|
|
-
|
|
-class Decoder : public QObject
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- Decoder();
|
|
- ~Decoder();
|
|
-
|
|
- void setMedia(const QUrl &media, QIODevice *stream);
|
|
-
|
|
- void init();
|
|
- void setState(QMediaPlayer::PlaybackState state);
|
|
- void play() {
|
|
- setState(QMediaPlayer::PlayingState);
|
|
- }
|
|
- void pause() {
|
|
- setState(QMediaPlayer::PausedState);
|
|
- }
|
|
- void stop() {
|
|
- setState(QMediaPlayer::StoppedState);
|
|
- }
|
|
-
|
|
- void triggerStep();
|
|
-
|
|
- void setVideoSink(QVideoSink *sink);
|
|
- void setAudioSink(QPlatformAudioOutput *output);
|
|
-
|
|
- void changeAVTrack(QPlatformMediaPlayer::TrackType type);
|
|
-
|
|
- void seek(qint64 pos);
|
|
- void setPlaybackRate(float rate);
|
|
-
|
|
- int activeTrack(QPlatformMediaPlayer::TrackType type);
|
|
- void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber);
|
|
-
|
|
- bool isSeekable() const
|
|
- {
|
|
- return m_isSeekable;
|
|
- }
|
|
-
|
|
-signals:
|
|
- void endOfStream();
|
|
- void errorOccured(int error, const QString &errorString);
|
|
- void positionChanged(qint64 time);
|
|
-
|
|
-public slots:
|
|
- void streamAtEnd();
|
|
-
|
|
-public:
|
|
- struct StreamInfo {
|
|
- int avStreamIndex = -1;
|
|
- bool isDefault = false;
|
|
- QMediaMetaData metaData;
|
|
- };
|
|
-
|
|
- // Accessed from multiple threads, but API is threadsafe
|
|
- ClockController clockController;
|
|
-
|
|
-private:
|
|
- void setPaused(bool b);
|
|
-
|
|
-protected:
|
|
- friend QFFmpegMediaPlayer;
|
|
-
|
|
- QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState;
|
|
- bool m_isSeekable = false;
|
|
-
|
|
- Demuxer *demuxer = nullptr;
|
|
- QVideoSink *videoSink = nullptr;
|
|
- Renderer *videoRenderer = nullptr;
|
|
- QPlatformAudioOutput *audioOutput = nullptr;
|
|
- Renderer *audioRenderer = nullptr;
|
|
-
|
|
- QList<StreamInfo> m_streamMap[QPlatformMediaPlayer::NTrackTypes];
|
|
- int m_requestedStreams[QPlatformMediaPlayer::NTrackTypes] = { -1, -1, -1 };
|
|
- qint64 m_duration = 0;
|
|
- QMediaMetaData m_metaData;
|
|
-
|
|
- int avStreamIndex(QPlatformMediaPlayer::TrackType type)
|
|
- {
|
|
- int i = m_requestedStreams[type];
|
|
- return i < 0 || i >= m_streamMap[type].size() ? -1 : m_streamMap[type][i].avStreamIndex;
|
|
- }
|
|
-};
|
|
-
|
|
-class Demuxer : public Thread
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- Demuxer(Decoder *decoder, AVFormatContext *context);
|
|
- ~Demuxer();
|
|
-
|
|
- StreamDecoder *addStream(int streamIndex);
|
|
- void removeStream(int streamIndex);
|
|
-
|
|
- bool isStopped() const
|
|
- {
|
|
- return m_isStopped.loadRelaxed();
|
|
- }
|
|
- void startDecoding()
|
|
- {
|
|
- m_isStopped.storeRelaxed(false);
|
|
- updateEnabledStreams();
|
|
- wake();
|
|
- }
|
|
- void stopDecoding();
|
|
-
|
|
- int seek(qint64 pos);
|
|
-
|
|
-private:
|
|
- void updateEnabledStreams();
|
|
- void sendFinalPacketToStreams();
|
|
-
|
|
- void init() override;
|
|
- void cleanup() override;
|
|
- bool shouldWait() const override;
|
|
- void loop() override;
|
|
-
|
|
- Decoder *decoder;
|
|
- AVFormatContext *context = nullptr;
|
|
- QList<StreamDecoder *> streamDecoders;
|
|
-
|
|
- QAtomicInteger<bool> m_isStopped = true;
|
|
- qint64 last_pts = -1;
|
|
-};
|
|
-
|
|
-
|
|
-class StreamDecoder : public Thread
|
|
-{
|
|
- Q_OBJECT
|
|
-protected:
|
|
- Demuxer *demuxer = nullptr;
|
|
- Renderer *m_renderer = nullptr;
|
|
-
|
|
- struct PacketQueue {
|
|
- mutable QMutex mutex;
|
|
- QQueue<Packet> queue;
|
|
- qint64 size = 0;
|
|
- qint64 duration = 0;
|
|
- };
|
|
- PacketQueue packetQueue;
|
|
-
|
|
- struct FrameQueue {
|
|
- mutable QMutex mutex;
|
|
- QQueue<Frame> queue;
|
|
- int maxSize = 3;
|
|
- };
|
|
- FrameQueue frameQueue;
|
|
- QAtomicInteger<bool> eos = false;
|
|
- bool decoderHasNoFrames = false;
|
|
-
|
|
-public:
|
|
- StreamDecoder(Demuxer *demuxer, const Codec &codec);
|
|
-
|
|
- void addPacket(AVPacket *packet);
|
|
-
|
|
- qint64 queuedPacketSize() const {
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- return packetQueue.size;
|
|
- }
|
|
- qint64 queuedDuration() const {
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- return packetQueue.duration;
|
|
- }
|
|
-
|
|
- const Frame *lockAndPeekFrame()
|
|
- {
|
|
- frameQueue.mutex.lock();
|
|
- return frameQueue.queue.isEmpty() ? nullptr : &frameQueue.queue.first();
|
|
- }
|
|
- void removePeekedFrame()
|
|
- {
|
|
- frameQueue.queue.takeFirst();
|
|
- wake();
|
|
- }
|
|
- void unlockAndReleaseFrame()
|
|
- {
|
|
- frameQueue.mutex.unlock();
|
|
- }
|
|
- Frame takeFrame();
|
|
-
|
|
- void flush();
|
|
-
|
|
- Codec codec;
|
|
-
|
|
- void setRenderer(Renderer *r);
|
|
- Renderer *renderer() const { return m_renderer; }
|
|
-
|
|
- bool isAtEnd() const { return eos.loadAcquire(); }
|
|
-
|
|
- void killHelper() override;
|
|
-
|
|
-private:
|
|
- Packet takePacket();
|
|
- Packet peekPacket();
|
|
-
|
|
- void addFrame(const Frame &f);
|
|
-
|
|
- bool hasEnoughFrames() const
|
|
- {
|
|
- QMutexLocker locker(&frameQueue.mutex);
|
|
- return frameQueue.queue.size() >= frameQueue.maxSize;
|
|
- }
|
|
- bool hasNoPackets() const
|
|
- {
|
|
- QMutexLocker locker(&packetQueue.mutex);
|
|
- return packetQueue.queue.isEmpty();
|
|
- }
|
|
-
|
|
- void init() override;
|
|
- bool shouldWait() const override;
|
|
- void loop() override;
|
|
-
|
|
- void decode();
|
|
- void decodeSubtitle();
|
|
-
|
|
- QPlatformMediaPlayer::TrackType type() const;
|
|
-};
|
|
-
|
|
-class Renderer : public Thread
|
|
-{
|
|
- Q_OBJECT
|
|
-protected:
|
|
- QPlatformMediaPlayer::TrackType type;
|
|
-
|
|
- bool step = false;
|
|
- bool paused = true;
|
|
- StreamDecoder *streamDecoder = nullptr;
|
|
- QAtomicInteger<bool> eos = false;
|
|
-
|
|
-public:
|
|
- Renderer(QPlatformMediaPlayer::TrackType type);
|
|
-
|
|
- void setPaused(bool p) {
|
|
- QMutexLocker locker(&mutex);
|
|
- paused = p;
|
|
- if (!p)
|
|
- wake();
|
|
- }
|
|
- void singleStep() {
|
|
- QMutexLocker locker(&mutex);
|
|
- if (!paused)
|
|
- return;
|
|
- step = true;
|
|
- wake();
|
|
- }
|
|
- void doneStep() {
|
|
- step = false;
|
|
- }
|
|
- bool isAtEnd() { return !streamDecoder || eos.loadAcquire(); }
|
|
-
|
|
- void setStream(StreamDecoder *stream);
|
|
- virtual void setSubtitleStream(StreamDecoder *) {}
|
|
-
|
|
- void killHelper() override;
|
|
-
|
|
- virtual void streamChanged() {}
|
|
-
|
|
-Q_SIGNALS:
|
|
- void atEnd();
|
|
-
|
|
-protected:
|
|
- bool shouldWait() const override;
|
|
-
|
|
-public:
|
|
-};
|
|
-
|
|
-class ClockedRenderer : public Renderer, public Clock
|
|
-{
|
|
-public:
|
|
- ClockedRenderer(Decoder *decoder, QPlatformMediaPlayer::TrackType type)
|
|
- : Renderer(type)
|
|
- , Clock(&decoder->clockController)
|
|
- {
|
|
- }
|
|
- ~ClockedRenderer()
|
|
- {
|
|
- }
|
|
- void setPaused(bool paused) override;
|
|
-};
|
|
-
|
|
-class VideoRenderer : public ClockedRenderer
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
- StreamDecoder *subtitleStreamDecoder = nullptr;
|
|
-public:
|
|
- VideoRenderer(Decoder *decoder, QVideoSink *sink);
|
|
-
|
|
- void killHelper() override;
|
|
-
|
|
- void setSubtitleStream(StreamDecoder *stream) override;
|
|
-private:
|
|
-
|
|
- void init() override;
|
|
- void loop() override;
|
|
-
|
|
- QVideoSink *sink;
|
|
-};
|
|
-
|
|
-class AudioRenderer : public ClockedRenderer
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- AudioRenderer(Decoder *decoder, QAudioOutput *output);
|
|
- ~AudioRenderer() = default;
|
|
-
|
|
- // Clock interface
|
|
- void syncTo(qint64 usecs) override;
|
|
- void setPlaybackRate(float rate, qint64 currentTime) override;
|
|
-
|
|
-private slots:
|
|
- void updateAudio();
|
|
- void setSoundVolume(float volume);
|
|
-
|
|
-private:
|
|
- void updateOutput(const Codec *codec);
|
|
- void initResempler(const Codec *codec);
|
|
- void freeOutput();
|
|
-
|
|
- void init() override;
|
|
- void cleanup() override;
|
|
- void loop() override;
|
|
- void streamChanged() override;
|
|
- Type type() const override { return AudioClock; }
|
|
-
|
|
- int outputSamples(int inputSamples) {
|
|
- return qRound(inputSamples/playbackRate());
|
|
- }
|
|
-
|
|
- // Used for timing update calculations based on processed data
|
|
- qint64 audioBaseTime = 0;
|
|
- qint64 processedBase = 0;
|
|
- qint64 processedUSecs = 0;
|
|
-
|
|
- bool deviceChanged = false;
|
|
- QAudioOutput *output = nullptr;
|
|
- qint64 writtenUSecs = 0;
|
|
- qint64 latencyUSecs = 0;
|
|
-
|
|
- QAudioFormat format;
|
|
- QAudioSink *audioSink = nullptr;
|
|
- QIODevice *audioDevice = nullptr;
|
|
- std::unique_ptr<Resampler> resampler;
|
|
- QAudioBuffer bufferedData;
|
|
- qsizetype bufferWritten = 0;
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
-
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp
|
|
deleted file mode 100644
|
|
index 86e33c83c..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp
|
|
+++ /dev/null
|
|
@@ -1,557 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include "qffmpegencoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpegvideoframeencoder_p.h"
|
|
-#include "private/qmultimediautils_p.h"
|
|
-
|
|
-#include <qdebug.h>
|
|
-#include <qiodevice.h>
|
|
-#include <qaudiosource.h>
|
|
-#include <qaudiobuffer.h>
|
|
-#include "qffmpegaudioinput_p.h"
|
|
-#include <private/qplatformcamera_p.h>
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "qffmpegmediametadata_p.h"
|
|
-#include "qffmpegencoderoptions_p.h"
|
|
-
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/pixdesc.h>
|
|
-#include <libavutil/common.h>
|
|
-}
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcFFmpegEncoder, "qt.multimedia.ffmpeg.encoder")
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-Encoder::Encoder(const QMediaEncoderSettings &settings, const QUrl &url)
|
|
- : settings(settings)
|
|
-{
|
|
- const AVOutputFormat *avFormat = QFFmpegMediaFormatInfo::outputFormatForFileFormat(settings.fileFormat());
|
|
-
|
|
- formatContext = avformat_alloc_context();
|
|
- formatContext->oformat = const_cast<AVOutputFormat *>(avFormat); // constness varies
|
|
-
|
|
- QByteArray encoded = url.toEncoded();
|
|
- formatContext->url = (char *)av_malloc(encoded.size() + 1);
|
|
- memcpy(formatContext->url, encoded.constData(), encoded.size() + 1);
|
|
- formatContext->pb = nullptr;
|
|
- avio_open2(&formatContext->pb, formatContext->url, AVIO_FLAG_WRITE, nullptr, nullptr);
|
|
- qCDebug(qLcFFmpegEncoder) << "opened" << formatContext->url;
|
|
-
|
|
- muxer = new Muxer(this);
|
|
-}
|
|
-
|
|
-Encoder::~Encoder()
|
|
-{
|
|
-}
|
|
-
|
|
-void Encoder::addAudioInput(QFFmpegAudioInput *input)
|
|
-{
|
|
- audioEncode = new AudioEncoder(this, input, settings);
|
|
- connect(input, &QFFmpegAudioInput::newAudioBuffer, this, &Encoder::newAudioBuffer);
|
|
- input->setRunning(true);
|
|
-}
|
|
-
|
|
-void Encoder::addVideoSource(QPlatformCamera *source)
|
|
-{
|
|
- videoEncode = new VideoEncoder(this, source, settings);
|
|
- connect(source, &QPlatformCamera::newVideoFrame, this, &Encoder::newVideoFrame);
|
|
-}
|
|
-
|
|
-void Encoder::start()
|
|
-{
|
|
- qCDebug(qLcFFmpegEncoder) << "Encoder::start!";
|
|
-
|
|
- formatContext->metadata = QFFmpegMetaData::toAVMetaData(metaData);
|
|
-
|
|
- int res = avformat_write_header(formatContext, nullptr);
|
|
- if (res < 0)
|
|
- qWarning() << "could not write header" << res;
|
|
-
|
|
- muxer->start();
|
|
- if (audioEncode)
|
|
- audioEncode->start();
|
|
- if (videoEncode)
|
|
- videoEncode->start();
|
|
- isRecording = true;
|
|
-}
|
|
-
|
|
-void EncodingFinalizer::run()
|
|
-{
|
|
- if (encoder->audioEncode)
|
|
- encoder->audioEncode->kill();
|
|
- if (encoder->videoEncode)
|
|
- encoder->videoEncode->kill();
|
|
- encoder->muxer->kill();
|
|
-
|
|
- int res = av_write_trailer(encoder->formatContext);
|
|
- if (res < 0)
|
|
- qWarning() << "could not write trailer" << res;
|
|
-
|
|
- avformat_free_context(encoder->formatContext);
|
|
- qDebug() << " done finalizing.";
|
|
- emit encoder->finalizationDone();
|
|
- delete encoder;
|
|
- deleteLater();
|
|
-}
|
|
-
|
|
-void Encoder::finalize()
|
|
-{
|
|
- qDebug() << ">>>>>>>>>>>>>>> finalize";
|
|
-
|
|
- isRecording = false;
|
|
- auto *finalizer = new EncodingFinalizer(this);
|
|
- finalizer->start();
|
|
-}
|
|
-
|
|
-void Encoder::setPaused(bool p)
|
|
-{
|
|
- if (audioEncode)
|
|
- audioEncode->setPaused(p);
|
|
- if (videoEncode)
|
|
- videoEncode->setPaused(p);
|
|
-}
|
|
-
|
|
-void Encoder::setMetaData(const QMediaMetaData &metaData)
|
|
-{
|
|
- this->metaData = metaData;
|
|
-}
|
|
-
|
|
-void Encoder::newAudioBuffer(const QAudioBuffer &buffer)
|
|
-{
|
|
- if (audioEncode && isRecording)
|
|
- audioEncode->addBuffer(buffer);
|
|
-}
|
|
-
|
|
-void Encoder::newVideoFrame(const QVideoFrame &frame)
|
|
-{
|
|
- if (videoEncode && isRecording)
|
|
- videoEncode->addFrame(frame);
|
|
-}
|
|
-
|
|
-void Encoder::newTimeStamp(qint64 time)
|
|
-{
|
|
- QMutexLocker locker(&timeMutex);
|
|
- if (time > timeRecorded) {
|
|
- timeRecorded = time;
|
|
- emit durationChanged(time);
|
|
- }
|
|
-}
|
|
-
|
|
-Muxer::Muxer(Encoder *encoder)
|
|
- : encoder(encoder)
|
|
-{
|
|
- setObjectName(QLatin1String("Muxer"));
|
|
-}
|
|
-
|
|
-void Muxer::addPacket(AVPacket *packet)
|
|
-{
|
|
-// qCDebug(qLcFFmpegEncoder) << "Muxer::addPacket" << packet->pts << packet->stream_index;
|
|
- QMutexLocker locker(&queueMutex);
|
|
- packetQueue.enqueue(packet);
|
|
- wake();
|
|
-}
|
|
-
|
|
-AVPacket *Muxer::takePacket()
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- if (packetQueue.isEmpty())
|
|
- return nullptr;
|
|
-// qCDebug(qLcFFmpegEncoder) << "Muxer::takePacket" << packetQueue.first()->pts;
|
|
- return packetQueue.dequeue();
|
|
-}
|
|
-
|
|
-void Muxer::init()
|
|
-{
|
|
-}
|
|
-
|
|
-void Muxer::cleanup()
|
|
-{
|
|
-}
|
|
-
|
|
-bool QFFmpeg::Muxer::shouldWait() const
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- return packetQueue.isEmpty();
|
|
-}
|
|
-
|
|
-void Muxer::loop()
|
|
-{
|
|
- auto *packet = takePacket();
|
|
-// qCDebug(qLcFFmpegEncoder) << "writing packet to file" << packet->pts << packet->duration << packet->stream_index;
|
|
- av_interleaved_write_frame(encoder->formatContext, packet);
|
|
-}
|
|
-
|
|
-
|
|
-static AVSampleFormat bestMatchingSampleFormat(AVSampleFormat requested, const AVSampleFormat *available)
|
|
-{
|
|
- if (!available)
|
|
- return requested;
|
|
-
|
|
- const AVSampleFormat *f = available;
|
|
- AVSampleFormat best = *f;
|
|
-/*
|
|
- enum {
|
|
- First,
|
|
- Planar,
|
|
- Exact,
|
|
- } score = First;
|
|
-*/
|
|
- for (; *f != AV_SAMPLE_FMT_NONE; ++f) {
|
|
- qCDebug(qLcFFmpegEncoder) << "format:" << *f;
|
|
- if (*f == requested) {
|
|
- best = *f;
|
|
-// score = Exact;
|
|
- break;
|
|
- }
|
|
-
|
|
- if (av_get_planar_sample_fmt(requested) == *f) {
|
|
-// score = Planar;
|
|
- best = *f;
|
|
- }
|
|
- }
|
|
- return best;
|
|
-}
|
|
-
|
|
-AudioEncoder::AudioEncoder(Encoder *encoder, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings)
|
|
- : input(input)
|
|
-{
|
|
- this->encoder = encoder;
|
|
-
|
|
- setObjectName(QLatin1String("AudioEncoder"));
|
|
- qCDebug(qLcFFmpegEncoder) << "AudioEncoder" << settings.audioCodec();
|
|
-
|
|
- format = input->device.preferredFormat();
|
|
- auto codecID = QFFmpegMediaFormatInfo::codecIdForAudioCodec(settings.audioCodec());
|
|
- Q_ASSERT(avformat_query_codec(encoder->formatContext->oformat, codecID, FF_COMPLIANCE_NORMAL));
|
|
-
|
|
- auto *avCodec = avcodec_find_encoder(codecID);
|
|
-
|
|
- AVSampleFormat requested = QFFmpegMediaFormatInfo::avSampleFormat(format.sampleFormat());
|
|
- AVSampleFormat bestSampleFormat = bestMatchingSampleFormat(requested, avCodec->sample_fmts);
|
|
-
|
|
- stream = avformat_new_stream(encoder->formatContext, nullptr);
|
|
- stream->id = encoder->formatContext->nb_streams - 1;
|
|
- stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
|
|
- stream->codecpar->codec_id = codecID;
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- stream->codecpar->channel_layout = av_get_default_channel_layout(format.channelCount());
|
|
- stream->codecpar->channels = format.channelCount();
|
|
-#else
|
|
- av_channel_layout_default(&stream->codecpar->ch_layout, format.channelCount());
|
|
-#endif
|
|
- stream->codecpar->sample_rate = format.sampleRate();
|
|
- stream->codecpar->frame_size = 1024;
|
|
- stream->codecpar->format = bestSampleFormat;
|
|
- stream->time_base = AVRational{ 1, format.sampleRate() };
|
|
-
|
|
- Q_ASSERT(avCodec);
|
|
- codec = avcodec_alloc_context3(avCodec);
|
|
- avcodec_parameters_to_context(codec, stream->codecpar);
|
|
-
|
|
- AVDictionary *opts = nullptr;
|
|
- applyAudioEncoderOptions(settings, avCodec->name, codec, &opts);
|
|
-
|
|
- int res = avcodec_open2(codec, avCodec, &opts);
|
|
- qCDebug(qLcFFmpegEncoder) << "audio codec opened" << res;
|
|
- qCDebug(qLcFFmpegEncoder) << "audio codec params: fmt=" << codec->sample_fmt << "rate=" << codec->sample_rate;
|
|
-
|
|
- if (codec->sample_fmt != requested) {
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context
|
|
- codec->channel_layout, // out_ch_layout
|
|
- codec->sample_fmt, // out_sample_fmt
|
|
- codec->sample_rate, // out_sample_rate
|
|
- av_get_default_channel_layout(format.channelCount()), // in_ch_layout
|
|
- requested, // in_sample_fmt
|
|
- format.sampleRate(), // in_sample_rate
|
|
- 0, // log_offset
|
|
- nullptr);
|
|
-#else
|
|
- AVChannelLayout in_ch_layout = {};
|
|
- av_channel_layout_default(&in_ch_layout, format.channelCount());
|
|
- swr_alloc_set_opts2(&resampler, // we're allocating a new context
|
|
- &codec->ch_layout, codec->sample_fmt, codec->sample_rate,
|
|
- &in_ch_layout, requested, format.sampleRate(),
|
|
- 0, nullptr);
|
|
-#endif
|
|
-
|
|
- swr_init(resampler);
|
|
- }
|
|
-}
|
|
-
|
|
-void AudioEncoder::addBuffer(const QAudioBuffer &buffer)
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- if (!paused.loadRelaxed()) {
|
|
- audioBufferQueue.enqueue(buffer);
|
|
- wake();
|
|
- }
|
|
-}
|
|
-
|
|
-QAudioBuffer AudioEncoder::takeBuffer()
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- if (audioBufferQueue.isEmpty())
|
|
- return QAudioBuffer();
|
|
- return audioBufferQueue.dequeue();
|
|
-}
|
|
-
|
|
-void AudioEncoder::init()
|
|
-{
|
|
- if (input) {
|
|
- input->setFrameSize(codec->frame_size);
|
|
- }
|
|
- qCDebug(qLcFFmpegEncoder) << "AudioEncoder::init started audio device thread.";
|
|
-}
|
|
-
|
|
-void AudioEncoder::cleanup()
|
|
-{
|
|
- while (!audioBufferQueue.isEmpty())
|
|
- loop();
|
|
- while (avcodec_send_frame(codec, nullptr) == AVERROR(EAGAIN))
|
|
- retrievePackets();
|
|
- retrievePackets();
|
|
-}
|
|
-
|
|
-bool AudioEncoder::shouldWait() const
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- return audioBufferQueue.isEmpty();
|
|
-}
|
|
-
|
|
-void AudioEncoder::retrievePackets()
|
|
-{
|
|
- while (1) {
|
|
- AVPacket *packet = av_packet_alloc();
|
|
- int ret = avcodec_receive_packet(codec, packet);
|
|
- if (ret < 0) {
|
|
- av_packet_unref(packet);
|
|
- if (ret != AVERROR(EOF))
|
|
- break;
|
|
- if (ret != AVERROR(EAGAIN)) {
|
|
- char errStr[1024];
|
|
- av_strerror(ret, errStr, 1024);
|
|
- qCDebug(qLcFFmpegEncoder) << "receive packet" << ret << errStr;
|
|
- }
|
|
- break;
|
|
- }
|
|
-
|
|
- // qCDebug(qLcFFmpegEncoder) << "writing video packet" << packet->size << packet->pts << timeStamp(packet->pts, stream->time_base) << packet->stream_index;
|
|
- packet->stream_index = stream->id;
|
|
- encoder->muxer->addPacket(packet);
|
|
- }
|
|
-}
|
|
-
|
|
-void AudioEncoder::loop()
|
|
-{
|
|
- QAudioBuffer buffer = takeBuffer();
|
|
- if (!buffer.isValid() || paused.loadAcquire())
|
|
- return;
|
|
-
|
|
-// qCDebug(qLcFFmpegEncoder) << "new audio buffer" << buffer.byteCount() << buffer.format() << buffer.frameCount() << codec->frame_size;
|
|
- retrievePackets();
|
|
-
|
|
- AVFrame *frame = av_frame_alloc();
|
|
- frame->format = codec->sample_fmt;
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- frame->channel_layout = codec->channel_layout;
|
|
- frame->channels = codec->channels;
|
|
-#else
|
|
- frame->ch_layout = codec->ch_layout;
|
|
-#endif
|
|
- frame->sample_rate = codec->sample_rate;
|
|
- frame->nb_samples = buffer.frameCount();
|
|
- if (frame->nb_samples)
|
|
- av_frame_get_buffer(frame, 0);
|
|
-
|
|
- if (resampler) {
|
|
- const uint8_t *data = buffer.constData<uint8_t>();
|
|
- swr_convert(resampler, frame->extended_data, frame->nb_samples, &data, frame->nb_samples);
|
|
- } else {
|
|
- memcpy(frame->buf[0]->data, buffer.constData<uint8_t>(), buffer.byteCount());
|
|
- }
|
|
-
|
|
- frame->pts = samplesWritten;
|
|
- samplesWritten += buffer.frameCount();
|
|
-
|
|
- qint64 time = format.durationForFrames(samplesWritten);
|
|
- encoder->newTimeStamp(time/1000);
|
|
-
|
|
-// qCDebug(qLcFFmpegEncoder) << "sending audio frame" << buffer.byteCount() << frame->pts << ((double)buffer.frameCount()/frame->sample_rate);
|
|
- int ret = avcodec_send_frame(codec, frame);
|
|
- if (ret < 0) {
|
|
- char errStr[1024];
|
|
- av_strerror(ret, errStr, 1024);
|
|
-// qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << errStr;
|
|
- }
|
|
-}
|
|
-
|
|
-VideoEncoder::VideoEncoder(Encoder *encoder, QPlatformCamera *camera, const QMediaEncoderSettings &settings)
|
|
- : m_encoderSettings(settings)
|
|
- , m_camera(camera)
|
|
-{
|
|
- this->encoder = encoder;
|
|
-
|
|
- setObjectName(QLatin1String("VideoEncoder"));
|
|
- qCDebug(qLcFFmpegEncoder) << "VideoEncoder" << settings.videoCodec();
|
|
-
|
|
- auto format = m_camera->cameraFormat();
|
|
- std::optional<AVPixelFormat> hwFormat = camera->ffmpegHWPixelFormat()
|
|
- ? AVPixelFormat(*camera->ffmpegHWPixelFormat())
|
|
- : std::optional<AVPixelFormat>{};
|
|
-
|
|
- AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat());
|
|
- AVPixelFormat pixelFormat = hwFormat ? *hwFormat : swFormat;
|
|
- frameEncoder = new VideoFrameEncoder(settings, format.resolution(), format.maxFrameRate(), pixelFormat, swFormat);
|
|
-
|
|
- frameEncoder->initWithFormatContext(encoder->formatContext);
|
|
-}
|
|
-
|
|
-VideoEncoder::~VideoEncoder()
|
|
-{
|
|
- delete frameEncoder;
|
|
-}
|
|
-
|
|
-void VideoEncoder::addFrame(const QVideoFrame &frame)
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- if (!paused.loadRelaxed()) {
|
|
- videoFrameQueue.enqueue(frame);
|
|
- wake();
|
|
- }
|
|
-}
|
|
-
|
|
-QVideoFrame VideoEncoder::takeFrame()
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- if (videoFrameQueue.isEmpty())
|
|
- return QVideoFrame();
|
|
- return videoFrameQueue.dequeue();
|
|
-}
|
|
-
|
|
-void VideoEncoder::retrievePackets()
|
|
-{
|
|
- if (!frameEncoder)
|
|
- return;
|
|
- while (AVPacket *packet = frameEncoder->retrievePacket())
|
|
- encoder->muxer->addPacket(packet);
|
|
-}
|
|
-
|
|
-void VideoEncoder::init()
|
|
-{
|
|
- qCDebug(qLcFFmpegEncoder) << "VideoEncoder::init started video device thread.";
|
|
- bool ok = frameEncoder->open();
|
|
- if (!ok)
|
|
- encoder->error(QMediaRecorder::ResourceError, "Could not initialize encoder");
|
|
-}
|
|
-
|
|
-void VideoEncoder::cleanup()
|
|
-{
|
|
- while (!videoFrameQueue.isEmpty())
|
|
- loop();
|
|
- if (frameEncoder) {
|
|
- while (frameEncoder->sendFrame(nullptr) == AVERROR(EAGAIN))
|
|
- retrievePackets();
|
|
- retrievePackets();
|
|
- }
|
|
-}
|
|
-
|
|
-bool VideoEncoder::shouldWait() const
|
|
-{
|
|
- QMutexLocker locker(&queueMutex);
|
|
- return videoFrameQueue.isEmpty();
|
|
-}
|
|
-
|
|
-struct QVideoFrameHolder
|
|
-{
|
|
- QVideoFrame f;
|
|
- QImage i;
|
|
-};
|
|
-
|
|
-static void freeQVideoFrame(void *opaque, uint8_t *)
|
|
-{
|
|
- delete reinterpret_cast<QVideoFrameHolder *>(opaque);
|
|
-}
|
|
-
|
|
-void VideoEncoder::loop()
|
|
-{
|
|
- if (paused.loadAcquire())
|
|
- return;
|
|
-
|
|
- retrievePackets();
|
|
-
|
|
- auto frame = takeFrame();
|
|
- if (!frame.isValid())
|
|
- return;
|
|
-
|
|
- if (frameEncoder->isNull())
|
|
- return;
|
|
-
|
|
-// qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime();
|
|
-
|
|
- AVFrame *avFrame = nullptr;
|
|
-
|
|
- auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer());
|
|
- if (videoBuffer) {
|
|
- // ffmpeg video buffer, let's use the native AVFrame stored in there
|
|
- auto *hwFrame = videoBuffer->getHWFrame();
|
|
- if (hwFrame && hwFrame->format == frameEncoder->sourceFormat())
|
|
- avFrame = av_frame_clone(hwFrame);
|
|
- }
|
|
-
|
|
- if (!avFrame) {
|
|
- frame.map(QVideoFrame::ReadOnly);
|
|
- auto size = frame.size();
|
|
- avFrame = av_frame_alloc();
|
|
- avFrame->format = frameEncoder->sourceFormat();
|
|
- avFrame->width = size.width();
|
|
- avFrame->height = size.height();
|
|
- av_frame_get_buffer(avFrame, 0);
|
|
-
|
|
- for (int i = 0; i < 4; ++i) {
|
|
- avFrame->data[i] = const_cast<uint8_t *>(frame.bits(i));
|
|
- avFrame->linesize[i] = frame.bytesPerLine(i);
|
|
- }
|
|
-
|
|
- QImage img;
|
|
- if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
|
|
- // the QImage is cached inside the video frame, so we can take the pointer to the image data here
|
|
- img = frame.toImage();
|
|
- avFrame->data[0] = (uint8_t *)img.bits();
|
|
- avFrame->linesize[0] = img.bytesPerLine();
|
|
- }
|
|
-
|
|
- Q_ASSERT(avFrame->data[0]);
|
|
- // ensure the video frame and it's data is alive as long as it's being used in the encoder
|
|
- avFrame->opaque_ref = av_buffer_create(nullptr, 0, freeQVideoFrame, new QVideoFrameHolder{frame, img}, 0);
|
|
- }
|
|
-
|
|
- if (baseTime.loadAcquire() < 0) {
|
|
- baseTime.storeRelease(frame.startTime() - lastFrameTime);
|
|
-// qCDebug(qLcFFmpegEncoder) << ">>>> adjusting base time to" << baseTime.loadAcquire() << frame.startTime() << lastFrameTime;
|
|
- }
|
|
-
|
|
- qint64 time = frame.startTime() - baseTime.loadAcquire();
|
|
- lastFrameTime = frame.endTime() - baseTime.loadAcquire();
|
|
- avFrame->pts = frameEncoder->getPts(time);
|
|
-
|
|
- encoder->newTimeStamp(time/1000);
|
|
-
|
|
-// qCDebug(qLcFFmpegEncoder) << ">>> sending frame" << avFrame->pts << time;
|
|
- int ret = frameEncoder->sendFrame(avFrame);
|
|
- if (ret < 0) {
|
|
- qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << err2str(ret);
|
|
- encoder->error(QMediaRecorder::ResourceError, err2str(ret));
|
|
- }
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h
|
|
deleted file mode 100644
|
|
index b673b718c..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h
|
|
+++ /dev/null
|
|
@@ -1,197 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGENCODER_P_H
|
|
-#define QFFMPEGENCODER_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpegthread_p.h"
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-#include <private/qplatformmediarecorder_p.h>
|
|
-#include <qaudioformat.h>
|
|
-#include <qaudiobuffer.h>
|
|
-
|
|
-#include <qqueue.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QFFmpegAudioInput;
|
|
-class QVideoFrame;
|
|
-class QPlatformCamera;
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-class Encoder;
|
|
-class Muxer;
|
|
-class AudioEncoder;
|
|
-class VideoEncoder;
|
|
-class VideoFrameEncoder;
|
|
-
|
|
-class EncodingFinalizer : public QThread
|
|
-{
|
|
-public:
|
|
- EncodingFinalizer(Encoder *e)
|
|
- : encoder(e)
|
|
- {}
|
|
- void run() override;
|
|
-
|
|
- Encoder *encoder = nullptr;
|
|
-};
|
|
-
|
|
-class Encoder : public QObject
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- Encoder(const QMediaEncoderSettings &settings, const QUrl &url);
|
|
- ~Encoder();
|
|
-
|
|
- void addAudioInput(QFFmpegAudioInput *input);
|
|
- void addVideoSource(QPlatformCamera *source);
|
|
-
|
|
- void start();
|
|
- void finalize();
|
|
-
|
|
- void setPaused(bool p);
|
|
-
|
|
- void setMetaData(const QMediaMetaData &metaData);
|
|
-
|
|
-public Q_SLOTS:
|
|
- void newAudioBuffer(const QAudioBuffer &buffer);
|
|
- void newVideoFrame(const QVideoFrame &frame);
|
|
- void newTimeStamp(qint64 time);
|
|
-
|
|
-Q_SIGNALS:
|
|
- void durationChanged(qint64 duration);
|
|
- void error(QMediaRecorder::Error code, const QString &description);
|
|
- void finalizationDone();
|
|
-
|
|
-public:
|
|
-
|
|
- QMediaEncoderSettings settings;
|
|
- QMediaMetaData metaData;
|
|
- AVFormatContext *formatContext = nullptr;
|
|
- Muxer *muxer = nullptr;
|
|
- bool isRecording = false;
|
|
-
|
|
- AudioEncoder *audioEncode = nullptr;
|
|
- VideoEncoder *videoEncode = nullptr;
|
|
-
|
|
- QMutex timeMutex;
|
|
- qint64 timeRecorded = 0;
|
|
-};
|
|
-
|
|
-
|
|
-class Muxer : public Thread
|
|
-{
|
|
- mutable QMutex queueMutex;
|
|
- QQueue<AVPacket *> packetQueue;
|
|
-public:
|
|
- Muxer(Encoder *encoder);
|
|
-
|
|
- void addPacket(AVPacket *);
|
|
-
|
|
-private:
|
|
- AVPacket *takePacket();
|
|
-
|
|
- void init() override;
|
|
- void cleanup() override;
|
|
- bool shouldWait() const override;
|
|
- void loop() override;
|
|
-
|
|
- Encoder *encoder;
|
|
-};
|
|
-
|
|
-class EncoderThread : public Thread
|
|
-{
|
|
-public:
|
|
- virtual void setPaused(bool b)
|
|
- {
|
|
- paused.storeRelease(b);
|
|
- }
|
|
-
|
|
-protected:
|
|
- QAtomicInteger<bool> paused = false;
|
|
- Encoder *encoder = nullptr;
|
|
-};
|
|
-
|
|
-class AudioEncoder : public EncoderThread
|
|
-{
|
|
- mutable QMutex queueMutex;
|
|
- QQueue<QAudioBuffer> audioBufferQueue;
|
|
-public:
|
|
- AudioEncoder(Encoder *encoder, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings);
|
|
-
|
|
- void addBuffer(const QAudioBuffer &buffer);
|
|
-
|
|
- QFFmpegAudioInput *audioInput() const { return input; }
|
|
-
|
|
-private:
|
|
- QAudioBuffer takeBuffer();
|
|
- void retrievePackets();
|
|
-
|
|
- void init() override;
|
|
- void cleanup() override;
|
|
- bool shouldWait() const override;
|
|
- void loop() override;
|
|
-
|
|
- AVStream *stream = nullptr;
|
|
- AVCodecContext *codec = nullptr;
|
|
- QFFmpegAudioInput *input;
|
|
- QAudioFormat format;
|
|
-
|
|
- SwrContext *resampler = nullptr;
|
|
- qint64 samplesWritten = 0;
|
|
-};
|
|
-
|
|
-
|
|
-class VideoEncoder : public EncoderThread
|
|
-{
|
|
- mutable QMutex queueMutex;
|
|
- QQueue<QVideoFrame> videoFrameQueue;
|
|
-public:
|
|
- VideoEncoder(Encoder *encoder, QPlatformCamera *camera, const QMediaEncoderSettings &settings);
|
|
- ~VideoEncoder();
|
|
-
|
|
- void addFrame(const QVideoFrame &frame);
|
|
-
|
|
- void setPaused(bool b) override
|
|
- {
|
|
- EncoderThread::setPaused(b);
|
|
- if (b)
|
|
- baseTime.storeRelease(-1);
|
|
- }
|
|
-
|
|
-private:
|
|
- QVideoFrame takeFrame();
|
|
- void retrievePackets();
|
|
-
|
|
- void init() override;
|
|
- void cleanup() override;
|
|
- bool shouldWait() const override;
|
|
- void loop() override;
|
|
-
|
|
- QMediaEncoderSettings m_encoderSettings;
|
|
- QPlatformCamera *m_camera = nullptr;
|
|
- VideoFrameEncoder *frameEncoder = nullptr;
|
|
-
|
|
- QAtomicInteger<qint64> baseTime = -1;
|
|
- qint64 lastFrameTime = 0;
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp b/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp
|
|
deleted file mode 100644
|
|
index 2535048c3..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp
|
|
+++ /dev/null
|
|
@@ -1,272 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include "qffmpegencoderoptions_p.h"
|
|
-
|
|
-#if QT_CONFIG(vaapi)
|
|
-#include <va/va.h>
|
|
-#endif
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-// unfortunately there is no common way to specify options for the encoders. The code here tries to map our settings sensibly
|
|
-// to options available in different encoders
|
|
-
|
|
-// For constant quality options, we're trying to map things to approx those bit rates for 1080p@30fps (in Mbps):
|
|
-// VeryLow Low Normal High VeryHigh
|
|
-// H264: 0.8M 1.5M 3.5M 6M 10M
|
|
-// H265: 0.5M 1.0M 2.5M 4M 7M
|
|
-
|
|
-[[maybe_unused]]
|
|
-static int bitrateForSettings(const QMediaEncoderSettings &settings, bool hdr = false)
|
|
-{
|
|
- // calculate an acceptable bitrate depending on video codec, resolution, framerate and requested quality
|
|
- // The calculations are rather heuristic here, trying to take into account how well codecs compress using
|
|
- // the tables above.
|
|
-
|
|
- // The table here is for 30FPS
|
|
- const double bitsPerPixel[int(QMediaFormat::VideoCodec::LastVideoCodec)+1][QMediaRecorder::VeryHighQuality+1] = {
|
|
- { 1.2, 2.25, 5, 9, 15 }, // MPEG1,
|
|
- { 0.8, 1.5, 3.5, 6, 10 }, // MPEG2
|
|
- { 0.4, 0.75, 1.75, 3, 5 }, // MPEG4
|
|
- { 0.4, 0.75, 1.75, 3, 5 }, // H264
|
|
- { 0.3, 0.5, 0.2, 2, 3 }, // H265
|
|
- { 0.4, 0.75, 1.75, 3, 5 }, // VP8
|
|
- { 0.3, 0.5, 0.2, 2, 3 }, // VP9
|
|
- { 0.2, 0.4, 0.9, 1.5, 2.5 }, // AV1
|
|
- { 0.4, 0.75, 1.75, 3, 5 }, // Theora
|
|
- { 0.8, 1.5, 3.5, 6, 10 }, // WMV
|
|
- { 16, 24, 32, 40, 48 }, // MotionJPEG
|
|
- };
|
|
-
|
|
- QSize s = settings.videoResolution();
|
|
- double bitrate = bitsPerPixel[int(settings.videoCodec())][settings.quality()]*s.width()*s.height();
|
|
-
|
|
- if (settings.videoCodec() != QMediaFormat::VideoCodec::MotionJPEG) {
|
|
- // We assume that doubling the framerate requires 1.5 times the amount of data (not twice, as intraframe
|
|
- // differences will be smaller). 4 times the frame rate uses thus 2.25 times the data, etc.
|
|
- float rateMultiplier = log2(settings.videoFrameRate()/30.);
|
|
- bitrate *= pow(1.5, rateMultiplier);
|
|
- } else {
|
|
- // MotionJPEG doesn't optimize between frames, so we have a linear dependency on framerate
|
|
- bitrate *= settings.videoFrameRate()/30.;
|
|
- }
|
|
-
|
|
- // HDR requires 10bits per pixel instead of 8, so apply a factor of 1.25.
|
|
- if (hdr)
|
|
- bitrate *= 1.25;
|
|
- return bitrate;
|
|
-}
|
|
-
|
|
-static void apply_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- } else {
|
|
- const char *scales[] = {
|
|
- "29", "26", "23", "21", "19"
|
|
- };
|
|
- av_dict_set(opts, "crf", scales[settings.quality()], 0);
|
|
- }
|
|
-}
|
|
-
|
|
-static void apply_x265(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- } else {
|
|
- const char *scales[QMediaRecorder::VeryHighQuality+1] = {
|
|
- "40", "34", "28", "26", "24",
|
|
- };
|
|
- av_dict_set(opts, "crf", scales[settings.quality()], 0);
|
|
- }
|
|
-}
|
|
-
|
|
-static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- } else {
|
|
- const char *scales[QMediaRecorder::VeryHighQuality+1] = {
|
|
- "38", "34", "31", "28", "25",
|
|
- };
|
|
- av_dict_set(opts, "crf", scales[settings.quality()], 0);
|
|
- av_dict_set(opts, "b", 0, 0);
|
|
- }
|
|
- av_dict_set(opts, "row-mt", "1", 0); // better multithreading
|
|
-}
|
|
-
|
|
-#ifdef Q_OS_DARWIN
|
|
-static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **)
|
|
-{
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- } else {
|
|
- // only use quality on macOS/ARM, as FFmpeg doesn't support it on the other platforms and would throw
|
|
- // an error when initializing the codec
|
|
-#if defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM_64)
|
|
- // Videotoolbox describes quality as a number from 0 to 1, with low == 0.25, normal 0.5, high 0.75 and lossless = 1
|
|
- // ffmpeg uses a different scale going from 0 to 11800.
|
|
- // Values here are adjusted to agree approximately with the target bit rates listed above
|
|
- const int scales[] = {
|
|
- 3000, 4800, 5900, 6900, 7700,
|
|
- };
|
|
- codec->global_quality = scales[settings.quality()];
|
|
- codec->flags |= AV_CODEC_FLAG_QSCALE;
|
|
-#else
|
|
- codec->bit_rate = bitrateForSettings(settings);
|
|
-#endif
|
|
- }
|
|
-}
|
|
-#endif
|
|
-
|
|
-#if QT_CONFIG(vaapi)
|
|
-static void apply_vaapi(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **/*opts*/)
|
|
-{
|
|
- // See also vaapi_encode_init_rate_control() in libavcodec
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- codec->rc_max_rate = settings.videoBitRate();
|
|
- } else if (settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- } else {
|
|
- const int *quality = nullptr;
|
|
- // unfortunately, all VA codecs use different quality scales :/
|
|
- switch (settings.videoCodec()) {
|
|
- case QMediaFormat::VideoCodec::MPEG2: {
|
|
- static const int q[] = { 20, 15, 10, 8, 6 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::MPEG4:
|
|
- case QMediaFormat::VideoCodec::H264: {
|
|
- static const int q[] = { 29, 26, 23, 21, 19 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::H265: {
|
|
- static const int q[] = { 40, 34, 28, 26, 24 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::VP8: {
|
|
- static const int q[] = { 56, 48, 40, 34, 28 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::VP9: {
|
|
- static const int q[] = { 124, 112, 100, 88, 76 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::MotionJPEG: {
|
|
- static const int q[] = { 40, 60, 80, 90, 95 };
|
|
- quality = q;
|
|
- break;
|
|
- }
|
|
- case QMediaFormat::VideoCodec::AV1:
|
|
- case QMediaFormat::VideoCodec::Theora:
|
|
- case QMediaFormat::VideoCodec::WMV:
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- if (quality) {
|
|
- qDebug() << "using quality" << settings.quality() << quality[settings.quality()];
|
|
- codec->global_quality = quality[settings.quality()];
|
|
- }
|
|
- }
|
|
-}
|
|
-#endif
|
|
-
|
|
-#ifdef Q_OS_WINDOWS
|
|
-static void apply_mf(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
|
|
- codec->bit_rate = settings.videoBitRate();
|
|
- av_dict_set(opts, "rate_control", "cbr", 0);
|
|
- } else {
|
|
- av_dict_set(opts, "rate_control", "quality", 0);
|
|
- const char *scales[] = {
|
|
- "25", "50", "75", "90", "100"
|
|
- };
|
|
- av_dict_set(opts, "quality", scales[settings.quality()], 0);
|
|
- }
|
|
-}
|
|
-#endif
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-using ApplyOptions = void (*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts);
|
|
-
|
|
-const struct {
|
|
- const char *name;
|
|
- ApplyOptions apply;
|
|
-} videoCodecOptionTable[] = {
|
|
- { "libx264", apply_x264 },
|
|
- { "libx265xx", apply_x265 },
|
|
- { "libvpx", apply_libvpx },
|
|
- { "libvpx_vp9", apply_libvpx },
|
|
-#ifdef Q_OS_DARWIN
|
|
- { "h264_videotoolbox", apply_videotoolbox },
|
|
- { "hevc_videotoolbox", apply_videotoolbox },
|
|
- { "prores_videotoolbox", apply_videotoolbox },
|
|
- { "vp9_videotoolbox", apply_videotoolbox },
|
|
-#endif
|
|
-#if QT_CONFIG(vaapi)
|
|
- { "mpeg2_vaapi", apply_vaapi },
|
|
- { "mjpeg_vaapi", apply_vaapi },
|
|
- { "h264_vaapi", apply_vaapi },
|
|
- { "hevc_vaapi", apply_vaapi },
|
|
- { "vp8_vaapi", apply_vaapi },
|
|
- { "vp9_vaapi", apply_vaapi },
|
|
-#endif
|
|
-#ifdef Q_OS_WINDOWS
|
|
- { "hevc_mf", apply_mf },
|
|
- { "h264_mf", apply_mf },
|
|
-#endif
|
|
- { nullptr, nullptr }
|
|
-};
|
|
-
|
|
-const struct {
|
|
- const char *name;
|
|
- ApplyOptions apply;
|
|
-} audioCodecOptionTable[] = {
|
|
- { nullptr, nullptr }
|
|
-};
|
|
-
|
|
-void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- av_dict_set(opts, "threads", "auto", 0); // we always want automatic threading
|
|
-
|
|
- auto *table = videoCodecOptionTable;
|
|
- while (table->name) {
|
|
- if (codecName == table->name) {
|
|
- table->apply(settings, codec, opts);
|
|
- return;
|
|
- }
|
|
-
|
|
- ++table;
|
|
- }
|
|
-}
|
|
-
|
|
-void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
|
|
-{
|
|
- codec->thread_count = -1; // we always want automatic threading
|
|
- if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding)
|
|
- codec->bit_rate = settings.audioBitRate();
|
|
-
|
|
- auto *table = audioCodecOptionTable;
|
|
- while (table->name) {
|
|
- if (codecName == table->name) {
|
|
- table->apply(settings, codec, opts);
|
|
- return;
|
|
- }
|
|
-
|
|
- ++table;
|
|
- }
|
|
-
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h
|
|
deleted file mode 100644
|
|
index 005ad7652..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h
|
|
+++ /dev/null
|
|
@@ -1,32 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGENCODEROPTIONS_P_H
|
|
-#define QFFMPEGENCODEROPTIONS_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#include "qvideoframeformat.h"
|
|
-#include "private/qplatformmediarecorder_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
|
|
-void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
|
|
deleted file mode 100644
|
|
index e5d90239b..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
|
|
+++ /dev/null
|
|
@@ -1,372 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#if QT_CONFIG(vaapi)
|
|
-#include "qffmpeghwaccel_vaapi_p.h"
|
|
-#endif
|
|
-#ifdef Q_OS_DARWIN
|
|
-#include "qffmpeghwaccel_videotoolbox_p.h"
|
|
-#endif
|
|
-#if QT_CONFIG(wmf)
|
|
-#include "qffmpeghwaccel_d3d11_p.h"
|
|
-#endif
|
|
-#ifdef Q_OS_ANDROID
|
|
-# include "qffmpeghwaccel_mediacodec_p.h"
|
|
-#endif
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-
|
|
-#include <private/qrhi_p.h>
|
|
-#include <qdebug.h>
|
|
-
|
|
-/* Infrastructure for HW acceleration goes into this file. */
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-static const AVHWDeviceType preferredHardwareAccelerators[] = {
|
|
-#if defined(Q_OS_LINUX)
|
|
- AV_HWDEVICE_TYPE_VAAPI,
|
|
-// AV_HWDEVICE_TYPE_DRM,
|
|
-#elif defined (Q_OS_WIN)
|
|
- AV_HWDEVICE_TYPE_D3D11VA,
|
|
-#elif defined (Q_OS_DARWIN)
|
|
- AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
|
|
-#elif defined (Q_OS_ANDROID)
|
|
- AV_HWDEVICE_TYPE_MEDIACODEC,
|
|
-#endif
|
|
-};
|
|
-
|
|
-static AVBufferRef *loadHWContext(const AVHWDeviceType type)
|
|
-{
|
|
- AVBufferRef *hwContext = nullptr;
|
|
- int ret = av_hwdevice_ctx_create(&hwContext, type, nullptr, nullptr, 0);
|
|
- qDebug() << " Checking HW context:" << av_hwdevice_get_type_name(type);
|
|
- if (ret == 0) {
|
|
- qDebug() << " Using above hw context.";
|
|
- return hwContext;
|
|
- }
|
|
- qDebug() << " Could not create hw context:" << ret << strerror(-ret);
|
|
- return nullptr;
|
|
-}
|
|
-
|
|
-static AVBufferRef *hardwareContextForCodec(const AVCodec *codec)
|
|
-{
|
|
- qDebug() << "Checking HW acceleration for decoder" << codec->name;
|
|
-
|
|
- // First try our preferred accelerators. Those are the ones where we can
|
|
- // set up a zero copy pipeline
|
|
- for (auto type : preferredHardwareAccelerators) {
|
|
- for (int i = 0;; ++i) {
|
|
- const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
|
|
- if (!config)
|
|
- break;
|
|
- if (config->device_type == type) {
|
|
- auto *hwContext = loadHWContext(config->device_type);
|
|
- if (hwContext)
|
|
- return hwContext;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // Ok, let's see if we can get any HW acceleration at all. It'll still involve one buffer copy,
|
|
- // as we can't move the data into RHI textures without a CPU copy
|
|
- for (int i = 0;; ++i) {
|
|
- const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
|
|
- if (!config)
|
|
- break;
|
|
-
|
|
- auto *hwContext = loadHWContext(config->device_type);
|
|
- if (hwContext)
|
|
- return hwContext;
|
|
- }
|
|
- qDebug() << " No HW accelerators found, using SW decoding.";
|
|
- return nullptr;
|
|
-
|
|
-}
|
|
-
|
|
-// Used for the AVCodecContext::get_format callback
|
|
-AVPixelFormat getFormat(AVCodecContext *s, const AVPixelFormat *fmt)
|
|
-{
|
|
- // First check HW accelerated codecs, the HW device context must be set
|
|
- if (s->hw_device_ctx) {
|
|
- auto *device_ctx = (AVHWDeviceContext*)s->hw_device_ctx->data;
|
|
- for (int i = 0; const AVCodecHWConfig *config = avcodec_get_hw_config(s->codec, i); i++) {
|
|
- if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
|
|
- continue;
|
|
- if (device_ctx->type != config->device_type)
|
|
- continue;
|
|
- for (int n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
|
|
- if (config->pix_fmt == fmt[n]) {
|
|
-#if QT_CONFIG(wmf)
|
|
- if (fmt[n] == AV_PIX_FMT_D3D11)
|
|
- QFFmpeg::D3D11TextureConverter::SetupDecoderTextures(s);
|
|
-#endif
|
|
-#ifdef Q_OS_ANDROID
|
|
- if (fmt[n] == AV_PIX_FMT_MEDIACODEC)
|
|
- QFFmpeg::MediaCodecTextureConverter::setupDecoderSurface(s);
|
|
-#endif
|
|
- return fmt[n];
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // prefer video formats we can handle directly
|
|
- for (int n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
|
|
- bool needsConversion = true;
|
|
- QFFmpegVideoBuffer::toQtPixelFormat(fmt[n], &needsConversion);
|
|
- if (!needsConversion)
|
|
- return fmt[n];
|
|
- }
|
|
-
|
|
- // take the native format, this will involve one additional format conversion on the CPU side
|
|
- return *fmt;
|
|
-}
|
|
-
|
|
-TextureConverter::Data::~Data()
|
|
-{
|
|
- delete backend;
|
|
-}
|
|
-
|
|
-HWAccel::~HWAccel()
|
|
-{
|
|
- if (m_hwDeviceContext)
|
|
- av_buffer_unref(&m_hwDeviceContext);
|
|
- if (m_hwFramesContext)
|
|
- av_buffer_unref(&m_hwFramesContext);
|
|
-}
|
|
-
|
|
-std::unique_ptr<HWAccel> HWAccel::create(const AVCodec *codec)
|
|
-{
|
|
- if (codec->type == AVMEDIA_TYPE_VIDEO) {
|
|
- if (auto *ctx = hardwareContextForCodec(codec))
|
|
- return std::unique_ptr<HWAccel>(new HWAccel(ctx));
|
|
- }
|
|
- return {};
|
|
-}
|
|
-
|
|
-std::unique_ptr<HWAccel> HWAccel::create(AVHWDeviceType deviceType)
|
|
-{
|
|
- if (auto *ctx = loadHWContext(deviceType))
|
|
- return std::unique_ptr<HWAccel>(new HWAccel(ctx));
|
|
- else
|
|
- return {};
|
|
-}
|
|
-
|
|
-AVPixelFormat HWAccel::format(AVFrame *frame)
|
|
-{
|
|
- if (!frame->hw_frames_ctx)
|
|
- return AVPixelFormat(frame->format);
|
|
-
|
|
- auto *hwFramesContext = (AVHWFramesContext *)frame->hw_frames_ctx->data;
|
|
- Q_ASSERT(hwFramesContext);
|
|
- return AVPixelFormat(hwFramesContext->sw_format);
|
|
-}
|
|
-
|
|
-std::pair<const AVHWDeviceType*, qsizetype> HWAccel::preferredDeviceTypes()
|
|
-{
|
|
- return { preferredHardwareAccelerators,
|
|
- sizeof(preferredHardwareAccelerators) / sizeof(AVHWDeviceType) };
|
|
-}
|
|
-
|
|
-AVHWDeviceContext *HWAccel::hwDeviceContext() const
|
|
-{
|
|
- return m_hwDeviceContext ? (AVHWDeviceContext *)m_hwDeviceContext->data : nullptr;
|
|
-}
|
|
-
|
|
-AVPixelFormat HWAccel::hwFormat() const
|
|
-{
|
|
- switch (deviceType()) {
|
|
- case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
|
|
- return AV_PIX_FMT_VIDEOTOOLBOX;
|
|
- case AV_HWDEVICE_TYPE_VAAPI:
|
|
- return AV_PIX_FMT_VAAPI;
|
|
- case AV_HWDEVICE_TYPE_MEDIACODEC:
|
|
- return AV_PIX_FMT_MEDIACODEC;
|
|
- default:
|
|
- return AV_PIX_FMT_NONE;
|
|
- }
|
|
-}
|
|
-
|
|
-const AVCodec *HWAccel::hardwareDecoderForCodecId(AVCodecID id)
|
|
-{
|
|
- const AVCodec *codec = nullptr;
|
|
-#ifdef Q_OS_ANDROID
|
|
- const auto getDecoder = [](AVCodecID id) {
|
|
- switch (id) {
|
|
- case AV_CODEC_ID_H264:
|
|
- return avcodec_find_decoder_by_name("h264_mediacodec");
|
|
- case AV_CODEC_ID_HEVC:
|
|
- return avcodec_find_decoder_by_name("hevc_mediacodec");
|
|
- case AV_CODEC_ID_MPEG2VIDEO:
|
|
- return avcodec_find_decoder_by_name("mpeg2_mediacodec");
|
|
- case AV_CODEC_ID_MPEG4:
|
|
- return avcodec_find_decoder_by_name("mpeg4_mediacodec");
|
|
- case AV_CODEC_ID_VP8:
|
|
- return avcodec_find_decoder_by_name("vp8_mediacodec");
|
|
- case AV_CODEC_ID_VP9:
|
|
- return avcodec_find_decoder_by_name("vp9_mediacodec");
|
|
- default:
|
|
- return avcodec_find_decoder(id);
|
|
- }
|
|
- };
|
|
- codec = getDecoder(id);
|
|
-#endif
|
|
-
|
|
- if (!codec)
|
|
- codec = avcodec_find_decoder(id);
|
|
-
|
|
- return codec;
|
|
-}
|
|
-
|
|
-const AVCodec *HWAccel::hardwareEncoderForCodecId(AVCodecID id) const
|
|
-{
|
|
- const char *codec = nullptr;
|
|
- switch (deviceType()) {
|
|
-#ifdef Q_OS_DARWIN
|
|
- case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
|
|
- switch (id) {
|
|
- case AV_CODEC_ID_H264:
|
|
- codec = "h264_videotoolbox";
|
|
- break;
|
|
- case AV_CODEC_ID_HEVC:
|
|
- codec = "hevc_videotoolbox";
|
|
- break;
|
|
- case AV_CODEC_ID_PRORES:
|
|
- codec = "prores_videotoolbox";
|
|
- break;
|
|
- case AV_CODEC_ID_VP9:
|
|
- codec = "vp9_videotoolbox";
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- break;
|
|
-#endif
|
|
- case AV_HWDEVICE_TYPE_VAAPI:
|
|
- switch (id) {
|
|
- case AV_CODEC_ID_H264:
|
|
- codec = "h264_vaapi";
|
|
- break;
|
|
- case AV_CODEC_ID_HEVC:
|
|
- codec = "hevc_vaapi";
|
|
- break;
|
|
- case AV_CODEC_ID_MJPEG:
|
|
- codec = "mjpeg_vaapi";
|
|
- break;
|
|
- case AV_CODEC_ID_MPEG2VIDEO:
|
|
- codec = "mpeg2_vaapi";
|
|
- break;
|
|
- case AV_CODEC_ID_VP8:
|
|
- codec = "vp8_vaapi";
|
|
- break;
|
|
- case AV_CODEC_ID_VP9:
|
|
- codec = "vp9_vaapi";
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- if (!codec)
|
|
- return nullptr;
|
|
- const AVCodec *c = avcodec_find_encoder_by_name(codec);
|
|
- qDebug() << "searching for HW codec" << codec << "got" << c;
|
|
- return c;
|
|
-}
|
|
-
|
|
-std::unique_ptr<HWAccel> HWAccel::findHardwareAccelForCodecID(AVCodecID id)
|
|
-{
|
|
- for (auto type : preferredHardwareAccelerators) {
|
|
- auto accel = HWAccel::create(type);
|
|
- if (accel && accel->hardwareEncoderForCodecId(id))
|
|
- return accel;
|
|
- }
|
|
- return {};
|
|
-}
|
|
-
|
|
-AVHWDeviceType HWAccel::deviceType() const
|
|
-{
|
|
- return m_hwDeviceContext ? hwDeviceContext()->type : AV_HWDEVICE_TYPE_NONE;
|
|
-}
|
|
-
|
|
-void HWAccel::createFramesContext(AVPixelFormat swFormat, const QSize &size)
|
|
-{
|
|
- if (m_hwDeviceContext)
|
|
- return;
|
|
- m_hwFramesContext = av_hwframe_ctx_alloc(m_hwDeviceContext);
|
|
- auto *c = (AVHWFramesContext *)m_hwFramesContext->data;
|
|
- c->format = hwFormat();
|
|
- c->sw_format = swFormat;
|
|
- c->width = size.width();
|
|
- c->height = size.height();
|
|
- qDebug() << "init frames context";
|
|
- int err = av_hwframe_ctx_init(m_hwFramesContext);
|
|
- if (err < 0)
|
|
- qWarning() << "failed to init HW frame context" << err << err2str(err);
|
|
- else
|
|
- qDebug() << "Initialized frames context" << size << c->format << c->sw_format;
|
|
-}
|
|
-
|
|
-AVHWFramesContext *HWAccel::hwFramesContext() const
|
|
-{
|
|
- return m_hwFramesContext ? (AVHWFramesContext *)m_hwFramesContext->data : nullptr;
|
|
-}
|
|
-
|
|
-
|
|
-TextureConverter::TextureConverter(QRhi *rhi)
|
|
- : d(new Data)
|
|
-{
|
|
- d->rhi = rhi;
|
|
-}
|
|
-
|
|
-TextureSet *TextureConverter::getTextures(AVFrame *frame)
|
|
-{
|
|
- if (!frame || isNull())
|
|
- return nullptr;
|
|
-
|
|
- Q_ASSERT(frame->format == d->format);
|
|
- return d->backend->getTextures(frame);
|
|
-}
|
|
-
|
|
-void TextureConverter::updateBackend(AVPixelFormat fmt)
|
|
-{
|
|
- d->backend = nullptr;
|
|
- if (!d->rhi)
|
|
- return;
|
|
- switch (fmt) {
|
|
-#if QT_CONFIG(vaapi)
|
|
- case AV_PIX_FMT_VAAPI:
|
|
- d->backend = new VAAPITextureConverter(d->rhi);
|
|
- break;
|
|
-#endif
|
|
-#ifdef Q_OS_DARWIN
|
|
- case AV_PIX_FMT_VIDEOTOOLBOX:
|
|
- d->backend = new VideoToolBoxTextureConverter(d->rhi);
|
|
- break;
|
|
-#endif
|
|
-#if QT_CONFIG(wmf)
|
|
- case AV_PIX_FMT_D3D11:
|
|
- d->backend = new D3D11TextureConverter(d->rhi);
|
|
- break;
|
|
-#endif
|
|
-#ifdef Q_OS_ANDROID
|
|
- case AV_PIX_FMT_MEDIACODEC:
|
|
- d->backend = new MediaCodecTextureConverter(d->rhi);
|
|
- break;
|
|
-#endif
|
|
- default:
|
|
- break;
|
|
- }
|
|
- d->format = fmt;
|
|
-}
|
|
-
|
|
-} // namespace QFFmpeg
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
|
|
deleted file mode 100644
|
|
index f0a6c7b91..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
|
|
+++ /dev/null
|
|
@@ -1,158 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpeghwaccel_d3d11_p.h"
|
|
-
|
|
-#include <qvideoframeformat.h>
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-
|
|
-
|
|
-#include <private/qvideotexturehelper_p.h>
|
|
-#include <private/qwindowsiupointer_p.h>
|
|
-#include <private/qrhi_p.h>
|
|
-#include <private/qrhid3d11_p.h>
|
|
-
|
|
-#include <qopenglfunctions.h>
|
|
-#include <qdebug.h>
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-#include <libavutil/hwcontext_d3d11va.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel")
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class D3D11TextureSet : public TextureSet
|
|
-{
|
|
-public:
|
|
- D3D11TextureSet(QWindowsIUPointer<ID3D11Texture2D> &&tex)
|
|
- : m_tex(tex)
|
|
- {}
|
|
-
|
|
- qint64 textureHandle(int plane) override
|
|
- {
|
|
- return qint64(m_tex.get());
|
|
- }
|
|
-
|
|
-private:
|
|
- QWindowsIUPointer<ID3D11Texture2D> m_tex;
|
|
-};
|
|
-
|
|
-
|
|
-D3D11TextureConverter::D3D11TextureConverter(QRhi *rhi)
|
|
- : TextureConverterBackend(rhi)
|
|
-{
|
|
-}
|
|
-
|
|
-static QWindowsIUPointer<ID3D11Texture2D> getSharedTextureForDevice(ID3D11Device *dev, ID3D11Texture2D *tex)
|
|
-{
|
|
- QWindowsIUPointer<IDXGIResource> dxgiResource;
|
|
- HRESULT hr = tex->QueryInterface(__uuidof(IDXGIResource), reinterpret_cast<void **>(dxgiResource.address()));
|
|
- if (FAILED(hr)) {
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to obtain resource handle from FFMpeg texture" << hr;
|
|
- return {};
|
|
- }
|
|
- HANDLE shared = nullptr;
|
|
- hr = dxgiResource->GetSharedHandle(&shared);
|
|
- if (FAILED(hr)) {
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to obtain shared handle for FFmpeg texture" << hr;
|
|
- return {};
|
|
- }
|
|
-
|
|
- QWindowsIUPointer<ID3D11Texture2D> sharedTex;
|
|
- hr = dev->OpenSharedResource(shared, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(sharedTex.address()));
|
|
- if (FAILED(hr))
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to share FFmpeg texture" << hr;
|
|
- return sharedTex;
|
|
-}
|
|
-
|
|
-static QWindowsIUPointer<ID3D11Texture2D> copyTextureFromArray(ID3D11Device *dev, ID3D11Texture2D *array, int index)
|
|
-{
|
|
- D3D11_TEXTURE2D_DESC arrayDesc = {};
|
|
- array->GetDesc(&arrayDesc);
|
|
-
|
|
- D3D11_TEXTURE2D_DESC texDesc = {};
|
|
- texDesc.Width = arrayDesc.Width;
|
|
- texDesc.Height = arrayDesc.Height;
|
|
- texDesc.Format = arrayDesc.Format;
|
|
- texDesc.ArraySize = 1;
|
|
- texDesc.MipLevels = 1;
|
|
- texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
- texDesc.MiscFlags = 0;
|
|
- texDesc.SampleDesc = { 1, 0};
|
|
-
|
|
- QWindowsIUPointer<ID3D11Texture2D> texCopy;
|
|
- HRESULT hr = dev->CreateTexture2D(&texDesc, nullptr, texCopy.address());
|
|
- if (FAILED(hr)) {
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to create texture" << hr;
|
|
- return {};
|
|
- }
|
|
-
|
|
- QWindowsIUPointer<ID3D11DeviceContext> ctx;
|
|
- dev->GetImmediateContext(ctx.address());
|
|
- ctx->CopySubresourceRegion(texCopy.get(), 0, 0, 0, 0, array, index, nullptr);
|
|
-
|
|
- return texCopy;
|
|
-}
|
|
-
|
|
-TextureSet *D3D11TextureConverter::getTextures(AVFrame *frame)
|
|
-{
|
|
- if (!frame || !frame->hw_frames_ctx || frame->format != AV_PIX_FMT_D3D11)
|
|
- return nullptr;
|
|
-
|
|
- auto *fCtx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
|
|
- auto *ctx = fCtx->device_ctx;
|
|
- if (!ctx || ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
|
|
- return nullptr;
|
|
-
|
|
- auto nh = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
|
|
- if (!nh)
|
|
- return nullptr;
|
|
-
|
|
- auto ffmpegTex = (ID3D11Texture2D *)frame->data[0];
|
|
- int index = (intptr_t)frame->data[1];
|
|
-
|
|
- if (rhi->backend() == QRhi::D3D11) {
|
|
- auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
|
|
- if (!dev)
|
|
- return nullptr;
|
|
- auto sharedTex = getSharedTextureForDevice(dev, ffmpegTex);
|
|
- if (sharedTex) {
|
|
- auto tex = copyTextureFromArray(dev, sharedTex.get(), index);
|
|
- if (tex) {
|
|
- QVideoFrameFormat::PixelFormat format = QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat(fCtx->sw_format));
|
|
- return new D3D11TextureSet(std::move(tex));
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return nullptr;
|
|
-}
|
|
-
|
|
-void D3D11TextureConverter::SetupDecoderTextures(AVCodecContext *s)
|
|
-{
|
|
- int ret = avcodec_get_hw_frames_parameters(s,
|
|
- s->hw_device_ctx,
|
|
- AV_PIX_FMT_D3D11,
|
|
- &s->hw_frames_ctx);
|
|
- if (ret < 0) {
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
|
|
- return;
|
|
- }
|
|
-
|
|
- auto *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx->data;
|
|
- auto *hwctx = (AVD3D11VAFramesContext *)frames_ctx->hwctx;
|
|
- hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
|
|
- hwctx->BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
|
|
- ret = av_hwframe_ctx_init(s->hw_frames_ctx);
|
|
- if (ret < 0) {
|
|
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
|
|
- av_buffer_unref(&s->hw_frames_ctx);
|
|
- }
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
|
|
deleted file mode 100644
|
|
index 2e9c77f5b..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
|
|
+++ /dev/null
|
|
@@ -1,43 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGHWACCEL_D3D11_P_H
|
|
-#define QFFMPEGHWACCEL_D3D11_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-#if QT_CONFIG(wmf)
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QRhi;
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class D3D11TextureConverter : public TextureConverterBackend
|
|
-{
|
|
-public:
|
|
- D3D11TextureConverter(QRhi *rhi);
|
|
-
|
|
- TextureSet *getTextures(AVFrame *frame) override;
|
|
-
|
|
- static void SetupDecoderTextures(AVCodecContext *s);
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
|
|
deleted file mode 100644
|
|
index 20a06c3ab..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
|
|
+++ /dev/null
|
|
@@ -1,70 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpeghwaccel_mediacodec_p.h"
|
|
-
|
|
-#include <androidsurfacetexture_p.h>
|
|
-#include <QtGui/private/qrhi_p.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavcodec/mediacodec.h>
|
|
-}
|
|
-
|
|
-#if !defined(Q_OS_ANDROID)
|
|
-# error "Configuration error"
|
|
-#endif
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-Q_GLOBAL_STATIC(AndroidSurfaceTexture, androidSurfaceTexture, 0);
|
|
-
|
|
-class MediaCodecTextureSet : public TextureSet
|
|
-{
|
|
-public:
|
|
- MediaCodecTextureSet(qint64 textureHandle) : handle(textureHandle) { }
|
|
-
|
|
- qint64 textureHandle(int plane) override { return (plane == 0) ? handle : 0; }
|
|
-
|
|
-private:
|
|
- qint64 handle;
|
|
-};
|
|
-
|
|
-void MediaCodecTextureConverter::setupDecoderSurface(AVCodecContext *avCodecContext)
|
|
-{
|
|
- AVMediaCodecContext *mediacodecContext = av_mediacodec_alloc_context();
|
|
- av_mediacodec_default_init(avCodecContext, mediacodecContext, androidSurfaceTexture->surface());
|
|
-}
|
|
-
|
|
-TextureSet *MediaCodecTextureConverter::getTextures(AVFrame *frame)
|
|
-{
|
|
- if (!androidSurfaceTexture->isValid())
|
|
- return {};
|
|
-
|
|
- if (!externalTexture) {
|
|
- androidSurfaceTexture->detachFromGLContext();
|
|
- externalTexture = std::unique_ptr<QRhiTexture>(
|
|
- rhi->newTexture(QRhiTexture::Format::RGBA8, { frame->width, frame->height }, 1,
|
|
- QRhiTexture::ExternalOES));
|
|
-
|
|
- if (!externalTexture->create()) {
|
|
- qWarning() << "Failed to create the external texture!";
|
|
- return {};
|
|
- }
|
|
-
|
|
- quint64 textureHandle = externalTexture->nativeTexture().object;
|
|
- androidSurfaceTexture->attachToGLContext(textureHandle);
|
|
- }
|
|
-
|
|
- // release a MediaCodec buffer and render it to the surface
|
|
- AVMediaCodecBuffer *buffer = (AVMediaCodecBuffer *)frame->data[3];
|
|
- int result = av_mediacodec_release_buffer(buffer, 1);
|
|
- if (result < 0) {
|
|
- qWarning() << "Failed to render buffer to surface.";
|
|
- return {};
|
|
- }
|
|
-
|
|
- androidSurfaceTexture->updateTexImage();
|
|
-
|
|
- return new MediaCodecTextureSet(externalTexture->nativeTexture().object);
|
|
-}
|
|
-}
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h
|
|
deleted file mode 100644
|
|
index 95982ba4d..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h
|
|
+++ /dev/null
|
|
@@ -1,35 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#ifndef QFFMPEGHWACCEL_MEDIACODEC_P_H
|
|
-#define QFFMPEGHWACCEL_MEDIACODEC_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#include <memory>
|
|
-
|
|
-namespace QFFmpeg {
|
|
-struct Frame;
|
|
-
|
|
-class MediaCodecTextureConverter : public TextureConverterBackend
|
|
-{
|
|
-public:
|
|
- MediaCodecTextureConverter(QRhi *rhi) : TextureConverterBackend(rhi){};
|
|
- TextureSet *getTextures(AVFrame *frame) override;
|
|
-
|
|
- static void setupDecoderSurface(AVCodecContext *s);
|
|
-private:
|
|
- std::unique_ptr<QRhiTexture> externalTexture;
|
|
-};
|
|
-}
|
|
-#endif // QFFMPEGHWACCEL_MEDIACODEC_P_H
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
|
|
deleted file mode 100644
|
|
index 81bb163bb..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
|
|
+++ /dev/null
|
|
@@ -1,121 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGHWACCEL_P_H
|
|
-#define QFFMPEGHWACCEL_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qvideoframeformat.h"
|
|
-#include <private/qabstractvideobuffer_p.h>
|
|
-#include <qshareddata.h>
|
|
-#include <memory>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QRhi;
|
|
-class QRhiTexture;
|
|
-class QFFmpegVideoBuffer;
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-// used for the get_format callback for the decoder
|
|
-enum AVPixelFormat getFormat(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
|
|
-
|
|
-class HWAccel;
|
|
-
|
|
-class TextureSet {
|
|
-public:
|
|
- // ### Should add QVideoFrameFormat::PixelFormat here
|
|
- virtual ~TextureSet() {}
|
|
- virtual qint64 textureHandle(int /*plane*/) { return 0; }
|
|
-};
|
|
-
|
|
-class TextureConverterBackend
|
|
-{
|
|
-public:
|
|
- TextureConverterBackend(QRhi *rhi)
|
|
- : rhi(rhi)
|
|
- {}
|
|
- virtual ~TextureConverterBackend() {}
|
|
- virtual TextureSet *getTextures(AVFrame * /*frame*/) { return nullptr; }
|
|
-
|
|
- QRhi *rhi = nullptr;
|
|
-};
|
|
-
|
|
-class TextureConverter
|
|
-{
|
|
- class Data final
|
|
- {
|
|
- public:
|
|
- ~Data();
|
|
- QAtomicInt ref = 0;
|
|
- QRhi *rhi = nullptr;
|
|
- AVPixelFormat format = AV_PIX_FMT_NONE;
|
|
- TextureConverterBackend *backend = nullptr;
|
|
- };
|
|
-public:
|
|
- TextureConverter(QRhi *rhi = nullptr);
|
|
-
|
|
- void init(AVFrame *frame) {
|
|
- AVPixelFormat fmt = frame ? AVPixelFormat(frame->format) : AV_PIX_FMT_NONE;
|
|
- if (fmt != d->format)
|
|
- updateBackend(fmt);
|
|
- }
|
|
- TextureSet *getTextures(AVFrame *frame);
|
|
- bool isNull() const { return !d->backend || !d->backend->rhi; }
|
|
-
|
|
-private:
|
|
- void updateBackend(AVPixelFormat format);
|
|
-
|
|
- QExplicitlySharedDataPointer<Data> d;
|
|
-};
|
|
-
|
|
-class HWAccel
|
|
-{
|
|
- AVBufferRef *m_hwDeviceContext = nullptr;
|
|
- AVBufferRef *m_hwFramesContext = nullptr;
|
|
-
|
|
-public:
|
|
- ~HWAccel();
|
|
-
|
|
- static std::unique_ptr<HWAccel> create(const AVCodec *decoder);
|
|
- static std::unique_ptr<HWAccel> create(AVHWDeviceType deviceType);
|
|
- static std::unique_ptr<HWAccel> findHardwareAccelForCodecID(AVCodecID id);
|
|
-
|
|
- static const AVCodec *hardwareDecoderForCodecId(AVCodecID id);
|
|
- const AVCodec *hardwareEncoderForCodecId(AVCodecID id) const;
|
|
-
|
|
- AVHWDeviceType deviceType() const;
|
|
-
|
|
- AVBufferRef *hwDeviceContextAsBuffer() const { return m_hwDeviceContext; }
|
|
- AVHWDeviceContext *hwDeviceContext() const;
|
|
- AVPixelFormat hwFormat() const;
|
|
-
|
|
- void createFramesContext(AVPixelFormat swFormat, const QSize &size);
|
|
- AVBufferRef *hwFramesContextAsBuffer() const { return m_hwFramesContext; }
|
|
- AVHWFramesContext *hwFramesContext() const;
|
|
-
|
|
- static AVPixelFormat format(AVFrame *frame);
|
|
- static std::pair<const AVHWDeviceType*, qsizetype> preferredDeviceTypes();
|
|
-
|
|
-private:
|
|
- HWAccel(AVBufferRef *hwDeviceContext, AVBufferRef *hwFrameContext = nullptr)
|
|
- : m_hwDeviceContext(hwDeviceContext), m_hwFramesContext(hwFrameContext)
|
|
- {}
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
|
|
deleted file mode 100644
|
|
index 7b9976fe0..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
|
|
+++ /dev/null
|
|
@@ -1,346 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpeghwaccel_vaapi_p.h"
|
|
-
|
|
-#if !QT_CONFIG(vaapi)
|
|
-#error "Configuration error"
|
|
-#endif
|
|
-
|
|
-#include <va/va.h>
|
|
-
|
|
-#include <qvideoframeformat.h>
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "private/qvideotexturehelper_p.h"
|
|
-
|
|
-#include <private/qrhi_p.h>
|
|
-#include <private/qrhigles2_p.h>
|
|
-
|
|
-#include <qguiapplication.h>
|
|
-#include <qpa/qplatformnativeinterface.h>
|
|
-
|
|
-#include <qopenglfunctions.h>
|
|
-
|
|
-//#define VA_EXPORT_USE_LAYERS
|
|
-
|
|
-#if __has_include("drm/drm_fourcc.h")
|
|
-#include <drm/drm_fourcc.h>
|
|
-#elif __has_include("libdrm/drm_fourcc.h")
|
|
-#include <libdrm/drm_fourcc.h>
|
|
-#else
|
|
-// keep things building without drm_fourcc.h
|
|
-#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
|
|
- ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
|
-
|
|
-#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
|
|
-#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
|
|
-#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
|
|
-#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
|
|
-#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
|
|
-#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
|
|
-#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
|
|
-#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
|
|
-#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
|
|
-#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
|
|
-#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
|
|
-#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
|
|
-#endif
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/hwcontext_vaapi.h>
|
|
-}
|
|
-
|
|
-#include <va/va_drm.h>
|
|
-#include <va/va_drmcommon.h>
|
|
-
|
|
-#include <EGL/egl.h>
|
|
-#include <EGL/eglext.h>
|
|
-
|
|
-#include <unistd.h>
|
|
-
|
|
-#include <qdebug.h>
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-static const quint32 *fourccFromPixelFormat(const QVideoFrameFormat::PixelFormat format)
|
|
-{
|
|
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
- const quint32 rgba_fourcc = DRM_FORMAT_ABGR8888;
|
|
- const quint32 rg_fourcc = DRM_FORMAT_GR88;
|
|
- const quint32 rg16_fourcc = DRM_FORMAT_GR1616;
|
|
-#else
|
|
- const quint32 rgba_fourcc = DRM_FORMAT_RGBA8888;
|
|
- const quint32 rg_fourcc = DRM_FORMAT_RG88;
|
|
- const quint32 rg16_fourcc = DRM_FORMAT_RG1616;
|
|
-#endif
|
|
-
|
|
-// qDebug() << "Getting DRM fourcc for pixel format" << format;
|
|
-
|
|
- switch (format) {
|
|
- case QVideoFrameFormat::Format_Invalid:
|
|
- case QVideoFrameFormat::Format_IMC1:
|
|
- case QVideoFrameFormat::Format_IMC2:
|
|
- case QVideoFrameFormat::Format_IMC3:
|
|
- case QVideoFrameFormat::Format_IMC4:
|
|
- case QVideoFrameFormat::Format_SamplerExternalOES:
|
|
- case QVideoFrameFormat::Format_Jpeg:
|
|
- case QVideoFrameFormat::Format_SamplerRect:
|
|
- return nullptr;
|
|
-
|
|
- case QVideoFrameFormat::Format_ARGB8888:
|
|
- case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
|
|
- case QVideoFrameFormat::Format_XRGB8888:
|
|
- case QVideoFrameFormat::Format_BGRA8888:
|
|
- case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
|
|
- case QVideoFrameFormat::Format_BGRX8888:
|
|
- case QVideoFrameFormat::Format_ABGR8888:
|
|
- case QVideoFrameFormat::Format_XBGR8888:
|
|
- case QVideoFrameFormat::Format_RGBA8888:
|
|
- case QVideoFrameFormat::Format_RGBX8888:
|
|
- case QVideoFrameFormat::Format_AYUV:
|
|
- case QVideoFrameFormat::Format_AYUV_Premultiplied:
|
|
- case QVideoFrameFormat::Format_UYVY:
|
|
- case QVideoFrameFormat::Format_YUYV:
|
|
- {
|
|
- static constexpr quint32 format[] = { rgba_fourcc, 0, 0, 0 };
|
|
- return format;
|
|
- }
|
|
-
|
|
- case QVideoFrameFormat::Format_Y8:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R8, 0, 0, 0 };
|
|
- return format;
|
|
- }
|
|
- case QVideoFrameFormat::Format_Y16:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R16, 0, 0, 0 };
|
|
- return format;
|
|
- }
|
|
-
|
|
- case QVideoFrameFormat::Format_YUV420P:
|
|
- case QVideoFrameFormat::Format_YUV422P:
|
|
- case QVideoFrameFormat::Format_YV12:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8, 0 };
|
|
- return format;
|
|
- }
|
|
- case QVideoFrameFormat::Format_YUV420P10:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16, 0 };
|
|
- return format;
|
|
- }
|
|
-
|
|
- case QVideoFrameFormat::Format_NV12:
|
|
- case QVideoFrameFormat::Format_NV21:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R8, rg_fourcc, 0, 0 };
|
|
- return format;
|
|
- }
|
|
-
|
|
- case QVideoFrameFormat::Format_P010:
|
|
- case QVideoFrameFormat::Format_P016:
|
|
- {
|
|
- static constexpr quint32 format[] = { DRM_FORMAT_R16, rg16_fourcc, 0, 0 };
|
|
- return format;
|
|
- }
|
|
- }
|
|
- return nullptr;
|
|
-}
|
|
-
|
|
-class VAAPITextureSet : public TextureSet
|
|
-{
|
|
-public:
|
|
- ~VAAPITextureSet();
|
|
- qint64 textureHandle(int plane) override {
|
|
- return textures[plane];
|
|
- }
|
|
-
|
|
- QRhi *rhi = nullptr;
|
|
- QOpenGLContext *glContext = nullptr;
|
|
- int nPlanes = 0;
|
|
- GLuint textures[4] = {};
|
|
-};
|
|
-
|
|
-
|
|
-VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
|
|
- : TextureConverterBackend(nullptr)
|
|
-{
|
|
- qDebug() << ">>>> Creating VAAPI HW accelerator";
|
|
-
|
|
- if (!rhi || rhi->backend() != QRhi::OpenGLES2) {
|
|
- qWarning() << "VAAPITextureConverter: No rhi or non openGL based RHI";
|
|
- this->rhi = nullptr;
|
|
- return;
|
|
- }
|
|
-
|
|
- auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
|
|
- glContext = nativeHandles->context;
|
|
- if (!glContext) {
|
|
- qDebug() << " no GL context, disabling";
|
|
- return;
|
|
- }
|
|
- const QString platform = QGuiApplication::platformName();
|
|
- QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
|
|
- eglDisplay = pni->nativeResourceForIntegration("egldisplay");
|
|
- qDebug() << " platform is" << platform << eglDisplay;
|
|
-
|
|
- if (!eglDisplay) {
|
|
- qDebug() << " no egl display, disabling";
|
|
- return;
|
|
- }
|
|
- eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
|
- if (!eglDisplay) {
|
|
- qDebug() << " no eglImageTargetTexture2D, disabling";
|
|
- return;
|
|
- }
|
|
-
|
|
- // everything ok, indicate that we can do zero copy
|
|
- this->rhi = rhi;
|
|
-}
|
|
-
|
|
-VAAPITextureConverter::~VAAPITextureConverter()
|
|
-{
|
|
-}
|
|
-
|
|
-//#define VA_EXPORT_USE_LAYERS
|
|
-TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
|
|
-{
|
|
-// qDebug() << "VAAPIAccel::getTextures";
|
|
- if (frame->format != AV_PIX_FMT_VAAPI || !eglDisplay) {
|
|
- qDebug() << "format/egl error" << frame->format << eglDisplay;
|
|
- return nullptr;
|
|
- }
|
|
-
|
|
- if (!frame->hw_frames_ctx)
|
|
- return nullptr;
|
|
-
|
|
- auto *fCtx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
|
|
- auto *ctx = fCtx->device_ctx;
|
|
- if (!ctx)
|
|
- return nullptr;
|
|
-
|
|
- auto *vaCtx = (AVVAAPIDeviceContext *)ctx->hwctx;
|
|
- auto vaDisplay = vaCtx->display;
|
|
- if (!vaDisplay) {
|
|
- qDebug() << " no VADisplay, disabling";
|
|
- return nullptr;
|
|
- }
|
|
-
|
|
- VASurfaceID vaSurface = (uintptr_t)frame->data[3];
|
|
-
|
|
- VADRMPRIMESurfaceDescriptor prime;
|
|
- if (vaExportSurfaceHandle(vaDisplay, vaSurface,
|
|
- VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
|
- VA_EXPORT_SURFACE_READ_ONLY |
|
|
-#ifdef VA_EXPORT_USE_LAYERS
|
|
- VA_EXPORT_SURFACE_SEPARATE_LAYERS,
|
|
-#else
|
|
- VA_EXPORT_SURFACE_COMPOSED_LAYERS,
|
|
-#endif
|
|
- &prime) != VA_STATUS_SUCCESS)
|
|
- {
|
|
- qWarning() << "vaExportSurfaceHandle failed";
|
|
- return nullptr;
|
|
- }
|
|
- // ### Check that prime.fourcc is what we expect
|
|
- vaSyncSurface(vaDisplay, vaSurface);
|
|
-
|
|
-// qDebug() << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
|
|
-// << prime.num_objects << "num layers" << prime.num_layers;
|
|
-
|
|
- QOpenGLFunctions functions(glContext);
|
|
-
|
|
- AVPixelFormat fmt = HWAccel::format(frame);
|
|
- bool needsConversion;
|
|
- auto qtFormat = QFFmpegVideoBuffer::toQtPixelFormat(fmt, &needsConversion);
|
|
- auto *drm_formats = fourccFromPixelFormat(qtFormat);
|
|
- if (!drm_formats || needsConversion) {
|
|
- qWarning() << "can't use DMA transfer for pixel format" << fmt << qtFormat;
|
|
- return nullptr;
|
|
- }
|
|
-
|
|
- auto *desc = QVideoTextureHelper::textureDescription(qtFormat);
|
|
- int nPlanes = 0;
|
|
- for (; nPlanes < 5; ++nPlanes) {
|
|
- if (drm_formats[nPlanes] == 0)
|
|
- break;
|
|
- }
|
|
- Q_ASSERT(nPlanes == desc->nplanes);
|
|
- nPlanes = desc->nplanes;
|
|
-// qDebug() << "VAAPIAccel: nPlanes" << nPlanes;
|
|
-
|
|
- rhi->makeThreadLocalNativeContextCurrent();
|
|
-
|
|
- EGLImage images[4];
|
|
- GLuint glTextures[4] = {};
|
|
- functions.glGenTextures(nPlanes, glTextures);
|
|
- for (int i = 0; i < nPlanes; ++i) {
|
|
-#ifdef VA_EXPORT_USE_LAYERS
|
|
-#define LAYER i
|
|
-#define PLANE 0
|
|
- if (prime.layers[i].drm_format != drm_formats[i]) {
|
|
- qWarning() << "expected DRM format check failed expected"
|
|
- << Qt::hex << drm_formats[i] << "got" << prime.layers[i].drm_format;
|
|
- }
|
|
-#else
|
|
-#define LAYER 0
|
|
-#define PLANE i
|
|
-#endif
|
|
-
|
|
- EGLAttrib img_attr[] = {
|
|
- EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_formats[i],
|
|
- EGL_WIDTH, desc->widthForPlane(frame->width, i),
|
|
- EGL_HEIGHT, desc->heightForPlane(frame->height, i),
|
|
- EGL_DMA_BUF_PLANE0_FD_EXT, prime.objects[prime.layers[LAYER].object_index[PLANE]].fd,
|
|
- EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)prime.layers[LAYER].offset[PLANE],
|
|
- EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)prime.layers[LAYER].pitch[PLANE],
|
|
- EGL_NONE
|
|
- };
|
|
- images[i] = eglCreateImage(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr);
|
|
- if (!images[i]) {
|
|
- qWarning() << "eglCreateImage failed for plane" << i << Qt::hex << eglGetError();
|
|
- return nullptr;
|
|
- }
|
|
- functions.glActiveTexture(GL_TEXTURE0 + i);
|
|
- functions.glBindTexture(GL_TEXTURE_2D, glTextures[i]);
|
|
-
|
|
- PFNGLEGLIMAGETARGETTEXTURE2DOESPROC eglImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)this->eglImageTargetTexture2D;
|
|
- eglImageTargetTexture2D(GL_TEXTURE_2D, images[i]);
|
|
- if (glGetError()) {
|
|
- qWarning() << "eglImageTargetTexture2D failed";
|
|
- }
|
|
- }
|
|
-
|
|
- for (int i = 0; i < (int)prime.num_objects; ++i)
|
|
- close(prime.objects[i].fd);
|
|
-
|
|
- for (int i = 0; i < nPlanes; ++i) {
|
|
- functions.glActiveTexture(GL_TEXTURE0 + i);
|
|
- functions.glBindTexture(GL_TEXTURE_2D, 0);
|
|
- eglDestroyImage(eglDisplay, images[i]);
|
|
- }
|
|
-
|
|
- VAAPITextureSet *textureSet = new VAAPITextureSet;
|
|
- textureSet->nPlanes = nPlanes;
|
|
- textureSet->rhi = rhi;
|
|
- textureSet->glContext = glContext;
|
|
-
|
|
- for (int i = 0; i < 4; ++i)
|
|
- textureSet->textures[i] = glTextures[i];
|
|
-// qDebug() << "VAAPIAccel: got textures" << textures[0] << textures[1] << textures[2] << textures[3];
|
|
-
|
|
- return textureSet;
|
|
-}
|
|
-
|
|
-VAAPITextureSet::~VAAPITextureSet()
|
|
-{
|
|
- if (rhi) {
|
|
- rhi->makeThreadLocalNativeContextCurrent();
|
|
- QOpenGLFunctions functions(glContext);
|
|
- functions.glDeleteTextures(nPlanes, textures);
|
|
- }
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
|
|
deleted file mode 100644
|
|
index 03084cc72..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
|
|
+++ /dev/null
|
|
@@ -1,48 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGHWACCEL_VAAPI_P_H
|
|
-#define QFFMPEGHWACCEL_VAAPI_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-#if QT_CONFIG(vaapi)
|
|
-
|
|
-#include <qshareddata.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QRhi;
|
|
-class QOpenGLContext;
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class VAAPITextureConverter : public TextureConverterBackend
|
|
-{
|
|
-public:
|
|
- VAAPITextureConverter(QRhi *rhi);
|
|
- ~VAAPITextureConverter();
|
|
-
|
|
- TextureSet *getTextures(AVFrame *frame) override;
|
|
-
|
|
- Qt::HANDLE eglDisplay = nullptr;
|
|
- QOpenGLContext *glContext = nullptr;
|
|
- QFunctionPointer eglImageTargetTexture2D = nullptr;
|
|
-};
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
|
|
deleted file mode 100644
|
|
index db64f2003..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
|
|
+++ /dev/null
|
|
@@ -1,281 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpeghwaccel_videotoolbox_p.h"
|
|
-
|
|
-#if !defined(Q_OS_DARWIN)
|
|
-#error "Configuration error"
|
|
-#endif
|
|
-
|
|
-#include <qvideoframeformat.h>
|
|
-#include <qffmpegvideobuffer_p.h>
|
|
-#include "private/qvideotexturehelper_p.h"
|
|
-
|
|
-#include <private/qrhi_p.h>
|
|
-#include <private/qrhimetal_p.h>
|
|
-#include <private/qrhigles2_p.h>
|
|
-
|
|
-#include <CoreVideo/CVMetalTexture.h>
|
|
-#include <CoreVideo/CVMetalTextureCache.h>
|
|
-
|
|
-#include <qopenglcontext.h>
|
|
-#ifdef Q_OS_MACOS
|
|
-#import <AppKit/AppKit.h>
|
|
-#endif
|
|
-#import <Metal/Metal.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-static CVMetalTextureCacheRef &mtc(void *&cache) { return reinterpret_cast<CVMetalTextureCacheRef &>(cache); }
|
|
-
|
|
-class VideoToolBoxTextureSet : public TextureSet
|
|
-{
|
|
-public:
|
|
- ~VideoToolBoxTextureSet();
|
|
- qint64 textureHandle(int plane) override;
|
|
-
|
|
- QRhi *rhi = nullptr;
|
|
- CVMetalTextureRef cvMetalTexture[3] = {};
|
|
-
|
|
-#if defined(Q_OS_MACOS)
|
|
- CVOpenGLTextureRef cvOpenGLTexture = nullptr;
|
|
-#elif defined(Q_OS_IOS)
|
|
- CVOpenGLESTextureRef cvOpenGLESTexture = nullptr;
|
|
-#endif
|
|
-
|
|
- CVImageBufferRef m_buffer = nullptr;
|
|
-};
|
|
-
|
|
-VideoToolBoxTextureConverter::VideoToolBoxTextureConverter(QRhi *rhi)
|
|
- : TextureConverterBackend(rhi)
|
|
-{
|
|
- if (!rhi)
|
|
- return;
|
|
-
|
|
- if (rhi->backend() == QRhi::Metal) {
|
|
- qDebug() << " using metal backend";
|
|
- const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles());
|
|
-
|
|
- // Create a Metal Core Video texture cache from the pixel buffer.
|
|
- Q_ASSERT(!cvMetalTextureCache);
|
|
- if (CVMetalTextureCacheCreate(
|
|
- kCFAllocatorDefault,
|
|
- nil,
|
|
- (id<MTLDevice>)metal->dev,
|
|
- nil,
|
|
- &mtc(cvMetalTextureCache)) != kCVReturnSuccess) {
|
|
- qWarning() << "Metal texture cache creation failed";
|
|
- rhi = nullptr;
|
|
- }
|
|
- } else if (rhi->backend() == QRhi::OpenGLES2) {
|
|
-#if QT_CONFIG(opengl)
|
|
-#ifdef Q_OS_MACOS
|
|
- const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
|
|
-
|
|
- auto nsGLContext = gl->context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
|
|
- auto nsGLPixelFormat = nsGLContext.pixelFormat.CGLPixelFormatObj;
|
|
-
|
|
- // Create an OpenGL CoreVideo texture cache from the pixel buffer.
|
|
- if (CVOpenGLTextureCacheCreate(
|
|
- kCFAllocatorDefault,
|
|
- nullptr,
|
|
- reinterpret_cast<CGLContextObj>(nsGLContext.CGLContextObj),
|
|
- nsGLPixelFormat,
|
|
- nil,
|
|
- &cvOpenGLTextureCache)) {
|
|
- qWarning() << "OpenGL texture cache creation failed";
|
|
- rhi = nullptr;
|
|
- }
|
|
-#endif
|
|
-#ifdef Q_OS_IOS
|
|
- // Create an OpenGL CoreVideo texture cache from the pixel buffer.
|
|
- if (CVOpenGLESTextureCacheCreate(
|
|
- kCFAllocatorDefault,
|
|
- nullptr,
|
|
- [EAGLContext currentContext],
|
|
- nullptr,
|
|
- &cvOpenGLESTextureCache)) {
|
|
- qWarning() << "OpenGL texture cache creation failed";
|
|
- rhi = nullptr;
|
|
- }
|
|
-#endif
|
|
-#else
|
|
- rhi = nullptr;
|
|
-#endif // QT_CONFIG(opengl)
|
|
- }
|
|
-}
|
|
-
|
|
-VideoToolBoxTextureConverter::~VideoToolBoxTextureConverter()
|
|
-{
|
|
- freeTextureCaches();
|
|
-}
|
|
-
|
|
-void VideoToolBoxTextureConverter::freeTextureCaches()
|
|
-{
|
|
- if (cvMetalTextureCache)
|
|
- CFRelease(cvMetalTextureCache);
|
|
- cvMetalTextureCache = nullptr;
|
|
-#if defined(Q_OS_MACOS)
|
|
- if (cvOpenGLTextureCache)
|
|
- CFRelease(cvOpenGLTextureCache);
|
|
- cvOpenGLTextureCache = nullptr;
|
|
-#elif defined(Q_OS_IOS)
|
|
- if (cvOpenGLESTextureCache)
|
|
- CFRelease(cvOpenGLESTextureCache);
|
|
- cvOpenGLESTextureCache = nullptr;
|
|
-#endif
|
|
-}
|
|
-
|
|
-static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
|
|
-{
|
|
- switch (f) {
|
|
- default:
|
|
- case QRhiTexture::UnknownFormat:
|
|
- return MTLPixelFormatInvalid;
|
|
- case QRhiTexture::RGBA8:
|
|
- return MTLPixelFormatRGBA8Unorm;
|
|
- case QRhiTexture::BGRA8:
|
|
- return MTLPixelFormatBGRA8Unorm;
|
|
- case QRhiTexture::R8:
|
|
- return MTLPixelFormatR8Unorm;
|
|
- case QRhiTexture::RG8:
|
|
- return MTLPixelFormatRG8Unorm;
|
|
- case QRhiTexture::R16:
|
|
- return MTLPixelFormatR16Unorm;
|
|
- case QRhiTexture::RG16:
|
|
- return MTLPixelFormatRG16Unorm;
|
|
-
|
|
- case QRhiTexture::RGBA16F:
|
|
- return MTLPixelFormatRGBA16Float;
|
|
- case QRhiTexture::RGBA32F:
|
|
- return MTLPixelFormatRGBA32Float;
|
|
- case QRhiTexture::R16F:
|
|
- return MTLPixelFormatR16Float;
|
|
- case QRhiTexture::R32F:
|
|
- return MTLPixelFormatR32Float;
|
|
- }
|
|
-}
|
|
-
|
|
-TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
|
|
-{
|
|
- if (!rhi)
|
|
- return nullptr;
|
|
-
|
|
- bool needsConversion = false;
|
|
- QVideoFrameFormat::PixelFormat pixelFormat = QFFmpegVideoBuffer::toQtPixelFormat(HWAccel::format(frame), &needsConversion);
|
|
- if (needsConversion) {
|
|
- qDebug() << "XXXXXXXXXXXX pixel format needs conversion" << pixelFormat << HWAccel::format(frame);
|
|
- return nullptr;
|
|
- }
|
|
-
|
|
- CVPixelBufferRef buffer = (CVPixelBufferRef)frame->data[3];
|
|
-
|
|
- VideoToolBoxTextureSet *textureSet = new VideoToolBoxTextureSet;
|
|
- textureSet->m_buffer = buffer;
|
|
- textureSet->rhi = rhi;
|
|
- CVPixelBufferRetain(buffer);
|
|
-
|
|
- auto *textureDescription = QVideoTextureHelper::textureDescription(pixelFormat);
|
|
- int bufferPlanes = CVPixelBufferGetPlaneCount(buffer);
|
|
-// qDebug() << "XXXXX getTextures" << pixelFormat << bufferPlanes << buffer;
|
|
-
|
|
- if (rhi->backend() == QRhi::Metal) {
|
|
- for (int plane = 0; plane < bufferPlanes; ++plane) {
|
|
- size_t width = CVPixelBufferGetWidth(buffer);
|
|
- size_t height = CVPixelBufferGetHeight(buffer);
|
|
- width = textureDescription->widthForPlane(width, plane);
|
|
- height = textureDescription->heightForPlane(height, plane);
|
|
-
|
|
- // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
|
|
- auto ret = CVMetalTextureCacheCreateTextureFromImage(
|
|
- kCFAllocatorDefault,
|
|
- mtc(cvMetalTextureCache),
|
|
- buffer, nil,
|
|
- rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
|
|
- width, height,
|
|
- plane,
|
|
- &textureSet->cvMetalTexture[plane]);
|
|
-
|
|
- if (ret != kCVReturnSuccess)
|
|
- qWarning() << "texture creation failed" << ret;
|
|
-// auto t = CVMetalTextureGetTexture(textureSet->cvMetalTexture[plane]);
|
|
-// qDebug() << " metal texture for plane" << plane << "is" << quint64(textureSet->cvMetalTexture[plane]) << width << height;
|
|
-// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height;
|
|
- }
|
|
- } else if (rhi->backend() == QRhi::OpenGLES2) {
|
|
-#if QT_CONFIG(opengl)
|
|
-#ifdef Q_OS_MACOS
|
|
- CVOpenGLTextureCacheFlush(cvOpenGLTextureCache, 0);
|
|
- // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
|
|
- const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
|
|
- kCFAllocatorDefault,
|
|
- cvOpenGLTextureCache,
|
|
- buffer,
|
|
- nil,
|
|
- &textureSet->cvOpenGLTexture);
|
|
- if (cvret != kCVReturnSuccess)
|
|
- qWarning() << "OpenGL texture creation failed" << cvret;
|
|
-
|
|
- Q_ASSERT(CVOpenGLTextureGetTarget(textureSet->cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
|
|
-#endif
|
|
-#ifdef Q_OS_IOS
|
|
- CVOpenGLESTextureCacheFlush(cvOpenGLESTextureCache, 0);
|
|
- // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
|
|
- const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
|
|
- kCFAllocatorDefault,
|
|
- cvOpenGLESTextureCache,
|
|
- buffer,
|
|
- nil,
|
|
- GL_TEXTURE_2D,
|
|
- GL_RGBA,
|
|
- CVPixelBufferGetWidth(buffer),
|
|
- CVPixelBufferGetHeight(buffer),
|
|
- GL_RGBA,
|
|
- GL_UNSIGNED_BYTE,
|
|
- 0,
|
|
- &textureSet->cvOpenGLESTexture);
|
|
- if (cvret != kCVReturnSuccess)
|
|
- qWarning() << "OpenGL ES texture creation failed" << cvret;
|
|
-#endif
|
|
-#endif
|
|
- }
|
|
-
|
|
- return textureSet;
|
|
-}
|
|
-
|
|
-VideoToolBoxTextureSet::~VideoToolBoxTextureSet()
|
|
-{
|
|
- for (int i = 0; i < 4; ++i)
|
|
- if (cvMetalTexture[i])
|
|
- CFRelease(cvMetalTexture[i]);
|
|
-#if defined(Q_OS_MACOS)
|
|
- if (cvOpenGLTexture)
|
|
- CVOpenGLTextureRelease(cvOpenGLTexture);
|
|
-#elif defined(Q_OS_IOS)
|
|
- if (cvOpenGLESTexture)
|
|
- CFRelease(cvOpenGLESTexture);
|
|
-#endif
|
|
- CVPixelBufferRelease(m_buffer);
|
|
-}
|
|
-
|
|
-qint64 VideoToolBoxTextureSet::textureHandle(int plane)
|
|
-{
|
|
- if (rhi->backend() == QRhi::Metal)
|
|
- return cvMetalTexture[plane] ? qint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
|
|
-#if QT_CONFIG(opengl)
|
|
- Q_ASSERT(plane == 0);
|
|
-#ifdef Q_OS_MACOS
|
|
- return CVOpenGLTextureGetName(cvOpenGLTexture);
|
|
-#endif
|
|
-#ifdef Q_OS_IOS
|
|
- return CVOpenGLESTextureGetName(cvOpenGLESTexture);
|
|
-#endif
|
|
-#endif
|
|
-}
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
|
|
deleted file mode 100644
|
|
index 44fa32dd2..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
|
|
+++ /dev/null
|
|
@@ -1,63 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGHWACCEL_VIDEOTOOLBOX_P_H
|
|
-#define QFFMPEGHWACCEL_VIDEOTOOLBOX_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-#ifdef Q_OS_DARWIN
|
|
-
|
|
-#include <CoreVideo/CVBase.h>
|
|
-#include <CoreVideo/CVPixelBuffer.h>
|
|
-#include <CoreVideo/CVImageBuffer.h>
|
|
-
|
|
-#include <CoreVideo/CVMetalTexture.h>
|
|
-#if defined(Q_OS_MACOS)
|
|
-#include <CoreVideo/CVOpenGLTextureCache.h>
|
|
-#elif defined(Q_OS_IOS)
|
|
-#include <CoreVideo/CVOpenGLESTextureCache.h>
|
|
-#endif
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QRhi;
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class VideoToolBoxTextureConverter : public TextureConverterBackend
|
|
-{
|
|
-public:
|
|
- VideoToolBoxTextureConverter(QRhi *rhi);
|
|
- ~VideoToolBoxTextureConverter();
|
|
- TextureSet *getTextures(AVFrame *frame) override;
|
|
-
|
|
-private:
|
|
- void freeTextureCaches();
|
|
-
|
|
- // can not forward declare that type from C++ :/
|
|
- void *cvMetalTextureCache = nullptr;
|
|
-#if defined(Q_OS_MACOS)
|
|
- CVOpenGLTextureCacheRef cvOpenGLTextureCache = nullptr;
|
|
-#elif defined(Q_OS_IOS)
|
|
- CVOpenGLESTextureCacheRef cvOpenGLESTextureCache = nullptr;
|
|
-#endif
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
|
|
index 3d5fbc039..b6865761c 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
|
|
@@ -6,7 +6,6 @@
|
|
#include "private/qplatformaudioinput_p.h"
|
|
#include "private/qplatformaudiooutput_p.h"
|
|
#include "qffmpegimagecapture_p.h"
|
|
-#include "qffmpegmediarecorder_p.h"
|
|
#include "private/qplatformcamera_p.h"
|
|
#include "qvideosink.h"
|
|
|
|
@@ -73,22 +72,12 @@ void QFFmpegMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCap
|
|
|
|
void QFFmpegMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *recorder)
|
|
{
|
|
- auto *r = static_cast<QFFmpegMediaRecorder *>(recorder);
|
|
- if (m_mediaRecorder == r)
|
|
- return;
|
|
-
|
|
- if (m_mediaRecorder)
|
|
- m_mediaRecorder->setCaptureSession(nullptr);
|
|
- m_mediaRecorder = r;
|
|
- if (m_mediaRecorder)
|
|
- m_mediaRecorder->setCaptureSession(this);
|
|
-
|
|
- emit encoderChanged();
|
|
+ return;
|
|
}
|
|
|
|
QPlatformMediaRecorder *QFFmpegMediaCaptureSession::mediaRecorder()
|
|
{
|
|
- return m_mediaRecorder;
|
|
+ return nullptr;
|
|
}
|
|
|
|
void QFFmpegMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
|
|
index 9e9c77551..858a537cc 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
|
|
@@ -54,7 +54,6 @@ private:
|
|
QPlatformCamera *m_camera = nullptr;
|
|
QPlatformAudioInput *m_audioInput = nullptr;
|
|
QFFmpegImageCapture *m_imageCapture = nullptr;
|
|
- QFFmpegMediaRecorder *m_mediaRecorder = nullptr;
|
|
QPlatformAudioOutput *m_audioOutput = nullptr;
|
|
QVideoSink *m_videoSink = nullptr;
|
|
};
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
|
|
index 2561d564d..00b838d50 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
|
|
@@ -2,236 +2,13 @@
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpeg_p.h"
|
|
#include "qaudioformat.h"
|
|
#include "qimagewriter.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
-static struct {
|
|
- AVCodecID id;
|
|
- QMediaFormat::VideoCodec codec;
|
|
-} videoCodecMap [] = {
|
|
- { AV_CODEC_ID_MPEG1VIDEO, QMediaFormat::VideoCodec::MPEG1 },
|
|
- { AV_CODEC_ID_MPEG2VIDEO, QMediaFormat::VideoCodec::MPEG2 },
|
|
- { AV_CODEC_ID_MPEG4, QMediaFormat::VideoCodec::MPEG4 },
|
|
- { AV_CODEC_ID_H264, QMediaFormat::VideoCodec::H264 },
|
|
- { AV_CODEC_ID_HEVC, QMediaFormat::VideoCodec::H265 },
|
|
- { AV_CODEC_ID_VP8, QMediaFormat::VideoCodec::VP8 },
|
|
- { AV_CODEC_ID_VP9, QMediaFormat::VideoCodec::VP9 },
|
|
- { AV_CODEC_ID_AV1, QMediaFormat::VideoCodec::AV1 },
|
|
- { AV_CODEC_ID_THEORA, QMediaFormat::VideoCodec::Theora },
|
|
- { AV_CODEC_ID_WMV3, QMediaFormat::VideoCodec::WMV },
|
|
- { AV_CODEC_ID_MJPEG, QMediaFormat::VideoCodec::MotionJPEG }
|
|
-};
|
|
-
|
|
-static AVCodecID codecId(QMediaFormat::VideoCodec codec)
|
|
-{
|
|
- for (const auto &c : videoCodecMap) {
|
|
- if (c.codec == codec)
|
|
- return c.id;
|
|
- }
|
|
- return AV_CODEC_ID_NONE;
|
|
-}
|
|
-
|
|
-static struct {
|
|
- AVCodecID id;
|
|
- QMediaFormat::AudioCodec codec;
|
|
-} audioCodecMap [] = {
|
|
- { AV_CODEC_ID_MP3, QMediaFormat::AudioCodec::MP3 },
|
|
- { AV_CODEC_ID_AAC, QMediaFormat::AudioCodec::AAC },
|
|
- { AV_CODEC_ID_AC3, QMediaFormat::AudioCodec::AC3 },
|
|
- { AV_CODEC_ID_EAC3, QMediaFormat::AudioCodec::EAC3 },
|
|
- { AV_CODEC_ID_FLAC, QMediaFormat::AudioCodec::FLAC },
|
|
- { AV_CODEC_ID_TRUEHD, QMediaFormat::AudioCodec::DolbyTrueHD },
|
|
- { AV_CODEC_ID_OPUS, QMediaFormat::AudioCodec::Opus },
|
|
- { AV_CODEC_ID_VORBIS, QMediaFormat::AudioCodec::Vorbis },
|
|
- { AV_CODEC_ID_PCM_S16LE, QMediaFormat::AudioCodec::Wave },
|
|
- { AV_CODEC_ID_WMAPRO, QMediaFormat::AudioCodec::WMA },
|
|
- { AV_CODEC_ID_ALAC, QMediaFormat::AudioCodec::ALAC }
|
|
-};
|
|
-
|
|
-static AVCodecID codecId(QMediaFormat::AudioCodec codec)
|
|
-{
|
|
- for (const auto &c : audioCodecMap) {
|
|
- if (c.codec == codec)
|
|
- return c.id;
|
|
- }
|
|
- return AV_CODEC_ID_NONE;
|
|
-}
|
|
-
|
|
-// mimetypes are mostly copied from qmediaformat.cpp. Unfortunately, FFmpeg uses
|
|
-// in some cases slightly different mimetypes
|
|
-static const struct
|
|
-{
|
|
- QMediaFormat::FileFormat fileFormat;
|
|
- const char *mimeType;
|
|
- const char *name; // disambiguate if we have several muxers/demuxers
|
|
-} map[QMediaFormat::LastFileFormat + 1] = {
|
|
- { QMediaFormat::WMV, "video/x-ms-asf", "asf" },
|
|
- { QMediaFormat::AVI, "video/x-msvideo", nullptr },
|
|
- { QMediaFormat::Matroska, "video/x-matroska", nullptr },
|
|
- { QMediaFormat::MPEG4, "video/mp4", "mp4" },
|
|
- { QMediaFormat::Ogg, "video/ogg", nullptr },
|
|
- // QuickTime is the same as MP4
|
|
- { QMediaFormat::WebM, "video/webm", "webm" },
|
|
- // Audio Formats
|
|
- // Mpeg4Audio is the same as MP4 without the video codecs
|
|
- { QMediaFormat::AAC, "audio/aac", nullptr },
|
|
- // WMA is the same as WMV
|
|
- { QMediaFormat::FLAC, "audio/x-flac", nullptr },
|
|
- { QMediaFormat::MP3, "audio/mpeg", "mp3" },
|
|
- { QMediaFormat::Wave, "audio/x-wav", nullptr },
|
|
- { QMediaFormat::UnspecifiedFormat, nullptr, nullptr }
|
|
-};
|
|
-
|
|
-template <typename AVFormat>
|
|
-static QMediaFormat::FileFormat formatForAVFormat(AVFormat *format)
|
|
-{
|
|
-
|
|
- if (!format->mime_type || !*format->mime_type)
|
|
- return QMediaFormat::UnspecifiedFormat;
|
|
-
|
|
- auto *m = map;
|
|
- while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
|
|
- if (m->mimeType && !strcmp(m->mimeType, format->mime_type)) {
|
|
- // check if the name matches. This is used to disambiguate where FFmpeg provides
|
|
- // multiple muxers or demuxers
|
|
- if (!m->name || !strcmp(m->name, format->name))
|
|
- return m->fileFormat;
|
|
- }
|
|
- ++m;
|
|
- }
|
|
-
|
|
- return QMediaFormat::UnspecifiedFormat;
|
|
-}
|
|
-
|
|
-static const AVOutputFormat *avFormatForFormat(QMediaFormat::FileFormat format)
|
|
-{
|
|
- if (format == QMediaFormat::QuickTime || format == QMediaFormat::Mpeg4Audio)
|
|
- format = QMediaFormat::MPEG4;
|
|
- if (format == QMediaFormat::WMA)
|
|
- format = QMediaFormat::WMV;
|
|
-
|
|
- auto *m = map;
|
|
- while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
|
|
- if (m->fileFormat == format)
|
|
- return av_guess_format(m->name, nullptr, m->mimeType);
|
|
- ++m;
|
|
- }
|
|
-
|
|
- return nullptr;
|
|
-}
|
|
-
|
|
-
|
|
QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
|
|
{
|
|
- qDebug() << ">>>> listing codecs";
|
|
-
|
|
- QList<QMediaFormat::AudioCodec> audioEncoders;
|
|
- QList<QMediaFormat::AudioCodec> extraAudioDecoders;
|
|
- QList<QMediaFormat::VideoCodec> videoEncoders;
|
|
- QList<QMediaFormat::VideoCodec> extraVideoDecoders;
|
|
-
|
|
- const AVCodecDescriptor *descriptor = nullptr;
|
|
- while ((descriptor = avcodec_descriptor_next(descriptor))) {
|
|
- bool canEncode = (avcodec_find_encoder(descriptor->id) != nullptr);
|
|
- bool canDecode = (avcodec_find_decoder(descriptor->id) != nullptr);
|
|
- auto videoCodec = videoCodecForAVCodecId(descriptor->id);
|
|
- auto audioCodec = audioCodecForAVCodecId(descriptor->id);
|
|
- if (descriptor->type == AVMEDIA_TYPE_VIDEO && videoCodec != QMediaFormat::VideoCodec::Unspecified) {
|
|
- if (canEncode) {
|
|
- if (!videoEncoders.contains(videoCodec))
|
|
- videoEncoders.append(videoCodec);
|
|
- } else if (canDecode) {
|
|
- if (!extraVideoDecoders.contains(videoCodec))
|
|
- extraVideoDecoders.append(videoCodec);
|
|
- }
|
|
- }
|
|
-
|
|
- else if (descriptor->type == AVMEDIA_TYPE_AUDIO && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
|
|
- if (canEncode) {
|
|
- if (!audioEncoders.contains(audioCodec))
|
|
- audioEncoders.append(audioCodec);
|
|
- } else if (canDecode) {
|
|
- if (!extraAudioDecoders.contains(audioCodec))
|
|
- extraAudioDecoders.append(audioCodec);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // get demuxers
|
|
-// qDebug() << ">>>> Muxers";
|
|
- void *opaque = nullptr;
|
|
- const AVOutputFormat *outputFormat = nullptr;
|
|
- while ((outputFormat = av_muxer_iterate(&opaque))) {
|
|
- auto mediaFormat = formatForAVFormat(outputFormat);
|
|
- if (mediaFormat == QMediaFormat::UnspecifiedFormat)
|
|
- continue;
|
|
-// qDebug() << " mux:" << outputFormat->name << outputFormat->long_name << outputFormat->mime_type << outputFormat->extensions << mediaFormat;
|
|
-
|
|
- CodecMap encoder;
|
|
- encoder.format = mediaFormat;
|
|
-
|
|
- for (auto codec : audioEncoders) {
|
|
- auto id = codecId(codec);
|
|
- // only add the codec if it can be used with this container
|
|
- if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
|
|
- // add codec for container
|
|
-// qDebug() << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
|
|
- encoder.audio.append(codec);
|
|
- }
|
|
- }
|
|
- for (auto codec : videoEncoders) {
|
|
- auto id = codecId(codec);
|
|
- // only add the codec if it can be used with this container
|
|
- if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
|
|
- // add codec for container
|
|
-// qDebug() << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
|
|
- encoder.video.append(codec);
|
|
- }
|
|
- }
|
|
-
|
|
- // sanity checks and handling special cases
|
|
- if (encoder.audio.isEmpty() && encoder.video.isEmpty())
|
|
- continue;
|
|
- switch (encoder.format) {
|
|
- case QMediaFormat::WMV:
|
|
- // add WMA
|
|
- encoders.append({ QMediaFormat::WMA, encoder.audio, {} });
|
|
- break;
|
|
- case QMediaFormat::MPEG4:
|
|
- // add Mpeg4Audio and QuickTime
|
|
- encoders.append({ QMediaFormat::QuickTime, encoder.audio, encoder.video });
|
|
- encoders.append({ QMediaFormat::Mpeg4Audio, encoder.audio, {} });
|
|
- break;
|
|
- case QMediaFormat::Wave:
|
|
- // FFmpeg allows other encoded formats in WAV containers, but we do not want that
|
|
- if (!encoder.audio.contains(QMediaFormat::AudioCodec::Wave))
|
|
- continue;
|
|
- encoder.audio = { QMediaFormat::AudioCodec::Wave };
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- encoders.append(encoder);
|
|
- }
|
|
-
|
|
- // FFmpeg doesn't allow querying supported codecs for decoders
|
|
- // we take a simple approximation stating that we can decode what we
|
|
- // can encode. That's a safe subset.
|
|
- decoders = encoders;
|
|
-
|
|
-// qDebug() << "extraDecoders:" << extraAudioDecoders << extraVideoDecoders;
|
|
- // FFmpeg can currently only decode WMA and WMV, not encode
|
|
- if (extraAudioDecoders.contains(QMediaFormat::AudioCodec::WMA)) {
|
|
- decoders[QMediaFormat::WMA].audio.append(QMediaFormat::AudioCodec::WMA);
|
|
- decoders[QMediaFormat::WMV].audio.append(QMediaFormat::AudioCodec::WMA);
|
|
- }
|
|
- if (extraVideoDecoders.contains(QMediaFormat::VideoCodec::WMV)) {
|
|
- decoders[QMediaFormat::WMV].video.append(QMediaFormat::VideoCodec::WMV);
|
|
- }
|
|
-
|
|
// Add image formats we support. We currently simply use Qt's built-in image write
|
|
// to save images. That doesn't give us HDR support or support for larger bit depths,
|
|
// but most cameras can currently not generate those anyway.
|
|
@@ -251,256 +28,5 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
|
|
|
|
QFFmpegMediaFormatInfo::~QFFmpegMediaFormatInfo() = default;
|
|
|
|
-QMediaFormat::AudioCodec QFFmpegMediaFormatInfo::audioCodecForAVCodecId(AVCodecID id)
|
|
-{
|
|
- for (const auto &c : audioCodecMap) {
|
|
- if (c.id == id)
|
|
- return c.codec;
|
|
- }
|
|
- return QMediaFormat::AudioCodec::Unspecified;
|
|
-}
|
|
-
|
|
-QMediaFormat::VideoCodec QFFmpegMediaFormatInfo::videoCodecForAVCodecId(AVCodecID id)
|
|
-{
|
|
- for (const auto &c : videoCodecMap) {
|
|
- if (c.id == id)
|
|
- return c.codec;
|
|
- }
|
|
- return QMediaFormat::VideoCodec::Unspecified;
|
|
-}
|
|
-
|
|
-QMediaFormat::FileFormat
|
|
-QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(const AVInputFormat *format)
|
|
-{
|
|
- // Seems like FFmpeg uses different names for muxers and demuxers of the same format.
|
|
- // that makes it somewhat cumbersome to detect things correctly.
|
|
- // The input formats have a comma separated list of short names. We check the first one of those
|
|
- // as the docs specify that you only append to the list
|
|
- static const struct
|
|
- {
|
|
- QMediaFormat::FileFormat fileFormat;
|
|
- const char *name;
|
|
- } map[QMediaFormat::LastFileFormat + 1] = {
|
|
- { QMediaFormat::WMV, "asf" },
|
|
- { QMediaFormat::AVI, "avi" },
|
|
- { QMediaFormat::Matroska, "matroska" },
|
|
- { QMediaFormat::MPEG4, "mov" },
|
|
- { QMediaFormat::Ogg, "ogg" },
|
|
- { QMediaFormat::WebM, "webm" },
|
|
- // Audio Formats
|
|
- // Mpeg4Audio is the same as MP4 without the video codecs
|
|
- { QMediaFormat::AAC, "aac"},
|
|
- // WMA is the same as WMV
|
|
- { QMediaFormat::FLAC, "flac" },
|
|
- { QMediaFormat::MP3, "mp3" },
|
|
- { QMediaFormat::Wave, "wav" },
|
|
- { QMediaFormat::UnspecifiedFormat, nullptr }
|
|
- };
|
|
-
|
|
- if (!format->name)
|
|
- return QMediaFormat::UnspecifiedFormat;
|
|
-
|
|
- auto *m = map;
|
|
- while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
|
|
- if (!strncmp(m->name, format->name, strlen(m->name)))
|
|
- return m->fileFormat;
|
|
- ++m;
|
|
- }
|
|
-
|
|
- return QMediaFormat::UnspecifiedFormat;
|
|
-}
|
|
-
|
|
-const AVOutputFormat *
|
|
-QFFmpegMediaFormatInfo::outputFormatForFileFormat(QMediaFormat::FileFormat format)
|
|
-{
|
|
- return avFormatForFormat(format);
|
|
-}
|
|
-
|
|
-AVCodecID QFFmpegMediaFormatInfo::codecIdForVideoCodec(QMediaFormat::VideoCodec codec)
|
|
-{
|
|
- return codecId(codec);
|
|
-}
|
|
-
|
|
-AVCodecID QFFmpegMediaFormatInfo::codecIdForAudioCodec(QMediaFormat::AudioCodec codec)
|
|
-{
|
|
- return codecId(codec);
|
|
-}
|
|
-
|
|
-QAudioFormat::SampleFormat QFFmpegMediaFormatInfo::sampleFormat(AVSampleFormat format)
|
|
-{
|
|
- switch (format) {
|
|
- case AV_SAMPLE_FMT_NONE:
|
|
- default:
|
|
- return QAudioFormat::Unknown;
|
|
- case AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
|
|
- case AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits: planar
|
|
- return QAudioFormat::UInt8;
|
|
- case AV_SAMPLE_FMT_S16: ///< signed 16 bits
|
|
- case AV_SAMPLE_FMT_S16P: ///< signed 16 bits: planar
|
|
- return QAudioFormat::Int16;
|
|
- case AV_SAMPLE_FMT_S32: ///< signed 32 bits
|
|
- case AV_SAMPLE_FMT_S32P: ///< signed 32 bits: planar
|
|
- return QAudioFormat::Int32;
|
|
- case AV_SAMPLE_FMT_FLT: ///< float
|
|
- case AV_SAMPLE_FMT_FLTP: ///< float: planar
|
|
- return QAudioFormat::Float;
|
|
- case AV_SAMPLE_FMT_DBL: ///< double
|
|
- case AV_SAMPLE_FMT_DBLP: ///< double: planar
|
|
- case AV_SAMPLE_FMT_S64: ///< signed 64 bits
|
|
- case AV_SAMPLE_FMT_S64P: ///< signed 64 bits, planar
|
|
- // let's use float
|
|
- return QAudioFormat::Float;
|
|
- }
|
|
-}
|
|
-
|
|
-AVSampleFormat QFFmpegMediaFormatInfo::avSampleFormat(QAudioFormat::SampleFormat format)
|
|
-{
|
|
- switch (format) {
|
|
- case QAudioFormat::UInt8:
|
|
- return AV_SAMPLE_FMT_U8;
|
|
- case QAudioFormat::Int16:
|
|
- return AV_SAMPLE_FMT_S16;
|
|
- case QAudioFormat::Int32:
|
|
- return AV_SAMPLE_FMT_S32;
|
|
- case QAudioFormat::Float:
|
|
- return AV_SAMPLE_FMT_FLT;
|
|
- default:
|
|
- return AV_SAMPLE_FMT_NONE;
|
|
- }
|
|
-}
|
|
-
|
|
-int64_t QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::ChannelConfig channelConfig)
|
|
-{
|
|
- int64_t avChannelLayout = 0;
|
|
- if (channelConfig & (1 << QAudioFormat::FrontLeft))
|
|
- avChannelLayout |= AV_CH_FRONT_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::FrontRight))
|
|
- avChannelLayout |= AV_CH_FRONT_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::FrontCenter))
|
|
- avChannelLayout |= AV_CH_FRONT_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::LFE))
|
|
- avChannelLayout |= AV_CH_LOW_FREQUENCY;
|
|
- if (channelConfig & (1 << QAudioFormat::BackLeft))
|
|
- avChannelLayout |= AV_CH_BACK_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::BackRight))
|
|
- avChannelLayout |= AV_CH_BACK_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::FrontLeftOfCenter))
|
|
- avChannelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::FrontRightOfCenter))
|
|
- avChannelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::BackCenter))
|
|
- avChannelLayout |= AV_CH_BACK_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::LFE2))
|
|
- avChannelLayout |= AV_CH_LOW_FREQUENCY_2;
|
|
- if (channelConfig & (1 << QAudioFormat::SideLeft))
|
|
- avChannelLayout |= AV_CH_SIDE_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::SideRight))
|
|
- avChannelLayout |= AV_CH_SIDE_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopFrontLeft))
|
|
- avChannelLayout |= AV_CH_TOP_FRONT_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopFrontRight))
|
|
- avChannelLayout |= AV_CH_TOP_FRONT_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopFrontCenter))
|
|
- avChannelLayout |= AV_CH_TOP_FRONT_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::TopCenter))
|
|
- avChannelLayout |= AV_CH_TOP_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::TopBackLeft))
|
|
- avChannelLayout |= AV_CH_TOP_BACK_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopBackRight))
|
|
- avChannelLayout |= AV_CH_TOP_BACK_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopBackCenter))
|
|
- avChannelLayout |= AV_CH_TOP_BACK_CENTER;
|
|
- // The defines used below got added together for FFmpeg 4.4
|
|
-#ifdef AV_CH_TOP_SIDE_LEFT
|
|
- if (channelConfig & (1 << QAudioFormat::TopSideLeft))
|
|
- avChannelLayout |= AV_CH_TOP_SIDE_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::TopSideRight))
|
|
- avChannelLayout |= AV_CH_TOP_SIDE_RIGHT;
|
|
- if (channelConfig & (1 << QAudioFormat::BottomFrontCenter))
|
|
- avChannelLayout |= AV_CH_BOTTOM_FRONT_CENTER;
|
|
- if (channelConfig & (1 << QAudioFormat::BottomFrontLeft))
|
|
- avChannelLayout |= AV_CH_BOTTOM_FRONT_LEFT;
|
|
- if (channelConfig & (1 << QAudioFormat::BottomFrontRight))
|
|
- avChannelLayout |= AV_CH_BOTTOM_FRONT_RIGHT;
|
|
-#endif
|
|
- return avChannelLayout;
|
|
-}
|
|
-
|
|
-QAudioFormat::ChannelConfig QFFmpegMediaFormatInfo::channelConfigForAVLayout(int64_t avChannelLayout)
|
|
-{
|
|
- quint32 channelConfig = 0;
|
|
- if (avChannelLayout & AV_CH_FRONT_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeft);
|
|
- if (avChannelLayout & AV_CH_FRONT_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRight);
|
|
- if (avChannelLayout & AV_CH_FRONT_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontCenter);
|
|
- if (avChannelLayout & AV_CH_LOW_FREQUENCY)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE);
|
|
- if (avChannelLayout & AV_CH_BACK_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackLeft);
|
|
- if (avChannelLayout & AV_CH_BACK_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackRight);
|
|
- if (avChannelLayout & AV_CH_FRONT_LEFT_OF_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeftOfCenter);
|
|
- if (avChannelLayout & AV_CH_FRONT_RIGHT_OF_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRightOfCenter);
|
|
- if (avChannelLayout & AV_CH_BACK_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackCenter);
|
|
- if (avChannelLayout & AV_CH_LOW_FREQUENCY_2)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE2);
|
|
- if (avChannelLayout & AV_CH_SIDE_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideLeft);
|
|
- if (avChannelLayout & AV_CH_SIDE_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideRight);
|
|
- if (avChannelLayout & AV_CH_TOP_FRONT_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontLeft);
|
|
- if (avChannelLayout & AV_CH_TOP_FRONT_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontRight);
|
|
- if (avChannelLayout & AV_CH_TOP_FRONT_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontCenter);
|
|
- if (avChannelLayout & AV_CH_TOP_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopCenter);
|
|
- if (avChannelLayout & AV_CH_TOP_BACK_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackLeft);
|
|
- if (avChannelLayout & AV_CH_TOP_BACK_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackRight);
|
|
- if (avChannelLayout & AV_CH_TOP_BACK_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackCenter);
|
|
- // The defines used below got added together for FFmpeg 4.4
|
|
-#ifdef AV_CH_TOP_SIDE_LEFT
|
|
- if (avChannelLayout & AV_CH_TOP_SIDE_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideLeft);
|
|
- if (avChannelLayout & AV_CH_TOP_SIDE_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideRight);
|
|
- if (avChannelLayout & AV_CH_BOTTOM_FRONT_CENTER)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontCenter);
|
|
- if (avChannelLayout & AV_CH_BOTTOM_FRONT_LEFT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontLeft);
|
|
- if (avChannelLayout & AV_CH_BOTTOM_FRONT_RIGHT)
|
|
- channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontRight);
|
|
-#endif
|
|
- return QAudioFormat::ChannelConfig(channelConfig);
|
|
-}
|
|
-
|
|
-QAudioFormat QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(AVCodecParameters *codecpar)
|
|
-{
|
|
- QAudioFormat format;
|
|
- format.setSampleFormat(sampleFormat(AVSampleFormat(codecpar->format)));
|
|
- format.setSampleRate(codecpar->sample_rate);
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- uint64_t channelLayout = codecpar->channel_layout;
|
|
- if (!channelLayout)
|
|
- channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
|
|
-#else
|
|
- uint64_t channelLayout = 0;
|
|
- if (codecpar->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
|
|
- channelLayout = codecpar->ch_layout.u.mask;
|
|
- else
|
|
- channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->ch_layout.nb_channels));
|
|
-#endif
|
|
- format.setChannelConfig(channelConfigForAVLayout(channelLayout));
|
|
- return format;
|
|
-}
|
|
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
|
|
index 52fcf6f72..e34005bbf 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
|
|
@@ -19,7 +19,6 @@
|
|
#include <qhash.h>
|
|
#include <qlist.h>
|
|
#include <qaudioformat.h>
|
|
-#include "qffmpeg_p.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
@@ -28,23 +27,6 @@ class QFFmpegMediaFormatInfo : public QPlatformMediaFormatInfo
|
|
public:
|
|
QFFmpegMediaFormatInfo();
|
|
~QFFmpegMediaFormatInfo();
|
|
-
|
|
- static QMediaFormat::VideoCodec videoCodecForAVCodecId(AVCodecID id);
|
|
- static QMediaFormat::AudioCodec audioCodecForAVCodecId(AVCodecID id);
|
|
- static QMediaFormat::FileFormat fileFormatForAVInputFormat(const AVInputFormat *format);
|
|
-
|
|
- static const AVOutputFormat *outputFormatForFileFormat(QMediaFormat::FileFormat format);
|
|
-
|
|
- static AVCodecID codecIdForVideoCodec(QMediaFormat::VideoCodec codec);
|
|
- static AVCodecID codecIdForAudioCodec(QMediaFormat::AudioCodec codec);
|
|
-
|
|
- static QAudioFormat::SampleFormat sampleFormat(AVSampleFormat format);
|
|
- static AVSampleFormat avSampleFormat(QAudioFormat::SampleFormat format);
|
|
-
|
|
- static int64_t avChannelLayout(QAudioFormat::ChannelConfig channelConfig);
|
|
- static QAudioFormat::ChannelConfig channelConfigForAVLayout(int64_t avChannelLayout);
|
|
-
|
|
- static QAudioFormat audioFormatFromCodecParameters(AVCodecParameters *codecPar);
|
|
};
|
|
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
|
|
index e55444cc2..c07c0ebc7 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
|
|
@@ -5,13 +5,9 @@
|
|
#include <qcameradevice.h>
|
|
#include "qffmpegmediaintegration_p.h"
|
|
#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpegmediaplayer_p.h"
|
|
#include "qffmpegvideosink_p.h"
|
|
#include "qffmpegmediacapturesession_p.h"
|
|
-#include "qffmpegmediarecorder_p.h"
|
|
#include "qffmpegimagecapture_p.h"
|
|
-#include "qffmpegaudioinput_p.h"
|
|
-#include "qffmpegaudiodecoder_p.h"
|
|
|
|
#ifdef Q_OS_MACOS
|
|
#include <VideoToolbox/VideoToolbox.h>
|
|
@@ -24,13 +20,6 @@
|
|
#include "qwindowsvideodevices_p.h"
|
|
#endif
|
|
|
|
-#ifdef Q_OS_ANDROID
|
|
-# include "jni.h"
|
|
-extern "C" {
|
|
-# include <libavcodec/jni.h>
|
|
-}
|
|
-#endif
|
|
-
|
|
#if QT_CONFIG(linux_v4l)
|
|
#include "qv4l2camera_p.h"
|
|
#endif
|
|
@@ -86,21 +75,11 @@ QPlatformMediaFormatInfo *QFFmpegMediaIntegration::formatInfo()
|
|
return m_formatsInfo;
|
|
}
|
|
|
|
-QMaybe<QPlatformAudioDecoder *> QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
|
|
-{
|
|
- return new QFFmpegAudioDecoder(decoder);
|
|
-}
|
|
-
|
|
QMaybe<QPlatformMediaCaptureSession *> QFFmpegMediaIntegration::createCaptureSession()
|
|
{
|
|
return new QFFmpegMediaCaptureSession();
|
|
}
|
|
|
|
-QMaybe<QPlatformMediaPlayer *> QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
|
|
-{
|
|
- return new QFFmpegMediaPlayer(player);
|
|
-}
|
|
-
|
|
QMaybe<QPlatformCamera *> QFFmpegMediaIntegration::createCamera(QCamera *camera)
|
|
{
|
|
#ifdef Q_OS_DARWIN
|
|
@@ -115,11 +94,6 @@ QMaybe<QPlatformCamera *> QFFmpegMediaIntegration::createCamera(QCamera *camera)
|
|
#endif
|
|
}
|
|
|
|
-QMaybe<QPlatformMediaRecorder *> QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder)
|
|
-{
|
|
- return new QFFmpegMediaRecorder(recorder);
|
|
-}
|
|
-
|
|
QMaybe<QPlatformImageCapture *> QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
|
|
{
|
|
return new QFFmpegImageCapture(imageCapture);
|
|
@@ -130,11 +104,6 @@ QMaybe<QPlatformVideoSink *> QFFmpegMediaIntegration::createVideoSink(QVideoSink
|
|
return new QFFmpegVideoSink(sink);
|
|
}
|
|
|
|
-QMaybe<QPlatformAudioInput *> QFFmpegMediaIntegration::createAudioInput(QAudioInput *input)
|
|
-{
|
|
- return new QFFmpegAudioInput(input);
|
|
-}
|
|
-
|
|
#ifdef Q_OS_ANDROID
|
|
Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
|
|
{
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
|
|
index 35c062f16..8b44da741 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
|
|
@@ -30,19 +30,11 @@ public:
|
|
static QFFmpegMediaIntegration *instance() { return static_cast<QFFmpegMediaIntegration *>(QPlatformMediaIntegration::instance()); }
|
|
QPlatformMediaFormatInfo *formatInfo() override;
|
|
|
|
-
|
|
- QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
|
|
QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
|
|
- QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
|
|
QMaybe<QPlatformCamera *> createCamera(QCamera *) override;
|
|
- QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) override;
|
|
QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) override;
|
|
-
|
|
QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
|
|
|
|
- QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *input) override;
|
|
-// QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override;
|
|
-
|
|
QFFmpegMediaFormatInfo *m_formatsInfo = nullptr;
|
|
};
|
|
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
|
|
index fecce3f1b..dda577d44 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
|
|
@@ -64,114 +64,9 @@ static const char *keyToTag(QMediaMetaData::Key key)
|
|
return nullptr;
|
|
}
|
|
|
|
-//internal
|
|
-void QFFmpegMetaData::addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry)
|
|
-{
|
|
-// qDebug() << " checking:" << entry->key << entry->value;
|
|
- QByteArray tag(entry->key);
|
|
- QMediaMetaData::Key key = tagToKey(tag.toLower());
|
|
- if (key == QMediaMetaData::Key(-1))
|
|
- return;
|
|
-// qDebug() << " adding" << key;
|
|
-
|
|
- auto *map = &metaData;
|
|
-
|
|
- int metaTypeId = keyType(key).id();
|
|
- switch (metaTypeId) {
|
|
- case qMetaTypeId<QString>():
|
|
- map->insert(key, QString::fromUtf8(entry->value));
|
|
- return;
|
|
- case qMetaTypeId<QStringList>():
|
|
- map->insert(key, QString::fromUtf8(entry->value).split(QLatin1Char(',')));
|
|
- return;
|
|
- case qMetaTypeId<QDateTime>(): {
|
|
- QDateTime date;
|
|
- if (!qstrcmp(entry->key, "year")) {
|
|
- if (map->keys().contains(QMediaMetaData::Date))
|
|
- return;
|
|
- date = QDateTime(QDate(QByteArray(entry->value).toInt(), 1, 1), QTime(0, 0, 0));
|
|
- } else {
|
|
- date = QDateTime::fromString(QString::fromUtf8(entry->value), Qt::ISODate);
|
|
- }
|
|
- map->insert(key, date);
|
|
- return;
|
|
- }
|
|
- case qMetaTypeId<QUrl>():
|
|
- map->insert(key, QUrl::fromEncoded(entry->value));
|
|
- return;
|
|
- case qMetaTypeId<qint64>():
|
|
- map->insert(key, (qint64)QByteArray(entry->value).toLongLong());
|
|
- return;
|
|
- case qMetaTypeId<int>():
|
|
- map->insert(key, QByteArray(entry->value).toInt());
|
|
- return;
|
|
- case qMetaTypeId<qreal>():
|
|
- map->insert(key, (qreal)QByteArray(entry->value).toDouble());
|
|
- return;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- if (metaTypeId == qMetaTypeId<QLocale::Language>()) {
|
|
- map->insert(key, QVariant::fromValue(QLocale::codeToLanguage(QString::fromUtf8(entry->value), QLocale::ISO639Part2)));
|
|
- }
|
|
-}
|
|
-
|
|
-
|
|
-QMediaMetaData QFFmpegMetaData::fromAVMetaData(const AVDictionary *tags)
|
|
-{
|
|
- QMediaMetaData metaData;
|
|
- AVDictionaryEntry *entry = nullptr;
|
|
- while ((entry = av_dict_get(tags, "", entry, AV_DICT_IGNORE_SUFFIX)))
|
|
- addEntry(metaData, entry);
|
|
-
|
|
- return metaData;
|
|
-}
|
|
-
|
|
QByteArray QFFmpegMetaData::value(const QMediaMetaData &metaData, QMediaMetaData::Key key)
|
|
{
|
|
-// qDebug() << " checking:" << entry->key << entry->value;
|
|
-
|
|
- const int metaTypeId = keyType(key).id();
|
|
- const QVariant val = metaData.value(key);
|
|
- switch (metaTypeId) {
|
|
- case qMetaTypeId<QString>():
|
|
- return val.toString().toUtf8();
|
|
- case qMetaTypeId<QStringList>():
|
|
- return val.toStringList().join(u",").toUtf8();
|
|
- case qMetaTypeId<QDateTime>():
|
|
- return val.toDateTime().toString(Qt::ISODate).toUtf8();
|
|
- case qMetaTypeId<QUrl>():
|
|
- return val.toUrl().toEncoded();
|
|
- case qMetaTypeId<qint64>():
|
|
- case qMetaTypeId<int>():
|
|
- return QByteArray::number(val.toLongLong());
|
|
- case qMetaTypeId<qreal>():
|
|
- return QByteArray::number(val.toDouble());
|
|
- default:
|
|
- break;
|
|
- }
|
|
- if (metaTypeId == qMetaTypeId<QLocale::Language>())
|
|
- return QLocale::languageToCode(val.value<QLocale::Language>(), QLocale::ISO639Part2).toUtf8();
|
|
return {};
|
|
}
|
|
|
|
-
|
|
-AVDictionary *QFFmpegMetaData::toAVMetaData(const QMediaMetaData &metaData)
|
|
-{
|
|
- const QList<Key> keys = metaData.keys();
|
|
- AVDictionary *dict = nullptr;
|
|
- for (const auto &k : keys) {
|
|
- const char *key = ::keyToTag(k);
|
|
- if (!key)
|
|
- continue;
|
|
- QByteArray val = value(metaData, k);
|
|
- if (val.isEmpty())
|
|
- continue;
|
|
- av_dict_set(&dict, key, val.constData(), 0);
|
|
- }
|
|
- return dict;
|
|
-}
|
|
-
|
|
-
|
|
-
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
|
|
index 201287495..95b069b64 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
|
|
@@ -16,18 +16,13 @@
|
|
//
|
|
|
|
#include <qmediametadata.h>
|
|
-#include <qffmpeg_p.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
class QFFmpegMetaData : public QMediaMetaData
|
|
{
|
|
public:
|
|
- static void addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry);
|
|
- static QMediaMetaData fromAVMetaData(const AVDictionary *tags);
|
|
-
|
|
static QByteArray value(const QMediaMetaData &metaData, QMediaMetaData::Key key);
|
|
- static AVDictionary *toAVMetaData(const QMediaMetaData &metaData);
|
|
};
|
|
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
|
|
deleted file mode 100644
|
|
index 5e6062f42..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
|
|
+++ /dev/null
|
|
@@ -1,236 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegmediaplayer_p.h"
|
|
-#include "qffmpegdecoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qlocale.h"
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpegmediametadata_p.h"
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "private/qplatformaudiooutput_p.h"
|
|
-#include "qvideosink.h"
|
|
-#include "qaudiosink.h"
|
|
-#include "qaudiooutput.h"
|
|
-
|
|
-#include <qlocale.h>
|
|
-#include <qthread.h>
|
|
-#include <qatomic.h>
|
|
-#include <qwaitcondition.h>
|
|
-#include <qmutex.h>
|
|
-#include <qtimer.h>
|
|
-#include <qqueue.h>
|
|
-
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-using namespace QFFmpeg;
|
|
-
|
|
-QFFmpegMediaPlayer::QFFmpegMediaPlayer(QMediaPlayer *player)
|
|
- : QPlatformMediaPlayer(player)
|
|
-{
|
|
- positionUpdateTimer.setInterval(100);
|
|
- positionUpdateTimer.setTimerType(Qt::PreciseTimer);
|
|
- connect(&positionUpdateTimer, &QTimer::timeout, this, &QFFmpegMediaPlayer::updatePosition);
|
|
-}
|
|
-
|
|
-QFFmpegMediaPlayer::~QFFmpegMediaPlayer()
|
|
-{
|
|
- delete decoder;
|
|
-}
|
|
-
|
|
-qint64 QFFmpegMediaPlayer::duration() const
|
|
-{
|
|
- return decoder ? decoder->m_duration/1000 : 0;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setPosition(qint64 position)
|
|
-{
|
|
- if (decoder) {
|
|
- decoder->seek(position * 1000);
|
|
- updatePosition();
|
|
- }
|
|
- if (state() == QMediaPlayer::StoppedState)
|
|
- mediaStatusChanged(QMediaPlayer::LoadedMedia);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::updatePosition()
|
|
-{
|
|
- positionChanged(decoder ? decoder->clockController.currentTime() / 1000 : 0);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::endOfStream()
|
|
-{
|
|
- positionChanged(duration());
|
|
- stateChanged(QMediaPlayer::StoppedState);
|
|
- mediaStatusChanged(QMediaPlayer::EndOfMedia);
|
|
-}
|
|
-
|
|
-float QFFmpegMediaPlayer::bufferProgress() const
|
|
-{
|
|
- return 1.;
|
|
-}
|
|
-
|
|
-QMediaTimeRange QFFmpegMediaPlayer::availablePlaybackRanges() const
|
|
-{
|
|
- return {};
|
|
-}
|
|
-
|
|
-qreal QFFmpegMediaPlayer::playbackRate() const
|
|
-{
|
|
- return m_playbackRate;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setPlaybackRate(qreal rate)
|
|
-{
|
|
- if (m_playbackRate == rate)
|
|
- return;
|
|
- m_playbackRate = rate;
|
|
- if (decoder)
|
|
- decoder->setPlaybackRate(rate);
|
|
-}
|
|
-
|
|
-QUrl QFFmpegMediaPlayer::media() const
|
|
-{
|
|
- return m_url;
|
|
-}
|
|
-
|
|
-const QIODevice *QFFmpegMediaPlayer::mediaStream() const
|
|
-{
|
|
- return m_device;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setMedia(const QUrl &media, QIODevice *stream)
|
|
-{
|
|
- m_url = media;
|
|
- m_device = stream;
|
|
- if (decoder)
|
|
- delete decoder;
|
|
- decoder = nullptr;
|
|
-
|
|
- positionChanged(0);
|
|
-
|
|
- if (media.isEmpty() && !stream) {
|
|
- seekableChanged(false);
|
|
- audioAvailableChanged(false);
|
|
- videoAvailableChanged(false);
|
|
- metaDataChanged();
|
|
- mediaStatusChanged(QMediaPlayer::NoMedia);
|
|
- return;
|
|
- }
|
|
-
|
|
- mediaStatusChanged(QMediaPlayer::LoadingMedia);
|
|
- decoder = new Decoder;
|
|
- connect(decoder, &Decoder::endOfStream, this, &QFFmpegMediaPlayer::endOfStream);
|
|
- connect(decoder, &Decoder::errorOccured, this, &QFFmpegMediaPlayer::error);
|
|
- decoder->setMedia(media, stream);
|
|
- decoder->setAudioSink(m_audioOutput);
|
|
- decoder->setVideoSink(m_videoSink);
|
|
-
|
|
- durationChanged(duration());
|
|
- tracksChanged();
|
|
- metaDataChanged();
|
|
- seekableChanged(decoder->isSeekable());
|
|
-
|
|
- audioAvailableChanged(!decoder->m_streamMap[QPlatformMediaPlayer::AudioStream].isEmpty());
|
|
- videoAvailableChanged(!decoder->m_streamMap[QPlatformMediaPlayer::VideoStream].isEmpty());
|
|
-
|
|
-
|
|
- QMetaObject::invokeMethod(this, "delayedLoadedStatus", Qt::QueuedConnection);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::play()
|
|
-{
|
|
- if (!decoder)
|
|
- return;
|
|
-
|
|
- if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState) {
|
|
- decoder->seek(0);
|
|
- positionChanged(0);
|
|
- }
|
|
- decoder->play();
|
|
- positionUpdateTimer.start();
|
|
- stateChanged(QMediaPlayer::PlayingState);
|
|
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::pause()
|
|
-{
|
|
- if (!decoder)
|
|
- return;
|
|
- if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState) {
|
|
- decoder->seek(0);
|
|
- positionChanged(0);
|
|
- }
|
|
- decoder->pause();
|
|
- positionUpdateTimer.stop();
|
|
- stateChanged(QMediaPlayer::PausedState);
|
|
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::stop()
|
|
-{
|
|
- if (!decoder)
|
|
- return;
|
|
- decoder->stop();
|
|
- positionUpdateTimer.stop();
|
|
- positionChanged(0);
|
|
- stateChanged(QMediaPlayer::StoppedState);
|
|
- mediaStatusChanged(QMediaPlayer::LoadedMedia);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
|
|
-{
|
|
- if (m_audioOutput == output)
|
|
- return;
|
|
-
|
|
- m_audioOutput = output;
|
|
- if (decoder)
|
|
- decoder->setAudioSink(output);
|
|
-}
|
|
-
|
|
-QMediaMetaData QFFmpegMediaPlayer::metaData() const
|
|
-{
|
|
- return decoder ? decoder->m_metaData : QMediaMetaData{};
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setVideoSink(QVideoSink *sink)
|
|
-{
|
|
- if (m_videoSink == sink)
|
|
- return;
|
|
-
|
|
- m_videoSink = sink;
|
|
- if (decoder)
|
|
- decoder->setVideoSink(sink);
|
|
-}
|
|
-
|
|
-QVideoSink *QFFmpegMediaPlayer::videoSink() const
|
|
-{
|
|
- return m_videoSink;
|
|
-}
|
|
-
|
|
-int QFFmpegMediaPlayer::trackCount(TrackType type)
|
|
-{
|
|
- return decoder ? decoder->m_streamMap[type].count() : 0;
|
|
-}
|
|
-
|
|
-QMediaMetaData QFFmpegMediaPlayer::trackMetaData(TrackType type, int streamNumber)
|
|
-{
|
|
- if (!decoder || streamNumber < 0 || streamNumber >= decoder->m_streamMap[type].count())
|
|
- return {};
|
|
- return decoder->m_streamMap[type].at(streamNumber).metaData;
|
|
-}
|
|
-
|
|
-int QFFmpegMediaPlayer::activeTrack(TrackType type)
|
|
-{
|
|
- return decoder ? decoder->m_requestedStreams[type] : -1;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaPlayer::setActiveTrack(TrackType type, int streamNumber)
|
|
-{
|
|
- if (decoder)
|
|
- decoder->setActiveTrack(type, streamNumber);
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
|
|
deleted file mode 100644
|
|
index 8e2753c82..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
|
|
+++ /dev/null
|
|
@@ -1,98 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#ifndef QFFMPEGMEDIAPLAYER_H
|
|
-#define QFFMPEGMEDIAPLAYER_H
|
|
-
|
|
-#include <private/qplatformmediaplayer_p.h>
|
|
-#include <qmediametadata.h>
|
|
-#include <qtimer.h>
|
|
-#include "qffmpeg_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-class Decoder;
|
|
-}
|
|
-class QPlatformAudioOutput;
|
|
-
|
|
-class QFFmpegMediaPlayer : public QObject, public QPlatformMediaPlayer
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- QFFmpegMediaPlayer(QMediaPlayer *player);
|
|
- ~QFFmpegMediaPlayer();
|
|
-
|
|
- qint64 duration() const override;
|
|
-
|
|
- void setPosition(qint64 position) override;
|
|
-
|
|
- float bufferProgress() const override;
|
|
-
|
|
- QMediaTimeRange availablePlaybackRanges() const override;
|
|
-
|
|
- qreal playbackRate() const override;
|
|
- void setPlaybackRate(qreal rate) override;
|
|
-
|
|
- QUrl media() const override;
|
|
- const QIODevice *mediaStream() const override;
|
|
- void setMedia(const QUrl &media, QIODevice *stream) override;
|
|
-
|
|
- void play() override;
|
|
- void pause() override;
|
|
- void stop() override;
|
|
-
|
|
-// bool streamPlaybackSupported() const { return false; }
|
|
-
|
|
- void setAudioOutput(QPlatformAudioOutput *) override;
|
|
-
|
|
- QMediaMetaData metaData() const override;
|
|
-
|
|
- void setVideoSink(QVideoSink *sink) override;
|
|
- QVideoSink *videoSink() const;
|
|
-
|
|
- int trackCount(TrackType) override;
|
|
- QMediaMetaData trackMetaData(TrackType type, int streamNumber) override;
|
|
- int activeTrack(TrackType) override;
|
|
- void setActiveTrack(TrackType, int streamNumber) override;
|
|
-
|
|
- Q_INVOKABLE void delayedLoadedStatus() { mediaStatusChanged(QMediaPlayer::LoadedMedia); }
|
|
-
|
|
-private slots:
|
|
- void updatePosition();
|
|
- void endOfStream();
|
|
- void error(int error, const QString &errorString)
|
|
- {
|
|
- QPlatformMediaPlayer::error(error, errorString);
|
|
- }
|
|
-
|
|
-private:
|
|
- friend class QFFmpeg::Decoder;
|
|
-
|
|
- QTimer positionUpdateTimer;
|
|
-
|
|
- QFFmpeg::Decoder *decoder = nullptr;
|
|
- QPlatformAudioOutput *m_audioOutput = nullptr;
|
|
- QVideoSink *m_videoSink = nullptr;
|
|
-
|
|
- QUrl m_url;
|
|
- QIODevice *m_device = nullptr;
|
|
- float m_playbackRate = 1.;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-
|
|
-#endif // QMEDIAPLAYERCONTROL_H
|
|
-
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
|
|
deleted file mode 100644
|
|
index 870b8ad38..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
|
|
+++ /dev/null
|
|
@@ -1,157 +0,0 @@
|
|
-// Copyright (C) 2016 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegmediarecorder_p.h"
|
|
-#include "qaudiodevice.h"
|
|
-#include <private/qmediastoragelocation_p.h>
|
|
-#include <private/qplatformcamera_p.h>
|
|
-#include "qaudiosource.h"
|
|
-#include "qffmpegaudioinput_p.h"
|
|
-#include "qaudiobuffer.h"
|
|
-#include "qffmpegencoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-
|
|
-#include <qdebug.h>
|
|
-#include <qeventloop.h>
|
|
-#include <qstandardpaths.h>
|
|
-#include <qmimetype.h>
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.encoder")
|
|
-
|
|
-QFFmpegMediaRecorder::QFFmpegMediaRecorder(QMediaRecorder *parent)
|
|
- : QPlatformMediaRecorder(parent)
|
|
-{
|
|
-}
|
|
-
|
|
-QFFmpegMediaRecorder::~QFFmpegMediaRecorder()
|
|
-{
|
|
- if (encoder)
|
|
- encoder->finalize();
|
|
-}
|
|
-
|
|
-bool QFFmpegMediaRecorder::isLocationWritable(const QUrl &) const
|
|
-{
|
|
- return true;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::handleSessionError(QMediaRecorder::Error code, const QString &description)
|
|
-{
|
|
- error(code, description);
|
|
- stop();
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings)
|
|
-{
|
|
- if (!m_session || state() != QMediaRecorder::StoppedState)
|
|
- return;
|
|
-
|
|
- const auto hasVideo = m_session->camera() && m_session->camera()->isActive();
|
|
- const auto hasAudio = m_session->audioInput() != nullptr;
|
|
-
|
|
- if (!hasVideo && !hasAudio) {
|
|
- error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input"));
|
|
- return;
|
|
- }
|
|
-
|
|
- const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
|
|
-
|
|
- auto primaryLocation = audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation;
|
|
- auto container = settings.mimeType().preferredSuffix();
|
|
- auto location = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), primaryLocation, container);
|
|
-
|
|
- QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(location);
|
|
- qCDebug(qLcMediaEncoder) << "recording new video to" << actualSink;
|
|
- qDebug() << "requested format:" << settings.fileFormat() << settings.audioCodec();
|
|
-
|
|
- Q_ASSERT(!actualSink.isEmpty());
|
|
-
|
|
- encoder = new QFFmpeg::Encoder(settings, actualSink);
|
|
- encoder->setMetaData(m_metaData);
|
|
- connect(encoder, &QFFmpeg::Encoder::durationChanged, this, &QFFmpegMediaRecorder::newDuration);
|
|
- connect(encoder, &QFFmpeg::Encoder::finalizationDone, this, &QFFmpegMediaRecorder::finalizationDone);
|
|
- connect(encoder, &QFFmpeg::Encoder::error, this, &QFFmpegMediaRecorder::handleSessionError);
|
|
-
|
|
- auto *audioInput = m_session->audioInput();
|
|
- if (audioInput)
|
|
- encoder->addAudioInput(static_cast<QFFmpegAudioInput *>(audioInput));
|
|
-
|
|
- auto *camera = m_session->camera();
|
|
- if (camera)
|
|
- encoder->addVideoSource(camera);
|
|
-
|
|
- durationChanged(0);
|
|
- stateChanged(QMediaRecorder::RecordingState);
|
|
- actualLocationChanged(QUrl::fromLocalFile(location));
|
|
-
|
|
- encoder->start();
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::pause()
|
|
-{
|
|
- if (!m_session || state() != QMediaRecorder::RecordingState)
|
|
- return;
|
|
-
|
|
- Q_ASSERT(encoder);
|
|
- encoder->setPaused(true);
|
|
-
|
|
- stateChanged(QMediaRecorder::PausedState);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::resume()
|
|
-{
|
|
- if (!m_session || state() != QMediaRecorder::PausedState)
|
|
- return;
|
|
-
|
|
- Q_ASSERT(encoder);
|
|
- encoder->setPaused(false);
|
|
-
|
|
- stateChanged(QMediaRecorder::RecordingState);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::stop()
|
|
-{
|
|
- if (!m_session || state() == QMediaRecorder::StoppedState)
|
|
- return;
|
|
- auto * input = m_session ? m_session->audioInput() : nullptr;
|
|
- if (input)
|
|
- static_cast<QFFmpegAudioInput *>(input)->setRunning(false);
|
|
- qCDebug(qLcMediaEncoder) << "stop";
|
|
- // ### all of the below should be done asynchronous. finalize() should do it's work in a thread
|
|
- // to avoid blocking the UI in case of slow codecs
|
|
- if (encoder) {
|
|
- encoder->finalize();
|
|
- encoder = nullptr;
|
|
- }
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::finalizationDone()
|
|
-{
|
|
- stateChanged(QMediaRecorder::StoppedState);
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::setMetaData(const QMediaMetaData &metaData)
|
|
-{
|
|
- if (!m_session)
|
|
- return;
|
|
- m_metaData = metaData;
|
|
-}
|
|
-
|
|
-QMediaMetaData QFFmpegMediaRecorder::metaData() const
|
|
-{
|
|
- return m_metaData;
|
|
-}
|
|
-
|
|
-void QFFmpegMediaRecorder::setCaptureSession(QPlatformMediaCaptureSession *session)
|
|
-{
|
|
- auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
|
|
- if (m_session == captureSession)
|
|
- return;
|
|
-
|
|
- if (m_session)
|
|
- stop();
|
|
-
|
|
- m_session = captureSession;
|
|
- if (!m_session)
|
|
- return;
|
|
-}
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
|
|
deleted file mode 100644
|
|
index 26f4c16ad..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
|
|
+++ /dev/null
|
|
@@ -1,68 +0,0 @@
|
|
-// Copyright (C) 2016 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-
|
|
-#ifndef QFFMPEGMEDIARECODER_H
|
|
-#define QFFMPEGMEDIARECODER_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include <private/qplatformmediarecorder_p.h>
|
|
-#include "qffmpegmediacapturesession_p.h"
|
|
-
|
|
-#include "qffmpeg_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QAudioSource;
|
|
-class QAudioSourceIO;
|
|
-class QAudioBuffer;
|
|
-class QMediaMetaData;
|
|
-
|
|
-namespace QFFmpeg {
|
|
-class Encoder;
|
|
-}
|
|
-
|
|
-class QFFmpegMediaRecorder : public QObject, public QPlatformMediaRecorder
|
|
-{
|
|
- Q_OBJECT
|
|
-public:
|
|
- QFFmpegMediaRecorder(QMediaRecorder *parent);
|
|
- virtual ~QFFmpegMediaRecorder();
|
|
-
|
|
- bool isLocationWritable(const QUrl &sink) const override;
|
|
-
|
|
- void record(QMediaEncoderSettings &settings) override;
|
|
- void pause() override;
|
|
- void resume() override;
|
|
- void stop() override;
|
|
-
|
|
- void setMetaData(const QMediaMetaData &) override;
|
|
- QMediaMetaData metaData() const override;
|
|
-
|
|
- void setCaptureSession(QPlatformMediaCaptureSession *session);
|
|
-
|
|
-private Q_SLOTS:
|
|
- void newDuration(qint64 d) { durationChanged(d); }
|
|
- void finalizationDone();
|
|
- void handleSessionError(QMediaRecorder::Error code, const QString &description);
|
|
-
|
|
-private:
|
|
- QFFmpegMediaCaptureSession *m_session = nullptr;
|
|
- QMediaMetaData m_metaData;
|
|
-
|
|
- QFFmpeg::Encoder *encoder = nullptr;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif // QFFMPEGMEDIARECODER_H
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
|
|
deleted file mode 100644
|
|
index bb15aa0e1..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
|
|
+++ /dev/null
|
|
@@ -1,95 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#include "qffmpegresampler_p.h"
|
|
-#include "qffmpegdecoder_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/opt.h>
|
|
-}
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-Resampler::Resampler(const Codec *codec, const QAudioFormat &outputFormat)
|
|
- : m_outputFormat(outputFormat)
|
|
-{
|
|
- qCDebug(qLcResampler) << "createResampler";
|
|
- const AVStream *audioStream = codec->stream();
|
|
- const auto *codecpar = audioStream->codecpar;
|
|
-
|
|
- if (!m_outputFormat.isValid())
|
|
- // want the native format
|
|
- m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
|
|
-
|
|
- QAudioFormat::ChannelConfig config = m_outputFormat.channelConfig();
|
|
- if (config == QAudioFormat::ChannelConfigUnknown)
|
|
- config = QAudioFormat::defaultChannelConfigForChannelCount(m_outputFormat.channelCount());
|
|
-
|
|
-
|
|
- qCDebug(qLcResampler) << "init resampler" << m_outputFormat.sampleRate() << config << codecpar->sample_rate;
|
|
-#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
|
|
- auto inConfig = codecpar->channel_layout;
|
|
- if (inConfig == 0)
|
|
- inConfig = QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
|
|
- resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context
|
|
- QFFmpegMediaFormatInfo::avChannelLayout(config), // out_ch_layout
|
|
- QFFmpegMediaFormatInfo::avSampleFormat(m_outputFormat.sampleFormat()), // out_sample_fmt
|
|
- m_outputFormat.sampleRate(), // out_sample_rate
|
|
- inConfig, // in_ch_layout
|
|
- AVSampleFormat(codecpar->format), // in_sample_fmt
|
|
- codecpar->sample_rate, // in_sample_rate
|
|
- 0, // log_offset
|
|
- nullptr);
|
|
-#else
|
|
- AVChannelLayout in_ch_layout = codecpar->ch_layout;
|
|
- AVChannelLayout out_ch_layout = {};
|
|
- av_channel_layout_from_mask(&out_ch_layout, QFFmpegMediaFormatInfo::avChannelLayout(config));
|
|
- swr_alloc_set_opts2(&resampler, // we're allocating a new context
|
|
- &out_ch_layout,
|
|
- QFFmpegMediaFormatInfo::avSampleFormat(m_outputFormat.sampleFormat()),
|
|
- m_outputFormat.sampleRate(),
|
|
- &in_ch_layout,
|
|
- AVSampleFormat(codecpar->format),
|
|
- codecpar->sample_rate,
|
|
- 0,
|
|
- nullptr);
|
|
-#endif
|
|
- // if we're not the master clock, we might need to handle clock adjustments, initialize for that
|
|
- av_opt_set_double(resampler, "async", m_outputFormat.sampleRate()/50, 0);
|
|
-
|
|
- swr_init(resampler);
|
|
-}
|
|
-
|
|
-Resampler::~Resampler()
|
|
-{
|
|
- swr_free(&resampler);
|
|
-}
|
|
-
|
|
-QAudioBuffer Resampler::resample(const AVFrame *frame)
|
|
-{
|
|
- const int outSamples = swr_get_out_samples(resampler, frame->nb_samples);
|
|
- QByteArray samples(m_outputFormat.bytesForFrames(outSamples), Qt::Uninitialized);
|
|
- auto **in = const_cast<const uint8_t **>(frame->extended_data);
|
|
- auto *out = reinterpret_cast<uint8_t *>(samples.data());
|
|
- const int out_samples = swr_convert(resampler, &out, outSamples,
|
|
- in, frame->nb_samples);
|
|
- samples.resize(m_outputFormat.bytesForFrames(out_samples));
|
|
-
|
|
- qint64 startTime = m_outputFormat.durationForFrames(m_samplesProcessed);
|
|
- m_samplesProcessed += out_samples;
|
|
-
|
|
- qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << frame->nb_samples << out_samples << outSamples;
|
|
- QAudioBuffer buffer(samples, m_outputFormat, startTime);
|
|
- return buffer;
|
|
-}
|
|
-
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h b/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
|
|
deleted file mode 100644
|
|
index 4b5b59537..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
|
|
+++ /dev/null
|
|
@@ -1,46 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGRESAMPLER_P_H
|
|
-#define QFFMPEGRESAMPLER_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qaudiobuffer.h"
|
|
-#include "qffmpeg_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-struct Codec;
|
|
-
|
|
-class Resampler
|
|
-{
|
|
-public:
|
|
- Resampler(const Codec *codec, const QAudioFormat &outputFormat);
|
|
- ~Resampler();
|
|
-
|
|
- QAudioBuffer resample(const AVFrame *frame);
|
|
- qint64 samplesProcessed() const { return m_samplesProcessed; }
|
|
-
|
|
-private:
|
|
- QAudioFormat m_outputFormat;
|
|
- SwrContext *resampler = nullptr;
|
|
- qint64 m_samplesProcessed = 0;
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
|
|
deleted file mode 100644
|
|
index 804ba424f..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
|
|
+++ /dev/null
|
|
@@ -1,57 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegthread_p.h"
|
|
-
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-using namespace QFFmpeg;
|
|
-
|
|
-void Thread::kill()
|
|
-{
|
|
- {
|
|
- QMutexLocker locker(&mutex);
|
|
- exit.storeRelease(true);
|
|
- killHelper();
|
|
- }
|
|
- wake();
|
|
- wait();
|
|
- delete this;
|
|
-}
|
|
-
|
|
-void Thread::maybePause()
|
|
-{
|
|
- while (timeOut > 0 || shouldWait()) {
|
|
- if (exit.loadAcquire())
|
|
- break;
|
|
-
|
|
- QElapsedTimer timer;
|
|
- timer.start();
|
|
- if (condition.wait(&mutex, QDeadlineTimer(timeOut, Qt::PreciseTimer))) {
|
|
- if (timeOut >= 0) {
|
|
- timeOut -= timer.elapsed();
|
|
- if (timeOut < 0)
|
|
- timeOut = -1;
|
|
- }
|
|
- } else {
|
|
- timeOut = -1;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void Thread::run()
|
|
-{
|
|
- init();
|
|
- QMutexLocker locker(&mutex);
|
|
- while (1) {
|
|
- maybePause();
|
|
- if (exit.loadAcquire())
|
|
- break;
|
|
- loop();
|
|
- }
|
|
- cleanup();
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
|
|
deleted file mode 100644
|
|
index e5c87e237..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
|
|
+++ /dev/null
|
|
@@ -1,68 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGTHREAD_P_H
|
|
-#define QFFMPEGTHREAD_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include <private/qtmultimediaglobal_p.h>
|
|
-
|
|
-#include <qmutex.h>
|
|
-#include <qwaitcondition.h>
|
|
-#include <qthread.h>
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QAudioSink;
|
|
-
|
|
-namespace QFFmpeg
|
|
-{
|
|
-
|
|
-class Thread : public QThread
|
|
-{
|
|
-public:
|
|
- mutable QMutex mutex;
|
|
- qint64 timeOut = -1;
|
|
-private:
|
|
- QWaitCondition condition;
|
|
-
|
|
-protected:
|
|
- QAtomicInteger<bool> exit = false;
|
|
-
|
|
-public:
|
|
- // public API is thread-safe
|
|
-
|
|
- void kill();
|
|
- virtual void killHelper() {}
|
|
-
|
|
- void wake() {
|
|
- condition.wakeAll();
|
|
- }
|
|
-
|
|
-protected:
|
|
- virtual void init() {}
|
|
- virtual void cleanup() {}
|
|
- // loop() should never block, all blocking has to happen in shouldWait()
|
|
- virtual void loop() = 0;
|
|
- virtual bool shouldWait() const { return false; }
|
|
-
|
|
-private:
|
|
- void maybePause();
|
|
-
|
|
- void run() override;
|
|
-};
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
|
|
deleted file mode 100644
|
|
index b17c04938..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
|
|
+++ /dev/null
|
|
@@ -1,356 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "private/qvideotexturehelper_p.h"
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/pixdesc.h>
|
|
-#include <libavutil/hdr_dynamic_metadata.h>
|
|
-#include <libavutil/mastering_display_metadata.h>
|
|
-}
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-QFFmpegVideoBuffer::QFFmpegVideoBuffer(AVFrame *frame)
|
|
- : QAbstractVideoBuffer(QVideoFrame::NoHandle)
|
|
- , frame(frame)
|
|
-{
|
|
- if (frame->hw_frames_ctx) {
|
|
- hwFrame = frame;
|
|
- m_pixelFormat = toQtPixelFormat(QFFmpeg::HWAccel::format(frame));
|
|
- return;
|
|
- }
|
|
-
|
|
- swFrame = frame;
|
|
- m_pixelFormat = toQtPixelFormat(AVPixelFormat(swFrame->format));
|
|
-
|
|
- convertSWFrame();
|
|
-}
|
|
-
|
|
-QFFmpegVideoBuffer::~QFFmpegVideoBuffer()
|
|
-{
|
|
- delete textures;
|
|
- if (swFrame)
|
|
- av_frame_free(&swFrame);
|
|
- if (hwFrame)
|
|
- av_frame_free(&hwFrame);
|
|
-}
|
|
-
|
|
-void QFFmpegVideoBuffer::convertSWFrame()
|
|
-{
|
|
- Q_ASSERT(swFrame);
|
|
- bool needsConversion = false;
|
|
- auto pixelFormat = toQtPixelFormat(AVPixelFormat(swFrame->format), &needsConversion);
|
|
-// qDebug() << "SW frame format:" << pixelFormat << swFrame->format << needsConversion;
|
|
-
|
|
- if (pixelFormat != m_pixelFormat) {
|
|
- AVPixelFormat newFormat = toAVPixelFormat(m_pixelFormat);
|
|
- // convert the format into something we can handle
|
|
- SwsContext *c = sws_getContext(swFrame->width, swFrame->height, AVPixelFormat(swFrame->format),
|
|
- swFrame->width, swFrame->height, newFormat,
|
|
- SWS_BICUBIC, nullptr, nullptr, nullptr);
|
|
-
|
|
- AVFrame *newFrame = av_frame_alloc();
|
|
- newFrame->width = swFrame->width;
|
|
- newFrame->height = swFrame->height;
|
|
- newFrame->format = newFormat;
|
|
- av_frame_get_buffer(newFrame, 0);
|
|
-
|
|
- sws_scale(c, swFrame->data, swFrame->linesize, 0, swFrame->height, newFrame->data, newFrame->linesize);
|
|
- av_frame_free(&swFrame);
|
|
- swFrame = newFrame;
|
|
- sws_freeContext(c);
|
|
- }
|
|
-}
|
|
-
|
|
-void QFFmpegVideoBuffer::setTextureConverter(const QFFmpeg::TextureConverter &converter)
|
|
-{
|
|
- textureConverter = converter;
|
|
- textureConverter.init(hwFrame);
|
|
- m_type = converter.isNull() ? QVideoFrame::NoHandle : QVideoFrame::RhiTextureHandle;
|
|
-}
|
|
-
|
|
-QVideoFrameFormat::ColorSpace QFFmpegVideoBuffer::colorSpace() const
|
|
-{
|
|
- switch (frame->colorspace) {
|
|
- default:
|
|
- case AVCOL_SPC_UNSPECIFIED:
|
|
- case AVCOL_SPC_RESERVED:
|
|
- case AVCOL_SPC_FCC:
|
|
- case AVCOL_SPC_SMPTE240M:
|
|
- case AVCOL_SPC_YCGCO:
|
|
- case AVCOL_SPC_SMPTE2085:
|
|
- case AVCOL_SPC_CHROMA_DERIVED_NCL:
|
|
- case AVCOL_SPC_CHROMA_DERIVED_CL:
|
|
- case AVCOL_SPC_ICTCP: // BT.2100 ICtCp
|
|
- return QVideoFrameFormat::ColorSpace_Undefined;
|
|
- case AVCOL_SPC_RGB:
|
|
- return QVideoFrameFormat::ColorSpace_AdobeRgb;
|
|
- case AVCOL_SPC_BT709:
|
|
- return QVideoFrameFormat::ColorSpace_BT709;
|
|
- case AVCOL_SPC_BT470BG: // BT601
|
|
- case AVCOL_SPC_SMPTE170M: // Also BT601
|
|
- return QVideoFrameFormat::ColorSpace_BT601;
|
|
- case AVCOL_SPC_BT2020_NCL: // Non constant luminence
|
|
- case AVCOL_SPC_BT2020_CL: // Constant luminence
|
|
- return QVideoFrameFormat::ColorSpace_BT2020;
|
|
- }
|
|
-}
|
|
-
|
|
-QVideoFrameFormat::ColorTransfer QFFmpegVideoBuffer::colorTransfer() const
|
|
-{
|
|
- switch (frame->color_trc) {
|
|
- case AVCOL_TRC_BT709:
|
|
- // The following three cases have transfer characteristics identical to BT709
|
|
- case AVCOL_TRC_BT1361_ECG:
|
|
- case AVCOL_TRC_BT2020_10:
|
|
- case AVCOL_TRC_BT2020_12:
|
|
- case AVCOL_TRC_SMPTE240M: // almost identical to bt709
|
|
- return QVideoFrameFormat::ColorTransfer_BT709;
|
|
- case AVCOL_TRC_GAMMA22:
|
|
- case AVCOL_TRC_SMPTE428 : // No idea, let's hope for the best...
|
|
- case AVCOL_TRC_IEC61966_2_1: // sRGB, close enough to 2.2...
|
|
- case AVCOL_TRC_IEC61966_2_4: // not quite, but probably close enough
|
|
- return QVideoFrameFormat::ColorTransfer_Gamma22;
|
|
- case AVCOL_TRC_GAMMA28:
|
|
- return QVideoFrameFormat::ColorTransfer_Gamma28;
|
|
- case AVCOL_TRC_SMPTE170M:
|
|
- return QVideoFrameFormat::ColorTransfer_BT601;
|
|
- case AVCOL_TRC_LINEAR:
|
|
- return QVideoFrameFormat::ColorTransfer_Linear;
|
|
- case AVCOL_TRC_SMPTE2084:
|
|
- return QVideoFrameFormat::ColorTransfer_ST2084;
|
|
- case AVCOL_TRC_ARIB_STD_B67:
|
|
- return QVideoFrameFormat::ColorTransfer_STD_B67;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- return QVideoFrameFormat::ColorTransfer_Unknown;
|
|
-}
|
|
-
|
|
-QVideoFrameFormat::ColorRange QFFmpegVideoBuffer::colorRange() const
|
|
-{
|
|
- switch (frame->color_range) {
|
|
- case AVCOL_RANGE_MPEG:
|
|
- return QVideoFrameFormat::ColorRange_Video;
|
|
- case AVCOL_RANGE_JPEG:
|
|
- return QVideoFrameFormat::ColorRange_Full;
|
|
- default:
|
|
- return QVideoFrameFormat::ColorRange_Unknown;
|
|
- }
|
|
-}
|
|
-
|
|
-float QFFmpegVideoBuffer::maxNits()
|
|
-{
|
|
- float maxNits = -1;
|
|
- for (int i = 0; i <frame->nb_side_data; ++i) {
|
|
- AVFrameSideData *sd = frame->side_data[i];
|
|
- // TODO: Longer term we might want to also support HDR10+ dynamic metadata
|
|
- if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) {
|
|
- auto *data = reinterpret_cast<AVMasteringDisplayMetadata *>(sd->data);
|
|
- auto maybeLum = QFFmpeg::mul(10'000., data->max_luminance);
|
|
- if (maybeLum)
|
|
- maxNits = float(maybeLum.value());
|
|
- }
|
|
- }
|
|
- return maxNits;
|
|
-}
|
|
-
|
|
-QVideoFrame::MapMode QFFmpegVideoBuffer::mapMode() const
|
|
-{
|
|
- return m_mode;
|
|
-}
|
|
-
|
|
-QAbstractVideoBuffer::MapData QFFmpegVideoBuffer::map(QVideoFrame::MapMode mode)
|
|
-{
|
|
- if (!swFrame) {
|
|
- Q_ASSERT(hwFrame && hwFrame->hw_frames_ctx);
|
|
- swFrame = av_frame_alloc();
|
|
- /* retrieve data from GPU to CPU */
|
|
- int ret = av_hwframe_transfer_data(swFrame, hwFrame, 0);
|
|
- if (ret < 0) {
|
|
- qWarning() << "Error transferring the data to system memory\n";
|
|
- return {};
|
|
- }
|
|
- convertSWFrame();
|
|
- }
|
|
-
|
|
- m_mode = mode;
|
|
-
|
|
-// qDebug() << "MAP:";
|
|
- MapData mapData;
|
|
- auto *desc = QVideoTextureHelper::textureDescription(pixelFormat());
|
|
- mapData.nPlanes = desc->nplanes;
|
|
- for (int i = 0; i < mapData.nPlanes; ++i) {
|
|
- mapData.data[i] = swFrame->data[i];
|
|
- mapData.bytesPerLine[i] = swFrame->linesize[i];
|
|
- mapData.size[i] = mapData.bytesPerLine[i]*desc->heightForPlane(swFrame->height, i);
|
|
-// qDebug() << " " << i << mapData.data[i] << mapData.size[i];
|
|
- }
|
|
- return mapData;
|
|
-}
|
|
-
|
|
-void QFFmpegVideoBuffer::unmap()
|
|
-{
|
|
- // nothing to do here for SW buffers
|
|
-}
|
|
-
|
|
-std::unique_ptr<QVideoFrameTextures> QFFmpegVideoBuffer::mapTextures(QRhi *)
|
|
-{
|
|
- if (textures)
|
|
- return {};
|
|
- if (!hwFrame)
|
|
- return {};
|
|
- textures = textureConverter.getTextures(hwFrame);
|
|
- if (!textures)
|
|
- qWarning() << " failed to get textures for frame" << textureConverter.isNull();
|
|
- return {};
|
|
-}
|
|
-
|
|
-quint64 QFFmpegVideoBuffer::textureHandle(int plane) const
|
|
-{
|
|
- return textures ? textures->textureHandle(plane) : 0;
|
|
-}
|
|
-
|
|
-QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::pixelFormat() const
|
|
-{
|
|
- return m_pixelFormat;
|
|
-}
|
|
-
|
|
-QSize QFFmpegVideoBuffer::size() const
|
|
-{
|
|
- return QSize(frame->width, frame->height);
|
|
-}
|
|
-
|
|
-QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion)
|
|
-{
|
|
- if (needsConversion)
|
|
- *needsConversion = false;
|
|
-
|
|
- switch (avPixelFormat) {
|
|
- default:
|
|
- break;
|
|
- case AV_PIX_FMT_ARGB:
|
|
- return QVideoFrameFormat::Format_ARGB8888;
|
|
- case AV_PIX_FMT_0RGB:
|
|
- return QVideoFrameFormat::Format_XRGB8888;
|
|
- case AV_PIX_FMT_BGRA:
|
|
- return QVideoFrameFormat::Format_BGRA8888;
|
|
- case AV_PIX_FMT_BGR0:
|
|
- return QVideoFrameFormat::Format_BGRX8888;
|
|
- case AV_PIX_FMT_ABGR:
|
|
- return QVideoFrameFormat::Format_ABGR8888;
|
|
- case AV_PIX_FMT_0BGR:
|
|
- return QVideoFrameFormat::Format_XBGR8888;
|
|
- case AV_PIX_FMT_RGBA:
|
|
- return QVideoFrameFormat::Format_RGBA8888;
|
|
- case AV_PIX_FMT_RGB0:
|
|
- return QVideoFrameFormat::Format_RGBX8888;
|
|
-
|
|
- case AV_PIX_FMT_YUV422P:
|
|
- return QVideoFrameFormat::Format_YUV422P;
|
|
- case AV_PIX_FMT_YUV420P:
|
|
- return QVideoFrameFormat::Format_YUV420P;
|
|
- case AV_PIX_FMT_YUV420P10:
|
|
- return QVideoFrameFormat::Format_YUV420P10;
|
|
- case AV_PIX_FMT_UYVY422:
|
|
- return QVideoFrameFormat::Format_UYVY;
|
|
- case AV_PIX_FMT_YUYV422:
|
|
- return QVideoFrameFormat::Format_YUYV;
|
|
- case AV_PIX_FMT_NV12:
|
|
- return QVideoFrameFormat::Format_NV12;
|
|
- case AV_PIX_FMT_NV21:
|
|
- return QVideoFrameFormat::Format_NV21;
|
|
- case AV_PIX_FMT_GRAY8:
|
|
- return QVideoFrameFormat::Format_Y8;
|
|
- case AV_PIX_FMT_GRAY16:
|
|
- return QVideoFrameFormat::Format_Y16;
|
|
-
|
|
- case AV_PIX_FMT_P010:
|
|
- return QVideoFrameFormat::Format_P010;
|
|
- case AV_PIX_FMT_P016:
|
|
- return QVideoFrameFormat::Format_P016;
|
|
- case AV_PIX_FMT_MEDIACODEC:
|
|
- return QVideoFrameFormat::Format_SamplerExternalOES;
|
|
- }
|
|
-
|
|
- if (needsConversion)
|
|
- *needsConversion = true;
|
|
-
|
|
- const AVPixFmtDescriptor *descriptor = av_pix_fmt_desc_get(avPixelFormat);
|
|
-
|
|
- if (descriptor->flags & AV_PIX_FMT_FLAG_RGB)
|
|
- return QVideoFrameFormat::Format_RGBA8888;
|
|
-
|
|
- if (descriptor->comp[0].depth > 8)
|
|
- return QVideoFrameFormat::Format_P016;
|
|
- return QVideoFrameFormat::Format_YUV420P;
|
|
-}
|
|
-
|
|
-AVPixelFormat QFFmpegVideoBuffer::toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat)
|
|
-{
|
|
- switch (pixelFormat) {
|
|
- default:
|
|
- case QVideoFrameFormat::Format_Invalid:
|
|
- case QVideoFrameFormat::Format_AYUV:
|
|
- case QVideoFrameFormat::Format_AYUV_Premultiplied:
|
|
- case QVideoFrameFormat::Format_YV12:
|
|
- case QVideoFrameFormat::Format_IMC1:
|
|
- case QVideoFrameFormat::Format_IMC2:
|
|
- case QVideoFrameFormat::Format_IMC3:
|
|
- case QVideoFrameFormat::Format_IMC4:
|
|
- return AV_PIX_FMT_NONE;
|
|
- case QVideoFrameFormat::Format_Jpeg:
|
|
- // We're using the data from the converted QImage here, which is in BGRA.
|
|
- return AV_PIX_FMT_BGRA;
|
|
- case QVideoFrameFormat::Format_ARGB8888:
|
|
- case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
|
|
- return AV_PIX_FMT_ARGB;
|
|
- case QVideoFrameFormat::Format_XRGB8888:
|
|
- return AV_PIX_FMT_0RGB;
|
|
- case QVideoFrameFormat::Format_BGRA8888:
|
|
- case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
|
|
- return AV_PIX_FMT_BGRA;
|
|
- case QVideoFrameFormat::Format_BGRX8888:
|
|
- return AV_PIX_FMT_BGR0;
|
|
- case QVideoFrameFormat::Format_ABGR8888:
|
|
- return AV_PIX_FMT_ABGR;
|
|
- case QVideoFrameFormat::Format_XBGR8888:
|
|
- return AV_PIX_FMT_0BGR;
|
|
- case QVideoFrameFormat::Format_RGBA8888:
|
|
- return AV_PIX_FMT_RGBA;
|
|
- case QVideoFrameFormat::Format_RGBX8888:
|
|
- return AV_PIX_FMT_RGB0;
|
|
-
|
|
- case QVideoFrameFormat::Format_YUV422P:
|
|
- return AV_PIX_FMT_YUV422P;
|
|
- case QVideoFrameFormat::Format_YUV420P:
|
|
- return AV_PIX_FMT_YUV420P;
|
|
- case QVideoFrameFormat::Format_YUV420P10:
|
|
- return AV_PIX_FMT_YUV420P10;
|
|
- case QVideoFrameFormat::Format_UYVY:
|
|
- return AV_PIX_FMT_UYVY422;
|
|
- case QVideoFrameFormat::Format_YUYV:
|
|
- return AV_PIX_FMT_YUYV422;
|
|
- case QVideoFrameFormat::Format_NV12:
|
|
- return AV_PIX_FMT_NV12;
|
|
- case QVideoFrameFormat::Format_NV21:
|
|
- return AV_PIX_FMT_NV21;
|
|
- case QVideoFrameFormat::Format_Y8:
|
|
- return AV_PIX_FMT_GRAY8;
|
|
- case QVideoFrameFormat::Format_Y16:
|
|
- return AV_PIX_FMT_GRAY16;
|
|
-
|
|
- case QVideoFrameFormat::Format_P010:
|
|
- return AV_PIX_FMT_P010;
|
|
- case QVideoFrameFormat::Format_P016:
|
|
- return AV_PIX_FMT_P016;
|
|
-
|
|
- case QVideoFrameFormat::Format_SamplerExternalOES:
|
|
- return AV_PIX_FMT_MEDIACODEC;
|
|
- }
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
|
|
deleted file mode 100644
|
|
index a981ec245..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
|
|
+++ /dev/null
|
|
@@ -1,72 +0,0 @@
|
|
-// Copyright (C) 2021 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#ifndef QFFMPEGVIDEOBUFFER_P_H
|
|
-#define QFFMPEGVIDEOBUFFER_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include <private/qtmultimediaglobal_p.h>
|
|
-#include <private/qabstractvideobuffer_p.h>
|
|
-#include <qvideoframe.h>
|
|
-#include <QtCore/qvariant.h>
|
|
-
|
|
-#include "qffmpeg_p.h"
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-class QFFmpegVideoBuffer : public QAbstractVideoBuffer
|
|
-{
|
|
-public:
|
|
-
|
|
- QFFmpegVideoBuffer(AVFrame *frame);
|
|
- ~QFFmpegVideoBuffer();
|
|
-
|
|
- QVideoFrame::MapMode mapMode() const override;
|
|
- MapData map(QVideoFrame::MapMode mode) override;
|
|
- void unmap() override;
|
|
-
|
|
- virtual std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *) override;
|
|
- virtual quint64 textureHandle(int plane) const override;
|
|
-
|
|
- QVideoFrameFormat::PixelFormat pixelFormat() const;
|
|
- QSize size() const;
|
|
-
|
|
- static QVideoFrameFormat::PixelFormat toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion = nullptr);
|
|
- static AVPixelFormat toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat);
|
|
-
|
|
- void convertSWFrame();
|
|
-
|
|
- AVFrame *getHWFrame() const { return hwFrame; }
|
|
-
|
|
- void setTextureConverter(const QFFmpeg::TextureConverter &converter);
|
|
-
|
|
- QVideoFrameFormat::ColorSpace colorSpace() const;
|
|
- QVideoFrameFormat::ColorTransfer colorTransfer() const;
|
|
- QVideoFrameFormat::ColorRange colorRange() const;
|
|
-
|
|
- float maxNits();
|
|
-
|
|
-private:
|
|
- QVideoFrameFormat::PixelFormat m_pixelFormat;
|
|
- AVFrame *frame = nullptr;
|
|
- AVFrame *hwFrame = nullptr;
|
|
- AVFrame *swFrame = nullptr;
|
|
- QFFmpeg::TextureConverter textureConverter;
|
|
- QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped;
|
|
- QFFmpeg::TextureSet *textures = nullptr;
|
|
-};
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp
|
|
deleted file mode 100644
|
|
index 6cb34f56c..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp
|
|
+++ /dev/null
|
|
@@ -1,374 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-
|
|
-#include "qffmpegvideoframeencoder_p.h"
|
|
-#include "qffmpegvideobuffer_p.h"
|
|
-#include "qffmpegmediaformatinfo_p.h"
|
|
-#include "qffmpegencoderoptions_p.h"
|
|
-#include "private/qplatformmediarecorder_p.h"
|
|
-#include "private/qmultimediautils_p.h"
|
|
-#include <qloggingcategory.h>
|
|
-
|
|
-extern "C" {
|
|
-#include <libavutil/pixdesc.h>
|
|
-}
|
|
-
|
|
-/* Infrastructure for HW acceleration goes into this file. */
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-Q_LOGGING_CATEGORY(qLcVideoFrameEncoder, "qt.multimedia.ffmpeg.videoencoder")
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-VideoFrameEncoder::Data::~Data()
|
|
-{
|
|
- if (converter)
|
|
- sws_freeContext(converter);
|
|
- avcodec_free_context(&codecContext);
|
|
-}
|
|
-
|
|
-VideoFrameEncoder::VideoFrameEncoder(const QMediaEncoderSettings &encoderSettings,
|
|
- const QSize &sourceSize, float frameRate, AVPixelFormat sourceFormat, AVPixelFormat swFormat)
|
|
- : d(new Data)
|
|
-{
|
|
- d->settings = encoderSettings;
|
|
- d->frameRate = frameRate;
|
|
- d->sourceSize = sourceSize;
|
|
-
|
|
- if (!d->settings.videoResolution().isValid())
|
|
- d->settings.setVideoResolution(d->sourceSize);
|
|
-
|
|
- d->sourceFormat = sourceFormat;
|
|
- d->sourceSWFormat = swFormat;
|
|
-
|
|
- auto qVideoCodec = encoderSettings.videoCodec();
|
|
- auto codecID = QFFmpegMediaFormatInfo::codecIdForVideoCodec(qVideoCodec);
|
|
-
|
|
-#ifndef QT_DISABLE_HW_ENCODING
|
|
- auto [preferredTypes, size] = HWAccel::preferredDeviceTypes();
|
|
- for (qsizetype i = 0; i < size; i++) {
|
|
- auto accel = HWAccel::create(preferredTypes[i]);
|
|
- if (!accel)
|
|
- continue;
|
|
-
|
|
- auto matchesSizeConstraints = [&]() -> bool {
|
|
- auto *constraints = av_hwdevice_get_hwframe_constraints(accel->hwDeviceContextAsBuffer(), nullptr);
|
|
- if (!constraints)
|
|
- return true;
|
|
- // Check size constraints
|
|
- bool result = (d->sourceSize.width() >= constraints->min_width && d->sourceSize.height() >= constraints->min_height &&
|
|
- d->sourceSize.width() <= constraints->max_width && d->sourceSize.height() <= constraints->max_height);
|
|
- av_hwframe_constraints_free(&constraints);
|
|
- return result;
|
|
- };
|
|
-
|
|
- if (!matchesSizeConstraints())
|
|
- continue;
|
|
-
|
|
- d->codec = accel->hardwareEncoderForCodecId(codecID);
|
|
- if (!d->codec)
|
|
- continue;
|
|
- d->accel = std::move(accel);
|
|
- break;
|
|
- }
|
|
-#endif
|
|
-
|
|
- if (!d->accel) {
|
|
- d->codec = avcodec_find_encoder(codecID);
|
|
- if (!d->codec) {
|
|
- qWarning() << "Could not find encoder for codecId" << codecID;
|
|
- d = {};
|
|
- return;
|
|
- }
|
|
- }
|
|
- auto supportsFormat = [&](AVPixelFormat fmt) {
|
|
- auto *f = d->codec->pix_fmts;
|
|
- while (*f != -1) {
|
|
- if (*f == fmt)
|
|
- return true;
|
|
- ++f;
|
|
- }
|
|
- return false;
|
|
- };
|
|
-
|
|
- d->targetFormat = d->sourceFormat;
|
|
-
|
|
- if (!supportsFormat(d->sourceFormat)) {
|
|
- if (supportsFormat(swFormat))
|
|
- d->targetFormat = swFormat;
|
|
- else
|
|
- // Take first format the encoder supports. Might want to improve upon this
|
|
- d->targetFormat = *d->codec->pix_fmts;
|
|
- }
|
|
-
|
|
- auto desc = av_pix_fmt_desc_get(d->sourceFormat);
|
|
- d->sourceFormatIsHWFormat = desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
|
|
- desc = av_pix_fmt_desc_get(d->targetFormat);
|
|
- d->targetFormatIsHWFormat = desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
|
|
-
|
|
- bool needToScale = d->sourceSize != d->settings.videoResolution();
|
|
- bool zeroCopy = d->sourceFormatIsHWFormat && d->sourceFormat == d->targetFormat && !needToScale;
|
|
-
|
|
- if (zeroCopy)
|
|
- // no need to initialize any converters
|
|
- return;
|
|
-
|
|
- if (d->sourceFormatIsHWFormat) {
|
|
- // if source and target formats don't agree, but the source is a HW format or sizes do't agree, we need to download
|
|
- if (d->sourceFormat != d->targetFormat || needToScale)
|
|
- d->downloadFromHW = true;
|
|
- } else {
|
|
- d->sourceSWFormat = d->sourceFormat;
|
|
- }
|
|
-
|
|
- if (d->targetFormatIsHWFormat) {
|
|
- Q_ASSERT(d->accel);
|
|
- // if source and target formats don't agree, but the target is a HW format, we need to upload
|
|
- if (d->sourceFormat != d->targetFormat || needToScale) {
|
|
- d->uploadToHW = true;
|
|
-
|
|
- // determine the format used by the encoder.
|
|
- // We prefer YUV422 based formats such as NV12 or P010. Selection trues to find the best matching
|
|
- // format for the encoder depending on the bit depth of the source format
|
|
- auto desc = av_pix_fmt_desc_get(d->sourceSWFormat);
|
|
- int sourceDepth = desc->comp[0].depth;
|
|
-
|
|
- d->targetSWFormat = AV_PIX_FMT_NONE;
|
|
-
|
|
- auto *constraints = av_hwdevice_get_hwframe_constraints(d->accel->hwDeviceContextAsBuffer(), nullptr);
|
|
- auto *f = constraints->valid_sw_formats;
|
|
- int score = INT_MIN;
|
|
- while (*f != AV_PIX_FMT_NONE) {
|
|
- auto calcScore = [&](AVPixelFormat fmt) -> int {
|
|
- auto *desc = av_pix_fmt_desc_get(fmt);
|
|
- int s = 0;
|
|
- if (fmt == d->sourceSWFormat)
|
|
- // prefer exact matches
|
|
- s += 10;
|
|
- if (desc->comp[0].depth == sourceDepth)
|
|
- s += 100;
|
|
- else if (desc->comp[0].depth < sourceDepth)
|
|
- s -= 100;
|
|
- if (desc->log2_chroma_h == 1)
|
|
- s += 1;
|
|
- if (desc->log2_chroma_w == 1)
|
|
- s += 1;
|
|
- if (desc->flags & AV_PIX_FMT_FLAG_BE)
|
|
- s -= 10;
|
|
- if (desc->flags & AV_PIX_FMT_FLAG_PAL)
|
|
- // we don't want paletted formats
|
|
- s -= 10000;
|
|
- if (desc->flags & AV_PIX_FMT_FLAG_RGB)
|
|
- // we don't want RGB formats
|
|
- s -= 1000;
|
|
- if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
|
|
- // we really don't want HW accelerated formats here
|
|
- s -= 1000000;
|
|
- qCDebug(qLcVideoFrameEncoder) << "checking format" << fmt << Qt::hex << desc->flags << desc->comp[0].depth
|
|
- << desc->log2_chroma_h << desc->log2_chroma_w << "score:" << s;
|
|
- return s;
|
|
- };
|
|
-
|
|
- int s = calcScore(*f);
|
|
- if (s > score) {
|
|
- d->targetSWFormat = *f;
|
|
- score = s;
|
|
- }
|
|
- ++f;
|
|
- }
|
|
- if (d->targetSWFormat == AV_PIX_FMT_NONE) // shouldn't happen
|
|
- d->targetSWFormat = *constraints->valid_sw_formats;
|
|
-
|
|
- qCDebug(qLcVideoFrameEncoder) << "using format" << d->targetSWFormat << "as transfer format.";
|
|
-
|
|
- av_hwframe_constraints_free(&constraints);
|
|
- // need to create a frames context to convert the input data
|
|
- d->accel->createFramesContext(d->targetSWFormat, sourceSize);
|
|
- }
|
|
- } else {
|
|
- d->targetSWFormat = d->targetFormat;
|
|
- }
|
|
-
|
|
- if (d->sourceSWFormat != d->targetSWFormat || needToScale) {
|
|
- auto resolution = d->settings.videoResolution();
|
|
- qCDebug(qLcVideoFrameEncoder) << "camera and encoder use different formats:" << d->sourceSWFormat << d->targetSWFormat;
|
|
- d->converter = sws_getContext(d->sourceSize.width(), d->sourceSize.height(), d->sourceSWFormat,
|
|
- resolution.width(), resolution.height(), d->targetSWFormat,
|
|
- SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
|
|
- }
|
|
-}
|
|
-
|
|
-VideoFrameEncoder::~VideoFrameEncoder()
|
|
-{
|
|
-}
|
|
-
|
|
-void QFFmpeg::VideoFrameEncoder::initWithFormatContext(AVFormatContext *formatContext)
|
|
-{
|
|
- d->stream = avformat_new_stream(formatContext, nullptr);
|
|
- d->stream->id = formatContext->nb_streams - 1;
|
|
- //qCDebug(qLcVideoFrameEncoder) << "Video stream: index" << d->stream->id;
|
|
- d->stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
- d->stream->codecpar->codec_id = d->codec->id;
|
|
-
|
|
- // Apples HEVC decoders don't like the hev1 tag ffmpeg uses by default, use hvc1 as the more commonly accepted tag
|
|
- if (d->codec->id == AV_CODEC_ID_HEVC)
|
|
- d->stream->codecpar->codec_tag = MKTAG('h','v','c','1');
|
|
-
|
|
- // ### Fix hardcoded values
|
|
- d->stream->codecpar->format = d->targetFormat;
|
|
- d->stream->codecpar->width = d->settings.videoResolution().width();
|
|
- d->stream->codecpar->height = d->settings.videoResolution().height();
|
|
- d->stream->codecpar->sample_aspect_ratio = AVRational{1, 1};
|
|
- float requestedRate = d->frameRate;
|
|
- d->stream->time_base = AVRational{ 1, (int)(requestedRate*1000) };
|
|
-
|
|
- float delta = 1e10;
|
|
- if (d->codec->supported_framerates) {
|
|
- // codec only supports fixed frame rates
|
|
- auto *best = d->codec->supported_framerates;
|
|
- qCDebug(qLcVideoFrameEncoder) << "Finding fixed rate:";
|
|
- for (auto *f = d->codec->supported_framerates; f->num != 0; f++) {
|
|
- auto maybeRate = toFloat(*f);
|
|
- if (!maybeRate)
|
|
- continue;
|
|
- float d = qAbs(*maybeRate - requestedRate);
|
|
- qCDebug(qLcVideoFrameEncoder) << " " << f->num << f->den << d;
|
|
- if (d < delta) {
|
|
- best = f;
|
|
- delta = d;
|
|
- }
|
|
- }
|
|
- qCDebug(qLcVideoFrameEncoder) << "Fixed frame rate required. Requested:" << requestedRate << "Using:" << best->num << "/" << best->den;
|
|
- d->stream->time_base = *best;
|
|
- requestedRate = toFloat(*best).value_or(0.f);
|
|
- }
|
|
-
|
|
- Q_ASSERT(d->codec);
|
|
- d->codecContext = avcodec_alloc_context3(d->codec);
|
|
- if (!d->codecContext) {
|
|
- qWarning() << "Could not allocate codec context";
|
|
- d = {};
|
|
- return;
|
|
- }
|
|
-
|
|
- avcodec_parameters_to_context(d->codecContext, d->stream->codecpar);
|
|
- d->codecContext->time_base = d->stream->time_base;
|
|
- qCDebug(qLcVideoFrameEncoder) << "requesting time base" << d->codecContext->time_base.num << d->codecContext->time_base.den;
|
|
- auto [num, den] = qRealToFraction(requestedRate);
|
|
- d->codecContext->framerate = { num, den };
|
|
- if (d->accel) {
|
|
- auto deviceContext = d->accel->hwDeviceContextAsBuffer();
|
|
- if (deviceContext)
|
|
- d->codecContext->hw_device_ctx = av_buffer_ref(deviceContext);
|
|
- auto framesContext = d->accel->hwFramesContextAsBuffer();
|
|
- if (framesContext)
|
|
- d->codecContext->hw_frames_ctx = av_buffer_ref(framesContext);
|
|
- }
|
|
-}
|
|
-
|
|
-bool VideoFrameEncoder::open()
|
|
-{
|
|
- AVDictionary *opts = nullptr;
|
|
- applyVideoEncoderOptions(d->settings, d->codec->name, d->codecContext, &opts);
|
|
- int res = avcodec_open2(d->codecContext, d->codec, &opts);
|
|
- if (res < 0) {
|
|
- avcodec_free_context(&d->codecContext);
|
|
- qWarning() << "Couldn't open codec for writing" << err2str(res);
|
|
- return false;
|
|
- }
|
|
- qCDebug(qLcVideoFrameEncoder) << "video codec opened" << res << "time base" << d->codecContext->time_base.num << d->codecContext->time_base.den;
|
|
- d->stream->time_base = d->codecContext->time_base;
|
|
- return true;
|
|
-}
|
|
-
|
|
-qint64 VideoFrameEncoder::getPts(qint64 us)
|
|
-{
|
|
- Q_ASSERT(d);
|
|
- qint64 div = 1'000'000 * d->stream->time_base.num;
|
|
- return div != 0 ? (us * d->stream->time_base.den + div / 2) / div : 0;
|
|
-}
|
|
-
|
|
-int VideoFrameEncoder::sendFrame(AVFrame *frame)
|
|
-{
|
|
- if (!frame)
|
|
- return avcodec_send_frame(d->codecContext, frame);
|
|
- auto pts = frame->pts;
|
|
-
|
|
- if (d->downloadFromHW) {
|
|
- auto *f = av_frame_alloc();
|
|
- f->format = d->sourceSWFormat;
|
|
- int err = av_hwframe_transfer_data(f, frame, 0);
|
|
- if (err < 0) {
|
|
- qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
|
|
- return err;
|
|
- }
|
|
- av_frame_free(&frame);
|
|
- frame = f;
|
|
- }
|
|
-
|
|
- if (d->converter) {
|
|
- auto *f = av_frame_alloc();
|
|
- f->format = d->targetSWFormat;
|
|
- f->width = d->settings.videoResolution().width();
|
|
- f->height = d->settings.videoResolution().height();
|
|
- av_frame_get_buffer(f, 0);
|
|
- sws_scale(d->converter, frame->data, frame->linesize, 0, f->height, f->data, f->linesize);
|
|
- av_frame_free(&frame);
|
|
- frame = f;
|
|
- }
|
|
-
|
|
- if (d->uploadToHW) {
|
|
- auto *hwFramesContext = d->accel->hwFramesContextAsBuffer();
|
|
- Q_ASSERT(hwFramesContext);
|
|
- auto *f = av_frame_alloc();
|
|
- if (!f)
|
|
- return AVERROR(ENOMEM);
|
|
- int err = av_hwframe_get_buffer(hwFramesContext, f, 0);
|
|
- if (err < 0) {
|
|
- qCDebug(qLcVideoFrameEncoder) << "Error getting HW buffer" << err2str(err);
|
|
- return err;
|
|
- } else {
|
|
- qCDebug(qLcVideoFrameEncoder) << "got HW buffer";
|
|
- }
|
|
- if (!f->hw_frames_ctx) {
|
|
- qCDebug(qLcVideoFrameEncoder) << "no hw frames context";
|
|
- return AVERROR(ENOMEM);
|
|
- }
|
|
- err = av_hwframe_transfer_data(f, frame, 0);
|
|
- if (err < 0) {
|
|
- qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
|
|
- return err;
|
|
- }
|
|
- av_frame_free(&frame);
|
|
- frame = f;
|
|
- }
|
|
-
|
|
- qCDebug(qLcVideoFrameEncoder) << "sending frame" << pts;
|
|
- frame->pts = pts;
|
|
- int ret = avcodec_send_frame(d->codecContext, frame);
|
|
- av_frame_free(&frame);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-AVPacket *VideoFrameEncoder::retrievePacket()
|
|
-{
|
|
- if (!d || !d->codecContext)
|
|
- return nullptr;
|
|
- AVPacket *packet = av_packet_alloc();
|
|
- int ret = avcodec_receive_packet(d->codecContext, packet);
|
|
- if (ret < 0) {
|
|
- av_packet_free(&packet);
|
|
- if (ret != AVERROR(EOF) && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
|
- qCDebug(qLcVideoFrameEncoder) << "Error receiving packet" << ret << err2str(ret);
|
|
- return nullptr;
|
|
- }
|
|
- auto ts = timeStampMs(packet->pts, d->stream->time_base);
|
|
- qCDebug(qLcVideoFrameEncoder) << "got a packet" << packet->pts << (ts ? *ts : 0);
|
|
- packet->stream_index = d->stream->id;
|
|
- return packet;
|
|
-}
|
|
-
|
|
-} // namespace QFFmpeg
|
|
-
|
|
-QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h
|
|
deleted file mode 100644
|
|
index f71460799..000000000
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h
|
|
+++ /dev/null
|
|
@@ -1,76 +0,0 @@
|
|
-// Copyright (C) 2022 The Qt Company Ltd.
|
|
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
-#ifndef QFFMPEGVIDEOFRAMEENCODER_P_H
|
|
-#define QFFMPEGVIDEOFRAMEENCODER_P_H
|
|
-
|
|
-//
|
|
-// W A R N I N G
|
|
-// -------------
|
|
-//
|
|
-// This file is not part of the Qt API. It exists purely as an
|
|
-// implementation detail. This header file may change from version to
|
|
-// version without notice, or even be removed.
|
|
-//
|
|
-// We mean it.
|
|
-//
|
|
-
|
|
-#include "qffmpeghwaccel_p.h"
|
|
-#include "qvideoframeformat.h"
|
|
-#include "private/qplatformmediarecorder_p.h"
|
|
-
|
|
-QT_BEGIN_NAMESPACE
|
|
-
|
|
-namespace QFFmpeg {
|
|
-
|
|
-class VideoFrameEncoder
|
|
-{
|
|
- class Data final
|
|
- {
|
|
- public:
|
|
- ~Data();
|
|
- QAtomicInt ref = 0;
|
|
- QMediaEncoderSettings settings;
|
|
- float frameRate = 0.;
|
|
- QSize sourceSize;
|
|
-
|
|
- std::unique_ptr<HWAccel> accel;
|
|
- const AVCodec *codec = nullptr;
|
|
- AVStream *stream = nullptr;
|
|
- AVCodecContext *codecContext = nullptr;
|
|
- SwsContext *converter = nullptr;
|
|
- AVPixelFormat sourceFormat = AV_PIX_FMT_NONE;
|
|
- AVPixelFormat sourceSWFormat = AV_PIX_FMT_NONE;
|
|
- AVPixelFormat targetFormat = AV_PIX_FMT_NONE;
|
|
- AVPixelFormat targetSWFormat = AV_PIX_FMT_NONE;
|
|
- bool sourceFormatIsHWFormat = false;
|
|
- bool targetFormatIsHWFormat = false;
|
|
- bool downloadFromHW = false;
|
|
- bool uploadToHW = false;
|
|
- };
|
|
-
|
|
- QExplicitlySharedDataPointer<Data> d;
|
|
-public:
|
|
- VideoFrameEncoder() = default;
|
|
- VideoFrameEncoder(const QMediaEncoderSettings &encoderSettings, const QSize &sourceSize, float frameRate, AVPixelFormat sourceFormat, AVPixelFormat swFormat);
|
|
- ~VideoFrameEncoder();
|
|
-
|
|
- void initWithFormatContext(AVFormatContext *formatContext);
|
|
- bool open();
|
|
-
|
|
- bool isNull() const { return !d; }
|
|
-
|
|
- AVPixelFormat sourceFormat() const { return d ? d->sourceFormat : AV_PIX_FMT_NONE; }
|
|
- AVPixelFormat targetFormat() const { return d ? d->targetFormat : AV_PIX_FMT_NONE; }
|
|
-
|
|
- qint64 getPts(qint64 ms);
|
|
-
|
|
- int sendFrame(AVFrame *frame);
|
|
- AVPacket *retrievePacket();
|
|
-};
|
|
-
|
|
-
|
|
-}
|
|
-
|
|
-QT_END_NAMESPACE
|
|
-
|
|
-#endif
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
|
|
index 3cb31b473..93e7ceeed 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
|
|
@@ -1,7 +1,6 @@
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
#include <qffmpegvideosink_p.h>
|
|
-#include <qffmpegvideobuffer_p.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
@@ -10,21 +9,8 @@ QFFmpegVideoSink::QFFmpegVideoSink(QVideoSink *sink)
|
|
{
|
|
}
|
|
|
|
-void QFFmpegVideoSink::setRhi(QRhi *rhi)
|
|
-{
|
|
- if (m_rhi == rhi)
|
|
- return;
|
|
- m_rhi = rhi;
|
|
- textureConverter = QFFmpeg::TextureConverter(rhi);
|
|
- emit rhiChanged(rhi);
|
|
-}
|
|
-
|
|
void QFFmpegVideoSink::setVideoFrame(const QVideoFrame &frame)
|
|
{
|
|
- auto *buffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer());
|
|
- if (buffer)
|
|
- buffer->setTextureConverter(textureConverter);
|
|
-
|
|
QPlatformVideoSink::setVideoFrame(frame);
|
|
}
|
|
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
|
|
index dbd9ac7f2..cbaa810d7 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
|
|
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
|
|
@@ -16,7 +16,7 @@
|
|
//
|
|
|
|
#include <private/qplatformvideosink_p.h>
|
|
-#include <qffmpeghwaccel_p.h>
|
|
+//#include <qffmpeghwaccel_p.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
@@ -29,16 +29,8 @@ class QFFmpegVideoSink : public QPlatformVideoSink
|
|
|
|
public:
|
|
QFFmpegVideoSink(QVideoSink *sink);
|
|
- void setRhi(QRhi *rhi) override;
|
|
|
|
void setVideoFrame(const QVideoFrame &frame) override;
|
|
-
|
|
-Q_SIGNALS:
|
|
- void rhiChanged(QRhi *rhi);
|
|
-
|
|
-private:
|
|
- QFFmpeg::TextureConverter textureConverter;
|
|
- QRhi *m_rhi = nullptr;
|
|
};
|
|
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp b/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
|
|
index 4189bdedf..c61c452f3 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
|
|
@@ -147,7 +147,7 @@ void QV4L2CameraDevices::doCheckCameras()
|
|
|
|
while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) {
|
|
if (frameSize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
|
|
- continue;
|
|
+ break;
|
|
|
|
QSize resolution(frameSize.discrete.width, frameSize.discrete.height);
|
|
float min = 1e10;
|
|
@@ -160,7 +160,7 @@ void QV4L2CameraDevices::doCheckCameras()
|
|
|
|
while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) {
|
|
if (frameInterval.type != V4L2_FRMIVAL_TYPE_DISCRETE)
|
|
- continue;
|
|
+ break;
|
|
++frameInterval.index;
|
|
float rate = float(frameInterval.discrete.denominator)/float(frameInterval.discrete.numerator);
|
|
if (rate > max)
|
|
diff --git a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
|
|
index 790c49858..4a4e7a921 100644
|
|
--- a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
|
|
+++ b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
|
|
@@ -11,8 +11,8 @@
|
|
|
|
#include <mfapi.h>
|
|
#include <mfidl.h>
|
|
-#include <Mferror.h>
|
|
-#include <Mfreadwrite.h>
|
|
+#include <mferror.h>
|
|
+#include <mfreadwrite.h>
|
|
|
|
#include <system_error>
|
|
|
|
diff --git a/src/plugins/multimedia/windows/common/mfmetadata_p.h b/src/plugins/multimedia/windows/common/mfmetadata_p.h
|
|
index 81a03b126..9ff196240 100644
|
|
--- a/src/plugins/multimedia/windows/common/mfmetadata_p.h
|
|
+++ b/src/plugins/multimedia/windows/common/mfmetadata_p.h
|
|
@@ -16,7 +16,7 @@
|
|
//
|
|
|
|
#include <qmediametadata.h>
|
|
-#include "Mfidl.h"
|
|
+#include "mfidl.h"
|
|
|
|
QT_USE_NAMESPACE
|
|
|
|
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
|
|
index 45bc70d65..0e27a2779 100644
|
|
--- a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
|
|
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
|
|
@@ -4,7 +4,7 @@
|
|
#include <system_error>
|
|
#include <mferror.h>
|
|
#include <qglobal.h>
|
|
-#include "Wmcodecdsp.h"
|
|
+#include "wmcodecdsp.h"
|
|
#include "mfaudiodecodercontrol_p.h"
|
|
#include <private/qwindowsaudioutils_p.h>
|
|
|
|
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
|
|
index 0205eafe2..4699a463a 100644
|
|
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
|
|
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
|
|
@@ -17,8 +17,8 @@
|
|
|
|
#include <mfapi.h>
|
|
#include <mfidl.h>
|
|
-#include <Mferror.h>
|
|
-#include <Mfreadwrite.h>
|
|
+#include <mferror.h>
|
|
+#include <mfreadwrite.h>
|
|
|
|
#include <QtCore/qobject.h>
|
|
#include <QtCore/qmutex.h>
|
|
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
|
|
index d5eb07980..dc87afc4b 100644
|
|
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
|
|
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
|
|
@@ -8,7 +8,7 @@
|
|
#include "mfmetadata_p.h"
|
|
#include <QtCore/QUrl>
|
|
#include <QtCore/QMimeType>
|
|
-#include <Mferror.h>
|
|
+#include <mferror.h>
|
|
#include <shobjidl.h>
|
|
#include <private/qmediastoragelocation_p.h>
|
|
#include <private/qmediarecorder_p.h>
|
|
diff --git a/src/plugins/multimedia/windows/player/mfplayercontrol_p.h b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
|
|
index ac60e8c29..78ff71439 100644
|
|
--- a/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
|
|
+++ b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
|
|
@@ -15,7 +15,7 @@
|
|
// We mean it.
|
|
//
|
|
|
|
-#include "QUrl.h"
|
|
+#include "qurl.h"
|
|
#include "private/qplatformmediaplayer_p.h"
|
|
|
|
#include <QtCore/qcoreevent.h>
|
|
diff --git a/src/plugins/multimedia/windows/player/mfplayersession.cpp b/src/plugins/multimedia/windows/player/mfplayersession.cpp
|
|
index 58efaa87e..5d3372152 100644
|
|
--- a/src/plugins/multimedia/windows/player/mfplayersession.cpp
|
|
+++ b/src/plugins/multimedia/windows/player/mfplayersession.cpp
|
|
@@ -32,7 +32,7 @@
|
|
|
|
#include <mmdeviceapi.h>
|
|
#include <propvarutil.h>
|
|
-#include <Functiondiscoverykeys_devpkey.h>
|
|
+#include <functiondiscoverykeys_devpkey.h>
|
|
|
|
//#define DEBUG_MEDIAFOUNDATION
|
|
|
|
diff --git a/src/plugins/multimedia/windows/player/mftvideo.cpp b/src/plugins/multimedia/windows/player/mftvideo.cpp
|
|
index 601c51e42..06a8769a7 100644
|
|
--- a/src/plugins/multimedia/windows/player/mftvideo.cpp
|
|
+++ b/src/plugins/multimedia/windows/player/mftvideo.cpp
|
|
@@ -7,7 +7,7 @@
|
|
#include <mferror.h>
|
|
#include <strmif.h>
|
|
#include <uuids.h>
|
|
-#include <InitGuid.h>
|
|
+#include <initguid.h>
|
|
#include <d3d9.h>
|
|
#include <qdebug.h>
|
|
|
|
diff --git a/src/plugins/multimedia/windows/qwindowsvideodevices.cpp b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
|
|
index 878c4730b..41ccb4733 100644
|
|
--- a/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
|
|
+++ b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
|
|
@@ -8,11 +8,11 @@
|
|
#include <private/qwindowsmultimediautils_p.h>
|
|
#include <private/qwindowsiupointer_p.h>
|
|
|
|
-#include <Dbt.h>
|
|
+#include <dbt.h>
|
|
|
|
#include <mfapi.h>
|
|
#include <mfreadwrite.h>
|
|
-#include <Mferror.h>
|
|
+#include <mferror.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
--
|
|
2.39.0
|
|
|