From a84258d769a3d06958a017bb3fc47521ade5751b Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 29 Oct 2013 21:35:29 +0100 Subject: Move files part of the playback core to player sub-directory All these files access mp_core.h and MPContext, and form the actual player application. They should be all in one place, and separate from the other sources that are mere utility helpers. Preparation for splitting mplayer.c into multiple smaller parts. --- .gitignore | 2 +- Makefile | 32 +- mpvcore/command.c | 3021 -------------------- mpvcore/command.h | 50 - mpvcore/lua/assdraw.lua | 98 - mpvcore/lua/defaults.lua | 82 - mpvcore/lua/osc.lua | 1288 --------- mpvcore/mp_core.h | 356 --- mpvcore/mp_lua.c | 683 ----- mpvcore/mp_lua.h | 14 - mpvcore/mp_osd.h | 52 - mpvcore/mplayer.c | 5079 --------------------------------- mpvcore/options.c | 2 +- mpvcore/player/command.c | 3019 ++++++++++++++++++++ mpvcore/player/command.h | 50 + mpvcore/player/lua/assdraw.lua | 98 + mpvcore/player/lua/defaults.lua | 82 + mpvcore/player/lua/osc.lua | 1288 +++++++++ mpvcore/player/mp_core.h | 356 +++ mpvcore/player/mp_lua.c | 683 +++++ mpvcore/player/mp_lua.h | 14 + mpvcore/player/mp_osd.h | 52 + mpvcore/player/mplayer.c | 5079 +++++++++++++++++++++++++++++++++ mpvcore/player/screenshot.c | 405 +++ mpvcore/player/screenshot.h | 46 + mpvcore/player/timeline/tl_cue.c | 417 +++ mpvcore/player/timeline/tl_edl.c | 393 +++ mpvcore/player/timeline/tl_matroska.c | 575 ++++ mpvcore/screenshot.c | 405 --- mpvcore/screenshot.h | 46 - mpvcore/timeline/tl_cue.c | 417 --- mpvcore/timeline/tl_edl.c | 393 --- mpvcore/timeline/tl_matroska.c | 575 ---- 33 files changed, 12575 insertions(+), 12577 deletions(-) delete mode 100644 mpvcore/command.c delete mode 100644 mpvcore/command.h delete mode 100644 mpvcore/lua/assdraw.lua delete mode 100644 mpvcore/lua/defaults.lua delete mode 100644 mpvcore/lua/osc.lua delete mode 100644 mpvcore/mp_core.h delete mode 100644 mpvcore/mp_lua.c delete mode 100644 mpvcore/mp_lua.h delete mode 100644 mpvcore/mp_osd.h delete mode 100644 mpvcore/mplayer.c create mode 100644 mpvcore/player/command.c create mode 100644 mpvcore/player/command.h create mode 100644 mpvcore/player/lua/assdraw.lua create mode 100644 mpvcore/player/lua/defaults.lua create mode 100644 mpvcore/player/lua/osc.lua create mode 100644 mpvcore/player/mp_core.h create mode 100644 mpvcore/player/mp_lua.c create mode 100644 mpvcore/player/mp_lua.h create mode 100644 mpvcore/player/mp_osd.h create mode 100644 mpvcore/player/mplayer.c create mode 100644 mpvcore/player/screenshot.c create mode 100644 mpvcore/player/screenshot.h create mode 100644 mpvcore/player/timeline/tl_cue.c create mode 100644 mpvcore/player/timeline/tl_edl.c create mode 100644 mpvcore/player/timeline/tl_matroska.c delete mode 100644 mpvcore/screenshot.c delete mode 100644 mpvcore/screenshot.h delete mode 100644 mpvcore/timeline/tl_cue.c delete mode 100644 mpvcore/timeline/tl_edl.c delete mode 100644 mpvcore/timeline/tl_matroska.c diff --git a/.gitignore b/.gitignore index 07eab0dfbd..f4361056da 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ /demux/ebml_defs.c /demux/ebml_types.h /sub/osd_font.h -/mpvcore/lua/*.inc +/mpvcore/player/lua/*.inc /DOCS/man/*/mpv.1 /DOCS/man/*/mpv.aux /DOCS/man/*/mpv.log diff --git a/Makefile b/Makefile index 6d86320ffa..7c9807f175 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ SOURCES-$(WAYLAND) += video/out/vo_wayland.c video/out/wayland_comm SOURCES-$(VF_LAVFI) += video/filter/vf_lavfi.c SOURCES-$(AF_LAVFI) += audio/filter/af_lavfi.c -SOURCES-$(LUA) += mpvcore/mp_lua.c +SOURCES-$(LUA) += mpvcore/player/mp_lua.c ifeq ($(HAVE_AVUTIL_REFCOUNTING),no) SOURCES-yes += video/decode/lavc_dr1.c @@ -194,7 +194,6 @@ SOURCES = audio/audio.c \ mpvcore/bstr.c \ mpvcore/charset_conv.c \ mpvcore/codecs.c \ - mpvcore/command.c \ mpvcore/cpudetect.c \ mpvcore/m_config.c \ mpvcore/m_option.c \ @@ -202,19 +201,20 @@ SOURCES = audio/audio.c \ mpvcore/mp_common.c \ mpvcore/mp_msg.c \ mpvcore/mp_ring.c \ - mpvcore/mplayer.c \ mpvcore/options.c \ mpvcore/parser-cfg.c \ mpvcore/parser-mpcmd.c \ mpvcore/path.c \ mpvcore/playlist.c \ mpvcore/playlist_parser.c \ - mpvcore/screenshot.c \ mpvcore/version.c \ mpvcore/input/input.c \ - mpvcore/timeline/tl_edl.c \ - mpvcore/timeline/tl_matroska.c \ - mpvcore/timeline/tl_cue.c \ + mpvcore/player/command.c \ + mpvcore/player/mplayer.c \ + mpvcore/player/screenshot.c \ + mpvcore/player/timeline/tl_edl.c \ + mpvcore/player/timeline/tl_matroska.c \ + mpvcore/player/timeline/tl_cue.c \ osdep/io.c \ osdep/numcores.c \ osdep/timer.c \ @@ -408,16 +408,16 @@ sub/osd_libass.c: sub/osd_font.h sub/osd_font.h: TOOLS/file2string.pl sub/osd_font.otf ./$^ >$@ -mpvcore/mp_lua.c: mpvcore/lua/defaults.inc -mpvcore/lua/defaults.inc: TOOLS/file2string.pl mpvcore/lua/defaults.lua +mpvcore/player/mp_lua.c: mpvcore/player/lua/defaults.inc +mpvcore/player/lua/defaults.inc: TOOLS/file2string.pl mpvcore/player/lua/defaults.lua ./$^ >$@ -mpvcore/mp_lua.c: mpvcore/lua/assdraw.inc -mpvcore/lua/assdraw.inc: TOOLS/file2string.pl mpvcore/lua/assdraw.lua +mpvcore/player/mp_lua.c: mpvcore/player/lua/assdraw.inc +mpvcore/player/lua/assdraw.inc: TOOLS/file2string.pl mpvcore/player/lua/assdraw.lua ./$^ >$@ -mpvcore/mp_lua.c: mpvcore/lua/osc.inc -mpvcore/lua/osc.inc: TOOLS/file2string.pl mpvcore/lua/osc.lua +mpvcore/player/mp_lua.c: mpvcore/player/lua/osc.inc +mpvcore/player/lua/osc.inc: TOOLS/file2string.pl mpvcore/player/lua/osc.lua ./$^ >$@ # ./configure must be rerun if it changed @@ -518,9 +518,9 @@ clean: -$(RM) video/out/gl_video_shaders.h -$(RM) video/out/x11_icon.inc -$(RM) sub/osd_font.h - -$(RM) mpvcore/lua/defaults.inc - -$(RM) mpvcore/lua/assdraw.inc - -$(RM) mpvcore/lua/osc.inc + -$(RM) mpvcore/player/lua/defaults.inc + -$(RM) mpvcore/player/lua/assdraw.inc + -$(RM) mpvcore/player/lua/osc.inc distclean: clean -$(RM) config.log config.mak config.h TAGS tags diff --git a/mpvcore/command.c b/mpvcore/command.c deleted file mode 100644 index f20a6517b8..0000000000 --- a/mpvcore/command.c +++ /dev/null @@ -1,3021 +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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "config.h" -#include "talloc.h" -#include "command.h" -#include "input/input.h" -#include "stream/stream.h" -#include "demux/demux.h" -#include "demux/stheader.h" -#include "resolve.h" -#include "playlist.h" -#include "playlist_parser.h" -#include "sub/sub.h" -#include "sub/dec_sub.h" -#include "mpvcore/m_option.h" -#include "m_property.h" -#include "m_config.h" -#include "video/filter/vf.h" -#include "video/decode/vd.h" -#include "mp_osd.h" -#include "video/out/vo.h" -#include "video/csputils.h" -#include "playlist.h" -#include "audio/mixer.h" -#include "audio/out/ao.h" -#include "mpvcore/mp_common.h" -#include "audio/filter/af.h" -#include "video/decode/dec_video.h" -#include "audio/decode/dec_audio.h" -#include "mpvcore/path.h" -#include "stream/tv.h" -#include "stream/stream_radio.h" -#include "stream/pvr.h" -#ifdef CONFIG_DVBIN -#include "stream/dvbin.h" -#endif -#include "screenshot.h" -#ifdef HAVE_SYS_MMAN_H -#include -#endif - -#include "mpvcore/mp_core.h" - -#include "mp_lua.h" - -struct command_ctx { - int events; - -#define OVERLAY_MAX_ID 64 - void *overlay_map[OVERLAY_MAX_ID]; -}; - -static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype, - const char *cmd, const char *arg); -static int set_filters(struct MPContext *mpctx, enum stream_type mediatype, - struct m_obj_settings *new_chain); - -static char *format_bitrate(int rate) -{ - return talloc_asprintf(NULL, "%d kbps", rate * 8 / 1000); -} - -static char *format_delay(double time) -{ - return talloc_asprintf(NULL, "%d ms", ROUND(time * 1000)); -} - -// Property-option bridge. -static int mp_property_generic_option(struct m_option *prop, int action, - void *arg, MPContext *mpctx) -{ - char *optname = prop->priv; - struct m_config_option *opt = m_config_get_co(mpctx->mconfig, - bstr0(optname)); - void *valptr = opt->data; - - switch (action) { - case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = *(opt->opt); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - m_option_copy(opt->opt, arg, valptr); - return M_PROPERTY_OK; - case M_PROPERTY_SET: - m_option_copy(opt->opt, valptr, arg); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Playback speed (RW) -static int mp_property_playback_speed(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - double orig_speed = opts->playback_speed; - switch (action) { - case M_PROPERTY_SET: { - opts->playback_speed = *(double *) arg; - // Adjust time until next frame flip for nosound mode - mpctx->time_frame *= orig_speed / opts->playback_speed; - if (mpctx->sh_audio) - reinit_audio_chain(mpctx); - return M_PROPERTY_OK; - } - case M_PROPERTY_PRINT: - *(char **)arg = talloc_asprintf(NULL, "x %6.2f", orig_speed); - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// filename with path (RO) -static int mp_property_path(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->filename) - return M_PROPERTY_UNAVAILABLE; - return m_property_strdup_ro(prop, action, arg, mpctx->filename); -} - -static int mp_property_filename(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->filename) - return M_PROPERTY_UNAVAILABLE; - char *filename = talloc_strdup(NULL, mpctx->filename); - if (mp_is_url(bstr0(filename))) - mp_url_unescape_inplace(filename); - char *f = (char *)mp_basename(filename); - int r = m_property_strdup_ro(prop, action, arg, f[0] ? f : filename); - talloc_free(filename); - return r; -} - -static int mp_property_media_title(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - char *name = NULL; - if (mpctx->resolve_result) - name = mpctx->resolve_result->title; - if (name && name[0]) - return m_property_strdup_ro(prop, action, arg, name); - if (mpctx->master_demuxer) { - name = demux_info_get(mpctx->master_demuxer, "title"); - if (name && name[0]) - return m_property_strdup_ro(prop, action, arg, name); - } - return mp_property_filename(prop, action, arg, mpctx); -} - -static int mp_property_stream_path(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct stream *stream = mpctx->stream; - if (!stream || !stream->url) - return M_PROPERTY_UNAVAILABLE; - return m_property_strdup_ro(prop, action, arg, stream->url); -} - -static int mp_property_stream_capture(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->stream) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_SET) { - char *filename = *(char **)arg; - stream_set_capture_file(mpctx->stream, filename); - // fall through to mp_property_generic_option - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Demuxer name (RO) -static int mp_property_demuxer(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - return m_property_strdup_ro(prop, action, arg, demuxer->desc->name); -} - -/// Position in the stream (RW) -static int mp_property_stream_pos(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct stream *stream = mpctx->stream; - if (!stream) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_GET: - *(int64_t *) arg = stream_tell(stream); - return M_PROPERTY_OK; - case M_PROPERTY_SET: - stream_seek(stream, *(int64_t *) arg); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Stream start offset (RO) -static int mp_property_stream_start(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct stream *stream = mpctx->stream; - if (!stream) - return M_PROPERTY_UNAVAILABLE; - return m_property_int64_ro(prop, action, arg, stream->start_pos); -} - -/// Stream end offset (RO) -static int mp_property_stream_end(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct stream *stream = mpctx->stream; - if (!stream) - return M_PROPERTY_UNAVAILABLE; - return m_property_int64_ro(prop, action, arg, stream->end_pos); -} - -/// Stream length (RO) -static int mp_property_stream_length(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct stream *stream = mpctx->stream; - if (!stream) - return M_PROPERTY_UNAVAILABLE; - return m_property_int64_ro(prop, action, arg, - stream->end_pos - stream->start_pos); -} - -// Does some magic to handle "/full" as time formatted with milliseconds. -// Assumes prop is the type of the actual property. -static int property_time(m_option_t *prop, int action, void *arg, double time) -{ - switch (action) { - case M_PROPERTY_GET: - *(double *)arg = time; - return M_PROPERTY_OK; - case M_PROPERTY_KEY_ACTION: { - struct m_property_action_arg *ka = arg; - - if (strcmp(ka->key, "full") != 0) - return M_PROPERTY_UNKNOWN; - - switch (ka->action) { - case M_PROPERTY_GET: - *(double *)ka->arg = time; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - *(char **)ka->arg = mp_format_time(time, true); - return M_PROPERTY_OK; - case M_PROPERTY_GET_TYPE: - *(struct m_option *)ka->arg = *prop; - return M_PROPERTY_OK; - } - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Current stream position in seconds (RO) -static int mp_property_stream_time_pos(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - double pts = demuxer->stream_pts; - if (pts == MP_NOPTS_VALUE) - return M_PROPERTY_UNAVAILABLE; - - return property_time(prop, action, arg, pts); -} - - -/// Media length in seconds (RO) -static int mp_property_length(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - double len; - - if (!(int) (len = get_time_length(mpctx))) - return M_PROPERTY_UNAVAILABLE; - - return property_time(prop, action, arg, len); -} - -static int mp_property_avsync(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->sh_audio || !mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - if (mpctx->last_av_difference == MP_NOPTS_VALUE) - return M_PROPERTY_UNAVAILABLE; - return m_property_double_ro(prop, action, arg, mpctx->last_av_difference); -} - -/// Current position in percent (RW) -static int mp_property_percent_pos(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->num_sources) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SET: ; - double pos = *(double *)arg; - queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(double *)arg = get_current_pos_ratio(mpctx, false) * 100.0; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - *(char **)arg = talloc_asprintf(NULL, "%d", get_percent_pos(mpctx)); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Current position in seconds (RW) -static int mp_property_time_pos(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->num_sources) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_SET) { - queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, 0); - return M_PROPERTY_OK; - } - return property_time(prop, action, arg, get_current_time(mpctx)); -} - -static int mp_property_remaining(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - double len = get_time_length(mpctx); - double pos = get_current_time(mpctx); - double start = get_start_time(mpctx); - - if (!(int)len) - return M_PROPERTY_UNAVAILABLE; - - return property_time(prop, action, arg, len - (pos - start)); -} - -/// Current chapter (RW) -static int mp_property_chapter(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - int chapter = get_current_chapter(mpctx); - if (chapter < -1) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_GET: - *(int *) arg = chapter; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: { - *(char **) arg = chapter_display_name(mpctx, chapter); - return M_PROPERTY_OK; - } - case M_PROPERTY_SWITCH: - case M_PROPERTY_SET: ; - int step_all; - if (action == M_PROPERTY_SWITCH) { - struct m_property_switch_arg *sarg = arg; - step_all = ROUND(sarg->inc); - // Check threshold for relative backward seeks - if (mpctx->opts->chapter_seek_threshold >= 0 && step_all < 0) { - double current_chapter_start = - chapter_start_time(mpctx, chapter); - // If we are far enough into a chapter, seek back to the - // beginning of current chapter instead of previous one - if (current_chapter_start >= 0 && - get_current_time(mpctx) - current_chapter_start > - mpctx->opts->chapter_seek_threshold) - step_all++; - } - } else // Absolute set - step_all = *(int *)arg - chapter; - chapter += step_all; - if (chapter < -1) - chapter = -1; - if (chapter >= get_chapter_count(mpctx) && step_all > 0) { - mpctx->stop_play = PT_NEXT_ENTRY; - } else { - mp_seek_chapter(mpctx, chapter); - } - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_list_chapters(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action == M_PROPERTY_GET) { - int count = get_chapter_count(mpctx); - int cur = mpctx->num_sources ? get_current_chapter(mpctx) : -1; - char *res = NULL; - int n; - - if (count < 1) { - res = talloc_asprintf_append(res, "No chapters."); - } - - for (n = 0; n < count; n++) { - char *name = chapter_display_name(mpctx, n); - double t = chapter_start_time(mpctx, n); - char* time = mp_format_time(t, false); - res = talloc_asprintf_append(res, "%s", time); - talloc_free(time); - char *m1 = "> ", *m2 = " <"; - if (n != cur) - m1 = m2 = ""; - res = talloc_asprintf_append(res, " %s%s%s\n", m1, name, m2); - talloc_free(name); - } - - *(char **)arg = res; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_edition(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - struct demuxer *demuxer = mpctx->master_demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - if (demuxer->num_editions <= 0) - return M_PROPERTY_UNAVAILABLE; - - int edition = demuxer->edition; - - switch (action) { - case M_PROPERTY_GET: - *(int *)arg = edition; - return M_PROPERTY_OK; - case M_PROPERTY_SET: { - edition = *(int *)arg; - if (edition != demuxer->edition) { - opts->edition_id = edition; - mpctx->stop_play = PT_RESTART; - } - return M_PROPERTY_OK; - } - case M_PROPERTY_GET_TYPE: { - struct m_option opt = { - .name = prop->name, - .type = CONF_TYPE_INT, - .flags = CONF_RANGE, - .min = 0, - .max = demuxer->num_editions - 1, - }; - *(struct m_option *)arg = opt; - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static struct mp_resolve_src *find_source(struct mp_resolve_result *res, - char *encid, char *url) -{ - if (res->num_srcs == 0) - return NULL; - - int src = 0; - for (int n = 0; n < res->num_srcs; n++) { - char *s_url = res->srcs[n]->url; - char *s_encid = res->srcs[n]->encid; - if (url && s_url && strcmp(url, s_url) == 0) { - src = n; - break; - } - // Prefer source URL if possible; so continue in case encid isn't unique - if (encid && s_encid && strcmp(encid, s_encid) == 0) - src = n; - } - return res->srcs[src]; -} - -static int mp_property_quvi_format(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - struct mp_resolve_result *res = mpctx->resolve_result; - if (!res || !res->num_srcs) - return M_PROPERTY_UNAVAILABLE; - - struct mp_resolve_src *cur = find_source(res, opts->quvi_format, res->url); - if (!cur) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_GET: - *(char **)arg = talloc_strdup(NULL, cur->encid); - return M_PROPERTY_OK; - case M_PROPERTY_SET: { - mpctx->stop_play = PT_RESTART; - // Make it restart at the same position. This will have disastrous - // consequences if the stream is not arbitrarily seekable, but whatever. - m_config_backup_opt(mpctx->mconfig, "start"); - opts->play_start = (struct m_rel_time) { - .type = REL_TIME_ABSOLUTE, - .pos = get_current_time(mpctx), - }; - break; - } - case M_PROPERTY_SWITCH: { - struct m_property_switch_arg *sarg = arg; - int pos = 0; - for (int n = 0; n < res->num_srcs; n++) { - if (res->srcs[n] == cur) { - pos = n; - break; - } - } - pos += sarg->inc; - if (pos < 0 || pos >= res->num_srcs) { - if (sarg->wrap) { - pos = (res->num_srcs + pos) % res->num_srcs; - } else { - pos = av_clip(pos, 0, res->num_srcs); - } - } - char *fmt = res->srcs[pos]->encid; - return mp_property_quvi_format(prop, M_PROPERTY_SET, &fmt, mpctx); - } - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Number of titles in file -static int mp_property_titles(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - unsigned int num_titles; - if (!demuxer || stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_TITLES, - &num_titles) < 1) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, num_titles); -} - -/// Number of chapters in file -static int mp_property_chapters(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->num_sources) - return M_PROPERTY_UNAVAILABLE; - int count = get_chapter_count(mpctx); - return m_property_int_ro(prop, action, arg, count); -} - -static int mp_property_editions(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - if (demuxer->num_editions <= 0) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, demuxer->num_editions); -} - -/// Current dvd angle (RW) -static int mp_property_angle(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - int angle = -1; - int angles; - - if (demuxer) - angle = demuxer_get_current_angle(demuxer); - if (angle < 0) - return M_PROPERTY_UNAVAILABLE; - angles = demuxer_angles_count(demuxer); - if (angles <= 1) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_GET: - *(int *) arg = angle; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: { - *(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles); - return M_PROPERTY_OK; - } - case M_PROPERTY_SET: - angle = demuxer_set_angle(demuxer, *(int *)arg); - if (angle >= 0) { - if (mpctx->sh_video) - resync_video_stream(mpctx->sh_video); - - if (mpctx->sh_audio) - resync_audio_stream(mpctx->sh_audio); - } - return M_PROPERTY_OK; - case M_PROPERTY_GET_TYPE: { - struct m_option opt = { - .name = prop->name, - .type = CONF_TYPE_INT, - .flags = CONF_RANGE, - .min = 1, - .max = angles, - }; - *(struct m_option *)arg = opt; - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int tag_property(m_option_t *prop, int action, void *arg, - struct mp_tags *tags) -{ - static const m_option_t key_type = - { - "tags", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL - }; - - switch (action) { - case M_PROPERTY_GET: { - char **slist = NULL; - int num = 0; - for (int n = 0; n < tags->num_keys; n++) { - MP_TARRAY_APPEND(NULL, slist, num, tags->keys[n]); - MP_TARRAY_APPEND(NULL, slist, num, tags->values[n]); - } - MP_TARRAY_APPEND(NULL, slist, num, NULL); - *(char ***)arg = slist; - return M_PROPERTY_OK; - } - case M_PROPERTY_PRINT: { - char *res = NULL; - for (int n = 0; n < tags->num_keys; n++) { - res = talloc_asprintf_append_buffer(res, "%s: %s\n", - tags->keys[n], tags->values[n]); - } - *(char **)arg = res; - return res ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE; - } - case M_PROPERTY_KEY_ACTION: { - struct m_property_action_arg *ka = arg; - char *meta = mp_tags_get_str(tags, ka->key); - if (!meta) - return M_PROPERTY_UNKNOWN; - switch (ka->action) { - case M_PROPERTY_GET: - *(char **)ka->arg = talloc_strdup(NULL, meta); - return M_PROPERTY_OK; - case M_PROPERTY_GET_TYPE: - *(struct m_option *)ka->arg = key_type; - return M_PROPERTY_OK; - } - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Demuxer meta data -static int mp_property_metadata(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - - return tag_property(prop, action, arg, demuxer->metadata); -} - -static int mp_property_chapter_metadata(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct demuxer *demuxer = mpctx->master_demuxer; - int chapter = get_current_chapter(mpctx); - if (!demuxer || chapter < 0) - return M_PROPERTY_UNAVAILABLE; - - assert(chapter < demuxer->num_chapters); - - return tag_property(prop, action, arg, demuxer->chapters[chapter].metadata); -} - -static int mp_property_pause(m_option_t *prop, int action, void *arg, - void *ctx) -{ - MPContext *mpctx = ctx; - - if (action == M_PROPERTY_SET) { - if (*(int *)arg) { - pause_player(mpctx); - } else { - unpause_player(mpctx); - } - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, ctx); -} - -static int mp_property_cache(m_option_t *prop, int action, void *arg, - void *ctx) -{ - MPContext *mpctx = ctx; - int cache = mp_get_cache_percent(mpctx); - if (cache < 0) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, cache); -} - -static int mp_property_clock(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - char outstr[6]; - time_t t = time(NULL); - struct tm *tmp = localtime(&t); - - if ((tmp != NULL) && (strftime(outstr, sizeof(outstr), "%H:%M", tmp) == 5)) - return m_property_strdup_ro(prop, action, arg, outstr); - return M_PROPERTY_UNAVAILABLE; -} - -/// Volume (RW) -static int mp_property_volume(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - switch (action) { - case M_PROPERTY_GET: - mixer_getbothvolume(mpctx->mixer, arg); - return M_PROPERTY_OK; - case M_PROPERTY_SET: - if (!mixer_audio_initialized(mpctx->mixer)) - return M_PROPERTY_ERROR; - mixer_setvolume(mpctx->mixer, *(float *) arg, *(float *) arg); - return M_PROPERTY_OK; - case M_PROPERTY_SWITCH: { - if (!mixer_audio_initialized(mpctx->mixer)) - return M_PROPERTY_ERROR; - struct m_property_switch_arg *sarg = arg; - if (sarg->inc <= 0) - mixer_decvolume(mpctx->mixer); - else - mixer_incvolume(mpctx->mixer); - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Mute (RW) -static int mp_property_mute(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - switch (action) { - case M_PROPERTY_SET: - if (!mixer_audio_initialized(mpctx->mixer)) - return M_PROPERTY_ERROR; - mixer_setmute(mpctx->mixer, *(int *) arg); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = mixer_getmute(mpctx->mixer); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_volrestore(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - switch (action) { - case M_PROPERTY_GET: { - char *s = mixer_get_volume_restore_data(mpctx->mixer); - *(char **)arg = s; - return s ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE; - } - case M_PROPERTY_SET: - return M_PROPERTY_NOT_IMPLEMENTED; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Audio delay (RW) -static int mp_property_audio_delay(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!(mpctx->sh_audio && mpctx->sh_video)) - return M_PROPERTY_UNAVAILABLE; - float delay = mpctx->opts->audio_delay; - switch (action) { - case M_PROPERTY_PRINT: - *(char **)arg = format_delay(delay); - return M_PROPERTY_OK; - case M_PROPERTY_SET: - mpctx->audio_delay = mpctx->opts->audio_delay = *(float *)arg; - mpctx->delay -= mpctx->audio_delay - delay; - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Audio codec tag (RO) -static int mp_property_audio_format(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - const char *c = mpctx->sh_audio ? mpctx->sh_audio->gsh->codec : NULL; - return m_property_strdup_ro(prop, action, arg, c); -} - -/// Audio codec name (RO) -static int mp_property_audio_codec(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - const char *c = mpctx->sh_audio ? mpctx->sh_audio->gsh->decoder_desc : NULL; - return m_property_strdup_ro(prop, action, arg, c); -} - -/// Audio bitrate (RO) -static int mp_property_audio_bitrate(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->sh_audio) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_PRINT: - *(char **)arg = format_bitrate(mpctx->sh_audio->i_bps); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = mpctx->sh_audio->i_bps; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Samplerate (RO) -static int mp_property_samplerate(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->sh_audio) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_PRINT: - *(char **)arg = talloc_asprintf(NULL, "%d kHz", - mpctx->sh_audio->samplerate / 1000); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = mpctx->sh_audio->samplerate; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Number of channels (RO) -static int mp_property_channels(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->sh_audio) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_PRINT: - *(char **) arg = mp_chmap_to_str(&mpctx->sh_audio->channels); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = mpctx->sh_audio->channels.num; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Balance (RW) -static int mp_property_balance(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - float bal; - - switch (action) { - case M_PROPERTY_GET: - mixer_getbalance(mpctx->mixer, arg); - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: { - char **str = arg; - mixer_getbalance(mpctx->mixer, &bal); - if (bal == 0.f) - *str = talloc_strdup(NULL, "center"); - else if (bal == -1.f) - *str = talloc_strdup(NULL, "left only"); - else if (bal == 1.f) - *str = talloc_strdup(NULL, "right only"); - else { - unsigned right = (bal + 1.f) / 2.f * 100.f; - *str = talloc_asprintf(NULL, "left %d%%, right %d%%", - 100 - right, right); - } - return M_PROPERTY_OK; - } - case M_PROPERTY_SET: - mixer_setbalance(mpctx->mixer, *(float *)arg); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static struct track* track_next(struct MPContext *mpctx, enum stream_type type, - int direction, struct track *track) -{ - assert(direction == -1 || direction == +1); - struct track *prev = NULL, *next = NULL; - bool seen = track == NULL; - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *cur = mpctx->tracks[n]; - if (cur->type == type) { - if (cur == track) { - seen = true; - } else { - if (seen && !next) { - next = cur; - } - if (!seen || !track) { - prev = cur; - } - } - } - } - return direction > 0 ? next : prev; -} - -static int property_switch_track(m_option_t *prop, int action, void *arg, - MPContext *mpctx, enum stream_type type) -{ - if (!mpctx->num_sources) - return M_PROPERTY_UNAVAILABLE; - struct track *track = mpctx->current_track[type]; - - switch (action) { - case M_PROPERTY_GET: - *(int *) arg = track ? track->user_tid : -2; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - if (!track) - *(char **) arg = talloc_strdup(NULL, "no"); - else { - char *lang = track->lang; - if (!lang) - lang = mp_gtext("unknown"); - - if (track->title) - *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")", - track->user_tid, lang, track->title); - else - *(char **)arg = talloc_asprintf(NULL, "(%d) %s", - track->user_tid, lang); - } - return M_PROPERTY_OK; - - case M_PROPERTY_SWITCH: { - struct m_property_switch_arg *sarg = arg; - mp_switch_track(mpctx, type, - track_next(mpctx, type, sarg->inc >= 0 ? +1 : -1, track)); - return M_PROPERTY_OK; - } - case M_PROPERTY_SET: - mp_switch_track(mpctx, type, mp_track_by_tid(mpctx, type, *(int *)arg)); - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -static const char *track_type_name(enum stream_type t) -{ - switch (t) { - case STREAM_VIDEO: return "Video"; - case STREAM_AUDIO: return "Audio"; - case STREAM_SUB: return "Sub"; - } - return NULL; -} - -static int property_list_tracks(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action == M_PROPERTY_GET) { - char *res = NULL; - - for (int type = 0; type < STREAM_TYPE_COUNT; type++) { - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; - if (track->type != type) - continue; - - bool selected = mpctx->current_track[track->type] == track; - res = talloc_asprintf_append(res, "%s: ", - track_type_name(track->type)); - if (selected) - res = talloc_asprintf_append(res, "> "); - res = talloc_asprintf_append(res, "(%d) ", track->user_tid); - if (track->title) - res = talloc_asprintf_append(res, "'%s' ", track->title); - if (track->lang) - res = talloc_asprintf_append(res, "(%s) ", track->lang); - if (track->is_external) - res = talloc_asprintf_append(res, "(external) "); - if (selected) - res = talloc_asprintf_append(res, "<"); - res = talloc_asprintf_append(res, "\n"); - } - - res = talloc_asprintf_append(res, "\n"); - } - - struct demuxer *demuxer = mpctx->master_demuxer; - if (demuxer && demuxer->num_editions > 1) - res = talloc_asprintf_append(res, "\nEdition: %d of %d\n", - demuxer->edition + 1, - demuxer->num_editions); - - *(char **)arg = res; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -/// Selected audio id (RW) -static int mp_property_audio(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_switch_track(prop, action, arg, mpctx, STREAM_AUDIO); -} - -/// Selected video id (RW) -static int mp_property_video(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_switch_track(prop, action, arg, mpctx, STREAM_VIDEO); -} - -static struct track *find_track_by_demuxer_id(MPContext *mpctx, - enum stream_type type, - int demuxer_id) -{ - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; - if (track->type == type && track->demuxer_id == demuxer_id) - return track; - } - return NULL; -} - -static int mp_property_program(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - demux_program_t prog; - - struct demuxer *demuxer = mpctx->master_demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SWITCH: - case M_PROPERTY_SET: - if (action == M_PROPERTY_SET && arg) - prog.progid = *((int *) arg); - else - prog.progid = -1; - if (demux_control(demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM, &prog) == - DEMUXER_CTRL_NOTIMPL) - return M_PROPERTY_ERROR; - - if (prog.aid < 0 && prog.vid < 0) { - mp_msg(MSGT_CPLAYER, MSGL_ERR, - "Selected program contains no audio or video streams!\n"); - return M_PROPERTY_ERROR; - } - mp_switch_track(mpctx, STREAM_VIDEO, - find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.vid)); - mp_switch_track(mpctx, STREAM_AUDIO, - find_track_by_demuxer_id(mpctx, STREAM_AUDIO, prog.aid)); - mp_switch_track(mpctx, STREAM_SUB, - find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.sid)); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - - -/// Fullscreen state (RW) -static int mp_property_fullscreen(m_option_t *prop, - int action, - void *arg, - MPContext *mpctx) -{ - if (!mpctx->video_out) - return M_PROPERTY_UNAVAILABLE; - struct mp_vo_opts *opts = mpctx->video_out->opts; - - if (action == M_PROPERTY_SET) { - int val = *(int *)arg; - opts->fullscreen = val; - if (mpctx->video_out->config_ok) - vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0); - return opts->fullscreen == val ? M_PROPERTY_OK : M_PROPERTY_ERROR; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -#define VF_DEINTERLACE_LABEL "deinterlace" - -static const char *deint_filters[] = { -#ifdef CONFIG_VF_LAVFI - "lavfi=yadif", -#endif - "yadif", -#if CONFIG_VAAPI_VPP - "vavpp", -#endif - NULL -}; - -static int probe_deint_filters(struct MPContext *mpctx, const char *cmd) -{ - for (int n = 0; deint_filters[n]; n++) { - char filter[80]; - // add a label so that removing the filter is easier - snprintf(filter, sizeof(filter), "@%s:%s", VF_DEINTERLACE_LABEL, - deint_filters[n]); - if (edit_filters(mpctx, STREAM_VIDEO, cmd, filter) >= 0) - return 0; - } - return -1; -} - -static int get_deinterlacing(struct MPContext *mpctx) -{ - vf_instance_t *vf = mpctx->sh_video->vfilter; - int enabled = 0; - if (vf->control(vf, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK) - enabled = -1; - if (enabled < 0) { - // vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE - if (vf_find_by_label(vf, VF_DEINTERLACE_LABEL)) - enabled = 1; - } - return enabled; -} - -static void set_deinterlacing(struct MPContext *mpctx, bool enable) -{ - vf_instance_t *vf = mpctx->sh_video->vfilter; - if (vf_find_by_label(vf, VF_DEINTERLACE_LABEL)) { - if (!enable) - edit_filters(mpctx, STREAM_VIDEO, "del", "@" VF_DEINTERLACE_LABEL); - } else { - if ((get_deinterlacing(mpctx) > 0) != enable) { - int arg = enable; - if (vf->control(vf, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK) - probe_deint_filters(mpctx, "pre"); - } - } - mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0; -} - -static int mp_property_deinterlace(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->sh_video || !mpctx->sh_video->vfilter) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_GET: - *(int *)arg = get_deinterlacing(mpctx) > 0; - return M_PROPERTY_OK; - case M_PROPERTY_SET: - set_deinterlacing(mpctx, *(int *)arg); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -// Generic option + requires hard refresh to make changes take effect. -static int video_refresh_property_helper(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - int r = mp_property_generic_option(prop, action, arg, mpctx); - if (action == M_PROPERTY_SET) { - if (mpctx->sh_video) { - reinit_video_filters(mpctx); - mp_force_video_refresh(mpctx); - } - } - return r; -} - -static int mp_property_colormatrix(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action != M_PROPERTY_PRINT) - return video_refresh_property_helper(prop, action, arg, mpctx); - - struct MPOpts *opts = mpctx->opts; - - struct mp_csp_details vo_csp = {0}; - if (mpctx->video_out) - vo_control(mpctx->video_out, VOCTRL_GET_YUV_COLORSPACE, &vo_csp); - - struct mp_image_params vd_csp = {0}; - if (mpctx->sh_video) - vd_control(mpctx->sh_video, VDCTRL_GET_PARAMS, &vd_csp); - - char *res = talloc_asprintf(NULL, "%s", - mp_csp_names[opts->requested_colorspace]); - if (!vo_csp.format) { - res = talloc_asprintf_append(res, " (VO: unknown)"); - } else if (vo_csp.format != opts->requested_colorspace) { - res = talloc_asprintf_append(res, " (VO: %s)", - mp_csp_names[vo_csp.format]); - } - if (!vd_csp.colorspace) { - res = talloc_asprintf_append(res, " (VD: unknown)"); - } else if (!vo_csp.format || vd_csp.colorspace != vo_csp.format) { - res = talloc_asprintf_append(res, " (VD: %s)", - mp_csp_names[vd_csp.colorspace]); - } - *(char **)arg = res; - return M_PROPERTY_OK; -} - -static int mp_property_colormatrix_input_range(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (action != M_PROPERTY_PRINT) - return video_refresh_property_helper(prop, action, arg, mpctx); - - struct MPOpts *opts = mpctx->opts; - - struct mp_csp_details vo_csp = {0}; - if (mpctx->video_out) - vo_control(mpctx->video_out, VOCTRL_GET_YUV_COLORSPACE, &vo_csp ); - - struct mp_image_params vd_csp = {0}; - if (mpctx->sh_video) - vd_control(mpctx->sh_video, VDCTRL_GET_PARAMS, &vd_csp); - - char *res = talloc_asprintf(NULL, "%s", - mp_csp_levels_names[opts->requested_input_range]); - if (!vo_csp.levels_in) { - res = talloc_asprintf_append(res, " (VO: unknown)"); - } else if (vo_csp.levels_in != opts->requested_input_range) { - res = talloc_asprintf_append(res, " (VO: %s)", - mp_csp_levels_names[vo_csp.levels_in]); - } - if (!vd_csp.colorlevels) { - res = talloc_asprintf_append(res, " (VD: unknown)"); - } else if (!vo_csp.levels_in || vd_csp.colorlevels != vo_csp.levels_in) { - res = talloc_asprintf_append(res, " (VD: %s)", - mp_csp_levels_names[vd_csp.colorlevels]); - } - *(char **)arg = res; - return M_PROPERTY_OK; -} - -static int mp_property_colormatrix_output_range(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (action != M_PROPERTY_PRINT) - return video_refresh_property_helper(prop, action, arg, mpctx); - - struct MPOpts *opts = mpctx->opts; - - int req = opts->requested_output_range; - struct mp_csp_details actual = {0}; - if (mpctx->video_out) - vo_control(mpctx->video_out, VOCTRL_GET_YUV_COLORSPACE, &actual); - - char *res = talloc_asprintf(NULL, "%s", mp_csp_levels_names[req]); - if (!actual.levels_out) { - res = talloc_asprintf_append(res, " (Actual: unknown)"); - } else if (actual.levels_out != req) { - res = talloc_asprintf_append(res, " (Actual: %s)", - mp_csp_levels_names[actual.levels_out]); - } - *(char **)arg = res; - return M_PROPERTY_OK; -} - -// Update options which are managed through VOCTRL_GET/SET_PANSCAN. -static int panscan_property_helper(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - - if (!mpctx->video_out - || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE) - return M_PROPERTY_UNAVAILABLE; - - int r = mp_property_generic_option(prop, action, arg, mpctx); - if (action == M_PROPERTY_SET) - vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL); - return r; -} - -/// Helper to set vo flags. -/** \ingroup PropertyImplHelper - */ -static int mp_property_vo_flag(m_option_t *prop, int action, void *arg, - int vo_ctrl, int *vo_var, MPContext *mpctx) -{ - - if (!mpctx->video_out) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_SET) { - if (*vo_var == !!*(int *) arg) - return M_PROPERTY_OK; - if (mpctx->video_out->config_ok) - vo_control(mpctx->video_out, vo_ctrl, 0); - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Window always on top (RW) -static int mp_property_ontop(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP, - &mpctx->opts->vo.ontop, mpctx); -} - -/// Show window borders (RW) -static int mp_property_border(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER, - &mpctx->opts->vo.border, mpctx); -} - -static int mp_property_framedrop(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - - return mp_property_generic_option(prop, action, arg, mpctx); -} - -static int mp_property_video_color(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SET: { - if (set_video_colors(mpctx->sh_video, prop->name, *(int *) arg) <= 0) - return M_PROPERTY_UNAVAILABLE; - break; - } - case M_PROPERTY_GET: - if (get_video_colors(mpctx->sh_video, prop->name, (int *)arg) <= 0) - return M_PROPERTY_UNAVAILABLE; - // Write new value to option variable - mp_property_generic_option(prop, M_PROPERTY_SET, arg, mpctx); - return M_PROPERTY_OK; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Video codec tag (RO) -static int mp_property_video_format(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - const char *c = mpctx->sh_video ? mpctx->sh_video->gsh->codec : NULL; - return m_property_strdup_ro(prop, action, arg, c); -} - -/// Video codec name (RO) -static int mp_property_video_codec(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - const char *c = mpctx->sh_video ? mpctx->sh_video->gsh->decoder_desc : NULL; - return m_property_strdup_ro(prop, action, arg, c); -} - - -/// Video bitrate (RO) -static int mp_property_video_bitrate(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - if (action == M_PROPERTY_PRINT) { - *(char **)arg = format_bitrate(mpctx->sh_video->i_bps); - return M_PROPERTY_OK; - } - return m_property_int_ro(prop, action, arg, mpctx->sh_video->i_bps); -} - -/// Video display width (RO) -static int mp_property_width(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct sh_video *sh = mpctx->sh_video; - if (!sh) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, - sh->vf_input ? sh->vf_input->w : sh->disp_w); -} - -/// Video display height (RO) -static int mp_property_height(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct sh_video *sh = mpctx->sh_video; - if (!sh) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, - sh->vf_input ? sh->vf_input->h : sh->disp_h); -} - -static int property_vo_wh(m_option_t *prop, int action, void *arg, - MPContext *mpctx, bool get_w) -{ - struct vo *vo = mpctx->video_out; - if (!mpctx->sh_video && !vo || !vo->hasframe) - return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(prop, action, arg, - get_w ? vo->aspdat.prew : vo->aspdat.preh); -} - -static int mp_property_dwidth(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_vo_wh(prop, action, arg, mpctx, true); -} - -static int mp_property_dheight(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_vo_wh(prop, action, arg, mpctx, false); -} - -static int mp_property_osd_w(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return m_property_int_ro(prop, action, arg, mpctx->osd->last_vo_res.w); -} - -static int mp_property_osd_h(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return m_property_int_ro(prop, action, arg, mpctx->osd->last_vo_res.w); -} - -static int mp_property_osd_par(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return m_property_double_ro(prop, action, arg, - mpctx->osd->last_vo_res.display_par); -} - -/// Video fps (RO) -static int mp_property_fps(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps); -} - -/// Video aspect (RO) -static int mp_property_aspect(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct sh_video *sh_video = mpctx->sh_video; - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_SET: { - mpctx->opts->movie_aspect = *(float *)arg; - reinit_video_filters(mpctx); - mp_force_video_refresh(mpctx); - return M_PROPERTY_OK; - } - case M_PROPERTY_GET: { - float aspect = -1; - struct mp_image_params *params = sh_video->vf_input; - if (params && params->d_w && params->d_h) { - aspect = (float)params->d_w / params->d_h; - } else if (sh_video->disp_w && sh_video->disp_h) { - aspect = (float)sh_video->disp_w / sh_video->disp_h; - } - if (aspect <= 0) - return M_PROPERTY_UNAVAILABLE; - *(float *)arg = aspect; - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -// For OSD and subtitle related properties using the generic option bridge. -// - Fail as unavailable if no video is active -// - Trigger OSD state update when property is set -static int property_osd_helper(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (!mpctx->video_out) - return M_PROPERTY_UNAVAILABLE; - if (action == M_PROPERTY_SET) - osd_changed_all(mpctx->osd); - return mp_property_generic_option(prop, action, arg, mpctx); -} - -/// Selected subtitles (RW) -static int mp_property_sub(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_switch_track(prop, action, arg, mpctx, STREAM_SUB); -} - -/// Subtitle delay (RW) -static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - if (!mpctx->video_out) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_PRINT: - *(char **)arg = format_delay(opts->sub_delay); - return M_PROPERTY_OK; - } - return property_osd_helper(prop, action, arg, mpctx); -} - -static int mp_property_sub_pos(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct MPOpts *opts = mpctx->opts; - if (!mpctx->video_out) - return M_PROPERTY_UNAVAILABLE; - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos); - return M_PROPERTY_OK; - } - return property_osd_helper(prop, action, arg, mpctx); -} - -#ifdef CONFIG_TV - -static tvi_handle_t *get_tvh(struct MPContext *mpctx) -{ - if (!(mpctx->master_demuxer && mpctx->master_demuxer->type == DEMUXER_TYPE_TV)) - return NULL; - return mpctx->master_demuxer->priv; -} - -/// TV color settings (RW) -static int mp_property_tv_color(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - tvi_handle_t *tvh = get_tvh(mpctx); - if (!tvh) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SET: - return tv_set_color_options(tvh, prop->offset, *(int *) arg); - case M_PROPERTY_GET: - return tv_get_color_options(tvh, prop->offset, arg); - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -#endif - -static int mp_property_playlist_pos(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - struct playlist *pl = mpctx->playlist; - if (!pl->first) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_GET: { - int pos = playlist_entry_to_index(pl, pl->current); - if (pos < 0) - return M_PROPERTY_UNAVAILABLE; - *(int *)arg = pos; - return M_PROPERTY_OK; - } - case M_PROPERTY_SET: { - struct playlist_entry *e = playlist_entry_from_index(pl, *(int *)arg); - if (!e) - return M_PROPERTY_ERROR; - mp_set_playlist_entry(mpctx, e); - return M_PROPERTY_OK; - } - case M_PROPERTY_GET_TYPE: { - struct m_option opt = { - .name = prop->name, - .type = CONF_TYPE_INT, - .flags = CONF_RANGE, - .min = 0, - .max = playlist_entry_count(pl) - 1, - }; - *(struct m_option *)arg = opt; - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_playlist_count(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action == M_PROPERTY_GET) { - *(int *)arg = playlist_entry_count(mpctx->playlist); - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_playlist(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action == M_PROPERTY_GET) { - char *res = talloc_strdup(NULL, ""); - - for (struct playlist_entry *e = mpctx->playlist->first; e; e = e->next) - { - if (mpctx->playlist->current == e) { - res = talloc_asprintf_append(res, "> %s <\n", e->filename); - } else { - res = talloc_asprintf_append(res, "%s\n", e->filename); - } - } - - *(char **)arg = res; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static char *print_obj_osd_list(struct m_obj_settings *list) -{ - char *res = NULL; - for (int n = 0; list && list[n].name; n++) { - res = talloc_asprintf_append(res, "%s [", list[n].name); - for (int i = 0; list[n].attribs && list[n].attribs[i]; i += 2) { - res = talloc_asprintf_append(res, "%s%s=%s", i > 0 ? " " : "", - list[n].attribs[i], - list[n].attribs[i + 1]); - } - res = talloc_asprintf_append(res, "]\n"); - } - if (!res) - res = talloc_strdup(NULL, "(empty)"); - return res; -} - -static int property_filter(m_option_t *prop, int action, void *arg, - MPContext *mpctx, enum stream_type mt) -{ - switch (action) { - case M_PROPERTY_PRINT: { - struct m_config_option *opt = m_config_get_co(mpctx->mconfig, - bstr0(prop->name)); - *(char **)arg = print_obj_osd_list(*(struct m_obj_settings **)opt->data); - return M_PROPERTY_OK; - } - case M_PROPERTY_SET: - return set_filters(mpctx, mt, *(struct m_obj_settings **)arg) >= 0 - ? M_PROPERTY_OK : M_PROPERTY_ERROR; - } - return mp_property_generic_option(prop, action, arg, mpctx); -} - -static int mp_property_vf(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_filter(prop, action, arg, mpctx, STREAM_VIDEO); -} - -static int mp_property_af(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - return property_filter(prop, action, arg, mpctx, STREAM_AUDIO); -} - -static int mp_property_alias(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - const char *real_property = prop->priv; - int r = mp_property_do(real_property, action, arg, mpctx); - if (action == M_PROPERTY_GET_TYPE && r >= 0) { - // Fix the property name - struct m_option *type = arg; - type->name = prop->name; - } - return r; -} - -static int mp_property_options(m_option_t *prop, int action, void *arg, - MPContext *mpctx) -{ - if (action != M_PROPERTY_KEY_ACTION) - return M_PROPERTY_NOT_IMPLEMENTED; - - struct m_property_action_arg *ka = arg; - - struct m_config_option *opt = m_config_get_co(mpctx->mconfig, - bstr0(ka->key)); - if (!opt) - return M_PROPERTY_UNKNOWN; - if (!opt->data) - return M_PROPERTY_UNAVAILABLE; - - switch (ka->action) { - case M_PROPERTY_GET: - m_option_copy(opt->opt, ka->arg, opt->data); - return M_PROPERTY_OK; - case M_PROPERTY_SET: - if (!(mpctx->initialized_flags & INITIALIZED_PLAYBACK) && - !(opt->opt->flags & (M_OPT_PRE_PARSE | M_OPT_GLOBAL))) - { - m_option_copy(opt->opt, opt->data, ka->arg); - return M_PROPERTY_OK; - } - return M_PROPERTY_ERROR; - case M_PROPERTY_GET_TYPE: - *(struct m_option *)ka->arg = *opt->opt; - return M_PROPERTY_OK; - } - - return M_PROPERTY_NOT_IMPLEMENTED; -} - -// Use option-to-property-bridge. (The property and option have the same names.) -#define M_OPTION_PROPERTY(name) \ - {(name), mp_property_generic_option, &m_option_type_dummy, 0, 0, 0, (name)} - -// OPTION_PROPERTY(), but with a custom property handler. The custom handler -// must let unknown operations fall back to mp_property_generic_option(). -#define M_OPTION_PROPERTY_CUSTOM(name, handler) \ - {(name), (handler), &m_option_type_dummy, 0, 0, 0, (name)} -#define M_OPTION_PROPERTY_CUSTOM_(name, handler, ...) \ - {(name), (handler), &m_option_type_dummy, 0, 0, 0, (name), __VA_ARGS__} - -// Redirect a property name to another -#define M_PROPERTY_ALIAS(name, real_property) \ - {(name), mp_property_alias, &m_option_type_dummy, 0, 0, 0, (real_property)} - -/// All properties available in MPlayer. -/** \ingroup Properties - */ -static const m_option_t mp_properties[] = { - // General - M_OPTION_PROPERTY("osd-level"), - M_OPTION_PROPERTY_CUSTOM("osd-scale", property_osd_helper), - M_OPTION_PROPERTY("loop"), - M_OPTION_PROPERTY_CUSTOM("speed", mp_property_playback_speed), - { "filename", mp_property_filename, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "path", mp_property_path, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "media-title", mp_property_media_title, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "stream-path", mp_property_stream_path, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - M_OPTION_PROPERTY_CUSTOM("stream-capture", mp_property_stream_capture), - { "demuxer", mp_property_demuxer, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "stream-pos", mp_property_stream_pos, CONF_TYPE_INT64, - M_OPT_MIN, 0, 0, NULL }, - { "stream-start", mp_property_stream_start, CONF_TYPE_INT64, - M_OPT_MIN, 0, 0, NULL }, - { "stream-end", mp_property_stream_end, CONF_TYPE_INT64, - M_OPT_MIN, 0, 0, NULL }, - { "stream-length", mp_property_stream_length, CONF_TYPE_INT64, - M_OPT_MIN, 0, 0, NULL }, - { "stream-time-pos", mp_property_stream_time_pos, CONF_TYPE_TIME, - M_OPT_MIN, 0, 0, NULL }, - { "length", mp_property_length, CONF_TYPE_TIME, - M_OPT_MIN, 0, 0, NULL }, - { "avsync", mp_property_avsync, CONF_TYPE_DOUBLE }, - { "percent-pos", mp_property_percent_pos, CONF_TYPE_DOUBLE, - M_OPT_RANGE, 0, 100, NULL }, - { "time-pos", mp_property_time_pos, CONF_TYPE_TIME, - M_OPT_MIN, 0, 0, NULL }, - { "time-remaining", mp_property_remaining, CONF_TYPE_TIME }, - { "chapter", mp_property_chapter, CONF_TYPE_INT, - M_OPT_MIN, -1, 0, NULL }, - M_OPTION_PROPERTY_CUSTOM("edition", mp_property_edition), - M_OPTION_PROPERTY_CUSTOM("quvi-format", mp_property_quvi_format), - { "titles", mp_property_titles, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "chapters", mp_property_chapters, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "editions", mp_property_editions, CONF_TYPE_INT }, - { "angle", mp_property_angle, &m_option_type_dummy }, - { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST }, - { "chapter-metadata", mp_property_chapter_metadata, CONF_TYPE_STRING_LIST }, - M_OPTION_PROPERTY_CUSTOM("pause", mp_property_pause), - { "cache", mp_property_cache, CONF_TYPE_INT }, - M_OPTION_PROPERTY("pts-association-mode"), - M_OPTION_PROPERTY("hr-seek"), - { "clock", mp_property_clock, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - - { "chapter-list", mp_property_list_chapters, CONF_TYPE_STRING }, - { "track-list", property_list_tracks, CONF_TYPE_STRING }, - - { "playlist", mp_property_playlist, CONF_TYPE_STRING }, - { "playlist-pos", mp_property_playlist_pos, CONF_TYPE_INT }, - { "playlist-count", mp_property_playlist_count, CONF_TYPE_INT }, - - // Audio - { "volume", mp_property_volume, CONF_TYPE_FLOAT, - M_OPT_RANGE, 0, 100, NULL }, - { "mute", mp_property_mute, CONF_TYPE_FLAG, - M_OPT_RANGE, 0, 1, NULL }, - M_OPTION_PROPERTY_CUSTOM("audio-delay", mp_property_audio_delay), - { "audio-format", mp_property_audio_format, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "audio-codec", mp_property_audio_codec, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "audio-bitrate", mp_property_audio_bitrate, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "samplerate", mp_property_samplerate, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "channels", mp_property_channels, CONF_TYPE_INT, - 0, 0, 0, NULL }, - M_OPTION_PROPERTY_CUSTOM("aid", mp_property_audio), - { "balance", mp_property_balance, CONF_TYPE_FLOAT, - M_OPT_RANGE, -1, 1, NULL }, - M_OPTION_PROPERTY_CUSTOM("volume-restore-data", mp_property_volrestore), - - // Video - M_OPTION_PROPERTY_CUSTOM("fullscreen", mp_property_fullscreen), - { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG, - M_OPT_RANGE, 0, 1, NULL }, - M_OPTION_PROPERTY_CUSTOM("colormatrix", mp_property_colormatrix), - M_OPTION_PROPERTY_CUSTOM("colormatrix-input-range", - mp_property_colormatrix_input_range), - M_OPTION_PROPERTY_CUSTOM("colormatrix-output-range", - mp_property_colormatrix_output_range), - M_OPTION_PROPERTY_CUSTOM("ontop", mp_property_ontop), - M_OPTION_PROPERTY_CUSTOM("border", mp_property_border), - M_OPTION_PROPERTY_CUSTOM("framedrop", mp_property_framedrop), - M_OPTION_PROPERTY_CUSTOM("gamma", mp_property_video_color), - M_OPTION_PROPERTY_CUSTOM("brightness", mp_property_video_color), - M_OPTION_PROPERTY_CUSTOM("contrast", mp_property_video_color), - M_OPTION_PROPERTY_CUSTOM("saturation", mp_property_video_color), - M_OPTION_PROPERTY_CUSTOM("hue", mp_property_video_color), - M_OPTION_PROPERTY_CUSTOM("panscan", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-zoom", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-align-x", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-align-y", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-pan-x", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-pan-y", panscan_property_helper), - M_OPTION_PROPERTY_CUSTOM("video-unscaled", panscan_property_helper), - { "video-format", mp_property_video_format, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "video-codec", mp_property_video_codec, CONF_TYPE_STRING, - 0, 0, 0, NULL }, - { "video-bitrate", mp_property_video_bitrate, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "width", mp_property_width, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "height", mp_property_height, CONF_TYPE_INT, - 0, 0, 0, NULL }, - { "dwidth", mp_property_dwidth, CONF_TYPE_INT }, - { "dheight", mp_property_dheight, CONF_TYPE_INT }, - { "fps", mp_property_fps, CONF_TYPE_FLOAT, - 0, 0, 0, NULL }, - { "aspect", mp_property_aspect, CONF_TYPE_FLOAT, - CONF_RANGE, -1, 10, NULL }, - M_OPTION_PROPERTY_CUSTOM("vid", mp_property_video), - { "program", mp_property_program, CONF_TYPE_INT, - CONF_RANGE, -1, 65535, NULL }, - - { "osd-width", mp_property_osd_w, CONF_TYPE_INT }, - { "osd-height", mp_property_osd_h, CONF_TYPE_INT }, - { "osd-par", mp_property_osd_par, CONF_TYPE_DOUBLE }, - - // Subs - M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub), - M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay), - M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos), - M_OPTION_PROPERTY_CUSTOM("sub-visibility", property_osd_helper), - M_OPTION_PROPERTY_CUSTOM("sub-forced-only", property_osd_helper), - M_OPTION_PROPERTY_CUSTOM("sub-scale", property_osd_helper), -#ifdef CONFIG_ASS - M_OPTION_PROPERTY_CUSTOM("ass-use-margins", property_osd_helper), - M_OPTION_PROPERTY_CUSTOM("ass-vsfilter-aspect-compat", property_osd_helper), - M_OPTION_PROPERTY_CUSTOM("ass-style-override", property_osd_helper), -#endif - - M_OPTION_PROPERTY_CUSTOM("vf*", mp_property_vf), - M_OPTION_PROPERTY_CUSTOM("af*", mp_property_af), - -#ifdef CONFIG_TV - { "tv-brightness", mp_property_tv_color, CONF_TYPE_INT, - M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS }, - { "tv-contrast", mp_property_tv_color, CONF_TYPE_INT, - M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST }, - { "tv-saturation", mp_property_tv_color, CONF_TYPE_INT, - M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION }, - { "tv-hue", mp_property_tv_color, CONF_TYPE_INT, - M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE }, -#endif - - M_PROPERTY_ALIAS("video", "vid"), - M_PROPERTY_ALIAS("audio", "aid"), - M_PROPERTY_ALIAS("sub", "sid"), - - { "options", mp_property_options, &m_option_type_dummy }, - - {0}, -}; - -const struct m_option *mp_get_property_list(void) -{ - return mp_properties; -} - -int mp_property_do(const char *name, int action, void *val, - struct MPContext *ctx) -{ - return m_property_do(mp_properties, name, action, val, ctx); -} - -char *mp_property_expand_string(struct MPContext *mpctx, const char *str) -{ - return m_properties_expand_string(mp_properties, str, mpctx); -} - -void property_print_help(void) -{ - m_properties_print_help_list(mp_properties); -} - - -/* List of default ways to show a property on OSD. - * - * If osd_progbar is set, a bar showing the current position between min/max - * values of the property is shown. In this case osd_msg is only used for - * terminal output if there is no video; it'll be a label shown together with - * percentage. - */ -static struct property_osd_display { - // property name - const char *name; - // name used on OSD - const char *osd_name; - // progressbar type - int osd_progbar; - // osd msg id if it must be shared - int osd_id; - // Needs special ways to display the new value (seeks are delayed) - int seek_msg, seek_bar; - // Free-form message (if NULL, osd_name or the property name is used) - const char *msg; - // Extra free-from message (just for volume) - const char *extra_msg; -} property_osd_display[] = { - // general - { "loop", _("Loop") }, - { "chapter", .seek_msg = OSD_SEEK_INFO_CHAPTER_TEXT, - .seek_bar = OSD_SEEK_INFO_BAR }, - { "edition", .seek_msg = OSD_SEEK_INFO_EDITION }, - { "pts-association-mode", "PTS association mode" }, - { "hr-seek", "hr-seek" }, - { "speed", _("Speed") }, - { "clock", _("Clock") }, - // audio - { "volume", _("Volume"), - .extra_msg = "${?mute==yes:(Muted)}", .osd_progbar = OSD_VOLUME }, - { "mute", _("Mute") }, - { "audio-delay", _("A-V delay") }, - { "audio", _("Audio") }, - { "balance", _("Balance"), .osd_progbar = OSD_BALANCE }, - // video - { "panscan", _("Panscan"), .osd_progbar = OSD_PANSCAN }, - { "ontop", _("Stay on top") }, - { "border", _("Border") }, - { "framedrop", _("Framedrop") }, - { "deinterlace", _("Deinterlace") }, - { "colormatrix", _("YUV colormatrix") }, - { "colormatrix-input-range", _("YUV input range") }, - { "colormatrix-output-range", _("RGB output range") }, - { "gamma", _("Gamma"), .osd_progbar = OSD_BRIGHTNESS }, - { "brightness", _("Brightness"), .osd_progbar = OSD_BRIGHTNESS }, - { "contrast", _("Contrast"), .osd_progbar = OSD_CONTRAST }, - { "saturation", _("Saturation"), .osd_progbar = OSD_SATURATION }, - { "hue", _("Hue"), .osd_progbar = OSD_HUE }, - { "angle", _("Angle") }, - // subs - { "sub", _("Subtitles") }, - { "sub-pos", _("Sub position") }, - { "sub-delay", _("Sub delay"), .osd_id = OSD_MSG_SUB_DELAY }, - { "sub-visibility", _("Subtitles") }, - { "sub-forced-only", _("Forced sub only") }, - { "sub-scale", _("Sub Scale")}, - { "ass-vsfilter-aspect-compat", _("Subtitle VSFilter aspect compat")}, - { "ass-style-override", _("ASS subtitle style override")}, - { "vf*", _("Video filters"), .msg = "Video filters:\n${vf}"}, - { "af*", _("Audio filters"), .msg = "Audio filters:\n${af}"}, -#ifdef CONFIG_TV - { "tv-brightness", _("Brightness"), .osd_progbar = OSD_BRIGHTNESS }, - { "tv-hue", _("Hue"), .osd_progbar = OSD_HUE}, - { "tv-saturation", _("Saturation"), .osd_progbar = OSD_SATURATION }, - { "tv-contrast", _("Contrast"), .osd_progbar = OSD_CONTRAST }, -#endif - {0} -}; - -static void show_property_osd(MPContext *mpctx, const char *pname, - enum mp_on_osd osd_mode) -{ - struct MPOpts *opts = mpctx->opts; - struct m_option prop = {0}; - struct property_osd_display *p; - - if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0) - return; - - int osd_progbar = 0; - const char *osd_name = NULL; - const char *msg = NULL; - const char *extra_msg = NULL; - - // look for the command - for (p = property_osd_display; p->name; p++) { - if (!strcmp(p->name, prop.name)) { - osd_progbar = p->seek_bar ? 1 : p->osd_progbar; - osd_name = p->seek_msg ? "" : mp_gtext(p->osd_name); - break; - } - } - if (!p->name) - p = NULL; - - if (p) { - msg = p->msg; - extra_msg = p->extra_msg; - } - - if (osd_mode != MP_ON_OSD_AUTO) { - osd_name = osd_name ? osd_name : prop.name; - if (!(osd_mode & MP_ON_OSD_MSG)) { - osd_name = NULL; - msg = NULL; - extra_msg = NULL; - } - osd_progbar = osd_progbar ? osd_progbar : ' '; - if (!(osd_mode & MP_ON_OSD_BAR)) - osd_progbar = 0; - } - - if (p && (p->seek_msg || p->seek_bar)) { - mpctx->add_osd_seek_info |= - (osd_name ? p->seek_msg : 0) | (osd_progbar ? p->seek_bar : 0); - return; - } - - void *tmp = talloc_new(NULL); - - if (!msg && osd_name) - msg = talloc_asprintf(tmp, "%s: ${%s}", osd_name, prop.name); - - if (osd_progbar && (prop.flags & CONF_RANGE) == CONF_RANGE) { - bool ok = false; - if (prop.type == CONF_TYPE_INT) { - int i; - ok = mp_property_do(prop.name, M_PROPERTY_GET, &i, mpctx) > 0; - if (ok) - set_osd_bar(mpctx, osd_progbar, osd_name, prop.min, prop.max, i); - } else if (prop.type == CONF_TYPE_FLOAT) { - float f; - ok = mp_property_do(prop.name, M_PROPERTY_GET, &f, mpctx) > 0; - if (ok) - set_osd_bar(mpctx, osd_progbar, osd_name, prop.min, prop.max, f); - } - if (ok && osd_mode == MP_ON_OSD_AUTO && opts->osd_bar_visible) - msg = NULL; - } - - char *osd_msg = NULL; - if (msg) - osd_msg = talloc_steal(tmp, mp_property_expand_string(mpctx, msg)); - if (extra_msg) { - char *t = talloc_steal(tmp, mp_property_expand_string(mpctx, extra_msg)); - osd_msg = talloc_asprintf(tmp, "%s%s%s", osd_msg ? osd_msg : "", - osd_msg && osd_msg[0] ? " " : "", t); - } - - if (osd_msg && osd_msg[0]) { - int osd_id = 0; - if (p) { - int index = p - property_osd_display; - osd_id = p->osd_id ? p->osd_id : OSD_MSG_PROPERTY + index; - } - set_osd_tmsg(mpctx, osd_id, 1, opts->osd_duration, "%s", osd_msg); - } - - talloc_free(tmp); -} - -static const char *property_error_string(int error_value) -{ - switch (error_value) { - case M_PROPERTY_ERROR: - return "ERROR"; - case M_PROPERTY_UNAVAILABLE: - return "PROPERTY_UNAVAILABLE"; - case M_PROPERTY_NOT_IMPLEMENTED: - return "NOT_IMPLEMENTED"; - case M_PROPERTY_UNKNOWN: - return "PROPERTY_UNKNOWN"; - } - return "UNKNOWN"; -} - -static bool reinit_filters(MPContext *mpctx, enum stream_type mediatype) -{ - switch (mediatype) { - case STREAM_VIDEO: - return reinit_video_filters(mpctx) >= 0; - case STREAM_AUDIO: - return reinit_audio_filters(mpctx) >= 0; - } - return false; -} - -static const char *filter_opt[STREAM_TYPE_COUNT] = { - [STREAM_VIDEO] = "vf", - [STREAM_AUDIO] = "af", -}; - -static int set_filters(struct MPContext *mpctx, enum stream_type mediatype, - struct m_obj_settings *new_chain) -{ - bstr option = bstr0(filter_opt[mediatype]); - struct m_config_option *co = m_config_get_co(mpctx->mconfig, option); - if (!co) - return -1; - - struct m_obj_settings **list = co->data; - struct m_obj_settings *old_settings = *list; - *list = NULL; - m_option_copy(co->opt, list, &new_chain); - - bool success = reinit_filters(mpctx, mediatype); - - if (success) { - m_option_free(co->opt, &old_settings); - } else { - m_option_free(co->opt, list); - *list = old_settings; - reinit_filters(mpctx, mediatype); - } - - if (mediatype == STREAM_VIDEO) - mp_force_video_refresh(mpctx); - - return success ? 0 : -1; -} - -static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype, - const char *cmd, const char *arg) -{ - bstr option = bstr0(filter_opt[mediatype]); - struct m_config_option *co = m_config_get_co(mpctx->mconfig, option); - if (!co) - return -1; - - // The option parser is used to modify the filter list itself. - char optname[20]; - snprintf(optname, sizeof(optname), "%.*s-%s", BSTR_P(option), cmd); - - struct m_obj_settings *new_chain = NULL; - m_option_copy(co->opt, &new_chain, co->data); - - int r = m_option_parse(co->opt, bstr0(optname), bstr0(arg), &new_chain); - if (r >= 0) - r = set_filters(mpctx, mediatype, new_chain); - - m_option_free(co->opt, &new_chain); - - return r >= 0 ? 0 : -1; -} - -static int edit_filters_osd(struct MPContext *mpctx, enum stream_type mediatype, - const char *cmd, const char *arg, bool on_osd) -{ - int r = edit_filters(mpctx, mediatype, cmd, arg); - if (on_osd) { - if (r >= 0) { - const char *prop = filter_opt[mediatype]; - show_property_osd(mpctx, prop, MP_ON_OSD_MSG); - } else { - set_osd_tmsg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts->osd_duration, - "Changing filters failed!"); - } - } - return r; -} - -#ifdef HAVE_SYS_MMAN_H - -static int ext2_sub_find(struct MPContext *mpctx, int id) -{ - struct command_ctx *cmd = mpctx->command_ctx; - struct sub_bitmaps *sub = &mpctx->osd->external2; - void *p = NULL; - if (id >= 0 && id < OVERLAY_MAX_ID) - p = cmd->overlay_map[id]; - if (sub && p) { - for (int n = 0; n < sub->num_parts; n++) { - if (sub->parts[n].bitmap == p) - return n; - } - } - return -1; -} - -static int ext2_sub_alloc(struct MPContext *mpctx) -{ - struct osd_state *osd = mpctx->osd; - struct sub_bitmaps *sub = &osd->external2; - struct sub_bitmap b = {0}; - MP_TARRAY_APPEND(osd, sub->parts, sub->num_parts, b); - return sub->num_parts - 1; -} - -static int overlay_add(struct MPContext *mpctx, int id, int x, int y, - char *file, int offset, char *fmt, int w, int h, - int stride) -{ - struct command_ctx *cmd = mpctx->command_ctx; - struct osd_state *osd = mpctx->osd; - if (strcmp(fmt, "bgra") != 0) { - MP_ERR(mpctx, "overlay_add: unsupported OSD format '%s'\n", fmt); - return -1; - } - if (id < 0 || id >= OVERLAY_MAX_ID) { - MP_ERR(mpctx, "overlay_add: invalid id %d\n", id); - return -1; - } - int fd = -1; - bool close_fd = true; - if (file[0] == '@') { - char *end; - fd = strtol(&file[1], &end, 10); - if (!file[1] || end[0]) - fd = -1; - close_fd = false; - } else { - fd = open(file, O_RDONLY | O_BINARY); - } - void *p = mmap(NULL, h * stride, PROT_READ, MAP_SHARED, fd, offset); - if (fd >= 0 && close_fd) - close(fd); - if (!p) { - MP_ERR(mpctx, "overlay_add: could not open or map '%s'\n", file); - return -1; - } - int index = ext2_sub_find(mpctx, id); - if (index < 0) - index = ext2_sub_alloc(mpctx); - if (index < 0) { - munmap(p, h * stride); - return -1; - } - cmd->overlay_map[id] = p; - osd->external2.parts[index] = (struct sub_bitmap) { - .bitmap = p, - .stride = stride, - .x = x, .y = y, - .w = w, .h = h, - .dw = w, .dh = h, - }; - osd->external2.bitmap_id = osd->external2.bitmap_pos_id = 1; - osd->external2.format = SUBBITMAP_RGBA; - osd->want_redraw = true; - return 0; -} - -static void overlay_remove(struct MPContext *mpctx, int id) -{ - struct command_ctx *cmd = mpctx->command_ctx; - struct osd_state *osd = mpctx->osd; - int index = ext2_sub_find(mpctx, id); - if (index >= 0) { - struct sub_bitmaps *sub = &osd->external2; - struct sub_bitmap *part = &sub->parts[index]; - munmap(part->bitmap, part->h * part->stride); - MP_TARRAY_REMOVE_AT(sub->parts, sub->num_parts, index); - cmd->overlay_map[id] = NULL; - sub->bitmap_id = sub->bitmap_pos_id = 1; - } -} - -static void overlay_uninit(struct MPContext *mpctx) -{ - for (int id = 0; id < OVERLAY_MAX_ID; id++) - overlay_remove(mpctx, id); -} - -#else - -static void overlay_uninit(struct MPContext *mpctx){} - -#endif - -// Whether this property should react to key events generated by auto-repeat. -static bool check_property_autorepeat(char *property, struct MPContext *mpctx) -{ - struct m_option prop = {0}; - if (mp_property_do(property, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0) - return true; - - // This is a heuristic at best. - if (prop.type == &m_option_type_flag || prop.type == &m_option_type_choice) - return false; - - return true; -} - -void run_command(MPContext *mpctx, mp_cmd_t *cmd) -{ - struct MPOpts *opts = mpctx->opts; - int osd_duration = opts->osd_duration; - bool auto_osd = cmd->on_osd == MP_ON_OSD_AUTO; - bool msg_osd = auto_osd || (cmd->on_osd & MP_ON_OSD_MSG); - bool bar_osd = auto_osd || (cmd->on_osd & MP_ON_OSD_BAR); - bool msg_or_nobar_osd = msg_osd && !(auto_osd && opts->osd_bar_visible); - int osdl = msg_osd ? 1 : OSD_LEVEL_INVISIBLE; - - if (!cmd->raw_args) { - for (int n = 0; n < cmd->nargs; n++) { - if (cmd->args[n].type.type == CONF_TYPE_STRING) { - cmd->args[n].v.s = - mp_property_expand_string(mpctx, cmd->args[n].v.s); - if (!cmd->args[n].v.s) - return; - talloc_steal(cmd, cmd->args[n].v.s); - } - } - } - - switch (cmd->id) { - case MP_CMD_SEEK: { - double v = cmd->args[0].v.d * cmd->scale; - int abs = cmd->args[1].v.i; - int exact = cmd->args[2].v.i; - if (abs == 2) { // Absolute seek to a timestamp in seconds - queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact); - set_osd_function(mpctx, - v > get_current_time(mpctx) ? OSD_FFW : OSD_REW); - } else if (abs) { /* Absolute seek by percentage */ - queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact); - set_osd_function(mpctx, OSD_FFW); // Direction isn't set correctly - } else { - queue_seek(mpctx, MPSEEK_RELATIVE, v, exact); - set_osd_function(mpctx, (v > 0) ? OSD_FFW : OSD_REW); - } - if (bar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; - if (msg_or_nobar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; - break; - } - - case MP_CMD_SET: { - int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_SET_STRING, - cmd->args[1].v.s, mpctx); - if (r == M_PROPERTY_OK || r == M_PROPERTY_UNAVAILABLE) { - show_property_osd(mpctx, cmd->args[0].v.s, cmd->on_osd); - } else if (r == M_PROPERTY_UNKNOWN) { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, - "Unknown property: '%s'", cmd->args[0].v.s); - } else if (r <= 0) { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, - "Failed to set property '%s' to '%s'", - cmd->args[0].v.s, cmd->args[1].v.s); - } - break; - } - - case MP_CMD_ADD: - case MP_CMD_CYCLE: - { - struct m_property_switch_arg s = { - .inc = 1, - .wrap = cmd->id == MP_CMD_CYCLE, - }; - if (cmd->args[1].v.d) - s.inc = cmd->args[1].v.d * cmd->scale; - char *property = cmd->args[0].v.s; - if (cmd->repeated && !check_property_autorepeat(property, mpctx)) { - mp_msg(MSGT_CPLAYER, MSGL_V, - "Dropping command '%.*s' from auto-repeated key.\n", - BSTR_P(cmd->original)); - break; - } - int r = mp_property_do(property, M_PROPERTY_SWITCH, &s, mpctx); - if (r == M_PROPERTY_OK || r == M_PROPERTY_UNAVAILABLE) { - show_property_osd(mpctx, property, cmd->on_osd); - } else if (r == M_PROPERTY_UNKNOWN) { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, - "Unknown property: '%s'", property); - } else if (r <= 0) { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, - "Failed to increment property '%s' by %g", - property, s.inc); - } - break; - } - - case MP_CMD_GET_PROPERTY: { - char *tmp; - int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_GET_STRING, - &tmp, mpctx); - if (r <= 0) { - mp_msg(MSGT_CPLAYER, MSGL_WARN, - "Failed to get value of property '%s'.\n", - cmd->args[0].v.s); - mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", - property_error_string(r)); - break; - } - mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n", - cmd->args[0].v.s, tmp); - talloc_free(tmp); - break; - } - - case MP_CMD_SPEED_MULT: { - double v = cmd->args[0].v.d * cmd->scale; - v *= mpctx->opts->playback_speed; - mp_property_do("speed", M_PROPERTY_SET, &v, mpctx); - show_property_osd(mpctx, "speed", cmd->on_osd); - break; - } - - case MP_CMD_FRAME_STEP: - add_step_frame(mpctx, 1); - break; - - case MP_CMD_FRAME_BACK_STEP: - add_step_frame(mpctx, -1); - break; - - case MP_CMD_QUIT: - mpctx->stop_play = PT_QUIT; - mpctx->quit_custom_rc = cmd->args[0].v.i; - mpctx->has_quit_custom_rc = true; - break; - - case MP_CMD_QUIT_WATCH_LATER: - mp_write_watch_later_conf(mpctx); - mpctx->stop_play = PT_QUIT; - mpctx->quit_player_rc = 0; - break; - - case MP_CMD_PLAYLIST_NEXT: - case MP_CMD_PLAYLIST_PREV: - { - int dir = cmd->id == MP_CMD_PLAYLIST_PREV ? -1 : +1; - int force = cmd->args[0].v.i; - - struct playlist_entry *e = mp_next_file(mpctx, dir, force); - if (!e && !force) - break; - mpctx->playlist->current = e; - mpctx->playlist->current_was_replaced = false; - mpctx->stop_play = PT_CURRENT_ENTRY; - break; - } - - case MP_CMD_SUB_STEP: - case MP_CMD_SUB_SEEK: - if (mpctx->osd->dec_sub) { - double a[2]; - a[0] = mpctx->video_pts - mpctx->osd->video_offset + opts->sub_delay; - a[1] = cmd->args[0].v.i; - if (sub_control(mpctx->osd->dec_sub, SD_CTRL_SUB_STEP, a) > 0) { - if (cmd->id == MP_CMD_SUB_STEP) { - opts->sub_delay += a[0]; - osd_changed_all(mpctx->osd); - set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration, - "Sub delay: %d ms", ROUND(opts->sub_delay * 1000)); - } else { - // We can easily get stuck by failing to seek to the video - // frame which actually shows the sub first (because video - // frame PTS and sub PTS rarely match exactly). Add some - // rounding for the mess of it. - a[0] += 0.01 * (a[1] > 0 ? 1 : -1); - queue_seek(mpctx, MPSEEK_RELATIVE, a[0], 1); - set_osd_function(mpctx, (a[0] > 0) ? OSD_FFW : OSD_REW); - if (bar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; - if (msg_or_nobar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; - } - } - } - break; - - case MP_CMD_OSD: { - int v = cmd->args[0].v.i; - int max = (opts->term_osd && !mpctx->video_out) ? MAX_TERM_OSD_LEVEL - : MAX_OSD_LEVEL; - if (opts->osd_level > max) - opts->osd_level = max; - if (v < 0) - opts->osd_level = (opts->osd_level + 1) % (max + 1); - else - opts->osd_level = v > max ? max : v; - if (msg_osd && opts->osd_level <= 1) - set_osd_tmsg(mpctx, OSD_MSG_OSD_STATUS, 0, osd_duration, - "OSD: %s", opts->osd_level ? "yes" : "no"); - else - rm_osd_msg(mpctx, OSD_MSG_OSD_STATUS); - break; - } - - case MP_CMD_PRINT_TEXT: { - mp_msg(MSGT_GLOBAL, MSGL_INFO, "%s\n", cmd->args[0].v.s); - break; - } - - case MP_CMD_SHOW_TEXT: { - // if no argument supplied use default osd_duration, else ms. - set_osd_msg(mpctx, OSD_MSG_TEXT, cmd->args[2].v.i, - (cmd->args[1].v.i < 0 ? osd_duration : cmd->args[1].v.i), - "%s", cmd->args[0].v.s); - break; - } - - case MP_CMD_LOADFILE: { - char *filename = cmd->args[0].v.s; - bool append = cmd->args[1].v.i; - - if (!append) - playlist_clear(mpctx->playlist); - - playlist_add(mpctx->playlist, playlist_entry_new(filename)); - - if (!append) - mp_set_playlist_entry(mpctx, mpctx->playlist->first); - break; - } - - case MP_CMD_LOADLIST: { - char *filename = cmd->args[0].v.s; - bool append = cmd->args[1].v.i; - struct playlist *pl = playlist_parse_file(filename, opts); - if (pl) { - if (!append) - playlist_clear(mpctx->playlist); - playlist_transfer_entries(mpctx->playlist, pl); - talloc_free(pl); - - if (!append && mpctx->playlist->first) { - struct playlist_entry *e = - mp_resume_playlist(mpctx->playlist, opts); - mp_set_playlist_entry(mpctx, e ? e : mpctx->playlist->first); - } - } else { - mp_tmsg(MSGT_CPLAYER, MSGL_ERR, - "\nUnable to load playlist %s.\n", filename); - } - break; - } - - case MP_CMD_PLAYLIST_CLEAR: { - // Supposed to clear the playlist, except the currently played item. - if (mpctx->playlist->current_was_replaced) - mpctx->playlist->current = NULL; - while (mpctx->playlist->first) { - struct playlist_entry *e = mpctx->playlist->first; - if (e == mpctx->playlist->current) { - e = e->next; - if (!e) - break; - } - playlist_remove(mpctx->playlist, e); - } - break; - } - - case MP_CMD_PLAYLIST_REMOVE: { - struct playlist_entry *e = playlist_entry_from_index(mpctx->playlist, - cmd->args[0].v.i); - if (e) { - // Can't play a removed entry - if (mpctx->playlist->current == e) - mpctx->stop_play = PT_CURRENT_ENTRY; - playlist_remove(mpctx->playlist, e); - } - break; - } - - case MP_CMD_PLAYLIST_MOVE: { - struct playlist_entry *e1 = playlist_entry_from_index(mpctx->playlist, - cmd->args[0].v.i); - struct playlist_entry *e2 = playlist_entry_from_index(mpctx->playlist, - cmd->args[1].v.i); - if (e1) { - playlist_move(mpctx->playlist, e1, e2); - } - break; - } - - case MP_CMD_STOP: - // Go back to the starting point. - mpctx->stop_play = PT_STOP; - break; - - case MP_CMD_SHOW_PROGRESS: - mpctx->add_osd_seek_info |= - (msg_osd ? OSD_SEEK_INFO_TEXT : 0) | - (bar_osd ? OSD_SEEK_INFO_BAR : 0); - break; - -#ifdef CONFIG_RADIO - case MP_CMD_RADIO_STEP_CHANNEL: - if (mpctx->stream && mpctx->stream->type == STREAMTYPE_RADIO) { - int v = cmd->args[0].v.i; - if (v > 0) - radio_step_channel(mpctx->stream, RADIO_CHANNEL_HIGHER); - else - radio_step_channel(mpctx->stream, RADIO_CHANNEL_LOWER); - if (radio_get_channel_name(mpctx->stream)) { - set_osd_tmsg(mpctx, OSD_MSG_RADIO_CHANNEL, osdl, osd_duration, - "Channel: %s", - radio_get_channel_name(mpctx->stream)); - } - } - break; - - case MP_CMD_RADIO_SET_CHANNEL: - if (mpctx->stream && mpctx->stream->type == STREAMTYPE_RADIO) { - radio_set_channel(mpctx->stream, cmd->args[0].v.s); - if (radio_get_channel_name(mpctx->stream)) { - set_osd_tmsg(mpctx, OSD_MSG_RADIO_CHANNEL, osdl, osd_duration, - "Channel: %s", - radio_get_channel_name(mpctx->stream)); - } - } - break; - - case MP_CMD_RADIO_SET_FREQ: - if (mpctx->stream && mpctx->stream->type == STREAMTYPE_RADIO) - radio_set_freq(mpctx->stream, cmd->args[0].v.f); - break; - - case MP_CMD_RADIO_STEP_FREQ: - if (mpctx->stream && mpctx->stream->type == STREAMTYPE_RADIO) - radio_step_freq(mpctx->stream, cmd->args[0].v.f); - break; -#endif - -#ifdef CONFIG_TV - case MP_CMD_TV_START_SCAN: - if (get_tvh(mpctx)) - tv_start_scan(get_tvh(mpctx), 1); - break; - case MP_CMD_TV_SET_FREQ: - if (get_tvh(mpctx)) - tv_set_freq(get_tvh(mpctx), cmd->args[0].v.f * 16.0); -#ifdef CONFIG_PVR - else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) { - pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f)); - set_osd_msg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "%s: %s", - pvr_get_current_channelname(mpctx->stream), - pvr_get_current_stationname(mpctx->stream)); - } -#endif /* CONFIG_PVR */ - break; - - case MP_CMD_TV_STEP_FREQ: - if (get_tvh(mpctx)) - tv_step_freq(get_tvh(mpctx), cmd->args[0].v.f * 16.0); -#ifdef CONFIG_PVR - else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) { - pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f)); - set_osd_msg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "%s: f %d", - pvr_get_current_channelname(mpctx->stream), - pvr_get_current_frequency(mpctx->stream)); - } -#endif /* CONFIG_PVR */ - break; - - case MP_CMD_TV_SET_NORM: - if (get_tvh(mpctx)) - tv_set_norm(get_tvh(mpctx), cmd->args[0].v.s); - break; - - case MP_CMD_TV_STEP_CHANNEL: - if (get_tvh(mpctx)) { - int v = cmd->args[0].v.i; - if (v > 0) { - tv_step_channel(get_tvh(mpctx), TV_CHANNEL_HIGHER); - } else { - tv_step_channel(get_tvh(mpctx), TV_CHANNEL_LOWER); - } - if (tv_channel_list) { - set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, - "Channel: %s", tv_channel_current->name); - } - } -#ifdef CONFIG_PVR - else if (mpctx->stream && - mpctx->stream->type == STREAMTYPE_PVR) { - pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i); - set_osd_msg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "%s: %s", - pvr_get_current_channelname(mpctx->stream), - pvr_get_current_stationname(mpctx->stream)); - } -#endif /* CONFIG_PVR */ -#ifdef CONFIG_DVBIN - if (mpctx->stream->type == STREAMTYPE_DVB) { - int dir; - int v = cmd->args[0].v.i; - - mpctx->last_dvb_step = v; - if (v > 0) - dir = DVB_CHANNEL_HIGHER; - else - dir = DVB_CHANNEL_LOWER; - - - if (dvb_step_channel(mpctx->stream, dir)) { - mpctx->stop_play = PT_NEXT_ENTRY; - mpctx->dvbin_reopen = 1; - } - } -#endif /* CONFIG_DVBIN */ - break; - - case MP_CMD_TV_SET_CHANNEL: - if (get_tvh(mpctx)) { - tv_set_channel(get_tvh(mpctx), cmd->args[0].v.s); - if (tv_channel_list) { - set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, - "Channel: %s", tv_channel_current->name); - } - } -#ifdef CONFIG_PVR - else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) { - pvr_set_channel(mpctx->stream, cmd->args[0].v.s); - set_osd_msg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "%s: %s", - pvr_get_current_channelname(mpctx->stream), - pvr_get_current_stationname(mpctx->stream)); - } -#endif /* CONFIG_PVR */ - break; - -#ifdef CONFIG_DVBIN - case MP_CMD_DVB_SET_CHANNEL: - if (mpctx->stream->type == STREAMTYPE_DVB) { - mpctx->last_dvb_step = 1; - - if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i, - cmd->args[0].v.i)) { - mpctx->stop_play = PT_NEXT_ENTRY; - mpctx->dvbin_reopen = 1; - } - } - break; -#endif /* CONFIG_DVBIN */ - - case MP_CMD_TV_LAST_CHANNEL: - if (get_tvh(mpctx)) { - tv_last_channel(get_tvh(mpctx)); - if (tv_channel_list) { - set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, - "Channel: %s", tv_channel_current->name); - } - } -#ifdef CONFIG_PVR - else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) { - pvr_set_lastchannel(mpctx->stream); - set_osd_msg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "%s: %s", - pvr_get_current_channelname(mpctx->stream), - pvr_get_current_stationname(mpctx->stream)); - } -#endif /* CONFIG_PVR */ - break; - - case MP_CMD_TV_STEP_NORM: - if (get_tvh(mpctx)) - tv_step_norm(get_tvh(mpctx)); - break; - - case MP_CMD_TV_STEP_CHANNEL_LIST: - if (get_tvh(mpctx)) - tv_step_chanlist(get_tvh(mpctx)); - break; -#endif /* CONFIG_TV */ - - case MP_CMD_SUB_ADD: - mp_add_subtitles(mpctx, cmd->args[0].v.s); - break; - - case MP_CMD_SUB_REMOVE: { - struct track *sub = mp_track_by_tid(mpctx, STREAM_SUB, cmd->args[0].v.i); - if (sub) - mp_remove_track(mpctx, sub); - break; - } - - case MP_CMD_SUB_RELOAD: { - struct track *sub = mp_track_by_tid(mpctx, STREAM_SUB, cmd->args[0].v.i); - if (sub && sub->is_external && sub->external_filename) { - struct track *nsub = mp_add_subtitles(mpctx, sub->external_filename); - if (nsub) { - mp_remove_track(mpctx, sub); - mp_switch_track(mpctx, nsub->type, nsub); - } - } - break; - } - - case MP_CMD_SCREENSHOT: - screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i, msg_osd); - break; - - case MP_CMD_SCREENSHOT_TO_FILE: - screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd); - break; - - case MP_CMD_RUN: -#ifndef __MINGW32__ - if (!fork()) { - execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL); - exit(0); - } -#endif - break; - - case MP_CMD_KEYDOWN_EVENTS: - mp_input_put_key(mpctx->input, cmd->args[0].v.i); - break; - - case MP_CMD_ENABLE_INPUT_SECTION: - mp_input_enable_section(mpctx->input, cmd->args[0].v.s, - cmd->args[1].v.i == 1 ? MP_INPUT_EXCLUSIVE : 0); - break; - - case MP_CMD_DISABLE_INPUT_SECTION: - mp_input_disable_section(mpctx->input, cmd->args[0].v.s); - break; - - case MP_CMD_VO_CMDLINE: - if (mpctx->video_out) { - char *s = cmd->args[0].v.s; - mp_msg(MSGT_CPLAYER, MSGL_INFO, "Setting vo cmd line to '%s'.\n", - s); - if (vo_control(mpctx->video_out, VOCTRL_SET_COMMAND_LINE, s) > 0) { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, "vo='%s'", s); - } else { - set_osd_msg(mpctx, OSD_MSG_TEXT, osdl, osd_duration, "Failed!"); - } - } - break; - - case MP_CMD_AF: - edit_filters_osd(mpctx, STREAM_AUDIO, cmd->args[0].v.s, - cmd->args[1].v.s, msg_osd); - break; - - case MP_CMD_VF: - edit_filters_osd(mpctx, STREAM_VIDEO, cmd->args[0].v.s, - cmd->args[1].v.s, msg_osd); - break; - - case MP_CMD_SCRIPT_DISPATCH: - if (mpctx->lua_ctx) { -#ifdef CONFIG_LUA - mp_lua_script_dispatch(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, - cmd->key_up_follows ? "keyup_follows" : "press"); -#endif - } - break; - -#ifdef HAVE_SYS_MMAN_H - case MP_CMD_OVERLAY_ADD: - overlay_add(mpctx, - cmd->args[0].v.i, cmd->args[1].v.i, cmd->args[2].v.i, - cmd->args[3].v.s, cmd->args[4].v.i, cmd->args[5].v.s, - cmd->args[6].v.i, cmd->args[7].v.i, cmd->args[8].v.i); - break; - - case MP_CMD_OVERLAY_REMOVE: - overlay_remove(mpctx, cmd->args[0].v.i); - break; -#endif - - case MP_CMD_COMMAND_LIST: { - for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) - run_command(mpctx, sub); - break; - } - - case MP_CMD_IGNORE: - break; - - default: - mp_msg(MSGT_CPLAYER, MSGL_V, - "Received unknown cmd %s\n", cmd->name); - } - - switch (cmd->pausing) { - case 1: // "pausing" - pause_player(mpctx); - break; - case 3: // "pausing_toggle" - if (opts->pause) - unpause_player(mpctx); - else - pause_player(mpctx); - break; - } -} - -void command_uninit(struct MPContext *mpctx) -{ - overlay_uninit(mpctx); - talloc_free(mpctx->command_ctx); - mpctx->command_ctx = NULL; -} - -void command_init(struct MPContext *mpctx) -{ - mpctx->command_ctx = talloc_zero(NULL, struct command_ctx); -} - -// Notify that a property might have changed. -void mp_notify_property(struct MPContext *mpctx, const char *property) -{ - mp_notify(mpctx, MP_EVENT_PROPERTY, (void *)property); -} - -void mp_notify(struct MPContext *mpctx, enum mp_event event, void *arg) -{ - struct command_ctx *ctx = mpctx->command_ctx; - ctx->events |= 1u << event; -} - -static void handle_script_event(struct MPContext *mpctx, const char *name, - const char *arg) -{ -#ifdef CONFIG_LUA - mp_lua_event(mpctx, name, arg); -#endif -} - -void mp_flush_events(struct MPContext *mpctx) -{ - struct command_ctx *ctx = mpctx->command_ctx; - - ctx->events |= (1u << MP_EVENT_TICK); - - for (int n = 0; n < 16; n++) { - enum mp_event event = n; - unsigned mask = 1 << event; - if (ctx->events & mask) { - // The event handler could set event flags again; in this case let - // the next mp_flush_events() call handle it to avoid infinite loops. - ctx->events &= ~mask; - const char *name = NULL; - switch (event) { - case MP_EVENT_TICK: name = "tick"; break; - case MP_EVENT_TRACKS_CHANGED: name = "track-layout"; break; - case MP_EVENT_START_FILE: name = "start"; break; - case MP_EVENT_END_FILE: name = "end"; break; - default: ; - } - if (name) - handle_script_event(mpctx, name, ""); - } - } -} diff --git a/mpvcore/command.h b/mpvcore/command.h deleted file mode 100644 index d3469fc131..0000000000 --- a/mpvcore/command.h +++ /dev/null @@ -1,50 +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. - */ - -#ifndef MPLAYER_COMMAND_H -#define MPLAYER_COMMAND_H - -struct MPContext; -struct mp_cmd; - -void command_init(struct MPContext *mpctx); -void command_uninit(struct MPContext *mpctx); - -void run_command(struct MPContext *mpctx, struct mp_cmd *cmd); -char *mp_property_expand_string(struct MPContext *mpctx, const char *str); -void property_print_help(void); -int mp_property_do(const char* name, int action, void* val, - struct MPContext *mpctx); - -const struct m_option *mp_get_property_list(void); - -enum mp_event { - MP_EVENT_NONE, - MP_EVENT_TICK, - MP_EVENT_PROPERTY, // char*, property that is changed - MP_EVENT_TRACKS_CHANGED, - MP_EVENT_START_FILE, - MP_EVENT_END_FILE, -}; - -void mp_notify(struct MPContext *mpctx, enum mp_event event, void *arg); -void mp_notify_property(struct MPContext *mpctx, const char *property); - -void mp_flush_events(struct MPContext *mpctx); - -#endif /* MPLAYER_COMMAND_H */ diff --git a/mpvcore/lua/assdraw.lua b/mpvcore/lua/assdraw.lua deleted file mode 100644 index fc3b727f57..0000000000 --- a/mpvcore/lua/assdraw.lua +++ /dev/null @@ -1,98 +0,0 @@ -local ass_mt = {} -ass_mt.__index = ass_mt - -local function ass_new() - return setmetatable({ scale = 4, text = "" }, ass_mt) -end - -function ass_mt.new_event(ass) - -- osd_libass.c adds an event per line - if #ass.text > 0 then - ass.text = ass.text .. "\n" - end -end - -function ass_mt.draw_start(ass) - ass.text = string.format("%s{\\p%d}", ass.text, ass.scale) -end - -function ass_mt.draw_stop(ass) - ass.text = ass.text .. "{\\p0}" -end - -function ass_mt.coord(ass, x, y) - local scale = math.pow(2, ass.scale - 1) - local ix = math.ceil(x * scale) - local iy = math.ceil(y * scale) - ass.text = string.format("%s %d %d", ass.text, ix, iy) -end - -function ass_mt.append(ass, s) - ass.text = ass.text .. s -end - -function ass_mt.merge(ass1, ass2) - ass1.text = ass1.text .. ass2.text -end - -function ass_mt.pos(ass, x, y) - ass:append(string.format("{\\pos(%f,%f)}", x, y)) -end - -function ass_mt.an(ass, an) - ass:append(string.format("{\\an%d}", an)) -end - -function ass_mt.move_to(ass, x, y) - ass:append(" m") - ass:coord(x, y) -end - -function ass_mt.line_to(ass, x, y) - ass:append(" l") - ass:coord(x, y) -end - -function ass_mt.bezier_curve(ass, x1, y1, x2, y2, x3, y3) - ass:append(" b") - ass:coord(x1, y1) - ass:coord(x2, y2) - ass:coord(x3, y3) -end - - -function ass_mt.rect_ccw(ass, x0, y0, x1, y1) - ass:move_to(x0, y0) - ass:line_to(x0, y1) - ass:line_to(x1, y1) - ass:line_to(x1, y0) -end - -function ass_mt.rect_cw(ass, x0, y0, x1, y1) - ass:move_to(x0, y0) - ass:line_to(x1, y0) - ass:line_to(x1, y1) - ass:line_to(x0, y1) -end - -function ass_mt.round_rect_cw(ass, x0, y0, x1, y1, r) - ass:move_to(x0 + r, y0) - ass:line_to(x1 - r, y0) -- top line - if r > 0 then - ass:bezier_curve(x1, y0, x1, y0, x1, y0 + r) -- top right corner - end - ass:line_to(x1, y1 - r) -- right line - if r > 0 then - ass:bezier_curve(x1, y1, x1, y1, x1 - r, y1) -- bottom right corner - end - ass:line_to(x0 + r, y1) -- bottom line - if r > 0 then - ass:bezier_curve(x0, y1, x0, y1, x0, y1 - r) -- bottom left corner - end - ass:line_to(x0, y0 + r) -- left line - if r > 0 then - ass:bezier_curve(x0, y0, x0, y0, x0 + r, y0) -- top left corner - end -end - -return {ass_new = ass_new} diff --git a/mpvcore/lua/defaults.lua b/mpvcore/lua/defaults.lua deleted file mode 100644 index d24cda9cbe..0000000000 --- a/mpvcore/lua/defaults.lua +++ /dev/null @@ -1,82 +0,0 @@ - -local callbacks = {} --- each script has its own section, so that they don't conflict -local default_section = "input_" .. mp.script_name - --- Set the list of key bindings. These will override the user's bindings, so --- you should use this sparingly. --- A call to this function will remove all bindings previously set with this --- function. For example, set_key_bindings({}) would remove all script defined --- key bindings. --- Note: the bindings are not active by default. Use enable_key_bindings(). --- --- list is an array of key bindings, where each entry is an array as follow: --- {key, callback} --- {key, callback, callback_down} --- key is the key string as used in input.conf, like "ctrl+a" --- callback is a Lua function that is called when the key binding is used. --- callback_down can be given too, and is called when a mouse button is pressed --- if the key is a mouse button. (The normal callback will be for mouse button --- down.) --- --- callback can be a string too, in which case the following will be added like --- an input.conf line: key .. " " .. callback --- (And callback_down is ignored.) -function mp.set_key_bindings(list, section) - local cfg = "" - for i = 1, #list do - local entry = list[i] - local key = entry[1] - local cb = entry[2] - local cb_down = entry[3] - if type(cb) == "function" then - callbacks[#callbacks + 1] = {press=cb, before_press=cb_down} - cfg = cfg .. key .. " script_dispatch " .. mp.script_name - .. " " .. #callbacks .. "\n" - else - cfg = cfg .. key .. " " .. cb .. "\n" - end - end - mp.input_define_section(section or default_section, cfg) -end - -function mp.enable_key_bindings(section, flags) - mp.input_enable_section(section or default_section, flags) -end - -function mp.disable_key_bindings(section) - mp.input_disable_section(section or default_section) -end - -function mp.set_mouse_area(x0, y0, x1, y1, section) - mp.input_set_section_mouse_area(section or default_section, x0, y0, x1, y1) -end - --- called by C on script_dispatch input command -function mp_script_dispatch(id, event) - local cb = callbacks[id] - if cb then - if event == "press" and cb.press then - cb.press() - elseif event == "keyup_follows" and cb.before_press then - cb.before_press() - end - end -end - -mp.msg = { - log = mp.log, - fatal = function(...) return mp.log("fatal", ...) end, - error = function(...) return mp.log("error", ...) end, - warn = function(...) return mp.log("warn", ...) end, - info = function(...) return mp.log("info", ...) end, - verbose = function(...) return mp.log("verbose", ...) end, - debug = function(...) return mp.log("debug", ...) end, -} - -_G.print = mp.msg.info - -package.loaded["mp"] = mp -package.loaded["mp.msg"] = mp.msg - -return {} diff --git a/mpvcore/lua/osc.lua b/mpvcore/lua/osc.lua deleted file mode 100644 index f105d10a9c..0000000000 --- a/mpvcore/lua/osc.lua +++ /dev/null @@ -1,1288 +0,0 @@ --- osc.lua - -local assdraw = require 'mp.assdraw' -local msg = require 'mp.msg' - --- --- Parameters --- - --- default user option values --- do not touch, change them in plugin_osc.conf -local user_opts = { - showwindowed = true, -- show OSC when windowed? - showfullscreen = true, -- show OSC when fullscreen? - scalewindowed = 1, -- scaling of the controller when windowed - scalefullscreen = 1, -- scaling of the controller when fullscreen - scaleforcedwindow = 2, -- scaling of the controller when rendered on a forced (dummy) window - vidscale = true, -- scale the controller with the video? - valign = 0.8, -- vertical alignment, -1 (top) to 1 (bottom) - halign = 0, -- horizontal alignment, -1 (left) to 1 (right) - boxalpha = 80, -- alpha of the background box, 0 (opaque) to 255 (fully transparent) - hidetimeout = 500, -- duration in ms until the OSC hides if no mouse movement, negative value disables autohide - fadeduration = 200, -- duration of fade out in ms, 0 = no fade - deadzonesize = 0, -- size of deadzone - minmousemove = 3, -- minimum amount of pixels the mouse has to move between ticks to make the OSC show up - seektooltip = false, -- display tooltip over the seekbar indicating time at mouse position - iamaprogrammer = false, -- use native mpv values and disable OSC internal playlist management (and some functions that depend on it) -} - -local osc_param = { - osc_w = 550, -- width, height, corner-radius, padding of the OSC box - osc_h = 138, - osc_r = 10, - osc_p = 15, - - -- calculated by osc_init() - playresy = 0, -- canvas size Y - playresx = 0, -- canvas size X - posX, posY = 0,0, -- position of the controler - pos_offsetX, pos_offsetY = 0,0, -- vertical/horizontal position offset for contents aligned at the borders of the box -} - -local osc_styles = { - bigButtons = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs50\\fnmpv-osd-symbols}", - smallButtonsL = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs20\\fnmpv-osd-symbols}", - smallButtonsLlabel = "{\\fs17\\fn" .. mp.property_get("options/osd-font") .. "}", - smallButtonsR = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs30\\fnmpv-osd-symbols}", - - elementDown = "{\\1c&H999999}", - timecodes = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs20}", - vidtitle = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs12}", - box = "{\\rDefault\\blur0\\bord1\\1c&H000000\\3c&HFFFFFF}", -} - --- internal states, do not touch -local state = { - showtime, -- time of last invocation (last mouse move) - osc_visible = false, - anistart, -- time when the animation started - anitype, -- current type of animation - animation, -- current animation alpha - mouse_down_counter = 0, -- used for softrepeat - active_element = nil, -- nil = none, 0 = background, 1+ = see elements[] - active_event_source = nil, -- the "button" that issued the current event - rightTC_trem = true, -- if the right timcode should display total or remaining time - tc_ms = false, -- Should the timecodes display their time with milliseconds - mp_screen_sizeX, mp_screen_sizeY, -- last screen-resolution, to detect resolution changes to issue reINITs - initREQ = false, -- is a re-init request pending? - last_seek, -- last seek position, to avoid deadlocks by repeatedly seeking to the same position - last_mouseX, last_mouseY, -- last mouse position, to detect siginificant mouse movement - message_text, - message_timeout, -} - --- --- User Settings Management --- - -function val2str(val) - local strval = val - if type(val) == "boolean" then - if val then strval = "yes" else strval = "no" end - end - - return strval -end - --- converts val to type of desttypeval -function typeconv(desttypeval, val) - if type(desttypeval) == "boolean" then - if val == "yes" then - val = true - elseif val == "no" then - val = false - else - msg.error("Error: Can't convert " .. val .. " to boolean!") - val = nil - end - elseif type(desttypeval) == "number" then - if not (tonumber(val) == nil) then - val = tonumber(val) - else - msg.error("Error: Can't convert " .. val .. " to number!") - val = nil - end - end - return val -end - --- Automagical config handling --- options: A table with options setable via config with assigned default values. The type of the default values is important for --- converting the values read from the config file back. Do not use "nil" as a default value! --- identifier: A simple indentifier string for the config file. Make sure this doesn't collide with other scripts. - --- How does it work: --- Existance of the configfile will be checked, if it doesn't exist, the default values from the options table will be written in a new --- file, commented out. If it exits, the key/value pairs will be read, and values of keys that exist in the options table will overwrite --- their value. Keys that don't exist in the options table will be ignored, keys that don't exits in the config will keep their default --- value. The value's types will automatically be converted to the type used in the options table. -function read_config(options, identifier) - - local conffilename = "plugin_" .. identifier .. ".conf" - local conffile = mp.find_config_file(conffilename) - local f = io.open(conffile,"r") - if f == nil then - -- config not found - else - -- config exists, read values - local linecounter = 1 - for line in f:lines() do - if string.find(line, "#") == 1 then - - else - local eqpos = string.find(line, "=") - if eqpos == nil then - - else - local key = string.sub(line, 1, eqpos-1) - local val = string.sub(line, eqpos+1) - - -- match found values with defaults - if options[key] == nil then - msg.warn(conffilename..":"..linecounter.." unknown key " .. key .. ", ignoring") - else - local convval = typeconv(options[key], val) - if convval == nil then - msg.error(conffilename..":"..linecounter.." error converting value '" .. val .. "' for key '" .. key .. "'") - else - options[key] = convval - end - end - end - end - linecounter = linecounter + 1 - end - io.close(f) - end -end - --- read configfile -read_config(user_opts, "osc") - - --- --- Helperfunctions --- - -function scale_value(x0, x1, y0, y1, val) - local m = (y1 - y0) / (x1 - x0) - local b = y0 - (m * x0) - return (m * val) + b -end - --- returns hitbox spanning coordinates (top left, bottom right corner) according to alignment -function get_hitbox_coords(x, y, an, w, h) - - local alignments = { - [1] = function () return x, y-h, x+w, y end, - [2] = function () return x-(w/2), y-h, x+(w/2), y end, - [3] = function () return x-w, y-h, x, y end, - - [4] = function () return x, y-(h/2), x+w, y+(h/2) end, - [5] = function () return x-(w/2), y-(h/2), x+(w/2), y+(h/2) end, - [6] = function () return x-w, y-(h/2), x, y+(h/2) end, - - [7] = function () return x, y, x+w, y+h end, - [8] = function () return x-(w/2), y, x+(w/2), y+h end, - [9] = function () return x-w, y, x, y+h end, - } - - return alignments[an]() -end - -function get_element_hitbox(element) - return element.hitbox.x1, element.hitbox.y1, element.hitbox.x2, element.hitbox.y2 -end - -function mouse_hit(element) - local mX, mY = mp.get_mouse_pos() - local bX1, bY1, bX2, bY2 = get_element_hitbox(element) - - return (mX >= bX1 and mX <= bX2 and mY >= bY1 and mY <= bY2) -end - -function limit_range(min, max, val) - if val > max then - val = max - elseif val < min then - val = min - end - return val -end - -function get_slider_value(element) - local fill_offsetV = element.metainfo.slider.border + element.metainfo.slider.gap - local paddingH = (element.h - (2*fill_offsetV)) / 2 - - local b_x1, b_x2 = element.hitbox.x1 + paddingH, element.hitbox.x2 - paddingH - local s_min, s_max = element.metainfo.slider.min, element.metainfo.slider.max - - local pos = scale_value(b_x1, b_x2, s_min, s_max, mp.get_mouse_pos()) - - return limit_range(s_min, s_max, pos) -end - -function countone(val) - if not (user_opts.iamaprogrammer) then - val = val + 1 - end - return val -end - --- align: -1 .. +1 --- frame: size of the containing area --- obj: size of the object that should be positioned inside the area --- margin: min. distance from object to frame (as long as -1 <= align <= +1) -function get_align(align, frame, obj, margin) - return (frame / 2) + (((frame / 2) - margin - (obj / 2)) * align) -end - --- multiplies two alpha values, formular can probably be improved -function mult_alpha(alphaA, alphaB) - return 255 - (((1-(alphaA/255)) * (1-(alphaB/255))) * 255) -end - --- --- Tracklist Management --- - -local nicetypes = {video = "Video", audio = "Audio", sub = "Subtitle"} - --- updates the OSC internal playlists, should be run each time the track-layout changes -function update_tracklist() - local tracktable = mp.get_track_list() - - -- by osc_id - tracks_osc = {} - tracks_osc.video, tracks_osc.audio, tracks_osc.sub = {}, {}, {} - -- by mpv_id - tracks_mpv = {} - tracks_mpv.video, tracks_mpv.audio, tracks_mpv.sub = {}, {}, {} - for n = 1, #tracktable do - if not (tracktable[n].type == "unkown") then - local type = tracktable[n].type - local mpv_id = tonumber(tracktable[n].id) - - -- by osc_id - table.insert(tracks_osc[type], tracktable[n]) - - -- by mpv_id - tracks_mpv[type][mpv_id] = tracktable[n] - tracks_mpv[type][mpv_id].osc_id = #tracks_osc[type] - end - end -end - --- return a nice list of tracks of the given type (video, audio, sub) -function get_tracklist(type) - local msg = "Available " .. nicetypes[type] .. " Tracks: " - local select_scale = 100 - if #tracks_osc[type] == 0 then - msg = msg .. "none" - else - for n = 1, #tracks_osc[type] do - local track = tracks_osc[type][n] - local lang, title, selected = "unkown", "", "{\\fscx" .. select_scale .. "\\fscy" .. select_scale .. "}○{\\fscx100\\fscy100}" - if not(track.language == nil) then lang = track.language end - if not(track.title == nil) then title = track.title end - if (track.id == tonumber(mp.property_get(type))) then - selected = "{\\fscx" .. select_scale .. "\\fscy" .. select_scale .. "}●{\\fscx100\\fscy100}" - end - msg = msg .. "\n" .. selected .. " " .. n .. ": [" .. lang .. "] " .. title - end - end - return msg -end - --- relatively change the track of given by tracks (+1 -> next, -1 -> previous) -function set_track(type, next) - local current_track_mpv, current_track_osc - if (mp.property_get(type) == "no") then - current_track_osc = 0 - else - current_track_mpv = tonumber(mp.property_get(type)) - current_track_osc = tracks_mpv[type][current_track_mpv].osc_id - end - local new_track_osc = (current_track_osc + next) % (#tracks_osc[type] + 1) - local new_track_mpv - if new_track_osc == 0 then - new_track_mpv = "no" - else - new_track_mpv = tracks_osc[type][new_track_osc].id - end - - mp.send_command("no-osd set " .. type .. " " .. new_track_mpv) - - if (new_track_osc == 0) then - show_message(nicetypes[type] .. " Track: none") - else - show_message(nicetypes[type] .. " Track: " .. new_track_osc .. "/" .. #tracks_osc[type] - .. " [" .. (tracks_osc[type][new_track_osc].language or "unkown") .. "] " .. (tracks_osc[type][new_track_osc].title or "")) - end -end - --- get the currently selected track of , OSC-style counted -function get_track(type) - local track = mp.property_get(type) - if (track == "no" or track == nil) then - return 0 - else - return tracks_mpv[type][tonumber(track)].osc_id - end -end - - --- --- Element Management --- - --- do not use this function, use the wrappers below -function register_element(type, x, y, an, w, h, style, content, eventresponder, metainfo2) - -- type button, slider or box - -- x, y position - -- an alignment (see ASS standard) - -- w, h size of hitbox - -- style main style - -- content what the element should display, can be a string or a function(ass) - -- eventresponder A table containing functions mapped to events that shall be run on those events - -- metainfo A table containing additional parameters for the element - - -- set default metainfo - local metainfo = {} - if not (metainfo2 == nil) then metainfo = metainfo2 end - if metainfo.visible == nil then metainfo.visible = true end -- element visible at all? - if metainfo.enabled == nil then metainfo.enabled = true end -- element clickable? - if metainfo.styledown == nil then metainfo.styledown = true end -- should the element be styled with the elementDown style when clicked? - if metainfo.softrepeat == nil then metainfo.softrepeat = false end -- should the *_down event be executed with "hold for repeat" behaviour? - if metainfo.alpha1 == nil then metainfo.alpha1 = 0 end -- alpha1 of the element, 0 = opaque, 255 = transparent (primary fill alpha) - if metainfo.alpha2 == nil then metainfo.alpha2 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (secondary fill alpha) - if metainfo.alpha3 == nil then metainfo.alpha3 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (border alpha) - if metainfo.alpha4 == nil then metainfo.alpha4 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (shadow alpha) - - if metainfo.visible then - local ass = assdraw.ass_new() - - ass:append("{}") -- shitty hack to troll the new_event function into inserting a \n - ass:new_event() - ass:pos(x, y) -- positioning - ass:an(an) - ass:append(style) -- styling - - -- if the element is supposed to be disabled, style it accordingly and kill the eventresponders - if metainfo.enabled == false then - metainfo.alpha1 = 136 - eventresponder = nil - end - - -- Calculate the hitbox - local bX1, bY1, bX2, bY2 = get_hitbox_coords(x, y, an, w, h) - local hitbox - if type == "slider" then - -- if it's a slider, cut the border and gap off, as those aren't of interest for eventhandling - local fill_offset = metainfo.slider.border + metainfo.slider.gap - hitbox = {x1 = bX1 + fill_offset, y1 = bY1 + fill_offset, x2 = bX2 - fill_offset, y2 = bY2 - fill_offset} - else - hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2} - end - - local element = { - type = type, - elem_ass = ass, - hitbox = hitbox, - w = w, - h = h, - x = x, - y = y, - content = content, - eventresponder = eventresponder, - metainfo = metainfo, - } - - table.insert(elements, element) - end -end - -function register_button(x, y, an, w, h, style, content, eventresponder, metainfo) - register_element("button", x, y, an, w, h, style, content, eventresponder, metainfo) -end - -function register_box(x, y, an, w, h, r, style, metainfo2) - local ass = assdraw.ass_new() - ass:draw_start() - ass:round_rect_cw(0, 0, w, h, r) - ass:draw_stop() - - local metainfo = {} - if not (metainfo2 == nil) then metainfo = metainfo2 end - - metainfo.styledown = false - - register_element("box", x, y, an, w, h, style, ass, nil, metainfo) -end - -function register_slider(x, y, an, w, h, style, min, max, markerF, posF, eventresponder, metainfo2) - local metainfo = {} - if not (metainfo2 == nil) then metainfo = metainfo2 end - local slider1 = {} - if (metainfo.slider == nil) then metainfo.slider = slider1 end - - -- defaults - if min == nil then metainfo.slider.min = 0 else metainfo.slider.min = min end - if max == nil then metainfo.slider.max = 100 else metainfo.slider.max = max end - if metainfo.slider.border == nil then metainfo.slider.border = 1 end - if metainfo.slider.gap == nil then metainfo.slider.gap = 2 end - if metainfo.slider.type == nil then metainfo.slider.type = "slider" end - - metainfo.slider.markerF = markerF - metainfo.slider.posF = posF - - -- prepare the box with markers - local ass = assdraw.ass_new() - local border, gap = metainfo.slider.border, metainfo.slider.gap - local fill_offsetV = border + gap -- Vertical offset between element outline and drag-area - local fill_offsetH = h / 2 -- Horizontal offset between element outline and drag-area - - ass:draw_start() - - -- the box - ass:rect_cw(0, 0, w, h); - - -- the "hole" - ass:rect_ccw(border, border, w - border, h - border) - - -- marker nibbles - if not (markerF == nil) and gap > 0 then - local markers = markerF() - for n = 1, #markers do - if (markers[n] > min) and (markers[n] < max) then - - local coordL, coordR = fill_offsetH, (w - fill_offsetH) - - local s = scale_value(min, max, coordL, coordR, markers[n]) - - if gap > 1 then - -- draw triangles - local a = gap / 0.5 --0.866 - --top - ass:move_to(s - (a/2), border) - ass:line_to(s + (a/2), border) - ass:line_to(s, border + gap) - - --bottom - ass:move_to(s - (a/2), h - border) - ass:line_to(s, h - border - gap) - ass:line_to(s + (a/2), h - border) - - else - -- draw 1px nibbles - ass:rect_cw(s - 0.5, border, s + 0.5, border*2); - ass:rect_cw(s - 0.5, h - border*2, s + 0.5, h - border); - end - - end - end - end - - register_element("slider", x, y, an, w, h, style, ass, eventresponder, metainfo) -end - --- --- Element Rendering --- - -function render_elements(master_ass) - - for n = 1, #elements do - - local element = elements[n] - local elem_ass = assdraw.ass_new() - local elem_ass1 = element.elem_ass - elem_ass:merge(elem_ass1) - - --alpha - local alpha1 = element.metainfo.alpha1 - local alpha2 = element.metainfo.alpha2 - local alpha3 = element.metainfo.alpha3 - local alpha4 = element.metainfo.alpha4 - - if not(state.animation == nil) then - alpha1 = mult_alpha(element.metainfo.alpha1, state.animation) - alpha2 = mult_alpha(element.metainfo.alpha2, state.animation) - alpha3 = mult_alpha(element.metainfo.alpha3, state.animation) - alpha4 = mult_alpha(element.metainfo.alpha4, state.animation) - end - - elem_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}", alpha1, alpha2, alpha3, alpha4)) - - - if state.active_element == n then - - -- run render event functions - if not (element.eventresponder.render == nil) then - element.eventresponder.render(element) - end - - if mouse_hit(element) then - -- mouse down styling - if element.metainfo.styledown then - elem_ass:append(osc_styles.elementDown) - end - - if (element.metainfo.softrepeat == true) and (state.mouse_down_counter >= 15 and state.mouse_down_counter % 5 == 0) then - element.eventresponder[state.active_event_source .. "_down"](element) - end - state.mouse_down_counter = state.mouse_down_counter + 1 - end - - end - - if element.type == "slider" then - - elem_ass:merge(element.content) -- ASS objects - - -- draw pos marker - local pos = element.metainfo.slider.posF() - - if not (pos == nil) then - - pos = limit_range(element.metainfo.slider.min, element.metainfo.slider.max, pos) - - local fill_offsetV = element.metainfo.slider.border + element.metainfo.slider.gap - local fill_offsetH = element.h/2 - - local coordL, coordR = fill_offsetH, (element.w - fill_offsetH) - - local xp = scale_value(element.metainfo.slider.min, element.metainfo.slider.max, coordL, coordR, pos) - - -- the filling, draw it only if positive - local innerH = element.h - (2*fill_offsetV) - - if element.metainfo.slider.type == "bar" then - elem_ass:rect_cw(fill_offsetV, fill_offsetV, xp, element.h - fill_offsetV) - else - elem_ass:move_to(xp, fill_offsetV) - elem_ass:line_to(xp+(innerH/2), (innerH/2)+fill_offsetV) - elem_ass:line_to(xp, (innerH)+fill_offsetV) - elem_ass:line_to(xp-(innerH/2), (innerH/2)+fill_offsetV) - end - end - - elem_ass:draw_stop() - - -- add tooltip - if not (element.metainfo.slider.tooltipF == nil) then - - if mouse_hit(element) then - local sliderpos = get_slider_value(element) - local tooltiplabel = element.metainfo.slider.tooltipF(sliderpos) - local s_min, s_max = element.metainfo.slider.min, element.metainfo.slider.max - - local an = 2 - if (sliderpos < (s_min + 10)) then - an = 1 - elseif (sliderpos > (s_max - 10)) then - an = 3 - end - - elem_ass:new_event() - elem_ass:pos(mp.get_mouse_pos(), element.y - (element.h) - 0) -- positioning - elem_ass:an(an) - elem_ass:append(osc_styles.vidtitle) -- styling - elem_ass:append(tooltiplabel) - - end - end - - - - elseif element.type == "box" then - elem_ass:merge(element.content) -- ASS objects - elseif type(element.content) == "function" then - element.content(elem_ass) -- function objects - else - elem_ass:append(element.content) -- text objects - end - - master_ass:merge(elem_ass) - end -end - --- --- Message display --- - -function show_message(text, duration) - - if duration == nil then - duration = tonumber(mp.property_get("options/osd-duration")) / 1000 - end - - -- cut the text short, otherwise the following functions may slow down massively on huge input - text = string.sub(text, 0, 4000) - - -- replace actual linebreaks with ASS linebreaks and get the amount of lines along the way - local lines - text, lines = string.gsub(text, "\n", "\\N") - - -- append a Zero-Width-Space to . and _ to enable linebreaking of long filenames - text = string.gsub(text, "%.", ".\226\128\139") - text = string.gsub(text, "_", "_\226\128\139") - - -- scale the fontsize for longer multi-line output - local fontsize, outline = tonumber(mp.property_get("options/osd-font-size")), tonumber(mp.property_get("options/osd-border-size")) - if lines > 12 then - fontsize, outline = fontsize / 2, outline / 1.5 - elseif lines > 8 then - fontsize, outline = fontsize / 1.5, outline / 1.25 - end - - local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}" - - state.message_text = style .. text - state.message_timeout = mp.get_timer() + duration -end - -function render_message(ass) - if not(state.message_timeout == nil) and not(state.message_text == nil) and state.message_timeout > mp.get_timer() then - ass:new_event() - ass:append(state.message_text) - else - state.message_text = nil - state.message_timeout = nil - end -end - --- --- Initialisation and Layout --- - --- OSC INIT -function osc_init() - -- kill old Elements - elements = {} - - -- set canvas resolution according to display aspect and scaling setting - local baseResY = 720 - local display_w, display_h, display_aspect = mp.get_screen_size() - local scale = 1 - - if (mp.property_get("video") == "no") then -- dummy/forced window - scale = user_opts.scaleforcedwindow - elseif (mp.property_get("fullscreen") == "yes") then - scale = user_opts.scalefullscreen - else - scale = user_opts.scalewindowed - end - - - if user_opts.vidscale then - osc_param.playresy = baseResY / scale - else - osc_param.playresy = display_h / scale - end - osc_param.playresx = osc_param.playresy * display_aspect - - -- make sure the OSC actually fits into the video - if (osc_param.playresx < (osc_param.osc_w + (2 * osc_param.osc_p))) then - osc_param.playresy = (osc_param.osc_w + (2 * osc_param.osc_p)) / display_aspect - osc_param.playresx = osc_param.playresy * display_aspect - end - - -- position of the controller according to video aspect and valignment - osc_param.posX = math.floor(get_align(user_opts.halign, osc_param.playresx, osc_param.osc_w, 0)) - osc_param.posY = math.floor(get_align(user_opts.valign, osc_param.playresy, osc_param.osc_h, 0)) - - -- Some calculations on stuff we'll need - -- vertical/horizontal position offset for contents aligned at the borders of the box - osc_param.pos_offsetX, osc_param.pos_offsetY = (osc_param.osc_w - (2*osc_param.osc_p)) / 2, (osc_param.osc_h - (2*osc_param.osc_p)) / 2 - - -- fetch values - local osc_w, osc_h, osc_r, osc_p = osc_param.osc_w, osc_param.osc_h, osc_param.osc_r, osc_param.osc_p - local pos_offsetX, pos_offsetY = osc_param.pos_offsetX, osc_param.pos_offsetY - local posX, posY = osc_param.posX, osc_param.posY - - -- - -- Backround box - -- - - local metainfo = {} - metainfo.alpha1 = user_opts.boxalpha - metainfo.alpha3 = user_opts.boxalpha - register_box(posX, posY, 5, osc_w, osc_h, osc_r, osc_styles.box, metainfo) - - -- - -- Title row - -- - - local titlerowY = posY - pos_offsetY - 10 - - -- title - local contentF = function (ass) - local title = mp.property_get_string("media-title") - if not (title == nil) then - - if #title > 80 then - title = string.format("{\\fscx%f}", (80 / #title) * 100) .. title - end - - ass:append(title) - else - ass:append("mpv") - end - end - - local eventresponder = {} - eventresponder.mouse_btn0_up = function () - - local title = mp.property_get("media-title") - local pl_count = tonumber(mp.property_get("playlist-count")) - - if pl_count > 1 then - local playlist_pos = countone(tonumber(mp.property_get("playlist-pos"))) - title = "[" .. playlist_pos .. "/" .. pl_count .. "] " .. title - end - - show_message(title) - end - eventresponder.mouse_btn2_up = function () show_message(mp.property_get("filename")) end - - register_button(posX, titlerowY, 8, 496, 12, osc_styles.vidtitle, contentF, eventresponder, nil) - - -- If we have more than one playlist entry, render playlist navigation buttons - local metainfo = {} - metainfo.visible = (tonumber(mp.property_get("playlist-count")) > 1) - - -- playlist prev - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("playlist_prev weak") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("playlist"), 3) end - register_button(posX - pos_offsetX, titlerowY, 7, 12, 12, osc_styles.vidtitle, "◀", eventresponder, metainfo) - - -- playlist next - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("playlist_next weak") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("playlist"), 3) end - register_button(posX + pos_offsetX, titlerowY, 9, 12, 12, osc_styles.vidtitle, "▶", eventresponder, metainfo) - - -- - -- Big buttons - -- - - local bigbuttonrowY = posY - pos_offsetY + 35 - local bigbuttondistance = 60 - - --play/pause - local contentF = function (ass) - if mp.property_get("pause") == "yes" then - ass:append("\238\132\129") - else - ass:append("\238\128\130") - end - end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("no-osd cycle pause") end - register_button(posX, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, contentF, eventresponder, nil) - - --skipback - local metainfo = {} - metainfo.softrepeat = true - - local eventresponder = {} - eventresponder.mouse_btn0_down = function () mp.send_command("no-osd seek -5 relative keyframes") end - eventresponder["shift+mouse_btn0_down"] = function () mp.send_command("no-osd frame_back_step") end - eventresponder.mouse_btn2_down = function () mp.send_command("no-osd seek -30 relative keyframes") end - register_button(posX - bigbuttondistance, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\128\132", eventresponder, metainfo) - - --skipfrwd - local eventresponder = {} - eventresponder.mouse_btn0_down = function () mp.send_command("no-osd seek 10 relative keyframes") end - eventresponder["shift+mouse_btn0_down"] = function () mp.send_command("no-osd frame_step") end - eventresponder.mouse_btn2_down = function () mp.send_command("no-osd seek 60 relative keyframes") end - register_button(posX + bigbuttondistance, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\128\133", eventresponder, metainfo) - - --chapters - -- do we have any? - local metainfo = {} - metainfo.enabled = ((#mp.get_chapter_list()) > 0) - - --prev - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add chapter -1") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("chapter-list"), 3) end - register_button(posX - (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\132", eventresponder, metainfo) - - --next - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add chapter 1") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("chapter-list"), 3) end - register_button(posX + (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\133", eventresponder, metainfo) - - - -- - -- Smaller buttons - -- - - if not (user_opts.iamaprogrammer) then - update_tracklist() - end - - --cycle audio tracks - - local metainfo = {} - local eventresponder = {} - local contentF - - if not (user_opts.iamaprogrammer) then - metainfo.enabled = (#tracks_osc.audio > 0) - - contentF = function (ass) - local aid = "–" - if not (get_track("audio") == 0) then - aid = get_track("audio") - end - ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel .. " " .. aid .. "/" .. #tracks_osc.audio) - end - - eventresponder.mouse_btn0_up = function () set_track("audio", 1) end - eventresponder.mouse_btn2_up = function () set_track("audio", -1) end - eventresponder["shift+mouse_btn0_down"] = function () - show_message(get_tracklist("audio"), 2) - end - else - metainfo.enabled = true - contentF = function (ass) - local aid = mp.property_get("audio") - - ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel .. " " .. aid) - end - - eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add audio 1") end - eventresponder.mouse_btn2_up = function () mp.send_command("osd-msg add audio -1") end - end - - register_button(posX - pos_offsetX, bigbuttonrowY, 1, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo) - - - --cycle sub tracks - - local metainfo = {} - local eventresponder = {} - local contentF - - if not (user_opts.iamaprogrammer) then - metainfo.enabled = (#tracks_osc.sub > 0) - - contentF = function (ass) - local sid = "–" - if not (get_track("sub") == 0) then - sid = get_track("sub") - end - ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid .. "/" .. #tracks_osc.sub) - end - - eventresponder.mouse_btn0_up = function () set_track("sub", 1) end - eventresponder.mouse_btn2_up = function () set_track("sub", -1) end - eventresponder["shift+mouse_btn0_down"] = function () - show_message(get_tracklist("sub"), 2) - end - else - metainfo.enabled = true - contentF = function (ass) - local sid = mp.property_get("sub") - - ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid) - end - - eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add sub 1") end - eventresponder.mouse_btn2_up = function () mp.send_command("osd-msg add sub -1") end - end - register_button(posX - pos_offsetX, bigbuttonrowY, 7, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo) - - - --toggle FS - local contentF = function (ass) - if mp.property_get("fullscreen") == "yes" then - ass:append("\238\132\137") - else - ass:append("\238\132\136") - end - end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.send_command("no-osd cycle fullscreen") end - register_button(posX+pos_offsetX, bigbuttonrowY, 6, 25, 25, osc_styles.smallButtonsR, contentF, eventresponder, nil) - - - -- - -- Seekbar - -- - - local markerF = function () - local duration = 0 - if not (mp.property_get("length") == nil) then - duration = tonumber(mp.property_get("length")) - end - - local chapters = mp.get_chapter_list() - local markers = {} - for n = 1, #chapters do - markers[n] = (chapters[n].time / duration * 100) - end - return markers - end - - local posF = function () - if mp.property_get("length") == nil then - return nil - else - return tonumber(mp.property_get("percent-pos")) - end - end - - local tooltipF = function (pos) - if not (mp.property_get("length") == nil) then - duration = tonumber(mp.property_get("length")) - possec = duration * (pos / 100) - return mp.format_time(possec) - else - return nil - end - end - - local metainfo = {} - - - metainfo.enabled = (not (mp.property_get("length") == nil)) and (tonumber(mp.property_get("length")) > 0) - metainfo.styledown = false - metainfo.slider = {} - metainfo.slider.border = 1 - metainfo.slider.gap = 1 -- >1 will draw triangle markers - metainfo.slider.type = "slider" -- "bar" for old bar-style filling - if (user_opts.seektooltip) and (not (mp.property_get("length") == nil)) then - metainfo.slider.tooltipF = tooltipF - end - - local eventresponder = {} - local sliderF = function (element) - local seek_to = get_slider_value(element) - -- ignore identical seeks - if not(state.last_seek == seek_to) then - mp.send_command(string.format("no-osd seek %f absolute-percent keyframes", seek_to)) - state.last_seek = seek_to - end - end - eventresponder.render = sliderF - eventresponder.mouse_btn0_down = sliderF - register_slider(posX, posY+pos_offsetY-22, 2, pos_offsetX*2, 15, osc_styles.timecodes, 0, 100, markerF, posF, eventresponder, metainfo) - - -- - -- Timecodes + Volume - -- - - local bottomrowY = posY + pos_offsetY - 5 - - -- left (current pos) - local metainfo = {} - local eventresponder = {} - - local contentF = function (ass) - if state.tc_ms then - ass:append(mp.property_get_string("time-pos/full")) - else - ass:append(mp.property_get_string("time-pos")) - end - end - - eventresponder.mouse_btn0_up = function () state.tc_ms = not state.tc_ms end - register_button(posX - pos_offsetX, bottomrowY, 4, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo) - - -- center (Cache) - local metainfo = {} - local eventresponder = {} - - local contentF = function (ass) - local cache = mp.property_get("cache") - if not (cache == nil) then - cache = tonumber(mp.property_get("cache")) - if (cache < 45) then - ass:append("Cache: " .. (cache) .."%") - end - end - end - register_button(posX, bottomrowY, 5, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo) - - - -- right (total/remaining time) - -- do we have a usuable duration? - local metainfo = {} - metainfo.visible = (not (mp.property_get("length") == nil)) and (tonumber(mp.property_get("length")) > 0) - - local contentF = function (ass) - if state.rightTC_trem == true then - if state.tc_ms then - ass:append("-" .. mp.property_get_string("time-remaining/full")) - else - ass:append("-" .. mp.property_get_string("time-remaining")) - end - else - if state.tc_ms then - ass:append(mp.property_get_string("length/full")) - else - ass:append(mp.property_get_string("length")) - end - end - end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () state.rightTC_trem = not state.rightTC_trem end - - register_button(posX + pos_offsetX, bottomrowY, 6, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo) - -end - --- --- Other important stuff --- - - -function show_osc() - - --remember last time of invocation (mouse move) - state.showtime = mp.get_timer() - - state.osc_visible = true - - if (user_opts.fadeduration > 0) then - state.anitype = nil - end - -end - -function hide_osc() - if (user_opts.fadeduration > 0) then - if not(state.osc_visible == false) then - state.anitype = "out" - end - else - state.osc_visible = false - end -end - -function mouse_leave() - hide_osc() - -- reset mouse position - state.last_mouseX, state.last_mouseY = nil, nil -end - -function request_init() - state.initREQ = true -end - -function render() - local current_screen_sizeX, current_screen_sizeY = mp.get_screen_size() - local mouseX, mouseY = mp.get_mouse_pos() - local now = mp.get_timer() - - -- check if display changed, if so request reinit - if not (state.mp_screen_sizeX == current_screen_sizeX and state.mp_screen_sizeY == current_screen_sizeY) then - request_init() - state.mp_screen_sizeX, state.mp_screen_sizeY = current_screen_sizeX, current_screen_sizeY - end - - -- init management - if state.initREQ then - osc_init() - state.initREQ = false - - -- store initial mouse position - if (state.last_mouseX == nil or state.last_mouseY == nil) and not (mouseX == nil or mouseY == nil) then - state.last_mouseX, state.last_mouseY = mouseX, mouseY - end - end - - -- autohide - if not (state.showtime == nil) and (user_opts.hidetimeout >= 0) and (state.showtime + (user_opts.hidetimeout/1000) < now) and (state.active_element == nil) - and not (mouseX >= osc_param.posX - (osc_param.osc_w / 2) and mouseX <= osc_param.posX + (osc_param.osc_w / 2) - and mouseY >= osc_param.posY - (osc_param.osc_h / 2) and mouseY <= osc_param.posY + (osc_param.osc_h / 2)) then - hide_osc() - end - - -- fade animation - if not(state.anitype == nil) then - - if (state.anistart == nil) then - state.anistart = now - end - - if (now < state.anistart + (user_opts.fadeduration/1000)) then - - if (state.anitype == "in") then --fade in - state.osc_visible = true - state.animation = scale_value(state.anistart, (state.anistart + (user_opts.fadeduration/1000)), 255, 0, now) - elseif (state.anitype == "out") then --fade in - state.animation = scale_value(state.anistart, (state.anistart + (user_opts.fadeduration/1000)), 0, 255, now) - end - - else - if (state.anitype == "out") then state.osc_visible = false end - state.anistart = nil - state.animation = nil - state.anitype = nil - end - else - state.anistart = nil - state.animation = nil - state.anitype = nil - end - - -- actual rendering - local ass = assdraw.ass_new() - - -- Messages - render_message(ass) - - -- actual OSC - if state.osc_visible then - render_elements(ass) - end - - -- submit - local w, h, aspect = mp.get_screen_size() - mp.set_osd_ass(osc_param.playresy * aspect, osc_param.playresy, ass.text) - - -- set mouse area - local area_y0, area_y1 - if user_opts.valign > 0 then - -- deadzone above OSC - area_y0 = get_align(-1 + (2*user_opts.deadzonesize), osc_param.posY - (osc_param.osc_h / 2), 0, 0) - area_y1 = osc_param.playresy - else - -- deadzone below OSC - area_y0 = 0 - area_y1 = (osc_param.posY + (osc_param.osc_h / 2)) - + get_align(1 - (2*user_opts.deadzonesize), osc_param.playresy - (osc_param.posY + (osc_param.osc_h / 2)), 0, 0) - end - - --mouse show/hide area - mp.set_mouse_area(0, area_y0, osc_param.playresx, area_y1, "showhide") - - --mouse input area - if state.osc_visible then -- activate only when OSC is actually visible - mp.set_mouse_area( - osc_param.posX - (osc_param.osc_w / 2), osc_param.posY - (osc_param.osc_h / 2), - osc_param.posX + (osc_param.osc_w / 2), osc_param.posY + (osc_param.osc_h / 2), - "input") - mp.enable_key_bindings("input") - else - mp.disable_key_bindings("input") - end - -end - --- --- Eventhandling --- - -function process_event(source, what) - - if what == "down" then - - for n = 1, #elements do - - if not (elements[n].eventresponder == nil) then - if not (elements[n].eventresponder[source .. "_up"] == nil) or not (elements[n].eventresponder[source .. "_down"] == nil) then - - if mouse_hit(elements[n]) then - state.active_element = n - state.active_event_source = source - -- fire the down event if the element has one - if not (elements[n].eventresponder[source .. "_" .. what] == nil) then - elements[n].eventresponder[source .. "_" .. what](elements[n]) - end - end - end - - end - end - - elseif what == "up" then - - if not (state.active_element == nil) then - - local n = state.active_element - - if n == 0 then - --click on background (does not work) - elseif n > 0 and not (elements[n].eventresponder[source .. "_" .. what] == nil) then - - if mouse_hit(elements[n]) then - elements[n].eventresponder[source .. "_" .. what](elements[n]) - end - end - end - state.active_element = nil - state.mouse_down_counter = 0 - state.last_seek = nil - - elseif source == "mouse_move" then - local mouseX, mouseY = mp.get_mouse_pos() - if (user_opts.minmousemove == 0) or - (not ((state.last_mouseX == nil) or (state.last_mouseY == nil)) and - ((math.abs(mouseX - state.last_mouseX) >= user_opts.minmousemove) - or (math.abs(mouseY - state.last_mouseY) >= user_opts.minmousemove) - ) - ) then - show_osc() - end - state.last_mouseX, state.last_mouseY = mouseX, mouseY - - if not (state.active_element == nil) then - - local n = state.active_element - - if not (elements[n].eventresponder == nil) then - if not (elements[n].eventresponder[source] == nil) then - elements[n].eventresponder[source](elements[n]) - end - end - end - end -end - --- called by mpv on every frame -function tick() - if (mp.property_get("fullscreen") == "yes" and user_opts.showfullscreen) or (mp.property_get("fullscreen") == "no" and user_opts.showwindowed) then - render() - else - mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "") - end -end - -function mp_event(name, arg) - if name == "tick" then - tick() - elseif name == "start" or name == "track-layout" then - request_init() - elseif name == "end" then - end -end - --- mouse show/hide bindings -mp.set_key_bindings({ - {"mouse_move", function(e) process_event("mouse_move", nil) end}, - {"mouse_leave", mouse_leave}, -}, "showhide") -mp.enable_key_bindings("showhide", "allow-vo-dragging|allow-hide-cursor") - ---mouse input bindings -mp.set_key_bindings({ - {"mouse_btn0", function(e) process_event("mouse_btn0", "up") end, - function(e) process_event("mouse_btn0", "down") end}, - {"shift+mouse_btn0", function(e) process_event("shift+mouse_btn0", "up") end, - function(e) process_event("shift+mouse_btn0", "down") end}, - {"mouse_btn2", function(e) process_event("mouse_btn2", "up") end, - function(e) process_event("mouse_btn2", "down") end}, - {"mouse_btn0_dbl", "ignore"}, - {"shift+mouse_btn0_dbl", "ignore"}, - {"mouse_btn2_dbl", "ignore"}, -}, "input") -mp.enable_key_bindings("input") diff --git a/mpvcore/mp_core.h b/mpvcore/mp_core.h deleted file mode 100644 index 6d83be182a..0000000000 --- a/mpvcore/mp_core.h +++ /dev/null @@ -1,356 +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. - */ - -#ifndef MPLAYER_MP_CORE_H -#define MPLAYER_MP_CORE_H - -#include - -#include "mpvcore/options.h" -#include "demux/demux.h" - -// definitions used internally by the core player code - -#define INITIALIZED_VO 1 -#define INITIALIZED_AO 2 -#define INITIALIZED_GETCH2 8 -#define INITIALIZED_PLAYBACK 16 -#define INITIALIZED_LIBASS 32 -#define INITIALIZED_STREAM 64 -#define INITIALIZED_DEMUXER 512 -#define INITIALIZED_ACODEC 1024 -#define INITIALIZED_VCODEC 2048 -#define INITIALIZED_SUB 4096 -#define INITIALIZED_ALL 0xFFFF - - -enum stop_play_reason { - KEEP_PLAYING = 0, // must be 0, numeric values of others do not matter - AT_END_OF_FILE, // file has ended, prepare to play next - // also returned on unrecoverable playback errors - PT_NEXT_ENTRY, // prepare to play next entry in playlist - PT_CURRENT_ENTRY, // prepare to play mpctx->playlist->current - PT_STOP, // stop playback, clear playlist - PT_RESTART, // restart previous file - PT_QUIT, // stop playback, quit player -}; - -enum exit_reason { - EXIT_NONE, - EXIT_QUIT, - EXIT_PLAYED, - EXIT_ERROR, - EXIT_NOTPLAYED, - EXIT_SOMENOTPLAYED -}; - -struct timeline_part { - double start; - double source_start; - struct demuxer *source; -}; - -struct chapter { - double start; - char *name; -}; - -enum mp_osd_seek_info { - OSD_SEEK_INFO_BAR = 1, - OSD_SEEK_INFO_TEXT = 2, - OSD_SEEK_INFO_CHAPTER_TEXT = 4, - OSD_SEEK_INFO_EDITION = 8, -}; - -enum seek_type { - MPSEEK_NONE = 0, - MPSEEK_RELATIVE, - MPSEEK_ABSOLUTE, - MPSEEK_FACTOR, -}; - -struct track { - enum stream_type type; - // The type specific ID, also called aid (audio), sid (subs), vid (video). - // For UI purposes only; this ID doesn't have anything to do with any - // IDs coming from demuxers or container files. - int user_tid; - - // Same as stream->demuxer_id. -1 if not set. - int demuxer_id; - - char *title; - bool default_track; - bool attached_picture; - char *lang; - - // If this track is from an external file (e.g. subtitle file). - bool is_external; - char *external_filename; - bool auto_loaded; - - // If the track's stream changes with the timeline (ordered chapters). - bool under_timeline; - - // Value can change if under_timeline==true. - struct demuxer *demuxer; - // Invariant: !stream || stream->demuxer == demuxer - struct sh_stream *stream; - - // For external subtitles, which are read fully on init. Do not attempt - // to read packets from them. - bool preloaded; -}; - -enum { - MAX_NUM_VO_PTS = 100, -}; - -typedef struct MPContext { - struct mpv_global *global; - struct MPOpts *opts; - struct mp_log *log; - struct m_config *mconfig; - struct input_ctx *input; - struct osd_state *osd; - struct mp_osd_msg *osd_msg_stack; - char *terminal_osd_text; - - int add_osd_seek_info; // bitfield of enum mp_osd_seek_info - double osd_visible; // for the osd bar only - int osd_function; - double osd_function_visible; - double osd_last_update; - - struct playlist *playlist; - char *filename; // currently playing file - struct mp_resolve_result *resolve_result; - enum stop_play_reason stop_play; - unsigned int initialized_flags; // which subsystems have been initialized - - // Return code to use with PT_QUIT - enum exit_reason quit_player_rc; - int quit_custom_rc; - bool has_quit_custom_rc; - bool error_playing; - - int64_t shown_vframes, shown_aframes; - - struct demuxer **sources; - int num_sources; - - struct timeline_part *timeline; - int num_timeline_parts; - int timeline_part; - // NOTE: even if num_chapters==0, chapters being not NULL signifies presence - // of chapter metadata - struct chapter *chapters; - int num_chapters; - double video_offset; - - struct stream *stream; - struct demuxer *demuxer; - - struct track **tracks; - int num_tracks; - - char *track_layout_hash; - - // Selected tracks. NULL if no track selected. - struct track *current_track[STREAM_TYPE_COUNT]; - - struct sh_stream *sh[STREAM_TYPE_COUNT]; - struct sh_audio *sh_audio; // same as sh[STREAM_AUDIO]->audio - struct sh_video *sh_video; // same as sh[STREAM_VIDEO]->video - struct sh_sub *sh_sub; // same as sh[STREAM_SUB]->sub - - // Uses: accessing metadata (consider ordered chapters case, where the main - // demuxer defines metadata), or special purpose demuxers like TV. - struct demuxer *master_demuxer; - - struct mixer *mixer; - struct ao *ao; - struct vo *video_out; - - /* We're starting playback from scratch or after a seek. Show first - * video frame immediately and reinitialize sync. */ - bool restart_playback; - /* Set if audio should be timed to start with video frame after seeking, - * not set when e.g. playing cover art */ - bool sync_audio_to_video; - /* After playback restart (above) or audio stream change, adjust audio - * stream by cutting samples or adding silence at the beginning to make - * audio playback position match video position. */ - bool syncing_audio; - bool hrseek_active; - bool hrseek_framedrop; - double hrseek_pts; - // AV sync: the next frame should be shown when the audio out has this - // much (in seconds) buffered data left. Increased when more data is - // written to the ao, decreased when moving to the next frame. - // In the audio-only case used as a timer since the last seek - // by the audio CPU usage meter. - double delay; - // AV sync: time until next frame should be shown - double time_frame; - // How long the last vo flip() call took. Used to adjust timing with - // the goal of making flip() calls finish (rather than start) at the - // specified time. - double last_vo_flip_duration; - // How much video timing has been changed to make it match the audio - // timeline. Used for status line information only. - double total_avsync_change; - // Total number of dropped frames that were "approved" to be dropped. - // Actual dropping depends on --framedrop and decoder internals. - int drop_frame_cnt; - // Number of frames dropped in a row. - int dropped_frames; - // A-V sync difference when last frame was displayed. Kept to display - // the same value if the status line is updated at a time where no new - // video frame is shown. - double last_av_difference; - /* timestamp of video frame currently visible on screen - * (or at least queued to be flipped by VO) */ - double video_pts; - double last_seek_pts; - // As video_pts, but is not reset when seeking away. (For the very short - // period of time until a new frame is decoded and shown.) - double last_vo_pts; - // Video PTS, or audio PTS if video has ended. - double playback_pts; - // Used to determine whether the video filter chain was rebuilt. - long last_vf_reconfig_count; - - // History of video frames timestamps that were queued in the VO - // This includes even skipped frames during hr-seek - double vo_pts_history_pts[MAX_NUM_VO_PTS]; - // Whether the PTS at vo_pts_history[n] is after a seek reset - uint64_t vo_pts_history_seek[MAX_NUM_VO_PTS]; - uint64_t vo_pts_history_seek_ts; - uint64_t backstep_start_seek_ts; - bool backstep_active; - - double audio_delay; - - double last_heartbeat; - double last_metadata_update; - - double mouse_timer; - unsigned int mouse_event_ts; - bool mouse_cursor_visible; - - // used to prevent hanging in some error cases - double start_timestamp; - - // Timestamp from the last time some timing functions read the - // current time, in (occasionally wrapping) microseconds. Used - // to turn a new time value to a delta from last time. - int64_t last_time; - - // Used to communicate the parameters of a seek between parts - struct seek_params { - enum seek_type type; - double amount; - int exact; // -1 = disable, 0 = default, 1 = enable - // currently not set by commands, only used internally by seek() - int direction; // -1 = backward, 0 = default, 1 = forward - } seek; - - /* Heuristic for relative chapter seeks: keep track which chapter - * the user wanted to go to, even if we aren't exactly within the - * boundaries of that chapter due to an inaccurate seek. */ - int last_chapter_seek; - double last_chapter_pts; - - struct ass_library *ass_library; - - int last_dvb_step; - int dvbin_reopen; - - bool paused; - // step this many frames, then pause - int step_frames; - // Counted down each frame, stop playback if 0 is reached. (-1 = disable) - int max_frames; - bool playing_msg_shown; - - bool paused_for_cache; - - // Set after showing warning about decoding being too slow for realtime - // playback rate. Used to avoid showing it multiple times. - bool drop_message_shown; - - struct screenshot_ctx *screenshot_ctx; - struct command_ctx *command_ctx; - struct encode_lavc_context *encode_lavc_ctx; - struct lua_ctx *lua_ctx; -} MPContext; - - -// should not be global -extern FILE *edl_fd; -// These appear in options list -extern int forced_subs_only; - -void uninit_player(struct MPContext *mpctx, unsigned int mask); -void reinit_audio_chain(struct MPContext *mpctx); -double playing_audio_pts(struct MPContext *mpctx); -struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename); -int reinit_video_chain(struct MPContext *mpctx); -int reinit_video_filters(struct MPContext *mpctx); -int reinit_audio_filters(struct MPContext *mpctx); -void pause_player(struct MPContext *mpctx); -void unpause_player(struct MPContext *mpctx); -void add_step_frame(struct MPContext *mpctx, int dir); -void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, - int exact); -bool mp_seek_chapter(struct MPContext *mpctx, int chapter); -double get_time_length(struct MPContext *mpctx); -double get_start_time(struct MPContext *mpctx); -double get_current_time(struct MPContext *mpctx); -int get_percent_pos(struct MPContext *mpctx); -double get_current_pos_ratio(struct MPContext *mpctx, bool use_range); -int get_current_chapter(struct MPContext *mpctx); -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 mp_switch_track(struct MPContext *mpctx, enum stream_type type, - struct track *track); -struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type, - int tid); -bool mp_remove_track(struct MPContext *mpctx, struct track *track); -struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction, - bool force); -int mp_get_cache_percent(struct MPContext *mpctx); -void mp_write_watch_later_conf(struct MPContext *mpctx); -void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e); -struct playlist_entry *mp_resume_playlist(struct playlist *playlist, - struct MPOpts *opts); -void mp_force_video_refresh(struct MPContext *mpctx); - -void mp_print_version(int always); - -// timeline/tl_matroska.c -void build_ordered_chapter_timeline(struct MPContext *mpctx); -// timeline/tl_edl.c -void build_edl_timeline(struct MPContext *mpctx); -// timeline/tl_cue.c -void build_cue_timeline(struct MPContext *mpctx); - -#endif /* MPLAYER_MP_CORE_H */ diff --git a/mpvcore/mp_lua.c b/mpvcore/mp_lua.c deleted file mode 100644 index 4741cb1f33..0000000000 --- a/mpvcore/mp_lua.c +++ /dev/null @@ -1,683 +0,0 @@ -#include -#include - -#include -#include -#include - -#include "talloc.h" - -#include "mp_common.h" -#include "mp_lua.h" -#include "mp_core.h" -#include "mp_msg.h" -#include "m_property.h" -#include "m_option.h" -#include "command.h" -#include "input/input.h" -#include "sub/sub.h" -#include "osdep/timer.h" -#include "path.h" -#include "bstr.h" - -// List of builtin modules and their contents as strings. -// All these are generated from mpvcore/lua/*.lua -static const char *builtin_lua_scripts[][2] = { - {"mp.defaults", -# include "lua/defaults.inc" - }, - {"mp.assdraw", -# include "lua/assdraw.inc" - }, - {"@osc", -# include "lua/osc.inc" - }, - {0} -}; - -// Represents a loaded script. Each has its own Lua state. -struct script_ctx { - const char *name; - lua_State *state; - struct mp_log *log; - struct MPContext *mpctx; -}; - -struct lua_ctx { - struct script_ctx **scripts; - int num_scripts; -}; - -static struct script_ctx *find_script(struct lua_ctx *lctx, const char *name) -{ - for (int n = 0; n < lctx->num_scripts; n++) { - if (strcmp(lctx->scripts[n]->name, name) == 0) - return lctx->scripts[n]; - } - return NULL; -} - -static struct script_ctx *get_ctx(lua_State *L) -{ - lua_getfield(L, LUA_REGISTRYINDEX, "ctx"); - struct script_ctx *ctx = lua_touserdata(L, -1); - lua_pop(L, 1); - assert(ctx); - return ctx; -} - -static struct MPContext *get_mpctx(lua_State *L) -{ - return get_ctx(L)->mpctx; -} - -static int wrap_cpcall(lua_State *L) -{ - lua_CFunction fn = lua_touserdata(L, -1); - lua_pop(L, 1); - return fn(L); -} - -// Call the given function fn under a Lua error handler (similar to lua_cpcall). -// Pass the given number of args from the Lua stack to fn. -// Returns 0 (and empty stack) on success. -// Returns LUA_ERR[RUN|MEM|ERR] otherwise, with the error value on the stack. -static int mp_cpcall(lua_State *L, lua_CFunction fn, int args) -{ - // Don't use lua_pushcfunction() - it allocates memory on Lua 5.1. - // Instead, emulate C closures by making wrap_cpcall call fn. - lua_pushlightuserdata(L, fn); // args... fn - // Will always succeed if mp_lua_init() set it up correctly. - lua_getfield(L, LUA_REGISTRYINDEX, "wrap_cpcall"); // args... fn wrap_cpcall - lua_insert(L, -(args + 2)); // wrap_cpcall args... fn - return lua_pcall(L, args + 1, 0, 0); -} - -static void report_error(lua_State *L) -{ - const char *err = lua_tostring(L, -1); - mp_msg(MSGT_CPLAYER, MSGL_WARN, "[lua] Error: %s\n", - err ? err : "[unknown]"); - lua_pop(L, 1); -} - -static void add_functions(struct script_ctx *ctx); - -static char *script_name_from_filename(void *talloc_ctx, struct lua_ctx *lctx, - const char *fname) -{ - fname = mp_basename(fname); - if (fname[0] == '@') - fname += 1; - char *name = talloc_strdup(talloc_ctx, fname); - // Drop .lua extension - char *dot = strrchr(name, '.'); - if (dot) - *dot = '\0'; - // Turn it into a safe identifier - this is used with e.g. dispatching - // input via: "send scriptname ..." - for (int n = 0; name[n]; n++) { - char c = name[n]; - if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && - !(c >= '0' && c <= '9')) - name[n] = '_'; - } - // Make unique (stupid but simple) - while (find_script(lctx, name)) - name = talloc_strdup_append(name, "_"); - return name; -} - -static int load_file(struct script_ctx *ctx, const char *fname) -{ - int r = 0; - lua_State *L = ctx->state; - if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) { - report_error(L); - r = -1; - } - assert(lua_gettop(L) == 0); - return r; -} - -static int load_builtin(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - for (int n = 0; builtin_lua_scripts[n][0]; n++) { - if (strcmp(name, builtin_lua_scripts[n][0]) == 0) { - if (luaL_loadstring(L, builtin_lua_scripts[n][1])) - lua_error(L); - lua_call(L, 0, 1); - return 1; - } - } - return 0; -} - -// Execute "require " .. name -static bool require(lua_State *L, const char *name) -{ - char buf[80]; - // Lazy, but better than calling the "require" function manually - snprintf(buf, sizeof(buf), "require '%s'", name); - if (luaL_loadstring(L, buf) || lua_pcall(L, 0, 0, 0)) { - report_error(L); - return false; - } - return true; -} - -static void mp_lua_load_script(struct MPContext *mpctx, const char *fname) -{ - struct lua_ctx *lctx = mpctx->lua_ctx; - struct script_ctx *ctx = talloc_ptrtype(NULL, ctx); - *ctx = (struct script_ctx) { - .mpctx = mpctx, - .name = script_name_from_filename(ctx, lctx, fname), - }; - char *log_name = talloc_asprintf(ctx, "lua/%s", ctx->name); - ctx->log = mp_log_new(ctx, mpctx->log, log_name); - - lua_State *L = ctx->state = luaL_newstate(); - if (!L) - goto error_out; - - // used by get_ctx() - lua_pushlightuserdata(L, ctx); // ctx - lua_setfield(L, LUA_REGISTRYINDEX, "ctx"); // - - - lua_pushcfunction(L, wrap_cpcall); // closure - lua_setfield(L, LUA_REGISTRYINDEX, "wrap_cpcall"); // - - - luaL_openlibs(L); - - lua_newtable(L); // mp - lua_pushvalue(L, -1); // mp mp - lua_setglobal(L, "mp"); // mp - - add_functions(ctx); // mp - - lua_pushstring(L, ctx->name); // mp name - lua_setfield(L, -2, "script_name"); // mp - - lua_pop(L, 1); // - - - // Add a preloader for each builtin Lua module - lua_getglobal(L, "package"); // package - assert(lua_type(L, -1) == LUA_TTABLE); - lua_getfield(L, -1, "preload"); // package preload - assert(lua_type(L, -1) == LUA_TTABLE); - for (int n = 0; builtin_lua_scripts[n][0]; n++) { - lua_pushcfunction(L, load_builtin); // package preload load_builtin - lua_setfield(L, -2, builtin_lua_scripts[n][0]); - } - lua_pop(L, 2); // - - - assert(lua_gettop(L) == 0); - - if (!require(L, "mp.defaults")) { - report_error(L); - goto error_out; - } - - assert(lua_gettop(L) == 0); - - if (fname[0] == '@') { - if (!require(L, fname)) - goto error_out; - } else { - if (load_file(ctx, fname) < 0) - goto error_out; - } - - MP_TARRAY_APPEND(lctx, lctx->scripts, lctx->num_scripts, ctx); - return; - -error_out: - if (ctx->state) - lua_close(ctx->state); - talloc_free(ctx); -} - -static void kill_script(struct script_ctx *ctx) -{ - if (!ctx) - return; - struct lua_ctx *lctx = ctx->mpctx->lua_ctx; - lua_close(ctx->state); - for (int n = 0; n < lctx->num_scripts; n++) { - if (lctx->scripts[n] == ctx) { - MP_TARRAY_REMOVE_AT(lctx->scripts, lctx->num_scripts, n); - break; - } - } - talloc_free(ctx); -} - -static const char *log_level[] = { - [MSGL_FATAL] = "fatal", - [MSGL_ERR] = "error", - [MSGL_WARN] = "warn", - [MSGL_INFO] = "info", - [MSGL_V] = "verbose", - [MSGL_DBG2] = "debug", -}; - -static int script_log(lua_State *L) -{ - struct script_ctx *ctx = get_ctx(L); - - const char *level = luaL_checkstring(L, 1); - int msgl = -1; - for (int n = 0; n < MP_ARRAY_SIZE(log_level); n++) { - if (log_level[n] && strcasecmp(log_level[n], level) == 0) { - msgl = n; - break; - } - } - if (msgl < 0) - luaL_error(L, "Invalid log level '%s'", level); - - int last = lua_gettop(L); - lua_getglobal(L, "tostring"); // args... tostring - for (int i = 2; i <= last; i++) { - lua_pushvalue(L, -1); // args... tostring tostring - lua_pushvalue(L, i); // args... tostring tostring args[i] - lua_call(L, 1, 1); // args... tostring str - const char *s = lua_tostring(L, -1); - if (s == NULL) - return luaL_error(L, "Invalid argument"); - mp_msg_log(ctx->log, msgl, "%s%s", s, i > 0 ? " " : ""); - lua_pop(L, 1); // args... tostring - } - mp_msg_log(ctx->log, msgl, "\n"); - - return 0; -} - -static int script_find_config_file(lua_State *L) -{ - const char *s = luaL_checkstring(L, 1); - char *path = mp_find_user_config_file(s); - if (path) { - lua_pushstring(L, path); - } else { - lua_pushnil(L); - } - talloc_free(path); - return 1; -} - -static int run_event(lua_State *L) -{ - lua_getglobal(L, "mp_event"); // name arg mp_event - if (lua_isnil(L, -1)) - return 0; - lua_insert(L, -3); // mp_event name arg - lua_call(L, 2, 0); - return 0; -} - -void mp_lua_event(struct MPContext *mpctx, const char *name, const char *arg) -{ - // There is no proper subscription mechanism yet, so all scripts get it. - struct lua_ctx *lctx = mpctx->lua_ctx; - for (int n = 0; n < lctx->num_scripts; n++) { - struct script_ctx *ctx = lctx->scripts[n]; - lua_State *L = ctx->state; - lua_pushstring(L, name); - if (arg) { - lua_pushstring(L, arg); - } else { - lua_pushnil(L); - } - if (mp_cpcall(L, run_event, 2) != 0) - report_error(L); - } -} - -static int run_script_dispatch(lua_State *L) -{ - int id = lua_tointeger(L, 1); - const char *event = lua_tostring(L, 2); - lua_getglobal(L, "mp_script_dispatch"); - if (lua_isnil(L, -1)) - return 0; - lua_pushinteger(L, id); - lua_pushstring(L, event); - lua_call(L, 2, 0); - return 0; -} - -void mp_lua_script_dispatch(struct MPContext *mpctx, char *script_name, - int id, char *event) -{ - struct script_ctx *ctx = find_script(mpctx->lua_ctx, script_name); - if (!ctx) { - mp_msg(MSGT_CPLAYER, MSGL_V, - "Can't find script '%s' when handling input.\n", script_name); - return; - } - lua_State *L = ctx->state; - lua_pushinteger(L, id); - lua_pushstring(L, event); - if (mp_cpcall(L, run_script_dispatch, 2) != 0) - report_error(L); -} - -static int script_send_command(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - const char *s = luaL_checkstring(L, 1); - - mp_cmd_t *cmd = mp_input_parse_cmd(mpctx->input, bstr0((char*)s), ""); - if (!cmd) - luaL_error(L, "error parsing command"); - mp_input_queue_cmd(mpctx->input, cmd); - - return 0; -} - -static int script_property_list(lua_State *L) -{ - const struct m_option *props = mp_get_property_list(); - lua_newtable(L); - for (int i = 0; props[i].name; i++) { - lua_pushinteger(L, i + 1); - lua_pushstring(L, props[i].name); - lua_settable(L, -3); - } - return 1; -} - -static int script_property_string(lua_State *L) -{ - const struct m_option *props = mp_get_property_list(); - struct MPContext *mpctx = get_mpctx(L); - const char *name = luaL_checkstring(L, 1); - int type = lua_tointeger(L, lua_upvalueindex(1)) - ? M_PROPERTY_PRINT : M_PROPERTY_GET_STRING; - - char *result = NULL; - if (m_property_do(props, name, type, &result, mpctx) >= 0 && result) { - lua_pushstring(L, result); - talloc_free(result); - return 1; - } - if (type == M_PROPERTY_PRINT) { - lua_pushstring(L, ""); - return 1; - } - return 0; -} - -static int script_set_osd_ass(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - int res_x = luaL_checkinteger(L, 1); - int res_y = luaL_checkinteger(L, 2); - const char *text = luaL_checkstring(L, 3); - if (!mpctx->osd->external || - strcmp(mpctx->osd->external, text) != 0 || - mpctx->osd->external_res_x != res_x || - mpctx->osd->external_res_y != res_y) - { - talloc_free(mpctx->osd->external); - mpctx->osd->external = talloc_strdup(mpctx->osd, text); - mpctx->osd->external_res_x = res_x; - mpctx->osd->external_res_y = res_y; - osd_changed(mpctx->osd, OSDTYPE_EXTERNAL); - } - return 0; -} - -static int script_get_osd_resolution(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - int w, h; - osd_object_get_resolution(mpctx->osd, mpctx->osd->objs[OSDTYPE_EXTERNAL], - &w, &h); - lua_pushnumber(L, w); - lua_pushnumber(L, h); - return 2; -} - -static int script_get_screen_size(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - struct osd_object *obj = mpctx->osd->objs[OSDTYPE_EXTERNAL]; - double aspect = 1.0 * obj->vo_res.w / MPMAX(obj->vo_res.h, 1) / - obj->vo_res.display_par; - lua_pushnumber(L, obj->vo_res.w); - lua_pushnumber(L, obj->vo_res.h); - lua_pushnumber(L, aspect); - return 3; -} - -static int script_get_mouse_pos(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - int px, py; - mp_input_get_mouse_pos(mpctx->input, &px, &py); - double sw, sh; - osd_object_get_scale_factor(mpctx->osd, mpctx->osd->objs[OSDTYPE_EXTERNAL], - &sw, &sh); - lua_pushnumber(L, px * sw); - lua_pushnumber(L, py * sh); - return 2; -} - -static int script_get_timer(lua_State *L) -{ - lua_pushnumber(L, mp_time_sec()); - return 1; -} - -static int script_get_chapter_list(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - lua_newtable(L); // list - int num = get_chapter_count(mpctx); - for (int n = 0; n < num; n++) { - double time = chapter_start_time(mpctx, n); - char *name = chapter_display_name(mpctx, n); - lua_newtable(L); // list ch - lua_pushnumber(L, time); // list ch time - lua_setfield(L, -2, "time"); // list ch - lua_pushstring(L, name); // list ch name - lua_setfield(L, -2, "name"); // list ch - lua_pushinteger(L, n + 1); // list ch n1 - lua_insert(L, -2); // list n1 ch - lua_settable(L, -3); // list - talloc_free(name); - } - return 1; -} - -static const char *stream_type(enum stream_type t) -{ - switch (t) { - case STREAM_VIDEO: return "video"; - case STREAM_AUDIO: return "audio"; - case STREAM_SUB: return "sub"; - default: return "unknown"; - } -} - -static int script_get_track_list(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - lua_newtable(L); // list - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; - lua_newtable(L); // list track - - lua_pushstring(L, stream_type(track->type)); - lua_setfield(L, -2, "type"); - lua_pushinteger(L, track->user_tid); - lua_setfield(L, -2, "id"); - lua_pushboolean(L, track->default_track); - lua_setfield(L, -2, "default"); - lua_pushboolean(L, track->attached_picture); - lua_setfield(L, -2, "attached_picture"); - if (track->lang) { - lua_pushstring(L, track->lang); - lua_setfield(L, -2, "language"); - } - if (track->title) { - lua_pushstring(L, track->title); - lua_setfield(L, -2, "title"); - } - lua_pushboolean(L, track->is_external); - lua_setfield(L, -2, "external"); - if (track->external_filename) { - lua_pushstring(L, track->external_filename); - lua_setfield(L, -2, "external_filename"); - } - lua_pushboolean(L, track->auto_loaded); - lua_setfield(L, -2, "auto_loaded"); - - lua_pushinteger(L, n + 1); // list track n1 - lua_insert(L, -2); // list n1 track - lua_settable(L, -3); // list - } - return 1; -} - -static int script_input_define_section(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - char *section = (char *)luaL_checkstring(L, 1); - char *contents = (char *)luaL_checkstring(L, 2); - mp_input_define_section(mpctx->input, section, "