xref: /haiku/src/add-ons/media/plugins/ffmpeg/Utilities.h (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
1 /*
2  * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
3  * Copyright 2014, Colin Günther <coling@gmx.de>
4  * All rights reserved. Distributed under the terms of the GNU L-GPL license.
5  */
6 #ifndef UTILITIES_H
7 #define UTILITIES_H
8 
9 
10 /*! \brief This file contains functions to convert and calculate values from
11 		FFmpeg to Media Kit and vice versa.
12 */
13 
14 
15 #include <assert.h>
16 
17 #include <GraphicsDefs.h>
18 
19 extern "C" {
20 	#include "avcodec.h"
21 }
22 
23 
24 /*! \brief Converts FFmpeg notation of video aspect ratio into the Media Kits
25 		notation.
26 
27 	\see ConvertVideoAspectWidthAndHeightToAVCodecContext() for converting in
28 		the other direction.
29 
30 	\param contextIn An AVCodeContext structure of FFmpeg containing the values
31 		needed to calculate the Media Kit video aspect ratio.
32 		The following fields are used for the calculation:
33 			- AVCodecContext.sample_aspect_ratio.num (optional)
34 			- AVCodecContext.sample_aspect_ratio.den (optional)
35 			- AVCodecContext.width (must)
36 			- AVCodecContext.height (must)
37 	\param pixelWidthAspectOut On return contains Media Kits notation of the
38 		video aspect ratio width. E.g. 16:9 -> 16 is returned here
39 	\param pixelHeightAspectOut On return contains Media Kits notation of the
40 		video aspect ratio height. E.g. 16:9 -> 9 is returned here
41 */
42 inline void
43 ConvertAVCodecContextToVideoAspectWidthAndHeight(AVCodecContext& contextIn,
44 	uint16& pixelWidthAspectOut, uint16& pixelHeightAspectOut)
45 {
46 	assert(contextIn.sample_aspect_ratio.num >= 0);
47 	assert(contextIn.sample_aspect_ratio.den > 0);
48 	assert(contextIn.width > 0);
49 	assert(contextIn.height > 0);
50 
51 	// The following code is based on code originally located in
52 	// AVFormatReader::Stream::Init() and thus should be copyrighted to Stephan
53 	// Aßmus
54 	AVRational pixelAspectRatio;
55 
56 	if (contextIn.sample_aspect_ratio.num == 0) {
57 		// AVCodecContext doesn't contain a video aspect ratio, so calculate it
58 		// ourselve based solely on the video dimensions
59 		av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, contextIn.width,
60 			contextIn.height, 1024 * 1024);
61 
62 		pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
63 		pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
64 		return;
65 	}
66 
67 	// AVCodecContext contains a video aspect ratio, so use it
68 	av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den,
69 		contextIn.width * contextIn.sample_aspect_ratio.num,
70 		contextIn.height * contextIn.sample_aspect_ratio.den,
71 		1024 * 1024);
72 
73 	pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
74 	pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
75 }
76 
77 
78 /*!	\brief Converts the Media Kits notation of video aspect ratio into FFmpegs
79 		notation.
80 
81 	\see ConvertAVCodecContextToVideoAspectWidthAndHeight() for converting in
82 		the other direction.
83 
84 	\param pixelWidthAspectIn Contains Media Kits notation of the video aspect
85 		ratio width. E.g. 16:9 -> 16 is passed here.
86 	\param pixelHeightAspectIn Contains Media Kits notation of the video aspect
87 		ratio height. E.g. 16:9 -> 9 is passed here.
88 	\param contextInOut	An AVCodecContext structure of FFmpeg.
89 		On input must contain the following fields already initialized
90 		otherwise the behaviour is undefined:
91 			- AVCodecContext.width (must)
92 			- AVCodecContext.height (must)
93 		On output contains converted values in the following fields (other
94 		fields stay as they were on input):
95 			- AVCodecContext.sample_aspect_ratio.num
96 			- AVCodecContext.sample_aspect_ratio.den
97 */
98 inline void
99 ConvertVideoAspectWidthAndHeightToAVCodecContext(uint16 pixelWidthAspectIn,
100 	uint16 pixelHeightAspectIn, AVCodecContext& contextInOut)
101 {
102 	assert(pixelWidthAspectIn > 0);
103 	assert(pixelHeightAspectIn > 0);
104 	assert(contextInOut.width > 0);
105 	assert(contextInOut.height > 0);
106 
107 	AVRational pureVideoDimensionAspectRatio;
108 	av_reduce(&pureVideoDimensionAspectRatio.num,
109 		&pureVideoDimensionAspectRatio.den, contextInOut.width,
110 		contextInOut.height, 1024 * 1024);
111 
112 	if (pureVideoDimensionAspectRatio.num == pixelWidthAspectIn
113 		&& pureVideoDimensionAspectRatio.den == pixelHeightAspectIn) {
114 		// The passed Media Kit pixel aspect ratio equals the video dimension
115 		// aspect ratio. Set sample_aspect_ratio to "ignore".
116 		contextInOut.sample_aspect_ratio.num = 0;
117 		contextInOut.sample_aspect_ratio.den = 1;
118 		return;
119 	}
120 
121 	av_reduce(&contextInOut.sample_aspect_ratio.num,
122 		&contextInOut.sample_aspect_ratio.den,
123 		contextInOut.height * pixelWidthAspectIn,
124 		contextInOut.width * pixelHeightAspectIn,
125 		1024 * 1024);
126 }
127 
128 
129 /*! \brief Calculates bytes per row for a video frame.
130 
131 	\param colorSpace The Media Kit color space the video frame uses.
132 	\param videoWidth The width of the video frame.
133 
134 	\returns bytes per video frame row
135 	\returns Zero, when bytes per video frame cannot be calculated.
136 */
137 inline uint32
138 CalculateBytesPerRowWithColorSpaceAndVideoWidth(color_space colorSpace, int videoWidth)
139 {
140 	assert(videoWidth >= 0);
141 
142 	const uint32 kBytesPerRowUnknown = 0;
143 	size_t bytesPerPixel;
144 	size_t rowAlignment;
145 
146 	if (get_pixel_size_for(colorSpace, &bytesPerPixel, &rowAlignment, NULL) != B_OK)
147 		return kBytesPerRowUnknown;
148 
149 	uint32 bytesPerRow = bytesPerPixel * videoWidth;
150 	uint32 numberOfUnalignedBytes = bytesPerRow % rowAlignment;
151 
152 	if (numberOfUnalignedBytes == 0)
153 		return bytesPerRow;
154 
155 	uint32 numberOfBytesNeededForAlignment = rowAlignment - numberOfUnalignedBytes;
156 	bytesPerRow += numberOfBytesNeededForAlignment;
157 
158 	return bytesPerRow;
159 }
160 
161 
162 /*! \brief Converts FFmpeg notation of video frame rate into the Media Kits
163 		notation.
164 
165 	\see ConvertAVCodecContextToVideoFrameRate() for converting in the other
166 		direction.
167 
168 	\param contextIn An AVCodeContext structure of FFmpeg containing the values
169 		needed to calculate the Media Kit video frame rate.
170 		The following fields are used for the calculation:
171 			- AVCodecContext.time_base.num (must)
172 			- AVCodecContext.time_base.den (must)
173 			- AVCodecContext.ticks_per_frame (must)
174 	\param frameRateOut On return contains Media Kits notation of the video
175 		frame rate.
176 */
177 inline void
178 ConvertAVCodecContextToVideoFrameRate(AVCodecContext& contextIn, float& frameRateOut)
179 {
180 	// assert that av_q2d(contextIn.time_base) > 0 and computable
181 	assert(contextIn.time_base.num > 0);
182 	assert(contextIn.time_base.den > 0);
183 
184 	// The following code is based on private get_fps() function of FFmpeg's
185 	// ratecontrol.c:
186 	// https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2012-April/049280.html
187 	double possiblyInterlacedFrameRate = 1.0 / av_q2d(contextIn.time_base);
188 	double numberOfInterlacedFramesPerFullFrame = FFMAX(contextIn.ticks_per_frame, 1);
189 
190 	frameRateOut
191 		= possiblyInterlacedFrameRate / numberOfInterlacedFramesPerFullFrame;
192 }
193 
194 
195 /*!	\brief Converts the Media Kits notation of video frame rate to FFmpegs
196 	notation.
197 
198 	\see ConvertAVCodecContextToVideoFrameRate() for converting in the other
199 		direction.
200 
201 	\param frameRateIn Contains Media Kits notation of the video frame rate
202 		that will be converted into FFmpegs notation. Must be greater than
203 		zero.
204 	\param contextOut An AVCodecContext structure of FFmpeg.
205 		On output contains converted values in the following fields (other
206 		fields stay as they were on input):
207 			- AVCodecContext.time_base.num
208 			- AVCodecContext.time_base.den
209 			- AVCodecContext.ticks_per_frame is set to 1
210 */
211 inline void
212 ConvertVideoFrameRateToAVCodecContext(float frameRateIn,
213 	AVCodecContext& contextOut)
214 {
215 	assert(frameRateIn > 0);
216 
217 	contextOut.ticks_per_frame = 1;
218 	contextOut.time_base = av_d2q(1.0 / frameRateIn, 1024);
219 }
220 
221 
222 /*!	\brief Converts the Media Kits notation of an audio sample format to
223 		FFmpegs notation.
224 
225 	\see ConvertAVSampleFormatToRawAudioFormat() for converting in the other
226 		direction.
227 
228 	\param rawAudioFormatIn Contains Media Kits notation of an audio sample
229 		format that will be converted into FFmpegs notation.
230 	\param sampleFormatOut On output contains FFmpegs notation of the passed
231 		audio sample format. Might return AV_SAMPLE_FMT_NONE if there is no
232 		conversion path.
233 */
234 inline void
235 ConvertRawAudioFormatToAVSampleFormat(uint32 rawAudioFormatIn,
236 	AVSampleFormat& sampleFormatOut)
237 {
238 	switch (rawAudioFormatIn) {
239 		case media_raw_audio_format::B_AUDIO_FLOAT:
240 			sampleFormatOut = AV_SAMPLE_FMT_FLT;
241 			return;
242 
243 		case media_raw_audio_format::B_AUDIO_DOUBLE:
244 			sampleFormatOut = AV_SAMPLE_FMT_DBL;
245 			return;
246 
247 		case media_raw_audio_format::B_AUDIO_INT:
248 			sampleFormatOut = AV_SAMPLE_FMT_S32;
249 			return;
250 
251 		case media_raw_audio_format::B_AUDIO_SHORT:
252 			sampleFormatOut = AV_SAMPLE_FMT_S16;
253 			return;
254 
255 		case media_raw_audio_format::B_AUDIO_UCHAR:
256 			sampleFormatOut = AV_SAMPLE_FMT_U8;
257 			return;
258 
259 		default:
260 			// Silence compiler warnings about unhandled enumeration values.
261 			break;
262 	}
263 
264 	sampleFormatOut = AV_SAMPLE_FMT_NONE;
265 }
266 
267 
268 /*!	\brief Converts FFmpegs notation of an audio sample format to the Media
269 		Kits notation.
270 
271 	\see ConvertAVSampleFormatToRawAudioFormat() for converting in the other
272 		direction.
273 
274 	\param sampleFormatIn Contains FFmpegs notation of an audio sample format
275 		that will be converted into the Media Kits notation.
276 	\param rawAudioFormatOut On output contains Media Kits notation of the
277 		passed audio sample format. Might return 0 if there is no conversion
278 		path.
279 */
280 inline void
281 ConvertAVSampleFormatToRawAudioFormat(AVSampleFormat sampleFormatIn,
282 	uint32& rawAudioFormatOut)
283 {
284 	switch (sampleFormatIn) {
285 		case AV_SAMPLE_FMT_FLT:
286 		case AV_SAMPLE_FMT_FLTP:
287 			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_FLOAT;
288 			return;
289 
290 		case AV_SAMPLE_FMT_DBL:
291 		case AV_SAMPLE_FMT_DBLP:
292 			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_DOUBLE;
293 			return;
294 
295 		case AV_SAMPLE_FMT_S32:
296 		case AV_SAMPLE_FMT_S32P:
297 			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_INT;
298 			return;
299 
300 		case AV_SAMPLE_FMT_S16:
301 		case AV_SAMPLE_FMT_S16P:
302 			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_SHORT;
303 			return;
304 
305 		case AV_SAMPLE_FMT_U8:
306 		case AV_SAMPLE_FMT_U8P:
307 			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_UCHAR;
308 			return;
309 
310 		default:
311 			// Silence compiler warnings about unhandled enumeration values.
312 			break;
313 	}
314 
315 	const uint32 kBAudioNone = 0;
316 	rawAudioFormatOut = kBAudioNone;
317 }
318 
319 
320 #endif // UTILITIES_H
321