Revision control

++ // 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
++
++ //
++ // 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 <QtFFmpegMediaPluginImpl/private/qffmpegdefs_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegcodec_p.h>
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegavaudioformat_p.h>
++ #include <QtMultimedia/qvideoframeformat.h>
++ #include <QtMultimedia/private/qmultimedia_ranges_p.h>
++
++ #include <qstring.h>
++ #include <optional>
++
++ [[maybe_unused]] static inline bool operator==(const AVRational &lhs, const AVRational &rhs)
++ {
++ return lhs.num == rhs.num && lhs.den == rhs.den;
++ }
++
++ [[maybe_unused]] static inline bool operator!=(const AVRational &lhs, const AVRational &rhs)
++ {
++ return !(lhs == rhs);
++ }
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QFFmpeg
++ {
++
++ namespace ranges = QtMultimediaPrivate::ranges;
++
++ inline std::optional<qint64> mul(qint64 a, AVRational b)
++ {
++ if (b.den == 0)
++ return {};
++
++ auto multiplyAndRound = [](qint64 a, AVRational b) { //
++ return (a * b.num + b.den / 2) / b.den;
++ };
++
++ if (a < 0)
++ return -multiplyAndRound(-a, b);
++ else
++ return multiplyAndRound(a, b);
++ }
++
++ 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);
++ }
++
++ template <typename Float>
++ inline std::optional<Float> toFloat(AVRational r)
++ {
++ return r.den != 0 ? Float(r.num) / Float(r.den) : std::optional<Float>{};
++ }
++
++ inline std::optional<qreal> toReal(AVRational r)
++ {
++ return toFloat<qreal>(r);
++ }
++
++ 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);
++ }
++
++ inline void setAVFrameTime(AVFrame &frame, int64_t pts, const AVRational &timeBase)
++ {
++ frame.pts = pts;
++ #if QT_FFMPEG_HAS_FRAME_TIME_BASE
++ frame.time_base = timeBase;
++ #else
++ Q_UNUSED(timeBase);
++ #endif
++ }
++
++ inline void getAVFrameTime(const AVFrame &frame, int64_t &pts, AVRational &timeBase)
++ {
++ pts = frame.pts;
++ #if QT_FFMPEG_HAS_FRAME_TIME_BASE
++ timeBase = frame.time_base;
++ #else
++ timeBase = { 0, 1 };
++ #endif
++ }
++
++ inline int64_t getAVFrameDuration(const AVFrame &frame)
++ {
++ #if QT_FFMPEG_HAS_FRAME_DURATION
++ return frame.duration;
++ #else
++ return frame.pkt_duration;
++ #endif
++ }
++
++ struct AVDictionaryHolder
++ {
++ AVDictionary *opts = nullptr;
++
++ operator AVDictionary **() { return &opts; }
++
++ AVDictionaryHolder() = default;
++
++ Q_DISABLE_COPY(AVDictionaryHolder)
++
++ AVDictionaryHolder(AVDictionaryHolder &&other) noexcept
++ : opts(std::exchange(other.opts, nullptr))
++ {
++ }
++
++ ~AVDictionaryHolder()
++ {
++ if (opts)
++ av_dict_free(&opts);
++ }
++ };
++
++ struct AVFreeDeleter
++ {
++ template <typename T>
++ void operator()(T *object) const
++ {
++ if (object)
++ av_free(object);
++ }
++ };
++
++ template <auto F>
++ struct AVDeleter
++ {
++ template <typename T>
++ void operator()(T *object) const
++ {
++ using FunctionType = decltype(F);
++
++ static_assert(std::is_invocable_v<FunctionType, T *>
++ || std::is_invocable_v<FunctionType, T **>,
++ "F must be invocable with either T* or T**");
++
++ if constexpr (std::is_invocable_v<FunctionType, T **>) {
++ if (object)
++ F(&object);
++ } else
++ F(object);
++ }
++ };
++
++ using AVFrameUPtr = std::unique_ptr<AVFrame, AVDeleter<av_frame_free>>;
++
++ inline AVFrameUPtr makeAVFrame()
++ {
++ return AVFrameUPtr(av_frame_alloc());
++ }
++
++ using AVPacketUPtr = std::unique_ptr<AVPacket, AVDeleter<av_packet_free>>;
++ using AVCodecContextUPtr = std::unique_ptr<AVCodecContext, AVDeleter<avcodec_free_context>>;
++ using AVBufferUPtr = std::unique_ptr<AVBufferRef, AVDeleter<av_buffer_unref>>;
++ using AVHWFramesConstraintsUPtr =
++ std::unique_ptr<AVHWFramesConstraints, AVDeleter<av_hwframe_constraints_free>>;
++ using SwrContextUPtr = std::unique_ptr<SwrContext, AVDeleter<swr_free>>;
++ using SwsContextUPtr = std::unique_ptr<SwsContext, AVDeleter<sws_freeContext>>;
++
++ void freeAVIOContext(AVIOContext *&);
++
++ struct AVDemuxerContextDeleter
++ {
++ void operator()(AVFormatContext *ctx) const
++ {
++ if (!ctx)
++ return;
++
++ if (ctx->flags & AVFMT_FLAG_CUSTOM_IO)
++ freeAVIOContext(ctx->pb);
++
++ avformat_close_input(&ctx);
++ }
++ };
++
++ using AVDemuxerContextUPtr = std::unique_ptr<AVFormatContext, AVDemuxerContextDeleter>;
++ using AVMuxerContextUPtr = std::unique_ptr<AVFormatContext, AVDeleter<avformat_free_context>>;
++
++ bool isAVFormatSupported(const Codec &, const PixelOrSampleFormat &);
++
++ // Search for the first element in the range that satisfies the predicate
++ // The predicate is evaluated for each value in the range until it returns
++ // true, and the corresponding value is returned. If no match is found,
++ // std::nullopt is returned.
++ template <typename Value, typename Predicate>
++ std::optional<Value> findIf(QSpan<const Value> range, const Predicate &predicate)
++ {
++ const auto value = ranges::find_if(range, predicate);
++ if (value == range.end())
++ return {};
++ return *value;
++ }
++
++ // Search the codec's pixel formats for a format that matches the predicate.
++ // If no pixel format is found, repeat the search through the pixel formats
++ // of all the codec's hardware configs. If no matching pixel format is found,
++ // std::nullopt is returned. The predicate is evaluated once for each pixel
++ // format until the predicate returns true.
++ template <typename Predicate>
++ std::optional<AVPixelFormat> findAVPixelFormat(const Codec &codec, const Predicate &predicate)
++ {
++ if (codec.type() != AVMEDIA_TYPE_VIDEO)
++ return {};
++
++ const auto pixelFormats = codec.pixelFormats();
++
++ if (const auto format = findIf(pixelFormats, predicate))
++ return format;
++
++ // No matching pixel format was found. Check the pixel format
++ // of the codec's hardware config.
++ for (const AVCodecHWConfig *const config : codec.hwConfigs()) {
++ const AVPixelFormat format = config->pix_fmt;
++
++ if (format == AV_PIX_FMT_NONE)
++ continue;
++
++ if (predicate(format))
++ return format;
++ }
++ return {};
++ }
++
++ // Evaluate the function for each of the codec's pixel formats and each of
++ // the pixel formats supported by the codec's hardware configs.
++ template <typename Function>
++ void forEachAVPixelFormat(const Codec &codec, const Function &function)
++ {
++ findAVPixelFormat(codec, [&function](AVPixelFormat format) {
++ function(format);
++ return false; // Evaluate the function for all pixel formats
++ });
++ }
++
++ template <typename ValueT, typename ScoreT = AVScore>
++ struct ValueAndScore
++ {
++ ValueT value;
++ ScoreT score = std::numeric_limits<ScoreT>::min();
++ };
++
++ template <typename ValueRange, typename CalculateScore,
++ typename Value = QtMultimediaPrivate::ranges::range_value_t<ValueRange>,
++ typename ScoreType = std::invoke_result_t<CalculateScore, Value>>
++ std::optional<ValueAndScore<Value, ScoreType>>
++ findBestAVValueWithScore(ValueRange &&values, const CalculateScore &calculateScore)
++ {
++ static_assert(std::is_invocable_v<CalculateScore, Value>);
++
++ std::optional<ValueAndScore<Value, ScoreType>> result;
++ for (const Value &val : values) {
++ const ScoreType score = calculateScore(val);
++ if (score == std::numeric_limits<ScoreType>::min())
++ continue;
++
++ if (!result || score > result->score)
++ result = { val, score };
++
++ if (result->score == std::numeric_limits<ScoreType>::max())
++ return result;
++ }
++
++ return result;
++ }
++
++ template <typename ValueRange, typename CalculateScore,
++ typename Value = QtMultimediaPrivate::ranges::range_value_t<ValueRange>>
++ std::optional<Value> findBestAVValue(ValueRange &&values, const CalculateScore &calculateScore)
++ {
++ auto optionalValueAndScore = findBestAVValueWithScore(values, calculateScore);
++ if (optionalValueAndScore)
++ return optionalValueAndScore->value;
++ else
++ return std::nullopt;
++ }
++
++ bool isHwPixelFormat(AVPixelFormat format);
++
++ inline bool isSwPixelFormat(AVPixelFormat format)
++ {
++ return !isHwPixelFormat(format);
++ }
++
++ void applyExperimentalCodecOptions(const Codec &codec, AVDictionary **opts);
++
++ AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType);
++
++ AVPacketSideData *addStreamSideData(AVStream *stream, AVPacketSideData sideData);
++
++ const AVPacketSideData *streamSideData(const AVStream *stream, AVPacketSideDataType type);
++
++ SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat,
++ const AVAudioFormat &outputFormat);
++
++ QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc);
++
++ AVColorTransferCharacteristic toAvColorTransfer(QVideoFrameFormat::ColorTransfer colorTrc);
++
++ QVideoFrameFormat::ColorSpace fromAvColorSpace(AVColorSpace colorSpace);
++
++ AVColorSpace toAvColorSpace(QVideoFrameFormat::ColorSpace colorSpace);
++
++ QVideoFrameFormat::ColorRange fromAvColorRange(AVColorRange colorRange);
++
++ AVColorRange toAvColorRange(QVideoFrameFormat::ColorRange colorRange);
++
++ AVHWDeviceContext *avFrameDeviceContext(const AVFrame *frame);
++
++ SwsContextUPtr createSwsContext(const QSize &srcSize, AVPixelFormat srcPixFmt, const QSize &dstSize,
++ AVPixelFormat dstPixFmt, SwsFlags conversionType = SWS_BICUBIC);
++
++ QSize frameSize(const AVFrame &);
++ QSize frameSize(const AVFrame *);
++ QSize frameSize(const AVFrameUPtr &);
++
++ #ifdef Q_OS_DARWIN
++ bool isCVFormatSupported(uint32_t format);
++
++ std::string cvFormatToString(uint32_t format);
++
++ #endif
++
++ enum class AVError : int {
++ Success = 0,
++ };
++
++ enum class AVCodecCapabilities : uint64_t { };
++ enum class AVPixelFormatFlags : uint64_t { };
++ enum class AVHwConfigMethods : uint64_t { };
++
++ QString toString(AVCodecCapabilities capabilities);
++ QString toString(AVPixelFormatFlags flags);
++ QString toString(AVHwConfigMethods methods);
++
++ } // namespace QFFmpeg
++
++ QDebug operator<<(QDebug, const AVRational &);
++ QDebug operator<<(QDebug, const AVDictionary &);
++ QDebug operator<<(QDebug, const QFFmpeg::AVDictionaryHolder &);
++ QDebug operator<<(QDebug, QFFmpeg::AVError);
++ QDebug operator<<(QDebug, AVPixelFormat);
++ QDebug operator<<(QDebug, AVHWDeviceType);
++ QDebug operator<<(QDebug, AVMediaType);
++ QDebug operator<<(QDebug, AVCodecID);
++ QDebug operator<<(QDebug, const AVOutputFormat *);
++ QDebug operator<<(QDebug, const AVInputFormat *);
++ QDebug operator<<(QDebug, QFFmpeg::AVCodecCapabilities);
++ QDebug operator<<(QDebug, QFFmpeg::AVPixelFormatFlags);
++ QDebug operator<<(QDebug, QFFmpeg::AVHwConfigMethods);
++
++ #if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
++ QDebug operator<<(QDebug, const AVChannelLayout &);
++ #endif
++
++ QT_END_NAMESPACE
++
++ #endif