summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
Diffstat (limited to 'player')
-rw-r--r--player/audio.c2
-rw-r--r--player/client.c19
-rw-r--r--player/command.c162
-rw-r--r--player/core.h40
-rw-r--r--player/external_files.c21
-rw-r--r--player/lavfi.c26
-rw-r--r--player/loadfile.c287
-rw-r--r--player/lua.c2
-rw-r--r--player/lua/defaults.lua10
-rw-r--r--player/lua/osc.lua62
-rw-r--r--player/lua/ytdl_hook.lua90
-rw-r--r--player/main.c13
-rw-r--r--player/misc.c71
-rw-r--r--player/osd.c18
-rw-r--r--player/playloop.c6
-rw-r--r--player/screenshot.c16
-rw-r--r--player/scripting.c44
-rw-r--r--player/video.c34
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)) {
+