diff options
Diffstat (limited to 'player/command.c')
-rw-r--r-- | player/command.c | 1659 |
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, ¤t); - 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 = |