summaryrefslogtreecommitdiffstats
path: root/core/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/command.c')
-rw-r--r--core/command.c789
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 =
+