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