summaryrefslogtreecommitdiffstats
path: root/player/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/command.c')
-rw-r--r--player/command.c1659
1 files changed, 1230 insertions, 429 deletions
diff --git a/player/command.c b/player/command.c
index 098a11d24f..4814a897fd 100644
--- a/player/command.c
+++ b/player/command.c
@@ -24,16 +24,15 @@
#include <assert.h>
#include <time.h>
#include <math.h>
-#include <pthread.h>
#include <sys/types.h>
#include <ass/ass.h>
#include <libavutil/avstring.h>
#include <libavutil/common.h>
-#include "config.h"
#include "mpv_talloc.h"
#include "client.h"
+#include "external_files.h"
#include "common/av_common.h"
#include "common/codecs.h"
#include "common/msg.h"
@@ -41,6 +40,7 @@
#include "common/stats.h"
#include "filters/f_decoder_wrapper.h"
#include "command.h"
+#include "osdep/threads.h"
#include "osdep/timer.h"
#include "common/common.h"
#include "input/input.h"
@@ -49,11 +49,15 @@
#include "demux/demux.h"
#include "demux/stheader.h"
#include "common/playlist.h"
-#include "sub/osd.h"
#include "sub/dec_sub.h"
+#include "sub/osd.h"
+#include "sub/sd.h"
#include "options/m_option.h"
#include "options/m_property.h"
#include "options/m_config_frontend.h"
+#include "options/parse_configfile.h"
+#include "osdep/getpid.h"
+#include "video/out/gpu/context.h"
#include "video/out/vo.h"
#include "video/csputils.h"
#include "video/hwdec.h"
@@ -70,6 +74,7 @@
#include "osdep/io.h"
#include "osdep/subprocess.h"
+#include "osdep/terminal.h"
#include "core.h"
@@ -89,6 +94,8 @@ struct command_ctx {
char **warned_deprecated;
int num_warned_deprecated;
+ bool command_opts_processed;
+
struct overlay *overlays;
int num_overlays;
// One of these is in use by the OSD; the other one exists so that the
@@ -106,6 +113,8 @@ struct command_ctx {
struct mp_cmd_ctx *cache_dump_cmd; // in progress cache dumping
char **script_props;
+ mpv_node udata;
+ mpv_node mdata;
double cached_window_scale;
};
@@ -114,9 +123,18 @@ static const struct m_option script_props_type = {
.type = &m_option_type_keyvalue_list
};
+static const struct m_option udata_type = {
+ .type = CONF_TYPE_NODE
+};
+
+static const struct m_option mdata_type = {
+ .type = CONF_TYPE_NODE
+};
+
struct overlay {
struct mp_image *source;
int x, y;
+ int dw, dh;
};
struct hook_handler {
@@ -129,12 +147,27 @@ struct hook_handler {
bool active; // hook is currently in progress (only 1 at a time for now)
};
-// U+279C HEAVY ROUND-TIPPED RIGHTWARDS ARROW
+enum load_action_type {
+ LOAD_TYPE_REPLACE,
+ LOAD_TYPE_INSERT_AT,
+ LOAD_TYPE_INSERT_NEXT,
+ LOAD_TYPE_APPEND,
+};
+
+struct load_action {
+ enum load_action_type type;
+ bool play;
+};
+
+// U+25CB WHITE CIRCLE
+// U+25CF BLACK CIRCLE
// U+00A0 NO-BREAK SPACE
-#define ARROW_SP "\342\236\234\302\240"
+#define WHITECIRCLE "\xe2\x97\x8b"
+#define BLACKCIRCLE "\xe2\x97\x8f"
+#define NBSP "\xc2\xa0"
-const char list_current[] = OSD_ASS_0 ARROW_SP OSD_ASS_1;
-const char list_normal[] = OSD_ASS_0 "{\\alpha&HFF}" ARROW_SP "{\\r}" OSD_ASS_1;
+const char list_current[] = BLACKCIRCLE NBSP;
+const char list_normal[] = WHITECIRCLE NBSP;
static int edit_filters(struct MPContext *mpctx, struct mp_log *log,
enum stream_type mediatype,
@@ -154,7 +187,7 @@ static void hook_remove(struct MPContext *mpctx, struct hook_handler *h)
return;
}
}
- assert(0);
+ MP_ASSERT_UNREACHABLE();
}
bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
@@ -391,9 +424,9 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (action == M_PROPERTY_PRINT) {
- double speed = mpctx->opts->playback_speed;
- *(char **)arg = talloc_asprintf(NULL, "%.2f", speed);
+ if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
+ *(char **)arg = mp_format_double(NULL, mpctx->opts->playback_speed, 2,
+ false, false, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@@ -408,11 +441,12 @@ static int mp_property_av_speed_correction(void *ctx, struct m_property *prop,
switch (type[0]) {
case 'a': val = mpctx->speed_factor_a; break;
case 'v': val = mpctx->speed_factor_v; break;
- default: abort();
+ default: MP_ASSERT_UNREACHABLE();
}
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = talloc_asprintf(NULL, "%+.05f%%", (val - 1) * 100);
+ if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
+ *(char **)arg = mp_format_double(NULL, (val - 1) * 100, 2, true,
+ true, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
@@ -423,7 +457,14 @@ static int mp_property_display_sync_active(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_flag_ro(action, arg, mpctx->display_sync_active);
+ return m_property_bool_ro(action, arg, mpctx->display_sync_active);
+}
+
+static int mp_property_pid(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ // 32 bit on linux/windows - which C99 `int' is not guaranteed to hold
+ return m_property_int64_ro(action, arg, mp_getpid());
}
/// filename with path (RO)
@@ -503,28 +544,34 @@ static int mp_property_file_size(void *ctx, struct m_property *prop,
return m_property_int64_ro(action, arg, size);
}
-static int mp_property_media_title(void *ctx, struct m_property *prop,
- int action, void *arg)
+static const char *find_non_filename_media_title(MPContext *mpctx)
{
- MPContext *mpctx = ctx;
- char *name = NULL;
- if (mpctx->opts->media_title)
- name = mpctx->opts->media_title;
+ const char *name = mpctx->opts->media_title;
if (name && name[0])
- return m_property_strdup_ro(action, arg, name);
+ return name;
if (mpctx->demuxer) {
name = mp_tags_get_str(mpctx->demuxer->metadata, "service_name");
if (name && name[0])
- return m_property_strdup_ro(action, arg, name);
+ return name;
name = mp_tags_get_str(mpctx->demuxer->metadata, "title");
if (name && name[0])
- return m_property_strdup_ro(action, arg, name);
+ return name;
name = mp_tags_get_str(mpctx->demuxer->metadata, "icy-title");
if (name && name[0])
- return m_property_strdup_ro(action, arg, name);
+ return name;
}
if (mpctx->playing && mpctx->playing->title)
- return m_property_strdup_ro(action, arg, mpctx->playing->title);
+ return mpctx->playing->title;
+ return NULL;
+}
+
+static int mp_property_media_title(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ const char *name = find_non_filename_media_title(mpctx);
+ if (name && name[0])
+ return m_property_strdup_ro(action, arg, name);
return mp_property_filename(ctx, prop, action, arg);
}
@@ -631,8 +678,9 @@ static int mp_property_avsync(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->ao_chain || !mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = talloc_asprintf(NULL, "%7.3f", mpctx->last_av_difference);
+ if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
+ *(char **)arg = mp_format_double(NULL, mpctx->last_av_difference, 4,
+ true, false, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
return m_property_double_ro(action, arg, mpctx->last_av_difference);
@@ -887,8 +935,8 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
} else // Absolute set
step_all = *(int *)arg - chapter;
chapter += step_all;
- if (chapter < -1)
- chapter = -1;
+ if (chapter < 0) // avoid using -1 if first chapter starts at 0
+ chapter = (chapter_start_time(mpctx, 0) <= 0) ? 0 : -1;
if (chapter >= num && step_all > 0) {
if (mpctx->opts->keep_open) {
seek_to_last_frame(mpctx);
@@ -903,9 +951,9 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
} else {
double pts = chapter_start_time(mpctx, chapter);
if (pts != MP_NOPTS_VALUE) {
- queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, MPSEEK_DEFAULT, 0);
+ queue_seek(mpctx, MPSEEK_CHAPTER, pts, MPSEEK_DEFAULT, 0);
mpctx->last_chapter_seek = chapter;
- mpctx->last_chapter_pts = pts;
+ mpctx->last_chapter_flag = true;
}
}
return M_PROPERTY_OK;
@@ -985,7 +1033,7 @@ static int parse_node_chapters(struct MPContext *mpctx,
}
}
- mp_notify(mpctx, MPV_EVENT_CHAPTER_CHANGE, NULL);
+ mp_notify(mpctx, MP_EVENT_CHAPTER_CHANGE, NULL);
mp_notify_property(mpctx, "chapter-list");
return M_PROPERTY_OK;
@@ -1091,7 +1139,7 @@ static int get_edition_entry(int item, int action, void *arg, void *ctx)
{"id", SUB_PROP_INT(item)},
{"title", SUB_PROP_STR(title),
.unavailable = !title},
- {"default", SUB_PROP_FLAG(ed->default_edition)},
+ {"default", SUB_PROP_BOOL(ed->default_edition)},
{0}
};
@@ -1326,14 +1374,37 @@ static int mp_property_core_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_flag_ro(action, arg, !mpctx->playback_active);
+ return m_property_bool_ro(action, arg, !mpctx->playback_active);
+}
+
+static int mp_property_deinterlace(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (!vo_c)
+ return M_PROPERTY_UNAVAILABLE;
+
+ bool deinterlace_active = mp_output_chain_deinterlace_active(vo_c->filter);
+ return m_property_bool_ro(action, arg, deinterlace_active);
}
static int mp_property_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_flag_ro(action, arg, mpctx->stop_play == PT_STOP);
+ return m_property_bool_ro(action, arg, mpctx->stop_play == PT_STOP);
+}
+
+static int mp_property_window_id(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct vo *vo = mpctx->video_out;
+ int64_t wid;
+ if (!vo || vo_control(vo, VOCTRL_GET_WINDOW_ID, &wid) <= 0)
+ return M_PROPERTY_UNAVAILABLE;
+ return m_property_int64_ro(action, arg, wid);
}
static int mp_property_eof_reached(void *ctx, struct m_property *prop,
@@ -1344,7 +1415,7 @@ static int mp_property_eof_reached(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
bool eof = mpctx->video_status == STATUS_EOF &&
mpctx->audio_status == STATUS_EOF;
- return m_property_flag_ro(action, arg, eof);
+ return m_property_bool_ro(action, arg, eof);
}
static int mp_property_seeking(void *ctx, struct m_property *prop,
@@ -1353,14 +1424,14 @@ static int mp_property_seeking(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->playback_initialized)
return M_PROPERTY_UNAVAILABLE;
- return m_property_flag_ro(action, arg, !mpctx->restart_complete);
+ return m_property_bool_ro(action, arg, !mpctx->restart_complete);
}
static int mp_property_playback_abort(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_flag_ro(action, arg, !mpctx->playing || mpctx->stop_play);
+ return m_property_bool_ro(action, arg, !mpctx->playing || mpctx->stop_play);
}
static int mp_property_cache_speed(void *ctx, struct m_property *prop,
@@ -1424,7 +1495,7 @@ static int mp_property_demuxer_cache_idle(void *ctx, struct m_property *prop,
struct demux_reader_state s;
demux_get_reader_state(mpctx->demuxer, &s);
- return m_property_flag_ro(action, arg, s.idle);
+ return m_property_bool_ro(action, arg, s.idle);
}
static int mp_property_demuxer_cache_state(void *ctx, struct m_property *prop,
@@ -1503,7 +1574,7 @@ static int mp_property_paused_for_cache(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->playback_initialized)
return M_PROPERTY_UNAVAILABLE;
- return m_property_flag_ro(action, arg, mpctx->paused_for_cache);
+ return m_property_bool_ro(action, arg, mpctx->paused_for_cache);
}
static int mp_property_cache_buffering(void *ctx, struct m_property *prop,
@@ -1523,7 +1594,7 @@ static int mp_property_demuxer_is_network(void *ctx, struct m_property *prop,
if (!mpctx->demuxer)
return M_PROPERTY_UNAVAILABLE;
- return m_property_flag_ro(action, arg, mpctx->demuxer->is_network);
+ return m_property_bool_ro(action, arg, mpctx->demuxer->is_network);
}
@@ -1545,7 +1616,7 @@ static int mp_property_seekable(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->demuxer)
return M_PROPERTY_UNAVAILABLE;
- return m_property_flag_ro(action, arg, mpctx->demuxer->seekable);
+ return m_property_bool_ro(action, arg, mpctx->demuxer->seekable);
}
static int mp_property_partially_seekable(void *ctx, struct m_property *prop,
@@ -1554,14 +1625,14 @@ static int mp_property_partially_seekable(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->demuxer)
return M_PROPERTY_UNAVAILABLE;
- return m_property_flag_ro(action, arg, mpctx->demuxer->partially_seekable);
+ return m_property_bool_ro(action, arg, mpctx->demuxer->partially_seekable);
}
static int mp_property_mixer_active(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_flag_ro(action, arg, !!mpctx->ao);
+ return m_property_bool_ro(action, arg, !!mpctx->ao);
}
/// Volume (RW)
@@ -1587,6 +1658,28 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
return mp_property_generic_option(mpctx, prop, action, arg);
}
+static int mp_property_volume_gain(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct MPOpts *opts = mpctx->opts;
+
+ switch (action) {
+ case M_PROPERTY_GET_CONSTRICTED_TYPE:
+ *(struct m_option *)arg = (struct m_option){
+ .type = CONF_TYPE_FLOAT,
+ .min = opts->softvol_gain_min,
+ .max = opts->softvol_gain_max,
+ };
+ return M_PROPERTY_OK;
+ case M_PROPERTY_PRINT:
+ *(char **)arg = talloc_asprintf(NULL, "%.1f", opts->softvol_gain);
+ return M_PROPERTY_OK;
+ }
+
+ return mp_property_generic_option(mpctx, prop, action, arg);
+}
+
static int mp_property_ao_volume(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -1597,17 +1690,14 @@ static int mp_property_ao_volume(void *ctx, struct m_property *prop,
switch (action) {
case M_PROPERTY_SET: {
- float value = *(float *)arg;
- ao_control_vol_t vol = {value, value};
+ float vol = *(float *)arg;
if (ao_control(ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
return M_PROPERTY_OK;
}
case M_PROPERTY_GET: {
- ao_control_vol_t vol = {0};
- if (ao_control(ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK)
+ if (ao_control(ao, AOCONTROL_GET_VOLUME, arg) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
- *(float *)arg = (vol.left + vol.right) / 2.0f;
return M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
@@ -1618,10 +1708,10 @@ static int mp_property_ao_volume(void *ctx, struct m_property *prop,
};
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
- ao_control_vol_t vol = {0};
+ float vol = 0;
if (ao_control(ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
- *(char **)arg = talloc_asprintf(NULL, "%.f", (vol.left + vol.right) / 2.0f);
+ *(char **)arg = talloc_asprintf(NULL, "%.f", vol);
return M_PROPERTY_OK;
}
}
@@ -1652,7 +1742,7 @@ static int mp_property_ao_mute(void *ctx, struct m_property *prop,
return M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
- *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_BOOL};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -1694,7 +1784,7 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop,
if (mp_property_generic_option(mpctx, prop, M_PROPERTY_GET, &name) < 1)
name = NULL;
- struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
+ struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug, mpctx->ao);
for (int n = 0; n < list->num_devices; n++) {
struct ao_device_desc *dev = &list->devices[n];
if (dev->name && name && strcmp(dev->name, name) == 0) {
@@ -1716,7 +1806,7 @@ static int mp_property_audio_devices(void *ctx, struct m_property *prop,
struct command_ctx *cmd = mpctx->command_ctx;
create_hotplug(mpctx);
- struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
+ struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug, mpctx->ao);
return m_property_read_list(action, arg, list->num_devices,
get_device_entry, list);
}
@@ -1724,8 +1814,7 @@ static int mp_property_audio_devices(void *ctx, struct m_property *prop,
static int mp_property_ao(void *ctx, struct m_property *p, int action, void *arg)
{
MPContext *mpctx = ctx;
- return m_property_strdup_ro(action, arg,
- mpctx->ao ? ao_get_name(mpctx->ao) : NULL);
+ return m_property_strdup_ro(action, arg, mpctx->ao ? ao_get_name(mpctx->ao) : NULL);
}
/// Audio delay (RW)
@@ -1740,28 +1829,6 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop,
return mp_property_generic_option(mpctx, prop, action, arg);
}
-/// Audio codec tag (RO)
-static int mp_property_audio_codec_name(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_AUDIO];
- const char *c = track && track->stream ? track->stream->codec->codec : NULL;
- return m_property_strdup_ro(action, arg, c);
-}
-
-/// Audio codec name (RO)
-static int mp_property_audio_codec(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_AUDIO];
- char desc[256] = "";
- if (track && track->dec)
- mp_decoder_wrapper_get_desc(track->dec, desc, sizeof(desc));
- return m_property_strdup_ro(action, arg, desc[0] ? desc : NULL);
-}
-
static int property_audiofmt(struct mp_aframe *fmt, int action, void *arg)
{
if (!fmt || !mp_aframe_config_is_valid(fmt))
@@ -1855,8 +1922,11 @@ static int property_switch_track(void *ctx, struct m_property *prop,
case M_PROPERTY_PRINT:
if (track) {
char *lang = track->lang;
- if (!lang)
+ if (!lang && type != STREAM_VIDEO) {
lang = "unknown";
+ } else if (!lang) {
+ lang = "";
+ }
if (track->title) {
*(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
@@ -1907,10 +1977,6 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
struct mp_codec_params p =
track->stream ? *track->stream->codec : (struct mp_codec_params){0};
- char decoder_desc[256] = {0};
- if (track->dec)
- mp_decoder_wrapper_get_desc(track->dec, decoder_desc, sizeof(decoder_desc));
-
bool has_rg = track->stream && track->stream->codec->replaygain_data;
struct replaygain_data rg = has_rg ? *track->stream->codec->replaygain_data
: (struct replaygain_data){0};
@@ -1929,6 +1995,7 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
}
}
+ bool has_crop = mp_rect_w(p.crop) > 0 && mp_rect_h(p.crop) > 0;
struct m_sub_property props[] = {
{"id", SUB_PROP_INT(track->user_tid)},
{"type", SUB_PROP_STR(stream_type_name(track->type)),
@@ -1941,24 +2008,39 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
.unavailable = !track->lang},
{"audio-channels", SUB_PROP_INT(track_channels(track)),
.unavailable = track_channels(track) <= 0},
- {"albumart", SUB_PROP_FLAG(track->attached_picture)},
- {"default", SUB_PROP_FLAG(track->default_track)},
- {"forced", SUB_PROP_FLAG(track->forced_track)},
- {"dependent", SUB_PROP_FLAG(track->dependent_track)},
- {"visual-impaired", SUB_PROP_FLAG(track->visual_impaired_track)},
- {"hearing-impaired", SUB_PROP_FLAG(track->hearing_impaired_track)},
- {"external", SUB_PROP_FLAG(track->is_external)},
- {"selected", SUB_PROP_FLAG(track->selected)},
+ {"image", SUB_PROP_BOOL(track->image)},
+ {"albumart", SUB_PROP_BOOL(track->attached_picture)},
+ {"default", SUB_PROP_BOOL(track->default_track)},
+ {"forced", SUB_PROP_BOOL(track->forced_track)},
+ {"dependent", SUB_PROP_BOOL(track->dependent_track)},
+ {"visual-impaired", SUB_PROP_BOOL(track->visual_impaired_track)},
+ {"hearing-impaired", SUB_PROP_BOOL(track->hearing_impaired_track)},
+ {"external", SUB_PROP_BOOL(track->is_external)},
+ {"selected", SUB_PROP_BOOL(track->selected)},
{"main-selection", SUB_PROP_INT(order), .unavailable = order < 0},
{"external-filename", SUB_PROP_STR(track->external_filename),
.unavailable = !track->external_filename},
{"ff-index", SUB_PROP_INT(track->ff_index)},
- {"decoder-desc", SUB_PROP_STR(decoder_desc),
- .unavailable = !decoder_desc[0]},
+ {"hls-bitrate", SUB_PROP_INT(track->hls_bitrate),
+ .unavailable = !track->hls_bitrate},
+ {"program-id", SUB_PROP_INT(track->program_id),
+ .unavailable = track->program_id < 0},
+ {"decoder", SUB_PROP_STR(p.decoder),
+ .unavailable = !p.decoder},
+ {"decoder-desc", SUB_PROP_STR(p.decoder_desc),
+ .unavailable = !p.decoder_desc},
{"codec", SUB_PROP_STR(p.codec),
.unavailable = !p.codec},
+ {"codec-desc", SUB_PROP_STR(p.codec_desc),
+ .unavailable = !p.codec_desc},
+ {"codec-profile", SUB_PROP_STR(p.codec_profile),
+ .unavailable = !p.codec_profile},
{"demux-w", SUB_PROP_INT(p.disp_w), .unavailable = !p.disp_w},
{"demux-h", SUB_PROP_INT(p.disp_h), .unavailable = !p.disp_h},
+ {"demux-crop-x",SUB_PROP_INT(p.crop.x0), .unavailable = !has_crop},
+ {"demux-crop-y",SUB_PROP_INT(p.crop.y0), .unavailable = !has_crop},
+ {"demux-crop-w",SUB_PROP_INT(mp_rect_w(p.crop)), .unavailable = !has_crop},
+ {"demux-crop-h",SUB_PROP_INT(mp_rect_h(p.crop)), .unavailable = !has_crop},
{"demux-channel-count", SUB_PROP_INT(p.channels.num),
.unavailable = !p.channels.num},
{"demux-channels", SUB_PROP_STR(mp_chmap_to_str(&p.channels)),
@@ -1969,6 +2051,7 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
{"demux-bitrate", SUB_PROP_INT(p.bitrate), .unavailable = p.bitrate <= 0},
{"demux-rotation", SUB_PROP_INT(p.rotate), .unavailable = p.rotate <= 0},
{"demux-par", SUB_PROP_DOUBLE(par), .unavailable = par <= 0},
+ {"format-name", SUB_PROP_STR(p.format_name), .unavailable = !p.format_name},
{"replaygain-track-peak", SUB_PROP_FLOAT(rg.track_peak),
.unavailable = !has_rg},
{"replaygain-track-gain", SUB_PROP_FLOAT(rg.track_gain),
@@ -2067,6 +2150,16 @@ static int property_current_tracks(void *ctx, struct m_property *prop,
return M_PROPERTY_UNKNOWN;
struct track *t = mpctx->current_track[order][type];
+
+ if (!t && mpctx->lavfi) {
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ if (mpctx->tracks[n]->type == type && mpctx->tracks[n]->selected) {
+ t = mpctx->tracks[n];
+ break;
+ }
+ }
+ }
+
if (!t)
return M_PROPERTY_UNAVAILABLE;
@@ -2095,7 +2188,7 @@ static int mp_property_hwdec_current(void *ctx, struct m_property *prop,
char *current = NULL;
mp_decoder_wrapper_control(dec, VDCTRL_GET_HWDEC, &current);
- if (!current)
+ if (!current || !current[0])
current = "no";
return m_property_strdup_ro(action, arg, current);
}
@@ -2151,80 +2244,129 @@ static int mp_property_frame_count(void *ctx, struct m_property *prop,
return m_property_int_ro(action, arg, frames);
}
-/// Video codec tag (RO)
-static int mp_property_video_format(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- const char *c = track && track->stream ? track->stream->codec->codec : NULL;
- return m_property_strdup_ro(action, arg, c);
-}
+static const char *get_aspect_ratio_name(double ratio)
+{
+ // Depending on cropping/mastering exact ratio may differ.
+#define RATIO_THRESH 0.025
+#define RATIO_CASE(ref, name) \
+ if (fabs(ratio - (ref)) < RATIO_THRESH) \
+ return name; \
+
+ // https://en.wikipedia.org/wiki/Aspect_ratio_(image)
+ RATIO_CASE(9.0 / 16.0, "Vertical")
+ RATIO_CASE(1.0, "Square");
+ RATIO_CASE(19.0 / 16.0, "Movietone Ratio");
+ RATIO_CASE(5.0 / 4.0, "5:4");
+ RATIO_CASE(4.0 / 3.0, "4:3");
+ RATIO_CASE(11.0 / 8.0, "Academy Ratio");
+ RATIO_CASE(1.43, "IMAX Ratio");
+ RATIO_CASE(3.0 / 2.0, "VistaVision Ratio");
+ RATIO_CASE(16.0 / 10.0, "16:10");
+ RATIO_CASE(5.0 / 3.0, "35mm Widescreen Ratio");
+ RATIO_CASE(16.0 / 9.0, "16:9");
+ RATIO_CASE(7.0 / 4.0, "Early 35mm Widescreen Ratio");
+ RATIO_CASE(1.85, "Academy Flat");
+ RATIO_CASE(256.0 / 135.0, "SMPTE/DCI Ratio");
+ RATIO_CASE(2.0, "Univisium");
+ RATIO_CASE(2.208, "70mm film");
+ RATIO_CASE(2.35, "Scope");
+ RATIO_CASE(2.39, "Panavision");
+ RATIO_CASE(2.55, "Original CinemaScope");
+ RATIO_CASE(2.59, "Full-frame Cinerama");
+ RATIO_CASE(24.0 / 9.0, "Full-frame Super 16mm");
+ RATIO_CASE(2.76, "Ultra Panavision 70");
+ RATIO_CASE(32.0 / 9.0, "32:9");
+ RATIO_CASE(3.6, "Ultra-WideScreen 3.6");
+ RATIO_CASE(4.0, "Polyvision");
+ RATIO_CASE(12.0, "Circle-Vision 360°");
-/// Video codec name (RO)
-static int mp_property_video_codec(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- char desc[256] = "";
- if (track && track->dec)
- mp_decoder_wrapper_get_desc(track->dec, desc, sizeof(desc));
- return m_property_strdup_ro(action, arg, desc[0] ? desc : NULL);
+ return NULL;
+
+#undef RATIO_THRESH
+#undef RATIO_CASE
}
-static int property_imgparams(struct mp_image_params p, int action, void *arg)
+static int property_imgparams(const struct mp_image_params *p, int action, void *arg)
{
- if (!p.imgfmt)
+ if (!p->imgfmt && !p->imgfmt_name)
return M_PROPERTY_UNAVAILABLE;
int d_w, d_h;
- mp_image_params_get_dsize(&p, &d_w, &d_h);
+ mp_image_params_get_dsize(p, &d_w, &d_h);
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p.imgfmt);
int bpp = 0;
- for (int i = 0; i < desc.num_planes; i++)
- bpp += desc.bpp[i] >> (desc.xs[i] + desc.ys[i]);
-
- // Alpha type is not supported by FFmpeg, so MP_ALPHA_AUTO may mean alpha
- // is of an unknown type, or simply not present. Normalize to AUTO=no alpha.
- if (!!(desc.flags & MP_IMGFLAG_ALPHA) != (p.alpha != MP_ALPHA_AUTO)) {
- p.alpha =
- (desc.flags & MP_IMGFLAG_ALPHA) ? MP_ALPHA_STRAIGHT : MP_ALPHA_AUTO;
- }
-
+ enum pl_alpha_mode alpha = p->repr.alpha;
+ int fmt = p->hw_subfmt ? p->hw_subfmt : p->imgfmt;
+ if (fmt) {
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
+ for (int i = 0; i < desc.num_planes; i++)
+ bpp += desc.bpp[i] >> (desc.xs[i] + desc.ys[i]);
+
+ // Alpha type is not supported by FFmpeg, so PL_ALPHA_UNKNOWN may mean alpha
+ // is of an unknown type, or simply not present. Normalize to AUTO=no alpha.
+ if (!!(desc.flags & MP_IMGFLAG_ALPHA) != (alpha != PL_ALPHA_UNKNOWN))
+ alpha = (desc.flags & MP_IMGFLAG_ALPHA) ? PL_ALPHA_INDEPENDENT : PL_ALPHA_UNKNOWN;
+ }
+
+ const struct pl_hdr_metadata *hdr = &p->color.hdr;
+ bool has_cie_y = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_CIE_Y);
+ bool has_hdr10 = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_HDR10);
+ bool has_hdr10plus = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_HDR10PLUS);
+
+ bool has_crop = mp_rect_w(p->crop) > 0 && mp_rect_h(p->crop) > 0;
+ const char *aspect_name = get_aspect_ratio_name(d_w / (double)d_h);
+ const char *sar_name = get_aspect_ratio_name(p->w / (double)p->h);
+ const char *pixelformat_name = p->imgfmt_name ? p->imgfmt_name :
+ mp_imgfmt_to_name(p->imgfmt);
struct m_sub_property props[] = {
- {"pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p.imgfmt))},
- {"hw-pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p.hw_subfmt)),
- .unavailable = !p.hw_subfmt},
+ {"pixelformat", SUB_PROP_STR(pixelformat_name)},
+ {"hw-pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p->hw_subfmt)),
+ .unavailable = !p->hw_subfmt},
{"average-bpp", SUB_PROP_INT(bpp),
.unavailable = !bpp},
- {"w", SUB_PROP_INT(p.w)},
- {"h", SUB_PROP_INT(p.h)},
+ {"w", SUB_PROP_INT(p->w)},
+ {"h", SUB_PROP_INT(p->h)},
{"dw", SUB_PROP_INT(d_w)},
{"dh", SUB_PROP_INT(d_h)},
+ {"crop-x", SUB_PROP_INT(p->crop.x0), .unavailable = !has_crop},
+ {"crop-y", SUB_PROP_INT(p->crop.y0), .unavailable = !has_crop},
+ {"crop-w", SUB_PROP_INT(mp_rect_w(p->crop)), .unavailable = !has_crop},
+ {"crop-h", SUB_PROP_INT(mp_rect_h(p->crop)), .unavailable = !has_crop},
{"aspect", SUB_PROP_FLOAT(d_w / (double)d_h)},
- {"par", SUB_PROP_FLOAT(p.p_w / (double)p.p_h)},
+ {"aspect-name", SUB_PROP_STR(aspect_name), .unavailable = !aspect_name},
+ {"par", SUB_PROP_FLOAT(p->p_w / (double)p->p_h)},
+ {"sar", SUB_PROP_FLOAT(p->w / (double)p->h)},
+ {"sar-name", SUB_PROP_STR(sar_name), .unavailable = !sar_name},
{"colormatrix",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.color.space))},
+ SUB_PROP_STR(m_opt_choice_str(pl_csp_names, p->repr.sys))},
{"colorlevels",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.color.levels))},
+ SUB_PROP_STR(m_opt_choice_str(pl_csp_levels_names, p->repr.levels))},
{"primaries",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.color.primaries))},
+ SUB_PROP_STR(m_opt_choice_str(pl_csp_prim_names, p->color.primaries))},
{"gamma",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.color.gamma))},
- {"sig-peak", SUB_PROP_FLOAT(p.color.sig_peak)},
+ SUB_PROP_STR(m_opt_choice_str(pl_csp_trc_names, p->color.transfer))},
+ {"sig-peak", SUB_PROP_FLOAT(p->color.hdr.max_luma / MP_REF_WHITE)},
{"light",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_light_names, p.color.light))},
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_light_names, p->light))},
{"chroma-location",
- SUB_PROP_STR(m_opt_choice_str(mp_chroma_names, p.chroma_location))},
+ SUB_PROP_STR(m_opt_choice_str(pl_chroma_names, p->chroma_location))},
{"stereo-in",
- SUB_PROP_STR(m_opt_choice_str(mp_stereo3d_names, p.stereo3d))},
- {"rotate", SUB_PROP_INT(p.rotate)},
+ SUB_PROP_STR(m_opt_choice_str(mp_stereo3d_names, p->stereo3d))},
+ {"rotate", SUB_PROP_INT(p->rotate)},
{"alpha",
- SUB_PROP_STR(m_opt_choice_str(mp_alpha_names, p.alpha)),
+ SUB_PROP_STR(m_opt_choice_str(pl_alpha_names, alpha)),
// avoid using "auto" for "no", so just make it unavailable
- .unavailable = p.alpha == MP_ALPHA_AUTO},
+ .unavailable = alpha == PL_ALPHA_UNKNOWN},
+ {"min-luma", SUB_PROP_FLOAT(hdr->min_luma), .unavailable = !has_hdr10},
+ {"max-luma", SUB_PROP_FLOAT(hdr->max_luma), .unavailable = !has_hdr10},
+ {"max-cll", SUB_PROP_FLOAT(hdr->max_cll), .unavailable = !has_hdr10},
+ {"max-fall", SUB_PROP_FLOAT(hdr->max_fall), .unavailable = !has_hdr10},
+ {"scene-max-r", SUB_PROP_FLOAT(hdr->scene_max[0]), .unavailable = !has_hdr10plus},
+ {"scene-max-g", SUB_PROP_FLOAT(hdr->scene_max[1]), .unavailable = !has_hdr10plus},
+ {"scene-max-b", SUB_PROP_FLOAT(hdr->scene_max[2]), .unavailable = !has_hdr10plus},
+ {"scene-avg", SUB_PROP_FLOAT(hdr->scene_avg), .unavailable = !has_hdr10plus},
+ {"max-pq-y", SUB_PROP_FLOAT(hdr->max_pq_y), .unavailable = !has_cie_y},
+ {"avg-pq-y", SUB_PROP_FLOAT(hdr->avg_pq_y), .unavailable = !has_cie_y},
{0}
};
@@ -2236,13 +2378,48 @@ static struct mp_image_params get_video_out_params(struct MPContext *mpctx)
if (!mpctx->vo_chain)
return (struct mp_image_params){0};
- return mpctx->vo_chain->filter->output_params;
+ struct mp_image_params o_params = mpctx->vo_chain->filter->output_params;
+ if (mpctx->video_out) {
+ struct m_geometry *gm = &mpctx->video_out->opts->video_crop;
+ if (gm->xy_valid || (gm->wh_valid && (gm->w > 0 || gm->h > 0)))
+ {
+ m_rect_apply(&o_params.crop, o_params.w, o_params.h, gm);
+ }
+ }
+
+ return o_params;
}
static int mp_property_vo_imgparams(void *ctx, struct m_property *prop,
int action, void *arg)
{
- return property_imgparams(get_video_out_params(ctx), action, arg);
+ MPContext *mpctx = ctx;
+ struct vo *vo = mpctx->video_out;
+ if (!vo)
+ return M_PROPERTY_UNAVAILABLE;
+
+ int valid = m_property_read_sub_validate(ctx, prop, action, arg);
+ if (valid != M_PROPERTY_VALID)
+ return valid;
+
+ struct mp_image_params p = vo_get_current_params(vo);
+ return property_imgparams(&p, action, arg);
+}
+
+static int mp_property_tgt_imgparams(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct vo *vo = mpctx->video_out;
+ if (!mpctx->video_out)
+ return M_PROPERTY_UNAVAILABLE;
+
+ int valid = m_property_read_sub_validate(ctx, prop, action, arg);
+ if (valid != M_PROPERTY_VALID)
+ return valid;
+
+ struct mp_image_params p = vo_get_target_params(vo);
+ return property_imgparams(&p, action, arg);
}
static int mp_property_dec_imgparams(void *ctx, struct m_property *prop,
@@ -2251,11 +2428,17 @@ static int mp_property_dec_imgparams(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
struct mp_image_params p = {0};
struct vo_chain *vo_c = mpctx->vo_chain;
- if (vo_c && vo_c->track)
- mp_decoder_wrapper_get_video_dec_params(vo_c->track->dec, &p);
+ if (!vo_c || !vo_c->track)
+ return M_PROPERTY_UNAVAILABLE;
+
+ int valid = m_property_read_sub_validate(ctx, prop, action, arg);
+ if (valid != M_PROPERTY_VALID)
+ return valid;
+
+ mp_decoder_wrapper_get_video_dec_params(vo_c->track->dec, &p);
if (!p.imgfmt)
return M_PROPERTY_UNAVAILABLE;
- return property_imgparams(p, action, arg);
+ return property_imgparams(&p, action, arg);
}
static int mp_property_vd_imgparams(void *ctx, struct m_property *prop,
@@ -2269,7 +2452,7 @@ static int mp_property_vd_imgparams(void *ctx, struct m_property *prop,
struct mp_codec_params *c =