diff options
Diffstat (limited to 'core/mplayer.c')
-rw-r--r-- | core/mplayer.c | 494 |
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 */ |