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:
wm4 2012-04-01 22:52:33 +02:00
commit 1aa2e36122
38 changed files with 1288 additions and 2572 deletions

View File

@ -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)

View File

@ -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},

View File

@ -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;
}

27
configure vendored
View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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
*/

View File

@ -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);

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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, \

View File

@ -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";

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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, &param) == 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, &param) == 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, &param) == 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, &param) == 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
};
//===========================================================================//

View File

@ -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')

View File

@ -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);

View File

@ -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];
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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);
}

View File

@ -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
View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
View File

@ -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, &timestamp);
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

View File

@ -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;

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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;

View File

@ -76,6 +76,7 @@ struct osd_state {
struct font_desc *sub_font;
struct ass_track *ass_track;
double pts;
double sub_offset;
bool ass_track_changed;
bool vsfilter_aspect;
};