Media Kit: Add MP3 audio stream decoder test.
- This test currently fails and thus showing that there is something wrong in the Media Kit. - This test was first developed and tested on Haiku R1 Alpha 4.1 where every thing was hacked together to make this test succeed, thus showing that the current Media Kit state of affair has some issues and not this test :) - The test comes with documentation (@see mp3_decoder_test.cpp). Signed-off-by: Colin Günther <[email protected]>
This commit is contained in:
@@ -19,6 +19,7 @@ SimpleTest mediaDescriptions :
|
||||
|
||||
SubInclude HAIKU_TOP src tests kits media media_decoder ;
|
||||
SubInclude HAIKU_TOP src tests kits media mpeg2_decoder_test ;
|
||||
SubInclude HAIKU_TOP src tests kits media mp3_decoder_test ;
|
||||
SubInclude HAIKU_TOP src tests kits media notificationtest ;
|
||||
SubInclude HAIKU_TOP src tests kits media nodetest ;
|
||||
SubInclude HAIKU_TOP src tests kits media playwav ;
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
SubDir HAIKU_TOP src tests kits media mp3_decoder_test ;
|
||||
|
||||
local ffmpegHeaders = [ BuildFeatureAttribute ffmpeg : headers : path ] ;
|
||||
UseHeaders $(ffmpegHeaders) ;
|
||||
UseHeaders [ FDirName $(ffmpegHeaders) libavcodec ] ;
|
||||
UseHeaders [ FDirName $(ffmpegHeaders) libavformat ] ;
|
||||
UseHeaders [ FDirName $(ffmpegHeaders) libavutil ] ;
|
||||
|
||||
SubDirC++Flags -D__STDC_CONSTANT_MACROS -Wno-deprecated-declarations ;
|
||||
|
||||
SimpleTest mp3_decoder_test :
|
||||
mp3_decoder_test.cpp
|
||||
: be media $(TARGET_LIBSUPC++)
|
||||
;
|
||||
|
||||
Includes [ FGristFiles mp3_decoder_test.cpp ]
|
||||
: [ BuildFeatureAttribute ffmpeg : headers ] ;
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2014 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Colin Günther, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
/*! Tests audio stream decoding functionality of the FFMPEG decoder plugin.
|
||||
|
||||
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
|
||||
// Needed to fix debug build, otherwise the linker complains about
|
||||
// "undefined reference to `ff_log2_tab'"
|
||||
const uint8_t ff_log2_tab[256] = {0};
|
||||
#endif
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
||||
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_raw_audio_format* audioOutputFormat
|
||||
= &CreateRawMediaFormat().u.raw_audio;
|
||||
|
||||
BSoundPlayer player(audioOutputFormat, "wave_player", Mp3Decoding,
|
||||
NULL, &decodingCookie);
|
||||
player.Start();
|
||||
player.SetHasData(true);
|
||||
player.SetVolume(0.5);
|
||||
|
||||
// Wait as long as we are playing sound
|
||||
pthread_mutex_t playingFinishedMutex;
|
||||
pthread_mutex_init(&playingFinishedMutex, NULL);
|
||||
pthread_mutex_lock(&playingFinishedMutex);
|
||||
pthread_cond_wait(&decodingCookie.playingFinishedCondition,
|
||||
&playingFinishedMutex);
|
||||
|
||||
player.SetHasData(false);
|
||||
player.Stop();
|
||||
|
||||
// Cleaning up
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*! The caller takes ownership of the returned media_format value.
|
||||
Thus the caller needs to free the returned value.
|
||||
The returned value may be NULL, when there was an error.
|
||||
*/
|
||||
media_format*
|
||||
CreateMp3MediaFormat()
|
||||
{
|
||||
// copy 'n' paste from src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp:
|
||||
// GetHeaderFormatMpegAudio()
|
||||
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;
|
||||
}
|
||||
|
||||
// The following code block can be removed, once the ffmpeg addon can
|
||||
// determine the codec output parameters from the encoded data.
|
||||
mp3MediaFormat->u.encoded_audio.output.frame_rate = 48000;
|
||||
mp3MediaFormat->u.encoded_audio.output.channel_count = 2;
|
||||
mp3MediaFormat->u.encoded_audio.output.buffer_size = 1024;
|
||||
mp3MediaFormat->u.encoded_audio.output.format
|
||||
= media_raw_audio_format::B_AUDIO_SHORT;
|
||||
|
||||
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;
|
||||
// comment from src/add-ons/media/media-add-ons/dvb/
|
||||
// DVBMediaNode.cpp::InitDefaultFormats(): when set to anything
|
||||
// different from 32768 haiku mixer has problems
|
||||
|
||||
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);
|
||||
// Give the player some time to catch up playing all decoded data.
|
||||
// The player may still have some data to play, even though we run
|
||||
// out of new data, so don't just tell that we finished playing
|
||||
// quiet yet.
|
||||
pthread_cond_signal(&decodingCookie->playingFinishedCondition);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user