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