summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-05-05 19:37:29 +0200
committerwm4 <wm4@nowhere>2013-05-05 20:08:11 +0200
commitce9a854d54900d050cf1efcf26081d875223e448 (patch)
treeb3a50d4df4f88c28f6d36084edee2778de46979b
parent38ce91170456913c9d7c4604615a5cca62c846bb (diff)
downloadmpv-ce9a854d54900d050cf1efcf26081d875223e448.tar.bz2
mpv-ce9a854d54900d050cf1efcf26081d875223e448.tar.xz
core: add playback resume feature (manual/opt-in)
A "watch later" command is now mapped to Shift+Q. This quits the player and stores the playback state in a config file in ~/.mpv/watch_later/. When calling the player with the same file again, playback is resumed at that time position. It's also possible to make mpv save playback state always on quit with the --save-position-on-quit option. Likewise, resuming can be disabled with the --no-resume-playback option. This also attempts to save some playback parameters, like fullscreen state or track selection. This will unconditionally override config settings and command line options (which is probably not what you would expect, but in general nobody will really care about this). Some things are not backed up, because that would cause various problems. Additional subtitle files, video filters, etc. are not stored because that would be too hard and fragile. Volume/mute state are not stored because it would mess up if the system mixer is used, or if the system mixer was readjusted in the meantime. Basically, the tradeoff between perfect state restoration and complexity/fragility makes it not worth to attempt to implement it perfectly, even if the result is a little bit inconsistent.
-rw-r--r--DOCS/man/en/input.rst4
-rw-r--r--DOCS/man/en/mpv.rst4
-rw-r--r--DOCS/man/en/options.rst13
-rw-r--r--core/cfg-mplayer.h3
-rw-r--r--core/command.c6
-rw-r--r--core/defaultopts.c1
-rw-r--r--core/input/input.c1
-rw-r--r--core/input/input.h1
-rw-r--r--core/mp_core.h1
-rw-r--r--core/mplayer.c125
-rw-r--r--core/options.h2
-rw-r--r--core/path.c14
-rw-r--r--core/path.h2
-rw-r--r--etc/input.conf1
14 files changed, 177 insertions, 1 deletions
diff --git a/DOCS/man/en/input.rst b/DOCS/man/en/input.rst
index 7938502573..6d1072353f 100644
--- a/DOCS/man/en/input.rst
+++ b/DOCS/man/en/input.rst
@@ -169,6 +169,10 @@ run "<command>"
quit [<code>]
Exit the player using the given exit code.
+quit_watch_later
+ Exit player, and store current playback position. Playing that file later
+ will seek to the previous position on start.
+
sub_add "<file>"
Load the given subtitle file. It's not selected as current subtitle after
loading.
diff --git a/DOCS/man/en/mpv.rst b/DOCS/man/en/mpv.rst
index 9a62333983..2ebdcab506 100644
--- a/DOCS/man/en/mpv.rst
+++ b/DOCS/man/en/mpv.rst
@@ -90,6 +90,10 @@ p / SPACE
q / ESC
Stop playing and quit.
+Q
+ Like ``q``, but store the current playback position. Playing the same file
+ later will resume at the old playback position if possible.
+
U
Stop playing (and quit if ``--idle`` is not used).
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index 766c3ae562..3a98ca9c93 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -1329,6 +1329,10 @@
Do not play sound. With some demuxers this may not work. In those cases
you can try ``--ao=null`` instead.
+--no-resume-playback
+ Do not restore playback position from ``~/.mpv/watch_later/``.
+ See ``quit_watch_later`` input command.
+
--no-sub
Disables display of internal and external subtitles.
@@ -1791,6 +1795,15 @@
grayscale output with this option. Not supported by all video output
drivers.
+--save-position-on-quit
+ Always save the current playback position on quit. When this file is
+ played again later, the player will seek to the old playback position on
+ start. This affects any form of stopping playback (quitting, going to the
+ next file).
+
+ This behavior is disabled by default, but is always available when quitting
+ the player with Shift+Q.
+
--sb=<n>
Seek to byte position. Useful for playback from CD-ROM images or VOB files
with junk at the beginning. See also ``--start``.
diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h
index 570c403f9c..f2e90d07d2 100644
--- a/core/cfg-mplayer.h
+++ b/core/cfg-mplayer.h
@@ -650,6 +650,9 @@ const m_option_t mplayer_opts[]={
{"{", NULL, CONF_TYPE_STORE, CONF_NOCFG, 0, 0, NULL},
{"}", NULL, CONF_TYPE_STORE, CONF_NOCFG, 0, 0, NULL},
+ OPT_FLAG("resume-playback", position_resume, 0),
+ OPT_FLAG("save-position-on-quit", position_save_on_quit, 0),
+
OPT_FLAG("ordered-chapters", ordered_chapters, 0),
OPT_INTRANGE("chapter-merge-threshold", chapter_merge_threshold, 0, 0, 10000),
diff --git a/core/command.c b/core/command.c
index 4831c939c0..72d6f34469 100644
--- a/core/command.c
+++ b/core/command.c
@@ -1856,6 +1856,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
mpctx->quit_player_rc = cmd->args[0].v.i;
break;
+ case MP_CMD_QUIT_WATCH_LATER:
+ mp_write_watch_later_conf(mpctx);
+ mpctx->stop_play = PT_QUIT;
+ mpctx->quit_player_rc = 0;
+ break;
+
case MP_CMD_PLAYLIST_NEXT:
case MP_CMD_PLAYLIST_PREV:
{
diff --git a/core/defaultopts.c b/core/defaultopts.c
index 03d697d732..b80bea5c1e 100644
--- a/core/defaultopts.c
+++ b/core/defaultopts.c
@@ -52,6 +52,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.ordered_chapters = 1,
.chapter_merge_threshold = 100,
.load_config = 1,
+ .position_resume = 1,
.stream_cache_min_percent = 20.0,
.stream_cache_seek_min_percent = 50.0,
.stream_cache_pause = 10.0,
diff --git a/core/input/input.c b/core/input/input.c
index 31b807d92f..7ba6b64cdc 100644
--- a/core/input/input.c
+++ b/core/input/input.c
@@ -126,6 +126,7 @@ static const mp_cmd_t mp_cmds[] = {
}},
{ MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } },
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
+ { MP_CMD_QUIT_WATCH_LATER, "quit_watch_later", },
{ MP_CMD_STOP, "stop", },
{ MP_CMD_FRAME_STEP, "frame_step", },
{ MP_CMD_FRAME_BACK_STEP, "frame_back_step", },
diff --git a/core/input/input.h b/core/input/input.h
index ff79222c9b..367abedfca 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -28,6 +28,7 @@ enum mp_command_type {
MP_CMD_IGNORE,
MP_CMD_SEEK,
MP_CMD_QUIT,
+ MP_CMD_QUIT_WATCH_LATER,
MP_CMD_PLAYLIST_NEXT,
MP_CMD_PLAYLIST_PREV,
MP_CMD_OSD,
diff --git a/core/mp_core.h b/core/mp_core.h
index 9588a77b24..2590eefb96 100644
--- a/core/mp_core.h
+++ b/core/mp_core.h
@@ -321,6 +321,7 @@ struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
bool mp_remove_track(struct MPContext *mpctx, struct track *track);
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction);
int mp_get_cache_percent(struct MPContext *mpctx);
+void mp_write_watch_later_conf(struct MPContext *mpctx);
// timeline/tl_matroska.c
void build_ordered_chapter_timeline(struct MPContext *mpctx);
diff --git a/core/mplayer.c b/core/mplayer.c
index 9028cbafce..a6cc189df6 100644
--- a/core/mplayer.c
+++ b/core/mplayer.c
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <math.h>
#include <assert.h>
+#include <ctype.h>
#ifdef PTW32_STATIC_LIB
#include <pthread.h>
@@ -28,6 +29,7 @@
#include <libavutil/intreadwrite.h>
#include <libavutil/attributes.h>
+#include <libavutil/md5.h>
#include <libavcodec/version.h>
@@ -763,6 +765,121 @@ static void load_per_file_config(m_config_t *conf, const char * const file,
}
}
+static bool might_be_an_url(bstr f)
+{
+ return bstr_find0(f, "://") >= 0;
+}
+
+#define MP_WATCH_LATER_CONF "watch_later"
+
+static char *get_playback_resume_config_filename(const char *fname)
+{
+ char *res = NULL;
+ void *tmp = talloc_new(NULL);
+ const char *realpath = fname;
+ if (!might_be_an_url(bstr0(fname))) {
+ char *cwd = mp_getcwd(tmp);
+ if (!cwd)
+ goto exit;
+ realpath = mp_path_join(tmp, bstr0(cwd), bstr0(fname));
+ }
+ uint8_t md5[16];
+ av_md5_sum(md5, realpath, strlen(realpath));
+ char *conf = talloc_strdup(tmp, "");
+ for (int i = 0; i < 16; i++)
+ conf = talloc_asprintf_append(conf, "%02X", md5[i]);
+
+ conf = talloc_asprintf(tmp, "%s/%s", MP_WATCH_LATER_CONF, conf);
+
+ res = mp_find_user_config_file(conf);
+
+exit:
+ talloc_free(tmp);
+ return res;
+}
+
+static const char *backup_properties[] = {
+ "osd-level",
+ //"loop",
+ "speed",
+ "edition",
+ "pause",
+ //"volume",
+ //"mute",
+ "audio-delay",
+ //"balance",
+ "fullscreen",
+ "colormatrix",
+ "colormatrix-input-range",
+ "colormatrix-output-range",
+ "ontop",
+ "border",
+ "gamma",
+ "brightness",
+ "contrast",
+ "saturation",
+ "hue",
+ "panscan",
+ "aid",
+ "vid",
+ "sid",
+ "sub-delay",
+ "sub-pos",
+ //"sub-visibility",
+ "sub-scale",
+ "ass-use-margins",
+ "ass-vsfilter-aspect-compat",
+ "ass-style-override",
+ 0
+};
+
+void mp_write_watch_later_conf(struct MPContext *mpctx)
+{
+ void *tmp = talloc_new(NULL);
+ char *filename = mpctx->filename;
+ if (!filename)
+ goto exit;
+
+ double pos = get_current_time(mpctx);
+ int percent = get_percent_pos(mpctx);
+ if (percent < 1 || percent > 99 || pos == MP_NOPTS_VALUE)
+ goto exit;
+
+ mk_config_dir(MP_WATCH_LATER_CONF);
+
+ char *conffile = get_playback_resume_config_filename(mpctx->filename);
+ talloc_steal(tmp, conffile);
+ if (!conffile)
+ goto exit;
+
+ FILE *file = fopen(conffile, "wb");
+ if (!file)
+ goto exit;
+ fprintf(file, "start=%f\n", pos);
+ for (int i = 0; backup_properties[i]; i++) {
+ const char *pname = backup_properties[i];
+ char *tmp = NULL;
+ int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &tmp, mpctx);
+ if (r == M_PROPERTY_OK)
+ fprintf(file, "%s=%s\n", pname, tmp);
+ talloc_free(tmp);
+ }
+ fclose(file);
+
+exit:
+ talloc_free(tmp);
+}
+
+static void load_playback_resume(m_config_t *conf, const char *file)
+{
+ char *fname = get_playback_resume_config_filename(file);
+ if (fname) {
+ try_load_config(conf, fname);
+ unlink(fname);
+ }
+ talloc_free(fname);
+}
+
static void load_per_file_options(m_config_t *conf,
struct playlist_param *params,
int params_count)
@@ -3988,7 +4105,9 @@ static void play_current_file(struct MPContext *mpctx)
load_per_output_config(mpctx->mconfig, PROFILE_CFG_AO,
opts->audio_driver_list[0]);
- assert(mpctx->playlist->current);
+ if (opts->position_resume)
+ load_playback_resume(mpctx->mconfig, mpctx->filename);
+
load_per_file_options(mpctx->mconfig, mpctx->playlist->current->params,
mpctx->playlist->current->num_params);
@@ -4283,6 +4402,9 @@ goto_enable_cache: ;
terminate_playback: // don't jump here after ao/vo/getch initialization!
+ if (opts->position_save_on_quit && mpctx->stop_play != PT_RESTART)
+ mp_write_watch_later_conf(mpctx);
+
if (mpctx->step_frames)
opts->pause = 1;
@@ -4566,6 +4688,7 @@ int main(int argc, char *argv[])
init_input(mpctx);
mpctx->playlist->current = mpctx->playlist->first;
+
play_files(mpctx);
exit_player(mpctx, mpctx->stop_play == PT_QUIT ? EXIT_QUIT : EXIT_EOF,
diff --git a/core/options.h b/core/options.h
index 4729d7ca6a..276745492d 100644
--- a/core/options.h
+++ b/core/options.h
@@ -125,6 +125,8 @@ typedef struct MPOpts {
int play_frames;
double step_sec;
int64_t seek_to_byte;
+ int position_resume;
+ int position_save_on_quit;
int pause;
int keep_open;
int audio_id;
diff --git a/core/path.c b/core/path.c
index ed53d5d465..e373df21ab 100644
--- a/core/path.c
+++ b/core/path.c
@@ -31,6 +31,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <errno.h>
#include "config.h"
#include "core/mp_msg.h"
#include "core/path.h"
@@ -178,6 +179,19 @@ char *mp_path_join(void *talloc_ctx, struct bstr p1, struct bstr p2)
have_separator ? "" : "/", BSTR_P(p2));
}
+char *mp_getcwd(void *talloc_ctx)
+{
+ char *wd = talloc_array(talloc_ctx, char, 20);
+ while (getcwd(wd, talloc_get_size(wd)) == NULL) {
+ if (errno != ERANGE) {
+ talloc_free(wd);
+ return NULL;
+ }
+ wd = talloc_realloc(talloc_ctx, wd, char, talloc_get_size(wd) * 2);
+ }
+ return wd;
+}
+
bool mp_path_exists(const char *path)
{
struct stat st;
diff --git a/core/path.h b/core/path.h
index 3711be44b9..a3033199df 100644
--- a/core/path.h
+++ b/core/path.h
@@ -51,6 +51,8 @@ struct bstr mp_dirname(const char *path);
*/
char *mp_path_join(void *talloc_ctx, struct bstr p1, struct bstr p2);
+char *mp_getcwd(void *talloc_ctx);
+
bool mp_path_exists(const char *path);
bool mp_path_isdir(const char *path);
diff --git a/etc/input.conf b/etc/input.conf
index 300f20c307..5dbb7c8a06 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -56,6 +56,7 @@ PGDWN seek -600
} speed_mult 2.0
BS set speed 1.0 # reset speed to normal
q quit
+Q quit_watch_later
q {encode} quit
ESC quit
p cycle pause # toggle pause/playback mode