mirror of https://github.com/mpv-player/mpv
Merge remote-tracking branch 'origin/master'
Conflicts: bstr.c bstr.h etc/input.conf input/input.c input/input.h libao2/ao_pulse.c libmpcodecs/vf_ass.c libmpcodecs/vf_vo.c libvo/gl_common.c libvo/x11_common.c mixer.c mixer.h mplayer.c
This commit is contained in:
commit
1aa2e36122
9
Makefile
9
Makefile
|
@ -99,7 +99,7 @@ SRCS_COMMON-$(LIBTHEORA) += libmpcodecs/vd_theora.c
|
|||
SRCS_COMMON-$(LIVE555) += libmpdemux/demux_rtp.cpp \
|
||||
libmpdemux/demux_rtp_codec.cpp \
|
||||
stream/stream_live555.c
|
||||
SRCS_COMMON-$(MACOSX_FINDER) += osdep/macosx_finder_args.c
|
||||
SRCS_COMMON-$(MACOSX_FINDER) += osdep/macosx_finder_args.m
|
||||
SRCS_COMMON-$(MNG) += libmpdemux/demux_mng.c
|
||||
SRCS_COMMON-$(MPG123) += libmpcodecs/ad_mpg123.c
|
||||
|
||||
|
@ -416,12 +416,12 @@ SRCS_COMMON = asxparser.c \
|
|||
stream/stream_null.c \
|
||||
stream/url.c \
|
||||
sub/av_sub.c \
|
||||
sub/sub.c \
|
||||
sub/sub_cc.c \
|
||||
sub/dec_sub.c \
|
||||
sub/find_sub.c \
|
||||
sub/find_subfiles.c \
|
||||
sub/spudec.c \
|
||||
sub/sub.c \
|
||||
sub/sub_cc.c \
|
||||
sub/subassconvert.c \
|
||||
sub/subreader.c \
|
||||
sub/vobsub.c \
|
||||
|
@ -472,7 +472,6 @@ 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-$(QUARTZ) += libvo/vo_quartz.c libvo/osx_common.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
|
||||
|
@ -528,7 +527,7 @@ OBJS_MPLAYER-$(PE_EXECUTABLE) += osdep/mplayer-rc.o
|
|||
OBJS_MPLAYER += $(OBJS_MPLAYER-yes)
|
||||
|
||||
MPLAYER_DEPS = $(OBJS_MPLAYER) $(OBJS_COMMON) $(COMMON_LIBS)
|
||||
DEP_FILES = $(patsubst %.S,%.d,$(patsubst %.cpp,%.d,$(patsubst %.c,%.d,$(SRCS_COMMON) $(SRCS_MPLAYER:.m=.d))))
|
||||
DEP_FILES = $(patsubst %.S,%.d,$(patsubst %.cpp,%.d,$(patsubst %.c,%.d,$(SRCS_COMMON:.m=.d) $(SRCS_MPLAYER:.m=.d))))
|
||||
|
||||
ALL_PRG-$(MPLAYER) += mplayer$(EXESUF)
|
||||
|
||||
|
|
|
@ -731,11 +731,10 @@ const m_option_t mplayer_opts[]={
|
|||
{"border", &vo_border, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"noborder", &vo_border, CONF_TYPE_FLAG, 0, 1, 0, NULL},
|
||||
|
||||
{"mixer", &mixer_device, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"mixer-channel", &mixer_channel, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"softvol", &soft_vol, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"nosoftvol", &soft_vol, CONF_TYPE_FLAG, 0, 1, 0, NULL},
|
||||
{"softvol-max", &soft_vol_max, CONF_TYPE_FLOAT, CONF_RANGE, 10, 10000, NULL},
|
||||
OPT_STRING("mixer", mixer_device, 0),
|
||||
OPT_STRING("mixer-channel", mixer_channel, 0),
|
||||
OPT_MAKE_FLAGS("softvol", softvol, 0),
|
||||
OPT_FLOATRANGE("softvol-max", softvol_max, 0, 10, 10000),
|
||||
{"volstep", &volstep, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL},
|
||||
{"volume", &start_volume, CONF_TYPE_FLOAT, CONF_RANGE, -1, 10000, NULL},
|
||||
OPT_MAKE_FLAGS("gapless-audio", gapless_audio, 0),
|
||||
|
@ -807,6 +806,7 @@ const m_option_t mplayer_opts[]={
|
|||
|
||||
{"grabpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"nograbpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 1, 0, NULL},
|
||||
OPT_INTRANGE("cursor-autohide-delay", cursor_autohide_delay, 0, -2, 30000),
|
||||
|
||||
{"adapter", &vo_adapter_num, CONF_TYPE_INT, CONF_RANGE, 0, 5, NULL},
|
||||
{"refreshrate",&vo_refresh_rate,CONF_TYPE_INT,CONF_RANGE, 0,100, NULL},
|
||||
|
|
|
@ -1764,7 +1764,7 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg,
|
|||
}
|
||||
#endif
|
||||
|
||||
update_subtitles(mpctx, 0, 0, true);
|
||||
update_subtitles(mpctx, 0, true);
|
||||
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
|
|
|
@ -438,7 +438,6 @@ Video output:
|
|||
--disable-yuv4mpeg disable yuv4mpeg video output [enable]
|
||||
--disable-corevideo disable CoreVideo video output [autodetect]
|
||||
--disable-cocoa disable Cocoa OpenGL backend [autodetect]
|
||||
--disable-quartz disable Quartz video output [autodetect]
|
||||
|
||||
Audio output:
|
||||
--disable-alsa disable ALSA audio output [autodetect]
|
||||
|
@ -701,7 +700,6 @@ _qtx=auto
|
|||
_coreaudio=auto
|
||||
_corevideo=auto
|
||||
_cocoa=auto
|
||||
_quartz=auto
|
||||
quicktime=auto
|
||||
_macosx_finder=no
|
||||
_macosx_bundle=auto
|
||||
|
@ -1139,8 +1137,6 @@ for ac_option do
|
|||
--disable-corevideo) _corevideo=no ;;
|
||||
--enable-cocoa) _cocoa=yes ;;
|
||||
--disable-cocoa) _cocoa=no ;;
|
||||
--enable-quartz) _quartz=yes ;;
|
||||
--disable-quartz) _quartz=no ;;
|
||||
--enable-macosx-finder) _macosx_finder=yes ;;
|
||||
--disable-macosx-finder) _macosx_finder=no ;;
|
||||
--enable-macosx-bundle) _macosx_bundle=yes ;;
|
||||
|
@ -3583,7 +3579,7 @@ echocheck "Mac OS X Finder Support"
|
|||
def_macosx_finder='#undef CONFIG_MACOSX_FINDER'
|
||||
if test "$_macosx_finder" = yes ; then
|
||||
def_macosx_finder='#define CONFIG_MACOSX_FINDER 1'
|
||||
extra_ldflags="$extra_ldflags -framework Carbon"
|
||||
extra_ldflags="$extra_ldflags -framework Cocoa"
|
||||
fi
|
||||
echores "$_macosx_finder"
|
||||
|
||||
|
@ -3592,7 +3588,6 @@ def_macosx_bundle='#undef CONFIG_MACOSX_BUNDLE'
|
|||
test "$_macosx_bundle" = auto && _macosx_bundle=$_macosx_finder
|
||||
if test "$_macosx_bundle" = yes ; then
|
||||
def_macosx_bundle='#define CONFIG_MACOSX_BUNDLE 1'
|
||||
extra_ldflags="$extra_ldflags -framework Carbon"
|
||||
fi
|
||||
echores "$_macosx_bundle"
|
||||
|
||||
|
@ -4218,25 +4213,9 @@ if test "$quicktime" = yes ; then
|
|||
def_quicktime='#define CONFIG_QUICKTIME 1'
|
||||
else
|
||||
def_quicktime='#undef CONFIG_QUICKTIME'
|
||||
_quartz=no
|
||||
fi
|
||||
echores $quicktime
|
||||
|
||||
echocheck "Quartz"
|
||||
if test "$_quartz" = auto ; then
|
||||
_quartz=no
|
||||
statement_check Carbon/Carbon.h 'CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false)' -framework Carbon && _quartz=yes
|
||||
fi
|
||||
if test "$_quartz" = yes ; then
|
||||
libs_mplayer="$libs_mplayer -framework Carbon"
|
||||
def_quartz='#define CONFIG_QUARTZ 1'
|
||||
vomodules="quartz $vomodules"
|
||||
else
|
||||
def_quartz='#undef CONFIG_QUARTZ'
|
||||
novomodules="quartz $novomodules"
|
||||
fi
|
||||
echores $_quartz
|
||||
|
||||
echocheck "CoreVideo"
|
||||
if test "$_corevideo" = auto ; then
|
||||
cat > $TMPC <<EOF
|
||||
|
@ -4573,7 +4552,7 @@ int main(int argc, char *argv[]) {
|
|||
EOF
|
||||
_gl=no
|
||||
for _ld_tmp in "" -lGL "-lGL -lXdamage" "-lGL $_ld_pthread" ; do
|
||||
if cc_check $_ld_tmp $_ld_lm ; then
|
||||
if test "$_cocoa" != yes && cc_check $_ld_tmp $_ld_lm ; then
|
||||
_gl=yes
|
||||
_gl_x11=yes
|
||||
libs_mplayer="$libs_mplayer $_ld_tmp $_ld_dl"
|
||||
|
@ -6563,7 +6542,6 @@ PVR = $_pvr
|
|||
QTX_CODECS = $_qtx
|
||||
QTX_CODECS_WIN32 = $_qtx_codecs_win32
|
||||
QTX_EMULATION = $_qtx_emulation
|
||||
QUARTZ = $_quartz
|
||||
RADIO=$_radio
|
||||
RADIO_CAPTURE=$_radio_capture
|
||||
REAL_CODECS = $_real
|
||||
|
@ -6930,7 +6908,6 @@ $def_mga
|
|||
$def_mng
|
||||
$def_png
|
||||
$def_pnm
|
||||
$def_quartz
|
||||
$def_s3fb
|
||||
$def_sdl
|
||||
$def_sdl_sdl_h
|
||||
|
|
|
@ -10,9 +10,11 @@ void set_default_mplayer_options(struct MPOpts *opts)
|
|||
.audio_driver_list = NULL,
|
||||
.video_driver_list = NULL,
|
||||
.fixed_vo = 1,
|
||||
.softvol_max = 110,
|
||||
.ao_buffersize = -1,
|
||||
.monitor_pixel_aspect = 1.0,
|
||||
.vo_panscanrange = 1.0,
|
||||
.cursor_autohide_delay = 1000,
|
||||
.vo_gamma_gamma = 1000,
|
||||
.vo_gamma_brightness = 1000,
|
||||
.vo_gamma_contrast = 1000,
|
||||
|
|
|
@ -33,16 +33,16 @@ MOUSE_BTN4 seek -10
|
|||
MOUSE_BTN5 volume 1
|
||||
MOUSE_BTN6 volume -1
|
||||
|
||||
# Seek units are in seconds, but note that these are mostly limited by keyframes
|
||||
RIGHT seek 10
|
||||
LEFT seek -10
|
||||
UP seek 60
|
||||
DOWN seek -60
|
||||
# Seek units are in seconds, but note that these are limited by keyframes
|
||||
RIGHT seek 10
|
||||
LEFT seek -10
|
||||
UP seek 60
|
||||
DOWN seek -60
|
||||
# Do smaller, always exact (non-keyframe-limited), seeks with shift.
|
||||
Shift+RIGHT seek 1 0 1
|
||||
Shift+LEFT seek -1 0 1
|
||||
Shift+UP seek 5 0 1
|
||||
Shift+DOWN seek -5 0 1
|
||||
Shift+UP seek 5 0 1
|
||||
Shift+DOWN seek -5 0 1
|
||||
PGUP seek 600
|
||||
PGDWN seek -600
|
||||
+ audio_delay 0.100 # this changes audio/video sync
|
||||
|
@ -87,9 +87,10 @@ m mute
|
|||
7 saturation -1
|
||||
8 saturation 1
|
||||
d frame_drop # cycle through framedrop modes
|
||||
D step_property_osd deinterlace # toggle deinterlacer, requires -vf yadif or kerndeint
|
||||
# toggle deinterlacer; requires either vdpau output, -vf yadif or kerndeint
|
||||
D step_property_osd deinterlace
|
||||
c step_property_osd colormatrix
|
||||
# These currently only work with --no-ass
|
||||
# Next 3 currently only work with --no-ass
|
||||
r sub_pos -1 # move subtitles up
|
||||
t sub_pos +1 # down
|
||||
a sub_alignment
|
||||
|
@ -106,7 +107,7 @@ i edl_mark # for use with --edlout mode
|
|||
T vo_ontop # toggle video window ontop of other windows
|
||||
f vo_fullscreen # toggle fullscreen
|
||||
C step_property_osd capturing
|
||||
s screenshot 0 # take a png screenshot with -vf screenshot
|
||||
s screenshot 0 # take a png screenshot
|
||||
S screenshot 1 # ...on every frame
|
||||
Alt+s screenshot 0 1 # take a screenshot of window contents
|
||||
Alt+S screenshot 1 1 # ...on every frame
|
||||
|
|
314
input/input.c
314
input/input.c
|
@ -71,150 +71,152 @@ struct key_name {
|
|||
char *name;
|
||||
};
|
||||
|
||||
/// This array defines all known commands.
|
||||
/// The first field is an id used to recognize the command without too many strcmp.
|
||||
/// The second is obviously the command name.
|
||||
/// The third is the minimum number of arguments this command needs.
|
||||
/// Then comes the definition of each argument, terminated with an arg of
|
||||
/// type 0 (this doesn't need to be explicit, because C will default initialize
|
||||
/// the following arguments to type 0).
|
||||
/// A command can take a maximum of MP_CMD_MAX_ARGS-1 arguments (-1 because of
|
||||
/// the last one) which is actually 9.
|
||||
|
||||
/// For the args, the first field is the type (actually int, float or string), the second
|
||||
/// is the default value wich is used for optional arguments
|
||||
/* This array defines all known commands.
|
||||
* The first field is an id used to recognize the command.
|
||||
* The second is the command name used in slave mode and input.conf.
|
||||
* Then comes the definition of each argument, first mandatory arguments
|
||||
* (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments
|
||||
* (OARG_INT(default), etc) if any. The command will be given the default
|
||||
* argument value if the user didn't give enough arguments to specify it.
|
||||
* A command can take a maximum of MP_CMD_MAX_ARGS arguments (10).
|
||||
*/
|
||||
#define ARG_INT { .type = MP_CMD_ARG_INT }
|
||||
#define OARG_INT(def) { .type = MP_CMD_ARG_INT, .optional = true, .v.i = def }
|
||||
#define ARG_FLOAT { .type = MP_CMD_ARG_FLOAT }
|
||||
#define OARG_FLOAT(def) { .type = MP_CMD_ARG_FLOAT, .optional = true, .v.f = def }
|
||||
#define ARG_STRING { .type = MP_CMD_ARG_STRING }
|
||||
#define OARG_STRING(def) { .type = MP_CMD_ARG_STRING, .optional = true, .v.s = def }
|
||||
|
||||
static const mp_cmd_t mp_cmds[] = {
|
||||
#ifdef CONFIG_RADIO
|
||||
{ MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", 1, { { MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", 1, { { MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_RADIO_SET_FREQ, "radio_set_freq", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } },
|
||||
{ MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } },
|
||||
{ MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } },
|
||||
#endif
|
||||
{ MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_EDL_MARK, "edl_mark", 0 },
|
||||
{ MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SPEED_INCR, "speed_incr", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_SPEED_MULT, "speed_mult", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_SPEED_SET, "speed_set", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_QUIT, "quit", 0, { {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_STOP, "stop", 0 },
|
||||
{ MP_CMD_PAUSE, "pause", 0 },
|
||||
{ MP_CMD_FRAME_STEP, "frame_step", 0 },
|
||||
{ MP_CMD_PLAY_TREE_STEP, "pt_step", 1, { { MP_CMD_ARG_INT }, { MP_CMD_ARG_INT } } },
|
||||
{ MP_CMD_PLAY_TREE_UP_STEP, "pt_up_step", 1, { { MP_CMD_ARG_INT }, { MP_CMD_ARG_INT } } },
|
||||
{ MP_CMD_PLAY_ALT_SRC_STEP, "alt_src_step", 1, { { MP_CMD_ARG_INT } } },
|
||||
{ MP_CMD_LOOP, "loop", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SUB_DELAY, "sub_delay", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SUB_STEP, "sub_step", 1, { { MP_CMD_ARG_INT }, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_OSD, "osd", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_OSD_SHOW_TEXT, "osd_show_text", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT, {.i = -1}}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text",1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT, {.i = -1}}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", 0 },
|
||||
{ MP_CMD_VOLUME, "volume", 1, { { MP_CMD_ARG_FLOAT }, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_BALANCE, "balance", 1, { { MP_CMD_ARG_FLOAT }, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_MIXER_USEMASTER, "use_master", 0 },
|
||||
{ MP_CMD_MUTE, "mute", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_CONTRAST, "contrast", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_GAMMA, "gamma", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_BRIGHTNESS, "brightness", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_HUE, "hue", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SATURATION, "saturation", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_FRAMEDROPPING, "frame_drop", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SUB_POS, "sub_pos", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SUB_ALIGNMENT, "sub_alignment", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SUB_VISIBILITY, "sub_visibility", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SUB_LOAD, "sub_load", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_SUB_REMOVE, "sub_remove", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SUB_SELECT, "vobsub_lang", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, // for compatibility
|
||||
{ MP_CMD_SUB_SELECT, "sub_select", 0, { { MP_CMD_ARG_INT, {.i = -2}} } },
|
||||
{ MP_CMD_SUB_SOURCE, "sub_source", 0, { { MP_CMD_ARG_INT, {.i = -2}} } },
|
||||
{ MP_CMD_SUB_VOB, "sub_vob", 0, { { MP_CMD_ARG_INT, {.i = -2}} } },
|
||||
{ MP_CMD_SUB_DEMUX, "sub_demux", 0, { { MP_CMD_ARG_INT, {.i = -2}} } },
|
||||
{ MP_CMD_SUB_FILE, "sub_file", 0, { { MP_CMD_ARG_INT, {.i = -2}} } },
|
||||
{ MP_CMD_SUB_LOG, "sub_log", 0 },
|
||||
{ MP_CMD_SUB_SCALE, "sub_scale", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SEEK, "seek", { ARG_FLOAT, OARG_INT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_EDL_MARK, "edl_mark", },
|
||||
{ MP_CMD_AUDIO_DELAY, "audio_delay", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SPEED_INCR, "speed_incr", { ARG_FLOAT } },
|
||||
{ MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } },
|
||||
{ MP_CMD_SPEED_SET, "speed_set", { ARG_FLOAT } },
|
||||
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
|
||||
{ MP_CMD_STOP, "stop", },
|
||||
{ MP_CMD_PAUSE, "pause", },
|
||||
{ MP_CMD_FRAME_STEP, "frame_step", },
|
||||
{ MP_CMD_PLAY_TREE_STEP, "pt_step", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_PLAY_TREE_UP_STEP, "pt_up_step", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_PLAY_ALT_SRC_STEP, "alt_src_step", { ARG_INT } },
|
||||
{ MP_CMD_LOOP, "loop", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_DELAY, "sub_delay", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_STEP, "sub_step", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_OSD, "osd", { OARG_INT(-1) } },
|
||||
{ MP_CMD_OSD_SHOW_TEXT, "osd_show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
|
||||
{ MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
|
||||
{ MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", },
|
||||
{ MP_CMD_VOLUME, "volume", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_BALANCE, "balance", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_MIXER_USEMASTER, "use_master", },
|
||||
{ MP_CMD_MUTE, "mute", { OARG_INT(-1) } },
|
||||
{ MP_CMD_CONTRAST, "contrast", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_GAMMA, "gamma", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_BRIGHTNESS, "brightness", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_HUE, "hue", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SATURATION, "saturation", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_FRAMEDROPPING, "frame_drop", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_POS, "sub_pos", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_ALIGNMENT, "sub_alignment", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_VISIBILITY, "sub_visibility", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } },
|
||||
{ MP_CMD_SUB_REMOVE, "sub_remove", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility
|
||||
{ MP_CMD_SUB_SELECT, "sub_select", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_SOURCE, "sub_source", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_VOB, "sub_vob", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_DEMUX, "sub_demux", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_FILE, "sub_file", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_LOG, "sub_log", },
|
||||
{ MP_CMD_SUB_SCALE, "sub_scale", { ARG_FLOAT, OARG_INT(0) } },
|
||||
#ifdef CONFIG_ASS
|
||||
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } },
|
||||
#endif
|
||||
{ MP_CMD_GET_PERCENT_POS, "get_percent_pos", 0 },
|
||||
{ MP_CMD_GET_TIME_POS, "get_time_pos", 0 },
|
||||
{ MP_CMD_GET_TIME_LENGTH, "get_time_length", 0 },
|
||||
{ MP_CMD_GET_FILENAME, "get_file_name", 0 },
|
||||
{ MP_CMD_GET_VIDEO_CODEC, "get_video_codec", 0 },
|
||||
{ MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", 0 },
|
||||
{ MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", 0 },
|
||||
{ MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", 0 },
|
||||
{ MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", 0 },
|
||||
{ MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", 0 },
|
||||
{ MP_CMD_GET_META_TITLE, "get_meta_title", 0 },
|
||||
{ MP_CMD_GET_META_ARTIST, "get_meta_artist", 0 },
|
||||
{ MP_CMD_GET_META_ALBUM, "get_meta_album", 0 },
|
||||
{ MP_CMD_GET_META_YEAR, "get_meta_year", 0 },
|
||||
{ MP_CMD_GET_META_COMMENT, "get_meta_comment", 0 },
|
||||
{ MP_CMD_GET_META_TRACK, "get_meta_track", 0 },
|
||||
{ MP_CMD_GET_META_GENRE, "get_meta_genre", 0 },
|
||||
{ MP_CMD_SWITCH_AUDIO, "switch_audio", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SWITCH_ANGLE, "switch_angle", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SWITCH_TITLE, "switch_title", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_GET_PERCENT_POS, "get_percent_pos", },
|
||||
{ MP_CMD_GET_TIME_POS, "get_time_pos", },
|
||||
{ MP_CMD_GET_TIME_LENGTH, "get_time_length", },
|
||||
{ MP_CMD_GET_FILENAME, "get_file_name", },
|
||||
{ MP_CMD_GET_VIDEO_CODEC, "get_video_codec", },
|
||||
{ MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", },
|
||||
{ MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", },
|
||||
{ MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", },
|
||||
{ MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", },
|
||||
{ MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", },
|
||||
{ MP_CMD_GET_META_TITLE, "get_meta_title", },
|
||||
{ MP_CMD_GET_META_ARTIST, "get_meta_artist", },
|
||||
{ MP_CMD_GET_META_ALBUM, "get_meta_album", },
|
||||
{ MP_CMD_GET_META_YEAR, "get_meta_year", },
|
||||
{ MP_CMD_GET_META_COMMENT, "get_meta_comment", },
|
||||
{ MP_CMD_GET_META_TRACK, "get_meta_track", },
|
||||
{ MP_CMD_GET_META_GENRE, "get_meta_genre", },
|
||||
{ MP_CMD_SWITCH_AUDIO, "switch_audio", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SWITCH_ANGLE, "switch_angle", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SWITCH_TITLE, "switch_title", { OARG_INT(-1) } },
|
||||
#ifdef CONFIG_TV
|
||||
{ MP_CMD_TV_START_SCAN, "tv_start_scan", 0 },
|
||||
{ MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", 1, { {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_TV_STEP_NORM, "tv_step_norm", 0 },
|
||||
{ MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", 0 },
|
||||
{ MP_CMD_TV_SET_CHANNEL, "tv_set_channel", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", 0 },
|
||||
{ MP_CMD_TV_SET_FREQ, "tv_set_freq", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_TV_STEP_FREQ, "tv_step_freq", 1, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_TV_SET_NORM, "tv_set_norm", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } },
|
||||
{ MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } },
|
||||
{ MP_CMD_TV_SET_HUE, "tv_set_hue", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } },
|
||||
{ MP_CMD_TV_SET_SATURATION, "tv_set_saturation", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } },
|
||||
{ MP_CMD_TV_START_SCAN, "tv_start_scan", },
|
||||
{ MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", { ARG_INT } },
|
||||
{ MP_CMD_TV_STEP_NORM, "tv_step_norm", },
|
||||
{ MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", },
|
||||
{ MP_CMD_TV_SET_CHANNEL, "tv_set_channel", { ARG_STRING } },
|
||||
{ MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", },
|
||||
{ MP_CMD_TV_SET_FREQ, "tv_set_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_TV_STEP_FREQ, "tv_step_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_TV_SET_NORM, "tv_set_norm", { ARG_STRING } },
|
||||
{ MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_HUE, "tv_set_hue", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_SATURATION, "tv_set_saturation", { ARG_INT, OARG_INT(1) } },
|
||||
#endif
|
||||
{ MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", { OARG_INT(-1) } },
|
||||
#ifdef CONFIG_DVBIN
|
||||
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", 2, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } },
|
||||
#endif
|
||||
{ MP_CMD_SWITCH_RATIO, "switch_ratio", 0, { {MP_CMD_ARG_FLOAT} } },
|
||||
{ MP_CMD_VO_FULLSCREEN, "vo_fullscreen", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_VO_ONTOP, "vo_ontop", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_VO_ROOTWIN, "vo_rootwin", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_VO_BORDER, "vo_border", 0, { {MP_CMD_ARG_INT, {.i = -1}} } },
|
||||
{ MP_CMD_SCREENSHOT, "screenshot", 0, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_PANSCAN, "panscan",1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", 0, { {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_LOADFILE, "loadfile", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_LOADLIST, "loadlist", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_PLAY_TREE_CLEAR, "pt_clear", 0 },
|
||||
{ MP_CMD_RUN, "run", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_CAPTURING, "capturing", 0 },
|
||||
{ MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", 2, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_TV_TELETEXT_ADD_DEC, "teletext_add_dec", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link", 1, { {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SWITCH_RATIO, "switch_ratio", { OARG_FLOAT(0) } },
|
||||
{ MP_CMD_VO_FULLSCREEN, "vo_fullscreen", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_ONTOP, "vo_ontop", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_ROOTWIN, "vo_rootwin", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_BORDER, "vo_border", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SCREENSHOT, "screenshot", { OARG_INT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_PANSCAN, "panscan", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", { OARG_INT(0) } },
|
||||
{ MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_INT(0) } },
|
||||
{ MP_CMD_LOADLIST, "loadlist", { ARG_STRING, OARG_INT(0) } },
|
||||
{ MP_CMD_PLAY_TREE_CLEAR, "pt_clear", },
|
||||
{ MP_CMD_RUN, "run", { ARG_STRING } },
|
||||
{ MP_CMD_CAPTURING, "capturing", },
|
||||
{ MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", { ARG_INT, ARG_INT } },
|
||||
{ MP_CMD_TV_TELETEXT_ADD_DEC, "teletext_add_dec", { ARG_STRING } },
|
||||
{ MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link", { ARG_INT } },
|
||||
|
||||
#ifdef CONFIG_DVDNAV
|
||||
{ MP_CMD_DVDNAV, "dvdnav", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_DVDNAV, "dvdnav", { ARG_STRING } },
|
||||
#endif
|
||||
|
||||
{ MP_CMD_GET_VO_FULLSCREEN, "get_vo_fullscreen", 0 },
|
||||
{ MP_CMD_GET_SUB_VISIBILITY, "get_sub_visibility", 0 },
|
||||
{ MP_CMD_KEYDOWN_EVENTS, "key_down_event", 1, { {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SET_PROPERTY, "set_property", 2, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_SET_PROPERTY_OSD, "set_property_osd", 2, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_GET_PROPERTY, "get_property", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_STEP_PROPERTY, "step_property", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_GET_VO_FULLSCREEN, "get_vo_fullscreen", },
|
||||
{ MP_CMD_GET_SUB_VISIBILITY, "get_sub_visibility", },
|
||||
{ MP_CMD_KEYDOWN_EVENTS, "key_down_event", { ARG_INT } },
|
||||
{ MP_CMD_SET_PROPERTY, "set_property", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_SET_PROPERTY_OSD, "set_property_osd", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_GET_PROPERTY, "get_property", { ARG_STRING } },
|
||||
{ MP_CMD_STEP_PROPERTY, "step_property", { ARG_STRING, OARG_FLOAT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", { ARG_STRING, OARG_FLOAT(0), OARG_INT(0) } },
|
||||
|
||||
{ MP_CMD_SEEK_CHAPTER, "seek_chapter", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
{ MP_CMD_SET_MOUSE_POS, "set_mouse_pos", 2, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } },
|
||||
|
||||
{ MP_CMD_AF_SWITCH, "af_switch", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_AF_ADD, "af_add", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_AF_DEL, "af_del", 1, { {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_AF_CLR, "af_clr", 0 },
|
||||
{ MP_CMD_AF_CMDLINE, "af_cmdline", 2, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_STRING} } },
|
||||
{ MP_CMD_SEEK_CHAPTER, "seek_chapter", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SET_MOUSE_POS, "set_mouse_pos", { ARG_INT, ARG_INT } },
|
||||
|
||||
{ MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } },
|
||||
{ MP_CMD_AF_ADD, "af_add", { ARG_STRING } },
|
||||
{ MP_CMD_AF_DEL, "af_del", { ARG_STRING } },
|
||||
{ MP_CMD_AF_CLR, "af_clr", },
|
||||
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
|
||||
{0}
|
||||
};
|
||||
|
||||
|
@ -618,6 +620,8 @@ struct input_ctx {
|
|||
|
||||
struct cmd_queue key_cmd_queue;
|
||||
struct cmd_queue control_cmd_queue;
|
||||
|
||||
int wakeup_pipe[2];
|
||||
};
|
||||
|
||||
|
||||
|
@ -990,10 +994,14 @@ mp_cmd_t *mp_input_parse_cmd(char *str)
|
|||
}
|
||||
cmd->nargs = i;
|
||||
|
||||
if (cmd_def->nargs > cmd->nargs) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s requires at least %d "
|
||||
int min_args;
|
||||
for (min_args = 0; min_args < MP_CMD_MAX_ARGS
|
||||
&& cmd_def->args[min_args].type
|
||||
&& !cmd_def->args[min_args].optional; min_args++);
|
||||
if (cmd->nargs < min_args) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command \"%s\" requires at least %d "
|
||||
"arguments, we found only %d so far.\n", cmd_def->name,
|
||||
cmd_def->nargs, cmd->nargs);
|
||||
min_args, cmd->nargs);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -1116,6 +1124,13 @@ static int default_cmd_func(int fd, char *buf, int l)
|
|||
}
|
||||
}
|
||||
|
||||
static int read_wakeup(void *ctx, int fd)
|
||||
{
|
||||
char buf[100];
|
||||
read(fd, buf, sizeof(buf));
|
||||
return MP_INPUT_NOTHING;
|
||||
}
|
||||
|
||||
|
||||
static char *find_bind_for_key(const struct cmd_bind *binds, int n, int *keys)
|
||||
{
|
||||
|
@ -1138,7 +1153,7 @@ static char *find_bind_for_key(const struct cmd_bind *binds, int n, int *keys)
|
|||
}
|
||||
|
||||
static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
|
||||
char *section)
|
||||
char *section)
|
||||
{
|
||||
struct cmd_bind_section *bind_section = ictx->cmd_bind_sections;
|
||||
|
||||
|
@ -1743,8 +1758,25 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf)
|
|||
.ar_delay = input_conf->ar_delay,
|
||||
.ar_rate = input_conf->ar_rate,
|
||||
.default_bindings = input_conf->default_bindings,
|
||||
.wakeup_pipe = {-1, -1},
|
||||
};
|
||||
|
||||
#ifndef __MINGW32__
|
||||
long ret = pipe(ictx->wakeup_pipe);
|
||||
for (int i = 0; i < 2 && ret >= 0; i++) {
|
||||
ret = fcntl(ictx->wakeup_pipe[i], F_GETFL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = fcntl(ictx->wakeup_pipe[i], F_SETFL, ret | O_NONBLOCK);
|
||||
}
|
||||
if (ret < 0)
|
||||
mp_msg(MSGT_INPUT, MSGL_ERR,
|
||||
"Failed to initialize wakeup pipe: %s\n", strerror(errno));
|
||||
else
|
||||
mp_input_add_key_fd(ictx, ictx->wakeup_pipe[0], true, read_wakeup,
|
||||
NULL, NULL);
|
||||
#endif
|
||||
|
||||
char *file;
|
||||
char *config_file = input_conf->config_file;
|
||||
file = config_file[0] != '/' ? get_path(config_file) : config_file;
|
||||
|
@ -1842,17 +1874,17 @@ void mp_input_uninit(struct input_ctx *ictx)
|
|||
if (!ictx)
|
||||
return;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ictx->num_key_fd; i++) {
|
||||
for (int i = 0; i < ictx->num_key_fd; i++) {
|
||||
if (ictx->key_fds[i].close_func)
|
||||
ictx->key_fds[i].close_func(ictx->key_fds[i].fd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ictx->num_cmd_fd; i++) {
|
||||
for (int i = 0; i < ictx->num_cmd_fd; i++) {
|
||||
if (ictx->cmd_fds[i].close_func)
|
||||
ictx->cmd_fds[i].close_func(ictx->cmd_fds[i].fd);
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (ictx->wakeup_pipe[i] != -1)
|
||||
close(ictx->wakeup_pipe[i]);
|
||||
talloc_free(ictx);
|
||||
}
|
||||
|
||||
|
@ -1892,7 +1924,7 @@ static int print_cmd_list(m_option_t *cfg)
|
|||
default:
|
||||
type = "??";
|
||||
}
|
||||
if (j + 1 > cmd->nargs)
|
||||
if (cmd->args[j].optional)
|
||||
printf(" [%s]", type);
|
||||
else
|
||||
printf(" %s", type);
|
||||
|
@ -1902,6 +1934,12 @@ static int print_cmd_list(m_option_t *cfg)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
void mp_input_wakeup(struct input_ctx *ictx)
|
||||
{
|
||||
if (ictx->wakeup_pipe[1] >= 0)
|
||||
write(ictx->wakeup_pipe[1], &(char){0}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* \param time time to wait for an interruption in milliseconds
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef MPLAYER_INPUT_H
|
||||
#define MPLAYER_INPUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// All command IDs
|
||||
enum mp_command_type {
|
||||
MP_CMD_SEEK,
|
||||
|
@ -154,14 +156,11 @@ enum mp_command_type {
|
|||
};
|
||||
|
||||
// The arg types
|
||||
#define MP_CMD_ARG_VOID 0
|
||||
#define MP_CMD_ARG_INT 1
|
||||
#define MP_CMD_ARG_FLOAT 2
|
||||
#define MP_CMD_ARG_STRING 3
|
||||
|
||||
#ifndef MP_CMD_MAX_ARGS
|
||||
#define MP_CMD_MAX_ARGS 10
|
||||
#endif
|
||||
|
||||
// Error codes for the drivers
|
||||
|
||||
|
@ -181,6 +180,7 @@ struct input_ctx;
|
|||
|
||||
struct mp_cmd_arg {
|
||||
int type;
|
||||
bool optional;
|
||||
union {
|
||||
int i;
|
||||
float f;
|
||||
|
@ -191,8 +191,8 @@ struct mp_cmd_arg {
|
|||
typedef struct mp_cmd {
|
||||
int id;
|
||||
char *name;
|
||||
int nargs;
|
||||
struct mp_cmd_arg args[MP_CMD_MAX_ARGS];
|
||||
int nargs;
|
||||
int pausing;
|
||||
struct mp_cmd *queue_next;
|
||||
} mp_cmd_t;
|
||||
|
@ -272,6 +272,9 @@ void mp_input_uninit(struct input_ctx *ictx);
|
|||
struct m_config;
|
||||
void mp_input_register_options(struct m_config *cfg);
|
||||
|
||||
// Wake up sleeping input loop from another thread.
|
||||
void mp_input_wakeup(struct input_ctx *ictx);
|
||||
|
||||
// Interruptible usleep: (used by libmpdemux)
|
||||
int mp_input_check_interrupt(struct input_ctx *ictx, int time);
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -29,67 +31,80 @@
|
|||
#include "libaf/af_format.h"
|
||||
#include "mp_msg.h"
|
||||
#include "audio_out.h"
|
||||
#include "audio_out_internal.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#define PULSE_CLIENT_NAME "MPlayer"
|
||||
#define PULSE_CLIENT_NAME "mplayer2"
|
||||
|
||||
/** General driver info */
|
||||
static const ao_info_t info = {
|
||||
"PulseAudio audio output",
|
||||
"pulse",
|
||||
"Lennart Poettering",
|
||||
""
|
||||
|
||||
struct priv {
|
||||
// PulseAudio playback stream object
|
||||
struct pa_stream *stream;
|
||||
|
||||
// PulseAudio connection context
|
||||
struct pa_context *context;
|
||||
|
||||
// Main event loop object
|
||||
struct pa_threaded_mainloop *mainloop;
|
||||
|
||||
// temporary during control()
|
||||
struct pa_sink_input_info pi;
|
||||
|
||||
bool broken_pause;
|
||||
int retval;
|
||||
bool did_reset;
|
||||
};
|
||||
|
||||
/** PulseAudio playback stream object */
|
||||
static struct pa_stream *stream;
|
||||
|
||||
/** PulseAudio connection context */
|
||||
static struct pa_context *context;
|
||||
|
||||
/** Main event loop object */
|
||||
static struct pa_threaded_mainloop *mainloop;
|
||||
|
||||
static int broken_pause;
|
||||
|
||||
LIBAO_EXTERN(pulse)
|
||||
|
||||
#define GENERIC_ERR_MSG(ctx, str) \
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] "str": %s\n", \
|
||||
pa_strerror(pa_context_errno(ctx)))
|
||||
pa_strerror(pa_context_errno(ctx)))
|
||||
|
||||
static void context_state_cb(pa_context *c, void *userdata) {
|
||||
static void context_state_cb(pa_context *c, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
switch (pa_context_get_state(c)) {
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
case PA_CONTEXT_FAILED:
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
break;
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
case PA_CONTEXT_FAILED:
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_state_cb(pa_stream *s, void *userdata) {
|
||||
static void stream_state_cb(pa_stream *s, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
switch (pa_stream_get_state(s)) {
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
break;
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
}
|
||||
|
||||
static void stream_latency_update_cb(pa_stream *s, void *userdata) {
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
static void stream_latency_update_cb(pa_stream *s, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
}
|
||||
|
||||
static void success_cb(pa_stream *s, int success, void *userdata) {
|
||||
if (userdata)
|
||||
*(int *)userdata = success;
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
static void success_cb(pa_stream *s, int success, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
priv->retval = success;
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,85 +113,116 @@ static void success_cb(pa_stream *s, int success, void *userdata) {
|
|||
* \param op operation to wait for
|
||||
* \return 1 if operation has finished normally (DONE state), 0 otherwise
|
||||
*/
|
||||
static int waitop(pa_operation *op) {
|
||||
pa_operation_state_t state;
|
||||
static int waitop(struct priv *priv, pa_operation *op)
|
||||
{
|
||||
if (!op) {
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return 0;
|
||||
}
|
||||
state = pa_operation_get_state(op);
|
||||
pa_operation_state_t state = pa_operation_get_state(op);
|
||||
while (state == PA_OPERATION_RUNNING) {
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
state = pa_operation_get_state(op);
|
||||
}
|
||||
pa_operation_unref(op);
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return state == PA_OPERATION_DONE;
|
||||
}
|
||||
|
||||
static const struct format_map_s {
|
||||
static const struct format_map {
|
||||
int mp_format;
|
||||
pa_sample_format_t pa_format;
|
||||
} format_maps[] = {
|
||||
{AF_FORMAT_S16_LE, PA_SAMPLE_S16LE},
|
||||
{AF_FORMAT_S16_BE, PA_SAMPLE_S16BE},
|
||||
#ifdef PA_SAMPLE_S32NE
|
||||
{AF_FORMAT_S32_LE, PA_SAMPLE_S32LE},
|
||||
{AF_FORMAT_S32_BE, PA_SAMPLE_S32BE},
|
||||
#endif
|
||||
#ifdef PA_SAMPLE_FLOAT32NE
|
||||
{AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE},
|
||||
{AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE},
|
||||
#endif
|
||||
{AF_FORMAT_U8, PA_SAMPLE_U8},
|
||||
{AF_FORMAT_MU_LAW, PA_SAMPLE_ULAW},
|
||||
{AF_FORMAT_A_LAW, PA_SAMPLE_ALAW},
|
||||
{AF_FORMAT_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
static int init(int rate_hz, int channels, int format, int flags) {
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
if (priv->stream && !cut_audio) {
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
waitop(priv, pa_stream_drain(priv->stream, success_cb, ao));
|
||||
}
|
||||
|
||||
if (priv->mainloop)
|
||||
pa_threaded_mainloop_stop(priv->mainloop);
|
||||
|
||||
if (priv->stream) {
|
||||
pa_stream_disconnect(priv->stream);
|
||||
pa_stream_unref(priv->stream);
|
||||
priv->stream = NULL;
|
||||
}
|
||||
|
||||
if (priv->context) {
|
||||
pa_context_disconnect(priv->context);
|
||||
pa_context_unref(priv->context);
|
||||
priv->context = NULL;
|
||||
}
|
||||
|
||||
if (priv->mainloop) {
|
||||
pa_threaded_mainloop_free(priv->mainloop);
|
||||
priv->mainloop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int init(struct ao *ao, char *params)
|
||||
{
|
||||
struct pa_sample_spec ss;
|
||||
struct pa_channel_map map;
|
||||
const struct format_map_s *fmt_map;
|
||||
char *devarg = NULL;
|
||||
char *host = NULL;
|
||||
char *sink = NULL;
|
||||
const char *version = pa_get_library_version();
|
||||
|
||||
if (ao_subdevice) {
|
||||
struct priv *priv = talloc_zero(ao, struct priv);
|
||||
ao->priv = priv;
|
||||
|
||||
if (params) {
|
||||
devarg = strdup(ao_subdevice);
|
||||
sink = strchr(devarg, ':');
|
||||
if (sink) *sink++ = 0;
|
||||
if (devarg[0]) host = devarg;
|
||||
if (sink)
|
||||
*sink++ = 0;
|
||||
if (devarg[0])
|
||||
host = devarg;
|
||||
}
|
||||
|
||||
broken_pause = 0;
|
||||
// not sure which versions are affected, assume 0.9.11* to 0.9.14*
|
||||
// known bad: 0.9.14, 0.9.13
|
||||
// known good: 0.9.9, 0.9.10, 0.9.15
|
||||
// to test: pause, wait ca. 5 seconds framestep and see if MPlayer hangs somewhen
|
||||
if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') {
|
||||
mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n"
|
||||
" see http://www.pulseaudio.org/ticket/440\n");
|
||||
broken_pause = 1;
|
||||
priv->broken_pause = false;
|
||||
/* not sure which versions are affected, assume 0.9.11* to 0.9.14*
|
||||
* known bad: 0.9.14, 0.9.13
|
||||
* known good: 0.9.9, 0.9.10, 0.9.15
|
||||
* To test: pause, wait ca. 5 seconds, framestep and see if MPlayer
|
||||
* hangs somewhen. */
|
||||
if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1'
|
||||
&& version[5] <= '4') {
|
||||
mp_msg(MSGT_AO, MSGL_WARN,
|
||||
"[pulse] working around probably broken pause functionality,\n"
|
||||
" see http://www.pulseaudio.org/ticket/440\n");
|
||||
priv->broken_pause = true;
|
||||
}
|
||||
|
||||
ss.channels = channels;
|
||||
ss.rate = rate_hz;
|
||||
ss.channels = ao->channels;
|
||||
ss.rate = ao->samplerate;
|
||||
|
||||
ao_data.samplerate = rate_hz;
|
||||
ao_data.channels = channels;
|
||||
|
||||
fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != format) {
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n");
|
||||
mp_msg(MSGT_AO, MSGL_V,
|
||||
"AO: [pulse] Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
}
|
||||
ao_data.format = fmt_map->mp_format;
|
||||
ao->format = fmt_map->mp_format;
|
||||
ss.format = fmt_map->pa_format;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss)) {
|
||||
|
@ -185,245 +231,295 @@ static int init(int rate_hz, int channels, int format, int flags) {
|
|||
}
|
||||
|
||||
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
|
||||
ao_data.bps = pa_bytes_per_second(&ss);
|
||||
ao->bps = pa_bytes_per_second(&ss);
|
||||
|
||||
if (!(mainloop = pa_threaded_mainloop_new())) {
|
||||
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) {
|
||||
if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api(
|
||||
priv->mainloop), PULSE_CLIENT_NAME))) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(context, context_state_cb, NULL);
|
||||
pa_context_set_state_callback(priv->context, context_state_cb, ao);
|
||||
|
||||
if (pa_context_connect(context, host, 0, NULL) < 0)
|
||||
if (pa_context_connect(priv->context, host, 0, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start(mainloop) < 0)
|
||||
if (pa_threaded_mainloop_start(priv->mainloop) < 0)
|
||||
goto unlock_and_fail;
|
||||
|
||||
/* Wait until the context is ready */
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
|
||||
if (pa_context_get_state(context) != PA_CONTEXT_READY)
|
||||
if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
if (!(stream = pa_stream_new(context, "audio stream", &ss, &map)))
|
||||
if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss,
|
||||
&map)))
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_stream_set_state_callback(stream, stream_state_cb, NULL);
|
||||
pa_stream_set_write_callback(stream, stream_request_cb, NULL);
|
||||
pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);
|
||||
|
||||
if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0)
|
||||
pa_stream_set_state_callback(priv->stream, stream_state_cb, ao);
|
||||
pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
|
||||
pa_stream_set_latency_update_callback(priv->stream,
|
||||
stream_latency_update_cb, ao);
|
||||
pa_buffer_attr bufattr = {
|
||||
.maxlength = -1,
|
||||
.tlength = pa_usec_to_bytes(1000000, &ss),
|
||||
.prebuf = -1,
|
||||
.minreq = -1,
|
||||
.fragsize = -1,
|
||||
};
|
||||
if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
| PA_STREAM_AUTO_TIMING_UPDATE, NULL,
|
||||
NULL) < 0)
|
||||
goto unlock_and_fail;
|
||||
|
||||
/* Wait until the stream is ready */
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
|
||||
if (pa_stream_get_state(stream) != PA_STREAM_READY)
|
||||
if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
free(devarg);
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
unlock_and_fail:
|
||||
|
||||
if (mainloop)
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
if (priv->mainloop)
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
fail:
|
||||
if (context)
|
||||
GENERIC_ERR_MSG(context, "Init failed");
|
||||
if (priv->context)
|
||||
GENERIC_ERR_MSG(priv->context, "Init failed");
|
||||
free(devarg);
|
||||
uninit(1);
|
||||
return 0;
|
||||
uninit(ao, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Destroy libao driver */
|
||||
static void uninit(int immed) {
|
||||
if (stream && !immed) {
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
waitop(pa_stream_drain(stream, success_cb, NULL));
|
||||
}
|
||||
|
||||
if (mainloop)
|
||||
pa_threaded_mainloop_stop(mainloop);
|
||||
|
||||
if (stream) {
|
||||
pa_stream_disconnect(stream);
|
||||
pa_stream_unref(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
pa_context_disconnect(context);
|
||||
pa_context_unref(context);
|
||||
context = NULL;
|
||||
}
|
||||
|
||||
if (mainloop) {
|
||||
pa_threaded_mainloop_free(mainloop);
|
||||
mainloop = NULL;
|
||||
}
|
||||
static void cork(struct ao *ao, bool pause)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
priv->retval = 0;
|
||||
if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) ||
|
||||
!priv->retval)
|
||||
GENERIC_ERR_MSG(priv->context, "pa_stream_cork() failed");
|
||||
}
|
||||
|
||||
/** Play the specified data to the pulseaudio server */
|
||||
static int play(void* data, int len, int flags) {
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (pa_stream_write(stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) {
|
||||
GENERIC_ERR_MSG(context, "pa_stream_write() failed");
|
||||
// Play the specified data to the pulseaudio server
|
||||
static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
/* For some reason Pulseaudio behaves worse if this is done after
|
||||
* the write - rapidly repeated seeks result in bogus increasing
|
||||
* reported latency. */
|
||||
if (priv->did_reset)
|
||||
cork(ao, false);
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
if (pa_stream_write(priv->stream, data, len, NULL, 0,
|
||||
PA_SEEK_RELATIVE) < 0) {
|
||||
GENERIC_ERR_MSG(priv->context, "pa_stream_write() failed");
|
||||
len = -1;
|
||||
}
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
if (priv->did_reset) {
|
||||
priv->did_reset = false;
|
||||
if (!waitop(priv, pa_stream_update_timing_info(priv->stream,
|
||||
success_cb, ao))
|
||||
|| !priv->retval)
|
||||
GENERIC_ERR_MSG(priv->context, "pa_stream_UPP() failed");
|
||||
} else
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return len;
|
||||
}
|
||||
|
||||
static void cork(int b) {
|
||||
int success = 0;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (!waitop(pa_stream_cork(stream, b, success_cb, &success)) ||
|
||||
!success)
|
||||
GENERIC_ERR_MSG(context, "pa_stream_cork() failed");
|
||||
// Reset the audio stream, i.e. flush the playback buffer on the server side
|
||||
static void reset(struct ao *ao)
|
||||
{
|
||||
// pa_stream_flush() works badly if not corked
|
||||
cork(ao, true);
|
||||
struct priv *priv = ao->priv;
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
priv->retval = 0;
|
||||
if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) ||
|
||||
!priv->retval)
|
||||
GENERIC_ERR_MSG(priv->context, "pa_stream_flush() failed");
|
||||
priv->did_reset = true;
|
||||
}
|
||||
|
||||
/** Pause the audio stream by corking it on the server */
|
||||
static void audio_pause(void) {
|
||||
cork(1);
|
||||
// Pause the audio stream by corking it on the server
|
||||
static void pause(struct ao *ao)
|
||||
{
|
||||
cork(ao, true);
|
||||
}
|
||||
|
||||
/** Resume the audio stream by uncorking it on the server */
|
||||
static void audio_resume(void) {
|
||||
// without this, certain versions will cause an infinite hang because
|
||||
// pa_stream_writable_size returns 0 always.
|
||||
// Note that this workaround causes A-V desync after pause
|
||||
if (broken_pause) reset();
|
||||
cork(0);
|
||||
// Resume the audio stream by uncorking it on the server
|
||||
static void resume(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
if (priv->did_reset)
|
||||
return;
|
||||
/* Without this, certain versions will cause an infinite hang because
|
||||
* pa_stream_writable_size returns 0 always.
|
||||
* Note that this workaround causes A-V desync after pause. */
|
||||
if (priv->broken_pause)
|
||||
reset(ao);
|
||||
cork(ao, false);
|
||||
}
|
||||
|
||||
/** Reset the audio stream, i.e. flush the playback buffer on the server side */
|
||||
static void reset(void) {
|
||||
int success = 0;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (!waitop(pa_stream_flush(stream, success_cb, &success)) ||
|
||||
!success)
|
||||
GENERIC_ERR_MSG(context, "pa_stream_flush() failed");
|
||||
// Return number of bytes that may be written to the server without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
size_t space = pa_stream_writable_size(priv->stream);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return space;
|
||||
}
|
||||
|
||||
/** Return number of bytes that may be written to the server without blocking */
|
||||
static int get_space(void) {
|
||||
size_t l;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
l = pa_stream_writable_size(stream);
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Return the current latency in seconds */
|
||||
static float get_delay(void) {
|
||||
// Return the current latency in seconds
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
pa_usec_t latency = (pa_usec_t) -1;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
while (pa_stream_get_latency(stream, &latency, NULL) < 0) {
|
||||
if (pa_context_errno(context) != PA_ERR_NODATA) {
|
||||
GENERIC_ERR_MSG(context, "pa_stream_get_latency() failed");
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
while (pa_stream_get_latency(priv->stream, &latency, NULL) < 0) {
|
||||
if (pa_context_errno(priv->context) != PA_ERR_NODATA) {
|
||||
GENERIC_ERR_MSG(priv->context, "pa_stream_get_latency() failed");
|
||||
break;
|
||||
}
|
||||
/* Wait until latency data is available again */
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return latency == (pa_usec_t) -1 ? 0 : latency / 1000000.0;
|
||||
}
|
||||
|
||||
/** A callback function that is called when the
|
||||
* pa_context_get_sink_input_info() operation completes. */
|
||||
static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
|
||||
struct pa_sink_input_info *pi = userdata;
|
||||
/* A callback function that is called when the
|
||||
* pa_context_get_sink_input_info() operation completes. Saves the
|
||||
* volume field of the specified structure to the global variable volume.
|
||||
*/
|
||||
static void info_func(struct pa_context *c, const struct pa_sink_input_info *i,
|
||||
int is_last, void *userdata)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
struct priv *priv = ao->priv;
|
||||
if (is_last < 0) {
|
||||
GENERIC_ERR_MSG(context, "Failed to get sink input info");
|
||||
GENERIC_ERR_MSG(priv->context, "Failed to get sink input info");
|
||||
return;
|
||||
}
|
||||
if (!i)
|
||||
return;
|
||||
*pi = *i;
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
priv->pi = *i;
|
||||
pa_threaded_mainloop_signal(priv->mainloop, 0);
|
||||
}
|
||||
|
||||
static int control(int cmd, void *arg) {
|
||||
static int control(struct ao *ao, int cmd, void *arg)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
switch (cmd) {
|
||||
case AOCONTROL_GET_MUTE: // fallthrough
|
||||
case AOCONTROL_GET_VOLUME: {
|
||||
struct pa_sink_input_info pi;
|
||||
ao_control_vol_t *vol = arg;
|
||||
uint32_t devidx = pa_stream_get_index(stream);
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &pi))) {
|
||||
GENERIC_ERR_MSG(context, "pa_stream_get_sink_input_info() failed");
|
||||
case AOCONTROL_GET_MUTE: // fallthrough
|
||||
case AOCONTROL_GET_VOLUME: {
|
||||
ao_control_vol_t *vol = arg;
|
||||
uint32_t devidx = pa_stream_get_index(priv->stream);
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx,
|
||||
info_func, ao)))
|
||||
{
|
||||
GENERIC_ERR_MSG(priv->context,
|
||||
"pa_stream_get_sink_input_info() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
// Warning: some information in pi might be unaccessible, because
|
||||
// we naively copied the struct, without updating pointers etc.
|
||||
// Pointers might point to invalid data, accessors might fail.
|
||||
if (cmd == AOCONTROL_GET_VOLUME) {
|
||||
if (priv->pi.volume.channels != 2)
|
||||
vol->left = vol->right =
|
||||
pa_cvolume_avg(&priv->pi.volume) * 100 / PA_VOLUME_NORM;
|
||||
else {
|
||||
vol->left = priv->pi.volume.values[0] * 100 / PA_VOLUME_NORM;
|
||||
vol->right = priv->pi.volume.values[1] * 100 / PA_VOLUME_NORM;
|
||||
}
|
||||
} else if (cmd == AOCONTROL_GET_MUTE) {
|
||||
vol->left = vol->right = priv->pi.mute ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
case AOCONTROL_SET_MUTE: // fallthrough
|
||||
case AOCONTROL_SET_VOLUME: {
|
||||
const ao_control_vol_t *vol = arg;
|
||||
pa_operation *o;
|
||||
struct pa_cvolume volume;
|
||||
|
||||
pa_cvolume_reset(&volume, ao->channels);
|
||||
if (volume.channels != 2)
|
||||
pa_cvolume_set(&volume, volume.channels,
|
||||
(pa_volume_t)vol->left * PA_VOLUME_NORM / 100);
|
||||
else {
|
||||
volume.values[0] = (pa_volume_t)vol->left * PA_VOLUME_NORM / 100;
|
||||
volume.values[1] = (pa_volume_t)vol->right * PA_VOLUME_NORM / 100;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
uint32_t stream_index = pa_stream_get_index(priv->stream);
|
||||
if (cmd == AOCONTROL_SET_VOLUME) {
|
||||
o = pa_context_set_sink_input_volume(priv->context, stream_index,
|
||||
&volume, NULL, NULL);
|
||||
if (!o) {
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
GENERIC_ERR_MSG(priv->context,
|
||||
"pa_context_set_sink_input_volume() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
// Warning: some information in pi might be unaccessible, because
|
||||
// we naively copied the struct, without updating pointers etc.
|
||||
// Pointers might point to invalid data, accessors might fail.
|
||||
if (cmd == AOCONTROL_GET_VOLUME) {
|
||||
if (pi.volume.channels != 2)
|
||||
vol->left = vol->right = pa_cvolume_avg(&pi.volume)*100/PA_VOLUME_NORM;
|
||||
else {
|
||||
vol->left = pi.volume.values[0]*100/PA_VOLUME_NORM;
|
||||
vol->right = pi.volume.values[1]*100/PA_VOLUME_NORM;
|
||||
}
|
||||
} else if (cmd == AOCONTROL_GET_MUTE) {
|
||||
vol->left = vol->right = pi.mute ? 0.0f : 1.0f;
|
||||
} else if (cmd == AOCONTROL_SET_MUTE) {
|
||||
int mute = vol->left == 0.0f || vol->right == 0.0f;
|
||||
o = pa_context_set_sink_input_mute(priv->context, stream_index,
|
||||
mute, NULL, NULL);
|
||||
if (!o) {
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
GENERIC_ERR_MSG(priv->context,
|
||||
"pa_context_set_sink_input_mute() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
|
||||
return CONTROL_OK;
|
||||
} else
|
||||
abort();
|
||||
/* We don't wait for completion here */
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
case AOCONTROL_SET_MUTE: // fallthrough
|
||||
case AOCONTROL_SET_VOLUME: {
|
||||
const ao_control_vol_t *vol = arg;
|
||||
pa_operation *o;
|
||||
struct pa_cvolume volume;
|
||||
|
||||
pa_cvolume_reset(&volume, ao_data.channels);
|
||||
if (volume.channels != 2)
|
||||
pa_cvolume_set(&volume, volume.channels, (pa_volume_t)vol->left*PA_VOLUME_NORM/100);
|
||||
else {
|
||||
volume.values[0] = (pa_volume_t)vol->left*PA_VOLUME_NORM/100;
|
||||
volume.values[1] = (pa_volume_t)vol->right*PA_VOLUME_NORM/100;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (cmd == AOCONTROL_SET_VOLUME) {
|
||||
o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL);
|
||||
if (!o) {
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
} else if (cmd == AOCONTROL_SET_MUTE) {
|
||||
int mute = vol->left == 0.0f || vol->right == 0.0f;
|
||||
o = pa_context_set_sink_input_mute(context, pa_stream_get_index(stream), mute, NULL, NULL);
|
||||
if (!o) {
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
GENERIC_ERR_MSG(context, "pa_context_set_sink_input_mute() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
} else
|
||||
abort();
|
||||
/* We don't wait for completion here */
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_pulse = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"PulseAudio audio output",
|
||||
"pulse",
|
||||
"Lennart Poettering",
|
||||
"",
|
||||
},
|
||||
.control = control,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.reset = reset,
|
||||
.get_space = get_space,
|
||||
.play = play,
|
||||
.get_delay = get_delay,
|
||||
.pause = pause,
|
||||
.resume = resume,
|
||||
};
|
||||
|
|
|
@ -136,10 +136,11 @@ void list_audio_out(void)
|
|||
mp_msg(MSGT_GLOBAL, MSGL_INFO,"\n");
|
||||
}
|
||||
|
||||
struct ao *ao_create(void)
|
||||
struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input)
|
||||
{
|
||||
struct ao *r = talloc(NULL, struct ao);
|
||||
*r = (struct ao){.outburst = OUTBURST, .buffersize = -1};
|
||||
*r = (struct ao){.outburst = OUTBURST, .buffersize = -1,
|
||||
.opts = opts, .input_ctx = input };
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ struct ao {
|
|||
bool no_persistent_volume;
|
||||
const struct ao_driver *driver;
|
||||
void *priv;
|
||||
struct MPOpts *opts;
|
||||
struct input_ctx *input_ctx;
|
||||
};
|
||||
|
||||
extern char *ao_subdevice;
|
||||
|
@ -118,7 +120,7 @@ typedef struct ao_control_vol {
|
|||
float right;
|
||||
} ao_control_vol_t;
|
||||
|
||||
struct ao *ao_create(void);
|
||||
struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input);
|
||||
void ao_init(struct ao *ao, char **ao_list);
|
||||
void ao_uninit(struct ao *ao, bool cut_audio);
|
||||
int ao_play(struct ao *ao, void *data, int len, int flags);
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef MPLAYER_AUDIO_OUT_INTERNAL_H
|
||||
#define MPLAYER_AUDIO_OUT_INTERNAL_H
|
||||
|
||||
#include "options.h"
|
||||
|
||||
// prototypes:
|
||||
//static ao_info_t info;
|
||||
static int control(int cmd, void *arg);
|
||||
|
@ -33,6 +35,8 @@ static void audio_resume(void);
|
|||
|
||||
extern struct ao *global_ao;
|
||||
#define ao_data (*global_ao)
|
||||
#define mixer_channel (global_ao->opts->mixer_channel)
|
||||
#define mixer_device (global_ao->opts->mixer_device)
|
||||
|
||||
#define LIBAO_EXTERN(x) const struct ao_driver audio_out_##x = { \
|
||||
.info = &info, \
|
||||
|
|
|
@ -103,9 +103,6 @@ const char *vo_format_name(int format)
|
|||
case IMGFMT_YUVP: return "Packed YUVP";
|
||||
case IMGFMT_UYVP: return "Packed UYVP";
|
||||
case IMGFMT_MPEGPES: return "Mpeg PES";
|
||||
case IMGFMT_ZRMJPEGNI: return "Zoran MJPEG non-interlaced";
|
||||
case IMGFMT_ZRMJPEGIT: return "Zoran MJPEG top field first";
|
||||
case IMGFMT_ZRMJPEGIB: return "Zoran MJPEG bottom field first";
|
||||
case IMGFMT_XVMC_MOCO_MPEG2: return "MPEG1/2 Motion Compensation";
|
||||
case IMGFMT_XVMC_IDCT_MPEG2: return "MPEG1/2 Motion Compensation and IDCT";
|
||||
case IMGFMT_VDPAU_MPEG1: return "MPEG1 VDPAU acceleration";
|
||||
|
|
|
@ -192,11 +192,6 @@
|
|||
/* Compressed Formats */
|
||||
#define IMGFMT_MPEGPES (('M'<<24)|('P'<<16)|('E'<<8)|('S'))
|
||||
#define IMGFMT_MJPEG (('M')|('J'<<8)|('P'<<16)|('G'<<24))
|
||||
/* Formats that are understood by zoran chips, we include
|
||||
* non-interlaced, interlaced top-first, interlaced bottom-first */
|
||||
#define IMGFMT_ZRMJPEGNI (('Z'<<24)|('R'<<16)|('N'<<8)|('I'))
|
||||
#define IMGFMT_ZRMJPEGIT (('Z'<<24)|('R'<<16)|('I'<<8)|('T'))
|
||||
#define IMGFMT_ZRMJPEGIB (('Z'<<24)|('R'<<16)|('I'<<8)|('B'))
|
||||
|
||||
// I think that this code could not be used by any other codec/format
|
||||
#define IMGFMT_XVMC 0x1DC70000
|
||||
|
|
|
@ -99,9 +99,7 @@ void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
|
|||
mpi->flags&=~(MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV|MP_IMGFLAG_SWAPPED);
|
||||
mpi->imgfmt=out_fmt;
|
||||
// compressed formats
|
||||
if(out_fmt == IMGFMT_MPEGPES ||
|
||||
out_fmt == IMGFMT_ZRMJPEGNI || out_fmt == IMGFMT_ZRMJPEGIT || out_fmt == IMGFMT_ZRMJPEGIB ||
|
||||
IMGFMT_IS_HWACCEL(out_fmt)){
|
||||
if(out_fmt == IMGFMT_MPEGPES || IMGFMT_IS_HWACCEL(out_fmt)){
|
||||
mpi->bpp=0;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -370,7 +370,7 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
|
|||
mp_ass_reload_options(vf->priv->renderer, vf->opts);
|
||||
osd->ass_force_reload = false;
|
||||
images = ass_render_frame(vf->priv->renderer, osd->ass_track,
|
||||
(pts + sub_delay) * 1000 + .5, NULL);
|
||||
(pts - osd->sub_offset + sub_delay) * 1000 + .5, NULL);
|
||||
}
|
||||
|
||||
prepare_image(vf, mpi);
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#include "sub/ass_mp.h"
|
||||
#include "sub/sub.h"
|
||||
|
||||
//===========================================================================//
|
||||
|
||||
extern float sub_delay;
|
||||
|
||||
struct vf_priv_s {
|
||||
|
@ -47,45 +45,47 @@ struct vf_priv_s {
|
|||
};
|
||||
#define video_out (vf->priv->vo)
|
||||
|
||||
static int query_format(struct vf_instance *vf, unsigned int fmt); /* forward declaration */
|
||||
static void draw_slice(struct vf_instance *vf, unsigned char** src, int* stride, int w,int h, int x, int y);
|
||||
static int query_format(struct vf_instance *vf, unsigned int fmt);
|
||||
static void draw_slice(struct vf_instance *vf, unsigned char **src,
|
||||
int *stride, int w, int h, int x, int y);
|
||||
|
||||
static int config(struct vf_instance *vf,
|
||||
int width, int height, int d_width, int d_height,
|
||||
unsigned int flags, unsigned int outfmt){
|
||||
int width, int height, int d_width, int d_height,
|
||||
unsigned int flags, unsigned int outfmt)
|
||||
{
|
||||
|
||||
if ((width <= 0) || (height <= 0) || (d_width <= 0) || (d_height <= 0))
|
||||
{
|
||||
mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n");
|
||||
return 0;
|
||||
if ((width <= 0) || (height <= 0) || (d_width <= 0) || (d_height <= 0)) {
|
||||
mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const vo_info_t *info = video_out->driver->info;
|
||||
mp_msg(MSGT_CPLAYER,MSGL_INFO,"VO: [%s] %dx%d => %dx%d %s %s%s%s%s\n",info->short_name,
|
||||
width, height,
|
||||
d_width, d_height,
|
||||
vo_format_name(outfmt),
|
||||
(flags&VOFLAG_FULLSCREEN)?" [fs]":"",
|
||||
(flags&VOFLAG_MODESWITCHING)?" [vm]":"",
|
||||
(flags&VOFLAG_SWSCALE)?" [zoom]":"",
|
||||
(flags&VOFLAG_FLIPPING)?" [flip]":"");
|
||||
mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Description: %s\n",info->name);
|
||||
mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Author: %s\n", info->author);
|
||||
if(info->comment && strlen(info->comment) > 0)
|
||||
mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Comment: %s\n", info->comment);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_INFO, "VO: [%s] %dx%d => %dx%d %s %s%s%s%s\n",
|
||||
info->short_name,
|
||||
width, height,
|
||||
d_width, d_height,
|
||||
vo_format_name(outfmt),
|
||||
(flags & VOFLAG_FULLSCREEN) ? " [fs]" : "",
|
||||
(flags & VOFLAG_MODESWITCHING) ? " [vm]" : "",
|
||||
(flags & VOFLAG_SWSCALE) ? " [zoom]" : "",
|
||||
(flags & VOFLAG_FLIPPING) ? " [flip]" : "");
|
||||
mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Description: %s\n", info->name);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Author: %s\n", info->author);
|
||||
if (info->comment && strlen(info->comment) > 0)
|
||||
mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Comment: %s\n", info->comment);
|
||||
|
||||
// save vo's stride capability for the wanted colorspace:
|
||||
vf->default_caps=query_format(vf,outfmt);
|
||||
vf->default_caps = query_format(vf, outfmt);
|
||||
vf->draw_slice = (vf->default_caps & VOCAP_NOSLICES) ? NULL : draw_slice;
|
||||
|
||||
if (vo_config(video_out, width, height, d_width, d_height, flags, outfmt))
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_ASS
|
||||
vf->priv->scale_ratio = (double) d_width / d_height * height / width;
|
||||
|
||||
if (vf->priv->renderer) {
|
||||
mp_ass_configure(vf->priv->renderer, vf->opts, width, height,
|
||||
mp_ass_configure(vf->priv->renderer, vf->opts, width, height,
|
||||
vf->default_caps & VFCAP_EOSD_UNSCALED);
|
||||
}
|
||||
|
||||
|
@ -96,40 +96,43 @@ static int config(struct vf_instance *vf,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int control(struct vf_instance *vf, int request, void* data)
|
||||
static int control(struct vf_instance *vf, int request, void *data)
|
||||
{
|
||||
switch(request){
|
||||
switch (request) {
|
||||
case VFCTRL_GET_DEINTERLACE:
|
||||
{
|
||||
if(!video_out) return CONTROL_FALSE; // vo not configured?
|
||||
if (!video_out)
|
||||
return CONTROL_FALSE; // vo not configured?
|
||||
return vo_control(video_out, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE;
|
||||
}
|
||||
case VFCTRL_SET_DEINTERLACE:
|
||||
{
|
||||
if(!video_out) return CONTROL_FALSE; // vo not configured?
|
||||
if (!video_out)
|
||||
return CONTROL_FALSE; // vo not configured?
|
||||
return vo_control(video_out, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE;
|
||||
}
|
||||
case VFCTRL_GET_YUV_COLORSPACE:
|
||||
return vo_control(video_out, VOCTRL_GET_YUV_COLORSPACE, data) == true;
|
||||
case VFCTRL_SET_YUV_COLORSPACE:
|
||||
return vo_control(video_out, VOCTRL_SET_YUV_COLORSPACE, data) == true;
|
||||
case VFCTRL_DRAW_OSD:
|
||||
if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured?
|
||||
vo_draw_osd(video_out, data);
|
||||
return CONTROL_TRUE;
|
||||
case VFCTRL_SET_EQUALIZER:
|
||||
{
|
||||
vf_equalizer_t *eq=data;
|
||||
if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured?
|
||||
struct voctrl_set_equalizer_args param = {eq->item, eq->value};
|
||||
return vo_control(video_out, VOCTRL_SET_EQUALIZER, ¶m) == VO_TRUE;
|
||||
if (!video_out->config_ok)
|
||||
return CONTROL_FALSE; // vo not configured?
|
||||
vo_draw_osd(video_out, data);
|
||||
return CONTROL_TRUE;
|
||||
case VFCTRL_SET_EQUALIZER: {
|
||||
vf_equalizer_t *eq = data;
|
||||
if (!video_out->config_ok)
|
||||
return CONTROL_FALSE; // vo not configured?
|
||||
struct voctrl_set_equalizer_args param = {
|
||||
eq->item, eq->value
|
||||
};
|
||||
return vo_control(video_out, VOCTRL_SET_EQUALIZER, ¶m) == VO_TRUE;
|
||||
}
|
||||
case VFCTRL_GET_EQUALIZER:
|
||||
{
|
||||
vf_equalizer_t *eq=data;
|
||||
if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured?
|
||||
struct voctrl_get_equalizer_args param = {eq->item, &eq->value};
|
||||
return vo_control(video_out, VOCTRL_GET_EQUALIZER, ¶m) == VO_TRUE;
|
||||
case VFCTRL_GET_EQUALIZER: {
|
||||
vf_equalizer_t *eq = data;
|
||||
if (!video_out->config_ok)
|
||||
return CONTROL_FALSE; // vo not configured?
|
||||
struct voctrl_get_equalizer_args param = {
|
||||
eq->item, &eq->value
|
||||
};
|
||||
return vo_control(video_out, VOCTRL_GET_EQUALIZER, ¶m) == VO_TRUE;
|
||||
}
|
||||
#ifdef CONFIG_ASS
|
||||
case VFCTRL_INIT_EOSD:
|
||||
|
@ -141,8 +144,7 @@ static int control(struct vf_instance *vf, int request, void* data)
|
|||
vf->priv->prev_visibility = false;
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
case VFCTRL_DRAW_EOSD:
|
||||
{
|
||||
case VFCTRL_DRAW_EOSD: {
|
||||
struct osd_state *osd = data;
|
||||
mp_eosd_images_t images = {NULL, 2};
|
||||
ASS_Renderer *renderer = vf->priv->renderer;
|
||||
|
@ -158,7 +160,7 @@ static int control(struct vf_instance *vf, int request, void* data)
|
|||
vf->priv->prev_visibility = false;
|
||||
osd->ass_track_changed = false;
|
||||
if (sub_visibility && osd->ass_track && (osd->pts != MP_NOPTS_VALUE)) {
|
||||
struct mp_eosd_res res = {0};
|
||||
struct mp_eosd_res res = { 0 };
|
||||
if (vo_control(video_out, VOCTRL_GET_EOSD_RES, &res) == VO_TRUE) {
|
||||
ass_set_frame_size(renderer, res.w, res.h);
|
||||
ass_set_margins(renderer, res.mt, res.mb, res.ml, res.mr);
|
||||
|
@ -168,7 +170,7 @@ static int control(struct vf_instance *vf, int request, void* data)
|
|||
if (osd->ass_force_reload)
|
||||
mp_ass_reload_options(vf->priv->renderer, vf->opts);
|
||||
images.imgs = ass_render_frame(renderer, osd->ass_track,
|
||||
(osd->pts+sub_delay) * 1000 + .5,
|
||||
(osd->pts + sub_delay) * 1000 + .5,
|
||||
&images.changed);
|
||||
if (!vf->priv->prev_visibility || osd->ass_force_reload)
|
||||
images.changed = 2;
|
||||
|
@ -183,53 +185,58 @@ static int control(struct vf_instance *vf, int request, void* data)
|
|||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
static int query_format(struct vf_instance *vf, unsigned int fmt){
|
||||
static int query_format(struct vf_instance *vf, unsigned int fmt)
|
||||
{
|
||||
int flags = vo_control(video_out, VOCTRL_QUERY_FORMAT, &fmt);
|
||||
// draw_slice() accepts stride, draw_frame() doesn't:
|
||||
if(flags)
|
||||
if(fmt==IMGFMT_YV12 || fmt==IMGFMT_I420 || fmt==IMGFMT_IYUV)
|
||||
flags|=VFCAP_ACCEPT_STRIDE;
|
||||
if (flags)
|
||||
if (fmt == IMGFMT_YV12 || fmt == IMGFMT_I420 || fmt == IMGFMT_IYUV)
|
||||
flags |= VFCAP_ACCEPT_STRIDE;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void get_image(struct vf_instance *vf,
|
||||
mp_image_t *mpi){
|
||||
mp_image_t *mpi)
|
||||
{
|
||||
if (!video_out->config_ok)
|
||||
return;
|
||||
// GET_IMAGE is required for hardware-accelerated formats
|
||||
if(vo_directrendering ||
|
||||
IMGFMT_IS_HWACCEL(mpi->imgfmt))
|
||||
vo_control(video_out, VOCTRL_GET_IMAGE, mpi);
|
||||
if (vo_directrendering || IMGFMT_IS_HWACCEL(mpi->imgfmt))
|
||||
vo_control(video_out, VOCTRL_GET_IMAGE, mpi);
|
||||
}
|
||||
|
||||
static int put_image(struct vf_instance *vf,
|
||||
mp_image_t *mpi, double pts){
|
||||
if(!video_out->config_ok) return 0; // vo not configured?
|
||||
// first check, maybe the vo/vf plugin implements draw_image using mpi:
|
||||
if (vo_draw_image(video_out, mpi, pts) >= 0)
|
||||
return 1;
|
||||
// nope, fallback to old draw_frame/draw_slice:
|
||||
if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))){
|
||||
// blit frame:
|
||||
// if(mpi->flags&MP_IMGFLAG_PLANAR)
|
||||
if(vf->default_caps&VFCAP_ACCEPT_STRIDE)
|
||||
vo_draw_slice(video_out, mpi->planes,mpi->stride,mpi->w,mpi->h,0,0);
|
||||
else
|
||||
vo_draw_frame(video_out, mpi->planes);
|
||||
}
|
||||
return 1;
|
||||
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
|
||||
{
|
||||
if (!video_out->config_ok)
|
||||
return 0;
|
||||
// first check, maybe the vo/vf plugin implements draw_image using mpi:
|
||||
if (vo_draw_image(video_out, mpi, pts) >= 0)
|
||||
return 1;
|
||||
// nope, fallback to old draw_frame/draw_slice:
|
||||
if (!(mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))) {
|
||||
// blit frame:
|
||||
if (vf->default_caps & VFCAP_ACCEPT_STRIDE)
|
||||
vo_draw_slice(video_out, mpi->planes, mpi->stride, mpi->w, mpi->h,
|
||||
0, 0);
|
||||
else
|
||||
vo_draw_frame(video_out, mpi->planes);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void start_slice(struct vf_instance *vf,
|
||||
mp_image_t *mpi) {
|
||||
if(!video_out->config_ok) return; // vo not configured?
|
||||
vo_control(video_out, VOCTRL_START_SLICE,mpi);
|
||||
static void start_slice(struct vf_instance *vf, mp_image_t *mpi)
|
||||
{
|
||||
if (!video_out->config_ok)
|
||||
return;
|
||||
vo_control(video_out, VOCTRL_START_SLICE, mpi);
|
||||
}
|
||||
|
||||
static void draw_slice(struct vf_instance *vf,
|
||||
unsigned char** src, int* stride, int w,int h, int x, int y){
|
||||
if(!video_out->config_ok) return; // vo not configured?
|
||||
vo_draw_slice(video_out, src,stride,w,h,x,y);
|
||||
static void draw_slice(struct vf_instance *vf, unsigned char **src,
|
||||
int *stride, int w, int h, int x, int y)
|
||||
{
|
||||
if (!video_out->config_ok)
|
||||
return;
|
||||
vo_draw_slice(video_out, src, stride, w, h, x, y);
|
||||
}
|
||||
|
||||
static void uninit(struct vf_instance *vf)
|
||||
|
@ -245,20 +252,21 @@ static void uninit(struct vf_instance *vf)
|
|||
free(vf->priv);
|
||||
}
|
||||
}
|
||||
//===========================================================================//
|
||||
|
||||
static int vf_open(vf_instance_t *vf, char *args){
|
||||
vf->config=config;
|
||||
vf->control=control;
|
||||
vf->query_format=query_format;
|
||||
vf->get_image=get_image;
|
||||
vf->put_image=put_image;
|
||||
vf->draw_slice=draw_slice;
|
||||
vf->start_slice=start_slice;
|
||||
vf->uninit=uninit;
|
||||
vf->priv=calloc(1, sizeof(struct vf_priv_s));
|
||||
static int vf_open(vf_instance_t *vf, char *args)
|
||||
{
|
||||
vf->config = config;
|
||||
vf->control = control;
|
||||
vf->query_format = query_format;
|
||||
vf->get_image = get_image;
|
||||
vf->put_image = put_image;
|
||||
vf->draw_slice = draw_slice;
|
||||
vf->start_slice = start_slice;
|
||||
vf->uninit = uninit;
|
||||
vf->priv = calloc(1, sizeof(struct vf_priv_s));
|
||||
vf->priv->vo = (struct vo *)args;
|
||||
if(!video_out) return 0; // no vo ?
|
||||
if (!video_out)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -270,5 +278,3 @@ const vf_info_t vf_info_vo = {
|
|||
vf_open,
|
||||
NULL
|
||||
};
|
||||
|
||||
//===========================================================================//
|
||||
|
|
|
@ -517,7 +517,7 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
|
|||
AVCodec *avc = avcodec_find_decoder(codec->codec_id);
|
||||
const char *codec_name = avc ? avc->name : "unknown";
|
||||
if (!avc && *stream_type == 's' && demuxer->s_streams[i])
|
||||
codec_name = sh_sub_type2str(((sh_sub_t *)demuxer->s_streams[i])->type);
|
||||
codec_name = sh_sub_type2str((demuxer->s_streams[i])->type);
|
||||
mp_msg(MSGT_DEMUX, MSGL_INFO, "[lavf] stream %d: %s (%s), -%cid %d",
|
||||
i, stream_type, codec_name, *stream_type, stream_id);
|
||||
if (lang && lang->value && *stream_type != 'v')
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* Cocoa OpenGL Backend
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_COCOA_COMMON_H
|
||||
#define MPLAYER_COCOA_COMMON_H
|
||||
|
||||
|
@ -16,6 +35,7 @@ int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
|
|||
void vo_cocoa_swap_buffers(void);
|
||||
int vo_cocoa_check_events(struct vo *vo);
|
||||
void vo_cocoa_fullscreen(struct vo *vo);
|
||||
void vo_cocoa_ontop(struct vo *vo);
|
||||
|
||||
// returns an int to conform to the gl extensions from other platforms
|
||||
int vo_cocoa_swap_interval(int enabled);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* Cocoa OpenGL Backend
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
@ -62,8 +81,15 @@ struct vo_cocoa_state {
|
|||
|
||||
NSString *window_title;
|
||||
|
||||
NSInteger windowed_window_level;
|
||||
NSInteger fullscreen_window_level;
|
||||
|
||||
int last_screensaver_update;
|
||||
|
||||
int display_cursor;
|
||||
int cursor_timer;
|
||||
int cursor_autohide_delay;
|
||||
|
||||
bool did_resize;
|
||||
bool out_fs_resize;
|
||||
};
|
||||
|
@ -74,8 +100,10 @@ struct vo *l_vo;
|
|||
|
||||
// local function definitions
|
||||
struct vo_cocoa_state *vo_cocoa_init_state(void);
|
||||
void vo_set_level(int ontop);
|
||||
void update_screen_info(void);
|
||||
void resize_window(struct vo *vo);
|
||||
void vo_cocoa_display_cursor(int requested_state);
|
||||
void create_menu(void);
|
||||
|
||||
bool is_lion_or_better(void);
|
||||
|
@ -89,8 +117,10 @@ struct vo_cocoa_state *vo_cocoa_init_state(void)
|
|||
.previous_video_size = {0,0},
|
||||
.windowed_mask = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask,
|
||||
.fullscreen_mask = NSBorderlessWindowMask,
|
||||
.fullscreen_window_level = NSNormalWindowLevel + 1,
|
||||
.windowed_frame = {{0,0},{0,0}},
|
||||
.out_fs_resize = NO,
|
||||
.display_cursor = 1,
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
@ -99,6 +129,7 @@ int vo_cocoa_init(struct vo *vo)
|
|||
{
|
||||
s = vo_cocoa_init_state();
|
||||
s->pool = [[NSAutoreleasePool alloc] init];
|
||||
s->cursor_autohide_delay = vo->opts->cursor_autohide_delay;
|
||||
NSApplicationLoad();
|
||||
NSApp = [NSApplication sharedApplication];
|
||||
[NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
|
||||
|
@ -155,10 +186,30 @@ void resize_window(struct vo *vo)
|
|||
[s->glContext update];
|
||||
}
|
||||
|
||||
void vo_set_level(int ontop)
|
||||
{
|
||||
if (ontop) {
|
||||
s->windowed_window_level = NSNormalWindowLevel + 1;
|
||||
} else {
|
||||
s->windowed_window_level = NSNormalWindowLevel;
|
||||
}
|
||||
|
||||
if (!vo_fs)
|
||||
[s->window setLevel:s->windowed_window_level];
|
||||
}
|
||||
|
||||
void vo_cocoa_ontop(struct vo *vo)
|
||||
{
|
||||
struct MPOpts *opts = vo->opts;
|
||||
opts->vo_ontop = !opts->vo_ontop;
|
||||
vo_set_level(opts->vo_ontop);
|
||||
}
|
||||
|
||||
int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
|
||||
uint32_t d_height, uint32_t flags,
|
||||
int gl3profile)
|
||||
{
|
||||
struct MPOpts *opts = vo->opts;
|
||||
if (s->current_video_size.width > 0 || s->current_video_size.height > 0)
|
||||
s->previous_video_size = s->current_video_size;
|
||||
s->current_video_size = NSMakeSize(d_width, d_height);
|
||||
|
@ -207,6 +258,8 @@ int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
|
|||
|
||||
if (flags & VOFLAG_FULLSCREEN)
|
||||
vo_cocoa_fullscreen(vo);
|
||||
|
||||
vo_set_level(opts->vo_ontop);
|
||||
} else {
|
||||
if (s->current_video_size.width != s->previous_video_size.width ||
|
||||
s->current_video_size.height != s->previous_video_size.height) {
|
||||
|
@ -238,18 +291,42 @@ void vo_cocoa_swap_buffers()
|
|||
[s->glContext flushBuffer];
|
||||
}
|
||||
|
||||
void vo_cocoa_display_cursor(int requested_state)
|
||||
{
|
||||
if (requested_state) {
|
||||
if (!vo_fs || s->cursor_autohide_delay > -2) {
|
||||
s->display_cursor = requested_state;
|
||||
CGDisplayShowCursor(kCGDirectMainDisplay);
|
||||
}
|
||||
} else {
|
||||
if (s->cursor_autohide_delay != -1) {
|
||||
s->display_cursor = requested_state;
|
||||
CGDisplayHideCursor(kCGDirectMainDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int vo_cocoa_check_events(struct vo *vo)
|
||||
{
|
||||
//update activity every 30 seconds to prevent
|
||||
//screensaver from starting up.
|
||||
int curTime = TickCount()/60;
|
||||
if (curTime - s->last_screensaver_update >= 30 || s->last_screensaver_update == 0)
|
||||
{
|
||||
UpdateSystemActivity(UsrActivity);
|
||||
s->last_screensaver_update = curTime;
|
||||
NSEvent *event;
|
||||
float curTime = TickCount()/60;
|
||||
int msCurTime = (int) (curTime * 1000);
|
||||
|
||||
// automatically hide mouse cursor
|
||||
if (vo_fs && s->display_cursor &&
|
||||
(msCurTime - s->cursor_timer >= s->cursor_autohide_delay)) {
|
||||
vo_cocoa_display_cursor(0);
|
||||
s->cursor_timer = msCurTime;
|
||||
}
|
||||
|
||||
//update activity every 30 seconds to prevent
|
||||
//screensaver from starting up.
|
||||
if ((int)curTime - s->last_screensaver_update >= 30 || s->last_screensaver_update == 0)
|
||||
{
|
||||
UpdateSystemActivity(UsrActivity);
|
||||
s->last_screensaver_update = (int)curTime;
|
||||
}
|
||||
|
||||
NSEvent *event;
|
||||
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil
|
||||
inMode:NSEventTrackingRunLoopMode dequeue:YES];
|
||||
if (event == nil)
|
||||
|
@ -333,14 +410,16 @@ bool is_lion_or_better(void)
|
|||
- (void) fullscreen
|
||||
{
|
||||
if (!vo_fs) {
|
||||
update_screen_info();
|
||||
[NSApp setPresentationOptions:NSApplicationPresentationHideDock|NSApplicationPresentationHideMenuBar];
|
||||
s->windowed_frame = [self frame];
|
||||
[self setHasShadow:NO];
|
||||
[self setStyleMask:s->fullscreen_mask];
|
||||
[self setFrame:s->screen_frame display:YES animate:NO];
|
||||
[self setLevel:NSNormalWindowLevel + 1];
|
||||
CGDisplayHideCursor(kCGDirectMainDisplay);
|
||||
[self setLevel:s->fullscreen_window_level];
|
||||
vo_fs = VO_TRUE;
|
||||
vo_cocoa_display_cursor(0);
|
||||
[self setMovableByWindowBackground: NO];
|
||||
} else {
|
||||
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
|
||||
[self setHasShadow:YES];
|
||||
|
@ -352,9 +431,10 @@ bool is_lion_or_better(void)
|
|||
s->out_fs_resize = NO;
|
||||
}
|
||||
[self setContentAspectRatio:s->current_video_size];
|
||||
[self setLevel:NSNormalWindowLevel];
|
||||
CGDisplayShowCursor(kCGDirectMainDisplay);
|
||||
[self setLevel:s->windowed_window_level];
|
||||
vo_fs = VO_FALSE;
|
||||
vo_cocoa_display_cursor(1);
|
||||
[self setMovableByWindowBackground: YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,6 +452,12 @@ bool is_lion_or_better(void)
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) isMovableByWindowBackground
|
||||
{
|
||||
// this is only valid as a starting value. it will be rewritten in the -fullscreen method.
|
||||
return !vo_fs;
|
||||
}
|
||||
|
||||
- (void) handleQuitEvent:(NSAppleEventDescriptor*)e withReplyEvent:(NSAppleEventDescriptor*)r
|
||||
{
|
||||
mplayer_put_key(l_vo->key_fifo, KEY_CLOSE_WIN);
|
||||
|
@ -400,6 +486,12 @@ bool is_lion_or_better(void)
|
|||
}
|
||||
}
|
||||
|
||||
- (void) mouseMoved: (NSEvent *) theEvent
|
||||
{
|
||||
if (vo_fs)
|
||||
vo_cocoa_display_cursor(1);
|
||||
}
|
||||
|
||||
- (void) mouseDragged:(NSEvent *)theEvent
|
||||
{
|
||||
[self mouseEvent: theEvent];
|
||||
|
@ -445,23 +537,23 @@ bool is_lion_or_better(void)
|
|||
|
||||
- (void) mouseEvent:(NSEvent *)theEvent
|
||||
{
|
||||
if ( [theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9 )
|
||||
{
|
||||
if ([theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9) {
|
||||
int buttonNumber = [theEvent buttonNumber];
|
||||
// Fix to mplayer defined button order: left, middle, right
|
||||
if (buttonNumber == 1)
|
||||
buttonNumber = 2;
|
||||
else if (buttonNumber == 2)
|
||||
buttonNumber = 1;
|
||||
if (buttonNumber == 1) buttonNumber = 2;
|
||||
else if (buttonNumber == 2) buttonNumber = 1;
|
||||
switch ([theEvent type]) {
|
||||
case NSLeftMouseDown:
|
||||
break;
|
||||
case NSRightMouseDown:
|
||||
case NSOtherMouseDown:
|
||||
mplayer_put_key(l_vo->key_fifo, (MOUSE_BTN0 + buttonNumber) | MP_KEY_DOWN);
|
||||
// Looks like Cocoa doesn't create MouseUp events when we are
|
||||
// doing the second click in a double click. Put in the key_fifo
|
||||
// the key that would be put from the MouseUp handling code.
|
||||
if([theEvent clickCount] == 2)
|
||||
mplayer_put_key(l_vo->key_fifo, MOUSE_BTN0 + buttonNumber);
|
||||
break;
|
||||
case NSLeftMouseUp:
|
||||
break;
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseUp:
|
||||
mplayer_put_key(l_vo->key_fifo, MOUSE_BTN0 + buttonNumber);
|
||||
|
@ -473,7 +565,7 @@ bool is_lion_or_better(void)
|
|||
- (void) applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
{
|
||||
if (vo_fs) {
|
||||
[s->window setLevel:NSNormalWindowLevel + 1];
|
||||
[s->window setLevel:s->fullscreen_window_level];
|
||||
[NSApp setPresentationOptions:NSApplicationPresentationHideDock|NSApplicationPresentationHideMenuBar];
|
||||
[s->window makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps: YES];
|
||||
|
@ -483,7 +575,7 @@ bool is_lion_or_better(void)
|
|||
- (void) applicationWillResignActive:(NSNotification *)aNotification
|
||||
{
|
||||
if (vo_fs) {
|
||||
[s->window setLevel:NSNormalWindowLevel];
|
||||
[s->window setLevel:s->windowed_window_level];
|
||||
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2447,6 +2447,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo)
|
|||
ctx->check_events = cocoa_check_events;
|
||||
ctx->update_xinerama_info = cocoa_update_xinerama_info;
|
||||
ctx->fullscreen = cocoa_fullscreen;
|
||||
ctx->ontop = vo_cocoa_ontop;
|
||||
ctx->vo_uninit = vo_cocoa_uninit;
|
||||
if (vo_cocoa_init(vo))
|
||||
return ctx;
|
||||
|
|
|
@ -118,7 +118,6 @@ extern struct vo_driver video_out_tdfx_vid;
|
|||
extern struct vo_driver video_out_xvr100;
|
||||
extern struct vo_driver video_out_tga;
|
||||
extern struct vo_driver video_out_corevideo;
|
||||
extern struct vo_driver video_out_quartz;
|
||||
extern struct vo_driver video_out_pnm;
|
||||
extern struct vo_driver video_out_md5sum;
|
||||
|
||||
|
@ -140,12 +139,12 @@ const struct vo_driver *video_out_drivers[] =
|
|||
#ifdef CONFIG_KVA
|
||||
&video_out_kva,
|
||||
#endif
|
||||
#ifdef CONFIG_GL_COCOA
|
||||
&video_out_gl,
|
||||
#endif
|
||||
#ifdef CONFIG_COREVIDEO
|
||||
&video_out_corevideo,
|
||||
#endif
|
||||
#ifdef CONFIG_QUARTZ
|
||||
&video_out_quartz,
|
||||
#endif
|
||||
#ifdef CONFIG_XMGA
|
||||
&video_out_xmga,
|
||||
#endif
|
||||
|
@ -183,7 +182,7 @@ const struct vo_driver *video_out_drivers[] =
|
|||
#ifdef CONFIG_SDL
|
||||
&video_out_sdl,
|
||||
#endif
|
||||
#ifdef CONFIG_GL
|
||||
#if (defined CONFIG_GL && !defined CONFIG_GL_COCOA)
|
||||
&video_out_gl,
|
||||
#endif
|
||||
#ifdef CONFIG_DGA
|
||||
|
@ -378,6 +377,7 @@ void vo_seek_reset(struct vo *vo)
|
|||
{
|
||||
vo_control(vo, VOCTRL_RESET, NULL);
|
||||
vo->frame_loaded = false;
|
||||
vo->hasframe = false;
|
||||
}
|
||||
|
||||
void vo_destroy(struct vo *vo)
|
||||
|
|
|
@ -378,7 +378,6 @@ struct vo_rect {
|
|||
void calc_src_dst_rects(struct vo *vo, int src_width, int src_height,
|
||||
struct vo_rect *src, struct vo_rect *dst,
|
||||
struct vo_rect *borders, const struct vo_rect *crop);
|
||||
struct input_ctx;
|
||||
void vo_mouse_movement(struct vo *vo, int posx, int posy);
|
||||
|
||||
static inline int aspect_scaling(void)
|
||||
|
|
1371
libvo/vo_quartz.c
1371
libvo/vo_quartz.c
File diff suppressed because it is too large
Load Diff
149
libvo/vo_xv.c
149
libvo/vo_xv.c
|
@ -18,58 +18,18 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Number of buffers _FOR_DOUBLEBUFFERING_MODE_
|
||||
// Use option -double to enable double buffering! (default: single buffer)
|
||||
#define NUM_BUFFERS 3
|
||||
|
||||
/*
|
||||
Buffer allocation:
|
||||
|
||||
-nodr:
|
||||
1: TEMP
|
||||
2: 2*TEMP
|
||||
|
||||
-dr:
|
||||
1: TEMP
|
||||
3: 2*STATIC+TEMP
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "options.h"
|
||||
#include "talloc.h"
|
||||
#include "mp_msg.h"
|
||||
#include "video_out.h"
|
||||
#include "libmpcodecs/vfcap.h"
|
||||
#include "libmpcodecs/mp_image.h"
|
||||
#include "osd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "x11_common.h"
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "fastmemcpy.h"
|
||||
#include "sub/sub.h"
|
||||
#include "aspect.h"
|
||||
#include "csputils.h"
|
||||
|
||||
#include "subopt-helper.h"
|
||||
|
||||
#include "libavutil/common.h"
|
||||
|
||||
static const vo_info_t info = {
|
||||
"X11/Xv",
|
||||
"xv",
|
||||
"Gerd Knorr <kraxel@goldbach.in-berlin.de> and others",
|
||||
""
|
||||
};
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_SHM
|
||||
#include <sys/ipc.h>
|
||||
|
@ -81,6 +41,27 @@ static const vo_info_t info = {
|
|||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "talloc.h"
|
||||
#include "mp_msg.h"
|
||||
#include "video_out.h"
|
||||
#include "libmpcodecs/vfcap.h"
|
||||
#include "libmpcodecs/mp_image.h"
|
||||
#include "osd.h"
|
||||
#include "x11_common.h"
|
||||
#include "fastmemcpy.h"
|
||||
#include "sub/sub.h"
|
||||
#include "aspect.h"
|
||||
#include "csputils.h"
|
||||
#include "subopt-helper.h"
|
||||
|
||||
static const vo_info_t info = {
|
||||
"X11/Xv",
|
||||
"xv",
|
||||
"Gerd Knorr <kraxel@goldbach.in-berlin.de> and others",
|
||||
""
|
||||
};
|
||||
|
||||
struct xvctx {
|
||||
XvAdaptorInfo *ai;
|
||||
XvImageFormatValues *fo;
|
||||
|
@ -92,7 +73,7 @@ struct xvctx {
|
|||
bool have_image_copy;
|
||||
bool unchanged_image;
|
||||
int visible_buf;
|
||||
XvImage *xvimage[NUM_BUFFERS + 1];
|
||||
XvImage *xvimage[2 + 1];
|
||||
uint32_t image_width;
|
||||
uint32_t image_height;
|
||||
uint32_t image_format;
|
||||
|
@ -108,7 +89,7 @@ struct xvctx {
|
|||
unsigned char *src, unsigned char *srca,
|
||||
int stride);
|
||||
#ifdef HAVE_SHM
|
||||
XShmSegmentInfo Shminfo[NUM_BUFFERS + 1];
|
||||
XShmSegmentInfo Shminfo[2 + 1];
|
||||
int Shmem_Flag;
|
||||
#endif
|
||||
};
|
||||
|
@ -301,8 +282,7 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
|
|||
for (i = 0; i < ctx->total_buffers; i++)
|
||||
deallocate_xvimage(vo, i);
|
||||
|
||||
ctx->num_buffers =
|
||||
vo_doublebuffering ? (vo_directrendering ? NUM_BUFFERS : 2) : 1;
|
||||
ctx->num_buffers = 2;
|
||||
ctx->total_buffers = ctx->num_buffers + 1;
|
||||
|
||||
for (i = 0; i < ctx->total_buffers; i++)
|
||||
|
@ -462,12 +442,8 @@ static void flip_page(struct vo *vo)
|
|||
/* remember the currently visible buffer */
|
||||
ctx->visible_buf = ctx->current_buf;
|
||||
|
||||
if (ctx->num_buffers > 1) {
|
||||
ctx->current_buf = vo_directrendering ? 0 : ((ctx->current_buf + 1) %
|
||||
ctx->num_buffers);
|
||||
XFlush(vo->x11->display);
|
||||
} else
|
||||
XSync(vo->x11->display, False);
|
||||
ctx->current_buf = (ctx->current_buf + 1) % ctx->num_buffers;
|
||||
XFlush(vo->x11->display);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -553,10 +529,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
|
|||
|
||||
ctx->have_image_copy = false;
|
||||
|
||||
if (mpi->flags & MP_IMGFLAG_DIRECT)
|
||||
// direct rendering:
|
||||
ctx->current_buf = (size_t)(mpi->priv); // hack!
|
||||
else if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
|
||||
if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
|
||||
; // done
|
||||
else if (mpi->flags & MP_IMGFLAG_PLANAR)
|
||||
draw_slice(vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
|
||||
|
@ -577,66 +550,6 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
|
|||
return true;
|
||||
}
|
||||
|
||||
static uint32_t get_image(struct xvctx *ctx, mp_image_t *mpi)
|
||||
{
|
||||
// we shouldn't change current_buf unless we do DR!
|
||||
int buf = ctx->current_buf;
|
||||
|
||||
if (mpi->type == MP_IMGTYPE_STATIC && ctx->num_buffers > 1)
|
||||
return VO_FALSE; // it is not static
|
||||
if (mpi->imgfmt != ctx->image_format)
|
||||
return VO_FALSE; // needs conversion :(
|
||||
if (mpi->flags & MP_IMGFLAG_READABLE
|
||||
&& (mpi->type == MP_IMGTYPE_IPB || mpi->type == MP_IMGTYPE_IP)) {
|
||||
// reference (I/P) frame of IP or IPB:
|
||||
if (ctx->num_buffers < 2)
|
||||
return VO_FALSE; // not enough
|
||||
ctx->current_ip_buf ^= 1;
|
||||
// for IPB with 2 buffers we can DR only one of the 2 P frames:
|
||||
if (mpi->type == MP_IMGTYPE_IPB && ctx->num_buffers < 3
|
||||
&& ctx->current_ip_buf)
|
||||
return VO_FALSE;
|
||||
buf = ctx->current_ip_buf;
|
||||
if (mpi->type == MP_IMGTYPE_IPB)
|
||||
++buf; // preserve space for B
|
||||
}
|
||||
if (mpi->height > ctx->xvimage[buf]->height)
|
||||
return VO_FALSE; //buffer to small
|
||||
if (mpi->width * (mpi->bpp / 8) > ctx->xvimage[buf]->pitches[0])
|
||||
return VO_FALSE; //buffer to small
|
||||
if ((mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))
|
||||
|| (mpi->width * (mpi->bpp / 8) == ctx->xvimage[buf]->pitches[0])) {
|
||||
ctx->current_buf = buf;
|
||||
XvImage *current_image = ctx->xvimage[ctx->current_buf];
|
||||
mpi->planes[0] = current_image->data + current_image->offsets[0];
|
||||
mpi->stride[0] = current_image->pitches[0];
|
||||
mpi->width = mpi->stride[0] / (mpi->bpp / 8);
|
||||
if (mpi->flags & MP_IMGFLAG_PLANAR) {
|
||||
if (mpi->flags & MP_IMGFLAG_SWAPPED) {
|
||||
// I420
|
||||
mpi->planes[1] = current_image->data
|
||||
+ current_image->offsets[1];
|
||||
mpi->planes[2] = current_image->data
|
||||
+ current_image->offsets[2];
|
||||
mpi->stride[1] = current_image->pitches[1];
|
||||
mpi->stride[2] = current_image->pitches[2];
|
||||
} else {
|
||||
// YV12
|
||||
mpi->planes[1] = current_image->data
|
||||
+ current_image->offsets[2];
|
||||
mpi->planes[2] = current_image->data
|
||||
+ current_image->offsets[1];
|
||||
mpi->stride[1] = current_image->pitches[2];
|
||||
mpi->stride[2] = current_image->pitches[1];
|
||||
}
|
||||
}
|
||||
mpi->flags |= MP_IMGFLAG_DIRECT;
|
||||
mpi->priv = (void *)(size_t)ctx->current_buf;
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_FALSE;
|
||||
}
|
||||
|
||||
static int query_format(struct xvctx *ctx, uint32_t format)
|
||||
{
|
||||
uint32_t i;
|
||||
|
@ -813,8 +726,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
|||
return (ctx->is_paused = 0);
|
||||
case VOCTRL_QUERY_FORMAT:
|
||||
return query_format(ctx, *((uint32_t *) data));
|
||||
case VOCTRL_GET_IMAGE:
|
||||
return get_image(ctx, data);
|
||||
case VOCTRL_DRAW_IMAGE:
|
||||
return draw_image(vo, data);
|
||||
case VOCTRL_GET_PANSCAN:
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/keysymdef.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#ifdef CONFIG_XSS
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
|
@ -780,12 +780,13 @@ static int check_resize(struct vo *vo)
|
|||
int vo_x11_check_events(struct vo *vo)
|
||||
{
|
||||
struct vo_x11_state *x11 = vo->x11;
|
||||
struct MPOpts *opts = vo->opts;
|
||||
Display *display = vo->x11->display;
|
||||
int ret = 0;
|
||||
XEvent Event;
|
||||
|
||||
if (x11->vo_mouse_autohide && x11->mouse_waiting_hide &&
|
||||
(GetTimerMS() - x11->mouse_timer >= 1000)) {
|
||||
if (x11->mouse_waiting_hide && opts->cursor_autohide_delay != -1 &&
|
||||
(GetTimerMS() - x11->mouse_timer >= opts->cursor_autohide_delay)) {
|
||||
vo_hidecursor(display, x11->window);
|
||||
x11->mouse_waiting_hide = 0;
|
||||
}
|
||||
|
@ -846,16 +847,14 @@ int vo_x11_check_events(struct vo *vo)
|
|||
case MotionNotify:
|
||||
vo_mouse_movement(vo, Event.xmotion.x, Event.xmotion.y);
|
||||
|
||||
if (x11->vo_mouse_autohide)
|
||||
{
|
||||
if (opts->cursor_autohide_delay > -2) {
|
||||
vo_showcursor(display, x11->window);
|
||||
x11->mouse_waiting_hide = 1;
|
||||
x11->mouse_timer = GetTimerMS();
|
||||
}
|
||||
break;
|
||||
case ButtonPress:
|
||||
if (x11->vo_mouse_autohide)
|
||||
{
|
||||
if (opts->cursor_autohide_delay > -2) {
|
||||
vo_showcursor(display, x11->window);
|
||||
x11->mouse_waiting_hide = 1;
|
||||
x11->mouse_timer = GetTimerMS();
|
||||
|
@ -865,8 +864,7 @@ int vo_x11_check_events(struct vo *vo)
|
|||
| MP_KEY_DOWN);
|
||||
break;
|
||||
case ButtonRelease:
|
||||
if (x11->vo_mouse_autohide)
|
||||
{
|
||||
if (opts->cursor_autohide_delay > -2) {
|
||||
vo_showcursor(display, x11->window);
|
||||
x11->mouse_waiting_hide = 1;
|
||||
x11->mouse_timer = GetTimerMS();
|
||||
|
@ -1177,7 +1175,6 @@ final:
|
|||
x11->vo_gc = XCreateGC(mDisplay, x11->window, 0, NULL);
|
||||
|
||||
XSync(mDisplay, False);
|
||||
x11->vo_mouse_autohide = 1;
|
||||
vo->event_fd = ConnectionNumber(x11->display);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ struct vo_x11_state {
|
|||
unsigned long xv_colorkey;
|
||||
unsigned int xv_port;
|
||||
|
||||
int vo_mouse_autohide;
|
||||
int wm_type;
|
||||
int fs_type;
|
||||
int window_state;
|
||||
|
|
24
mixer.c
24
mixer.c
|
@ -25,11 +25,6 @@
|
|||
#include "mixer.h"
|
||||
|
||||
|
||||
char *mixer_device = NULL;
|
||||
char *mixer_channel = NULL;
|
||||
int soft_vol = 0;
|
||||
float soft_vol_max = 110.0;
|
||||
|
||||
static void internal_setvolume(mixer_t *mixer, float l, float r);
|
||||
|
||||
|
||||
|
@ -87,7 +82,7 @@ static void internal_getvolume(mixer_t *mixer, float *l, float *r)
|
|||
*l = 0;
|
||||
*r = 0;
|
||||
if (mixer->ao) {
|
||||
if (soft_vol ||
|
||||
if (mixer->softvol ||
|
||||
CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol))
|
||||
{
|
||||
if (!mixer->afilter)
|
||||
|
@ -100,8 +95,8 @@ static void internal_getvolume(mixer_t *mixer, float *l, float *r)
|
|||
} else {
|
||||
af_from_dB(2, db_vals, db_vals, 20.0, -200.0, 60.0);
|
||||
}
|
||||
vol.left = (db_vals[0] / (soft_vol_max / 100.0)) * 100.0;
|
||||
vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0;
|
||||
vol.left = (db_vals[0] / (mixer->softvol_max / 100.0)) * 100.0;
|
||||
vol.right = (db_vals[1] / (mixer->softvol_max / 100.0)) * 100.0;
|
||||
}
|
||||
*r = vol.right;
|
||||
*l = vol.left;
|
||||
|
@ -121,7 +116,7 @@ static void internal_setvolume(mixer_t *mixer, float l, float r)
|
|||
vol.right = r;
|
||||
vol.left = l;
|
||||
if (mixer->ao) {
|
||||
bool use_softvol = soft_vol;
|
||||
bool use_softvol = mixer->softvol;
|
||||
if (!use_softvol) {
|
||||
if (CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol))
|
||||
{
|
||||
|
@ -136,10 +131,11 @@ static void internal_setvolume(mixer_t *mixer, float l, float r)
|
|||
// af_volume uses values in dB
|
||||
float db_vals[AF_NCH];
|
||||
int i;
|
||||
db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0);
|
||||
db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0);
|
||||
db_vals[0] = (l / 100.0) * (mixer->softvol_max / 100.0);
|
||||
db_vals[1] = (r / 100.0) * (mixer->softvol_max / 100.0);
|
||||
for (i = 2; i < AF_NCH; i++)
|
||||
db_vals[i] = ((l + r) / 100.0) * (soft_vol_max / 100.0) / 2.0;
|
||||
db_vals[i] = ((l + r) / 100.0) * (mixer->softvol_max / 100.0)
|
||||
/ 2.0;
|
||||
af_to_dB(AF_NCH, db_vals, db_vals, 20.0);
|
||||
if (!af_control_any_rev(mixer->afilter,
|
||||
AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals))
|
||||
|
@ -225,7 +221,7 @@ void mixer_mute(mixer_t *mixer)
|
|||
bool mixer_getmuted(mixer_t *mixer)
|
||||
{
|
||||
ao_control_vol_t vol = {0};
|
||||
if (!soft_vol &&
|
||||
if (!mixer->softvol &&
|
||||
CONTROL_OK == ao_control(mixer->ao, AOCONTROL_GET_MUTE, &vol))
|
||||
{
|
||||
mixer->muted = vol.left == 0.0f || vol.right == 0.0f;
|
||||
|
@ -243,7 +239,7 @@ void mixer_setmuted(mixer_t *mixer, bool mute)
|
|||
return;
|
||||
ao_control_vol_t vol;
|
||||
vol.left = vol.right = mute ? 0.0f : 1.0f;
|
||||
mixer->mute_emulation = soft_vol ||
|
||||
mixer->mute_emulation = mixer->softvol ||
|
||||
CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_MUTE, &vol);
|
||||
if (mixer->mute_emulation) {
|
||||
// mute is emulated by setting volume to 0
|
||||
|
|
9
mixer.h
9
mixer.h
|
@ -24,17 +24,14 @@
|
|||
#include "libaf/af.h"
|
||||
#include "libao2/audio_out.h"
|
||||
|
||||
extern char * mixer_device;
|
||||
extern char * mixer_channel;
|
||||
extern int soft_vol;
|
||||
extern float soft_vol_max;
|
||||
|
||||
typedef struct mixer_s {
|
||||
typedef struct mixer {
|
||||
struct ao *ao;
|
||||
af_stream_t *afilter;
|
||||
int volstep;
|
||||
bool muted;
|
||||
bool mute_emulation;
|
||||
bool softvol;
|
||||
float softvol_max;
|
||||
float last_l, last_r;
|
||||
float restore_vol_l, restore_vol_r;
|
||||
bool restore_volume;
|
||||
|
|
|
@ -254,8 +254,7 @@ char *chapter_display_name(struct MPContext *mpctx, int chapter);
|
|||
char *chapter_name(struct MPContext *mpctx, int chapter);
|
||||
double chapter_start_time(struct MPContext *mpctx, int chapter);
|
||||
int get_chapter_count(struct MPContext *mpctx);
|
||||
void update_subtitles(struct MPContext *mpctx, double refpts,
|
||||
double sub_offset, bool reset);
|
||||
void update_subtitles(struct MPContext *mpctx, double refpts, bool reset);
|
||||
|
||||
|
||||
// timeline/tl_matroska.c
|
||||
|
|
4
mp_msg.c
4
mp_msg.c
|
@ -339,7 +339,7 @@ char *mp_gtext(const char *string)
|
|||
* couple of reasons (locale stuff is badly designed and sucks in
|
||||
* general).
|
||||
*
|
||||
* First setting the locale, especially LC_CTYPE, changes the
|
||||
* First, setting the locale, especially LC_CTYPE, changes the
|
||||
* behavior of various C functions and we don't want that - we
|
||||
* want isalpha() for example to always behave like in the C
|
||||
* locale.
|
||||
|
@ -361,7 +361,7 @@ char *mp_gtext(const char *string)
|
|||
* affect gettext itself because it supports specifying the
|
||||
* character set directly with bind_textdomain_codeset()).
|
||||
*
|
||||
* So the only solution (at leat short of trying to work around
|
||||
* So the only solution (at least short of trying to work around
|
||||
* things possibly producing non-utf-8 output) is to leave all the
|
||||
* locale variables unset. Note that this means it's not possible
|
||||
* to get translated output from any libraries we call if they
|
||||
|
|
663
mplayer.c
663
mplayer.c
|
@ -1229,6 +1229,8 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
|||
if (mpctx->time_frame > 0)
|
||||
mpctx->last_av_difference +=
|
||||
mpctx->time_frame * opts->playback_speed;
|
||||
if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE)
|
||||
mpctx->last_av_difference = MP_NOPTS_VALUE;
|
||||
if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50
|
||||
&& !mpctx->drop_message_shown) {
|
||||
mp_tmsg(MSGT_AVSYNC, MSGL_WARN, SystemTooSlow);
|
||||
|
@ -1238,9 +1240,6 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
|||
if (opts->quiet)
|
||||
return;
|
||||
|
||||
if (a_pos == MP_NOPTS_VALUE)
|
||||
a_pos = -9; // don't print a huge negative number
|
||||
|
||||
int width;
|
||||
char *line;
|
||||
unsigned pos = 0;
|
||||
|
@ -1258,8 +1257,11 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
|||
|
||||
// Audio time
|
||||
if (mpctx->sh_audio) {
|
||||
saddf(line, &pos, width, "A:%6.1f ", a_pos);
|
||||
if (!sh_video) {
|
||||
if (a_pos != MP_NOPTS_VALUE)
|
||||
saddf(line, &pos, width, "A:%6.1f ", a_pos);
|
||||
else
|
||||
saddf(line, &pos, width, "A: ??? ");
|
||||
if (!sh_video && a_pos != MP_NOPTS_VALUE) {
|
||||
float len = get_time_length(mpctx);
|
||||
saddf(line, &pos, width, "(");
|
||||
sadd_hhmmssf(line, &pos, width, a_pos);
|
||||
|
@ -1270,13 +1272,22 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
|||
}
|
||||
|
||||
// Video time
|
||||
if (sh_video)
|
||||
saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts);
|
||||
if (sh_video) {
|
||||
if (mpctx->video_pts != MP_NOPTS_VALUE)
|
||||
saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts);
|
||||
else
|
||||
saddf(line, &pos, width, "V: ??? ", mpctx->video_pts);
|
||||
}
|
||||
|
||||
// A-V sync
|
||||
if (mpctx->sh_audio && sh_video)
|
||||
saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ",
|
||||
mpctx->last_av_difference, mpctx->total_avsync_change);
|
||||
if (mpctx->sh_audio && sh_video) {
|
||||
if (mpctx->last_av_difference != MP_NOPTS_VALUE)
|
||||
saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ",
|
||||
mpctx->last_av_difference, mpctx->total_avsync_change);
|
||||
else
|
||||
saddf(line, &pos, width, "A-V: ??? ct:%7.3f ",
|
||||
mpctx->total_avsync_change);
|
||||
}
|
||||
|
||||
// Video stats
|
||||
if (sh_video)
|
||||
|
@ -1770,7 +1781,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
|||
current_module = "af_preinit";
|
||||
if (!(mpctx->initialized_flags & INITIALIZED_AO)) {
|
||||
mpctx->initialized_flags |= INITIALIZED_AO;
|
||||
mpctx->ao = ao_create();
|
||||
mpctx->ao = ao_create(opts, mpctx->input);
|
||||
mpctx->ao->samplerate = force_srate;
|
||||
mpctx->ao->format = opts->audio_output_format;
|
||||
}
|
||||
|
@ -1818,6 +1829,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
|||
}
|
||||
mpctx->mixer.ao = ao;
|
||||
mpctx->mixer.volstep = volstep;
|
||||
mpctx->mixer.softvol = opts->softvol;
|
||||
mpctx->mixer.softvol_max = opts->softvol_max;
|
||||
mixer_reinit(&mpctx->mixer);
|
||||
mpctx->syncing_audio = true;
|
||||
return;
|
||||
|
@ -1898,13 +1911,14 @@ static bool is_av_sub(int type)
|
|||
return type == 'b' || type == 'p' || type == 'x';
|
||||
}
|
||||
|
||||
void update_subtitles(struct MPContext *mpctx, double refpts,
|
||||
double sub_offset, bool reset)
|
||||
void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
|
||||
{
|
||||
mpctx->osd->sub_offset = mpctx->video_offset;
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
struct sh_video *sh_video = mpctx->sh_video;
|
||||
struct demux_stream *d_sub = mpctx->d_sub;
|
||||
double curpts = refpts + sub_delay;
|
||||
double refpts_s = refpts_tl - mpctx->osd->sub_offset;
|
||||
double curpts_s = refpts_s + sub_delay;
|
||||
unsigned char *packet = NULL;
|
||||
int len;
|
||||
struct sh_sub *sh_sub = d_sub->sh;
|
||||
|
@ -1929,7 +1943,7 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
if (sub_fps == 0)
|
||||
sub_fps = sh_video ? sh_video->fps : 25;
|
||||
current_module = "find_sub";
|
||||
find_sub(mpctx, mpctx->subdata, curpts *
|
||||
find_sub(mpctx, mpctx->subdata, curpts_s *
|
||||
(mpctx->subdata->sub_uses_time ? 100. : sub_fps));
|
||||
if (vo_sub)
|
||||
mpctx->vo_sub_last = vo_sub;
|
||||
|
@ -1944,13 +1958,13 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
// Vobsub
|
||||
len = 0;
|
||||
if (vo_vobsub) {
|
||||
if (curpts >= 0) {
|
||||
len = vobsub_get_packet(vo_vobsub, curpts,
|
||||
if (curpts_s >= 0) {
|
||||
len = vobsub_get_packet(vo_vobsub, curpts_s,
|
||||
(void **)&packet, ×tamp);
|
||||
if (len > 0)
|
||||
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rVOB sub: len=%d "
|
||||
"v_pts=%5.3f v_timer=%5.3f sub=%5.3f ts=%d \n",
|
||||
len, refpts, sh_video->timer,
|
||||
len, refpts_s, sh_video->timer,
|
||||
timestamp / 90000.0, timestamp);
|
||||
}
|
||||
} else {
|
||||
|
@ -1962,14 +1976,14 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
// d_video->pts which would have been the simplest
|
||||
// improvement doesn't work because mpeg specific hacks
|
||||
// in video.c set d_video->pts to 0.
|
||||
float x = d_sub->pts - refpts;
|
||||
float x = d_sub->pts - refpts_s;
|
||||
if (x > -20 && x < 20) // prevent missing subs on pts reset
|
||||
timestamp = 90000 * d_sub->pts;
|
||||
else
|
||||
timestamp = 90000 * curpts;
|
||||
timestamp = 90000 * curpts_s;
|
||||
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d "
|
||||
"v_pts=%5.3f s_pts=%5.3f ts=%d \n", len,
|
||||
refpts, d_sub->pts, timestamp);
|
||||
refpts_s, d_sub->pts, timestamp);
|
||||
}
|
||||
}
|
||||
if (len <= 0 || !packet)
|
||||
|
@ -1995,19 +2009,19 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
ds_get_next_pts(d_sub);
|
||||
|
||||
while (d_sub->first) {
|
||||
double subpts = ds_get_next_pts(d_sub) + sub_offset;
|
||||
if (subpts > curpts) {
|
||||
double subpts_s = ds_get_next_pts(d_sub);
|
||||
if (subpts_s > curpts_s) {
|
||||
// Libass handled subs can be fed to it in advance
|
||||
if (!opts->ass_enabled || !is_text_sub(type))
|
||||
break;
|
||||
// Try to avoid demuxing whole file at once
|
||||
if (d_sub->non_interleaved && subpts > curpts + 1)
|
||||
if (d_sub->non_interleaved && subpts_s > curpts_s + 1)
|
||||
break;
|
||||
}
|
||||
double duration = d_sub->first->duration;
|
||||
len = ds_get_packet_sub(d_sub, &packet);
|
||||
if (is_av_sub(type)) {
|
||||
int ret = decode_avsub(sh_sub, packet, len, subpts, duration);
|
||||
int ret = decode_avsub(sh_sub, packet, len, subpts_s, duration);
|
||||
if (ret < 0)
|
||||
mp_msg(MSGT_SPUDEC, MSGL_WARN, "lavc failed decoding "
|
||||
"subtitle\n");
|
||||
|
@ -2036,10 +2050,10 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
continue;
|
||||
}
|
||||
if (sh_sub && sh_sub->active) {
|
||||
sub_decode(sh_sub, mpctx->osd, packet, len, subpts, duration);
|
||||
sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration);
|
||||
continue;
|
||||
}
|
||||
if (subpts != MP_NOPTS_VALUE) {
|
||||
if (subpts_s != MP_NOPTS_VALUE) {
|
||||
if (duration < 0)
|
||||
sub_clear_text(&subs, MP_NOPTS_VALUE);
|
||||
if (type == 'a') { // ssa/ass subs without libass => convert to plaintext
|
||||
|
@ -2053,21 +2067,21 @@ void update_subtitles(struct MPContext *mpctx, double refpts,
|
|||
len -= p - packet;
|
||||
packet = p;
|
||||
}
|
||||
double endpts = MP_NOPTS_VALUE;
|
||||
if (subpts != MP_NOPTS_VALUE && duration >= 0)
|
||||
endpts = subpts + duration;
|
||||
sub_add_text(&subs, packet, len, endpts);
|
||||
double endpts_s = MP_NOPTS_VALUE;
|
||||
if (subpts_s != MP_NOPTS_VALUE && duration >= 0)
|
||||
endpts_s = subpts_s + duration;
|
||||
sub_add_text(&subs, packet, len, endpts_s);
|
||||
set_osd_subtitle(mpctx, &subs);
|
||||
}
|
||||
if (d_sub->non_interleaved)
|
||||
ds_get_next_pts(d_sub);
|
||||
}
|
||||
if (!opts->ass_enabled)
|
||||
if (sub_clear_text(&subs, curpts))
|
||||
if (sub_clear_text(&subs, curpts_s))
|
||||
set_osd_subtitle(mpctx, &subs);
|
||||
}
|
||||
if (vo_spudec) {
|
||||
spudec_heartbeat(vo_spudec, 90000 * curpts);
|
||||
spudec_heartbeat(vo_spudec, 90000 * curpts_s);
|
||||
if (spudec_changed(vo_spudec))
|
||||
vo_osd_changed(OSDTYPE_SPU);
|
||||
}
|
||||
|
@ -2500,7 +2514,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize)
|
|||
return decode_audio(sh_audio, &ao->buffer, playsize);
|
||||
}
|
||||
|
||||
static int fill_audio_out_buffers(struct MPContext *mpctx)
|
||||
static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
struct ao *ao = mpctx->ao;
|
||||
|
@ -2562,8 +2576,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx)
|
|||
t = GetTimer() - t;
|
||||
tt = t * 0.000001f;
|
||||
audio_time_usage += tt;
|
||||
if (mpctx->timeline && modifiable_audio_format) {
|
||||
double endpts = mpctx->timeline[mpctx->timeline_part + 1].start;
|
||||
if (endpts != MP_NOPTS_VALUE && modifiable_audio_format) {
|
||||
double bytes = (endpts - written_audio_pts(mpctx) + audio_delay)
|
||||
* ao->bps / opts->playback_speed;
|
||||
if (playsize > bytes) {
|
||||
|
@ -2605,65 +2618,6 @@ static int fill_audio_out_buffers(struct MPContext *mpctx)
|
|||
return -partial_fill;
|
||||
}
|
||||
|
||||
static int sleep_until_near_frame(struct MPContext *mpctx, float *time_frame,
|
||||
bool sync_to_audio, float *aq_sleep_time)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
double audio_limit = 2;
|
||||
current_module = "calc_sleep_time";
|
||||
|
||||
if (mpctx->restart_playback)
|
||||
return 0;
|
||||
|
||||
*time_frame -= get_relative_time(mpctx); // reset timer
|
||||
|
||||
if (sync_to_audio) {
|
||||
float delay = ao_get_delay(mpctx->ao);
|
||||
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "delay=%f\n", delay);
|
||||
|
||||
if (opts->autosync) {
|
||||
/*
|
||||
* Adjust this raw delay value by calculating the expected
|
||||
* delay for this frame and generating a new value which is
|
||||
* weighted between the two. The higher autosync is, the
|
||||
* closer to the delay value gets to that which "-nosound"
|
||||
* would have used, and the longer it will take for A/V
|
||||
* sync to settle at the right value (but it eventually will.)
|
||||
* This settling time is very short for values below 100.
|
||||
*/
|
||||
float predicted = mpctx->delay / opts->playback_speed + *time_frame;
|
||||
float difference = delay - predicted;
|
||||
delay = predicted + difference / (float)opts->autosync;
|
||||
}
|
||||
|
||||
*time_frame = delay - mpctx->delay / opts->playback_speed;
|
||||
|
||||
// delay = amount of audio buffered in soundcard/driver
|
||||
delay = FFMIN(delay, 0.5);
|
||||
delay = FFMAX(delay, 0.1);
|
||||
audio_limit = delay;
|
||||
} else {
|
||||
// If we're lagging more than 200 ms behind the right playback rate,
|
||||
// don't try to "catch up".
|
||||
// If benchmark is set always output frames as fast as possible
|
||||
// without sleeping.
|
||||
if (*time_frame < -0.2 || opts->benchmark)
|
||||
*time_frame = 0;
|
||||
}
|
||||
|
||||
double t = *time_frame - mpctx->video_out->flip_queue_offset;
|
||||
|
||||
if (t <= 0.05)
|
||||
return 0;
|
||||
|
||||
t -= 0.05;
|
||||
if (t > audio_limit * 0.6)
|
||||
t = audio_limit * 0.5;
|
||||
*aq_sleep_time += t;
|
||||
mp_input_get_cmd(mpctx->input, t * 1000 + 1, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int reinit_video_chain(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
|
@ -3070,7 +3024,7 @@ static int redraw_osd(struct MPContext *mpctx)
|
|||
return -1;
|
||||
if (vo_redraw_frame(mpctx->video_out) < 0)
|
||||
return -1;
|
||||
mpctx->osd->pts = mpctx->video_pts;
|
||||
mpctx->osd->pts = mpctx->video_pts - mpctx->osd->sub_offset;
|
||||
if (!(sh_video->output_flags & VFCAP_EOSD_FILTER))
|
||||
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
|
||||
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
|
||||
|
@ -3086,39 +3040,7 @@ void add_step_frame(struct MPContext *mpctx)
|
|||
unpause_player(mpctx);
|
||||
}
|
||||
|
||||
static void pause_loop(struct MPContext *mpctx)
|
||||
{
|
||||
mp_cmd_t *cmd;
|
||||
|
||||
update_pause_message(mpctx);
|
||||
|
||||
while ((cmd = mp_input_get_cmd(mpctx->input, 20, 1)) == NULL
|
||||
|| cmd->id == MP_CMD_SET_MOUSE_POS || cmd->pausing == 4) {
|
||||
if (cmd) {
|
||||
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
||||
run_command(mpctx, cmd);
|
||||
mp_cmd_free(cmd);
|
||||
continue;
|
||||
}
|
||||
if (mpctx->sh_video && mpctx->video_out)
|
||||
vo_check_events(mpctx->video_out);
|
||||
update_osd_msg(mpctx);
|
||||
int hack = vo_osd_changed(0);
|
||||
vo_osd_changed(hack);
|
||||
if (hack || mpctx->sh_video && mpctx->video_out->want_redraw)
|
||||
break;
|
||||
update_pause_message(mpctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void reinit_decoders(struct MPContext *mpctx)
|
||||
{
|
||||
reinit_video_chain(mpctx);
|
||||
reinit_audio_chain(mpctx);
|
||||
mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos}, mpctx);
|
||||
}
|
||||
|
||||
static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
||||
static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
|
||||
{
|
||||
if (mpctx->sh_video) {
|
||||
current_module = "seek_video_reset";
|
||||
|
@ -3130,18 +3052,16 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
|||
mpctx->sh_video->last_pts = MP_NOPTS_VALUE;
|
||||
mpctx->delay = 0;
|
||||
mpctx->time_frame = 0;
|
||||
mpctx->restart_playback = true;
|
||||
// Not all demuxers set d_video->pts during seek, so this value
|
||||
// (which is used by at least vobsub code below) may be completely
|
||||
// wrong (probably 0).
|
||||
mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset;
|
||||
mpctx->video_pts = mpctx->sh_video->pts;
|
||||
update_subtitles(mpctx, mpctx->sh_video->pts, mpctx->video_offset,
|
||||
true);
|
||||
update_subtitles(mpctx, mpctx->sh_video->pts, true);
|
||||
update_teletext(mpctx->sh_video, mpctx->demuxer, 1);
|
||||
}
|
||||
|
||||
if (mpctx->sh_audio) {
|
||||
if (mpctx->sh_audio && reset_ac) {
|
||||
current_module = "seek_audio_reset";
|
||||
resync_audio_stream(mpctx->sh_audio);
|
||||
if (reset_ao)
|
||||
|
@ -3149,8 +3069,7 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
|||
mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size;
|
||||
mpctx->sh_audio->a_buffer_len = 0;
|
||||
if (!mpctx->sh_video)
|
||||
update_subtitles(mpctx, mpctx->sh_audio->pts,
|
||||
mpctx->video_offset, true);
|
||||
update_subtitles(mpctx, mpctx->sh_audio->pts, true);
|
||||
}
|
||||
|
||||
if (vo_vobsub && mpctx->sh_video) {
|
||||
|
@ -3158,6 +3077,7 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
|||
vobsub_seek(vo_vobsub, mpctx->sh_video->pts);
|
||||
}
|
||||
|
||||
mpctx->restart_playback = true;
|
||||
mpctx->hrseek_active = false;
|
||||
mpctx->hrseek_framedrop = false;
|
||||
mpctx->total_avsync_change = 0;
|
||||
|
@ -3249,13 +3169,19 @@ static int seek(MPContext *mpctx, struct seek_params seek,
|
|||
if (demuxer_amount == -1) {
|
||||
mpctx->stop_play = AT_END_OF_FILE;
|
||||
// Clear audio from current position
|
||||
if (mpctx->sh_audio) {
|
||||
if (mpctx->sh_audio && !timeline_fallthrough) {
|
||||
ao_reset(mpctx->ao);
|
||||
mpctx->sh_audio->a_buffer_len = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (need_reset) {
|
||||
reinit_video_chain(mpctx);
|
||||
mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos},
|
||||
mpctx);
|
||||
}
|
||||
|
||||
int demuxer_style = 0;
|
||||
switch (seek.type) {
|
||||
case MPSEEK_FACTOR:
|
||||
|
@ -3272,12 +3198,19 @@ static int seek(MPContext *mpctx, struct seek_params seek,
|
|||
demuxer_amount -= opts->hr_seek_demuxer_offset;
|
||||
int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, audio_delay,
|
||||
demuxer_style);
|
||||
if (need_reset)
|
||||
reinit_decoders(mpctx);
|
||||
if (seekresult == 0)
|
||||
if (seekresult == 0) {
|
||||
if (need_reset) {
|
||||
reinit_audio_chain(mpctx);
|
||||
seek_reset(mpctx, !timeline_fallthrough, false);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
seek_reset(mpctx, !timeline_fallthrough);
|
||||
if (need_reset)
|
||||
reinit_audio_chain(mpctx);
|
||||
/* If we just reinitialized audio it doesn't need to be reset,
|
||||
* and resetting could lose audio some decoders produce during init. */
|
||||
seek_reset(mpctx, !timeline_fallthrough, !need_reset);
|
||||
|
||||
/* Use the target time as "current position" for further relative
|
||||
* seeks etc until a new video frame has been decoded */
|
||||
|
@ -3368,9 +3301,11 @@ double get_current_time(struct MPContext *mpctx)
|
|||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
if (demuxer->stream_pts != MP_NOPTS_VALUE)
|
||||
return demuxer->stream_pts;
|
||||
struct sh_video *sh_video = demuxer->video->sh;
|
||||
if (sh_video)
|
||||
return mpctx->video_pts;
|
||||
if (mpctx->sh_video) {
|
||||
double pts = mpctx->video_pts;
|
||||
if (pts != MP_NOPTS_VALUE)
|
||||
return pts;
|
||||
}
|
||||
double apts = playing_audio_pts(mpctx);
|
||||
if (apts != MP_NOPTS_VALUE)
|
||||
return apts;
|
||||
|
@ -3463,7 +3398,7 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts)
|
|||
int res = demuxer_seek_chapter(mpctx->demuxer, chapter, seek_pts);
|
||||
if (res >= 0) {
|
||||
if (*seek_pts == -1)
|
||||
seek_reset(mpctx, true);
|
||||
seek_reset(mpctx, true, true);
|
||||
else {
|
||||
mpctx->last_chapter_seek = res;
|
||||
mpctx->last_chapter_pts = *seek_pts;
|
||||
|
@ -3486,8 +3421,20 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts)
|
|||
static void run_playloop(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
float aq_sleep_time = 0;
|
||||
bool full_audio_buffers = false;
|
||||
bool audio_left = false, video_left = false;
|
||||
double endpts = end_at.type == END_AT_TIME ? end_at.pos : MP_NOPTS_VALUE;
|
||||
bool end_is_chapter = false;
|
||||
double sleeptime = 0.5;
|
||||
bool was_restart = mpctx->restart_playback;
|
||||
|
||||
if (mpctx->timeline) {
|
||||
double end = mpctx->timeline[mpctx->timeline_part + 1].start;
|
||||
if (endpts == MP_NOPTS_VALUE || end < endpts) {
|
||||
endpts = end;
|
||||
end_is_chapter = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->chapterrange[1] > 0) {
|
||||
int cur_chapter = get_current_chapter(mpctx);
|
||||
|
@ -3501,79 +3448,27 @@ static void run_playloop(struct MPContext *mpctx)
|
|||
reinit_audio_chain(mpctx);
|
||||
}
|
||||
|
||||
/*========================== PLAY AUDIO ============================*/
|
||||
|
||||
if (!mpctx->sh_video)
|
||||
mpctx->restart_playback = false;
|
||||
|
||||
if (mpctx->sh_audio && !mpctx->restart_playback) {
|
||||
int status = fill_audio_out_buffers(mpctx);
|
||||
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
||||
if (status == -2)
|
||||
// at eof, all audio at least written to ao
|
||||
if (!mpctx->sh_video)
|
||||
mpctx->stop_play = AT_END_OF_FILE;
|
||||
if (mpctx->step_frames && !mpctx->sh_video) {
|
||||
mpctx->step_frames = 0;
|
||||
pause_player(mpctx);
|
||||
}
|
||||
|
||||
if (mpctx->sh_audio && !mpctx->restart_playback) {
|
||||
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 (!mpctx->sh_video) {
|
||||
if (mpctx->step_frames) {
|
||||
mpctx->step_frames = 0;
|
||||
pause_player(mpctx);
|
||||
}
|
||||
// handle audio-only case:
|
||||
double a_pos = 0, a_buf = 0;
|
||||
// sh_audio can be NULL due to video stream switching
|
||||
// TODO: handle this better
|
||||
if (mpctx->sh_audio) {
|
||||
a_buf = ao_get_delay(mpctx->ao);
|
||||
a_pos = written_audio_pts(mpctx) - mpctx->opts.playback_speed *
|
||||
a_buf;
|
||||
}
|
||||
|
||||
print_status(mpctx, a_pos, false);
|
||||
|
||||
update_subtitles(mpctx, a_pos, mpctx->video_offset, false);
|
||||
update_osd_msg(mpctx);
|
||||
if (end_at.type == END_AT_TIME && end_at.pos < a_pos)
|
||||
mpctx->stop_play = AT_END_OF_FILE;
|
||||
else if (mpctx->timeline && mpctx->stop_play == AT_END_OF_FILE
|
||||
&& mpctx->timeline_part + 1 < mpctx->num_timeline_parts
|
||||
&& mpctx->sh_audio) {
|
||||
struct timeline_part *p = mpctx->timeline + mpctx->timeline_part;
|
||||
if (!opts->gapless_audio && p->source != (p + 1)->source
|
||||
&& a_buf > 0.05) {
|
||||
mpctx->stop_play = KEEP_PLAYING;
|
||||
mp_input_get_cmd(mpctx->input, (a_buf - .05) * 1000, true);
|
||||
} else {
|
||||
seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE,
|
||||
.amount = (p + 1)->start },
|
||||
true);
|
||||
}
|
||||
} else if (!mpctx->stop_play) {
|
||||
int sleep_time = 100;
|
||||
if (mpctx->sh_audio) {
|
||||
if (mpctx->ao->untimed)
|
||||
sleep_time = 0;
|
||||
else if (full_audio_buffers)
|
||||
sleep_time = FFMAX(20, a_buf * 1000 - 50);
|
||||
else
|
||||
sleep_time = 20;
|
||||
sleep_time = FFMIN(sleep_time, 100);
|
||||
}
|
||||
mp_input_get_cmd(mpctx->input, sleep_time, true);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*========================== PLAY VIDEO ============================*/
|
||||
|
||||
double buffered_audio = -1;
|
||||
while (mpctx->sh_video) { // never loops, for "break;" only
|
||||
vo_pts = mpctx->sh_video->timer * 90000.0;
|
||||
vo_fps = mpctx->sh_video->fps;
|
||||
|
||||
bool blit_frame = mpctx->video_out->frame_loaded;
|
||||
if (!blit_frame) {
|
||||
video_left = mpctx->video_out->hasframe;
|
||||
if (!mpctx->video_out->frame_loaded
|
||||
&& (!mpctx->paused || mpctx->restart_playback)) {
|
||||
double frame_time = update_video(mpctx);
|
||||
blit_frame = mpctx->video_out->frame_loaded;
|
||||
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "*** ftime=%5.3f ***\n", frame_time);
|
||||
if (mpctx->sh_video->vf_initialized < 0) {
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
||||
|
@ -3582,25 +3477,14 @@ static void run_playloop(struct MPContext *mpctx)
|
|||
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||
return;
|
||||
}
|
||||
if (frame_time < 0)
|
||||
mpctx->stop_play = AT_END_OF_FILE;
|
||||
else if (!mpctx->restart_playback) {
|
||||
video_left = frame_time >= 0;
|
||||
if (endpts != MP_NOPTS_VALUE)
|
||||
video_left &= mpctx->sh_video->pts < endpts;
|
||||
if (video_left && !mpctx->restart_playback) {
|
||||
mpctx->time_frame += frame_time / opts->playback_speed;
|
||||
adjust_sync(mpctx, frame_time);
|
||||
}
|
||||
}
|
||||
if (mpctx->timeline) {
|
||||
struct timeline_part *next =
|
||||
mpctx->timeline + mpctx->timeline_part + 1;
|
||||
if (mpctx->sh_video->pts >= next->start
|
||||
|| mpctx->stop_play == AT_END_OF_FILE
|
||||
&& mpctx->timeline_part + 1 < mpctx->num_timeline_parts) {
|
||||
seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE,
|
||||
.amount = next->start },
|
||||
true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
|
@ -3622,78 +3506,116 @@ static void run_playloop(struct MPContext *mpctx)
|
|||
}
|
||||
}
|
||||
|
||||
bool frame_time_remaining = sleep_until_near_frame(mpctx,
|
||||
&mpctx->time_frame,
|
||||
full_audio_buffers,
|
||||
&aq_sleep_time);
|
||||
if (!video_left || (mpctx->paused && !mpctx->restart_playback))
|
||||
break;
|
||||
if (!mpctx->video_out->frame_loaded) {
|
||||
sleeptime = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mpctx->time_frame -= get_relative_time(mpctx);
|
||||
if (full_audio_buffers && !mpctx->restart_playback) {
|
||||
buffered_audio = ao_get_delay(mpctx->ao);
|
||||
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "delay=%f\n", buffered_audio);
|
||||
|
||||
if (opts->autosync) {
|
||||
/* Smooth reported playback position from AO by averaging
|
||||
* it with the value expected based on previus value and
|
||||
* time elapsed since then. May help smooth video timing
|
||||
* with audio output that have inaccurate position reporting.
|
||||
* This is badly implemented; the behavior of the smoothing
|
||||
* now undesirably depends on how often this code runs
|
||||
* (mainly depends on video frame rate). */
|
||||
float predicted = (mpctx->delay / opts->playback_speed +
|
||||
mpctx->time_frame);
|
||||
float difference = buffered_audio - predicted;
|
||||
buffered_audio = predicted + difference / opts->autosync;
|
||||
}
|
||||
|
||||
mpctx->time_frame = (buffered_audio -
|
||||
mpctx->delay / opts->playback_speed);
|
||||
} else {
|
||||
/* If we're more than 200 ms behind the right playback
|
||||
* position, don't try to speed up display of following
|
||||
* frames to catch up; continue with default speed from
|
||||
* the current frame instead.
|
||||
* If benchmark is set always output frames immediately
|
||||
* without sleeping.
|
||||
*/
|
||||
if (mpctx->time_frame < -0.2 || opts->benchmark)
|
||||
mpctx->time_frame = 0;
|
||||
}
|
||||
|
||||
double vsleep = mpctx->time_frame - mpctx->video_out->flip_queue_offset;
|
||||
if (vsleep > 0.050) {
|
||||
sleeptime = FFMIN(sleeptime, vsleep - 0.040);
|
||||
break;
|
||||
}
|
||||
sleeptime = 0;
|
||||
|
||||
//=================== FLIP PAGE (VIDEO BLT): ======================
|
||||
|
||||
current_module = "flip_page";
|
||||
if (!frame_time_remaining && blit_frame) {
|
||||
vo_new_frame_imminent(mpctx->video_out);
|
||||
struct sh_video *sh_video = mpctx->sh_video;
|
||||
mpctx->video_pts = sh_video->pts;
|
||||
update_subtitles(mpctx, sh_video->pts, mpctx->video_offset, false);
|
||||
update_teletext(sh_video, mpctx->demuxer, 0);
|
||||
update_osd_msg(mpctx);
|
||||
struct vf_instance *vf = sh_video->vfilter;
|
||||
mpctx->osd->pts = mpctx->video_pts;
|
||||
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
|
||||
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
|
||||
vo_osd_changed(0);
|
||||
vo_new_frame_imminent(mpctx->video_out);
|
||||
struct sh_video *sh_video = mpctx->sh_video;
|
||||
mpctx->video_pts = sh_video->pts;
|
||||
update_subtitles(mpctx, sh_video->pts, false);
|
||||
update_teletext(sh_video, mpctx->demuxer, 0);
|
||||
update_osd_msg(mpctx);
|
||||
struct vf_instance *vf = sh_video->vfilter;
|
||||
mpctx->osd->pts = mpctx->video_pts - mpctx->osd->sub_offset;
|
||||
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
|
||||
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
|
||||
vo_osd_changed(0);
|
||||
|
||||
mpctx->time_frame -= mpctx->video_out->flip_queue_offset;
|
||||
aq_sleep_time += mpctx->time_frame;
|
||||
// flag 256 means: libvo driver does its timing (dvb card)
|
||||
if (mpctx->time_frame > 0.001
|
||||
&& !(mpctx->sh_video->output_flags & VFCAP_TIMER))
|
||||
mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame);
|
||||
mpctx->time_frame += mpctx->video_out->flip_queue_offset;
|
||||
mpctx->time_frame -= mpctx->video_out->flip_queue_offset;
|
||||
float aq_sleep_time = mpctx->time_frame;
|
||||
if (mpctx->time_frame > 0.001
|
||||
&& !(mpctx->sh_video->output_flags & VFCAP_TIMER))
|
||||
mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame);
|
||||
mpctx->time_frame += mpctx->video_out->flip_queue_offset;
|
||||
|
||||
unsigned int t2 = GetTimer();
|
||||
/* Playing with playback speed it's possible to get pathological
|
||||
* cases with mpctx->time_frame negative enough to cause an
|
||||
* overflow in pts_us calculation, thus the FFMAX. */
|
||||
double time_frame = FFMAX(mpctx->time_frame, -1);
|
||||
unsigned int pts_us = mpctx->last_time + time_frame * 1e6;
|
||||
int duration = -1;
|
||||
double pts2 = mpctx->video_out->next_pts2;
|
||||
if (pts2 != MP_NOPTS_VALUE && opts->correct_pts
|
||||
&& !mpctx->restart_playback) {
|
||||
// expected A/V sync correction is ignored
|
||||
double diff = (pts2 - mpctx->video_pts);
|
||||
diff /= opts->playback_speed;
|
||||
if (mpctx->time_frame < 0)
|
||||
diff += mpctx->time_frame;
|
||||
if (diff < 0)
|
||||
diff = 0;
|
||||
if (diff > 10)
|
||||
diff = 10;
|
||||
duration = diff * 1e6;
|
||||
}
|
||||
vo_flip_page(mpctx->video_out, pts_us | 1, duration);
|
||||
unsigned int t2 = GetTimer();
|
||||
/* Playing with playback speed it's possible to get pathological
|
||||
* cases with mpctx->time_frame negative enough to cause an
|
||||
* overflow in pts_us calculation, thus the FFMAX. */
|
||||
double time_frame = FFMAX(mpctx->time_frame, -1);
|
||||
unsigned int pts_us = mpctx->last_time + time_frame * 1e6;
|
||||
int duration = -1;
|
||||
double pts2 = mpctx->video_out->next_pts2;
|
||||
if (pts2 != MP_NOPTS_VALUE && opts->correct_pts &&
|
||||
!mpctx->restart_playback) {
|
||||
// expected A/V sync correction is ignored
|
||||
double diff = (pts2 - mpctx->video_pts);
|
||||
diff /= opts->playback_speed;
|
||||
if (mpctx->time_frame < 0)
|
||||
diff += mpctx->time_frame;
|
||||
if (diff < 0)
|
||||
diff = 0;
|
||||
if (diff > 10)
|
||||
diff = 10;
|
||||
duration = diff * 1e6;
|
||||
}
|
||||
vo_flip_page(mpctx->video_out, pts_us | 1, duration);
|
||||
|
||||
mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001;
|
||||
vout_time_usage += mpctx->last_vo_flip_duration;
|
||||
if (mpctx->video_out->driver->flip_page_timed) {
|
||||
// No need to adjust sync based on flip speed
|
||||
mpctx->last_vo_flip_duration = 0;
|
||||
// For print_status - VO call finishing early is OK for sync
|
||||
mpctx->time_frame -= get_relative_time(mpctx);
|
||||
}
|
||||
if (mpctx->restart_playback) {
|
||||
mpctx->syncing_audio = true;
|
||||
if (mpctx->sh_audio)
|
||||
fill_audio_out_buffers(mpctx);
|
||||
mpctx->restart_playback = false;
|
||||
mpctx->time_frame = 0;
|
||||
get_relative_time(mpctx);
|
||||
}
|
||||
print_status(mpctx, MP_NOPTS_VALUE, true);
|
||||
screenshot_flip(mpctx);
|
||||
} else
|
||||
print_status(mpctx, MP_NOPTS_VALUE, false);
|
||||
mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001;
|
||||
vout_time_usage += mpctx->last_vo_flip_duration;
|
||||
if (mpctx->video_out->driver->flip_page_timed) {
|
||||
// No need to adjust sync based on flip speed
|
||||
mpctx->last_vo_flip_duration = 0;
|
||||
// For print_status - VO call finishing early is OK for sync
|
||||
mpctx->time_frame -= get_relative_time(mpctx);
|
||||
}
|
||||
if (mpctx->restart_playback) {
|
||||
mpctx->syncing_audio = true;
|
||||
if (mpctx->sh_audio)
|
||||
fill_audio_out_buffers(mpctx, endpts);
|
||||
mpctx->restart_playback = false;
|
||||
mpctx->time_frame = 0;
|
||||
get_relative_time(mpctx);
|
||||
}
|
||||
print_status(mpctx, MP_NOPTS_VALUE, true);
|
||||
screenshot_flip(mpctx);
|
||||
|
||||
if (opts->auto_quality > 0) {
|
||||
current_module = "autoq";
|
||||
|
@ -3706,25 +3628,18 @@ static void run_playloop(struct MPContext *mpctx)
|
|||
set_video_quality(mpctx->sh_video, output_quality);
|
||||
}
|
||||
|
||||
if (!frame_time_remaining && blit_frame) {
|
||||
if (play_n_frames >= 0) {
|
||||
--play_n_frames;
|
||||
if (play_n_frames <= 0)
|
||||
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||
}
|
||||
if (mpctx->step_frames > 0) {
|
||||
mpctx->step_frames--;
|
||||
if (mpctx->step_frames == 0)
|
||||
pause_player(mpctx);
|
||||
}
|
||||
if (play_n_frames >= 0) {
|
||||
--play_n_frames;
|
||||
if (play_n_frames <= 0)
|
||||
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||
}
|
||||
|
||||
// FIXME: add size based support for -endpos
|
||||
if (end_at.type == END_AT_TIME &&
|
||||
!frame_time_remaining && end_at.pos <= mpctx->sh_video->pts)
|
||||
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||
|
||||
} // end if(mpctx->sh_video)
|
||||
if (mpctx->step_frames > 0) {
|
||||
mpctx->step_frames--;
|
||||
if (mpctx->step_frames == 0)
|
||||
pause_player(mpctx);
|
||||
}
|
||||
break;
|
||||
} // video
|
||||
|
||||
#ifdef CONFIG_DVDNAV
|
||||
if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
||||
|
@ -3750,48 +3665,116 @@ static void run_playloop(struct MPContext *mpctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
//================= Keyboard events, SEEKing ====================
|
||||
|
||||
current_module = "key_events";
|
||||
|
||||
while (1) {
|
||||
mp_cmd_t *cmd;
|
||||
while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) {
|
||||
/* Allow running consecutive seek commands to combine them,
|
||||
* but execute the seek before running other commands.
|
||||
* If the user seeks continuously (keeps arrow key down)
|
||||
* try to finish showing a frame from one location before doing
|
||||
* another seek (which could lead to unchanging display). */
|
||||
if (mpctx->seek.type && cmd->id != MP_CMD_SEEK
|
||||
|| mpctx->restart_playback && cmd->id == MP_CMD_SEEK
|
||||
&& GetTimerMS() - mpctx->start_timestamp < 300)
|
||||
break;
|
||||
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
||||
run_command(mpctx, cmd);
|
||||
mp_cmd_free(cmd);
|
||||
if (mpctx->stop_play)
|
||||
break;
|
||||
if (mpctx->restart_playback && !video_left) {
|
||||
if (mpctx->sh_audio) {
|
||||
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;
|
||||
}
|
||||
bool slow_video = mpctx->sh_video && mpctx->video_out->frame_loaded;
|
||||
if (!(mpctx->paused || slow_video) || mpctx->stop_play
|
||||
|| mpctx->seek.type || mpctx->restart_playback)
|
||||
break;
|
||||
if (mpctx->sh_video) {
|
||||
update_osd_msg(mpctx);
|
||||
mpctx->restart_playback = false;
|
||||
}
|
||||
if (mpctx->sh_audio && buffered_audio == -1)
|
||||
buffered_audio = mpctx->paused ? 0 : ao_get_delay(mpctx->ao);
|
||||
|
||||
update_osd_msg(mpctx);
|
||||
if (mpctx->paused)
|
||||
update_pause_message(mpctx);
|
||||
if (!video_left && (!mpctx->paused || was_restart)) {
|
||||
double a_pos = 0;
|
||||
if (mpctx->sh_audio) {
|
||||
a_pos = (written_audio_pts(mpctx) -
|
||||
mpctx->opts.playback_speed * buffered_audio);
|
||||
}
|
||||
print_status(mpctx, a_pos, false);
|
||||
|
||||
if (!mpctx->sh_video)
|
||||
update_subtitles(mpctx, a_pos, false);
|
||||
}
|
||||
|
||||
/* It's possible for the user to simultaneously switch both audio
|
||||
* and video streams to "disabled" at runtime. Handle this by waiting
|
||||
* rather than immediately stopping playback due to EOF.
|
||||
*
|
||||
* When all audio has been written to output driver, stay in the
|
||||
* main loop handling commands until it has been mostly consumed,
|
||||
* except in the gapless case, where the next file will be started
|
||||
* while audio from the current one still remains to be played.
|
||||
*
|
||||
* We want this check to trigger if we seeked to this position,
|
||||
* but not if we paused at it with audio possibly still buffered in
|
||||
* the AO. There's currently no working way to check buffered audio
|
||||
* inside AO while paused. Thus the "was_restart" check below, which
|
||||
* should trigger after seek only, when we know there's no audio
|
||||
* buffered.
|
||||
*/
|
||||
if ((mpctx->sh_audio || mpctx->sh_video) && !audio_left && !video_left
|
||||
&& (opts->gapless_audio || buffered_audio < 0.05)
|
||||
&& (!mpctx->paused || was_restart)) {
|
||||
if (end_is_chapter) {
|
||||
seek(mpctx, (struct seek_params){
|
||||
.type = MPSEEK_ABSOLUTE,
|
||||
.amount = mpctx->timeline[mpctx->timeline_part+1].start
|
||||
}, true);
|
||||
} else
|
||||
mpctx->stop_play = AT_END_OF_FILE;
|
||||
} else if (!mpctx->stop_play) {
|
||||
double audio_sleep = 9;
|
||||
if (mpctx->sh_audio && !mpctx->paused) {
|
||||
if (mpctx->ao->untimed) {
|
||||
if (!mpctx->sh_video)
|
||||
audio_sleep = 0;
|
||||
} else if (full_audio_buffers) {
|
||||
audio_sleep = buffered_audio - 0.050;
|
||||
// Keep extra safety margin if the buffers are large
|
||||
if (audio_sleep > 0.100)
|
||||
audio_sleep = FFMAX(audio_sleep - 0.200, 0.100);
|
||||
else
|
||||
audio_sleep = FFMAX(audio_sleep, 0.020);
|
||||
} else
|
||||
audio_sleep = 0.020;
|
||||
}
|
||||
sleeptime = FFMIN(sleeptime, audio_sleep);
|
||||
if (sleeptime > 0) {
|
||||
if (!mpctx->sh_video)
|
||||
goto novideo;
|
||||
int hack = vo_osd_changed(0);
|
||||
vo_osd_changed(hack);
|
||||
if (hack || mpctx->video_out->want_redraw) {
|
||||
if (redraw_osd(mpctx) < 0) {
|
||||
if (mpctx->paused)
|
||||
if (mpctx->paused && video_left)
|
||||
add_step_frame(mpctx);
|
||||
break;
|
||||
else
|
||||
goto novideo;
|
||||
} else
|
||||
vo_osd_changed(0);
|
||||
} else {
|
||||
novideo:
|
||||
mp_input_get_cmd(mpctx->input, sleeptime * 1000, true);
|
||||
}
|
||||
}
|
||||
if (!mpctx->paused)
|
||||
}
|
||||
|
||||
//================= Keyboard events, SEEKing ====================
|
||||
|
||||
current_module = "key_events";
|
||||
|
||||
mp_cmd_t *cmd;
|
||||
while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) {
|
||||
/* Allow running consecutive seek commands to combine them,
|
||||
* but execute the seek before running other commands.
|
||||
* If the user seeks continuously (keeps arrow key down)
|
||||
* try to finish showing a frame from one location before doing
|
||||
* another seek (which could lead to unchanging display). */
|
||||
if (mpctx->seek.type && cmd->id != MP_CMD_SEEK
|
||||
|| mpctx->restart_playback && cmd->id == MP_CMD_SEEK
|
||||
&& GetTimerMS() - mpctx->start_timestamp < 300)
|
||||
break;
|
||||
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
||||
run_command(mpctx, cmd);
|
||||
mp_cmd_free(cmd);
|
||||
if (mpctx->stop_play)
|
||||
break;
|
||||
pause_loop(mpctx);
|
||||
}
|
||||
|
||||
// handle -sstep
|
||||
|
|
|
@ -6,6 +6,10 @@ typedef struct MPOpts {
|
|||
char **audio_driver_list;
|
||||
int fixed_vo;
|
||||
int vo_ontop;
|
||||
char *mixer_device;
|
||||
char *mixer_channel;
|
||||
int softvol;
|
||||
float softvol_max;
|
||||
int gapless_audio;
|
||||
int ao_buffersize;
|
||||
int screen_size_x;
|
||||
|
@ -24,6 +28,7 @@ typedef struct MPOpts {
|
|||
int requested_colorspace;
|
||||
int requested_input_range;
|
||||
int requested_output_range;
|
||||
int cursor_autohide_delay;
|
||||
|
||||
// ranges -100 - 100, 1000 if the vo default should be used
|
||||
int vo_gamma_gamma;
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* 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 <Carbon/Carbon.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "stream/url.h"
|
||||
#include "mp_msg.h"
|
||||
#include "m_option.h"
|
||||
#include "m_config.h"
|
||||
#include "playtree.h"
|
||||
#include "macosx_finder_args.h"
|
||||
|
||||
static play_tree_t *files=NULL;
|
||||
|
||||
static inline void add_entry(play_tree_t **last_parentp, play_tree_t **last_entryp, play_tree_t *entry) {
|
||||
|
||||
if(*last_entryp==NULL)
|
||||
play_tree_set_child(*last_parentp, entry);
|
||||
else
|
||||
play_tree_append_entry(*last_entryp, entry);
|
||||
|
||||
*last_entryp=entry;
|
||||
}
|
||||
|
||||
static pascal OSErr AppleEventHandlerProc(const AppleEvent *theAppleEvent, AppleEvent* reply, SInt32 handlerRefcon) {
|
||||
OSErr err=errAEEventNotHandled, res=noErr;
|
||||
AEDescList docList;
|
||||
long itemsInList;
|
||||
|
||||
AERemoveEventHandler(kCoreEventClass, kAEOpenDocuments, NULL, FALSE);
|
||||
if((res=AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList))==noErr) {
|
||||
if((res=AECountItems(&docList, &itemsInList))==noErr) {
|
||||
Size currentSize=0;
|
||||
int valid=0,i;
|
||||
char *parm=NULL;
|
||||
play_tree_t *last_entry=NULL;
|
||||
|
||||
files=play_tree_new();
|
||||
for(i=1;i<=itemsInList;++i) {
|
||||
|
||||
for(;;) {
|
||||
OSErr e;
|
||||
Size actualSize=0;
|
||||
AEKeyword keywd;
|
||||
DescType returnedType;
|
||||
|
||||
if((e=AEGetNthPtr(&docList, i, typeFileURL, &keywd, &returnedType, (Ptr)parm, currentSize, &actualSize))==noErr) {
|
||||
if(actualSize>=currentSize) {
|
||||
currentSize=actualSize+1;
|
||||
parm=realloc(parm, currentSize);
|
||||
}
|
||||
else {
|
||||
parm[actualSize]=0;
|
||||
valid=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
valid=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(valid) {
|
||||
URL_t *url=url_new(parm);
|
||||
|
||||
if(url && !strcmp(url->protocol,"file") && !strcmp(url->hostname,"localhost")) {
|
||||
play_tree_t *entry=play_tree_new();
|
||||
|
||||
url_unescape_string(url->file, url->file);
|
||||
play_tree_add_file(entry, url->file);
|
||||
add_entry(&files, &last_entry, entry);
|
||||
}
|
||||
|
||||
url_free(url);
|
||||
}
|
||||
}
|
||||
|
||||
free(parm);
|
||||
|
||||
err=noErr;
|
||||
}
|
||||
else
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "AECountItems() error %d\n", res);
|
||||
|
||||
AEDisposeDesc(&docList);
|
||||
}
|
||||
else
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR, "AEGetParamDesc() error %d\n", res);
|
||||
|
||||
QuitApplicationEventLoop();
|
||||
return err;
|
||||
}
|
||||
|
||||
play_tree_t *macosx_finder_args(m_config_t *config, int argc, char **argv) {
|
||||
ProcessSerialNumber myPsn;
|
||||
char myPsnStr[5+10+1+10+1];
|
||||
|
||||
GetCurrentProcess(&myPsn);
|
||||
snprintf(myPsnStr, 5+10+1+10+1, "-psn_%u_%u", myPsn.highLongOfPSN, myPsn.lowLongOfPSN);
|
||||
myPsnStr[5+10+1+10]=0;
|
||||
|
||||
if((argc==2) && !strcmp(myPsnStr, argv[1])) {
|
||||
m_config_set_option0(config, "quiet", NULL, false);
|
||||
InitCursor();
|
||||
AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(AppleEventHandlerProc), 0, FALSE);
|
||||
RunApplicationEventLoop();
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
* This file is part of MPlayer.
|
||||
* This file is part of mplayer2.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* 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.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* with mplayer2; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#include <stdio.h>
|
||||
#include "macosx_finder_args.h"
|
||||
|
||||
static play_tree_t *files = NULL;
|
||||
|
||||
void macosx_wait_fileopen_events(void);
|
||||
void macosx_redirect_output_to_logfile(const char *filename);
|
||||
bool psn_matches_current_process(char *psn_arg_to_check);
|
||||
|
||||
@interface FileOpenDelegate : NSObject
|
||||
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames;
|
||||
@end
|
||||
|
||||
@implementation FileOpenDelegate
|
||||
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
|
||||
{
|
||||
files = play_tree_new();
|
||||
play_tree_t *last_entry = nil;
|
||||
for (NSString *filename in filenames) {
|
||||
play_tree_t *entry = play_tree_new();
|
||||
play_tree_add_file(entry, [filename UTF8String]);
|
||||
|
||||
if (last_entry)
|
||||
play_tree_append_entry(files, entry);
|
||||
else
|
||||
play_tree_set_child(files, entry);
|
||||
|
||||
last_entry = entry;
|
||||
}
|
||||
[NSApp stop:nil]; // stop the runloop (give back control to mplayer2 code)
|
||||
}
|
||||
@end
|
||||
|
||||
void macosx_wait_fileopen_events()
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSApp = [NSApplication sharedApplication];
|
||||
[NSApp setDelegate: [[[FileOpenDelegate alloc] init] autorelease]];
|
||||
[NSApp run]; // block until we recive the fileopen events
|
||||
[pool release];
|
||||
}
|
||||
|
||||
void macosx_redirect_output_to_logfile(const char *filename)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *log_path = [NSHomeDirectory() stringByAppendingPathComponent:
|
||||
[@"Library/Logs/" stringByAppendingFormat:@"%s.log", filename]];
|
||||
freopen([log_path fileSystemRepresentation], "a", stdout);
|
||||
freopen([log_path fileSystemRepresentation], "a", stderr);
|
||||
[pool release];
|
||||
}
|
||||
|
||||
bool psn_matches_current_process(char *psn_arg_to_check)
|
||||
{
|
||||
ProcessSerialNumber psn;
|
||||
char psn_arg[5+10+1+10+1];
|
||||
|
||||
GetCurrentProcess(&psn);
|
||||
snprintf(psn_arg, 5+10+1+10+1, "-psn_%u_%u",
|
||||
psn.highLongOfPSN, psn.lowLongOfPSN);
|
||||
psn_arg[5+10+1+10]=0;
|
||||
|
||||
return strcmp(psn_arg, psn_arg_to_check) == 0;
|
||||
}
|
||||
|
||||
play_tree_t *macosx_finder_args(m_config_t *config, int argc, char **argv)
|
||||
{
|
||||
if (argc==2 && psn_matches_current_process(argv[1])) {
|
||||
macosx_redirect_output_to_logfile("mplayer2");
|
||||
m_config_set_option0(config, "quiet", NULL, false);
|
||||
macosx_wait_fileopen_events();
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
|
@ -56,7 +56,7 @@ void vstream_error(const char *format, ...) {
|
|||
va_start(va, format);
|
||||
vsnprintf(buf, 1024, format, va);
|
||||
va_end(va);
|
||||
mp_msg(MSGT_STREAM, MSGL_ERR, buf);
|
||||
mp_msg(MSGT_STREAM, MSGL_ERR, "%s", buf);
|
||||
}
|
||||
|
||||
static struct stream_priv_s {
|
||||
|
@ -152,7 +152,8 @@ static int open_s(stream_t *stream, int mode, void* opts, int* file_format) {
|
|||
|
||||
stream->start_pos = 0;
|
||||
stream->end_pos = vstream_streamsize();
|
||||
mp_msg(MSGT_OPEN, MSGL_DBG2, "Tivo stream size is %d\n", stream->end_pos);
|
||||
mp_msg(MSGT_OPEN, MSGL_DBG2, "Tivo stream size is %lld\n",
|
||||
(long long)stream->end_pos);
|
||||
|
||||
stream->priv = p;
|
||||
stream->fill_buffer = fill_buffer;
|
||||
|
|
Loading…
Reference in New Issue