diff options
Diffstat (limited to 'player')
-rw-r--r-- | player/audio.c | 2 | ||||
-rw-r--r-- | player/client.c | 19 | ||||
-rw-r--r-- | player/command.c | 162 | ||||
-rw-r--r-- | player/core.h | 40 | ||||
-rw-r--r-- | player/external_files.c | 21 | ||||
-rw-r--r-- | player/lavfi.c | 26 | ||||
-rw-r--r-- | player/loadfile.c | 287 | ||||
-rw-r--r-- | player/lua.c | 2 | ||||
-rw-r--r-- | player/lua/defaults.lua | 10 | ||||
-rw-r--r-- | player/lua/osc.lua | 62 | ||||
-rw-r--r-- | player/lua/ytdl_hook.lua | 90 | ||||
-rw-r--r-- | player/main.c | 13 | ||||
-rw-r--r-- | player/misc.c | 71 | ||||
-rw-r--r-- | player/osd.c | 18 | ||||
-rw-r--r-- | player/playloop.c | 6 | ||||
-rw-r--r-- | player/screenshot.c | 16 | ||||
-rw-r--r-- | player/scripting.c | 44 | ||||
-rw-r--r-- | player/video.c | 34 |
18 files changed, 681 insertions, 242 deletions
diff --git a/player/audio.c b/player/audio.c index 5c393f3d3a..e1766143b9 100644 --- a/player/audio.c +++ b/player/audio.c @@ -817,7 +817,7 @@ static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf, break; res = decode_new_frame(ao_c); - if (res == AD_NO_PROGRESS) + if (res == AD_NO_PROGRESS || res == AD_WAIT) break; if (res < 0) { // drain filters first (especially for true EOF case) diff --git a/player/client.c b/player/client.c index 85e3c4021c..31a55332d2 100644 --- a/player/client.c +++ b/player/client.c @@ -359,6 +359,8 @@ void mpv_detach_destroy(mpv_handle *ctx) if (!ctx) return; + MP_VERBOSE(ctx, "Exiting...\n"); + // reserved_events equals the number of asynchronous requests that weren't // yet replied. In order to avoid that trying to reply to a removed client // causes a crash, block until all asynchronous requests were served. @@ -407,7 +409,13 @@ void mpv_terminate_destroy(mpv_handle *ctx) if (!ctx) return; - mpv_command(ctx, (const char*[]){"quit", NULL}); + if (ctx->mpctx->initialized) { + mpv_command(ctx, (const char*[]){"quit", NULL}); + } else { + mp_dispatch_lock(ctx->mpctx->dispatch); + ctx->mpctx->stop_play = PT_QUIT; + mp_dispatch_unlock(ctx->mpctx->dispatch); + } if (!ctx->owner) { mpv_detach_destroy(ctx); @@ -851,6 +859,12 @@ static bool compare_value(void *a, void *b, mpv_format format) return false; return compare_value(&a_n->u, &b_n->u, a_n->format); } + case MPV_FORMAT_BYTE_ARRAY: { + struct mpv_byte_array *a_r = a, *b_r = b; + if (a_r->size != b_r->size) + return false; + return memcmp(a_r->data, b_r->data, a_r->size) == 0; + } case MPV_FORMAT_NODE_ARRAY: case MPV_FORMAT_NODE_MAP: { @@ -968,7 +982,7 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res return MPV_ERROR_INVALID_PARAMETER; if (mp_input_is_abort_cmd(cmd)) - mp_cancel_trigger(ctx->mpctx->playback_abort); + mp_abort_playback_async(ctx->mpctx); cmd->sender = ctx->name; @@ -1088,6 +1102,7 @@ int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format, mp_get_property_id(ctx->mpctx, name) >= 0) return MPV_ERROR_PROPERTY_UNAVAILABLE; switch (r) { + case MPV_ERROR_SUCCESS: return MPV_ERROR_SUCCESS; case MPV_ERROR_OPTION_FORMAT: return MPV_ERROR_PROPERTY_FORMAT; case MPV_ERROR_OPTION_NOT_FOUND: return MPV_ERROR_PROPERTY_NOT_FOUND; default: return MPV_ERROR_PROPERTY_ERROR; diff --git a/player/command.c b/player/command.c index 74c7e26966..cde67ebe51 100644 --- a/player/command.c +++ b/player/command.c @@ -243,6 +243,78 @@ void mark_seek(struct MPContext *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) +{ + int count = 0; + while (text) { + char *next = strchr(text, '\n'); + if (!next || (next[0] == '\n' && !next[1])) + break; + text = next + 1; + count++; + } + 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 *text, int pos) +{ + int screen_h, font_h; + osd_get_text_size(mpctx->osd, &screen_h, &font_h); + int max_lines = screen_h / MPMAX(font_h, 1) - 1; + + if (!text || max_lines < 5) + return text; + + int count = count_lines(text); + if (count <= max_lines) + return text; + + char *new = talloc_strdup(NULL, ""); + + int start = pos - max_lines / 2; + if (start == 1) + start = 0; // avoid weird transition when pad_h becomes visible + int pad_h = start > 0; + if (pad_h) + new = talloc_strdup_append_buffer(new, "\342\206\221 (hidden items)\n"); + + int space = max_lines - pad_h - 1; + int pad_t = count - start > space; + if (!pad_t) + start = count - space; + + char *head = skip_n_lines(text, start); + if (!head) { + talloc_free(new); + return text; + } + + char *tail = skip_n_lines(head, max_lines - pad_h - pad_t); + new = talloc_asprintf_append_buffer(new, "%.*s", + (int)(tail ? tail - head : strlen(head)), head); + if (pad_t) + new = talloc_strdup_append_buffer(new, "\342\206\223 (hidden items)\n"); + + talloc_free(text); + return new; +} + static char *format_file_size(int64_t size) { double s = size; @@ -515,29 +587,6 @@ static int mp_property_stream_path(void *ctx, struct m_property *prop, return m_property_strdup_ro(action, arg, mpctx->demuxer->filename); } -struct change_stream_capture_args { - char *filename; - struct demuxer *demux; -}; - -static void do_change_stream_capture(void *p) -{ - struct change_stream_capture_args *args = p; - stream_set_capture_file(args->demux->stream, args->filename); -} - -static int mp_property_stream_capture(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (mpctx->demuxer && action == M_PROPERTY_SET) { - struct change_stream_capture_args args = {*(char **)arg, mpctx->demuxer}; - demux_run_on_thread(mpctx->demuxer, do_change_stream_capture, &args); - // fall through to mp_property_generic_option - } - return mp_property_generic_option(mpctx, prop, action, arg); -} - /// Demuxer name (RO) static int mp_property_demuxer(void *ctx, struct m_property *prop, int action, void *arg) @@ -650,7 +699,7 @@ static int mp_property_total_avsync_change(void *ctx, struct m_property *prop, return m_property_double_ro(action, arg, mpctx->total_avsync_change); } -static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop, +static int mp_property_frame_drop_dec(void *ctx, struct m_property *prop, int action, void *arg) { MPContext *mpctx = ctx; @@ -692,8 +741,8 @@ static int mp_property_vsync_ratio(void *ctx, struct m_property *prop, return m_property_double_ro(action, arg, vsyncs / (double)frames); } -static int mp_property_vo_drop_frame_count(void *ctx, struct m_property *prop, - int action, void *arg) +static int mp_property_frame_drop_vo(void *ctx, struct m_property *prop, + int action, void *arg) { MPContext *mpctx = ctx; if (!mpctx->vo_chain) @@ -3344,9 +3393,10 @@ static int mp_property_playlist(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; if (action == M_PROPERTY_PRINT) { + struct playlist *pl = mpctx->playlist; char *res = talloc_strdup(NULL, ""); - for (struct playlist_entry *e = mpctx->playlist->first; e; e = e->next) + for (struct playlist_entry *e = pl->first; e; e = e->next) { char *p = e->filename; if (!mp_is_url(bstr0(p))) { @@ -3354,12 +3404,12 @@ static int mp_property_playlist(void *ctx, struct m_property *prop, if (s[0]) p = s; } - const char *m = mpctx->playlist->current == e ? - list_current : list_normal; + const char *m = pl->current == e ? list_current : list_normal; res = talloc_asprintf_append(res, "%s%s\n", m, p); } - *(char **)arg = res; + *(char **)arg = + cut_osd_list(mpctx, res, playlist_entry_to_index(pl, pl->current)); return M_PROPERTY_OK; } @@ -3499,6 +3549,26 @@ static int mp_property_cwd(void *ctx, struct m_property *prop, return M_PROPERTY_NOT_IMPLEMENTED; } +static int mp_property_record_file(void *ctx, struct m_property *prop, + int action, void *arg) +{ + struct MPContext *mpctx = ctx; + struct MPOpts *opts = mpctx->opts; + if (action == M_PROPERTY_SET) { + char *new = *(char **)arg; + if (!bstr_equals(bstr0(new), bstr0(opts->record_file))) { + talloc_free(opts->record_file); + opts->record_file = talloc_strdup(NULL, new); + open_recorder(mpctx, false); + // open_recorder() unsets it on failure. + if (new && !opts->record_file) + return M_PROPERTY_ERROR; + } + return M_PROPERTY_OK; + } + return mp_property_generic_option(mpctx, prop, action, arg); +} + static int mp_property_protocols(void *ctx, struct m_property *prop, int action, void *arg) { @@ -3789,7 +3859,6 @@ static const struct m_property mp_properties_base[] = { {"path", mp_property_path}, {"media-title", mp_property_media_title}, {"stream-path", mp_property_stream_path}, - {"stream-capture", mp_property_stream_capture}, {"current-demuxer", mp_property_demuxer}, {"file-format", mp_property_file_format}, {"stream-pos", mp_property_stream_pos}, @@ -3797,10 +3866,10 @@ static const struct m_property mp_properties_base[] = { {"duration", mp_property_duration}, {"avsync", mp_property_avsync}, {"total-avsync-change", mp_property_total_avsync_change}, - {"drop-frame-count", mp_property_drop_frame_cnt}, {"mistimed-frame-count", mp_property_mistimed_frame_count}, {"vsync-ratio", mp_property_vsync_ratio}, - {"vo-drop-frame-count", mp_property_vo_drop_frame_count}, + {"decoder-frame-drop-count", mp_property_frame_drop_dec}, + {"frame-drop-count", mp_property_frame_drop_vo}, {"vo-delayed-frame-count", mp_property_vo_delayed_frame_count}, {"percent-pos", mp_property_percent_pos}, {"time-start", mp_property_time_start}, @@ -3971,6 +4040,8 @@ static const struct m_property mp_properties_base[] = { {"working-directory", mp_property_cwd}, + {"record-file", mp_property_record_file}, + {"protocol-list", mp_property_protocols}, {"decoder-list", mp_property_decoders}, {"encoder-list", mp_property_encoders}, @@ -3994,6 +4065,9 @@ static const struct m_property mp_properties_base[] = { M_PROPERTY_ALIAS("colormatrix-input-range", "video-params/colorlevels"), M_PROPERTY_ALIAS("colormatrix-primaries", "video-params/primaries"), M_PROPERTY_ALIAS("colormatrix-gamma", "video-params/gamma"), + + M_PROPERTY_DEPRECATED_ALIAS("drop-frame-count", "decoder-frame-drop-count"), + M_PROPERTY_DEPRECATED_ALIAS("vo-drop-frame-count", "frame-drop-count"), }; // Each entry describes which properties an event (possibly) changes. @@ -4015,7 +4089,8 @@ static const char *const *const mp_event_property_change[] = { "total-avsync-change", "audio-speed-correction", "video-speed-correction", "vo-delayed-frame-count", "mistimed-frame-count", "vsync-ratio", "estimated-display-fps", "vsync-jitter", "sub-text", "audio-bitrate", - "video-bitrate", "sub-bitrate"), + "video-bitrate", "sub-bitrate", "decoder-frame-drop-count", + "frame-drop-count"), E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params", "video-format", "video-codec", "video-bitrate", "dwidth", "dheight", "width", "height", "fps", "aspect", "vo-configured", "current-vo", @@ -4197,9 +4272,9 @@ static const struct property_osd_display { } property_osd_display[] = { // general { "loop", "Loop" }, + { "loop-file", "Loop current file" }, { "chapter", .seek_msg = OSD_SEEK_INFO_CHAPTER_TEXT, .seek_bar = OSD_SEEK_INFO_BAR }, - { "edition", .seek_msg = OSD_SEEK_INFO_EDITION }, { "hr-seek", "hr-seek" }, { "speed", "Speed" }, { "clock", "Clock" }, @@ -4218,6 +4293,7 @@ static const struct property_osd_display { // video { "panscan", "Panscan", .osd_progbar = OSD_PANSCAN }, { "taskbar-progress", "Progress in taskbar" }, + { "snap-window", "Snap to screen edges" }, { "ontop", "Stay on top" }, { "border", "Border" }, { "framedrop", "Framedrop" }, @@ -4960,7 +5036,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re int dir = cmd->id == MP_CMD_PLAYLIST_PREV ? -1 : +1; int force = cmd->args[0].v.i; - struct playlist_entry *e = mp_next_file(mpctx, dir, force); + struct playlist_entry *e = mp_next_file(mpctx, dir, force, true); if (!e && !force) return -1; mp_set_playlist_entry(mpctx, e); @@ -5309,20 +5385,19 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re case MP_CMD_AB_LOOP: { double now = get_current_time(mpctx); - int r = 0; if (opts->ab_loop[0] == MP_NOPTS_VALUE) { - r = mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx); + mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx); show_property_osd(mpctx, "ab-loop-a", on_osd); } else if (opts->ab_loop[1] == MP_NOPTS_VALUE) { - r = mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx); + mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx); show_property_osd(mpctx, "ab-loop-b", on_osd); } else { now = MP_NOPTS_VALUE; - r = mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx); - r = mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx); + mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx); + mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx); set_osd_msg(mpctx, osdl, osd_duration, "Clear A-B loop"); } - return r > 0; + break; } case MP_CMD_DROP_BUFFERS: { @@ -5376,7 +5451,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re if (cmd->is_up_down) state[0] = cmd->repeated ? 'r' : (cmd->is_up ? 'u' : 'd'); event.num_args = 4; - event.args = (const char*[4]){"key-binding", name, state, cmd->key_name}; + event.args = (const char*[4]){"key-binding", name, state, + cmd->key_name ? cmd->key_name : ""}; if (mp_client_send_event_dup(mpctx, target, MPV_EVENT_CLIENT_MESSAGE, &event) < 0) { diff --git a/player/core.h b/player/core.h index 5d055482a5..79120decd3 100644 --- a/player/core.h +++ b/player/core.h @@ -52,8 +52,7 @@ enum mp_osd_seek_info { OSD_SEEK_INFO_BAR = 1, OSD_SEEK_INFO_TEXT = 2, OSD_SEEK_INFO_CHAPTER_TEXT = 4, - OSD_SEEK_INFO_EDITION = 8, - OSD_SEEK_INFO_CURRENT_FILE = 16, + OSD_SEEK_INFO_CURRENT_FILE = 8, }; @@ -154,6 +153,9 @@ struct track { struct vo_chain *vo_c; struct ao_chain *ao_c; struct lavfi_pad *sink; + + // For stream recording (remuxing mode). + struct mp_recorder_sink *remux_sink; }; // Summarizes video filtering and output. @@ -179,6 +181,8 @@ struct vo_chain { // - video consists of a single picture, which should be shown only once // - do not sync audio to video in any way bool is_coverart; + // Just to avoid decoding the coverart picture again after a seek. + struct mp_image *cached_coverart; }; // Like vo_chain, for audio. @@ -420,6 +424,8 @@ typedef struct MPContext { // playback rate. Used to avoid showing it multiple times. bool drop_message_shown; + struct mp_recorder *recorder; + char *cached_watch_later_configdir; struct screenshot_ctx *screenshot_ctx; @@ -429,6 +435,26 @@ typedef struct MPContext { struct mp_ipc_ctx *ipc_ctx; struct mpv_opengl_cb_context *gl_cb_ctx; + + pthread_mutex_t lock; + + // --- The following fields are protected by lock + struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer + + // --- Owned by MPContext + pthread_t open_thread; + bool open_active; // open_thread is a valid thread handle, all setup + atomic_bool open_done; + // --- All fields below are immutable while open_active is true. + // Otherwise, they're owned by MPContext. + struct mp_cancel *open_cancel; + char *open_url; + char *open_format; + int open_url_flags; + // --- All fields below are owned by open_thread, unless open_done was set + // to true. + struct demuxer *open_res_demuxer; + int open_res_error; } MPContext; // audio.c @@ -459,6 +485,7 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx, struct playlist *playlist); // loadfile.c +void mp_abort_playback_async(struct MPContext *mpctx); void uninit_player(struct MPContext *mpctx, unsigned int mask); struct track *mp_add_external_file(struct MPContext *mpctx, char *filename, enum stream_type filter); @@ -473,7 +500,7 @@ struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type, void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer); bool mp_remove_track(struct MPContext *mpctx, struct track *track); struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction, - bool force); + bool force, bool mutate); void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e); void mp_play_files(struct MPContext *mpctx); void update_demuxer_properties(struct MPContext *mpctx); @@ -483,6 +510,10 @@ void prepare_playlist(struct MPContext *mpctx, struct playlist *pl); void autoload_external_files(struct MPContext *mpctx); struct track *select_default_track(struct MPContext *mpctx, int order, enum stream_type type); +void prefetch_next(struct MPContext *mpctx); +void close_recorder(struct MPContext *mpctx); +void close_recorder_and_error(struct MPContext *mpctx); +void open_recorder(struct MPContext *mpctx, bool on_init); // main.c int mp_initialize(struct MPContext *mpctx, char **argv); @@ -501,8 +532,6 @@ void update_vo_playback_state(struct MPContext *mpctx); void update_window_title(struct MPContext *mpctx, bool force); void error_on_track(struct MPContext *mpctx, struct track *track); int stream_dump(struct MPContext *mpctx, const char *source_filename); -int mpctx_run_reentrant(struct MPContext *mpctx, void (*thread_fn)(void *arg), - void *thread_arg); double get_track_seek_offset(struct MPContext *mpctx, struct track *track); // osd.c @@ -549,6 +578,7 @@ void update_screensaver_state(struct MPContext *mpctx); // scripting.c struct mp_scripting { + const char *name; // e.g. "lua script" const char *file_ext; // e.g. "lua" int (*load)(struct mpv_handle *client, const char *filename); }; diff --git a/player/external_files.c b/player/external_files.c index eb7345ac84..5eb3a1d730 100644 --- a/player/external_files.c +++ b/player/external_files.c @@ -10,13 +10,15 @@ #include "common/global.h" #include "common/msg.h" #include "misc/ctype.h" +#include "misc/charset_conv.h" #include "options/options.h" #include "options/path.h" #include "external_files.h" static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "srt", - "smi", "rt", "txt", "ssa", "aqt", "jss", - "js", "ass", "mks", "vtt", "sup", NULL}; + "smi", "rt", "ssa", "aqt", "jss", + "js", "ass", "mks", "vtt", "sup", "scc", + NULL}; static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac", "ogg", "m4a", "ac3", "opus", "wav", @@ -97,11 +99,16 @@ static void append_dir_subtitles(struct mpv_global *global, if (mp_is_url(bstr0(fname))) goto out; - struct bstr f_fname = bstr0(mp_basename(fname)); + struct bstr f_fbname = bstr0(mp_basename(fname)); + struct bstr f_fname = mp_iconv_to_utf8(log, f_fbname, + "UTF-8-MAC", MP_NO_LATIN1_FALLBACK); struct bstr f_fname_noext = bstrdup(tmpmem, bstr_strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); + if (f_fbname.start != f_fname.start) + talloc_steal(tmpmem, f_fname.start); + // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name @@ -113,15 +120,19 @@ static void append_dir_subtitles(struct mpv_global *global, mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { - struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); - + struct bstr den = bstr0(de->d_name); + struct bstr dename = mp_iconv_to_utf8(log, den, + "UTF-8-MAC", MP_NO_LATIN1_FALLBACK); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, bstr_strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = bstr_get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); + if (den.start != dename.start) + talloc_steal(tmpmem2, dename.start); + // check what it is (most likely) int type = test_ext(tmp_fname_ext); char **langs = NULL; diff --git a/player/lavfi.c b/player/lavfi.c index 50c2fd8fc2..2a41aa8a96 100644 --- a/player/lavfi.c +++ b/player/lavfi.c @@ -43,6 +43,11 @@ #include "lavfi.h" +#if LIBAVFILTER_VERSION_MICRO < 100 +#define av_buffersink_get_frame_flags(a, b, c) av_buffersink_get_frame(a, b) +#define AV_BUFFERSINK_FLAG_NO_REQUEST 0 +#endif + struct lavfi { struct mp_log *log; char *graph_string; @@ -266,6 +271,10 @@ enum stream_type lavfi_pad_type(struct lavfi_pad *pad) void lavfi_set_connected(struct lavfi_pad *pad, bool connected) { pad->connected = connected; + if (!pad->connected) { + pad->output_needed = false; + drop_pad_data(pad); + } } bool lavfi_get_connected(struct lavfi_pad *pad) @@ -545,19 +554,17 @@ static void read_output_pads(struct lavfi *c) if (pad->dir != LAVFI_OUT) continue; - // If disconnected, read and discard everything. - if (!pad->pending_v && !pad->pending_a && !pad->connected) - pad->output_needed = true; - - if (!pad->output_needed) + // If disconnected, read and discard everything (passively). + if (pad->connected && !pad->output_needed) continue; assert(pad->buffer); assert(!pad->pending_v && !pad->pending_a); + int flags = pad->output_needed ? 0 : AV_BUFFERSINK_FLAG_NO_REQUEST; int r = AVERROR_EOF; if (!pad->buffer_is_eof) - r = av_buffersink_get_frame(pad->buffer, pad->tmp_frame); + r = av_buffersink_get_frame_flags(pad->buffer, pad->tmp_frame, flags); if (r >= 0) { pad->output_needed = false; double pts = mp_pts_from_av(pad->tmp_frame->pts, &pad->timebase); @@ -583,6 +590,7 @@ static void read_output_pads(struct lavfi *c) // input pads (via av_buffersrc_get_nb_failed_requests()). pad->output_eof = false; } else if (r == AVERROR_EOF) { + pad->output_needed = false; pad->buffer_is_eof = true; if (!c->draining_recover_eof && !c->draining_new_format) pad->output_eof = true; @@ -611,7 +619,7 @@ bool lavfi_process(struct lavfi *c) bool all_waiting = true; bool any_needs_input = false; bool any_needs_output = false; - bool all_lavfi_eof = true; + bool all_output_eof = true; bool all_input_eof = true; // Determine the graph state @@ -623,12 +631,12 @@ bool lavfi_process(struct lavfi *c) any_needs_input |= pad->input_needed; all_input_eof &= pad->input_eof; } else if (pad->dir == LAVFI_OUT) { - all_lavfi_eof &= pad->buffer_is_eof; + all_output_eof &= pad->buffer_is_eof; any_needs_output |= pad->output_needed; } } - if (all_lavfi_eof && !all_input_eof) { + if (all_output_eof && !all_input_eof) { free_graph(c); precreate_graph(c); all_waiting = false; diff --git a/player/loadfile.c b/player/loadfile.c index b94ce8af43..dba02ee828 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -40,6 +40,7 @@ #include "options/m_property.h" #include "common/common.h" #include "common/encode.h" +#include "common/recorder.h" #include "input/input.h" #include "audio/audio.h" @@ -57,6 +58,17 @@ #include "command.h" #include "libmpv/client.h" +// Called by foreign threads when playback should be stopped and such. +void mp_abort_playback_async(struct MPContext *mpctx) +{ + mp_cancel_trigger(mpctx->playback_abort); + + pthread_mutex_lock(&mpctx->lock); + if (mpctx->demuxer_cancel) + mp_cancel_trigger(mpctx->demuxer_cancel); + pthread_mutex_unlock(&mpctx->lock); +} + static void uninit_demuxer(struct MPContext *mpctx) { for (int r = 0; r < NUM_PTRACKS; r++) { @@ -80,6 +92,11 @@ static void uninit_demuxer(struct MPContext *mpctx) free_demuxer_and_stream(mpctx->demuxer); mpctx->demuxer = NULL; + + pthread_mutex_lock(&mpctx->lock); + talloc_free(mpctx->demuxer_cancel); + mpctx->demuxer_cancel = NULL; + pthread_mutex_unlock(&mpctx->lock); } #define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__) @@ -453,6 +470,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type uninit_sub(mpctx, current); if (current) { + if (current->remux_sink) + close_recorder_and_error(mpctx); current->selected = false; reselect_demux_stream(mpctx, current); } @@ -774,57 +793,144 @@ static void load_per_file_options(m_config_t *conf, } } -struct demux_open_args { - int stream_flags; - char *url; - struct mpv_global *global; - struct mp_cancel *cancel; - struct mp_log *log; - // results - struct demuxer *demux; - int err; -}; - -static void open_demux_thread(void *pctx) +static void *open_demux_thread(void *ctx) { - struct demux_open_args *args = pctx; - struct mpv_global *global = args->global; + struct MPContext *mpctx = ctx; + struct demuxer_params p = { - .force_format = global->opts->demuxer_name, - .allow_capture = true, - .stream_flags = args->stream_flags, + .force_format = mpctx->open_format, + .stream_flags = mpctx->open_url_flags, + .initial_readahead = true, }; - args->demux = demux_open_url(args->url, &p, args->cancel, global); - if (!args->demux) { + mpctx->open_res_demuxer = + demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global); + + if (mpctx->open_res_demuxer) { + MP_VERBOSE(mpctx, "Opening done: %s\n", mpctx->open_url); + } else { + MP_VERBOSE(mpctx, "Opening failed or was aborted: %s\n", mpctx->open_url); + if (p.demuxer_failed) { - args->err = MPV_ERROR_UNKNOWN_FORMAT; + mpctx->open_res_error = MPV_ERROR_UNKNOWN_FORMAT; } else { - args->err = MPV_ERROR_LOADING_FAILED; + mpctx->open_res_error = MPV_ERROR_LOADING_FAILED; } } - if (args->demux && global->opts->rebase_start_time) - demux_set_ts_offset(args->demux, -args->demux->start_time); + + atomic_store(&mpctx->open_done, true); + mp_wakeup_core(mpctx); + return NULL; } -static void open_demux_reentrant(struct MPContext *mpctx) +static void cancel_open(struct MPContext *mpctx) { - struct demux_open_args args = { - .global = mpctx->global, - .cancel = mpctx->playback_abort, - .log = mpctx->log, - .stream_flags = mpctx->playing->stream_flags, - .url = talloc_strdup(NULL, mpctx->stream_open_filename), - }; + if (mpctx->open_cancel) + mp_cancel_trigger(mpctx->open_cancel); + + if (mpctx->open_active) + pthread_join(mpctx->open_thread, NULL); + mpctx->open_active = false; + + TA_FREEP(&mpctx->open_cancel); + TA_FREEP(&mpctx->open_url); + TA_FREEP(&mpctx->open_format); + + if (mpctx->open_res_demuxer) + free_demuxer_and_stream(mpctx->open_res_demuxer); + mpctx->open_res_demuxer = NULL; + + atomic_store(&mpctx->open_done, false); +} + +// Setup all the field to open this url, and make sure a thread is running. +static void start_open(struct MPContext *mpctx, char *url, int url_flags) +{ + cancel_open(mpctx); + + assert(!mpctx->open_active); + assert(!mpctx->open_cancel); + assert(!mpctx->open_res_demuxer); + assert(!atomic_load(&mpctx->open_done)); + + mpctx->open_cancel = mp_cancel_new(NULL); + mpctx->open_url = talloc_strdup(NULL, url); + mpctx->open_format = talloc_strdup(NULL, mpctx->opts->demuxer_name); + mpctx->open_url_flags = url_flags; if (mpctx->opts->load_unsafe_playlists) - args.stream_flags = 0; - mpctx_run_reentrant(mpctx, open_demux_thread, &args); - if (args.demux) { - mpctx->demuxer = args.demux; - enable_demux_thread(mpctx, mpctx->demuxer); + mpctx->open_url_flags = 0; + + if (pthread_create(&mpctx->open_thread, NULL, open_demux_thread, mpctx)) { + cancel_open(mpctx); + return; + } + + mpctx->open_active = true; +} + +static void open_demux_reentrant(struct MPContext *mpctx) +{ + char *url = mpctx->stream_open_filename; + + if (mpctx->open_active) { + bool done = atomic_load(&mpctx->open_done); + bool failed = done && !mpctx->open_res_demuxer; + bool correct_url = strcmp(mpctx->open_url, url) == 0; + + if (correct_url && !failed) { + MP_VERBOSE(mpctx, "Using prefetched/prefetching URL.\n"); + } else if (correct_url && failed) { + MP_VERBOSE(mpctx, "Prefetched URL failed, retrying.\n"); + cancel_open(mpctx); + } else { + if (done) { + MP_VERBOSE(mpctx, "Dropping finished prefetch of wrong URL.\n"); + } else { + MP_VERBOSE(mpctx, "Aborting onging prefetch of wrong URL.\n"); + } + cancel_open(mpctx); + } + } + + if (!mpctx->open_active) + start_open(mpctx, url, mpctx->playing->stream_flags); + + // User abort should cancel the opener now. + pthread_mutex_lock(&mpctx->lock); + mpctx->demuxer_cancel = mpctx->open_cancel; + pthread_mutex_unlock(&mpctx->lock); + + while (!atomic_load(&mpctx->open_done)) { + |