7710 lines · 255.5 KB · C
RawDownload
/* * This file is part of mpv. * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mpv.  If not, see <http://www.gnu.org/licenses/>. */ #include <float.h>#include <stdlib.h>#include <inttypes.h>#include <string.h>#include <stdbool.h>#include <assert.h>#include <time.h>#include <math.h>#include <sys/types.h> #include <ass/ass.h>#include <libavutil/avstring.h>#include <libavutil/common.h>#include <libavutil/timecode.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"#include "input/keycodes.h"#include "stream/stream.h"#include "demux/demux.h"#include "demux/stheader.h"#include "common/playlist.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/vo.h"#include "video/csputils.h"#include "video/hwdec.h"#include "audio/aframe.h"#include "audio/format.h"#include "audio/out/ao.h"#include "video/out/bitmap_packer.h"#include "options/path.h"#include "screenshot.h"#include "misc/dispatch.h"#include "misc/language.h"#include "misc/node.h"#include "misc/thread_pool.h"#include "misc/thread_tools.h" #include "osdep/io.h"#include "osdep/subprocess.h"#include "osdep/terminal.h" #ifdef _WIN32#include <windows.h>#endif struct command_ctx {    // All properties, terminated with a {0} item.    struct m_property *properties;     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    // bitmap list can be manipulated without additional synchronization.    struct sub_bitmaps overlay_osd[2];    int overlay_osd_current;    struct bitmap_packer *overlay_packer;     struct hook_handler **hooks;    int num_hooks;    int64_t hook_seq; // for hook_handler.seq     struct ao_hotplug *hotplug;     struct mp_cmd_ctx *cache_dump_cmd; // in progress cache dumping     char **script_props;    mpv_node udata;    mpv_node mdata;     double cached_window_scale;}; 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 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 active;    // hook is currently in progress (only 1 at a time for now)}; 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+00A0 NO-BREAK SPACE#define NBSP "\xc2\xa0" const char list_current[] = BLACK_CIRCLE NBSP;const char list_normal[] = WHITE_CIRCLE NBSP; static int edit_filters(struct MPContext *mpctx, struct mp_log *log,                        enum stream_type mediatype,                        const char *cmd, const char *arg);static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,                       struct m_obj_settings *new_chain); static bool is_property_set(int action, void *val); static void hook_remove(struct MPContext *mpctx, struct hook_handler *h){    struct command_ctx *cmd = mpctx->command_ctx;    for (int n = 0; n < cmd->num_hooks; n++) {        if (cmd->hooks[n] == h) {            talloc_free(cmd->hooks[n]);            MP_TARRAY_REMOVE_AT(cmd->hooks, cmd->num_hooks, n);            return;        }    }    MP_ASSERT_UNREACHABLE();} bool mp_hook_test_completion(struct MPContext *mpctx, char *type){    struct command_ctx *cmd = mpctx->command_ctx;    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_id_exists(mpctx, h->client_id)) {                MP_WARN(mpctx, "client removed during hook handling\n");                hook_remove(mpctx, h);                break;            }            return false;        }    }    return true;} static int invoke_hook_handler(struct MPContext *mpctx, struct hook_handler *h){    MP_VERBOSE(mpctx, "Running hook: %s/%s\n", h->client, h->type);    h->active = true;     uint64_t reply_id = 0;    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_MSG(mpctx, mp_client_id_exists(mpctx, h->client_id) ? MSGL_WARN : MSGL_V,               "Failed sending hook command %s/%s. Removing hook.\n", h->client,               h->type);        hook_remove(mpctx, h);        mp_wakeup_core(mpctx); // repeat next iteration to finish    }    return r;} static int run_next_hook_handler(struct MPContext *mpctx, char *type, int index){    struct command_ctx *cmd = mpctx->command_ctx;     for (int n = index; n < cmd->num_hooks; n++) {        struct hook_handler *h = cmd->hooks[n];        if (strcmp(h->type, type) == 0)            return invoke_hook_handler(mpctx, h);    }     mp_wakeup_core(mpctx); // finished hook    return 0;} // Start processing script/client API hooks. This is asynchronous, and the// caller needs to use mp_hook_test_completion() to check whether they're done.void mp_hook_start(struct MPContext *mpctx, char *type){    while (run_next_hook_handler(mpctx, type, 0) < 0) {        // We can repeat this until all broken clients have been removed, and        // hook processing is successfully started.    }} 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 (h->client_id == client_id && h->seq == id) {            if (!h->active)                break;            h->active = false;            return run_next_hook_handler(mpctx, h->type, n + 1);        }    }     MP_ERR(mpctx, "invalid hook API usage\n");    return MPV_ERROR_INVALID_PARAMETER;} static int compare_hook(const void *pa, const void *pb){    struct hook_handler **h1 = (void *)pa;    struct hook_handler **h2 = (void *)pb;    if ((*h1)->priority != (*h2)->priority)        return (*h1)->priority - (*h2)->priority;    return (*h1)->seq - (*h2)->seq;} void mp_hook_add(struct MPContext *mpctx, char *client, int64_t client_id,                 const char *name, uint64_t user_id, int pri){    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,    };    MP_TARRAY_APPEND(cmd, cmd->hooks, cmd->num_hooks, h);    qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);} // Call before a seek, in order to allow revert-seek to undo the seek.void mark_seek(struct MPContext *mpctx){    struct command_ctx *cmd = mpctx->command_ctx;    double now = mp_time_sec();    if (now > cmd->last_seek_time + 2.0 || cmd->last_seek_pts == MP_NOPTS_VALUE)        cmd->last_seek_pts = get_current_time(mpctx);    cmd->last_seek_time = now;} static char *skip_n_lines(char *text, int lines){    while (text && lines > 0) {        char *next = strchr(text, '\n');        text = next ? next + 1 : NULL;        lines--;    }    return text;} static int count_lines(char *text){    if (!text[0])        return 0;     int count = 0;    while (text) {        count++;        char *next = strchr(text, '\n');        if (!next || (next[0] == '\n' && !next[1]))            break;        text = next + 1;    }    return count;} // Given a huge string separated by new lines, attempts to cut off text above// the current line to keep the line visible, and below to keep rendering// performance up. pos gives the current line (0 for the first line).// "text" might be returned as is, or it can be freed and a new allocation is// returned.// This is only a heuristic - we can't deal with line breaking.static char *cut_osd_list(struct MPContext *mpctx, char *header, char *text, int pos){    int count = count_lines(text);    if (!count)        return text;     int max_lines;    if (mpctx->video_out && mpctx->opts->video_osd) {        int screen_h, font_h;        osd_get_text_size(mpctx->osd, &screen_h, &font_h);        max_lines = screen_h / MPMAX(font_h, 1);    } else {        int w = -1;        max_lines = 24;        terminal_get_size(&w, &max_lines);        char *msg = mp_property_expand_escaped_string(mpctx, mpctx->opts->status_msg);        max_lines -= msg[0] ? count_lines(msg) : 1;        talloc_free(msg);    }    // Subtract 1 for the header.    max_lines--;     char *new = talloc_asprintf(NULL, "%s [%d/%d]:\n", header, pos + 1, count);    int start = MPMIN(MPMAX(pos - max_lines / 2, 0), count - max_lines);    char *head = skip_n_lines(text, start);    char *tail = skip_n_lines(head, max_lines);    new = talloc_asprintf_append_buffer(new, "%.*s",                            (int)(tail ? tail - head : strlen(head)), head);    // Strip the final newline to not print it in the terminal.    new[strlen(new) - 1] = '\0';     talloc_free(text);    return new;} static char *format_delay(double time){    return talloc_asprintf(NULL, "%.f ms", time * 1000);} // Property-option bridge. (Maps the property to the option with the same name.)static int mp_property_generic_option(void *ctx, struct m_property *prop,                                      int action, void *arg){    MPContext *mpctx = ctx;    struct m_config_option *opt =        m_config_get_co(mpctx->mconfig, bstr0(prop->name));     if (!opt)        return M_PROPERTY_UNKNOWN;     switch (action) {    case M_PROPERTY_GET_TYPE:        *(struct m_option *)arg = *(opt->opt);        return M_PROPERTY_OK;    case M_PROPERTY_GET:        if (!opt->data)            return M_PROPERTY_NOT_IMPLEMENTED;        m_option_copy(opt->opt, arg, opt->data);        return M_PROPERTY_OK;    case M_PROPERTY_SET:        if (m_config_set_option_raw(mpctx->mconfig, opt, arg, 0) < 0)            return M_PROPERTY_ERROR;        return M_PROPERTY_OK;    }    return M_PROPERTY_NOT_IMPLEMENTED;} /// Playback speed (RW)static int mp_property_playback_speed(void *ctx, struct m_property *prop,                                      int action, void *arg){    MPContext *mpctx = ctx;    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);} /// Playback pitch (RW)static int mp_property_playback_pitch(void *ctx, struct m_property *prop,                                      int action, void *arg){    MPContext *mpctx = ctx;    if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {        *(char **)arg = mp_format_double(NULL, mpctx->opts->playback_pitch, 2,                                         false, false, action != M_PROPERTY_FIXED_LEN_PRINT);        return M_PROPERTY_OK;    }    return mp_property_generic_option(mpctx, prop, action, arg);} static int mp_property_av_speed_correction(void *ctx, struct m_property *prop,                                           int action, void *arg){    MPContext *mpctx = ctx;    char *type = prop->priv;    double val = 0;    switch (type[0]) {    case 'a': val = mpctx->speed_factor_a; break;    case 'v': val = mpctx->speed_factor_v; break;    default: MP_ASSERT_UNREACHABLE();    }     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;    }     return m_property_double_ro(action, arg, val);} static int mp_property_display_sync_active(void *ctx, struct m_property *prop,                                           int action, void *arg){    MPContext *mpctx = ctx;    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)static int mp_property_path(void *ctx, struct m_property *prop,                            int action, void *arg){    MPContext *mpctx = ctx;    if (!mpctx->filename)        return M_PROPERTY_UNAVAILABLE;    return m_property_strdup_ro(action, arg, mpctx->filename);} static int mp_property_filename(void *ctx, struct m_property *prop,                                int action, void *arg){    MPContext *mpctx = ctx;    if (!mpctx->filename)        return M_PROPERTY_UNAVAILABLE;    char *filename = talloc_strdup(NULL, mpctx->filename);    if (mp_is_url(bstr0(filename)))        mp_url_unescape_inplace(filename);    char *f = (char *)mp_basename(filename);    if (!f[0])        f = filename;    if (action == M_PROPERTY_KEY_ACTION) {        struct m_property_action_arg *ka = arg;        if (strcmp(ka->key, "no-ext") == 0) {            action = ka->action;            arg = ka->arg;            bstr root;            if (mp_splitext(f, &root))                f = bstrto0(filename, root);        }    }    int r = m_property_strdup_ro(action, arg, f);

Lines 1–500 of 7710.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t