Click here to Skip to main content
Click here to Skip to main content
Go to top

Use mplayer as our Audio Decoder

, 7 Dec 2011
Rate this:
Please Sign up or sign in to vote.
This article demonstrates how to use mplayer as an audio decoder by callback functions.
Sample Image - maximum width is 668 pixels

Setup Development Environment

  1. 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.
  2. 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’.
  3. 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.

Sample Image - maximum width is 171 pixels

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.

Sample Image - maximum width is 317 pixels

Modify mplayer.c

Here, we will do some coding works by ourselves.

  1. Hide main function through define DISABLE_MAIN.
    Find the line ‘int main’, and define DISABLE_MAIN top of it.

    Sample Image - maximum width is 593 pixels

  2. 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;
  3. 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;
    }
  4. 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;
    }
  5. 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;
    }
  6. Compile mplayer.
    Type command ‘make’ to compile mplayer, 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

  1. Load mplayer by Windows API ‘LoadLibrary
  2. Load exported function ‘mplayer_dll_main’ by Windows API ‘GetProcAddress
  3. 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

jacky_zz
Software Developer none
China China
To be, or not to be, this is question. That's are all depend on your decision. What do you think?

Comments and Discussions

 
GeneralMy vote of 3 PinmemberLightMeUp10-Dec-11 18:05 
GeneralRe: My vote of 3 Pinmemberjacky_zz11-Dec-11 15:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140926.1 | Last Updated 7 Dec 2011
Article Copyright 2011 by jacky_zz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid