diff options
Diffstat (limited to 'core/command.c')
-rw-r--r-- | core/command.c | 789 |
1 files changed, 504 insertions, 285 deletions
diff --git a/core/command.c b/core/command.c index 05608c7df4..80f6f5d9d2 100644 --- a/core/command.c +++ b/core/command.c @@ -22,6 +22,10 @@ #include <string.h> #include <stdbool.h> #include <assert.h> +#include <time.h> + +#include <libavutil/avstring.h> +#include <libavutil/common.h> #include "config.h" #include "talloc.h" @@ -30,7 +34,7 @@ #include "stream/stream.h" #include "demux/demux.h" #include "demux/stheader.h" -#include "mplayer.h" +#include "resolve.h" #include "playlist.h" #include "playlist_parser.h" #include "sub/sub.h" @@ -50,9 +54,7 @@ #include "audio/filter/af.h" #include "video/decode/dec_video.h" #include "audio/decode/dec_audio.h" -#include "sub/spudec.h" #include "core/path.h" -#include "sub/ass_mp.h" #include "stream/tv.h" #include "stream/stream_radio.h" #include "stream/pvr.h" @@ -66,8 +68,9 @@ #include "screenshot.h" #include "core/mp_core.h" -#include "mp_fifo.h" -#include "libavutil/avstring.h" + +static void change_video_filters(MPContext *mpctx, const char *cmd, + const char *arg); static char *format_bitrate(int rate) { @@ -79,37 +82,17 @@ static char *format_delay(double time) return talloc_asprintf(NULL, "%d ms", ROUND(time * 1000)); } -static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy, - double *dx, double *dy) +// Get current mouse position in OSD coordinate space. +void mp_get_osd_mouse_pos(struct MPContext *mpctx, float *x, float *y) { - struct MPOpts *opts = &mpctx->opts; - struct vo *vo = mpctx->video_out; - //remove the borders, if any, and rescale to the range [0,1],[0,1] - if (opts->vo.fs) { //we are in full-screen mode - if (opts->vo.screenwidth > vo->dwidth) - // there are borders along the x axis - ix -= (opts->vo.screenwidth - vo->dwidth) / 2; - if (opts->vo.screenheight > vo->dheight) - // there are borders along the y axis (usual way) - iy -= (opts->vo.screenheight - vo->dheight) / 2; - - if (ix < 0 || ix > vo->dwidth) { - *dx = *dy = -1.0; - return; - } //we are on one of the borders - if (iy < 0 || iy > vo->dheight) { - *dx = *dy = -1.0; - return; - } //we are on one of the borders - } - - *dx = (double) ix / (double) vo->dwidth; - *dy = (double) iy / (double) vo->dheight; - - mp_msg(MSGT_CPLAYER, MSGL_V, - "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n", - *dx, *dy, opts->vo.screenwidth, opts->vo.screenheight, vo->dwidth, - vo->dheight, opts->vo.fs); + int wx, wy; + mp_input_get_mouse_pos(mpctx->input, &wx, &wy); + float p[2] = {wx, wy}; + // Raw window coordinates (VO mouse events) to OSD resolution. + if (mpctx->video_out) + vo_control(mpctx->video_out, VOCTRL_WINDOW_TO_OSD_COORDS, p); + *x = p[0]; + *y = p[1]; } // Property-option bridge. @@ -181,11 +164,14 @@ static int mp_property_media_title(m_option_t *prop, int action, void *arg, char *name = NULL; if (mpctx->resolve_result) name = mpctx->resolve_result->title; - if (name && name[0]) { + if (name && name[0]) return m_property_strdup_ro(prop, action, arg, name); - } else { - return mp_property_filename(prop, action, arg, mpctx); + 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, @@ -197,6 +183,20 @@ static int mp_property_stream_path(m_option_t *prop, int action, void *arg, 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) @@ -300,11 +300,14 @@ static int mp_property_percent_pos(m_option_t *prop, int action, switch (action) { case M_PROPERTY_SET: ; - int pos = *(int *)arg; + double pos = *(double *)arg; queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0); return M_PROPERTY_OK; case M_PROPERTY_GET: - *(int *)arg = get_percent_pos(mpctx); + *(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; @@ -328,6 +331,19 @@ static int mp_property_time_pos(m_option_t *prop, int action, return M_PROPERTY_NOT_IMPLEMENTED; } +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 m_property_double_ro(prop, action, arg, len - (pos - start)); +} + /// Current chapter (RW) static int mp_property_chapter(m_option_t *prop, int action, void *arg, MPContext *mpctx) @@ -350,14 +366,43 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg, case M_PROPERTY_SET: ; int step_all = *(int *)arg - chapter; chapter += step_all; - double next_pts = 0; - queue_seek(mpctx, MPSEEK_NONE, 0, 0); - chapter = seek_chapter(mpctx, chapter, &next_pts); - if (chapter >= 0) { - if (next_pts > -1.0) - queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0); - } else if (step_all > 0) + 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; @@ -402,6 +447,65 @@ static int mp_property_edition(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } +static struct mp_resolve_src *find_source(struct mp_resolve_result *res, + char *url) +{ + if (res->num_srcs == 0) + return NULL; + + int src = 0; + for (int n = 0; n < res->num_srcs; n++) { + if (strcmp(res->srcs[n]->url, res->url) == 0) { + src = n; + break; + } + } + return res->srcs[src]; +} + +static int mp_property_quvi_format(m_option_t *prop, int action, void *arg, + MPContext *mpctx) +{ + 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, 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; + 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 *arg = res->srcs[pos]->encid; + return mp_property_quvi_format(prop, M_PROPERTY_SET, &arg, 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) @@ -471,6 +575,17 @@ static int mp_property_angle(m_option_t *prop, int action, void *arg, resync_audio_stream(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; } @@ -495,6 +610,16 @@ static int mp_property_metadata(m_option_t *prop, int action, void *arg, *(char ***)arg = slist; return M_PROPERTY_OK; } + case M_PROPERTY_PRINT: { + char **list = demuxer->info; + char *res = NULL; + for (int n = 0; list && list[n]; n += 2) { + res = talloc_asprintf_append_buffer(res, "%s: %s\n", + list[n], list[n + 1]); + } + *(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 = demux_info_get(demuxer, ka->key); @@ -539,6 +664,18 @@ static int mp_property_cache(m_option_t *prop, int action, void *arg, 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) @@ -663,20 +800,10 @@ static int mp_property_channels(m_option_t *prop, int action, void *arg, return M_PROPERTY_UNAVAILABLE; switch (action) { case M_PROPERTY_PRINT: - switch (mpctx->sh_audio->channels) { - case 1: - *(char **) arg = talloc_strdup(NULL, "mono"); - break; - case 2: - *(char **) arg = talloc_strdup(NULL, "stereo"); - break; - default: - *(char **) arg = talloc_asprintf(NULL, "%d channels", - mpctx->sh_audio->channels); - } + *(char **) arg = mp_chmap_to_str(&mpctx->sh_audio->channels); return M_PROPERTY_OK; case M_PROPERTY_GET: - *(int *)arg = mpctx->sh_audio->channels; + *(int *)arg = mpctx->sh_audio->channels.num; return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; @@ -747,7 +874,7 @@ static int property_switch_track(m_option_t *prop, int action, void *arg, switch (action) { case M_PROPERTY_GET: - *(int *) arg = track ? track->user_tid : -1; + *(int *) arg = track ? track->user_tid : -2; return M_PROPERTY_OK; case M_PROPERTY_PRINT: if (!track) @@ -775,9 +902,62 @@ static int property_switch_track(m_option_t *prop, int action, void *arg, case M_PROPERTY_SET: mp_switch_track(mpctx, type, mp_track_by_tid(mpctx, type, *(int *)arg)); return M_PROPERTY_OK; - default: - return M_PROPERTY_NOT_IMPLEMENTED; } + 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, enum stream_type type) +{ + 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) @@ -864,21 +1044,52 @@ static int mp_property_fullscreen(m_option_t *prop, return mp_property_generic_option(prop, action, arg, mpctx); } -static int mp_property_deinterlace(m_option_t *prop, int action, - void *arg, MPContext *mpctx) +#define VF_DEINTERLACE_LABEL "deinterlace" + +#ifdef CONFIG_VF_LAVFI +#define VF_DEINTERLACE "@" VF_DEINTERLACE_LABEL ":lavfi=yadif" +#else +#define VF_DEINTERLACE "@" VF_DEINTERLACE_LABEL ":yadif" +#endif + +static int get_deinterlacing(struct MPContext *mpctx) { - if (!mpctx->sh_video || !mpctx->sh_video->vfilter) - return M_PROPERTY_UNAVAILABLE; 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) + change_video_filters(mpctx, "del", VF_DEINTERLACE); + } else { + int arg = enable; + if (vf->control(vf, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK) + change_video_filters(mpctx, "add", VF_DEINTERLACE); + } +} + +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 = !!enabled; + *(int *)arg = get_deinterlacing(mpctx) > 0; return M_PROPERTY_OK; case M_PROPERTY_SET: - vf->control(vf, VFCTRL_SET_DEINTERLACE, arg); + set_deinterlacing(mpctx, *(int *)arg); return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; @@ -1178,7 +1389,7 @@ static int mp_property_aspect(m_option_t *prop, int action, void *arg, if (f < 0.1) f = (float)mpctx->sh_video->disp_w / mpctx->sh_video->disp_h; mpctx->opts.movie_aspect = f; - video_reset_aspect(mpctx->sh_video); + video_reinit_vo(mpctx->sh_video); return M_PROPERTY_OK; } case M_PROPERTY_GET: @@ -1188,16 +1399,16 @@ static int mp_property_aspect(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } -// For subtitle related properties using the generic option bridge. +// 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_sub_helper(m_option_t *prop, int action, void *arg, +static int property_osd_helper(m_option_t *prop, int action, void *arg, MPContext *mpctx) { if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; if (action == M_PROPERTY_SET) - osd_subs_changed(mpctx->osd); + osd_changed_all(mpctx->osd); return mp_property_generic_option(prop, action, arg, mpctx); } @@ -1212,96 +1423,138 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg, static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; switch (action) { case M_PROPERTY_PRINT: - *(char **)arg = format_delay(sub_delay); + *(char **)arg = format_delay(opts->sub_delay); return M_PROPERTY_OK; } - return mp_property_generic_option(prop, action, arg, mpctx); + 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->sh_video) return M_PROPERTY_UNAVAILABLE; if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d/100", sub_pos); + *(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos); return M_PROPERTY_OK; } - return property_sub_helper(prop, action, arg, mpctx); + return property_osd_helper(prop, action, arg, mpctx); } -/// Subtitle visibility (RW) -static int mp_property_sub_visibility(m_option_t *prop, int action, - void *arg, MPContext *mpctx) +#ifdef CONFIG_TV + +static tvi_handle_t *get_tvh(struct MPContext *mpctx) { - struct MPOpts *opts = &mpctx->opts; + if (!(mpctx->master_demuxer && mpctx->master_demuxer->type == DEMUXER_TYPE_TV)) + return NULL; + return mpctx->master_demuxer->priv; +} - if (!mpctx->sh_video) +/// 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: - opts->sub_visibility = *(int *)arg; - vo_osd_changed(OSDTYPE_SUBTITLE); - if (vo_spudec) - vo_osd_changed(OSDTYPE_SPU); - return M_PROPERTY_OK; + return tv_set_color_options(tvh, prop->offset, *(int *) arg); case M_PROPERTY_GET: - *(int *)arg = opts->sub_visibility; - return M_PROPERTY_OK; + return tv_get_color_options(tvh, prop->offset, arg); } return M_PROPERTY_NOT_IMPLEMENTED; } -/// Show only forced subtitles (RW) -static int mp_property_sub_forced_only(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct MPOpts *opts = &mpctx->opts; +#endif - if (!vo_spudec) +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; - if (action == M_PROPERTY_SET) { - opts->forced_subs_only = *(int *)arg; - spudec_set_forced_subs_only(vo_spudec, opts->forced_subs_only); + 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; } - return mp_property_generic_option(prop, action, arg, mpctx); + 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; } - -#ifdef CONFIG_TV - -static tvi_handle_t *get_tvh(struct MPContext *mpctx) +static int mp_property_playlist_count(m_option_t *prop, int action, void *arg, + MPContext *mpctx) { - if (!(mpctx->master_demuxer && mpctx->master_demuxer->type == DEMUXER_TYPE_TV)) - return NULL; - return mpctx->master_demuxer->priv; + if (action == M_PROPERTY_GET) { + *(int *)arg = playlist_entry_count(mpctx->playlist); + return M_PROPERTY_OK; + } + return M_PROPERTY_NOT_IMPLEMENTED; } -/// TV color settings (RW) -static int mp_property_tv_color(m_option_t *prop, int action, void *arg, +static int mp_property_playlist(m_option_t *prop, int action, void *arg, MPContext *mpctx) { - tvi_handle_t *tvh = get_tvh(mpctx); - if (!tvh) - return M_PROPERTY_UNAVAILABLE; + if (action == M_PROPERTY_GET) { + char *res = talloc_strdup(NULL, ""); - 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); + 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; } -#endif +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; +} // Use option-to-property-bridge. (The property and option have the same names.) #define M_OPTION_PROPERTY(name) \ @@ -1314,12 +1567,17 @@ static int mp_property_tv_color(m_option_t *prop, int action, void *arg, #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, @@ -1330,6 +1588,7 @@ static const m_option_t mp_properties[] = { 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, @@ -1345,26 +1604,36 @@ static const m_option_t mp_properties[] = { { "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_INT, + { "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, 0, 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, CONF_TYPE_INT, - CONF_RANGE, -2, 10, NULL }, + { "angle", mp_property_angle, &m_option_type_dummy }, { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL }, 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, @@ -1382,8 +1651,7 @@ static const m_option_t mp_properties[] = { 0, 0, 0, NULL }, { "channels", mp_property_channels, CONF_TYPE_INT, 0, 0, 0, NULL }, - { "audio", mp_property_audio, CONF_TYPE_INT, - CONF_RANGE, -2, 65535, NULL }, + M_OPTION_PROPERTY_CUSTOM("aid", mp_property_audio), { "balance", mp_property_balance, CONF_TYPE_FLOAT, M_OPT_RANGE, -1, 1, NULL }, @@ -1426,24 +1694,21 @@ static const m_option_t mp_properties[] = { 0, 0, 0, NULL }, { "aspect", mp_property_aspect, CONF_TYPE_FLOAT, CONF_RANGE, 0, 10, NULL }, - { "video", mp_property_video, CONF_TYPE_INT, - CONF_RANGE, -2, 65535, NULL }, + M_OPTION_PROPERTY_CUSTOM("vid", mp_property_video), { "program", mp_property_program, CONF_TYPE_INT, CONF_RANGE, -1, 65535, NULL }, // Subs - { "sub", mp_property_sub, CONF_TYPE_INT, - M_OPT_MIN, -1, 0, NULL }, + 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), - { "sub-visibility", mp_property_sub_visibility, CONF_TYPE_FLAG, - M_OPT_RANGE, 0, 1, NULL }, - M_OPTION_PROPERTY_CUSTOM("sub-forced-only", mp_property_sub_forced_only), - M_OPTION_PROPERTY_CUSTOM("sub-scale", property_sub_helper), + 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_sub_helper), - M_OPTION_PROPERTY_CUSTOM("ass-vsfilter-aspect-compat", property_sub_helper), - M_OPTION_PROPERTY_CUSTOM("ass-style-override", property_sub_helper), + 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 #ifdef CONFIG_TV @@ -1457,6 +1722,10 @@ static const m_option_t mp_properties[] = { 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"), + {0}, }; @@ -1504,6 +1773,7 @@ static struct property_osd_display { { "pts-association-mode", "PTS association mode" }, { "hr-seek", "hr-seek" }, { "speed", _("Speed") }, + { "clock", _("Clock") }, // audio { "volume", _("Volume"), .osd_progbar = OSD_VOLUME }, { "mute", _("Mute") }, @@ -1633,99 +1903,40 @@ static const char *property_error_string(int error_value) return "UNKNOWN"; } -static void show_chapters_on_osd(MPContext *mpctx) -{ - 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); - } - - set_osd_msg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts.osd_duration, "%s", res); - talloc_free(res); -} - -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 void show_tracks_on_osd(MPContext *mpctx) +static void change_video_filters(MPContext *mpctx, const char *cmd, + const char *arg) { struct MPOpts *opts = &mpctx->opts; - 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"); - } + struct m_config *conf = mpctx->mconfig; + struct m_obj_settings *old_vf_settings = NULL; + bool success = false; + bool need_refresh = false; + double refresh_pts = mpctx->last_vo_pts; - res = talloc_asprintf_append(res, "\n"); - } + // The option parser is used to modify the filter list itself. + char optname[20]; + snprintf(optname, sizeof(optname), "vf-%s", cmd); + const struct m_option *type = m_config_get_option(conf, bstr0(optname)); - 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); + // Backup old settings, in case it fails + m_option_copy(type, &old_vf_settings, &opts->vf_settings); - set_osd_msg(mpctx, OSD_MSG_TEXT, 1, opts->osd_duration, "%s", res); - talloc_free(res); -} - -static void show_playlist_on_osd(MPContext *mpctx) -{ - struct MPOpts *opts = &mpctx->opts; - char *res = NULL; + if (m_config_set_option0(conf, optname, arg) >= 0) { + need_refresh = true; + success = reinit_video_filters(mpctx) >= 0; + } - 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); - } + if (!success) { + m_option_copy(type, &opts->vf_settings, &old_vf_settings); + if (need_refresh) + reinit_video_filters(mpctx); } + m_option_free(type, &old_vf_settings); - set_osd_msg(mpctx, OSD_MSG_TEXT, 1, opts->osd_duration, "%s", res); - talloc_free(res); + // Try to refresh the video by doing a precise seek to the currently + // displayed frame. + if (need_refresh && opts->pause) + queue_seek(mpctx, MPSEEK_ABSOLUTE, refresh_pts, 1); } void run_command(MPContext *mpctx, mp_cmd_t *cmd) @@ -1738,6 +1949,19 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) bool msg_osd = auto_osd || (cmd->on_osd & MP_ON_OSD_MSG); bool bar_osd = auto_osd || (cmd->on_osd & MP_ON_OSD_BAR); 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 = + |