mirror of
				https://github.com/telegramdesktop/tdesktop
				synced 2025-10-25 14:58:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			197 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			197 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 <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;
 | |
| 
 | |
| 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<AVPacket*>(_data);
 | |
| 	}
 | |
| 	[[nodiscard]] const AVPacket &fields() const {
 | |
| 		return *reinterpret_cast<const AVPacket*>(_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<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>;
 | |
| [[nodiscard]] CodecPointer MakeCodecPointer(not_null<AVStream*> stream);
 | |
| 
 | |
| 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(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<AVStream*> 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
 |