mirror of https://github.com/mpv-player/mpv
290 lines
7.6 KiB
C
290 lines
7.6 KiB
C
#include "../config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "mp_msg.h"
|
|
#include "help_mp.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 plugins
|
|
ao_plugin_cfg_t ao_plugin_cfg=CFG_DEFAULTS; // Set in cfg-mplayer.h
|
|
|
|
// to set/get/query special features/parameters
|
|
static int control(int cmd,void *arg){
|
|
switch(cmd){
|
|
case AOCONTROL_SET_PLUGIN_DRIVER:
|
|
ao_plugin_local_data.driver=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 error
|
|
int 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++;
|
|
}
|
|
mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_PLUGIN_InvalidPlugin,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++;
|
|
}
|
|
mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_PLUGIN_InvalidPlugin,cfg);
|
|
return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
return 0; // Will never happen...
|
|
}
|
|
|
|
// open & setup audio device and plugins
|
|
// return: 1=success 0=fail
|
|
static 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)+1);
|
|
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 device
|
|
static void uninit(int immed){
|
|
int i=0;
|
|
driver()->uninit(immed);
|
|
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 blocking
|
|
static 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 played
|
|
static 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 buffer
|
|
static 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;
|
|
}
|
|
|
|
|
|
|
|
|