diff options
38 files changed, 1270 insertions, 2554 deletions
@@ -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) diff --git a/cfg-mplayer.h b/cfg-mplayer.h index 75aa5553fe..1d18949ce0 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -731,11 +731,10 @@ const m_option_t mplayer_opts[]={ {"border", &vo_border, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"noborder", &vo_border, CONF_TYPE_FLAG, 0, 1, 0, NULL}, - {"mixer", &mixer_device, CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"mixer-channel", &mixer_channel, CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"softvol", &soft_vol, CONF_TYPE_FLAG, 0, 0, 1, NULL}, - {"nosoftvol", &soft_vol, CONF_TYPE_FLAG, 0, 1, 0, NULL}, - {"softvol-max", &soft_vol_max, CONF_TYPE_FLOAT, CONF_RANGE, 10, 10000, NULL}, + OPT_STRING("mixer", mixer_device, 0), + OPT_STRING("mixer-channel", mixer_channel, 0), + OPT_MAKE_FLAGS("softvol", softvol, 0), + OPT_FLOATRANGE("softvol-max", softvol_max, 0, 10, 10000), {"volstep", &volstep, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL}, {"volume", &start_volume, CONF_TYPE_FLOAT, CONF_RANGE, -1, 10000, NULL}, OPT_MAKE_FLAGS("gapless-audio", gapless_audio, 0), @@ -807,6 +806,7 @@ const m_option_t mplayer_opts[]={ {"grabpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"nograbpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 1, 0, NULL}, + OPT_INTRANGE("cursor-autohide-delay", cursor_autohide_delay, 0, -2, 30000), {"adapter", &vo_adapter_num, CONF_TYPE_INT, CONF_RANGE, 0, 5, NULL}, {"refreshrate",&vo_refresh_rate,CONF_TYPE_INT,CONF_RANGE, 0,100, NULL}, @@ -1764,7 +1764,7 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg, } #endif - update_subtitles(mpctx, 0, 0, true); + update_subtitles(mpctx, 0, true); return M_PROPERTY_OK; } @@ -438,7 +438,6 @@ Video output: --disable-yuv4mpeg disable yuv4mpeg video output [enable] --disable-corevideo disable CoreVideo video output [autodetect] --disable-cocoa disable Cocoa OpenGL backend [autodetect] - --disable-quartz disable Quartz video output [autodetect] Audio output: --disable-alsa disable ALSA audio output [autodetect] @@ -701,7 +700,6 @@ _qtx=auto _coreaudio=auto _corevideo=auto _cocoa=auto -_quartz=auto quicktime=auto _macosx_finder=no _macosx_bundle=auto @@ -1139,8 +1137,6 @@ for ac_option do --disable-corevideo) _corevideo=no ;; --enable-cocoa) _cocoa=yes ;; --disable-cocoa) _cocoa=no ;; - --enable-quartz) _quartz=yes ;; - --disable-quartz) _quartz=no ;; --enable-macosx-finder) _macosx_finder=yes ;; --disable-macosx-finder) _macosx_finder=no ;; --enable-macosx-bundle) _macosx_bundle=yes ;; @@ -3583,7 +3579,7 @@ echocheck "Mac OS X Finder Support" def_macosx_finder='#undef CONFIG_MACOSX_FINDER' if test "$_macosx_finder" = yes ; then def_macosx_finder='#define CONFIG_MACOSX_FINDER 1' - extra_ldflags="$extra_ldflags -framework Carbon" + extra_ldflags="$extra_ldflags -framework Cocoa" fi echores "$_macosx_finder" @@ -3592,7 +3588,6 @@ def_macosx_bundle='#undef CONFIG_MACOSX_BUNDLE' test "$_macosx_bundle" = auto && _macosx_bundle=$_macosx_finder if test "$_macosx_bundle" = yes ; then def_macosx_bundle='#define CONFIG_MACOSX_BUNDLE 1' - extra_ldflags="$extra_ldflags -framework Carbon" fi echores "$_macosx_bundle" @@ -4218,25 +4213,9 @@ if test "$quicktime" = yes ; then def_quicktime='#define CONFIG_QUICKTIME 1' else def_quicktime='#undef CONFIG_QUICKTIME' - _quartz=no fi echores $quicktime -echocheck "Quartz" -if test "$_quartz" = auto ; then - _quartz=no - statement_check Carbon/Carbon.h 'CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false)' -framework Carbon && _quartz=yes -fi -if test "$_quartz" = yes ; then - libs_mplayer="$libs_mplayer -framework Carbon" - def_quartz='#define CONFIG_QUARTZ 1' - vomodules="quartz $vomodules" -else - def_quartz='#undef CONFIG_QUARTZ' - novomodules="quartz $novomodules" -fi -echores $_quartz - echocheck "CoreVideo" if test "$_corevideo" = auto ; then cat > $TMPC <<EOF @@ -4573,7 +4552,7 @@ int main(int argc, char *argv[]) { EOF _gl=no for _ld_tmp in "" -lGL "-lGL -lXdamage" "-lGL $_ld_pthread" ; do - if cc_check $_ld_tmp $_ld_lm ; then + if test "$_cocoa" != yes && cc_check $_ld_tmp $_ld_lm ; then _gl=yes _gl_x11=yes libs_mplayer="$libs_mplayer $_ld_tmp $_ld_dl" @@ -6563,7 +6542,6 @@ PVR = $_pvr QTX_CODECS = $_qtx QTX_CODECS_WIN32 = $_qtx_codecs_win32 QTX_EMULATION = $_qtx_emulation -QUARTZ = $_quartz RADIO=$_radio RADIO_CAPTURE=$_radio_capture REAL_CODECS = $_real @@ -6930,7 +6908,6 @@ $def_mga $def_mng $def_png $def_pnm -$def_quartz $def_s3fb $def_sdl $def_sdl_sdl_h diff --git a/defaultopts.c b/defaultopts.c index bf0489a335..8cb86c0457 100644 --- a/defaultopts.c +++ b/defaultopts.c @@ -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, diff --git a/etc/input.conf b/etc/input.conf index b358d33243..7b61344f7f 100644 --- a/etc/input.conf +++ b/etc/input.conf @@ -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 diff --git a/input/input.c b/input/input.c index 008c908634..57b2d21f9d 100644 --- a/input/input.c +++ b/input/input.c @@ -71,150 +71,152 @@ struct key_name { char *name; }; -/// This array defines all known commands. -/// The first field is an id used to recognize the command without too many strcmp. -/// The second is obviously the command name. -/// The third is the minimum number of arguments this command needs. -/// Then comes the definition of each argument, terminated with an arg of -/// type 0 (this doesn't need to be explicit, because C will default initialize -/// the following arguments to type 0). -/// A command can take a maximum of MP_CMD_MAX_ARGS-1 arguments (-1 because of -/// the last one) which is actually 9. - -/// For the args, the first field is the type (actually int, float or string), the second -/// is the default value wich is used for optional arguments +/* This array defines all known commands. + * The first field is an id used to recognize the command. + * The second is the command name used in slave mode and input.conf. + * Then comes the definition of each argument, first mandatory arguments + * (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments + * (OARG_INT(default), etc) if any. The command will be given the default + * argument value if the user didn't give enough arguments to specify it. + * A command can take a maximum of MP_CMD_MAX_ARGS arguments (10). + */ +#define ARG_INT { .type = MP_CMD_ARG_INT } +#define OARG_INT(def) { .type = MP_CMD_ARG_INT, .optional = true, .v.i = def } +#define ARG_FLOAT { .type = MP_CMD_ARG_FLOAT } +#define OARG_FLOAT(def) { .type = MP_CMD_ARG_FLOAT, .optional = true, .v.f = def } +#define ARG_STRING { .type = MP_CMD_ARG_STRING } +#define OARG_STRING(def) { .type = MP_CMD_ARG_STRING, .optional = true, .v.s = def } static const mp_cmd_t mp_cmds[] = { #ifdef CONFIG_RADIO - { MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", 1, { { MP_CMD_ARG_INT} } }, - { MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", 1, { { MP_CMD_ARG_STRING} } }, - { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", 1, { {MP_CMD_ARG_FLOAT} } }, + { MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } }, + { MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } }, + { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } }, + { MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } }, #endif - { MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_EDL_MARK, "edl_mark", 0 }, - { MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SPEED_INCR, "speed_incr", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_SPEED_MULT, "speed_mult", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_SPEED_SET, "speed_set", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_QUIT, "quit", 0, { {MP_CMD_ARG_INT} } }, - { MP_CMD_STOP, "stop", 0 }, - { MP_CMD_PAUSE, "pause", 0 }, - { MP_CMD_FRAME_STEP, "frame_step", 0 }, - { MP_CMD_PLAY_TREE_STEP, "pt_step", 1, { { MP_CMD_ARG_INT }, { MP_CMD_ARG_INT } } }, - { MP_CMD_PLAY_TREE_UP_STEP, "pt_up_step", 1, { { MP_CMD_ARG_INT }, { MP_CMD_ARG_INT } } }, - { MP_CMD_PLAY_ALT_SRC_STEP, "alt_src_step", 1, { { MP_CMD_ARG_INT } } }, - { MP_CMD_LOOP, "loop", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SUB_DELAY, "sub_delay", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SUB_STEP, "sub_step", 1, { { MP_CMD_ARG_INT }, {MP_CMD_ARG_INT} } }, - { MP_CMD_OSD, "osd", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_OSD_SHOW_TEXT, "osd_show_text", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT, {.i = -1}}, {MP_CMD_ARG_INT} } }, - { MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text",1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT, {.i = -1}}, {MP_CMD_ARG_INT} } }, - { MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", 0 }, - { MP_CMD_VOLUME, "volume", 1, { { MP_CMD_ARG_FLOAT }, {MP_CMD_ARG_INT} } }, - { MP_CMD_BALANCE, "balance", 1, { { MP_CMD_ARG_FLOAT }, {MP_CMD_ARG_INT} } }, - { MP_CMD_MIXER_USEMASTER, "use_master", 0 }, - { MP_CMD_MUTE, "mute", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_CONTRAST, "contrast", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_GAMMA, "gamma", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_BRIGHTNESS, "brightness", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_HUE, "hue", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SATURATION, "saturation", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_FRAMEDROPPING, "frame_drop", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SUB_POS, "sub_pos", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SUB_ALIGNMENT, "sub_alignment", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SUB_VISIBILITY, "sub_visibility", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SUB_LOAD, "sub_load", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_SUB_REMOVE, "sub_remove", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SUB_SELECT, "vobsub_lang", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, // for compatibility - { MP_CMD_SUB_SELECT, "sub_select", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, - { MP_CMD_SUB_SOURCE, "sub_source", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, - { MP_CMD_SUB_VOB, "sub_vob", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, - { MP_CMD_SUB_DEMUX, "sub_demux", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, - { MP_CMD_SUB_FILE, "sub_file", 0, { { MP_CMD_ARG_INT, {.i = -2}} } }, - { MP_CMD_SUB_LOG, "sub_log", 0 }, - { MP_CMD_SUB_SCALE, "sub_scale", 1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, + { MP_CMD_SEEK, "seek", { ARG_FLOAT, OARG_INT(0), OARG_INT(0) } }, + { MP_CMD_EDL_MARK, "edl_mark", }, + { MP_CMD_AUDIO_DELAY, "audio_delay", { ARG_FLOAT, OARG_INT(0) } }, + { MP_CMD_SPEED_INCR, "speed_incr", { ARG_FLOAT } }, + { MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } }, + { MP_CMD_SPEED_SET, "speed_set", { ARG_FLOAT } }, + { MP_CMD_QUIT, "quit", { OARG_INT(0) } }, + { MP_CMD_STOP, "stop", }, + { MP_CMD_PAUSE, "pause", }, + { MP_CMD_FRAME_STEP, "frame_step", }, + { MP_CMD_PLAY_TREE_STEP, "pt_step", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_PLAY_TREE_UP_STEP, "pt_up_step", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_PLAY_ALT_SRC_STEP, "alt_src_step", { ARG_INT } }, + { MP_CMD_LOOP, "loop", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_SUB_DELAY, "sub_delay", { ARG_FLOAT, OARG_INT(0) } }, + { MP_CMD_SUB_STEP, "sub_step", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_OSD, "osd", { OARG_INT(-1) } }, + { MP_CMD_OSD_SHOW_TEXT, "osd_show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } }, + { MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } }, + { MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", }, + { MP_CMD_VOLUME, "volume", { ARG_FLOAT, OARG_INT(0) } }, + { MP_CMD_BALANCE, "balance", { ARG_FLOAT, OARG_INT(0) } }, + { MP_CMD_MIXER_USEMASTER, "use_master", }, + { MP_CMD_MUTE, "mute", { OARG_INT(-1) } }, + { MP_CMD_CONTRAST, "contrast", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_GAMMA, "gamma", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_BRIGHTNESS, "brightness", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_HUE, "hue", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_SATURATION, "saturation", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_FRAMEDROPPING, "frame_drop", { OARG_INT(-1) } }, + { MP_CMD_SUB_POS, "sub_pos", { ARG_INT, OARG_INT(0) } }, + { MP_CMD_SUB_ALIGNMENT, "sub_alignment", { OARG_INT(-1) } }, + { MP_CMD_SUB_VISIBILITY, "sub_visibility", { OARG_INT(-1) } }, + { MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } }, + { MP_CMD_SUB_REMOVE, "sub_remove", { OARG_INT(-1) } }, + { MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility + { MP_CMD_SUB_SELECT, "sub_select", { OARG_INT(-2) } }, + { MP_CMD_SUB_SOURCE, "sub_source", { OARG_INT(-2) } }, + { MP_CMD_SUB_VOB, "sub_vob", { OARG_INT(-2) } }, + { MP_CMD_SUB_DEMUX, "sub_demux", { OARG_INT(-2) } }, + { MP_CMD_SUB_FILE, "sub_file", { OARG_INT(-2) } }, + { MP_CMD_SUB_LOG, "sub_log", }, + { MP_CMD_SUB_SCALE, "sub_scale", { ARG_FLOAT, OARG_INT(0) } }, #ifdef CONFIG_ASS - { MP_CMD_ASS_USE_MARGINS, "ass_use_margins", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, + { MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } }, #endif - { MP_CMD_GET_PERCENT_POS, "get_percent_pos", 0 }, - { MP_CMD_GET_TIME_POS, "get_time_pos", 0 }, - { MP_CMD_GET_TIME_LENGTH, "get_time_length", 0 }, - { MP_CMD_GET_FILENAME, "get_file_name", 0 }, - { MP_CMD_GET_VIDEO_CODEC, "get_video_codec", 0 }, - { MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", 0 }, - { MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", 0 }, - { MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", 0 }, - { MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", 0 }, - { MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", 0 }, - { MP_CMD_GET_META_TITLE, "get_meta_title", 0 }, - { MP_CMD_GET_META_ARTIST, "get_meta_artist", 0 }, - { MP_CMD_GET_META_ALBUM, "get_meta_album", 0 }, - { MP_CMD_GET_META_YEAR, "get_meta_year", 0 }, - { MP_CMD_GET_META_COMMENT, "get_meta_comment", 0 }, - { MP_CMD_GET_META_TRACK, "get_meta_track", 0 }, - { MP_CMD_GET_META_GENRE, "get_meta_genre", 0 }, - { MP_CMD_SWITCH_AUDIO, "switch_audio", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SWITCH_ANGLE, "switch_angle", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SWITCH_TITLE, "switch_title", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, + { MP_CMD_GET_PERCENT_POS, "get_percent_pos", }, + { MP_CMD_GET_TIME_POS, "get_time_pos", }, + { MP_CMD_GET_TIME_LENGTH, "get_time_length", }, + { MP_CMD_GET_FILENAME, "get_file_name", }, + { MP_CMD_GET_VIDEO_CODEC, "get_video_codec", }, + { MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", }, + { MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", }, + { MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", }, + { MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", }, + { MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", }, + { MP_CMD_GET_META_TITLE, "get_meta_title", }, + { MP_CMD_GET_META_ARTIST, "get_meta_artist", }, + { MP_CMD_GET_META_ALBUM, "get_meta_album", }, + { MP_CMD_GET_META_YEAR, "get_meta_year", }, + { MP_CMD_GET_META_COMMENT, "get_meta_comment", }, + { MP_CMD_GET_META_TRACK, "get_meta_track", }, + { MP_CMD_GET_META_GENRE, "get_meta_genre", }, + { MP_CMD_SWITCH_AUDIO, "switch_audio", { OARG_INT(-1) } }, + { MP_CMD_SWITCH_ANGLE, "switch_angle", { OARG_INT(-1) } }, + { MP_CMD_SWITCH_TITLE, "switch_title", { OARG_INT(-1) } }, #ifdef CONFIG_TV - { MP_CMD_TV_START_SCAN, "tv_start_scan", 0 }, - { MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", 1, { {MP_CMD_ARG_INT} } }, - { MP_CMD_TV_STEP_NORM, "tv_step_norm", 0 }, - { MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", 0 }, - { MP_CMD_TV_SET_CHANNEL, "tv_set_channel", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", 0 }, - { MP_CMD_TV_SET_FREQ, "tv_set_freq", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_TV_STEP_FREQ, "tv_step_freq", 1, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_TV_SET_NORM, "tv_set_norm", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } }, - { MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } }, - { MP_CMD_TV_SET_HUE, "tv_set_hue", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } }, - { MP_CMD_TV_SET_SATURATION, "tv_set_saturation", 1, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT, {.i = 1}} } }, + { MP_CMD_TV_START_SCAN, "tv_start_scan", }, + { MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", { ARG_INT } }, + { MP_CMD_TV_STEP_NORM, "tv_step_norm", }, + { MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", }, + { MP_CMD_TV_SET_CHANNEL, "tv_set_channel", { ARG_STRING } }, + { MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", }, + { MP_CMD_TV_SET_FREQ, "tv_set_freq", { ARG_FLOAT } }, + { MP_CMD_TV_STEP_FREQ, "tv_step_freq", { ARG_FLOAT } }, + { MP_CMD_TV_SET_NORM, "tv_set_norm", { ARG_STRING } }, + { MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", { ARG_INT, OARG_INT(1) } }, + { MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", { ARG_INT, OARG_INT(1) } }, + { MP_CMD_TV_SET_HUE, "tv_set_hue", { ARG_INT, OARG_INT(1) } }, + { MP_CMD_TV_SET_SATURATION, "tv_set_saturation", { ARG_INT, OARG_INT(1) } }, #endif - { MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, + { MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", { OARG_INT(-1) } }, #ifdef CONFIG_DVBIN - { MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", 2, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, + { MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } }, #endif - { MP_CMD_SWITCH_RATIO, "switch_ratio", 0, { {MP_CMD_ARG_FLOAT} } }, - { MP_CMD_VO_FULLSCREEN, "vo_fullscreen", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_VO_ONTOP, "vo_ontop", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_VO_ROOTWIN, "vo_rootwin", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_VO_BORDER, "vo_border", 0, { {MP_CMD_ARG_INT, {.i = -1}} } }, - { MP_CMD_SCREENSHOT, "screenshot", 0, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_PANSCAN, "panscan",1, { {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_SWITCH_VSYNC, "switch_vsync", 0, { {MP_CMD_ARG_INT} } }, - { MP_CMD_LOADFILE, "loadfile", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT} } }, - { MP_CMD_LOADLIST, "loadlist", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_INT} } }, - { MP_CMD_PLAY_TREE_CLEAR, "pt_clear", 0 }, - { MP_CMD_RUN, "run", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_CAPTURING, "capturing", 0 }, - { MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", 2, { {MP_CMD_ARG_INT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_TV_TELETEXT_ADD_DEC, "teletext_add_dec", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link", 1, { {MP_CMD_ARG_INT} } }, + { MP_CMD_SWITCH_RATIO, "switch_ratio", { OARG_FLOAT(0) } }, + { MP_CMD_VO_FULLSCREEN, "vo_fullscreen", { OARG_INT(-1) } }, + { MP_CMD_VO_ONTOP, "vo_ontop", { OARG_INT(-1) } }, + { MP_CMD_VO_ROOTWIN, "vo_rootwin", { OARG_INT(-1) } }, + { MP_CMD_VO_BORDER, "vo_border", { OARG_INT(-1) } }, + { MP_CMD_SCREENSHOT, "screenshot", { OARG_INT(0), OARG_INT(0) } }, + { MP_CMD_PANSCAN, "panscan", { ARG_FLOAT, OARG_INT(0) } }, + { MP_CMD_SWITCH_VSYNC, "switch_vsync", { OARG_INT(0) } }, + { MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_INT(0) } }, + { MP_CMD_LOADLIST, "loadlist", { ARG_STRING, OARG_INT(0) } }, + { MP_CMD_PLAY_TREE_CLEAR, "pt_clear", }, + { MP_CMD_RUN, "run", { ARG_STRING } }, + { MP_CMD_CAPTURING, "capturing", }, + { MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", { ARG_INT, ARG_INT } }, + { MP_CMD_TV_TELETEXT_ADD_DEC, "teletext_add_dec", { ARG_STRING } }, + { MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link", { ARG_INT } }, #ifdef CONFIG_DVDNAV - { MP_CMD_DVDNAV, "dvdnav", 1, { {MP_CMD_ARG_STRING} } }, + { MP_CMD_DVDNAV, "dvdnav", { ARG_STRING } }, #endif - { MP_CMD_GET_VO_FULLSCREEN, "get_vo_fullscreen", 0 }, - { MP_CMD_GET_SUB_VISIBILITY, "get_sub_visibility", 0 }, - { MP_CMD_KEYDOWN_EVENTS, "key_down_event", 1, { {MP_CMD_ARG_INT} } }, - { MP_CMD_SET_PROPERTY, "set_property", 2, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_STRING} } }, - { MP_CMD_SET_PROPERTY_OSD, "set_property_osd", 2, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_STRING} } }, - { MP_CMD_GET_PROPERTY, "get_property", 1, { {MP_CMD_ARG_STRING} } }, - { MP_CMD_STEP_PROPERTY, "step_property", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, - { MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", 1, { {MP_CMD_ARG_STRING}, {MP_CMD_ARG_FLOAT}, {MP_CMD_ARG_INT} } }, - - { MP_CMD_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_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", { 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 */ diff --git a/input/input.h b/input/input.h index 084e2dd3e7..87814dd56c 100644 --- a/input/input.h +++ b/input/input.h @@ -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); diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index 24a448b5f9..5e71f7bf7e 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -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", - "" -}; -/** PulseAudio playback stream object */ -static struct pa_stream *stream; +struct priv { + // PulseAudio playback stream object + struct pa_stream *stream; -/** PulseAudio connection context */ -static struct pa_context *context; + // PulseAudio connection context + struct pa_context *context; -/** Main event loop object */ -static struct pa_threaded_mainloop *mainloop; + // Main event loop object + struct pa_threaded_mainloop *mainloop; -static int broken_pause; + // temporary during control() + struct pa_sink_input_info pi; -LIBAO_EXTERN(pulse) + bool broken_pause; + int retval; + bool did_reset; +}; #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; - - ao_data.samplerate = rate_hz; - ao_data.channels = channels; + ss.channels = ao->channels; + ss.rate = ao->samplerate; - 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); -} - -/** 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"); +// 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); } -/** 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 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 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"); - 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; + 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; + 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; + 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_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(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; } - - 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; + } 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; + } + } else + abort(); + /* We don't wait for completion here */ + pa_operation_unref(o); + pa_threaded_mainloop_unlock(priv->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, +}; diff --git a/libao2/audio_out.c b/libao2/audio_out.c index a91a0d6d72..6130e2ed33 100644 --- a/libao2/audio_out.c +++ b/libao2/audio_out.c @@ -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; } diff --git a/libao2/audio_out.h b/libao2/audio_out.h index c5ddb5bf60..aafedbf178 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -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); diff --git a/libao2/audio_out_internal.h b/libao2/audio_out_internal.h index 67bcfa953d..215428fb0e 100644 --- a/libao2/audio_out_internal.h +++ b/libao2/audio_out_internal.h @@ -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, \ diff --git a/libmpcodecs/img_format.c b/libmpcodecs/img_format.c index 54ccc42b2a..033c0a4e3e 100644 --- a/libmpcodecs/img_format.c +++ b/libmpcodecs/img_format.c @@ -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"; diff --git a/libmpcodecs/img_format.h b/libmpcodecs/img_format.h index a3a475a68b..4200282f98 100644 --- a/libmpcodecs/img_format.h +++ b/libmpcodecs/img_format.h @@ -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 diff --git a/libmpcodecs/mp_image.c b/libmpcodecs/mp_image.c index 8cdb5eee6b..4e20dee119 100644 --- a/libmpcodecs/mp_image.c +++ b/libmpcodecs/mp_image.c @@ -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; } diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c index ce80caa74b..4dcddb1d08 100644 --- a/libmpcodecs/vf_ass.c +++ b/libmpcodecs/vf_ass.c @@ -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); diff --git a/libmpcodecs/vf_vo.c b/libmpcodecs/vf_vo.c index b7c16a7759..a6bd165dee 100644 --- a/libmpcodecs/vf_vo.c +++ b/libmpcodecs/vf_vo.c @@ -33,8 +33,6 @@ #include "sub/ass_mp.h" #include "sub/sub.h" -//===========================================================================// - extern float sub_delay; struct vf_priv_s { @@ -47,45 +45,47 @@ struct vf_priv_s { }; #define video_out (vf->priv->vo) -static int query_format(struct vf_instance *vf, unsigned int fmt); /* forward declaration */ -static void draw_slice(struct vf_instance *vf, unsigned char** src, int* stride, int w,int h, int x, int y); +static int query_format(struct vf_instance *vf, unsigned int fmt); +static void draw_slice(struct vf_instance *vf, unsigned char **src, + int *stride, int w, int h, int x, int y); static int config(struct vf_instance *vf, - int width, int height, int d_width, int d_height, - unsigned int flags, unsigned int outfmt){ + int width, int height, int d_width, int d_height, + unsigned int flags, unsigned int outfmt) +{ - if ((width <= 0) || (height <= 0) || (d_width <= 0) || (d_height <= 0)) - { - mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n"); - return 0; + if ((width <= 0) || (height <= 0) || (d_width <= 0) || (d_height <= 0)) { + mp_msg(MSGT_CPLAYER, MSGL_ERR, "VO: invalid dimensions!\n"); + return 0; } const vo_info_t *info = video_out->driver->info; - mp_msg(MSGT_CPLAYER,MSGL_INFO,"VO: [%s] %dx%d => %dx%d %s %s%s%s%s\n",info->short_name, - width, height, - d_width, d_height, - vo_format_name(outfmt), - (flags&VOFLAG_FULLSCREEN)?" [fs]":"", - (flags&VOFLAG_MODESWITCHING)?" [vm]":"", - (flags&VOFLAG_SWSCALE)?" [zoom]":"", - (flags&VOFLAG_FLIPPING)?" [flip]":""); - mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Description: %s\n",info->name); - mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Author: %s\n", info->author); - if(info->comment && strlen(info->comment) > 0) - mp_msg(MSGT_CPLAYER,MSGL_V,"VO: Comment: %s\n", info->comment); + mp_msg(MSGT_CPLAYER, MSGL_INFO, "VO: [%s] %dx%d => %dx%d %s %s%s%s%s\n", + info->short_name, + width, height, + d_width, d_height, + vo_format_name(outfmt), + (flags & VOFLAG_FULLSCREEN) ? " [fs]" : "", + (flags & VOFLAG_MODESWITCHING) ? " [vm]" : "", + (flags & VOFLAG_SWSCALE) ? " [zoom]" : "", + (flags & VOFLAG_FLIPPING) ? " [flip]" : ""); + mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Description: %s\n", info->name); + mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Author: %s\n", info->author); + if (info->comment && strlen(info->comment) > 0) + mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Comment: %s\n", info->comment); // save vo's stride capability for the wanted colorspace: - vf->default_caps=query_format(vf,outfmt); + vf->default_caps = query_format(vf, outfmt); vf->draw_slice = (vf->default_caps & VOCAP_NOSLICES) ? NULL : draw_slice; if (vo_config(video_out, width, height, d_width, d_height, flags, outfmt)) - return 0; + return 0; #ifdef CONFIG_ASS vf->priv->scale_ratio = (double) d_width / d_height * height / width; if (vf->priv->renderer) { - mp_ass_configure(vf->priv->renderer, vf->opts, width, height, + mp_ass_configure(vf->priv->renderer, vf->opts, width, height, vf->default_caps & VFCAP_EOSD_UNSCALED); } @@ -96,40 +96,43 @@ static int config(struct vf_instance *vf, return 1; } -static int control(struct vf_instance *vf, int request, void* data) +static int control(struct vf_instance *vf, int request, void *data) { - switch(request){ + switch (request) { case VFCTRL_GET_DEINTERLACE: - { - if(!video_out) return CONTROL_FALSE; // vo not configured? + if (!video_out) + return CONTROL_FALSE; // vo not configured? return vo_control(video_out, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE; - } case VFCTRL_SET_DEINTERLACE: - { - if(!video_out) return CONTROL_FALSE; // vo not configured? + if (!video_out) + return CONTROL_FALSE; // vo not configured? return vo_control(video_out, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE; - } case VFCTRL_GET_YUV_COLORSPACE: return vo_control(video_out, VOCTRL_GET_YUV_COLORSPACE, data) == true; case VFCTRL_SET_YUV_COLORSPACE: return vo_control(video_out, VOCTRL_SET_YUV_COLORSPACE, data) == true; case VFCTRL_DRAW_OSD: - if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured? - vo_draw_osd(video_out, data); - return CONTROL_TRUE; - case VFCTRL_SET_EQUALIZER: - { - vf_equalizer_t *eq=data; - if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured? - struct voctrl_set_equalizer_args param = {eq->item, eq->value}; - return vo_control(video_out, VOCTRL_SET_EQUALIZER, ¶m) == VO_TRUE; + if (!video_out->config_ok) + return CONTROL_FALSE; // vo not configured? + vo_draw_osd(video_out, data); + return CONTROL_TRUE; + case VFCTRL_SET_EQUALIZER: { + vf_equalizer_t *eq = data; + if (!video_out->config_ok) + return CONTROL_FALSE; // vo not configured? + struct voctrl_set_equalizer_args param = { + eq->item, eq->value + }; + return vo_control(video_out, VOCTRL_SET_EQUALIZER, ¶m) == VO_TRUE; } - case VFCTRL_GET_EQUALIZER: - { - vf_equalizer_t *eq=data; - if(!video_out->config_ok) return CONTROL_FALSE; // vo not configured? - struct voctrl_get_equalizer_args param = {eq->item, &eq->value}; - return vo_control(video_out, VOCTRL_GET_EQUALIZER, ¶m) == VO_TRUE; + case VFCTRL_GET_EQUALIZER: { + vf_equalizer_t *eq = data; + if (!video_out->config_ok) + return CONTROL_FALSE; // vo not configured? + struct voctrl_get_equalizer_args param = { + eq->item, &eq->value + }; + return vo_control(video_out, VOCTRL_GET_EQUALIZER, ¶m) == VO_TRUE; } #ifdef CONFIG_ASS case VFCTRL_INIT_EOSD: @@ -141,8 +144,7 @@ static int control(struct vf_instance *vf, int request, void* data) vf->priv->prev_visibility = false; return CONTROL_TRUE; } - case VFCTRL_DRAW_EOSD: - { + case VFCTRL_DRAW_EOSD: { struct osd_state *osd = data; mp_eosd_images_t images = {NULL, 2}; ASS_Renderer *renderer = vf->priv->renderer; @@ -158,7 +160,7 @@ static int control(struct vf_instance *vf, int request, void* data) vf->priv->prev_visibility = false; osd->ass_track_changed = false; if (sub_visibility && osd->ass_track && (osd->pts != MP_NOPTS_VALUE)) { - struct mp_eosd_res res = {0}; + struct mp_eosd_res res = { 0 }; if (vo_control(video_out, VOCTRL_GET_EOSD_RES, &res) == VO_TRUE) { ass_set_frame_size(renderer, res.w, res.h); ass_set_margins(renderer, res.mt, res.mb, res.ml, res.mr); @@ -168,7 +170,7 @@ static int control(struct vf_instance *vf, int request, void* data) if (osd->ass_force_reload) mp_ass_reload_options(vf->priv->renderer, vf->opts); images.imgs = ass_render_frame(renderer, osd->ass_track, - (osd->pts+sub_delay) * 1000 + .5, + (osd->pts + sub_delay) * 1000 + .5, &images.changed); if (!vf->priv->prev_visibility || osd->ass_force_reload) images.changed = 2; @@ -183,53 +185,58 @@ static int control(struct vf_instance *vf, int request, void* data) return CONTROL_UNKNOWN; } -static int query_format(struct vf_instance *vf, unsigned int fmt){ +static int query_format(struct vf_instance *vf, unsigned int fmt) +{ int flags = vo_control(video_out, VOCTRL_QUERY_FORMAT, &fmt); // draw_slice() accepts stride, draw_frame() doesn't: - if(flags) - if(fmt==IMGFMT_YV12 || fmt==IMGFMT_I420 || fmt==IMGFMT_IYUV) - flags|=VFCAP_ACCEPT_STRIDE; + if (flags) + if (fmt == IMGFMT_YV12 || fmt == IMGFMT_I420 || fmt == IMGFMT_IYUV) + flags |= VFCAP_ACCEPT_STRIDE; return flags; } static void get_image(struct vf_instance *vf, - mp_image_t *mpi){ + mp_image_t *mpi) +{ if (!video_out->config_ok) return; // GET_IMAGE is required for hardware-accelerated formats - if(vo_directrendering || - IMGFMT_IS_HWACCEL(mpi->imgfmt)) - vo_control(video_out, VOCTRL_GET_IMAGE, mpi); + if (vo_directrendering || IMGFMT_IS_HWACCEL(mpi->imgfmt)) + vo_control(video_out, VOCTRL_GET_IMAGE, mpi); } -static int put_image(struct vf_instance *vf, - mp_image_t *mpi, double pts){ - if(!video_out->config_ok) return 0; // vo not configured? - // first check, maybe the vo/vf plugin implements draw_image using mpi: - if (vo_draw_image(video_out, mpi, pts) >= 0) - return 1; - // nope, fallback to old draw_frame/draw_slice: - if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))){ - // blit frame: -// if(mpi->flags&MP_IMGFLAG_PLANAR) - if(vf->default_caps&VFCAP_ACCEPT_STRIDE) - vo_draw_slice(video_out, mpi->planes,mpi->stride,mpi->w,mpi->h,0,0); - else - vo_draw_frame(video_out, mpi->planes); - } - return 1; +static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) +{ + if (!video_out->config_ok) + return 0; + // first check, maybe the vo/vf plugin implements draw_image using mpi: + if (vo_draw_image(video_out, mpi, pts) >= 0) + return 1; + // nope, fallback to old draw_frame/draw_slice: + if (!(mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))) { + // blit frame: + if (vf->default_caps & VFCAP_ACCEPT_STRIDE) + vo_draw_slice(video_out, mpi->planes, mpi->stride, mpi->w, mpi->h, + 0, 0); + else + vo_draw_frame(video_out, mpi->planes); + } + return 1; } -static void start_slice(struct vf_instance *vf, - mp_image_t *mpi) { - if(!video_out->config_ok) return; // vo not configured? - vo_control(video_out, VOCTRL_START_SLICE,mpi); +static void start_slice(struct vf_instance *vf, mp_image_t *mpi) +{ + if (!video_out->config_ok) + return; + vo_control(video_out, VOCTRL_START_SLICE, mpi); } -static void draw_slice(struct vf_instance *vf, - unsigned char** src, int* stride, int w,int h, int x, int y){ - if(!video_out->config_ok) return; // vo not configured? - vo_draw_slice(video_out, src,stride,w,h,x,y); +static void draw_slice(struct vf_instance *vf, unsigned char **src, + int *stride, int w, int h, int x, int y) +{ + if (!video_out->config_ok) + return; + vo_draw_slice(video_out, src, stride, w, h, x, y); } static void uninit(struct vf_instance *vf) @@ -245,20 +252,21 @@ static void uninit(struct vf_instance *vf) free(vf->priv); } } -//===========================================================================// -static int vf_open(vf_instance_t *vf, char *args){ - vf->config=config; - vf->control=control; - vf->query_format=query_format; - vf->get_image=get_image; - vf->put_image=put_image; - vf->draw_slice=draw_slice; - vf->start_slice=start_slice; - vf->uninit=uninit; - vf->priv=calloc(1, sizeof(struct vf_priv_s)); +static int vf_open(vf_instance_t *vf, char *args) +{ + vf->config = config; + vf->control = control; + vf->query_format = query_format; + vf->get_image = get_image; + vf->put_image = put_image; + vf->draw_slice = draw_slice; + vf->start_slice = start_slice; + vf->uninit = uninit; + vf->priv = calloc(1, sizeof(struct vf_priv_s)); vf->priv->vo = (struct vo *)args; - if(!video_out) return 0; // no vo ? + if (!video_out) + return 0; return 1; } @@ -270,5 +278,3 @@ const vf_info_t vf_info_vo = { vf_open, NULL }; - -//===========================================================================// diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c index 5fb66adb9d..f8311e215c 100644 --- a/libmpdemux/demux_lavf.c +++ b/libmpdemux/demux_lavf.c @@ -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') diff --git a/libvo/cocoa_common.h b/libvo/cocoa_common.h index 1330caacc5..d47ac51500 100644 --- a/libvo/cocoa_common.h +++ b/libvo/cocoa_common.h @@ -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); diff --git a/libvo/cocoa_common.m b/libvo/cocoa_common.m index 4eccf1a320..e8ef278b1e 100644 --- a/libvo/cocoa_common.m +++ b/libvo/cocoa_common.m @@ -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) { + 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. - int curTime = TickCount()/60; - if (curTime - s->last_screensaver_update >= 30 || s->last_screensaver_update == 0) + if ((int)curTime - s->last_screensaver_update >= 30 || s->last_screensaver_update == 0) { UpdateSystemActivity(UsrActivity); - s->last_screensaver_update = curTime; + 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]; } } diff --git a/libvo/gl_common.c b/libvo/gl_common.c index c5abc81e15..414e52dbd2 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -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; diff --git a/libvo/video_out.c b/libvo/video_out.c index 094d5b1a12..540fedb132 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -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) diff --git a/libvo/video_out.h b/libvo/video_out.h index 554b97d207..3dd3ca8a8d 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -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) diff --git a/libvo/vo_quartz.c b/libvo/vo_quartz.c deleted file mode 100644 index 9098598aa8..0000000000 --- a/libvo/vo_quartz.c +++ /dev/null @@ -1,1371 +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. - */ - -/** - \author Nicolas Plourde <nicolasplourde@gmail.com> - - Copyright (c) Nicolas Plourde - April 2004 - - YUV support Copyright (C) 2004 Romain Dolbeau <romain@dolbeau.org> - - \brief MPlayer Mac OSX Quartz video out module. - - \todo: -screen overlay output - -fit osd in black bar when available - -fix RGB32 - -(add sugestion here) -*/ - -//SYS -#include <stdio.h> - -//OSX -#include <Carbon/Carbon.h> -#include <QuickTime/QuickTime.h> - -//MPLAYER -#include "config.h" -#include "fastmemcpy.h" -#include "video_out.h" -#include "video_out_internal.h" -#include "aspect.h" -#include "mp_msg.h" -#include "m_option.h" -#include "mp_fifo.h" -#include "mpbswap.h" -#include "sub/sub.h" - -#include "input/input.h" -#include "input/keycodes.h" - -#include "osx_common.h" - -static const vo_info_t info = -{ - "Mac OSX (Quartz)", - "quartz", - "Nicolas Plourde <nicolasplourde@hotmail.com>, Romain Dolbeau <romain@dolbeau.org>", - "" -}; - -const LIBVO_EXTERN(quartz) - -static uint32_t image_depth; -static uint32_t image_format; -static uint32_t image_size; -static uint32_t image_buffer_size; -static char *image_data; - -static ImageSequence seqId; -static CodecType image_qtcodec; -static PlanarPixmapInfoYUV420 *P = NULL; -static struct -{ - ImageDescriptionHandle desc; - Handle extension_colr; - Handle extension_fiel; - Handle extension_clap; - Handle extension_pasp; -} yuv_qt_stuff; -static MatrixRecord matrix; -static int EnterMoviesDone = 0; -static int get_image_done = 0; - -static int vo_quartz_fs; // we are in fullscreen - -static int winLevel = 1; -int levelList[] = -{ - kCGDesktopWindowLevelKey, - kCGNormalWindowLevelKey, - kCGScreenSaverWindowLevelKey -}; - -static int int_pause = 0; -static float winAlpha = 1; -static int mouseHide = FALSE; -static float winSizeMult = 1; - -static int device_id = 0; - -static short fs_res_x = 0; -static short fs_res_y = 0; - -static WindowRef theWindow = NULL; -static WindowGroupRef winGroup = NULL; -static CGRect bounds; -static CGDirectDisplayID displayId = 0; -static CFDictionaryRef originalMode = NULL; - -static CGDataProviderRef dataProviderRef = NULL; -static CGImageRef image = NULL; - -static Rect imgRect; // size of the original image (unscaled) -static Rect dstRect; // size of the displayed image (after scaling) -static Rect winRect; // size of the window containg the displayed image (include padding) -static Rect oldWinRect; // size of the window containg the displayed image (include padding) when NOT in FS mode -static Rect oldWinBounds; - -static MenuRef windMenu; -static MenuRef movMenu; -static MenuRef aspectMenu; - -static int lastScreensaverUpdate = 0; -static int lastMouseHide = 0; - -enum -{ - kHalfScreenCmd = 2, - kNormalScreenCmd = 3, - kDoubleScreenCmd = 4, - kFullScreenCmd = 5, - kKeepAspectCmd = 6, - kAspectOrgCmd = 7, - kAspectFullCmd = 8, - kAspectWideCmd = 9, - kPanScanCmd = 10 -}; - -//PROTOTYPE///////////////////////////////////////////////////////////////// -static OSStatus KeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); -static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); -static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); -void window_resized(void); -void window_ontop(void); -void window_fullscreen(void); -void window_panscan(void); - -static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, unsigned char *srca, int stride) -{ - switch (image_format) - { - case IMGFMT_RGB32: - vo_draw_alpha_rgb32(w, h, src, srca, stride, image_data + 4 * (y0 * imgRect.right + x0), 4 * imgRect.right); - break; - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - vo_draw_alpha_yv12(w, h, src, srca, stride, ((char *)P) + be2me_32(P->componentInfoY.offset) + x0 + y0 * imgRect.right, imgRect.right); - break; - case IMGFMT_UYVY: - vo_draw_alpha_uyvy(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2); - break; - case IMGFMT_YUY2: - vo_draw_alpha_yuy2(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2); - break; - } -} - -//default keyboard event handler -static OSStatus KeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) -{ - OSStatus result = noErr; - UInt32 class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - - result = CallNextEventHandler(nextHandler, event); - - if (class == kEventClassKeyboard) - { - char macCharCodes; - UInt32 macKeyCode; - UInt32 macKeyModifiers; - - GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(macCharCodes), NULL, &macCharCodes); - GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode); - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(macKeyModifiers), NULL, &macKeyModifiers); - - if (macKeyModifiers != 256) - { - if (kind == kEventRawKeyRepeat || kind == kEventRawKeyDown) - { - int key = convert_key(macKeyCode, macCharCodes); - - if (key != -1) - mplayer_put_key(key); - } - } - else if (macKeyModifiers == 256) - { - switch (macCharCodes) - { - case '[': SetWindowAlpha(theWindow, winAlpha -= 0.05); break; - case ']': SetWindowAlpha(theWindow, winAlpha += 0.05); break; - } - } - else - result = eventNotHandledErr; - } - - return result; -} - -//default mouse event handler -static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) -{ - OSStatus result = noErr; - UInt32 class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - - result = CallNextEventHandler(nextHandler, event); - - if (class == kEventClassMouse) - { - WindowPtr tmpWin; - Point mousePos; - Point winMousePos; - - GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(mousePos), 0, &mousePos); - GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, 0, sizeof(winMousePos), 0, &winMousePos); - - switch (kind) - { - case kEventMouseMoved: - { - if (vo_quartz_fs) - { - CGDisplayShowCursor(displayId); - mouseHide = FALSE; - } - } - break; - - case kEventMouseWheelMoved: - { - int wheel; - short part; - - GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0, sizeof(wheel), 0, &wheel); - - part = FindWindow(mousePos, &tmpWin); - - if (part == inContent) - { - if (wheel > 0) - mplayer_put_key(MOUSE_BTN3); - else - mplayer_put_key(MOUSE_BTN4); - } - } - break; - - case kEventMouseDown: - case kEventMouseUp: - { - EventMouseButton button; - short part; - Rect bounds; - - GetWindowPortBounds(theWindow, &bounds); - GetEventParameter(event, kEventParamMouseButton, typeMouseButton, 0, sizeof(button), 0, &button); - - part = FindWindow(mousePos, &tmpWin); - if (kind == kEventMouseUp) - { - if (part != inContent) - break; - switch (button) - { - case kEventMouseButtonPrimary: - mplayer_put_key(MOUSE_BTN0); - break; - case kEventMouseButtonSecondary: - mplayer_put_key(MOUSE_BTN2); - break; - case kEventMouseButtonTertiary: - mplayer_put_key(MOUSE_BTN1); - break; - - default: - result = eventNotHandledErr; - break; - } - break; - } - if (winMousePos.h > bounds.right - 15 && winMousePos.v > bounds.bottom) - { - if (!vo_quartz_fs) - { - Rect newSize; - - ResizeWindow(theWindow, mousePos, NULL, &newSize); - } - } - else if (part == inMenuBar) - { - MenuSelect(mousePos); - HiliteMenu(0); - } - else if (part == inContent) - { - switch (button) - { - case kEventMouseButtonPrimary: - mplayer_put_key(MOUSE_BTN0 | MP_KEY_DOWN); - break; - case kEventMouseButtonSecondary: - mplayer_put_key(MOUSE_BTN2 | MP_KEY_DOWN); - break; - case kEventMouseButtonTertiary: - mplayer_put_key(MOUSE_BTN1 | MP_KEY_DOWN); - break; - - default: - result = eventNotHandledErr; - break; - } - } - } - break; - - case kEventMouseDragged: - break; - - default: - result = eventNotHandledErr; - break; - } - } - - return result; -} - -static void set_winSizeMult(float mult) -{ - int d_width, d_height; - aspect(&d_width, &d_height, A_NOZOOM); - - if (vo_quartz_fs) - { - vo_fs = !vo_fs; - window_fullscreen(); - } - - winSizeMult = mult; - SizeWindow(theWindow, d_width * mult, d_height * mult, 1); - window_resized(); -} - -//default window event handler -static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) -{ - OSStatus result = noErr; - UInt32 class = GetEventClass(event); - UInt32 kind = GetEventKind(event); - - result = CallNextEventHandler(nextHandler, event); - - if (class == kEventClassCommand) - { - HICommand theHICommand; - - GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(theHICommand), NULL, &theHICommand); - - switch (theHICommand.commandID) - { - case kHICommandQuit: - mplayer_put_key(KEY_CLOSE_WIN); - break; - - case kHalfScreenCmd: - set_winSizeMult(0.5); - break; - - case kNormalScreenCmd: - set_winSizeMult(1); - break; - - case kDoubleScreenCmd: - set_winSizeMult(2); - break; - - case kFullScreenCmd: - vo_fs = !vo_fs; - window_fullscreen(); - break; - - case kKeepAspectCmd: - vo_keepaspect = !vo_keepaspect; - CheckMenuItem(aspectMenu, 1, vo_keepaspect); - window_resized(); - break; - - case kAspectOrgCmd: - change_movie_aspect(-1); - break; - - case kAspectFullCmd: - change_movie_aspect(4.0 / 3.0); - break; - - case kAspectWideCmd: - change_movie_aspect(16.0 / 9.0); - break; - - case kPanScanCmd: - vo_panscan = !vo_panscan; - CheckMenuItem(aspectMenu, 2, vo_panscan); - window_panscan(); - window_resized(); - break; - - default: - result = eventNotHandledErr; - break; - } - } - else if (class == kEventClassWindow) - { - WindowRef window; - Rect rectWindow = { 0, 0, 0, 0 }; - - GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(window), NULL, &window); - - if (window) - { - GetWindowBounds(window, kWindowGlobalPortRgn, &rectWindow); - } - - switch (kind) - { - case kEventWindowClosed: - theWindow = NULL; - mplayer_put_key(KEY_CLOSE_WIN); - break; - - // resize window - case kEventWindowZoomed: - case kEventWindowBoundsChanged: - window_resized(); - flip_page(); - window_resized(); - break; - - default: - result = eventNotHandledErr; - break; - } - } - - return result; -} - -static void quartz_CreateWindow(uint32_t d_width, uint32_t d_height, WindowAttributes windowAttrs) -{ - CFStringRef titleKey; - CFStringRef windowTitle; - OSStatus result; - - MenuItemIndex index; - CFStringRef movMenuTitle; - CFStringRef aspMenuTitle; - - const EventTypeSpec win_events[] = { - {kEventClassWindow, kEventWindowClosed}, - {kEventClassWindow, kEventWindowBoundsChanged}, - {kEventClassCommand, kEventCommandProcess} - }; - - const EventTypeSpec key_events[] = { - {kEventClassKeyboard, kEventRawKeyDown}, - {kEventClassKeyboard, kEventRawKeyRepeat} - }; - - const EventTypeSpec mouse_events[] = { - {kEventClassMouse, kEventMouseMoved}, - {kEventClassMouse, kEventMouseWheelMoved}, - {kEventClassMouse, kEventMouseDown}, - {kEventClassMouse, kEventMouseUp}, - {kEventClassMouse, kEventMouseDragged} - }; - - SetRect(&winRect, 0, 0, d_width, d_height); - SetRect(&oldWinRect, 0, 0, d_width, d_height); - SetRect(&dstRect, 0, 0, d_width, d_height); - - // Clear Menu Bar - ClearMenuBar(); - - // Create Window Menu - CreateStandardWindowMenu(0, &windMenu); - InsertMenu(windMenu, 0); - - // Create Movie Menu - CreateNewMenu(1004, 0, &movMenu); - movMenuTitle = CFSTR("Movie"); - SetMenuTitleWithCFString(movMenu, movMenuTitle); - - AppendMenuItemTextWithCFString(movMenu, CFSTR("Half Size"), 0, kHalfScreenCmd, &index); - SetMenuItemCommandKey(movMenu, index, 0, '0'); - - AppendMenuItemTextWithCFString(movMenu, CFSTR("Normal Size"), 0, kNormalScreenCmd, &index); - SetMenuItemCommandKey(movMenu, index, 0, '1'); - - AppendMenuItemTextWithCFString(movMenu, CFSTR("Double Size"), 0, kDoubleScreenCmd, &index); - SetMenuItemCommandKey(movMenu, index, 0, '2'); - - AppendMenuItemTextWithCFString(movMenu, CFSTR("Full Size"), 0, kFullScreenCmd, &index); - SetMenuItemCommandKey(movMenu, index, 0, 'F'); - - AppendMenuItemTextWithCFString(movMenu, NULL, kMenuItemAttrSeparator, 0, &index); - - AppendMenuItemTextWithCFString(movMenu, CFSTR("Aspect Ratio"), 0, 0, &index); - - //// Create Aspect Ratio Sub Menu - CreateNewMenu(0, 0, &aspectMenu); - aspMenuTitle = CFSTR("Aspect Ratio"); - SetMenuTitleWithCFString(aspectMenu, aspMenuTitle); - SetMenuItemHierarchicalMenu(movMenu, 6, aspectMenu); - - AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Keep"), 0, kKeepAspectCmd, &index); - CheckMenuItem(aspectMenu, 1, vo_keepaspect); - AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Pan-Scan"), 0, kPanScanCmd, &index); - CheckMenuItem(aspectMenu, 2, vo_panscan); - AppendMenuItemTextWithCFString(aspectMenu, NULL, kMenuItemAttrSeparator, 0, &index); - AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Original"), 0, kAspectOrgCmd, &index); - AppendMenuItemTextWithCFString(aspectMenu, CFSTR("4:3"), 0, kAspectFullCmd, &index); - AppendMenuItemTextWithCFString(aspectMenu, CFSTR("16:9"), 0, kAspectWideCmd, &index); - - InsertMenu(movMenu, GetMenuID(windMenu)); //insert before Window menu - - DrawMenuBar(); - - // create window - CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow); - - CreateWindowGroup(0, &winGroup); - SetWindowGroup(theWindow, winGroup); - - // Set window title - titleKey = CFSTR("MPlayer - The Movie Player"); - windowTitle = CFCopyLocalizedString(titleKey, NULL); - result = SetWindowTitleWithCFString(theWindow, windowTitle); - CFRelease(titleKey); - CFRelease(windowTitle); - - // Install event handler - InstallApplicationEventHandler(NewEventHandlerUPP(KeyEventHandler), GetEventTypeCount(key_events), key_events, NULL, NULL); - InstallApplicationEventHandler(NewEventHandlerUPP(MouseEventHandler), GetEventTypeCount(mouse_events), mouse_events, NULL, NULL); - InstallWindowEventHandler(theWindow, NewEventHandlerUPP(WindowEventHandler), GetEventTypeCount(win_events), win_events, theWindow, NULL); -} - -static void update_screen_info(void) -{ - CGRect displayRect; - CGDisplayCount displayCount; - CGDirectDisplayID *displays; - // Display IDs might not be consecutive, get the list of all devices up to # device_id - displayCount = device_id + 1; - displays = malloc(sizeof(*displays) * displayCount); - if (kCGErrorSuccess != CGGetActiveDisplayList(displayCount, displays, &displayCount) || displayCount < device_id + 1) { - mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: Device ID %d do not exist, falling back to main device.\n", device_id); - displayId = kCGDirectMainDisplay; - device_id = 0; - } - else - { - displayId = displays[device_id]; - } - free(displays); - - displayRect = CGDisplayBounds(displayId); - xinerama_x = displayRect.origin.x; - xinerama_y = displayRect.origin.y; - vo_screenwidth = displayRect.size.width; - vo_screenheight = displayRect.size.height; - aspect_save_screenres(vo_screenwidth, vo_screenheight); -} - -static void free_video_specific(void) -{ - if (seqId) CDSequenceEnd(seqId); - seqId = 0; - free(image_data); - image_data = NULL; - free(P); - P = NULL; - CGDataProviderRelease(dataProviderRef); - dataProviderRef = NULL; - CGImageRelease(image); - image = NULL; -} - -static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format) -{ - WindowAttributes windowAttrs; - OSErr qterr; - CGRect tmpBounds; - - free_video_specific(); - - vo_dwidth = d_width *= winSizeMult; - vo_dheight = d_height *= winSizeMult; - config_movie_aspect((float)d_width / d_height); - - // misc mplayer setup///////////////////////////////////////////////////// - SetRect(&imgRect, 0, 0, width, height); - switch (image_format) - { - case IMGFMT_RGB32: - image_depth = 32; - break; - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - image_depth = 16; - break; - } - image_size = (imgRect.right * imgRect.bottom * image_depth + 7) / 8; - - image_data = malloc(image_size); - - // Create player window////////////////////////////////////////////////// - windowAttrs = kWindowStandardDocumentAttributes - | kWindowStandardHandlerAttribute - | kWindowLiveResizeAttribute; - - windowAttrs &= ~kWindowResizableAttribute; - - if (theWindow == NULL) - { - CGContextRef context; - - quartz_CreateWindow(d_width, d_height, windowAttrs); - - if (theWindow == NULL) - { - mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: Couldn't create window !!!!!\n"); - return -1; - } - tmpBounds = CGRectMake(0, 0, winRect.right, winRect.bottom); - QDBeginCGContext(GetWindowPort(theWindow), &context); - CGContextFillRect(context, tmpBounds); - QDEndCGContext(GetWindowPort(theWindow), &context); - } - else - { - HideWindow(theWindow); - ChangeWindowAttributes(theWindow, ~windowAttrs, windowAttrs); - SetRect(&winRect, 0, 0, d_width, d_height); - SetRect(&oldWinRect, 0, 0, d_width, d_height); - SizeWindow(theWindow, d_width, d_height, 1); - } - - switch (image_format) - { - case IMGFMT_RGB32: - { - CGContextRef context; - - QDBeginCGContext(GetWindowPort(theWindow), &context); - - dataProviderRef = CGDataProviderCreateWithData(0, image_data, imgRect.right * imgRect.bottom * 4, 0); - - image = CGImageCreate(imgRect.right, - imgRect.bottom, - 8, - image_depth, - (imgRect.right * 32 + 7) / 8, - CGColorSpaceCreateDeviceRGB(), - kCGImageAlphaNoneSkipFirst, - dataProviderRef, 0, 1, kCGRenderingIntentDefault); - - QDEndCGContext(GetWindowPort(theWindow), &context); - break; - } - - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - { - get_image_done = 0; - - if (!EnterMoviesDone) - { - qterr = EnterMovies(); - EnterMoviesDone = 1; - } - else - qterr = 0; - - if (qterr) - { - mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: EnterMovies (%d)\n", qterr); - return -1; - } - - - SetIdentityMatrix(&matrix); - - if (d_width != width || d_height != height) - { - ScaleMatrix(&matrix, FixDiv(Long2Fix(d_width), Long2Fix(width)), FixDiv(Long2Fix(d_height), Long2Fix(height)), 0, 0); - } - - yuv_qt_stuff.desc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription)); - - yuv_qt_stuff.extension_colr = NewHandleClear(sizeof(NCLCColorInfoImageDescriptionExtension)); - ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->colorParamType = kVideoColorInfoImageDescriptionExtensionType; - ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->primaries = 2; - ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->transferFunction = 2; - ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->matrix = 2; - - yuv_qt_stuff.extension_fiel = NewHandleClear(sizeof(FieldInfoImageDescriptionExtension)); - ((FieldInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_fiel))->fieldCount = 1; - ((FieldInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_fiel))->fieldOrderings = 0; - - yuv_qt_stuff.extension_clap = NewHandleClear(sizeof(CleanApertureImageDescriptionExtension)); - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureWidthN = imgRect.right; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureWidthD = 1; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureHeightN = imgRect.bottom; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureHeightD = 1; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->horizOffN = 0; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->horizOffD = 1; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->vertOffN = 0; - ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->vertOffD = 1; - - yuv_qt_stuff.extension_pasp = NewHandleClear(sizeof(PixelAspectRatioImageDescriptionExtension)); - ((PixelAspectRatioImageDescriptionExtension *) (*yuv_qt_stuff.extension_pasp))->hSpacing = 1; - ((PixelAspectRatioImageDescriptionExtension *) (*yuv_qt_stuff.extension_pasp))->vSpacing = 1; - - (*yuv_qt_stuff.desc)->idSize = sizeof(ImageDescription); - (*yuv_qt_stuff.desc)->cType = image_qtcodec; - (*yuv_qt_stuff.desc)->version = 2; - (*yuv_qt_stuff.desc)->revisionLevel = 0; - (*yuv_qt_stuff.desc)->vendor = 'mpla'; - (*yuv_qt_stuff.desc)->width = imgRect.right; - (*yuv_qt_stuff.desc)->height = imgRect.bottom; - (*yuv_qt_stuff.desc)->hRes = Long2Fix(72); - (*yuv_qt_stuff.desc)->vRes = Long2Fix(72); - (*yuv_qt_stuff.desc)->temporalQuality = 0; - (*yuv_qt_stuff.desc)->spatialQuality = codecLosslessQuality; - (*yuv_qt_stuff.desc)->frameCount = 1; - (*yuv_qt_stuff.desc)->dataSize = 0; - (*yuv_qt_stuff.desc)->depth = 24; - (*yuv_qt_stuff.desc)->clutID = -1; - - qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_colr, kColorInfoImageDescriptionExtension); - if (qterr) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [colr] (%d)\n", qterr); - } - - qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_fiel, kFieldInfoImageDescriptionExtension); - if (qterr) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [fiel] (%d)\n", qterr); - } - - qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_clap, kCleanApertureImageDescriptionExtension); - if (qterr) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [clap] (%d)\n", qterr); - } - - qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_pasp, kCleanApertureImageDescriptionExtension); - if (qterr) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [pasp] (%d)\n", qterr); - } - P = calloc(sizeof(PlanarPixmapInfoYUV420) + image_size, 1); - switch (image_format) - { - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - P->componentInfoY.offset = be2me_32(sizeof(PlanarPixmapInfoYUV420)); - P->componentInfoCb.offset = be2me_32(be2me_32(P->componentInfoY.offset) + image_size / 2); - P->componentInfoCr.offset = be2me_32(be2me_32(P->componentInfoCb.offset) + image_size / 4); - P->componentInfoY.rowBytes = be2me_32(imgRect.right); - P->componentInfoCb.rowBytes = be2me_32(imgRect.right / 2); - P->componentInfoCr.rowBytes = be2me_32(imgRect.right / 2); - image_buffer_size = image_size + sizeof(PlanarPixmapInfoYUV420); - break; - case IMGFMT_UYVY: - case IMGFMT_YUY2: - image_buffer_size = image_size; - break; - } - - qterr = DecompressSequenceBeginS(&seqId, - yuv_qt_stuff.desc, - (char *)P, - image_buffer_size, - GetWindowPort(theWindow), - NULL, - NULL, - d_width != width || d_height != height ? - &matrix : NULL, - srcCopy, - NULL, - 0, - codecLosslessQuality, - bestSpeedCodec); - - if (qterr) - { - mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr); - return -1; - } - } - break; - } - - // Show window - RepositionWindow(theWindow, NULL, kWindowCenterOnMainScreen); - ShowWindow(theWindow); - - if (vo_fs) - window_fullscreen(); - - if (vo_ontop) - window_ontop(); - - if (vo_rootwin) - { - vo_fs = TRUE; - winLevel = 0; - SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel])); - window_fullscreen(); - } - - window_resized(); - - return 0; -} - -static void check_events(void) -{ - EventRef theEvent; - EventTargetRef theTarget; - OSStatus theErr; - - // Get event - theTarget = GetEventDispatcherTarget(); - theErr = ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &theEvent); - if (theErr == noErr && theEvent != NULL) - { - SendEventToEventTarget(theEvent, theTarget); - ReleaseEvent(theEvent); - } -} - -static void draw_osd(void) -{ - vo_draw_text(imgRect.right, imgRect.bottom, draw_alpha); -} - -static void flip_page(void) -{ - int curTime; - - if (theWindow == NULL) - return; - - switch (image_format) - { - case IMGFMT_RGB32: - { - CGContextRef context; - - QDBeginCGContext(GetWindowPort(theWindow), &context); - CGContextDrawImage(context, bounds, image); - QDEndCGContext(GetWindowPort(theWindow), &context); - } - break; - - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - if (EnterMoviesDone) - { - OSErr qterr; - CodecFlags flags = 0; - - qterr = DecompressSequenceFrameWhen(seqId, - (char *)P, - image_buffer_size, - 0, //codecFlagUseImageBuffer, - &flags, - NULL, - NULL); - if (qterr) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d) flags:0x%08x\n", qterr, flags); - } - } - break; - } - - if (!vo_quartz_fs) - { - CGContextRef context; - - QDBeginCGContext(GetWindowPort(theWindow), &context); - // render resize box - CGContextBeginPath(context); - CGContextSetAllowsAntialiasing(context, false); - //CGContextSaveGState(context); - - // line white - CGContextSetRGBStrokeColor(context, 0.2, 0.2, 0.2, 0.5); - CGContextMoveToPoint(context, winRect.right - 1, 1); CGContextAddLineToPoint(context, winRect.right - 1, 1); - CGContextMoveToPoint(context, winRect.right - 1, 5); CGContextAddLineToPoint(context, winRect.right - 5, 1); - CGContextMoveToPoint(context, winRect.right - 1, 9); CGContextAddLineToPoint(context, winRect.right - 9, 1); - CGContextStrokePath(context); - - // line gray - CGContextSetRGBStrokeColor(context, 0.4, 0.4, 0.4, 0.5); - CGContextMoveToPoint(context, winRect.right - 1, 2); CGContextAddLineToPoint(context, winRect.right - 2, 1); - CGContextMoveToPoint(context, winRect.right - 1, 6); CGContextAddLineToPoint(context, winRect.right - 6, 1); - CGContextMoveToPoint(context, winRect.right - 1, 10); CGContextAddLineToPoint(context, winRect.right - 10, 1); - CGContextStrokePath(context); - - // line black - CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.6, 0.5); - CGContextMoveToPoint(context, winRect.right - 1, 3); CGContextAddLineToPoint(context, winRect.right - 3, 1); - CGContextMoveToPoint(context, winRect.right - 1, 7); CGContextAddLineToPoint(context, winRect.right - 7, 1); - CGContextMoveToPoint(context, winRect.right - 1, 11); CGContextAddLineToPoint(context, winRect.right - 11, 1); - CGContextStrokePath(context); - - // CGContextRestoreGState( context ); - CGContextFlush(context); - QDEndCGContext(GetWindowPort(theWindow), &context); - } - - curTime = TickCount() / 60; - - // auto hide mouse cursor (and future on-screen control?) - if (vo_quartz_fs && !mouseHide) - { - if (curTime - lastMouseHide >= 5 || lastMouseHide == 0) - { - CGDisplayHideCursor(displayId); - mouseHide = TRUE; - lastMouseHide = curTime; - } - } - // update activity every 30 seconds to prevent - // screensaver from starting up. - if (curTime - lastScreensaverUpdate >= 30 || lastScreensaverUpdate == 0) - { - UpdateSystemActivity(UsrActivity); - lastScreensaverUpdate = curTime; - } -} - -static int draw_slice(uint8_t * src[], int stride[], int w, int h, int x, int y) -{ - switch (image_format) - { - case IMGFMT_YV12: - case IMGFMT_I420: - memcpy_pic(((char *)P) + be2me_32(P->componentInfoY.offset) + x + imgRect.right * y, src[0], w, h, imgRect.right, stride[0]); - x=x/2;y=y/2;w=w/2;h=h/2; - - memcpy_pic(((char *)P) + be2me_32(P->componentInfoCb.offset) + x + imgRect.right / 2 * y, src[1], w, h, imgRect.right / 2, stride[1]); - memcpy_pic(((char *)P) + be2me_32(P->componentInfoCr.offset) + x + imgRect.right / 2 * y, src[2], w, h, imgRect.right / 2, stride[2]); - return 0; - - case IMGFMT_IYUV: - memcpy_pic(((char *)P) + be2me_32(P->componentInfoY.offset) + x + imgRect.right * y, src[0], w, h, imgRect.right, stride[0]); - x=x/2;y=y/2;w=w/2;h=h/2; - - memcpy_pic(((char *)P) + be2me_32(P->componentInfoCr.offset) + x + imgRect.right / 2 * y, src[1], w, h, imgRect.right / 2, stride[1]); - memcpy_pic(((char *)P) + be2me_32(P->componentInfoCb.offset) + x + imgRect.right / 2 * y, src[2], w, h, imgRect.right / 2, stride[2]); - return 0; - } - return -1; -} - -static int draw_frame(uint8_t * src[]) -{ - switch (image_format) - { - case IMGFMT_RGB32: - fast_memcpy(image_data, src[0], image_size); - return 0; - - case IMGFMT_UYVY: - case IMGFMT_YUY2: - memcpy_pic(((char *)P), src[0], imgRect.right * 2, imgRect.bottom, imgRect.right * 2, imgRect.right * 2); - return 0; - } - return -1; -} - -static int query_format(uint32_t format) -{ - image_format = format; - image_qtcodec = 0; - - if (format == IMGFMT_RGB32) - { - return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; - } - - if (format == IMGFMT_YV12 || format == IMGFMT_IYUV || format == IMGFMT_I420) - { - image_qtcodec = kMpegYUV420CodecType; //kYUV420CodecType ?; - return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE; - } - - if (format == IMGFMT_YUY2) - { - image_qtcodec = kComponentVideoUnsigned; - return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; - } - - if (format == IMGFMT_UYVY) - { - image_qtcodec = k422YpCbCr8CodecType; - return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; - } - - return 0; -} - -static void uninit(void) -{ - free_video_specific(); - if (EnterMoviesDone) - ExitMovies(); - EnterMoviesDone = 0; - - ShowMenuBar(); -} - -static int preinit(const char *arg) -{ - int parse_err = 0; - - if(arg) - { - char *parse_pos = (char *)&arg[0]; - - while (parse_pos[0] && !parse_err) - { - if (strncmp(parse_pos, "device_id=", 10) == 0) - { - parse_pos = &parse_pos[10]; - device_id = strtol(parse_pos, &parse_pos, 0); - } - if (strncmp(parse_pos, "fs_res=", 7) == 0) - { - parse_pos = &parse_pos[7]; - fs_res_x = strtol(parse_pos, &parse_pos, 0); - parse_pos = &parse_pos[1]; - fs_res_y = strtol(parse_pos, &parse_pos, 0); - } - if (parse_pos[0] == ':') - parse_pos = &parse_pos[1]; - else if (parse_pos[0]) - parse_err = 1; - } - } - - osx_foreground_hack(); - - return 0; -} - -static uint32_t draw_yuv_image(mp_image_t * mpi) -{ - // ATM we're only called for planar IMGFMT - // drawing is done directly in P - // and displaying is in flip_page. - return get_image_done ? VO_TRUE : VO_FALSE; -} - -static uint32_t get_yuv_image(mp_image_t * mpi) -{ - if (mpi->type != MP_IMGTYPE_EXPORT) return VO_FALSE; - - if (mpi->imgfmt != image_format) return VO_FALSE; - - if (mpi->flags & MP_IMGFLAG_PLANAR) - { - if (mpi->num_planes != 3) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: only 3 planes allowed in get_yuv_image for planar (%d) \n", mpi->num_planes); - return VO_FALSE; - } - - mpi->planes[0] = ((char *)P) + be2me_32(P->componentInfoY.offset); - mpi->stride[0] = imgRect.right; - mpi->width = imgRect.right; - - if (mpi->flags & MP_IMGFLAG_SWAPPED) - { - // I420 - mpi->planes[1] = ((char *)P) + be2me_32(P->componentInfoCb.offset); - mpi->planes[2] = ((char *)P) + be2me_32(P->componentInfoCr.offset); - mpi->stride[1] = imgRect.right / 2; - mpi->stride[2] = imgRect.right / 2; - } - else - { - // YV12 - mpi->planes[1] = ((char *)P) + be2me_32(P->componentInfoCr.offset); - mpi->planes[2] = ((char *)P) + be2me_32(P->componentInfoCb.offset); - mpi->stride[1] = imgRect.right / 2; - mpi->stride[2] = imgRect.right / 2; - } - - mpi->flags |= MP_IMGFLAG_DIRECT; - get_image_done = 1; - return VO_TRUE; - } - else - { - // doesn't work yet - if (mpi->num_planes != 1) - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: only 1 plane allowed in get_yuv_image for packed (%d) \n", mpi->num_planes); - return VO_FALSE; - } - - mpi->planes[0] = (char *)P; - mpi->stride[0] = imgRect.right * 2; - mpi->width = imgRect.right; - mpi->flags |= MP_IMGFLAG_DIRECT; - get_image_done = 1; - return VO_TRUE; - } - return VO_FALSE; -} - -static int control(uint32_t request, void *data) -{ - switch (request) - { - case VOCTRL_PAUSE: return int_pause = 1; - case VOCTRL_RESUME: return int_pause = 0; - case VOCTRL_FULLSCREEN: vo_fs = !vo_fs; window_fullscreen(); return VO_TRUE; - case VOCTRL_ONTOP: vo_ontop = !vo_ontop; window_ontop(); return VO_TRUE; - case VOCTRL_QUERY_FORMAT: return query_format(*(uint32_t *) data); - case VOCTRL_GET_PANSCAN: return VO_TRUE; - case VOCTRL_SET_PANSCAN: window_panscan(); return VO_TRUE; - - case VOCTRL_GET_IMAGE: - switch (image_format) - { - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - return get_yuv_image(data); - break; - default: - break; - } - case VOCTRL_DRAW_IMAGE: - switch (image_format) - { - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - return draw_yuv_image(data); - break; - default: - break; - } - case VOCTRL_UPDATE_SCREENINFO: - update_screen_info(); - return VO_TRUE; - } - return VO_NOTIMPL; -} - -void window_resized(void) -{ - uint32_t d_width; - uint32_t d_height; - - CGRect tmpBounds; - - CGContextRef context; - - GetWindowPortBounds(theWindow, &winRect); - d_width = vo_dwidth = winRect.right; - d_height = vo_dheight = winRect.bottom; - - if (vo_keepaspect) - aspect(&d_width, &d_height, A_WINZOOM); - SetRect(&dstRect, (vo_dwidth - d_width) / 2, (vo_dheight - d_height) / 2, d_width, d_height); - - switch (image_format) - { - case IMGFMT_RGB32: - { - bounds = CGRectMake(dstRect.left, dstRect.top, dstRect.right - dstRect.left, dstRect.bottom - dstRect.top); - break; - } - case IMGFMT_YV12: - case IMGFMT_IYUV: - case IMGFMT_I420: - case IMGFMT_UYVY: - case IMGFMT_YUY2: - { - long scale_X = FixDiv(Long2Fix(dstRect.right - dstRect.left), Long2Fix(imgRect.right)); - long scale_Y = FixDiv(Long2Fix(dstRect.bottom - dstRect.top), Long2Fix(imgRect.bottom)); - - SetIdentityMatrix(&matrix); - if (dstRect.right - dstRect.left != imgRect.right || dstRect.bottom - dstRect.right != imgRect.bottom) - { - ScaleMatrix(&matrix, scale_X, scale_Y, 0, 0); - - if (vo_dwidth > d_width || vo_dheight > d_height) - { - TranslateMatrix(&matrix, Long2Fix(dstRect.left), Long2Fix(dstRect.top)); - } - } - - SetDSequenceMatrix(seqId, &matrix); - break; - } - default: - break; - } - - // Clear Background - tmpBounds = CGRectMake(0, 0, winRect.right, winRect.bottom); - QDBeginCGContext(GetWindowPort(theWindow), &context); - CGContextFillRect(context, tmpBounds); - QDEndCGContext(GetWindowPort(theWindow), &context); -} - -void window_ontop(void) -{ - if (!vo_quartz_fs) - { - // Cycle between level - winLevel++; - if (winLevel > 2) - winLevel = 1; - } - SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel])); -} - -void window_fullscreen(void) -{ - // go fullscreen - if (vo_fs) - { - if (winLevel != 0) - { - if (displayId == kCGDirectMainDisplay) - { - SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); - CGDisplayHideCursor(displayId); - mouseHide = TRUE; - } - - if (fs_res_x != 0 || fs_res_y != 0) - { - CFDictionaryRef mode; - size_t desiredBitDepth = 32; - boolean_t exactMatch; - - originalMode = CGDisplayCurrentMode(displayId); - - mode = CGDisplayBestModeForParameters(displayId, desiredBitDepth, fs_res_x, fs_res_y, &exactMatch); - - if (mode != NULL) - { - if (!exactMatch) - { - // Warn if the mode doesn't match exactly - mp_msg(MSGT_VO, MSGL_WARN, "Quartz warning: did not get exact mode match (got %dx%d) \n", (int)CFDictionaryGetValue(mode, kCGDisplayWidth), (int)CFDictionaryGetValue(mode, kCGDisplayHeight)); - } - - CGDisplayCapture(displayId); - CGDisplaySwitchToMode(displayId, mode); - } - else - { - mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: can't switch to fullscreen \n"); - } - - // Get Main device info/////////////////////////////////////////////////// - update_screen_info(); - } - } - // save old window size - if (!vo_quartz_fs) - { - GetWindowPortBounds(theWindow, &oldWinRect); - GetWindowBounds(theWindow, kWindowContentRgn, &oldWinBounds); - } - // go fullscreen - ChangeWindowAttributes(theWindow, kWindowNoShadowAttribute, 0); - - vo_quartz_fs = 1; - window_panscan(); - } - else //go back to windowed mode - { - vo_quartz_fs = 0; - if (originalMode != NULL) - { - CGDisplaySwitchToMode(displayId, originalMode); - CGDisplayRelease(displayId); - - // Get Main device info/////////////////////////////////////////////////// - update_screen_info(); - - originalMode = NULL; - } - SetSystemUIMode(kUIModeNormal, 0); - - // show mouse cursor - CGDisplayShowCursor(displayId); - mouseHide = FALSE; - - // revert window to previous setting - ChangeWindowAttributes(theWindow, 0, kWindowNoShadowAttribute); - SizeWindow(theWindow, oldWinRect.right, oldWinRect.bottom, 1); - MoveWindow(theWindow, oldWinBounds.left, oldWinBounds.top, 1); - } - window_resized(); -} - -void window_panscan(void) -{ - panscan_calc(); - - if (vo_panscan > 0) - CheckMenuItem(aspectMenu, 2, 1); - else - CheckMenuItem(aspectMenu, 2, 0); - - if (vo_quartz_fs) - { - MoveWindow(theWindow, xinerama_x - (vo_panscan_x >> 1), xinerama_y - (vo_panscan_y >> 1), 1); - SizeWindow(theWindow, vo_screenwidth + vo_panscan_x, vo_screenheight + vo_panscan_y, 1); - } -} diff --git a/libvo/vo_xv.c b/libvo/vo_xv.c index 4180ac0fd0..5bbcfc4d0a 100644 --- a/libvo/vo_xv.c +++ b/libvo/vo_xv.c @@ -18,29 +18,29 @@ * 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 <errno.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <libavutil/common.h> #include "config.h" + +#ifdef HAVE_SHM +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#endif + +// Note: depends on the inclusion of X11/extensions/XShm.h +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> + #include "options.h" #include "talloc.h" #include "mp_msg.h" @@ -48,22 +48,13 @@ Buffer allocation: #include "libmpcodecs/vfcap.h" #include "libmpcodecs/mp_image.h" #include "osd.h" - -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <errno.h> - #include "x11_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", @@ -71,16 +62,6 @@ static const vo_info_t info = { "" }; -#ifdef HAVE_SHM -#include <sys/ipc.h> -#include <sys/shm.h> -#include <X11/extensions/XShm.h> -#endif - -// Note: depends on the inclusion of X11/extensions/XShm.h -#include <X11/extensions/Xv.h> -#include <X11/extensions/Xvlib.h> - 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: diff --git a/libvo/x11_common.c b/libvo/x11_common.c index 7796d626db..2fae9f480a 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -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); } diff --git a/libvo/x11_common.h b/libvo/x11_common.h index 6ba2780747..14c7e44549 100644 --- a/libvo/x11_common.h +++ b/libvo/x11_common.h @@ -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; @@ -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 @@ -24,17 +24,14 @@ #include "libaf/af.h" #include "libao2/audio_out.h" -extern char * mixer_device; -extern char * mixer_channel; -extern int soft_vol; -extern float soft_vol_max; - -typedef struct mixer_s { +typedef struct mixer { struct ao *ao; af_stream_t *afilter; int volstep; bool muted; bool mute_emulation; + bool softvol; + float softvol_max; float last_l, last_r; float restore_vol_l, restore_vol_r; bool restore_volume; @@ -254,8 +254,7 @@ char *chapter_display_name(struct MPContext *mpctx, int chapter); char *chapter_name(struct MPContext *mpctx, int chapter); double chapter_start_time(struct MPContext *mpctx, int chapter); int get_chapter_count(struct MPContext *mpctx); -void update_subtitles(struct MPContext *mpctx, double refpts, - double sub_offset, bool reset); +void update_subtitles(struct MPContext *mpctx, double refpts, bool reset); // timeline/tl_matroska.c @@ -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 @@ -1229,6 +1229,8 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) if (mpctx->time_frame > 0) mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed; + if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE) + mpctx->last_av_difference = MP_NOPTS_VALUE; if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50 && !mpctx->drop_message_shown) { mp_tmsg(MSGT_AVSYNC, MSGL_WARN, SystemTooSlow); @@ -1238,9 +1240,6 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) if (opts->quiet) return; - if (a_pos == MP_NOPTS_VALUE) - a_pos = -9; // don't print a huge negative number - int width; char *line; unsigned pos = 0; @@ -1258,8 +1257,11 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) // Audio time if (mpctx->sh_audio) { - saddf(line, &pos, width, "A:%6.1f ", a_pos); - if (!sh_video) { + if (a_pos != MP_NOPTS_VALUE) + saddf(line, &pos, width, "A:%6.1f ", a_pos); + else + saddf(line, &pos, width, "A: ??? "); + if (!sh_video && a_pos != MP_NOPTS_VALUE) { float len = get_time_length(mpctx); saddf(line, &pos, width, "("); sadd_hhmmssf(line, &pos, width, a_pos); @@ -1270,13 +1272,22 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) } // Video time - if (sh_video) - saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts); + if (sh_video) { + if (mpctx->video_pts != MP_NOPTS_VALUE) + saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts); + else + saddf(line, &pos, width, "V: ??? ", mpctx->video_pts); + } // A-V sync - if (mpctx->sh_audio && sh_video) - saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ", - mpctx->last_av_difference, mpctx->total_avsync_change); + if (mpctx->sh_audio && sh_video) { + if (mpctx->last_av_difference != MP_NOPTS_VALUE) + saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ", + mpctx->last_av_difference, mpctx->total_avsync_change); + else + saddf(line, &pos, width, "A-V: ??? ct:%7.3f ", + mpctx->total_avsync_change); + } // Video stats if (sh_video) @@ -1770,7 +1781,7 @@ void reinit_audio_chain(struct MPContext *mpctx) current_module = "af_preinit"; if (!(mpctx->initialized_flags & INITIALIZED_AO)) { mpctx->initialized_flags |= INITIALIZED_AO; - mpctx->ao = ao_create(); + mpctx->ao = ao_create(opts, mpctx->input); mpctx->ao->samplerate = force_srate; mpctx->ao->format = opts->audio_output_format; } @@ -1818,6 +1829,8 @@ void reinit_audio_chain(struct MPContext *mpctx) } mpctx->mixer.ao = ao; mpctx->mixer.volstep = volstep; + mpctx->mixer.softvol = opts->softvol; + mpctx->mixer.softvol_max = opts->softvol_max; mixer_reinit(&mpctx->mixer); mpctx->syncing_audio = true; return; @@ -1898,13 +1911,14 @@ static bool is_av_sub(int type) return type == 'b' || type == 'p' || type == 'x'; } -void update_subtitles(struct MPContext *mpctx, double refpts, - double sub_offset, bool reset) +void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset) { + mpctx->osd->sub_offset = mpctx->video_offset; struct MPOpts *opts = &mpctx->opts; struct sh_video *sh_video = mpctx->sh_video; struct demux_stream *d_sub = mpctx->d_sub; - double curpts = refpts + sub_delay; + double refpts_s = refpts_tl - mpctx->osd->sub_offset; + double curpts_s = refpts_s + sub_delay; unsigned char *packet = NULL; int len; struct sh_sub *sh_sub = d_sub->sh; @@ -1929,7 +1943,7 @@ void update_subtitles(struct MPContext *mpctx, double refpts, if (sub_fps == 0) sub_fps = sh_video ? sh_video->fps : 25; current_module = "find_sub"; - find_sub(mpctx, mpctx->subdata, curpts * + find_sub(mpctx, mpctx->subdata, curpts_s * (mpctx->subdata->sub_uses_time ? 100. : sub_fps)); if (vo_sub) mpctx->vo_sub_last = vo_sub; @@ -1944,13 +1958,13 @@ void update_subtitles(struct MPContext *mpctx, double refpts, // Vobsub len = 0; if (vo_vobsub) { - if (curpts >= 0) { - len = vobsub_get_packet(vo_vobsub, curpts, + if (curpts_s >= 0) { + len = vobsub_get_packet(vo_vobsub, curpts_s, (void **)&packet, ×tamp); if (len > 0) mp_dbg(MSGT_CPLAYER, MSGL_V, "\rVOB sub: len=%d " "v_pts=%5.3f v_timer=%5.3f sub=%5.3f ts=%d \n", - len, refpts, sh_video->timer, + len, refpts_s, sh_video->timer, timestamp / 90000.0, timestamp); } } else { @@ -1962,14 +1976,14 @@ void update_subtitles(struct MPContext *mpctx, double refpts, // d_video->pts which would have been the simplest // improvement doesn't work because mpeg specific hacks // in video.c set d_video->pts to 0. - float x = d_sub->pts - refpts; + float x = d_sub->pts - refpts_s; if (x > -20 && x < 20) // prevent missing subs on pts reset timestamp = 90000 * d_sub->pts; else - timestamp = 90000 * curpts; + timestamp = 90000 * curpts_s; mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d " "v_pts=%5.3f s_pts=%5.3f ts=%d \n", len, - refpts, d_sub->pts, timestamp); + refpts_s, d_sub->pts, timestamp); } } if (len <= 0 || !packet) @@ -1995,19 +2009,19 @@ void update_subtitles(struct MPContext *mpctx, double refpts, ds_get_next_pts(d_sub); while (d_sub->first) { - double subpts = ds_get_next_pts(d_sub) + sub_offset; - if (subpts > curpts) { + double subpts_s = ds_get_next_pts(d_sub); + if (subpts_s > curpts_s) { // Libass handled subs can be fed to it in advance if (!opts->ass_enabled || !is_text_sub(type)) break; // Try to avoid demuxing whole file at once - if (d_sub->non_interleaved && subpts > curpts + 1) + if (d_sub->non_interleaved && subpts_s > curpts_s + 1) break; } double duration = d_sub->first->duration; len = ds_get_packet_sub(d_sub, &packet); if (is_av_sub(type)) { - int ret = decode_avsub(sh_sub, packet, len, subpts, duration); + int ret = decode_avsub(sh_sub, packet, len, subpts_s, duration); if (ret < 0) mp_msg(MSGT_SPUDEC, MSGL_WARN, "lavc failed decoding " "subtitle\n"); @@ -2036,10 +2050,10 @@ void update_subtitles(struct MPContext *mpctx, double refpts, continue; } if (sh_sub && sh_sub->active) { - sub_decode(sh_sub, mpctx->osd, packet, len, subpts, duration); + sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration); continue; } - if (subpts != MP_NOPTS_VALUE) { + if (subpts_s != MP_NOPTS_VALUE) { if (duration < 0) sub_clear_text(&subs, MP_NOPTS_VALUE); if (type == 'a') { // ssa/ass subs without libass => convert to plaintext @@ -2053,21 +2067,21 @@ void update_subtitles(struct MPContext *mpctx, double refpts, len -= p - packet; packet = p; } - double endpts = MP_NOPTS_VALUE; - if (subpts != MP_NOPTS_VALUE && duration >= 0) - endpts = subpts + duration; - sub_add_text(&subs, packet, len, endpts); + double endpts_s = MP_NOPTS_VALUE; + if (subpts_s != MP_NOPTS_VALUE && duration >= 0) + endpts_s = subpts_s + duration; + sub_add_text(&subs, packet, len, endpts_s); set_osd_subtitle(mpctx, &subs); } if (d_sub->non_interleaved) ds_get_next_pts(d_sub); } if (!opts->ass_enabled) - if (sub_clear_text(&subs, curpts)) + if (sub_clear_text(&subs, curpts_s)) set_osd_subtitle(mpctx, &subs); } if (vo_spudec) { - spudec_heartbeat(vo_spudec, 90000 * curpts); + spudec_heartbeat(vo_spudec, 90000 * curpts_s); if (spudec_changed(vo_spudec)) vo_osd_changed(OSDTYPE_SPU); } @@ -2500,7 +2514,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) return decode_audio(sh_audio, &ao->buffer, playsize); } -static int fill_audio_out_buffers(struct MPContext *mpctx) +static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) { struct MPOpts *opts = &mpctx->opts; struct ao *ao = mpctx->ao; @@ -2562,8 +2576,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) t = GetTimer() - t; tt = t * 0.000001f; audio_time_usage += tt; - if (mpctx->timeline && modifiable_audio_format) { - double endpts = mpctx->timeline[mpctx->timeline_part + 1].start; + if (endpts != MP_NOPTS_VALUE && modifiable_audio_format) { double bytes = (endpts - written_audio_pts(mpctx) + audio_delay) * ao->bps / opts->playback_speed; if (playsize > bytes) { @@ -2605,65 +2618,6 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) return -partial_fill; } -static int sleep_until_near_frame(struct MPContext *mpctx, float *time_frame, - bool sync_to_audio, float *aq_sleep_time) -{ - struct MPOpts *opts = &mpctx->opts; - double audio_limit = 2; - current_module = "calc_sleep_time"; - - if (mpctx->restart_playback) - return 0; - - *time_frame -= get_relative_time(mpctx); // reset timer - - if (sync_to_audio) { - float delay = ao_get_delay(mpctx->ao); - mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "delay=%f\n", delay); - - if (opts->autosync) { - /* - * Adjust this raw delay value by calculating the expected - * delay for this frame and generating a new value which is - * weighted between the two. The higher autosync is, the - * closer to the delay value gets to that which "-nosound" - * would have used, and the longer it will take for A/V - * sync to settle at the right value (but it eventually will.) - * This settling time is very short for values below 100. - */ - float predicted = mpctx->delay / opts->playback_speed + *time_frame; - float difference = delay - predicted; - delay = predicted + difference / (float)opts->autosync; - } - - *time_frame = delay - mpctx->delay / opts->playback_speed; - - // delay = amount of audio buffered in soundcard/driver - delay = FFMIN(delay, 0.5); - delay = FFMAX(delay, 0.1); - audio_limit = delay; - } else { - // If we're lagging more than 200 ms behind the right playback rate, - // don't try to "catch up". - // If benchmark is set always output frames as fast as possible - // without sleeping. - if (*time_frame < -0.2 || opts->benchmark) - *time_frame = 0; - } - - double t = *time_frame - mpctx->video_out->flip_queue_offset; - - if (t <= 0.05) - return 0; - - t -= 0.05; - if (t > audio_limit * 0.6) - t = audio_limit * 0.5; - *aq_sleep_time += t; - mp_input_get_cmd(mpctx->input, t * 1000 + 1, 1); - return 1; -} - int reinit_video_chain(struct MPContext *mpctx) { struct MPOpts *opts = &mpctx->opts; @@ -3070,7 +3024,7 @@ static int redraw_osd(struct MPContext *mpctx) return -1; if (vo_redraw_frame(mpctx->video_out) < 0) return -1; - mpctx->osd->pts = mpctx->video_pts; + mpctx->osd->pts = mpctx->video_pts - mpctx->osd->sub_offset; if (!(sh_video->output_flags & VFCAP_EOSD_FILTER)) vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd); vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); @@ -3086,39 +3040,7 @@ void add_step_frame(struct MPContext *mpctx) unpause_player(mpctx); } -static void pause_loop(struct MPContext *mpctx) -{ - mp_cmd_t *cmd; - - update_pause_message(mpctx); - - while ((cmd = mp_input_get_cmd(mpctx->input, 20, 1)) == NULL - || cmd->id == MP_CMD_SET_MOUSE_POS || cmd->pausing == 4) { - if (cmd) { - cmd = mp_input_get_cmd(mpctx->input, 0, 0); - run_command(mpctx, cmd); - mp_cmd_free(cmd); - continue; - } - if (mpctx->sh_video && mpctx->video_out) - vo_check_events(mpctx->video_out); - update_osd_msg(mpctx); - int hack = vo_osd_changed(0); - vo_osd_changed(hack); - if (hack || mpctx->sh_video && mpctx->video_out->want_redraw) - break; - update_pause_message(mpctx); - } -} - -static void reinit_decoders(struct MPContext *mpctx) -{ - reinit_video_chain(mpctx); - reinit_audio_chain(mpctx); - mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos}, mpctx); -} - -static void seek_reset(struct MPContext *mpctx, bool reset_ao) +static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac) { if (mpctx->sh_video) { current_module = "seek_video_reset"; @@ -3130,18 +3052,16 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao) mpctx->sh_video->last_pts = MP_NOPTS_VALUE; mpctx->delay = 0; mpctx->time_frame = 0; - mpctx->restart_playback = true; // Not all demuxers set d_video->pts during seek, so this value // (which is used by at least vobsub code below) may be completely // wrong (probably 0). mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset; mpctx->video_pts = mpctx->sh_video->pts; - update_subtitles(mpctx, mpctx->sh_video->pts, mpctx->video_offset, - true); + update_subtitles(mpctx, mpctx->sh_video->pts, true); update_teletext(mpctx->sh_video, mpctx->demuxer, 1); } - if (mpctx->sh_audio) { + if (mpctx->sh_audio && reset_ac) { current_module = "seek_audio_reset"; resync_audio_stream(mpctx->sh_audio); if (reset_ao) @@ -3149,8 +3069,7 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao) mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size; mpctx->sh_audio->a_buffer_len = 0; if (!mpctx->sh_video) - update_subtitles(mpctx, mpctx->sh_audio->pts, - mpctx->video_offset, true); + update_subtitles(mpctx, mpctx->sh_audio->pts, true); } if (vo_vobsub && mpctx->sh_video) { @@ -3158,6 +3077,7 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao) vobsub_seek(vo_vobsub, mpctx->sh_video->pts); } + mpctx->restart_playback = true; mpctx->hrseek_active = false; mpctx->hrseek_framedrop = false; mpctx->total_avsync_change = 0; @@ -3249,13 +3169,19 @@ static int seek(MPContext *mpctx, struct seek_params seek, if (demuxer_amount == -1) { mpctx->stop_play = AT_END_OF_FILE; // Clear audio from current position - if (mpctx->sh_audio) { + if (mpctx->sh_audio && !timeline_fallthrough) { ao_reset(mpctx->ao); mpctx->sh_audio->a_buffer_len = 0; } return -1; } } + if (need_reset) { + reinit_video_chain(mpctx); + mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos}, + mpctx); + } + int demuxer_style = 0; switch (seek.type) { case MPSEEK_FACTOR: @@ -3272,12 +3198,19 @@ static int seek(MPContext *mpctx, struct seek_params seek, demuxer_amount -= opts->hr_seek_demuxer_offset; int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, audio_delay, demuxer_style); - if (need_reset) - reinit_decoders(mpctx); - if (seekresult == 0) + if (seekresult == 0) { + if (need_reset) { + reinit_audio_chain(mpctx); + seek_reset(mpctx, !timeline_fallthrough, false); + } return -1; + } - seek_reset(mpctx, !timeline_fallthrough); + if (need_reset) + reinit_audio_chain(mpctx); + /* If we just reinitialized audio it doesn't need to be reset, + * and resetting could lose audio some decoders produce during init. */ + seek_reset(mpctx, !timeline_fallthrough, !need_reset); /* Use the target time as "current position" for further relative * seeks etc until a new video frame has been decoded */ @@ -3368,9 +3301,11 @@ double get_current_time(struct MPContext *mpctx) struct demuxer *demuxer = mpctx->demuxer; if (demuxer->stream_pts != MP_NOPTS_VALUE) return demuxer->stream_pts; - struct sh_video *sh_video = demuxer->video->sh; - if (sh_video) - return mpctx->video_pts; + if (mpctx->sh_video) { + double pts = mpctx->video_pts; + if (pts != MP_NOPTS_VALUE) + return pts; + } double apts = playing_audio_pts(mpctx); if (apts != MP_NOPTS_VALUE) return apts; @@ -3463,7 +3398,7 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts) int res = demuxer_seek_chapter(mpctx->demuxer, chapter, seek_pts); if (res >= 0) { if (*seek_pts == -1) - seek_reset(mpctx, true); + seek_reset(mpctx, true, true); else { mpctx->last_chapter_seek = res; mpctx->last_chapter_pts = *seek_pts; @@ -3486,8 +3421,20 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts) static void run_playloop(struct MPContext *mpctx) { struct MPOpts *opts = &mpctx->opts; - float aq_sleep_time = 0; bool full_audio_buffers = false; + bool audio_left = false, video_left = false; + double endpts = end_at.type == END_AT_TIME ? end_at.pos : MP_NOPTS_VALUE; + bool end_is_chapter = false; + double sleeptime = 0.5; + bool was_restart = mpctx->restart_playback; + + if (mpctx->timeline) { + double end = mpctx->timeline[mpctx->timeline_part + 1].start; + if (endpts == MP_NOPTS_VALUE || end < endpts) { + endpts = end; + end_is_chapter = true; + } + } if (opts->chapterrange[1] > 0) { int cur_chapter = get_current_chapter(mpctx); @@ -3501,79 +3448,27 @@ static void run_playloop(struct MPContext *mpctx) reinit_audio_chain(mpctx); } - /*========================== PLAY AUDIO ============================*/ - - if (!mpctx->sh_video) - mpctx->restart_playback = false; + if (mpctx->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); + int status = fill_audio_out_buffers(mpctx, endpts); 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; + // 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); - - 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; - - 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); + 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; + 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); + + 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 ==================== + 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; + } + mpctx->restart_playback = false; + } + if (mpctx->sh_audio && buffered_audio == -1) + buffered_audio = mpctx->paused ? 0 : ao_get_delay(mpctx->ao); - current_module = "key_events"; + 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); - 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->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; } - 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); + sleeptime = FFMIN(sleeptime, audio_sleep); + if (sleeptime > 0) { + if (!mpctx->sh_video) + goto novideo; int hack = vo_osd_changed(0); vo_osd_changed(hack); if (hack || mpctx->video_out->want_redraw) { if (redraw_osd(mpctx) < 0) { - if (mpctx->paused) + if (mpctx->paused && video_left) add_step_frame(mpctx); - break; + else + goto novideo; } else vo_osd_changed(0); + } else { + novideo: + mp_input_get_cmd(mpctx->input, sleeptime * 1000, true); } } - if (!mpctx->paused) + } + + //================= Keyboard events, SEEKing ==================== + + current_module = "key_events"; + + mp_cmd_t *cmd; + while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) { + /* Allow running consecutive seek commands to combine them, + * but execute the seek before running other commands. + * If the user seeks continuously (keeps arrow key down) + * try to finish showing a frame from one location before doing + * another seek (which could lead to unchanging display). */ + if (mpctx->seek.type && cmd->id != MP_CMD_SEEK + || mpctx->restart_playback && cmd->id == MP_CMD_SEEK + && GetTimerMS() - mpctx->start_timestamp < 300) + break; + cmd = mp_input_get_cmd(mpctx->input, 0, 0); + run_command(mpctx, cmd); + mp_cmd_free(cmd); + if (mpctx->stop_play) break; - pause_loop(mpctx); } // handle -sstep @@ -6,6 +6,10 @@ typedef struct MPOpts { char **audio_driver_list; int fixed_vo; int vo_ontop; + char *mixer_device; + char *mixer_channel; + int softvol; + float softvol_max; int gapless_audio; int ao_buffersize; int screen_size_x; @@ -24,6 +28,7 @@ typedef struct MPOpts { int requested_colorspace; int requested_input_range; int requested_output_range; + int cursor_autohide_delay; // ranges -100 - 100, 1000 if the vo default should be used int vo_gamma_gamma; diff --git a/osdep/macosx_finder_args.c b/osdep/macosx_finder_args.c deleted file mode 100644 index 6b5ef321f4..0000000000 --- a/osdep/macosx_finder_args.c +++ /dev/null @@ -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; -} diff --git a/osdep/macosx_finder_args.h b/osdep/macosx_finder_args.h index 4f06a139a6..1f18931712 100644 --- a/osdep/macosx_finder_args.h +++ b/osdep/macosx_finder_args.h @@ -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. */ diff --git a/osdep/macosx_finder_args.m b/osdep/macosx_finder_args.m new file mode 100644 index 0000000000..b006f11a79 --- /dev/null +++ b/osdep/macosx_finder_args.m @@ -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; +} diff --git a/stream/stream_vstream.c b/stream/stream_vstream.c index 3bc096f71b..380b682c46 100644 --- a/stream/stream_vstream.c +++ b/stream/stream_vstream.c @@ -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; @@ -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; }; |