14#include <unordered_set>
17#include <libavutil/pixdesc.h>
18#include <libavutil/samplefmt.h>
21#include <libavutil/hwcontext_videotoolbox.h>
26#include <QtCore/qjniobject.h>
27#include <QtCore/qjniarray.h>
28#include <QtCore/qjnitypes.h>
35 "org/qtproject/qt/android/multimedia/QtVideoDeviceManager");
45enum CodecStorageType {
51 CODEC_STORAGE_TYPE_COUNT
54using CodecsStorage = std::vector<const AVCodec *>;
56struct CodecsComparator
58 bool operator()(
const AVCodec *
a,
const AVCodec *
b)
const
64 bool operator()(
const AVCodec *
a, AVCodecID
id)
const {
return a->id <
id; }
67template<
typename FlagNames>
68QString flagsToString(
int flags,
const FlagNames &flagNames)
72 for (
const auto &flagAndName : flagNames)
74 leftover &= ~flagAndName.
first;
77 result += flagAndName.second;
88void dumpCodecInfo(
const AVCodec *
codec)
90 using FlagNames = std::initializer_list<std::pair<int, const char *>>;
91 const auto mediaType =
codec->type == AVMEDIA_TYPE_VIDEO ?
"video"
92 :
codec->type == AVMEDIA_TYPE_AUDIO ?
"audio"
93 :
codec->type == AVMEDIA_TYPE_SUBTITLE ?
"subtitle"
96 const auto type = av_codec_is_encoder(
codec)
97 ? av_codec_is_decoder(
codec) ?
"encoder/decoder:" :
"encoder:"
100 static const FlagNames capabilitiesNames = {
101 { AV_CODEC_CAP_DRAW_HORIZ_BAND,
"DRAW_HORIZ_BAND" },
102 { AV_CODEC_CAP_DR1,
"DRAW_HORIZ_DR1" },
103 { AV_CODEC_CAP_DELAY,
"DELAY" },
104 { AV_CODEC_CAP_SMALL_LAST_FRAME,
"SMALL_LAST_FRAME" },
105 { AV_CODEC_CAP_SUBFRAMES,
"SUBFRAMES" },
106 { AV_CODEC_CAP_EXPERIMENTAL,
"EXPERIMENTAL" },
107 { AV_CODEC_CAP_CHANNEL_CONF,
"CHANNEL_CONF" },
108 { AV_CODEC_CAP_FRAME_THREADS,
"FRAME_THREADS" },
109 { AV_CODEC_CAP_SLICE_THREADS,
"SLICE_THREADS" },
110 { AV_CODEC_CAP_PARAM_CHANGE,
"PARAM_CHANGE" },
111#ifdef AV_CODEC_CAP_OTHER_THREADS
112 { AV_CODEC_CAP_OTHER_THREADS,
"OTHER_THREADS" },
114 { AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
"VARIABLE_FRAME_SIZE" },
115 { AV_CODEC_CAP_AVOID_PROBING,
"AVOID_PROBING" },
116 { AV_CODEC_CAP_HARDWARE,
"HARDWARE" },
117 { AV_CODEC_CAP_HYBRID,
"HYBRID" },
118 { AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
"ENCODER_REORDERED_OPAQUE" },
119#ifdef AV_CODEC_CAP_ENCODER_FLUSH
120 { AV_CODEC_CAP_ENCODER_FLUSH,
"ENCODER_FLUSH" },
126 << flagsToString(
codec->capabilities, capabilitiesNames);
128 if (
codec->pix_fmts) {
129 static const FlagNames flagNames = {
130 { AV_PIX_FMT_FLAG_BE,
"BE" },
131 { AV_PIX_FMT_FLAG_PAL,
"PAL" },
132 { AV_PIX_FMT_FLAG_BITSTREAM,
"BITSTREAM" },
133 { AV_PIX_FMT_FLAG_HWACCEL,
"HWACCEL" },
134 { AV_PIX_FMT_FLAG_PLANAR,
"PLANAR" },
135 { AV_PIX_FMT_FLAG_RGB,
"RGB" },
136 { AV_PIX_FMT_FLAG_ALPHA,
"ALPHA" },
137 { AV_PIX_FMT_FLAG_BAYER,
"BAYER" },
138 { AV_PIX_FMT_FLAG_FLOAT,
"FLOAT" },
141 qCDebug(qLcFFmpegUtils) <<
" pix_fmts:";
142 for (
auto f =
codec->pix_fmts; *
f != AV_PIX_FMT_NONE; ++
f) {
143 auto desc = av_pix_fmt_desc_get(*
f);
145 <<
" id:" << *
f <<
desc->name <<
"depth:" <<
desc->comp[0].depth
146 <<
"flags:" << flagsToString(
desc->flags, flagNames);
148 }
else if (
codec->type == AVMEDIA_TYPE_VIDEO) {
149 qCDebug(qLcFFmpegUtils) <<
" pix_fmts: null";
152 if (
codec->sample_fmts) {
153 qCDebug(qLcFFmpegUtils) <<
" sample_fmts:";
154 for (
auto f =
codec->sample_fmts; *
f != AV_SAMPLE_FMT_NONE; ++
f) {
155 const auto name = av_get_sample_fmt_name(*
f);
157 <<
"bytes_per_sample:" << av_get_bytes_per_sample(*
f)
158 <<
"is_planar:" << av_sample_fmt_is_planar(*
f);
160 }
else if (
codec->type == AVMEDIA_TYPE_AUDIO) {
161 qCDebug(qLcFFmpegUtils) <<
" sample_fmts: null";
164 if (avcodec_get_hw_config(
codec, 0)) {
165 static const FlagNames hwConfigMethodNames = {
166 { AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
"HW_DEVICE_CTX" },
167 { AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX,
"HW_FRAMES_CTX" },
168 { AV_CODEC_HW_CONFIG_METHOD_INTERNAL,
"INTERNAL" },
169 { AV_CODEC_HW_CONFIG_METHOD_AD_HOC,
"AD_HOC" }
172 qCDebug(qLcFFmpegUtils) <<
" hw config:";
175 auto pixFmtDesc = av_pix_fmt_desc_get(
config->pix_fmt);
176 auto pixFmtForDeviceDesc = av_pix_fmt_desc_get(pixFmtForDevice);
178 <<
" device_type:" <<
config->device_type <<
"pix_fmt:" <<
config->pix_fmt
179 << (pixFmtDesc ? pixFmtDesc->name :
"unknown")
181 << (pixFmtForDeviceDesc ? pixFmtForDeviceDesc->name :
"unknown")
182 <<
"hw_config_methods:" << flagsToString(
config->methods, hwConfigMethodNames);
187bool isCodecValid(
const AVCodec *
codec,
const std::vector<AVHWDeviceType> &availableHwDeviceTypes,
188 const std::optional<std::unordered_set<AVCodecID>> &codecAvailableOnDevice)
190 if (
codec->type != AVMEDIA_TYPE_VIDEO)
193 const auto pixFmts =
codec->pix_fmts;
196#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
199 if (std::strstr(
codec->name,
"_v4l2m2m") && av_codec_is_encoder(
codec))
204 if (std::strstr(
codec->name,
"_mediacodec")
205 && (
codec->capabilities & AV_CODEC_CAP_HARDWARE)
206 && codecAvailableOnDevice && codecAvailableOnDevice->count(
codec->id) == 0)
217 if ((
codec->capabilities & AV_CODEC_CAP_HARDWARE) == 0)
220 auto checkDeviceType = [pixFmts](AVHWDeviceType
type) {
224 if (codecAvailableOnDevice && codecAvailableOnDevice->count(
codec->id) == 0)
227 return std::any_of(availableHwDeviceTypes.begin(), availableHwDeviceTypes.end(),
231std::optional<std::unordered_set<AVCodecID>> availableHWCodecs(
const CodecStorageType
type)
235 std::unordered_set<AVCodecID> availabeCodecs;
237 auto getCodecId = [] (
const QString& codecName) {
238 if (codecName ==
"3gpp"_L1)
return AV_CODEC_ID_H263;
239 if (codecName ==
"avc"_L1)
return AV_CODEC_ID_H264;
240 if (codecName ==
"hevc"_L1)
return AV_CODEC_ID_HEVC;
241 if (codecName ==
"mp4v-es"_L1)
return AV_CODEC_ID_MPEG4;
242 if (codecName ==
"x-vnd.on2.vp8"_L1)
return AV_CODEC_ID_VP8;
243 if (codecName ==
"x-vnd.on2.vp9"_L1)
return AV_CODEC_ID_VP9;
244 return AV_CODEC_ID_NONE;
248 QtJniTypes::QtVideoDeviceManager::callStaticMethod<QtJniTypes::String[]>(
249 type == ENCODERS ?
"getHWVideoEncoders" :
"getHWVideoDecoders");
251 QJniArray<QtJniTypes::String> arrCodecs(jniCodecs.object<jobjectArray>());
252 for (
int i = 0;
i < arrCodecs.size(); ++
i) {
253 availabeCodecs.insert(getCodecId(arrCodecs.at(
i).toString()));
255 return availabeCodecs;
262const CodecsStorage &codecsStorage(CodecStorageType codecsType)
264 static const auto &storages = []() {
265 std::array<CodecsStorage, CODEC_STORAGE_TYPE_COUNT>
result;
266 void *opaque =
nullptr;
267 const auto platformHwEncoders = availableHWCodecs(ENCODERS);
268 const auto platformHwDecoders = availableHWCodecs(DECODERS);
270 while (
auto codec = av_codec_iterate(&opaque)) {
278 static const auto experimentalCodecsEnabled =
282 qCDebug(qLcFFmpegUtils) <<
"Skip experimental codec" <<
codec->name;
286 if (av_codec_is_decoder(
codec)) {
291 <<
"Skip decoder" <<
codec->name
292 <<
"due to disabled matching hw acceleration, or dysfunctional codec";
295 if (av_codec_is_encoder(
codec)) {
300 <<
"Skip encoder" <<
codec->name
301 <<
"due to disabled matching hw acceleration, or dysfunctional codec";
309 std::stable_sort(
storage.begin(),
storage.end(), CodecsComparator{});
313 const bool shouldDumpCodecsInfo = qLcFFmpegUtils().isEnabled(
QtDebugMsg)
316 if (shouldDumpCodecsInfo) {
317 qCDebug(qLcFFmpegUtils) <<
"Advanced FFmpeg codecs info:";
320 qCDebug(qLcFFmpegUtils) <<
"---------------------------";
327 return storages[codecsType];
330const char *preferredHwCodecNameSuffix(
bool isEncoder, AVHWDeviceType
deviceType)
333 case AV_HWDEVICE_TYPE_VAAPI:
335 case AV_HWDEVICE_TYPE_MEDIACODEC:
336 return "_mediacodec";
337 case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
338 return "_videotoolbox";
339 case AV_HWDEVICE_TYPE_D3D11VA:
340 case AV_HWDEVICE_TYPE_DXVA2:
341#if QT_FFMPEG_HAS_D3D12VA
342 case AV_HWDEVICE_TYPE_D3D12VA:
345 case AV_HWDEVICE_TYPE_CUDA:
346 case AV_HWDEVICE_TYPE_VDPAU:
347 return isEncoder ?
"_nvenc" :
"_cuvid";
353template<
typename CodecScoreGetter>
354const AVCodec *findAVCodec(CodecStorageType codecsType, AVCodecID
codecId,
355 const CodecScoreGetter &scoreGetter)
357 const auto &
storage = codecsStorage(codecsType);
360 const AVCodec *
result =
nullptr;
364 const auto score = scoreGetter(*
it);
366 if (score > resultScore) {
377 if (
auto suffix = preferredHwCodecNameSuffix(av_codec_is_encoder(
codec),
deviceType)) {
378 const auto substr = strstr(
codec->name, suffix);
379 if (substr && !substr[strlen(suffix)])
388const AVCodec *findAVCodec(CodecStorageType codecsType, AVCodecID
codecId,
389 const std::optional<AVHWDeviceType> &
deviceType,
390 const std::optional<PixelOrSampleFormat> &
format)
392 return findAVCodec(codecsType,
codecId, [&](
const AVCodec *
codec) {
428 const std::optional<PixelOrSampleFormat> &
format)
434 const std::optional<PixelOrSampleFormat> &
format)
440 const std::function<
AVScore(
const AVCodec *)> &scoresGetter)
442 return findAVCodec(ENCODERS,
codecId, scoresGetter);
447 if (
codec->type == AVMEDIA_TYPE_VIDEO)
450 if (
codec->type == AVMEDIA_TYPE_AUDIO)
458 const auto desc = av_pix_fmt_desc_get(
format);
459 return desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
464 return (
codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) != 0;
470 qCWarning(qLcFFmpegUtils) <<
"Applying the option 'strict -2' for the experimental codec"
471 <<
codec->name <<
". it's unlikely to work properly";
472 av_dict_set(opts,
"strict",
"-2", 0);
479 case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
480 return AV_PIX_FMT_VIDEOTOOLBOX;
481 case AV_HWDEVICE_TYPE_VAAPI:
482 return AV_PIX_FMT_VAAPI;
483 case AV_HWDEVICE_TYPE_MEDIACODEC:
484 return AV_PIX_FMT_MEDIACODEC;
485 case AV_HWDEVICE_TYPE_CUDA:
486 return AV_PIX_FMT_CUDA;
487 case AV_HWDEVICE_TYPE_VDPAU:
488 return AV_PIX_FMT_VDPAU;
489 case AV_HWDEVICE_TYPE_OPENCL:
490 return AV_PIX_FMT_OPENCL;
491 case AV_HWDEVICE_TYPE_QSV:
492 return AV_PIX_FMT_QSV;
493 case AV_HWDEVICE_TYPE_D3D11VA:
494 return AV_PIX_FMT_D3D11;
495#if QT_FFMPEG_HAS_D3D12VA
496 case AV_HWDEVICE_TYPE_D3D12VA:
497 return AV_PIX_FMT_D3D12;
499 case AV_HWDEVICE_TYPE_DXVA2:
500 return AV_PIX_FMT_DXVA2_VLD;
501 case AV_HWDEVICE_TYPE_DRM:
502 return AV_PIX_FMT_DRM_PRIME;
503#if QT_FFMPEG_HAS_VULKAN
504 case AV_HWDEVICE_TYPE_VULKAN:
505 return AV_PIX_FMT_VULKAN;
508 return AV_PIX_FMT_NONE;
514 QScopeGuard freeData([&sideData]() { av_free(sideData.data); });
515#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
516 AVPacketSideData *
result = av_packet_side_data_add(
517 &
stream->codecpar->coded_side_data,
518 &
stream->codecpar->nb_coded_side_data,
532 qWarning() <<
"Adding stream side data is not supported for FFmpeg < 6.1";
542#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
543 return av_packet_side_data_get(
stream->codecpar->coded_side_data,
548 const auto found = std::find_if(
stream->side_data,
end, checkType);
556 SwrContext *resampler =
nullptr;
557#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
558 resampler = swr_alloc_set_opts(
nullptr,
559 outputFormat.channelLayoutMask,
562 inputFormat.channelLayoutMask,
569#if QT_FFMPEG_SWR_CONST_CH_LAYOUT
570 using AVChannelLayoutPrm =
const AVChannelLayout*;
572 using AVChannelLayoutPrm = AVChannelLayout*;
575 swr_alloc_set_opts2(&resampler,
592 case AVCOL_TRC_BT709:
594 case AVCOL_TRC_BT1361_ECG:
595 case AVCOL_TRC_BT2020_10:
596 case AVCOL_TRC_BT2020_12:
597 case AVCOL_TRC_SMPTE240M:
599 case AVCOL_TRC_GAMMA22:
600 case AVCOL_TRC_SMPTE428:
601 case AVCOL_TRC_IEC61966_2_1:
602 case AVCOL_TRC_IEC61966_2_4:
604 case AVCOL_TRC_GAMMA28:
606 case AVCOL_TRC_SMPTE170M:
608 case AVCOL_TRC_LINEAR:
610 case AVCOL_TRC_SMPTE2084:
612 case AVCOL_TRC_ARIB_STD_B67:
621bool isCVFormatSupported(uint32_t cvFormat)
623 return av_map_videotoolbox_format_to_pixfmt(cvFormat) != AV_PIX_FMT_NONE;
626std::string cvFormatToString(uint32_t cvFormat)
628 auto formatDescIt = std::make_reverse_iterator(
reinterpret_cast<const char *
>(&cvFormat));
629 return std::string(formatDescIt - 4, formatDescIt);
static const std::vector< AVHWDeviceType > & decodingDeviceTypes()
static const std::vector< AVHWDeviceType > & encodingDeviceTypes()
virtual int type() const
Returns the type of an item as an int.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString first(qsizetype n) const &
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QSet< QString >::iterator it
constexpr AVScore BestAVScore
const AVCodec * findAVEncoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
bool isHwPixelFormat(AVPixelFormat format)
bool isAVCodecExperimental(const AVCodec *codec)
AVPacketSideData * addStreamSideData(AVStream *stream, AVPacketSideData sideData)
bool isAVFormatSupported(const AVCodec *codec, PixelOrSampleFormat format)
const AVCodec * findAVDecoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
bool isSwPixelFormat(AVPixelFormat format)
constexpr AVScore DefaultAVScore
bool hasAVFormat(const Format *fmts, Format format)
Format findAVFormat(const Format *fmts, const Predicate &predicate)
QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc)
void applyExperimentalCodecOptions(const AVCodec *codec, AVDictionary **opts)
constexpr AVScore NotSuitableAVScore
SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat, const AVAudioFormat &outputFormat)
AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType)
std::unique_ptr< SwrContext, AVDeleter< decltype(&swr_free), &swr_free > > SwrContextUPtr
const AVPacketSideData * streamSideData(const AVStream *stream, AVPacketSideDataType type)
Combined button and popup list for selecting options.
static org qtproject qt android multimedia QtVideoDeviceManager
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QDebug operator<<(QDebug dbg, const AVRational &value)
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLint GLsizei GLsizei GLenum format
QT_BEGIN_NAMESPACE Q_DECLARE_JNI_CLASS(Environment, "android/os/Environment")
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
static QInputDevice::DeviceType deviceType(const UINT cursorType)
if(qFloatDistance(a, b)<(1<< 7))
[0]