summaryrefslogtreecommitdiffstats
path: root/player/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/command.c')
-rw-r--r--player/command.c2620
1 files changed, 1902 insertions, 718 deletions
diff --git a/player/command.c b/player/command.c
index 37ace97ba6..b7130814d8 100644
--- a/player/command.c
+++ b/player/command.c
@@ -15,6 +15,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <float.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
@@ -23,21 +24,23 @@
#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"
#include "common/msg_control.h"
+#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"
@@ -46,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.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"
@@ -67,6 +74,7 @@
#include "osdep/io.h"
#include "osdep/subprocess.h"
+#include "osdep/terminal.h"
#include "core.h"
@@ -78,15 +86,16 @@ struct command_ctx {
// All properties, terminated with a {0} item.
struct m_property *properties;
- bool is_idle;
-
double last_seek_time;
double last_seek_pts;
double marked_pts;
+ bool marked_permanent;
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
@@ -104,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;
};
@@ -112,27 +123,51 @@ 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 {
- char *client; // client API user name
+ char *client; // client mpv_handle name (for logging)
+ int64_t client_id; // client mpv_handle ID
char *type; // kind of hook, e.g. "on_load"
uint64_t user_id; // user-chosen ID
int priority; // priority for global hook order
int64_t seq; // unique ID, != 0, also for fixed order on equal priorities
- bool legacy; // old cmd based hook API
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,
@@ -152,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)
@@ -161,7 +196,7 @@ bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
for (int n = 0; n < cmd->num_hooks; n++) {
struct hook_handler *h = cmd->hooks[n];
if (h->active && strcmp(h->type, type) == 0) {
- if (!mp_client_exists(mpctx, h->client)) {
+ if (!mp_client_id_exists(mpctx, h->client_id)) {
MP_WARN(mpctx, "client removed during hook handling\n");
hook_remove(mpctx, h);
break;
@@ -178,29 +213,14 @@ static int invoke_hook_handler(struct MPContext *mpctx, struct hook_handler *h)
h->active = true;
uint64_t reply_id = 0;
- void *data;
- int msg;
- if (h->legacy) {
- mpv_event_client_message *m = talloc_ptrtype(NULL, m);
- *m = (mpv_event_client_message){0};
- MP_TARRAY_APPEND(m, m->args, m->num_args, "hook_run");
- MP_TARRAY_APPEND(m, m->args, m->num_args,
- talloc_asprintf(m, "%llu", (long long)h->user_id));
- MP_TARRAY_APPEND(m, m->args, m->num_args,
- talloc_asprintf(m, "%llu", (long long)h->seq));
- data = m;
- msg = MPV_EVENT_CLIENT_MESSAGE;
- } else {
- mpv_event_hook *m = talloc_ptrtype(NULL, m);
- *m = (mpv_event_hook){
- .name = talloc_strdup(m, h->type),
- .id = h->seq,
- },
- reply_id = h->user_id;
- data = m;
- msg = MPV_EVENT_HOOK;
- }
- int r = mp_client_send_event(mpctx, h->client, reply_id, msg, data);
+ mpv_event_hook *m = talloc_ptrtype(NULL, m);
+ *m = (mpv_event_hook){
+ .name = talloc_strdup(m, h->type),
+ .id = h->seq,
+ },
+ reply_id = h->user_id;
+ char *name = mp_tprintf(22, "@%"PRIi64, h->client_id);
+ int r = mp_client_send_event(mpctx, name, reply_id, MPV_EVENT_HOOK, m);
if (r < 0) {
MP_WARN(mpctx, "Sending hook command failed. Removing hook.\n");
hook_remove(mpctx, h);
@@ -233,13 +253,13 @@ void mp_hook_start(struct MPContext *mpctx, char *type)
}
}
-int mp_hook_continue(struct MPContext *mpctx, char *client, uint64_t id)
+int mp_hook_continue(struct MPContext *mpctx, int64_t client_id, uint64_t id)
{
struct command_ctx *cmd = mpctx->command_ctx;
for (int n = 0; n < cmd->num_hooks; n++) {
struct hook_handler *h = cmd->hooks[n];
- if (strcmp(h->client, client) == 0 && h->seq == id) {
+ if (h->client_id == client_id && h->seq == id) {
if (!h->active)
break;
h->active = false;
@@ -260,22 +280,19 @@ static int compare_hook(const void *pa, const void *pb)
return (*h1)->seq - (*h2)->seq;
}
-void mp_hook_add(struct MPContext *mpctx, const char *client, const char *name,
- uint64_t user_id, int pri, bool legacy)
+void mp_hook_add(struct MPContext *mpctx, char *client, int64_t client_id,
+ const char *name, uint64_t user_id, int pri)
{
- if (legacy)
- MP_WARN(mpctx, "The old hook API is deprecated! Use the libmpv API.\n");
-
struct command_ctx *cmd = mpctx->command_ctx;
struct hook_handler *h = talloc_ptrtype(cmd, h);
int64_t seq = ++cmd->hook_seq;
*h = (struct hook_handler){
.client = talloc_strdup(h, client),
+ .client_id = client_id,
.type = talloc_strdup(h, name),
.user_id = user_id,
.priority = pri,
.seq = seq,
- .legacy = legacy,
};
MP_TARRAY_APPEND(cmd, cmd->hooks, cmd->num_hooks, h);
qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);
@@ -407,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);
@@ -424,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;
}
@@ -439,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)
@@ -519,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);
}
@@ -647,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);
@@ -674,7 +706,8 @@ static int mp_property_frame_drop_dec(void *ctx, struct m_property *prop,
if (!dec)
return M_PROPERTY_UNAVAILABLE;
- return m_property_int_ro(action, arg, dec->dropped_frames);
+ return m_property_int_ro(action, arg,
+ mp_decoder_wrapper_get_frames_dropped(dec));
}
static int mp_property_mistimed_frame_count(void *ctx, struct m_property *prop,
@@ -753,7 +786,6 @@ static int mp_property_percent_pos(void *ctx, struct m_property *prop,
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_DOUBLE,
- .flags = M_OPT_RANGE,
.min = 0,
.max = 100,
};
@@ -872,7 +904,6 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_INT,
- .flags = M_OPT_MIN | M_OPT_MAX,
.min = -1,
.max = num - 1,
};
@@ -904,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);
@@ -920,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;
@@ -1002,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;
@@ -1060,18 +1091,39 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct demuxer *demuxer = mpctx->demuxer;
+ char *name = NULL;
+
+ if (!demuxer)
+ return mp_property_generic_option(mpctx, prop, action, arg);
+
+ int ed = demuxer->edition;
- if (action == M_PROPERTY_GET_CONSTRICTED_TYPE && demuxer) {
+ if (demuxer->num_editions <= 1)
+ return M_PROPERTY_UNAVAILABLE;
+
+ switch (action) {
+ case M_PROPERTY_GET_CONSTRICTED_TYPE: {
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_INT,
- .flags = M_OPT_RANGE,
.min = 0,
.max = demuxer->num_editions - 1,
};
return M_PROPERTY_OK;
}
-
- return mp_property_generic_option(mpctx, prop, action, arg);
+ case M_PROPERTY_PRINT: {
+ if (ed < 0)
+ return M_PROPERTY_UNAVAILABLE;
+ name = mp_tags_get_str(demuxer->editions[ed].metadata, "title");
+ if (name) {
+ *(char **) arg = talloc_strdup(NULL, name);
+ } else {
+ *(char **) arg = talloc_asprintf(NULL, "%d", ed + 1);
+ }
+ return M_PROPERTY_OK;
+ }
+ default:
+ return mp_property_generic_option(mpctx, prop, action, arg);
+ }
}
static int get_edition_entry(int item, int action, void *arg, void *ctx)
@@ -1087,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}
};
@@ -1322,15 +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;
- struct command_ctx *cmd = mpctx->command_ctx;
- return m_property_flag_ro(action, arg, cmd->is_idle);
+ 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,
@@ -1341,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,
@@ -1350,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,
@@ -1421,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,
@@ -1460,6 +1534,8 @@ static int mp_property_demuxer_cache_state(void *ctx, struct m_property *prop,
node_map_add_int64(r, "fw-bytes", s.fw_bytes);
if (s.file_cache_bytes >= 0)
node_map_add_int64(r, "file-cache-bytes", s.file_cache_bytes);
+ if (s.bytes_per_second > 0)
+ node_map_add_int64(r, "raw-input-rate", s.bytes_per_second);
if (s.seeking != MP_NOPTS_VALUE)
node_map_add_double(r, "debug-seeking", s.seeking);
node_map_add_int64(r, "debug-low-level-seeks", s.low_level_seeks);
@@ -1498,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,
@@ -1518,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);
}
@@ -1540,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,
@@ -1549,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)
@@ -1570,7 +1646,6 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
case M_PROPERTY_GET_CONSTRICTED_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
- .flags = M_OPT_RANGE,
.min = 0,
.max = opts->softvol_max,
};
@@ -1583,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)
{
@@ -1593,32 +1690,28 @@ 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:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
- .flags = M_OPT_RANGE,
.min = 0,
.max = 100,
};
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;
}
}
@@ -1649,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;
@@ -1691,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) {
@@ -1713,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);
}
@@ -1721,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)
@@ -1737,26 +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];
- const char *c = track && track->dec ? track->dec->decoder_desc : NULL;
- return m_property_strdup_ro(action, arg, c);
-}
-
static int property_audiofmt(struct mp_aframe *fmt, int action, void *arg)
{
if (!fmt || !mp_aframe_config_is_valid(fmt))
@@ -1850,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\")",
@@ -1880,10 +1955,8 @@ static int property_switch_track(void *ctx, struct m_property *prop,
} else {
// Simply cycle between "no" and "auto". It's possible that this does
// not always do what the user means, but keep the complexity low.
- mpctx->opts->stream_id[order][type] =
- mpctx->opts->stream_id[order][type] == -1 ? -2 : -1;
- m_config_notify_change_opt_ptr(mpctx->mconfig,
- &mpctx->opts->stream_id[order][type]);
+ mark_track_selection(mpctx, order, type,
+ mpctx->opts->stream_id[order][type] == -1 ? -2 : -1);
}
return M_PROPERTY_OK;
}
@@ -1904,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};
- const char *decoder_desc = NULL;
- if (track->dec)
- decoder_desc = track->dec->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};
@@ -1916,6 +1985,17 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
if (p.par_h)
par = p.par_w / (double) p.par_h;
+ int order = -1;
+ if (track->selected) {
+ for (int i = 0; i < num_ptracks[track->type]; i++) {
+ if (mpctx->current_track[i][track->type] == track) {
+ order = i;
+ break;
+ }
+ }
+ }
+
+ 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)),
@@ -1928,23 +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},
+ {"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)),
@@ -1955,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),
@@ -2022,6 +2119,63 @@ static int property_list_tracks(void *ctx, struct m_property *prop,
get_track_entry, mpctx);
}
+static int property_current_tracks(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+
+ if (action != M_PROPERTY_KEY_ACTION)
+ return M_PROPERTY_UNAVAILABLE;
+
+ int type = -1;
+ int order = 0;
+
+ struct m_property_action_arg *ka = arg;
+ bstr key;
+ char *rem;
+ m_property_split_path(ka->key, &key, &rem);
+
+ if (bstr_equals0(key, "video")) {
+ type = STREAM_VIDEO;
+ } else if (bstr_equals0(key, "audio")) {
+ type = STREAM_AUDIO;
+ } else if (bstr_equals0(key, "sub")) {
+ type = STREAM_SUB;
+ } else if (bstr_equals0(key, "sub2")) {
+ type = STREAM_SUB;
+ order = 1;
+ }
+
+ if (type < 0)
+ 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;
+
+ int index = -1;
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ if (mpctx->tracks[n] == t) {
+ index = n;
+ break;
+ }
+ }
+ assert(index >= 0);
+
+ char *name = mp_tprintf(80, "track-list/%d/%s", index, rem);
+ return mp_property_do(name, ka->action, ka->arg, ctx);
+}
+
static int mp_property_hwdec_current(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2034,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);
}
@@ -2090,67 +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)</