xref: /haiku/src/tests/kits/media/mp3_decoder_test/mp3_decoder_test.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2  * Copyright 2014 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Colin Günther, coling@gmx.de
7  */
8 
9 
10 /*!	Tests audio stream decoding functionality of the FFMPEG decoder plugin.
11 
12 	This test is designed with testing the dvb media-addon audio decoding
13 	capability in mind. Thus we are restricting this test to MP3-Audio.
14 
15 	The test requires a MP3 test file at the same directory you start the
16 	test from. Normally there is a test file included at the same location
17 	this source file is located if not have a look at the git history.
18 
19 	Successful completion of this test results in audio being played on the
20 	standard system audio output. So turn on your speakers or put on your head
21 	phones.
22 
23 	The originally included test file results in an audio signal containing
24 	jingles.
25 	This test file has the following properties:
26 		- encoded_audio.output.frame_rate = 48000
27 		- encoded_audio.output.channel_count = 2
28 		- encoded_audio.output.buffer_size = 1024
29 		- encoded_audio.output.format = media_raw_audio_format::B_AUDIO_SHORT
30 
31 	In any way, there -MUST- be no need to properly initialize those audio
32 	properties for this test to succeed. To put it in other terms: The
33 	FFMPEG decoder plugin should determine those properties by its own and
34 	decode the audio accordingly.
35 
36 */
37 
38 
39 #include <pthread.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include <AppKit.h>
44 #include <InterfaceKit.h>
45 #include <MediaKit.h>
46 #include <SupportKit.h>
47 
48 #include <media/Buffer.h>
49 #include <media/BufferGroup.h>
50 #include <media/MediaDecoder.h>
51 #include <media/Sound.h>
52 #include <media/SoundPlayer.h>
53 #include <storage/File.h>
54 #include <support/Errors.h>
55 
56 
57 extern "C" {
58 	#include "avcodec.h"
59 
60 #ifdef DEBUG
61   // Needed to fix debug build, otherwise the linker complains about
62   // "undefined reference to `ff_log2_tab'"
63   const uint8_t ff_log2_tab[256] = {0};
64 #endif
65 
66 }  // extern "C"
67 
68 
69 const char*	kTestAudioFilename = "./AVCodecTestMp3AudioStreamRaw";
70 
71 
72 class FileDecoder : public BMediaDecoder {
73 private:
74 	BFile* sourceFile;
75 
76 public:
77 	FileDecoder(BFile* file) : BMediaDecoder() {
78 		sourceFile = file;
79 	}
80 
81 protected:
82 	virtual status_t	GetNextChunk(const void** chunkData, size_t* chunkLen,
83 							media_header* mh) {
84 		static const uint kReadSizeInBytes = 4096;
85 
86 		memset(mh, 0, sizeof(media_header));
87 
88 		void* fileData = malloc(kReadSizeInBytes);
89 		ssize_t readLength = this->sourceFile->Read(fileData,
90 			kReadSizeInBytes);
91 		if (readLength < 0)
92 			return B_ERROR;
93 
94 		if (readLength == 0)
95 			return B_LAST_BUFFER_ERROR;
96 
97 		*chunkData = fileData;
98 		*chunkLen = readLength;
99 
100 		return B_OK;
101 	}
102 };
103 
104 
105 typedef struct cookie_decode {
106 	BFile* inputFile;
107 	BMediaDecoder* decoder;
108 	BBufferGroup* decodedDataGroup;
109 	uint32 decodedDataBufferSizeMax;
110 	pthread_cond_t playingFinishedCondition;
111 } cookie_decode;
112 
113 
114 status_t InitializeMp3DecodingCookie(cookie_decode* cookie);
115 void FreeMp3DecodingCookie(cookie_decode* cookie);
116 media_format* CreateMp3MediaFormat();
117 media_format CreateRawMediaFormat();
118 void Mp3Decoding(void* cookie, void* buffer, size_t bufferSize,
119 	const media_raw_audio_format& format);
120 
121 
122 int
123 main(int argc, char* argv[])
124 {
125 	BApplication app("application/x-vnd.mp3-decoder-test");
126 
127 	cookie_decode decodingCookie;
128 	if (InitializeMp3DecodingCookie(&decodingCookie) != B_OK)
129 		exit(1);
130 
131 	media_format rawAudioFormat = CreateRawMediaFormat();
132 
133 	media_raw_audio_format* audioOutputFormat
134 		= &rawAudioFormat.u.raw_audio;
135 
136 	BSoundPlayer player(audioOutputFormat, "wave_player", Mp3Decoding,
137 		NULL, &decodingCookie);
138 	player.Start();
139 	player.SetHasData(true);
140 	player.SetVolume(0.5);
141 
142 	// Wait as long as we are playing sound
143 	pthread_mutex_t playingFinishedMutex;
144 	pthread_mutex_init(&playingFinishedMutex, NULL);
145 	pthread_mutex_lock(&playingFinishedMutex);
146 	pthread_cond_wait(&decodingCookie.playingFinishedCondition,
147 		&playingFinishedMutex);
148 
149 	player.SetHasData(false);
150 	player.Stop();
151 
152 	// Cleaning up
153 	FreeMp3DecodingCookie(&decodingCookie);
154 	pthread_mutex_destroy(&playingFinishedMutex);
155 }
156 
157 
158 status_t
159 InitializeMp3DecodingCookie(cookie_decode* cookie)
160 {
161 	cookie->inputFile = new BFile(kTestAudioFilename, O_RDONLY);
162 	cookie->decoder = new FileDecoder(cookie->inputFile);
163 
164 	media_format* mp3MediaFormat = CreateMp3MediaFormat();
165 	cookie->decoder->SetTo(mp3MediaFormat);
166 	status_t settingDecoderStatus = cookie->decoder->InitCheck();
167 	if (settingDecoderStatus < B_OK)
168 		return B_ERROR;
169 
170 	media_format rawMediaFormat = CreateRawMediaFormat();
171 	status_t settingDecoderOutputStatus
172 		= cookie->decoder->SetOutputFormat(&rawMediaFormat);
173 	if (settingDecoderOutputStatus < B_OK)
174 		return B_ERROR;
175 
176 	cookie->decodedDataBufferSizeMax
177 		= rawMediaFormat.u.raw_audio.buffer_size * 3;
178 	cookie->decodedDataGroup
179 		= new BBufferGroup(cookie->decodedDataBufferSizeMax, 25);
180 
181 	if (pthread_cond_init(&cookie->playingFinishedCondition, NULL) < 0)
182 		return B_ERROR;
183 
184 	return B_OK;
185 }
186 
187 
188 void
189 FreeMp3DecodingCookie(cookie_decode* cookie)
190 {
191 	pthread_cond_destroy(&cookie->playingFinishedCondition);
192 	cookie->decodedDataGroup->ReclaimAllBuffers();
193 	free(cookie->decodedDataGroup);
194 	free(cookie->decoder);
195 	free(cookie->inputFile);
196 }
197 
198 
199 /*!	The caller takes ownership of the returned media_format value.
200 	Thus the caller needs to free the returned value.
201 	The returned value may be NULL, when there was an error.
202 */
203 media_format*
204 CreateMp3MediaFormat()
205 {
206 	// copy 'n' paste from src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp:
207 	// GetHeaderFormatMpegAudio()
208 	status_t status;
209 	media_format_description desc;
210 	desc.family = B_MISC_FORMAT_FAMILY;
211 	desc.u.misc.file_format = 'ffmp';
212 	desc.u.misc.codec = CODEC_ID_MP3;
213 	static media_format* sNoMp3MediaFormat = NULL;
214 
215 	BMediaFormats formats;
216 	status = formats.InitCheck();
217 	if (status < B_OK) {
218 		printf("formats.InitCheck failed, error %lu\n", status);
219 		return sNoMp3MediaFormat;
220 	}
221 
222 	media_format* mp3MediaFormat
223 		= static_cast<media_format*>(malloc(sizeof(media_format)));
224 	memset(mp3MediaFormat, 0, sizeof(media_format));
225 	status = formats.GetFormatFor(desc, mp3MediaFormat);
226 	if (status < B_OK) {
227 		printf("formats.GetFormatFor failed, error %lu\n", status);
228 		return sNoMp3MediaFormat;
229 	}
230 
231 	return mp3MediaFormat;
232 }
233 
234 
235 media_format
236 CreateRawMediaFormat()
237 {
238 	media_format rawMediaFormat;
239 	memset(&rawMediaFormat, 0, sizeof(media_format));
240 
241 	rawMediaFormat.type = B_MEDIA_RAW_AUDIO;
242 	rawMediaFormat.u.raw_audio.frame_rate = 48000;
243 	rawMediaFormat.u.raw_audio.channel_count = 2;
244 	rawMediaFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
245 	rawMediaFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
246 	rawMediaFormat.u.raw_audio.buffer_size = 32768;
247 		// comment from src/add-ons/media/media-add-ons/dvb/
248 		// DVBMediaNode.cpp::InitDefaultFormats(): when set to anything
249 		// different from 32768 haiku mixer has problems
250 
251 	return rawMediaFormat;
252 }
253 
254 
255 void
256 Mp3Decoding(void* cookie, void* buffer, size_t bufferSize,
257 	const media_raw_audio_format& format)
258 {
259 	cookie_decode* decodingCookie = static_cast<cookie_decode*>(cookie);
260 	int64 rawAudioFrameCount = 0;
261 	media_header mh;
262 	status_t decodingAudioFramesStatus
263 		= decodingCookie->decoder->Decode(buffer, &rawAudioFrameCount, &mh,
264 			NULL);
265 	if (decodingAudioFramesStatus < B_OK) {
266 		sleep(2);
267 			// Give the player some time to catch up playing all decoded data.
268 			// The player may still have some data to play, even though we run
269 			// out of new data, so don't just tell that we finished playing
270 			// quiet yet.
271 		pthread_cond_signal(&decodingCookie->playingFinishedCondition);
272 	}
273 }
274