mirror of
https://github.com/mpv-player/mpv
synced 2024-09-09 01:16:56 +02:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
2793e7eb70
4
Makefile
4
Makefile
@ -438,7 +438,6 @@ SRCS_MPLAYER-$(AA) += libvo/vo_aa.c
|
||||
SRCS_MPLAYER-$(ALSA) += libao2/ao_alsa.c
|
||||
SRCS_MPLAYER-$(APPLE_IR) += input/appleir.c
|
||||
SRCS_MPLAYER-$(APPLE_REMOTE) += input/ar.c
|
||||
SRCS_MPLAYER-$(ARTS) += libao2/ao_arts.c
|
||||
SRCS_MPLAYER-$(BL) += libvo/vo_bl.c
|
||||
SRCS_MPLAYER-$(CACA) += libvo/vo_caca.c
|
||||
SRCS_MPLAYER-$(COREAUDIO) += libao2/ao_coreaudio.c
|
||||
@ -449,7 +448,6 @@ SRCS_MPLAYER-$(DIRECT3D) += libvo/vo_direct3d.c libvo/w32_common.c
|
||||
SRCS_MPLAYER-$(DIRECTFB) += libvo/vo_directfb2.c libvo/vo_dfbmga.c
|
||||
SRCS_MPLAYER-$(DIRECTX) += libao2/ao_dsound.c libvo/vo_directx.c
|
||||
SRCS_MPLAYER-$(DXR3) += libvo/vo_dxr3.c
|
||||
SRCS_MPLAYER-$(ESD) += libao2/ao_esd.c
|
||||
SRCS_MPLAYER-$(FBDEV) += libvo/vo_fbdev.c libvo/vo_fbdev2.c
|
||||
SRCS_MPLAYER-$(GGI) += libvo/vo_ggi.c
|
||||
SRCS_MPLAYER-$(GIF) += libvo/vo_gif89a.c
|
||||
@ -471,10 +469,10 @@ SRCS_MPLAYER-$(OPENAL) += libao2/ao_openal.c
|
||||
SRCS_MPLAYER-$(OSS) += libao2/ao_oss.c
|
||||
SRCS_MPLAYER-$(PNM) += libvo/vo_pnm.c
|
||||
SRCS_MPLAYER-$(PULSE) += libao2/ao_pulse.c
|
||||
SRCS_MPLAYER-$(PORTAUDIO) += libao2/ao_portaudio.c
|
||||
SRCS_MPLAYER-$(RSOUND) += libao2/ao_rsound.c
|
||||
SRCS_MPLAYER-$(S3FB) += libvo/vo_s3fb.c
|
||||
SRCS_MPLAYER-$(SDL) += libao2/ao_sdl.c libvo/vo_sdl.c libvo/sdl_common.c
|
||||
SRCS_MPLAYER-$(SGIAUDIO) += libao2/ao_sgi.c
|
||||
SRCS_MPLAYER-$(SUNAUDIO) += libao2/ao_sun.c
|
||||
SRCS_MPLAYER-$(SVGA) += libvo/vo_svga.c
|
||||
SRCS_MPLAYER-$(TDFXFB) += libvo/vo_tdfxfb.c
|
||||
|
@ -373,7 +373,8 @@ const m_option_t common_opts[] = {
|
||||
// ------------------------- common options --------------------
|
||||
OPT_MAKE_FLAGS("quiet", quiet, CONF_GLOBAL),
|
||||
{"really-quiet", &verbose, CONF_TYPE_FLAG, CONF_GLOBAL|CONF_PRE_PARSE, 0, -10, NULL},
|
||||
{"v", cfg_inc_verbose, CONF_TYPE_FUNC, CONF_GLOBAL|CONF_NOSAVE, 0, 0, NULL},
|
||||
// -v is handled in command line preparser
|
||||
{"v", NULL, CONF_TYPE_FLAG, CONF_NOCFG, 0, 0, NULL},
|
||||
{"msglevel", (void *) msgl_config, CONF_TYPE_SUBCONFIG, CONF_GLOBAL, 0, 0, NULL},
|
||||
{"msgcolor", &mp_msg_color, CONF_TYPE_FLAG, CONF_GLOBAL, 0, 1, NULL},
|
||||
{"nomsgcolor", &mp_msg_color, CONF_TYPE_FLAG, CONF_GLOBAL, 1, 0, NULL},
|
||||
|
164
configure
vendored
164
configure
vendored
@ -209,7 +209,6 @@ dragonfly() { issystem "DragonFly"; }
|
||||
freebsd() { issystem "FreeBSD" || issystem "GNU/kFreeBSD"; }
|
||||
gnu() { issystem "GNU"; }
|
||||
hpux() { issystem "HP-UX"; }
|
||||
irix() { issystem "IRIX"; }
|
||||
linux() { issystem "Linux"; }
|
||||
mingw32() { issystem "MINGW32"; }
|
||||
morphos() { issystem "MorphOS"; }
|
||||
@ -440,14 +439,12 @@ Video output:
|
||||
Audio output:
|
||||
--disable-alsa disable ALSA audio output [autodetect]
|
||||
--disable-ossaudio disable OSS audio output [autodetect]
|
||||
--disable-arts disable aRts audio output [autodetect]
|
||||
--disable-esd disable esd audio output [autodetect]
|
||||
--disable-rsound disable RSound audio output [autodetect]
|
||||
--disable-pulse disable Pulseaudio audio output [autodetect]
|
||||
--disable-portaudio disable PortAudio audio output [autodetect]
|
||||
--disable-jack disable JACK audio output [autodetect]
|
||||
--enable-openal enable OpenAL audio output [disable]
|
||||
--disable-nas disable NAS audio output [autodetect]
|
||||
--disable-sgiaudio disable SGI audio output [autodetect]
|
||||
--disable-sunaudio disable Sun audio output [autodetect]
|
||||
--disable-win32waveout disable Windows waveout audio output [autodetect]
|
||||
--disable-coreaudio disable CoreAudio audio output [autodetect]
|
||||
@ -599,10 +596,9 @@ _iconv=auto
|
||||
_langinfo=auto
|
||||
_rtc=auto
|
||||
_ossaudio=auto
|
||||
_arts=auto
|
||||
_esd=auto
|
||||
_rsound=auto
|
||||
_pulse=auto
|
||||
_portaudio=auto
|
||||
_jack=auto
|
||||
_openal=no
|
||||
_libcdio=auto
|
||||
@ -637,7 +633,6 @@ _mga=auto
|
||||
_xmga=auto
|
||||
_vm=auto
|
||||
_xf86keysym=auto
|
||||
_sgiaudio=auto
|
||||
_sunaudio=auto
|
||||
_alsa=auto
|
||||
_fastmemcpy=yes
|
||||
@ -918,14 +913,12 @@ for ac_option do
|
||||
--disable-libdv) _libdv=no ;;
|
||||
--enable-ossaudio) _ossaudio=yes ;;
|
||||
--disable-ossaudio) _ossaudio=no ;;
|
||||
--enable-arts) _arts=yes ;;
|
||||
--disable-arts) _arts=no ;;
|
||||
--enable-esd) _esd=yes ;;
|
||||
--disable-esd) _esd=no ;;
|
||||
--enable-rsound) _rsound=yes ;;
|
||||
--disable-rsound) _rsound=no ;;
|
||||
--enable-pulse) _pulse=yes ;;
|
||||
--disable-pulse) _pulse=no ;;
|
||||
--enable-portaudio) _portaudio=yes ;;
|
||||
--disable-portaudio) _portaudio=no ;;
|
||||
--enable-jack) _jack=yes ;;
|
||||
--disable-jack) _jack=no ;;
|
||||
--enable-openal) _openal=yes ;;
|
||||
@ -992,8 +985,6 @@ for ac_option do
|
||||
--disable-xf86keysym) _xf86keysym=no ;;
|
||||
--enable-sunaudio) _sunaudio=yes ;;
|
||||
--disable-sunaudio) _sunaudio=no ;;
|
||||
--enable-sgiaudio) _sgiaudio=yes ;;
|
||||
--disable-sgiaudio) _sgiaudio=no ;;
|
||||
--enable-alsa) _alsa=yes ;;
|
||||
--disable-alsa) _alsa=no ;;
|
||||
--enable-tv) _tv=yes ;;
|
||||
@ -1254,9 +1245,6 @@ if test -z "$_target" ; then
|
||||
Haiku)
|
||||
system_name=BeOS
|
||||
;;
|
||||
IRIX*)
|
||||
system_name=IRIX
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
system_name=FreeBSD
|
||||
;;
|
||||
@ -1355,9 +1343,6 @@ fi
|
||||
|
||||
if darwin; then
|
||||
extra_cflags="-mdynamic-no-pic $extra_cflags"
|
||||
if test "$(basename $_cc)" != "clang" ; then
|
||||
extra_cflags="-falign-loops=16 -shared-libgcc $extra_cflags"
|
||||
fi
|
||||
_timer=timer-darwin.c
|
||||
fi
|
||||
|
||||
@ -1365,9 +1350,7 @@ if aix ; then
|
||||
extra_ldflags="$extra_ldflags -lC"
|
||||
fi
|
||||
|
||||
if irix ; then
|
||||
_ranlib='ar -r'
|
||||
elif linux ; then
|
||||
if linux ; then
|
||||
_ranlib='true'
|
||||
fi
|
||||
|
||||
@ -2169,24 +2152,6 @@ EOF
|
||||
arch='mips'
|
||||
iproc='mips'
|
||||
|
||||
if irix ; then
|
||||
echocheck "CPU type"
|
||||
proc=$(hinv -c processor | grep CPU | cut -d " " -f3)
|
||||
case "$(echo $proc)" in
|
||||
R3000) _march='-mips1' _mcpu='-mtune=r2000' ;;
|
||||
R4000) _march='-mips3' _mcpu='-mtune=r4000' ;;
|
||||
R4400) _march='-mips3' _mcpu='-mtune=r4400' ;;
|
||||
R4600) _march='-mips3' _mcpu='-mtune=r4600' ;;
|
||||
R5000) _march='-mips4' _mcpu='-mtune=r5000' ;;
|
||||
R8000|R10000|R12000|R14000|R16000) _march='-mips4' _mcpu='-mtune=r8000' ;;
|
||||
esac
|
||||
# gcc < 3.x does not support -mtune.
|
||||
if test "$cc_vendor" = "gnu" && test "$_cc_major" -lt 3 ; then
|
||||
_mcpu=''
|
||||
fi
|
||||
echores "$proc"
|
||||
fi
|
||||
|
||||
test $_fast_clz = "auto" && _fast_clz=yes
|
||||
|
||||
;;
|
||||
@ -2339,6 +2304,10 @@ else
|
||||
warn_cflags=yes
|
||||
fi
|
||||
|
||||
if darwin && test "$cc_vendor" = "gnu" ; then
|
||||
extra_cflags="-falign-loops=16 -shared-libgcc $extra_cflags"
|
||||
fi
|
||||
|
||||
if test "$cc_vendor" = "gnu" ; then
|
||||
cflag_check -Wundef && WARNFLAGS="-Wundef $WARNFLAGS"
|
||||
# -std=gnu99 is not a warning flag but is placed in WARN_CFLAGS because
|
||||
@ -4004,12 +3973,11 @@ echocheck "VDPAU"
|
||||
if test "$_vdpau" = auto && test "$_x11" = yes ; then
|
||||
_vdpau=no
|
||||
if test "$_dl" = yes ; then
|
||||
return_statement_check vdpau/vdpau_x11.h 'vdp_device_create_x11(0, 0, 0, 0)' VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 -lvdpau && _vdpau=yes
|
||||
pkg_config_add 'vdpau >= 0.2' && _vdpau=yes
|
||||
fi
|
||||
fi
|
||||
if test "$_vdpau" = yes ; then
|
||||
def_vdpau='#define CONFIG_VDPAU 1'
|
||||
libs_mplayer="$libs_mplayer -lvdpau"
|
||||
vomodules="vdpau $vomodules"
|
||||
else
|
||||
def_vdpau='#define CONFIG_VDPAU 0'
|
||||
@ -4274,11 +4242,6 @@ fi
|
||||
echocheck "PNG support"
|
||||
if test "$_png" = auto ; then
|
||||
_png=no
|
||||
if irix ; then
|
||||
# Don't check for -lpng on irix since it has its own libpng
|
||||
# incompatible with the GNU libpng
|
||||
res_comment="disabled on irix (not GNU libpng)"
|
||||
else
|
||||
cat > $TMPC << EOF
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -4291,7 +4254,6 @@ int main(void) {
|
||||
EOF
|
||||
cc_check -lpng -lz $_ld_lm && _png=yes
|
||||
fi
|
||||
fi
|
||||
echores "$_png"
|
||||
if test "$_png" = yes ; then
|
||||
def_png='#define CONFIG_PNG 1'
|
||||
@ -4561,6 +4523,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
EOF
|
||||
_gl=no
|
||||
if test "$_x11" = yes ; then
|
||||
for _ld_tmp in "" -lGL "-lGL -lXdamage" "-lGL $_ld_pthread" ; do
|
||||
if cc_check $_ld_tmp $_ld_lm ; then
|
||||
_gl=yes
|
||||
@ -4569,7 +4532,8 @@ EOF
|
||||
break
|
||||
fi
|
||||
done
|
||||
if cc_check -DGL_WIN32 -lopengl32 ; then
|
||||
fi
|
||||
if win32 && cc_check -DGL_WIN32 -lopengl32 ; then
|
||||
_gl=yes
|
||||
_gl_win32=yes
|
||||
libs_mplayer="$libs_mplayer -lopengl32 -lgdi32"
|
||||
@ -4806,51 +4770,6 @@ fi
|
||||
echores "$_ossaudio"
|
||||
|
||||
|
||||
echocheck "aRts"
|
||||
if test "$_arts" = auto ; then
|
||||
_arts=no
|
||||
if ( artsc-config --version ) >> "$TMPLOG" 2>&1 ; then
|
||||
statement_check artsc.h 'arts_init()' $(artsc-config --libs) $(artsc-config --cflags) &&
|
||||
_arts=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$_arts" = yes ; then
|
||||
def_arts='#define CONFIG_ARTS 1'
|
||||
aomodules="arts $aomodules"
|
||||
libs_mplayer="$libs_mplayer $(artsc-config --libs)"
|
||||
extra_cflags="$extra_cflags $(artsc-config --cflags)"
|
||||
else
|
||||
noaomodules="arts $noaomodules"
|
||||
fi
|
||||
echores "$_arts"
|
||||
|
||||
|
||||
echocheck "EsounD"
|
||||
if test "$_esd" = auto ; then
|
||||
_esd=no
|
||||
if ( esd-config --version ) >> "$TMPLOG" 2>&1 ; then
|
||||
statement_check esd.h 'esd_open_sound("test")' $(esd-config --libs) $(esd-config --cflags) && _esd=yes
|
||||
fi
|
||||
fi
|
||||
echores "$_esd"
|
||||
|
||||
if test "$_esd" = yes ; then
|
||||
def_esd='#define CONFIG_ESD 1'
|
||||
aomodules="esd $aomodules"
|
||||
libs_mplayer="$libs_mplayer $(esd-config --libs)"
|
||||
extra_cflags="$extra_cflags $(esd-config --cflags)"
|
||||
|
||||
echocheck "esd_get_latency()"
|
||||
statement_check esd.h 'esd_get_latency(0)' $(esd-config --libs) $(esd-config --cflags) &&
|
||||
_esd_latency=yes && def_esd_latency='#define CONFIG_ESD_LATENCY 1'
|
||||
echores "$_esd_latency"
|
||||
else
|
||||
def_esd='#undef CONFIG_ESD'
|
||||
def_esd_latency='#undef CONFIG_ESD_LATENCY'
|
||||
noaomodules="esd $noaomodules"
|
||||
fi
|
||||
|
||||
echocheck "RSound"
|
||||
if test "$_rsound" = auto ; then
|
||||
_rsound=no
|
||||
@ -4902,6 +4821,28 @@ else
|
||||
fi
|
||||
|
||||
|
||||
echocheck "PortAudio"
|
||||
if test "$_portaudio" = auto && test "$_pthreads" != yes ; then
|
||||
_portaudio=no
|
||||
res_comment="pthreads not enabled"
|
||||
fi
|
||||
if test "$_portaudio" = auto ; then
|
||||
_portaudio=no
|
||||
if pkg_config_add 'portaudio-2.0 >= 19' ; then
|
||||
_portaudio=yes
|
||||
fi
|
||||
fi
|
||||
echores "$_portaudio"
|
||||
|
||||
if test "$_portaudio" = yes ; then
|
||||
def_portaudio='#define CONFIG_PORTAUDIO 1'
|
||||
aomodules="portaudio $aomodules"
|
||||
else
|
||||
def_portaudio='#undef CONFIG_PORTAUDIO'
|
||||
noaomodules="portaudio $noaomodules"
|
||||
fi
|
||||
|
||||
|
||||
echocheck "JACK"
|
||||
if test "$_jack" = auto ; then
|
||||
_jack=no
|
||||
@ -5012,24 +4953,6 @@ echores $_coreaudio
|
||||
fi #if darwin
|
||||
|
||||
|
||||
if irix; then
|
||||
echocheck "SGI audio"
|
||||
if test "$_sgiaudio" = auto ; then
|
||||
_sgiaudio=no
|
||||
header_check dmedia/audio.h && _sgiaudio=yes
|
||||
fi
|
||||
if test "$_sgiaudio" = "yes" ; then
|
||||
def_sgiaudio='#define CONFIG_SGI_AUDIO 1'
|
||||
libs_mplayer="$libs_mplayer -laudio"
|
||||
aomodules="sgi $aomodules"
|
||||
else
|
||||
def_sgiaudio='#undef CONFIG_SGI_AUDIO'
|
||||
noaomodules="sgi $noaomodules"
|
||||
fi
|
||||
echores "$_sgiaudio"
|
||||
fi #if irix
|
||||
|
||||
|
||||
# set default CD/DVD devices
|
||||
if win32 ; then
|
||||
default_cdrom_device="D:"
|
||||
@ -5085,11 +5008,10 @@ echores "$_vcd"
|
||||
echocheck "Blu-ray support"
|
||||
if test "$_bluray" = auto ; then
|
||||
_bluray=no
|
||||
statement_check libbluray/bluray.h 'bd_get_title_info(0, 0, 0)' -lbluray && _bluray=yes
|
||||
pkg_config_add 'libbluray >= 0.2.1' && _bluray=yes
|
||||
fi
|
||||
if test "$_bluray" = yes ; then
|
||||
def_bluray='#define CONFIG_LIBBLURAY 1'
|
||||
extra_ldflags="$extra_ldflags -lbluray"
|
||||
inputmodules="bluray $inputmodules"
|
||||
else
|
||||
def_bluray='#undef CONFIG_LIBBLURAY'
|
||||
@ -5411,12 +5333,13 @@ else
|
||||
fi
|
||||
echores "$_theora"
|
||||
|
||||
# Any version of libmpg123 shall be fine.
|
||||
# Any version of libmpg123 that knows MPG123_RESYNC_LIMIT shall be fine.
|
||||
# That is, 1.2.0 onwards. Recommened is 1.14 onwards, though.
|
||||
echocheck "mpg123 support"
|
||||
def_mpg123='#undef CONFIG_MPG123'
|
||||
if test "$_mpg123" = auto; then
|
||||
_mpg123=no
|
||||
statement_check mpg123.h 'mpg123_init()' -lmpg123 && _mpg123=yes && extra_ldflags="$extra_ldflags -lmpg123"
|
||||
pkg_config_add 'libmpg123 >= 1.2.0' && _mpg123=yes
|
||||
fi
|
||||
if test "$_mpg123" = yes ; then
|
||||
def_mpg123='#define CONFIG_MPG123 1'
|
||||
@ -6379,7 +6302,6 @@ AA = $_aa
|
||||
ALSA = $_alsa
|
||||
APPLE_IR = $_apple_ir
|
||||
APPLE_REMOTE = $_apple_remote
|
||||
ARTS = $_arts
|
||||
AUDIO_INPUT = $_audio_input
|
||||
BITMAP_FONT = $_bitmap_font
|
||||
BL = $_bl
|
||||
@ -6400,7 +6322,6 @@ DVDNAV_INTERNAL = $dvdnav_internal
|
||||
DVDREAD = $_dvdread
|
||||
DVDREAD_INTERNAL = $_dvdread_internal
|
||||
DXR3 = $_dxr3
|
||||
ESD = $_esd
|
||||
FAAD = $_faad
|
||||
FASTMEMCPY = $_fastmemcpy
|
||||
FBDEV = $_fbdev
|
||||
@ -6452,6 +6373,7 @@ PNG = $_png
|
||||
PNM = $_pnm
|
||||
PRIORITY = $_priority
|
||||
PULSE = $_pulse
|
||||
PORTAUDIO = $_portaudio
|
||||
PVR = $_pvr
|
||||
QTX_CODECS = $_qtx
|
||||
QTX_CODECS_WIN32 = $_qtx_codecs_win32
|
||||
@ -6464,7 +6386,6 @@ S3FB = $_s3fb
|
||||
SDL = $_sdl
|
||||
SPEEX = $_speex
|
||||
STREAM_CACHE = $_stream_cache
|
||||
SGIAUDIO = $_sgiaudio
|
||||
SUNAUDIO = $_sunaudio
|
||||
SVGA = $_svga
|
||||
TDFXFB = $_tdfxfb
|
||||
@ -6717,10 +6638,7 @@ $def_xmms
|
||||
|
||||
/* Audio output drivers */
|
||||
$def_alsa
|
||||
$def_arts
|
||||
$def_coreaudio
|
||||
$def_esd
|
||||
$def_esd_latency
|
||||
$def_jack
|
||||
$def_nas
|
||||
$def_openal
|
||||
@ -6729,8 +6647,8 @@ $def_ossaudio
|
||||
$def_ossaudio_devdsp
|
||||
$def_ossaudio_devmixer
|
||||
$def_pulse
|
||||
$def_portaudio
|
||||
$def_rsound
|
||||
$def_sgiaudio
|
||||
$def_sunaudio
|
||||
$def_win32waveout
|
||||
|
||||
|
@ -631,16 +631,16 @@ struct input_ctx {
|
||||
|
||||
int async_quit_request;
|
||||
|
||||
static int print_key_list(m_option_t *cfg);
|
||||
static int print_cmd_list(m_option_t *cfg);
|
||||
static int print_key_list(m_option_t *cfg, char *optname, char *optparam);
|
||||
static int print_cmd_list(m_option_t *cfg, char *optname, char *optparam);
|
||||
|
||||
// Our command line options
|
||||
static const m_option_t input_conf[] = {
|
||||
OPT_STRING("conf", input.config_file, CONF_GLOBAL),
|
||||
OPT_INT("ar-delay", input.ar_delay, CONF_GLOBAL),
|
||||
OPT_INT("ar-rate", input.ar_rate, CONF_GLOBAL),
|
||||
{ "keylist", print_key_list, CONF_TYPE_FUNC, CONF_GLOBAL, 0, 0, NULL },
|
||||
{ "cmdlist", print_cmd_list, CONF_TYPE_FUNC, CONF_GLOBAL, 0, 0, NULL },
|
||||
{ "keylist", print_key_list, CONF_TYPE_PRINT_FUNC, CONF_NOCFG },
|
||||
{ "cmdlist", print_cmd_list, CONF_TYPE_PRINT_FUNC, CONF_NOCFG },
|
||||
OPT_STRING("js-dev", input.js_dev, CONF_GLOBAL),
|
||||
OPT_STRING("ar-dev", input.ar_dev, CONF_GLOBAL),
|
||||
OPT_STRING("file", input.in_file, CONF_GLOBAL),
|
||||
@ -1915,16 +1915,16 @@ void mp_input_register_options(m_config_t *cfg)
|
||||
m_config_register_options(cfg, mp_input_opts);
|
||||
}
|
||||
|
||||
static int print_key_list(m_option_t *cfg)
|
||||
static int print_key_list(m_option_t *cfg, char *optname, char *optparam)
|
||||
{
|
||||
int i;
|
||||
printf("\n");
|
||||
for (i = 0; key_names[i].name != NULL; i++)
|
||||
printf("%s\n", key_names[i].name);
|
||||
exit(0);
|
||||
return M_OPT_EXIT;
|
||||
}
|
||||
|
||||
static int print_cmd_list(m_option_t *cfg)
|
||||
static int print_cmd_list(m_option_t *cfg, char *optname, char *optparam)
|
||||
{
|
||||
const mp_cmd_t *cmd;
|
||||
int i, j;
|
||||
@ -1953,7 +1953,7 @@ static int print_cmd_list(m_option_t *cfg)
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
exit(0);
|
||||
return M_OPT_EXIT;
|
||||
}
|
||||
|
||||
void mp_input_wakeup(struct input_ctx *ictx)
|
||||
|
148
libao2/ao_arts.c
148
libao2/ao_arts.c
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* aRts audio output driver for MPlayer
|
||||
*
|
||||
* copyright (c) 2002 Michele Balistreri <brain87@gmx.net>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <artsc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "audio_out.h"
|
||||
#include "audio_out_internal.h"
|
||||
#include "libaf/af_format.h"
|
||||
#include "mp_msg.h"
|
||||
|
||||
#define OBTAIN_BITRATE(a) (((a != AF_FORMAT_U8) && (a != AF_FORMAT_S8)) ? 16 : 8)
|
||||
|
||||
/* Feel free to experiment with the following values: */
|
||||
#define ARTS_PACKETS 10 /* Number of audio packets */
|
||||
#define ARTS_PACKET_SIZE_LOG2 11 /* Log2 of audio packet size */
|
||||
|
||||
static arts_stream_t stream;
|
||||
|
||||
static const ao_info_t info =
|
||||
{
|
||||
"aRts audio output",
|
||||
"arts",
|
||||
"Michele Balistreri <brain87@gmx.net>",
|
||||
""
|
||||
};
|
||||
|
||||
LIBAO_EXTERN(arts)
|
||||
|
||||
static int control(int cmd, void *arg)
|
||||
{
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
static int init(int rate_hz, int channels, int format, int flags)
|
||||
{
|
||||
int err;
|
||||
int frag_spec;
|
||||
|
||||
if( (err=arts_init()) ) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] %s\n", arts_error_text(err));
|
||||
return 0;
|
||||
}
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Connected to sound server.\n");
|
||||
|
||||
/*
|
||||
* arts supports 8bit unsigned and 16bit signed sample formats
|
||||
* (16bit apparently in little endian format, even in the case
|
||||
* when artsd runs on a big endian cpu).
|
||||
*
|
||||
* Unsupported formats are translated to one of these two formats
|
||||
* using mplayer's audio filters.
|
||||
*/
|
||||
switch (format) {
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S8:
|
||||
format = AF_FORMAT_U8;
|
||||
break;
|
||||
default:
|
||||
format = AF_FORMAT_S16_LE; /* artsd always expects little endian?*/
|
||||
break;
|
||||
}
|
||||
|
||||
ao_data.format = format;
|
||||
ao_data.channels = channels;
|
||||
ao_data.samplerate = rate_hz;
|
||||
ao_data.bps = (rate_hz*channels);
|
||||
|
||||
if(format != AF_FORMAT_U8 && format != AF_FORMAT_S8)
|
||||
ao_data.bps*=2;
|
||||
|
||||
stream=arts_play_stream(rate_hz, OBTAIN_BITRATE(format), channels, "MPlayer");
|
||||
|
||||
if(stream == NULL) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] Unable to open a stream.\n");
|
||||
arts_free();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the stream to blocking: it will not block anyway, but it seems */
|
||||
/* to be working better */
|
||||
arts_stream_set(stream, ARTS_P_BLOCKING, 1);
|
||||
frag_spec = ARTS_PACKET_SIZE_LOG2 | ARTS_PACKETS << 16;
|
||||
arts_stream_set(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
|
||||
ao_data.buffersize = arts_stream_get(stream, ARTS_P_BUFFER_SIZE);
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Stream opened.\n");
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n",
|
||||
ao_data.buffersize);
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n",
|
||||
arts_stream_get(stream, ARTS_P_PACKET_SIZE));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void uninit(int immed)
|
||||
{
|
||||
arts_close_stream(stream);
|
||||
arts_free();
|
||||
}
|
||||
|
||||
static int play(void* data,int len,int flags)
|
||||
{
|
||||
return arts_write(stream, data, len);
|
||||
}
|
||||
|
||||
static void audio_pause(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void audio_resume(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void reset(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int get_space(void)
|
||||
{
|
||||
return arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
|
||||
}
|
||||
|
||||
static float get_delay(void)
|
||||
{
|
||||
return ((float) (ao_data.buffersize - arts_stream_get(stream,
|
||||
ARTS_P_BUFFER_SPACE))) / ((float) ao_data.bps);
|
||||
}
|
477
libao2/ao_esd.c
477
libao2/ao_esd.c
@ -1,477 +0,0 @@
|
||||
/*
|
||||
* EsounD audio output driver for MPlayer
|
||||
*
|
||||
* copyright (c) 2002 Juergen Keil <jk@tools.de>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO / known problems:
|
||||
* - does not work well when the esd daemon has autostandby disabled
|
||||
* (workaround: run esd with option "-as 2" - fortunatelly this is
|
||||
* the default)
|
||||
* - plays noise on a linux 2.4.4 kernel with a SB16PCI card, when using
|
||||
* a local tcp connection to the esd daemon; there is no noise when using
|
||||
* a unix domain socket connection.
|
||||
* (there are EIO errors reported by the sound card driver, so this is
|
||||
* most likely a linux sound card driver problem)
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#ifdef __svr4__
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
#include <esd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "audio_out.h"
|
||||
#include "audio_out_internal.h"
|
||||
#include "libaf/af_format.h"
|
||||
#include "mp_msg.h"
|
||||
|
||||
|
||||
#define ESD_RESAMPLES 0
|
||||
#define ESD_DEBUG 0
|
||||
|
||||
#if ESD_DEBUG
|
||||
#define dprintf(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...) /**/
|
||||
#endif
|
||||
|
||||
|
||||
#define ESD_CLIENT_NAME "MPlayer"
|
||||
#define ESD_MAX_DELAY (1.0f) /* max amount of data buffered in esd (#sec) */
|
||||
|
||||
static const ao_info_t info =
|
||||
{
|
||||
"EsounD audio output",
|
||||
"esd",
|
||||
"Juergen Keil <jk@tools.de>",
|
||||
""
|
||||
};
|
||||
|
||||
LIBAO_EXTERN(esd)
|
||||
|
||||
static int esd_fd = -1;
|
||||
static int esd_play_fd = -1;
|
||||
static esd_server_info_t *esd_svinfo;
|
||||
static int esd_latency;
|
||||
static int esd_bytes_per_sample;
|
||||
static unsigned long esd_samples_written;
|
||||
static struct timeval esd_play_start;
|
||||
extern float audio_delay;
|
||||
|
||||
/*
|
||||
* to set/get/query special features/parameters
|
||||
*/
|
||||
static int control(int cmd, void *arg)
|
||||
{
|
||||
esd_player_info_t *esd_pi;
|
||||
esd_info_t *esd_i;
|
||||
time_t now;
|
||||
static time_t vol_cache_time;
|
||||
static ao_control_vol_t vol_cache;
|
||||
|
||||
switch (cmd) {
|
||||
case AOCONTROL_GET_VOLUME:
|
||||
time(&now);
|
||||
if (now == vol_cache_time) {
|
||||
*(ao_control_vol_t *)arg = vol_cache;
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
dprintf("esd: get vol\n");
|
||||
if ((esd_i = esd_get_all_info(esd_fd)) == NULL)
|
||||
return CONTROL_ERROR;
|
||||
|
||||
for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next)
|
||||
if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0)
|
||||
break;
|
||||
|
||||
if (esd_pi != NULL) {
|
||||
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||
vol->left = esd_pi->left_vol_scale * 100 / ESD_VOLUME_BASE;
|
||||
vol->right = esd_pi->right_vol_scale * 100 / ESD_VOLUME_BASE;
|
||||
|
||||
vol_cache = *vol;
|
||||
vol_cache_time = now;
|
||||
}
|
||||
esd_free_all_info(esd_i);
|
||||
|
||||
return CONTROL_OK;
|
||||
|
||||
case AOCONTROL_SET_VOLUME:
|
||||
dprintf("esd: set vol\n");
|
||||
if ((esd_i = esd_get_all_info(esd_fd)) == NULL)
|
||||
return CONTROL_ERROR;
|
||||
|
||||
for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next)
|
||||
if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0)
|
||||
break;
|
||||
|
||||
if (esd_pi != NULL) {
|
||||
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||
esd_set_stream_pan(esd_fd, esd_pi->source_id,
|
||||
vol->left * ESD_VOLUME_BASE / 100,
|
||||
vol->right * ESD_VOLUME_BASE / 100);
|
||||
|
||||
vol_cache = *vol;
|
||||
time(&vol_cache_time);
|
||||
}
|
||||
esd_free_all_info(esd_i);
|
||||
return CONTROL_OK;
|
||||
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open & setup audio device
|
||||
* return: 1=success 0=fail
|
||||
*/
|
||||
static int init(int rate_hz, int channels, int format, int flags)
|
||||
{
|
||||
esd_format_t esd_fmt;
|
||||
int bytes_per_sample;
|
||||
int fl;
|
||||
char *server = ao_subdevice; /* NULL for localhost */
|
||||
float lag_seconds, lag_net = 0., lag_serv;
|
||||
struct timeval proto_start, proto_end;
|
||||
|
||||
global_ao->no_persistent_volume = true;
|
||||
|
||||
if (esd_fd < 0) {
|
||||
esd_fd = esd_open_sound(server);
|
||||
if (esd_fd < 0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ESD] esd_open_sound failed: %s\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get server info, and measure network latency */
|
||||
gettimeofday(&proto_start, NULL);
|
||||
esd_svinfo = esd_get_server_info(esd_fd);
|
||||
if(server) {
|
||||
gettimeofday(&proto_end, NULL);
|
||||
lag_net = (proto_end.tv_sec - proto_start.tv_sec) +
|
||||
(proto_end.tv_usec - proto_start.tv_usec) / 1000000.0;
|
||||
lag_net /= 2.0; /* round trip -> one way */
|
||||
} else
|
||||
lag_net = 0.0; /* no network lag */
|
||||
|
||||
/*
|
||||
if (esd_svinfo) {
|
||||
mp_msg(MSGT_AO, MSGL_INFO, "AO: [esd] server info:\n");
|
||||
esd_print_server_info(esd_svinfo);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
esd_fmt = ESD_STREAM | ESD_PLAY;
|
||||
|
||||
#if ESD_RESAMPLES
|
||||
/* let the esd daemon convert sample rate */
|
||||
#else
|
||||
/* let mplayer's audio filter convert the sample rate */
|
||||
if (esd_svinfo != NULL)
|
||||
rate_hz = esd_svinfo->rate;
|
||||
#endif
|
||||
ao_data.samplerate = rate_hz;
|
||||
|
||||
/* EsounD can play mono or stereo */
|
||||
switch (channels) {
|
||||
case 1:
|
||||
esd_fmt |= ESD_MONO;
|
||||
ao_data.channels = bytes_per_sample = 1;
|
||||
break;
|
||||
default:
|
||||
esd_fmt |= ESD_STEREO;
|
||||
ao_data.channels = bytes_per_sample = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* EsounD can play 8bit unsigned and 16bit signed native */
|
||||
switch (format) {
|
||||
case AF_FORMAT_S8:
|
||||
case AF_FORMAT_U8:
|
||||
esd_fmt |= ESD_BITS8;
|
||||
ao_data.format = AF_FORMAT_U8;
|
||||
break;
|
||||
default:
|
||||
esd_fmt |= ESD_BITS16;
|
||||
ao_data.format = AF_FORMAT_S16_NE;
|
||||
bytes_per_sample *= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* modify audio_delay depending on esd_latency
|
||||
* latency is number of samples @ 44.1khz stereo 16 bit
|
||||
* adjust according to rate_hz & bytes_per_sample
|
||||
*/
|
||||
#ifdef CONFIG_ESD_LATENCY
|
||||
esd_latency = esd_get_latency(esd_fd);
|
||||
#else
|
||||
esd_latency = ((channels == 1 ? 2 : 1) * ESD_DEFAULT_RATE *
|
||||
(ESD_BUF_SIZE + 64 * (4.0f / bytes_per_sample))
|
||||
) / rate_hz;
|
||||
esd_latency += ESD_BUF_SIZE * 2;
|
||||
#endif
|
||||
if(esd_latency > 0) {
|
||||
lag_serv = (esd_latency * 4.0f) / (bytes_per_sample * rate_hz);
|
||||
lag_seconds = lag_net + lag_serv;
|
||||
audio_delay += lag_seconds;
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO,"[AO ESD] latency: [server: %0.2fs, net: %0.2fs] (adjust %0.2fs)\n",
|
||||
lag_serv, lag_net, lag_seconds);
|
||||
}
|
||||
|
||||
esd_play_fd = esd_play_stream_fallback(esd_fmt, rate_hz,
|
||||
server, ESD_CLIENT_NAME);
|
||||
if (esd_play_fd < 0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ESD] failed to open ESD playback stream: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enable non-blocking i/o on the socket connection to the esd server */
|
||||
if ((fl = fcntl(esd_play_fd, F_GETFL)) >= 0)
|
||||
fcntl(esd_play_fd, F_SETFL, O_NDELAY|fl);
|
||||
|
||||
#if ESD_DEBUG
|
||||
{
|
||||
int sbuf, rbuf, len;
|
||||
len = sizeof(sbuf);
|
||||
getsockopt(esd_play_fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &len);
|
||||
len = sizeof(rbuf);
|
||||
getsockopt(esd_play_fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &len);
|
||||
dprintf("esd: send/receive socket buffer space %d/%d bytes\n",
|
||||
sbuf, rbuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
ao_data.bps = bytes_per_sample * rate_hz;
|
||||
ao_data.outburst = ao_data.bps > 100000 ? 4*ESD_BUF_SIZE : 2*ESD_BUF_SIZE;
|
||||
|
||||
esd_play_start.tv_sec = 0;
|
||||
esd_samples_written = 0;
|
||||
esd_bytes_per_sample = bytes_per_sample;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* close audio device
|
||||
*/
|
||||
static void uninit(int immed)
|
||||
{
|
||||
if (esd_play_fd >= 0) {
|
||||
esd_close(esd_play_fd);
|
||||
esd_play_fd = -1;
|
||||
}
|
||||
|
||||
if (esd_svinfo) {
|
||||
esd_free_server_info(esd_svinfo);
|
||||
esd_svinfo = NULL;
|
||||
}
|
||||
|
||||
if (esd_fd >= 0) {
|
||||
esd_close(esd_fd);
|
||||
esd_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* plays 'len' bytes of 'data'
|
||||
* it should round it down to outburst*n
|
||||
* return: number of bytes played
|
||||
*/
|
||||
static int play(void* data, int len, int flags)
|
||||
{
|
||||
int offs;
|
||||
int nwritten;
|
||||
int nsamples;
|
||||
int n;
|
||||
|
||||
/* round down buffersize to a multiple of ESD_BUF_SIZE bytes */
|
||||
len = len / ESD_BUF_SIZE * ESD_BUF_SIZE;
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
#define SINGLE_WRITE 0
|
||||
#if SINGLE_WRITE
|
||||
nwritten = write(esd_play_fd, data, len);
|
||||
#else
|
||||
for (offs = 0, nwritten=0; offs + ESD_BUF_SIZE <= len; offs += ESD_BUF_SIZE) {
|
||||
/*
|
||||
* note: we're writing to a non-blocking socket here.
|
||||
* A partial write means, that the socket buffer is full.
|
||||
*/
|
||||
n = write(esd_play_fd, (char*)data + offs, ESD_BUF_SIZE);
|
||||
if ( n < 0 ) {
|
||||
if ( errno != EAGAIN )
|
||||
dprintf("esd play: write failed: %s\n", strerror(errno));
|
||||
break;
|
||||
} else if ( n != ESD_BUF_SIZE ) {
|
||||
nwritten += n;
|
||||
break;
|
||||
} else
|
||||
nwritten += n;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (nwritten > 0) {
|
||||
if (!esd_play_start.tv_sec)
|
||||
gettimeofday(&esd_play_start, NULL);
|
||||
nsamples = nwritten / esd_bytes_per_sample;
|
||||
esd_samples_written += nsamples;
|
||||
|
||||
dprintf("esd play: %d %lu\n", nsamples, esd_samples_written);
|
||||
} else {
|
||||
dprintf("esd play: blocked / %lu\n", esd_samples_written);
|
||||
}
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* stop playing, keep buffers (for pause)
|
||||
*/
|
||||
static void audio_pause(void)
|
||||
{
|
||||
/*
|
||||
* not possible with esd. the esd daemom will continue playing
|
||||
* buffered data (not more than ESD_MAX_DELAY seconds of samples)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* resume playing, after audio_pause()
|
||||
*/
|
||||
static void audio_resume(void)
|
||||
{
|
||||
/*
|
||||
* not possible with esd.
|
||||
*
|
||||
* Let's hope the pause was long enough that the esd ran out of
|
||||
* buffered data; we restart our time based delay computation
|
||||
* for an audio resume.
|
||||
*/
|
||||
esd_play_start.tv_sec = 0;
|
||||
esd_samples_written = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* stop playing and empty buffers (for seeking/pause)
|
||||
*/
|
||||
static void reset(void)
|
||||
{
|
||||
#ifdef __svr4__
|
||||
/* throw away data buffered in the esd connection */
|
||||
if (ioctl(esd_play_fd, I_FLUSH, FLUSHW))
|
||||
perror("I_FLUSH");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* return: how many bytes can be played without blocking
|
||||
*/
|
||||
static int get_space(void)
|
||||
{
|
||||
struct timeval tmout;
|
||||
fd_set wfds;
|
||||
float current_delay;
|
||||
int space;
|
||||
|
||||
/*
|
||||
* Don't buffer too much data in the esd daemon.
|
||||
*
|
||||
* If we send too much, esd will block in write()s to the sound
|
||||
* device, and the consequence is a huge slow down for things like
|
||||
* esd_get_all_info().
|
||||
*/
|
||||
if ((current_delay = get_delay()) >= ESD_MAX_DELAY) {
|
||||
dprintf("esd get_space: too much data buffered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(esd_play_fd, &wfds);
|
||||
tmout.tv_sec = 0;
|
||||
tmout.tv_usec = 0;
|
||||
|
||||
if (select(esd_play_fd + 1, NULL, &wfds, NULL, &tmout) != 1)
|
||||
return 0;
|
||||
|
||||
if (!FD_ISSET(esd_play_fd, &wfds))
|
||||
return 0;
|
||||
|
||||
/* try to fill 50% of the remaining "free" buffer space */
|
||||
space = (ESD_MAX_DELAY - current_delay) * ao_data.bps * 0.5f;
|
||||
|
||||
/* round up to next multiple of ESD_BUF_SIZE */
|
||||
space = (space + ESD_BUF_SIZE-1) / ESD_BUF_SIZE * ESD_BUF_SIZE;
|
||||
|
||||
dprintf("esd get_space: %d\n", space);
|
||||
return space;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* return: delay in seconds between first and last sample in buffer
|
||||
*/
|
||||
static float get_delay(void)
|
||||
{
|
||||
struct timeval now;
|
||||
double buffered_samples_time;
|
||||
double play_time;
|
||||
|
||||
if (!esd_play_start.tv_sec)
|
||||
return 0;
|
||||
|
||||
buffered_samples_time = (float)esd_samples_written / ao_data.samplerate;
|
||||
gettimeofday(&now, NULL);
|
||||
play_time = now.tv_sec - esd_play_start.tv_sec;
|
||||
play_time += (now.tv_usec - esd_play_start.tv_usec) / 1000000.;
|
||||
|
||||
/* dprintf("esd delay: %f %f\n", play_time, buffered_samples_time); */
|
||||
|
||||
if (play_time > buffered_samples_time) {
|
||||
dprintf("esd: underflow\n");
|
||||
esd_play_start.tv_sec = 0;
|
||||
esd_samples_written = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dprintf("esd: get_delay %f\n", buffered_samples_time - play_time);
|
||||
return buffered_samples_time - play_time;
|
||||
}
|
431
libao2/ao_portaudio.c
Normal file
431
libao2/ao_portaudio.c
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* This file is part of mplayer2.
|
||||
*
|
||||
* mplayer2 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mplayer2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mplayer2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <libavutil/avutil.h>
|
||||
#include <portaudio.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "subopt-helper.h"
|
||||
#include "libaf/af_format.h"
|
||||
#include "mp_msg.h"
|
||||
#include "audio_out.h"
|
||||
|
||||
struct priv {
|
||||
PaStream *stream;
|
||||
int framelen;
|
||||
|
||||
pthread_mutex_t ring_mutex;
|
||||
|
||||
// protected by ring_mutex
|
||||
unsigned char *ring;
|
||||
int ring_size; // max size of the ring
|
||||
int read_pos; // points to first byte that can be read
|
||||
int read_len; // number of bytes that can be read
|
||||
double play_time; // time when last packet returned to PA is on speaker
|
||||
// 0 is N/A (0 is not a valid PA time value)
|
||||
int play_silence; // play this many bytes of silence, before real data
|
||||
bool play_remaining;// play what's left in the buffer, then stop stream
|
||||
};
|
||||
|
||||
struct format_map {
|
||||
int mp_format;
|
||||
PaSampleFormat pa_format;
|
||||
};
|
||||
|
||||
static const struct format_map format_maps[] = {
|
||||
// first entry is the default format
|
||||
{AF_FORMAT_S16_NE, paInt16},
|
||||
{AF_FORMAT_S24_NE, paInt24},
|
||||
{AF_FORMAT_S32_NE, paInt32},
|
||||
{AF_FORMAT_S8, paInt8},
|
||||
{AF_FORMAT_U8, paUInt8},
|
||||
{AF_FORMAT_FLOAT_NE, paFloat32},
|
||||
{AF_FORMAT_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
mp_msg(MSGT_AO, MSGL_FATAL,
|
||||
"\n-ao portaudio commandline help:\n"
|
||||
"Example: mplayer -ao portaudio:device=subdevice\n"
|
||||
"\nOptions:\n"
|
||||
" device=subdevice\n"
|
||||
" Audio device PortAudio should use. Devices can be listed\n"
|
||||
" with -ao portaudio:device=help\n"
|
||||
" The subdevice can be passed as index, or as complete name.\n");
|
||||
}
|
||||
|
||||
static bool check_pa_ret(int ret)
|
||||
{
|
||||
if (ret < 0) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] %s\n",
|
||||
Pa_GetErrorText(ret));
|
||||
if (ret == paUnanticipatedHostError) {
|
||||
const PaHostErrorInfo* hosterr = Pa_GetLastHostErrorInfo();
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Host error: %s\n",
|
||||
hosterr->errorText);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Amount of bytes that contain audio of the given duration, aligned to frames.
|
||||
static int seconds_to_bytes(struct ao *ao, double duration_seconds)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
int bytes = duration_seconds * ao->bps;
|
||||
if (bytes % priv->framelen)
|
||||
bytes += priv->framelen - (bytes % priv->framelen);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int to_int(const char *s, int return_on_error)
|
||||
{
|
||||
char *endptr;
|
||||
int res = strtol(s, &endptr, 10);
|
||||
return (s[0] && !endptr[0]) ? res : return_on_error;
|
||||
}
|
||||
|
||||
static int find_device(struct ao *ao, const char *name)
|
||||
{
|
||||
int help = strcmp(name, "help") == 0;
|
||||
int count = Pa_GetDeviceCount();
|
||||
check_pa_ret(count);
|
||||
int found = paNoDevice;
|
||||
int index = to_int(name, -1);
|
||||
if (help)
|
||||
mp_msg(MSGT_AO, MSGL_INFO, "PortAudio devices:\n");
|
||||
for (int n = 0; n < count; n++) {
|
||||
const PaDeviceInfo* info = Pa_GetDeviceInfo(n);
|
||||
if (help) {
|
||||
if (info->maxOutputChannels < 1)
|
||||
continue;
|
||||
mp_msg(MSGT_AO, MSGL_INFO, " %d '%s', %d channels, latency: %.2f "
|
||||
"ms, sample rate: %.0f\n", n, info->name,
|
||||
info->maxOutputChannels,
|
||||
info->defaultHighOutputLatency * 1000,
|
||||
info->defaultSampleRate);
|
||||
}
|
||||
if (strcmp(name, info->name) == 0 || n == index) {
|
||||
found = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == paNoDevice && !help)
|
||||
mp_msg(MSGT_AO, MSGL_FATAL, "[portaudio] Device '%s' not found!\n",
|
||||
name);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int ring_write(struct ao *ao, unsigned char *data, int len)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
int free = priv->ring_size - priv->read_len;
|
||||
int write_pos = (priv->read_pos + priv->read_len) % priv->ring_size;
|
||||
int write_len = FFMIN(len, free);
|
||||
int len1 = FFMIN(priv->ring_size - write_pos, write_len);
|
||||
int len2 = write_len - len1;
|
||||
|
||||
memcpy(priv->ring + write_pos, data, len1);
|
||||
memcpy(priv->ring, data + len1, len2);
|
||||
|
||||
priv->read_len += write_len;
|
||||
|
||||
return write_len;
|
||||
}
|
||||
|
||||
static int ring_read(struct ao *ao, unsigned char *data, int len)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
int read_len = FFMIN(len, priv->read_len);
|
||||
int len1 = FFMIN(priv->ring_size - priv->read_pos, read_len);
|
||||
int len2 = read_len - len1;
|
||||
|
||||
memcpy(data, priv->ring + priv->read_pos, len1);
|
||||
memcpy(data + len1, priv->ring, len2);
|
||||
|
||||
priv->read_len -= read_len;
|
||||
priv->read_pos = (priv->read_pos + read_len) % priv->ring_size;
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
static void fill_silence(unsigned char *ptr, int len)
|
||||
{
|
||||
memset(ptr, 0, len);
|
||||
}
|
||||
|
||||
static int stream_callback(const void *input,
|
||||
void *output_v,
|
||||
unsigned long frameCount,
|
||||
const PaStreamCallbackTimeInfo *timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData)
|
||||
{
|
||||
struct ao *ao = userData;
|
||||
struct priv *priv = ao->priv;
|
||||
int res = paContinue;
|
||||
unsigned char *output = output_v;
|
||||
int len_bytes = frameCount * priv->framelen;
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
// NOTE: PA + ALSA in dmix mode seems to pretend that there is no latency
|
||||
// (outputBufferDacTime == currentTime)
|
||||
priv->play_time = timeInfo->outputBufferDacTime
|
||||
+ len_bytes / (float)ao->bps;
|
||||
|
||||
if (priv->play_silence > 0) {
|
||||
int bytes = FFMIN(priv->play_silence, len_bytes);
|
||||
fill_silence(output, bytes);
|
||||
priv->play_silence -= bytes;
|
||||
len_bytes -= bytes;
|
||||
output += bytes;
|
||||
}
|
||||
int read = ring_read(ao, output, len_bytes);
|
||||
len_bytes -= read;
|
||||
output += read;
|
||||
|
||||
if (len_bytes > 0) {
|
||||
if (priv->play_remaining) {
|
||||
res = paComplete;
|
||||
priv->play_remaining = false;
|
||||
} else {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Buffer underflow!\n");
|
||||
}
|
||||
fill_silence(output, len_bytes);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
if (priv->stream) {
|
||||
if (!cut_audio && Pa_IsStreamActive(priv->stream) == 1) {
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
priv->play_remaining = true;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
|
||||
check_pa_ret(Pa_StopStream(priv->stream));
|
||||
}
|
||||
check_pa_ret(Pa_CloseStream(priv->stream));
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&priv->ring_mutex);
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
static int init(struct ao *ao, char *params)
|
||||
{
|
||||
struct priv *priv = talloc_zero(ao, struct priv);
|
||||
ao->priv = priv;
|
||||
|
||||
if (!check_pa_ret(Pa_Initialize()))
|
||||
return -1;
|
||||
|
||||
pthread_mutex_init(&priv->ring_mutex, NULL);
|
||||
|
||||
char *device = NULL;
|
||||
const opt_t subopts[] = {
|
||||
{"device", OPT_ARG_MSTRZ, &device, NULL},
|
||||
{NULL}
|
||||
};
|
||||
if (subopt_parse(params, subopts) != 0) {
|
||||
print_help();
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
int pa_device = Pa_GetDefaultOutputDevice();
|
||||
if (device)
|
||||
pa_device = find_device(ao, device);
|
||||
if (pa_device == paNoDevice)
|
||||
goto error_exit;
|
||||
|
||||
PaStreamParameters sp = {
|
||||
.device = pa_device,
|
||||
.channelCount = ao->channels,
|
||||
.suggestedLatency
|
||||
= Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
|
||||
};
|
||||
|
||||
const struct format_map *fmt = format_maps;
|
||||
while (fmt->pa_format) {
|
||||
if (fmt->mp_format == ao->format) {
|
||||
PaStreamParameters test = sp;
|
||||
test.sampleFormat = fmt->pa_format;
|
||||
if (Pa_IsFormatSupported(NULL, &test, ao->samplerate) == paNoError)
|
||||
break;
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
if (!fmt->pa_format) {
|
||||
mp_msg(MSGT_AO, MSGL_V,
|
||||
"[portaudio] Unsupported format, using default.\n");
|
||||
fmt = format_maps;
|
||||
}
|
||||
|
||||
ao->format = fmt->mp_format;
|
||||
sp.sampleFormat = fmt->pa_format;
|
||||
priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8);
|
||||
ao->bps = ao->samplerate * priv->framelen;
|
||||
|
||||
if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
|
||||
goto error_exit;
|
||||
if (!check_pa_ret(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate,
|
||||
paFramesPerBufferUnspecified, paNoFlag,
|
||||
stream_callback, ao)))
|
||||
goto error_exit;
|
||||
|
||||
priv->ring_size = seconds_to_bytes(ao, 0.5);
|
||||
priv->ring = talloc_zero_size(priv, priv->ring_size);
|
||||
|
||||
free(device);
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
uninit(ao, true);
|
||||
free(device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
int write_len = ring_write(ao, data, len);
|
||||
if (flags & AOPLAY_FINAL_CHUNK)
|
||||
priv->play_remaining = true;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
|
||||
if (Pa_IsStreamStopped(priv->stream) == 1)
|
||||
check_pa_ret(Pa_StartStream(priv->stream));
|
||||
|
||||
return write_len;
|
||||
}
|
||||
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
int free = priv->ring_size - priv->read_len;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
double stream_time = Pa_GetStreamTime(priv->stream);
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
|
||||
float buffer_latency = (priv->read_len + priv->play_silence)
|
||||
/ (float)ao->bps;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
|
||||
return buffer_latency + frame_time;
|
||||
}
|
||||
|
||||
static void reset(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
if (Pa_IsStreamStopped(priv->stream) != 1)
|
||||
check_pa_ret(Pa_AbortStream(priv->stream));
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
priv->read_len = 0;
|
||||
priv->read_pos = 0;
|
||||
priv->play_remaining = false;
|
||||
priv->play_time = 0;
|
||||
priv->play_silence = 0;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
}
|
||||
|
||||
static void pause(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
check_pa_ret(Pa_AbortStream(priv->stream));
|
||||
|
||||
double stream_time = Pa_GetStreamTime(priv->stream);
|
||||
|
||||
pthread_mutex_lock(&priv->ring_mutex);
|
||||
|
||||
// When playback resumes, replace the lost audio (due to dropping the
|
||||
// portaudio/driver/hardware internal buffers) with silence.
|
||||
float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
|
||||
priv->play_silence += seconds_to_bytes(ao, FFMAX(frame_time, 0));
|
||||
priv->play_time = 0;
|
||||
|
||||
pthread_mutex_unlock(&priv->ring_mutex);
|
||||
}
|
||||
|
||||
static void resume(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
check_pa_ret(Pa_StartStream(priv->stream));
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_portaudio = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"PortAudio",
|
||||
"portaudio",
|
||||
"wm4",
|
||||
"",
|
||||
},
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.reset = reset,
|
||||
.get_space = get_space,
|
||||
.play = play,
|
||||
.get_delay = get_delay,
|
||||
.pause = pause,
|
||||
.resume = resume,
|
||||
};
|
@ -186,7 +186,7 @@ static int init(struct ao *ao, char *params)
|
||||
ao->priv = priv;
|
||||
|
||||
if (params) {
|
||||
devarg = strdup(ao_subdevice);
|
||||
devarg = strdup(params);
|
||||
sink = strchr(devarg, ':');
|
||||
if (sink)
|
||||
*sink++ = 0;
|
||||
|
301
libao2/ao_sgi.c
301
libao2/ao_sgi.c
@ -1,301 +0,0 @@
|
||||
/*
|
||||
* SGI/IRIX audio output driver
|
||||
*
|
||||
* copyright (c) 2001 oliver.schoenbrunner@jku.at
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dmedia/audio.h>
|
||||
|
||||
#include "audio_out.h"
|
||||
#include "audio_out_internal.h"
|
||||
#include "mp_msg.h"
|
||||
#include "libaf/af_format.h"
|
||||
|
||||
static const ao_info_t info =
|
||||
{
|
||||
"sgi audio output",
|
||||
"sgi",
|
||||
"Oliver Schoenbrunner",
|
||||
""
|
||||
};
|
||||
|
||||
LIBAO_EXTERN(sgi)
|
||||
|
||||
|
||||
static ALconfig ao_config;
|
||||
static ALport ao_port;
|
||||
static int sample_rate;
|
||||
static int queue_size;
|
||||
static int bytes_per_frame;
|
||||
|
||||
/**
|
||||
* \param [in/out] format
|
||||
* \param [out] width
|
||||
*
|
||||
* \return the closest matching SGI AL sample format
|
||||
*
|
||||
* \note width is set to required per-channel sample width
|
||||
* format is updated to match the SGI AL sample format
|
||||
*/
|
||||
static int fmt2sgial(int *format, int *width) {
|
||||
int smpfmt = AL_SAMPFMT_TWOSCOMP;
|
||||
|
||||
/* SGI AL only supports float and signed integers in native
|
||||
* endianness. If this is something else, we must rely on the audio
|
||||
* filter to convert it to a compatible format. */
|
||||
|
||||
/* 24-bit audio is supported, but only with 32-bit alignment.
|
||||
* mplayer's 24-bit format is packed, unfortunately.
|
||||
* So we must upgrade 24-bit requests to 32 bits. Then we drop the
|
||||
* lowest 8 bits during playback. */
|
||||
|
||||
switch(*format) {
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S8:
|
||||
*width = AL_SAMPLE_8;
|
||||
*format = AF_FORMAT_S8;
|
||||
break;
|
||||
|
||||
case AF_FORMAT_U16_LE:
|
||||
case AF_FORMAT_U16_BE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S16_BE:
|
||||
*width = AL_SAMPLE_16;
|
||||
*format = AF_FORMAT_S16_NE;
|
||||
break;
|
||||
|
||||
case AF_FORMAT_U24_LE:
|
||||
case AF_FORMAT_U24_BE:
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S24_BE:
|
||||
case AF_FORMAT_U32_LE:
|
||||
case AF_FORMAT_U32_BE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
case AF_FORMAT_S32_BE:
|
||||
*width = AL_SAMPLE_24;
|
||||
*format = AF_FORMAT_S32_NE;
|
||||
break;
|
||||
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
*width = 4;
|
||||
*format = AF_FORMAT_FLOAT_NE;
|
||||
smpfmt = AL_SAMPFMT_FLOAT;
|
||||
break;
|
||||
|
||||
default:
|
||||
*width = AL_SAMPLE_16;
|
||||
*format = AF_FORMAT_S16_NE;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return smpfmt;
|
||||
}
|
||||
|
||||
// to set/get/query special features/parameters
|
||||
static int control(int cmd, void *arg){
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] control.\n");
|
||||
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
// open & setup audio device
|
||||
// return: 1=success 0=fail
|
||||
static int init(int rate, int channels, int format, int flags) {
|
||||
|
||||
int smpwidth, smpfmt;
|
||||
int rv = AL_DEFAULT_OUTPUT;
|
||||
|
||||
smpfmt = fmt2sgial(&format, &smpwidth);
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] init: Samplerate: %iHz Channels: %s Format %s\n", rate, (channels > 1) ? "Stereo" : "Mono", af_fmt2str_short(format));
|
||||
|
||||
{ /* from /usr/share/src/dmedia/audio/setrate.c */
|
||||
|
||||
double frate, realrate;
|
||||
ALpv x[2];
|
||||
|
||||
if(ao_subdevice) {
|
||||
rv = alGetResourceByName(AL_SYSTEM, ao_subdevice, AL_OUTPUT_DEVICE_TYPE);
|
||||
if (!rv) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] play: invalid device.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
frate = rate;
|
||||
|
||||
x[0].param = AL_RATE;
|
||||
x[0].value.ll = alDoubleToFixed(rate);
|
||||
x[1].param = AL_MASTER_CLOCK;
|
||||
x[1].value.i = AL_CRYSTAL_MCLK_TYPE;
|
||||
|
||||
if (alSetParams(rv,x, 2)<0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: setparams failed: %s\nCould not set desired samplerate.\n", alGetErrorString(oserror()));
|
||||
}
|
||||
|
||||
if (x[0].sizeOut < 0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: AL_RATE was not accepted on the given resource.\n");
|
||||
}
|
||||
|
||||
if (alGetParams(rv,x, 1)<0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: getparams failed: %s\n", alGetErrorString(oserror()));
|
||||
}
|
||||
|
||||
realrate = alFixedToDouble(x[0].value.ll);
|
||||
if (frate != realrate) {
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] init: samplerate is now %f (desired rate is %f)\n", realrate, frate);
|
||||
}
|
||||
sample_rate = (int)realrate;
|
||||
}
|
||||
|
||||
bytes_per_frame = channels * smpwidth;
|
||||
|
||||
ao_data.samplerate = sample_rate;
|
||||
ao_data.channels = channels;
|
||||
ao_data.format = format;
|
||||
ao_data.bps = sample_rate * bytes_per_frame;
|
||||
ao_data.buffersize=131072;
|
||||
ao_data.outburst = ao_data.buffersize/16;
|
||||
|
||||
ao_config = alNewConfig();
|
||||
|
||||
if (!ao_config) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: %s\n", alGetErrorString(oserror()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(alSetChannels(ao_config, channels) < 0 ||
|
||||
alSetWidth(ao_config, smpwidth) < 0 ||
|
||||
alSetSampFmt(ao_config, smpfmt) < 0 ||
|
||||
alSetQueueSize(ao_config, sample_rate) < 0 ||
|
||||
alSetDevice(ao_config, rv) < 0) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: %s\n", alGetErrorString(oserror()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao_port = alOpenPort("mplayer", "w", ao_config);
|
||||
|
||||
if (!ao_port) {
|
||||
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: Unable to open audio channel: %s\n", alGetErrorString(oserror()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// printf("ao_sgi, init: port %d config %d\n", ao_port, ao_config);
|
||||
queue_size = alGetQueueSize(ao_config);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(int immed) {
|
||||
|
||||
/* TODO: samplerate should be set back to the value before mplayer was started! */
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] uninit: ...\n");
|
||||
|
||||
if (ao_config) {
|
||||
alFreeConfig(ao_config);
|
||||
ao_config = NULL;
|
||||
}
|
||||
|
||||
if (ao_port) {
|
||||
if (!immed)
|
||||
while(alGetFilled(ao_port) > 0) sginap(1);
|
||||
alClosePort(ao_port);
|
||||
ao_port = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stop playing and empty buffers (for seeking/pause)
|
||||
static void reset(void) {
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] reset: ...\n");
|
||||
|
||||
alDiscardFrames(ao_port, queue_size);
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(void) {
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] audio_pause: ...\n");
|
||||
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(void) {
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] audio_resume: ...\n");
|
||||
|
||||
}
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
static int get_space(void) {
|
||||
|
||||
// printf("ao_sgi, get_space: (ao_outburst %d)\n", ao_data.outburst);
|
||||
// printf("ao_sgi, get_space: alGetFillable [%d] \n", alGetFillable(ao_port));
|
||||
|
||||
return alGetFillable(ao_port) * bytes_per_frame;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// plays 'len' bytes of 'data'
|
||||
// it should round it down to outburst*n
|
||||
// return: number of bytes played
|
||||
static int play(void* data, int len, int flags) {
|
||||
|
||||
/* Always process data in quadword-aligned chunks (64-bits). */
|
||||
const int plen = len / (sizeof(uint64_t) * bytes_per_frame);
|
||||
const int framecount = plen * sizeof(uint64_t);
|
||||
|
||||
// printf("ao_sgi, play: len %d flags %d (%d %d)\n", len, flags, ao_port, ao_config);
|
||||
// printf("channels %d\n", ao_data.channels);
|
||||
|
||||
if(ao_data.format == AF_FORMAT_S32_NE) {
|
||||
/* The zen of this is explained in fmt2sgial() */
|
||||
int32_t *smpls = data;
|
||||
const int32_t *smple = smpls + (framecount * ao_data.channels);
|
||||
while(smpls < smple)
|
||||
*smpls++ >>= 8;
|
||||
}
|
||||
|
||||
alWriteFrames(ao_port, data, framecount);
|
||||
|
||||
return framecount * bytes_per_frame;
|
||||
|
||||
}
|
||||
|
||||
// return: delay in seconds between first and last sample in buffer
|
||||
static float get_delay(void){
|
||||
|
||||
// printf("ao_sgi, get_delay: (ao_buffersize %d)\n", ao_buffersize);
|
||||
|
||||
// return (float)queue_size/((float)sample_rate);
|
||||
const int outstanding = alGetFilled(ao_port);
|
||||
return (float)((outstanding < 0) ? queue_size : outstanding) /
|
||||
((float)sample_rate);
|
||||
}
|
@ -55,6 +55,7 @@ extern const struct ao_driver audio_out_v4l2;
|
||||
extern const struct ao_driver audio_out_mpegpes;
|
||||
extern const struct ao_driver audio_out_pcm;
|
||||
extern const struct ao_driver audio_out_pss;
|
||||
extern const struct ao_driver audio_out_portaudio;
|
||||
|
||||
static const struct ao_driver * const audio_out_drivers[] = {
|
||||
// native:
|
||||
@ -82,19 +83,13 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#ifdef CONFIG_OSS_AUDIO
|
||||
&audio_out_oss,
|
||||
#endif
|
||||
#ifdef CONFIG_SGI_AUDIO
|
||||
&audio_out_sgi,
|
||||
#ifdef CONFIG_PORTAUDIO
|
||||
&audio_out_portaudio,
|
||||
#endif
|
||||
#ifdef CONFIG_SUN_AUDIO
|
||||
&audio_out_sun,
|
||||
#endif
|
||||
// wrappers:
|
||||
#ifdef CONFIG_ARTS
|
||||
&audio_out_arts,
|
||||
#endif
|
||||
#ifdef CONFIG_ESD
|
||||
&audio_out_esd,
|
||||
#endif
|
||||
#ifdef CONFIG_JACK
|
||||
&audio_out_jack,
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MPEG 1.0/2.0/2.5 audio layer I, II, III decoding with libmpg123
|
||||
*
|
||||
* Copyright (C) 2010 Thomas Orgis <thomas@orgis.org>
|
||||
* Copyright (C) 2010-2012 Thomas Orgis <thomas@orgis.org>
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -38,36 +38,20 @@ LIBAD_EXTERN(mpg123)
|
||||
|
||||
#include "libvo/fastmemcpy.h"
|
||||
|
||||
/* We avoid any usage of mpg123 API that is sensitive to the large file
|
||||
* support setting. This ensures compatibility with a wide range of libmpg123
|
||||
* installs. This code is intended to work with version 1.0.0 of libmpg123.
|
||||
*
|
||||
* Though the chosen API subset is not affected by the choice of large file
|
||||
* support, the mpg123 header (old versions of which) might include a check
|
||||
* for matching _FILE_OFFSET_BITS. Since MPlayer does always define this one
|
||||
* for large file support, we are safe for any default mpg123 install that
|
||||
* either doesn't have such checks or defaults to the large value of
|
||||
* _FILE_OFFSET_BITS .
|
||||
* So, in short: There's no worry unless you have a non-default libmpg123
|
||||
* with intentionally disabled large file support. */
|
||||
/* You might need to #undef _FILE_OFFSET_BITS here on a 64 bit system
|
||||
with released mpg123 1.12 when using callback API. SVN snapshots
|
||||
should work fine. */
|
||||
/* Reducing the ifdeffery to two main variants:
|
||||
* 1. most compatible to any libmpg123 version
|
||||
* 2. fastest variant with recent libmpg123 (>=1.14)
|
||||
* Running variant 2 on older libmpg123 versions may work in
|
||||
* principle, but is not supported.
|
||||
* So, please leave the check for MPG123_API_VERSION there, m-kay?
|
||||
*/
|
||||
#include <mpg123.h>
|
||||
|
||||
/* Selection of mpg123 usage patterns:
|
||||
* AD_MPG123_CALLBACK: Use callback API instead of feeding of memory buffers.
|
||||
* That needs mpg123>=1.12, on x86-64 SVN snapshot because of
|
||||
* _FILE_OFFSET_BITS being defined (see above).
|
||||
* AD_MPG123_PACKET: Use packet-based input (including pts handling).
|
||||
* AD_MPG123_SEEKBUFFER: Use internal mpg123 buffer to enhance stream parsing.
|
||||
* Makes sense with callback API only.
|
||||
* Any of those might affect I/O performance, might be significant compared
|
||||
* to the excessively optimized decoding.
|
||||
*/
|
||||
/* #define AD_MPG123_CALLBACK */
|
||||
#define AD_MPG123_PACKET
|
||||
/* #define AD_MPG123_SEEKBUFFER */
|
||||
/* Enable faster mode of operation with newer libmpg123, avoiding
|
||||
* unnecessary memcpy() calls. */
|
||||
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION >= 33)
|
||||
#define AD_MPG123_FRAMEWISE
|
||||
#endif
|
||||
|
||||
/* Switch for updating bitrate info of VBR files. Not essential. */
|
||||
#define AD_MPG123_MEAN_BITRATE
|
||||
@ -87,77 +71,8 @@ struct ad_mpg123_context {
|
||||
#endif
|
||||
/* If the stream is actually VBR. */
|
||||
char vbr;
|
||||
#if (defined AD_MPG123_CALLBACK) && (defined AD_MPG123_PACKET)
|
||||
unsigned char *packet;
|
||||
int packleft;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void context_reset(struct ad_mpg123_context *con)
|
||||
{
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
con->mean_rate = 0.;
|
||||
con->mean_count = 0;
|
||||
con->delay = 1;
|
||||
#endif
|
||||
#if (defined AD_MPG123_CALLBACK) && (defined AD_MPG123_PACKET)
|
||||
con->packet = NULL;
|
||||
con->packleft = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef AD_MPG123_CALLBACK
|
||||
/* Mpg123 calls that for retrieving data.
|
||||
* This wrapper is at least needed for the call frame (ssize_t vs. int). */
|
||||
static ssize_t read_callback(void *ash, void *buf, size_t count)
|
||||
{
|
||||
sh_audio_t *sh = ash;
|
||||
#ifdef AD_MPG123_PACKET
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
unsigned char *target = buf;
|
||||
int need = count;
|
||||
ssize_t got = 0;
|
||||
while (need > 0) {
|
||||
if (con->packleft > 0) {
|
||||
int get = need > con->packleft ? con->packleft : need;
|
||||
/* Any difference to normal memcpy? */
|
||||
fast_memcpy(target, con->packet, get);
|
||||
/* OK, that does look redundant. */
|
||||
con->packet += get;
|
||||
con->packleft -= get;
|
||||
target += get;
|
||||
need -= get;
|
||||
got += get;
|
||||
} else {
|
||||
double pts;
|
||||
/* Feed more input data. */
|
||||
con->packleft = ds_get_packet_pts(sh->ds, &con->packet, &pts);
|
||||
if (con->packleft <= 0)
|
||||
break; /* Apparently that's it. EOF. */
|
||||
|
||||
/* Next bytes from that presentation time. */
|
||||
if (pts != MP_NOPTS_VALUE) {
|
||||
sh->pts = pts;
|
||||
sh->pts_bytes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return got;
|
||||
#else
|
||||
/* It returns int... with the meaning of byte count. */
|
||||
return (ssize_t) demux_read_data(sh->ds, buf, count);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Arbitrary input seeking is not supported with this MPlayer API(?).
|
||||
That also means that we won't read any ID3v1 tags. */
|
||||
static off_t seek_callback(void *sh, off_t pos, int whence)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This initializes libmpg123 and prepares the handle, including funky
|
||||
* parameters. */
|
||||
static int preinit(sh_audio_t *sh)
|
||||
@ -173,8 +88,6 @@ static int preinit(sh_audio_t *sh)
|
||||
|
||||
sh->context = malloc(sizeof(struct ad_mpg123_context));
|
||||
con = sh->context;
|
||||
context_reset(con);
|
||||
|
||||
/* Auto-choice of optimized decoder (first argument NULL). */
|
||||
con->handle = mpg123_new(NULL, &err);
|
||||
if (!con->handle)
|
||||
@ -188,17 +101,6 @@ static int preinit(sh_audio_t *sh)
|
||||
if (mpg123_param(con->handle, MPG123_ADD_FLAGS, flag, 0.0) != MPG123_OK)
|
||||
goto bad_end;
|
||||
#endif
|
||||
#ifdef AD_MPG123_CALLBACK
|
||||
/* The I/O is handled via callbacks to MPlayer stream functions,
|
||||
* actually only the reading, as general seeking does not seem to be available */
|
||||
if (mpg123_replace_reader_handle(con->handle, read_callback,
|
||||
seek_callback, NULL) != MPG123_OK) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 error: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
mpg123_exit();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Basic settings.
|
||||
* Don't spill messages, enable better resync with non-seekable streams.
|
||||
@ -206,10 +108,6 @@ static int preinit(sh_audio_t *sh)
|
||||
* old libmpg123. Generally, it is not fatal if the flags are not
|
||||
* honored */
|
||||
mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0);
|
||||
/* Old headers don't know MPG123_SEEKBUFFER yet, so use the plain 0x100. */
|
||||
#ifdef AD_MPG123_SEEKBUFFER
|
||||
mpg123_param(con->handle, MPG123_ADD_FLAGS, 0x100, 0.0);
|
||||
#endif
|
||||
/* Do not bail out on malformed streams at all.
|
||||
* MPlayer does not handle a decoder throwing the towel on crappy input. */
|
||||
mpg123_param(con->handle, MPG123_RESYNC_LIMIT, -1, 0.0);
|
||||
@ -220,9 +118,25 @@ static int preinit(sh_audio_t *sh)
|
||||
* Don't forget to eventually enable ReplayGain/RVA support, too.
|
||||
* Let's try to run with the default for now. */
|
||||
|
||||
/* That would produce floating point output.
|
||||
* You can get 32 and 24 bit ints, even 8 bit via format matrix. */
|
||||
/* mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.); */
|
||||
|
||||
/* Example for RVA choice (available since libmpg123 1.0.0):
|
||||
mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Prevent funky automatic resampling.
|
||||
* This way, we can be sure that one frame will never produce
|
||||
* more than 1152 stereo samples. */
|
||||
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
|
||||
#else
|
||||
/* Older mpg123 is vulnerable to concatenated streams when gapless cutting
|
||||
* is enabled (will only play the jingle of a badly constructed radio
|
||||
* stream). The versions using framewise decoding are fine with that. */
|
||||
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
|
||||
bad_end:
|
||||
@ -294,7 +208,6 @@ static void print_header_compact(struct mpg123_frameinfo *i)
|
||||
smodes[i->mode]);
|
||||
}
|
||||
|
||||
#ifndef AD_MPG123_CALLBACK
|
||||
/* This tries to extract a requested amount of decoded data.
|
||||
* Even when you request 0 bytes, it will feed enough input so that
|
||||
* the decoder _could_ have delivered something.
|
||||
@ -321,19 +234,13 @@ static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
|
||||
/* There will be one MPG123_NEW_FORMAT message on first open.
|
||||
* This will be implicitly handled in reopen_stream(). */
|
||||
* This will be handled in init(). */
|
||||
do {
|
||||
size_t got_now = 0;
|
||||
ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got,
|
||||
&got_now);
|
||||
got += got_now;
|
||||
#ifdef AD_MPG123_PACKET
|
||||
sh->pts_bytes += got_now;
|
||||
#endif
|
||||
|
||||
/* Feed the decoder. This will only fire from the second round on. */
|
||||
if (ret == MPG123_NEED_MORE) {
|
||||
int incount;
|
||||
#ifdef AD_MPG123_PACKET
|
||||
double pts;
|
||||
unsigned char *inbuf;
|
||||
/* Feed more input data. */
|
||||
@ -346,26 +253,45 @@ static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
|
||||
sh->pts = pts;
|
||||
sh->pts_bytes = 0;
|
||||
}
|
||||
#else
|
||||
const int inbufsize = 4096;
|
||||
unsigned char inbuf[inbufsize];
|
||||
/* Feed more input data. */
|
||||
incount = demux_read_data(((sh_audio_t *) sh)->ds,
|
||||
inbuf, inbufsize);
|
||||
if (incount == 0)
|
||||
break; /* Apparently that's it. EOF. */
|
||||
#endif
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Have to use mpg123_feed() to avoid decoding here. */
|
||||
ret = mpg123_feed(con->handle, inbuf, incount);
|
||||
#else
|
||||
/* Do not use mpg123_feed(), added in later libmpg123 versions. */
|
||||
ret = mpg123_decode(con->handle, inbuf, incount, NULL, 0, NULL);
|
||||
/* Return value is checked in the loop condition.
|
||||
* It could be MPG12_OK now, it could need more. */
|
||||
#endif
|
||||
if (ret == MPG123_ERR)
|
||||
break;
|
||||
}
|
||||
/* Older mpg123 versions might indicate MPG123_DONE, so be prepared. */
|
||||
/* Theoretically, mpg123 could return MPG123_DONE, so be prepared.
|
||||
* Should not happen in our usage, but it is a valid return code. */
|
||||
else if (ret == MPG123_ERR || ret == MPG123_DONE)
|
||||
break;
|
||||
|
||||
/* Try to decode a bit. This is the return value that counts
|
||||
* for the loop condition. */
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
if (!buf) { /* fake call just for feeding to get format */
|
||||
ret = mpg123_getformat(con->handle, NULL, NULL, NULL);
|
||||
} else { /* This is the decoding. One frame at a time. */
|
||||
ret = mpg123_replace_buffer(con->handle, buf, count);
|
||||
if (ret == MPG123_OK)
|
||||
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
|
||||
}
|
||||
#else
|
||||
ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got,
|
||||
&got_now);
|
||||
#endif
|
||||
|
||||
got += got_now;
|
||||
sh->pts_bytes += got_now;
|
||||
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
} while (ret == MPG123_NEED_MORE || (got == 0 && count != 0));
|
||||
#else
|
||||
} while (ret == MPG123_NEED_MORE || got < count);
|
||||
#endif
|
||||
|
||||
if (ret == MPG123_ERR) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 decoding failed: %s\n",
|
||||
@ -376,29 +302,21 @@ static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
|
||||
|
||||
return got;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Close, reopen stream. Feed data until we know the format of the stream.
|
||||
* 1 on success, 0 on error */
|
||||
static int reopen_stream(sh_audio_t *sh)
|
||||
{
|
||||
long rate;
|
||||
int chan, enc;
|
||||
struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
|
||||
|
||||
mpg123_close(con->handle);
|
||||
context_reset(con);
|
||||
/* No resetting of the context:
|
||||
* We do not want to loose the mean bitrate data. */
|
||||
|
||||
#ifdef AD_MPG123_CALLBACK
|
||||
if (MPG123_OK == mpg123_open_handle(con->handle, sh) &&
|
||||
#else
|
||||
if (/* Open and make sure we have fed enough data to get stream properties. */
|
||||
MPG123_OK == mpg123_open_feed(con->handle) &&
|
||||
/* Open and make sure we have fed enough data to get stream properties. */
|
||||
if (MPG123_OK == mpg123_open_feed(con->handle) &&
|
||||
/* Feed data until mpg123 is ready (has found stream beginning). */
|
||||
!decode_a_bit(sh, NULL, 0) &&
|
||||
#endif
|
||||
/* Not handing NULL pointers for compatibility with old libmpg123. */
|
||||
MPG123_OK == mpg123_getformat(con->handle, &rate, &chan, &enc)) {
|
||||
!decode_a_bit(sh, NULL, 0)) {
|
||||
return 1;
|
||||
} else {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
@ -446,7 +364,11 @@ static int init(sh_audio_t *sh)
|
||||
* For VBR, the first frame will be a bad estimate. */
|
||||
sh->i_bps = (finfo.bitrate ? finfo.bitrate : compute_bitrate(&finfo))
|
||||
* 1000 / 8;
|
||||
context_reset(con);
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
con->delay = 1;
|
||||
con->mean_rate = 0.;
|
||||
con->mean_count = 0;
|
||||
#endif
|
||||
con->vbr = (finfo.vbr != MPG123_CBR);
|
||||
sh->channels = channels;
|
||||
sh->samplerate = rate;
|
||||
@ -480,6 +402,12 @@ static int init(sh_audio_t *sh)
|
||||
mpg123_close(con->handle);
|
||||
return 0;
|
||||
}
|
||||
#ifdef AD_MPG123_FRAMEWISE
|
||||
/* Going to decode directly to MPlayer's memory. It is important
|
||||
* to have MPG123_AUTO_RESAMPLE disabled for the buffer size
|
||||
* being an all-time limit. */
|
||||
sh->audio_out_minsize = 1152 * 2 * sh->samplesize;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
@ -529,29 +457,13 @@ static int decode_audio(sh_audio_t *sh, unsigned char *buf, int minlen,
|
||||
{
|
||||
int bytes;
|
||||
|
||||
#ifdef AD_MPG123_CALLBACK
|
||||
struct ad_mpg123_context *con = sh->context;
|
||||
size_t got_bytes = 0;
|
||||
if (MPG123_ERR == mpg123_read(con->handle, buf, minlen, &got_bytes)) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "Decoding error in mpg123: %s\n",
|
||||
mpg123_strerror(con->handle));
|
||||
return -1;
|
||||
}
|
||||
#ifdef AD_MPG123_PACKET
|
||||
sh->pts_bytes += got_bytes;
|
||||
#endif
|
||||
bytes = got_bytes;
|
||||
#else
|
||||
bytes = decode_a_bit(sh, buf, minlen);
|
||||
#endif
|
||||
|
||||
bytes = decode_a_bit(sh, buf, maxlen);
|
||||
if (bytes == 0)
|
||||
return -1; /* EOF */
|
||||
|
||||
#ifdef AD_MPG123_MEAN_BITRATE
|
||||
update_info(sh);
|
||||
#endif
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -569,6 +481,8 @@ static int control(sh_audio_t *sh, int cmd, void *arg, ...)
|
||||
#endif
|
||||
return CONTROL_TRUE;
|
||||
} else {
|
||||
/* MPlayer ignores this case! It just keeps on decoding.
|
||||
* So we have to make sure resync never fails ... */
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"mpg123 cannot reopen stream for resync.\n");
|
||||
return CONTROL_FALSE;
|
||||
|
110
m_config.c
110
m_config.c
@ -36,12 +36,21 @@
|
||||
|
||||
#define MAX_PROFILE_DEPTH 20
|
||||
|
||||
static int parse_profile(const struct m_option *opt, struct bstr name,
|
||||
struct bstr param, bool ambiguous_param, void *dst)
|
||||
static int parse_include(struct m_config *config, struct bstr param, bool set)
|
||||
{
|
||||
if (param.len == 0)
|
||||
return M_OPT_MISSING_PARAM;
|
||||
if (!set)
|
||||
return 1;
|
||||
char *filename = bstrdup0(NULL, param);
|
||||
config->includefunc(config, filename);
|
||||
talloc_free(filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_profile(struct m_config *config, const struct m_option *opt,
|
||||
struct bstr name, struct bstr param, bool set)
|
||||
{
|
||||
struct m_config *config = opt->priv;
|
||||
char **list = NULL;
|
||||
int i, r;
|
||||
if (!bstrcmp0(param, "help")) {
|
||||
struct m_profile *p;
|
||||
if (!config->profiles) {
|
||||
@ -57,40 +66,23 @@ static int parse_profile(const struct m_option *opt, struct bstr name,
|
||||
return M_OPT_EXIT - 1;
|
||||
}
|
||||
|
||||
r = m_option_type_string_list.parse(opt, name, param, false, &list);
|
||||
char **list = NULL;
|
||||
int r = m_option_type_string_list.parse(opt, name, param, false, &list);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!list || !list[0])
|
||||
return M_OPT_INVALID;
|
||||
for (i = 0; list[i]; i++)
|
||||
if (!m_config_get_profile(config, list[i])) {
|
||||
for (int i = 0; list[i]; i++) {
|
||||
struct m_profile *p = m_config_get_profile(config, list[i]);
|
||||
if (!p) {
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "Unknown profile '%s'.\n",
|
||||
list[i]);
|
||||
r = M_OPT_INVALID;
|
||||
}
|
||||
if (dst)
|
||||
m_option_copy(opt, dst, &list);
|
||||
else
|
||||
m_option_free(opt, &list);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void set_profile(const struct m_option *opt, void *dst, const void *src)
|
||||
{
|
||||
struct m_config *config = opt->priv;
|
||||
struct m_profile *p;
|
||||
char **list = NULL;
|
||||
int i;
|
||||
if (!src || !*(char ***)src)
|
||||
return;
|
||||
m_option_copy(opt, &list, src);
|
||||
for (i = 0; list[i]; i++) {
|
||||
p = m_config_get_profile(config, list[i]);
|
||||
if (!p)
|
||||
continue;
|
||||
} else if (set)
|
||||
m_config_set_profile(config, p);
|
||||
}
|
||||
m_option_free(opt, &list);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int show_profile(struct m_option *opt, char *name, char *param)
|
||||
@ -150,18 +142,18 @@ static int list_options(struct m_option *opt, char *name, char *param)
|
||||
static void m_option_save(const struct m_config *config,
|
||||
const struct m_option *opt, void *dst)
|
||||
{
|
||||
if (opt->type->save) {
|
||||
if (opt->type->copy) {
|
||||
const void *src = m_option_get_ptr(opt, config->optstruct);
|
||||
opt->type->save(opt, dst, src);
|
||||
opt->type->copy(opt, dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
static void m_option_set(const struct m_config *config,
|
||||
const struct m_option *opt, const void *src)
|
||||
{
|
||||
if (opt->type->set) {
|
||||
if (opt->type->copy) {
|
||||
void *dst = m_option_get_ptr(opt, config->optstruct);
|
||||
opt->type->set(opt, dst, src);
|
||||
opt->type->copy(opt, dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,40 +164,31 @@ static void m_config_add_option(struct m_config *config,
|
||||
const char *prefix, char *disabled_feature);
|
||||
|
||||
struct m_config *m_config_new(void *optstruct,
|
||||
int includefunc(struct m_option *conf,
|
||||
int includefunc(struct m_config *conf,
|
||||
char *filename))
|
||||
{
|
||||
struct m_config *config;
|
||||
static int initialized = 0;
|
||||
static struct m_option_type profile_opt_type;
|
||||
static const struct m_option ref_opts[] = {
|
||||
{ "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
|
||||
{ "profile", NULL, CONF_TYPE_STRING_LIST, CONF_NOSAVE, 0, 0, NULL },
|
||||
{ "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG },
|
||||
{ "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG },
|
||||
{ NULL }
|
||||
};
|
||||
int i;
|
||||
|
||||
config = talloc_zero(NULL, struct m_config);
|
||||
config->lvl = 1; // 0 Is the defaults
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
profile_opt_type = m_option_type_string_list;
|
||||
profile_opt_type.parse = parse_profile;
|
||||
profile_opt_type.set = set_profile;
|
||||
}
|
||||
struct m_option *self_opts = talloc_memdup(config, ref_opts,
|
||||
sizeof(ref_opts));
|
||||
for (i = 0; self_opts[i].name; i++)
|
||||
for (int i = 1; self_opts[i].name; i++)
|
||||
self_opts[i].priv = config;
|
||||
m_config_register_options(config, self_opts);
|
||||
if (includefunc) {
|
||||
struct m_option *p = talloc_ptrtype(config, p);
|
||||
*p = (struct m_option){
|
||||
"include", includefunc, CONF_TYPE_FUNC_PARAM,
|
||||
CONF_NOSAVE, 0, 0, config
|
||||
"include", NULL, CONF_TYPE_STRING, CONF_NOSAVE,
|
||||
};
|
||||
m_config_add_option(config, p, NULL, NULL);
|
||||
config->includefunc = includefunc;
|
||||
}
|
||||
config->optstruct = optstruct;
|
||||
|
||||
@ -420,18 +403,15 @@ static struct m_config_option *m_config_get_co(const struct m_config *config,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int m_config_parse_option(const struct m_config *config,
|
||||
static int m_config_parse_option(struct m_config *config,
|
||||
struct bstr name, struct bstr param,
|
||||
bool ambiguous_param, bool set)
|
||||
{
|
||||
struct m_config_option *co;
|
||||
int r = 0;
|
||||
|
||||
assert(config != NULL);
|
||||
assert(config->lvl > 0);
|
||||
assert(name.len != 0);
|
||||
|
||||
co = m_config_get_co(config, name);
|
||||
struct m_config_option *co = m_config_get_co(config, name);
|
||||
if (!co)
|
||||
return M_OPT_UNKNOWN;
|
||||
if (co->disabled_feature) {
|
||||
@ -466,21 +446,25 @@ static int m_config_parse_option(const struct m_config *config,
|
||||
(co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
|
||||
set = 0;
|
||||
|
||||
if (config->includefunc && !bstrcmp0(name, "include")) {
|
||||
return parse_include(config, param, set);
|
||||
} else if (!bstrcmp0(name, "profile"))
|
||||
return parse_profile(config, co->opt, name, param, set);
|
||||
|
||||
// Option with children are a bit different to parse
|
||||
if (co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
|
||||
char **lst = NULL;
|
||||
int i, sr;
|
||||
// Parse the child options
|
||||
r = m_option_parse(co->opt, name, param, false, &lst);
|
||||
int r = m_option_parse(co->opt, name, param, false, &lst);
|
||||
// Set them now
|
||||
if (r >= 0)
|
||||
for (i = 0; lst && lst[2 * i]; i++) {
|
||||
for (int i = 0; lst && lst[2 * i]; i++) {
|
||||
int l = strlen(co->name) + 1 + strlen(lst[2 * i]) + 1;
|
||||
if (r >= 0) {
|
||||
// Build the full name
|
||||
char n[l];
|
||||
sprintf(n, "%s:%s", co->name, lst[2 * i]);
|
||||
sr = m_config_parse_option(config, bstr(n),
|
||||
int sr = m_config_parse_option(config, bstr(n),
|
||||
bstr(lst[2 * i + 1]), false,
|
||||
set);
|
||||
if (sr < 0) {
|
||||
@ -502,18 +486,16 @@ static int m_config_parse_option(const struct m_config *config,
|
||||
talloc_free(lst[2 * i + 1]);
|
||||
}
|
||||
talloc_free(lst);
|
||||
} else
|
||||
r = m_option_parse(co->opt, name, param, ambiguous_param,
|
||||
set ? co->slots->data : NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
void *dst = set ? m_option_get_ptr(co->opt, config->optstruct) : NULL;
|
||||
int r = m_option_parse(co->opt, name, param, ambiguous_param, dst);
|
||||
// Parsing failed ?
|
||||
if (r < 0)
|
||||
return r;
|
||||
// Set the option
|
||||
if (set) {
|
||||
m_option_set(config, co->opt, co->slots->data);
|
||||
else if (set)
|
||||
co->flags |= M_CFG_OPT_SET;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -526,7 +508,7 @@ int m_config_set_option(struct m_config *config, struct bstr name,
|
||||
return m_config_parse_option(config, name, param, ambiguous_param, 1);
|
||||
}
|
||||
|
||||
int m_config_check_option(const struct m_config *config, struct bstr name,
|
||||
int m_config_check_option(struct m_config *config, struct bstr name,
|
||||
struct bstr param, bool ambiguous_param)
|
||||
{
|
||||
int r;
|
||||
|
@ -95,6 +95,7 @@ typedef struct m_config {
|
||||
int profile_depth;
|
||||
|
||||
void *optstruct; // struct mpopts or other
|
||||
int (*includefunc)(struct m_config *conf, char *filename);
|
||||
} m_config_t;
|
||||
|
||||
|
||||
@ -107,7 +108,7 @@ typedef struct m_config {
|
||||
// Create a new config object.
|
||||
struct m_config *
|
||||
m_config_new(void *optstruct,
|
||||
int includefunc(struct m_option *conf, char *filename));
|
||||
int includefunc(struct m_config *conf, char *filename));
|
||||
|
||||
// Free a config object.
|
||||
void m_config_free(struct m_config *config);
|
||||
@ -151,7 +152,7 @@ static inline int m_config_set_option0(struct m_config *config,
|
||||
/* Check if an option setting is valid.
|
||||
* Same as above m_config_set_option() but doesn't actually set anything.
|
||||
*/
|
||||
int m_config_check_option(const struct m_config *config, struct bstr name,
|
||||
int m_config_check_option(struct m_config *config, struct bstr name,
|
||||
struct bstr param, bool ambiguous_param);
|
||||
|
||||
static inline int m_config_check_option0(struct m_config *config,
|
||||
|
449
m_option.c
449
m_option.c
@ -129,16 +129,12 @@ static char *print_flag(const m_option_t *opt, const void *val)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_flag = {
|
||||
"Flag",
|
||||
"need yes or no in config files",
|
||||
sizeof(int),
|
||||
0,
|
||||
parse_flag,
|
||||
print_flag,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// need yes or no in config files
|
||||
.name = "Flag",
|
||||
.size = sizeof(int),
|
||||
.parse = parse_flag,
|
||||
.print = print_flag,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
// Integer
|
||||
@ -209,29 +205,19 @@ static char *print_int(const m_option_t *opt, const void *val)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_int = {
|
||||
"Integer",
|
||||
"",
|
||||
sizeof(int),
|
||||
0,
|
||||
parse_int,
|
||||
print_int,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Integer",
|
||||
.size = sizeof(int),
|
||||
.parse = parse_int,
|
||||
.print = print_int,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
const m_option_type_t m_option_type_int64 = {
|
||||
"Integer64",
|
||||
"",
|
||||
sizeof(int64_t),
|
||||
0,
|
||||
parse_int64,
|
||||
print_int,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Integer64",
|
||||
.size = sizeof(int64_t),
|
||||
.parse = parse_int64,
|
||||
.print = print_int,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
static int parse_intpair(const struct m_option *opt, struct bstr name,
|
||||
@ -274,8 +260,7 @@ const struct m_option_type m_option_type_intpair = {
|
||||
.name = "Int[-Int]",
|
||||
.size = sizeof(int[2]),
|
||||
.parse = parse_intpair,
|
||||
.save = copy_opt,
|
||||
.set = copy_opt,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
static int parse_choice(const struct m_option *opt, struct bstr name,
|
||||
@ -326,8 +311,7 @@ const struct m_option_type m_option_type_choice = {
|
||||
.size = sizeof(int),
|
||||
.parse = parse_choice,
|
||||
.print = print_choice,
|
||||
.save = copy_opt,
|
||||
.set = copy_opt,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
// Float
|
||||
@ -398,16 +382,12 @@ static char *print_double(const m_option_t *opt, const void *val)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_double = {
|
||||
"Double",
|
||||
"double precision floating point number or ratio (numerator[:/]denominator)",
|
||||
sizeof(double),
|
||||
0,
|
||||
parse_double,
|
||||
print_double,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// double precision float or ratio (numerator[:/]denominator)
|
||||
.name = "Double",
|
||||
.size = sizeof(double),
|
||||
.parse = parse_double,
|
||||
.print = print_double,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
#undef VAL
|
||||
@ -430,16 +410,12 @@ static char *print_float(const m_option_t *opt, const void *val)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_float = {
|
||||
"Float",
|
||||
"floating point number or ratio (numerator[:/]denominator)",
|
||||
sizeof(float),
|
||||
0,
|
||||
parse_float,
|
||||
print_float,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// floating point number or ratio (numerator[:/]denominator)
|
||||
.name = "Float",
|
||||
.size = sizeof(float),
|
||||
.parse = parse_float,
|
||||
.print = print_float,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
///////////// Position
|
||||
@ -462,16 +438,12 @@ static char *print_position(const m_option_t *opt, const void *val)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_position = {
|
||||
"Position",
|
||||
"Integer (off_t)",
|
||||
sizeof(off_t),
|
||||
0,
|
||||
parse_position,
|
||||
print_position,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// Integer (off_t)
|
||||
.name = "Position",
|
||||
.size = sizeof(off_t),
|
||||
.parse = parse_position,
|
||||
.print = print_position,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
|
||||
@ -528,16 +500,13 @@ static void free_str(void *src)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_string = {
|
||||
"String",
|
||||
"",
|
||||
sizeof(char *),
|
||||
M_OPT_TYPE_DYNAMIC,
|
||||
parse_str,
|
||||
print_str,
|
||||
copy_str,
|
||||
copy_str,
|
||||
copy_str,
|
||||
free_str
|
||||
.name = "String",
|
||||
.size = sizeof(char *),
|
||||
.flags = M_OPT_TYPE_DYNAMIC,
|
||||
.parse = parse_str,
|
||||
.print = print_str,
|
||||
.copy = copy_str,
|
||||
.free = free_str,
|
||||
};
|
||||
|
||||
//////////// String list
|
||||
@ -795,164 +764,24 @@ static char *print_str_list(const m_option_t *opt, const void *src)
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_string_list = {
|
||||
"String list",
|
||||
"A list of strings separated by ','\n"
|
||||
"Option with a name ending in an * permits using the following suffix: \n"
|
||||
"\t-add: Add the given parameters at the end of the list.\n"
|
||||
"\t-pre: Add the given parameters at the beginning of the list.\n"
|
||||
"\t-del: Remove the entry at the given indices.\n"
|
||||
"\t-clr: Clear the list.\n"
|
||||
"e.g: -vf-add flip,mirror -vf-del 2,5\n",
|
||||
sizeof(char **),
|
||||
M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
parse_str_list,
|
||||
print_str_list,
|
||||
copy_str_list,
|
||||
copy_str_list,
|
||||
copy_str_list,
|
||||
free_str_list
|
||||
/* A list of strings separated by ','.
|
||||
* Option with a name ending in '*' permits using the following suffixes:
|
||||
* -add: Add the given parameters at the end of the list.
|
||||
* -pre: Add the given parameters at the beginning of the list.
|
||||
* -del: Remove the entry at the given indices.
|
||||
* -clr: Clear the list.
|
||||
* e.g: -vf-add flip,mirror -vf-del 2,5
|
||||
*/
|
||||
.name = "String list",
|
||||
.size = sizeof(char **),
|
||||
.flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
.parse = parse_str_list,
|
||||
.print = print_str_list,
|
||||
.copy = copy_str_list,
|
||||
.free = free_str_list,
|
||||
};
|
||||
|
||||
|
||||
/////////////////// Func based options
|
||||
|
||||
// A chained list to save the various calls for func_param
|
||||
struct m_func_save {
|
||||
struct m_func_save *next;
|
||||
char *name;
|
||||
char *param;
|
||||
};
|
||||
|
||||
#undef VAL
|
||||
#define VAL(x) (*(struct m_func_save **)(x))
|
||||
|
||||
static void free_func_pf(void *src)
|
||||
{
|
||||
struct m_func_save *s, *n;
|
||||
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
s = VAL(src);
|
||||
|
||||
while (s) {
|
||||
n = s->next;
|
||||
talloc_free(s->name);
|
||||
talloc_free(s->param);
|
||||
talloc_free(s);
|
||||
s = n;
|
||||
}
|
||||
VAL(src) = NULL;
|
||||
}
|
||||
|
||||
// Parser for func_param
|
||||
static int parse_func_pf(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, bool ambiguous_param, void *dst)
|
||||
{
|
||||
struct m_func_save *s, *p;
|
||||
|
||||
if (!dst)
|
||||
return 1;
|
||||
|
||||
s = talloc_zero(NULL, struct m_func_save);
|
||||
s->name = bstrdup0(NULL, name);
|
||||
s->param = bstrdup0(NULL, param);
|
||||
|
||||
p = VAL(dst);
|
||||
if (p) {
|
||||
for (; p->next != NULL; p = p->next)
|
||||
/**/;
|
||||
p->next = s;
|
||||
} else
|
||||
VAL(dst) = s;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void copy_func_pf(const m_option_t *opt, void *dst, const void *src)
|
||||
{
|
||||
struct m_func_save *d = NULL, *s, *last = NULL;
|
||||
|
||||
if (!(dst && src))
|
||||
return;
|
||||
s = VAL(src);
|
||||
|
||||
if (VAL(dst))
|
||||
free_func_pf(dst);
|
||||
|
||||
while (s) {
|
||||
d = talloc_zero(NULL, struct m_func_save);
|
||||
d->name = talloc_strdup(NULL, s->name);
|
||||
d->param = talloc_strdup(NULL, s->param);
|
||||
if (last)
|
||||
last->next = d;
|
||||
else
|
||||
VAL(dst) = d;
|
||||
last = d;
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/////////////////// Func_param
|
||||
|
||||
static void set_func_param(const m_option_t *opt, void *dst, const void *src)
|
||||
{
|
||||
struct m_func_save *s;
|
||||
|
||||
if (!src)
|
||||
return;
|
||||
s = VAL(src);
|
||||
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
for (; s != NULL; s = s->next)
|
||||
((m_opt_func_param_t) opt->p)(opt, s->param);
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_func_param = {
|
||||
"Func param",
|
||||
"",
|
||||
sizeof(struct m_func_save *),
|
||||
M_OPT_TYPE_INDIRECT,
|
||||
parse_func_pf,
|
||||
NULL,
|
||||
NULL, // Nothing to do on save
|
||||
set_func_param,
|
||||
copy_func_pf,
|
||||
free_func_pf
|
||||
};
|
||||
|
||||
/////////////// Func
|
||||
|
||||
#undef VAL
|
||||
|
||||
static int parse_func(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, bool ambiguous_param, void *dst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_func(const m_option_t *opt, void *dst, const void *src)
|
||||
{
|
||||
((m_opt_func_t) opt->p)(opt);
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_func = {
|
||||
"Func",
|
||||
"",
|
||||
sizeof(int),
|
||||
M_OPT_TYPE_INDIRECT,
|
||||
parse_func,
|
||||
NULL,
|
||||
NULL, // Nothing to do on save
|
||||
set_func,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/////////////////// Print
|
||||
|
||||
static int parse_print(const m_option_t *opt, struct bstr name,
|
||||
@ -976,42 +805,19 @@ static int parse_print(const m_option_t *opt, struct bstr name,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_print = {
|
||||
"Print",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
parse_print,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Print",
|
||||
.parse = parse_print,
|
||||
};
|
||||
|
||||
const m_option_type_t m_option_type_print_indirect = {
|
||||
"Print",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
parse_print,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Print",
|
||||
.parse = parse_print,
|
||||
};
|
||||
|
||||
const m_option_type_t m_option_type_print_func = {
|
||||
"Print",
|
||||
"",
|
||||
0,
|
||||
M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
parse_print,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Print",
|
||||
.flags = M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
.parse = parse_print,
|
||||
};
|
||||
|
||||
|
||||
@ -1104,16 +910,10 @@ static int parse_subconf(const m_option_t *opt, struct bstr name,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_subconfig = {
|
||||
"Subconfig",
|
||||
"The syntax is -option opt1=foo:flag:opt2=blah",
|
||||
sizeof(int),
|
||||
M_OPT_TYPE_HAS_CHILD,
|
||||
parse_subconf,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
// The syntax is -option opt1=foo:flag:opt2=blah
|
||||
.name = "Subconfig",
|
||||
.flags = M_OPT_TYPE_HAS_CHILD,
|
||||
.parse = parse_subconf,
|
||||
};
|
||||
|
||||
#include "libmpcodecs/img_format.h"
|
||||
@ -1238,16 +1038,11 @@ static int parse_imgfmt(const m_option_t *opt, struct bstr name,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_imgfmt = {
|
||||
"Image format",
|
||||
"Please report any missing colorspaces.",
|
||||
sizeof(uint32_t),
|
||||
0,
|
||||
parse_imgfmt,
|
||||
NULL,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// Please report any missing colorspaces
|
||||
.name = "Image format",
|
||||
.size = sizeof(uint32_t),
|
||||
.parse = parse_imgfmt,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
#include "libaf/af_format.h"
|
||||
@ -1333,16 +1128,11 @@ static int parse_afmt(const m_option_t *opt, struct bstr name,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_afmt = {
|
||||
"Audio format",
|
||||
"Please report any missing formats.",
|
||||
sizeof(uint32_t),
|
||||
0,
|
||||
parse_afmt,
|
||||
NULL,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
// Please report any missing formats
|
||||
.name = "Audio format",
|
||||
.size = sizeof(uint32_t),
|
||||
.parse = parse_afmt,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
|
||||
@ -1385,16 +1175,11 @@ static int parse_time(const m_option_t *opt, struct bstr name,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_time = {
|
||||
"Time",
|
||||
"",
|
||||
sizeof(double),
|
||||
0,
|
||||
parse_time,
|
||||
print_double,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Time",
|
||||
.size = sizeof(double),
|
||||
.parse = parse_time,
|
||||
.print = print_double,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
|
||||
@ -1449,16 +1234,10 @@ out:
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_time_size = {
|
||||
"Time or size",
|
||||
"",
|
||||
sizeof(m_time_size_t),
|
||||
0,
|
||||
parse_time_size,
|
||||
NULL,
|
||||
copy_opt,
|
||||
copy_opt,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Time or size",
|
||||
.size = sizeof(m_time_size_t),
|
||||
.parse = parse_time_size,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
|
||||
@ -1668,16 +1447,8 @@ static int parse_obj_params(const m_option_t *opt, struct bstr name,
|
||||
|
||||
|
||||
const m_option_type_t m_option_type_obj_params = {
|
||||
"Object params",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
parse_obj_params,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Object params",
|
||||
.parse = parse_obj_params,
|
||||
};
|
||||
|
||||
/// Some predefined types as a definition would be quite lengthy
|
||||
@ -2007,16 +1778,12 @@ static void copy_obj_settings_list(const m_option_t *opt, void *dst,
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_obj_settings_list = {
|
||||
"Object settings list",
|
||||
"",
|
||||
sizeof(m_obj_settings_t *),
|
||||
M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
parse_obj_settings_list,
|
||||
NULL,
|
||||
copy_obj_settings_list,
|
||||
copy_obj_settings_list,
|
||||
copy_obj_settings_list,
|
||||
free_obj_settings_list,
|
||||
.name = "Object settings list",
|
||||
.size = sizeof(m_obj_settings_t *),
|
||||
.flags = M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
|
||||
.parse = parse_obj_settings_list,
|
||||
.copy = copy_obj_settings_list,
|
||||
.free = free_obj_settings_list,
|
||||
};
|
||||
|
||||
|
||||
@ -2093,16 +1860,8 @@ static int parse_obj_presets(const m_option_t *opt, struct bstr name,
|
||||
|
||||
|
||||
const m_option_type_t m_option_type_obj_presets = {
|
||||
"Object presets",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
parse_obj_presets,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Object presets",
|
||||
.parse = parse_obj_presets,
|
||||
};
|
||||
|
||||
static int parse_custom_url(const m_option_t *opt, struct bstr name,
|
||||
@ -2290,14 +2049,6 @@ static int parse_custom_url(const m_option_t *opt, struct bstr name,
|
||||
|
||||
/// TODO : Write the other needed funcs for 'normal' options
|
||||
const m_option_type_t m_option_type_custom_url = {
|
||||
"Custom URL",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
parse_custom_url,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
.name = "Custom URL",
|
||||
.parse = parse_custom_url,
|
||||
};
|
||||
|
71
m_option.h
71
m_option.h
@ -55,28 +55,9 @@ extern const m_option_type_t m_option_type_subconfig;
|
||||
extern const m_option_type_t m_option_type_imgfmt;
|
||||
extern const m_option_type_t m_option_type_afmt;
|
||||
|
||||
// Func-based types
|
||||
extern const m_option_type_t m_option_type_func_param;
|
||||
extern const m_option_type_t m_option_type_func;
|
||||
|
||||
// Callback used to reset func options.
|
||||
typedef void (*m_opt_default_func_t)(const m_option_t *, const char *);
|
||||
|
||||
// Callback used by m_option_type_func_full options.
|
||||
// Callback used by m_option_type_print_func options.
|
||||
typedef int (*m_opt_func_full_t)(const m_option_t *, const char *, const char *);
|
||||
|
||||
// Callback used by m_option_type_func_param options.
|
||||
typedef int (*m_opt_func_param_t)(const m_option_t *, const char *);
|
||||
|
||||
// Callback used by m_option_type_func options.
|
||||
typedef int (*m_opt_func_t)(const m_option_t *);
|
||||
|
||||
// Backwards compatibility
|
||||
typedef m_opt_default_func_t cfg_default_func_t;
|
||||
typedef m_opt_func_full_t cfg_func_arg_param_t;
|
||||
typedef m_opt_func_param_t cfg_func_param_t;
|
||||
typedef m_opt_func_t cfg_func_t;
|
||||
|
||||
#define END_AT_NONE 0
|
||||
#define END_AT_TIME 1
|
||||
#define END_AT_SIZE 2
|
||||
@ -175,8 +156,6 @@ struct m_opt_choice_alternatives {
|
||||
#define CONF_TYPE_FLOAT (&m_option_type_float)
|
||||
#define CONF_TYPE_DOUBLE (&m_option_type_double)
|
||||
#define CONF_TYPE_STRING (&m_option_type_string)
|
||||
#define CONF_TYPE_FUNC (&m_option_type_func)
|
||||
#define CONF_TYPE_FUNC_PARAM (&m_option_type_func_param)
|
||||
#define CONF_TYPE_PRINT (&m_option_type_print)
|
||||
#define CONF_TYPE_PRINT_INDIRECT (&m_option_type_print_indirect)
|
||||
#define CONF_TYPE_PRINT_FUNC (&m_option_type_print_func)
|
||||
@ -198,8 +177,6 @@ struct m_opt_choice_alternatives {
|
||||
// Option type description
|
||||
struct m_option_type {
|
||||
const char *name;
|
||||
// Syntax description, etc
|
||||
const char *comments;
|
||||
// Size needed for the data.
|
||||
unsigned int size;
|
||||
// See \ref OptionTypeFlags.
|
||||
@ -229,35 +206,12 @@ struct m_option_type {
|
||||
*/
|
||||
char *(*print)(const m_option_t *opt, const void *val);
|
||||
|
||||
/** \name
|
||||
* These functions are called to save/set/restore the status of the
|
||||
* variables. The difference between the 3 only matters for types like
|
||||
* \ref m_option_type_func where 'setting' needs to do more than just
|
||||
* copying some data.
|
||||
*/
|
||||
//@{
|
||||
|
||||
// Update a save slot (dst) from the current value in the program (src).
|
||||
/** \param opt The option to copy.
|
||||
* \param dst Pointer to the destination memory.
|
||||
* \param src Pointer to the source memory.
|
||||
*/
|
||||
void (*save)(const m_option_t *opt, void *dst, const void *src);
|
||||
|
||||
// Set the value in the program (dst) from a save slot.
|
||||
/** \param opt The option to copy.
|
||||
* \param dst Pointer to the destination memory.
|
||||
* \param src Pointer to the source memory.
|
||||
*/
|
||||
void (*set)(const m_option_t *opt, void *dst, const void *src);
|
||||
|
||||
// Copy the data between two save slots. If NULL and size is > 0 a memcpy will be used.
|
||||
// Copy data between two locations. Deep copy if the data has pointers.
|
||||
/** \param opt The option to copy.
|
||||
* \param dst Pointer to the destination memory.
|
||||
* \param src Pointer to the source memory.
|
||||
*/
|
||||
void (*copy)(const m_option_t *opt, void *dst, const void *src);
|
||||
//@}
|
||||
|
||||
// Free the data allocated for a save slot.
|
||||
/** This is only needed for dynamic types like strings.
|
||||
@ -296,7 +250,6 @@ struct m_option {
|
||||
// Type dependent data (for all kinds of extended settings).
|
||||
/** This used to be a function pointer to hold a 'reverse to defaults' func.
|
||||
* Now it can be used to pass any type of extra args needed by the parser.
|
||||
* Passing a 'default func' is still valid for all func based option types.
|
||||
*/
|
||||
void *priv;
|
||||
|
||||
@ -379,17 +332,6 @@ struct m_option {
|
||||
*/
|
||||
#define M_OPT_TYPE_DYNAMIC (1 << 2)
|
||||
|
||||
// Indirect option type.
|
||||
/** If this is set the parse function doesn't directly return
|
||||
* the wanted thing. Options use this if for some reasons they have to wait
|
||||
* until the set call to be able to correctly set the target var.
|
||||
* So for those types new values must first be parsed, then set to the target
|
||||
* var. If this flag isn't set then new values can be parsed directly to the
|
||||
* target var. It's used by the callback-based options as the callback call
|
||||
* may append later on.
|
||||
*/
|
||||
#define M_OPT_TYPE_INDIRECT (1 << 3)
|
||||
|
||||
///////////////////////////// Parser flags /////////////////////////////////
|
||||
|
||||
// On success parsers return the number of arguments consumed: 0 or 1.
|
||||
@ -421,13 +363,6 @@ struct m_option {
|
||||
*/
|
||||
#define M_OPT_EXIT -6
|
||||
|
||||
// These are kept for compatibility with older code.
|
||||
//
|
||||
#define ERR_NOT_AN_OPTION M_OPT_UNKNOWN
|
||||
#define ERR_MISSING_PARAM M_OPT_MISSING_PARAM
|
||||
#define ERR_OUT_OF_RANGE M_OPT_OUT_OF_RANGE
|
||||
#define ERR_FUNC_ERR M_OPT_PARSER_ERR
|
||||
|
||||
char *m_option_strerror(int code);
|
||||
|
||||
// Find the option matching the given name in the list.
|
||||
@ -470,8 +405,6 @@ static inline void m_option_copy(const m_option_t *opt, void *dst,
|
||||
{
|
||||
if (opt->type->copy)
|
||||
opt->type->copy(opt, dst, src);
|
||||
else if (opt->type->size > 0)
|
||||
memcpy(dst, src, opt->type->size);
|
||||
}
|
||||
|
||||
// Helper around \ref m_option_type::free.
|
||||
|
@ -48,13 +48,6 @@ m_struct_alloc(const m_struct_t* st) {
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Struct %s needs defaults\n",st->name);
|
||||
return NULL;
|
||||
}
|
||||
// Check the struct fields
|
||||
for(i = 0 ; st->fields[i].name ; i++) {
|
||||
if(st->fields[i].type->flags & M_OPT_TYPE_INDIRECT) {
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Struct %s->%s: Option types with the indirect flag are forbidden.\n",st->name,st->fields[i].name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
r = calloc(1,st->size);
|
||||
memcpy(r,st->defaults,st->size);
|
||||
|
28
mplayer.c
28
mplayer.c
@ -148,12 +148,6 @@ char *heartbeat_cmd;
|
||||
// Config file
|
||||
//**************************************************************************//
|
||||
|
||||
static int cfg_inc_verbose(m_option_t *conf)
|
||||
{
|
||||
++verbose;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "path.h"
|
||||
|
||||
//**************************************************************************//
|
||||
@ -864,9 +858,9 @@ static void exit_sighandler(int x)
|
||||
|
||||
#include "cfg-mplayer.h"
|
||||
|
||||
static int cfg_include(m_option_t *conf, char *filename)
|
||||
static int cfg_include(struct m_config *conf, char *filename)
|
||||
{
|
||||
return m_config_parse_config_file(conf->priv, filename);
|
||||
return m_config_parse_config_file(conf, filename);
|
||||
}
|
||||
|
||||
#define DEF_CONFIG "# Write your default config options here!\n\n\n"
|
||||
@ -2541,9 +2535,6 @@ static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
|
||||
current_module = "play_audio";
|
||||
|
||||
if (ao->untimed && mpctx->sh_video && mpctx->delay > 0)
|
||||
return 0;
|
||||
|
||||
// hack used by some mpeg-writing AOs
|
||||
ao->brokenpts = ((mpctx->sh_video ? mpctx->sh_video->timer : 0) +
|
||||
mpctx->delay) * 90000.0;
|
||||
@ -3465,9 +3456,9 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
pause_player(mpctx);
|
||||
}
|
||||
|
||||
if (mpctx->sh_audio && !mpctx->restart_playback) {
|
||||
if (mpctx->sh_audio && !mpctx->restart_playback && !mpctx->ao->untimed) {
|
||||
int status = fill_audio_out_buffers(mpctx, endpts);
|
||||
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
||||
full_audio_buffers = status >= 0;
|
||||
// Not at audio stream EOF yet
|
||||
audio_left = status > -2;
|
||||
}
|
||||
@ -3679,15 +3670,16 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mpctx->restart_playback && !video_left) {
|
||||
if (mpctx->sh_audio) {
|
||||
if (mpctx->sh_audio && (mpctx->restart_playback ? !video_left :
|
||||
mpctx->ao->untimed && (mpctx->delay <= 0 ||
|
||||
!video_left))) {
|
||||
int status = fill_audio_out_buffers(mpctx, endpts);
|
||||
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
||||
// Not at audio stream EOF yet
|
||||
audio_left = status > -2;
|
||||
}
|
||||
if (!video_left)
|
||||
mpctx->restart_playback = false;
|
||||
}
|
||||
if (mpctx->sh_audio && buffered_audio == -1)
|
||||
buffered_audio = mpctx->paused ? 0 : ao_get_delay(mpctx->ao);
|
||||
|
||||
@ -3736,7 +3728,7 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
double audio_sleep = 9;
|
||||
if (mpctx->sh_audio && !mpctx->paused) {
|
||||
if (mpctx->ao->untimed) {
|
||||
if (!mpctx->sh_video)
|
||||
if (!video_left)
|
||||
audio_sleep = 0;
|
||||
} else if (full_audio_buffers) {
|
||||
audio_sleep = buffered_audio - 0.050;
|
||||
@ -3961,7 +3953,7 @@ int main(int argc, char *argv[])
|
||||
mp_input_register_options(mpctx->mconfig);
|
||||
|
||||
// Preparse the command line
|
||||
m_config_preparse_command_line(mpctx->mconfig, argc, argv);
|
||||
m_config_preparse_command_line(mpctx->mconfig, argc, argv, &verbose);
|
||||
|
||||
#if (defined(__MINGW32__) || defined(__CYGWIN__)) && defined(CONFIG_WIN32DLL)
|
||||
set_path_env();
|
||||
|
@ -308,7 +308,8 @@ extern int mp_msg_levels[];
|
||||
* command line parsing), and --really-quiet suppresses messages printed
|
||||
* during normal options parsing.
|
||||
*/
|
||||
int m_config_preparse_command_line(m_config_t *config, int argc, char **argv)
|
||||
int m_config_preparse_command_line(m_config_t *config, int argc, char **argv,
|
||||
int *verbose)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@ -330,6 +331,11 @@ int m_config_preparse_command_line(m_config_t *config, int argc, char **argv)
|
||||
// Ignore invalid options
|
||||
if (map_to_option(config, old_syntax, NULL, &opt, ¶m) < 0)
|
||||
continue;
|
||||
// "-v" is handled here
|
||||
if (!bstrcmp0(opt, "v")) {
|
||||
(*verbose)++;
|
||||
continue;
|
||||
}
|
||||
// Set, non-pre-parse options will be ignored
|
||||
int r = m_config_set_option(config, opt, param, old_syntax);
|
||||
if (r < 0)
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
|
||||
char **argv);
|
||||
int m_config_preparse_command_line(m_config_t *config, int argc, char **argv);
|
||||
int m_config_preparse_command_line(m_config_t *config, int argc, char **argv,
|
||||
int *verbose);
|
||||
|
||||
#endif /* MPLAYER_PARSER_MPCMD_H */
|
||||
|
@ -1160,7 +1160,7 @@ void load_font_ft(int width, int height, font_desc_t** fontp, const char *font_n
|
||||
FcConfigSubstitute(0, fc_pattern, FcMatchPattern);
|
||||
FcDefaultSubstitute(fc_pattern);
|
||||
fc_pattern2 = fc_pattern;
|
||||
fc_pattern = FcFontMatch(0, fc_pattern, 0);
|
||||
fc_pattern = FcFontMatch(0, fc_pattern, &result);
|
||||
FcPatternDestroy(fc_pattern2);
|
||||
}
|
||||
// s doesn't need to be freed according to fontconfig docs
|
||||
|
Loading…
Reference in New Issue
Block a user