* Copyright 2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Colin GΓΌnther, coling@gmx.de
*/
This test is designed with testing the dvb media-addon audio decoding
capability in mind. Thus we are restricting this test to MP3-Audio.
The test requires a MP3 test file at the same directory you start the
test from. Normally there is a test file included at the same location
this source file is located if not have a look at the git history.
Successful completion of this test results in audio being played on the
standard system audio output. So turn on your speakers or put on your head
phones.
The originally included test file results in an audio signal containing
jingles.
This test file has the following properties:
- encoded_audio.output.frame_rate = 48000
- encoded_audio.output.channel_count = 2
- encoded_audio.output.buffer_size = 1024
- encoded_audio.output.format = media_raw_audio_format::B_AUDIO_SHORT
In any way, there -MUST- be no need to properly initialize those audio
properties for this test to succeed. To put it in other terms: The
FFMPEG decoder plugin should determine those properties by its own and
decode the audio accordingly.
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <AppKit.h>
#include <InterfaceKit.h>
#include <MediaKit.h>
#include <SupportKit.h>
#include <media/Buffer.h>
#include <media/BufferGroup.h>
#include <media/MediaDecoder.h>
#include <media/Sound.h>
#include <media/SoundPlayer.h>
#include <storage/File.h>
#include <support/Errors.h>
extern "C" {
#include "avcodec.h"
#ifdef DEBUG
const uint8_t ff_log2_tab[256] = {0};
#endif
}
const char* kTestAudioFilename = "./AVCodecTestMp3AudioStreamRaw";
class FileDecoder : public BMediaDecoder {
private:
BFile* sourceFile;
public:
FileDecoder(BFile* file) : BMediaDecoder() {
sourceFile = file;
}
protected:
virtual status_t GetNextChunk(const void** chunkData, size_t* chunkLen,
media_header* mh) {
static const uint kReadSizeInBytes = 4096;
memset(mh, 0, sizeof(media_header));
void* fileData = malloc(kReadSizeInBytes);
ssize_t readLength = this->sourceFile->Read(fileData,
kReadSizeInBytes);
if (readLength < 0)
return B_ERROR;
if (readLength == 0)
return B_LAST_BUFFER_ERROR;
*chunkData = fileData;
*chunkLen = readLength;
return B_OK;
}
};
typedef struct cookie_decode {
BFile* inputFile;
BMediaDecoder* decoder;
BBufferGroup* decodedDataGroup;
uint32 decodedDataBufferSizeMax;
pthread_cond_t playingFinishedCondition;
} cookie_decode;
status_t InitializeMp3DecodingCookie(cookie_decode* cookie);
void FreeMp3DecodingCookie(cookie_decode* cookie);
media_format* CreateMp3MediaFormat();
media_format CreateRawMediaFormat();
void Mp3Decoding(void* cookie, void* buffer, size_t bufferSize,
const media_raw_audio_format& format);
int
main(int argc, char* argv[])
{
BApplication app("application/x-vnd.mp3-decoder-test");
cookie_decode decodingCookie;
if (InitializeMp3DecodingCookie(&decodingCookie) != B_OK)
exit(1);
media_format rawAudioFormat = CreateRawMediaFormat();
media_raw_audio_format* audioOutputFormat
= &rawAudioFormat.u.raw_audio;
BSoundPlayer player(audioOutputFormat, "wave_player", Mp3Decoding,
NULL, &decodingCookie);
player.Start();
player.SetHasData(true);
player.SetVolume(0.5);
pthread_mutex_t playingFinishedMutex;
pthread_mutex_init(&playingFinishedMutex, NULL);
pthread_mutex_lock(&playingFinishedMutex);
pthread_cond_wait(&decodingCookie.playingFinishedCondition,
&playingFinishedMutex);
player.SetHasData(false);
player.Stop();
FreeMp3DecodingCookie(&decodingCookie);
pthread_mutex_destroy(&playingFinishedMutex);
}
status_t
InitializeMp3DecodingCookie(cookie_decode* cookie)
{
cookie->inputFile = new BFile(kTestAudioFilename, O_RDONLY);
cookie->decoder = new FileDecoder(cookie->inputFile);
media_format* mp3MediaFormat = CreateMp3MediaFormat();
cookie->decoder->SetTo(mp3MediaFormat);
status_t settingDecoderStatus = cookie->decoder->InitCheck();
if (settingDecoderStatus < B_OK)
return B_ERROR;
media_format rawMediaFormat = CreateRawMediaFormat();
status_t settingDecoderOutputStatus
= cookie->decoder->SetOutputFormat(&rawMediaFormat);
if (settingDecoderOutputStatus < B_OK)
return B_ERROR;
cookie->decodedDataBufferSizeMax
= rawMediaFormat.u.raw_audio.buffer_size * 3;
cookie->decodedDataGroup
= new BBufferGroup(cookie->decodedDataBufferSizeMax, 25);
if (pthread_cond_init(&cookie->playingFinishedCondition, NULL) < 0)
return B_ERROR;
return B_OK;
}
void
FreeMp3DecodingCookie(cookie_decode* cookie)
{
pthread_cond_destroy(&cookie->playingFinishedCondition);
cookie->decodedDataGroup->ReclaimAllBuffers();
free(cookie->decodedDataGroup);
free(cookie->decoder);
free(cookie->inputFile);
}
Thus the caller needs to free the returned value.
The returned value may be NULL, when there was an error.
*/
media_format*
CreateMp3MediaFormat()
{
status_t status;
media_format_description desc;
desc.family = B_MISC_FORMAT_FAMILY;
desc.u.misc.file_format = 'ffmp';
desc.u.misc.codec = CODEC_ID_MP3;
static media_format* sNoMp3MediaFormat = NULL;
BMediaFormats formats;
status = formats.InitCheck();
if (status < B_OK) {
printf("formats.InitCheck failed, error %lu\n", status);
return sNoMp3MediaFormat;
}
media_format* mp3MediaFormat
= static_cast<media_format*>(malloc(sizeof(media_format)));
memset(mp3MediaFormat, 0, sizeof(media_format));
status = formats.GetFormatFor(desc, mp3MediaFormat);
if (status < B_OK) {
printf("formats.GetFormatFor failed, error %lu\n", status);
return sNoMp3MediaFormat;
}
return mp3MediaFormat;
}
media_format
CreateRawMediaFormat()
{
media_format rawMediaFormat;
memset(&rawMediaFormat, 0, sizeof(media_format));
rawMediaFormat.type = B_MEDIA_RAW_AUDIO;
rawMediaFormat.u.raw_audio.frame_rate = 48000;
rawMediaFormat.u.raw_audio.channel_count = 2;
rawMediaFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
rawMediaFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
rawMediaFormat.u.raw_audio.buffer_size = 32768;
return rawMediaFormat;
}
void
Mp3Decoding(void* cookie, void* buffer, size_t bufferSize,
const media_raw_audio_format& format)
{
cookie_decode* decodingCookie = static_cast<cookie_decode*>(cookie);
int64 rawAudioFrameCount = 0;
media_header mh;
status_t decodingAudioFramesStatus
= decodingCookie->decoder->Decode(buffer, &rawAudioFrameCount, &mh,
NULL);
if (decodingAudioFramesStatus < B_OK) {
sleep(2);
pthread_cond_signal(&decodingCookie->playingFinishedCondition);
}
}