summaryrefslogtreecommitdiffstats
path: root/core/mplayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/mplayer.c')
-rw-r--r--core/mplayer.c494
1 files changed, 386 insertions, 108 deletions
diff --git a/core/mplayer.c b/core/mplayer.c
index bbc000d7cc..361d3d5466 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>
@@ -84,6 +86,10 @@
#include "video/out/x11_common.h"
#endif
+#ifdef CONFIG_COCOA
+#include "osdep/macosx_application.h"
+#endif
+
#include "audio/out/ao.h"
#include "core/codecs.h"
@@ -275,10 +281,10 @@ static void print_stream(struct MPContext *mpctx, struct track *t, int id)
if (t->title)
mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title);
const char *codec = s ? s->codec : NULL;
- if (s && t->type == STREAM_SUB)
- codec = sh_sub_type2str(s->sub->type);
- if (t->sh_sub) // external subs hack
- codec = sh_sub_type2str(t->sh_sub->type);
+ if (!codec && t->sh_sub) // external subs hack
+ codec = t->sh_sub->gsh->codec;
+ if (!codec && t->subdata)
+ codec = t->subdata->codec;
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : "<unknown>");
if (t->is_external)
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)");
@@ -605,7 +611,24 @@ static MP_NORETURN void exit_player(struct MPContext *mpctx,
talloc_free(mpctx);
+#ifdef CONFIG_COCOA
+ terminate_cocoa_application();
+ // never reach here:
+ // terminate calls exit itself, just silence compiler warning
+ exit(0);
+#else
exit(rc);
+#endif
+}
+
+static void mk_config_dir(char *subdir)
+{
+ void *tmp = talloc_new(NULL);
+ char *confdir = talloc_steal(tmp, mp_find_user_config_file(""));
+ if (subdir)
+ confdir = mp_path_join(tmp, bstr0(confdir), bstr0(subdir));
+ mkdir(confdir, 0777);
+ talloc_free(tmp);
}
#include "cfg-mplayer.h"
@@ -626,26 +649,21 @@ static bool parse_cfgfiles(struct MPContext *mpctx, m_config_t *conf)
return true;
if (!m_config_parse_config_file(conf, MPLAYER_CONFDIR "/mpv.conf") < 0)
return false;
- if ((conffile = mp_find_user_config_file("")) == NULL)
- mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "Cannot find HOME directory.\n");
+ mk_config_dir(NULL);
+ if ((conffile = mp_find_user_config_file("config")) == NULL)
+ mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
+ "mp_find_user_config_file(\"config\") problem\n");
else {
- mkdir(conffile, 0777);
- talloc_free(conffile);
- if ((conffile = mp_find_user_config_file("config")) == NULL)
- mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
- "mp_find_user_config_file(\"config\") problem\n");
- else {
- if ((conffile_fd = open(conffile, O_CREAT | O_EXCL | O_WRONLY,
- 0666)) != -1) {
- mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
- "Creating config file: %s\n", conffile);
- write(conffile_fd, DEF_CONFIG, sizeof(DEF_CONFIG) - 1);
- close(conffile_fd);
- }
- if (m_config_parse_config_file(conf, conffile) < 0)
- return false;
- talloc_free(conffile);
+ if ((conffile_fd = open(conffile, O_CREAT | O_EXCL | O_WRONLY,
+ 0666)) != -1) {
+ mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
+ "Creating config file: %s\n", conffile);
+ write(conffile_fd, DEF_CONFIG, sizeof(DEF_CONFIG) - 1);
+ close(conffile_fd);
}
+ if (m_config_parse_config_file(conf, conffile) < 0)
+ return false;
+ talloc_free(conffile);
}
return true;
}
@@ -758,6 +776,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)
@@ -910,16 +1043,21 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
struct ass_track *asst = mp_ass_read_stream(mpctx->ass_library,
filename, sub_cp);
bool is_native_ass = asst;
+ const char *codec = NULL;
if (!asst) {
subd = sub_read_file(filename, fps, &mpctx->opts);
if (subd) {
+ codec = subd->codec;
asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps);
talloc_free(subd);
subd = NULL;
}
}
- if (asst)
+ if (asst) {
sh = sd_ass_create_from_track(asst, is_native_ass, opts);
+ if (codec)
+ sh->gsh->codec = codec;
+ }
#endif
} else
subd = sub_read_file(filename, fps, &mpctx->opts);
@@ -1060,7 +1198,7 @@ static void print_status(struct MPContext *mpctx)
char *line = NULL;
// Playback status
- if (mpctx->paused_for_cache) {
+ if (mpctx->paused_for_cache && !opts->pause) {
saddf(&line, "(Buffering) ");
} else if (mpctx->paused) {
saddf(&line, "(Paused) ");
@@ -1114,7 +1252,7 @@ static void print_status(struct MPContext *mpctx)
position, get_current_time(mpctx) - startpos) >= 0)
{
// encoding stats
- saddf(&line, "%s ", lavcbuf);
+ saddf(&line, " %s", lavcbuf);
} else
#endif
{
@@ -1424,7 +1562,7 @@ static void sadd_osd_status(char **buffer, struct MPContext *mpctx, bool full)
bool fractions = mpctx->opts.osd_fractions;
int sym = mpctx->osd_function;
if (!sym) {
- if (mpctx->paused_for_cache) {
+ if (mpctx->paused_for_cache && !mpctx->opts.pause) {
sym = OSD_CLOCK;
} else if (mpctx->paused || mpctx->step_frames) {
sym = OSD_PAUSE;
@@ -1730,7 +1868,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL;
unsigned char *packet = NULL;
int len;
- int type = sh_sub ? sh_sub->type : '\0';
+ const char *type = sh_sub ? sh_sub->gsh->codec : NULL;
mpctx->osd->sub_offset = mpctx->video_offset;
@@ -1753,7 +1891,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
}
// DVD sub:
- if (type == 'v' && !(sh_sub && sh_sub->active)) {
+ if (is_dvd_sub(type) && !(sh_sub && sh_sub->active)) {
int timestamp;
// Get a sub packet from the demuxer (or the vobsub.c thing, which
// should be a demuxer, but isn't).
@@ -1814,7 +1952,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f "
"duration=%5.3f len=%d\n", curpts_s, subpts_s, duration,
len);
- if (type == 'm') {
+ if (type && strcmp(type, "mov_text") == 0) {
if (len < 2)
continue;
len = FFMIN(len - 2, AV_RB16(packet));
@@ -1826,7 +1964,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
// text sub
if (duration < 0)
sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
- if (type == 'a') { // ssa/ass subs without libass => convert to plaintext
+ if (is_ass_sub(type)) { // ssa/ass subs without libass => convert to plaintext
int i;
unsigned char *p = packet;
for (i = 0; i < 8 && *p != '\0'; p++)
@@ -1983,7 +2121,7 @@ static void reinit_subs(struct MPContext *mpctx)
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0)
broken_lavc = true;
#endif
- if (mpctx->sh_sub->type == 'v' && track->demuxer
+ if (is_dvd_sub(mpctx->sh_sub->gsh->codec) && track->demuxer
&& (track->demuxer->type == DEMUXER_TYPE_MPEG_PS || broken_lavc))
init_vo_spudec(mpctx);
else
@@ -2405,6 +2543,7 @@ int reinit_video_chain(struct MPContext *mpctx)
sh_video->next_frame_time = 0;
mpctx->restart_playback = true;
mpctx->delay = 0;
+ mpctx->vo_pts_history_seek_ts++;
// ========== Init display (sh_video->disp_w*sh_video->disp_h/out_fmt) ============
@@ -2420,6 +2559,40 @@ no_video:
return 0;
}
+static void add_frame_pts(struct MPContext *mpctx, double pts)
+{
+ if (pts == MP_NOPTS_VALUE || mpctx->hrseek_framedrop) {
+ mpctx->vo_pts_history_seek_ts++; // mark discontinuity
+ return;
+ }
+ for (int n = MAX_NUM_VO_PTS - 1; n >= 1; n--) {
+ mpctx->vo_pts_history_seek[n] = mpctx->vo_pts_history_seek[n - 1];
+ mpctx->vo_pts_history_pts[n] = mpctx->vo_pts_history_pts[n - 1];
+ }
+ mpctx->vo_pts_history_seek[0] = mpctx->vo_pts_history_seek_ts;
+ mpctx->vo_pts_history_pts[0] = pts;
+}
+
+static double find_previous_pts(struct MPContext *mpctx, double pts)
+{
+ for (int n = 0; n < MAX_NUM_VO_PTS - 1; n++) {
+ if (pts == mpctx->vo_pts_history_pts[n] &&
+ mpctx->vo_pts_history_seek[n] != 0 &&
+ mpctx->vo_pts_history_seek[n] == mpctx->vo_pts_history_seek[n + 1])
+ {
+ return mpctx->vo_pts_history_pts[n + 1];
+ }
+ }
+ return MP_NOPTS_VALUE;
+}
+
+static double get_last_frame_pts(struct MPContext *mpctx)
+{
+ if (mpctx->vo_pts_history_seek[0] == mpctx->vo_pts_history_seek_ts)
+ return mpctx->vo_pts_history_pts[0];
+ return MP_NOPTS_VALUE;
+}
+
static bool filter_output_queued_frame(struct MPContext *mpctx)
{
struct sh_video *sh_video = mpctx->sh_video;
@@ -2463,7 +2636,6 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx)
&packet, mpctx->opts.force_fps);
if (in_size < 0)
return -1;
- sh_video->timer += frame_time;
if (mpctx->sh_audio)
mpctx->delay -= frame_time;
// video_read_frame can change fps (e.g. for ASF video)
@@ -2516,7 +2688,7 @@ static void determine_frame_pts(struct MPContext *mpctx)
sh_video->codec_reordered_pts : sh_video->sorted_pts;
}
-static double update_video(struct MPContext *mpctx)
+static double update_video(struct MPContext *mpctx, double endpts)
{
struct sh_video *sh_video = mpctx->sh_video;
struct vo *video_out = mpctx->video_out;
@@ -2548,8 +2720,8 @@ static double update_video(struct MPContext *mpctx)
pts += mpctx->video_offset;
if (pts >= mpctx->hrseek_pts - .005)
mpctx->hrseek_framedrop = false;
- int framedrop_type = mpctx->hrseek_framedrop ? 1 :
- check_framedrop(mpctx, sh_video->frametime);
+ int framedrop_type = mpctx->hrseek_active && mpctx->hrseek_framedrop ?
+ 1 : check_framedrop(mpctx, sh_video->frametime);
struct mp_image *decoded_frame =
decode_video(sh_video, pkt, framedrop_type, pts);
if (decoded_frame) {
@@ -2573,6 +2745,8 @@ static double update_video(struct MPContext *mpctx)
if (pts == MP_NOPTS_VALUE)
pts = sh_video->last_pts;
}
+ if (endpts == MP_NOPTS_VALUE || pts < endpts)
+ add_frame_pts(mpctx, pts);
if (mpctx->hrseek_active && pts < mpctx->hrseek_pts - .005) {
vo_skip_frame(video_out);
return 0;
@@ -2596,7 +2770,6 @@ static double update_video(struct MPContext *mpctx)
}
double frame_time = sh_video->pts - sh_video->last_pts;
sh_video->last_pts = sh_video->pts;
- sh_video->timer += frame_time;
if (mpctx->sh_audio)
mpctx->delay -= frame_time;
return frame_time;
@@ -2604,12 +2777,15 @@ static double update_video(struct MPContext *mpctx)
void pause_player(struct MPContext *mpctx)
{
+ mpctx->opts.pause = 1;
+
if (mpctx->paused)
return;
- mpctx->paused = 1;
+ mpctx->paused = true;
mpctx->step_frames = 0;
mpctx->time_frame -= get_relative_time(mpctx);
mpctx->osd_function = 0;
+ mpctx->paused_for_cache = false;
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
@@ -2627,11 +2803,15 @@ void pause_player(struct MPContext *mpctx)
void unpause_player(struct MPContext *mpctx)
{
+ mpctx->opts.pause = 0;
+
if (!mpctx->paused)
return;
- mpctx->paused = 0;
+ // Don't actually unpause while cache is loading.
+ if (mpctx->paused_for_cache)
+ return;
+ mpctx->paused = false;
mpctx->osd_function = 0;
- mpctx->paused_for_cache = false;
if (mpctx->ao && mpctx->sh_audio)
ao_resume(mpctx->ao);
@@ -2661,23 +2841,29 @@ static bool redraw_osd(struct MPContext *mpctx)
return true;
}
-void add_step_frame(struct MPContext *mpctx)
+void add_step_frame(struct MPContext *mpctx, int dir)
{
- mpctx->step_frames++;
- if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
- vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
- unpause_player(mpctx);
+ if (dir > 0) {
+ mpctx->step_frames += 1;
+ if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
+ vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
+ unpause_player(mpctx);
+ } else if (dir < 0) {
+ if (!mpctx->backstep_active && !mpctx->hrseek_active) {
+ mpctx->backstep_active = true;
+ mpctx->backstep_start_seek_ts = mpctx->vo_pts_history_seek_ts;
+ pause_player(mpctx);
+ }
+ }
}
static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
{
if (mpctx->sh_video) {
resync_video_stream(mpctx->sh_video);
- mpctx->sh_video->timer = 0;
vo_seek_reset(mpctx->video_out);
if (mpctx->sh_video->vf_initialized == 1)
vf_chain_seek_reset(mpctx->sh_video->vfilter);
- mpctx->sh_video->timer = 0;
mpctx->sh_video->num_buffered_pts = 0;
mpctx->sh_video->last_pts = MP_NOPTS_VALUE;
mpctx->delay = 0;
@@ -2768,6 +2954,7 @@ static int seek(MPContext *mpctx, struct seek_params seek,
bool timeline_fallthrough)
{
struct MPOpts *opts = &mpctx->opts;
+ uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts;
if (!mpctx->demuxer)
return -1;
@@ -2874,6 +3061,14 @@ static int seek(MPContext *mpctx, struct seek_params seek,
* and resetting could lose audio some decoders produce during init. */
seek_reset(mpctx, !timeline_fallthrough, !need_reset);
+ if (timeline_fallthrough) {
+ // Important if video reinit happens.
+ mpctx->vo_pts_history_seek_ts = prev_seek_ts;
+ } else {
+ mpctx->vo_pts_history_seek_ts++;
+ mpctx->backstep_active = false;
+ }
+
/* Use the target time as "current position" for further relative
* seeks etc until a new video frame has been decoded */
if (seek.type == MPSEEK_ABSOLUTE) {
@@ -2935,6 +3130,14 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
abort();
}
+static void execute_queued_seek(struct MPContext *mpctx)
+{
+ if (mpctx->seek.type) {
+ seek(mpctx, mpctx->seek, false);
+ mpctx->seek = (struct seek_params){0};
+ }
+}
+
double get_time_length(struct MPContext *mpctx)
{
struct demuxer *demuxer = mpctx->demuxer;
@@ -2944,11 +3147,9 @@ double get_time_length(struct MPContext *mpctx)
if (mpctx->timeline)
return mpctx->timeline[mpctx->num_timeline_parts].start;
- double get_time_ans;
- // <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW
- if (demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH,
- (void *) &get_time_ans) > 0)
- return get_time_ans;
+ double len = demuxer_get_time_length(demuxer);
+ if (len >= 0)
+ return len;
struct sh_video *sh_video = mpctx->sh_video;
struct sh_audio *sh_audio = mpctx->sh_audio;
@@ -2986,9 +3187,7 @@ double get_start_time(struct MPContext *mpctx)
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return 0;
- double time = 0;
- demux_control(demuxer, DEMUXER_CTRL_GET_START_TIME, &time);
- return time;
+ return demuxer_get_start_time(demuxer);
}
// Return playback position in 0.0-1.0 ratio, or -1 if unknown.
@@ -3075,7 +3274,7 @@ double chapter_start_time(struct MPContext *mpctx, int chapter)
if (mpctx->chapters)
return mpctx->chapters[chapter].start;
if (mpctx->master_demuxer)
- return demuxer_chapter_time(mpctx->master_demuxer, chapter, NULL);
+ return demuxer_chapter_time(mpctx->master_demuxer, chapter);
return -1;
}
@@ -3088,33 +3287,42 @@ int get_chapter_count(struct MPContext *mpctx)
return 0;
}
-int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts)
+// Seek to a given chapter. Tries to queue the seek, but might seek immediately
+// in some cases. Returns success, no matter if seek is queued or immediate.
+bool mp_seek_chapter(struct MPContext *mpctx, int chapter)
{
+ int num = get_chapter_count(mpctx);
+ if (num == 0)
+ return false;
+ if (chapter < 0 || chapter >= num)
+ return false;
+
mpctx->last_chapter_seek = -2;
- if (mpctx->chapters) {
- if (chapter >= mpctx->num_chapters)
- return -1;
- if (chapter < 0)
- chapter = 0;
- *seek_pts = mpctx->chapters[chapter].start;
- mpctx->last_chapter_seek = chapter;
- mpctx->last_chapter_pts = *seek_pts;
- return chapter;
- }
- if (mpctx->master_demuxer) {
- int res = demuxer_seek_chapter(mpctx->master_demuxer, chapter, seek_pts);
+ double pts;
+ if (mpctx->chapters) {
+ pts = mpctx->chapters[chapter].start;
+ goto do_seek;
+ } else if (mpctx->master_demuxer) {
+ int res = demuxer_seek_chapter(mpctx->master_demuxer, chapter, &pts);
if (res >= 0) {
- if (*seek_pts == -1)
- seek_reset(mpctx, true, true); // for DVD
- else {
- mpctx->last_chapter_seek = res;
- mpctx->last_chapter_pts = *seek_pts;
+ if (pts == -1) {
+ // for DVD/BD - seek happened via stream layer
+ seek_reset(mpctx, true, true);
+ mpctx->seek = (struct seek_params){0};
+ return true;
}
+ chapter = res;
+ goto do_seek;
}
- return res;
}
- return -1;
+ return false;
+
+do_seek:
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0);
+ mpctx->last_chapter_seek = chapter;
+ mpctx->last_chapter_pts = pts;
+ return true;
}
static void update_avsync(struct MPContext *mpctx)
@@ -3143,12 +3351,17 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx)
int cache = mp_get_cache_percent(mpctx);
bool idle = mp_get_cache_idle(mpctx);
if (mpctx->paused && mpctx->paused_for_cache) {
- if (cache < 0 || cache >= opts->stream_cache_min_percent || idle)
- unpause_player(mpctx);
- } else if (!mpctx->paused) {
+ if (cache < 0 || cache >= opts->stream_cache_min_percent || idle) {
+ mpctx->paused_for_cache = false;
+ if (!opts->pause)
+ unpause_player(mpctx);
+ }
+ } else {
if (cache >= 0 && cache <= opts->stream_cache_pause && !idle) {
- mpctx->paused_for_cache = true;
+ bool prev_paused_user = opts->pause;
pause_player(mpctx);
+ mpctx->paused_for_cache = true;
+ opts->pause = prev_paused_user;
}
}
}
@@ -3225,7 +3438,7 @@ static void run_playloop(struct MPContext *mpctx)
video_left = vo->hasframe || vo->frame_loaded;
if (!vo->frame_loaded && (!mpctx->paused || mpctx->restart_playback)) {
- double frame_time = update_video(mpctx);
+ double frame_time = update_video(mpctx, endpts);
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "*** ftime=%5.3f ***\n", frame_time);
if (mpctx->sh_video->vf_initialized < 0) {
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
@@ -3493,7 +3706,7 @@ static void run_playloop(struct MPContext *mpctx)
sleeptime = 0;
} else if (mpctx->paused && video_left) {
// force redrawing OSD by framestepping
- add_step_frame(mpctx);
+ add_step_frame(mpctx, 1);
sleeptime = 0;
}
}
@@ -3530,6 +3743,49 @@ static void run_playloop(struct MPContext *mpctx)
break;
}
+ if (mpctx->backstep_active) {
+ double current_pts = mpctx->last_vo_pts;
+ mpctx->backstep_active = false;
+ bool demuxer_ok = mpctx->demuxer && mpctx->demuxer->accurate_seek;
+ if (demuxer_ok && mpctx->sh_video && current_pts != MP_NOPTS_VALUE) {
+ double seek_pts = find_previous_pts(mpctx, current_pts);
+ if (seek_pts != MP_NOPTS_VALUE) {
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, seek_pts, 1);
+ } else {
+ double last = get_last_frame_pts(mpctx);
+ if (last != MP_NOPTS_VALUE && last >= current_pts &&
+ mpctx->backstep_start_seek_ts != mpctx->vo_pts_history_seek_ts)
+ {
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "Backstep failed.\n");
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, current_pts, 1);
+ } else if (!mpctx->hrseek_active) {
+ mp_msg(MSGT_CPLAYER, MSGL_V, "Start backstep indexing.\n");
+ // Force it to index the video up until current_pts.
+ // The whole point is getting frames _before_ that PTS,
+ // so apply an arbitrary offset. (In theory the offset
+ // has to be large enough to reach the previous frame.)
+ seek(mpctx, (struct seek_params){
+ .type = MPSEEK_ABSOLUTE,
+ .amount = current_pts - 1.0,
+ }, false);
+ // Don't leave hr-seek mode. If all goes right, hr-seek
+ // mode is cancelled as soon as the frame before
+ // current_pts is found during hr-seeking.
+ // Note that current_pts should be part of the index,
+ // otherwise we can't find the previous frame, so set the
+ // seek target an arbitrary amount of time after it.
+ if (mpctx->hrseek_active) {
+ mpctx->hrseek_pts = current_pts + 10.0;
+ mpctx->hrseek_framedrop = false;
+ mpctx->backstep_active = true;
+ }
+ } else {
+ mpctx->backstep_active = true;
+ }
+ }
+ }
+ }
+
// handle -sstep
if (opts->step_sec > 0 && !mpctx->stop_play && !mpctx->paused &&
!mpctx->restart_playback)
@@ -3550,13 +3806,9 @@ static void run_playloop(struct MPContext *mpctx)
}
}
- if (mpctx->seek.type) {
- seek(mpctx, mpctx->seek, false);
- mpctx->seek = (struct seek_params){ 0 };
- }
+ execute_queued_seek(mpctx);
}
-
static int read_keys(void *ctx, int fd)
{
if (getch2(ctx))
@@ -3598,6 +3850,7 @@ static int match_lang(char **langs, char *lang)
* Sort tracks based on the following criteria, and pick the first:
* 0) track matches tid (always wins)
* 1) track is external
+ * 1b) track was passed explicitly (is not an auto-loaded subtitle)
* 2) earlier match in lang list
* 3) track is marked default
* 4) lower track number
@@ -3610,6 +3863,8 @@ static bool compare_track(struct track *t1, struct track *t2, char **langs)
{
if (t1->is_external != t2->is_external)
return t1->is_external;
+ if (t1->auto_loaded != t2->auto_loaded)
+ return !t1->auto_loaded;
int l1 = match_lang(langs, t1->lang), l2 = match_lang(langs, t2->lang);
if (l1 != l2)
return l1 > l2;
@@ -3677,6 +3932,10 @@ static void init_input(struct MPContext *mpctx)
mp_input_add_key_fd(mpctx->input, 0, 1, read_keys, NULL, mpctx->key_fifo);
// Set the libstream interrupt callback
stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input);
+
+#ifdef CONFIG_COCOA
+ cocoa_set_state(mpctx->input, mpctx->key_fifo);
+#endif
}
static void open_subtitles_from_options(struct MPContext *mpctx)
@@ -3692,8 +3951,11 @@ static void open_subtitles_from_options(struct MPContext *mpctx)
if (mpctx->opts.sub_auto) { // auto load sub file ...
char **tmp = find_text_subtitles(&mpctx->opts, mpctx->filename);
int nsub = MP_TALLOC_ELEMS(tmp);
- for (int i = 0; i < nsub; i++)
- mp_add_subtitles(mpctx, tmp[i], sub_fps, 1);
+ for (int i = 0; i < nsub; i++) {
+ struct track *track = mp_add_subtitles(mpctx, tmp[i], sub_fps, 1);
+ if (track)
+ track->auto_loaded = true;
+ }
talloc_free(tmp);
}
}
@@ -3877,7 +4139,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);
@@ -3999,10 +4263,16 @@ goto_enable_cache: ;
mpctx->demuxer);
}
- if (mpctx->timeline && !mpctx->demuxer->matroska_data.ordered_chapters) {
- // With Matroska, the "master" file dictates track layout etc.
- // On the contrary, the EDL and CUE demuxers are empty wrappers.
+ if (mpctx->timeline) {
+ // With Matroska, the "master" file usually dictates track layout etc.
+ // On the contrary, the EDL and CUE demuxers are empty wrappers, as
+ // well as Matroska ordered chapter playlist-like files.
+ for (int n = 0; n < mpctx->num_timeline_parts; n++) {
+ if (mpctx->timeline[n].source == mpctx->demuxer)
+ goto main_is_ok;
+ }
mpctx->demuxer = mpctx->timeline[0].source;
+ main_is_ok: ;
}
add_dvd_tracks(mpctx);
add_demuxer_tracks(mpctx, mpctx->demuxer);
@@ -4049,7 +4319,6 @@ goto_enable_cache: ;
//================ SETUP STREAMS ==========================
if (mpctx->sh_video) {
- mpctx->sh_video->timer = 0;
if (!opts->ignore_start)
mpctx->audio_delay += mpctx->sh_video->stream_delay;
}
@@ -4122,33 +4391,28 @@ goto_enable_cache: ;
mpctx->hrseek_active = false;
mpctx->hrseek_framedrop = false;
mpctx->step_frames = 0;
+ mpctx->backstep_active = false;
mpctx->total_avsync_change = 0;
mpctx->last_chapter_seek = -2;
mpctx->playing_msg_shown = false;
+ mpctx->paused = false;
+ mpctx->paused_for_cache = false;
+ mpctx->seek = (struct seek_params){ 0 };
// If there's a timeline force an absolute seek to initialize state
double startpos = rel_time_to_abs(mpctx, opts->play_start, -1);
if (startpos != -1 || mpctx->timeline) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
- seek(mpctx, mpctx->seek, false);
+ execute_queued_seek(mpctx);
}
if (opts->chapterrange[0] > 0) {
- double pts;
- if (seek_chapter(mpctx, opts->chapterrange[0] - 1, &pts) >= 0
- && pts > -1.0) {
- queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0);
- seek(mpctx, mpctx->seek, false);
- }
+ if (mp_seek_chapter(mpctx, opts->chapterrange[0] - 1))
+ execute_queued_seek(mpctx);
}
- mpctx->seek = (struct seek_params){ 0 };
get_relative_time(mpctx); // reset current delta
- // Make sure VO knows current pause state
- if (mpctx->sh_video)
- vo_control(mpctx->video_out,
- mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
- if (mpctx->opts.start_paused)
+ if (mpctx->opts.pause)
pause_player(mpctx);
while (!mpctx->stop_play)
@@ -4168,8 +4432,11 @@ 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)
- mpctx->paused = 1;
+ opts->pause = 1;
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
@@ -4366,7 +4633,7 @@ static void osdep_preinit(int *p_argc, char ***p_argv)
/* This preprocessor directive is a hack to generate a mplayer-nomain.o object
* file for some tools to link against. */
#ifndef DISABLE_MAIN
-int main(int argc, char *argv[])
+static int mpv_main(int argc, char *argv[])
{
osdep_preinit(&argc, &argv);
@@ -4451,6 +4718,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,
@@ -4458,4 +4726,14 @@ int main(int argc, char *argv[])
return 1;
}
+
+int main(int argc, char *argv[])
+{
+#ifdef CONFIG_COCOA
+ cocoa_main(mpv_main, argc, argv);
+#else
+ mpv_main(argc, argv);
+#endif
+}
+
#endif /* DISABLE_MAIN */