2010-01-30 23:24:23 +00:00
|
|
|
/*
|
|
|
|
* This file is part of MPlayer.
|
|
|
|
*
|
|
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2006-04-24 21:33:50 +00:00
|
|
|
|
2001-08-01 09:14:02 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2008-12-08 18:04:08 +00:00
|
|
|
#include <stdbool.h>
|
2010-11-13 17:27:01 +00:00
|
|
|
#include <math.h>
|
2011-05-07 17:20:13 +00:00
|
|
|
#include <assert.h>
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
#include <ctype.h>
|
2010-11-13 17:27:01 +00:00
|
|
|
|
2012-12-09 14:49:39 +00:00
|
|
|
#ifdef PTW32_STATIC_LIB
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
|
|
|
|
2012-02-01 18:01:16 +00:00
|
|
|
#include <libavutil/intreadwrite.h>
|
2012-10-31 21:35:48 +00:00
|
|
|
#include <libavutil/attributes.h>
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
#include <libavutil/md5.h>
|
2013-07-15 00:28:46 +00:00
|
|
|
#include <libavutil/common.h>
|
2012-02-01 18:01:16 +00:00
|
|
|
|
2013-01-24 11:25:58 +00:00
|
|
|
#include <libavcodec/version.h>
|
|
|
|
|
2004-04-30 20:05:54 +00:00
|
|
|
#include "config.h"
|
2008-04-20 04:36:34 +00:00
|
|
|
#include "talloc.h"
|
2004-04-30 20:05:54 +00:00
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
#include "osdep/io.h"
|
|
|
|
|
2008-10-13 16:23:55 +00:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
2004-04-30 16:56:00 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
2012-12-19 11:58:52 +00:00
|
|
|
#define WAKEUP_PERIOD 0.5
|
2001-08-01 09:14:02 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2002-03-23 21:12:38 +00:00
|
|
|
// #include <sys/mman.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <sys/types.h>
|
2003-04-04 20:02:53 +00:00
|
|
|
#ifndef __MINGW32__
|
|
|
|
#include <sys/ioctl.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <sys/wait.h>
|
2003-04-04 20:02:53 +00:00
|
|
|
#endif
|
|
|
|
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/stat.h>
|
2001-07-31 23:18:16 +00:00
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <fcntl.h>
|
2003-02-07 19:44:12 +00:00
|
|
|
#include <limits.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2002-03-15 22:25:57 +00:00
|
|
|
#include <errno.h>
|
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/mpv_global.h"
|
|
|
|
#include "mpvcore/mp_msg.h"
|
2009-10-31 21:03:48 +00:00
|
|
|
#include "av_log.h"
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2001-08-18 20:32:09 +00:00
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/m_option.h"
|
|
|
|
#include "mpvcore/m_config.h"
|
|
|
|
#include "mpvcore/resolve.h"
|
|
|
|
#include "mpvcore/m_property.h"
|
2003-03-30 17:13:04 +00:00
|
|
|
|
2011-02-26 19:47:49 +00:00
|
|
|
#include "sub/find_subfiles.h"
|
2011-01-16 18:03:08 +00:00
|
|
|
#include "sub/dec_sub.h"
|
2013-04-28 19:12:11 +00:00
|
|
|
#include "sub/sd.h"
|
2001-03-30 03:07:45 +00:00
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/mp_osd.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "video/out/vo.h"
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/screenshot.h"
|
2001-07-30 02:00:54 +00:00
|
|
|
|
2011-01-26 17:40:52 +00:00
|
|
|
#include "sub/sub.h"
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/cpudetect.h"
|
2001-03-04 21:01:54 +00:00
|
|
|
|
2008-08-04 06:16:23 +00:00
|
|
|
#ifdef CONFIG_X11
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "video/out/x11_common.h"
|
2003-05-30 17:57:41 +00:00
|
|
|
#endif
|
|
|
|
|
2013-02-23 17:28:22 +00:00
|
|
|
#ifdef CONFIG_COCOA
|
|
|
|
#include "osdep/macosx_application.h"
|
|
|
|
#endif
|
|
|
|
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "audio/out/ao.h"
|
2001-06-02 23:30:26 +00:00
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/codecs.h"
|
2001-03-20 22:11:38 +00:00
|
|
|
|
2003-02-09 20:18:23 +00:00
|
|
|
#include "osdep/getch2.h"
|
|
|
|
#include "osdep/timer.h"
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/input/input.h"
|
|
|
|
#include "mpvcore/encode.h"
|
2012-09-14 15:51:26 +00:00
|
|
|
|
2009-02-10 15:34:44 +00:00
|
|
|
#include "osdep/priority.h"
|
2005-04-02 18:29:16 +00:00
|
|
|
|
2006-07-31 17:39:17 +00:00
|
|
|
#include "stream/tv.h"
|
2006-08-28 17:05:18 +00:00
|
|
|
#include "stream/stream_radio.h"
|
2008-08-03 15:21:40 +00:00
|
|
|
#ifdef CONFIG_DVBIN
|
2006-07-31 17:39:17 +00:00
|
|
|
#include "stream/dvbin.h"
|
2003-03-16 20:13:28 +00:00
|
|
|
#endif
|
|
|
|
|
2002-01-08 02:01:04 +00:00
|
|
|
//**************************************************************************//
|
|
|
|
// Playtree
|
|
|
|
//**************************************************************************//
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/playlist.h"
|
|
|
|
#include "mpvcore/playlist_parser.h"
|
2003-05-30 17:57:41 +00:00
|
|
|
|
2002-01-14 23:38:49 +00:00
|
|
|
//**************************************************************************//
|
|
|
|
// Config
|
|
|
|
//**************************************************************************//
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/parser-cfg.h"
|
|
|
|
#include "mpvcore/parser-mpcmd.h"
|
2002-01-14 23:38:49 +00:00
|
|
|
|
2001-03-30 03:07:45 +00:00
|
|
|
//**************************************************************************//
|
|
|
|
// Config file
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/path.h"
|
2001-03-21 00:14:42 +00:00
|
|
|
|
2001-04-15 03:40:37 +00:00
|
|
|
//**************************************************************************//
|
2001-04-15 19:13:38 +00:00
|
|
|
//**************************************************************************//
|
|
|
|
// Input media streaming & demultiplexer:
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2006-07-31 17:39:17 +00:00
|
|
|
#include "stream/stream.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "demux/demux.h"
|
|
|
|
#include "demux/stheader.h"
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2013-06-07 20:57:00 +00:00
|
|
|
#include "audio/filter/af.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "audio/decode/dec_audio.h"
|
|
|
|
#include "video/decode/dec_video.h"
|
|
|
|
#include "video/mp_image.h"
|
|
|
|
#include "video/filter/vf.h"
|
|
|
|
#include "video/decode/vd.h"
|
2001-10-30 17:38:09 +00:00
|
|
|
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "audio/mixer.h"
|
2006-10-11 17:25:46 +00:00
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/mp_core.h"
|
|
|
|
#include "mpvcore/options.h"
|
2006-10-11 17:25:46 +00:00
|
|
|
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
#include "mp_lua.h"
|
|
|
|
|
2013-06-07 20:57:00 +00:00
|
|
|
const char mp_help_text[] = _(
|
2012-10-11 00:04:08 +00:00
|
|
|
"Usage: mpv [options] [url|path/]filename\n"
|
2010-03-07 16:05:48 +00:00
|
|
|
"\n"
|
2013-09-10 13:19:37 +00:00
|
|
|
"Basic options:\n"
|
2012-11-15 17:49:17 +00:00
|
|
|
" --start=<time> seek to given (percent, seconds, or hh:mm:ss) position\n"
|
2012-07-30 00:06:39 +00:00
|
|
|
" --no-audio do not play sound\n"
|
|
|
|
" --no-video do not play video\n"
|
|
|
|
" --fs fullscreen playback\n"
|
|
|
|
" --sub=<file> specify subtitle file to use\n"
|
|
|
|
" --playlist=<file> specify playlist file\n"
|
2013-09-10 13:19:37 +00:00
|
|
|
"\n"
|
|
|
|
" --list-options list all mpv options\n"
|
2010-03-07 16:05:48 +00:00
|
|
|
"\n");
|
|
|
|
|
2012-07-30 00:10:21 +00:00
|
|
|
static const char av_desync_help_text[] = _(
|
|
|
|
"\n\n"
|
|
|
|
" *************************************************\n"
|
|
|
|
" **** Audio/Video desynchronisation detected! ****\n"
|
|
|
|
" *************************************************\n\n"
|
|
|
|
"This means either the audio or the video is played too slowly.\n"
|
|
|
|
"Possible reasons, problems, workarounds:\n"
|
|
|
|
"- Your system is simply too slow for this file.\n"
|
|
|
|
" Transcode it to a lower bitrate file with tools like HandBrake.\n"
|
|
|
|
"- Broken/buggy _audio_ driver.\n"
|
|
|
|
" Experiment with different values for --autosync, 30 is a good start.\n"
|
|
|
|
" If you have PulseAudio, try --ao=alsa .\n"
|
|
|
|
"- Slow video output.\n"
|
|
|
|
" Try a different -vo driver (-vo help for a list) or try -framedrop!\n"
|
2012-09-23 14:10:00 +00:00
|
|
|
"- Playing a video file with --vo=opengl with higher FPS than the monitor.\n"
|
2013-04-12 12:36:26 +00:00
|
|
|
" This is due to vsync limiting the framerate.\n"
|
2012-07-30 00:10:21 +00:00
|
|
|
"- Playing from a slow network source.\n"
|
|
|
|
" Download the file instead.\n"
|
|
|
|
"- Try to find out whether audio or video is causing this by experimenting\n"
|
|
|
|
" with --no-video and --no-audio.\n"
|
|
|
|
"If none of this helps you, file a bug report.\n\n");
|
2009-07-06 22:15:02 +00:00
|
|
|
|
|
|
|
|
2001-02-24 20:28:24 +00:00
|
|
|
//**************************************************************************//
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2011-01-26 17:40:52 +00:00
|
|
|
#include "sub/ass_mp.h"
|
2006-07-07 18:26:51 +00:00
|
|
|
|
2002-07-19 20:51:48 +00:00
|
|
|
|
|
|
|
// ---
|
|
|
|
|
2013-08-06 20:41:30 +00:00
|
|
|
#include "mpvcore/mp_common.h"
|
|
|
|
#include "mpvcore/command.h"
|
2001-09-05 10:49:04 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static void reset_subtitles(struct MPContext *mpctx);
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
static void reinit_subs(struct MPContext *mpctx);
|
2013-10-03 22:24:17 +00:00
|
|
|
static void handle_force_window(struct MPContext *mpctx, bool reconfig);
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
static double get_relative_time(struct MPContext *mpctx)
|
2008-04-28 09:09:31 +00:00
|
|
|
{
|
2013-05-25 16:31:06 +00:00
|
|
|
int64_t new_time = mp_time_us();
|
|
|
|
int64_t delta = new_time - mpctx->last_time;
|
2008-04-28 09:09:31 +00:00
|
|
|
mpctx->last_time = new_time;
|
|
|
|
return delta * 0.000001;
|
|
|
|
}
|
|
|
|
|
2012-11-15 17:49:17 +00:00
|
|
|
static double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t,
|
|
|
|
double fallback_time)
|
|
|
|
{
|
|
|
|
double length = get_time_length(mpctx);
|
|
|
|
switch (t.type) {
|
|
|
|
case REL_TIME_ABSOLUTE:
|
|
|
|
return t.pos;
|
|
|
|
case REL_TIME_NEGATIVE:
|
|
|
|
if (length != 0)
|
|
|
|
return FFMAX(length - t.pos, 0.0);
|
|
|
|
break;
|
|
|
|
case REL_TIME_PERCENT:
|
|
|
|
if (length != 0)
|
|
|
|
return length * (t.pos / 100.0);
|
|
|
|
break;
|
2012-11-18 17:02:14 +00:00
|
|
|
case REL_TIME_CHAPTER:
|
|
|
|
if (chapter_start_time(mpctx, t.pos) >= 0)
|
|
|
|
return chapter_start_time(mpctx, t.pos);
|
|
|
|
break;
|
2012-11-15 17:49:17 +00:00
|
|
|
}
|
|
|
|
return fallback_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double get_play_end_pts(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-11-15 17:49:17 +00:00
|
|
|
if (opts->play_end.type) {
|
|
|
|
return rel_time_to_abs(mpctx, opts->play_end, MP_NOPTS_VALUE);
|
|
|
|
} else if (opts->play_length.type) {
|
2012-12-08 22:30:59 +00:00
|
|
|
double start = rel_time_to_abs(mpctx, opts->play_start, 0);
|
2012-11-15 17:49:17 +00:00
|
|
|
double length = rel_time_to_abs(mpctx, opts->play_length, -1);
|
|
|
|
if (start != -1 && length != -1)
|
|
|
|
return start + length;
|
|
|
|
}
|
|
|
|
return MP_NOPTS_VALUE;
|
|
|
|
}
|
|
|
|
|
2013-05-20 22:32:38 +00:00
|
|
|
static void print_stream(struct MPContext *mpctx, struct track *t)
|
2012-07-29 19:04:57 +00:00
|
|
|
{
|
2012-08-19 16:01:30 +00:00
|
|
|
struct sh_stream *s = t->stream;
|
2012-07-29 19:04:57 +00:00
|
|
|
const char *tname = "?";
|
|
|
|
const char *selopt = "?";
|
|
|
|
const char *langopt = "?";
|
2013-05-20 22:32:38 +00:00
|
|
|
const char *iid = NULL;
|
2012-08-19 16:01:30 +00:00
|
|
|
switch (t->type) {
|
2012-07-29 19:04:57 +00:00
|
|
|
case STREAM_VIDEO:
|
2013-05-20 22:32:38 +00:00
|
|
|
tname = "Video"; selopt = "vid"; langopt = NULL; iid = "VID";
|
2012-07-29 19:04:57 +00:00
|
|
|
break;
|
|
|
|
case STREAM_AUDIO:
|
2013-05-20 22:32:38 +00:00
|
|
|
tname = "Audio"; selopt = "aid"; langopt = "alang"; iid = "AID";
|
2012-07-29 19:04:57 +00:00
|
|
|
break;
|
2012-08-03 10:24:55 +00:00
|
|
|
case STREAM_SUB:
|
2013-05-20 22:32:38 +00:00
|
|
|
tname = "Subs"; selopt = "sid"; langopt = "slang"; iid = "SID";
|
2012-07-29 19:04:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "[stream] %-5s %3s",
|
|
|
|
tname, mpctx->current_track[t->type] == t ? "(+)" : "");
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%d", selopt, t->user_tid);
|
2013-02-09 14:15:28 +00:00
|
|
|
if (t->lang && langopt)
|
2012-08-19 16:01:30 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%s", langopt, t->lang);
|
|
|
|
if (t->default_track)
|
2012-07-29 19:04:57 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (*)");
|
2012-12-10 17:52:06 +00:00
|
|
|
if (t->attached_picture)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " [P]");
|
2012-08-19 16:01:30 +00:00
|
|
|
if (t->title)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title);
|
core: redo how codecs are mapped, remove codecs.conf
Use codec names instead of FourCCs to identify codecs. Rewrite how
codecs are selected and initialized. Now each decoder exports a list
of decoders (and the codec it supports) via add_decoders(). The order
matters, and the first decoder for a given decoder is preferred over
the other decoders. E.g. all ad_mpg123 decoders are preferred over
ad_lavc, because it comes first in the mpcodecs_ad_drivers array.
Likewise, decoders within ad_lavc that are enumerated first by
libavcodec (using av_codec_next()) are preferred. (This is actually
critical to select h264 software decoding by default instead of vdpau.
libavcodec and ffmpeg/avconv use the same method to select decoders by
default, so we hope this is sane.)
The codec names follow libavcodec's codec names as defined by
AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders
have names different from the canonical codec name. The AVCodecDescriptor
API is relatively new, so we need a compatibility layer for older
libavcodec versions for codec names that are referenced internally,
and which are different from the decoder name. (Add a configure check
for that, because checking versions is getting way too messy.)
demux/codec_tags.c is generated from the former codecs.conf (minus
"special" decoders like vdpau, and excluding the mappings that are the
same as the mappings libavformat's exported RIFF tables). It contains
all the mappings from FourCCs to codec name. This is needed for
demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the
codec as determined by libavformat, while the other demuxers have to do
this on their own, using the mp_set_audio/video_codec_from_tag()
functions. Note that the sh_audio/video->format members don't uniquely
identify the codec anymore, and sh->codec takes over this role.
Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which
provide cover the functionality of the removed switched.
Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure
container/video combinations (e.g. the sample Film_200_zygo_pro.mov)
are played flipped. ffplay/avplay doesn't handle this properly either,
so we don't care and blame ffmeg/libav instead.
2013-02-09 14:15:19 +00:00
|
|
|
const char *codec = s ? s->codec : NULL;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : "<unknown>");
|
2012-08-19 16:01:30 +00:00
|
|
|
if (t->is_external)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)");
|
2012-07-29 19:04:57 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
|
2013-05-20 22:32:38 +00:00
|
|
|
// legacy compatibility
|
|
|
|
if (!iid)
|
|
|
|
return;
|
|
|
|
int id = t->user_tid;
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_ID=%d\n", iid, id);
|
|
|
|
if (t->title)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_%d_NAME=%s\n", iid, id, t->title);
|
|
|
|
if (t->lang)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_%d_LANG=%s\n", iid, id, t->lang);
|
2012-07-29 19:04:57 +00:00
|
|
|
}
|
|
|
|
|
2010-11-07 22:54:32 +00:00
|
|
|
static void print_file_properties(struct MPContext *mpctx, const char *filename)
|
2010-06-07 17:12:49 +00:00
|
|
|
{
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILENAME=%s\n",
|
2012-07-30 23:35:53 +00:00
|
|
|
filename);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->sh_video) {
|
|
|
|
/* Assume FOURCC if all bytes >= 0x20 (' ') */
|
|
|
|
if (mpctx->sh_video->format >= 0x20202020)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_FORMAT=%.4s\n", (char *)&mpctx->sh_video->format);
|
|
|
|
else
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_FORMAT=0x%08X\n", mpctx->sh_video->format);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_BITRATE=%d\n", mpctx->sh_video->i_bps * 8);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_WIDTH=%d\n", mpctx->sh_video->disp_w);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_HEIGHT=%d\n", mpctx->sh_video->disp_h);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_FPS=%5.3f\n", mpctx->sh_video->fps);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_ASPECT=%1.4f\n", mpctx->sh_video->aspect);
|
|
|
|
}
|
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
/* Assume FOURCC if all bytes >= 0x20 (' ') */
|
|
|
|
if (mpctx->sh_audio->format >= 0x20202020)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_AUDIO_FORMAT=%.4s\n", (char *)&mpctx->sh_audio->format);
|
|
|
|
else
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_AUDIO_FORMAT=%d\n", mpctx->sh_audio->format);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_AUDIO_BITRATE=%d\n", mpctx->sh_audio->i_bps * 8);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_AUDIO_RATE=%d\n", mpctx->sh_audio->samplerate);
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
2013-04-06 20:43:12 +00:00
|
|
|
"ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels.num);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_LENGTH=%.2f\n", get_time_length(mpctx));
|
2012-08-19 16:07:06 +00:00
|
|
|
int chapter_count = get_chapter_count(mpctx);
|
|
|
|
if (chapter_count >= 0) {
|
2011-10-23 02:51:44 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTERS=%d\n", chapter_count);
|
|
|
|
for (int i = 0; i < chapter_count; i++) {
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_ID=%d\n", i);
|
2012-03-14 09:27:36 +00:00
|
|
|
// print in milliseconds
|
|
|
|
double time = chapter_start_time(mpctx, i) * 1000.0;
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_START=%"PRId64"\n",
|
|
|
|
i, (int64_t)(time < 0 ? -1 : time));
|
2011-10-23 02:51:44 +00:00
|
|
|
char *name = chapter_name(mpctx, i);
|
|
|
|
if (name) {
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_NAME=%s\n", i,
|
|
|
|
name);
|
|
|
|
talloc_free(name);
|
|
|
|
}
|
|
|
|
}
|
2012-08-19 16:07:06 +00:00
|
|
|
}
|
2012-08-25 23:19:42 +00:00
|
|
|
struct demuxer *demuxer = mpctx->master_demuxer;
|
|
|
|
if (demuxer->num_editions > 1)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Playing edition %d of %d (--edition=%d).\n",
|
|
|
|
demuxer->edition + 1, demuxer->num_editions, demuxer->edition);
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
|
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++)
|
|
|
|
if (mpctx->tracks[n]->type == t)
|
2013-05-20 22:32:38 +00:00
|
|
|
print_stream(mpctx, mpctx->tracks[n]);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2010-06-07 17:12:49 +00:00
|
|
|
}
|
|
|
|
|
2012-12-08 12:12:46 +00:00
|
|
|
// Time used to seek external tracks to.
|
|
|
|
static double get_main_demux_pts(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
double main_new_pos = MP_NOPTS_VALUE;
|
|
|
|
if (mpctx->demuxer) {
|
2013-07-11 17:17:51 +00:00
|
|
|
for (int n = 0; n < mpctx->demuxer->num_streams; n++) {
|
|
|
|
if (main_new_pos == MP_NOPTS_VALUE)
|
|
|
|
main_new_pos = demux_get_next_pts(mpctx->demuxer->streams[n]);
|
2012-12-08 12:12:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return main_new_pos;
|
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static void set_demux_field(struct MPContext *mpctx, enum stream_type type,
|
|
|
|
struct sh_stream *s)
|
|
|
|
{
|
|
|
|
mpctx->sh[type] = s;
|
|
|
|
// redundant fields for convenience access
|
|
|
|
switch(type) {
|
|
|
|
case STREAM_VIDEO: mpctx->sh_video = s ? s->video : NULL; break;
|
|
|
|
case STREAM_AUDIO: mpctx->sh_audio = s ? s->audio : NULL; break;
|
|
|
|
case STREAM_SUB: mpctx->sh_sub = s ? s->sub : NULL; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_demux_stream(struct MPContext *mpctx, enum stream_type type)
|
|
|
|
{
|
|
|
|
struct track *track = mpctx->current_track[type];
|
|
|
|
set_demux_field(mpctx, type, track ? track->stream : NULL);
|
|
|
|
struct sh_stream *stream = mpctx->sh[type];
|
2012-12-08 12:12:46 +00:00
|
|
|
if (stream) {
|
2012-08-19 16:01:30 +00:00
|
|
|
demuxer_switch_track(stream->demuxer, type, stream);
|
2012-12-08 12:12:46 +00:00
|
|
|
if (track->is_external) {
|
|
|
|
double pts = get_main_demux_pts(mpctx);
|
2013-08-22 17:13:29 +00:00
|
|
|
demux_seek(stream->demuxer, pts, SEEK_ABSOLUTE);
|
2012-12-08 12:12:46 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_demux_stream(struct MPContext *mpctx, enum stream_type type)
|
|
|
|
{
|
|
|
|
struct sh_stream *stream = mpctx->sh[type];
|
|
|
|
if (stream)
|
|
|
|
demuxer_switch_track(stream->demuxer, type, NULL);
|
|
|
|
set_demux_field(mpctx, type, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch the demuxers to current track selection. This is possibly important
|
|
|
|
// for intialization: if something reads packets from the demuxer (like at least
|
|
|
|
// reinit_audio_chain does, or when seeking), packets from the other streams
|
|
|
|
// should be queued instead of discarded. So all streams should be enabled
|
|
|
|
// before the first initialization function is called.
|
|
|
|
static void preselect_demux_streams(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
// Disable all streams, just to be sure no unwanted streams are selected.
|
|
|
|
for (int n = 0; n < mpctx->num_sources; n++) {
|
2013-05-29 12:54:51 +00:00
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
struct track *track = mpctx->current_track[type];
|
|
|
|
if (!(track && track->demuxer == mpctx->sources[n] &&
|
|
|
|
demuxer_stream_is_selected(track->demuxer, track->stream)))
|
|
|
|
demuxer_switch_track(mpctx->sources[n], type, NULL);
|
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
struct track *track = mpctx->current_track[type];
|
|
|
|
if (track && track->stream)
|
|
|
|
demuxer_switch_track(track->stream->demuxer, type, track->stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-16 18:03:08 +00:00
|
|
|
static void uninit_subs(struct demuxer *demuxer)
|
|
|
|
{
|
2013-07-07 23:26:13 +00:00
|
|
|
for (int i = 0; i < demuxer->num_streams; i++) {
|
|
|
|
struct sh_stream *sh = demuxer->streams[i];
|
|
|
|
if (sh->sub) {
|
|
|
|
sub_destroy(sh->sub->dec_sub);
|
|
|
|
sh->sub->dec_sub = NULL;
|
2013-06-01 17:44:12 +00:00
|
|
|
}
|
2011-01-16 18:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
|
|
|
{
|
|
|
|
mask &= mpctx->initialized_flags;
|
|
|
|
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG2, "\n*** uninit(0x%X)\n", mask);
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_ACODEC) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_ACODEC;
|
2013-09-19 12:33:26 +00:00
|
|
|
mixer_uninit_audio(mpctx->mixer);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->sh_audio)
|
|
|
|
uninit_audio(mpctx->sh_audio);
|
2012-08-19 16:01:30 +00:00
|
|
|
cleanup_demux_stream(mpctx, STREAM_AUDIO);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_SUB) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_SUB;
|
2012-08-19 16:01:30 +00:00
|
|
|
if (mpctx->sh_sub)
|
2013-06-01 17:44:12 +00:00
|
|
|
sub_reset(mpctx->sh_sub->dec_sub);
|
2012-08-19 16:01:30 +00:00
|
|
|
cleanup_demux_stream(mpctx, STREAM_SUB);
|
2013-06-01 17:44:12 +00:00
|
|
|
mpctx->osd->dec_sub = NULL;
|
2012-08-19 16:01:30 +00:00
|
|
|
reset_subtitles(mpctx);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_VCODEC) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_VCODEC;
|
|
|
|
if (mpctx->sh_video)
|
|
|
|
uninit_video(mpctx->sh_video);
|
2012-08-19 16:01:30 +00:00
|
|
|
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
mpctx->sync_audio_to_video = false;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_DEMUXER) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_DEMUXER;
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int i = 0; i < mpctx->num_tracks; i++) {
|
2012-11-15 19:22:41 +00:00
|
|
|
talloc_free(mpctx->tracks[i]);
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
|
|
|
mpctx->num_tracks = 0;
|
|
|
|
for (int t = 0; t < STREAM_TYPE_COUNT; t++)
|
|
|
|
mpctx->current_track[t] = NULL;
|
|
|
|
assert(!mpctx->sh_video && !mpctx->sh_audio && !mpctx->sh_sub);
|
2012-08-19 16:07:06 +00:00
|
|
|
mpctx->master_demuxer = NULL;
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int i = 0; i < mpctx->num_sources; i++) {
|
|
|
|
uninit_subs(mpctx->sources[i]);
|
|
|
|
struct demuxer *demuxer = mpctx->sources[i];
|
|
|
|
if (demuxer->stream != mpctx->stream)
|
2012-08-19 15:58:58 +00:00
|
|
|
free_stream(demuxer->stream);
|
2012-08-19 16:01:30 +00:00
|
|
|
free_demuxer(demuxer);
|
2009-03-29 19:45:06 +00:00
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
talloc_free(mpctx->sources);
|
|
|
|
mpctx->sources = NULL;
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->demuxer = NULL;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->num_sources = 0;
|
|
|
|
talloc_free(mpctx->timeline);
|
|
|
|
mpctx->timeline = NULL;
|
|
|
|
mpctx->num_timeline_parts = 0;
|
|
|
|
talloc_free(mpctx->chapters);
|
|
|
|
mpctx->chapters = NULL;
|
|
|
|
mpctx->num_chapters = 0;
|
|
|
|
mpctx->video_offset = 0;
|
2009-03-29 19:45:06 +00:00
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
|
|
|
|
// kill the cache process:
|
|
|
|
if (mask & INITIALIZED_STREAM) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_STREAM;
|
|
|
|
if (mpctx->stream)
|
|
|
|
free_stream(mpctx->stream);
|
|
|
|
mpctx->stream = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_VO) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_VO;
|
|
|
|
vo_destroy(mpctx->video_out);
|
|
|
|
mpctx->video_out = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must be after libvo uninit, as few vo drivers (svgalib) have tty code.
|
|
|
|
if (mask & INITIALIZED_GETCH2) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_GETCH2;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG2, "\n[[[uninit getch2]]]\n");
|
|
|
|
// restore terminal:
|
|
|
|
getch2_disable();
|
|
|
|
}
|
|
|
|
|
2013-05-27 23:49:07 +00:00
|
|
|
if (mask & INITIALIZED_AO) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_AO;
|
2012-10-02 20:22:19 +00:00
|
|
|
if (mpctx->ao)
|
2011-08-07 00:58:12 +00:00
|
|
|
ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE);
|
|
|
|
mpctx->ao = NULL;
|
|
|
|
}
|
2013-09-07 18:03:13 +00:00
|
|
|
|
|
|
|
if (mask & INITIALIZED_PLAYBACK)
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_PLAYBACK;
|
2001-09-05 10:49:04 +00:00
|
|
|
}
|
|
|
|
|
2012-11-10 15:20:00 +00:00
|
|
|
static MP_NORETURN void exit_player(struct MPContext *mpctx,
|
2013-08-02 08:32:38 +00:00
|
|
|
enum exit_reason how)
|
2008-12-03 23:55:52 +00:00
|
|
|
{
|
2013-08-02 08:32:38 +00:00
|
|
|
int rc;
|
2011-08-07 00:58:12 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_ALL);
|
2012-09-14 15:51:26 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
encode_lavc_finish(mpctx->encode_lavc_ctx);
|
|
|
|
encode_lavc_free(mpctx->encode_lavc_ctx);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mpctx->encode_lavc_ctx = NULL;
|
|
|
|
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
#ifdef CONFIG_LUA
|
|
|
|
mp_lua_uninit(mpctx);
|
|
|
|
#endif
|
|
|
|
|
2008-10-13 16:23:55 +00:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
2011-08-07 00:58:12 +00:00
|
|
|
timeEndPeriod(1);
|
2008-05-10 15:03:04 +00:00
|
|
|
#endif
|
2001-09-05 10:49:04 +00:00
|
|
|
|
2013-09-01 21:00:52 +00:00
|
|
|
#ifdef CONFIG_COCOA
|
|
|
|
cocoa_set_input_context(NULL);
|
|
|
|
#endif
|
|
|
|
|
2013-09-30 20:27:37 +00:00
|
|
|
command_uninit(mpctx);
|
|
|
|
|
2013-07-27 19:26:00 +00:00
|
|
|
mp_input_uninit(mpctx->input);
|
2008-04-30 06:14:06 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
osd_free(mpctx->osd);
|
2004-11-15 15:29:39 +00:00
|
|
|
|
2008-07-30 12:01:30 +00:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 00:58:12 +00:00
|
|
|
ass_library_done(mpctx->ass_library);
|
|
|
|
mpctx->ass_library = NULL;
|
2006-10-28 15:07:18 +00:00
|
|
|
#endif
|
|
|
|
|
2013-02-24 15:06:27 +00:00
|
|
|
if (how != EXIT_NONE) {
|
|
|
|
const char *reason;
|
|
|
|
switch (how) {
|
2013-08-02 08:32:38 +00:00
|
|
|
case EXIT_SOMENOTPLAYED:
|
|
|
|
case EXIT_PLAYED:
|
|
|
|
reason = "End of file";
|
|
|
|
break;
|
|
|
|
case EXIT_NOTPLAYED:
|
|
|
|
reason = "No files played";
|
|
|
|
break;
|
|
|
|
case EXIT_ERROR:
|
|
|
|
reason = "Fatal error";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
reason = "Quit";
|
2013-02-24 15:06:27 +00:00
|
|
|
}
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "\nExiting... (%s)\n", reason);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
|
2013-08-02 08:32:38 +00:00
|
|
|
if (mpctx->has_quit_custom_rc) {
|
|
|
|
rc = mpctx->quit_custom_rc;
|
|
|
|
} else {
|
|
|
|
switch (how) {
|
|
|
|
case EXIT_ERROR:
|
|
|
|
rc = 1; break;
|
|
|
|
case EXIT_NOTPLAYED:
|
|
|
|
rc = 2; break;
|
|
|
|
case EXIT_SOMENOTPLAYED:
|
|
|
|
rc = 3; break;
|
|
|
|
default:
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// must be last since e.g. mp_msg uses option values
|
|
|
|
// that will be freed by this.
|
2013-07-31 19:40:30 +00:00
|
|
|
|
|
|
|
mp_msg_uninit(mpctx->global);
|
2011-10-06 18:46:01 +00:00
|
|
|
talloc_free(mpctx);
|
|
|
|
|
2013-02-23 17:28:22 +00:00
|
|
|
#ifdef CONFIG_COCOA
|
|
|
|
terminate_cocoa_application();
|
|
|
|
// never reach here:
|
|
|
|
// terminate calls exit itself, just silence compiler warning
|
|
|
|
exit(0);
|
|
|
|
#else
|
2011-08-07 00:58:12 +00:00
|
|
|
exit(rc);
|
2013-02-23 17:28:22 +00:00
|
|
|
#endif
|
2002-12-29 21:06:20 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 20:47:30 +00:00
|
|
|
static void mk_config_dir(char *subdir)
|
|
|
|
{
|
|
|
|
void *tmp = talloc_new(NULL);
|
|
|
|
char *confdir = talloc_steal(tmp, mp_find_user_config_file(""));
|
2013-09-18 17:56:15 +00:00
|
|
|
if (confdir) {
|
|
|
|
if (subdir)
|
|
|
|
confdir = mp_path_join(tmp, bstr0(confdir), bstr0(subdir));
|
|
|
|
mkdir(confdir, 0777);
|
|
|
|
}
|
2013-04-01 20:47:30 +00:00
|
|
|
talloc_free(tmp);
|
|
|
|
}
|
|
|
|
|
2013-08-02 15:59:43 +00:00
|
|
|
static int cfg_include(struct m_config *conf, char *filename, int flags)
|
2008-04-26 07:44:59 +00:00
|
|
|
{
|
2013-08-02 15:59:43 +00:00
|
|
|
return m_config_parse_config_file(conf, filename, flags);
|
2008-04-26 07:44:59 +00:00
|
|
|
}
|
|
|
|
|
2011-03-10 22:54:16 +00:00
|
|
|
#define DEF_CONFIG "# Write your default config options here!\n\n\n"
|
|
|
|
|
2012-08-04 01:46:11 +00:00
|
|
|
static bool parse_cfgfiles(struct MPContext *mpctx, m_config_t *conf)
|
2001-05-08 12:17:03 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-08-07 00:58:12 +00:00
|
|
|
char *conffile;
|
|
|
|
int conffile_fd;
|
2013-02-08 22:52:06 +00:00
|
|
|
if (!opts->load_config)
|
|
|
|
return true;
|
2013-08-02 15:59:43 +00:00
|
|
|
if (!m_config_parse_config_file(conf, MPLAYER_CONFDIR "/mpv.conf", 0) < 0)
|
2012-08-04 01:46:11 +00:00
|
|
|
return false;
|
2013-04-01 20:47:30 +00:00
|
|
|
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");
|
2011-08-07 00:58:12 +00:00
|
|
|
else {
|
2013-04-01 20:47:30 +00:00
|
|
|
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);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2013-08-02 15:59:43 +00:00
|
|
|
if (m_config_parse_config_file(conf, conffile, 0) < 0)
|
2013-04-01 20:47:30 +00:00
|
|
|
return false;
|
|
|
|
talloc_free(conffile);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2012-08-04 01:46:11 +00:00
|
|
|
return true;
|
2001-02-24 20:28:24 +00:00
|
|
|
}
|
|
|
|
|
2008-01-10 18:45:08 +00:00
|
|
|
#define PROFILE_CFG_PROTOCOL "protocol."
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
static void load_per_protocol_config(m_config_t *conf, const char * const file)
|
2008-01-10 18:45:08 +00:00
|
|
|
{
|
|
|
|
char *str;
|
2011-08-07 00:58:12 +00:00
|
|
|
char protocol[strlen(PROFILE_CFG_PROTOCOL) + strlen(file) + 1];
|
2008-01-10 18:45:08 +00:00
|
|
|
m_profile_t *p;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2008-01-10 18:45:08 +00:00
|
|
|
/* does filename actually uses a protocol ? */
|
2013-09-04 12:04:35 +00:00
|
|
|
if (!mp_is_url(bstr0(file)))
|
|
|
|
return;
|
2011-08-07 00:58:12 +00:00
|
|
|
str = strstr(file, "://");
|
2008-01-10 18:45:08 +00:00
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
sprintf(protocol, "%s%s", PROFILE_CFG_PROTOCOL, file);
|
|
|
|
protocol[strlen(PROFILE_CFG_PROTOCOL) + strlen(file) - strlen(str)] = '\0';
|
2013-07-27 19:26:00 +00:00
|
|
|
p = m_config_get_profile0(conf, protocol);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading protocol-related profile '%s'\n", protocol);
|
2013-08-02 15:59:43 +00:00
|
|
|
m_config_set_profile(conf, p, M_SETOPT_BACKUP);
|
2008-01-10 18:45:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PROFILE_CFG_EXTENSION "extension."
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
static void load_per_extension_config(m_config_t *conf, const char * const file)
|
2008-01-10 18:45:08 +00:00
|
|
|
{
|
|
|
|
char *str;
|
2011-08-07 00:58:12 +00:00
|
|
|
char extension[strlen(PROFILE_CFG_EXTENSION) + 8];
|
2008-01-10 18:45:08 +00:00
|
|
|
m_profile_t *p;
|
|
|
|
|
|
|
|
/* does filename actually have an extension ? */
|
2011-08-07 00:58:12 +00:00
|
|
|
str = strrchr(file, '.');
|
2008-01-10 18:45:08 +00:00
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
sprintf(extension, PROFILE_CFG_EXTENSION);
|
|
|
|
strncat(extension, ++str, 7);
|
2013-07-27 19:26:00 +00:00
|
|
|
p = m_config_get_profile0(conf, extension);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading extension-related profile '%s'\n", extension);
|
2013-08-02 15:59:43 +00:00
|
|
|
m_config_set_profile(conf, p, M_SETOPT_BACKUP);
|
2008-01-10 18:45:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-10 23:32:50 +00:00
|
|
|
#define PROFILE_CFG_VO "vo."
|
|
|
|
#define PROFILE_CFG_AO "ao."
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
static void load_per_output_config(m_config_t *conf, char *cfg, char *out)
|
2008-01-10 23:32:50 +00:00
|
|
|
{
|
2011-08-07 00:58:12 +00:00
|
|
|
char profile[strlen(cfg) + strlen(out) + 1];
|
2008-01-10 23:32:50 +00:00
|
|
|
m_profile_t *p;
|
|
|
|
|
2013-07-21 19:17:48 +00:00
|
|
|
if (!out && !out[0])
|
|
|
|
return;
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
sprintf(profile, "%s%s", cfg, out);
|
2013-07-27 19:26:00 +00:00
|
|
|
p = m_config_get_profile0(conf, profile);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading extension-related profile '%s'\n", profile);
|
2013-08-02 15:59:43 +00:00
|
|
|
m_config_set_profile(conf, p, M_SETOPT_BACKUP);
|
2008-01-10 23:32:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-31 09:48:25 +00:00
|
|
|
/**
|
2013-08-02 15:59:43 +00:00
|
|
|
* Tries to load a config file (in file local mode)
|
2009-08-31 09:48:25 +00:00
|
|
|
* @return 0 if file was not found, 1 otherwise
|
|
|
|
*/
|
2013-08-17 19:56:39 +00:00
|
|
|
static int try_load_config(m_config_t *conf, const char *file, bool local)
|
2009-08-31 09:48:25 +00:00
|
|
|
{
|
2012-02-03 07:05:11 +00:00
|
|
|
if (!mp_path_exists(file))
|
2009-08-31 09:48:25 +00:00
|
|
|
return 0;
|
2009-09-04 16:49:35 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Loading config '%s'\n", file);
|
2013-08-17 19:56:39 +00:00
|
|
|
m_config_parse_config_file(conf, file, local ? M_SETOPT_BACKUP : 0);
|
2009-08-31 09:48:25 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
static void load_per_file_config(m_config_t *conf, const char * const file,
|
|
|
|
bool search_file_dir)
|
2002-12-28 14:29:41 +00:00
|
|
|
{
|
|
|
|
char *confpath;
|
2012-02-03 07:05:11 +00:00
|
|
|
char cfg[MP_PATH_MAX];
|
2010-12-04 10:24:42 +00:00
|
|
|
const char *name;
|
2002-12-28 14:29:41 +00:00
|
|
|
|
2012-02-03 07:05:11 +00:00
|
|
|
if (strlen(file) > MP_PATH_MAX - 14) {
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Filename is too long, "
|
|
|
|
"can not load file or directory specific config files\n");
|
2009-09-02 19:49:10 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
sprintf(cfg, "%s.conf", file);
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2010-12-04 10:24:42 +00:00
|
|
|
name = mp_basename(cfg);
|
2013-03-08 01:08:02 +00:00
|
|
|
if (search_file_dir) {
|
2012-02-03 07:05:11 +00:00
|
|
|
char dircfg[MP_PATH_MAX];
|
2009-09-02 19:43:05 +00:00
|
|
|
strcpy(dircfg, cfg);
|
2012-10-11 00:04:08 +00:00
|
|
|
strcpy(dircfg + (name - cfg), "mpv.conf");
|
2013-08-17 19:56:39 +00:00
|
|
|
try_load_config(conf, dircfg, true);
|
2009-09-02 19:43:05 +00:00
|
|
|
|
2013-08-17 19:56:39 +00:00
|
|
|
if (try_load_config(conf, cfg, true))
|
2009-09-02 19:43:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-09 14:05:21 +00:00
|
|
|
if ((confpath = mp_find_user_config_file(name)) != NULL) {
|
2013-08-17 19:56:39 +00:00
|
|
|
try_load_config(conf, confpath, true);
|
2002-12-28 14:29:41 +00:00
|
|
|
|
2012-12-09 14:05:21 +00:00
|
|
|
talloc_free(confpath);
|
2002-12-28 14:29:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
#define MP_WATCH_LATER_CONF "watch_later"
|
|
|
|
|
2013-09-22 01:04:57 +00:00
|
|
|
static char *get_playback_resume_config_filename(const char *fname,
|
|
|
|
struct MPOpts *opts)
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
{
|
|
|
|
char *res = NULL;
|
|
|
|
void *tmp = talloc_new(NULL);
|
|
|
|
const char *realpath = fname;
|
2013-09-22 01:04:57 +00:00
|
|
|
bstr bfname = bstr0(fname);
|
|
|
|
if (!mp_is_url(bfname)) {
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
char *cwd = mp_getcwd(tmp);
|
|
|
|
if (!cwd)
|
|
|
|
goto exit;
|
|
|
|
realpath = mp_path_join(tmp, bstr0(cwd), bstr0(fname));
|
|
|
|
}
|
2013-09-22 01:04:57 +00:00
|
|
|
#ifdef CONFIG_DVDREAD
|
|
|
|
if (bstr_startswith0(bfname, "dvd://"))
|
|
|
|
realpath = talloc_asprintf(tmp, "%s - %s", realpath, dvd_device);
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_LIBBLURAY
|
|
|
|
if (bstr_startswith0(bfname, "br://") || bstr_startswith0(bfname, "bd://") ||
|
|
|
|
bstr_startswith0(bfname, "bluray://"))
|
|
|
|
realpath = talloc_asprintf(tmp, "%s - %s", realpath, bluray_device);
|
|
|
|
#endif
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
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",
|
2013-09-19 12:32:47 +00:00
|
|
|
"volume-restore-data",
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
"audio-delay",
|
|
|
|
//"balance",
|
|
|
|
"fullscreen",
|
|
|
|
"colormatrix",
|
|
|
|
"colormatrix-input-range",
|
|
|
|
"colormatrix-output-range",
|
|
|
|
"ontop",
|
|
|
|
"border",
|
|
|
|
"gamma",
|
|
|
|
"brightness",
|
|
|
|
"contrast",
|
|
|
|
"saturation",
|
|
|
|
"hue",
|
core: add --deinterlace option, restore it with resume functionality
The --deinterlace option does on playback start what the "deinterlace"
property normally does at runtime. You could do this before by using the
--vf option or by messing with the vo_vdpau default options, but this
new option is supposed to be a "foolproof" way.
The main motivation for adding this is so that the deinterlace property
can be restored when using the video resume functionality
(quit_watch_later command).
Implementation-wise, this is a bit messy. The video chain is rebuilt in
mpcodecs_reconfig_vo(), where we don't have access to MPContext, so the
usual mechanism for enabling deinterlacing can't be used. Further,
mpcodecs_reconfig_vo() is called by the video decoder, which doesn't
have access to MPContext either. Moving this call to mplayer.c isn't
currently possible either (see below). So we just do this before frames
are filtered, which potentially means setting the deinterlacing every
frame. Fortunately, setting deinterlacing is stable and idempotent, so
this is hopefully not a problem. We also add a counter that is
incremented on each reconfig to reduce the amount of additional work per
frame to nearly zero.
The reason we can't move mpcodecs_reconfig_vo() to mplayer.c is because
of hardware decoding: we need to check whether the video chain works
before we decide that we can use hardware decoding. Changing it so that
this can be decided in advance without building a filter chain sounds
like a good idea and should be done, but we aren't there yet.
2013-09-13 16:06:08 +00:00
|
|
|
"deinterlace",
|
2013-09-13 17:07:30 +00:00
|
|
|
"vf",
|
|
|
|
"af",
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
"panscan",
|
|
|
|
"aid",
|
|
|
|
"vid",
|
|
|
|
"sid",
|
|
|
|
"sub-delay",
|
|
|
|
"sub-pos",
|
2013-06-12 21:55:24 +00:00
|
|
|
"sub-visibility",
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
"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);
|
2013-09-04 15:08:52 +00:00
|
|
|
if (pos == MP_NOPTS_VALUE)
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
goto exit;
|
|
|
|
|
|
|
|
mk_config_dir(MP_WATCH_LATER_CONF);
|
|
|
|
|
2013-09-22 01:04:57 +00:00
|
|
|
char *conffile = get_playback_resume_config_filename(mpctx->filename,
|
|
|
|
mpctx->opts);
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
talloc_steal(tmp, conffile);
|
|
|
|
if (!conffile)
|
|
|
|
goto exit;
|
|
|
|
|
2013-09-13 19:36:19 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "Saving state.\n");
|
|
|
|
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
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];
|
2013-07-22 22:45:23 +00:00
|
|
|
char *val = NULL;
|
|
|
|
int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &val, mpctx);
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
if (r == M_PROPERTY_OK)
|
2013-07-22 22:45:23 +00:00
|
|
|
fprintf(file, "%s=%s\n", pname, val);
|
|
|
|
talloc_free(val);
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
talloc_free(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void load_playback_resume(m_config_t *conf, const char *file)
|
|
|
|
{
|
2013-09-22 01:04:57 +00:00
|
|
|
char *fname = get_playback_resume_config_filename(file, conf->optstruct);
|
2013-09-05 16:00:30 +00:00
|
|
|
if (fname && mp_path_exists(fname)) {
|
2013-08-17 19:56:39 +00:00
|
|
|
// Never apply the saved start position to following files
|
|
|
|
m_config_backup_opt(conf, "start");
|
2013-09-04 16:16:47 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "Resuming playback. This behavior can "
|
|
|
|
"be disabled with --no-resume-playback.\n");
|
2013-08-17 19:56:39 +00:00
|
|
|
try_load_config(conf, fname, false);
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
unlink(fname);
|
|
|
|
}
|
|
|
|
talloc_free(fname);
|
|
|
|
}
|
|
|
|
|
2013-09-04 14:08:36 +00:00
|
|
|
// Returns the first file that has a resume config.
|
|
|
|
// Compared to hashing the playlist file or contents and managing separate
|
|
|
|
// resume file for them, this is simpler, and also has the nice property
|
|
|
|
// that appending to a playlist doesn't interfere with resuming (especially
|
|
|
|
// if the playlist comes from the command line).
|
2013-09-22 01:04:57 +00:00
|
|
|
struct playlist_entry *mp_resume_playlist(struct playlist *playlist,
|
|
|
|
struct MPOpts *opts)
|
2013-09-04 14:08:36 +00:00
|
|
|
{
|
2013-10-06 19:54:00 +00:00
|
|
|
if (!opts->position_resume)
|
|
|
|
return NULL;
|
2013-09-04 14:08:36 +00:00
|
|
|
for (struct playlist_entry *e = playlist->first; e; e = e->next) {
|
2013-09-22 01:04:57 +00:00
|
|
|
char *conf = get_playback_resume_config_filename(e->filename, opts);
|
2013-09-04 14:08:36 +00:00
|
|
|
bool exists = conf && mp_path_exists(conf);
|
|
|
|
talloc_free(conf);
|
|
|
|
if (exists)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
static void load_per_file_options(m_config_t *conf,
|
|
|
|
struct playlist_param *params,
|
|
|
|
int params_count)
|
|
|
|
{
|
2013-08-02 15:59:43 +00:00
|
|
|
for (int n = 0; n < params_count; n++) {
|
|
|
|
m_config_set_option_ext(conf, params[n].name, params[n].value,
|
|
|
|
M_SETOPT_BACKUP);
|
|
|
|
}
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
}
|
|
|
|
|
2012-11-09 00:06:43 +00:00
|
|
|
/* When demux performs a blocking operation (network connection or
|
2006-04-27 13:22:23 +00:00
|
|
|
* cache filling) if the operation fails we use this function to check
|
|
|
|
* if it was interrupted by the user.
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
* The function returns whether it was interrupted. */
|
2012-11-09 00:06:43 +00:00
|
|
|
static bool demux_was_interrupted(struct MPContext *mpctx)
|
2008-04-21 03:07:22 +00:00
|
|
|
{
|
2012-09-18 19:13:14 +00:00
|
|
|
for (;;) {
|
|
|
|
if (mpctx->stop_play != KEEP_PLAYING
|
|
|
|
&& mpctx->stop_play != AT_END_OF_FILE)
|
|
|
|
return true;
|
|
|
|
mp_cmd_t *cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
|
|
|
if (!cmd)
|
|
|
|
break;
|
|
|
|
if (mp_input_is_abort_cmd(cmd->id))
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
run_command(mpctx, cmd);
|
2012-09-18 19:13:14 +00:00
|
|
|
mp_cmd_free(cmd);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2012-09-18 19:13:14 +00:00
|
|
|
return false;
|
2003-01-12 19:41:38 +00:00
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
|
|
|
|
{
|
2013-08-21 16:24:56 +00:00
|
|
|
int new_id = 0;
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int i = 0; i < mpctx->num_tracks; i++) {
|
|
|
|
struct track *track = mpctx->tracks[i];
|
|
|
|
if (track->type == t)
|
|
|
|
new_id = FFMAX(new_id, track->user_tid);
|
|
|
|
}
|
|
|
|
return new_id + 1;
|
|
|
|
}
|
|
|
|
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
// Map stream number (as used by libdvdread) to MPEG IDs (as used by demuxer).
|
|
|
|
static int map_id_from_demuxer(struct demuxer *d, enum stream_type type, int id)
|
|
|
|
{
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 16:49:09 +00:00
|
|
|
if (d->stream->uncached_type == STREAMTYPE_DVD && type == STREAM_SUB)
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
id = id & 0x1F;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static struct track *add_stream_track(struct MPContext *mpctx,
|
|
|
|
struct sh_stream *stream,
|
|
|
|
bool under_timeline)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < mpctx->num_tracks; i++) {
|
|
|
|
struct track *track = mpctx->tracks[i];
|
|
|
|
if (track->stream == stream)
|
|
|
|
return track;
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
// DVD subtitle track that was added later
|
|
|
|
if (stream->type == STREAM_SUB && track->type == STREAM_SUB &&
|
|
|
|
map_id_from_demuxer(stream->demuxer, stream->type,
|
|
|
|
stream->demuxer_id) == track->demuxer_id
|
|
|
|
&& !track->stream)
|
|
|
|
{
|
|
|
|
track->stream = stream;
|
|
|
|
track->demuxer_id = stream->demuxer_id;
|
|
|
|
// Initialize lazily selected track
|
2013-07-11 17:22:24 +00:00
|
|
|
bool selected = track == mpctx->current_track[STREAM_SUB];
|
|
|
|
demuxer_select_track(track->demuxer, stream, selected);
|
|
|
|
if (selected)
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
reinit_subs(mpctx);
|
|
|
|
return track;
|
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
struct track *track = talloc_ptrtype(NULL, track);
|
|
|
|
*track = (struct track) {
|
|
|
|
.type = stream->type,
|
|
|
|
.user_tid = find_new_tid(mpctx, stream->type),
|
|
|
|
.demuxer_id = stream->demuxer_id,
|
|
|
|
.title = stream->title,
|
|
|
|
.default_track = stream->default_track,
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
.attached_picture = stream->attached_picture != NULL,
|
2013-02-09 14:15:28 +00:00
|
|
|
.lang = stream->lang,
|
2012-08-19 16:01:30 +00:00
|
|
|
.under_timeline = under_timeline,
|
|
|
|
.demuxer = stream->demuxer,
|
|
|
|
.stream = stream,
|
|
|
|
};
|
|
|
|
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
|
|
|
|
|
2013-06-22 00:09:52 +00:00
|
|
|
if (stream->type == STREAM_SUB)
|
|
|
|
track->preloaded = !!stream->sub->track;
|
|
|
|
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
// Needed for DVD and Blu-ray.
|
|
|
|
if (!track->lang) {
|
|
|
|
struct stream_lang_req req = {
|
|
|
|
.type = track->type,
|
|
|
|
.id = map_id_from_demuxer(track->demuxer, track->type,
|
|
|
|
track->demuxer_id)
|
|
|
|
};
|
|
|
|
stream_control(track->demuxer->stream, STREAM_CTRL_GET_LANG, &req);
|
2012-11-25 22:37:39 +00:00
|
|
|
if (req.name[0])
|
|
|
|
track->lang = talloc_strdup(track, req.name);
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2013-07-11 17:22:24 +00:00
|
|
|
demuxer_select_track(track->demuxer, stream, false);
|
|
|
|
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
return track;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer)
|
|
|
|
{
|
|
|
|
for (int n = 0; n < demuxer->num_streams; n++)
|
|
|
|
add_stream_track(mpctx, demuxer->streams[n], !!mpctx->timeline);
|
|
|
|
}
|
|
|
|
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
static void add_dvd_tracks(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_DVDREAD
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
struct stream *stream = demuxer->stream;
|
2013-06-04 23:59:04 +00:00
|
|
|
struct stream_dvd_info_req info;
|
|
|
|
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
|
|
|
|
for (int n = 0; n < info.num_subs; n++) {
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
struct track *track = talloc_ptrtype(NULL, track);
|
|
|
|
*track = (struct track) {
|
|
|
|
.type = STREAM_SUB,
|
|
|
|
.user_tid = find_new_tid(mpctx, STREAM_SUB),
|
|
|
|
.demuxer_id = n,
|
|
|
|
.demuxer = mpctx->demuxer,
|
|
|
|
};
|
|
|
|
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
|
|
|
|
|
|
|
|
struct stream_lang_req req = {.type = STREAM_SUB, .id = n};
|
|
|
|
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
|
2012-11-18 23:22:43 +00:00
|
|
|
track->lang = talloc_strdup(track, req.name);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
|
|
|
|
mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-11 17:22:24 +00:00
|
|
|
demuxer_enable_autoselect(demuxer);
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-02-17 19:24:59 +00:00
|
|
|
int mp_get_cache_percent(struct MPContext *mpctx)
|
2012-12-01 22:28:58 +00:00
|
|
|
{
|
|
|
|
if (mpctx->stream) {
|
|
|
|
int64_t size = -1;
|
|
|
|
int64_t fill = -1;
|
|
|
|
stream_control(mpctx->stream, STREAM_CTRL_GET_CACHE_SIZE, &size);
|
|
|
|
stream_control(mpctx->stream, STREAM_CTRL_GET_CACHE_FILL, &fill);
|
|
|
|
if (size > 0 && fill >= 0)
|
|
|
|
return fill / (size / 100);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-17 19:24:59 +00:00
|
|
|
static bool mp_get_cache_idle(struct MPContext *mpctx)
|
2012-12-01 23:22:54 +00:00
|
|
|
{
|
|
|
|
int idle = 0;
|
|
|
|
if (mpctx->stream)
|
|
|
|
stream_control(mpctx->stream, STREAM_CTRL_GET_CACHE_IDLE, &idle);
|
|
|
|
return idle;
|
|
|
|
}
|
|
|
|
|
2013-06-16 03:42:26 +00:00
|
|
|
static void vo_update_window_title(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->video_out)
|
|
|
|
return;
|
2013-07-27 19:24:54 +00:00
|
|
|
char *title = mp_property_expand_string(mpctx, mpctx->opts->wintitle);
|
2013-06-16 03:42:26 +00:00
|
|
|
if (!mpctx->video_out->window_title ||
|
|
|
|
strcmp(title, mpctx->video_out->window_title))
|
|
|
|
{
|
|
|
|
talloc_free(mpctx->video_out->window_title);
|
|
|
|
mpctx->video_out->window_title = talloc_steal(mpctx, title);
|
|
|
|
vo_control(mpctx->video_out, VOCTRL_UPDATE_WINDOW_TITLE, title);
|
|
|
|
} else {
|
|
|
|
talloc_free(title);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
#define saddf(var, ...) (*(var) = talloc_asprintf_append((*var), __VA_ARGS__))
|
2004-11-02 23:06:12 +00:00
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
// append time in the hh:mm:ss format (plus fractions if wanted)
|
|
|
|
static void sadd_hhmmssff(char **buf, double time, bool fractions)
|
2011-08-07 00:58:12 +00:00
|
|
|
{
|
2012-09-01 19:59:13 +00:00
|
|
|
char *s = mp_format_time(time, fractions);
|
2013-02-16 21:04:43 +00:00
|
|
|
*buf = talloc_strdup_append(*buf, s);
|
2012-09-01 19:59:13 +00:00
|
|
|
talloc_free(s);
|
2012-07-31 22:39:49 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
static void sadd_percentage(char **buf, int percent) {
|
2012-07-31 22:39:49 +00:00
|
|
|
if (percent >= 0)
|
2013-02-16 21:04:43 +00:00
|
|
|
*buf = talloc_asprintf_append(*buf, " (%d%%)", percent);
|
2005-10-04 13:59:25 +00:00
|
|
|
}
|
|
|
|
|
2012-10-02 01:12:09 +00:00
|
|
|
static int get_term_width(void)
|
|
|
|
{
|
|
|
|
get_screen_size();
|
|
|
|
int width = screen_width > 0 ? screen_width : 80;
|
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
|
|
/* Windows command line is broken (MinGW's rxvt works, but we
|
|
|
|
* should not depend on that). */
|
|
|
|
width--;
|
|
|
|
#endif
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_status_line(struct MPContext *mpctx, const char *line)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-05-27 20:19:47 +00:00
|
|
|
if (opts->slave_mode) {
|
2013-05-21 20:07:24 +00:00
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s\n", line);
|
|
|
|
} else if (erase_to_end_of_line) {
|
2012-10-02 01:12:09 +00:00
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS,
|
|
|
|
"%s%s\r", line, erase_to_end_of_line);
|
|
|
|
} else {
|
|
|
|
int pos = strlen(line);
|
|
|
|
int width = get_term_width() - pos;
|
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s%*s\r", line, width, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-15 19:51:34 +00:00
|
|
|
static void print_status(struct MPContext *mpctx)
|
2004-11-02 23:06:12 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-08-07 00:58:12 +00:00
|
|
|
sh_video_t * const sh_video = mpctx->sh_video;
|
|
|
|
|
2013-06-16 03:42:26 +00:00
|
|
|
vo_update_window_title(mpctx);
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
if (opts->quiet)
|
|
|
|
return;
|
|
|
|
|
2012-10-02 01:12:09 +00:00
|
|
|
if (opts->status_msg) {
|
|
|
|
char *r = mp_property_expand_string(mpctx, opts->status_msg);
|
|
|
|
write_status_line(mpctx, r);
|
|
|
|
talloc_free(r);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
char *line = NULL;
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-07-30 19:07:54 +00:00
|
|
|
// Playback status
|
2013-04-25 18:38:22 +00:00
|
|
|
if (mpctx->paused_for_cache && !opts->pause) {
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, "(Buffering) ");
|
2012-12-01 23:22:54 +00:00
|
|
|
} else if (mpctx->paused) {
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, "(Paused) ");
|
2012-12-01 23:22:54 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 16:47:01 +00:00
|
|
|
if (mpctx->sh_audio)
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, "A");
|
2012-07-30 16:47:01 +00:00
|
|
|
if (mpctx->sh_video)
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, "V");
|
|
|
|
saddf(&line, ": ");
|
2012-07-30 16:47:01 +00:00
|
|
|
|
2012-07-30 19:07:54 +00:00
|
|
|
// Playback position
|
2012-10-20 22:22:04 +00:00
|
|
|
double cur = get_current_time(mpctx);
|
2013-07-27 19:24:54 +00:00
|
|
|
sadd_hhmmssff(&line, cur, mpctx->opts->osd_fractions);
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-07-29 23:02:08 +00:00
|
|
|
double len = get_time_length(mpctx);
|
|
|
|
if (len >= 0) {
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, " / ");
|
2013-07-27 19:24:54 +00:00
|
|
|
sadd_hhmmssff(&line, len, mpctx->opts->osd_fractions);
|
2012-03-15 22:57:52 +00:00
|
|
|
}
|
2004-11-02 23:06:12 +00:00
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
sadd_percentage(&line, get_percent_pos(mpctx));
|
2012-07-31 22:39:49 +00:00
|
|
|
|
2012-08-15 21:12:17 +00:00
|
|
|
// other
|
|
|
|
if (opts->playback_speed != 1)
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, " x%4.2f", opts->playback_speed);
|
2012-08-15 21:12:17 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// A-V sync
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (mpctx->sh_audio && sh_video && mpctx->sync_audio_to_video) {
|
2012-03-15 22:57:52 +00:00
|
|
|
if (mpctx->last_av_difference != MP_NOPTS_VALUE)
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, " A-V:%7.3f", mpctx->last_av_difference);
|
2012-03-15 22:57:52 +00:00
|
|
|
else
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, " A-V: ???");
|
2012-08-15 21:12:17 +00:00
|
|
|
if (fabs(mpctx->total_avsync_change) > 0.05)
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(&line, " ct:%7.3f", mpctx->total_avsync_change);
|
2012-03-15 22:57:52 +00:00
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-09-14 15:51:26 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
2013-06-20 10:15:36 +00:00
|
|
|
double position = get_current_pos_ratio(mpctx, true);
|
2012-09-14 15:51:26 +00:00
|
|
|
char lavcbuf[80];
|
|
|
|
if (encode_lavc_getstatus(mpctx->encode_lavc_ctx, lavcbuf, sizeof(lavcbuf),
|
2013-06-20 09:16:51 +00:00
|
|
|
position) >= 0)
|
2012-11-15 17:49:17 +00:00
|
|
|
{
|
2012-09-14 15:51:26 +00:00
|
|
|
// encoding stats
|
2013-04-27 11:45:20 +00:00
|
|
|
saddf(&line, " %s", lavcbuf);
|
2012-09-14 15:51:26 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
// VO stats
|
2013-03-08 01:08:02 +00:00
|
|
|
if (sh_video && mpctx->drop_frame_cnt)
|
2013-08-21 16:42:18 +00:00
|
|
|
saddf(&line, " Late: %d", mpctx->drop_frame_cnt);
|
2012-09-14 15:51:26 +00:00
|
|
|
}
|
2004-11-02 23:06:12 +00:00
|
|
|
|
2013-02-17 19:24:59 +00:00
|
|
|
int cache = mp_get_cache_percent(mpctx);
|
2012-12-01 22:28:58 +00:00
|
|
|
if (cache >= 0)
|
2013-02-17 19:29:07 +00:00
|
|
|
saddf(&line, " Cache: %d%%", cache);
|
2004-11-02 23:06:12 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// end
|
2012-10-02 01:12:09 +00:00
|
|
|
write_status_line(mpctx, line);
|
2013-02-16 21:04:43 +00:00
|
|
|
talloc_free(line);
|
2004-11-02 23:06:12 +00:00
|
|
|
}
|
|
|
|
|
2005-11-16 16:51:48 +00:00
|
|
|
typedef struct mp_osd_msg mp_osd_msg_t;
|
|
|
|
struct mp_osd_msg {
|
2006-04-24 21:33:50 +00:00
|
|
|
/// Previous message on the stack.
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_osd_msg_t *prev;
|
2006-04-24 21:33:50 +00:00
|
|
|
/// Message text.
|
2011-08-08 08:07:17 +00:00
|
|
|
char *msg;
|
2011-08-07 00:58:12 +00:00
|
|
|
int id, level, started;
|
2013-05-25 16:31:06 +00:00
|
|
|
/// Display duration in seconds.
|
|
|
|
double time;
|
2012-08-21 21:12:12 +00:00
|
|
|
// Show full OSD for duration of message instead of msg
|
|
|
|
// (osd_show_progression command)
|
|
|
|
bool show_position;
|
2005-11-16 16:51:48 +00:00
|
|
|
};
|
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
// time is in ms
|
2012-08-21 21:12:12 +00:00
|
|
|
static mp_osd_msg_t *add_osd_msg(struct MPContext *mpctx, int id, int level,
|
|
|
|
int time)
|
2010-01-12 10:16:00 +00:00
|
|
|
{
|
2012-08-31 16:08:47 +00:00
|
|
|
rm_osd_msg(mpctx, id);
|
|
|
|
mp_osd_msg_t *msg = talloc_struct(mpctx, mp_osd_msg_t, {
|
|
|
|
.prev = mpctx->osd_msg_stack,
|
|
|
|
.msg = "",
|
|
|
|
.id = id,
|
|
|
|
.level = level,
|
2013-05-25 16:31:06 +00:00
|
|
|
.time = time / 1000.0,
|
2012-08-31 16:08:47 +00:00
|
|
|
});
|
|
|
|
mpctx->osd_msg_stack = msg;
|
2012-08-21 21:12:12 +00:00
|
|
|
return msg;
|
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2012-08-21 21:12:12 +00:00
|
|
|
static void set_osd_msg_va(struct MPContext *mpctx, int id, int level, int time,
|
|
|
|
const char *fmt, va_list ap)
|
|
|
|
{
|
2012-09-09 00:08:08 +00:00
|
|
|
if (level == OSD_LEVEL_INVISIBLE)
|
|
|
|
return;
|
2012-08-21 21:12:12 +00:00
|
|
|
mp_osd_msg_t *msg = add_osd_msg(mpctx, id, level, time);
|
|
|
|
msg->msg = talloc_vasprintf(msg, fmt, ap);
|
2005-11-16 16:51:48 +00:00
|
|
|
}
|
|
|
|
|
2012-08-04 01:50:23 +00:00
|
|
|
void set_osd_msg(struct MPContext *mpctx, int id, int level, int time,
|
|
|
|
const char *fmt, ...)
|
2010-01-12 10:16:00 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2012-08-04 01:50:23 +00:00
|
|
|
set_osd_msg_va(mpctx, id, level, time, fmt, ap);
|
2010-01-12 10:16:00 +00:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2012-08-04 01:50:23 +00:00
|
|
|
void set_osd_tmsg(struct MPContext *mpctx, int id, int level, int time,
|
|
|
|
const char *fmt, ...)
|
2010-01-12 10:16:00 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
2012-08-04 01:50:23 +00:00
|
|
|
set_osd_msg_va(mpctx, id, level, time, mp_gtext(fmt), ap);
|
2010-01-12 10:16:00 +00:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2005-11-27 23:29:33 +00:00
|
|
|
/**
|
|
|
|
* \brief Remove a message from the OSD stack
|
2009-07-06 23:26:13 +00:00
|
|
|
*
|
2005-11-27 23:29:33 +00:00
|
|
|
* This function can be used to get rid of a message right away.
|
2009-07-06 23:26:13 +00:00
|
|
|
*
|
2005-11-27 23:29:33 +00:00
|
|
|
*/
|
|
|
|
|
2012-08-04 01:50:23 +00:00
|
|
|
void rm_osd_msg(struct MPContext *mpctx, int id)
|
2011-08-07 00:58:12 +00:00
|
|
|
{
|
|
|
|
mp_osd_msg_t *msg, *last = NULL;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2005-11-27 23:29:33 +00:00
|
|
|
// Search for the msg
|
2012-08-04 01:50:23 +00:00
|
|
|
for (msg = mpctx->osd_msg_stack; msg && msg->id != id;
|
2011-08-07 00:58:12 +00:00
|
|
|
last = msg, msg = msg->prev) ;
|
|
|
|
if (!msg)
|
|
|
|
return;
|
2005-11-27 23:29:33 +00:00
|
|
|
|
|
|
|
// Detach it from the stack and free it
|
2011-08-07 00:58:12 +00:00
|
|
|
if (last)
|
2005-11-27 23:29:33 +00:00
|
|
|
last->prev = msg->prev;
|
|
|
|
else
|
2012-08-04 01:50:23 +00:00
|
|
|
mpctx->osd_msg_stack = msg->prev;
|
2011-08-08 08:07:17 +00:00
|
|
|
talloc_free(msg);
|
2005-11-27 23:29:33 +00:00
|
|
|
}
|
|
|
|
|
2005-11-16 16:51:48 +00:00
|
|
|
/**
|
2006-04-27 13:22:23 +00:00
|
|
|
* \brief Get the current message from the OSD stack.
|
2009-07-06 23:26:13 +00:00
|
|
|
*
|
2006-04-27 13:22:23 +00:00
|
|
|
* This function decrements the message timer and destroys the old ones.
|
2005-11-16 16:51:48 +00:00
|
|
|
* The message that should be displayed is returned (if any).
|
2009-07-06 23:26:13 +00:00
|
|
|
*
|
2005-11-16 16:51:48 +00:00
|
|
|
*/
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx)
|
2008-04-21 03:07:22 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_osd_msg_t *msg, *prev, *last = NULL;
|
2013-05-25 16:31:06 +00:00
|
|
|
double now = mp_time_sec();
|
|
|
|
double diff;
|
2005-11-16 16:51:48 +00:00
|
|
|
char hidden_dec_done = 0;
|
2007-08-21 00:24:13 +00:00
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
if (mpctx->osd_visible && now >= mpctx->osd_visible) {
|
|
|
|
mpctx->osd_visible = 0;
|
|
|
|
mpctx->osd->progbar_type = -1; // disable
|
2013-04-28 23:49:20 +00:00
|
|
|
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
|
2013-02-20 22:05:02 +00:00
|
|
|
}
|
2013-05-25 16:31:06 +00:00
|
|
|
if (mpctx->osd_function_visible && now >= mpctx->osd_function_visible) {
|
|
|
|
mpctx->osd_function_visible = 0;
|
|
|
|
mpctx->osd_function = 0;
|
2007-08-21 00:24:13 +00:00
|
|
|
}
|
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
if (!mpctx->osd_last_update)
|
|
|
|
mpctx->osd_last_update = now;
|
|
|
|
diff = now >= mpctx->osd_last_update ? now - mpctx->osd_last_update : 0;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
mpctx->osd_last_update = now;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2006-04-27 13:22:23 +00:00
|
|
|
// Look for the first message in the stack with high enough level.
|
2012-08-04 01:50:23 +00:00
|
|
|
for (msg = mpctx->osd_msg_stack; msg; last = msg, msg = prev) {
|
2005-11-16 16:51:48 +00:00
|
|
|
prev = msg->prev;
|
2009-03-29 23:06:58 +00:00
|
|
|
if (msg->level > opts->osd_level && hidden_dec_done)
|
|
|
|
continue;
|
2006-04-27 13:22:23 +00:00
|
|
|
// The message has a high enough level or it is the first hidden one
|
|
|
|
// in both cases we decrement the timer or kill it.
|
2011-08-07 00:58:12 +00:00
|
|
|
if (!msg->started || msg->time > diff) {
|
|
|
|
if (msg->started)
|
|
|
|
msg->time -= diff;
|
|
|
|
else
|
|
|
|
msg->started = 1;
|
2005-11-16 16:51:48 +00:00
|
|
|
// display it
|
2009-03-29 23:06:58 +00:00
|
|
|
if (msg->level <= opts->osd_level)
|
|
|
|
return msg;
|
2005-11-16 16:51:48 +00:00
|
|
|
hidden_dec_done = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// kill the message
|
2011-08-08 08:07:17 +00:00
|
|
|
talloc_free(msg);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (last) {
|
2005-11-16 16:51:48 +00:00
|
|
|
last->prev = prev;
|
|
|
|
msg = last;
|
|
|
|
} else {
|
2012-08-04 01:50:23 +00:00
|
|
|
mpctx->osd_msg_stack = prev;
|
2005-11-16 16:51:48 +00:00
|
|
|
msg = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nothing found
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-16 19:50:05 +00:00
|
|
|
// type: mp_osd_font_codepoints, ASCII, or OSD_BAR_*
|
|
|
|
// name: fallback for terminal OSD
|
2011-08-07 00:58:12 +00:00
|
|
|
void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
|
|
|
|
double min, double max, double val)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-02-16 20:17:59 +00:00
|
|
|
if (opts->osd_level < 1 || !opts->osd_bar_visible)
|
2009-03-29 23:06:58 +00:00
|
|
|
return;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2013-10-02 18:39:26 +00:00
|
|
|
if (mpctx->video_out && opts->term_osd != 1) {
|
2013-05-25 16:31:06 +00:00
|
|
|
mpctx->osd_visible = mp_time_sec() + opts->osd_duration / 1000.0;
|
2012-09-29 09:03:53 +00:00
|
|
|
mpctx->osd->progbar_type = type;
|
2013-03-30 19:08:56 +00:00
|
|
|
mpctx->osd->progbar_value = (val - min) / (max - min);
|
|
|
|
mpctx->osd->progbar_num_stops = 0;
|
2013-04-28 23:49:20 +00:00
|
|
|
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
|
2005-11-16 16:51:48 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2012-08-04 01:50:23 +00:00
|
|
|
set_osd_msg(mpctx, OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%",
|
2011-08-07 00:58:12 +00:00
|
|
|
name, ROUND(100 * (val - min) / (max - min)));
|
2005-11-16 16:51:48 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 19:50:05 +00:00
|
|
|
// Update a currently displayed bar of the same type, without resetting the
|
|
|
|
// timer.
|
|
|
|
static void update_osd_bar(struct MPContext *mpctx, int type,
|
|
|
|
double min, double max, double val)
|
|
|
|
{
|
|
|
|
if (mpctx->osd->progbar_type == type) {
|
2013-03-30 19:08:56 +00:00
|
|
|
float new_value = (val - min) / (max - min);
|
2013-02-20 22:05:02 +00:00
|
|
|
if (new_value != mpctx->osd->progbar_value) {
|
|
|
|
mpctx->osd->progbar_value = new_value;
|
2013-04-28 23:49:20 +00:00
|
|
|
osd_changed(mpctx->osd, OSDTYPE_PROGBAR);
|
2013-02-20 22:05:02 +00:00
|
|
|
}
|
2013-02-16 19:50:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-30 19:08:56 +00:00
|
|
|
static void set_osd_bar_chapters(struct MPContext *mpctx, int type)
|
|
|
|
{
|
|
|
|
struct osd_state *osd = mpctx->osd;
|
|
|
|
osd->progbar_num_stops = 0;
|
|
|
|
if (osd->progbar_type == type) {
|
|
|
|
double len = get_time_length(mpctx);
|
|
|
|
if (len > 0) {
|
|
|
|
int num = get_chapter_count(mpctx);
|
|
|
|
for (int n = 0; n < num; n++) {
|
|
|
|
double time = chapter_start_time(mpctx, n);
|
|
|
|
if (time >= 0) {
|
|
|
|
float pos = time / len;
|
|
|
|
MP_TARRAY_APPEND(osd, osd->progbar_stops,
|
|
|
|
osd->progbar_num_stops, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:20:45 +00:00
|
|
|
void set_osd_function(struct MPContext *mpctx, int osd_function)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-02-17 21:35:10 +00:00
|
|
|
|
2012-11-20 16:20:45 +00:00
|
|
|
mpctx->osd_function = osd_function;
|
2013-05-25 16:31:06 +00:00
|
|
|
mpctx->osd_function_visible = mp_time_sec() + opts->osd_duration / 1000.0;
|
2012-11-20 16:20:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-23 21:48:48 +00:00
|
|
|
/**
|
|
|
|
* \brief Display text subtitles on the OSD
|
|
|
|
*/
|
2013-04-28 19:12:11 +00:00
|
|
|
static void set_osd_subtitle(struct MPContext *mpctx, const char *text)
|
|
|
|
{
|
|
|
|
if (!text)
|
|
|
|
text = "";
|
|
|
|
if (strcmp(mpctx->osd->sub_text, text) != 0) {
|
|
|
|
osd_set_sub(mpctx->osd, text);
|
2013-10-01 23:32:03 +00:00
|
|
|
if (!mpctx->video_out) {
|
2013-04-28 19:12:11 +00:00
|
|
|
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
|
|
|
|
if (text && text[0])
|
|
|
|
set_osd_msg(mpctx, OSD_MSG_SUB_BASE, 1, INT_MAX, "%s", text);
|
2009-09-23 21:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
2013-06-03 22:17:51 +00:00
|
|
|
if (!text[0])
|
|
|
|
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
|
2009-09-23 21:48:48 +00:00
|
|
|
}
|
2005-11-16 16:51:48 +00:00
|
|
|
|
2012-07-31 22:39:49 +00:00
|
|
|
// sym == mpctx->osd_function
|
2013-02-16 21:04:43 +00:00
|
|
|
static void saddf_osd_function_sym(char **buffer, int sym)
|
2012-07-31 22:39:49 +00:00
|
|
|
{
|
|
|
|
char temp[10];
|
|
|
|
osd_get_function_sym(temp, sizeof(temp), sym);
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf(buffer, "%s ", temp);
|
2012-07-31 22:39:49 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 21:04:43 +00:00
|
|
|
static void sadd_osd_status(char **buffer, struct MPContext *mpctx, bool full)
|
2012-07-31 22:39:49 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
bool fractions = mpctx->opts->osd_fractions;
|
2012-11-20 16:20:45 +00:00
|
|
|
int sym = mpctx->osd_function;
|
2012-12-01 23:22:54 +00:00
|
|
|
if (!sym) {
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->paused_for_cache && !mpctx->opts->pause) {
|
2012-12-01 23:22:54 +00:00
|
|
|
sym = OSD_CLOCK;
|
|
|
|
} else if (mpctx->paused || mpctx->step_frames) {
|
|
|
|
sym = OSD_PAUSE;
|
|
|
|
} else {
|
|
|
|
sym = OSD_PLAY;
|
|
|
|
}
|
|
|
|
}
|
2013-02-16 21:04:43 +00:00
|
|
|
saddf_osd_function_sym(buffer, sym);
|
2013-07-27 19:24:54 +00:00
|
|
|
char *custom_msg = mpctx->opts->osd_status_msg;
|
2013-02-16 21:14:33 +00:00
|
|
|
if (custom_msg && full) {
|
|
|
|
char *text = mp_property_expand_string(mpctx, custom_msg);
|
|
|
|
*buffer = talloc_strdup_append(*buffer, text);
|
|
|
|
talloc_free(text);
|
|
|
|
} else {
|
|
|
|
sadd_hhmmssff(buffer, get_current_time(mpctx), fractions);
|
|
|
|
if (full) {
|
|
|
|
saddf(buffer, " / ");
|
|
|
|
sadd_hhmmssff(buffer, get_time_length(mpctx), fractions);
|
|
|
|
sadd_percentage(buffer, get_percent_pos(mpctx));
|
2013-02-17 19:27:57 +00:00
|
|
|
int cache = mp_get_cache_percent(mpctx);
|
|
|
|
if (cache >= 0)
|
|
|
|
saddf(buffer, " Cache: %d%%", cache);
|
2013-02-16 21:14:33 +00:00
|
|
|
}
|
2012-07-31 22:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-25 01:24:38 +00:00
|
|
|
// OSD messages initated by seeking commands are added lazily with this
|
|
|
|
// function, because multiple successive seek commands can be coalesced.
|
|
|
|
static void add_seek_osd_messages(struct MPContext *mpctx)
|
|
|
|
{
|
2013-02-16 19:50:05 +00:00
|
|
|
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_BAR) {
|
2013-02-26 00:31:35 +00:00
|
|
|
set_osd_bar(mpctx, OSD_BAR_SEEK, "Position", 0, 1,
|
2013-06-20 10:15:36 +00:00
|
|
|
av_clipf(get_current_pos_ratio(mpctx, false), 0, 1));
|
2013-03-30 19:08:56 +00:00
|
|
|
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
|
2013-02-16 19:50:05 +00:00
|
|
|
}
|
2012-09-25 01:24:38 +00:00
|
|
|
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_TEXT) {
|
|
|
|
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->osd_duration);
|
2012-09-25 01:24:38 +00:00
|
|
|
msg->show_position = true;
|
|
|
|
}
|
|
|
|
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CHAPTER_TEXT) {
|
|
|
|
char *chapter = chapter_display_name(mpctx, get_current_chapter(mpctx));
|
2013-07-27 19:24:54 +00:00
|
|
|
set_osd_tmsg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts->osd_duration,
|
2012-09-25 01:24:38 +00:00
|
|
|
"Chapter: %s", chapter);
|
|
|
|
talloc_free(chapter);
|
|
|
|
}
|
|
|
|
if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION)
|
|
|
|
&& mpctx->master_demuxer)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
set_osd_tmsg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts->osd_duration,
|
2012-09-25 01:24:38 +00:00
|
|
|
"Playing edition %d of %d.",
|
|
|
|
mpctx->master_demuxer->edition + 1,
|
|
|
|
mpctx->master_demuxer->num_editions);
|
|
|
|
}
|
|
|
|
mpctx->add_osd_seek_info = 0;
|
|
|
|
}
|
|
|
|
|
2005-11-16 16:51:48 +00:00
|
|
|
/**
|
|
|
|
* \brief Update the OSD message line.
|
|
|
|
*
|
2006-04-27 13:22:23 +00:00
|
|
|
* This function displays the current message on the vo OSD or on the term.
|
|
|
|
* If the stack is empty and the OSD level is high enough the timer
|
|
|
|
* is displayed (only on the vo OSD).
|
2009-07-06 23:26:13 +00:00
|
|
|
*
|
2005-11-16 16:51:48 +00:00
|
|
|
*/
|
|
|
|
|
2008-04-21 03:07:22 +00:00
|
|
|
static void update_osd_msg(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2008-06-23 22:53:58 +00:00
|
|
|
struct osd_state *osd = mpctx->osd;
|
2009-03-30 00:13:17 +00:00
|
|
|
|
2012-09-25 01:24:38 +00:00
|
|
|
add_seek_osd_messages(mpctx);
|
2013-02-26 00:31:35 +00:00
|
|
|
update_osd_bar(mpctx, OSD_BAR_SEEK, 0, 1,
|
2013-06-20 10:15:36 +00:00
|
|
|
av_clipf(get_current_pos_ratio(mpctx, false), 0, 1));
|
2009-03-30 00:13:17 +00:00
|
|
|
|
2005-11-16 16:51:48 +00:00
|
|
|
// Look if we have a msg
|
2012-08-21 21:12:12 +00:00
|
|
|
mp_osd_msg_t *msg = get_osd_msg(mpctx);
|
|
|
|
if (msg && !msg->show_position) {
|
2013-10-01 23:15:59 +00:00
|
|
|
if (mpctx->video_out && opts->term_osd != 1) {
|
2012-08-01 16:23:28 +00:00
|
|
|
osd_set_text(osd, msg->msg);
|
2012-01-06 18:48:50 +00:00
|
|
|
} else if (opts->term_osd) {
|
|
|
|
if (strcmp(mpctx->terminal_osd_text, msg->msg)) {
|
|
|
|
talloc_free(mpctx->terminal_osd_text);
|
|
|
|
mpctx->terminal_osd_text = talloc_strdup(mpctx, msg->msg);
|
2012-10-27 23:56:49 +00:00
|
|
|
// Multi-line message => clear what will be the second line
|
|
|
|
write_status_line(mpctx, "");
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s%s\n", opts->term_osd_esc,
|
2012-01-06 18:48:50 +00:00
|
|
|
mpctx->terminal_osd_text);
|
2012-11-15 19:51:34 +00:00
|
|
|
print_status(mpctx);
|
2012-01-06 18:48:50 +00:00
|
|
|
}
|
2005-11-16 16:51:48 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2012-08-21 21:12:12 +00:00
|
|
|
int osd_level = opts->osd_level;
|
|
|
|
if (msg && msg->show_position)
|
|
|
|
osd_level = 3;
|
|
|
|
|
2013-10-01 23:15:59 +00:00
|
|
|
if (mpctx->video_out && opts->term_osd != 1) {
|
2005-11-16 16:51:48 +00:00
|
|
|
// fallback on the timer
|
2013-02-16 21:04:43 +00:00
|
|
|
char *text = NULL;
|
osd: use libass for OSD rendering
The OSD will now be rendered with libass. The old rendering code, which
used freetype/fontconfig and did text layout manually, is disabled. To
re-enable the old code, use the --disable-libass-osd configure switch.
Some switches do nothing with the new code enabled, such as -subalign,
-sub-bg-alpha, -sub-bg-color, and many more. (The reason is mostly that
the code for rendering unstyled subtitles with libass doesn't make any
attempts to support them. Some of them could be supported in theory.)
Teletext rendering is not implemented in the new OSD rendering code. I
don't have any teletext sources for testing, and since teletext is
being phased out world-wide, the need for this is questionable.
Note that rendering is extremely inefficient, mostly because the libass
output is blended with the extremely strange mplayer OSD format. This
could be improved at a later point.
Remove most OSD rendering from vo_aa.c, because that was extremely
hacky, can't be made work with osd_libass, and didn't work anyway in
my tests.
Internally, some cleanup is done. Subtitle and OSD related variable
declarations were literally all over the place. Move them to sub.h and
sub.c, which were hoarding most of these declarations already. Make the
player core in mplayer.c free of concerns like bitmap font loading.
The old OSD rendering code has been moved to osd_ft.c. The font_load.c
and font_load_ft.c are only needed and compiled if the old OSD
rendering code is configured.
2012-03-22 05:26:37 +00:00
|
|
|
|
2012-08-21 21:12:12 +00:00
|
|
|
if (osd_level >= 2)
|
2013-02-16 21:04:43 +00:00
|
|
|
sadd_osd_status(&text, mpctx, osd_level == 3);
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2012-08-01 16:23:28 +00:00
|
|
|
osd_set_text(osd, text);
|
2013-02-16 21:04:43 +00:00
|
|
|
talloc_free(text);
|
2005-11-16 16:51:48 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2005-11-16 16:51:48 +00:00
|
|
|
// Clear the term osd line
|
2012-01-06 18:48:50 +00:00
|
|
|
if (opts->term_osd && mpctx->terminal_osd_text[0]) {
|
|
|
|
mpctx->terminal_osd_text[0] = '\0';
|
2012-02-25 12:49:29 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s\n", opts->term_osd_esc);
|
2005-11-16 16:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-22 13:12:39 +00:00
|
|
|
static int build_afilter_chain(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_audio *sh_audio = mpctx->sh_audio;
|
|
|
|
struct ao *ao = mpctx->ao;
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-07-22 13:12:39 +00:00
|
|
|
int new_srate;
|
|
|
|
if (af_control_any_rev(sh_audio->afilter,
|
|
|
|
AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET,
|
|
|
|
&opts->playback_speed))
|
|
|
|
new_srate = sh_audio->samplerate;
|
|
|
|
else {
|
|
|
|
new_srate = sh_audio->samplerate * opts->playback_speed;
|
|
|
|
if (new_srate != ao->samplerate) {
|
|
|
|
// limits are taken from libaf/af_resample.c
|
|
|
|
if (new_srate < 8000)
|
|
|
|
new_srate = 8000;
|
|
|
|
if (new_srate > 192000)
|
|
|
|
new_srate = 192000;
|
2013-08-04 21:56:20 +00:00
|
|
|
opts->playback_speed = (double)new_srate / sh_audio->samplerate;
|
2013-07-22 13:12:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return init_audio_filters(sh_audio, new_srate,
|
|
|
|
&ao->samplerate, &ao->channels, &ao->format);
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:43:58 +00:00
|
|
|
static int recreate_audio_filters(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
assert(mpctx->sh_audio);
|
|
|
|
|
|
|
|
// init audio filters:
|
|
|
|
if (!build_afilter_chain(mpctx)) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Couldn't find matching filter/ao format!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-09-19 12:33:26 +00:00
|
|
|
mixer_reinit_audio(mpctx->mixer, mpctx->ao, mpctx->sh_audio->afilter);
|
2013-07-22 12:43:58 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int reinit_audio_filters(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_audio *sh_audio = mpctx->sh_audio;
|
|
|
|
if (!sh_audio)
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
af_uninit(mpctx->sh_audio->afilter);
|
|
|
|
if (af_init(mpctx->sh_audio->afilter) < 0)
|
|
|
|
return -1;
|
|
|
|
if (recreate_audio_filters(mpctx) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-21 03:07:22 +00:00
|
|
|
void reinit_audio_chain(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-19 16:01:30 +00:00
|
|
|
init_demux_stream(mpctx, STREAM_AUDIO);
|
2012-04-23 18:52:54 +00:00
|
|
|
if (!mpctx->sh_audio) {
|
2013-09-19 12:32:09 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_AO);
|
2012-08-19 16:01:30 +00:00
|
|
|
goto no_audio;
|
2012-04-23 18:52:54 +00:00
|
|
|
}
|
2013-07-21 19:18:19 +00:00
|
|
|
|
2010-07-15 17:59:46 +00:00
|
|
|
if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) {
|
core: redo how codecs are mapped, remove codecs.conf
Use codec names instead of FourCCs to identify codecs. Rewrite how
codecs are selected and initialized. Now each decoder exports a list
of decoders (and the codec it supports) via add_decoders(). The order
matters, and the first decoder for a given decoder is preferred over
the other decoders. E.g. all ad_mpg123 decoders are preferred over
ad_lavc, because it comes first in the mpcodecs_ad_drivers array.
Likewise, decoders within ad_lavc that are enumerated first by
libavcodec (using av_codec_next()) are preferred. (This is actually
critical to select h264 software decoding by default instead of vdpau.
libavcodec and ffmpeg/avconv use the same method to select decoders by
default, so we hope this is sane.)
The codec names follow libavcodec's codec names as defined by
AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders
have names different from the canonical codec name. The AVCodecDescriptor
API is relatively new, so we need a compatibility layer for older
libavcodec versions for codec names that are referenced internally,
and which are different from the decoder name. (Add a configure check
for that, because checking versions is getting way too messy.)
demux/codec_tags.c is generated from the former codecs.conf (minus
"special" decoders like vdpau, and excluding the mappings that are the
same as the mappings libavformat's exported RIFF tables). It contains
all the mappings from FourCCs to codec name. This is needed for
demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the
codec as determined by libavformat, while the other demuxers have to do
this on their own, using the mp_set_audio/video_codec_from_tag()
functions. Note that the sh_audio/video->format members don't uniquely
identify the codec anymore, and sh->codec takes over this role.
Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which
provide cover the functionality of the removed switched.
Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure
container/video combinations (e.g. the sample Film_200_zygo_pro.mov)
are played flipped. ffplay/avplay doesn't handle this properly either,
so we don't care and blame ffmeg/libav instead.
2013-02-09 14:15:19 +00:00
|
|
|
if (!init_best_audio_codec(mpctx->sh_audio, opts->audio_decoders))
|
2010-07-15 17:59:46 +00:00
|
|
|
goto init_error;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_ACODEC;
|
2009-11-06 16:16:22 +00:00
|
|
|
}
|
2010-07-15 17:59:46 +00:00
|
|
|
|
2013-07-21 19:18:19 +00:00
|
|
|
int ao_srate = opts->force_srate;
|
|
|
|
int ao_format = opts->audio_output_format;
|
|
|
|
struct mp_chmap ao_channels = {0};
|
|
|
|
if (mpctx->initialized_flags & INITIALIZED_AO) {
|
|
|
|
ao_srate = mpctx->ao->samplerate;
|
|
|
|
ao_format = mpctx->ao->format;
|
|
|
|
ao_channels = mpctx->ao->channels;
|
|
|
|
} else {
|
2013-03-31 02:24:53 +00:00
|
|
|
// Automatic downmix
|
2013-04-06 20:43:12 +00:00
|
|
|
if (mp_chmap_is_stereo(&opts->audio_output_channels) &&
|
|
|
|
!mp_chmap_is_stereo(&mpctx->sh_audio->channels))
|
|
|
|
{
|
2013-07-21 19:18:19 +00:00
|
|
|
mp_chmap_from_channels(&ao_channels, 2);
|
2013-04-06 20:43:12 +00:00
|
|
|
}
|
2010-11-12 12:06:37 +00:00
|
|
|
}
|
2011-04-09 00:03:22 +00:00
|
|
|
|
2013-07-22 12:43:58 +00:00
|
|
|
// Determine what the filter chain outputs. build_afilter_chain() also
|
|
|
|
// needs this for testing whether playback speed is changed by resampling
|
|
|
|
// or using a special filter.
|
2011-08-07 00:58:12 +00:00
|
|
|
if (!init_audio_filters(mpctx->sh_audio, // preliminary init
|
|
|
|
// input:
|
|
|
|
mpctx->sh_audio->samplerate,
|
|
|
|
// output:
|
2013-07-21 19:18:19 +00:00
|
|
|
&ao_srate, &ao_channels, &ao_format)) {
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Error at audio filter chain "
|
2010-11-12 12:06:37 +00:00
|
|
|
"pre-init!\n");
|
2012-08-04 01:46:11 +00:00
|
|
|
goto init_error;
|
2010-11-12 12:06:37 +00:00
|
|
|
}
|
2013-07-21 19:18:19 +00:00
|
|
|
|
|
|
|
if (!(mpctx->initialized_flags & INITIALIZED_AO)) {
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_AO;
|
|
|
|
mp_chmap_remove_useless_channels(&ao_channels,
|
2013-04-07 18:19:13 +00:00
|
|
|
&opts->audio_output_channels);
|
2013-08-01 15:41:32 +00:00
|
|
|
mpctx->ao = ao_init_best(mpctx->global, mpctx->input,
|
|
|
|
mpctx->encode_lavc_ctx, ao_srate, ao_format,
|
|
|
|
ao_channels);
|
2013-07-21 19:18:19 +00:00
|
|
|
struct ao *ao = mpctx->ao;
|
2013-07-21 19:33:17 +00:00
|
|
|
if (!ao) {
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Could not open/initialize audio device -> no sound.\n");
|
2010-07-15 17:59:46 +00:00
|
|
|
goto init_error;
|
|
|
|
}
|
2011-07-02 06:22:32 +00:00
|
|
|
ao->buffer.start = talloc_new(ao);
|
2013-04-07 23:58:33 +00:00
|
|
|
char *s = mp_audio_fmt_to_str(ao->samplerate, &ao->channels, ao->format);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "AO: [%s] %s\n",
|
|
|
|
ao->driver->info->short_name, s);
|
|
|
|
talloc_free(s);
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Description: %s\nAO: Author: %s\n",
|
2011-04-09 00:03:22 +00:00
|
|
|
ao->driver->info->name, ao->driver->info->author);
|
|
|
|
if (strlen(ao->driver->info->comment) > 0)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Comment: %s\n",
|
|
|
|
ao->driver->info->comment);
|
2009-11-06 16:16:22 +00:00
|
|
|
}
|
2010-07-15 17:59:46 +00:00
|
|
|
|
2013-07-22 12:43:58 +00:00
|
|
|
if (recreate_audio_filters(mpctx) < 0)
|
2009-11-06 16:16:22 +00:00
|
|
|
goto init_error;
|
2013-07-22 12:43:58 +00:00
|
|
|
|
2010-11-13 17:27:01 +00:00
|
|
|
mpctx->syncing_audio = true;
|
2009-11-06 16:16:22 +00:00
|
|
|
return;
|
2009-11-06 15:56:30 +00:00
|
|
|
|
|
|
|
init_error:
|
2013-09-19 12:32:09 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_ACODEC | INITIALIZED_AO);
|
2012-08-19 16:01:30 +00:00
|
|
|
cleanup_demux_stream(mpctx, STREAM_AUDIO);
|
|
|
|
no_audio:
|
|
|
|
mpctx->current_track[STREAM_AUDIO] = NULL;
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Audio: no audio\n");
|
2006-06-11 21:16:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-14 19:48:54 +00:00
|
|
|
// Return pts value corresponding to the end point of audio written to the
|
|
|
|
// ao so far.
|
2008-04-21 03:55:23 +00:00
|
|
|
static double written_audio_pts(struct MPContext *mpctx)
|
2006-06-14 19:48:54 +00:00
|
|
|
{
|
2008-04-21 03:55:23 +00:00
|
|
|
sh_audio_t *sh_audio = mpctx->sh_audio;
|
2012-07-17 20:18:06 +00:00
|
|
|
if (!sh_audio)
|
|
|
|
return MP_NOPTS_VALUE;
|
2013-07-11 17:15:09 +00:00
|
|
|
|
|
|
|
double bps = sh_audio->channels.num * sh_audio->samplerate *
|
|
|
|
sh_audio->samplesize;
|
|
|
|
|
2006-06-14 19:48:54 +00:00
|
|
|
// first calculate the end pts of audio that has been output by decoder
|
|
|
|
double a_pts = sh_audio->pts;
|
2013-07-11 17:13:44 +00:00
|
|
|
if (a_pts == MP_NOPTS_VALUE)
|
|
|
|
return MP_NOPTS_VALUE;
|
|
|
|
|
|
|
|
// sh_audio->pts is the timestamp of the latest input packet with
|
|
|
|
// known pts that the decoder has decoded. sh_audio->pts_bytes is
|
|
|
|
// the amount of bytes the decoder has written after that timestamp.
|
2013-07-11 17:15:09 +00:00
|
|
|
a_pts += sh_audio->pts_bytes / bps;
|
2013-07-11 17:13:44 +00:00
|
|
|
|
2006-06-14 19:48:54 +00:00
|
|
|
// Now a_pts hopefully holds the pts for end of audio from decoder.
|
2013-07-11 17:13:44 +00:00
|
|
|
// Subtract data in buffers between decoder and audio out.
|
2006-06-14 19:48:54 +00:00
|
|
|
|
|
|
|
// Decoded but not filtered
|
2013-07-11 17:15:09 +00:00
|
|
|
a_pts -= sh_audio->a_buffer_len / bps;
|
2006-06-14 19:48:54 +00:00
|
|
|
|
2007-11-01 06:52:50 +00:00
|
|
|
// Data buffered in audio filters, measured in bytes of "missing" output
|
2011-07-02 06:22:32 +00:00
|
|
|
double buffered_output = af_calc_delay(sh_audio->afilter);
|
2007-11-01 06:52:50 +00:00
|
|
|
|
2006-06-14 19:48:54 +00:00
|
|
|
// Data that was ready for ao but was buffered because ao didn't fully
|
|
|
|
// accept everything to internal buffers yet
|
2011-07-02 06:22:32 +00:00
|
|
|
buffered_output += mpctx->ao->buffer.len;
|
2007-11-01 06:52:50 +00:00
|
|
|
|
|
|
|
// Filters divide audio length by playback_speed, so multiply by it
|
|
|
|
// to get the length in original units without speedup or slowdown
|
2013-07-27 19:24:54 +00:00
|
|
|
a_pts -= buffered_output * mpctx->opts->playback_speed / mpctx->ao->bps;
|
2006-06-14 19:48:54 +00:00
|
|
|
|
2009-03-29 19:45:06 +00:00
|
|
|
return a_pts + mpctx->video_offset;
|
2006-06-14 19:48:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return pts value corresponding to currently playing audio.
|
2008-04-21 03:55:23 +00:00
|
|
|
double playing_audio_pts(struct MPContext *mpctx)
|
2006-06-14 19:48:54 +00:00
|
|
|
{
|
2011-07-30 18:05:59 +00:00
|
|
|
double pts = written_audio_pts(mpctx);
|
|
|
|
if (pts == MP_NOPTS_VALUE)
|
|
|
|
return pts;
|
2013-07-27 19:24:54 +00:00
|
|
|
return pts - mpctx->opts->playback_speed * ao_get_delay(mpctx->ao);
|
2006-06-14 19:48:54 +00:00
|
|
|
}
|
|
|
|
|
2013-07-11 17:16:25 +00:00
|
|
|
// When reading subtitles from a demuxer, and we read video or audio from the
|
|
|
|
// demuxer, we should not explicitly read subtitle packets. (With external
|
|
|
|
// subs, we have to.)
|
|
|
|
static bool is_interleaved(struct MPContext *mpctx, struct track *track)
|
2012-11-15 20:09:15 +00:00
|
|
|
{
|
|
|
|
if (track->is_external || !track->demuxer)
|
2013-07-11 17:16:25 +00:00
|
|
|
return false;
|
2012-11-15 20:09:15 +00:00
|
|
|
|
|
|
|
struct demuxer *demuxer = track->demuxer;
|
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
struct track *other = mpctx->current_track[type];
|
2012-12-27 15:27:58 +00:00
|
|
|
if (other && other != track && other->demuxer && other->demuxer == demuxer)
|
2013-07-11 17:16:25 +00:00
|
|
|
return true;
|
2012-11-15 20:09:15 +00:00
|
|
|
}
|
2013-07-11 17:16:25 +00:00
|
|
|
return false;
|
2012-11-15 20:09:15 +00:00
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static void reset_subtitles(struct MPContext *mpctx)
|
2011-01-11 23:37:02 +00:00
|
|
|
{
|
2012-09-29 07:52:27 +00:00
|
|
|
if (mpctx->sh_sub)
|
2013-06-01 17:44:12 +00:00
|
|
|
sub_reset(mpctx->sh_sub->dec_sub);
|
2013-04-28 19:12:11 +00:00
|
|
|
set_osd_subtitle(mpctx, NULL);
|
2013-06-01 17:44:12 +00:00
|
|
|
osd_changed(mpctx->osd, OSDTYPE_SUB);
|
2011-01-11 23:37:02 +00:00
|
|
|
}
|
|
|
|
|
2013-09-15 22:27:53 +00:00
|
|
|
static void update_subtitles(struct MPContext *mpctx)
|
2011-01-11 23:37:02 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!(mpctx->initialized_flags & INITIALIZED_SUB))
|
|
|
|
return;
|
2012-12-29 22:28:31 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
struct track *track = mpctx->current_track[STREAM_SUB];
|
2013-06-01 17:44:12 +00:00
|
|
|
struct sh_sub *sh_sub = mpctx->sh_sub;
|
|
|
|
assert(track && sh_sub);
|
|
|
|
struct dec_sub *dec_sub = sh_sub->dec_sub;
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2013-08-24 17:37:34 +00:00
|
|
|
if (mpctx->sh_video && mpctx->sh_video->vf_input) {
|
|
|
|
struct mp_image_params params = *mpctx->sh_video->vf_input;
|
|
|
|
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms);
|
2013-07-14 23:48:25 +00:00
|
|
|
}
|
|
|
|
|
2013-06-28 23:31:19 +00:00
|
|
|
mpctx->osd->video_offset = track->under_timeline ? mpctx->video_offset : 0;
|
2012-12-29 22:28:31 +00:00
|
|
|
|
2013-09-15 22:27:53 +00:00
|
|
|
double refpts_s = mpctx->playback_pts - mpctx->osd->video_offset;
|
2013-06-28 23:31:19 +00:00
|
|
|
double curpts_s = refpts_s + opts->sub_delay;
|
2011-01-11 23:37:02 +00:00
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!track->preloaded) {
|
2013-07-11 17:16:25 +00:00
|
|
|
bool interleaved = is_interleaved(mpctx, track);
|
2011-01-11 23:37:02 +00:00
|
|
|
|
2013-06-01 17:40:09 +00:00
|
|
|
while (1) {
|
2013-07-11 17:16:25 +00:00
|
|
|
if (interleaved && !demux_has_packet(sh_sub->gsh))
|
2013-07-11 17:10:33 +00:00
|
|
|
break;
|
|
|
|
double subpts_s = demux_get_next_pts(sh_sub->gsh);
|
|
|
|
if (!demux_has_packet(sh_sub->gsh))
|
2013-06-01 17:40:09 +00:00
|
|
|
break;
|
timeline: subs: keep subtitle tracks in source time
Timeline handling converted the pts values from demuxed subtitles to
timeline scale. Change the code to do most subtitle handling in
original subtitle source pts, and instead convert current playback
timeline pts to those units when deciding which subtitle to show.
The main functionality changes are that now demuxed subtitles which
overlap chapter boundaries are handled correctly (at least for libass
subtitles), and external subtitles are assumed to use same pts scale
as current source (this needs improvements later).
Before, a video subtitle that had a duration continuing past the end
of the chapter would continue to be shown for the original duration,
even if the chapter ended and playback switched to a position in the
source where the subtitle shouldn't exist. Now, the subtitle will
correctly end.
Before, external subtitle files were interpreted as specifying pts
values in timeline scale. Now, they're interpreted as specifying pts
values in source file time scale, for _every_ source file. This is
probably more likely to be what the user wants for the "main" source
file in case there is one, but almost certainly not quite right for
multiple source files where the same subs could be shown over
different scenes. If the user wants them to match some main source
file, it's probably still better to have incorrect extra subs for
video from some files than to have every subtitle appearing at the
wrong time. The new code makes it easier to change the interpretation
of the subtitle times, and some configurability should be added in
the future.
2012-03-20 00:54:19 +00:00
|
|
|
if (subpts_s > curpts_s) {
|
2012-12-12 21:56:41 +00:00
|
|
|
mp_dbg(MSGT_CPLAYER, MSGL_DBG2,
|
2012-12-08 12:59:49 +00:00
|
|
|
"Sub early: c_pts=%5.3f s_pts=%5.3f\n",
|
|
|
|
curpts_s, subpts_s);
|
2011-01-11 23:37:02 +00:00
|
|
|
// Libass handled subs can be fed to it in advance
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!sub_accept_packets_in_advance(dec_sub))
|
2011-01-11 23:37:02 +00:00
|
|
|
break;
|
|
|
|
// Try to avoid demuxing whole file at once
|
2013-07-11 17:16:25 +00:00
|
|
|
if (subpts_s > curpts_s + 1 && !interleaved)
|
2011-01-11 23:37:02 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-07-11 17:10:33 +00:00
|
|
|
struct demux_packet *pkt = demux_read_packet(sh_sub->gsh);
|
2012-12-07 20:18:24 +00:00
|
|
|
mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f "
|
2013-07-10 00:09:10 +00:00
|
|
|
"duration=%5.3f len=%d\n", curpts_s, pkt->pts, pkt->duration,
|
|
|
|
pkt->len);
|
|
|
|
sub_decode(dec_sub, pkt);
|
2013-07-11 17:10:33 +00:00
|
|
|
talloc_free(pkt);
|
2011-01-11 23:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
2013-04-28 19:12:11 +00:00
|
|
|
|
2013-10-01 23:32:03 +00:00
|
|
|
if (!mpctx->osd->render_bitmap_subs || !mpctx->video_out)
|
2013-06-01 17:44:12 +00:00
|
|
|
set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s));
|
2011-01-11 23:37:02 +00:00
|
|
|
}
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
static int check_framedrop(struct MPContext *mpctx, double frame_time)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-08-07 00:58:12 +00:00
|
|
|
// check for frame-drop:
|
2013-07-11 17:17:51 +00:00
|
|
|
if (mpctx->sh_audio && !mpctx->ao->untimed &&
|
|
|
|
!demux_stream_eof(mpctx->sh_audio->gsh))
|
|
|
|
{
|
2011-08-07 00:58:12 +00:00
|
|
|
float delay = opts->playback_speed * ao_get_delay(mpctx->ao);
|
|
|
|
float d = delay - mpctx->delay;
|
2013-07-11 17:21:45 +00:00
|
|
|
if (frame_time < 0)
|
|
|
|
frame_time = mpctx->sh_video->fps > 0 ? 1.0 / mpctx->sh_video->fps : 0;
|
2011-08-07 00:58:12 +00:00
|
|
|
// we should avoid dropping too many frames in sequence unless we
|
|
|
|
// are too late. and we allow 100ms A-V delay here:
|
2013-03-08 01:08:02 +00:00
|
|
|
if (d < -mpctx->dropped_frames * frame_time - 0.100 && !mpctx->paused
|
2011-08-07 00:58:12 +00:00
|
|
|
&& !mpctx->restart_playback) {
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->drop_frame_cnt++;
|
|
|
|
mpctx->dropped_frames++;
|
2013-07-27 19:24:54 +00:00
|
|
|
return mpctx->opts->frame_dropping;
|
2011-08-07 00:58:12 +00:00
|
|
|
} else
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->dropped_frames = 0;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2008-03-17 20:21:16 +00:00
|
|
|
}
|
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
static double timing_sleep(struct MPContext *mpctx, double time_frame)
|
2006-11-14 01:40:06 +00:00
|
|
|
{
|
2012-07-29 16:48:50 +00:00
|
|
|
// assume kernel HZ=100 for softsleep, works with larger HZ but with
|
|
|
|
// unnecessarily high CPU usage
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-05-25 16:31:06 +00:00
|
|
|
double margin = opts->softsleep ? 0.011 : 0;
|
2012-07-29 16:48:50 +00:00
|
|
|
while (time_frame > margin) {
|
2013-05-25 17:56:52 +00:00
|
|
|
mp_sleep_us(1000000 * (time_frame - margin));
|
2012-07-29 16:48:50 +00:00
|
|
|
time_frame -= get_relative_time(mpctx);
|
|
|
|
}
|
|
|
|
if (opts->softsleep) {
|
|
|
|
if (time_frame < 0)
|
|
|
|
mp_tmsg(MSGT_AVSYNC, MSGL_WARN,
|
|
|
|
"Warning! Softsleep underflow!\n");
|
|
|
|
while (time_frame > 0)
|
|
|
|
time_frame -= get_relative_time(mpctx); // burn the CPU
|
2006-11-14 01:40:06 +00:00
|
|
|
}
|
|
|
|
return time_frame;
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st,
|
sub: add sd_spu.c to wrap spudec, cleanup mplayer.c
This unifies the subtitle rendering path. Now all subtitle rendering
goes through sd_ass.c/sd_lavc.c/sd_spu.c.
Before that commit, the spudec.h functions were used directly in
mplayer.c, which introduced many special cases. Add sd_spu.c, which is
just a small wrapper connecting the new subtitle render API with the
dusty old vobsub decoder in spudec.c.
One detail that changes is that we always pass the palette as extra
data, instead of passing the libdvdread palette as pointer to spudec
directly. This is a bit roundabout, but actually makes the code simpler
and more elegant: the difference between DVD and non-DVD dvdsubs is
reduced.
Ideally, we would just delete spudec.c and use libavcodec's DVD sub
decoder. However, DVD playback with demux_mpg produces packets
incompatible to lavc. There are incompatibilities the other way around
as well: packets from libavformat's vobsub demuxer are incompatible to
spudec.c. So we define a new subtitle codec name for demux_mpg subs,
"dvd_subtitle_mpg", which only sd_spu can decode.
There is actually code in spudec.c to "assemble" fragments into complete
packets, but using the whole spudec.c is easier than trying to move this
code into demux_mpg to fix subtitle packets.
As additional complication, Libav 9.x can't decode DVD subs correctly,
so use sd_spu in that case as well.
2013-04-28 23:13:22 +00:00
|
|
|
int width, int height)
|
2012-12-07 20:18:24 +00:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_DVDREAD
|
2013-06-04 23:59:04 +00:00
|
|
|
if (!st)
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct stream_dvd_info_req info;
|
|
|
|
if (stream_control(st, STREAM_CTRL_GET_DVD_INFO, &info) < 0)
|
2012-12-07 20:18:24 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
|
|
|
|
csp.int_bits_in = 8;
|
|
|
|
csp.int_bits_out = 8;
|
|
|
|
float cmatrix[3][4];
|
|
|
|
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
|
|
|
|
|
sub: add sd_spu.c to wrap spudec, cleanup mplayer.c
This unifies the subtitle rendering path. Now all subtitle rendering
goes through sd_ass.c/sd_lavc.c/sd_spu.c.
Before that commit, the spudec.h functions were used directly in
mplayer.c, which introduced many special cases. Add sd_spu.c, which is
just a small wrapper connecting the new subtitle render API with the
dusty old vobsub decoder in spudec.c.
One detail that changes is that we always pass the palette as extra
data, instead of passing the libdvdread palette as pointer to spudec
directly. This is a bit roundabout, but actually makes the code simpler
and more elegant: the difference between DVD and non-DVD dvdsubs is
reduced.
Ideally, we would just delete spudec.c and use libavcodec's DVD sub
decoder. However, DVD playback with demux_mpg produces packets
incompatible to lavc. There are incompatibilities the other way around
as well: packets from libavformat's vobsub demuxer are incompatible to
spudec.c. So we define a new subtitle codec name for demux_mpg subs,
"dvd_subtitle_mpg", which only sd_spu can decode.
There is actually code in spudec.c to "assemble" fragments into complete
packets, but using the whole spudec.c is easier than trying to move this
code into demux_mpg to fix subtitle packets.
As additional complication, Libav 9.x can't decode DVD subs correctly,
so use sd_spu in that case as well.
2013-04-28 23:13:22 +00:00
|
|
|
if (width == 0 || height == 0) {
|
|
|
|
width = 720;
|
|
|
|
height = 480;
|
|
|
|
}
|
|
|
|
|
2012-12-07 20:18:24 +00:00
|
|
|
char *s = NULL;
|
|
|
|
s = talloc_asprintf_append(s, "size: %dx%d\n", width, height);
|
|
|
|
s = talloc_asprintf_append(s, "palette: ");
|
|
|
|
for (int i = 0; i < 16; i++) {
|
2013-06-04 23:59:04 +00:00
|
|
|
int color = info.palette[i];
|
2012-12-07 20:18:24 +00:00
|
|
|
int c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
|
|
|
|
mp_map_int_color(cmatrix, 8, c);
|
|
|
|
color = (c[2] << 16) | (c[1] << 8) | c[0];
|
|
|
|
|
|
|
|
if (i != 0)
|
|
|
|
talloc_asprintf_append(s, ", ");
|
|
|
|
s = talloc_asprintf_append(s, "%06x", color);
|
|
|
|
}
|
|
|
|
s = talloc_asprintf_append(s, "\n");
|
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
sub_set_extradata(dec_sub, s, strlen(s));
|
2012-12-07 20:18:24 +00:00
|
|
|
talloc_free(s);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static void reinit_subs(struct MPContext *mpctx)
|
2009-11-16 04:54:22 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-19 16:01:30 +00:00
|
|
|
struct track *track = mpctx->current_track[STREAM_SUB];
|
|
|
|
|
|
|
|
assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
|
|
|
|
|
|
|
|
init_demux_stream(mpctx, STREAM_SUB);
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!mpctx->sh_sub)
|
2012-08-19 16:01:30 +00:00
|
|
|
return;
|
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!mpctx->sh_sub->dec_sub)
|
|
|
|
mpctx->sh_sub->dec_sub = sub_create(opts);
|
|
|
|
|
2013-06-11 17:26:57 +00:00
|
|
|
assert(track->demuxer);
|
2013-07-11 17:22:24 +00:00
|
|
|
// Lazily added DVD track - will be created on first sub packet
|
|
|
|
if (!track->stream)
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
return;
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_SUB;
|
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
struct sh_sub *sh_sub = mpctx->sh_sub;
|
|
|
|
struct dec_sub *dec_sub = sh_sub->dec_sub;
|
|
|
|
assert(dec_sub);
|
sub: add sd_spu.c to wrap spudec, cleanup mplayer.c
This unifies the subtitle rendering path. Now all subtitle rendering
goes through sd_ass.c/sd_lavc.c/sd_spu.c.
Before that commit, the spudec.h functions were used directly in
mplayer.c, which introduced many special cases. Add sd_spu.c, which is
just a small wrapper connecting the new subtitle render API with the
dusty old vobsub decoder in spudec.c.
One detail that changes is that we always pass the palette as extra
data, instead of passing the libdvdread palette as pointer to spudec
directly. This is a bit roundabout, but actually makes the code simpler
and more elegant: the difference between DVD and non-DVD dvdsubs is
reduced.
Ideally, we would just delete spudec.c and use libavcodec's DVD sub
decoder. However, DVD playback with demux_mpg produces packets
incompatible to lavc. There are incompatibilities the other way around
as well: packets from libavformat's vobsub demuxer are incompatible to
spudec.c. So we define a new subtitle codec name for demux_mpg subs,
"dvd_subtitle_mpg", which only sd_spu can decode.
There is actually code in spudec.c to "assemble" fragments into complete
packets, but using the whole spudec.c is easier than trying to move this
code into demux_mpg to fix subtitle packets.
As additional complication, Libav 9.x can't decode DVD subs correctly,
so use sd_spu in that case as well.
2013-04-28 23:13:22 +00:00
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
if (!sub_is_initialized(dec_sub)) {
|
|
|
|
int w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0;
|
|
|
|
int h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0;
|
2013-06-11 19:39:54 +00:00
|
|
|
float fps = mpctx->sh_video ? mpctx->sh_video->fps : 25;
|
2013-06-01 17:44:12 +00:00
|
|
|
|
|
|
|
set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
|
|
|
|
sub_set_video_res(dec_sub, w, h);
|
2013-06-11 19:39:54 +00:00
|
|
|
sub_set_video_fps(dec_sub, fps);
|
2013-06-01 17:44:12 +00:00
|
|
|
sub_set_ass_renderer(dec_sub, mpctx->osd->ass_library,
|
|
|
|
mpctx->osd->ass_renderer);
|
|
|
|
sub_init_from_sh(dec_sub, sh_sub);
|
2013-06-11 19:39:54 +00:00
|
|
|
|
|
|
|
// Don't do this if the file has video/audio streams. Don't do it even
|
|
|
|
// if it has only sub streams, because reading packets will change the
|
|
|
|
// demuxer position.
|
|
|
|
if (!track->preloaded && track->is_external) {
|
2013-08-22 17:13:29 +00:00
|
|
|
demux_seek(track->demuxer, 0, SEEK_ABSOLUTE);
|
2013-06-11 19:39:54 +00:00
|
|
|
track->preloaded = sub_read_all_packets(dec_sub, sh_sub);
|
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2013-04-28 19:12:11 +00:00
|
|
|
|
2013-06-01 17:44:12 +00:00
|
|
|
mpctx->osd->dec_sub = dec_sub;
|
|
|
|
|
2013-04-28 19:12:11 +00:00
|
|
|
// Decides whether to use OSD path or normal subtitle rendering path.
|
2013-06-01 17:44:12 +00:00
|
|
|
mpctx->osd->render_bitmap_subs =
|
|
|
|
opts->ass_enabled || !sub_has_get_text(dec_sub);
|
|
|
|
|
|
|
|
reset_subtitles(mpctx);
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
static char *track_layout_hash(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
char *h = talloc_strdup(NULL, "");
|
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *track = mpctx->tracks[n];
|
|
|
|
if (track->type != type)
|
|
|
|
continue;
|
|
|
|
h = talloc_asprintf_append_buffer(h, "%d-%d-%d-%d-%s\n", type,
|
|
|
|
track->user_tid, track->default_track, track->is_external,
|
|
|
|
track->lang ? track->lang : "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
|
|
|
|
struct track *track)
|
|
|
|
{
|
|
|
|
assert(!track || track->type == type);
|
|
|
|
|
|
|
|
struct track *current = mpctx->current_track[type];
|
|
|
|
if (track == current)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (type == STREAM_VIDEO) {
|
2013-10-01 23:15:59 +00:00
|
|
|
int uninit = INITIALIZED_VCODEC;
|
|
|
|
if (!mpctx->opts->force_vo)
|
|
|
|
uninit |= mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO;
|
|
|
|
uninit_player(mpctx, uninit);
|
2012-08-19 16:01:30 +00:00
|
|
|
} else if (type == STREAM_AUDIO) {
|
2013-09-19 12:32:09 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
|
2012-08-19 16:01:30 +00:00
|
|
|
} else if (type == STREAM_SUB) {
|
|
|
|
uninit_player(mpctx, INITIALIZED_SUB);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->current_track[type] = track;
|
|
|
|
|
|
|
|
int user_tid = track ? track->user_tid : -2;
|
|
|
|
if (type == STREAM_VIDEO) {
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->video_id = user_tid;
|
2012-08-19 16:01:30 +00:00
|
|
|
reinit_video_chain(mpctx);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify_property(mpctx, "vid");
|
2012-08-19 16:01:30 +00:00
|
|
|
} else if (type == STREAM_AUDIO) {
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->audio_id = user_tid;
|
2012-08-19 16:01:30 +00:00
|
|
|
reinit_audio_chain(mpctx);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify_property(mpctx, "aid");
|
2012-08-19 16:01:30 +00:00
|
|
|
} else if (type == STREAM_SUB) {
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->sub_id = user_tid;
|
2012-08-19 16:01:30 +00:00
|
|
|
reinit_subs(mpctx);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify_property(mpctx, "sid");
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
|
|
|
|
talloc_free(mpctx->track_layout_hash);
|
|
|
|
mpctx->track_layout_hash = talloc_steal(mpctx, track_layout_hash(mpctx));
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
|
|
|
|
int tid)
|
|
|
|
{
|
2012-11-15 19:26:52 +00:00
|
|
|
if (tid == -1)
|
|
|
|
return mpctx->current_track[type];
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *track = mpctx->tracks[n];
|
|
|
|
if (track->type == type && track->user_tid == tid)
|
|
|
|
return track;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
return NULL;
|
2009-11-11 11:19:50 +00:00
|
|
|
}
|
|
|
|
|
2012-11-15 19:26:52 +00:00
|
|
|
bool mp_remove_track(struct MPContext *mpctx, struct track *track)
|
|
|
|
{
|
|
|
|
if (track->under_timeline)
|
|
|
|
return false;
|
|
|
|
if (!track->is_external)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mpctx->current_track[track->type] == track) {
|
|
|
|
mp_switch_track(mpctx, track->type, NULL);
|
|
|
|
if (mpctx->current_track[track->type] == track)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = 0;
|
|
|
|
while (index < mpctx->num_tracks && mpctx->tracks[index] != track)
|
|
|
|
index++;
|
|
|
|
assert(index < mpctx->num_tracks);
|
|
|
|
while (index + 1 < mpctx->num_tracks) {
|
|
|
|
mpctx->tracks[index] = mpctx->tracks[index + 1];
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
mpctx->num_tracks--;
|
|
|
|
talloc_free(track);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
|
|
|
|
mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
|
|
|
|
|
2012-11-15 19:26:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-12-08 18:04:08 +00:00
|
|
|
/* Modify video timing to match the audio timeline. There are two main
|
|
|
|
* reasons this is needed. First, video and audio can start from different
|
|
|
|
* positions at beginning of file or after a seek (MPlayer starts both
|
|
|
|
* immediately even if they have different pts). Second, the file can have
|
|
|
|
* audio timestamps that are inconsistent with the duration of the audio
|
|
|
|
* packets, for example two consecutive timestamp values differing by
|
|
|
|
* one second but only a packet with enough samples for half a second
|
|
|
|
* of playback between them.
|
|
|
|
*/
|
|
|
|
static void adjust_sync(struct MPContext *mpctx, double frame_time)
|
2006-11-14 01:40:06 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-03-08 01:08:02 +00:00
|
|
|
|
2010-11-13 17:27:01 +00:00
|
|
|
if (!mpctx->sh_audio || mpctx->syncing_audio)
|
2008-12-08 18:04:08 +00:00
|
|
|
return;
|
2006-11-14 01:40:06 +00:00
|
|
|
|
2008-12-08 18:04:08 +00:00
|
|
|
double a_pts = written_audio_pts(mpctx) - mpctx->delay;
|
|
|
|
double v_pts = mpctx->sh_video->pts;
|
|
|
|
double av_delay = a_pts - v_pts;
|
|
|
|
// Try to sync vo_flip() so it will *finish* at given time
|
|
|
|
av_delay += mpctx->last_vo_flip_duration;
|
2013-03-08 01:08:02 +00:00
|
|
|
av_delay -= mpctx->audio_delay; // This much pts difference is desired
|
2006-11-14 01:40:06 +00:00
|
|
|
|
2008-12-08 18:04:08 +00:00
|
|
|
double change = av_delay * 0.1;
|
2013-03-08 01:08:02 +00:00
|
|
|
double max_change = opts->default_max_pts_correction >= 0 ?
|
|
|
|
opts->default_max_pts_correction : frame_time * 0.1;
|
2008-12-08 18:04:08 +00:00
|
|
|
if (change < -max_change)
|
|
|
|
change = -max_change;
|
|
|
|
else if (change > max_change)
|
|
|
|
change = max_change;
|
|
|
|
mpctx->delay += change;
|
|
|
|
mpctx->total_avsync_change += change;
|
2006-11-14 01:40:06 +00:00
|
|
|
}
|
|
|
|
|
2011-07-31 13:31:07 +00:00
|
|
|
static int write_to_ao(struct MPContext *mpctx, void *data, int len, int flags,
|
|
|
|
double pts)
|
2011-07-30 22:05:17 +00:00
|
|
|
{
|
|
|
|
if (mpctx->paused)
|
|
|
|
return 0;
|
2011-07-31 13:31:07 +00:00
|
|
|
struct ao *ao = mpctx->ao;
|
2013-07-27 19:24:54 +00:00
|
|
|
double bps = ao->bps / mpctx->opts->playback_speed;
|
2011-07-31 13:31:07 +00:00
|
|
|
ao->pts = pts;
|
|
|
|
int played = ao_play(mpctx->ao, data, len, flags);
|
|
|
|
if (played > 0) {
|
2013-09-29 19:10:36 +00:00
|
|
|
mpctx->shown_aframes += played / (af_fmt2bits(ao->format) / 8);
|
2011-07-31 13:31:07 +00:00
|
|
|
mpctx->delay += played / bps;
|
|
|
|
// Keep correct pts for remaining data - could be used to flush
|
|
|
|
// remaining buffer when closing ao.
|
|
|
|
ao->pts += played / bps;
|
2012-11-18 23:28:38 +00:00
|
|
|
return played;
|
2011-07-31 13:31:07 +00:00
|
|
|
}
|
2012-11-18 23:28:38 +00:00
|
|
|
return 0;
|
2011-07-30 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-13 17:27:01 +00:00
|
|
|
#define ASYNC_PLAY_DONE -3
|
|
|
|
static int audio_start_sync(struct MPContext *mpctx, int playsize)
|
|
|
|
{
|
2011-04-09 00:03:22 +00:00
|
|
|
struct ao *ao = mpctx->ao;
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2010-11-13 17:27:01 +00:00
|
|
|
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
// Timing info may not be set without
|
2011-07-02 06:22:32 +00:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, 1);
|
2010-11-13 17:27:01 +00:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
|
2010-11-21 17:25:18 +00:00
|
|
|
int bytes;
|
2010-12-16 19:26:12 +00:00
|
|
|
bool did_retry = false;
|
2011-07-31 13:31:07 +00:00
|
|
|
double written_pts;
|
|
|
|
double bps = ao->bps / opts->playback_speed;
|
2011-11-06 13:41:39 +00:00
|
|
|
bool hrseek = mpctx->hrseek_active; // audio only hrseek
|
|
|
|
mpctx->hrseek_active = false;
|
2010-11-21 17:25:18 +00:00
|
|
|
while (1) {
|
2011-07-31 13:31:07 +00:00
|
|
|
written_pts = written_audio_pts(mpctx);
|
2011-11-06 13:41:39 +00:00
|
|
|
double ptsdiff;
|
|
|
|
if (hrseek)
|
|
|
|
ptsdiff = written_pts - mpctx->hrseek_pts;
|
|
|
|
else
|
|
|
|
ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay
|
2013-03-08 01:08:02 +00:00
|
|
|
- mpctx->audio_delay;
|
2011-07-31 13:31:07 +00:00
|
|
|
bytes = ptsdiff * bps;
|
2013-04-05 21:06:22 +00:00
|
|
|
bytes -= bytes % (ao->channels.num * af_fmt2bits(ao->format) / 8);
|
2010-11-21 17:25:18 +00:00
|
|
|
|
2010-12-16 19:26:12 +00:00
|
|
|
// ogg demuxers give packets without timing
|
|
|
|
if (written_pts <= 1 && sh_audio->pts == MP_NOPTS_VALUE) {
|
|
|
|
if (!did_retry) {
|
|
|
|
// Try to read more data to see packets that have pts
|
2013-07-22 22:45:23 +00:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, ao->bps);
|
2010-12-16 19:26:12 +00:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
did_retry = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bytes = 0;
|
|
|
|
}
|
|
|
|
|
2012-11-18 23:57:41 +00:00
|
|
|
if (fabs(ptsdiff) > 300 || isnan(ptsdiff)) // pts reset or just broken?
|
2010-11-21 17:25:18 +00:00
|
|
|
bytes = 0;
|
|
|
|
|
|
|
|
if (bytes > 0)
|
|
|
|
break;
|
2010-11-13 17:27:01 +00:00
|
|
|
|
|
|
|
mpctx->syncing_audio = false;
|
2010-11-21 17:25:18 +00:00
|
|
|
int a = FFMIN(-bytes, FFMAX(playsize, 20000));
|
2013-07-22 22:45:23 +00:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, a);
|
2011-07-02 06:22:32 +00:00
|
|
|
bytes += ao->buffer.len;
|
2010-11-21 17:25:18 +00:00
|
|
|
if (bytes >= 0) {
|
2011-07-02 06:22:32 +00:00
|
|
|
memmove(ao->buffer.start,
|
|
|
|
ao->buffer.start + ao->buffer.len - bytes, bytes);
|
|
|
|
ao->buffer.len = bytes;
|
2010-11-13 17:27:01 +00:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
2011-07-02 06:22:32 +00:00
|
|
|
return decode_audio(sh_audio, &ao->buffer, playsize);
|
2010-11-13 17:27:01 +00:00
|
|
|
}
|
2011-07-02 06:22:32 +00:00
|
|
|
ao->buffer.len = 0;
|
2010-11-21 17:25:18 +00:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
}
|
2011-11-06 13:41:39 +00:00
|
|
|
if (hrseek)
|
|
|
|
// Don't add silence in audio-only case even if position is too late
|
|
|
|
return 0;
|
2010-11-21 17:25:18 +00:00
|
|
|
int fillbyte = 0;
|
2011-04-09 00:03:22 +00:00
|
|
|
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
|
2010-11-21 17:25:18 +00:00
|
|
|
fillbyte = 0x80;
|
|
|
|
if (bytes >= playsize) {
|
|
|
|
/* This case could fall back to the one below with
|
|
|
|
* bytes = playsize, but then silence would keep accumulating
|
|
|
|
* in a_out_buffer if the AO accepts less data than it asks for
|
|
|
|
* in playsize. */
|
|
|
|
char *p = malloc(playsize);
|
|
|
|
memset(p, fillbyte, playsize);
|
2011-07-31 13:31:07 +00:00
|
|
|
write_to_ao(mpctx, p, playsize, 0, written_pts - bytes / bps);
|
2010-11-21 17:25:18 +00:00
|
|
|
free(p);
|
|
|
|
return ASYNC_PLAY_DONE;
|
2010-11-13 17:27:01 +00:00
|
|
|
}
|
2010-11-21 17:25:18 +00:00
|
|
|
mpctx->syncing_audio = false;
|
2011-07-02 06:22:32 +00:00
|
|
|
decode_audio_prepend_bytes(&ao->buffer, bytes, fillbyte);
|
|
|
|
return decode_audio(sh_audio, &ao->buffer, playsize);
|
2010-11-13 17:27:01 +00:00
|
|
|
}
|
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
2006-11-14 03:13:45 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-04-09 00:03:22 +00:00
|
|
|
struct ao *ao = mpctx->ao;
|
2006-11-14 03:13:45 +00:00
|
|
|
int playsize;
|
2011-08-07 00:58:12 +00:00
|
|
|
int playflags = 0;
|
2011-04-07 18:17:51 +00:00
|
|
|
bool audio_eof = false;
|
|
|
|
bool partial_fill = false;
|
2007-02-21 00:49:24 +00:00
|
|
|
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
2011-04-09 00:03:22 +00:00
|
|
|
bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK);
|
2013-04-05 21:06:22 +00:00
|
|
|
int unitsize = ao->channels.num * af_fmt2bits(ao->format) / 8;
|
2006-11-14 03:13:45 +00:00
|
|
|
|
2011-07-30 22:05:17 +00:00
|
|
|
if (mpctx->paused)
|
|
|
|
playsize = 1; // just initialize things (audio pts at least)
|
|
|
|
else
|
|
|
|
playsize = ao_get_space(ao);
|
2006-11-14 03:13:45 +00:00
|
|
|
|
2011-11-06 13:41:39 +00:00
|
|
|
// Coming here with hrseek_active still set means audio-only
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (!mpctx->sh_video || !mpctx->sync_audio_to_video)
|
2010-11-13 17:27:01 +00:00
|
|
|
mpctx->syncing_audio = false;
|
2011-11-06 13:41:39 +00:00
|
|
|
if (!opts->initial_audio_sync || !modifiable_audio_format) {
|
|
|
|
mpctx->syncing_audio = false;
|
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
}
|
2010-11-13 17:27:01 +00:00
|
|
|
|
|
|
|
int res;
|
2011-11-06 13:41:39 +00:00
|
|
|
if (mpctx->syncing_audio || mpctx->hrseek_active)
|
2010-11-13 17:27:01 +00:00
|
|
|
res = audio_start_sync(mpctx, playsize);
|
|
|
|
else
|
2011-07-02 06:22:32 +00:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, playsize);
|
2013-03-25 21:31:34 +00:00
|
|
|
|
2010-07-15 17:59:46 +00:00
|
|
|
if (res < 0) { // EOF, error or format change
|
2011-07-02 06:22:32 +00:00
|
|
|
if (res == -2) {
|
|
|
|
/* The format change isn't handled too gracefully. A more precise
|
|
|
|
* implementation would require draining buffered old-format audio
|
|
|
|
* while displaying video, then doing the output format switch.
|
|
|
|
*/
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!mpctx->opts->gapless_audio)
|
2013-09-19 12:32:09 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_AO);
|
2011-07-02 06:22:32 +00:00
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
return -1;
|
|
|
|
} else if (res == ASYNC_PLAY_DONE)
|
2011-04-07 18:17:51 +00:00
|
|
|
return 0;
|
2013-07-11 17:17:51 +00:00
|
|
|
else if (demux_stream_eof(mpctx->sh_audio->gsh))
|
2011-04-07 18:17:51 +00:00
|
|
|
audio_eof = true;
|
2010-07-15 17:59:46 +00:00
|
|
|
}
|
2012-10-31 23:44:53 +00:00
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
if (endpts != MP_NOPTS_VALUE && modifiable_audio_format) {
|
2013-03-08 01:08:02 +00:00
|
|
|
double bytes = (endpts - written_audio_pts(mpctx) + mpctx->audio_delay)
|
2011-08-07 00:58:12 +00:00
|
|
|
* ao->bps / opts->playback_speed;
|
2011-04-07 18:17:51 +00:00
|
|
|
if (playsize > bytes) {
|
|
|
|
playsize = FFMAX(bytes, 0);
|
|
|
|
playflags |= AOPLAY_FINAL_CHUNK;
|
|
|
|
audio_eof = true;
|
|
|
|
partial_fill = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-02 06:22:32 +00:00
|
|
|
assert(ao->buffer.len % unitsize == 0);
|
|
|
|
if (playsize > ao->buffer.len) {
|
2011-04-07 18:17:51 +00:00
|
|
|
partial_fill = true;
|
2011-07-02 06:22:32 +00:00
|
|
|
playsize = ao->buffer.len;
|
2009-12-29 13:51:59 +00:00
|
|
|
if (audio_eof)
|
|
|
|
playflags |= AOPLAY_FINAL_CHUNK;
|
|
|
|
}
|
2011-04-07 18:17:51 +00:00
|
|
|
playsize -= playsize % unitsize;
|
2009-12-29 13:51:59 +00:00
|
|
|
if (!playsize)
|
2011-04-07 18:17:51 +00:00
|
|
|
return partial_fill && audio_eof ? -2 : -partial_fill;
|
2006-11-14 04:17:41 +00:00
|
|
|
|
2009-12-29 13:51:59 +00:00
|
|
|
// play audio:
|
2006-11-14 03:13:45 +00:00
|
|
|
|
2011-07-31 13:31:07 +00:00
|
|
|
int played = write_to_ao(mpctx, ao->buffer.start, playsize, playflags,
|
|
|
|
written_audio_pts(mpctx));
|
2011-07-02 06:22:32 +00:00
|
|
|
assert(played % unitsize == 0);
|
|
|
|
ao->buffer_playable_size = playsize - played;
|
|
|
|
|
|
|
|
if (played > 0) {
|
|
|
|
ao->buffer.len -= played;
|
|
|
|
memmove(ao->buffer.start, ao->buffer.start + played, ao->buffer.len);
|
2011-07-30 22:05:17 +00:00
|
|
|
} else if (!mpctx->paused && audio_eof && ao_get_delay(ao) < .04) {
|
2009-12-29 13:51:59 +00:00
|
|
|
// Sanity check to avoid hanging in case current ao doesn't output
|
|
|
|
// partial chunks and doesn't check for AOPLAY_FINAL_CHUNK
|
2011-07-02 06:22:32 +00:00
|
|
|
return -2;
|
2010-07-15 17:59:46 +00:00
|
|
|
}
|
|
|
|
|
2011-04-07 18:17:51 +00:00
|
|
|
return -partial_fill;
|
2006-11-14 03:13:45 +00:00
|
|
|
}
|
2006-11-14 01:40:06 +00:00
|
|
|
|
2013-03-04 21:21:57 +00:00
|
|
|
static void update_fps(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
if (mpctx->encode_lavc_ctx && sh_video)
|
|
|
|
encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, sh_video->fps);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-05-18 09:44:17 +00:00
|
|
|
static void recreate_video_filters(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-05-18 09:44:17 +00:00
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
assert(sh_video);
|
|
|
|
|
|
|
|
vf_uninit_filter_chain(sh_video->vfilter);
|
|
|
|
|
|
|
|
char *vf_arg[] = {
|
|
|
|
"_oldargs_", (char *)mpctx->video_out, NULL
|
|
|
|
};
|
|
|
|
sh_video->vfilter = vf_open_filter(opts, NULL, "vo", vf_arg);
|
|
|
|
|
|
|
|
sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings);
|
|
|
|
|
|
|
|
struct vf_instance *vf = sh_video->vfilter;
|
|
|
|
mpctx->osd->render_subs_in_filter
|
|
|
|
= vf->control(vf, VFCTRL_INIT_OSD, NULL) == VO_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int reinit_video_filters(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
|
|
|
|
if (!sh_video)
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
recreate_video_filters(mpctx);
|
|
|
|
video_reinit_vo(sh_video);
|
|
|
|
|
|
|
|
return sh_video->vf_initialized > 0 ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2008-04-21 03:07:22 +00:00
|
|
|
int reinit_video_chain(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-19 16:01:30 +00:00
|
|
|
assert(!(mpctx->initialized_flags & INITIALIZED_VCODEC));
|
|
|
|
init_demux_stream(mpctx, STREAM_VIDEO);
|
vdpau: split off decoder parts, use "new" libavcodec vdpau hwaccel API
Move the decoder parts from vo_vdpau.c to a new file vdpau_old.c. This
file is named so because because it's written against the "old"
libavcodec vdpau pseudo-decoder (e.g. "h264_vdpau").
Add support for the "new" libavcodec vdpau support. This was recently
added and replaces the "old" vdpau parts. (In fact, Libav is about to
deprecate and remove the "old" API without deprecation grace period,
so we have to support it now. Moreover, there will probably be no Libav
release which supports both, so the transition is even less smooth than
we could hope, and we have to support both the old and new API.)
Whether the old or new API is used is checked by a configure test: if
the new API is found, it is used, otherwise the old API is assumed.
Some details might be handled differently. Especially display preemption
is a bit problematic with the "new" libavcodec vdpau support: it wants
to keep a pointer to a specific vdpau API function (which can be driver
specific, because preemption might switch drivers). Also, surface IDs
are now directly stored in AVFrames (and mp_images), so they can't be
forced to VDP_INVALID_HANDLE on preemption. (This changes even with
older libavcodec versions, because mp_image always uses the newer
representation to make vo_vdpau.c simpler.)
Decoder initialization in the new code tries to deal with codec
profiles, while the old code always uses the highest profile per codec.
Surface allocation changes. Since the decoder won't call config() in
vo_vdpau.c on video size change anymore, we allow allocating surfaces
of arbitrary size instead of locking it to what the VO was configured.
The non-hwdec code also has slightly different allocation behavior now.
Enabling the old vdpau special decoders via e.g. --vd=lavc:h264_vdpau
doesn't work anymore (a warning suggesting the --hwdec option is
printed instead).
2013-07-27 23:49:45 +00:00
|
|
|
sh_video_t *sh_video = mpctx->sh_video;
|
2013-10-03 22:24:17 +00:00
|
|
|
if (!sh_video)
|
2012-08-19 16:01:30 +00:00
|
|
|
goto no_video;
|
|
|
|
|
2013-07-12 19:58:11 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "[V] fourcc:0x%X "
|
2013-07-11 17:21:45 +00:00
|
|
|
"size:%dx%d fps:%5.3f\n",
|
2013-07-12 19:58:11 +00:00
|
|
|
mpctx->sh_video->format,
|
2013-07-07 22:39:29 +00:00
|
|
|
mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
|
2013-07-11 17:21:45 +00:00
|
|
|
mpctx->sh_video->fps);
|
|
|
|
if (opts->force_fps)
|
2013-07-07 22:39:29 +00:00
|
|
|
mpctx->sh_video->fps = opts->force_fps;
|
|
|
|
update_fps(mpctx);
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2013-07-07 22:39:29 +00:00
|
|
|
if (!mpctx->sh_video->fps && !opts->force_fps && !opts->correct_pts) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "FPS not specified in the "
|
|
|
|
"header or invalid, use the -fps option.\n");
|
2011-10-21 10:34:33 +00:00
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
double ar = -1.0;
|
2006-11-16 21:22:07 +00:00
|
|
|
//================== Init VIDEO (codec & libvo) ==========================
|
2009-03-20 01:39:29 +00:00
|
|
|
if (!opts->fixed_vo || !(mpctx->initialized_flags & INITIALIZED_VO)) {
|
2013-07-31 19:44:21 +00:00
|
|
|
mpctx->video_out = init_best_video_out(mpctx->global, mpctx->input,
|
2013-07-02 12:00:24 +00:00
|
|
|
mpctx->encode_lavc_ctx);
|
2012-09-14 15:51:26 +00:00
|
|
|
if (!mpctx->video_out) {
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
|
|
|
|
"the selected video_out (-vo) device.\n");
|
|
|
|
goto err_out;
|
|
|
|
}
|
2013-09-08 00:46:19 +00:00
|
|
|
mpctx->mouse_cursor_visible = true;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_VO;
|
|
|
|
}
|
|
|
|
|
2013-08-09 16:42:29 +00:00
|
|
|
// dynamic allocation only to make stheader.h lighter
|
|
|
|
talloc_free(sh_video->hwdec_info);
|
|
|
|
sh_video->hwdec_info = talloc_zero(sh_video, struct mp_hwdec_info);
|
|
|
|
vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, sh_video->hwdec_info);
|
|
|
|
|
2012-08-02 00:36:26 +00:00
|
|
|
vo_update_window_title(mpctx);
|
|
|
|
|
2013-07-11 17:17:51 +00:00
|
|
|
if (stream_control(mpctx->sh_video->gsh->demuxer->stream,
|
2012-08-19 16:07:06 +00:00
|
|
|
STREAM_CTRL_GET_ASPECT_RATIO, &ar) != STREAM_UNSUPPORTED)
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->sh_video->stream_aspect = ar;
|
2012-08-19 16:07:06 +00:00
|
|
|
|
2013-05-18 09:44:17 +00:00
|
|
|
recreate_video_filters(mpctx);
|
2006-11-16 21:22:07 +00:00
|
|
|
|
core: redo how codecs are mapped, remove codecs.conf
Use codec names instead of FourCCs to identify codecs. Rewrite how
codecs are selected and initialized. Now each decoder exports a list
of decoders (and the codec it supports) via add_decoders(). The order
matters, and the first decoder for a given decoder is preferred over
the other decoders. E.g. all ad_mpg123 decoders are preferred over
ad_lavc, because it comes first in the mpcodecs_ad_drivers array.
Likewise, decoders within ad_lavc that are enumerated first by
libavcodec (using av_codec_next()) are preferred. (This is actually
critical to select h264 software decoding by default instead of vdpau.
libavcodec and ffmpeg/avconv use the same method to select decoders by
default, so we hope this is sane.)
The codec names follow libavcodec's codec names as defined by
AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders
have names different from the canonical codec name. The AVCodecDescriptor
API is relatively new, so we need a compatibility layer for older
libavcodec versions for codec names that are referenced internally,
and which are different from the decoder name. (Add a configure check
for that, because checking versions is getting way too messy.)
demux/codec_tags.c is generated from the former codecs.conf (minus
"special" decoders like vdpau, and excluding the mappings that are the
same as the mappings libavformat's exported RIFF tables). It contains
all the mappings from FourCCs to codec name. This is needed for
demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the
codec as determined by libavformat, while the other demuxers have to do
this on their own, using the mp_set_audio/video_codec_from_tag()
functions. Note that the sh_audio/video->format members don't uniquely
identify the codec anymore, and sh->codec takes over this role.
Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which
provide cover the functionality of the removed switched.
Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure
container/video combinations (e.g. the sample Film_200_zygo_pro.mov)
are played flipped. ffplay/avplay doesn't handle this properly either,
so we don't care and blame ffmeg/libav instead.
2013-02-09 14:15:19 +00:00
|
|
|
init_best_video_codec(sh_video, opts->video_decoders);
|
2006-11-16 21:22:07 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
if (!sh_video->initialized)
|
2011-08-07 00:58:12 +00:00
|
|
|
goto err_out;
|
2006-11-16 21:22:07 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_VCODEC;
|
2006-11-16 21:22:07 +00:00
|
|
|
|
2013-06-13 22:24:41 +00:00
|
|
|
bool saver_state = opts->pause || !opts->stop_screensaver;
|
|
|
|
vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
|
2013-06-13 22:03:32 +00:00
|
|
|
: VOCTRL_KILL_SCREENSAVER, NULL);
|
|
|
|
|
|
|
|
vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE
|
|
|
|
: VOCTRL_RESUME, NULL);
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
sh_video->last_pts = MP_NOPTS_VALUE;
|
|
|
|
sh_video->num_buffered_pts = 0;
|
|
|
|
sh_video->next_frame_time = 0;
|
core: add --deinterlace option, restore it with resume functionality
The --deinterlace option does on playback start what the "deinterlace"
property normally does at runtime. You could do this before by using the
--vf option or by messing with the vo_vdpau default options, but this
new option is supposed to be a "foolproof" way.
The main motivation for adding this is so that the deinterlace property
can be restored when using the video resume functionality
(quit_watch_later command).
Implementation-wise, this is a bit messy. The video chain is rebuilt in
mpcodecs_reconfig_vo(), where we don't have access to MPContext, so the
usual mechanism for enabling deinterlacing can't be used. Further,
mpcodecs_reconfig_vo() is called by the video decoder, which doesn't
have access to MPContext either. Moving this call to mplayer.c isn't
currently possible either (see below). So we just do this before frames
are filtered, which potentially means setting the deinterlacing every
frame. Fortunately, setting deinterlacing is stable and idempotent, so
this is hopefully not a problem. We also add a counter that is
incremented on each reconfig to reduce the amount of additional work per
frame to nearly zero.
The reason we can't move mpcodecs_reconfig_vo() to mplayer.c is because
of hardware decoding: we need to check whether the video chain works
before we decide that we can use hardware decoding. Changing it so that
this can be decided in advance without building a filter chain sounds
like a good idea and should be done, but we aren't there yet.
2013-09-13 16:06:08 +00:00
|
|
|
mpctx->last_vf_reconfig_count = 0;
|
2012-04-23 19:18:44 +00:00
|
|
|
mpctx->restart_playback = true;
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
mpctx->sync_audio_to_video = !sh_video->gsh->attached_picture;
|
2012-04-23 19:18:44 +00:00
|
|
|
mpctx->delay = 0;
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
mpctx->vo_pts_history_seek_ts++;
|
2006-11-20 09:12:07 +00:00
|
|
|
|
2013-10-03 22:24:17 +00:00
|
|
|
vo_seek_reset(mpctx->video_out);
|
2013-06-03 22:17:51 +00:00
|
|
|
reset_subtitles(mpctx);
|
2006-11-16 21:22:07 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
return 1;
|
2006-11-19 11:21:09 +00:00
|
|
|
|
|
|
|
err_out:
|
2012-08-19 16:01:30 +00:00
|
|
|
no_video:
|
2013-10-03 22:24:17 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_VCODEC | (opts->force_vo ? 0 : INITIALIZED_VO));
|
2013-10-05 16:12:06 +00:00
|
|
|
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
2013-10-03 22:24:17 +00:00
|
|
|
handle_force_window(mpctx, true);
|
2012-08-19 16:01:30 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Video: no video\n");
|
2011-08-07 00:58:12 +00:00
|
|
|
return 0;
|
2006-11-16 21:22:07 +00:00
|
|
|
}
|
2006-11-14 09:29:03 +00:00
|
|
|
|
2013-07-16 21:14:55 +00:00
|
|
|
// Try to refresh the video by doing a precise seek to the currently displayed
|
|
|
|
// frame. This can go wrong in all sorts of ways, so use sparingly.
|
|
|
|
void mp_force_video_refresh(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-07-16 21:14:55 +00:00
|
|
|
|
2013-07-16 21:22:55 +00:00
|
|
|
// If not paused, the next frame should come soon enough.
|
2013-07-16 21:14:55 +00:00
|
|
|
if (opts->pause && mpctx->last_vo_pts != MP_NOPTS_VALUE)
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts, 1);
|
|
|
|
}
|
|
|
|
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
static bool filter_output_queued_frame(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
struct vo *video_out = mpctx->video_out;
|
|
|
|
|
|
|
|
struct mp_image *img = vf_chain_output_queued_frame(sh_video->vfilter);
|
2013-03-28 20:39:10 +00:00
|
|
|
if (img)
|
|
|
|
vo_queue_image(video_out, img);
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
talloc_free(img);
|
|
|
|
|
|
|
|
return !!img;
|
|
|
|
}
|
|
|
|
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
static bool load_next_vo_frame(struct MPContext *mpctx, bool eof)
|
|
|
|
{
|
|
|
|
if (vo_get_buffered_frame(mpctx->video_out, eof) >= 0)
|
|
|
|
return true;
|
|
|
|
if (filter_output_queued_frame(mpctx))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
core: add --deinterlace option, restore it with resume functionality
The --deinterlace option does on playback start what the "deinterlace"
property normally does at runtime. You could do this before by using the
--vf option or by messing with the vo_vdpau default options, but this
new option is supposed to be a "foolproof" way.
The main motivation for adding this is so that the deinterlace property
can be restored when using the video resume functionality
(quit_watch_later command).
Implementation-wise, this is a bit messy. The video chain is rebuilt in
mpcodecs_reconfig_vo(), where we don't have access to MPContext, so the
usual mechanism for enabling deinterlacing can't be used. Further,
mpcodecs_reconfig_vo() is called by the video decoder, which doesn't
have access to MPContext either. Moving this call to mplayer.c isn't
currently possible either (see below). So we just do this before frames
are filtered, which potentially means setting the deinterlacing every
frame. Fortunately, setting deinterlacing is stable and idempotent, so
this is hopefully not a problem. We also add a counter that is
incremented on each reconfig to reduce the amount of additional work per
frame to nearly zero.
The reason we can't move mpcodecs_reconfig_vo() to mplayer.c is because
of hardware decoding: we need to check whether the video chain works
before we decide that we can use hardware decoding. Changing it so that
this can be decided in advance without building a filter chain sounds
like a good idea and should be done, but we aren't there yet.
2013-09-13 16:06:08 +00:00
|
|
|
static void init_filter_params(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
|
|
|
|
// Note that the video decoder already initializes the filter chain. This
|
|
|
|
// might recreate the chain a second time, which is not very elegant, but
|
|
|
|
// allows us to test whether enabling deinterlacing works with the current
|
|
|
|
// video format and other filters.
|
|
|
|
if (sh_video->vf_initialized != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sh_video->vf_reconfig_count <= mpctx->last_vf_reconfig_count) {
|
|
|
|
if (opts->deinterlace >= 0) {
|
|
|
|
mp_property_do("deinterlace", M_PROPERTY_SET, &opts->deinterlace,
|
|
|
|
mpctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Setting filter params has to be "stable" (no change if params already
|
|
|
|
// set) - checking the reconfig count is just an optimization.
|
|
|
|
mpctx->last_vf_reconfig_count = sh_video->vf_reconfig_count;
|
|
|
|
}
|
|
|
|
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
static void filter_video(struct MPContext *mpctx, struct mp_image *frame)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
|
core: add --deinterlace option, restore it with resume functionality
The --deinterlace option does on playback start what the "deinterlace"
property normally does at runtime. You could do this before by using the
--vf option or by messing with the vo_vdpau default options, but this
new option is supposed to be a "foolproof" way.
The main motivation for adding this is so that the deinterlace property
can be restored when using the video resume functionality
(quit_watch_later command).
Implementation-wise, this is a bit messy. The video chain is rebuilt in
mpcodecs_reconfig_vo(), where we don't have access to MPContext, so the
usual mechanism for enabling deinterlacing can't be used. Further,
mpcodecs_reconfig_vo() is called by the video decoder, which doesn't
have access to MPContext either. Moving this call to mplayer.c isn't
currently possible either (see below). So we just do this before frames
are filtered, which potentially means setting the deinterlacing every
frame. Fortunately, setting deinterlacing is stable and idempotent, so
this is hopefully not a problem. We also add a counter that is
incremented on each reconfig to reduce the amount of additional work per
frame to nearly zero.
The reason we can't move mpcodecs_reconfig_vo() to mplayer.c is because
of hardware decoding: we need to check whether the video chain works
before we decide that we can use hardware decoding. Changing it so that
this can be decided in advance without building a filter chain sounds
like a good idea and should be done, but we aren't there yet.
2013-09-13 16:06:08 +00:00
|
|
|
init_filter_params(mpctx);
|
|
|
|
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
frame->pts = sh_video->pts;
|
2013-08-24 14:33:13 +00:00
|
|
|
mp_image_set_params(frame, sh_video->vf_input);
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
vf_filter_frame(sh_video->vfilter, frame);
|
|
|
|
filter_output_queued_frame(mpctx);
|
|
|
|
}
|
|
|
|
|
2013-07-10 00:07:26 +00:00
|
|
|
|
2013-07-11 17:10:33 +00:00
|
|
|
static struct demux_packet *video_read_frame(struct MPContext *mpctx)
|
2013-07-10 00:07:26 +00:00
|
|
|
{
|
2013-07-11 17:10:33 +00:00
|
|
|
sh_video_t *sh_video = mpctx->sh_video;
|
2013-07-11 17:17:51 +00:00
|
|
|
demuxer_t *demuxer = sh_video->gsh->demuxer;
|
|
|
|
float pts1 = sh_video->last_pts;
|
2013-07-10 00:07:26 +00:00
|
|
|
|
2013-07-11 17:10:33 +00:00
|
|
|
struct demux_packet *pkt = demux_read_packet(sh_video->gsh);
|
|
|
|
if (!pkt)
|
|
|
|
return NULL; // EOF
|
2013-07-10 00:07:26 +00:00
|
|
|
|
2013-07-11 17:17:51 +00:00
|
|
|
if (pkt->pts != MP_NOPTS_VALUE)
|
|
|
|
sh_video->last_pts = pkt->pts;
|
|
|
|
|
2013-07-11 17:21:45 +00:00
|
|
|
float frame_time = sh_video->fps > 0 ? 1.0f / sh_video->fps : 0;
|
2013-07-10 00:07:26 +00:00
|
|
|
|
|
|
|
// override frame_time for variable/unknown FPS formats:
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!mpctx->opts->force_fps) {
|
2013-07-11 17:10:33 +00:00
|
|
|
double next_pts = demux_get_next_pts(sh_video->gsh);
|
2013-07-11 17:17:51 +00:00
|
|
|
double d = next_pts == MP_NOPTS_VALUE ? sh_video->last_pts - pts1
|
|
|
|
: next_pts - sh_video->last_pts;
|
2013-07-10 00:07:47 +00:00
|
|
|
if (d >= 0) {
|
2013-07-12 19:58:11 +00:00
|
|
|
if (demuxer->type == DEMUXER_TYPE_TV) {
|
2013-07-11 17:21:45 +00:00
|
|
|
if (d > 0)
|
2013-07-10 00:07:47 +00:00
|
|
|
sh_video->fps = 1.0f / d;
|
2013-07-10 00:07:26 +00:00
|
|
|
frame_time = d;
|
|
|
|
} else {
|
2013-07-10 00:07:47 +00:00
|
|
|
if ((int)sh_video->fps <= 1)
|
2013-07-10 00:07:26 +00:00
|
|
|
frame_time = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-11 17:17:51 +00:00
|
|
|
sh_video->pts = sh_video->last_pts;
|
2013-07-10 00:07:47 +00:00
|
|
|
sh_video->next_frame_time = frame_time;
|
2013-07-11 17:10:33 +00:00
|
|
|
return pkt;
|
2013-07-10 00:07:26 +00:00
|
|
|
}
|
|
|
|
|
2009-12-02 11:01:36 +00:00
|
|
|
static double update_video_nocorrect_pts(struct MPContext *mpctx)
|
2006-11-20 09:12:07 +00:00
|
|
|
{
|
2009-01-14 00:51:24 +00:00
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
double frame_time = 0;
|
2010-12-14 23:02:14 +00:00
|
|
|
while (1) {
|
2009-01-14 00:51:24 +00:00
|
|
|
// In nocorrect-pts mode there is no way to properly time these frames
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (load_next_vo_frame(mpctx, false))
|
2009-01-14 00:51:24 +00:00
|
|
|
break;
|
|
|
|
frame_time = sh_video->next_frame_time;
|
2010-11-11 10:29:14 +00:00
|
|
|
if (mpctx->restart_playback)
|
2009-01-14 00:51:24 +00:00
|
|
|
frame_time = 0;
|
2013-07-11 17:10:33 +00:00
|
|
|
struct demux_packet *pkt = video_read_frame(mpctx);
|
|
|
|
if (!pkt)
|
2009-01-14 00:51:24 +00:00
|
|
|
return -1;
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
mpctx->delay -= frame_time;
|
|
|
|
// video_read_frame can change fps (e.g. for ASF video)
|
2013-03-04 21:21:57 +00:00
|
|
|
update_fps(mpctx);
|
2009-01-14 00:51:24 +00:00
|
|
|
int framedrop_type = check_framedrop(mpctx, frame_time);
|
|
|
|
|
2013-07-11 17:10:33 +00:00
|
|
|
void *decoded_frame = decode_video(sh_video, pkt, framedrop_type,
|
2013-03-28 19:16:11 +00:00
|
|
|
sh_video->pts);
|
2013-07-11 17:10:33 +00:00
|
|
|
talloc_free(pkt);
|
2009-01-14 00:51:24 +00:00
|
|
|
if (decoded_frame) {
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
filter_video(mpctx, decoded_frame);
|
2009-01-14 00:51:24 +00:00
|
|
|
}
|
2010-12-14 23:02:14 +00:00
|
|
|
break;
|
2006-11-20 09:12:07 +00:00
|
|
|
}
|
2009-01-14 00:51:24 +00:00
|
|
|
return frame_time;
|
|
|
|
}
|
|
|
|
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
static double update_video_attached_pic(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
|
|
|
|
// Try to decode the picture multiple times, until it is displayed.
|
|
|
|
if (mpctx->video_out->hasframe)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
struct mp_image *decoded_frame =
|
|
|
|
decode_video(sh_video, sh_video->gsh->attached_picture, 0, 0);
|
|
|
|
if (decoded_frame)
|
|
|
|
filter_video(mpctx, decoded_frame);
|
|
|
|
load_next_vo_frame(mpctx, true);
|
|
|
|
mpctx->sh_video->pts = MP_NOPTS_VALUE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-21 18:53:10 +00:00
|
|
|
static void determine_frame_pts(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2009-11-21 18:53:10 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
if (opts->user_pts_assoc_mode)
|
2009-11-21 18:53:10 +00:00
|
|
|
sh_video->pts_assoc_mode = opts->user_pts_assoc_mode;
|
2011-08-07 00:58:12 +00:00
|
|
|
else if (sh_video->pts_assoc_mode == 0) {
|
2013-07-11 17:17:51 +00:00
|
|
|
if (mpctx->sh_video->gsh->demuxer->timestamp_type == TIMESTAMP_TYPE_PTS
|
2010-11-14 09:53:27 +00:00
|
|
|
&& sh_video->codec_reordered_pts != MP_NOPTS_VALUE)
|
2009-11-21 18:53:10 +00:00
|
|
|
sh_video->pts_assoc_mode = 1;
|
|
|
|
else
|
|
|
|
sh_video->pts_assoc_mode = 2;
|
|
|
|
} else {
|
|
|
|
int probcount1 = sh_video->num_reordered_pts_problems;
|
|
|
|
int probcount2 = sh_video->num_sorted_pts_problems;
|
|
|
|
if (sh_video->pts_assoc_mode == 2) {
|
|
|
|
int tmp = probcount1;
|
|
|
|
probcount1 = probcount2;
|
|
|
|
probcount2 = tmp;
|
|
|
|
}
|
|
|
|
if (probcount1 >= probcount2 * 1.5 + 2) {
|
|
|
|
sh_video->pts_assoc_mode = 3 - sh_video->pts_assoc_mode;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Switching to pts association mode "
|
|
|
|
"%d.\n", sh_video->pts_assoc_mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sh_video->pts = sh_video->pts_assoc_mode == 1 ?
|
2011-08-07 00:58:12 +00:00
|
|
|
sh_video->codec_reordered_pts : sh_video->sorted_pts;
|
2009-11-21 18:53:10 +00:00
|
|
|
}
|
|
|
|
|
2013-04-25 12:56:01 +00:00
|
|
|
static double update_video(struct MPContext *mpctx, double endpts)
|
2009-01-14 00:51:24 +00:00
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
2009-09-18 13:27:55 +00:00
|
|
|
struct vo *video_out = mpctx->video_out;
|
2009-01-14 00:51:24 +00:00
|
|
|
sh_video->vfilter->control(sh_video->vfilter, VFCTRL_SET_OSD_OBJ,
|
2012-10-21 16:31:34 +00:00
|
|
|
mpctx->osd); // for vf_sub
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!mpctx->opts->correct_pts)
|
2009-12-02 11:01:36 +00:00
|
|
|
return update_video_nocorrect_pts(mpctx);
|
2009-01-14 22:32:03 +00:00
|
|
|
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (sh_video->gsh->attached_picture)
|
|
|
|
return update_video_attached_pic(mpctx);
|
|
|
|
|
2009-01-14 22:32:03 +00:00
|
|
|
double pts;
|
|
|
|
|
2010-12-14 23:02:14 +00:00
|
|
|
while (1) {
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (load_next_vo_frame(mpctx, false))
|
2010-12-14 23:02:14 +00:00
|
|
|
break;
|
2011-08-20 17:25:43 +00:00
|
|
|
pts = MP_NOPTS_VALUE;
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
struct demux_packet *pkt = NULL;
|
2012-02-03 18:09:48 +00:00
|
|
|
while (1) {
|
2013-07-11 17:10:33 +00:00
|
|
|
pkt = demux_read_packet(mpctx->sh_video->gsh);
|
2012-02-03 18:09:48 +00:00
|
|
|
if (!pkt || pkt->len)
|
|
|
|
break;
|
|
|
|
/* Packets with size 0 are assumed to not correspond to frames,
|
|
|
|
* but to indicate the absence of a frame in formats like AVI
|
|
|
|
* that must have packets at fixed timecode intervals. */
|
2013-07-11 17:10:33 +00:00
|
|
|
talloc_free(pkt);
|
2012-02-03 18:09:48 +00:00
|
|
|
}
|
2013-03-28 19:16:11 +00:00
|
|
|
if (pkt)
|
2011-08-20 17:25:43 +00:00
|
|
|
pts = pkt->pts;
|
2009-03-29 19:45:06 +00:00
|
|
|
if (pts != MP_NOPTS_VALUE)
|
|
|
|
pts += mpctx->video_offset;
|
2010-12-14 23:09:47 +00:00
|
|
|
if (pts >= mpctx->hrseek_pts - .005)
|
|
|
|
mpctx->hrseek_framedrop = false;
|
2013-04-24 13:26:54 +00:00
|
|
|
int framedrop_type = mpctx->hrseek_active && mpctx->hrseek_framedrop ?
|
2013-07-11 17:21:45 +00:00
|
|
|
1 : check_framedrop(mpctx, -1);
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
struct mp_image *decoded_frame =
|
2013-03-28 19:16:11 +00:00
|
|
|
decode_video(sh_video, pkt, framedrop_type, pts);
|
2013-07-11 17:10:33 +00:00
|
|
|
talloc_free(pkt);
|
2009-01-14 22:32:03 +00:00
|
|
|
if (decoded_frame) {
|
2009-11-21 18:53:10 +00:00
|
|
|
determine_frame_pts(mpctx);
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
filter_video(mpctx, decoded_frame);
|
2011-08-20 17:25:43 +00:00
|
|
|
} else if (!pkt) {
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (!load_next_vo_frame(mpctx, true))
|
2010-12-14 23:02:14 +00:00
|
|
|
return -1;
|
2009-09-18 13:27:55 +00:00
|
|
|
}
|
2010-12-14 23:02:14 +00:00
|
|
|
break;
|
2009-01-14 22:32:03 +00:00
|
|
|
}
|
|
|
|
|
2010-12-14 23:02:14 +00:00
|
|
|
if (!video_out->frame_loaded)
|
|
|
|
return 0;
|
|
|
|
|
2009-09-18 13:27:55 +00:00
|
|
|
pts = video_out->next_pts;
|
2009-01-14 22:32:03 +00:00
|
|
|
if (pts == MP_NOPTS_VALUE) {
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Video pts after filters MISSING\n");
|
|
|
|
// Try to use decoder pts from before filters
|
|
|
|
pts = sh_video->pts;
|
2009-04-24 00:39:54 +00:00
|
|
|
if (pts == MP_NOPTS_VALUE)
|
|
|
|
pts = sh_video->last_pts;
|
2006-11-20 09:12:07 +00:00
|
|
|
}
|
2013-04-25 15:52:34 +00:00
|
|
|
if (endpts == MP_NOPTS_VALUE || pts < endpts)
|
2013-04-25 12:56:01 +00:00
|
|
|
add_frame_pts(mpctx, pts);
|
2011-03-03 10:54:36 +00:00
|
|
|
if (mpctx->hrseek_active && pts < mpctx->hrseek_pts - .005) {
|
|
|
|
vo_skip_frame(video_out);
|
2010-12-14 23:09:47 +00:00
|
|
|
return 0;
|
2011-03-03 10:54:36 +00:00
|
|
|
}
|
2010-12-14 23:09:47 +00:00
|
|
|
mpctx->hrseek_active = false;
|
2009-01-14 22:32:03 +00:00
|
|
|
sh_video->pts = pts;
|
|
|
|
if (sh_video->last_pts == MP_NOPTS_VALUE)
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
2009-02-03 22:28:17 +00:00
|
|
|
else if (sh_video->last_pts > sh_video->pts) {
|
2013-06-12 15:48:27 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Decreasing video pts: %f < %f\n",
|
2009-01-14 22:32:03 +00:00
|
|
|
sh_video->pts, sh_video->last_pts);
|
|
|
|
/* If the difference in pts is small treat it as jitter around the
|
|
|
|
* right value (possibly caused by incorrect timestamp ordering) and
|
|
|
|
* just show this frame immediately after the last one.
|
|
|
|
* Treat bigger differences as timestamp resets and start counting
|
|
|
|
* timing of later frames from the position of this one. */
|
|
|
|
if (sh_video->last_pts - sh_video->pts > 0.5)
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
|
|
|
else
|
|
|
|
sh_video->pts = sh_video->last_pts;
|
2013-06-12 15:48:27 +00:00
|
|
|
} else if (sh_video->pts >= sh_video->last_pts + 60) {
|
|
|
|
// Assume a PTS difference >= 60 seconds is a discontinuity.
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Jump in video pts: %f -> %f\n",
|
|
|
|
sh_video->last_pts, sh_video->pts);
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
2009-01-14 22:32:03 +00:00
|
|
|
}
|
|
|
|
double frame_time = sh_video->pts - sh_video->last_pts;
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
mpctx->delay -= frame_time;
|
2006-11-20 09:12:07 +00:00
|
|
|
return frame_time;
|
|
|
|
}
|
|
|
|
|
2008-11-29 06:09:57 +00:00
|
|
|
void pause_player(struct MPContext *mpctx)
|
|
|
|
{
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify_property(mpctx, "pause");
|
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->pause = 1;
|
2013-04-25 17:07:47 +00:00
|
|
|
|
2013-06-13 22:03:32 +00:00
|
|
|
if (mpctx->video_out)
|
|
|
|
vo_control(mpctx->video_out, VOCTRL_RESTORE_SCREENSAVER, NULL);
|
|
|
|
|
2008-11-29 06:09:57 +00:00
|
|
|
if (mpctx->paused)
|
|
|
|
return;
|
2013-04-25 18:38:22 +00:00
|
|
|
mpctx->paused = true;
|
2008-11-29 06:09:57 +00:00
|
|
|
mpctx->step_frames = 0;
|
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
2012-11-20 16:20:45 +00:00
|
|
|
mpctx->osd_function = 0;
|
2013-04-25 17:07:47 +00:00
|
|
|
mpctx->paused_for_cache = false;
|
2008-11-29 06:09:57 +00:00
|
|
|
|
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
|
2011-08-07 00:58:12 +00:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
|
2008-11-29 06:09:57 +00:00
|
|
|
|
2011-04-09 00:03:22 +00:00
|
|
|
if (mpctx->ao && mpctx->sh_audio)
|
2011-08-07 00:58:12 +00:00
|
|
|
ao_pause(mpctx->ao); // pause audio, keep data if possible
|
2012-01-06 18:32:03 +00:00
|
|
|
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
// Only print status if there's actually a file being played.
|
2012-08-19 16:07:06 +00:00
|
|
|
if (mpctx->num_sources)
|
2012-11-15 19:51:34 +00:00
|
|
|
print_status(mpctx);
|
2012-01-06 18:32:03 +00:00
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!mpctx->opts->quiet)
|
2012-01-06 18:32:03 +00:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_PAUSED\n");
|
2008-11-29 06:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void unpause_player(struct MPContext *mpctx)
|
|
|
|
{
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_notify_property(mpctx, "pause");
|
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->pause = 0;
|
2013-04-25 17:07:47 +00:00
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->video_out && mpctx->opts->stop_screensaver)
|
2013-06-13 22:03:32 +00:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_KILL_SCREENSAVER, NULL);
|
|
|
|
|
2008-11-29 06:09:57 +00:00
|
|
|
if (!mpctx->paused)
|
|
|
|
return;
|
2013-04-25 17:07:47 +00:00
|
|
|
// Don't actually unpause while cache is loading.
|
|
|
|
if (mpctx->paused_for_cache)
|
|
|
|
return;
|
2013-04-25 18:38:22 +00:00
|
|
|
mpctx->paused = false;
|
2012-11-20 16:20:45 +00:00
|
|
|
mpctx->osd_function = 0;
|
2008-11-29 06:09:57 +00:00
|
|
|
|
2011-04-09 00:03:22 +00:00
|
|
|
if (mpctx->ao && mpctx->sh_audio)
|
|
|
|
ao_resume(mpctx->ao);
|
2013-06-13 22:03:32 +00:00
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
|
2011-08-07 00:58:12 +00:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); // resume video
|
|
|
|
(void)get_relative_time(mpctx); // ignore time that passed during pause
|
2008-11-29 06:09:57 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 12:58:46 +00:00
|
|
|
static void draw_osd(struct MPContext *mpctx)
|
2011-12-05 03:24:18 +00:00
|
|
|
{
|
2012-10-21 12:58:46 +00:00
|
|
|
struct vo *vo = mpctx->video_out;
|
|
|
|
|
|
|
|
mpctx->osd->vo_pts = mpctx->video_pts;
|
|
|
|
vo_draw_osd(vo, mpctx->osd);
|
|
|
|
}
|
|
|
|
|
2012-10-21 13:54:22 +00:00
|
|
|
static bool redraw_osd(struct MPContext *mpctx)
|
2011-12-05 03:24:18 +00:00
|
|
|
{
|
VO, sub: refactor
Remove VFCTRL_DRAW_OSD, VFCAP_EOSD_FILTER, VFCAP_EOSD_RGBA, VFCAP_EOSD,
VOCTRL_DRAW_EOSD, VOCTRL_GET_EOSD_RES, VOCTRL_QUERY_EOSD_FORMAT.
Remove draw_osd_with_eosd(), which rendered the OSD by calling
VOCTRL_DRAW_EOSD. Change VOs to call osd_draw() directly, which takes
a callback as argument. (This basically works like the old OSD API,
except multiple OSD bitmap formats are supported and caching is
possible.)
Remove all mentions of "eosd". It's simply "osd" now.
Make OSD size per-OSD-object, as they can be different when using
vf_sub. Include display_par/video_par in resolution change detection.
Fix the issue with margin borders in vo_corevideo.
2012-10-19 17:25:18 +00:00
|
|
|
struct vo *vo = mpctx->video_out;
|
|
|
|
if (vo_redraw_frame(vo) < 0)
|
2012-10-21 13:54:22 +00:00
|
|
|
return false;
|
2012-08-25 18:22:39 +00:00
|
|
|
|
2012-10-21 12:58:46 +00:00
|
|
|
draw_osd(mpctx);
|
VO, sub: refactor
Remove VFCTRL_DRAW_OSD, VFCAP_EOSD_FILTER, VFCAP_EOSD_RGBA, VFCAP_EOSD,
VOCTRL_DRAW_EOSD, VOCTRL_GET_EOSD_RES, VOCTRL_QUERY_EOSD_FORMAT.
Remove draw_osd_with_eosd(), which rendered the OSD by calling
VOCTRL_DRAW_EOSD. Change VOs to call osd_draw() directly, which takes
a callback as argument. (This basically works like the old OSD API,
except multiple OSD bitmap formats are supported and caching is
possible.)
Remove all mentions of "eosd". It's simply "osd" now.
Make OSD size per-OSD-object, as they can be different when using
vf_sub. Include display_par/video_par in resolution change detection.
Fix the issue with margin borders in vo_corevideo.
2012-10-19 17:25:18 +00:00
|
|
|
|
|
|
|
vo_flip_page(vo, 0, -1);
|
2012-10-21 13:54:22 +00:00
|
|
|
return true;
|
2011-12-05 03:24:18 +00:00
|
|
|
}
|
|
|
|
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
void add_step_frame(struct MPContext *mpctx, int dir)
|
2008-12-01 17:53:57 +00:00
|
|
|
{
|
2013-10-02 18:38:10 +00:00
|
|
|
if (!mpctx->sh_video)
|
|
|
|
return;
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
if (dir > 0) {
|
|
|
|
mpctx->step_frames += 1;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 17:53:57 +00:00
|
|
|
}
|
|
|
|
|
2012-03-16 06:04:24 +00:00
|
|
|
static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
|
2010-04-24 15:50:19 +00:00
|
|
|
{
|
|
|
|
if (mpctx->sh_video) {
|
2011-08-07 00:58:12 +00:00
|
|
|
resync_video_stream(mpctx->sh_video);
|
2010-04-24 15:50:19 +00:00
|
|
|
vo_seek_reset(mpctx->video_out);
|
2013-01-20 02:03:40 +00:00
|
|
|
if (mpctx->sh_video->vf_initialized == 1)
|
|
|
|
vf_chain_seek_reset(mpctx->sh_video->vfilter);
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->sh_video->num_buffered_pts = 0;
|
|
|
|
mpctx->sh_video->last_pts = MP_NOPTS_VALUE;
|
2013-07-11 17:13:22 +00:00
|
|
|
mpctx->sh_video->pts = MP_NOPTS_VALUE;
|
|
|
|
mpctx->video_pts = MP_NOPTS_VALUE;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->delay = 0;
|
2010-04-24 15:50:19 +00:00
|
|
|
mpctx->time_frame = 0;
|
|
|
|
}
|
|
|
|
|
2012-03-16 06:04:24 +00:00
|
|
|
if (mpctx->sh_audio && reset_ac) {
|
2010-04-24 15:50:19 +00:00
|
|
|
resync_audio_stream(mpctx->sh_audio);
|
2011-04-08 02:15:43 +00:00
|
|
|
if (reset_ao)
|
2011-04-09 00:03:22 +00:00
|
|
|
ao_reset(mpctx->ao);
|
2011-07-02 06:22:32 +00:00
|
|
|
mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->sh_audio->a_buffer_len = 0;
|
2010-04-24 15:50:19 +00:00
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
reset_subtitles(mpctx);
|
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
mpctx->restart_playback = true;
|
2010-12-14 23:09:47 +00:00
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
mpctx->hrseek_framedrop = false;
|
2010-04-24 15:50:19 +00:00
|
|
|
mpctx->total_avsync_change = 0;
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->drop_frame_cnt = 0;
|
|
|
|
mpctx->dropped_frames = 0;
|
2013-04-03 23:18:19 +00:00
|
|
|
mpctx->playback_pts = MP_NOPTS_VALUE;
|
2012-09-14 15:51:26 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
|
|
|
#endif
|
2010-04-24 15:50:19 +00:00
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
static bool timeline_set_part(struct MPContext *mpctx, int i, bool force)
|
2009-03-29 19:45:06 +00:00
|
|
|
{
|
|
|
|
struct timeline_part *p = mpctx->timeline + mpctx->timeline_part;
|
|
|
|
struct timeline_part *n = mpctx->timeline + i;
|
|
|
|
mpctx->timeline_part = i;
|
|
|
|
mpctx->video_offset = n->start - n->source_start;
|
2012-08-19 16:01:30 +00:00
|
|
|
if (n->source == p->source && !force)
|
2009-03-29 19:45:06 +00:00
|
|
|
return false;
|
2011-04-08 02:15:43 +00:00
|
|
|
enum stop_play_reason orig_stop_play = mpctx->stop_play;
|
|
|
|
if (!mpctx->sh_video && mpctx->stop_play == KEEP_PLAYING)
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE; // let audio uninit drain data
|
2013-09-19 12:32:09 +00:00
|
|
|
uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB);
|
2011-04-08 02:15:43 +00:00
|
|
|
mpctx->stop_play = orig_stop_play;
|
2012-08-19 16:01:30 +00:00
|
|
|
|
2012-08-19 15:58:58 +00:00
|
|
|
mpctx->demuxer = n->source;
|
|
|
|
mpctx->stream = mpctx->demuxer->stream;
|
2012-08-19 16:01:30 +00:00
|
|
|
|
|
|
|
// While another timeline was active, the selection of active tracks might
|
|
|
|
// have been changed - possibly we need to update this source.
|
2013-07-22 22:45:23 +00:00
|
|
|
for (int x = 0; x < mpctx->num_tracks; x++) {
|
|
|
|
struct track *track = mpctx->tracks[x];
|
2012-08-19 16:01:30 +00:00
|
|
|
if (track->under_timeline) {
|
|
|
|
track->demuxer = mpctx->demuxer;
|
|
|
|
track->stream = demuxer_stream_by_demuxer_id(track->demuxer,
|
|
|
|
track->type,
|
|
|
|
track->demuxer_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
preselect_demux_streams(mpctx);
|
|
|
|
|
2009-03-29 19:45:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given pts, switch playback to the corresponding part.
|
|
|
|
// Return offset within that part.
|
|
|
|
static double timeline_set_from_time(struct MPContext *mpctx, double pts,
|
|
|
|
bool *need_reset)
|
|
|
|
{
|
|
|
|
if (pts < 0)
|
|
|
|
pts = 0;
|
|
|
|
for (int i = 0; i < mpctx->num_timeline_parts; i++) {
|
|
|
|
struct timeline_part *p = mpctx->timeline + i;
|
2011-08-07 00:58:12 +00:00
|
|
|
if (pts < (p + 1)->start) {
|
2012-08-19 16:01:30 +00:00
|
|
|
*need_reset = timeline_set_part(mpctx, i, false);
|
2009-03-29 19:45:06 +00:00
|
|
|
return pts - p->start + p->source_start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-02-22 21:05:31 +00:00
|
|
|
|
2007-02-23 01:49:51 +00:00
|
|
|
// return -1 if seek failed (non-seekable stream?), 0 otherwise
|
2011-04-08 02:15:43 +00:00
|
|
|
static int seek(MPContext *mpctx, struct seek_params seek,
|
|
|
|
bool timeline_fallthrough)
|
2007-02-23 01:49:51 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-04-25 12:56:01 +00:00
|
|
|
uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts;
|
2010-12-14 23:09:47 +00:00
|
|
|
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (!mpctx->demuxer)
|
|
|
|
return -1;
|
|
|
|
|
2009-03-29 19:45:06 +00:00
|
|
|
if (mpctx->stop_play == AT_END_OF_FILE)
|
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
2010-12-14 23:09:47 +00:00
|
|
|
bool hr_seek = mpctx->demuxer->accurate_seek && opts->correct_pts;
|
|
|
|
hr_seek &= seek.exact >= 0 && seek.type != MPSEEK_FACTOR;
|
2013-03-25 21:31:34 +00:00
|
|
|
hr_seek &= (opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) ||
|
|
|
|
opts->hr_seek > 0 || seek.exact > 0;
|
|
|
|
if (seek.type == MPSEEK_FACTOR || seek.amount < 0 ||
|
|
|
|
(seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts))
|
2011-08-04 20:14:44 +00:00
|
|
|
mpctx->last_chapter_seek = -2;
|
2013-03-01 12:20:33 +00:00
|
|
|
if (seek.type == MPSEEK_FACTOR) {
|
|
|
|
double len = get_time_length(mpctx);
|
|
|
|
if (len > 0 && !mpctx->demuxer->ts_resets_possible) {
|
|
|
|
seek.amount = seek.amount * len + get_start_time(mpctx);
|
|
|
|
seek.type = MPSEEK_ABSOLUTE;
|
|
|
|
}
|
2009-03-29 19:45:06 +00:00
|
|
|
}
|
2010-12-14 20:33:55 +00:00
|
|
|
if ((mpctx->demuxer->accurate_seek || mpctx->timeline)
|
2010-12-18 08:13:45 +00:00
|
|
|
&& seek.type == MPSEEK_RELATIVE) {
|
|
|
|
seek.type = MPSEEK_ABSOLUTE;
|
|
|
|
seek.direction = seek.amount > 0 ? 1 : -1;
|
|
|
|
seek.amount += get_current_time(mpctx);
|
2009-03-19 03:25:12 +00:00
|
|
|
}
|
|
|
|
|
2009-03-29 19:45:06 +00:00
|
|
|
/* At least the liba52 decoder wants to read from the input stream
|
|
|
|
* during initialization, so reinit must be done after the demux_seek()
|
|
|
|
* call that clears possible stream EOF. */
|
|
|
|
bool need_reset = false;
|
2010-12-18 08:13:45 +00:00
|
|
|
double demuxer_amount = seek.amount;
|
2009-03-29 19:45:06 +00:00
|
|
|
if (mpctx->timeline) {
|
2010-12-18 08:13:45 +00:00
|
|
|
demuxer_amount = timeline_set_from_time(mpctx, seek.amount,
|
|
|
|
&need_reset);
|
2010-12-14 23:02:14 +00:00
|
|
|
if (demuxer_amount == -1) {
|
2012-08-19 16:01:30 +00:00
|
|
|
assert(!need_reset);
|
2009-03-29 19:45:06 +00:00
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
|
|
|
// Clear audio from current position
|
2012-03-16 06:04:24 +00:00
|
|
|
if (mpctx->sh_audio && !timeline_fallthrough) {
|
2011-04-09 00:03:22 +00:00
|
|
|
ao_reset(mpctx->ao);
|
2009-03-29 19:45:06 +00:00
|
|
|
mpctx->sh_audio->a_buffer_len = 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2012-03-16 06:04:24 +00:00
|
|
|
if (need_reset) {
|
|
|
|
reinit_video_chain(mpctx);
|
2012-08-19 16:01:30 +00:00
|
|
|
reinit_subs(mpctx);
|
2012-03-16 06:04:24 +00:00
|
|
|
}
|
|
|
|
|
2010-12-18 08:13:45 +00:00
|
|
|
int demuxer_style = 0;
|
|
|
|
switch (seek.type) {
|
|
|
|
case MPSEEK_FACTOR:
|
2013-03-01 12:20:33 +00:00
|
|
|
demuxer_style |= SEEK_ABSOLUTE | SEEK_FACTOR;
|
|
|
|
break;
|
2010-12-18 08:13:45 +00:00
|
|
|
case MPSEEK_ABSOLUTE:
|
|
|
|
demuxer_style |= SEEK_ABSOLUTE;
|
2013-03-01 12:20:33 +00:00
|
|
|
break;
|
2010-12-18 08:13:45 +00:00
|
|
|
}
|
2010-12-14 23:09:47 +00:00
|
|
|
if (hr_seek || seek.direction < 0)
|
2010-12-18 08:13:45 +00:00
|
|
|
demuxer_style |= SEEK_BACKWARD;
|
|
|
|
else if (seek.direction > 0)
|
|
|
|
demuxer_style |= SEEK_FORWARD;
|
2013-04-03 23:43:14 +00:00
|
|
|
if (hr_seek || opts->mkv_subtitle_preroll)
|
|
|
|
demuxer_style |= SEEK_SUBPREROLL;
|
2010-12-18 08:13:45 +00:00
|
|
|
|
2011-11-06 14:54:57 +00:00
|
|
|
if (hr_seek)
|
|
|
|
demuxer_amount -= opts->hr_seek_demuxer_offset;
|
2013-08-22 17:13:29 +00:00
|
|
|
int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, demuxer_style);
|
2012-03-16 06:04:24 +00:00
|
|
|
if (seekresult == 0) {
|
|
|
|
if (need_reset) {
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
seek_reset(mpctx, !timeline_fallthrough, false);
|
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
return -1;
|
2012-03-16 06:04:24 +00:00
|
|
|
}
|
2007-02-23 01:49:51 +00:00
|
|
|
|
2012-08-19 16:11:53 +00:00
|
|
|
// If audio or demuxer subs come from different files, seek them too:
|
|
|
|
bool have_external_tracks = false;
|
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
struct track *track = mpctx->current_track[type];
|
|
|
|
have_external_tracks |= track && track->is_external && track->demuxer;
|
|
|
|
}
|
|
|
|
if (have_external_tracks) {
|
2012-12-08 12:12:46 +00:00
|
|
|
double main_new_pos;
|
|
|
|
if (seek.type == MPSEEK_ABSOLUTE) {
|
2012-08-19 16:11:53 +00:00
|
|
|
main_new_pos = seek.amount - mpctx->video_offset;
|
2012-12-08 12:12:46 +00:00
|
|
|
} else {
|
|
|
|
main_new_pos = get_main_demux_pts(mpctx);
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
|
|
|
|
struct track *track = mpctx->current_track[type];
|
|
|
|
if (track && track->is_external && track->demuxer)
|
2013-08-22 17:13:29 +00:00
|
|
|
demux_seek(track->demuxer, main_new_pos, SEEK_ABSOLUTE);
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-16 06:04:24 +00:00
|
|
|
if (need_reset)
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
/* If we just reinitialized audio it doesn't need to be reset,
|
|
|
|
* and resetting could lose audio some decoders produce during init. */
|
|
|
|
seek_reset(mpctx, !timeline_fallthrough, !need_reset);
|
2010-12-14 23:02:14 +00:00
|
|
|
|
2013-04-25 12:56:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-12-14 23:02:14 +00:00
|
|
|
/* Use the target time as "current position" for further relative
|
|
|
|
* seeks etc until a new video frame has been decoded */
|
2011-07-30 22:05:17 +00:00
|
|
|
if (seek.type == MPSEEK_ABSOLUTE) {
|
2010-12-18 08:13:45 +00:00
|
|
|
mpctx->video_pts = seek.amount;
|
2011-07-30 22:05:17 +00:00
|
|
|
mpctx->last_seek_pts = seek.amount;
|
|
|
|
} else
|
|
|
|
mpctx->last_seek_pts = MP_NOPTS_VALUE;
|
2010-12-14 23:02:14 +00:00
|
|
|
|
2013-02-13 12:22:12 +00:00
|
|
|
// The hr_seek==false case is for skipping frames with PTS before the
|
|
|
|
// current timeline chapter start. It's not really known where the demuxer
|
|
|
|
// level seek will end up, so the hrseek mechanism is abused to skip all
|
|
|
|
// frames before chapter start by setting hrseek_pts to the chapter start.
|
|
|
|
// It does nothing when the seek is inside of the current chapter, and
|
|
|
|
// seeking past the chapter is handled elsewhere.
|
2012-12-15 14:27:09 +00:00
|
|
|
if (hr_seek || mpctx->timeline) {
|
2010-12-14 23:09:47 +00:00
|
|
|
mpctx->hrseek_active = true;
|
|
|
|
mpctx->hrseek_framedrop = true;
|
2012-12-15 14:27:09 +00:00
|
|
|
mpctx->hrseek_pts = hr_seek ? seek.amount
|
|
|
|
: mpctx->timeline[mpctx->timeline_part].start;
|
2010-12-14 23:09:47 +00:00
|
|
|
}
|
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
mpctx->start_timestamp = mp_time_sec();
|
2010-12-14 23:02:14 +00:00
|
|
|
|
2007-02-23 01:49:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-18 08:13:45 +00:00
|
|
|
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
|
|
|
|
int exact)
|
|
|
|
{
|
|
|
|
struct seek_params *seek = &mpctx->seek;
|
|
|
|
switch (type) {
|
|
|
|
case MPSEEK_RELATIVE:
|
|
|
|
if (seek->type == MPSEEK_FACTOR)
|
2011-08-07 00:58:12 +00:00
|
|
|
return; // Well... not common enough to bother doing better
|
2010-12-18 08:13:45 +00:00
|
|
|
seek->amount += amount;
|
|
|
|
seek->exact = FFMAX(seek->exact, exact);
|
|
|
|
if (seek->type == MPSEEK_NONE)
|
|
|
|
seek->exact = exact;
|
|
|
|
if (seek->type == MPSEEK_ABSOLUTE)
|
|
|
|
return;
|
|
|
|
if (seek->amount == 0) {
|
2011-08-07 00:58:12 +00:00
|
|
|
*seek = (struct seek_params){ 0 };
|
2010-12-18 08:13:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
seek->type = MPSEEK_RELATIVE;
|
|
|
|
return;
|
|
|
|
case MPSEEK_ABSOLUTE:
|
|
|
|
case MPSEEK_FACTOR:
|
|
|
|
*seek = (struct seek_params) {
|
|
|
|
.type = type,
|
|
|
|
.amount = amount,
|
|
|
|
.exact = exact,
|
|
|
|
};
|
|
|
|
return;
|
|
|
|
case MPSEEK_NONE:
|
2011-08-07 00:58:12 +00:00
|
|
|
*seek = (struct seek_params){ 0 };
|
2010-12-18 08:13:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2013-05-03 22:36:53 +00:00
|
|
|
static void execute_queued_seek(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->seek.type) {
|
|
|
|
seek(mpctx, mpctx->seek, false);
|
|
|
|
mpctx->seek = (struct seek_params){0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-07 22:54:32 +00:00
|
|
|
double get_time_length(struct MPContext *mpctx)
|
|
|
|
{
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
if (!demuxer)
|
|
|
|
return 0;
|
|
|
|
|
2010-11-07 22:54:32 +00:00
|
|
|
if (mpctx->timeline)
|
|
|
|
return mpctx->timeline[mpctx->num_timeline_parts].start;
|
|
|
|
|
2013-05-03 18:07:04 +00:00
|
|
|
double len = demuxer_get_time_length(demuxer);
|
|
|
|
if (len >= 0)
|
|
|
|
return len;
|
2010-11-07 22:54:32 +00:00
|
|
|
|
2013-07-12 21:11:04 +00:00
|
|
|
// Unknown
|
2010-11-07 22:54:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there are timestamps from stream level then use those (for example
|
|
|
|
* DVDs can have consistent times there while the MPEG-level timestamps
|
|
|
|
* reset). */
|
|
|
|
double get_current_time(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (!demuxer)
|
|
|
|
return 0;
|
2010-11-07 22:54:32 +00:00
|
|
|
if (demuxer->stream_pts != MP_NOPTS_VALUE)
|
|
|
|
return demuxer->stream_pts;
|
2013-04-03 23:18:19 +00:00
|
|
|
if (mpctx->playback_pts != MP_NOPTS_VALUE)
|
|
|
|
return mpctx->playback_pts;
|
2012-10-20 22:22:04 +00:00
|
|
|
if (mpctx->last_seek_pts != MP_NOPTS_VALUE)
|
|
|
|
return mpctx->last_seek_pts;
|
|
|
|
return 0;
|
2010-11-07 22:54:32 +00:00
|
|
|
}
|
|
|
|
|
2013-03-01 12:20:33 +00:00
|
|
|
double get_start_time(struct MPContext *mpctx)
|
2013-02-25 23:38:36 +00:00
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
if (!demuxer)
|
|
|
|
return 0;
|
2013-05-03 18:07:04 +00:00
|
|
|
return demuxer_get_start_time(demuxer);
|
2013-02-25 23:38:36 +00:00
|
|
|
}
|
|
|
|
|
2013-02-26 00:31:35 +00:00
|
|
|
// Return playback position in 0.0-1.0 ratio, or -1 if unknown.
|
2013-06-20 10:15:36 +00:00
|
|
|
double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
|
2010-11-07 22:54:32 +00:00
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (!demuxer)
|
2013-02-26 00:31:35 +00:00
|
|
|
return -1;
|
|
|
|
double ans = -1;
|
2013-02-25 23:38:36 +00:00
|
|
|
double start = get_start_time(mpctx);
|
|
|
|
double len = get_time_length(mpctx);
|
2013-06-20 10:15:36 +00:00
|
|
|
if (use_range) {
|
2013-07-27 19:24:54 +00:00
|
|
|
double startpos = rel_time_to_abs(mpctx, mpctx->opts->play_start,
|
2013-06-20 10:15:36 +00:00
|
|
|
MP_NOPTS_VALUE);
|
|
|
|
double endpos = get_play_end_pts(mpctx);
|
|
|
|
if (endpos == MP_NOPTS_VALUE || endpos > start + len)
|
|
|
|
endpos = start + len;
|
|
|
|
if (startpos == MP_NOPTS_VALUE || startpos < start)
|
|
|
|
startpos = start;
|
|
|
|
if (endpos < startpos)
|
|
|
|
endpos = startpos;
|
|
|
|
start = startpos;
|
|
|
|
len = endpos - startpos;
|
|
|
|
}
|
2013-02-25 23:38:36 +00:00
|
|
|
double pos = get_current_time(mpctx);
|
2013-03-01 12:20:33 +00:00
|
|
|
if (len > 0 && !demuxer->ts_resets_possible) {
|
2013-02-26 00:31:35 +00:00
|
|
|
ans = av_clipf((pos - start) / len, 0, 1);
|
2013-02-25 23:38:36 +00:00
|
|
|
} else {
|
2013-07-22 22:45:23 +00:00
|
|
|
int64_t size = (demuxer->movi_end - demuxer->movi_start);
|
|
|
|
int64_t fpos = demuxer->filepos > 0 ?
|
|
|
|
demuxer->filepos : stream_tell(demuxer->stream);
|
|
|
|
if (size > 0)
|
|
|
|
ans = av_clipf((double)(fpos - demuxer->movi_start) / size, 0, 1);
|
2010-11-07 22:54:32 +00:00
|
|
|
}
|
2013-06-20 10:15:36 +00:00
|
|
|
if (use_range) {
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->play_frames > 0)
|
2013-06-20 10:15:36 +00:00
|
|
|
ans = max(ans, 1.0 -
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->max_frames / (double) mpctx->opts->play_frames);
|
2013-06-20 10:15:36 +00:00
|
|
|
}
|
2010-11-07 22:54:32 +00:00
|
|
|
return ans;
|
|
|
|
}
|
|
|
|
|
2013-02-26 00:31:35 +00:00
|
|
|
int get_percent_pos(struct MPContext *mpctx)
|
|
|
|
{
|
2013-06-20 10:15:36 +00:00
|
|
|
return av_clip(get_current_pos_ratio(mpctx, false) * 100, 0, 100);
|
2013-02-26 00:31:35 +00:00
|
|
|
}
|
|
|
|
|
2010-04-24 17:46:54 +00:00
|
|
|
// -2 is no chapters, -1 is before first chapter
|
2009-04-02 02:00:22 +00:00
|
|
|
int get_current_chapter(struct MPContext *mpctx)
|
|
|
|
{
|
2010-11-13 13:48:26 +00:00
|
|
|
double current_pts = get_current_time(mpctx);
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (mpctx->chapters) {
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < mpctx->num_chapters; i++)
|
|
|
|
if (current_pts < mpctx->chapters[i].start)
|
|
|
|
break;
|
|
|
|
return FFMAX(mpctx->last_chapter_seek, i - 1);
|
|
|
|
}
|
2012-08-19 16:07:06 +00:00
|
|
|
if (mpctx->master_demuxer)
|
2010-04-25 20:20:34 +00:00
|
|
|
return FFMAX(mpctx->last_chapter_seek,
|
2012-08-19 16:07:06 +00:00
|
|
|
demuxer_get_current_chapter(mpctx->master_demuxer, current_pts));
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
return -2;
|
2009-04-02 02:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *chapter_display_name(struct MPContext *mpctx, int chapter)
|
2011-10-23 02:51:44 +00:00
|
|
|
{
|
|
|
|
char *name = chapter_name(mpctx, chapter);
|
2012-08-04 01:47:05 +00:00
|
|
|
char *dname = name;
|
2011-10-23 02:51:44 +00:00
|
|
|
if (name) {
|
2012-08-04 01:47:05 +00:00
|
|
|
dname = talloc_asprintf(NULL, "(%d) %s", chapter + 1, name);
|
2013-02-19 22:56:39 +00:00
|
|
|
} else if (chapter < -1) {
|
|
|
|
dname = talloc_strdup(NULL, "(unavailable)");
|
2011-10-23 02:51:44 +00:00
|
|
|
} else {
|
|
|
|
int chapter_count = get_chapter_count(mpctx);
|
|
|
|
if (chapter_count <= 0)
|
2012-08-04 01:47:05 +00:00
|
|
|
dname = talloc_asprintf(NULL, "(%d)", chapter + 1);
|
2011-10-23 02:51:44 +00:00
|
|
|
else
|
2012-08-04 01:47:05 +00:00
|
|
|
dname = talloc_asprintf(NULL, "(%d) of %d", chapter + 1,
|
|
|
|
chapter_count);
|
2011-10-23 02:51:44 +00:00
|
|
|
}
|
2012-08-04 01:47:05 +00:00
|
|
|
if (dname != name)
|
|
|
|
talloc_free(name);
|
|
|
|
return dname;
|
2011-10-23 02:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// returns NULL if chapter name unavailable
|
|
|
|
char *chapter_name(struct MPContext *mpctx, int chapter)
|
2009-04-02 02:00:22 +00:00
|
|
|
{
|
2013-02-19 22:56:39 +00:00
|
|
|
if (mpctx->chapters) {
|
|
|
|
if (chapter < 0 || chapter >= mpctx->num_chapters)
|
|
|
|
return NULL;
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
return talloc_strdup(NULL, mpctx->chapters[chapter].name);
|
2013-02-19 22:56:39 +00:00
|
|
|
}
|
2012-08-19 16:07:06 +00:00
|
|
|
if (mpctx->master_demuxer)
|
|
|
|
return demuxer_chapter_name(mpctx->master_demuxer, chapter);
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
return NULL;
|
2009-04-02 02:00:22 +00:00
|
|
|
}
|
|
|
|
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
// returns the start of the chapter in seconds (-1 if unavailable)
|
2011-10-23 02:51:44 +00:00
|
|
|
double chapter_start_time(struct MPContext *mpctx, int chapter)
|
|
|
|
{
|
2013-08-15 19:49:06 +00:00
|
|
|
if (chapter == -1)
|
|
|
|
return get_start_time(mpctx);
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (mpctx->chapters)
|
|
|
|
return mpctx->chapters[chapter].start;
|
2012-08-19 16:07:06 +00:00
|
|
|
if (mpctx->master_demuxer)
|
2013-05-03 23:02:09 +00:00
|
|
|
return demuxer_chapter_time(mpctx->master_demuxer, chapter);
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
return -1;
|
2011-10-23 02:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int get_chapter_count(struct MPContext *mpctx)
|
|
|
|
{
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
if (mpctx->chapters)
|
|
|
|
return mpctx->num_chapters;
|
2012-08-19 16:07:06 +00:00
|
|
|
if (mpctx->master_demuxer)
|
|
|
|
return demuxer_chapter_count(mpctx->master_demuxer);
|
mplayer: fix idle mode regressions
Commit 89a17bcda6c16 simplified the idle loop to run any commands
mplayer receives, not just playlist related commands. Unfortunately, it
turns out many slave commands always assume the presence of a demuxer.
MPContext->demuxer is assumed not to be NULL. This made the player
crash when receiving slave commands like pause/unpause, chapter
control, subtitle selection.
We want mplayer being able to handle this. Any slave command or
property, as long as it's backed by a persistent setting, should be run
successfully, even if no file is being played. If the slave command
doesn't make sense in this state, it shouldn't crash the player.
Insert some NULL checks when accessing demuxers. If sh_video or
sh_audio are not NULL, assume demuxer can't be NULL.
(There actually aren't that many properties which need to be changed. If
it gets too complicated, we could employ alternative mechanisms instead,
such as explicitly marking safe properties with a flag.)
2012-08-04 00:00:28 +00:00
|
|
|
return 0;
|
2011-10-23 02:51:44 +00:00
|
|
|
}
|
|
|
|
|
2013-05-03 22:36:53 +00:00
|
|
|
// 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)
|
2009-04-02 02:00:22 +00:00
|
|
|
{
|
2013-05-03 22:36:53 +00:00
|
|
|
int num = get_chapter_count(mpctx);
|
|
|
|
if (num == 0)
|
|
|
|
return false;
|
2013-08-15 19:49:06 +00:00
|
|
|
if (chapter < -1 || chapter >= num)
|
2013-05-03 22:36:53 +00:00
|
|
|
return false;
|
|
|
|
|
2011-08-04 20:14:44 +00:00
|
|
|
mpctx->last_chapter_seek = -2;
|
2012-08-19 16:07:06 +00:00
|
|
|
|
2013-05-03 22:36:53 +00:00
|
|
|
double pts;
|
2013-08-15 19:49:06 +00:00
|
|
|
if (chapter == -1) {
|
|
|
|
pts = get_start_time(mpctx);
|
|
|
|
goto do_seek;
|
|
|
|
} else if (mpctx->chapters) {
|
2013-05-03 22:36:53 +00:00
|
|
|
pts = mpctx->chapters[chapter].start;
|
|
|
|
goto do_seek;
|
|
|
|
} else if (mpctx->master_demuxer) {
|
|
|
|
int res = demuxer_seek_chapter(mpctx->master_demuxer, chapter, &pts);
|
2010-04-25 20:20:34 +00:00
|
|
|
if (res >= 0) {
|
2013-05-03 22:36:53 +00:00
|
|
|
if (pts == -1) {
|
|
|
|
// for DVD/BD - seek happened via stream layer
|
|
|
|
seek_reset(mpctx, true, true);
|
|
|
|
mpctx->seek = (struct seek_params){0};
|
|
|
|
return true;
|
2010-04-25 20:20:34 +00:00
|
|
|
}
|
2013-05-03 22:36:53 +00:00
|
|
|
chapter = res;
|
|
|
|
goto do_seek;
|
2010-04-25 20:20:34 +00:00
|
|
|
}
|
2010-04-23 17:14:59 +00:00
|
|
|
}
|
2013-05-03 22:36:53 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
do_seek:
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0);
|
|
|
|
mpctx->last_chapter_seek = chapter;
|
|
|
|
mpctx->last_chapter_pts = pts;
|
|
|
|
return true;
|
2009-04-02 02:00:22 +00:00
|
|
|
}
|
|
|
|
|
2012-11-15 19:51:34 +00:00
|
|
|
static void update_avsync(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->sh_audio || !mpctx->sh_video)
|
|
|
|
return;
|
|
|
|
|
|
|
|
double a_pos = playing_audio_pts(mpctx);
|
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->last_av_difference = a_pos - mpctx->video_pts - mpctx->audio_delay;
|
2012-11-15 19:51:34 +00:00
|
|
|
if (mpctx->time_frame > 0)
|
|
|
|
mpctx->last_av_difference +=
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->time_frame * mpctx->opts->playback_speed;
|
2012-11-15 19:51:34 +00:00
|
|
|
if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE)
|
|
|
|
mpctx->last_av_difference = MP_NOPTS_VALUE;
|
2013-03-08 01:08:02 +00:00
|
|
|
if (mpctx->last_av_difference > 0.5 && mpctx->drop_frame_cnt > 50
|
2012-11-15 19:51:34 +00:00
|
|
|
&& !mpctx->drop_message_shown) {
|
|
|
|
mp_tmsg(MSGT_AVSYNC, MSGL_WARN, "%s", mp_gtext(av_desync_help_text));
|
|
|
|
mpctx->drop_message_shown = true;
|
|
|
|
}
|
|
|
|
}
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2013-10-02 18:38:10 +00:00
|
|
|
static bool handle_osd_redraw(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->video_out || !mpctx->video_out->config_ok)
|
|
|
|
return false;
|
|
|
|
bool want_redraw = vo_get_want_redraw(mpctx->video_out);
|
|
|
|
if (mpctx->video_out->driver->draw_osd)
|
|
|
|
want_redraw |= mpctx->osd->want_redraw;
|
|
|
|
mpctx->osd->want_redraw = false;
|
|
|
|
if (want_redraw) {
|
|
|
|
if (redraw_osd(mpctx)) {
|
|
|
|
return true;
|
|
|
|
} else if (mpctx->paused) {
|
|
|
|
// force redrawing OSD by framestepping
|
|
|
|
add_step_frame(mpctx, 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
static void handle_metadata_update(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mp_time_sec() > mpctx->last_metadata_update + 2) {
|
|
|
|
demux_info_update(mpctx->demuxer);
|
|
|
|
mpctx->last_metadata_update = mp_time_sec();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-01 23:22:54 +00:00
|
|
|
static void handle_pause_on_low_cache(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-02-17 19:24:59 +00:00
|
|
|
int cache = mp_get_cache_percent(mpctx);
|
|
|
|
bool idle = mp_get_cache_idle(mpctx);
|
2012-12-01 23:22:54 +00:00
|
|
|
if (mpctx->paused && mpctx->paused_for_cache) {
|
2013-04-25 17:07:47 +00:00
|
|
|
if (cache < 0 || cache >= opts->stream_cache_min_percent || idle) {
|
|
|
|
mpctx->paused_for_cache = false;
|
2013-04-25 18:38:22 +00:00
|
|
|
if (!opts->pause)
|
2013-04-25 17:07:47 +00:00
|
|
|
unpause_player(mpctx);
|
|
|
|
}
|
|
|
|
} else {
|
2012-12-01 23:22:54 +00:00
|
|
|
if (cache >= 0 && cache <= opts->stream_cache_pause && !idle) {
|
2013-04-25 18:38:22 +00:00
|
|
|
bool prev_paused_user = opts->pause;
|
2013-04-10 15:14:20 +00:00
|
|
|
pause_player(mpctx);
|
2013-04-25 17:07:47 +00:00
|
|
|
mpctx->paused_for_cache = true;
|
2013-04-25 18:38:22 +00:00
|
|
|
opts->pause = prev_paused_user;
|
2012-12-01 23:22:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
static void handle_heartbeat_cmd(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-09-15 00:13:54 +00:00
|
|
|
if (opts->heartbeat_cmd && !mpctx->paused) {
|
2013-09-08 00:52:14 +00:00
|
|
|
double now = mp_time_sec();
|
|
|
|
if (now - mpctx->last_heartbeat > opts->heartbeat_interval) {
|
|
|
|
mpctx->last_heartbeat = now;
|
|
|
|
system(opts->heartbeat_cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_cursor_autohide(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
struct vo *vo = mpctx->video_out;
|
|
|
|
|
|
|
|
if (!vo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool mouse_cursor_visible = mpctx->mouse_cursor_visible;
|
|
|
|
|
|
|
|
unsigned mouse_event_ts = mp_input_get_mouse_event_counter(mpctx->input);
|
|
|
|
if (mpctx->mouse_event_ts != mouse_event_ts) {
|
|
|
|
mpctx->mouse_event_ts = mouse_event_ts;
|
|
|
|
mpctx->mouse_timer =
|
|
|
|
mp_time_sec() + opts->cursor_autohide_delay / 1000.0;
|
|
|
|
mouse_cursor_visible = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp_time_sec() >= mpctx->mouse_timer)
|
|
|
|
mouse_cursor_visible = false;
|
|
|
|
|
|
|
|
if (opts->cursor_autohide_delay == -1)
|
|
|
|
mouse_cursor_visible = true;
|
|
|
|
|
|
|
|
if (opts->cursor_autohide_delay == -2)
|
|
|
|
mouse_cursor_visible = false;
|
|
|
|
|
|
|
|
if (opts->cursor_autohide_fs && !opts->vo.fullscreen)
|
|
|
|
mouse_cursor_visible = true;
|
|
|
|
|
|
|
|
if (mouse_cursor_visible != mpctx->mouse_cursor_visible)
|
|
|
|
vo_control(vo, VOCTRL_SET_CURSOR_VISIBILITY, &mouse_cursor_visible);
|
|
|
|
mpctx->mouse_cursor_visible = mouse_cursor_visible;
|
|
|
|
}
|
|
|
|
|
2013-09-19 15:28:27 +00:00
|
|
|
static void handle_input_and_seek_coalesce(struct MPContext *mpctx)
|
2013-09-08 00:52:14 +00:00
|
|
|
{
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_flush_events(mpctx);
|
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
mp_cmd_t *cmd;
|
|
|
|
while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) {
|
|
|
|
/* Allow running consecutive seek commands to combine them,
|
|
|
|
* but execute the seek before running other commands.
|
|
|
|
* If the user seeks continuously (keeps arrow key down)
|
|
|
|
* try to finish showing a frame from one location before doing
|
|
|
|
* another seek (which could lead to unchanging display). */
|
|
|
|
if ((mpctx->seek.type && cmd->id != MP_CMD_SEEK) ||
|
|
|
|
(mpctx->restart_playback && cmd->id == MP_CMD_SEEK &&
|
|
|
|
mp_time_sec() - mpctx->start_timestamp < 0.3))
|
|
|
|
break;
|
|
|
|
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
|
|
|
run_command(mpctx, cmd);
|
|
|
|
mp_cmd_free(cmd);
|
|
|
|
if (mpctx->stop_play)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_backstep(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->backstep_active)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_sstep(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
if (opts->step_sec > 0 && !mpctx->stop_play && !mpctx->paused &&
|
|
|
|
!mpctx->restart_playback)
|
|
|
|
{
|
|
|
|
set_osd_function(mpctx, OSD_FFW);
|
|
|
|
queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_keep_open(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
if (opts->keep_open && mpctx->stop_play == AT_END_OF_FILE) {
|
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
2013-10-01 21:48:51 +00:00
|
|
|
mpctx->playback_pts = mpctx->last_vo_pts;
|
2013-09-08 00:52:14 +00:00
|
|
|
pause_player(mpctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-01 23:15:59 +00:00
|
|
|
// Execute a forceful refresh of the VO window, if it hasn't had a valid frame
|
|
|
|
// for a while. The problem is that a VO with no valid frame (vo->hasframe==0)
|
|
|
|
// doesn't redraw video and doesn't OSD interaction. So screw it, hard.
|
2013-10-03 22:24:17 +00:00
|
|
|
static void handle_force_window(struct MPContext *mpctx, bool reconfig)
|
2013-10-01 23:15:59 +00:00
|
|
|
{
|
|
|
|
// Don't interfere with real video playback
|
|
|
|
if (mpctx->sh_video)
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct vo *vo = mpctx->video_out;
|
|
|
|
if (!vo)
|
|
|
|
return;
|
|
|
|
|
2013-10-03 22:24:17 +00:00
|
|
|
if (!vo->config_ok || reconfig) {
|
2013-10-01 23:15:59 +00:00
|
|
|
MP_INFO(mpctx, "Creating non-video VO window.\n");
|
|
|
|
// Pick whatever works
|
|
|
|
int config_format = 0;
|
|
|
|
for (int fmt = IMGFMT_START; fmt < IMGFMT_END; fmt++) {
|
|
|
|
if (vo->driver->query_format(vo, fmt)) {
|
|
|
|
config_format = fmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-10-02 18:30:44 +00:00
|
|
|
int w = 960;
|
2013-10-01 23:15:59 +00:00
|
|
|
int h = 480;
|
|
|
|
struct mp_image_params p = {
|
|
|
|
.imgfmt = config_format,
|
|
|
|
.w = w, .h = h,
|
|
|
|
.d_w = w, .d_h = h,
|
|
|
|
};
|
|
|
|
vo_reconfig(vo, &p, 0);
|
|
|
|
redraw_osd(mpctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-19 11:58:52 +00:00
|
|
|
static double get_wakeup_period(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
/* Even if we can immediately wake up in response to most input events,
|
|
|
|
* there are some timers which are not registered to the event loop
|
|
|
|
* and need to be checked periodically (like automatic mouse cursor hiding).
|
|
|
|
* OSD content updates behave similarly. Also some uncommon input devices
|
|
|
|
* may not have proper FD event support.
|
|
|
|
*/
|
|
|
|
double sleeptime = WAKEUP_PERIOD;
|
|
|
|
|
|
|
|
#ifndef HAVE_POSIX_SELECT
|
|
|
|
// No proper file descriptor event handling; keep waking up to poll input
|
|
|
|
sleeptime = FFMIN(sleeptime, 0.02);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mpctx->video_out)
|
|
|
|
if (mpctx->video_out->wakeup_period > 0)
|
|
|
|
sleeptime = FFMIN(sleeptime, mpctx->video_out->wakeup_period);
|
|
|
|
|
|
|
|
return sleeptime;
|
|
|
|
}
|
|
|
|
|
2010-12-13 19:12:49 +00:00
|
|
|
static void run_playloop(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2011-04-07 18:17:51 +00:00
|
|
|
bool full_audio_buffers = false;
|
2012-03-14 23:24:52 +00:00
|
|
|
bool audio_left = false, video_left = false;
|
2012-11-15 17:49:17 +00:00
|
|
|
double endpts = get_play_end_pts(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
bool end_is_chapter = false;
|
2012-12-19 11:58:52 +00:00
|
|
|
double sleeptime = get_wakeup_period(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
bool was_restart = mpctx->restart_playback;
|
2013-03-25 21:57:24 +00:00
|
|
|
bool new_frame_shown = false;
|
2012-03-14 23:24:52 +00:00
|
|
|
|
2012-09-14 15:51:26 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) {
|
|
|
|
mpctx->stop_play = PT_QUIT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
// Add tracks that were added by the demuxer later (e.g. MPEG)
|
|
|
|
if (!mpctx->timeline && mpctx->demuxer)
|
|
|
|
add_demuxer_tracks(mpctx, mpctx->demuxer);
|
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
if (mpctx->timeline) {
|
|
|
|
double end = mpctx->timeline[mpctx->timeline_part + 1].start;
|
|
|
|
if (endpts == MP_NOPTS_VALUE || end < endpts) {
|
|
|
|
endpts = end;
|
|
|
|
end_is_chapter = true;
|
|
|
|
}
|
|
|
|
}
|
2010-12-13 19:12:49 +00:00
|
|
|
|
|
|
|
if (opts->chapterrange[1] > 0) {
|
|
|
|
int cur_chapter = get_current_chapter(mpctx);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (cur_chapter != -1 && cur_chapter + 1 > opts->chapterrange[1])
|
2010-12-13 19:12:49 +00:00
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
}
|
|
|
|
|
2012-05-14 09:52:59 +00:00
|
|
|
if (mpctx->sh_audio && !mpctx->restart_playback && !mpctx->ao->untimed) {
|
2012-03-14 23:24:52 +00:00
|
|
|
int status = fill_audio_out_buffers(mpctx, endpts);
|
2012-05-14 09:52:59 +00:00
|
|
|
full_audio_buffers = status >= 0;
|
2012-03-14 23:24:52 +00:00
|
|
|
// Not at audio stream EOF yet
|
|
|
|
audio_left = status > -2;
|
2011-04-07 18:17:51 +00:00
|
|
|
}
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2013-10-01 23:15:59 +00:00
|
|
|
if (mpctx->video_out) {
|
|
|
|
vo_check_events(mpctx->video_out);
|
|
|
|
handle_cursor_autohide(mpctx);
|
|
|
|
}
|
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
double buffered_audio = -1;
|
|
|
|
while (mpctx->sh_video) { // never loops, for "break;" only
|
2012-04-04 21:04:01 +00:00
|
|
|
struct vo *vo = mpctx->video_out;
|
2013-03-04 21:21:57 +00:00
|
|
|
update_fps(mpctx);
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2012-04-04 21:04:01 +00:00
|
|
|
video_left = vo->hasframe || vo->frame_loaded;
|
|
|
|
if (!vo->frame_loaded && (!mpctx->paused || mpctx->restart_playback)) {
|
2013-04-25 12:56:01 +00:00
|
|
|
double frame_time = update_video(mpctx, endpts);
|
2010-12-13 19:12:49 +00:00
|
|
|
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,
|
|
|
|
"\nFATAL: Could not initialize video filters (-vf) "
|
|
|
|
"or video output (-vo).\n");
|
2013-10-01 23:15:59 +00:00
|
|
|
int uninit = INITIALIZED_VCODEC;
|
|
|
|
if (!opts->force_vo)
|
|
|
|
uninit |= INITIALIZED_VO;
|
|
|
|
uninit_player(mpctx, uninit);
|
2012-08-25 09:48:22 +00:00
|
|
|
mpctx->current_track[STREAM_VIDEO] = NULL;
|
|
|
|
if (!mpctx->current_track[STREAM_AUDIO])
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
2013-08-02 08:32:38 +00:00
|
|
|
mpctx->error_playing = true;
|
2013-10-03 22:24:17 +00:00
|
|
|
handle_force_window(mpctx, true);
|
2012-08-25 09:48:22 +00:00
|
|
|
break;
|
2010-12-13 19:12:49 +00:00
|
|
|
}
|
2012-03-14 23:24:52 +00:00
|
|
|
video_left = frame_time >= 0;
|
|
|
|
if (video_left && !mpctx->restart_playback) {
|
2010-12-13 19:12:49 +00:00
|
|
|
mpctx->time_frame += frame_time / opts->playback_speed;
|
|
|
|
adjust_sync(mpctx, frame_time);
|
|
|
|
}
|
2013-02-03 18:27:52 +00:00
|
|
|
if (!video_left) {
|
|
|
|
mpctx->delay = 0;
|
|
|
|
mpctx->last_av_difference = 0;
|
|
|
|
}
|
2010-12-13 19:12:49 +00:00
|
|
|
}
|
|
|
|
|
2012-04-04 21:04:01 +00:00
|
|
|
if (endpts != MP_NOPTS_VALUE)
|
|
|
|
video_left &= mpctx->sh_video->pts < endpts;
|
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
handle_heartbeat_cmd(mpctx);
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
if (!video_left || (mpctx->paused && !mpctx->restart_playback))
|
|
|
|
break;
|
2012-04-04 21:04:01 +00:00
|
|
|
if (!vo->frame_loaded) {
|
2012-03-14 23:24:52 +00:00
|
|
|
sleeptime = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
|
|
|
if (full_audio_buffers && !mpctx->restart_playback) {
|
|
|
|
buffered_audio = ao_get_delay(mpctx->ao);
|
|
|
|
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "delay=%f\n", buffered_audio);
|
|
|
|
|
|
|
|
if (opts->autosync) {
|
|
|
|
/* Smooth reported playback position from AO by averaging
|
|
|
|
* it with the value expected based on previus value and
|
|
|
|
* time elapsed since then. May help smooth video timing
|
|
|
|
* with audio output that have inaccurate position reporting.
|
|
|
|
* This is badly implemented; the behavior of the smoothing
|
|
|
|
* now undesirably depends on how often this code runs
|
|
|
|
* (mainly depends on video frame rate). */
|
|
|
|
float predicted = (mpctx->delay / opts->playback_speed +
|
|
|
|
mpctx->time_frame);
|
|
|
|
float difference = buffered_audio - predicted;
|
|
|
|
buffered_audio = predicted + difference / opts->autosync;
|
|
|
|
}
|
|
|
|
|
|
|
|
mpctx->time_frame = (buffered_audio -
|
|
|
|
mpctx->delay / opts->playback_speed);
|
|
|
|
} else {
|
|
|
|
/* If we're more than 200 ms behind the right playback
|
|
|
|
* position, don't try to speed up display of following
|
|
|
|
* frames to catch up; continue with default speed from
|
|
|
|
* the current frame instead.
|
2012-07-29 22:27:50 +00:00
|
|
|
* If untimed is set always output frames immediately
|
2012-03-14 23:24:52 +00:00
|
|
|
* without sleeping.
|
|
|
|
*/
|
2012-12-14 11:59:05 +00:00
|
|
|
if (mpctx->time_frame < -0.2 || opts->untimed || vo->untimed)
|
2012-03-14 23:24:52 +00:00
|
|
|
mpctx->time_frame = 0;
|
|
|
|
}
|
|
|
|
|
2012-04-04 21:04:01 +00:00
|
|
|
double vsleep = mpctx->time_frame - vo->flip_queue_offset;
|
2012-03-14 23:24:52 +00:00
|
|
|
if (vsleep > 0.050) {
|
|
|
|
sleeptime = FFMIN(sleeptime, vsleep - 0.040);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sleeptime = 0;
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2011-09-04 19:08:26 +00:00
|
|
|
//=================== FLIP PAGE (VIDEO BLT): ======================
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2012-04-04 21:04:01 +00:00
|
|
|
vo_new_frame_imminent(vo);
|
2012-03-14 23:24:52 +00:00
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
mpctx->video_pts = sh_video->pts;
|
core: add --keep-open, which doesn't close the file on EOF
The --keep-open option causes mpv not to close the current file.
Instead, it will pause, and allow the user to seek around. When
seeking beyond the end of the file, mpv does a precise seek back to
the previous last known position that produced video output.
In some corner cases, mpv might not be able to produce video output at
all, despite having created a VO. (Possibly when only 1 frame could be
decoded, but the video filter chain queues frames. Then a VO would be
created, without sending an actual video frame to the VO.) In these
cases, the VO window will not redraw, not even OSD.
Based on a patch by coax [1].
[1] http://devel.mplayer2.org/ticket/210#comment:4
2012-11-12 23:56:20 +00:00
|
|
|
mpctx->last_vo_pts = mpctx->video_pts;
|
2013-04-03 23:18:19 +00:00
|
|
|
mpctx->playback_pts = mpctx->video_pts;
|
2013-09-15 22:27:53 +00:00
|
|
|
update_subtitles(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
update_osd_msg(mpctx);
|
2012-10-21 12:58:46 +00:00
|
|
|
draw_osd(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
|
2012-04-04 21:23:32 +00:00
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
2012-04-04 21:04:01 +00:00
|
|
|
mpctx->time_frame -= vo->flip_queue_offset;
|
2012-12-14 11:59:05 +00:00
|
|
|
if (mpctx->time_frame > 0.001)
|
2012-03-14 23:24:52 +00:00
|
|
|
mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame);
|
2012-04-04 21:04:01 +00:00
|
|
|
mpctx->time_frame += vo->flip_queue_offset;
|
2012-03-14 23:24:52 +00:00
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
int64_t t2 = mp_time_us();
|
2012-03-14 23:24:52 +00:00
|
|
|
/* Playing with playback speed it's possible to get pathological
|
|
|
|
* cases with mpctx->time_frame negative enough to cause an
|
|
|
|
* overflow in pts_us calculation, thus the FFMAX. */
|
|
|
|
double time_frame = FFMAX(mpctx->time_frame, -1);
|
2013-05-25 16:31:06 +00:00
|
|
|
int64_t pts_us = mpctx->last_time + time_frame * 1e6;
|
2012-03-14 23:24:52 +00:00
|
|
|
int duration = -1;
|
2012-04-04 21:04:01 +00:00
|
|
|
double pts2 = vo->next_pts2;
|
2012-03-14 23:24:52 +00:00
|
|
|
if (pts2 != MP_NOPTS_VALUE && opts->correct_pts &&
|
|
|
|
!mpctx->restart_playback) {
|
|
|
|
// expected A/V sync correction is ignored
|
|
|
|
double diff = (pts2 - mpctx->video_pts);
|
|
|
|
diff /= opts->playback_speed;
|
|
|
|
if (mpctx->time_frame < 0)
|
|
|
|
diff += mpctx->time_frame;
|
|
|
|
if (diff < 0)
|
|
|
|
diff = 0;
|
|
|
|
if (diff > 10)
|
|
|
|
diff = 10;
|
|
|
|
duration = diff * 1e6;
|
|
|
|
}
|
2012-04-04 21:04:01 +00:00
|
|
|
vo_flip_page(vo, pts_us | 1, duration);
|
2012-03-14 23:24:52 +00:00
|
|
|
|
2013-05-25 16:31:06 +00:00
|
|
|
mpctx->last_vo_flip_duration = (mp_time_us() - t2) * 0.000001;
|
2012-04-04 21:04:01 +00:00
|
|
|
if (vo->driver->flip_page_timed) {
|
2012-03-14 23:24:52 +00:00
|
|
|
// No need to adjust sync based on flip speed
|
|
|
|
mpctx->last_vo_flip_duration = 0;
|
|
|
|
// For print_status - VO call finishing early is OK for sync
|
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
|
|
|
}
|
2013-09-29 19:10:36 +00:00
|
|
|
mpctx->shown_vframes++;
|
2012-03-14 23:24:52 +00:00
|
|
|
if (mpctx->restart_playback) {
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
if (mpctx->sync_audio_to_video) {
|
|
|
|
mpctx->syncing_audio = true;
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
fill_audio_out_buffers(mpctx, endpts);
|
|
|
|
mpctx->restart_playback = false;
|
|
|
|
}
|
2012-03-14 23:24:52 +00:00
|
|
|
mpctx->time_frame = 0;
|
|
|
|
get_relative_time(mpctx);
|
|
|
|
}
|
2012-11-15 19:51:34 +00:00
|
|
|
update_avsync(mpctx);
|
|
|
|
print_status(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
screenshot_flip(mpctx);
|
2013-03-25 21:57:24 +00:00
|
|
|
new_frame_shown = true;
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
break;
|
|
|
|
} // video
|
2010-12-13 19:12:49 +00:00
|
|
|
|
core: completely change handling of attached picture pseudo video
Before this commit, we tried to play along with libavformat and tried
to pretend that attached pictures are video streams with a single
frame, and that the frame magically appeared at the seek position when
seeking. The playback core would then switch to a mode where the video
has ended, and the "remaining" audio is played.
This didn't work very well:
- we needed a hack in demux.c, because we tried to read more packets in
order to find the "next" video frame (libavformat doesn't tell us if
a stream has ended)
- switching the video stream didn't work, because we can't tell
libavformat to send the packet again
- seeking and resuming after was hacky (for some reason libavformat sets
the returned packet's PTS to that of the previously returned audio
packet in generic code not related to attached pictures, and this
happened to work)
- if the user did something stupid and e.g. inserted a deinterlacer by
default, a picture was never displayed, only an inactive VO window)
- same when using a command that reconfigured the VO (like switching
aspect or video filters)
- hr-seek didn't work
For this reason, handle attached pictures as separate case with a
separate video decoding function, which doesn't read packets. Also,
do not synchronize audio to video start in this case.
2013-07-11 17:23:56 +00:00
|
|
|
video_left &= mpctx->sync_audio_to_video; // force no-video semantics
|
|
|
|
|
2012-05-14 09:52:59 +00:00
|
|
|
if (mpctx->sh_audio && (mpctx->restart_playback ? !video_left :
|
|
|
|
mpctx->ao->untimed && (mpctx->delay <= 0 ||
|
|
|
|
!video_left))) {
|
|
|
|
int status = fill_audio_out_buffers(mpctx, endpts);
|
|
|
|
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
|
|
|
// Not at audio stream EOF yet
|
|
|
|
audio_left = status > -2;
|
2012-03-14 23:24:52 +00:00
|
|
|
}
|
2012-05-14 09:52:59 +00:00
|
|
|
if (!video_left)
|
|
|
|
mpctx->restart_playback = false;
|
2012-03-14 23:24:52 +00:00
|
|
|
if (mpctx->sh_audio && buffered_audio == -1)
|
|
|
|
buffered_audio = mpctx->paused ? 0 : ao_get_delay(mpctx->ao);
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
update_osd_msg(mpctx);
|
2012-07-30 19:07:54 +00:00
|
|
|
|
|
|
|
// The cache status is part of the status line. Possibly update it.
|
2013-02-17 19:24:59 +00:00
|
|
|
if (mpctx->paused && mp_get_cache_percent(mpctx) >= 0)
|
2012-11-15 19:51:34 +00:00
|
|
|
print_status(mpctx);
|
2012-07-30 19:07:54 +00:00
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
if (!video_left && (!mpctx->paused || was_restart)) {
|
|
|
|
double a_pos = 0;
|
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
a_pos = (written_audio_pts(mpctx) -
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->playback_speed * buffered_audio);
|
2012-03-14 23:24:52 +00:00
|
|
|
}
|
2013-04-03 23:18:19 +00:00
|
|
|
mpctx->playback_pts = a_pos;
|
2012-11-15 19:51:34 +00:00
|
|
|
print_status(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
}
|
|
|
|
|
2013-09-15 22:27:53 +00:00
|
|
|
update_subtitles(mpctx);
|
|
|
|
|
2012-03-14 23:24:52 +00:00
|
|
|
/* It's possible for the user to simultaneously switch both audio
|
|
|
|
* and video streams to "disabled" at runtime. Handle this by waiting
|
|
|
|
* rather than immediately stopping playback due to EOF.
|
|
|
|
*
|
|
|
|
* When all audio has been written to output driver, stay in the
|
|
|
|
* main loop handling commands until it has been mostly consumed,
|
|
|
|
* except in the gapless case, where the next file will be started
|
|
|
|
* while audio from the current one still remains to be played.
|
|
|
|
*
|
|
|
|
* We want this check to trigger if we seeked to this position,
|
|
|
|
* but not if we paused at it with audio possibly still buffered in
|
|
|
|
* the AO. There's currently no working way to check buffered audio
|
|
|
|
* inside AO while paused. Thus the "was_restart" check below, which
|
|
|
|
* should trigger after seek only, when we know there's no audio
|
|
|
|
* buffered.
|
|
|
|
*/
|
|
|
|
if ((mpctx->sh_audio || mpctx->sh_video) && !audio_left && !video_left
|
|
|
|
&& (opts->gapless_audio || buffered_audio < 0.05)
|
|
|
|
&& (!mpctx->paused || was_restart)) {
|
|
|
|
if (end_is_chapter) {
|
|
|
|
seek(mpctx, (struct seek_params){
|
|
|
|
.type = MPSEEK_ABSOLUTE,
|
|
|
|
.amount = mpctx->timeline[mpctx->timeline_part+1].start
|
|
|
|
}, true);
|
|
|
|
} else
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
2013-04-03 23:16:55 +00:00
|
|
|
sleeptime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mpctx->stop_play && !mpctx->restart_playback) {
|
|
|
|
|
|
|
|
// If no more video is available, one frame means one playloop iteration.
|
|
|
|
// Otherwise, one frame means one video frame.
|
|
|
|
if (!video_left)
|
|
|
|
new_frame_shown = true;
|
|
|
|
|
|
|
|
if (opts->playing_msg && !mpctx->playing_msg_shown && new_frame_shown) {
|
|
|
|
mpctx->playing_msg_shown = true;
|
|
|
|
char *msg = mp_property_expand_string(mpctx, opts->playing_msg);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s\n", msg);
|
|
|
|
talloc_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpctx->max_frames >= 0) {
|
|
|
|
if (new_frame_shown)
|
|
|
|
mpctx->max_frames--;
|
|
|
|
if (mpctx->max_frames <= 0)
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpctx->step_frames > 0 && !mpctx->paused) {
|
|
|
|
if (new_frame_shown)
|
|
|
|
mpctx->step_frames--;
|
|
|
|
if (mpctx->step_frames == 0)
|
|
|
|
pause_player(mpctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mpctx->stop_play) {
|
2012-03-14 23:24:52 +00:00
|
|
|
double audio_sleep = 9;
|
|
|
|
if (mpctx->sh_audio && !mpctx->paused) {
|
|
|
|
if (mpctx->ao->untimed) {
|
2012-05-14 09:52:59 +00:00
|
|
|
if (!video_left)
|
2012-03-14 23:24:52 +00:00
|
|
|
audio_sleep = 0;
|
|
|
|
} else if (full_audio_buffers) {
|
|
|
|
audio_sleep = buffered_audio - 0.050;
|
|
|
|
// Keep extra safety margin if the buffers are large
|
|
|
|
if (audio_sleep > 0.100)
|
|
|
|
audio_sleep = FFMAX(audio_sleep - 0.200, 0.100);
|
|
|
|
else
|
|
|
|
audio_sleep = FFMAX(audio_sleep, 0.020);
|
|
|
|
} else
|
|
|
|
audio_sleep = 0.020;
|
2010-12-13 19:12:49 +00:00
|
|
|
}
|
2012-03-14 23:24:52 +00:00
|
|
|
sleeptime = FFMIN(sleeptime, audio_sleep);
|
2013-10-02 18:38:10 +00:00
|
|
|
if (sleeptime > 0) {
|
|
|
|
if (handle_osd_redraw(mpctx))
|
|
|
|
sleeptime = 0;
|
2010-12-13 19:12:49 +00:00
|
|
|
}
|
2013-05-25 16:39:46 +00:00
|
|
|
if (sleeptime > 0)
|
|
|
|
mp_input_get_cmd(mpctx->input, sleeptime * 1000, true);
|
2012-03-14 23:24:52 +00:00
|
|
|
}
|
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
handle_metadata_update(mpctx);
|
2012-03-14 23:24:52 +00:00
|
|
|
|
2012-12-01 23:22:54 +00:00
|
|
|
handle_pause_on_low_cache(mpctx);
|
|
|
|
|
2013-09-19 15:28:27 +00:00
|
|
|
handle_input_and_seek_coalesce(mpctx);
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
handle_backstep(mpctx);
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
handle_sstep(mpctx);
|
2010-12-13 19:12:49 +00:00
|
|
|
|
2013-09-08 00:52:14 +00:00
|
|
|
handle_keep_open(mpctx);
|
core: add --keep-open, which doesn't close the file on EOF
The --keep-open option causes mpv not to close the current file.
Instead, it will pause, and allow the user to seek around. When
seeking beyond the end of the file, mpv does a precise seek back to
the previous last known position that produced video output.
In some corner cases, mpv might not be able to produce video output at
all, despite having created a VO. (Possibly when only 1 frame could be
decoded, but the video filter chain queues frames. Then a VO would be
created, without sending an actual video frame to the VO.) In these
cases, the VO window will not redraw, not even OSD.
Based on a patch by coax [1].
[1] http://devel.mplayer2.org/ticket/210#comment:4
2012-11-12 23:56:20 +00:00
|
|
|
|
2013-10-03 22:24:17 +00:00
|
|
|
handle_force_window(mpctx, false);
|
2013-10-01 23:15:59 +00:00
|
|
|
|
2013-05-03 22:36:53 +00:00
|
|
|
execute_queued_seek(mpctx);
|
2010-12-13 19:12:49 +00:00
|
|
|
}
|
|
|
|
|
2011-01-11 23:37:02 +00:00
|
|
|
static bool attachment_is_font(struct demux_attachment *att)
|
|
|
|
{
|
|
|
|
if (!att->name || !att->type || !att->data || !att->data_size)
|
|
|
|
return false;
|
|
|
|
// match against MIME types
|
|
|
|
if (strcmp(att->type, "application/x-truetype-font") == 0
|
|
|
|
|| strcmp(att->type, "application/x-font") == 0)
|
|
|
|
return true;
|
|
|
|
// fallback: match against file extension
|
|
|
|
if (strlen(att->name) > 4) {
|
|
|
|
char *ext = att->name + strlen(att->name) - 4;
|
|
|
|
if (strcasecmp(ext, ".ttf") == 0 || strcasecmp(ext, ".ttc") == 0
|
|
|
|
|| strcasecmp(ext, ".otf") == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
// Result numerically higher => better match. 0 == no match.
|
|
|
|
static int match_lang(char **langs, char *lang)
|
2011-01-11 23:37:02 +00:00
|
|
|
{
|
2012-08-19 16:01:30 +00:00
|
|
|
for (int idx = 0; langs && langs[idx]; idx++) {
|
|
|
|
if (lang && strcmp(langs[idx], lang) == 0)
|
|
|
|
return INT_MAX - idx;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the track wanted by the user.
|
|
|
|
* tid is the track ID requested by the user (-2: deselect, -1: default)
|
|
|
|
* lang is a string list, NULL is same as empty list
|
|
|
|
* Sort tracks based on the following criteria, and pick the first:
|
|
|
|
* 0) track matches tid (always wins)
|
|
|
|
* 1) track is external
|
2013-04-20 21:48:26 +00:00
|
|
|
* 1b) track was passed explicitly (is not an auto-loaded subtitle)
|
2012-08-19 16:01:30 +00:00
|
|
|
* 2) earlier match in lang list
|
|
|
|
* 3) track is marked default
|
|
|
|
* 4) lower track number
|
|
|
|
* If select_fallback is not set, 4) is only used to determine whether a
|
|
|
|
* matching track is preferred over another track. Otherwise, always pick a
|
|
|
|
* track (if nothing else matches, return the track with lowest ID).
|
|
|
|
*/
|
|
|
|
// Return whether t1 is preferred over t2
|
|
|
|
static bool compare_track(struct track *t1, struct track *t2, char **langs)
|
|
|
|
{
|
|
|
|
if (t1->is_external != t2->is_external)
|
|
|
|
return t1->is_external;
|
2013-04-20 21:48:26 +00:00
|
|
|
if (t1->auto_loaded != t2->auto_loaded)
|
|
|
|
return !t1->auto_loaded;
|
2012-08-19 16:01:30 +00:00
|
|
|
int l1 = match_lang(langs, t1->lang), l2 = match_lang(langs, t2->lang);
|
|
|
|
if (l1 != l2)
|
|
|
|
return l1 > l2;
|
|
|
|
if (t1->default_track != t2->default_track)
|
|
|
|
return t1->default_track;
|
2012-12-10 17:52:06 +00:00
|
|
|
if (t1->attached_picture != t2->attached_picture)
|
|
|
|
return !t1->attached_picture;
|
2012-08-19 16:01:30 +00:00
|
|
|
return t1->user_tid <= t2->user_tid;
|
|
|
|
}
|
|
|
|
static struct track *select_track(struct MPContext *mpctx,
|
2012-12-10 17:52:06 +00:00
|
|
|
enum stream_type type, int tid, char **langs)
|
2012-08-19 16:01:30 +00:00
|
|
|
{
|
|
|
|
if (tid == -2)
|
|
|
|
return NULL;
|
2012-12-10 17:52:06 +00:00
|
|
|
bool select_fallback = type == STREAM_VIDEO || type == STREAM_AUDIO;
|
2012-08-19 16:01:30 +00:00
|
|
|
struct track *pick = NULL;
|
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *track = mpctx->tracks[n];
|
|
|
|
if (track->type != type)
|
|
|
|
continue;
|
|
|
|
if (track->user_tid == tid)
|
|
|
|
return track;
|
|
|
|
if (!pick || compare_track(track, pick, langs))
|
|
|
|
pick = track;
|
2011-01-11 23:37:02 +00:00
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
if (pick && !select_fallback && !pick->is_external
|
|
|
|
&& !match_lang(langs, pick->lang) && !pick->default_track)
|
|
|
|
pick = NULL;
|
2013-07-27 19:24:54 +00:00
|
|
|
if (pick && pick->attached_picture && !mpctx->opts->audio_display)
|
2012-12-10 17:52:06 +00:00
|
|
|
pick = NULL;
|
2012-08-19 16:01:30 +00:00
|
|
|
return pick;
|
2011-01-11 23:37:02 +00:00
|
|
|
}
|
|
|
|
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
// Normally, video/audio/sub track selection is persistent across files. This
|
|
|
|
// code resets track selection if the new file has a different track layout.
|
|
|
|
static void check_previous_track_selection(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
|
|
|
|
if (!mpctx->track_layout_hash)
|
|
|
|
return;
|
|
|
|
|
|
|
|
char *h = track_layout_hash(mpctx);
|
|
|
|
if (strcmp(h, mpctx->track_layout_hash) != 0) {
|
|
|
|
// Reset selection, but only if they're not "auto" or "off".
|
|
|
|
if (opts->video_id >= 0)
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->video_id = -1;
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
if (opts->audio_id >= 0)
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->audio_id = -1;
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
if (opts->sub_id >= 0)
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->sub_id = -1;
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
talloc_free(mpctx->track_layout_hash);
|
|
|
|
mpctx->track_layout_hash = NULL;
|
|
|
|
}
|
|
|
|
talloc_free(h);
|
|
|
|
}
|
|
|
|
|
2013-07-02 12:00:24 +00:00
|
|
|
static int read_keys(void *ctx, int fd)
|
|
|
|
{
|
|
|
|
if (getch2(ctx))
|
|
|
|
return MP_INPUT_NOTHING;
|
|
|
|
return MP_INPUT_DEAD;
|
|
|
|
}
|
|
|
|
|
2012-08-03 05:47:11 +00:00
|
|
|
static void init_input(struct MPContext *mpctx)
|
|
|
|
{
|
2013-09-10 06:29:45 +00:00
|
|
|
mpctx->input = mp_input_init(mpctx->global);
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->slave_mode)
|
2012-08-03 05:47:11 +00:00
|
|
|
mp_input_add_cmd_fd(mpctx->input, 0, USE_FD0_CMD_SELECT, MP_INPUT_SLAVE_CMD_FUNC, NULL);
|
2013-07-27 19:24:54 +00:00
|
|
|
else if (mpctx->opts->consolecontrols)
|
2013-07-02 12:00:24 +00:00
|
|
|
mp_input_add_key_fd(mpctx->input, 0, 1, read_keys, NULL, mpctx->input);
|
2012-08-03 05:47:11 +00:00
|
|
|
// Set the libstream interrupt callback
|
|
|
|
stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input);
|
2013-03-04 13:23:06 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_COCOA
|
2013-05-23 07:44:56 +00:00
|
|
|
cocoa_set_input_context(mpctx->input);
|
2013-03-04 13:23:06 +00:00
|
|
|
#endif
|
2012-08-03 05:47:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void open_subtitles_from_options(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
// after reading video params we should load subtitles because
|
|
|
|
// we know fps so now we can adjust subtitle time to ~6 seconds AST
|
|
|
|
// check .sub
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->sub_name) {
|
|
|
|
for (int i = 0; mpctx->opts->sub_name[i] != NULL; ++i)
|
2013-09-07 18:19:57 +00:00
|
|
|
mp_add_subtitles(mpctx, mpctx->opts->sub_name[i]);
|
2012-08-03 05:47:11 +00:00
|
|
|
}
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->sub_auto) { // auto load sub file ...
|
|
|
|
char **tmp = find_text_subtitles(mpctx->opts, mpctx->filename);
|
2012-08-03 05:47:11 +00:00
|
|
|
int nsub = MP_TALLOC_ELEMS(tmp);
|
2013-04-20 21:48:26 +00:00
|
|
|
for (int i = 0; i < nsub; i++) {
|
2013-09-07 18:33:47 +00:00
|
|
|
char *filename = tmp[i];
|
|
|
|
for (int n = 0; n < mpctx->num_sources; n++) {
|
|
|
|
if (strcmp(mpctx->sources[n]->stream->url, filename) == 0)
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
struct track *track = mp_add_subtitles(mpctx, filename);
|
2013-04-20 21:48:26 +00:00
|
|
|
if (track)
|
|
|
|
track->auto_loaded = true;
|
2013-09-07 18:33:47 +00:00
|
|
|
skip:;
|
2013-04-20 21:48:26 +00:00
|
|
|
}
|
2012-08-03 05:47:11 +00:00
|
|
|
talloc_free(tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-08 12:59:49 +00:00
|
|
|
static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
|
|
|
char *demuxer_name, int stream_cache,
|
|
|
|
enum stream_type filter)
|
2012-08-19 16:11:53 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-19 16:11:53 +00:00
|
|
|
if (!filename)
|
2012-12-08 12:59:49 +00:00
|
|
|
return NULL;
|
2013-06-27 16:21:07 +00:00
|
|
|
char *disp_filename = filename;
|
|
|
|
if (strncmp(disp_filename, "memory://", 9) == 0)
|
|
|
|
disp_filename = "memory://"; // avoid noise
|
2013-07-27 19:24:54 +00:00
|
|
|
struct stream *stream = stream_open(filename, mpctx->opts);
|
2012-08-19 16:11:53 +00:00
|
|
|
if (!stream)
|
|
|
|
goto err_out;
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 16:49:09 +00:00
|
|
|
stream_enable_cache_percent(&stream, stream_cache,
|
2013-07-10 13:03:54 +00:00
|
|
|
opts->stream_cache_def_size,
|
2012-11-17 17:12:13 +00:00
|
|
|
opts->stream_cache_min_percent,
|
|
|
|
opts->stream_cache_seek_min_percent);
|
2013-06-22 00:09:52 +00:00
|
|
|
struct demuxer_params params = {
|
|
|
|
.ass_library = mpctx->ass_library, // demux_libass requires it
|
|
|
|
};
|
2012-08-19 16:11:53 +00:00
|
|
|
struct demuxer *demuxer =
|
2013-07-27 19:24:54 +00:00
|
|
|
demux_open(stream, demuxer_name, ¶ms, mpctx->opts);
|
2012-08-19 16:11:53 +00:00
|
|
|
if (!demuxer) {
|
|
|
|
free_stream(stream);
|
|
|
|
goto err_out;
|
|
|
|
}
|
2012-12-08 12:59:49 +00:00
|
|
|
struct track *first = NULL;
|
2012-08-19 16:11:53 +00:00
|
|
|
for (int n = 0; n < demuxer->num_streams; n++) {
|
2013-07-22 22:45:23 +00:00
|
|
|
struct sh_stream *sh = demuxer->streams[n];
|
|
|
|
if (sh->type == filter) {
|
|
|
|
struct track *t = add_stream_track(mpctx, sh, false);
|
2012-08-19 16:11:53 +00:00
|
|
|
t->is_external = true;
|
2013-06-27 16:21:07 +00:00
|
|
|
t->title = talloc_strdup(t, disp_filename);
|
2012-11-15 19:26:52 +00:00
|
|
|
t->external_filename = talloc_strdup(t, filename);
|
2012-12-08 12:59:49 +00:00
|
|
|
first = t;
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-08 12:59:49 +00:00
|
|
|
if (!first) {
|
|
|
|
free_demuxer(demuxer);
|
2013-07-12 19:57:27 +00:00
|
|
|
free_stream(stream);
|
2012-08-19 16:11:53 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "No streams added from file %s.\n",
|
2013-06-27 16:21:07 +00:00
|
|
|
disp_filename);
|
2012-12-08 12:59:49 +00:00
|
|
|
goto err_out;
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
|
2012-12-08 12:59:49 +00:00
|
|
|
return first;
|
2012-08-19 16:11:53 +00:00
|
|
|
|
|
|
|
err_out:
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Can not open external file %s.\n",
|
2013-06-27 16:21:07 +00:00
|
|
|
disp_filename);
|
2012-12-08 12:59:49 +00:00
|
|
|
return false;
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void open_audiofiles_from_options(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-19 16:11:53 +00:00
|
|
|
open_external_file(mpctx, opts->audio_stream, opts->audio_demuxer_name,
|
|
|
|
opts->audio_stream_cache, STREAM_AUDIO);
|
|
|
|
}
|
|
|
|
|
2013-09-07 18:19:57 +00:00
|
|
|
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename)
|
2012-08-19 16:11:53 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-06-22 00:09:52 +00:00
|
|
|
return open_external_file(mpctx, filename, opts->sub_demuxer_name, 0,
|
|
|
|
STREAM_SUB);
|
2012-08-19 16:11:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-27 16:21:07 +00:00
|
|
|
static void open_subtitles_from_resolve(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-06-27 16:21:07 +00:00
|
|
|
struct mp_resolve_result *res = mpctx->resolve_result;
|
|
|
|
if (!res)
|
|
|
|
return;
|
|
|
|
for (int n = 0; n < res->num_subs; n++) {
|
|
|
|
struct mp_resolve_sub *sub = res->subs[n];
|
|
|
|
char *s = talloc_strdup(NULL, sub->url);
|
|
|
|
if (!s)
|
|
|
|
s = talloc_asprintf(NULL, "memory://%s", sub->data);
|
|
|
|
struct track *t =
|
|
|
|
open_external_file(mpctx, s, opts->sub_demuxer_name, 0, STREAM_SUB);
|
|
|
|
talloc_free(s);
|
|
|
|
if (t)
|
|
|
|
t->lang = talloc_strdup(t, sub->lang);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-03 05:47:11 +00:00
|
|
|
static void print_timeline(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->timeline) {
|
|
|
|
int part_count = mpctx->num_timeline_parts;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Timeline contains %d parts from %d "
|
|
|
|
"sources. Total length %.3f seconds.\n", part_count,
|
|
|
|
mpctx->num_sources, mpctx->timeline[part_count].start);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Source files:\n");
|
|
|
|
for (int i = 0; i < mpctx->num_sources; i++)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "%d: %s\n", i,
|
2012-08-19 15:58:58 +00:00
|
|
|
mpctx->sources[i]->filename);
|
2012-08-03 05:47:11 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Timeline parts: (number, start, "
|
|
|
|
"source_start, source):\n");
|
|
|
|
for (int i = 0; i < part_count; i++) {
|
|
|
|
struct timeline_part *p = mpctx->timeline + i;
|
2012-08-19 15:58:58 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "%3d %9.3f %9.3f %p/%s\n", i, p->start,
|
|
|
|
p->source_start, p->source, p->source->filename);
|
2012-08-03 05:47:11 +00:00
|
|
|
}
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "END %9.3f\n",
|
|
|
|
mpctx->timeline[part_count].start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_subtitle_fonts_from_sources(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_ASS
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->ass_enabled) {
|
2012-08-03 05:47:11 +00:00
|
|
|
for (int j = 0; j < mpctx->num_sources; j++) {
|
2012-08-19 15:58:58 +00:00
|
|
|
struct demuxer *d = mpctx->sources[j];
|
2012-08-03 05:47:11 +00:00
|
|
|
for (int i = 0; i < d->num_attachments; i++) {
|
|
|
|
struct demux_attachment *att = d->attachments + i;
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->use_embedded_fonts && attachment_is_font(att))
|
2012-08-03 05:47:11 +00:00
|
|
|
ass_add_font(mpctx->ass_library, att->name, att->data,
|
|
|
|
att->data_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-25 14:47:50 +00:00
|
|
|
|
|
|
|
// libass seems to misbehave if fonts are changed while a renderer
|
|
|
|
// exists, so we (re)create the renderer after fonts are set.
|
|
|
|
assert(!mpctx->osd->ass_renderer);
|
|
|
|
mpctx->osd->ass_renderer = ass_renderer_init(mpctx->osd->ass_library);
|
|
|
|
if (mpctx->osd->ass_renderer)
|
2013-01-06 15:06:34 +00:00
|
|
|
mp_ass_configure_fonts(mpctx->osd->ass_renderer,
|
2013-07-27 19:24:54 +00:00
|
|
|
mpctx->opts->sub_text_style);
|
2012-08-03 05:47:11 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-10-13 15:09:35 +00:00
|
|
|
static struct mp_resolve_result *resolve_url(const char *filename,
|
|
|
|
struct MPOpts *opts)
|
|
|
|
{
|
2013-06-27 16:21:07 +00:00
|
|
|
#if defined(CONFIG_LIBQUVI) || defined(CONFIG_LIBQUVI9)
|
2012-10-13 15:09:35 +00:00
|
|
|
return mp_resolve_quvi(filename, opts);
|
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-09-10 15:21:59 +00:00
|
|
|
static void print_resolve_contents(struct mp_log *log,
|
|
|
|
struct mp_resolve_result *res)
|
|
|
|
{
|
|
|
|
mp_msg_log(log, MSGL_V, "Resolve:\n");
|
|
|
|
mp_msg_log(log, MSGL_V, " title: %s\n", res->title);
|
|
|
|
mp_msg_log(log, MSGL_V, " url: %s\n", res->url);
|
|
|
|
for (int n = 0; n < res->num_srcs; n++) {
|
|
|
|
mp_msg_log(log, MSGL_V, " source %d:\n", n);
|
|
|
|
if (res->srcs[n]->url)
|
|
|
|
mp_msg_log(log, MSGL_V, " url: %s\n", res->srcs[n]->url);
|
|
|
|
if (res->srcs[n]->encid)
|
|
|
|
mp_msg_log(log, MSGL_V, " encid: %s\n", res->srcs[n]->encid);
|
|
|
|
}
|
|
|
|
for (int n = 0; n < res->num_subs; n++) {
|
|
|
|
mp_msg_log(log, MSGL_V, " subtitle %d:\n", n);
|
|
|
|
if (res->subs[n]->url)
|
|
|
|
mp_msg_log(log, MSGL_V, " url: %s\n", res->subs[n]->url);
|
|
|
|
if (res->subs[n]->lang)
|
|
|
|
mp_msg_log(log, MSGL_V, " lang: %s\n", res->subs[n]->lang);
|
|
|
|
if (res->subs[n]->data) {
|
2013-09-14 18:39:14 +00:00
|
|
|
mp_msg_log(log, MSGL_V, " data: %zd bytes\n",
|
2013-09-10 15:21:59 +00:00
|
|
|
strlen(res->subs[n]->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (res->playlist) {
|
|
|
|
mp_msg_log(log, MSGL_V, " playlist with %d entries\n",
|
|
|
|
playlist_entry_count(res->playlist));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
// Waiting for the slave master to send us a new file to play.
|
|
|
|
static void idle_loop(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
// ================= idle loop (STOP state) =========================
|
2013-10-03 22:24:17 +00:00
|
|
|
bool need_reinit = true;
|
2013-07-27 19:24:54 +00:00
|
|
|
while (mpctx->opts->player_idle_mode && !mpctx->playlist->current
|
2012-08-04 01:46:11 +00:00
|
|
|
&& mpctx->stop_play != PT_QUIT)
|
|
|
|
{
|
2013-10-03 22:24:17 +00:00
|
|
|
if (need_reinit)
|
|
|
|
handle_force_window(mpctx, true);
|
|
|
|
need_reinit = false;
|
2013-10-01 23:15:59 +00:00
|
|
|
int uninit = INITIALIZED_AO;
|
|
|
|
if (!mpctx->opts->force_vo)
|
|
|
|
uninit |= INITIALIZED_VO;
|
|
|
|
uninit_player(mpctx, uninit);
|
2013-10-03 22:24:17 +00:00
|
|
|
handle_force_window(mpctx, false);
|
2013-10-02 18:38:10 +00:00
|
|
|
if (mpctx->video_out)
|
|
|
|
vo_check_events(mpctx->video_out);
|
|
|
|
update_osd_msg(mpctx);
|
|
|
|
handle_osd_redraw(mpctx);
|
|
|
|
mp_cmd_t *cmd = mp_input_get_cmd(mpctx->input,
|
|
|
|
get_wakeup_period(mpctx) * 1000,
|
|
|
|
false);
|
|
|
|
if (cmd)
|
|
|
|
run_command(mpctx, cmd);
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
mp_cmd_free(cmd);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
mp_flush_events(mpctx);
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-11 20:40:46 +00:00
|
|
|
static void stream_dump(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-05-11 20:40:46 +00:00
|
|
|
char *filename = opts->stream_dump;
|
|
|
|
stream_t *stream = mpctx->stream;
|
|
|
|
assert(stream && filename);
|
|
|
|
|
|
|
|
stream_set_capture_file(stream, filename);
|
|
|
|
|
|
|
|
while (mpctx->stop_play == KEEP_PLAYING && !stream->eof) {
|
|
|
|
if (!opts->quiet && ((stream->pos / (1024 * 1024)) % 2) == 1) {
|
|
|
|
uint64_t pos = stream->pos - stream->start_pos;
|
|
|
|
uint64_t end = stream->end_pos - stream->start_pos;
|
|
|
|
char *line = talloc_asprintf(NULL, "Dumping %lld/%lld...",
|
|
|
|
(long long int)pos, (long long int)end);
|
|
|
|
write_status_line(mpctx, line);
|
|
|
|
talloc_free(line);
|
|
|
|
}
|
|
|
|
stream_fill_buffer(stream);
|
|
|
|
for (;;) {
|
|
|
|
mp_cmd_t *cmd = mp_input_get_cmd(mpctx->input, 0, false);
|
|
|
|
if (!cmd)
|
|
|
|
break;
|
|
|
|
run_command(mpctx, cmd);
|
|
|
|
talloc_free(cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-25 18:40:21 +00:00
|
|
|
// Replace the current playlist entry with playlist contents. Moves the entries
|
|
|
|
// from the given playlist pl, so the entries don't actually need to be copied.
|
|
|
|
static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
|
|
|
|
{
|
|
|
|
if (mpctx->demuxer->playlist->first) {
|
|
|
|
playlist_transfer_entries(mpctx->playlist, mpctx->demuxer->playlist);
|
|
|
|
if (mpctx->playlist->current)
|
|
|
|
playlist_remove(mpctx->playlist, mpctx->playlist->current);
|
|
|
|
} else {
|
|
|
|
MP_WARN(mpctx, "Empty playlist!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
// Start playing the current playlist entry.
|
|
|
|
// Handle initialization and deinitialization.
|
|
|
|
static void play_current_file(struct MPContext *mpctx)
|
2012-08-03 05:47:11 +00:00
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
double playback_start = -1e100;
|
2001-08-30 21:14:34 +00:00
|
|
|
|
2013-09-07 18:03:13 +00:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_PLAYBACK;
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
|
|
|
|
mp_notify(mpctx, MP_EVENT_START_FILE, NULL);
|
|
|
|
mp_flush_events(mpctx);
|
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
mpctx->stop_play = 0;
|
|
|
|
mpctx->filename = NULL;
|
2013-09-29 19:10:36 +00:00
|
|
|
mpctx->shown_aframes = 0;
|
|
|
|
mpctx->shown_vframes = 0;
|
2012-10-13 15:09:35 +00:00
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
if (mpctx->playlist->current)
|
|
|
|
mpctx->filename = mpctx->playlist->current->filename;
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
if (!mpctx->filename)
|
|
|
|
goto terminate_playback;
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-09-14 15:51:26 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
|
|
|
#endif
|
|
|
|
|
2012-09-25 01:24:38 +00:00
|
|
|
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION;
|
|
|
|
|
2013-09-03 22:31:01 +00:00
|
|
|
if (opts->reset_options) {
|
|
|
|
for (int n = 0; opts->reset_options[n]; n++) {
|
|
|
|
const char *opt = opts->reset_options[n];
|
2013-09-03 22:43:14 +00:00
|
|
|
if (opt[0]) {
|
|
|
|
if (strcmp(opt, "all") == 0) {
|
|
|
|
m_config_backup_all_opts(mpctx->mconfig);
|
|
|
|
} else {
|
|
|
|
m_config_backup_opt(mpctx->mconfig, opt);
|
|
|
|
}
|
2013-09-03 22:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
load_per_protocol_config(mpctx->mconfig, mpctx->filename);
|
|
|
|
load_per_extension_config(mpctx->mconfig, mpctx->filename);
|
2013-03-08 01:08:02 +00:00
|
|
|
load_per_file_config(mpctx->mconfig, mpctx->filename, opts->use_filedir_conf);
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
|
2013-03-04 21:41:27 +00:00
|
|
|
if (opts->vo.video_driver_list)
|
2011-08-07 00:58:12 +00:00
|
|
|
load_per_output_config(mpctx->mconfig, PROFILE_CFG_VO,
|
2013-07-21 19:17:48 +00:00
|
|
|
opts->vo.video_driver_list[0].name);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (opts->audio_driver_list)
|
|
|
|
load_per_output_config(mpctx->mconfig, PROFILE_CFG_AO,
|
2013-07-21 19:33:17 +00:00
|
|
|
opts->audio_driver_list[0].name);
|
2011-08-07 00:58:12 +00:00
|
|
|
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
if (opts->position_resume)
|
|
|
|
load_playback_resume(mpctx->mconfig, mpctx->filename);
|
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
load_per_file_options(mpctx->mconfig, mpctx->playlist->current->params,
|
2012-08-04 00:17:40 +00:00
|
|
|
mpctx->playlist->current->num_params);
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// We must enable getch2 here to be able to interrupt network connection
|
|
|
|
// or cache filling
|
2013-03-08 01:08:02 +00:00
|
|
|
if (opts->consolecontrols && !opts->slave_mode) {
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->initialized_flags & INITIALIZED_GETCH2)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
|
|
|
|
"WARNING: getch2_init called twice!\n");
|
|
|
|
else
|
|
|
|
getch2_enable(); // prepare stdin for hotkeys...
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_GETCH2;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG2, "\n[[[init getch2]]]\n");
|
|
|
|
}
|
|
|
|
|
2011-07-23 02:13:25 +00:00
|
|
|
#ifdef CONFIG_ASS
|
2012-10-11 00:23:29 +00:00
|
|
|
if (opts->ass_style_override)
|
|
|
|
ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
|
2011-07-23 02:13:25 +00:00
|
|
|
#endif
|
2010-02-19 23:35:21 +00:00
|
|
|
|
2013-09-13 19:00:45 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Playing: %s\n", mpctx->filename);
|
2001-08-23 11:33:58 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
//============ Open & Sync STREAM --- fork cache2 ====================
|
|
|
|
|
2012-08-19 16:07:06 +00:00
|
|
|
assert(mpctx->stream == NULL);
|
|
|
|
assert(mpctx->demuxer == NULL);
|
|
|
|
assert(mpctx->sh_audio == NULL);
|
|
|
|
assert(mpctx->sh_video == NULL);
|
2012-08-19 16:01:30 +00:00
|
|
|
assert(mpctx->sh_sub == NULL);
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2012-10-13 15:09:35 +00:00
|
|
|
char *stream_filename = mpctx->filename;
|
|
|
|
mpctx->resolve_result = resolve_url(stream_filename, opts);
|
2013-06-27 16:21:07 +00:00
|
|
|
if (mpctx->resolve_result) {
|
2013-09-10 15:21:59 +00:00
|
|
|
print_resolve_contents(mpctx->log, mpctx->resolve_result);
|
2013-06-27 16:21:07 +00:00
|
|
|
if (mpctx->resolve_result->playlist) {
|
2013-08-25 18:40:21 +00:00
|
|
|
transfer_playlist(mpctx, mpctx->resolve_result->playlist);
|
2013-06-27 16:21:07 +00:00
|
|
|
goto terminate_playback;
|
|
|
|
}
|
2012-10-13 15:09:35 +00:00
|
|
|
stream_filename = mpctx->resolve_result->url;
|
2013-06-27 16:21:07 +00:00
|
|
|
}
|
2013-07-11 19:10:42 +00:00
|
|
|
mpctx->stream = stream_open(stream_filename, opts);
|
2011-08-07 00:58:12 +00:00
|
|
|
if (!mpctx->stream) { // error...
|
2012-11-09 00:06:43 +00:00
|
|
|
demux_was_interrupted(mpctx);
|
2012-08-03 06:18:40 +00:00
|
|
|
goto terminate_playback;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_STREAM;
|
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->stream->start_pos += opts->seek_to_byte;
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2013-05-11 20:40:46 +00:00
|
|
|
if (opts->stream_dump && opts->stream_dump[0]) {
|
|
|
|
stream_dump(mpctx);
|
|
|
|
goto terminate_playback;
|
|
|
|
}
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// CACHE2: initial prefill: 20% later: 5% (should be set by -cacheopts)
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 16:49:09 +00:00
|
|
|
int res = stream_enable_cache_percent(&mpctx->stream,
|
2012-11-17 17:12:13 +00:00
|
|
|
opts->stream_cache_size,
|
2013-07-10 13:03:54 +00:00
|
|
|
opts->stream_cache_def_size,
|
2012-11-17 17:12:13 +00:00
|
|
|
opts->stream_cache_min_percent,
|
|
|
|
opts->stream_cache_seek_min_percent);
|
|
|
|
if (res == 0)
|
|
|
|
if (demux_was_interrupted(mpctx))
|
|
|
|
goto terminate_playback;
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2013-05-11 20:19:33 +00:00
|
|
|
stream_set_capture_file(mpctx->stream, opts->stream_capture);
|
|
|
|
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 16:49:09 +00:00
|
|
|
#ifdef CONFIG_DVBIN
|
|
|
|
goto_reopen_demuxer: ;
|
|
|
|
#endif
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
//============ Open DEMUXERS --- DETECT file type =======================
|
2001-08-22 21:35:44 +00:00
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->audio_delay = opts->audio_delay;
|
|
|
|
|
2013-07-22 12:48:41 +00:00
|
|
|
mpctx->demuxer = demux_open(mpctx->stream, opts->demuxer_name, NULL, opts);
|
2012-08-19 16:07:06 +00:00
|
|
|
mpctx->master_demuxer = mpctx->demuxer;
|
2011-08-07 00:58:12 +00:00
|
|
|
if (!mpctx->demuxer) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Failed to recognize file format.\n");
|
2012-08-03 06:18:40 +00:00
|
|
|
goto terminate_playback;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2009-03-29 19:45:06 +00:00
|
|
|
|
2013-08-25 18:40:21 +00:00
|
|
|
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, mpctx->demuxer);
|
|
|
|
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_DEMUXER;
|
|
|
|
|
|
|
|
if (mpctx->demuxer->playlist) {
|
|
|
|
if (mpctx->demuxer->stream->safe_origin || opts->load_unsafe_playlists) {
|
|
|
|
transfer_playlist(mpctx, mpctx->demuxer->playlist);
|
|
|
|
} else {
|
|
|
|
MP_ERR(mpctx, "\nThis looks like a playlist, but playlist support "
|
|
|
|
"will not be used automatically.\nThe main problem with "
|
|
|
|
"playlist safety is that playlist entries can be arbitrary,\n"
|
|
|
|
"and an attacker could make mpv poke around in your local "
|
|
|
|
"filesystem or network.\nUse --playlist=file or the "
|
|
|
|
"--load-unsafe-playlists option to load them anyway.\n");
|
|
|
|
}
|
|
|
|
goto terminate_playback;
|
|
|
|
}
|
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->demuxer->matroska_data.ordered_chapters)
|
|
|
|
build_ordered_chapter_timeline(mpctx);
|
EDL: add support for new EDL file format
The timeline code previously added to support Matroska ordered
chapters allows constructing a playback timeline from segments picked
from multiple source files. Add support for a new EDL format to make
this machinery available for use with file formats other than Matroska
and in a manner easier to use than creating files with ordered
chapters.
Unlike the old -edl option which specifies an additional file with
edits to apply to the video file given as the main argument, the new
EDL format is used by giving only the EDL file as the file to play;
that file then contains the filename(s) to use as source files where
actual video segments come from. Filename paths in the EDL file are
ignored. Currently the source files are only searched for in the
directory of the EDL file; support for a search path option will
likely be added in the future.
Format of the EDL files
The first line in the file must be "mplayer EDL file, version 2".
The rest of the lines belong to one of these classes:
1) lines specifying source files
2) empty lines
3) lines specifying timeline segments.
Lines beginning with '<' specify source files. These lines first
contain an identifier used to refer to the source file later, then the
filename separated by whitespace. The identifier must start with a
letter. Filenames that start or end with whitespace or contain
newlines are not supported.
On other lines '#' characters delimit comments. Lines that contain
only whitespace after comments have been removed are ignored.
Timeline segments must appear in the file in chronological order. Each
segment has the following information associated with it:
- duration
- output start time
- output end time (= output start time + duration)
- source id (specifies the file the content of the segment comes from)
- source start time (timestamp in the source file)
- source end time (= source start time + duration)
The output timestamps must form a continuous timeline from 0 to the
end of the last segment, such that each new segment starts from the
time the previous one ends at. Source files and times may change
arbitrarily between segments.
The general format for lines specifying timeline segments is
[output time info] source_id [source time info]
source_id must be an identifier defined on a '<' line. Both the time
info parts consists of zero or more of the following elements:
1) timestamp
2) -timestamp
3) +duration
4) *
5) -*
, where "timestamp" and "duration" are decimal numbers (computations
are done with nanosecond precision). Whitespace around "+" and "-" is
optional. 1) and 2) specify start and end time of the segment on
output or source side. 3) specifies duration; the semantics are the
same whether this appears on output or source side. 4) and 5) are
ignored on the output side (they're always implicitly assumed). On the
source side 4) specifies that the segment starts where the previous
segment _using this source_ ended; if there was no previous segment
time 0 is used. 5) specifies that the segment ends where the next
segment using this source starts.
Redundant information may be omitted. It will be filled in using the
following rules:
- output start for first segment is 0
- two of [output start, output end, duration] imply third
- two of [source start, source end, duration] imply third
- output start = output end of previous segment
- output end = output start of next segment
- if "*", source start = source end of earlier segment
- if "-*", source end = source start of a later segment
As a special rule, a last zero-duration segment without a source
specification may appear. This will produce no corresponding segment
in the resulting timeline, but can be used as syntax to specify the
end time of the timeline (with effect equal to adding -time on the
previous line).
Examples:
----- begin -----
mplayer EDL file, version 2
< id1 filename
0 id1 123
100 id1 456
200 id1 789
300
----- end -----
All segments come from the source file "filename". First segment
(output time 0-100) comes from time 123-223, second 456-556, third
789-889.
----- begin -----
mplayer EDL file, version 2
< f filename
f 60-120
f 600-660
f 30- 90
----- end -----
Play first seconds 60-120 from the file, then 600-660, then 30-90.
----- begin -----
mplayer EDL file, version 2
< id1 filename1
< id2 filename2
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
----- end -----
This plays time 0-10 from filename1, then 0-10 from filename1, then
10-20 from filename1, then 10-20 from filename2, then 20-30 from
filename1, then 20-30 from filename2.
----- begin -----
mplayer EDL file, version 2
< t1 filename1
< t2 filename2
t1 * +2 # segment 1
+2 t2 100 # segment 2
t1 * # segment 3
t2 *-* # segment 4
t1 3 -* # segment 5
+0.111111 t2 102.5 # segment 6
7.37 t1 5 +1 # segment 7
----- end -----
This rather pathological example illustrates the rules for filling in
implied data. All the values can be determined by recursively applying
the rules given above, and the full end result is this:
+2 0-2 t1 0-2 # segment 1
+2 2-4 t2 100-102 # segment 2
+0.758889 4-4.758889 t1 2-2.758889 # segment 3
+0.5 4.4758889-5.258889 t2 102-102.5 # segment 4
+2 5.258889-7.258889 t1 3-5 # segment 5
+0.111111 7.258889-7.37 t2 102.5-102.611111 # segment 6
+1 7.37-8.37 t1 5-6 # segment 7
2011-02-14 11:05:35 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->demuxer->type == DEMUXER_TYPE_EDL)
|
|
|
|
build_edl_timeline(mpctx);
|
EDL: add support for new EDL file format
The timeline code previously added to support Matroska ordered
chapters allows constructing a playback timeline from segments picked
from multiple source files. Add support for a new EDL format to make
this machinery available for use with file formats other than Matroska
and in a manner easier to use than creating files with ordered
chapters.
Unlike the old -edl option which specifies an additional file with
edits to apply to the video file given as the main argument, the new
EDL format is used by giving only the EDL file as the file to play;
that file then contains the filename(s) to use as source files where
actual video segments come from. Filename paths in the EDL file are
ignored. Currently the source files are only searched for in the
directory of the EDL file; support for a search path option will
likely be added in the future.
Format of the EDL files
The first line in the file must be "mplayer EDL file, version 2".
The rest of the lines belong to one of these classes:
1) lines specifying source files
2) empty lines
3) lines specifying timeline segments.
Lines beginning with '<' specify source files. These lines first
contain an identifier used to refer to the source file later, then the
filename separated by whitespace. The identifier must start with a
letter. Filenames that start or end with whitespace or contain
newlines are not supported.
On other lines '#' characters delimit comments. Lines that contain
only whitespace after comments have been removed are ignored.
Timeline segments must appear in the file in chronological order. Each
segment has the following information associated with it:
- duration
- output start time
- output end time (= output start time + duration)
- source id (specifies the file the content of the segment comes from)
- source start time (timestamp in the source file)
- source end time (= source start time + duration)
The output timestamps must form a continuous timeline from 0 to the
end of the last segment, such that each new segment starts from the
time the previous one ends at. Source files and times may change
arbitrarily between segments.
The general format for lines specifying timeline segments is
[output time info] source_id [source time info]
source_id must be an identifier defined on a '<' line. Both the time
info parts consists of zero or more of the following elements:
1) timestamp
2) -timestamp
3) +duration
4) *
5) -*
, where "timestamp" and "duration" are decimal numbers (computations
are done with nanosecond precision). Whitespace around "+" and "-" is
optional. 1) and 2) specify start and end time of the segment on
output or source side. 3) specifies duration; the semantics are the
same whether this appears on output or source side. 4) and 5) are
ignored on the output side (they're always implicitly assumed). On the
source side 4) specifies that the segment starts where the previous
segment _using this source_ ended; if there was no previous segment
time 0 is used. 5) specifies that the segment ends where the next
segment using this source starts.
Redundant information may be omitted. It will be filled in using the
following rules:
- output start for first segment is 0
- two of [output start, output end, duration] imply third
- two of [source start, source end, duration] imply third
- output start = output end of previous segment
- output end = output start of next segment
- if "*", source start = source end of earlier segment
- if "-*", source end = source start of a later segment
As a special rule, a last zero-duration segment without a source
specification may appear. This will produce no corresponding segment
in the resulting timeline, but can be used as syntax to specify the
end time of the timeline (with effect equal to adding -time on the
previous line).
Examples:
----- begin -----
mplayer EDL file, version 2
< id1 filename
0 id1 123
100 id1 456
200 id1 789
300
----- end -----
All segments come from the source file "filename". First segment
(output time 0-100) comes from time 123-223, second 456-556, third
789-889.
----- begin -----
mplayer EDL file, version 2
< f filename
f 60-120
f 600-660
f 30- 90
----- end -----
Play first seconds 60-120 from the file, then 600-660, then 30-90.
----- begin -----
mplayer EDL file, version 2
< id1 filename1
< id2 filename2
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
----- end -----
This plays time 0-10 from filename1, then 0-10 from filename1, then
10-20 from filename1, then 10-20 from filename2, then 20-30 from
filename1, then 20-30 from filename2.
----- begin -----
mplayer EDL file, version 2
< t1 filename1
< t2 filename2
t1 * +2 # segment 1
+2 t2 100 # segment 2
t1 * # segment 3
t2 *-* # segment 4
t1 3 -* # segment 5
+0.111111 t2 102.5 # segment 6
7.37 t1 5 +1 # segment 7
----- end -----
This rather pathological example illustrates the rules for filling in
implied data. All the values can be determined by recursively applying
the rules given above, and the full end result is this:
+2 0-2 t1 0-2 # segment 1
+2 2-4 t2 100-102 # segment 2
+0.758889 4-4.758889 t1 2-2.758889 # segment 3
+0.5 4.4758889-5.258889 t2 102-102.5 # segment 4
+2 5.258889-7.258889 t1 3-5 # segment 5
+0.111111 7.258889-7.37 t2 102.5-102.611111 # segment 6
+1 7.37-8.37 t1 5-6 # segment 7
2011-02-14 11:05:35 +00:00
|
|
|
|
2012-01-01 16:45:24 +00:00
|
|
|
if (mpctx->demuxer->type == DEMUXER_TYPE_CUE)
|
|
|
|
build_cue_timeline(mpctx);
|
|
|
|
|
2012-08-03 05:47:11 +00:00
|
|
|
print_timeline(mpctx);
|
EDL: add support for new EDL file format
The timeline code previously added to support Matroska ordered
chapters allows constructing a playback timeline from segments picked
from multiple source files. Add support for a new EDL format to make
this machinery available for use with file formats other than Matroska
and in a manner easier to use than creating files with ordered
chapters.
Unlike the old -edl option which specifies an additional file with
edits to apply to the video file given as the main argument, the new
EDL format is used by giving only the EDL file as the file to play;
that file then contains the filename(s) to use as source files where
actual video segments come from. Filename paths in the EDL file are
ignored. Currently the source files are only searched for in the
directory of the EDL file; support for a search path option will
likely be added in the future.
Format of the EDL files
The first line in the file must be "mplayer EDL file, version 2".
The rest of the lines belong to one of these classes:
1) lines specifying source files
2) empty lines
3) lines specifying timeline segments.
Lines beginning with '<' specify source files. These lines first
contain an identifier used to refer to the source file later, then the
filename separated by whitespace. The identifier must start with a
letter. Filenames that start or end with whitespace or contain
newlines are not supported.
On other lines '#' characters delimit comments. Lines that contain
only whitespace after comments have been removed are ignored.
Timeline segments must appear in the file in chronological order. Each
segment has the following information associated with it:
- duration
- output start time
- output end time (= output start time + duration)
- source id (specifies the file the content of the segment comes from)
- source start time (timestamp in the source file)
- source end time (= source start time + duration)
The output timestamps must form a continuous timeline from 0 to the
end of the last segment, such that each new segment starts from the
time the previous one ends at. Source files and times may change
arbitrarily between segments.
The general format for lines specifying timeline segments is
[output time info] source_id [source time info]
source_id must be an identifier defined on a '<' line. Both the time
info parts consists of zero or more of the following elements:
1) timestamp
2) -timestamp
3) +duration
4) *
5) -*
, where "timestamp" and "duration" are decimal numbers (computations
are done with nanosecond precision). Whitespace around "+" and "-" is
optional. 1) and 2) specify start and end time of the segment on
output or source side. 3) specifies duration; the semantics are the
same whether this appears on output or source side. 4) and 5) are
ignored on the output side (they're always implicitly assumed). On the
source side 4) specifies that the segment starts where the previous
segment _using this source_ ended; if there was no previous segment
time 0 is used. 5) specifies that the segment ends where the next
segment using this source starts.
Redundant information may be omitted. It will be filled in using the
following rules:
- output start for first segment is 0
- two of [output start, output end, duration] imply third
- two of [source start, source end, duration] imply third
- output start = output end of previous segment
- output end = output start of next segment
- if "*", source start = source end of earlier segment
- if "-*", source end = source start of a later segment
As a special rule, a last zero-duration segment without a source
specification may appear. This will produce no corresponding segment
in the resulting timeline, but can be used as syntax to specify the
end time of the timeline (with effect equal to adding -time on the
previous line).
Examples:
----- begin -----
mplayer EDL file, version 2
< id1 filename
0 id1 123
100 id1 456
200 id1 789
300
----- end -----
All segments come from the source file "filename". First segment
(output time 0-100) comes from time 123-223, second 456-556, third
789-889.
----- begin -----
mplayer EDL file, version 2
< f filename
f 60-120
f 600-660
f 30- 90
----- end -----
Play first seconds 60-120 from the file, then 600-660, then 30-90.
----- begin -----
mplayer EDL file, version 2
< id1 filename1
< id2 filename2
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
----- end -----
This plays time 0-10 from filename1, then 0-10 from filename1, then
10-20 from filename1, then 10-20 from filename2, then 20-30 from
filename1, then 20-30 from filename2.
----- begin -----
mplayer EDL file, version 2
< t1 filename1
< t2 filename2
t1 * +2 # segment 1
+2 t2 100 # segment 2
t1 * # segment 3
t2 *-* # segment 4
t1 3 -* # segment 5
+0.111111 t2 102.5 # segment 6
7.37 t1 5 +1 # segment 7
----- end -----
This rather pathological example illustrates the rules for filling in
implied data. All the values can be determined by recursively applying
the rules given above, and the full end result is this:
+2 0-2 t1 0-2 # segment 1
+2 2-4 t2 100-102 # segment 2
+0.758889 4-4.758889 t1 2-2.758889 # segment 3
+0.5 4.4758889-5.258889 t2 102-102.5 # segment 4
+2 5.258889-7.258889 t1 3-5 # segment 5
+0.111111 7.258889-7.37 t2 102.5-102.611111 # segment 6
+1 7.37-8.37 t1 5-6 # segment 7
2011-02-14 11:05:35 +00:00
|
|
|
|
2013-04-14 00:35:48 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->demuxer = mpctx->timeline[0].source;
|
2013-04-14 00:35:48 +00:00
|
|
|
main_is_ok: ;
|
2012-08-19 16:01:30 +00:00
|
|
|
}
|
core: fix DVD subtitle selection
Add all subtitle tracks as reported by libdvdread at playback start.
Display language for subtitle and audio tracks. This commit restores
these features to the state when demux_mpg was default for DVD playback,
and makes them work with demux_lavf and the recent changes to subtitle
selection in the frontend.
demux_mpg, which was the default demuxer for DVD playback, reordered
the subtitle streams according to the "logical" subtitle track number,
which conforms to the track layout reported by libdvdread, and is what
stream_dvd expects for the STREAM_CTRL_GET_LANG call. demux_lavf, on
the other hand, adds the streams in the order it encounters them in
the MPEG stream. It seems this order is essentially random, and can't
be mapped easily to what stream_dvd expects.
Solve this by making demux_lavf hand out the MPEG stream IDs (using the
demuxer_id field). The MPEG IDs are mapped by mplayer.c by special
casing DVD playback (map_id_from/to_demuxer() functions). This mapping
is essentially the same what demux_mpg did. Making demux_lavf reorder
the streams is out of the question, because its stream handling is
already messy enough.
(Note that demux_lavf doesn't export stream IDs for other formats,
because most time libavformat demuxers do not set AVStream.id, and we
don't know which demuxers do. But we know that MPEG is safe.)
Another major complication is that subtitle tracks are added lazily, as
soon as the demuxer encounters the first subtitle packet for a given
subtitle stream. Add the streams in advance. If a yet non-existent
stream is selected, demux_lavf must be made to auto-select that subtitle
stream as soon as it is added. Otherwise, the first subtitle packet
would be lost. This is done by DEMUXER_CTRL_PRESELECT_SUBTITLE.
demux_mpg didn't need this: the frontend code could just set ds->id to
the desired stream number. But demux_lavf's stream IDs don't map
directly to the stream number as used by libdvdread, which is why this
hack is needed.
2012-08-30 14:43:31 +00:00
|
|
|
add_dvd_tracks(mpctx);
|
2012-08-19 16:01:30 +00:00
|
|
|
add_demuxer_tracks(mpctx, mpctx->demuxer);
|
|
|
|
|
|
|
|
mpctx->timeline_part = 0;
|
|
|
|
if (mpctx->timeline)
|
|
|
|
timeline_set_part(mpctx, mpctx->timeline_part, true);
|
|
|
|
|
2012-08-03 05:47:11 +00:00
|
|
|
add_subtitle_fonts_from_sources(mpctx);
|
2008-01-12 01:12:39 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
open_subtitles_from_options(mpctx);
|
2013-06-27 16:21:07 +00:00
|
|
|
open_subtitles_from_resolve(mpctx);
|
2012-08-19 16:11:53 +00:00
|
|
|
open_audiofiles_from_options(mpctx);
|
2001-02-24 20:28:24 +00:00
|
|
|
|
mplayer: selectively reset track selection when playing multiple files
Normally, video/audio/sub track selection is persistent across files
played in the same mplayer instance. This is wanted, because settings
should not be reset across files in general. However, if the track
layout of a file is completely different from the previous, this will
essentially select random tracks. In this case, keeping the track
selection is confusing and unwanted.
Reset the track selection to default if the track layout changes. The
track layout is determined by number of tracks, track order, default
flag, whether the track is an external subtitle, and track language.
If a track layout change is detected when playing a new file, the -sid,
-aid and -vid options are reset to "auto".
This behavior is enabled only if the user selects tracks manually (via
keybinds and the "switch_audio" slave properties etc.). If no user
interactions take place, options specified on the command line will
follow the old behavior.
2012-09-08 21:02:02 +00:00
|
|
|
check_previous_track_selection(mpctx);
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->current_track[STREAM_VIDEO] =
|
2013-07-27 19:24:54 +00:00
|
|
|
select_track(mpctx, STREAM_VIDEO, mpctx->opts->video_id, NULL);
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->current_track[STREAM_AUDIO] =
|
2013-07-27 19:24:54 +00:00
|
|
|
select_track(mpctx, STREAM_AUDIO, mpctx->opts->audio_id,
|
|
|
|
mpctx->opts->audio_lang);
|
2012-08-19 16:01:30 +00:00
|
|
|
mpctx->current_track[STREAM_SUB] =
|
2013-07-27 19:24:54 +00:00
|
|
|
select_track(mpctx, STREAM_SUB, mpctx->opts->sub_id,
|
|
|
|
mpctx->opts->sub_lang);
|
2008-02-29 17:25:50 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
demux_info_print(mpctx->master_demuxer);
|
|
|
|
print_file_properties(mpctx, mpctx->filename);
|
2001-05-13 22:45:21 +00:00
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
preselect_demux_streams(mpctx);
|
2002-02-08 16:00:14 +00:00
|
|
|
|
2012-09-29 13:04:40 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
|
|
|
if (mpctx->encode_lavc_ctx && mpctx->current_track[STREAM_VIDEO])
|
|
|
|
encode_lavc_expect_stream(mpctx->encode_lavc_ctx, AVMEDIA_TYPE_VIDEO);
|
|
|
|
if (mpctx->encode_lavc_ctx && mpctx->current_track[STREAM_AUDIO])
|
|
|
|
encode_lavc_expect_stream(mpctx->encode_lavc_ctx, AVMEDIA_TYPE_AUDIO);
|
|
|
|
#endif
|
|
|
|
|
2012-08-19 16:01:30 +00:00
|
|
|
reinit_video_chain(mpctx);
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
reinit_subs(mpctx);
|
2002-02-09 01:29:11 +00:00
|
|
|
|
2012-08-03 05:47:11 +00:00
|
|
|
//================ SETUP STREAMS ==========================
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
if (opts->force_fps && mpctx->sh_video) {
|
|
|
|
mpctx->sh_video->fps = opts->force_fps;
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
2013-07-11 17:21:45 +00:00
|
|
|
"FPS forced to be %5.3f.\n", mpctx->sh_video->fps);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
//==================== START PLAYING =======================
|
|
|
|
|
2013-04-10 16:50:19 +00:00
|
|
|
if (!mpctx->sh_video && !mpctx->sh_audio) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
|
|
|
"No video or audio streams selected.\n");
|
|
|
|
#ifdef CONFIG_DVBIN
|
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVB) {
|
|
|
|
int dir;
|
|
|
|
int v = mpctx->last_dvb_step;
|
|
|
|
if (v > 0)
|
|
|
|
dir = DVB_CHANNEL_HIGHER;
|
|
|
|
else
|
|
|
|
dir = DVB_CHANNEL_LOWER;
|
|
|
|
|
|
|
|
if (dvb_step_channel(mpctx->stream, dir)) {
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
mpctx->dvbin_reopen = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
goto terminate_playback;
|
|
|
|
}
|
|
|
|
|
2012-07-29 19:40:56 +00:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "Starting playback...\n");
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
mpctx->drop_frame_cnt = 0;
|
|
|
|
mpctx->dropped_frames = 0;
|
|
|
|
mpctx->max_frames = opts->play_frames;
|
2011-08-07 00:58:12 +00:00
|
|
|
|
2013-03-08 01:08:02 +00:00
|
|
|
if (mpctx->max_frames == 0) {
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
2012-08-03 06:18:40 +00:00
|
|
|
goto terminate_playback;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2002-06-28 17:13:18 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->time_frame = 0;
|
|
|
|
mpctx->drop_message_shown = 0;
|
|
|
|
mpctx->restart_playback = true;
|
|
|
|
mpctx->video_pts = 0;
|
core: add --keep-open, which doesn't close the file on EOF
The --keep-open option causes mpv not to close the current file.
Instead, it will pause, and allow the user to seek around. When
seeking beyond the end of the file, mpv does a precise seek back to
the previous last known position that produced video output.
In some corner cases, mpv might not be able to produce video output at
all, despite having created a VO. (Possibly when only 1 frame could be
decoded, but the video filter chain queues frames. Then a VO would be
created, without sending an actual video frame to the VO.) In these
cases, the VO window will not redraw, not even OSD.
Based on a patch by coax [1].
[1] http://devel.mplayer2.org/ticket/210#comment:4
2012-11-12 23:56:20 +00:00
|
|
|
mpctx->last_vo_pts = MP_NOPTS_VALUE;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->last_seek_pts = 0;
|
2013-04-03 23:18:19 +00:00
|
|
|
mpctx->playback_pts = MP_NOPTS_VALUE;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
mpctx->hrseek_framedrop = false;
|
|
|
|
mpctx->step_frames = 0;
|
core: add backstep support
Allows stepping back one frame via the frame_back_step inout command,
bound to "," by default.
This uses the precise seeking facility, and a perfect frame index built
on the fly. The index is built during playback and precise seeking, and
contains (as of this commit) the last 100 displayed or skipped frames.
This index is used to find the PTS of the previous frame, which is then
used as target for a precise seek. If no PTS is found, the core attempts
to do a seek before the current frame, and skip decoded frames until the
current frame is reached; this will create a sufficient index and the
normal backstep algorithm can be applied.
This can be rather slow. The worst case for backstepping is about the
same as the worst case for precise seeking if the previous frame can be
deduced from the index. If not, the worst case will be twice as slow.
There's also some minor danger that the index is incorrect in case
framedropping is involved. For framedropping due to --framedrop, this
problem is ignored (use of --framedrop is discouraged anyway). For
framedropping during precise seeking (done to make it faster), we try
to not add frames to the index that are produced when this can happen.
I'm not sure how well that works (or if the logic is sane), and it's
sure to break with some video filters. In the worst case, backstepping
might silently skip frames if you backstep after a user-initiated
precise seek. (Precise seeks to do indexing are not affected.)
Likewise, video filters that somehow change timing of frames and do not
do this in a deterministic way (i.e. if you seek to a position, frames
with different timings are produced than when the position is reached
during normal playback) will make backstepping silently jump to the
wrong frame. Enabling/disabling filters during playback (like for
example deinterlacing) will have similar bad effects.
2013-04-24 17:31:48 +00:00
|
|
|
mpctx->backstep_active = false;
|
2011-08-07 00:58:12 +00:00
|
|
|
mpctx->total_avsync_change = 0;
|
|
|
|
mpctx->last_chapter_seek = -2;
|
2013-03-25 22:44:32 +00:00
|
|
|
mpctx->playing_msg_shown = false;
|
2013-04-25 18:38:22 +00:00
|
|
|
mpctx->paused = false;
|
|
|
|
mpctx->paused_for_cache = false;
|
2013-05-03 22:36:53 +00:00
|
|
|
mpctx->seek = (struct seek_params){ 0 };
|
2002-06-28 17:13:18 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
// If there's a timeline force an absolute seek to initialize state
|
2012-11-15 17:49:17 +00:00
|
|
|
double startpos = rel_time_to_abs(mpctx, opts->play_start, -1);
|
|
|
|
if (startpos != -1 || mpctx->timeline) {
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
2013-05-03 22:36:53 +00:00
|
|
|
execute_queued_seek(mpctx);
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2013-06-27 16:21:07 +00:00
|
|
|
if (startpos == -1 && mpctx->resolve_result &&
|
|
|
|
mpctx->resolve_result->start_time > 0)
|
|
|
|
{
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->resolve_result->start_time, 0);
|
|
|
|
execute_queued_seek(mpctx);
|
|
|
|
}
|
2011-08-07 00:58:12 +00:00
|
|
|
if (opts->chapterrange[0] > 0) {
|
2013-05-03 22:36:53 +00:00
|
|
|
if (mp_seek_chapter(mpctx, opts->chapterrange[0] - 1))
|
|
|
|
execute_queued_seek(mpctx);
|
2010-12-18 08:13:45 +00:00
|
|
|
}
|
2007-02-27 01:16:59 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
get_relative_time(mpctx); // reset current delta
|
2013-04-25 17:07:47 +00:00
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
if (mpctx->opts->pause)
|
2012-02-29 02:07:10 +00:00
|
|
|
pause_player(mpctx);
|
2012-02-08 23:36:53 +00:00
|
|
|
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
playback_start = mp_time_sec();
|
2013-08-02 08:32:38 +00:00
|
|
|
mpctx->error_playing = false;
|
2013-03-04 13:23:06 +00:00
|
|
|
while (!mpctx->stop_play)
|
|
|
|
run_playloop(mpctx);
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_GLOBAL, MSGL_V, "EOF code: %d \n", mpctx->stop_play);
|
2001-07-29 21:07:34 +00:00
|
|
|
|
2008-08-03 15:21:40 +00:00
|
|
|
#ifdef CONFIG_DVBIN
|
2011-08-07 00:58:12 +00:00
|
|
|
if (mpctx->dvbin_reopen) {
|
|
|
|
mpctx->stop_play = 0;
|
|
|
|
uninit_player(mpctx, INITIALIZED_ALL - (INITIALIZED_STREAM | INITIALIZED_GETCH2 | (opts->fixed_vo ? INITIALIZED_VO : 0)));
|
|
|
|
mpctx->dvbin_reopen = 0;
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 16:49:09 +00:00
|
|
|
goto goto_reopen_demuxer;
|
2011-08-07 00:58:12 +00:00
|
|
|
}
|
2006-04-25 20:32:46 +00:00
|
|
|
#endif
|
2001-08-22 21:35:44 +00:00
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
terminate_playback: // don't jump here after ao/vo/getch initialization!
|
2001-08-22 23:48:18 +00:00
|
|
|
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
if (mpctx->stop_play == KEEP_PLAYING)
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
|
|
|
|
2013-09-04 16:09:04 +00:00
|
|
|
if (opts->position_save_on_quit && mpctx->stop_play == PT_QUIT)
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
mp_write_watch_later_conf(mpctx);
|
|
|
|
|
2013-01-09 00:16:15 +00:00
|
|
|
if (mpctx->step_frames)
|
2013-04-25 18:38:22 +00:00
|
|
|
opts->pause = 1;
|
2013-01-09 00:16:15 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
|
|
|
|
|
|
|
|
// time to uninit all, except global stuff:
|
|
|
|
int uninitialize_parts = INITIALIZED_ALL;
|
|
|
|
if (opts->fixed_vo)
|
|
|
|
uninitialize_parts -= INITIALIZED_VO;
|
2013-03-25 21:31:34 +00:00
|
|
|
if ((opts->gapless_audio && mpctx->stop_play == AT_END_OF_FILE) ||
|
|
|
|
mpctx->encode_lavc_ctx)
|
2011-08-07 00:58:12 +00:00
|
|
|
uninitialize_parts -= INITIALIZED_AO;
|
|
|
|
uninit_player(mpctx, uninitialize_parts);
|
|
|
|
|
2013-04-10 17:07:26 +00:00
|
|
|
// xxx handle this as INITIALIZED_CONFIG?
|
2013-09-10 14:50:19 +00:00
|
|
|
if (mpctx->stop_play != PT_RESTART)
|
|
|
|
m_config_restore_backups(mpctx->mconfig);
|
2013-04-10 17:07:26 +00:00
|
|
|
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
mpctx->filename = NULL;
|
2012-10-13 15:09:35 +00:00
|
|
|
talloc_free(mpctx->resolve_result);
|
|
|
|
mpctx->resolve_result = NULL;
|
mplayer: turn playtree into a list, and change per-file option handling
Summary:
- There is no playtree anymore. It's reduced to a simple list.
- Options are now always global. You can still have per-file options,
but these are optional and require special syntax.
- The slave command pt_step has been removed, and playlist_next
and playlist_prev added. (See etc/input.conf changes.)
This is a user visible incompatible change, and will break slave-mode
applications.
- The pt_clear slave command is renamed to playlist_clear.
- Playtree entries could have multiple files. This is not the case
anymore, and playlist entries have always exactly one entry. Whenever
something adds more than one file (like ASX playlists or dvd:// or
dvdnav:// on the command line), all files are added as separate
playlist entries.
Note that some of the changes are quite deep and violent. Expect
regressions.
The playlist parsing code in particular is of low quality. I didn't try
to improve it, and merely spent to least effort necessary to keep it
somehow working. (Especially ASX playlist handling.)
The playtree code was complicated and bloated. It was also barely used.
Most users don't even know that mplayer manages the playlist as tree,
or how to use it. The most obscure features was probably specifying a
tree on command line (with '{' and '}' to create/close tree nodes). It
filled the player code with complexity and confused users with weird
slave commands like pt_up.
Replace the playtree with a simple flat playlist. Playlist parsers that
actually return trees are changed to append all files to the playlist
pre-order.
It used to be the responsibility of the playtree code to change per-file
config options. Now this is done by the player core, and the playlist
code is free of such details.
Options are not per-file by default anymore. This was a very obscure and
complicated feature that confused even experienced users. Consider the
following command line:
mplayer file1.mkv file2.mkv --no-audio file3.mkv
This will disable the audio for file2.mkv only, because options are
per-file by default. To make the option affect all files, you're
supposed to put it before the first file.
This is bad, because normally you don't need per-file options. They are
very rarely needed, and the only reasonable use cases I can imagine are
use of the encode backend (mplayer encode branch), or for debugging. The
normal use case is made harder, and the feature is perceived as bug.
Even worse, correct usage is hard to explain for users.
Make all options global by default. The position of an option isn't
significant anymore (except for options that compensate each other,
consider --shuffle --no-shuffle).
One other important change is that no options are reset anymore if a
new file is started. If you change settings with slave mode commands,
they will not be changed by playing a new file. (Exceptions include
settings that are too file specific, like audio/subtitle stream
selection.)
There is still some need for per-file options. Debugging and encoding
are use cases that profit from per-file options. Per-file profiles (as
well as per-protocol and per-VO/AO options) need the implementation
related mechanisms to backup and restore options when the playback file
changes.
Simplify the save-slot stuff, which is possible because there is no
hierarchical play tree anymore. Now there's a simple backup field.
Add a way to specify per-file options on command line. Example:
mplayer f1.mkv -o0 --{ -o1 f2.mkv -o2 f3.mkv --} f4.mkv -o3
will have the following options per file set:
f1.mkv, f4.mkv: -o0 -o3
f2.mkv, f3.mkv: -o0 -o3 -o1 -o2
The options --{ and --} start and end per-file options. All files inside
the { } will be affected by the options equally (similar to how global
options and multiple files are handled). When playback of a file starts,
the per-file options are set according to the command line. When
playback ends, the per-file options are restored to the values when
playback started.
2012-07-31 19:33:26 +00:00
|
|
|
|
2008-07-30 12:01:30 +00:00
|
|
|
#ifdef CONFIG_ASS
|
2012-08-25 14:47:50 +00:00
|
|
|
if (mpctx->osd->ass_renderer)
|
|
|
|
ass_renderer_done(mpctx->osd->ass_renderer);
|
|
|
|
mpctx->osd->ass_renderer = NULL;
|
|
|
|
ass_clear_fonts(mpctx->ass_library);
|
2006-07-07 18:26:51 +00:00
|
|
|
#endif
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
|
|
|
|
// Played/paused for longer than 3 seconds -> ok
|
2013-09-29 19:10:36 +00:00
|
|
|
bool playback_short = mpctx->stop_play == AT_END_OF_FILE &&
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
(playback_start < 0 || mp_time_sec() - playback_start < 3.0);
|
2013-09-29 19:10:36 +00:00
|
|
|
bool init_failed = mpctx->stop_play == AT_END_OF_FILE &&
|
|
|
|
(mpctx->shown_aframes == 0 && mpctx->shown_vframes == 0);
|
|
|
|
if (mpctx->playlist->current && !mpctx->playlist->current_was_replaced) {
|
|
|
|
mpctx->playlist->current->playback_short = playback_short;
|
|
|
|
mpctx->playlist->current->init_failed = init_failed;
|
|
|
|
}
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
|
|
|
|
mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
|
|
|
|
mp_notify(mpctx, MP_EVENT_END_FILE, NULL);
|
|
|
|
mp_flush_events(mpctx);
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
2003-01-20 22:49:50 +00:00
|
|
|
|
2013-02-03 13:54:28 +00:00
|
|
|
// Determine the next file to play. Note that if this function returns non-NULL,
|
|
|
|
// it can have side-effects and mutate mpctx.
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
// direction: -1 (previous) or +1 (next)
|
|
|
|
// force: if true, don't skip playlist entries marked as failed
|
|
|
|
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
|
|
|
|
bool force)
|
2013-02-03 13:54:28 +00:00
|
|
|
{
|
|
|
|
struct playlist_entry *next = playlist_get_next(mpctx->playlist, direction);
|
2013-10-05 16:30:45 +00:00
|
|
|
if (next && direction < 0 && !force) {
|
|
|
|
// Don't jump to files that would immediately go to next file anyway
|
2013-09-29 19:10:36 +00:00
|
|
|
while (next && next->playback_short)
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
next = next->prev;
|
2013-10-05 16:30:45 +00:00
|
|
|
// Always allow jumping to first file
|
|
|
|
if (!next && mpctx->opts->loop_times < 0)
|
|
|
|
next = mpctx->playlist->first;
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
}
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!next && mpctx->opts->loop_times >= 0) {
|
2013-02-03 13:54:28 +00:00
|
|
|
if (direction > 0) {
|
2013-08-18 22:50:39 +00:00
|
|
|
if (mpctx->opts->shuffle)
|
|
|
|
playlist_shuffle(mpctx->playlist);
|
2013-02-03 13:54:28 +00:00
|
|
|
next = mpctx->playlist->first;
|
2013-07-27 19:24:54 +00:00
|
|
|
if (next && mpctx->opts->loop_times > 0) {
|
|
|
|
mpctx->opts->loop_times--;
|
|
|
|
if (mpctx->opts->loop_times == 0)
|
|
|
|
mpctx->opts->loop_times = -1;
|
2013-02-03 13:54:28 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
next = mpctx->playlist->last;
|
2013-10-05 16:30:45 +00:00
|
|
|
// Don't jump to files that would immediately go to next file anyway
|
|
|
|
while (next && next->playback_short)
|
|
|
|
next = next->prev;
|
2013-02-03 13:54:28 +00:00
|
|
|
}
|
2013-09-29 19:10:36 +00:00
|
|
|
if (!force && next && next->init_failed) {
|
2013-10-05 16:30:45 +00:00
|
|
|
// Don't endless loop if no file in playlist is playable
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
bool all_failed = true;
|
|
|
|
struct playlist_entry *cur;
|
|
|
|
for (cur = mpctx->playlist->first; cur; cur = cur->next) {
|
2013-09-29 19:10:36 +00:00
|
|
|
all_failed &= cur->init_failed;
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
if (!all_failed)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (all_failed)
|
|
|
|
next = NULL;
|
|
|
|
}
|
2013-02-03 13:54:28 +00:00
|
|
|
}
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
// Play all entries on the playlist, starting from the current entry.
|
|
|
|
// Return if all done.
|
|
|
|
static void play_files(struct MPContext *mpctx)
|
|
|
|
{
|
2013-08-02 08:32:38 +00:00
|
|
|
mpctx->quit_player_rc = EXIT_NONE;
|
2012-08-03 06:18:40 +00:00
|
|
|
for (;;) {
|
|
|
|
idle_loop(mpctx);
|
2012-08-04 01:46:11 +00:00
|
|
|
if (mpctx->stop_play == PT_QUIT)
|
|
|
|
break;
|
|
|
|
|
2013-08-02 08:32:38 +00:00
|
|
|
mpctx->error_playing = true;
|
2012-08-03 06:18:40 +00:00
|
|
|
play_current_file(mpctx);
|
2013-08-02 08:32:38 +00:00
|
|
|
if (mpctx->error_playing) {
|
|
|
|
if (!mpctx->quit_player_rc) {
|
|
|
|
mpctx->quit_player_rc = EXIT_NOTPLAYED;
|
|
|
|
} else if (mpctx->quit_player_rc == EXIT_PLAYED) {
|
|
|
|
mpctx->quit_player_rc = EXIT_SOMENOTPLAYED;
|
|
|
|
}
|
|
|
|
} else if (mpctx->quit_player_rc == EXIT_NOTPLAYED) {
|
|
|
|
mpctx->quit_player_rc = EXIT_SOMENOTPLAYED;
|
|
|
|
} else {
|
|
|
|
mpctx->quit_player_rc = EXIT_PLAYED;
|
|
|
|
}
|
2012-08-04 01:46:11 +00:00
|
|
|
if (mpctx->stop_play == PT_QUIT)
|
|
|
|
break;
|
2012-08-03 06:18:40 +00:00
|
|
|
|
|
|
|
if (!mpctx->stop_play || mpctx->stop_play == AT_END_OF_FILE)
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
|
|
|
|
struct playlist_entry *new_entry = NULL;
|
2008-08-13 05:06:26 +00:00
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
if (mpctx->stop_play == PT_NEXT_ENTRY) {
|
mplayer: attempt to skip playlist entries which can't be played
This is for situations when repeated attempts at playing a playlist
entry failed, and playlist navigation becomes impossible due to that.
For example, it wasn't possible to skip backwards past an unplayable
playlist entry:
mpv file1.mkv doesntexist.mkv file3.mkv
You couldn't skip back to file1.mkv from file3.mkv. When running a
single "playlist_prev" command, doesntexist.mkv would be played, which
would fail to load. As reaction to the failure to load it, the next file
would be played, which is file3.mkv.
To make this even worse, the file could successfully load, but run only
for a split second. So just loading successfully isn't good enough.
Attempt to solve this by marking problematic playlist entries as failed,
and by having playlist_prev skip past such playlist entries. We define
failure as not being able to play more than 3 seconds (or failing to
initialize to begin with). (The 3 seconds are in real time, not file
duration.)
"playlist_prev force" still exhibits the old behavior.
Additionally, use the same mechanism to prevent pointless infinite
reloading if none of the files on the playlist exist. (See github issue
All in all, this is a heuristic, and later adjustments might be
necessary.
Note: forward skips (playlist_next) are not affected at all. (Except for
the interaction with --loop.)
2013-09-15 03:03:37 +00:00
|
|
|
new_entry = mp_next_file(mpctx, +1, false);
|
2012-08-03 06:18:40 +00:00
|
|
|
} else if (mpctx->stop_play == PT_CURRENT_ENTRY) {
|
|
|
|
new_entry = mpctx->playlist->current;
|
2012-08-25 23:19:42 +00:00
|
|
|
} else if (mpctx->stop_play == PT_RESTART) {
|
|
|
|
// The same as PT_CURRENT_ENTRY, unless we decide that the current
|
|
|
|
// playlist entry can be removed during playback.
|
|
|
|
new_entry = mpctx->playlist->current;
|
2012-08-03 06:18:40 +00:00
|
|
|
} else { // PT_STOP
|
|
|
|
playlist_clear(mpctx->playlist);
|
|
|
|
}
|
2008-08-13 05:06:26 +00:00
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
mpctx->playlist->current = new_entry;
|
|
|
|
mpctx->playlist->current_was_replaced = false;
|
|
|
|
mpctx->stop_play = 0;
|
|
|
|
|
2013-07-27 19:24:54 +00:00
|
|
|
if (!mpctx->playlist->current && !mpctx->opts->player_idle_mode)
|
2012-08-03 06:18:40 +00:00
|
|
|
break;
|
2003-02-09 13:17:12 +00:00
|
|
|
}
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
2001-08-22 21:35:44 +00:00
|
|
|
|
2013-06-28 20:16:29 +00:00
|
|
|
// Abort current playback and set the given entry to play next.
|
|
|
|
// e must be on the mpctx->playlist.
|
|
|
|
void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e)
|
|
|
|
{
|
|
|
|
assert(playlist_entry_to_index(mpctx->playlist, e) >= 0);
|
|
|
|
mpctx->playlist->current = e;
|
|
|
|
mpctx->playlist->current_was_replaced = false;
|
|
|
|
mpctx->stop_play = PT_CURRENT_ENTRY;
|
|
|
|
}
|
|
|
|
|
2013-06-07 20:57:00 +00:00
|
|
|
void mp_print_version(int always)
|
2012-08-03 06:18:40 +00:00
|
|
|
{
|
|
|
|
mp_msg(MSGT_CPLAYER, always ? MSGL_INFO : MSGL_V,
|
2013-01-04 14:23:23 +00:00
|
|
|
"%s (C) 2000-2013 mpv/MPlayer/mplayer2 projects\n built on %s\n", mplayer_version, mplayer_builddate);
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool handle_help_options(struct MPContext *mpctx)
|
|
|
|
{
|
2013-07-27 19:24:54 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-03 06:18:40 +00:00
|
|
|
int opt_exit = 0;
|
core: redo how codecs are mapped, remove codecs.conf
Use codec names instead of FourCCs to identify codecs. Rewrite how
codecs are selected and initialized. Now each decoder exports a list
of decoders (and the codec it supports) via add_decoders(). The order
matters, and the first decoder for a given decoder is preferred over
the other decoders. E.g. all ad_mpg123 decoders are preferred over
ad_lavc, because it comes first in the mpcodecs_ad_drivers array.
Likewise, decoders within ad_lavc that are enumerated first by
libavcodec (using av_codec_next()) are preferred. (This is actually
critical to select h264 software decoding by default instead of vdpau.
libavcodec and ffmpeg/avconv use the same method to select decoders by
default, so we hope this is sane.)
The codec names follow libavcodec's codec names as defined by
AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders
have names different from the canonical codec name. The AVCodecDescriptor
API is relatively new, so we need a compatibility layer for older
libavcodec versions for codec names that are referenced internally,
and which are different from the decoder name. (Add a configure check
for that, because checking versions is getting way too messy.)
demux/codec_tags.c is generated from the former codecs.conf (minus
"special" decoders like vdpau, and excluding the mappings that are the
same as the mappings libavformat's exported RIFF tables). It contains
all the mappings from FourCCs to codec name. This is needed for
demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the
codec as determined by libavformat, while the other demuxers have to do
this on their own, using the mp_set_audio/video_codec_from_tag()
functions. Note that the sh_audio/video->format members don't uniquely
identify the codec anymore, and sh->codec takes over this role.
Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which
provide cover the functionality of the removed switched.
Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure
container/video combinations (e.g. the sample Film_200_zygo_pro.mov)
are played flipped. ffplay/avplay doesn't handle this properly either,
so we don't care and blame ffmeg/libav instead.
2013-02-09 14:15:19 +00:00
|
|
|
if (opts->audio_decoders && strcmp(opts->audio_decoders, "help") == 0) {
|
|
|
|
struct mp_decoder_list *list = mp_audio_decoder_list();
|
|
|
|
mp_print_decoders(MSGT_CPLAYER, MSGL_INFO, "Audio decoders:", list);
|
|
|
|
talloc_free(list);
|
2012-08-03 06:18:40 +00:00
|
|
|
opt_exit = 1;
|
|
|
|
}
|
core: redo how codecs are mapped, remove codecs.conf
Use codec names instead of FourCCs to identify codecs. Rewrite how
codecs are selected and initialized. Now each decoder exports a list
of decoders (and the codec it supports) via add_decoders(). The order
matters, and the first decoder for a given decoder is preferred over
the other decoders. E.g. all ad_mpg123 decoders are preferred over
ad_lavc, because it comes first in the mpcodecs_ad_drivers array.
Likewise, decoders within ad_lavc that are enumerated first by
libavcodec (using av_codec_next()) are preferred. (This is actually
critical to select h264 software decoding by default instead of vdpau.
libavcodec and ffmpeg/avconv use the same method to select decoders by
default, so we hope this is sane.)
The codec names follow libavcodec's codec names as defined by
AVCodecDescriptor.name (see libavcodec/codec_desc.c). Some decoders
have names different from the canonical codec name. The AVCodecDescriptor
API is relatively new, so we need a compatibility layer for older
libavcodec versions for codec names that are referenced internally,
and which are different from the decoder name. (Add a configure check
for that, because checking versions is getting way too messy.)
demux/codec_tags.c is generated from the former codecs.conf (minus
"special" decoders like vdpau, and excluding the mappings that are the
same as the mappings libavformat's exported RIFF tables). It contains
all the mappings from FourCCs to codec name. This is needed for
demux_mkv, demux_mpg, demux_avi and demux_asf. demux_lavf will set the
codec as determined by libavformat, while the other demuxers have to do
this on their own, using the mp_set_audio/video_codec_from_tag()
functions. Note that the sh_audio/video->format members don't uniquely
identify the codec anymore, and sh->codec takes over this role.
Replace the --ac/--vc/--afm/--vfm with new --vd/--ad options, which
provide cover the functionality of the removed switched.
Note: there's no CODECS_FLAG_FLIP flag anymore. This means some obscure
container/video combinations (e.g. the sample Film_200_zygo_pro.mov)
are played flipped. ffplay/avplay doesn't handle this properly either,
so we don't care and blame ffmeg/libav instead.
2013-02-09 14:15:19 +00:00
|
|
|
if (opts->video_decoders && strcmp(opts->video_decoders, "help") == 0) {
|
|
|
|
struct mp_decoder_list *list = mp_video_decoder_list();
|
|
|
|
mp_print_decoders(MSGT_CPLAYER, MSGL_INFO, "Video decoders:", list);
|
|
|
|
talloc_free(list);
|
2012-08-03 06:18:40 +00:00
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_X11
|
2013-03-04 16:40:21 +00:00
|
|
|
if (opts->vo.fstype_list && strcmp(opts->vo.fstype_list[0], "help") == 0) {
|
2012-08-03 06:18:40 +00:00
|
|
|
fstype_help();
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if ((opts->demuxer_name && strcmp(opts->demuxer_name, "help") == 0) ||
|
|
|
|
(opts->audio_demuxer_name && strcmp(opts->audio_demuxer_name, "help") == 0) ||
|
|
|
|
(opts->sub_demuxer_name && strcmp(opts->sub_demuxer_name, "help") == 0)) {
|
|
|
|
demuxer_help();
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
if (opts->list_properties) {
|
|
|
|
property_print_help();
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
2012-09-14 15:51:26 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
2013-07-27 19:24:54 +00:00
|
|
|
if (encode_lavc_showhelp(mpctx->opts))
|
2012-09-14 15:51:26 +00:00
|
|
|
opt_exit = 1;
|
|
|
|
#endif
|
2012-08-03 06:18:40 +00:00
|
|
|
return opt_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PTW32_STATIC_LIB
|
|
|
|
static void detach_ptw32(void)
|
|
|
|
{
|
|
|
|
pthread_win32_thread_detach_np();
|
|
|
|
pthread_win32_process_detach_np();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void osdep_preinit(int *p_argc, char ***p_argv)
|
|
|
|
{
|
2012-10-11 00:04:08 +00:00
|
|
|
char *enable_talloc = getenv("MPV_LEAK_REPORT");
|
2013-03-25 21:31:34 +00:00
|
|
|
if (*p_argc > 1 && (strcmp((*p_argv)[1], "-leak-report") == 0 ||
|
|
|
|
strcmp((*p_argv)[1], "--leak-report") == 0))
|
2012-08-19 15:50:37 +00:00
|
|
|
enable_talloc = "1";
|
|
|
|
if (enable_talloc && strcmp(enable_talloc, "1") == 0)
|
|
|
|
talloc_enable_leak_report();
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
#ifdef __MINGW32__
|
2012-08-06 23:09:42 +00:00
|
|
|
mp_get_converted_argv(p_argc, p_argv);
|
2012-08-03 06:18:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PTW32_STATIC_LIB
|
|
|
|
pthread_win32_process_attach_np();
|
|
|
|
pthread_win32_thread_attach_np();
|
|
|
|
atexit(detach_ptw32);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
|
|
// stop Windows from showing all kinds of annoying error dialogs
|
|
|
|
SetErrorMode(0x8003);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
load_termcap(NULL); // load key-codes
|
2013-05-17 17:54:37 +00:00
|
|
|
|
|
|
|
mp_time_init();
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This preprocessor directive is a hack to generate a mplayer-nomain.o object
|
|
|
|
* file for some tools to link against. */
|
|
|
|
#ifndef DISABLE_MAIN
|
2013-03-04 13:23:06 +00:00
|
|
|
static int mpv_main(int argc, char *argv[])
|
2012-08-03 06:18:40 +00:00
|
|
|
{
|
|
|
|
osdep_preinit(&argc, &argv);
|
|
|
|
|
2012-08-05 21:34:28 +00:00
|
|
|
if (argc >= 1) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
struct MPContext *mpctx = talloc(NULL, MPContext);
|
|
|
|
*mpctx = (struct MPContext){
|
|
|
|
.last_dvb_step = 1,
|
|
|
|
.terminal_osd_text = talloc_strdup(mpctx, ""),
|
2012-08-05 21:34:28 +00:00
|
|
|
.playlist = talloc_struct(mpctx, struct playlist, {0}),
|
2012-08-03 06:18:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Create the config context and register the options
|
2013-07-27 19:26:00 +00:00
|
|
|
mpctx->mconfig = m_config_new(mpctx, sizeof(struct MPOpts),
|
2013-07-31 18:45:06 +00:00
|
|
|
&mp_default_opts, mp_opts, NULL);
|
2013-07-27 19:26:00 +00:00
|
|
|
mpctx->opts = mpctx->mconfig->optstruct;
|
|
|
|
mpctx->mconfig->includefunc = cfg_include;
|
|
|
|
mpctx->mconfig->use_profiles = true;
|
|
|
|
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2012-08-03 06:18:40 +00:00
|
|
|
|
2013-07-31 19:40:30 +00:00
|
|
|
|
|
|
|
mpctx->global = talloc_zero(mpctx, struct mpv_global);
|
|
|
|
mpctx->global->opts = opts;
|
|
|
|
|
|
|
|
// Nothing must call mp_msg() before this
|
|
|
|
mp_msg_init(mpctx->global);
|
|
|
|
mpctx->log = mp_log_new(mpctx, mpctx->global->log, "!mpv");
|
|
|
|
|
|
|
|
init_libav();
|
|
|
|
GetCpuCaps(&gCpuCaps);
|
|
|
|
screenshot_init(mpctx);
|
2013-09-19 12:33:26 +00:00
|
|
|
mpctx->mixer = mixer_init(mpctx, opts);
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
command_init(mpctx);
|
2013-07-31 19:40:30 +00:00
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
// Preparse the command line
|
2012-08-05 21:34:28 +00:00
|
|
|
m_config_preparse_command_line(mpctx->mconfig, argc, argv);
|
2012-08-03 06:18:40 +00:00
|
|
|
|
2013-06-07 20:57:00 +00:00
|
|
|
mp_print_version(false);
|
2012-08-03 06:18:40 +00:00
|
|
|
print_libav_versions();
|
|
|
|
|
2012-08-04 01:46:11 +00:00
|
|
|
if (!parse_cfgfiles(mpctx, mpctx->mconfig))
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
2013-05-15 14:02:52 +00:00
|
|
|
|
|
|
|
int r = m_config_parse_mp_command_line(mpctx->mconfig, mpctx->playlist,
|
|
|
|
argc, argv);
|
|
|
|
if (r < 0) {
|
|
|
|
if (r <= M_OPT_EXIT) {
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_NONE);
|
2013-05-15 14:02:52 +00:00
|
|
|
} else {
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
2013-05-15 14:02:52 +00:00
|
|
|
}
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handle_help_options(mpctx))
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_NONE);
|
2012-08-03 06:18:40 +00:00
|
|
|
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Configuration: " CONFIGURATION "\n");
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "Command line:");
|
2012-08-19 15:32:27 +00:00
|
|
|
for (int i = 0; i < argc; i++)
|
2012-08-03 06:18:40 +00:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " '%s'", argv[i]);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "\n");
|
|
|
|
|
|
|
|
if (!mpctx->playlist->first && !opts->player_idle_mode) {
|
2013-06-07 20:57:00 +00:00
|
|
|
mp_print_version(true);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", mp_gtext(mp_help_text));
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_NONE);
|
2012-08-03 06:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PRIORITY
|
|
|
|
set_priority();
|
|
|
|
#endif
|
|
|
|
|
input: handle mouse movement differently
Before this commit, mouse movement events emitted a special command
("set_mouse_pos"), which was specially handled in command.c. This was
once special-cased to the dvdnav and menu code, and did nothing after
libmenu and dvdnav were removed.
Change it so that mouse movement triggers a pseudo-key ("MOUSE_MOVE"),
which then can be bound to an arbitrary command. The mouse position is
now managed in input.c. A command which actually needs the mouse
position can use either mp_input_get_mouse_pos() or mp_get_osd_mouse_pos()
to query it. The former returns raw window-space coordinates, while the
latter returns coordinates transformed to OSD- space. (Both are the same
for most VOs, except vo_xv and vo_x11, which can't render OSD in
window-space. These require extra code for mapping mouse position.)
As of this commit, there is still nothing that uses mouse movement, so
MOUSE_MOVE is mapped to "ignore" to silence warnings when moving the
mouse (much like MOUSE_BTN0).
Extend the concept of input sections. Allow multiple sections to be
active at once, and organize them as stack. Bindings from the top of
the stack are preferred to lower ones.
Each section has a mouse input section associated, inside which mouse
events are associated with the bindings. If the mouse pointer is
outside of a section's mouse area, mouse events will be dispatched to
an input section lower on the stack of active sections. This is intended
for scripting, which is to be added later. Two scripts could occupy
different areas of the screen without conflicting with each other. (If
it turns out that this mechanism is useless, we'll just remove it
again.)
2013-04-26 00:13:30 +00:00
|
|
|
init_input(mpctx);
|
|
|
|
|
2012-09-14 15:51:26 +00:00
|
|
|
#ifdef CONFIG_ENCODING
|
2013-06-20 10:15:02 +00:00
|
|
|
if (opts->encode_output.file && *opts->encode_output.file) {
|
2012-09-14 15:51:26 +00:00
|
|
|
mpctx->encode_lavc_ctx = encode_lavc_init(&opts->encode_output);
|
2013-06-20 10:15:02 +00:00
|
|
|
if(!mpctx->encode_lavc_ctx) {
|
2012-09-14 15:51:26 +00:00
|
|
|
mp_msg(MSGT_VO, MSGL_INFO, "Encoding initialization failed.");
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
2013-06-20 10:15:02 +00:00
|
|
|
}
|
2012-09-14 15:51:26 +00:00
|
|
|
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
|
|
|
|
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
|
|
|
|
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
|
2013-10-01 23:15:59 +00:00
|
|
|
m_config_set_option0(mpctx->mconfig, "force-window", "no");
|
2012-09-14 15:51:26 +00:00
|
|
|
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
|
input: handle mouse movement differently
Before this commit, mouse movement events emitted a special command
("set_mouse_pos"), which was specially handled in command.c. This was
once special-cased to the dvdnav and menu code, and did nothing after
libmenu and dvdnav were removed.
Change it so that mouse movement triggers a pseudo-key ("MOUSE_MOVE"),
which then can be bound to an arbitrary command. The mouse position is
now managed in input.c. A command which actually needs the mouse
position can use either mp_input_get_mouse_pos() or mp_get_osd_mouse_pos()
to query it. The former returns raw window-space coordinates, while the
latter returns coordinates transformed to OSD- space. (Both are the same
for most VOs, except vo_xv and vo_x11, which can't render OSD in
window-space. These require extra code for mapping mouse position.)
As of this commit, there is still nothing that uses mouse movement, so
MOUSE_MOVE is mapped to "ignore" to silence warnings when moving the
mouse (much like MOUSE_BTN0).
Extend the concept of input sections. Allow multiple sections to be
active at once, and organize them as stack. Bindings from the top of
the stack are preferred to lower ones.
Each section has a mouse input section associated, inside which mouse
events are associated with the bindings. If the mouse pointer is
outside of a section's mouse area, mouse events will be dispatched to
an input section lower on the stack of active sections. This is intended
for scripting, which is to be added later. Two scripts could occupy
different areas of the screen without conflicting with each other. (If
it turns out that this mechanism is useless, we'll just remove it
again.)
2013-04-26 00:13:30 +00:00
|
|
|
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
|
2012-09-14 15:51:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
#ifdef CONFIG_ASS
|
|
|
|
mpctx->ass_library = mp_ass_init(opts);
|
|
|
|
#endif
|
|
|
|
|
2012-08-07 17:21:46 +00:00
|
|
|
mpctx->osd = osd_create(opts, mpctx->ass_library);
|
2012-08-03 06:18:40 +00:00
|
|
|
|
2013-10-01 23:15:59 +00:00
|
|
|
if (opts->force_vo) {
|
|
|
|
opts->fixed_vo = 1;
|
|
|
|
mpctx->video_out = init_best_video_out(mpctx->global, mpctx->input,
|
|
|
|
mpctx->encode_lavc_ctx);
|
|
|
|
if (!mpctx->video_out) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
|
|
|
|
"the selected video_out (-vo) device.\n");
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
|
|
|
mpctx->mouse_cursor_visible = true;
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_VO;
|
|
|
|
}
|
|
|
|
|
Add initial Lua scripting support
This is preliminary. There are still tons of issues, and any aspect
of scripting may change in the future. I decided to merge this
(preliminary) work now because it makes it easier to develop it, not
because it's done. lua.rst is clear enough about it (plus some
sarcasm).
This requires linking to Lua. Lua has no official pkg-config file, but
there are distribution specific .pc files, all with different names.
Adding a non-pkg-config based configure test was considered, but we'd
rather not.
One major complication is that libquvi links against Lua too, and if
the Lua version is different from mpv's, you will get a crash as soon
as libquvi uses Lua. (libquvi by design always runs when a file is
opened.) I would consider this the problem of distros and whoever
builds mpv, but to make things easier for users, we add a terrible
runtime test to the configure script, which probes whether libquvi
will crash. This is disabled when cross-compiling, but in that case
we hope the user knows what he is doing.
2013-09-25 22:41:14 +00:00
|
|
|
#ifdef CONFIG_LUA
|
|
|
|
// Lua user scripts can call arbitrary functions. Load them at a point
|
|
|
|
// where this is safe.
|
|
|
|
mp_lua_init(mpctx);
|
|
|
|
#endif
|
|
|
|
|
2013-08-18 22:50:39 +00:00
|
|
|
if (opts->shuffle)
|
|
|
|
playlist_shuffle(mpctx->playlist);
|
2013-09-04 14:08:36 +00:00
|
|
|
|
2013-09-22 01:04:57 +00:00
|
|
|
mpctx->playlist->current = mp_resume_playlist(mpctx->playlist, opts);
|
2013-09-04 14:08:36 +00:00
|
|
|
if (!mpctx->playlist->current)
|
|
|
|
mpctx->playlist->current = mpctx->playlist->first;
|
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.
2013-05-05 17:37:29 +00:00
|
|
|
|
2012-08-03 06:18:40 +00:00
|
|
|
play_files(mpctx);
|
2002-08-28 20:52:02 +00:00
|
|
|
|
2013-08-02 08:32:38 +00:00
|
|
|
exit_player(mpctx, mpctx->stop_play == PT_QUIT ? EXIT_QUIT : mpctx->quit_player_rc);
|
2001-08-22 21:35:44 +00:00
|
|
|
|
2011-08-07 00:58:12 +00:00
|
|
|
return 1;
|
2001-03-15 19:38:34 +00:00
|
|
|
}
|
2013-03-04 13:23:06 +00:00
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_COCOA
|
2013-05-15 13:48:40 +00:00
|
|
|
return cocoa_main(mpv_main, argc, argv);
|
2013-03-04 13:23:06 +00:00
|
|
|
#else
|
2013-05-15 13:48:40 +00:00
|
|
|
return mpv_main(argc, argv);
|
2013-03-04 13:23:06 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-06-06 13:28:25 +00:00
|
|
|
#endif /* DISABLE_MAIN */
|