Use mplayer as our Audio Decoder






4.87/5 (5 votes)
This article demonstrates how to use mplayer as an audio decoder by callback functions.

Setup Development Environment
- To compile mplayer under Windows, we can use MinGW or Cygwin, here I use MinGW, which can be downloaded from http://sourceforge.net/projects/mplayer-ww/files/MinGW-full/MinGW-full-20101119.7z/download, unpack it to C:\MinGW when complete.
- Download the latest source code of mplayer from http://www.mplayerhq.hu/design7/dload.html, I use TortoiseSVN to download the latest source code from svn://svn.mplayerhq.hu/mplayer/trunk, store it in C:\mplayer-yyyy-mm-dd, ‘yyyy-mm-dd’ could be defined as ‘2011-11-11’.
- Download the latest source code of ffmpeg cause source code of mplayer does not include it, download link is http://git.videolan.org/?p=ffmpeg.git;a=snapshot;h=HEAD;sf=tgz, a single tgz file could be downloaded, unpacked it to C:\mplayer-2011-11-11\ffmpeg when complete.
Create Executable Configure File and Run It
Create a file named ‘autoconf’ in c:\mplayer-2011-11-11, and input some configure options into it, I use below:
./configure --disable-mencoder --enable-static
--disable-sdl --disable-pthreads --enable-w32threads
Run C:\MinGW\msys_consolas.bat to display MinGW environment, execute ‘autoconf
’ to configure mplayer.
Modify mplayer.c
Here, we will do some coding works by ourselves.
- Hide main function through define
DISABLE_MAIN
.
Find the line ‘int main
’, and defineDISABLE_MAIN
top of it.
- Define a structure to hold some functions passed by external program.
typedef struct { int (*open)(int samplerate, int channels, int bits, void* data); int (*close)(void* data); int (*start)(void* data); int (*play)(void* pcmdata, int size, void* data); int (*seek)(off_t pos, void* data); int (*pause)(void* data); int (*stop)(void* data); int decode_size; void* user_data; } callback_t;
- Define a function named
mplayer_dll_main
and export it and call it by external program.
int mplayer_dll_main(int argc, char* argv[], callback_t* callback) { common_preinit(); common_init(); mpctx->stream = NULL; mpctx->demuxer = NULL; mpctx->sh_audio = NULL; mpctx->sh_video = NULL; filename = argv[1]; mpctx->stream = open_stream(filename, 0, &mpctx->file_format); if (!mpctx->stream) return 0; mpctx->demuxer = demux_open(mpctx->stream, mpctx->file_format, audio_id, video_id, dvdsub_id, filename); if (!mpctx->demuxer) return 0; mpctx->d_audio = mpctx->demuxer->audio; mpctx->d_video = mpctx->demuxer->video; mpctx->d_sub = mpctx->demuxer->sub; select_audio(mpctx->demuxer, audio_id, audio_lang); mpctx->sh_audio = mpctx->d_audio->sh; mpctx->sh_video = mpctx->d_video->sh; if (!mpctx->sh_audio) return 0; demux_info_print(mpctx->demuxer); reinit_audio_chain(); if (callback && callback->open) callback->open(ao_data.samplerate, ao_data.channels, af_fmt2bits(ao_data.format), callback); if (callback && callback->start) callback->start(callback); while (!mpctx->eof) { new_fill_audio_out_buffers(callback); } if (callback && callback->stop) callback->stop(callback); if (callback && callback->close) callback->close(callback); return 0; }
- Modify function
reinit_audio_chain
to fit our request.
void reinit_audio_chain(void) { if (!mpctx->sh_audio) return; if (!(initialized_flags & INITIALIZED_ACODEC)) { current_module = "init_audio_codec"; mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n"); if (!init_best_audio_codec(mpctx->sh_audio, audio_codec_list, audio_fm_list)) goto init_error; initialized_flags |= INITIALIZED_ACODEC; mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n"); } if (!(initialized_flags & INITIALIZED_AO)) { current_module = "af_preinit"; ao_data.samplerate = force_srate; ao_data.channels = 0; ao_data.format = audio_output_format; // first init to detect best values if (!init_audio_filters(mpctx->sh_audio, // preliminary init // input: mpctx->sh_audio->samplerate, // output: &ao_data.samplerate, &ao_data.channels, &ao_data.format)) { mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_AudioFilterChainPreinitError); exit_player(EXIT_ERROR); } // add by jacky_zz[2011-12-07] // code come from libao2/ao_dsound.c int format = ao_data.format; if (AF_FORMAT_IS_AC3(format)) format = AF_FORMAT_AC3_NE; switch(format) { case AF_FORMAT_AC3_NE: case AF_FORMAT_S24_LE: case AF_FORMAT_S16_LE: case AF_FORMAT_U8: break; default: format = AF_FORMAT_S16_LE; } ao_data.format = format; ao_data.bps = ao_data.channels * ao_data.samplerate * (af_fmt2bits(ao_data.format)>>3); ao_data.buffersize = ao_data.bps; ao_data.outburst = ao_data.channels * (af_fmt2bits(ao_data.format)>>3) * 512; // printf("bps=%d, channels=%d, samplerate=%d, // bits=%d, buffersize=%d, outburst=%d\n", // ao_data.bps, ao_data.channels, ao_data.samplerate, // af_fmt2bits(ao_data.format), // ao_data.buffersize, ao_data.outburst); // add by jacky_zz[2011-12-07] /* current_module = "ao2_init"; mpctx->audio_out = init_best_audio_out(audio_driver_list, 0, // plugin flag ao_data.samplerate, ao_data.channels, ao_data.format, 0); if (!mpctx->audio_out) { mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_CannotInitAO); goto init_error; } initialized_flags |= INITIALIZED_AO; mp_msg(MSGT_CPLAYER, MSGL_INFO, "AO: [%s] %dHz %dch %s (%d bytes per sample)\n", mpctx->audio_out->info->short_name, ao_data.samplerate, ao_data.channels, af_fmt2str_short(ao_data.format), af_fmt2bits(ao_data.format) / 8); mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Description: %s\nAO: Author: %s\n", mpctx->audio_out->info->name, mpctx->audio_out->info->author); if (strlen(mpctx->audio_out->info->comment) > 0) mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Comment: %s\n", mpctx->audio_out->info->comment); */ } // init audio filters: current_module = "af_init"; if (!build_afilter_chain(mpctx->sh_audio, &ao_data)) { mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_NoMatchingFilter); goto init_error; } // mpctx->mixer.audio_out = mpctx->audio_out; mpctx->mixer.volstep = volstep; return; init_error: uninit_player(INITIALIZED_ACODEC | INITIALIZED_AO); // close codec and // possibly AO mpctx->sh_audio = mpctx->d_audio->sh = NULL; // -> nosound mpctx->d_audio->id = -2; }
- Create a new function to decode audio data:
static int new_fill_audio_out_buffers(callback_t* callback) { int playsize; int audio_eof = 0; int bytes_to_write; int format_change = 0; sh_audio_t *const sh_audio = mpctx->sh_audio; bytes_to_write = callback->decode_size; while (bytes_to_write) { int res; playsize = bytes_to_write; if (playsize > MAX_OUTBURST) playsize = MAX_OUTBURST; bytes_to_write -= playsize; if (!format_change) { res = mp_decode_audio(sh_audio, playsize); // printf("[playsize=%d, a_out_buffer_len=%d]\n", // playsize, sh_audio->a_out_buffer_len); format_change = res == -2; } if (!format_change && res < 0) // EOF or error if (mpctx->d_audio->eof) { mpctx->eof = audio_eof = 1; if (sh_audio->a_out_buffer_len == 0) return 0; } if (playsize > sh_audio->a_out_buffer_len) { playsize = sh_audio->a_out_buffer_len; } if (!playsize) break; if (callback && callback->play) { playsize = callback->play (sh_audio->a_out_buffer, playsize, callback); sh_audio->a_out_buffer_len -= playsize; memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize], sh_audio->a_out_buffer_len); } } if (format_change) { uninit_player(INITIALIZED_AO); reinit_audio_chain(); } return 1; }
- Compile
mplayer
.
Type command ‘make
’ to compilemplayer
, we will get an error in the end because here is no main function defined in mplayer.c, ignore it.
Create a Makefile to Generate mplayer.dll
Create a Makefile
named ‘Makefile_dll’, content listed below:
include config.mak
DIRS = input libaf libao2 libass libdvdcss libdvdnav libdvdnav/vm
libdvdread4 libmpcodecs libmpdemux libmpeg2 libvo loader loader/dmo
loader/dshow mp3lib osdep stream
stream/freesdp stream/librtsp stream/realrtsp sub tremor vidix
ALL_OBJS = $(foreach DIR, $(DIRS), $(wildcard $(DIR)/*.o))
MPLAYER_OBJS = $(wildcard *.o)
INNER_LIBS = ffmpeg/libavcodec/libavcodec.a
ffmpeg/libavfilter/libavfilter.a ffmpeg/libavformat/
libavformat.a ffmpeg/libavutil/libavutil.a ffmpeg/libpostproc/
libpostproc.a ffmpeg/libswscale/libswscale.a
# $(EXTRALIBS) come from config.mak
OTHER_LIBS = $(EXTRALIBS)
# $(EXTRALIBS_MPLAYER) come from config.mak
LIBS = $(EXTRALIBS_MPLAYER)
TARGET_DIR = /d/SoftDevelop/C++/mplayer
mplayer.dll :
cc -shared -o $@ $(MPLAYER_OBJS) $(ALL_OBJS) $
(INNER_LIBS) $(OTHER_LIBS) $(LIBS) --output-def mplayer.def
rm -f $(TARGET_DIR)/$@
mv -f $@ $(TARGET_DIR)/$@
Type command ‘make –f Makefile_dll’ to generate mplayer.dll and copy it to D:\SoftDevelop\C++\mplayerdll.
Using the Code - Create a Project to Use mplayer.dll
- Load
mplayer
by Windows API ‘LoadLibrary
’ - Load exported function ‘
mplayer_dll_main
’ by Windows API ‘GetProcAddress
’ - Write proxy functions and pass them to structure
callback_t
typedef struct
{
int (*open)(int samplerate, int channels, int bits, void* data);
int (*close)(void* data);
int (*start)(void* data);
int (*play)(void* pcmdata, int size, void* data);
int (*seek)(long long pos, void* data);
int (*pause)(void* data);
int (*stop)(void* data);
int decode_size;
void* user_data;
} callback_t;
typedef int (*mplayer_dll_main)(int argc, char* argv[], callback_t* callback);
mplayer_dll_main mplayer_dll_main_ptr = NULL;
static int open(int samplerate, int channels, int bits, void* data)
{
if (!data)
return 0;
printf("samplerate=%d, channels=%d, bits=%d\n", samplerate, channels, bits);
callback_t* callback = reinterpret_cast<callback_t*>(data);
callback->user_data = DAUDIO_Open(0, 0, TRUE, DAUDIO_PCM,
(float)samplerate, bits, channels * (bits >> 3),
channels, TRUE, FALSE, 88200);
if (!callback->user_data)
return 0;
return 1;
}
static int close(void* data)
{
if (!data)
return 0;
callback_t* callback = reinterpret_cast<callback_t*>(data);
if (!callback->user_data)
return 0;
DAUDIO_Close(callback->user_data, TRUE);
callback->user_data = NULL;
return 1;
}
static int start(void* data)
{
if (!data)
return 0;
callback_t* callback = reinterpret_cast<callback_t*>(data);
if (!callback->user_data)
return 0;
return DAUDIO_Start(callback->user_data, TRUE);
}
static int stop(void* data)
{
if (!data)
return 0;
callback_t* callback = reinterpret_cast<callback_t*>(data);
if (!callback->user_data)
return 0;
Sleep(200); // for unplayed audio data
return DAUDIO_Stop(callback->user_data, TRUE);
}
static int play(void* buf, int size, void* data)
{
if (!data)
return 0;
callback_t* callback = reinterpret_cast<callback_t*>(data);
if (!callback->user_data)
return 0;
DWORD len = size;
DWORD offset = 0;
DWORD written = 0;
char* decode_buf = reinterpret_cast<char*>(buf);
for( ; ; )
{
int thisWritten = DAUDIO_Write(callback->user_data,
decode_buf + offset, len);
if(thisWritten < 0)
break;
len -= thisWritten;
written += thisWritten;
if(len > 0)
{
offset += thisWritten;
Sleep(125);
}
else break;
}
return written;
}
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hModule = LoadLibrary("mplayer.dll");
if (!hModule)
return 0;
mplayer_dll_main_ptr = (mplayer_dll_main)GetProcAddress
(hModule, "mplayer_dll_main");
if (!mplayer_dll_main_ptr)
{
FreeLibrary(hModule);
return 0;
}
char szFile[MAX_PATH];
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = GetDesktopWindow();
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = _T('\0');
ofn.nMaxFile = sizeof(szFile) / sizeof(char);
ofn.lpstrFilter = "All Audio Files\0*.*\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;
if(GetOpenFileName(&ofn) == FALSE)
return 0;
int _argc = 2;
char* _argv[2] = {"", szFile};
int ret = 0;
callback_t callback = {open, close, start, play,
NULL /*seek*/, NULL/*pause*/, stop, BLOCK_SIZE, NULL};
int count = DAUDIO_GetDirectAudioDeviceCount();
ret = mplayer_dll_main_ptr(_argc, _argv, &callback);
FreeLibrary(hModule);
return 0;
}
History
- First release on 2011-12-07