/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/bytes.h" #include #include #include extern "C" { #include #include #include } // extern "C" class QImage; namespace FFmpeg { inline constexpr auto kPixelBytesSize = 4; inline constexpr auto kAVBlockSize = 4096; // 4Kb for ffmpeg blocksize constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE }; constexpr auto kNormalAspect = AVRational{ 1, 1 }; class AvErrorWrap { public: AvErrorWrap(int code = 0) : _code(code) { } [[nodiscard]] bool failed() const { return (_code < 0); } [[nodiscard]] explicit operator bool() const { return failed(); } [[nodiscard]] int code() const { return _code; } [[nodiscard]] QString text() const { char string[AV_ERROR_MAX_STRING_SIZE] = { 0 }; return QString::fromUtf8(av_make_error_string( string, sizeof(string), _code)); } private: int _code = 0; }; class Packet { public: Packet() { setEmpty(); } Packet(const AVPacket &data) { bytes::copy(_data, bytes::object_as_span(&data)); } Packet(Packet &&other) { bytes::copy(_data, other._data); if (!other.empty()) { other.release(); } } Packet &operator=(Packet &&other) { if (this != &other) { av_packet_unref(&fields()); bytes::copy(_data, other._data); if (!other.empty()) { other.release(); } } return *this; } ~Packet() { av_packet_unref(&fields()); } [[nodiscard]] AVPacket &fields() { return *reinterpret_cast(_data); } [[nodiscard]] const AVPacket &fields() const { return *reinterpret_cast(_data); } [[nodiscard]] bool empty() const { return !fields().data; } void release() { setEmpty(); } private: void setEmpty() { auto &native = fields(); av_init_packet(&native); native.data = nullptr; native.size = 0; } alignas(alignof(AVPacket)) bytes::type _data[sizeof(AVPacket)]; }; struct IODeleter { void operator()(AVIOContext *value); }; using IOPointer = std::unique_ptr; [[nodiscard]] IOPointer MakeIOPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), int(*write)(void *opaque, uint8_t *buffer, int bufferSize), int64_t(*seek)(void *opaque, int64_t offset, int whence)); struct FormatDeleter { void operator()(AVFormatContext *value); }; using FormatPointer = std::unique_ptr; [[nodiscard]] FormatPointer MakeFormatPointer( void *opaque, int(*read)(void *opaque, uint8_t *buffer, int bufferSize), int(*write)(void *opaque, uint8_t *buffer, int bufferSize), int64_t(*seek)(void *opaque, int64_t offset, int whence)); struct CodecDeleter { void operator()(AVCodecContext *value); }; using CodecPointer = std::unique_ptr; [[nodiscard]] CodecPointer MakeCodecPointer(not_null stream); struct FrameDeleter { void operator()(AVFrame *value); }; using FramePointer = std::unique_ptr; [[nodiscard]] FramePointer MakeFramePointer(); [[nodiscard]] bool FrameHasData(AVFrame *frame); void ClearFrameMemory(AVFrame *frame); struct SwscaleDeleter { QSize srcSize; int srcFormat = int(AV_PIX_FMT_NONE); QSize dstSize; int dstFormat = int(AV_PIX_FMT_NONE); void operator()(SwsContext *value); }; using SwscalePointer = std::unique_ptr; [[nodiscard]] SwscalePointer MakeSwscalePointer( QSize srcSize, int srcFormat, QSize dstSize, int dstFormat, // This field doesn't take part in caching! SwscalePointer *existing = nullptr); [[nodiscard]] SwscalePointer MakeSwscalePointer( not_null frame, QSize resize, SwscalePointer *existing = nullptr); void LogError(QLatin1String method); void LogError(QLatin1String method, FFmpeg::AvErrorWrap error); [[nodiscard]] crl::time PtsToTime(int64_t pts, AVRational timeBase); // Used for full duration conversion. [[nodiscard]] crl::time PtsToTimeCeil(int64_t pts, AVRational timeBase); [[nodiscard]] int64_t TimeToPts(crl::time time, AVRational timeBase); [[nodiscard]] crl::time PacketPosition( const FFmpeg::Packet &packet, AVRational timeBase); [[nodiscard]] crl::time PacketDuration( const FFmpeg::Packet &packet, AVRational timeBase); [[nodiscard]] int DurationByPacket( const FFmpeg::Packet &packet, AVRational timeBase); [[nodiscard]] int ReadRotationFromMetadata(not_null stream); [[nodiscard]] AVRational ValidateAspectRatio(AVRational aspect); [[nodiscard]] bool RotationSwapWidthHeight(int rotation); [[nodiscard]] QSize CorrectByAspect(QSize size, AVRational aspect); [[nodiscard]] bool GoodStorageForFrame(const QImage &storage, QSize size); [[nodiscard]] QImage CreateFrameStorage(QSize size); void UnPremultiply(QImage &to, const QImage &from); void PremultiplyInplace(QImage &image); } // namespace FFmpeg