xref: /haiku/src/tests/kits/media/mp3_decoder_test/mp3_decoder_test.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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_raw_audio_format* audioOutputFormat
132 		= &CreateRawMediaFormat().u.raw_audio;
133 
134 	BSoundPlayer player(audioOutputFormat, "wave_player", Mp3Decoding,
135 		NULL, &decodingCookie);
136 	player.Start();
137 	player.SetHasData(true);
138 	player.SetVolume(0.5);
139 
140 	// Wait as long as we are playing sound
141 	pthread_mutex_t playingFinishedMutex;
142 	pthread_mutex_init(&playingFinishedMutex, NULL);
143 	pthread_mutex_lock(&playingFinishedMutex);
144 	pthread_cond_wait(&decodingCookie.playingFinishedCondition,
145 		&playingFinishedMutex);
146 
147 	player.SetHasData(false);
148 	player.Stop();
149 
150 	// Cleaning up
151 	FreeMp3DecodingCookie(&decodingCookie);
152 	pthread_mutex_destroy(&playingFinishedMutex);
153 }
154 
155 
156 status_t
157 InitializeMp3DecodingCookie(cookie_decode* cookie)
158 {
159 	cookie->inputFile = new BFile(kTestAudioFilename, O_RDONLY);
160 	cookie->decoder = new FileDecoder(cookie->inputFile);
161 
162 	media_format* mp3MediaFormat = CreateMp3MediaFormat();
163 	cookie->decoder->SetTo(mp3MediaFormat);
164 	status_t settingDecoderStatus = cookie->decoder->InitCheck();
165 	if (settingDecoderStatus < B_OK)
166 		return B_ERROR;
167 
168 	media_format rawMediaFormat = CreateRawMediaFormat();
169 	status_t settingDecoderOutputStatus
170 		= cookie->decoder->SetOutputFormat(&rawMediaFormat);
171 	if (settingDecoderOutputStatus < B_OK)
172 		return B_ERROR;
173 
174 	cookie->decodedDataBufferSizeMax
175 		= rawMediaFormat.u.raw_audio.buffer_size * 3;
176 	cookie->decodedDataGroup
177 		= new BBufferGroup(cookie->decodedDataBufferSizeMax, 25);
178 
179 	if (pthread_cond_init(&cookie->playingFinishedCondition, NULL) < 0)
180 		return B_ERROR;
181 
182 	return B_OK;
183 }
184 
185 
186 void
187 FreeMp3DecodingCookie(cookie_decode* cookie)
188 {
189 	pthread_cond_destroy(&cookie->playingFinishedCondition);
190 	cookie->decodedDataGroup->ReclaimAllBuffers();
191 	free(cookie->decodedDataGroup);
192 	free(cookie->decoder);
193 	free(cookie->inputFile);
194 }
195 
196 
197 /*!	The caller takes ownership of the returned media_format value.
198 	Thus the caller needs to free the returned value.
199 	The returned value may be NULL, when there was an error.
200 */
201 media_format*
202 CreateMp3MediaFormat()
203 {
204 	// copy 'n' paste from src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp:
205 	// GetHeaderFormatMpegAudio()
206 	status_t status;
207 	media_format_description desc;
208 	desc.family = B_MISC_FORMAT_FAMILY;
209 	desc.u.misc.file_format = 'ffmp';
210 	desc.u.misc.codec = CODEC_ID_MP3;
211 	static media_format* sNoMp3MediaFormat = NULL;
212 
213 	BMediaFormats formats;
214 	status = formats.InitCheck();
215 	if (status < B_OK) {
216 		printf("formats.InitCheck failed, error %lu\n", status);
217 		return sNoMp3MediaFormat;
218 	}
219 
220 	media_format* mp3MediaFormat
221 		= static_cast<media_format*>(malloc(sizeof(media_format)));
222 	memset(mp3MediaFormat, 0, sizeof(media_format));
223 	status = formats.GetFormatFor(desc, mp3MediaFormat);
224 	if (status < B_OK) {
225 		printf("formats.GetFormatFor failed, error %lu\n", status);
226 		return sNoMp3MediaFormat;
227 	}
228 
229 	return mp3MediaFormat;
230 }
231 
232 
233 media_format
234 CreateRawMediaFormat()
235 {
236 	media_format rawMediaFormat;
237 	memset(&rawMediaFormat, 0, sizeof(media_format));
238 
239 	rawMediaFormat.type = B_MEDIA_RAW_AUDIO;
240 	rawMediaFormat.u.raw_audio.frame_rate = 48000;
241 	rawMediaFormat.u.raw_audio.channel_count = 2;
242 	rawMediaFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
243 	rawMediaFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
244 	rawMediaFormat.u.raw_audio.buffer_size = 32768;
245 		// comment from src/add-ons/media/media-add-ons/dvb/
246 		// DVBMediaNode.cpp::InitDefaultFormats(): when set to anything
247 		// different from 32768 haiku mixer has problems
248 
249 	return rawMediaFormat;
250 }
251 
252 
253 void
254 Mp3Decoding(void* cookie, void* buffer, size_t bufferSize,
255 	const media_raw_audio_format& format)
256 {
257 	cookie_decode* decodingCookie = static_cast<cookie_decode*>(cookie);
258 	int64 rawAudioFrameCount = 0;
259 	media_header mh;
260 	status_t decodingAudioFramesStatus
261 		= decodingCookie->decoder->Decode(buffer, &rawAudioFrameCount, &mh,
262 			NULL);
263 	if (decodingAudioFramesStatus < B_OK) {
264 		sleep(2);
265 			// Give the player some time to catch up playing all decoded data.
266 			// The player may still have some data to play, even though we run
267 			// out of new data, so don't just tell that we finished playing
268 			// quiet yet.
269 		pthread_cond_signal(&decodingCookie->playingFinishedCondition);
270 	}
271 }
272