tdesktop/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h

193 lines
4.9 KiB
C++

/*
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 "base/algorithm.h"
#include <crl/crl_time.h>
#include <QSize>
#include <QString>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
} // 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() = default;
Packet(Packet &&other) : _data(base::take(other._data)) {
}
Packet &operator=(Packet &&other) {
if (this != &other) {
release();
_data = base::take(other._data);
}
return *this;
}
~Packet() {
release();
}
[[nodiscard]] AVPacket &fields() {
if (!_data) {
_data = av_packet_alloc();
}
return *_data;
}
[[nodiscard]] const AVPacket &fields() const {
if (!_data) {
_data = av_packet_alloc();
}
return *_data;
}
[[nodiscard]] bool empty() const {
return !_data || !fields().data;
}
void release() {
av_packet_free(&_data);
}
private:
mutable AVPacket *_data = nullptr;
};
struct IODeleter {
void operator()(AVIOContext *value);
};
using IOPointer = std::unique_ptr<AVIOContext, IODeleter>;
[[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<AVFormatContext, FormatDeleter>;
[[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<AVCodecContext, CodecDeleter>;
struct CodecDescriptor {
not_null<AVStream*> stream;
bool hwAllowed = false;
};
[[nodiscard]] CodecPointer MakeCodecPointer(CodecDescriptor descriptor);
struct FrameDeleter {
void operator()(AVFrame *value);
};
using FramePointer = std::unique_ptr<AVFrame, FrameDeleter>;
[[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<SwsContext, SwscaleDeleter>;
[[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<AVFrame*> frame,
QSize resize,
SwscalePointer *existing = nullptr);
void LogError(const QString &method);
void LogError(const QString &method, FFmpeg::AvErrorWrap error);
[[nodiscard]] const AVCodec *FindDecoder(not_null<AVCodecContext*> context);
[[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<AVStream*> stream);
[[nodiscard]] AVRational ValidateAspectRatio(AVRational aspect);
[[nodiscard]] bool RotationSwapWidthHeight(int rotation);
[[nodiscard]] QSize TransposeSizeByRotation(QSize size, 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