summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demux/demux_cue.c8
-rw-r--r--demux/demux_mkv.c7
-rw-r--r--etc/restore-old-bindings.conf4
-rw-r--r--player/lua.c338
4 files changed, 283 insertions, 74 deletions
diff --git a/demux/demux_cue.c b/demux/demux_cue.c
index 2f2e6fca46..bc1b727f5f 100644
--- a/demux/demux_cue.c
+++ b/demux/demux_cue.c
@@ -35,13 +35,9 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
{
struct stream *s = demuxer->stream;
if (check >= DEMUX_CHECK_UNSAFE) {
- char buf[PROBE_SIZE];
- int len = stream_read(s, buf, sizeof(buf));
- if (len <= 0)
+ bstr d = stream_peek(s, PROBE_SIZE);
+ if (d.len < 1 || !mp_probe_cue(d))
return -1;
- if (!mp_probe_cue((struct bstr) { buf, len }))
- return -1;
- stream_seek(s, 0);
}
demuxer->file_contents = stream_read_complete(s, demuxer, 1000000);
if (demuxer->file_contents.start == NULL)
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 100447d1f7..36ca0277ba 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -1757,6 +1757,13 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
int64_t start_pos;
int64_t end_pos;
+ bstr start = stream_peek(s, 4);
+ uint32_t start_id;
+ for (int n = 0; n < start.len; n++)
+ start_id = (start_id << 8) | start.start[n];
+ if (start_id != EBML_ID_EBML)
+ return -1;
+
if (!read_ebml_header(demuxer))
return -1;
MP_VERBOSE(demuxer, "Found the head...\n");
diff --git a/etc/restore-old-bindings.conf b/etc/restore-old-bindings.conf
index 8455dd802b..ea0c664fa2 100644
--- a/etc/restore-old-bindings.conf
+++ b/etc/restore-old-bindings.conf
@@ -9,6 +9,10 @@
#
# Older installations use ~/.mpv/input.conf instead.
+# changed in mpv 0.7.0
+
+ENTER playlist_next force
+
# changed in mpv 0.6.0
ESC quit
diff --git a/player/lua.c b/player/lua.c
index 78623a0651..8eb2184fc5 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -1168,6 +1168,182 @@ static int script_join_path(lua_State *L)
return 1;
}
+typedef void (*read_cb)(void *ctx, char *data, size_t size);
+
+#ifdef __MINGW32__
+#include <windows.h>
+#include "osdep/io.h"
+
+struct subprocess_ctx {
+ HANDLE stderr_read;
+ void *cb_ctx;
+ read_cb on_stderr;
+};
+
+static void write_arg(bstr *cmdline, char *arg)
+{
+ // If the string doesn't have characters that need to be escaped, it's best
+ // to leave it alone for the sake of Windows programs that don't process
+ // quoted args correctly.
+ if (!strpbrk(arg, " \t\"")) {
+ bstr_xappend(NULL, cmdline, bstr0(arg));
+ return;
+ }
+
+ // If there are characters that need to be escaped, write a quoted string
+ bstr_xappend(NULL, cmdline, bstr0("\""));
+
+ // Escape the argument. To match the behavior of CommandLineToArgvW,
+ // backslashes are only escaped if they appear before a quote or the end of
+ // the string.
+ int num_slashes = 0;
+ for (int pos = 0; arg[pos]; pos++) {
+ switch (arg[pos]) {
+ case '\\':
+ // Count backslashes that appear in a row
+ num_slashes++;
+ break;
+ case '"':
+ bstr_xappend(NULL, cmdline, (struct bstr){arg, pos});
+
+ // Double preceding slashes
+ for (int i = 0; i < num_slashes; i++)
+ bstr_xappend(NULL, cmdline, bstr0("\\"));
+
+ // Escape the following quote
+ bstr_xappend(NULL, cmdline, bstr0("\\"));
+
+ arg += pos;
+ pos = 0;
+ num_slashes = 0;
+ break;
+ default:
+ num_slashes = 0;
+ }
+ }
+
+ // Write the rest of the argument
+ bstr_xappend(NULL, cmdline, bstr0(arg));
+
+ // Double slashes that appear at the end of the string
+ for (int i = 0; i < num_slashes; i++)
+ bstr_xappend(NULL, cmdline, bstr0("\\"));
+
+ bstr_xappend(NULL, cmdline, bstr0("\""));
+}
+
+// Convert an array of arguments to a properly escaped command-line string
+static wchar_t *write_cmdline(void *ctx, char **argv)
+{
+ bstr cmdline = {0};
+
+ for (int i = 0; argv[i]; i++) {
+ write_arg(&cmdline, argv[i]);
+ if (argv[i + 1])
+ bstr_xappend(NULL, &cmdline, bstr0(" "));
+ }
+
+ wchar_t *wcmdline = mp_from_utf8(ctx, cmdline.start);
+ talloc_free(cmdline.start);
+ return wcmdline;
+}
+
+static void *stderr_routine(void *arg)
+{
+ struct subprocess_ctx *ctx = arg;
+
+ // Read from stderr until it's closed on process exit
+ char buf[4096];
+ DWORD r;
+ while (ReadFile(ctx->stderr_read, buf, 4096, &r, NULL))
+ ctx->on_stderr(ctx->cb_ctx, buf, r);
+
+ return NULL;
+}
+
+static int subprocess(char **args, struct mp_cancel *cancel, void *ctx,
+ read_cb on_stdout, read_cb on_stderr, char **error)
+{
+ wchar_t *tmp = talloc_new(NULL);
+ HANDLE stdout_read = NULL, stdout_write = NULL;
+ HANDLE stderr_read = NULL, stderr_write = NULL;
+ int status = -1;
+
+ // If the function exits before CreateProcess, there was an init error
+ *error = "init";
+
+ if (!CreatePipe(&stdout_read, &stdout_write, NULL, 0))
+ goto done;
+ if (!CreatePipe(&stderr_read, &stderr_write, NULL, 0))
+ goto done;
+ if (!SetHandleInformation(stdout_write, HANDLE_FLAG_INHERIT, 1))
+ goto done;
+ if (!SetHandleInformation(stderr_write, HANDLE_FLAG_INHERIT, 1))
+ goto done;
+
+ // Convert the args array to a UTF-16 Windows command-line string
+ wchar_t *cmdline = write_cmdline(tmp, args);
+
+ PROCESS_INFORMATION pi = {0};
+ STARTUPINFOW si = {
+ .cb = sizeof(si),
+ .dwFlags = STARTF_USESTDHANDLES,
+ .hStdInput = NULL,
+ .hStdOutput = stdout_write,
+ .hStdError = stderr_write,
+ };
+
+ if (!CreateProcessW(NULL, cmdline, NULL, NULL, TRUE,
+ CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
+ NULL, NULL, &si, &pi))
+ goto done;
+ talloc_free(cmdline);
+ CloseHandle(pi.hThread);
+
+ // Init is finished
+ *error = NULL;
+
+ // Close our copy of the write end of the pipes
+ CloseHandle(stdout_write);
+ stdout_write = NULL;
+ CloseHandle(stderr_write);
+ stderr_write = NULL;
+
+ // Create a thread to read stderr output
+ pthread_t stderr_thread;
+ struct subprocess_ctx sctx = {
+ .stderr_read = stderr_read,
+ .cb_ctx = ctx,
+ .on_stderr = on_stderr,
+ };
+ if (pthread_create(&stderr_thread, NULL, stderr_routine, &sctx))
+ goto done;
+
+ // Read from stdout until it's closed on process exit
+ char buf[4096];
+ DWORD r;
+ while (ReadFile(stdout_read, buf, 4096, &r, NULL))
+ on_stdout(ctx, buf, r);
+
+ pthread_join(stderr_thread, NULL);
+
+ // Get process exit code
+ DWORD exit_code;
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ GetExitCodeProcess(pi.hProcess, &exit_code);
+ status = exit_code;
+
+done:
+ CloseHandle(stdout_read);
+ CloseHandle(stdout_write);
+ CloseHandle(stderr_read);
+ CloseHandle(stderr_write);
+ CloseHandle(pi.hProcess);
+ talloc_free(tmp);
+ return status;
+}
+#endif
+
#if HAVE_POSIX_SPAWN
#include <spawn.h>
#include <poll.h>
@@ -1203,46 +1379,11 @@ static int sparse_poll(struct pollfd *fds, int num_fds, int timeout)
return r;
}
-static int script_subprocess(lua_State *L)
+static int subprocess(char **args, struct mp_cancel *cancel, void *ctx,
+ read_cb on_stdout, read_cb on_stderr, char **error)
{
- struct script_ctx *ctx = get_ctx(L);
- luaL_checktype(L, 1, LUA_TTABLE);
- void *tmp = mp_lua_PITA(L);
-
- resume_all(ctx);
-
- lua_getfield(L, 1, "args"); // args
- int num_args = mp_lua_len(L, -1);
- char *args[256];
- if (num_args > MP_ARRAY_SIZE(args) - 1) // last needs to be NULL
- luaL_error(L, "too many arguments");
- if (num_args < 1)
- luaL_error(L, "program name missing");
- for (int n = 0; n < num_args; n++) {
- lua_pushinteger(L, n + 1); // args n
- lua_gettable(L, -2); // args arg
- args[n] = talloc_strdup(tmp, lua_tostring(L, -1));
- if (!args[n])
- luaL_error(L, "program arguments must be strings");
- lua_pop(L, 1); // args
- }
- args[num_args] = NULL;
- lua_pop(L, 1); // -
-
- lua_getfield(L, 1, "cancellable"); // c
- struct mp_cancel *cancel = NULL;
- if (lua_isnil(L, -1) ? true : lua_toboolean(L, -1))
- cancel = ctx->mpctx->playback_abort;
- lua_pop(L, 1); // -
-
- lua_getfield(L, 1, "max_size"); // m
- int64_t max_size = lua_isnil(L, -1) ? 16 * 1024 * 1024 : lua_tointeger(L, -1);
-
- // --- no Lua errors from here
-
posix_spawn_file_actions_t fa;
bool fa_destroy = false;
- bstr output = {0};
int status = -1;
int p_stdout[2] = {-1, -1};
int p_stderr[2] = {-1, -1};
@@ -1272,44 +1413,35 @@ static int script_subprocess(lua_State *L)
close(p_stderr[1]);
p_stderr[1] = -1;
+ int *read_fds[2] = {&p_stdout[0], &p_stderr[0]};
+ read_cb read_cbs[2] = {on_stdout, on_stderr};
+
while (p_stdout[0] >= 0 || p_stderr[0] >= 0) {
struct pollfd fds[] = {
- {.events = POLLIN, .fd = p_stdout[0]},
- {.events = POLLIN, .fd = p_stderr[0]},
+ {.events = POLLIN, .fd = *read_fds[0]},
+ {.events = POLLIN, .fd = *read_fds[1]},
{.events = POLLIN, .fd = cancel ? mp_cancel_get_fd(cancel) : -1},
};
if (sparse_poll(fds, MP_ARRAY_SIZE(fds), -1) < 0 && errno != EINTR)
break;
- if (fds[0].revents) {
- char buf[4096];
- ssize_t r = read(p_stdout[0], buf, sizeof(buf));
- if (r < 0 && errno == EINTR)
- continue;
- if (r > 0)
- bstr_xappend(tmp, &output, (bstr){buf, r});
- if (r <= 0) {
- close(p_stdout[0]);
- p_stdout[0] = -1;
- }
- }
- if (fds[1].revents) {
- char buf[4096];
- ssize_t r = read(p_stderr[0], buf, sizeof(buf));
- if (r < 0 && errno == EINTR)
- continue;
- if (r > 0)
- MP_INFO(ctx, "%.*s", (int)r, buf);
- if (r <= 0) {
- close(p_stderr[0]);
- p_stderr[0] = -1;
+ for (int n = 0; n < 2; n++) {
+ if (fds[n].revents) {
+ char buf[4096];
+ ssize_t r = read(*read_fds[n], buf, sizeof(buf));
+ if (r < 0 && errno == EINTR)
+ continue;
+ if (r > 0 && read_cbs[n])
+ read_cbs[n](ctx, buf, r);
+ if (r <= 0) {
+ close(*read_fds[n]);
+ *read_fds[n] = -1;
+ }
}
}
if (fds[2].revents) {
kill(pid, SIGKILL);
break;
}
- if (output.len >= max_size)
- break;
}
// Note: it can happen that a child process closes the pipe, but does not
@@ -1326,14 +1458,84 @@ done:
close(p_stderr[0]);
close(p_stderr[1]);
- // --- Lua errors are ok again from here
- char *error = NULL;
if (WIFEXITED(status) && WEXITSTATUS(status) != 127) {
+ *error = NULL;
status = WEXITSTATUS(status);
} else {
- error = WEXITSTATUS(status) == 127 ? "init" : "killed";
+ *error = WEXITSTATUS(status) == 127 ? "init" : "killed";
status = -1;
}
+
+ return status;
+}
+#endif
+
+#if HAVE_POSIX_SPAWN || defined(__MINGW32__)
+struct subprocess_cb_ctx {
+ struct mp_log *log;
+ void* talloc_ctx;
+ int64_t max_size;
+ bstr output;
+};
+
+static void subprocess_stdout(void *p, char *data, size_t size)
+{
+ struct subprocess_cb_ctx *ctx = p;
+ if (ctx->output.len < ctx->max_size)
+ bstr_xappend(ctx->talloc_ctx, &ctx->output, (bstr){data, size});
+}
+
+static void subprocess_stderr(void *p, char *data, size_t size)
+{
+ struct subprocess_cb_ctx *ctx = p;
+ MP_INFO(ctx, "%.*s", (int)size, data);
+}
+
+static int script_subprocess(lua_State *L)
+{
+ struct script_ctx *ctx = get_ctx(L);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ void *tmp = mp_lua_PITA(L);
+
+ resume_all(ctx);
+
+ lua_getfield(L, 1, "args"); // args
+ int num_args = mp_lua_len(L, -1);
+ char *args[256];
+ if (num_args > MP_ARRAY_SIZE(args) - 1) // last needs to be NULL
+ luaL_error(L, "too many arguments");
+ if (num_args < 1)
+ luaL_error(L, "program name missing");
+ for (int n = 0; n < num_args; n++) {
+ lua_pushinteger(L, n + 1); // args n
+ lua_gettable(L, -2); // args arg
+ args[n] = talloc_strdup(tmp, lua_tostring(L, -1));
+ if (!args[n])
+ luaL_error(L, "program arguments must be strings");
+ lua_pop(L, 1); // args
+ }
+ args[num_args] = NULL;
+ lua_pop(L, 1); // -
+
+ lua_getfield(L, 1, "cancellable"); // c
+ struct mp_cancel *cancel = NULL;
+ if (lua_isnil(L, -1) ? true : lua_toboolean(L, -1))
+ cancel = ctx->mpctx->playback_abort;
+ lua_pop(L, 1); // -
+
+ lua_getfield(L, 1, "max_size"); // m
+ int64_t max_size = lua_isnil(L, -1) ? 16 * 1024 * 1024 : lua_tointeger(L, -1);
+
+ struct subprocess_cb_ctx cb_ctx = {
+ .log = ctx->log,
+ .talloc_ctx = tmp,
+ .max_size = max_size,
+ };
+
+ char *error = NULL;
+ int status = subprocess(args, cancel, &cb_ctx, subprocess_stdout,
+ subprocess_stderr, &error);
+
lua_newtable(L); // res
if (error) {
lua_pushstring(L, error); // res e
@@ -1341,7 +1543,7 @@ done:
}
lua_pushinteger(L, status); // res s
lua_setfield(L, -2, "status"); // res
- lua_pushlstring(L, output.start, output.len); // res d
+ lua_pushlstring(L, cb_ctx.output.start, cb_ctx.output.len); // res d
lua_setfield(L, -2, "stdout"); // res
return 1;
}
@@ -1417,7 +1619,7 @@ static const struct fn_entry utils_fns[] = {
FN_ENTRY(readdir),
FN_ENTRY(split_path),
FN_ENTRY(join_path),
-#if HAVE_POSIX_SPAWN
+#if HAVE_POSIX_SPAWN || defined(__MINGW32__)
FN_ENTRY(subprocess),
#endif
FN_ENTRY(parse_json),