Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Use mplayer as our Audio Decoder

4.87/5 (5 votes)
7 Dec 2011CPOL2 min read 29.2K   1.5K  
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:

C++
./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.
    C++
    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.
    C++
    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.
    C++
    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:
    C++
    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:

C++
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
C++
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)