adds "libdv" to the "input modules" list in "configure", and
takes out the note about "for mencoder" since it's actually for both. :)
patch by Kees Cook <mplayer@outflux.net>
#include "../config.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include "afmt.h"#include "audio_out.h"#include "audio_out_internal.h"#include "audio_plugin.h"static ao_info_t info = { "Plugin audio output", "plugin", "Anders", ""};LIBAO_EXTERN(plugin)#define plugin(i) (ao_plugin_local_data.plugins[i])#define driver() (ao_plugin_local_data.driver)// local data typedef struct ao_plugin_local_data_s{ void* buf; // Output data buffer int len; // Amount of data in buffer int channels; int format; int bpm; //bit of format float bps; // Bytes per second out ao_functions_t* driver; // Output driver ao_plugin_functions_t** plugins; // List of used plugins ao_plugin_functions_t* available_plugins[NPL]; // List of available plugins} ao_plugin_local_data_t;static ao_plugin_local_data_t ao_plugin_local_data={NULL,0,0,0,0,0.0,NULL,NULL,AO_PLUGINS};// global data volatile ao_plugin_data_t ao_plugin_data; // Data used by the pluginsao_plugin_cfg_t ao_plugin_cfg=CFG_DEFAULTS; // Set in cfg-mplayer.h// to set/get/query special features/parametersstatic int control(int cmd,int arg){ switch(cmd){ case AOCONTROL_SET_PLUGIN_DRIVER: ao_plugin_local_data.driver=(ao_functions_t*)arg; return CONTROL_OK; case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { int r=audio_plugin_volume.control(cmd,arg); if(CONTROL_OK != r) return driver()->control(cmd,arg); else return r; } default: return driver()->control(cmd,arg); } return CONTROL_UNKNOWN;}// Recursive function for adding plugins// return 1 for success and 0 for errorint add_plugin(int i,char* cfg){ int cnt=0; // Find end of plugin name while((cfg[cnt]!=',')&&(cfg[cnt]!='\0')&&(cnt<100)) cnt++; if(cnt >= 100) return 0; // Is this the last iteration or just another plugin if(cfg[cnt]=='\0'){ ao_plugin_local_data.plugins=malloc((i+2)*sizeof(ao_plugin_functions_t*)); if(ao_plugin_local_data.plugins){ ao_plugin_local_data.plugins[i+1]=NULL; // Find the plugin matching the cfg string name cnt=0; while(ao_plugin_local_data.available_plugins[cnt] && cnt<20){ if(0==strcmp(ao_plugin_local_data.available_plugins[cnt]->info->short_name,cfg)){ ao_plugin_local_data.plugins[i]=ao_plugin_local_data.available_plugins[cnt]; return 1; } cnt++; } printf("[plugin]: Invalid plugin: %s \n",cfg); return 0; } else return 0; } else { cfg[cnt]='\0'; if(add_plugin(i+1,&cfg[cnt+1])){ cnt=0; // Find the plugin matching the cfg string name while(ao_plugin_local_data.available_plugins[cnt] && cnt < 20){ if(0==strcmp(ao_plugin_local_data.available_plugins[cnt]->info->short_name,cfg)){ ao_plugin_local_data.plugins[i]=ao_plugin_local_data.available_plugins[cnt]; return 1; } cnt++; } printf("[plugin]: Invalid plugin: %s \n",cfg); return 0; } else return 0; } return 0; // Will never happen...}// open & setup audio device and plugins// return: 1=success 0=failstatic int init(int rate,int channels,int format,int flags){ int use_plugin[NPL]; int x,npl,unused=0; int ok=1; char *config; // Create list of plugins from cfg option int i=0; if(ao_plugin_cfg.plugin_list){ config = malloc(strlen(ao_plugin_cfg.plugin_list)); if(!config) return 0; if(!strcpy(config, ao_plugin_cfg.plugin_list) || !add_plugin(i,config)){ free(config); return 0; } free(config); } /* Set input parameters and itterate through plugins each plugin changes the parameters according to its output */ ao_plugin_local_data.format=format; ao_plugin_local_data.channels=channels; ao_plugin_local_data.bpm=audio_out_format_bits(format); ao_plugin_data.rate=rate; ao_plugin_data.channels=channels; ao_plugin_data.format=format; ao_plugin_data.sz_mult=1; ao_plugin_data.sz_fix=0; ao_plugin_data.delay_mult=1; ao_plugin_data.delay_fix=0; for(i=0;i<NPL && plugin(i);i++){ use_plugin[i]=plugin(i)->init(); if(!use_plugin[i]) plugin(i)->uninit(); } npl=i; for(i=0;i<npl && plugin(i);i++) if(!use_plugin[i+unused]){ unused++; for(x=i;x<npl && plugin(x+1);x++) plugin(x)=plugin(x+1); plugin(x)=NULL; npl--; i--; } i=npl; // Calculate bps ao_plugin_local_data.bps=(float)(ao_plugin_data.rate * ao_plugin_data.channels); ao_plugin_local_data.bps*=audio_out_format_bits(ao_plugin_data.format)/8; // This should never happen but check anyway if(NULL==ao_plugin_local_data.driver) return 0; ok = driver()->init(ao_plugin_data.rate, ao_plugin_data.channels, ao_plugin_data.format, flags); if(!ok) return 0; /* Now that the driver is initialized we can calculate and set the input and output buffers for each plugin */ ao_plugin_data.len=driver()->get_space(); while((i>0) && ok) ok=plugin(--i)->control(AOCONTROL_PLUGIN_SET_LEN,0); if(!ok) return 0; // Allocate output buffer if(ao_plugin_local_data.buf) free(ao_plugin_local_data.buf); ao_plugin_local_data.buf=malloc(MAX_OUTBURST); if(!ao_plugin_local_data.buf) return 0; return 1;}// close audio devicestatic void uninit(){ int i=0; driver()->uninit(); while(plugin(i)) plugin(i++)->uninit(); if(ao_plugin_local_data.plugins) free(ao_plugin_local_data.plugins); ao_plugin_local_data.plugins=NULL; if(ao_plugin_local_data.buf) free(ao_plugin_local_data.buf); ao_plugin_local_data.buf=NULL;}// stop playing and empty buffers (for seeking/pause)static void reset(){ int i=0; driver()->reset(); while(plugin(i)) plugin(i++)->reset(); ao_plugin_local_data.len=0;}// stop playing, keep buffers (for pause)static void audio_pause(){ driver()->pause();}// resume playing, after audio_pause()static void audio_resume(){ driver()->resume();}// return: how many bytes can be played without blockingstatic int get_space(){double sz;int isz; sz=(double)(driver()->get_space()); if(sz+(double)ao_plugin_local_data.len > (double)MAX_OUTBURST) sz=(double)MAX_OUTBURST-(double)ao_plugin_local_data.len; sz*=ao_plugin_data.sz_mult; sz+=ao_plugin_data.sz_fix; isz=(int)(sz); isz-=isz%(ao_plugin_local_data.channels*ao_plugin_local_data.bpm/8); return isz;}// plays 'len' bytes of 'data'// return: number of bytes playedstatic int play(void* data,int len,int flags){ int l,i=0; // Limit length to avoid over flow in plugins int tmp = get_space(); int ret_len =(tmp<len)?tmp:len; // keep all channels of each sample together ret_len -= ret_len % (ao_plugin_local_data.channels*ao_plugin_local_data.bpm/8); if(ret_len){ // Filter data ao_plugin_data.len=ret_len; ao_plugin_data.data=data; while(plugin(i)) plugin(i++)->play(); // Copy data to output buffer memcpy(ao_plugin_local_data.buf+ao_plugin_local_data.len, ao_plugin_data.data,ao_plugin_data.len); // Send data to output l=driver()->play(ao_plugin_local_data.buf, ao_plugin_data.len+ao_plugin_local_data.len,flags); // Save away unsent data ao_plugin_local_data.len=ao_plugin_data.len+ao_plugin_local_data.len-l; memcpy(ao_plugin_local_data.buf,ao_plugin_local_data.buf+l, ao_plugin_local_data.len); } return ret_len;}// return: delay in seconds between first and last sample in bufferstatic float get_delay(){ float delay=driver()->get_delay(); delay+=(float)ao_plugin_local_data.len/ao_plugin_local_data.bps; delay*=ao_plugin_data.delay_mult; delay+=ao_plugin_data.delay_fix; return delay;}