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