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:
FileDecoder(BFile * file)77 FileDecoder(BFile* file) : BMediaDecoder() {
78 sourceFile = file;
79 }
80
81 protected:
GetNextChunk(const void ** chunkData,size_t * chunkLen,media_header * mh)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
main(int argc,char * argv[])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
InitializeMp3DecodingCookie(cookie_decode * cookie)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
FreeMp3DecodingCookie(cookie_decode * cookie)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*
CreateMp3MediaFormat()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
CreateRawMediaFormat()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
Mp3Decoding(void * cookie,void * buffer,size_t bufferSize,const media_raw_audio_format & format)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