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
21extern "C" {
22	#include "avcodec.h"
23}
24
25
26/*! \brief Structure used for passing AVPacket metadata through media_header::user_data. */
27struct avpacket_user_data {
28    int64_t pts;
29    int64_t dts;
30    int stream_index;
31    int flags;
32    int64_t duration;
33    int64_t pos;
34};
35
36#define AVPACKET_USER_DATA_TYPE 'ffav'
37
38
39/*! \brief Converts FFmpeg notation of video aspect ratio into the Media Kits
40		notation.
41
42	\see ConvertVideoAspectWidthAndHeightToAVCodecContext() for converting in
43		the other direction.
44
45	\param contextIn An AVCodeContext structure of FFmpeg containing the values
46		needed to calculate the Media Kit video aspect ratio.
47		The following fields are used for the calculation:
48			- AVCodecContext.sample_aspect_ratio.num (optional)
49			- AVCodecContext.sample_aspect_ratio.den (optional)
50			- AVCodecContext.width (must)
51			- AVCodecContext.height (must)
52	\param pixelWidthAspectOut On return contains Media Kits notation of the
53		video aspect ratio width. E.g. 16:9 -> 16 is returned here
54	\param pixelHeightAspectOut On return contains Media Kits notation of the
55		video aspect ratio height. E.g. 16:9 -> 9 is returned here
56*/
57inline void
58ConvertAVCodecContextToVideoAspectWidthAndHeight(AVCodecContext& contextIn,
59	uint16& pixelWidthAspectOut, uint16& pixelHeightAspectOut)
60{
61	if (contextIn.width <= 0 || contextIn.height <= 0) {
62		fprintf(stderr, "Cannot compute video aspect ratio correctly\n");
63		pixelWidthAspectOut = 1;
64		pixelHeightAspectOut = 1;
65		return;
66	}
67
68	assert(contextIn.sample_aspect_ratio.num >= 0);
69
70	AVRational pixelAspectRatio;
71
72	if (contextIn.sample_aspect_ratio.num == 0
73		|| contextIn.sample_aspect_ratio.den == 0) {
74		// AVCodecContext doesn't contain a video aspect ratio, so calculate it
75		// ourselve based solely on the video dimensions
76		av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, contextIn.width,
77			contextIn.height, 1024 * 1024);
78
79		pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
80		pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
81		return;
82	}
83
84	// AVCodecContext contains a video aspect ratio, so use it
85	av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den,
86		contextIn.width * contextIn.sample_aspect_ratio.num,
87		contextIn.height * contextIn.sample_aspect_ratio.den,
88		1024 * 1024);
89
90	pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
91	pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
92}
93
94
95inline void
96ConvertAVCodecParametersToVideoAspectWidthAndHeight(AVCodecParameters& parametersIn,
97	uint16& pixelWidthAspectOut, uint16& pixelHeightAspectOut)
98{
99	if (parametersIn.width <= 0 || parametersIn.height <= 0) {
100		fprintf(stderr, "Cannot compute video aspect ratio correctly\n");
101		pixelWidthAspectOut = 1;
102		pixelHeightAspectOut = 1;
103		return;
104	}
105
106	assert(parametersIn.sample_aspect_ratio.num >= 0);
107
108	AVRational pixelAspectRatio;
109
110	if (parametersIn.sample_aspect_ratio.num == 0
111		|| parametersIn.sample_aspect_ratio.den == 0) {
112		// AVCodecContext doesn't contain a video aspect ratio, so calculate it
113		// ourselve based solely on the video dimensions
114		av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, parametersIn.width,
115			parametersIn.height, 1024 * 1024);
116
117		pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
118		pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
119		return;
120	}
121
122	// AVCodecContext contains a video aspect ratio, so use it
123	av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den,
124		parametersIn.width * parametersIn.sample_aspect_ratio.num,
125		parametersIn.height * parametersIn.sample_aspect_ratio.den,
126		1024 * 1024);
127
128	pixelWidthAspectOut = static_cast<int16>(pixelAspectRatio.num);
129	pixelHeightAspectOut = static_cast<int16>(pixelAspectRatio.den);
130}
131
132
133/*!	\brief Converts the Media Kits notation of video aspect ratio into FFmpegs
134		notation.
135
136	\see ConvertAVCodecContextToVideoAspectWidthAndHeight() for converting in
137		the other direction.
138
139	\param pixelWidthAspectIn Contains Media Kits notation of the video aspect
140		ratio width. E.g. 16:9 -> 16 is passed here.
141	\param pixelHeightAspectIn Contains Media Kits notation of the video aspect
142		ratio height. E.g. 16:9 -> 9 is passed here.
143	\param contextInOut	An AVCodecContext structure of FFmpeg.
144		On input must contain the following fields already initialized
145		otherwise the behaviour is undefined:
146			- AVCodecContext.width (must)
147			- AVCodecContext.height (must)
148		On output contains converted values in the following fields (other
149		fields stay as they were on input):
150			- AVCodecContext.sample_aspect_ratio.num
151			- AVCodecContext.sample_aspect_ratio.den
152*/
153inline void
154ConvertVideoAspectWidthAndHeightToAVCodecContext(uint16 pixelWidthAspectIn,
155	uint16 pixelHeightAspectIn, AVCodecContext& contextInOut)
156{
157	if (contextInOut.width <= 0 || contextInOut.height <= 0) {
158		fprintf(stderr, "Cannot compute video aspect ratio correctly\n");
159		// We can't do anything, set the aspect ratio to 'ignore'.
160		contextInOut.sample_aspect_ratio.num = 0;
161		contextInOut.sample_aspect_ratio.den = 1;
162		return;
163	}
164
165	assert(pixelWidthAspectIn > 0);
166	assert(pixelHeightAspectIn > 0);
167
168	AVRational pureVideoDimensionAspectRatio;
169	av_reduce(&pureVideoDimensionAspectRatio.num,
170		&pureVideoDimensionAspectRatio.den, contextInOut.width,
171		contextInOut.height, 1024 * 1024);
172
173	if (pureVideoDimensionAspectRatio.num == pixelWidthAspectIn
174		&& pureVideoDimensionAspectRatio.den == pixelHeightAspectIn) {
175		// The passed Media Kit pixel aspect ratio equals the video dimension
176		// aspect ratio. Set sample_aspect_ratio to "ignore".
177		contextInOut.sample_aspect_ratio.num = 0;
178		contextInOut.sample_aspect_ratio.den = 1;
179		return;
180	}
181
182	av_reduce(&contextInOut.sample_aspect_ratio.num,
183		&contextInOut.sample_aspect_ratio.den,
184		contextInOut.height * pixelWidthAspectIn,
185		contextInOut.width * pixelHeightAspectIn,
186		1024 * 1024);
187}
188
189
190/*! \brief Calculates bytes per row for a video frame.
191
192	\param colorSpace The Media Kit color space the video frame uses.
193	\param videoWidth The width of the video frame.
194
195	\returns bytes per video frame row
196	\returns Zero, when bytes per video frame cannot be calculated.
197*/
198inline uint32
199CalculateBytesPerRowWithColorSpaceAndVideoWidth(color_space colorSpace, int videoWidth)
200{
201	assert(videoWidth >= 0);
202
203	const uint32 kBytesPerRowUnknown = 0;
204	size_t pixelChunk;
205	size_t rowAlignment;
206	size_t pixelsPerChunk;
207
208	if (get_pixel_size_for(colorSpace, &pixelChunk, &rowAlignment, &pixelsPerChunk) != B_OK)
209		return kBytesPerRowUnknown;
210
211	uint32 bytesPerRow = pixelChunk * videoWidth / pixelsPerChunk;
212	uint32 numberOfUnalignedBytes = bytesPerRow % rowAlignment;
213
214	if (numberOfUnalignedBytes == 0)
215		return bytesPerRow;
216
217	uint32 numberOfBytesNeededForAlignment = rowAlignment - numberOfUnalignedBytes;
218	bytesPerRow += numberOfBytesNeededForAlignment;
219
220	return bytesPerRow;
221}
222
223
224/*!	\brief Converts the Media Kits notation of video frame rate to FFmpegs
225	notation.
226
227	\see ConvertAVCodecContextToVideoFrameRate() for converting in the other
228		direction.
229
230	\param frameRateIn Contains Media Kits notation of the video frame rate
231		that will be converted into FFmpegs notation. Must be greater than
232		zero.
233	\param contextOut An AVCodecContext structure of FFmpeg.
234		On output contains converted values in the following fields (other
235		fields stay as they were on input):
236			- AVCodecContext.time_base.num
237			- AVCodecContext.time_base.den
238			- AVCodecContext.framerate.num
239			- AVCodecContext.framerate.den
240*/
241inline void
242ConvertVideoFrameRateToAVCodecContext(float frameRateIn,
243	AVCodecContext& contextOut)
244{
245	assert(frameRateIn > 0);
246
247	contextOut.framerate = av_d2q(frameRateIn, 1024);
248	contextOut.time_base = av_d2q(1.0 / frameRateIn, 1024);
249}
250
251
252/*!	\brief Converts the Media Kits notation of an audio sample format to
253		FFmpegs notation.
254
255	\see ConvertAVSampleFormatToRawAudioFormat() for converting in the other
256		direction.
257
258	\param rawAudioFormatIn Contains Media Kits notation of an audio sample
259		format that will be converted into FFmpegs notation.
260	\param sampleFormatOut On output contains FFmpegs notation of the passed
261		audio sample format. Might return AV_SAMPLE_FMT_NONE if there is no
262		conversion path.
263*/
264inline void
265ConvertRawAudioFormatToAVSampleFormat(uint32 rawAudioFormatIn,
266	AVSampleFormat& sampleFormatOut)
267{
268	switch (rawAudioFormatIn) {
269		case media_raw_audio_format::B_AUDIO_FLOAT:
270			sampleFormatOut = AV_SAMPLE_FMT_FLT;
271			return;
272
273		case media_raw_audio_format::B_AUDIO_DOUBLE:
274			sampleFormatOut = AV_SAMPLE_FMT_DBL;
275			return;
276
277		case media_raw_audio_format::B_AUDIO_INT:
278			sampleFormatOut = AV_SAMPLE_FMT_S32;
279			return;
280
281		case media_raw_audio_format::B_AUDIO_SHORT:
282			sampleFormatOut = AV_SAMPLE_FMT_S16;
283			return;
284
285		case media_raw_audio_format::B_AUDIO_UCHAR:
286			sampleFormatOut = AV_SAMPLE_FMT_U8;
287			return;
288
289		default:
290			// Silence compiler warnings about unhandled enumeration values.
291			break;
292	}
293
294	sampleFormatOut = AV_SAMPLE_FMT_NONE;
295}
296
297
298/*!	\brief Converts FFmpegs notation of an audio sample format to the Media
299		Kits notation.
300
301	\see ConvertAVSampleFormatToRawAudioFormat() for converting in the other
302		direction.
303
304	\param sampleFormatIn Contains FFmpegs notation of an audio sample format
305		that will be converted into the Media Kits notation.
306	\param rawAudioFormatOut On output contains Media Kits notation of the
307		passed audio sample format. Might return 0 if there is no conversion
308		path.
309*/
310inline void
311ConvertAVSampleFormatToRawAudioFormat(AVSampleFormat sampleFormatIn,
312	uint32& rawAudioFormatOut)
313{
314	switch (sampleFormatIn) {
315		case AV_SAMPLE_FMT_FLT:
316		case AV_SAMPLE_FMT_FLTP:
317			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_FLOAT;
318			return;
319
320		case AV_SAMPLE_FMT_DBL:
321		case AV_SAMPLE_FMT_DBLP:
322			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_DOUBLE;
323			return;
324
325		case AV_SAMPLE_FMT_S32:
326		case AV_SAMPLE_FMT_S32P:
327			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_INT;
328			return;
329
330		case AV_SAMPLE_FMT_S16:
331		case AV_SAMPLE_FMT_S16P:
332			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_SHORT;
333			return;
334
335		case AV_SAMPLE_FMT_U8:
336		case AV_SAMPLE_FMT_U8P:
337			rawAudioFormatOut = media_raw_audio_format::B_AUDIO_UCHAR;
338			return;
339
340		default:
341			// Silence compiler warnings about unhandled enumeration values.
342			break;
343	}
344
345	const uint32 kBAudioNone = 0;
346	rawAudioFormatOut = kBAudioNone;
347}
348
349
350#endif // UTILITIES_H
351