1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 #include "base/bytes.h"
11 #include "base/algorithm.h"
12 
13 #include <crl/crl_time.h>
14 
15 #include <QSize>
16 #include <QString>
17 
18 extern "C" {
19 #include <libavcodec/avcodec.h>
20 #include <libavformat/avformat.h>
21 #include <libswscale/swscale.h>
22 } // extern "C"
23 
24 class QImage;
25 
26 namespace FFmpeg {
27 
28 inline constexpr auto kPixelBytesSize = 4;
29 inline constexpr auto kAVBlockSize = 4096; // 4Kb for ffmpeg blocksize
30 
31 constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE };
32 constexpr auto kNormalAspect = AVRational{ 1, 1 };
33 
34 class AvErrorWrap {
35 public:
_code(code)36 	AvErrorWrap(int code = 0) : _code(code) {
37 	}
38 
failed()39 	[[nodiscard]] bool failed() const {
40 		return (_code < 0);
41 	}
42 	[[nodiscard]] explicit operator bool() const {
43 		return failed();
44 	}
45 
code()46 	[[nodiscard]] int code() const {
47 		return _code;
48 	}
49 
text()50 	[[nodiscard]] QString text() const {
51 		char string[AV_ERROR_MAX_STRING_SIZE] = { 0 };
52 		return QString::fromUtf8(av_make_error_string(
53 			string,
54 			sizeof(string),
55 			_code));
56 	}
57 
58 private:
59 	int _code = 0;
60 
61 };
62 
63 class Packet {
64 public:
65 	Packet() = default;
Packet(Packet && other)66 	Packet(Packet &&other) : _data(base::take(other._data)) {
67 	}
68 	Packet &operator=(Packet &&other) {
69 		if (this != &other) {
70 			release();
71 			_data = base::take(other._data);
72 		}
73 		return *this;
74 	}
~Packet()75 	~Packet() {
76 		release();
77 	}
78 
fields()79 	[[nodiscard]] AVPacket &fields() {
80 		if (!_data) {
81 			_data = av_packet_alloc();
82 		}
83 		return *_data;
84 	}
fields()85 	[[nodiscard]] const AVPacket &fields() const {
86 		if (!_data) {
87 			_data = av_packet_alloc();
88 		}
89 		return *_data;
90 	}
91 
empty()92 	[[nodiscard]] bool empty() const {
93 		return !_data || !fields().data;
94 	}
release()95 	void release() {
96 		av_packet_free(&_data);
97 	}
98 
99 private:
100 	mutable AVPacket *_data = nullptr;
101 
102 };
103 
104 struct IODeleter {
105 	void operator()(AVIOContext *value);
106 };
107 using IOPointer = std::unique_ptr<AVIOContext, IODeleter>;
108 [[nodiscard]] IOPointer MakeIOPointer(
109 	void *opaque,
110 	int(*read)(void *opaque, uint8_t *buffer, int bufferSize),
111 	int(*write)(void *opaque, uint8_t *buffer, int bufferSize),
112 	int64_t(*seek)(void *opaque, int64_t offset, int whence));
113 
114 struct FormatDeleter {
115 	void operator()(AVFormatContext *value);
116 };
117 using FormatPointer = std::unique_ptr<AVFormatContext, FormatDeleter>;
118 [[nodiscard]] FormatPointer MakeFormatPointer(
119 	void *opaque,
120 	int(*read)(void *opaque, uint8_t *buffer, int bufferSize),
121 	int(*write)(void *opaque, uint8_t *buffer, int bufferSize),
122 	int64_t(*seek)(void *opaque, int64_t offset, int whence));
123 
124 struct CodecDeleter {
125 	void operator()(AVCodecContext *value);
126 };
127 using CodecPointer = std::unique_ptr<AVCodecContext, CodecDeleter>;
128 [[nodiscard]] CodecPointer MakeCodecPointer(not_null<AVStream*> stream);
129 
130 struct FrameDeleter {
131 	void operator()(AVFrame *value);
132 };
133 using FramePointer = std::unique_ptr<AVFrame, FrameDeleter>;
134 [[nodiscard]] FramePointer MakeFramePointer();
135 [[nodiscard]] bool FrameHasData(AVFrame *frame);
136 void ClearFrameMemory(AVFrame *frame);
137 
138 struct SwscaleDeleter {
139 	QSize srcSize;
140 	int srcFormat = int(AV_PIX_FMT_NONE);
141 	QSize dstSize;
142 	int dstFormat = int(AV_PIX_FMT_NONE);
143 
144 	void operator()(SwsContext *value);
145 };
146 using SwscalePointer = std::unique_ptr<SwsContext, SwscaleDeleter>;
147 [[nodiscard]] SwscalePointer MakeSwscalePointer(
148 	QSize srcSize,
149 	int srcFormat,
150 	QSize dstSize,
151 	int dstFormat, // This field doesn't take part in caching!
152 	SwscalePointer *existing = nullptr);
153 [[nodiscard]] SwscalePointer MakeSwscalePointer(
154 	not_null<AVFrame*> frame,
155 	QSize resize,
156 	SwscalePointer *existing = nullptr);
157 
158 void LogError(QLatin1String method);
159 void LogError(QLatin1String method, FFmpeg::AvErrorWrap error);
160 
161 [[nodiscard]] crl::time PtsToTime(int64_t pts, AVRational timeBase);
162 // Used for full duration conversion.
163 [[nodiscard]] crl::time PtsToTimeCeil(int64_t pts, AVRational timeBase);
164 [[nodiscard]] int64_t TimeToPts(crl::time time, AVRational timeBase);
165 [[nodiscard]] crl::time PacketPosition(
166 	const FFmpeg::Packet &packet,
167 	AVRational timeBase);
168 [[nodiscard]] crl::time PacketDuration(
169 	const FFmpeg::Packet &packet,
170 	AVRational timeBase);
171 [[nodiscard]] int DurationByPacket(
172 	const FFmpeg::Packet &packet,
173 	AVRational timeBase);
174 [[nodiscard]] int ReadRotationFromMetadata(not_null<AVStream*> stream);
175 [[nodiscard]] AVRational ValidateAspectRatio(AVRational aspect);
176 [[nodiscard]] bool RotationSwapWidthHeight(int rotation);
177 [[nodiscard]] QSize CorrectByAspect(QSize size, AVRational aspect);
178 
179 [[nodiscard]] bool GoodStorageForFrame(const QImage &storage, QSize size);
180 [[nodiscard]] QImage CreateFrameStorage(QSize size);
181 
182 void UnPremultiply(QImage &to, const QImage &from);
183 void PremultiplyInplace(QImage &image);
184 
185 } // namespace FFmpeg
186