2010-01-31 00:24:23 +01: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 23:33:50 +02:00
|
|
|
|
2001-08-01 11:14:02 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2008-12-08 19:04:08 +01:00
|
|
|
#include <stdbool.h>
|
2010-11-13 18:27:01 +01:00
|
|
|
#include <math.h>
|
2011-05-07 19:20:13 +02:00
|
|
|
#include <assert.h>
|
2010-11-13 18:27:01 +01:00
|
|
|
|
2004-04-30 22:05:54 +02:00
|
|
|
#include "config.h"
|
2008-04-20 06:36:34 +02:00
|
|
|
#include "talloc.h"
|
2004-04-30 22:05:54 +02:00
|
|
|
|
2008-10-13 18:23:55 +02:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
2004-04-30 18:56:00 +02:00
|
|
|
#define _UWIN 1 /*disable Non-underscored versions of non-ANSI functions as otherwise int eof would conflict with eof()*/
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
2001-08-01 11:14:02 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2002-03-23 22:12:38 +01:00
|
|
|
// #include <sys/mman.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
#include <sys/types.h>
|
2003-04-04 22:02:53 +02:00
|
|
|
#ifndef __MINGW32__
|
|
|
|
#include <sys/ioctl.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
#include <sys/wait.h>
|
2003-04-04 22:02:53 +02:00
|
|
|
#else
|
2011-08-07 02:58:12 +02:00
|
|
|
#define SIGHUP 1 /* hangup */
|
|
|
|
#define SIGQUIT 3 /* quit */
|
|
|
|
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
|
|
|
|
#define SIGBUS 10 /* bus error */
|
|
|
|
#define SIGPIPE 13 /* broken pipe */
|
2003-04-04 22:02:53 +02:00
|
|
|
#endif
|
|
|
|
|
2001-02-24 21:28:24 +01:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/stat.h>
|
2001-08-01 01:18:16 +02:00
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
#include <fcntl.h>
|
2003-02-07 20:44:12 +01:00
|
|
|
#include <limits.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2002-03-15 23:25:57 +01:00
|
|
|
#include <errno.h>
|
|
|
|
|
2001-08-17 00:40:06 +02:00
|
|
|
#include "mp_msg.h"
|
2009-10-31 22:03:48 +01:00
|
|
|
#include "av_log.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2001-08-18 22:32:09 +02:00
|
|
|
|
2002-11-12 02:56:42 +01:00
|
|
|
#include "m_option.h"
|
|
|
|
#include "m_config.h"
|
2008-08-12 13:54:51 +02:00
|
|
|
#include "mplayer.h"
|
|
|
|
#include "access_mpcontext.h"
|
2006-03-22 01:19:02 +01:00
|
|
|
#include "m_property.h"
|
2003-03-30 19:13:04 +02:00
|
|
|
|
2009-09-02 21:43:05 +02:00
|
|
|
#include "libavutil/avstring.h"
|
2008-01-27 00:47:25 +01:00
|
|
|
|
2011-01-26 18:40:52 +01:00
|
|
|
#include "sub/subreader.h"
|
2011-02-26 20:47:49 +01:00
|
|
|
#include "sub/find_subfiles.h"
|
2011-01-16 19:03:08 +01:00
|
|
|
#include "sub/dec_sub.h"
|
2001-03-30 05:07:45 +02:00
|
|
|
|
2008-04-23 08:49:25 +02:00
|
|
|
#include "mp_osd.h"
|
2001-03-04 22:01:54 +01:00
|
|
|
#include "libvo/video_out.h"
|
2011-10-06 20:46:01 +02:00
|
|
|
#include "screenshot.h"
|
2001-07-30 04:00:54 +02:00
|
|
|
|
2011-01-26 18:40:52 +01:00
|
|
|
#include "sub/font_load.h"
|
|
|
|
#include "sub/sub.h"
|
2011-01-12 00:37:02 +01:00
|
|
|
#include "ffmpeg_files/intreadwrite.h"
|
2011-01-26 18:40:52 +01:00
|
|
|
#include "sub/av_sub.h"
|
2011-01-12 00:37:02 +01:00
|
|
|
#include "libmpcodecs/dec_teletext.h"
|
|
|
|
#include "cpudetect.h"
|
|
|
|
#include "version.h"
|
2001-03-04 22:01:54 +01:00
|
|
|
|
2008-08-04 08:16:23 +02:00
|
|
|
#ifdef CONFIG_X11
|
2003-05-30 19:57:41 +02:00
|
|
|
#include "libvo/x11_common.h"
|
|
|
|
#endif
|
|
|
|
|
2001-06-03 01:30:26 +02:00
|
|
|
#include "libao2/audio_out.h"
|
|
|
|
|
2001-04-07 23:27:57 +02:00
|
|
|
#include "codec-cfg.h"
|
2001-03-20 23:11:38 +01:00
|
|
|
|
2002-12-23 01:33:22 +01:00
|
|
|
#include "edl.h"
|
|
|
|
|
2011-01-26 18:40:52 +01:00
|
|
|
#include "sub/spudec.h"
|
|
|
|
#include "sub/vobsub.h"
|
2001-04-18 00:04:44 +02:00
|
|
|
|
2003-02-09 21:18:23 +01:00
|
|
|
#include "osdep/getch2.h"
|
|
|
|
#include "osdep/timer.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2002-01-30 13:46:03 +01:00
|
|
|
#include "input/input.h"
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
int slave_mode = 0;
|
|
|
|
int enable_mouse_movements = 0;
|
2008-10-31 19:44:45 +01:00
|
|
|
float start_volume = -1;
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2009-02-10 16:34:44 +01:00
|
|
|
#include "osdep/priority.h"
|
2005-04-02 20:29:16 +02:00
|
|
|
|
2008-01-07 13:30:15 +01:00
|
|
|
char *heartbeat_cmd;
|
|
|
|
|
2001-11-20 08:53:20 +01:00
|
|
|
#ifdef HAVE_RTC
|
2005-01-06 01:06:56 +01:00
|
|
|
#ifdef __linux__
|
2001-11-14 01:26:28 +01:00
|
|
|
#include <linux/rtc.h>
|
2005-01-06 01:06:56 +01:00
|
|
|
#else
|
|
|
|
#include <rtc.h>
|
|
|
|
#define RTC_IRQP_SET RTCIO_IRQP_SET
|
|
|
|
#define RTC_PIE_ON RTCIO_PIE_ON
|
2005-09-15 05:08:35 +02:00
|
|
|
#endif /* __linux__ */
|
|
|
|
#endif /* HAVE_RTC */
|
2001-11-14 01:26:28 +01:00
|
|
|
|
2006-07-31 19:39:17 +02:00
|
|
|
#include "stream/tv.h"
|
2006-08-28 19:05:18 +02:00
|
|
|
#include "stream/stream_radio.h"
|
2008-08-03 17:21:40 +02:00
|
|
|
#ifdef CONFIG_DVBIN
|
2006-07-31 19:39:17 +02:00
|
|
|
#include "stream/dvbin.h"
|
2003-03-16 21:13:28 +01:00
|
|
|
#endif
|
2006-09-12 18:24:23 +02:00
|
|
|
#include "stream/cache2.h"
|
2003-03-16 21:13:28 +01:00
|
|
|
|
2002-01-08 03:01:04 +01:00
|
|
|
//**************************************************************************//
|
|
|
|
// Playtree
|
|
|
|
//**************************************************************************//
|
|
|
|
#include "playtree.h"
|
2003-05-30 19:57:41 +02:00
|
|
|
#include "playtreeparser.h"
|
|
|
|
|
2002-01-15 00:38:49 +01:00
|
|
|
//**************************************************************************//
|
|
|
|
// Config
|
|
|
|
//**************************************************************************//
|
2006-09-12 18:24:23 +02:00
|
|
|
#include "parser-cfg.h"
|
|
|
|
#include "parser-mpcmd.h"
|
2002-01-15 00:38:49 +01:00
|
|
|
|
2001-03-30 05:07:45 +02:00
|
|
|
//**************************************************************************//
|
|
|
|
// Config file
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static int cfg_inc_verbose(m_option_t *conf)
|
|
|
|
{
|
|
|
|
++verbose;
|
|
|
|
return 0;
|
|
|
|
}
|
2001-03-19 04:45:49 +01:00
|
|
|
|
2010-03-21 00:38:27 +01:00
|
|
|
#include "path.h"
|
2001-03-21 01:14:42 +01:00
|
|
|
|
2001-04-15 05:40:37 +02:00
|
|
|
//**************************************************************************//
|
2001-04-15 21:13:38 +02:00
|
|
|
//**************************************************************************//
|
|
|
|
// Input media streaming & demultiplexer:
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static int max_framesize = 0;
|
2001-07-07 20:46:15 +02:00
|
|
|
|
2006-07-31 19:39:17 +02:00
|
|
|
#include "stream/stream.h"
|
2002-09-29 21:26:40 +02:00
|
|
|
#include "libmpdemux/demuxer.h"
|
|
|
|
#include "libmpdemux/stheader.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDREAD
|
2006-07-31 19:39:17 +02:00
|
|
|
#include "stream/stream_dvd.h"
|
2006-06-11 23:29:00 +02:00
|
|
|
#endif
|
2006-11-25 14:03:51 +01:00
|
|
|
#include "stream/stream_dvdnav.h"
|
|
|
|
|
2002-04-13 21:14:34 +02:00
|
|
|
#include "libmpcodecs/dec_audio.h"
|
|
|
|
#include "libmpcodecs/dec_video.h"
|
2002-11-15 00:49:05 +01:00
|
|
|
#include "libmpcodecs/mp_image.h"
|
|
|
|
#include "libmpcodecs/vf.h"
|
2005-12-05 02:18:10 +01:00
|
|
|
#include "libmpcodecs/vd.h"
|
2001-10-30 18:38:09 +01:00
|
|
|
|
2007-02-21 01:49:24 +01:00
|
|
|
#include "mixer.h"
|
2006-10-11 19:25:46 +02:00
|
|
|
|
2007-02-21 01:49:24 +01:00
|
|
|
#include "mp_core.h"
|
2008-03-31 05:19:29 +02:00
|
|
|
#include "options.h"
|
|
|
|
#include "defaultopts.h"
|
2006-10-11 19:25:46 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static const char help_text[] = _(
|
2010-03-07 17:05:48 +01:00
|
|
|
"Usage: mplayer [options] [url|path/]filename\n"
|
|
|
|
"\n"
|
|
|
|
"Basic options: (complete list in the man page)\n"
|
|
|
|
" -vo <drv> select video output driver ('-vo help' for a list)\n"
|
|
|
|
" -ao <drv> select audio output driver ('-ao help' for a list)\n"
|
|
|
|
#ifdef CONFIG_VCD
|
|
|
|
" vcd://<trackno> play (S)VCD (Super Video CD) track (raw device, no mount)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_DVDREAD
|
|
|
|
" dvd://<titleno> play DVD title from device instead of plain file\n"
|
|
|
|
#endif
|
|
|
|
" -alang/-slang select DVD audio/subtitle language (by 2-char country code)\n"
|
|
|
|
" -ss <position> seek to given (seconds or hh:mm:ss) position\n"
|
|
|
|
" -nosound do not play sound\n"
|
|
|
|
" -fs fullscreen playback (or -vm, -zoom, details in the man page)\n"
|
|
|
|
" -x <x> -y <y> set display resolution (for use with -vm or -zoom)\n"
|
|
|
|
" -sub <file> specify subtitle file to use (also see -subfps, -subdelay)\n"
|
|
|
|
" -playlist <file> specify playlist file\n"
|
|
|
|
" -vid x -aid y select video (x) and audio (y) stream to play\n"
|
|
|
|
" -fps x -srate y change video (x fps) and audio (y Hz) rate\n"
|
|
|
|
" -pp <quality> enable postprocessing filter (details in the man page)\n"
|
|
|
|
" -framedrop enable frame dropping (for slow machines)\n"
|
|
|
|
"\n"
|
|
|
|
"Basic keys: (complete list in the man page, also check input.conf)\n"
|
|
|
|
" <- or -> seek backward/forward 10 seconds\n"
|
|
|
|
" down or up seek backward/forward 1 minute\n"
|
|
|
|
" pgdown or pgup seek backward/forward 10 minutes\n"
|
|
|
|
" < or > step backward/forward in playlist\n"
|
|
|
|
" p or SPACE pause movie (press any key to continue)\n"
|
|
|
|
" q or ESC stop playing and quit program\n"
|
|
|
|
" + or - adjust audio delay by +/- 0.1 second\n"
|
|
|
|
" o cycle OSD mode: none / seekbar / seekbar + timer\n"
|
|
|
|
" * or / increase or decrease PCM volume\n"
|
|
|
|
" x or z adjust subtitle delay by +/- 0.1 second\n"
|
|
|
|
" r or t adjust subtitle position up/down, also see -vf expand\n"
|
2011-03-09 13:30:56 +01:00
|
|
|
" double click toggle fullscreen\n"
|
|
|
|
" right click pause (press again to continue)\n"
|
2010-03-07 17:05:48 +01:00
|
|
|
"\n"
|
|
|
|
" * * * SEE THE MAN PAGE FOR DETAILS, FURTHER (ADVANCED) OPTIONS AND KEYS * * *\n"
|
|
|
|
"\n");
|
|
|
|
|
|
|
|
|
2009-07-07 00:15:02 +02:00
|
|
|
#define Exit_SIGILL_RTCpuSel _(\
|
|
|
|
"- MPlayer crashed by an 'Illegal Instruction'.\n"\
|
|
|
|
" It may be a bug in our new runtime CPU-detection code...\n"\
|
|
|
|
" Please read DOCS/HTML/en/bugreports.html.\n")
|
|
|
|
|
|
|
|
#define Exit_SIGILL _(\
|
|
|
|
"- MPlayer crashed by an 'Illegal Instruction'.\n"\
|
|
|
|
" It usually happens when you run it on a CPU different than the one it was\n"\
|
|
|
|
" compiled/optimized for.\n"\
|
|
|
|
" Verify this!\n")
|
|
|
|
|
|
|
|
#define Exit_SIGSEGV_SIGFPE _(\
|
|
|
|
"- MPlayer crashed by bad usage of CPU/FPU/RAM.\n"\
|
|
|
|
" Recompile MPlayer with --enable-debug and make a 'gdb' backtrace and\n"\
|
|
|
|
" disassembly. Details in DOCS/HTML/en/bugreports_what.html#bugreports_crash.\n")
|
|
|
|
|
|
|
|
#define Exit_SIGCRASH _(\
|
|
|
|
"- MPlayer crashed. This shouldn't happen.\n"\
|
|
|
|
" It can be a bug in the MPlayer code _or_ in your drivers _or_ in your\n"\
|
|
|
|
" gcc version. If you think it's MPlayer's fault, please read\n"\
|
|
|
|
" DOCS/HTML/en/bugreports.html and follow the instructions there. We can't and\n"\
|
|
|
|
" won't help unless you provide this information when reporting a possible bug.\n")
|
|
|
|
|
|
|
|
#define SystemTooSlow _("\n\n"\
|
|
|
|
" ************************************************\n"\
|
|
|
|
" **** Your system is too SLOW to play this! ****\n"\
|
|
|
|
" ************************************************\n\n"\
|
|
|
|
"Possible reasons, problems, workarounds:\n"\
|
|
|
|
"- Most common: broken/buggy _audio_ driver\n"\
|
|
|
|
" - Try -ao sdl or use the OSS emulation of ALSA.\n"\
|
|
|
|
" - Experiment with different values for -autosync, 30 is a good start.\n"\
|
|
|
|
"- Slow video output\n"\
|
|
|
|
" - Try a different -vo driver (-vo help for a list) or try -framedrop!\n"\
|
|
|
|
"- Slow CPU\n"\
|
|
|
|
" - Don't try to play a big DVD/DivX on a slow CPU! Try some of the lavdopts,\n"\
|
|
|
|
" e.g. -vfm ffmpeg -lavdopts lowres=1:fast:skiploopfilter=all.\n"\
|
|
|
|
"- Broken file\n"\
|
|
|
|
" - Try various combinations of -nobps -ni -forceidx -mc 0.\n"\
|
|
|
|
"- Slow media (NFS/SMB mounts, DVD, VCD etc)\n"\
|
|
|
|
" - Try -cache 8192.\n"\
|
|
|
|
"- Are you using -cache to play a non-interleaved AVI file?\n"\
|
|
|
|
" - Try -nocache.\n"\
|
|
|
|
"Read DOCS/HTML/en/video.html for tuning/speedup tips.\n"\
|
|
|
|
"If none of this helps you, read DOCS/HTML/en/bugreports.html.\n\n")
|
|
|
|
|
|
|
|
|
2001-02-24 21:28:24 +01:00
|
|
|
//**************************************************************************//
|
|
|
|
//**************************************************************************//
|
|
|
|
|
2007-03-29 19:16:11 +02:00
|
|
|
#include "mp_fifo.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2001-10-30 18:04:59 +01:00
|
|
|
// benchmark:
|
2011-08-07 02:58:12 +02:00
|
|
|
double video_time_usage;
|
|
|
|
double vout_time_usage;
|
|
|
|
static double audio_time_usage;
|
|
|
|
static int total_time_usage_start;
|
|
|
|
static int total_frame_cnt;
|
|
|
|
static int drop_frame_cnt; // total number of dropped frames
|
2001-06-13 23:55:24 +02:00
|
|
|
|
2001-06-18 16:19:13 +02:00
|
|
|
// options:
|
2011-08-07 02:58:12 +02:00
|
|
|
static int output_quality;
|
2001-08-04 15:52:41 +02:00
|
|
|
|
2001-10-30 18:04:59 +01:00
|
|
|
// seek:
|
2011-08-07 02:58:12 +02:00
|
|
|
static off_t seek_to_byte;
|
|
|
|
static off_t step_sec;
|
2002-09-29 21:26:40 +02:00
|
|
|
|
2006-09-25 18:47:56 +02:00
|
|
|
static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 };
|
|
|
|
|
2001-10-30 18:04:59 +01:00
|
|
|
// codecs:
|
2011-08-07 02:58:12 +02:00
|
|
|
char **audio_codec_list; // override audio codec
|
|
|
|
char **video_codec_list; // override video codec
|
|
|
|
char **audio_fm_list; // override audio codec family
|
|
|
|
char **video_fm_list; // override video codec family
|
2001-07-06 23:17:22 +02:00
|
|
|
|
2009-11-11 12:22:12 +01:00
|
|
|
// this dvdsub_id was selected via slang
|
|
|
|
// use this to allow dvdnav to follow -slang across stream resets,
|
|
|
|
// in particular the subtitle ID for a language changes
|
|
|
|
int dvdsub_lang_id;
|
2011-08-07 02:58:12 +02:00
|
|
|
int vobsub_id = -1;
|
|
|
|
static char *spudec_ifo = NULL;
|
|
|
|
int forced_subs_only = 0;
|
2001-06-18 16:19:13 +02:00
|
|
|
|
2001-10-22 01:05:05 +02:00
|
|
|
// cache2:
|
2011-08-07 02:58:12 +02:00
|
|
|
int stream_cache_size = -1;
|
2001-10-22 01:05:05 +02:00
|
|
|
|
2001-10-30 18:04:59 +01:00
|
|
|
// dump:
|
2011-08-07 02:58:12 +02:00
|
|
|
int stream_dump_type = 0;
|
2001-10-30 18:04:59 +01:00
|
|
|
|
|
|
|
// A-V sync:
|
2011-08-07 02:58:12 +02:00
|
|
|
static float default_max_pts_correction = -1;
|
|
|
|
float audio_delay = 0;
|
|
|
|
static int ignore_start = 0;
|
2001-06-18 16:19:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
double force_fps = 0;
|
|
|
|
static int force_srate = 0;
|
|
|
|
int frame_dropping = 0; // option 0=no drop 1= drop vo 2= drop decode
|
|
|
|
static int play_n_frames = -1;
|
|
|
|
static int play_n_frames_mf = -1;
|
2001-06-18 16:19:13 +02:00
|
|
|
|
|
|
|
// sub:
|
2011-08-07 02:58:12 +02:00
|
|
|
char *font_name = NULL;
|
|
|
|
char *sub_font_name = NULL;
|
2003-12-08 14:30:30 +01:00
|
|
|
extern int font_fontconfig;
|
2011-08-07 02:58:12 +02:00
|
|
|
float font_factor = 0.75;
|
|
|
|
float sub_delay = 0;
|
|
|
|
float sub_fps = 0;
|
|
|
|
int subcc_enabled = 0;
|
2002-12-05 01:03:35 +01:00
|
|
|
int suboverlap_enabled = 1;
|
2006-11-21 21:48:17 +01:00
|
|
|
|
2011-01-26 18:40:52 +01:00
|
|
|
#include "sub/ass_mp.h"
|
2006-07-07 20:26:51 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
char *current_module; // for debugging
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2002-07-19 22:51:48 +02:00
|
|
|
|
|
|
|
// ---
|
|
|
|
|
2004-11-03 01:08:03 +01:00
|
|
|
edl_record_ptr edl_records = NULL; ///< EDL entries memory area
|
|
|
|
edl_record_ptr next_edl_record = NULL; ///< only for traversing edl_records
|
2011-08-07 02:58:12 +02:00
|
|
|
FILE *edl_fd; // file to write to when in -edlout mode.
|
2006-10-15 23:27:15 +02:00
|
|
|
int use_filedir_conf;
|
2009-11-12 10:21:53 +01:00
|
|
|
int use_filename_title;
|
2002-12-23 01:33:22 +01:00
|
|
|
|
2007-02-17 22:04:46 +01:00
|
|
|
#include "mpcommon.h"
|
2007-02-21 01:49:24 +01:00
|
|
|
#include "command.h"
|
2001-09-05 12:49:04 +02:00
|
|
|
|
2006-08-26 01:14:32 +02:00
|
|
|
#include "metadata.h"
|
|
|
|
|
2011-05-07 07:51:48 +02:00
|
|
|
void *mpctx_get_video_out(MPContext *mpctx)
|
2007-02-21 19:28:48 +01:00
|
|
|
{
|
|
|
|
return mpctx->video_out;
|
|
|
|
}
|
|
|
|
|
2007-12-14 09:39:45 +01:00
|
|
|
void *mpctx_get_demuxer(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
return mpctx->demuxer;
|
|
|
|
}
|
2007-02-21 20:14:49 +01:00
|
|
|
|
2007-02-21 19:28:48 +01:00
|
|
|
void *mpctx_get_playtree_iter(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
return mpctx->playtree_iter;
|
|
|
|
}
|
|
|
|
|
2007-02-21 20:14:49 +01:00
|
|
|
void *mpctx_get_mixer(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
return &mpctx->mixer;
|
|
|
|
}
|
|
|
|
|
2007-02-22 06:39:17 +01:00
|
|
|
int mpctx_get_global_sub_size(MPContext *mpctx)
|
2007-02-21 20:14:49 +01:00
|
|
|
{
|
|
|
|
return mpctx->global_sub_size;
|
|
|
|
}
|
|
|
|
|
2007-11-15 02:07:56 +01:00
|
|
|
int mpctx_get_osd_function(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
return mpctx->osd_function;
|
|
|
|
}
|
|
|
|
|
2008-04-28 11:09:31 +02:00
|
|
|
static float get_relative_time(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
unsigned int new_time = GetTimer();
|
|
|
|
unsigned int delta = new_time - mpctx->last_time;
|
|
|
|
mpctx->last_time = new_time;
|
|
|
|
return delta * 0.000001;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static int is_valid_metadata_type(struct MPContext *mpctx, metadata_t type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
/* check for valid video stream */
|
|
|
|
case META_VIDEO_CODEC:
|
|
|
|
case META_VIDEO_BITRATE:
|
|
|
|
case META_VIDEO_RESOLUTION:
|
|
|
|
if (!mpctx->sh_video)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* check for valid audio stream */
|
|
|
|
case META_AUDIO_CODEC:
|
|
|
|
case META_AUDIO_BITRATE:
|
|
|
|
case META_AUDIO_SAMPLES:
|
|
|
|
if (!mpctx->sh_audio)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* check for valid demuxer */
|
|
|
|
case META_INFO_TITLE:
|
|
|
|
case META_INFO_ARTIST:
|
|
|
|
case META_INFO_ALBUM:
|
|
|
|
case META_INFO_YEAR:
|
|
|
|
case META_INFO_COMMENT:
|
|
|
|
case META_INFO_TRACK:
|
|
|
|
case META_INFO_GENRE:
|
|
|
|
if (!mpctx->demuxer)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2006-08-26 01:14:32 +02:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static char *get_demuxer_info(struct MPContext *mpctx, char *tag)
|
|
|
|
{
|
|
|
|
char **info = mpctx->demuxer->info;
|
|
|
|
int n;
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!info || !tag)
|
|
|
|
return talloc_strdup(NULL, "");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
for (n = 0; info[2 * n] != NULL; n++)
|
|
|
|
if (!strcasecmp(info[2 * n], tag))
|
|
|
|
break;
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
return talloc_strdup(NULL, info[2 * n + 1] ? info[2 * n + 1] : "");
|
2006-08-26 01:14:32 +02:00
|
|
|
}
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
char *get_metadata(struct MPContext *mpctx, metadata_t type)
|
|
|
|
{
|
2010-12-20 05:34:11 +01:00
|
|
|
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
|
|
|
sh_video_t * const sh_video = mpctx->sh_video;
|
|
|
|
|
|
|
|
if (!is_valid_metadata_type(mpctx, type))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case META_NAME:
|
|
|
|
return talloc_strdup(NULL, mp_basename(mpctx->filename));
|
|
|
|
case META_VIDEO_CODEC:
|
|
|
|
if (sh_video->format == 0x10000001)
|
|
|
|
return talloc_strdup(NULL, "mpeg1");
|
|
|
|
else if (sh_video->format == 0x10000002)
|
|
|
|
return talloc_strdup(NULL, "mpeg2");
|
|
|
|
else if (sh_video->format == 0x10000004)
|
|
|
|
return talloc_strdup(NULL, "mpeg4");
|
|
|
|
else if (sh_video->format == 0x10000005)
|
|
|
|
return talloc_strdup(NULL, "h264");
|
|
|
|
else if (sh_video->format >= 0x20202020)
|
|
|
|
return talloc_asprintf(NULL, "%.4s", (char *) &sh_video->format);
|
|
|
|
else
|
|
|
|
return talloc_asprintf(NULL, "0x%08X", sh_video->format);
|
|
|
|
case META_VIDEO_BITRATE:
|
|
|
|
return talloc_asprintf(NULL, "%d kbps",
|
|
|
|
(int) (sh_video->i_bps * 8 / 1024));
|
|
|
|
case META_VIDEO_RESOLUTION:
|
|
|
|
return talloc_asprintf(NULL, "%d x %d", sh_video->disp_w,
|
|
|
|
sh_video->disp_h);
|
|
|
|
case META_AUDIO_CODEC:
|
|
|
|
if (sh_audio->codec && sh_audio->codec->name)
|
|
|
|
return talloc_strdup(NULL, sh_audio->codec->name);
|
|
|
|
return talloc_strdup(NULL, "");
|
|
|
|
case META_AUDIO_BITRATE:
|
|
|
|
return talloc_asprintf(NULL, "%d kbps",
|
2011-08-07 02:58:12 +02:00
|
|
|
(int) (sh_audio->i_bps * 8 / 1000));
|
2010-12-20 05:34:11 +01:00
|
|
|
case META_AUDIO_SAMPLES:
|
|
|
|
return talloc_asprintf(NULL, "%d Hz, %d ch.", sh_audio->samplerate,
|
|
|
|
sh_audio->channels);
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/* check for valid demuxer */
|
|
|
|
case META_INFO_TITLE:
|
|
|
|
return get_demuxer_info(mpctx, "Title");
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_ARTIST:
|
|
|
|
return get_demuxer_info(mpctx, "Artist");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_ALBUM:
|
|
|
|
return get_demuxer_info(mpctx, "Album");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_YEAR:
|
|
|
|
return get_demuxer_info(mpctx, "Year");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_COMMENT:
|
|
|
|
return get_demuxer_info(mpctx, "Comment");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_TRACK:
|
|
|
|
return get_demuxer_info(mpctx, "Track");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
case META_INFO_GENRE:
|
|
|
|
return get_demuxer_info(mpctx, "Genre");
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2006-08-26 01:14:32 +02:00
|
|
|
|
2010-12-20 05:34:11 +01:00
|
|
|
return talloc_strdup(NULL, "");
|
2006-08-25 21:22:51 +02:00
|
|
|
}
|
|
|
|
|
2010-11-07 23:54:32 +01:00
|
|
|
static void print_file_properties(struct MPContext *mpctx, const char *filename)
|
2010-06-07 19:12:49 +02:00
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
double start_pts = MP_NOPTS_VALUE;
|
|
|
|
double video_start_pts = MP_NOPTS_VALUE;
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILENAME=%s\n",
|
|
|
|
filename_recode(filename));
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DEMUXER=%s\n",
|
|
|
|
mpctx->demuxer->desc->name);
|
|
|
|
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);
|
|
|
|
video_start_pts = ds_get_next_pts(mpctx->d_video);
|
|
|
|
}
|
|
|
|
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,
|
|
|
|
"ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels);
|
|
|
|
start_pts = ds_get_next_pts(mpctx->d_audio);
|
|
|
|
}
|
|
|
|
if (video_start_pts != MP_NOPTS_VALUE) {
|
|
|
|
if (start_pts == MP_NOPTS_VALUE || !mpctx->sh_audio ||
|
|
|
|
(mpctx->sh_video && video_start_pts < start_pts))
|
|
|
|
start_pts = video_start_pts;
|
|
|
|
}
|
|
|
|
if (start_pts != MP_NOPTS_VALUE)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_START_TIME=%.2f\n", start_pts);
|
2010-06-07 19:12:49 +02:00
|
|
|
else
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_START_TIME=unknown\n");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_LENGTH=%.2f\n", get_time_length(mpctx));
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SEEKABLE=%d\n",
|
|
|
|
mpctx->stream->seek
|
|
|
|
&& (!mpctx->demuxer || mpctx->demuxer->seekable));
|
|
|
|
if (mpctx->demuxer) {
|
2011-10-23 04:51:44 +02:00
|
|
|
int chapter_count = get_chapter_count(mpctx);
|
|
|
|
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);
|
|
|
|
// in milliseconds
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_START=%"PRIu64"\n",
|
|
|
|
i, (int64_t)(chapter_start_time(mpctx, i) * 1000.0));
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2010-06-07 19:12:49 +02:00
|
|
|
}
|
|
|
|
|
2007-01-04 17:30:06 +01:00
|
|
|
/// step size of mixer changes
|
|
|
|
int volstep = 3;
|
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2011-08-07 02:58:12 +02:00
|
|
|
static void mp_dvdnav_context_free(MPContext *ctx)
|
|
|
|
{
|
|
|
|
if (ctx->nav_smpi)
|
|
|
|
free_mp_image(ctx->nav_smpi);
|
2008-01-26 12:51:34 +01:00
|
|
|
ctx->nav_smpi = NULL;
|
2010-11-07 13:47:40 +01:00
|
|
|
free(ctx->nav_buffer);
|
2008-01-26 12:51:34 +01:00
|
|
|
ctx->nav_buffer = NULL;
|
|
|
|
ctx->nav_start = NULL;
|
|
|
|
ctx->nav_in_size = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-16 19:03:08 +01:00
|
|
|
static void uninit_subs(struct demuxer *demuxer)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < MAX_S_STREAMS; i++) {
|
|
|
|
struct sh_sub *sh = demuxer->s_streams[i];
|
|
|
|
if (sh && sh->initialized)
|
|
|
|
sub_uninit(sh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02: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;
|
|
|
|
current_module = "uninit_acodec";
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
uninit_audio(mpctx->sh_audio);
|
|
|
|
mpctx->sh_audio = NULL;
|
|
|
|
mpctx->mixer.afilter = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_SUB) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_SUB;
|
|
|
|
if (mpctx->d_sub->sh)
|
|
|
|
sub_switchoff(mpctx->d_sub->sh, mpctx->osd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_VCODEC) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_VCODEC;
|
|
|
|
current_module = "uninit_vcodec";
|
|
|
|
if (mpctx->sh_video)
|
|
|
|
uninit_video(mpctx->sh_video);
|
|
|
|
mpctx->sh_video = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_DEMUXER) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_DEMUXER;
|
|
|
|
current_module = "free_demuxer";
|
|
|
|
if (mpctx->num_sources) {
|
|
|
|
mpctx->demuxer = mpctx->sources[0].demuxer;
|
|
|
|
for (int i = 1; i < mpctx->num_sources; i++) {
|
|
|
|
uninit_subs(mpctx->sources[i].demuxer);
|
|
|
|
free_stream(mpctx->sources[i].stream);
|
|
|
|
free_demuxer(mpctx->sources[i].demuxer);
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
talloc_free(mpctx->sources);
|
|
|
|
mpctx->sources = NULL;
|
|
|
|
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;
|
|
|
|
if (mpctx->demuxer) {
|
|
|
|
mpctx->stream = mpctx->demuxer->stream;
|
|
|
|
uninit_subs(mpctx->demuxer);
|
|
|
|
free_demuxer(mpctx->demuxer);
|
|
|
|
}
|
|
|
|
mpctx->demuxer = NULL;
|
2009-03-29 21:45:06 +02:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
|
|
|
|
// kill the cache process:
|
|
|
|
if (mask & INITIALIZED_STREAM) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_STREAM;
|
|
|
|
current_module = "uninit_stream";
|
|
|
|
if (mpctx->stream)
|
|
|
|
free_stream(mpctx->stream);
|
|
|
|
mpctx->stream = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_VO) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_VO;
|
|
|
|
current_module = "uninit_vo";
|
|
|
|
vo_destroy(mpctx->video_out);
|
|
|
|
mpctx->video_out = NULL;
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_dvdnav_context_free(mpctx);
|
2008-01-26 12:51:34 +01:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Must be after libvo uninit, as few vo drivers (svgalib) have tty code.
|
|
|
|
if (mask & INITIALIZED_GETCH2) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_GETCH2;
|
|
|
|
current_module = "uninit_getch2";
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG2, "\n[[[uninit getch2]]]\n");
|
|
|
|
// restore terminal:
|
|
|
|
getch2_disable();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_VOBSUB) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_VOBSUB;
|
|
|
|
current_module = "uninit_vobsub";
|
|
|
|
if (vo_vobsub)
|
|
|
|
vobsub_close(vo_vobsub);
|
|
|
|
vo_vobsub = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_SPUDEC) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_SPUDEC;
|
|
|
|
current_module = "uninit_spudec";
|
|
|
|
spudec_free(vo_spudec);
|
|
|
|
vo_spudec = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & INITIALIZED_AO) {
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_AO;
|
|
|
|
current_module = "uninit_ao";
|
|
|
|
if (mpctx->edl_muted)
|
|
|
|
mixer_mute(&mpctx->mixer);
|
|
|
|
if (mpctx->ao)
|
|
|
|
ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE);
|
|
|
|
mpctx->ao = NULL;
|
2011-12-26 02:23:51 +01:00
|
|
|
mpctx->mixer.ao = NULL;
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
current_module = NULL;
|
2001-09-05 12:49:04 +02:00
|
|
|
}
|
|
|
|
|
2010-03-09 22:18:19 +01:00
|
|
|
void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc)
|
2008-12-04 00:55:52 +01:00
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->user_muted && !mpctx->edl_muted)
|
|
|
|
mixer_mute(&mpctx->mixer);
|
|
|
|
uninit_player(mpctx, INITIALIZED_ALL);
|
2008-10-13 18:23:55 +02:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
2011-08-07 02:58:12 +02:00
|
|
|
timeEndPeriod(1);
|
2008-05-10 17:03:04 +02:00
|
|
|
#endif
|
2008-08-04 08:16:23 +02:00
|
|
|
#ifdef CONFIG_X11
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_uninit(mpctx->x11_state); // Close the X11 connection (if any is open).
|
2002-05-08 21:21:36 +02:00
|
|
|
#endif
|
2001-09-05 12:49:04 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "uninit_input";
|
2008-04-30 08:14:06 +02:00
|
|
|
mp_input_uninit(mpctx->input);
|
|
|
|
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_FREETYPE
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "uninit_font";
|
|
|
|
if (mpctx->osd && mpctx->osd->sub_font != vo_font)
|
|
|
|
free_font_desc(mpctx->osd->sub_font);
|
|
|
|
free_font_desc(vo_font);
|
|
|
|
vo_font = NULL;
|
|
|
|
done_freetype();
|
2004-11-15 16:29:39 +01:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
osd_free(mpctx->osd);
|
2004-11-15 16:29:39 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
ass_library_done(mpctx->ass_library);
|
|
|
|
mpctx->ass_library = NULL;
|
2006-10-28 17:07:18 +02:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "exit_player";
|
|
|
|
|
|
|
|
if (mpctx->playtree_iter)
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
if (mpctx->playtree)
|
|
|
|
play_tree_free(mpctx->playtree, 1);
|
|
|
|
mpctx->playtree = NULL;
|
|
|
|
|
|
|
|
talloc_free(mpctx->key_fifo);
|
|
|
|
|
|
|
|
free(edl_records); // free mem allocated for EDL
|
|
|
|
edl_records = NULL;
|
|
|
|
switch (how) {
|
|
|
|
case EXIT_QUIT:
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "\nExiting... (%s)\n", "Quit");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_EXIT=QUIT\n");
|
|
|
|
break;
|
|
|
|
case EXIT_EOF:
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "\nExiting... (%s)\n", "End of file");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_EXIT=EOF\n");
|
|
|
|
break;
|
|
|
|
case EXIT_ERROR:
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "\nExiting... (%s)\n", "Fatal error");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_EXIT=ERROR\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_EXIT=NONE\n");
|
|
|
|
}
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG2,
|
|
|
|
"max framesize was %d bytes\n", max_framesize);
|
|
|
|
|
|
|
|
// must be last since e.g. mp_msg uses option values
|
|
|
|
// that will be freed by this.
|
|
|
|
if (mpctx->mconfig)
|
|
|
|
m_config_free(mpctx->mconfig);
|
|
|
|
mpctx->mconfig = NULL;
|
|
|
|
|
2011-10-06 20:46:01 +02:00
|
|
|
talloc_free(mpctx);
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
exit(rc);
|
2002-12-29 22:06:20 +01:00
|
|
|
}
|
|
|
|
|
2010-03-09 22:18:19 +01:00
|
|
|
static void exit_player(struct MPContext *mpctx, enum exit_reason how)
|
2008-04-21 05:07:22 +02:00
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
exit_player_with_rc(mpctx, how, 1);
|
2001-02-24 21:28:24 +01:00
|
|
|
}
|
|
|
|
|
2004-09-19 20:45:11 +02:00
|
|
|
#ifndef __MINGW32__
|
2011-08-07 02:58:12 +02:00
|
|
|
static void child_sighandler(int x)
|
|
|
|
{
|
|
|
|
pid_t pid;
|
|
|
|
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) ;
|
2004-09-19 20:45:11 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_CRASH_DEBUG
|
2004-10-29 01:25:18 +02:00
|
|
|
static char *prog_path;
|
|
|
|
static int crash_debug = 0;
|
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void exit_sighandler(int x)
|
|
|
|
{
|
|
|
|
static int sig_count = 0;
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_CRASH_DEBUG
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!crash_debug || x != SIGTRAP)
|
2004-10-29 01:25:18 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
++sig_count;
|
|
|
|
if (sig_count == 5) {
|
|
|
|
/* We're crashing bad and can't uninit cleanly :(
|
|
|
|
* by popular request, we make one last (dirty)
|
|
|
|
* effort to restore the user's
|
|
|
|
* terminal. */
|
|
|
|
getch2_disable();
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (sig_count == 6)
|
|
|
|
exit(1);
|
|
|
|
if (sig_count > 6) {
|
|
|
|
// can't stop :(
|
2003-04-04 22:02:53 +02:00
|
|
|
#ifndef __MINGW32__
|
2011-08-07 02:58:12 +02:00
|
|
|
kill(getpid(), SIGKILL);
|
2003-04-04 22:02:53 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_FATAL, "\n");
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
|
|
|
"\nMPlayer interrupted by signal %d in module: %s\n", x,
|
|
|
|
current_module ? current_module : "unknown");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SIGNAL=%d\n", x);
|
|
|
|
if (sig_count <= 1)
|
|
|
|
switch (x) {
|
|
|
|
case SIGINT:
|
|
|
|
case SIGPIPE:
|
|
|
|
case SIGQUIT:
|
|
|
|
case SIGTERM:
|
|
|
|
case SIGKILL:
|
|
|
|
async_quit_request = 1;
|
|
|
|
return; // killed from keyboard (^C) or killed [-9]
|
|
|
|
case SIGILL:
|
2009-04-08 22:21:21 +02:00
|
|
|
#if CONFIG_RUNTIME_CPUDETECT
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, Exit_SIGILL_RTCpuSel);
|
2002-03-16 03:15:10 +01:00
|
|
|
#else
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, Exit_SIGILL);
|
2002-03-16 03:15:10 +01:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
case SIGFPE:
|
|
|
|
case SIGSEGV:
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, Exit_SIGSEGV_SIGFPE);
|
|
|
|
default:
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, Exit_SIGCRASH);
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_CRASH_DEBUG
|
2011-08-07 02:58:12 +02:00
|
|
|
if (crash_debug) {
|
|
|
|
int gdb_pid;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "Forking...\n");
|
|
|
|
gdb_pid = fork();
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "Forked...\n");
|
|
|
|
if (gdb_pid == 0) { // We are the child
|
|
|
|
char spid[20];
|
|
|
|
snprintf(spid, sizeof(spid), "%i", getppid());
|
|
|
|
getch2_disable(); // allow terminal to work properly with gdb
|
|
|
|
if (execlp("gdb", "gdb", prog_path, spid, "-ex", "bt", NULL) == -1)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Couldn't start gdb\n");
|
|
|
|
} else if (gdb_pid < 0)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Couldn't fork\n");
|
|
|
|
else
|
|
|
|
waitpid(gdb_pid, NULL, 0);
|
|
|
|
if (x == SIGTRAP)
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
getch2_disable();
|
|
|
|
exit(1);
|
2001-02-24 21:28:24 +01:00
|
|
|
}
|
|
|
|
|
2001-03-19 00:32:31 +01:00
|
|
|
#include "cfg-mplayer.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2008-04-26 09:44:59 +02:00
|
|
|
static int cfg_include(m_option_t *conf, char *filename)
|
|
|
|
{
|
|
|
|
return m_config_parse_config_file(conf->priv, filename);
|
|
|
|
}
|
|
|
|
|
2011-03-10 23:54:16 +01:00
|
|
|
#define DEF_CONFIG "# Write your default config options here!\n\n\n"
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void parse_cfgfiles(struct MPContext *mpctx, m_config_t *conf)
|
2001-05-08 14:17:03 +02:00
|
|
|
{
|
2011-01-11 16:28:10 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
char *conffile;
|
|
|
|
int conffile_fd;
|
|
|
|
if (!(opts->noconfig & 2) &&
|
|
|
|
m_config_parse_config_file(conf, MPLAYER_CONFDIR "/mplayer.conf") < 0)
|
|
|
|
exit_player(mpctx, EXIT_NONE);
|
|
|
|
if ((conffile = get_path("")) == NULL)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "Cannot find HOME directory.\n");
|
|
|
|
else {
|
2003-04-04 22:02:53 +02:00
|
|
|
#ifdef __MINGW32__
|
2011-08-07 02:58:12 +02:00
|
|
|
mkdir(conffile);
|
2003-04-04 22:02:53 +02:00
|
|
|
#else
|
2011-08-07 02:58:12 +02:00
|
|
|
mkdir(conffile, 0777);
|
2003-04-04 22:02:53 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
free(conffile);
|
|
|
|
if ((conffile = get_path("config")) == NULL)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "get_path(\"config\") problem\n");
|
|
|
|
else {
|
|
|
|
if ((conffile_fd = open(conffile, O_CREAT | O_EXCL | O_WRONLY,
|
|
|
|
0666)) != -1) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Creating config file: %s\n", conffile);
|
|
|
|
write(conffile_fd, DEF_CONFIG, sizeof(DEF_CONFIG) - 1);
|
|
|
|
close(conffile_fd);
|
|
|
|
}
|
|
|
|
if (!(opts->noconfig & 1) &&
|
|
|
|
m_config_parse_config_file(conf, conffile) < 0)
|
|
|
|
exit_player(mpctx, EXIT_NONE);
|
|
|
|
free(conffile);
|
|
|
|
}
|
|
|
|
}
|
2001-02-24 21:28:24 +01:00
|
|
|
}
|
|
|
|
|
2008-01-10 19:45:08 +01:00
|
|
|
#define PROFILE_CFG_PROTOCOL "protocol."
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void load_per_protocol_config(m_config_t *conf, const char * const file)
|
2008-01-10 19:45:08 +01:00
|
|
|
{
|
|
|
|
char *str;
|
2011-08-07 02:58:12 +02:00
|
|
|
char protocol[strlen(PROFILE_CFG_PROTOCOL) + strlen(file) + 1];
|
2008-01-10 19:45:08 +01:00
|
|
|
m_profile_t *p;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2008-01-10 19:45:08 +01:00
|
|
|
/* does filename actually uses a protocol ? */
|
2011-08-07 02:58:12 +02:00
|
|
|
str = strstr(file, "://");
|
2008-01-10 19:45:08 +01:00
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
sprintf(protocol, "%s%s", PROFILE_CFG_PROTOCOL, file);
|
|
|
|
protocol[strlen(PROFILE_CFG_PROTOCOL) + strlen(file) - strlen(str)] = '\0';
|
|
|
|
p = m_config_get_profile(conf, protocol);
|
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading protocol-related profile '%s'\n", protocol);
|
|
|
|
m_config_set_profile(conf, p);
|
2008-01-10 19:45:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PROFILE_CFG_EXTENSION "extension."
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void load_per_extension_config(m_config_t *conf, const char * const file)
|
2008-01-10 19:45:08 +01:00
|
|
|
{
|
|
|
|
char *str;
|
2011-08-07 02:58:12 +02:00
|
|
|
char extension[strlen(PROFILE_CFG_EXTENSION) + 8];
|
2008-01-10 19:45:08 +01:00
|
|
|
m_profile_t *p;
|
|
|
|
|
|
|
|
/* does filename actually have an extension ? */
|
2011-08-07 02:58:12 +02:00
|
|
|
str = strrchr(file, '.');
|
2008-01-10 19:45:08 +01:00
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
sprintf(extension, PROFILE_CFG_EXTENSION);
|
|
|
|
strncat(extension, ++str, 7);
|
|
|
|
p = m_config_get_profile(conf, extension);
|
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading extension-related profile '%s'\n", extension);
|
|
|
|
m_config_set_profile(conf, p);
|
2008-01-10 19:45:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-11 00:32:50 +01:00
|
|
|
#define PROFILE_CFG_VO "vo."
|
|
|
|
#define PROFILE_CFG_AO "ao."
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void load_per_output_config(m_config_t *conf, char *cfg, char *out)
|
2008-01-11 00:32:50 +01:00
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
char profile[strlen(cfg) + strlen(out) + 1];
|
2008-01-11 00:32:50 +01:00
|
|
|
m_profile_t *p;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
sprintf(profile, "%s%s", cfg, out);
|
|
|
|
p = m_config_get_profile(conf, profile);
|
|
|
|
if (p) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"Loading extension-related profile '%s'\n", profile);
|
|
|
|
m_config_set_profile(conf, p);
|
2008-01-11 00:32:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-31 11:48:25 +02:00
|
|
|
/**
|
|
|
|
* Tries to load a config file
|
|
|
|
* @return 0 if file was not found, 1 otherwise
|
|
|
|
*/
|
|
|
|
static int try_load_config(m_config_t *conf, const char *file)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
if (stat(file, &st))
|
|
|
|
return 0;
|
2009-09-04 18:49:35 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Loading config '%s'\n", file);
|
2011-08-07 02:58:12 +02:00
|
|
|
m_config_parse_config_file(conf, file);
|
2009-08-31 11:48:25 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void load_per_file_config(m_config_t *conf, const char * const file)
|
2002-12-28 15:29:41 +01:00
|
|
|
{
|
|
|
|
char *confpath;
|
2009-09-02 21:49:10 +02:00
|
|
|
char cfg[PATH_MAX];
|
2010-12-04 11:24:42 +01:00
|
|
|
const char *name;
|
2002-12-28 15:29:41 +01:00
|
|
|
|
2009-09-02 21:49:10 +02:00
|
|
|
if (strlen(file) > PATH_MAX - 14) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Filename is too long, "
|
|
|
|
"can not load file or directory specific config files\n");
|
2009-09-02 21:49:10 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
sprintf(cfg, "%s.conf", file);
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2010-12-04 11:24:42 +01:00
|
|
|
name = mp_basename(cfg);
|
2009-09-02 21:43:05 +02:00
|
|
|
if (use_filedir_conf) {
|
2009-09-02 21:49:10 +02:00
|
|
|
char dircfg[PATH_MAX];
|
2009-09-02 21:43:05 +02:00
|
|
|
strcpy(dircfg, cfg);
|
|
|
|
strcpy(dircfg + (name - cfg), "mplayer.conf");
|
|
|
|
try_load_config(conf, dircfg);
|
|
|
|
|
|
|
|
if (try_load_config(conf, cfg))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if ((confpath = get_path(name)) != NULL) {
|
|
|
|
try_load_config(conf, confpath);
|
2002-12-28 15:29:41 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
free(confpath);
|
2002-12-28 15:29:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-27 15:22:23 +02:00
|
|
|
/* When libmpdemux performs a blocking operation (network connection or
|
|
|
|
* cache filling) if the operation fails we use this function to check
|
|
|
|
* if it was interrupted by the user.
|
|
|
|
* The function returns a new value for eof. */
|
2008-08-13 07:06:26 +02:00
|
|
|
static int libmpdemux_was_interrupted(struct MPContext *mpctx, int stop_play)
|
2008-04-21 05:07:22 +02:00
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_cmd_t *cmd;
|
|
|
|
if ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) {
|
|
|
|
switch (cmd->id) {
|
|
|
|
case MP_CMD_QUIT:
|
|
|
|
exit_player_with_rc(mpctx, EXIT_QUIT,
|
|
|
|
(cmd->nargs > 0) ? cmd->args[0].v.i : 0);
|
|
|
|
case MP_CMD_PLAY_TREE_STEP: {
|
|
|
|
stop_play = (cmd->args[0].v.i > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
|
|
|
|
mpctx->play_tree_step =
|
|
|
|
(cmd->args[0].v.i == 0) ? 1 : cmd->args[0].v.i;
|
|
|
|
} break;
|
|
|
|
case MP_CMD_PLAY_TREE_UP_STEP: {
|
|
|
|
stop_play = (cmd->args[0].v.i > 0) ? PT_UP_NEXT : PT_UP_PREV;
|
|
|
|
} break;
|
|
|
|
case MP_CMD_PLAY_ALT_SRC_STEP: {
|
|
|
|
stop_play = (cmd->args[0].v.i > 0) ? PT_NEXT_SRC : PT_PREV_SRC;
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
mp_cmd_free(cmd);
|
|
|
|
}
|
|
|
|
return stop_play;
|
2002-02-23 22:22:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static int playtree_add_playlist(struct MPContext *mpctx, play_tree_t *entry)
|
2003-01-12 20:41:38 +01:00
|
|
|
{
|
2011-07-29 06:24:09 +02:00
|
|
|
play_tree_add_bpf(entry, bstr(mpctx->filename));
|
2003-02-06 21:57:25 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
{
|
|
|
|
if (!entry) {
|
|
|
|
entry = mpctx->playtree_iter->tree;
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, 1, 0)
|
|
|
|
!= PLAY_TREE_ITER_ENTRY)
|
|
|
|
return PT_NEXT_ENTRY;
|
|
|
|
if (mpctx->playtree_iter->tree == entry) { // Single file loop
|
|
|
|
if (play_tree_iter_up_step(mpctx->playtree_iter, 1, 0)
|
|
|
|
!= PLAY_TREE_ITER_ENTRY)
|
|
|
|
return PT_NEXT_ENTRY;
|
|
|
|
}
|
|
|
|
play_tree_remove(entry, 1, 1);
|
|
|
|
return PT_NEXT_SRC;
|
|
|
|
}
|
|
|
|
play_tree_insert_entry(mpctx->playtree_iter->tree, entry);
|
|
|
|
play_tree_set_params_from(entry, mpctx->playtree_iter->tree);
|
|
|
|
entry = mpctx->playtree_iter->tree;
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, 1, 0)
|
|
|
|
!= PLAY_TREE_ITER_ENTRY)
|
|
|
|
return PT_NEXT_ENTRY;
|
|
|
|
play_tree_remove(entry, 1, 1);
|
2003-01-12 20:41:38 +01:00
|
|
|
}
|
|
|
|
return PT_NEXT_SRC;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
void add_subtitles(struct MPContext *mpctx, char *filename, float fps,
|
|
|
|
int noerr)
|
2003-04-07 18:04:02 +02:00
|
|
|
{
|
2009-12-02 16:36:59 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2010-01-16 16:38:58 +01:00
|
|
|
sub_data *subd = NULL;
|
|
|
|
struct ass_track *asst = NULL;
|
2011-01-19 19:13:48 +01:00
|
|
|
bool is_native_ass = false;
|
2003-04-07 18:04:02 +02:00
|
|
|
|
2011-01-19 17:00:22 +01:00
|
|
|
if (filename == NULL || mpctx->set_of_sub_size >= MAX_SUBTITLE_FILES)
|
2011-08-07 02:58:12 +02:00
|
|
|
return;
|
2003-04-07 18:04:02 +02:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2010-01-16 16:38:58 +01:00
|
|
|
if (opts->ass_enabled) {
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_ICONV
|
2011-07-23 00:55:13 +02:00
|
|
|
asst = mp_ass_read_stream(mpctx->ass_library, filename, sub_cp);
|
2006-10-28 17:07:18 +02:00
|
|
|
#else
|
2011-07-23 00:55:13 +02:00
|
|
|
asst = mp_ass_read_stream(mpctx->ass_library, filename, 0);
|
2006-10-28 17:07:18 +02:00
|
|
|
#endif
|
2011-01-19 19:13:48 +01:00
|
|
|
is_native_ass = asst;
|
2010-01-16 16:38:58 +01:00
|
|
|
if (!asst) {
|
2010-05-19 00:05:25 +02:00
|
|
|
subd = sub_read_file(filename, fps, &mpctx->opts);
|
2010-01-16 16:38:58 +01:00
|
|
|
if (subd) {
|
2011-09-03 12:47:56 +02:00
|
|
|
asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps);
|
2011-01-19 17:00:22 +01:00
|
|
|
sub_free(subd);
|
|
|
|
subd = NULL;
|
2010-01-16 16:38:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
2006-07-07 20:26:51 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
subd = sub_read_file(filename, fps, &mpctx->opts);
|
2010-01-16 16:38:58 +01:00
|
|
|
|
|
|
|
|
|
|
|
if (!asst && !subd) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR,
|
|
|
|
"Cannot load subtitles: %s\n", filename_recode(filename));
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2007-02-21 01:49:24 +01:00
|
|
|
mpctx->set_of_ass_tracks[mpctx->set_of_sub_size] = asst;
|
|
|
|
mpctx->set_of_subtitles[mpctx->set_of_sub_size] = subd;
|
2011-01-19 19:13:48 +01:00
|
|
|
mpctx->track_was_native_ass[mpctx->set_of_sub_size] = is_native_ass;
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_FILE_SUB_ID=%d\n", mpctx->set_of_sub_size);
|
2007-01-26 10:57:09 +01:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILE_SUB_FILENAME=%s\n",
|
2011-08-07 02:58:12 +02:00
|
|
|
filename_recode(filename));
|
2007-02-21 01:49:24 +01:00
|
|
|
++mpctx->set_of_sub_size;
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "SUB: Added subtitle file (%d): %s\n",
|
|
|
|
mpctx->set_of_sub_size, filename_recode(filename));
|
2003-04-07 18:04:02 +02:00
|
|
|
}
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
void init_vo_spudec(struct MPContext *mpctx)
|
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
unsigned width, height;
|
|
|
|
spudec_free(vo_spudec);
|
|
|
|
mpctx->initialized_flags &= ~INITIALIZED_SPUDEC;
|
|
|
|
vo_spudec = NULL;
|
2010-02-13 12:38:36 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// we currently can't work without video stream
|
|
|
|
if (!mpctx->sh_video)
|
|
|
|
return;
|
2010-02-13 12:38:36 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (spudec_ifo) {
|
|
|
|
unsigned int palette[16];
|
|
|
|
current_module = "spudec_init_vobsub";
|
|
|
|
if (vobsub_parse_ifo(NULL, spudec_ifo, palette, &width, &height,
|
|
|
|
1, -1, NULL) >= 0)
|
|
|
|
vo_spudec = spudec_new_scaled(palette, width, height, NULL, 0);
|
|
|
|
}
|
2006-11-21 16:43:23 +01:00
|
|
|
|
2011-06-19 18:05:35 +02:00
|
|
|
width = mpctx->sh_video->disp_w;
|
|
|
|
height = mpctx->sh_video->disp_h;
|
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDREAD
|
2011-08-07 02:58:12 +02:00
|
|
|
if (vo_spudec == NULL && mpctx->stream->type == STREAMTYPE_DVD) {
|
|
|
|
current_module = "spudec_init_dvdread";
|
|
|
|
vo_spudec = spudec_new_scaled(((dvd_priv_t *)(mpctx->stream->priv))->
|
|
|
|
cur_pgc->palette, width, height, NULL, 0);
|
|
|
|
}
|
2006-11-21 16:43:23 +01:00
|
|
|
#endif
|
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2011-08-07 02:58:12 +02:00
|
|
|
if (vo_spudec == NULL && mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
|
|
|
unsigned int *palette = mp_dvdnav_get_spu_clut(mpctx->stream);
|
|
|
|
current_module = "spudec_init_dvdnav";
|
|
|
|
vo_spudec = spudec_new_scaled(palette, width, height, NULL, 0);
|
|
|
|
}
|
2006-11-25 15:46:21 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (vo_spudec == NULL) {
|
|
|
|
sh_sub_t *sh = mpctx->d_sub->sh;
|
|
|
|
current_module = "spudec_init_normal";
|
|
|
|
vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata,
|
|
|
|
sh->extradata_len);
|
|
|
|
spudec_set_font_factor(vo_spudec, font_factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vo_spudec != NULL) {
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_SPUDEC;
|
|
|
|
mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only,
|
|
|
|
mpctx);
|
|
|
|
}
|
2006-11-21 16:43:23 +01:00
|
|
|
}
|
|
|
|
|
2002-08-31 17:04:18 +02:00
|
|
|
/*
|
|
|
|
* In Mac OS X the SDL-lib is built upon Cocoa. The easiest way to
|
2009-07-07 01:26:13 +02:00
|
|
|
* make it all work is to use the builtin SDL-bootstrap code, which
|
2002-08-31 17:04:18 +02:00
|
|
|
* will be done automatically by replacing our main() if we include SDL.h.
|
|
|
|
*/
|
2008-08-03 17:21:40 +02:00
|
|
|
#if defined(__APPLE__) && defined(CONFIG_SDL)
|
2010-01-03 12:33:33 +01:00
|
|
|
#ifdef CONFIG_SDL_SDL_H
|
|
|
|
#include <SDL/SDL.h>
|
|
|
|
#else
|
2002-08-31 17:04:18 +02:00
|
|
|
#include <SDL.h>
|
|
|
|
#endif
|
2010-01-03 12:33:33 +01:00
|
|
|
#endif
|
2002-08-31 17:04:18 +02:00
|
|
|
|
2004-11-03 00:06:12 +01:00
|
|
|
/**
|
|
|
|
* \brief append a formatted string
|
|
|
|
* \param buf buffer to print into
|
|
|
|
* \param pos position of terminating 0 in buf
|
|
|
|
* \param len maximum number of characters in buf, not including terminating 0
|
|
|
|
* \param format printf format string
|
|
|
|
*/
|
|
|
|
static void saddf(char *buf, unsigned *pos, int len, const char *format, ...)
|
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
|
|
*pos += vsnprintf(&buf[*pos], len - *pos, format, va);
|
|
|
|
va_end(va);
|
|
|
|
if (*pos >= len) {
|
|
|
|
buf[len] = 0;
|
|
|
|
*pos = len;
|
|
|
|
}
|
2004-11-03 00:06:12 +01:00
|
|
|
}
|
|
|
|
|
2005-10-04 15:59:25 +02:00
|
|
|
/**
|
|
|
|
* \brief append time in the hh:mm:ss.f format
|
|
|
|
* \param buf buffer to print into
|
|
|
|
* \param pos position of terminating 0 in buf
|
|
|
|
* \param len maximum number of characters in buf, not including terminating 0
|
|
|
|
* \param time time value to convert/append
|
|
|
|
*/
|
2011-08-07 02:58:12 +02:00
|
|
|
static void sadd_hhmmssf(char *buf, unsigned *pos, int len, float time)
|
|
|
|
{
|
|
|
|
int64_t tenths = 10 * time;
|
|
|
|
int f1 = tenths % 10;
|
|
|
|
int ss = (tenths / 10) % 60;
|
|
|
|
int mm = (tenths / 600) % 60;
|
|
|
|
int hh = tenths / 36000;
|
|
|
|
if (time < 0) {
|
|
|
|
saddf(buf, pos, len, "unknown");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (hh > 0)
|
|
|
|
saddf(buf, pos, len, "%2d:", hh);
|
|
|
|
if (hh > 0 || mm > 0)
|
|
|
|
saddf(buf, pos, len, "%02d:", mm);
|
|
|
|
saddf(buf, pos, len, "%02d.%1d", ss, f1);
|
2005-10-04 15:59:25 +02:00
|
|
|
}
|
|
|
|
|
2008-12-08 19:04:08 +01:00
|
|
|
static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
2004-11-03 00:06:12 +01:00
|
|
|
{
|
2008-04-21 05:55:23 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
sh_video_t * const sh_video = mpctx->sh_video;
|
|
|
|
|
|
|
|
if (mpctx->sh_audio && a_pos == MP_NOPTS_VALUE)
|
|
|
|
a_pos = playing_audio_pts(mpctx);
|
|
|
|
if (mpctx->sh_audio && sh_video && at_frame) {
|
|
|
|
mpctx->last_av_difference = a_pos - mpctx->video_pts - audio_delay;
|
|
|
|
if (mpctx->time_frame > 0)
|
|
|
|
mpctx->last_av_difference +=
|
|
|
|
mpctx->time_frame * opts->playback_speed;
|
|
|
|
if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50
|
|
|
|
&& !mpctx->drop_message_shown) {
|
|
|
|
mp_tmsg(MSGT_AVSYNC, MSGL_WARN, SystemTooSlow);
|
|
|
|
mpctx->drop_message_shown = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (opts->quiet)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (a_pos == MP_NOPTS_VALUE)
|
|
|
|
a_pos = -9; // don't print a huge negative number
|
|
|
|
|
|
|
|
int width;
|
|
|
|
char *line;
|
|
|
|
unsigned pos = 0;
|
|
|
|
get_screen_size();
|
|
|
|
if (screen_width > 0)
|
|
|
|
width = screen_width;
|
|
|
|
else
|
|
|
|
width = 80;
|
2008-10-13 18:23:55 +02:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__) || defined(__OS2__)
|
2011-08-07 02:58:12 +02:00
|
|
|
/* Windows command line is broken (MinGW's rxvt works, but we
|
|
|
|
* should not depend on that). */
|
|
|
|
width--;
|
2004-11-13 17:10:32 +01:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
line = malloc(width + 1); // one additional char for the terminating null
|
|
|
|
|
|
|
|
// Audio time
|
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
saddf(line, &pos, width, "A:%6.1f ", a_pos);
|
|
|
|
if (!sh_video) {
|
|
|
|
float len = get_time_length(mpctx);
|
|
|
|
saddf(line, &pos, width, "(");
|
|
|
|
sadd_hhmmssf(line, &pos, width, a_pos);
|
|
|
|
saddf(line, &pos, width, ") of %.1f (", len);
|
|
|
|
sadd_hhmmssf(line, &pos, width, len);
|
|
|
|
saddf(line, &pos, width, ") ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Video time
|
|
|
|
if (sh_video)
|
|
|
|
saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts);
|
2004-11-03 00:06:12 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// A-V sync
|
|
|
|
if (mpctx->sh_audio && sh_video)
|
|
|
|
saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ",
|
|
|
|
mpctx->last_av_difference, mpctx->total_avsync_change);
|
|
|
|
|
|
|
|
// Video stats
|
|
|
|
if (sh_video)
|
|
|
|
saddf(line, &pos, width, "%3d/%3d ",
|
|
|
|
(int)sh_video->num_frames,
|
|
|
|
(int)sh_video->num_frames_decoded);
|
|
|
|
|
|
|
|
// CPU usage
|
|
|
|
if (sh_video) {
|
|
|
|
if (sh_video->timer > 0.5)
|
|
|
|
saddf(line, &pos, width, "%2d%% %2d%% %4.1f%% ",
|
|
|
|
(int)(100.0 * video_time_usage * opts->playback_speed / (double)sh_video->timer),
|
|
|
|
(int)(100.0 * vout_time_usage * opts->playback_speed / (double)sh_video->timer),
|
|
|
|
(100.0 * audio_time_usage * opts->playback_speed / (double)sh_video->timer));
|
|
|
|
else
|
|
|
|
saddf(line, &pos, width, "??%% ??%% ??,?%% ");
|
|
|
|
} else if (mpctx->sh_audio) {
|
|
|
|
if (mpctx->delay > 0.5)
|
|
|
|
saddf(line, &pos, width, "%4.1f%% ",
|
|
|
|
100.0 * audio_time_usage / (double)mpctx->delay);
|
|
|
|
else
|
|
|
|
saddf(line, &pos, width, "??,?%% ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// VO stats
|
|
|
|
if (sh_video)
|
|
|
|
saddf(line, &pos, width, "%d %d ", drop_frame_cnt, output_quality);
|
2004-11-03 00:06:12 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_STREAM_CACHE
|
2011-08-07 02:58:12 +02:00
|
|
|
// cache stats
|
|
|
|
if (stream_cache_size > 0)
|
|
|
|
saddf(line, &pos, width, "%d%% ", cache_fill_status(mpctx->stream));
|
2004-11-03 00:06:12 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// other
|
|
|
|
if (opts->playback_speed != 1)
|
|
|
|
saddf(line, &pos, width, "%4.2fx ", opts->playback_speed);
|
|
|
|
|
|
|
|
// end
|
|
|
|
if (erase_to_end_of_line) {
|
|
|
|
line[pos] = 0;
|
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS,
|
|
|
|
"%s%s\r", line, erase_to_end_of_line);
|
|
|
|
} else {
|
|
|
|
memset(&line[pos], ' ', width - pos);
|
|
|
|
line[width] = 0;
|
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s\r", line);
|
|
|
|
}
|
|
|
|
free(line);
|
2004-11-03 00:06:12 +01:00
|
|
|
}
|
|
|
|
|
2011-05-21 20:46:39 +02:00
|
|
|
struct stream_dump_progress {
|
|
|
|
uint64_t count;
|
|
|
|
unsigned start_time;
|
|
|
|
unsigned last_print_time;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void stream_dump_progress_start(struct stream_dump_progress *p)
|
|
|
|
{
|
|
|
|
p->start_time = p->last_print_time = GetTimerMS();
|
|
|
|
p->count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stream_dump_progress(struct stream_dump_progress *p,
|
|
|
|
uint64_t len, stream_t *stream)
|
|
|
|
{
|
|
|
|
p->count += len;
|
|
|
|
unsigned t = GetTimerMS();
|
|
|
|
if (t - p->last_print_time < 1000)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint64_t start = stream->start_pos;
|
|
|
|
uint64_t end = stream->end_pos;
|
|
|
|
uint64_t pos = stream->pos;
|
|
|
|
|
|
|
|
p->last_print_time = t;
|
|
|
|
/* TODO: pretty print sizes; ETA */
|
|
|
|
if (end > start && pos >= start && pos <= end) {
|
|
|
|
mp_tmsg(MSGT_STATUSLINE, MSGL_STATUS,
|
2011-08-07 02:58:12 +02:00
|
|
|
"dump: %"PRIu64 " bytes written (~%.1f%%)",
|
|
|
|
p->count, 100.0 * (pos - start) / (end - start));
|
2011-05-21 20:46:39 +02:00
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "\r");
|
|
|
|
} else {
|
|
|
|
mp_tmsg(MSGT_STATUSLINE, MSGL_STATUS,
|
2011-08-07 02:58:12 +02:00
|
|
|
"dump: %"PRIu64 " bytes written", p->count);
|
2011-05-21 20:46:39 +02:00
|
|
|
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "\r");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stream_dump_progress_end(struct stream_dump_progress *p, char *name)
|
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "dump: %"PRIu64 " bytes written to '%s'.\n",
|
2011-05-21 20:46:39 +02:00
|
|
|
p->count, name);
|
|
|
|
}
|
|
|
|
|
2004-10-20 22:07:50 +02:00
|
|
|
/**
|
|
|
|
* \brief build a chain of audio filters that converts the input format
|
|
|
|
* to the ao's format, taking into account the current playback_speed.
|
2011-04-09 02:03:22 +02:00
|
|
|
* sh_audio describes the requested input format of the chain.
|
|
|
|
* ao describes the requested output format of the chain.
|
2004-10-20 22:07:50 +02:00
|
|
|
*/
|
2011-04-09 02:03:22 +02:00
|
|
|
static int build_afilter_chain(struct MPContext *mpctx)
|
2004-10-20 22:07:50 +02:00
|
|
|
{
|
2011-04-09 02:03:22 +02:00
|
|
|
struct sh_audio *sh_audio = mpctx->sh_audio;
|
|
|
|
struct ao *ao = mpctx->ao;
|
2008-04-21 05:55:23 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
int new_srate;
|
|
|
|
int result;
|
|
|
|
if (!sh_audio) {
|
|
|
|
mpctx->mixer.afilter = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
opts->playback_speed = (float)new_srate / sh_audio->samplerate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = init_audio_filters(sh_audio, new_srate,
|
|
|
|
&ao->samplerate, &ao->channels, &ao->format);
|
|
|
|
mpctx->mixer.afilter = sh_audio->afilter;
|
2011-12-22 07:02:19 +01:00
|
|
|
mixer_reinit(&mpctx->mixer);
|
2011-08-07 02:58:12 +02:00
|
|
|
return result;
|
2004-10-20 22:07:50 +02:00
|
|
|
}
|
2003-01-05 16:39:16 +01:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
|
|
|
|
typedef struct mp_osd_msg mp_osd_msg_t;
|
|
|
|
struct mp_osd_msg {
|
2006-04-24 23:33:50 +02:00
|
|
|
/// Previous message on the stack.
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_osd_msg_t *prev;
|
2006-04-24 23:33:50 +02:00
|
|
|
/// Message text.
|
2011-08-08 10:07:17 +02:00
|
|
|
char *msg;
|
2011-08-07 02:58:12 +02:00
|
|
|
int id, level, started;
|
2006-04-24 23:33:50 +02:00
|
|
|
/// Display duration in ms.
|
2011-08-07 02:58:12 +02:00
|
|
|
unsigned time;
|
2005-11-16 17:51:48 +01:00
|
|
|
};
|
|
|
|
|
2006-04-24 23:33:50 +02:00
|
|
|
/// OSD message stack.
|
2011-08-07 02:58:12 +02:00
|
|
|
static mp_osd_msg_t *osd_msg_stack = NULL;
|
2005-11-16 17:51:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Add a message on the OSD message stack
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2005-11-16 17:51:48 +01:00
|
|
|
* If a message with the same id is already present in the stack
|
|
|
|
* it is pulled on top of the stack, otherwise a new message is created.
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2005-11-16 17:51:48 +01:00
|
|
|
*/
|
2010-01-12 11:16:00 +01:00
|
|
|
static void set_osd_msg_va(int id, int level, int time, const char *fmt,
|
|
|
|
va_list ap)
|
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_osd_msg_t *msg, *last = NULL;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
// look if the id is already in the stack
|
2011-08-07 02:58:12 +02:00
|
|
|
for (msg = osd_msg_stack; msg && msg->id != id;
|
|
|
|
last = msg, msg = msg->prev) ;
|
2005-11-16 17:51:48 +01:00
|
|
|
// not found: alloc it
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!msg) {
|
2011-08-08 10:07:17 +02:00
|
|
|
msg = talloc_zero(NULL, mp_osd_msg_t);
|
2005-11-16 17:51:48 +01:00
|
|
|
msg->prev = osd_msg_stack;
|
|
|
|
osd_msg_stack = msg;
|
2011-08-07 02:58:12 +02:00
|
|
|
} else if (last) { // found, but it's not on top of the stack
|
2005-11-16 17:51:48 +01:00
|
|
|
last->prev = msg->prev;
|
|
|
|
msg->prev = osd_msg_stack;
|
|
|
|
osd_msg_stack = msg;
|
|
|
|
}
|
2011-08-08 10:07:17 +02:00
|
|
|
talloc_free(msg->msg);
|
2005-11-16 17:51:48 +01:00
|
|
|
// write the msg
|
2011-08-08 10:07:17 +02:00
|
|
|
msg->msg = talloc_vasprintf(msg, fmt, ap);
|
2005-11-16 17:51:48 +01:00
|
|
|
// set id and time
|
|
|
|
msg->id = id;
|
|
|
|
msg->level = level;
|
|
|
|
msg->time = time;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
2010-01-12 11:16:00 +01:00
|
|
|
void set_osd_msg(int id, int level, int time, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
set_osd_msg_va(id, level, time, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_osd_tmsg(int id, int level, int time, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
set_osd_msg_va(id, level, time, mp_gtext(fmt), ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2005-11-28 00:29:33 +01:00
|
|
|
/**
|
|
|
|
* \brief Remove a message from the OSD stack
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2005-11-28 00:29:33 +01:00
|
|
|
* This function can be used to get rid of a message right away.
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2005-11-28 00:29:33 +01:00
|
|
|
*/
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
void rm_osd_msg(int id)
|
|
|
|
{
|
|
|
|
mp_osd_msg_t *msg, *last = NULL;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-28 00:29:33 +01:00
|
|
|
// Search for the msg
|
2011-08-07 02:58:12 +02:00
|
|
|
for (msg = osd_msg_stack; msg && msg->id != id;
|
|
|
|
last = msg, msg = msg->prev) ;
|
|
|
|
if (!msg)
|
|
|
|
return;
|
2005-11-28 00:29:33 +01:00
|
|
|
|
|
|
|
// Detach it from the stack and free it
|
2011-08-07 02:58:12 +02:00
|
|
|
if (last)
|
2005-11-28 00:29:33 +01:00
|
|
|
last->prev = msg->prev;
|
|
|
|
else
|
|
|
|
osd_msg_stack = msg->prev;
|
2011-08-08 10:07:17 +02:00
|
|
|
talloc_free(msg);
|
2005-11-28 00:29:33 +01:00
|
|
|
}
|
|
|
|
|
2006-03-24 21:42:23 +01:00
|
|
|
/**
|
|
|
|
* \brief Remove all messages from the OSD stack
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2006-03-24 21:42:23 +01:00
|
|
|
*/
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void clear_osd_msgs(void)
|
|
|
|
{
|
|
|
|
mp_osd_msg_t *msg = osd_msg_stack, *prev = NULL;
|
|
|
|
while (msg) {
|
2006-03-24 21:42:23 +01:00
|
|
|
prev = msg->prev;
|
2011-08-08 10:07:17 +02:00
|
|
|
talloc_free(msg);
|
2006-03-24 21:42:23 +01:00
|
|
|
msg = prev;
|
|
|
|
}
|
|
|
|
osd_msg_stack = NULL;
|
|
|
|
}
|
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
/**
|
2006-04-27 15:22:23 +02:00
|
|
|
* \brief Get the current message from the OSD stack.
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2006-04-27 15:22:23 +02:00
|
|
|
* This function decrements the message timer and destroys the old ones.
|
2005-11-16 17:51:48 +01:00
|
|
|
* The message that should be displayed is returned (if any).
|
2009-07-07 01:26:13 +02:00
|
|
|
*
|
2005-11-16 17:51:48 +01:00
|
|
|
*/
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx)
|
2008-04-21 05:07:22 +02:00
|
|
|
{
|
2009-03-30 01:06:58 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_osd_msg_t *msg, *prev, *last = NULL;
|
2005-11-16 17:51:48 +01:00
|
|
|
static unsigned last_update = 0;
|
|
|
|
unsigned now = GetTimerMS();
|
|
|
|
unsigned diff;
|
|
|
|
char hidden_dec_done = 0;
|
2007-08-21 02:24:13 +02:00
|
|
|
|
2009-03-30 02:13:17 +02:00
|
|
|
if (mpctx->osd_visible) {
|
2011-08-07 02:58:12 +02:00
|
|
|
// 36000000 means max timed visibility is 1 hour into the future, if
|
|
|
|
// the difference is greater assume it's wrapped around from below 0
|
2009-03-30 02:13:17 +02:00
|
|
|
if (mpctx->osd_visible - now > 36000000) {
|
|
|
|
mpctx->osd_visible = 0;
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_osd_progbar_type = -1; // disable
|
|
|
|
vo_osd_changed(OSDTYPE_PROGBAR);
|
2008-12-09 17:39:38 +01:00
|
|
|
mpctx->osd_function = mpctx->paused ? OSD_PAUSE : OSD_PLAY;
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2007-08-21 02:24:13 +02:00
|
|
|
}
|
2009-03-30 02:13:17 +02:00
|
|
|
if (mpctx->osd_show_percentage_until - now > 36000000)
|
|
|
|
mpctx->osd_show_percentage_until = 0;
|
2007-08-21 02:24:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!last_update)
|
|
|
|
last_update = now;
|
2005-11-16 17:51:48 +01:00
|
|
|
diff = now >= last_update ? now - last_update : 0;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
last_update = now;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2006-04-27 15:22:23 +02:00
|
|
|
// Look for the first message in the stack with high enough level.
|
2011-08-07 02:58:12 +02:00
|
|
|
for (msg = osd_msg_stack; msg; last = msg, msg = prev) {
|
2005-11-16 17:51:48 +01:00
|
|
|
prev = msg->prev;
|
2009-03-30 01:06:58 +02:00
|
|
|
if (msg->level > opts->osd_level && hidden_dec_done)
|
|
|
|
continue;
|
2006-04-27 15:22:23 +02: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 02:58:12 +02:00
|
|
|
if (!msg->started || msg->time > diff) {
|
|
|
|
if (msg->started)
|
|
|
|
msg->time -= diff;
|
|
|
|
else
|
|
|
|
msg->started = 1;
|
2005-11-16 17:51:48 +01:00
|
|
|
// display it
|
2009-03-30 01:06:58 +02:00
|
|
|
if (msg->level <= opts->osd_level)
|
|
|
|
return msg;
|
2005-11-16 17:51:48 +01:00
|
|
|
hidden_dec_done = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// kill the message
|
2011-08-08 10:07:17 +02:00
|
|
|
talloc_free(msg);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (last) {
|
2005-11-16 17:51:48 +01:00
|
|
|
last->prev = prev;
|
|
|
|
msg = last;
|
|
|
|
} else {
|
|
|
|
osd_msg_stack = prev;
|
|
|
|
msg = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nothing found
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Display the OSD bar.
|
|
|
|
*
|
2006-04-27 15:22:23 +02:00
|
|
|
* Display the OSD bar or fall back on a simple message.
|
2005-11-16 17:51:48 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
|
|
|
|
double min, double max, double val)
|
|
|
|
{
|
2009-03-30 01:06:58 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
if (opts->osd_level < 1)
|
|
|
|
return;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_video) {
|
2009-03-30 02:13:17 +02:00
|
|
|
mpctx->osd_visible = (GetTimerMS() + 1000) | 1;
|
2005-11-16 17:51:48 +01:00
|
|
|
vo_osd_progbar_type = type;
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_osd_progbar_value = 256 * (val - min) / (max - min);
|
2005-11-16 17:51:48 +01:00
|
|
|
vo_osd_changed(OSDTYPE_PROGBAR);
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2009-03-30 01:06:58 +02:00
|
|
|
set_osd_msg(OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%",
|
2011-08-07 02:58:12 +02:00
|
|
|
name, ROUND(100 * (val - min) / (max - min)));
|
2005-11-16 17:51:48 +01:00
|
|
|
}
|
|
|
|
|
2009-09-23 23:48:48 +02:00
|
|
|
/**
|
|
|
|
* \brief Display text subtitles on the OSD
|
|
|
|
*/
|
2009-10-06 03:28:59 +02:00
|
|
|
void set_osd_subtitle(struct MPContext *mpctx, subtitle *subs)
|
|
|
|
{
|
2009-09-23 23:48:48 +02:00
|
|
|
int i;
|
|
|
|
vo_sub = subs;
|
|
|
|
vo_osd_changed(OSDTYPE_SUBTITLE);
|
|
|
|
if (!mpctx->sh_video) {
|
|
|
|
// reverse order, since newest set_osd_msg is displayed first
|
|
|
|
for (i = SUB_MAX_TEXT - 1; i >= 0; i--) {
|
|
|
|
if (!subs || i >= subs->lines || !subs->text[i])
|
|
|
|
rm_osd_msg(OSD_MSG_SUB_BASE + i);
|
|
|
|
else {
|
2011-08-07 02:58:12 +02:00
|
|
|
// HACK: currently display time for each sub line
|
|
|
|
// except the last is set to 2 seconds.
|
2009-09-23 23:48:48 +02:00
|
|
|
int display_time = i == subs->lines - 1 ? 180000 : 2000;
|
2011-08-07 02:58:12 +02:00
|
|
|
set_osd_msg(OSD_MSG_SUB_BASE + i, 1, display_time,
|
|
|
|
"%s", subs->text[i]);
|
2009-09-23 23:48:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-11-16 17:51:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Update the OSD message line.
|
|
|
|
*
|
2006-04-27 15:22:23 +02: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-07 01:26:13 +02:00
|
|
|
*
|
2005-11-16 17:51:48 +01:00
|
|
|
*/
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
static void update_osd_msg(struct MPContext *mpctx)
|
|
|
|
{
|
2009-03-30 01:06:58 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2005-11-16 17:51:48 +01:00
|
|
|
mp_osd_msg_t *msg;
|
2008-06-24 00:53:58 +02:00
|
|
|
struct osd_state *osd = mpctx->osd;
|
2009-02-03 23:28:17 +01:00
|
|
|
char osd_text_timer[128];
|
2009-03-30 02:13:17 +02:00
|
|
|
|
|
|
|
if (mpctx->add_osd_seek_info) {
|
2010-11-07 23:54:32 +01:00
|
|
|
double percentage = get_percent_pos(mpctx);
|
2009-03-29 21:45:06 +02:00
|
|
|
set_osd_bar(mpctx, 0, "Position", 0, 100, percentage);
|
2009-03-30 02:13:17 +02:00
|
|
|
if (mpctx->sh_video)
|
|
|
|
mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1;
|
|
|
|
mpctx->add_osd_seek_info = false;
|
|
|
|
}
|
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
// Look if we have a msg
|
2011-08-07 02:58:12 +02:00
|
|
|
if ((msg = get_osd_msg(mpctx))) {
|
2008-06-24 00:53:58 +02:00
|
|
|
if (strcmp(osd->osd_text, msg->msg)) {
|
2011-08-08 10:07:17 +02:00
|
|
|
osd_set_text(osd, msg->msg);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_video)
|
|
|
|
vo_osd_changed(OSDTYPE_OSD);
|
|
|
|
else if (opts->term_osd)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s%s\n", opts->term_osd_esc,
|
2010-11-13 22:10:58 +01:00
|
|
|
msg->msg);
|
2005-11-16 17:51:48 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_video) {
|
2005-11-16 17:51:48 +01:00
|
|
|
// fallback on the timer
|
2009-03-30 01:06:58 +02:00
|
|
|
if (opts->osd_level >= 2) {
|
2010-11-07 23:54:32 +01:00
|
|
|
int len = get_time_length(mpctx);
|
2005-11-16 17:51:48 +01:00
|
|
|
int percentage = -1;
|
|
|
|
char percentage_text[10];
|
2011-04-12 17:11:12 +02:00
|
|
|
char fractions_text[4];
|
|
|
|
double fpts = get_current_time(mpctx);
|
|
|
|
int pts = fpts;
|
2010-11-07 23:54:32 +01:00
|
|
|
|
|
|
|
if (mpctx->osd_show_percentage_until)
|
|
|
|
percentage = get_percent_pos(mpctx);
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
if (percentage >= 0)
|
|
|
|
snprintf(percentage_text, 9, " (%d%%)", percentage);
|
|
|
|
else
|
|
|
|
percentage_text[0] = 0;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-04-12 17:11:12 +02:00
|
|
|
if (opts->osd_fractions == 1) {
|
|
|
|
//print fractions as sub-second timestamp
|
|
|
|
snprintf(fractions_text, sizeof(fractions_text), ".%02d",
|
|
|
|
(int)((fpts - pts) * 100));
|
|
|
|
} else if (opts->osd_fractions == 2) {
|
|
|
|
/* Print fractions by estimating the frame count within the
|
|
|
|
* second.
|
|
|
|
*
|
|
|
|
* Rounding or cutting off numbers after the decimal point
|
|
|
|
* causes problems because of float's precision and movies
|
|
|
|
* whose first frame is not exactly at timestamp 0. Therefore,
|
|
|
|
* we add 0.2 and cut off at the decimal point, which proved
|
|
|
|
* to be good heuristic.
|
|
|
|
*/
|
|
|
|
double fps = mpctx->sh_video->fps;
|
|
|
|
if (fps <= 1 || fps > 99)
|
|
|
|
strcpy(fractions_text, ".??");
|
|
|
|
else
|
|
|
|
snprintf(fractions_text, sizeof(fractions_text), ".%02d",
|
2011-08-07 02:58:12 +02:00
|
|
|
(int) ((fpts - pts) * fps + 0.2));
|
2011-04-12 17:11:12 +02:00
|
|
|
} else {
|
|
|
|
//do not print fractions
|
|
|
|
fractions_text[0] = 0;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:26:13 +02:00
|
|
|
if (opts->osd_level == 3)
|
2011-08-08 10:07:17 +02:00
|
|
|
snprintf(osd_text_timer, sizeof(osd_text_timer),
|
2011-04-12 17:11:12 +02:00
|
|
|
"%c %02d:%02d:%02d%s / %02d:%02d:%02d%s",
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->osd_function, pts / 3600, (pts / 60) % 60, pts % 60,
|
|
|
|
fractions_text, len / 3600, (len / 60) % 60, len % 60,
|
2011-04-12 17:11:12 +02:00
|
|
|
percentage_text);
|
2005-11-16 17:51:48 +01:00
|
|
|
else
|
2011-08-08 10:07:17 +02:00
|
|
|
snprintf(osd_text_timer, sizeof(osd_text_timer),
|
|
|
|
"%c %02d:%02d:%02d%s%s",
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->osd_function, pts / 3600, (pts / 60) % 60,
|
|
|
|
pts % 60, fractions_text, percentage_text);
|
2005-11-16 17:51:48 +01:00
|
|
|
} else
|
2011-08-07 02:58:12 +02:00
|
|
|
osd_text_timer[0] = 0;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2008-06-24 00:53:58 +02:00
|
|
|
if (strcmp(osd->osd_text, osd_text_timer)) {
|
2011-08-08 10:07:17 +02:00
|
|
|
osd_set_text(osd, osd_text_timer);
|
2005-11-16 17:51:48 +01:00
|
|
|
vo_osd_changed(OSDTYPE_OSD);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2005-11-16 17:51:48 +01:00
|
|
|
// Clear the term osd line
|
2010-11-13 22:10:58 +01:00
|
|
|
if (opts->term_osd && osd->osd_text[0]) {
|
2008-06-24 00:53:58 +02:00
|
|
|
osd->osd_text[0] = 0;
|
2010-11-13 22:10:58 +01:00
|
|
|
printf("%s\n", opts->term_osd_esc);
|
2005-11-16 17:51:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-23 00:05:13 +01:00
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
void reinit_audio_chain(struct MPContext *mpctx)
|
|
|
|
{
|
2008-04-01 16:35:10 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-04-09 02:03:22 +02:00
|
|
|
struct ao *ao;
|
2009-11-06 17:16:22 +01:00
|
|
|
if (!mpctx->sh_audio)
|
|
|
|
return;
|
2010-07-15 19:59:46 +02:00
|
|
|
if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "init_audio_codec";
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n");
|
|
|
|
if (!init_best_audio_codec(mpctx->sh_audio, audio_codec_list, audio_fm_list))
|
2010-07-15 19:59:46 +02:00
|
|
|
goto init_error;
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_ACODEC;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n");
|
2009-11-06 17:16:22 +01:00
|
|
|
}
|
2010-07-15 19:59:46 +02:00
|
|
|
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "af_preinit";
|
2010-07-15 19:59:46 +02:00
|
|
|
if (!(mpctx->initialized_flags & INITIALIZED_AO)) {
|
2011-04-09 02:03:22 +02:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_AO;
|
|
|
|
mpctx->ao = ao_create();
|
|
|
|
mpctx->ao->samplerate = force_srate;
|
|
|
|
mpctx->ao->format = opts->audio_output_format;
|
2010-11-12 13:06:37 +01:00
|
|
|
}
|
2011-04-09 02:03:22 +02:00
|
|
|
ao = mpctx->ao;
|
|
|
|
|
2010-11-12 13:06:37 +01:00
|
|
|
// first init to detect best values
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!init_audio_filters(mpctx->sh_audio, // preliminary init
|
|
|
|
// input:
|
|
|
|
mpctx->sh_audio->samplerate,
|
|
|
|
// output:
|
|
|
|
&ao->samplerate, &ao->channels, &ao->format)) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Error at audio filter chain "
|
2010-11-12 13:06:37 +01:00
|
|
|
"pre-init!\n");
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
2011-04-09 02:03:22 +02:00
|
|
|
if (!ao->initialized) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "ao2_init";
|
2011-04-09 02:03:22 +02:00
|
|
|
ao->buffersize = opts->ao_buffersize;
|
|
|
|
ao_init(ao, opts->audio_driver_list);
|
|
|
|
if (!ao->initialized) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Could not open/initialize audio device -> no sound.\n");
|
2010-07-15 19:59:46 +02:00
|
|
|
goto init_error;
|
|
|
|
}
|
2011-07-02 08:22:32 +02:00
|
|
|
ao->buffer.start = talloc_new(ao);
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"AO: [%s] %dHz %dch %s (%d bytes per sample)\n",
|
2011-04-09 02:03:22 +02:00
|
|
|
ao->driver->info->short_name,
|
|
|
|
ao->samplerate, ao->channels,
|
|
|
|
af_fmt2str_short(ao->format),
|
2011-08-07 02:58:12 +02:00
|
|
|
af_fmt2bits(ao->format) / 8);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Description: %s\nAO: Author: %s\n",
|
2011-04-09 02:03:22 +02: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 17:16:22 +01:00
|
|
|
}
|
2010-07-15 19:59:46 +02:00
|
|
|
|
2006-06-11 23:16:12 +02:00
|
|
|
// init audio filters:
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "af_init";
|
2011-04-09 02:03:22 +02:00
|
|
|
if (!build_afilter_chain(mpctx)) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Couldn't find matching filter/ao format!\n");
|
2009-11-06 17:16:22 +01:00
|
|
|
goto init_error;
|
2006-06-11 23:16:12 +02:00
|
|
|
}
|
2011-04-09 02:03:22 +02:00
|
|
|
mpctx->mixer.ao = ao;
|
2009-11-06 17:16:22 +01:00
|
|
|
mpctx->mixer.volstep = volstep;
|
2010-11-13 18:27:01 +01:00
|
|
|
mpctx->syncing_audio = true;
|
2009-11-06 17:16:22 +01:00
|
|
|
return;
|
2009-11-06 16:56:30 +01:00
|
|
|
|
|
|
|
init_error:
|
2011-08-07 02:58:12 +02:00
|
|
|
uninit_player(mpctx, INITIALIZED_ACODEC | INITIALIZED_AO);
|
|
|
|
mpctx->sh_audio = mpctx->d_audio->sh = NULL; // -> nosound
|
2009-11-06 16:56:30 +01:00
|
|
|
mpctx->d_audio->id = -2;
|
2006-06-11 23:16:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-14 21:48:54 +02:00
|
|
|
// Return pts value corresponding to the end point of audio written to the
|
|
|
|
// ao so far.
|
2008-04-21 05:55:23 +02:00
|
|
|
static double written_audio_pts(struct MPContext *mpctx)
|
2006-06-14 21:48:54 +02:00
|
|
|
{
|
2008-04-21 05:55:23 +02:00
|
|
|
sh_audio_t *sh_audio = mpctx->sh_audio;
|
|
|
|
demux_stream_t *d_audio = mpctx->d_audio;
|
2006-06-14 21:48:54 +02:00
|
|
|
// first calculate the end pts of audio that has been output by decoder
|
|
|
|
double a_pts = sh_audio->pts;
|
|
|
|
if (a_pts != MP_NOPTS_VALUE)
|
2011-08-07 02:58:12 +02:00
|
|
|
// Good, decoder supports new way of calculating audio pts.
|
|
|
|
// 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.
|
|
|
|
a_pts += sh_audio->pts_bytes / (double) sh_audio->o_bps;
|
2006-06-14 21:48:54 +02:00
|
|
|
else {
|
2011-08-07 02:58:12 +02:00
|
|
|
// Decoder doesn't support new way of calculating pts (or we're
|
|
|
|
// being called before it has decoded anything with known timestamp).
|
|
|
|
// Use the old method of audio pts calculation: take the timestamp
|
|
|
|
// of last packet with known pts the decoder has read data from,
|
|
|
|
// and add amount of bytes read after the beginning of that packet
|
|
|
|
// divided by input bps. This will be inaccurate if the input/output
|
|
|
|
// ratio is not constant for every audio packet or if it is constant
|
|
|
|
// but not accurately known in sh_audio->i_bps.
|
|
|
|
|
|
|
|
a_pts = d_audio->pts;
|
2011-07-30 20:05:59 +02:00
|
|
|
if (a_pts == MP_NOPTS_VALUE)
|
|
|
|
return a_pts;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// ds_tell_pts returns bytes read after last timestamp from
|
|
|
|
// demuxing layer, decoder might use sh_audio->a_in_buffer for bytes
|
|
|
|
// it has read but not decoded
|
|
|
|
if (sh_audio->i_bps)
|
|
|
|
a_pts += (ds_tell_pts(d_audio) - sh_audio->a_in_buffer_len) /
|
|
|
|
(double)sh_audio->i_bps;
|
2006-06-14 21:48:54 +02:00
|
|
|
}
|
|
|
|
// Now a_pts hopefully holds the pts for end of audio from decoder.
|
|
|
|
// Substract data in buffers between decoder and audio out.
|
|
|
|
|
|
|
|
// Decoded but not filtered
|
|
|
|
a_pts -= sh_audio->a_buffer_len / (double)sh_audio->o_bps;
|
|
|
|
|
2007-11-01 07:52:50 +01:00
|
|
|
// Data buffered in audio filters, measured in bytes of "missing" output
|
2011-07-02 08:22:32 +02:00
|
|
|
double buffered_output = af_calc_delay(sh_audio->afilter);
|
2007-11-01 07:52:50 +01:00
|
|
|
|
2006-06-14 21:48:54 +02:00
|
|
|
// Data that was ready for ao but was buffered because ao didn't fully
|
|
|
|
// accept everything to internal buffers yet
|
2011-07-02 08:22:32 +02:00
|
|
|
buffered_output += mpctx->ao->buffer.len;
|
2007-11-01 07:52:50 +01:00
|
|
|
|
|
|
|
// Filters divide audio length by playback_speed, so multiply by it
|
|
|
|
// to get the length in original units without speedup or slowdown
|
2011-04-09 02:03:22 +02:00
|
|
|
a_pts -= buffered_output * mpctx->opts.playback_speed / mpctx->ao->bps;
|
2006-06-14 21:48:54 +02:00
|
|
|
|
2009-03-29 21:45:06 +02:00
|
|
|
return a_pts + mpctx->video_offset;
|
2006-06-14 21:48:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return pts value corresponding to currently playing audio.
|
2008-04-21 05:55:23 +02:00
|
|
|
double playing_audio_pts(struct MPContext *mpctx)
|
2006-06-14 21:48:54 +02:00
|
|
|
{
|
2011-07-30 20:05:59 +02:00
|
|
|
double pts = written_audio_pts(mpctx);
|
|
|
|
if (pts == MP_NOPTS_VALUE)
|
|
|
|
return pts;
|
2011-08-07 02:58:12 +02:00
|
|
|
return pts - mpctx->opts.playback_speed *ao_get_delay(mpctx->ao);
|
2006-06-14 21:48:54 +02:00
|
|
|
}
|
|
|
|
|
2011-01-12 00:37:02 +01:00
|
|
|
static bool is_av_sub(int type)
|
|
|
|
{
|
|
|
|
return type == 'b' || type == 'p' || type == 'x';
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_subtitles(struct MPContext *mpctx, double refpts,
|
|
|
|
double sub_offset, bool reset)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
struct demux_stream *d_sub = mpctx->d_sub;
|
|
|
|
double curpts = refpts + sub_delay;
|
2011-08-07 02:58:12 +02:00
|
|
|
unsigned char *packet = NULL;
|
2011-01-12 00:37:02 +01:00
|
|
|
int len;
|
2011-01-16 19:03:08 +01:00
|
|
|
struct sh_sub *sh_sub = d_sub->sh;
|
|
|
|
int type = sh_sub ? sh_sub->type : 'v';
|
2011-01-12 00:37:02 +01:00
|
|
|
static subtitle subs;
|
|
|
|
if (reset) {
|
2011-01-16 19:03:08 +01:00
|
|
|
if (sh_sub)
|
|
|
|
sub_reset(sh_sub, mpctx->osd);
|
2011-01-12 00:37:02 +01:00
|
|
|
sub_clear_text(&subs, MP_NOPTS_VALUE);
|
|
|
|
if (vo_sub)
|
|
|
|
set_osd_subtitle(mpctx, NULL);
|
|
|
|
if (vo_spudec) {
|
|
|
|
spudec_reset(vo_spudec);
|
|
|
|
vo_osd_changed(OSDTYPE_SPU);
|
|
|
|
}
|
|
|
|
if (is_av_sub(type))
|
2011-01-16 19:03:08 +01:00
|
|
|
reset_avsub(sh_sub);
|
2011-01-12 00:37:02 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// find sub
|
|
|
|
if (mpctx->subdata) {
|
2011-08-07 02:58:12 +02:00
|
|
|
if (sub_fps == 0)
|
|
|
|
sub_fps = sh_video ? sh_video->fps : 25;
|
2011-01-12 00:37:02 +01:00
|
|
|
current_module = "find_sub";
|
|
|
|
find_sub(mpctx, mpctx->subdata, curpts *
|
|
|
|
(mpctx->subdata->sub_uses_time ? 100. : sub_fps));
|
|
|
|
if (vo_sub)
|
|
|
|
mpctx->vo_sub_last = vo_sub;
|
|
|
|
}
|
|
|
|
|
|
|
|
// DVD sub:
|
|
|
|
if (vobsub_id >= 0 || type == 'v') {
|
|
|
|
int timestamp;
|
|
|
|
current_module = "spudec";
|
|
|
|
/* Get a sub packet from the DVD or a vobsub */
|
2011-08-07 02:58:12 +02:00
|
|
|
while (1) {
|
2011-01-12 00:37:02 +01:00
|
|
|
// Vobsub
|
|
|
|
len = 0;
|
|
|
|
if (vo_vobsub) {
|
|
|
|
if (curpts >= 0) {
|
|
|
|
len = vobsub_get_packet(vo_vobsub, curpts,
|
2011-08-07 02:58:12 +02:00
|
|
|
(void **)&packet, ×tamp);
|
|
|
|
if (len > 0)
|
|
|
|
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rVOB sub: len=%d "
|
|
|
|
"v_pts=%5.3f v_timer=%5.3f sub=%5.3f ts=%d \n",
|
|
|
|
len, refpts, sh_video->timer,
|
|
|
|
timestamp / 90000.0, timestamp);
|
2011-01-12 00:37:02 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// DVD sub
|
2011-08-07 02:58:12 +02:00
|
|
|
len = ds_get_packet_sub(d_sub, (unsigned char **)&packet);
|
2011-01-12 00:37:02 +01:00
|
|
|
if (len > 0) {
|
|
|
|
// XXX This is wrong, sh_video->pts can be arbitrarily
|
|
|
|
// much behind demuxing position. Unfortunately using
|
|
|
|
// d_video->pts which would have been the simplest
|
|
|
|
// improvement doesn't work because mpeg specific hacks
|
|
|
|
// in video.c set d_video->pts to 0.
|
|
|
|
float x = d_sub->pts - refpts;
|
|
|
|
if (x > -20 && x < 20) // prevent missing subs on pts reset
|
2011-08-07 02:58:12 +02:00
|
|
|
timestamp = 90000 * d_sub->pts;
|
|
|
|
else
|
|
|
|
timestamp = 90000 * curpts;
|
2011-01-12 00:37:02 +01:00
|
|
|
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d "
|
|
|
|
"v_pts=%5.3f s_pts=%5.3f ts=%d \n", len,
|
|
|
|
refpts, d_sub->pts, timestamp);
|
|
|
|
}
|
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
if (len <= 0 || !packet)
|
|
|
|
break;
|
2011-01-12 00:37:02 +01:00
|
|
|
// create it only here, since with some broken demuxers we might
|
|
|
|
// type = v but no DVD sub and we currently do not change the
|
|
|
|
// "original frame size" ever after init, leading to wrong-sized
|
|
|
|
// PGS subtitles.
|
|
|
|
if (!vo_spudec)
|
|
|
|
vo_spudec = spudec_new(NULL);
|
|
|
|
if (vo_vobsub || timestamp >= 0)
|
|
|
|
spudec_assemble(vo_spudec, packet, len, timestamp);
|
|
|
|
}
|
|
|
|
} else if (is_text_sub(type) || is_av_sub(type) || type == 'd') {
|
|
|
|
if (type == 'd' && !d_sub->demuxer->teletext) {
|
2011-08-07 02:58:12 +02:00
|
|
|
tt_stream_props tsp = { 0 };
|
2011-01-12 00:37:02 +01:00
|
|
|
void *ptr = &tsp;
|
2011-08-07 02:58:12 +02:00
|
|
|
if (teletext_control(NULL, TV_VBI_CONTROL_START, &ptr) ==
|
|
|
|
VBI_CONTROL_TRUE)
|
2011-01-12 00:37:02 +01:00
|
|
|
d_sub->demuxer->teletext = ptr;
|
|
|
|
}
|
|
|
|
if (d_sub->non_interleaved)
|
|
|
|
ds_get_next_pts(d_sub);
|
|
|
|
|
|
|
|
while (d_sub->first) {
|
|
|
|
double subpts = ds_get_next_pts(d_sub) + sub_offset;
|
|
|
|
if (subpts > curpts) {
|
|
|
|
// Libass handled subs can be fed to it in advance
|
|
|
|
if (!opts->ass_enabled || !is_text_sub(type))
|
|
|
|
break;
|
|
|
|
// Try to avoid demuxing whole file at once
|
|
|
|
if (d_sub->non_interleaved && subpts > curpts + 1)
|
|
|
|
break;
|
|
|
|
}
|
2011-01-16 19:51:48 +01:00
|
|
|
double duration = d_sub->first->duration;
|
2011-01-12 00:37:02 +01:00
|
|
|
len = ds_get_packet_sub(d_sub, &packet);
|
|
|
|
if (is_av_sub(type)) {
|
2011-02-06 15:03:10 +01:00
|
|
|
int ret = decode_avsub(sh_sub, packet, len, subpts, duration);
|
|
|
|
if (ret < 0)
|
|
|
|
mp_msg(MSGT_SPUDEC, MSGL_WARN, "lavc failed decoding "
|
|
|
|
"subtitle\n");
|
2011-01-14 13:05:22 +01:00
|
|
|
continue;
|
2011-01-12 00:37:02 +01:00
|
|
|
}
|
|
|
|
if (type == 'm') {
|
2011-08-07 02:58:12 +02:00
|
|
|
if (len < 2)
|
|
|
|
continue;
|
2011-01-12 00:37:02 +01:00
|
|
|
len = FFMIN(len - 2, AV_RB16(packet));
|
|
|
|
packet += 2;
|
|
|
|
}
|
|
|
|
if (type == 'd') {
|
|
|
|
if (d_sub->demuxer->teletext) {
|
|
|
|
uint8_t *p = packet;
|
|
|
|
p++;
|
|
|
|
len--;
|
|
|
|
while (len >= 46) {
|
|
|
|
int sublen = p[1];
|
|
|
|
if (p[0] == 2 || p[0] == 3)
|
|
|
|
teletext_control(d_sub->demuxer->teletext,
|
2011-08-07 02:58:12 +02:00
|
|
|
TV_VBI_CONTROL_DECODE_DVB, p + 2);
|
2011-01-12 00:37:02 +01:00
|
|
|
p += sublen + 2;
|
|
|
|
len -= sublen + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2011-01-16 19:03:08 +01:00
|
|
|
if (sh_sub && sh_sub->active) {
|
|
|
|
sub_decode(sh_sub, mpctx->osd, packet, len, subpts, duration);
|
2011-01-12 00:37:02 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (subpts != MP_NOPTS_VALUE) {
|
2011-01-16 19:51:48 +01:00
|
|
|
if (duration < 0)
|
2011-01-12 00:37:02 +01:00
|
|
|
sub_clear_text(&subs, MP_NOPTS_VALUE);
|
|
|
|
if (type == 'a') { // ssa/ass subs without libass => convert to plaintext
|
|
|
|
int i;
|
2011-08-07 02:58:12 +02:00
|
|
|
unsigned char *p = packet;
|
|
|
|
for (i = 0; i < 8 && *p != '\0'; p++)
|
2011-01-12 00:37:02 +01:00
|
|
|
if (*p == ',')
|
|
|
|
i++;
|
|
|
|
if (*p == '\0') /* Broken line? */
|
|
|
|
continue;
|
|
|
|
len -= p - packet;
|
|
|
|
packet = p;
|
|
|
|
}
|
2011-01-16 19:51:48 +01:00
|
|
|
double endpts = MP_NOPTS_VALUE;
|
|
|
|
if (subpts != MP_NOPTS_VALUE && duration >= 0)
|
|
|
|
endpts = subpts + duration;
|
2011-01-12 00:37:02 +01:00
|
|
|
sub_add_text(&subs, packet, len, endpts);
|
|
|
|
set_osd_subtitle(mpctx, &subs);
|
|
|
|
}
|
|
|
|
if (d_sub->non_interleaved)
|
|
|
|
ds_get_next_pts(d_sub);
|
|
|
|
}
|
|
|
|
if (!opts->ass_enabled)
|
|
|
|
if (sub_clear_text(&subs, curpts))
|
|
|
|
set_osd_subtitle(mpctx, &subs);
|
|
|
|
}
|
|
|
|
if (vo_spudec) {
|
2011-08-07 02:58:12 +02:00
|
|
|
spudec_heartbeat(vo_spudec, 90000 * curpts);
|
2011-01-12 00:37:02 +01:00
|
|
|
if (spudec_changed(vo_spudec))
|
|
|
|
vo_osd_changed(OSDTYPE_SPU);
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = NULL;
|
2011-01-12 00:37:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void update_teletext(sh_video_t *sh_video, demuxer_t *demuxer, int reset)
|
|
|
|
{
|
|
|
|
int page_changed;
|
|
|
|
|
|
|
|
if (!demuxer->teletext)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//Also forcing page update when such ioctl is not supported or call error occured
|
2011-08-07 02:58:12 +02:00
|
|
|
if (teletext_control(demuxer->teletext, TV_VBI_CONTROL_IS_CHANGED,
|
|
|
|
&page_changed) != VBI_CONTROL_TRUE)
|
|
|
|
page_changed = 1;
|
2011-01-12 00:37:02 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!page_changed)
|
2011-01-12 00:37:02 +01:00
|
|
|
return;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (teletext_control(demuxer->teletext, TV_VBI_CONTROL_GET_VBIPAGE,
|
|
|
|
&vo_osd_teletext_page) != VBI_CONTROL_TRUE)
|
|
|
|
vo_osd_teletext_page = NULL;
|
|
|
|
if (teletext_control(demuxer->teletext, TV_VBI_CONTROL_GET_HALF_PAGE,
|
|
|
|
&vo_osd_teletext_half) != VBI_CONTROL_TRUE)
|
|
|
|
vo_osd_teletext_half = 0;
|
|
|
|
if (teletext_control(demuxer->teletext, TV_VBI_CONTROL_GET_MODE,
|
|
|
|
&vo_osd_teletext_mode) != VBI_CONTROL_TRUE)
|
|
|
|
vo_osd_teletext_mode = 0;
|
|
|
|
if (teletext_control(demuxer->teletext, TV_VBI_CONTROL_GET_FORMAT,
|
|
|
|
&vo_osd_teletext_format) != VBI_CONTROL_TRUE)
|
|
|
|
vo_osd_teletext_format = 0;
|
2011-01-12 00:37:02 +01:00
|
|
|
vo_osd_changed(OSDTYPE_TELETEXT);
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
teletext_control(demuxer->teletext, TV_VBI_CONTROL_MARK_UNCHANGED, NULL);
|
2011-01-12 00:37:02 +01:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static int check_framedrop(struct MPContext *mpctx, double frame_time)
|
|
|
|
{
|
2008-04-21 05:55:23 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
// check for frame-drop:
|
|
|
|
current_module = "check_framedrop";
|
|
|
|
if (mpctx->sh_audio && !mpctx->ao->untimed && !mpctx->d_audio->eof) {
|
|
|
|
static int dropped_frames;
|
|
|
|
float delay = opts->playback_speed * ao_get_delay(mpctx->ao);
|
|
|
|
float d = delay - mpctx->delay;
|
|
|
|
++total_frame_cnt;
|
|
|
|
// we should avoid dropping too many frames in sequence unless we
|
|
|
|
// are too late. and we allow 100ms A-V delay here:
|
|
|
|
if (d < -dropped_frames * frame_time - 0.100 && !mpctx->paused
|
|
|
|
&& !mpctx->restart_playback) {
|
|
|
|
++drop_frame_cnt;
|
|
|
|
++dropped_frames;
|
|
|
|
return frame_dropping;
|
|
|
|
} else
|
|
|
|
dropped_frames = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
2008-03-17 21:21:16 +01:00
|
|
|
}
|
|
|
|
|
2006-07-06 08:58:17 +02:00
|
|
|
|
2006-11-14 02:40:06 +01:00
|
|
|
#ifdef HAVE_RTC
|
2011-08-07 02:58:12 +02:00
|
|
|
int rtc_fd = -1;
|
2006-11-14 02:40:06 +01:00
|
|
|
#endif
|
|
|
|
|
2008-04-28 11:09:31 +02:00
|
|
|
static float timing_sleep(struct MPContext *mpctx, float time_frame)
|
2006-11-14 02:40:06 +01:00
|
|
|
{
|
|
|
|
#ifdef HAVE_RTC
|
2011-08-07 02:58:12 +02:00
|
|
|
if (rtc_fd >= 0) {
|
|
|
|
// -------- RTC -----------
|
|
|
|
current_module = "sleep_rtc";
|
2006-11-14 02:40:06 +01:00
|
|
|
while (time_frame > 0.000) {
|
2011-08-07 02:58:12 +02:00
|
|
|
unsigned long rtc_ts;
|
|
|
|
if (read(rtc_fd, &rtc_ts, sizeof(rtc_ts)) <= 0)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Linux RTC read error: %s\n", strerror(errno));
|
2008-04-28 11:09:31 +02:00
|
|
|
time_frame -= get_relative_time(mpctx);
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2006-11-14 02:40:06 +01:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2011-08-07 02:58:12 +02:00
|
|
|
// assume kernel HZ=100 for softsleep, works with larger HZ but with
|
|
|
|
// unnecessarily high CPU usage
|
2010-11-12 21:04:16 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
float margin = opts->softsleep ? 0.011 : 0;
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "sleep_timer";
|
|
|
|
while (time_frame > margin) {
|
|
|
|
usec_sleep(1000000 * (time_frame - margin));
|
2008-04-28 11:09:31 +02:00
|
|
|
time_frame -= get_relative_time(mpctx);
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
if (opts->softsleep) {
|
|
|
|
current_module = "sleep_soft";
|
|
|
|
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 02:40:06 +01:00
|
|
|
}
|
|
|
|
return time_frame;
|
|
|
|
}
|
|
|
|
|
2010-07-10 22:48:50 +02:00
|
|
|
static int select_subtitle(MPContext *mpctx)
|
2009-11-16 05:54:22 +01:00
|
|
|
{
|
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
// find the best sub to use
|
|
|
|
int id;
|
|
|
|
int found = 0;
|
|
|
|
mpctx->global_sub_pos = -1; // no subs by default
|
|
|
|
if (vobsub_id >= 0) {
|
|
|
|
// if user asks for a vobsub id, use that first.
|
|
|
|
id = vobsub_id;
|
|
|
|
found = mp_property_do("sub_vob", M_PROPERTY_SET, &id, mpctx) ==
|
|
|
|
M_PROPERTY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found && opts->sub_id >= 0) {
|
|
|
|
// if user asks for a dvd sub id, use that next.
|
|
|
|
id = opts->sub_id;
|
|
|
|
found = mp_property_do("sub_demux", M_PROPERTY_SET, &id, mpctx) ==
|
|
|
|
M_PROPERTY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
// if there are text subs to use, use those. (autosubs come last here)
|
|
|
|
id = 0;
|
|
|
|
found = mp_property_do("sub_file", M_PROPERTY_SET, &id, mpctx) ==
|
|
|
|
M_PROPERTY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found && opts->sub_id == -1) {
|
|
|
|
// finally select subs by language and container hints
|
|
|
|
if (opts->sub_id == -1)
|
|
|
|
opts->sub_id =
|
|
|
|
demuxer_sub_track_by_lang_and_default(mpctx->d_sub->demuxer,
|
|
|
|
opts->sub_lang);
|
|
|
|
if (opts->sub_id >= 0) {
|
|
|
|
id = opts->sub_id;
|
|
|
|
found = mp_property_do("sub_demux", M_PROPERTY_SET, &id, mpctx) ==
|
|
|
|
M_PROPERTY_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
2009-11-11 12:19:50 +01:00
|
|
|
}
|
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2008-01-26 12:51:34 +01:00
|
|
|
#ifndef FF_B_TYPE
|
|
|
|
#define FF_B_TYPE 3
|
|
|
|
#endif
|
|
|
|
/// store decoded video image
|
2011-08-07 02:58:12 +02:00
|
|
|
static mp_image_t *mp_dvdnav_copy_mpi(mp_image_t *to_mpi,
|
|
|
|
mp_image_t *from_mpi)
|
|
|
|
{
|
2008-01-26 12:51:34 +01:00
|
|
|
mp_image_t *mpi;
|
|
|
|
|
|
|
|
/// Do not store B-frames
|
|
|
|
if (from_mpi->pict_type == FF_B_TYPE)
|
|
|
|
return to_mpi;
|
|
|
|
|
|
|
|
if (to_mpi &&
|
|
|
|
to_mpi->w == from_mpi->w &&
|
|
|
|
to_mpi->h == from_mpi->h &&
|
|
|
|
to_mpi->imgfmt == from_mpi->imgfmt)
|
|
|
|
mpi = to_mpi;
|
|
|
|
else {
|
|
|
|
if (to_mpi)
|
|
|
|
free_mp_image(to_mpi);
|
|
|
|
if (from_mpi->w == 0 || from_mpi->h == 0)
|
|
|
|
return NULL;
|
2011-08-07 02:58:12 +02:00
|
|
|
mpi = alloc_mpi(from_mpi->w, from_mpi->h, from_mpi->imgfmt);
|
2008-01-26 12:51:34 +01:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
copy_mpi(mpi, from_mpi);
|
2008-01-26 12:51:34 +01:00
|
|
|
return mpi;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void mp_dvdnav_reset_stream(MPContext *ctx)
|
|
|
|
{
|
2008-06-26 05:42:03 +02:00
|
|
|
struct MPOpts *opts = &ctx->opts;
|
2008-01-26 12:51:34 +01:00
|
|
|
if (ctx->sh_video) {
|
|
|
|
/// clear video pts
|
|
|
|
ctx->d_video->pts = 0.0f;
|
|
|
|
ctx->sh_video->pts = 0.0f;
|
|
|
|
ctx->sh_video->i_pts = 0.0f;
|
|
|
|
ctx->sh_video->last_pts = 0.0f;
|
|
|
|
ctx->sh_video->num_buffered_pts = 0;
|
|
|
|
ctx->sh_video->num_frames = 0;
|
|
|
|
ctx->sh_video->num_frames_decoded = 0;
|
|
|
|
ctx->sh_video->timer = 0.0f;
|
|
|
|
ctx->sh_video->stream_delay = 0.0f;
|
|
|
|
ctx->sh_video->timer = 0;
|
|
|
|
ctx->demuxer->stream_pts = MP_NOPTS_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->sh_audio) {
|
|
|
|
/// free audio packets and reset
|
|
|
|
ds_free_packs(ctx->d_audio);
|
|
|
|
audio_delay -= ctx->sh_audio->stream_delay;
|
2011-07-07 23:41:42 +02:00
|
|
|
ctx->delay = -audio_delay;
|
2011-04-09 02:03:22 +02:00
|
|
|
ao_reset(ctx->ao);
|
2008-01-26 12:51:34 +01:00
|
|
|
resync_audio_stream(ctx->sh_audio);
|
|
|
|
}
|
|
|
|
|
|
|
|
audio_delay = 0.0f;
|
2010-07-10 22:48:50 +02:00
|
|
|
ctx->sub_counts[SUB_SOURCE_DEMUX] = mp_dvdnav_number_of_subs(ctx->stream);
|
2010-05-21 22:57:29 +02:00
|
|
|
if (opts->sub_lang && opts->sub_id == dvdsub_lang_id) {
|
|
|
|
dvdsub_lang_id = mp_dvdnav_sid_from_lang(ctx->stream, opts->sub_lang);
|
2009-11-16 14:10:35 +01:00
|
|
|
if (dvdsub_lang_id != opts->sub_id) {
|
|
|
|
opts->sub_id = dvdsub_lang_id;
|
2009-11-11 12:22:12 +01:00
|
|
|
select_subtitle(ctx);
|
|
|
|
}
|
|
|
|
}
|
2008-01-26 12:51:34 +01:00
|
|
|
|
|
|
|
/// clear all EOF related flags
|
|
|
|
ctx->d_video->eof = ctx->d_audio->eof = ctx->stream->eof = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Restore last decoded DVDNAV (still frame)
|
2008-06-26 05:42:03 +02:00
|
|
|
static mp_image_t *mp_dvdnav_restore_smpi(struct MPContext *mpctx,
|
|
|
|
int *in_size,
|
2008-01-26 12:51:34 +01:00
|
|
|
unsigned char **start,
|
|
|
|
mp_image_t *decoded_frame)
|
|
|
|
{
|
|
|
|
if (mpctx->stream->type != STREAMTYPE_DVDNAV)
|
|
|
|
return decoded_frame;
|
|
|
|
|
2011-06-12 11:57:07 +02:00
|
|
|
/// a change occurred in dvdnav stream
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mp_dvdnav_cell_has_changed(mpctx->stream, 0)) {
|
2008-01-26 12:51:34 +01:00
|
|
|
mp_dvdnav_read_wait(mpctx->stream, 1, 1);
|
|
|
|
mp_dvdnav_context_free(mpctx);
|
|
|
|
mp_dvdnav_reset_stream(mpctx);
|
|
|
|
mp_dvdnav_read_wait(mpctx->stream, 0, 1);
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_dvdnav_cell_has_changed(mpctx->stream, 1);
|
2008-01-26 12:51:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*in_size < 0) {
|
|
|
|
float len;
|
|
|
|
|
|
|
|
/// Display still frame, if any
|
|
|
|
if (mpctx->nav_smpi && !mpctx->nav_buffer)
|
|
|
|
decoded_frame = mpctx->nav_smpi;
|
|
|
|
|
|
|
|
/// increment video frame : continue playing after still frame
|
2010-11-07 23:54:32 +01:00
|
|
|
len = get_time_length(mpctx);
|
2008-01-26 12:51:34 +01:00
|
|
|
if (mpctx->sh_video->pts >= len &&
|
|
|
|
mpctx->sh_video->pts > 0.0 && len > 0.0) {
|
|
|
|
mp_dvdnav_skip_still(mpctx->stream);
|
|
|
|
mp_dvdnav_skip_wait(mpctx->stream);
|
|
|
|
}
|
|
|
|
mpctx->sh_video->pts += 1 / mpctx->sh_video->fps;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2008-01-26 12:51:34 +01:00
|
|
|
if (mpctx->nav_buffer) {
|
|
|
|
*start = mpctx->nav_start;
|
|
|
|
*in_size = mpctx->nav_in_size;
|
|
|
|
if (mpctx->nav_start)
|
2011-08-07 02:58:12 +02:00
|
|
|
memcpy(*start, mpctx->nav_buffer, mpctx->nav_in_size);
|
2008-01-26 12:51:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return decoded_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Save last decoded DVDNAV (still frame)
|
2008-06-26 05:42:03 +02:00
|
|
|
static void mp_dvdnav_save_smpi(struct MPContext *mpctx, int in_size,
|
2008-01-26 12:51:34 +01:00
|
|
|
unsigned char *start,
|
|
|
|
mp_image_t *decoded_frame)
|
|
|
|
{
|
|
|
|
if (mpctx->stream->type != STREAMTYPE_DVDNAV)
|
|
|
|
return;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2010-11-07 13:47:40 +01:00
|
|
|
free(mpctx->nav_buffer);
|
2011-06-12 11:57:07 +02:00
|
|
|
mpctx->nav_buffer = NULL;
|
|
|
|
mpctx->nav_start = NULL;
|
|
|
|
mpctx->nav_in_size = -1;
|
|
|
|
|
|
|
|
if (in_size > 0)
|
|
|
|
mpctx->nav_buffer = malloc(in_size);
|
|
|
|
if (mpctx->nav_buffer) {
|
|
|
|
mpctx->nav_start = start;
|
2011-08-10 22:26:48 +02:00
|
|
|
mpctx->nav_in_size = in_size;
|
2011-08-07 02:58:12 +02:00
|
|
|
memcpy(mpctx->nav_buffer, start, in_size);
|
2011-06-12 11:57:07 +02:00
|
|
|
}
|
2008-01-26 12:51:34 +01:00
|
|
|
|
|
|
|
if (decoded_frame && mpctx->nav_smpi != decoded_frame)
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->nav_smpi = mp_dvdnav_copy_mpi(mpctx->nav_smpi, decoded_frame);
|
2008-01-26 12:51:34 +01:00
|
|
|
}
|
2008-07-30 14:01:30 +02:00
|
|
|
#endif /* CONFIG_DVDNAV */
|
2008-01-26 12:51:34 +01:00
|
|
|
|
2008-12-08 19:04:08 +01: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 02:40:06 +01:00
|
|
|
{
|
2008-12-08 19:04:08 +01:00
|
|
|
current_module = "av_sync";
|
2006-11-14 02:40:06 +01:00
|
|
|
|
2010-11-13 18:27:01 +01:00
|
|
|
if (!mpctx->sh_audio || mpctx->syncing_audio)
|
2008-12-08 19:04:08 +01:00
|
|
|
return;
|
2006-11-14 02:40:06 +01:00
|
|
|
|
2008-12-08 19:04:08 +01: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;
|
|
|
|
av_delay -= audio_delay; // This much pts difference is desired
|
2006-11-14 02:40:06 +01:00
|
|
|
|
2008-12-08 19:04:08 +01:00
|
|
|
double change = av_delay * 0.1;
|
|
|
|
double max_change = default_max_pts_correction >= 0 ?
|
2011-08-07 02:58:12 +02:00
|
|
|
default_max_pts_correction : frame_time * 0.1;
|
2008-12-08 19:04:08 +01: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 02:40:06 +01:00
|
|
|
}
|
|
|
|
|
2011-07-31 15:31:07 +02:00
|
|
|
static int write_to_ao(struct MPContext *mpctx, void *data, int len, int flags,
|
|
|
|
double pts)
|
2011-07-31 00:05:17 +02:00
|
|
|
{
|
|
|
|
if (mpctx->paused)
|
|
|
|
return 0;
|
2011-07-31 15:31:07 +02:00
|
|
|
struct ao *ao = mpctx->ao;
|
|
|
|
double bps = ao->bps / mpctx->opts.playback_speed;
|
|
|
|
ao->pts = pts;
|
|
|
|
// hack used by some mpeg-writing AOs
|
2011-08-07 02:58:12 +02:00
|
|
|
ao->brokenpts = ((mpctx->sh_video ? mpctx->sh_video->timer : 0) +
|
|
|
|
mpctx->delay) * 90000.0;
|
2011-07-31 15:31:07 +02:00
|
|
|
int played = ao_play(mpctx->ao, data, len, flags);
|
|
|
|
if (played > 0) {
|
|
|
|
mpctx->delay += played / bps;
|
|
|
|
// Keep correct pts for remaining data - could be used to flush
|
|
|
|
// remaining buffer when closing ao.
|
|
|
|
ao->pts += played / bps;
|
|
|
|
}
|
|
|
|
return played;
|
2011-07-31 00:05:17 +02:00
|
|
|
}
|
|
|
|
|
2010-11-13 18:27:01 +01:00
|
|
|
#define ASYNC_PLAY_DONE -3
|
|
|
|
static int audio_start_sync(struct MPContext *mpctx, int playsize)
|
|
|
|
{
|
2011-04-09 02:03:22 +02:00
|
|
|
struct ao *ao = mpctx->ao;
|
2010-11-13 18:27:01 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
// Timing info may not be set without
|
2011-07-02 08:22:32 +02:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, 1);
|
2010-11-13 18:27:01 +01:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
|
2010-11-21 18:25:18 +01:00
|
|
|
int bytes;
|
2010-12-16 20:26:12 +01:00
|
|
|
bool did_retry = false;
|
2011-07-31 15:31:07 +02:00
|
|
|
double written_pts;
|
|
|
|
double bps = ao->bps / opts->playback_speed;
|
2011-11-06 14:41:39 +01:00
|
|
|
bool hrseek = mpctx->hrseek_active; // audio only hrseek
|
|
|
|
mpctx->hrseek_active = false;
|
2010-11-21 18:25:18 +01:00
|
|
|
while (1) {
|
2011-07-31 15:31:07 +02:00
|
|
|
written_pts = written_audio_pts(mpctx);
|
2011-11-06 14:41:39 +01:00
|
|
|
double ptsdiff;
|
|
|
|
if (hrseek)
|
|
|
|
ptsdiff = written_pts - mpctx->hrseek_pts;
|
|
|
|
else
|
|
|
|
ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay
|
|
|
|
- audio_delay;
|
2011-07-31 15:31:07 +02:00
|
|
|
bytes = ptsdiff * bps;
|
2011-04-09 02:03:22 +02:00
|
|
|
bytes -= bytes % (ao->channels * af_fmt2bits(ao->format) / 8);
|
2010-11-21 18:25:18 +01:00
|
|
|
|
2010-12-16 20:26:12 +01: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
|
2011-07-02 08:22:32 +02:00
|
|
|
int res = decode_audio(sh_audio, &ao->buffer, ao->bps);
|
2010-12-16 20:26:12 +01:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
did_retry = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(ptsdiff) > 300) // pts reset or just broken?
|
2010-11-21 18:25:18 +01:00
|
|
|
bytes = 0;
|
|
|
|
|
|
|
|
if (bytes > 0)
|
|
|
|
break;
|
2010-11-13 18:27:01 +01:00
|
|
|
|
|
|
|
mpctx->syncing_audio = false;
|
2010-11-21 18:25:18 +01:00
|
|
|
int a = FFMIN(-bytes, FFMAX(playsize, 20000));
|
2011-07-02 08:22:32 +02:00
|
|
|
int res = decode_audio(sh_audio, &ao->buffer, a);
|
|
|
|
bytes += ao->buffer.len;
|
2010-11-21 18:25:18 +01:00
|
|
|
if (bytes >= 0) {
|
2011-07-02 08:22:32 +02:00
|
|
|
memmove(ao->buffer.start,
|
|
|
|
ao->buffer.start + ao->buffer.len - bytes, bytes);
|
|
|
|
ao->buffer.len = bytes;
|
2010-11-13 18:27:01 +01:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
2011-07-02 08:22:32 +02:00
|
|
|
return decode_audio(sh_audio, &ao->buffer, playsize);
|
2010-11-13 18:27:01 +01:00
|
|
|
}
|
2011-07-02 08:22:32 +02:00
|
|
|
ao->buffer.len = 0;
|
2010-11-21 18:25:18 +01:00
|
|
|
if (res < 0)
|
|
|
|
return res;
|
|
|
|
}
|
2011-11-06 14:41:39 +01:00
|
|
|
if (hrseek)
|
|
|
|
// Don't add silence in audio-only case even if position is too late
|
|
|
|
return 0;
|
2010-11-21 18:25:18 +01:00
|
|
|
int fillbyte = 0;
|
2011-04-09 02:03:22 +02:00
|
|
|
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
|
2010-11-21 18:25:18 +01: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 15:31:07 +02:00
|
|
|
write_to_ao(mpctx, p, playsize, 0, written_pts - bytes / bps);
|
2010-11-21 18:25:18 +01:00
|
|
|
free(p);
|
|
|
|
return ASYNC_PLAY_DONE;
|
2010-11-13 18:27:01 +01:00
|
|
|
}
|
2010-11-21 18:25:18 +01:00
|
|
|
mpctx->syncing_audio = false;
|
2011-07-02 08:22:32 +02:00
|
|
|
decode_audio_prepend_bytes(&ao->buffer, bytes, fillbyte);
|
|
|
|
return decode_audio(sh_audio, &ao->buffer, playsize);
|
2010-11-13 18:27:01 +01:00
|
|
|
}
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
static int fill_audio_out_buffers(struct MPContext *mpctx)
|
2006-11-14 04:13:45 +01:00
|
|
|
{
|
2008-04-21 05:55:23 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-04-09 02:03:22 +02:00
|
|
|
struct ao *ao = mpctx->ao;
|
2006-11-14 04:13:45 +01:00
|
|
|
unsigned int t;
|
|
|
|
double tt;
|
|
|
|
int playsize;
|
2011-08-07 02:58:12 +02:00
|
|
|
int playflags = 0;
|
2011-04-07 20:17:51 +02:00
|
|
|
bool audio_eof = false;
|
|
|
|
bool partial_fill = false;
|
2007-02-21 01:49:24 +01:00
|
|
|
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
2011-04-09 02:03:22 +02:00
|
|
|
bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK);
|
|
|
|
int unitsize = ao->channels * af_fmt2bits(ao->format) / 8;
|
2006-11-14 04:13:45 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "play_audio";
|
2006-11-14 04:13:45 +01:00
|
|
|
|
2011-05-05 20:34:17 +02:00
|
|
|
if (ao->untimed && mpctx->sh_video && mpctx->delay > 0)
|
|
|
|
return 0;
|
|
|
|
|
2011-07-31 15:31:07 +02:00
|
|
|
// hack used by some mpeg-writing AOs
|
2011-08-07 02:58:12 +02:00
|
|
|
ao->brokenpts = ((mpctx->sh_video ? mpctx->sh_video->timer : 0) +
|
|
|
|
mpctx->delay) * 90000.0;
|
2011-07-31 15:31:07 +02:00
|
|
|
|
2011-07-31 00:05:17 +02:00
|
|
|
if (mpctx->paused)
|
|
|
|
playsize = 1; // just initialize things (audio pts at least)
|
|
|
|
else
|
|
|
|
playsize = ao_get_space(ao);
|
2006-11-14 04:13:45 +01:00
|
|
|
|
2009-12-29 14:51:59 +01:00
|
|
|
// Fill buffer if needed:
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "decode_audio";
|
2009-12-29 14:51:59 +01:00
|
|
|
t = GetTimer();
|
2010-11-13 18:27:01 +01:00
|
|
|
|
2011-11-06 14:41:39 +01:00
|
|
|
// Coming here with hrseek_active still set means audio-only
|
|
|
|
if (!mpctx->sh_video)
|
2010-11-13 18:27:01 +01:00
|
|
|
mpctx->syncing_audio = false;
|
2011-11-06 14:41:39 +01:00
|
|
|
if (!opts->initial_audio_sync || !modifiable_audio_format) {
|
|
|
|
mpctx->syncing_audio = false;
|
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
}
|
2010-11-13 18:27:01 +01:00
|
|
|
|
|
|
|
int res;
|
2011-11-06 14:41:39 +01:00
|
|
|
if (mpctx->syncing_audio || mpctx->hrseek_active)
|
2010-11-13 18:27:01 +01:00
|
|
|
res = audio_start_sync(mpctx, playsize);
|
|
|
|
else
|
2011-07-02 08:22:32 +02:00
|
|
|
res = decode_audio(sh_audio, &ao->buffer, playsize);
|
2010-07-15 19:59:46 +02:00
|
|
|
if (res < 0) { // EOF, error or format change
|
2011-07-02 08:22:32 +02: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.
|
|
|
|
*/
|
|
|
|
uninit_player(mpctx, INITIALIZED_AO);
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
return -1;
|
|
|
|
} else if (res == ASYNC_PLAY_DONE)
|
2011-04-07 20:17:51 +02:00
|
|
|
return 0;
|
|
|
|
else if (mpctx->d_audio->eof)
|
|
|
|
audio_eof = true;
|
2010-07-15 19:59:46 +02:00
|
|
|
}
|
2009-12-29 14:51:59 +01:00
|
|
|
t = GetTimer() - t;
|
2011-08-07 02:58:12 +02:00
|
|
|
tt = t * 0.000001f;
|
|
|
|
audio_time_usage += tt;
|
2011-04-07 20:17:51 +02:00
|
|
|
if (mpctx->timeline && modifiable_audio_format) {
|
|
|
|
double endpts = mpctx->timeline[mpctx->timeline_part + 1].start;
|
|
|
|
double bytes = (endpts - written_audio_pts(mpctx) + audio_delay)
|
2011-08-07 02:58:12 +02:00
|
|
|
* ao->bps / opts->playback_speed;
|
2011-04-07 20:17:51 +02:00
|
|
|
if (playsize > bytes) {
|
|
|
|
playsize = FFMAX(bytes, 0);
|
|
|
|
playflags |= AOPLAY_FINAL_CHUNK;
|
|
|
|
audio_eof = true;
|
|
|
|
partial_fill = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-02 08:22:32 +02:00
|
|
|
assert(ao->buffer.len % unitsize == 0);
|
|
|
|
if (playsize > ao->buffer.len) {
|
2011-04-07 20:17:51 +02:00
|
|
|
partial_fill = true;
|
2011-07-02 08:22:32 +02:00
|
|
|
playsize = ao->buffer.len;
|
2009-12-29 14:51:59 +01:00
|
|
|
if (audio_eof)
|
|
|
|
playflags |= AOPLAY_FINAL_CHUNK;
|
|
|
|
}
|
2011-04-07 20:17:51 +02:00
|
|
|
playsize -= playsize % unitsize;
|
2009-12-29 14:51:59 +01:00
|
|
|
if (!playsize)
|
2011-04-07 20:17:51 +02:00
|
|
|
return partial_fill && audio_eof ? -2 : -partial_fill;
|
2006-11-14 05:17:41 +01:00
|
|
|
|
2009-12-29 14:51:59 +01:00
|
|
|
// play audio:
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "play_audio";
|
2006-11-14 04:13:45 +01:00
|
|
|
|
2011-07-31 15:31:07 +02:00
|
|
|
int played = write_to_ao(mpctx, ao->buffer.start, playsize, playflags,
|
|
|
|
written_audio_pts(mpctx));
|
2011-07-02 08:22:32 +02: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-31 00:05:17 +02:00
|
|
|
} else if (!mpctx->paused && audio_eof && ao_get_delay(ao) < .04) {
|
2009-12-29 14:51:59 +01: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 08:22:32 +02:00
|
|
|
return -2;
|
2010-07-15 19:59:46 +02:00
|
|
|
}
|
|
|
|
|
2011-04-07 20:17:51 +02:00
|
|
|
return -partial_fill;
|
2006-11-14 04:13:45 +01:00
|
|
|
}
|
2006-11-14 02:40:06 +01:00
|
|
|
|
2010-12-14 19:20:30 +01:00
|
|
|
static int sleep_until_near_frame(struct MPContext *mpctx, float *time_frame,
|
2011-04-07 20:17:51 +02:00
|
|
|
bool sync_to_audio, float *aq_sleep_time)
|
2006-11-14 10:29:03 +01:00
|
|
|
{
|
2008-04-21 05:55:23 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2010-12-14 19:20:30 +01:00
|
|
|
double audio_limit = 2;
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "calc_sleep_time";
|
2006-11-14 10:29:03 +01:00
|
|
|
|
2011-01-01 04:37:28 +01:00
|
|
|
if (mpctx->restart_playback)
|
|
|
|
return 0;
|
|
|
|
|
2008-04-28 11:09:31 +02:00
|
|
|
*time_frame -= get_relative_time(mpctx); // reset timer
|
2006-11-14 10:29:03 +01:00
|
|
|
|
2011-04-07 20:17:51 +02:00
|
|
|
if (sync_to_audio) {
|
2011-08-07 02:58:12 +02:00
|
|
|
float delay = ao_get_delay(mpctx->ao);
|
|
|
|
mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "delay=%f\n", delay);
|
|
|
|
|
|
|
|
if (opts->autosync) {
|
|
|
|
/*
|
|
|
|
* Adjust this raw delay value by calculating the expected
|
|
|
|
* delay for this frame and generating a new value which is
|
|
|
|
* weighted between the two. The higher autosync is, the
|
|
|
|
* closer to the delay value gets to that which "-nosound"
|
|
|
|
* would have used, and the longer it will take for A/V
|
|
|
|
* sync to settle at the right value (but it eventually will.)
|
|
|
|
* This settling time is very short for values below 100.
|
|
|
|
*/
|
|
|
|
float predicted = mpctx->delay / opts->playback_speed + *time_frame;
|
|
|
|
float difference = delay - predicted;
|
|
|
|
delay = predicted + difference / (float)opts->autosync;
|
|
|
|
}
|
|
|
|
|
|
|
|
*time_frame = delay - mpctx->delay / opts->playback_speed;
|
|
|
|
|
|
|
|
// delay = amount of audio buffered in soundcard/driver
|
2010-12-14 19:20:30 +01:00
|
|
|
delay = FFMIN(delay, 0.5);
|
|
|
|
delay = FFMAX(delay, 0.1);
|
|
|
|
audio_limit = delay;
|
2006-11-14 10:29:03 +01:00
|
|
|
} else {
|
2011-08-07 02:58:12 +02:00
|
|
|
// If we're lagging more than 200 ms behind the right playback rate,
|
|
|
|
// don't try to "catch up".
|
|
|
|
// If benchmark is set always output frames as fast as possible
|
|
|
|
// without sleeping.
|
|
|
|
if (*time_frame < -0.2 || opts->benchmark)
|
|
|
|
*time_frame = 0;
|
2006-11-14 10:29:03 +01:00
|
|
|
}
|
|
|
|
|
2010-12-14 19:20:30 +01:00
|
|
|
double t = *time_frame - mpctx->video_out->flip_queue_offset;
|
2006-11-14 10:29:03 +01:00
|
|
|
|
2010-12-14 19:20:30 +01:00
|
|
|
if (t <= 0.05)
|
|
|
|
return 0;
|
2006-11-14 10:29:03 +01:00
|
|
|
|
2010-12-14 19:20:30 +01:00
|
|
|
t -= 0.05;
|
|
|
|
if (t > audio_limit * 0.6)
|
|
|
|
t = audio_limit * 0.5;
|
|
|
|
*aq_sleep_time += t;
|
|
|
|
mp_input_get_cmd(mpctx->input, t * 1000 + 1, 1);
|
|
|
|
return 1;
|
2006-11-14 10:29:03 +01:00
|
|
|
}
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
int reinit_video_chain(struct MPContext *mpctx)
|
|
|
|
{
|
2008-04-01 16:35:10 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2007-02-21 01:49:24 +01:00
|
|
|
sh_video_t * const sh_video = mpctx->sh_video;
|
2011-04-08 04:15:43 +02:00
|
|
|
if (!sh_video)
|
|
|
|
return 0;
|
2011-08-07 02:58:12 +02:00
|
|
|
double ar = -1.0;
|
2006-11-16 22:22:07 +01:00
|
|
|
//================== Init VIDEO (codec & libvo) ==========================
|
2009-03-20 02:39:29 +01:00
|
|
|
if (!opts->fixed_vo || !(mpctx->initialized_flags & INITIALIZED_VO)) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "preinit_libvo";
|
|
|
|
|
|
|
|
//shouldn't we set dvideo->id=-2 when we fail?
|
|
|
|
//if((mpctx->video_out->preinit(vo_subdevice))!=0){
|
|
|
|
if (!(mpctx->video_out = init_best_video_out(opts, mpctx->x11_state,
|
|
|
|
mpctx->key_fifo,
|
|
|
|
mpctx->input))) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
|
|
|
|
"the selected video_out (-vo) device.\n");
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_VO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO,
|
|
|
|
&ar) != STREAM_UNSUPPORTED)
|
|
|
|
mpctx->sh_video->stream_aspect = ar;
|
|
|
|
current_module = "init_video_filters";
|
|
|
|
{
|
|
|
|
char *vf_arg[] = {
|
|
|
|
"_oldargs_", (char *)mpctx->video_out, NULL
|
|
|
|
};
|
|
|
|
sh_video->vfilter = vf_open_filter(opts, NULL, "vo", vf_arg);
|
|
|
|
}
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->ass_enabled) {
|
|
|
|
int i;
|
|
|
|
int insert = 1;
|
|
|
|
if (opts->vf_settings)
|
|
|
|
for (i = 0; opts->vf_settings[i].name; ++i)
|
|
|
|
if (strcmp(opts->vf_settings[i].name, "ass") == 0) {
|
|
|
|
insert = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (insert) {
|
|
|
|
extern vf_info_t vf_info_ass;
|
|
|
|
const vf_info_t *libass_vfs[] = {
|
|
|
|
&vf_info_ass, NULL
|
|
|
|
};
|
|
|
|
char *vf_arg[] = {
|
|
|
|
"auto", "1", NULL
|
|
|
|
};
|
|
|
|
int retcode = 0;
|
|
|
|
struct vf_instance *vf_ass = vf_open_plugin_noerr(opts, libass_vfs,
|
|
|
|
sh_video->vfilter,
|
|
|
|
"ass", vf_arg,
|
|
|
|
&retcode);
|
|
|
|
if (vf_ass)
|
|
|
|
sh_video->vfilter = vf_ass;
|
|
|
|
else if (retcode == -1) // vf_ass open() returns -1 VO has EOSD
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "[ass] vf_ass not needed\n");
|
|
|
|
else
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"ASS: cannot add video filter\n");
|
2006-11-16 22:22:07 +01:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2006-11-16 22:22:07 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings);
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->ass_enabled)
|
|
|
|
sh_video->vfilter->control(sh_video->vfilter, VFCTRL_INIT_EOSD,
|
|
|
|
mpctx->ass_library);
|
2006-11-16 22:22:07 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "init_video_codec";
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n");
|
|
|
|
init_best_video_codec(sh_video, video_codec_list, video_fm_list);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "==========================================================================\n");
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!sh_video->initialized) {
|
|
|
|
if (!opts->fixed_vo)
|
|
|
|
uninit_player(mpctx, INITIALIZED_VO);
|
|
|
|
goto err_out;
|
|
|
|
}
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_VCODEC;
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (sh_video->codec)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_VIDEO_CODEC=%s\n", sh_video->codec->name);
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
sh_video->last_pts = MP_NOPTS_VALUE;
|
|
|
|
sh_video->num_buffered_pts = 0;
|
|
|
|
sh_video->next_frame_time = 0;
|
2006-11-20 10:12:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->auto_quality > 0) {
|
|
|
|
// Auto quality option enabled
|
|
|
|
output_quality = get_video_quality_max(sh_video);
|
|
|
|
if (opts->auto_quality > output_quality)
|
|
|
|
opts->auto_quality = output_quality;
|
|
|
|
else
|
|
|
|
output_quality = opts->auto_quality;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V,
|
|
|
|
"AutoQ: setting quality to %d.\n", output_quality);
|
|
|
|
set_video_quality(sh_video, output_quality);
|
|
|
|
}
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// ========== Init display (sh_video->disp_w*sh_video->disp_h/out_fmt) ============
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "init_vo";
|
2006-11-16 22:22:07 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
return 1;
|
2006-11-19 12:21:09 +01:00
|
|
|
|
|
|
|
err_out:
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->sh_video = mpctx->d_video->sh = NULL;
|
|
|
|
return 0;
|
2006-11-16 22:22:07 +01:00
|
|
|
}
|
2006-11-14 10:29:03 +01:00
|
|
|
|
2009-12-02 12:01:36 +01:00
|
|
|
static double update_video_nocorrect_pts(struct MPContext *mpctx)
|
2006-11-20 10:12:07 +01:00
|
|
|
{
|
2009-01-14 01:51:24 +01:00
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
double frame_time = 0;
|
2009-11-23 03:31:15 +01:00
|
|
|
struct vo *video_out = mpctx->video_out;
|
2010-12-15 00:02:14 +01:00
|
|
|
while (1) {
|
2009-01-14 01:51:24 +01:00
|
|
|
current_module = "filter_video";
|
|
|
|
// In nocorrect-pts mode there is no way to properly time these frames
|
2009-11-23 03:31:15 +01:00
|
|
|
if (vo_get_buffered_frame(video_out, 0) >= 0)
|
2009-01-14 01:51:24 +01:00
|
|
|
break;
|
2009-11-23 03:31:15 +01:00
|
|
|
if (vf_output_queued_frame(sh_video->vfilter))
|
2010-12-15 00:02:14 +01:00
|
|
|
break;
|
2009-01-14 01:51:24 +01:00
|
|
|
unsigned char *packet = NULL;
|
|
|
|
frame_time = sh_video->next_frame_time;
|
2010-11-11 11:29:14 +01:00
|
|
|
if (mpctx->restart_playback)
|
2009-01-14 01:51:24 +01:00
|
|
|
frame_time = 0;
|
|
|
|
int in_size = video_read_frame(sh_video, &sh_video->next_frame_time,
|
|
|
|
&packet, force_fps);
|
|
|
|
if (in_size < 0) {
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2009-01-14 01:51:24 +01:00
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
|
|
|
if (mp_dvdnav_is_eof(mpctx->stream))
|
|
|
|
return -1;
|
|
|
|
if (mpctx->d_video)
|
|
|
|
mpctx->d_video->eof = 0;
|
|
|
|
if (mpctx->d_audio)
|
|
|
|
mpctx->d_audio->eof = 0;
|
|
|
|
mpctx->stream->eof = 0;
|
|
|
|
} else
|
2008-01-26 12:51:34 +01:00
|
|
|
#endif
|
2009-01-14 01:51:24 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (in_size > max_framesize)
|
|
|
|
max_framesize = in_size;
|
|
|
|
sh_video->timer += frame_time;
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
mpctx->delay -= frame_time;
|
|
|
|
// video_read_frame can change fps (e.g. for ASF video)
|
|
|
|
vo_fps = sh_video->fps;
|
|
|
|
int framedrop_type = check_framedrop(mpctx, frame_time);
|
|
|
|
current_module = "decode video";
|
|
|
|
|
|
|
|
void *decoded_frame;
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2009-01-14 01:51:24 +01:00
|
|
|
decoded_frame = mp_dvdnav_restore_smpi(mpctx, &in_size, &packet, NULL);
|
|
|
|
if (in_size >= 0 && !decoded_frame)
|
2008-01-26 12:51:34 +01:00
|
|
|
#endif
|
2011-08-20 19:25:43 +02:00
|
|
|
decoded_frame = decode_video(sh_video, NULL, packet, in_size,
|
|
|
|
framedrop_type, sh_video->pts);
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2009-01-14 01:51:24 +01:00
|
|
|
// Save last still frame for future display
|
2009-01-15 05:00:38 +01:00
|
|
|
mp_dvdnav_save_smpi(mpctx, in_size, packet, decoded_frame);
|
2008-01-26 12:51:34 +01:00
|
|
|
#endif
|
2009-01-14 01:51:24 +01:00
|
|
|
if (decoded_frame) {
|
|
|
|
current_module = "filter video";
|
2010-12-15 00:02:14 +01:00
|
|
|
filter_video(sh_video, decoded_frame, sh_video->pts);
|
2009-01-14 01:51:24 +01:00
|
|
|
}
|
2010-12-15 00:02:14 +01:00
|
|
|
break;
|
2006-11-20 10:12:07 +01:00
|
|
|
}
|
2009-01-14 01:51:24 +01:00
|
|
|
return frame_time;
|
|
|
|
}
|
|
|
|
|
2009-11-21 19:53:10 +01:00
|
|
|
static void determine_frame_pts(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->user_pts_assoc_mode)
|
2009-11-21 19:53:10 +01:00
|
|
|
sh_video->pts_assoc_mode = opts->user_pts_assoc_mode;
|
2011-08-07 02:58:12 +02:00
|
|
|
else if (sh_video->pts_assoc_mode == 0) {
|
2010-11-14 10:53:27 +01:00
|
|
|
if (mpctx->d_video->demuxer->timestamp_type == TIMESTAMP_TYPE_PTS
|
|
|
|
&& sh_video->codec_reordered_pts != MP_NOPTS_VALUE)
|
2009-11-21 19:53:10 +01: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 02:58:12 +02:00
|
|
|
sh_video->codec_reordered_pts : sh_video->sorted_pts;
|
2009-11-21 19:53:10 +01:00
|
|
|
}
|
|
|
|
|
2009-12-02 12:01:36 +01:00
|
|
|
static double update_video(struct MPContext *mpctx)
|
2009-01-14 01:51:24 +01:00
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
2009-09-18 15:27:55 +02:00
|
|
|
struct vo *video_out = mpctx->video_out;
|
2009-01-14 01:51:24 +01:00
|
|
|
sh_video->vfilter->control(sh_video->vfilter, VFCTRL_SET_OSD_OBJ,
|
|
|
|
mpctx->osd); // hack for vf_expand
|
|
|
|
if (!mpctx->opts.correct_pts)
|
2009-12-02 12:01:36 +01:00
|
|
|
return update_video_nocorrect_pts(mpctx);
|
2009-01-14 23:32:03 +01:00
|
|
|
|
|
|
|
double pts;
|
|
|
|
|
2010-12-15 00:02:14 +01:00
|
|
|
while (1) {
|
2009-01-14 23:32:03 +01:00
|
|
|
current_module = "filter_video";
|
2011-12-06 07:27:49 +01:00
|
|
|
if (vo_get_buffered_frame(video_out, false) >= 0)
|
2009-09-18 15:27:55 +02:00
|
|
|
break;
|
2009-01-14 23:32:03 +01:00
|
|
|
// XXX Time used in this call is not counted in any performance
|
2010-12-15 00:02:14 +01:00
|
|
|
// timer now
|
2009-01-14 23:32:03 +01:00
|
|
|
if (vf_output_queued_frame(sh_video->vfilter))
|
2010-12-15 00:02:14 +01:00
|
|
|
break;
|
2011-08-20 19:25:43 +02:00
|
|
|
int in_size = 0;
|
|
|
|
unsigned char *buf = NULL;
|
|
|
|
pts = MP_NOPTS_VALUE;
|
|
|
|
struct demux_packet *pkt = ds_get_packet2(mpctx->d_video);
|
|
|
|
if (pkt) {
|
|
|
|
in_size = pkt->len;
|
|
|
|
buf = pkt->buffer;
|
|
|
|
pts = pkt->pts;
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
if (pts != MP_NOPTS_VALUE)
|
|
|
|
pts += mpctx->video_offset;
|
2009-01-14 23:32:03 +01:00
|
|
|
if (in_size > max_framesize)
|
|
|
|
max_framesize = in_size;
|
|
|
|
current_module = "decode video";
|
2010-12-15 00:09:47 +01:00
|
|
|
if (pts >= mpctx->hrseek_pts - .005)
|
|
|
|
mpctx->hrseek_framedrop = false;
|
|
|
|
int framedrop_type = mpctx->hrseek_framedrop ? 1 :
|
2011-08-07 02:58:12 +02:00
|
|
|
check_framedrop(mpctx, sh_video->frametime);
|
2011-08-20 19:25:43 +02:00
|
|
|
void *decoded_frame = decode_video(sh_video, pkt, buf, in_size,
|
2009-01-14 23:32:03 +01:00
|
|
|
framedrop_type, pts);
|
|
|
|
if (decoded_frame) {
|
2009-11-21 19:53:10 +01:00
|
|
|
determine_frame_pts(mpctx);
|
2009-01-14 23:32:03 +01:00
|
|
|
current_module = "filter video";
|
2010-12-15 00:02:14 +01:00
|
|
|
filter_video(sh_video, decoded_frame, sh_video->pts);
|
2011-08-20 19:25:43 +02:00
|
|
|
} else if (!pkt) {
|
2010-12-15 00:02:14 +01:00
|
|
|
if (vo_get_buffered_frame(video_out, true) < 0)
|
|
|
|
return -1;
|
2009-09-18 15:27:55 +02:00
|
|
|
}
|
2010-12-15 00:02:14 +01:00
|
|
|
break;
|
2009-01-14 23:32:03 +01:00
|
|
|
}
|
|
|
|
|
2010-12-15 00:02:14 +01:00
|
|
|
if (!video_out->frame_loaded)
|
|
|
|
return 0;
|
|
|
|
|
2009-09-18 15:27:55 +02:00
|
|
|
pts = video_out->next_pts;
|
2009-01-14 23:32:03 +01: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 02:39:54 +02:00
|
|
|
if (pts == MP_NOPTS_VALUE)
|
|
|
|
pts = sh_video->last_pts;
|
2006-11-20 10:12:07 +01:00
|
|
|
}
|
2011-03-03 11:54:36 +01:00
|
|
|
if (mpctx->hrseek_active && pts < mpctx->hrseek_pts - .005) {
|
|
|
|
vo_skip_frame(video_out);
|
2010-12-15 00:09:47 +01:00
|
|
|
return 0;
|
2011-03-03 11:54:36 +01:00
|
|
|
}
|
2010-12-15 00:09:47 +01:00
|
|
|
mpctx->hrseek_active = false;
|
2009-01-14 23:32:03 +01:00
|
|
|
sh_video->pts = pts;
|
|
|
|
if (sh_video->last_pts == MP_NOPTS_VALUE)
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
2009-02-03 23:28:17 +01:00
|
|
|
else if (sh_video->last_pts > sh_video->pts) {
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "Decreasing video pts: %f < %f\n",
|
2009-01-14 23:32:03 +01: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;
|
|
|
|
}
|
|
|
|
double frame_time = sh_video->pts - sh_video->last_pts;
|
|
|
|
sh_video->last_pts = sh_video->pts;
|
|
|
|
sh_video->timer += frame_time;
|
|
|
|
if (mpctx->sh_audio)
|
|
|
|
mpctx->delay -= frame_time;
|
2006-11-20 10:12:07 +01:00
|
|
|
return frame_time;
|
|
|
|
}
|
|
|
|
|
2008-11-29 07:09:57 +01:00
|
|
|
void pause_player(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->paused)
|
|
|
|
return;
|
|
|
|
mpctx->paused = 1;
|
|
|
|
mpctx->step_frames = 0;
|
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
|
|
|
|
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
|
2008-11-29 07:09:57 +01:00
|
|
|
|
2011-04-09 02:03:22 +02:00
|
|
|
if (mpctx->ao && mpctx->sh_audio)
|
2011-08-07 02:58:12 +02:00
|
|
|
ao_pause(mpctx->ao); // pause audio, keep data if possible
|
2008-11-29 07:09:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void unpause_player(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->paused)
|
|
|
|
return;
|
|
|
|
mpctx->paused = 0;
|
|
|
|
|
2011-04-09 02:03:22 +02:00
|
|
|
if (mpctx->ao && mpctx->sh_audio)
|
|
|
|
ao_resume(mpctx->ao);
|
2008-12-01 18:53:57 +01:00
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok
|
|
|
|
&& !mpctx->step_frames)
|
2011-08-07 02:58:12 +02: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 07:09:57 +01:00
|
|
|
}
|
|
|
|
|
2011-12-05 04:24:18 +01:00
|
|
|
static int redraw_osd(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
|
|
|
struct vf_instance *vf = sh_video->vfilter;
|
|
|
|
if (sh_video->output_flags & VFCAP_OSD_FILTER)
|
|
|
|
return -1;
|
|
|
|
if (vo_redraw_frame(mpctx->video_out) < 0)
|
|
|
|
return -1;
|
|
|
|
mpctx->osd->pts = mpctx->video_pts;
|
|
|
|
if (!(sh_video->output_flags & VFCAP_EOSD_FILTER))
|
|
|
|
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
|
|
|
|
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
|
|
|
|
vo_flip_page(mpctx->video_out, 0, -1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-12-01 18:53:57 +01:00
|
|
|
void add_step_frame(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
mpctx->step_frames++;
|
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
|
2008-12-01 18:53:57 +01:00
|
|
|
unpause_player(mpctx);
|
|
|
|
}
|
|
|
|
|
2008-04-21 05:07:22 +02:00
|
|
|
static void pause_loop(struct MPContext *mpctx)
|
2006-11-20 12:37:23 +01:00
|
|
|
{
|
2010-11-12 21:04:16 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_cmd_t *cmd;
|
2011-01-26 22:48:15 +01:00
|
|
|
#ifdef CONFIG_STREAM_CACHE
|
|
|
|
int old_cache_fill = stream_cache_size > 0 ?
|
2011-08-07 02:58:12 +02:00
|
|
|
cache_fill_status(mpctx->stream) : 0;
|
2011-01-26 22:48:15 +01:00
|
|
|
#endif
|
2010-11-12 21:04:16 +01:00
|
|
|
if (!opts->quiet) {
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->term_osd && !mpctx->sh_video) {
|
|
|
|
set_osd_tmsg(OSD_MSG_PAUSE, 1, 0, " ===== PAUSE =====");
|
|
|
|
update_osd_msg(mpctx);
|
|
|
|
} else
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "\n%s\r",
|
2011-02-15 17:38:43 +01:00
|
|
|
mp_gtext(" ===== PAUSE ====="));
|
2006-11-20 12:37:23 +01:00
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_PAUSED\n");
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
while ((cmd = mp_input_get_cmd(mpctx->input, 20, 1)) == NULL
|
|
|
|
|| cmd->id == MP_CMD_SET_MOUSE_POS || cmd->pausing == 4) {
|
|
|
|
if (cmd) {
|
2011-02-15 17:38:43 +01:00
|
|
|
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
|
|
|
run_command(mpctx, cmd);
|
|
|
|
mp_cmd_free(cmd);
|
|
|
|
continue;
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
if (mpctx->sh_video && mpctx->video_out)
|
|
|
|
vo_check_events(mpctx->video_out);
|
2011-02-15 17:38:43 +01:00
|
|
|
update_osd_msg(mpctx);
|
|
|
|
int hack = vo_osd_changed(0);
|
|
|
|
vo_osd_changed(hack);
|
2011-12-05 05:36:20 +01:00
|
|
|
if (hack || mpctx->sh_video && mpctx->video_out->want_redraw)
|
2011-02-15 17:38:43 +01:00
|
|
|
break;
|
2011-01-26 22:48:15 +01:00
|
|
|
#ifdef CONFIG_STREAM_CACHE
|
|
|
|
if (!opts->quiet && stream_cache_size > 0) {
|
|
|
|
int new_cache_fill = cache_fill_status(mpctx->stream);
|
|
|
|
if (new_cache_fill != old_cache_fill) {
|
|
|
|
if (opts->term_osd && !mpctx->sh_video) {
|
|
|
|
set_osd_tmsg(OSD_MSG_PAUSE, 1, 0, "%s %d%%",
|
|
|
|
mp_gtext(" ===== PAUSE ====="),
|
|
|
|
new_cache_fill);
|
|
|
|
update_osd_msg(mpctx);
|
|
|
|
} else
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s %d%%\r",
|
|
|
|
mp_gtext(" ===== PAUSE ====="),
|
|
|
|
new_cache_fill);
|
|
|
|
old_cache_fill = new_cache_fill;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2006-11-20 12:37:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-22 22:05:31 +01:00
|
|
|
|
|
|
|
// Find the right mute status and record position for new file position
|
|
|
|
static void edl_seek_reset(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
mpctx->edl_muted = 0;
|
|
|
|
next_edl_record = edl_records;
|
|
|
|
|
|
|
|
while (next_edl_record) {
|
2011-08-07 02:58:12 +02:00
|
|
|
if (next_edl_record->start_sec >= get_current_time(mpctx))
|
|
|
|
break;
|
2007-02-22 22:05:31 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (next_edl_record->action == EDL_MUTE)
|
|
|
|
mpctx->edl_muted = !mpctx->edl_muted;
|
|
|
|
next_edl_record = next_edl_record->next;
|
2007-02-22 22:05:31 +01:00
|
|
|
}
|
|
|
|
if ((mpctx->user_muted | mpctx->edl_muted) != mpctx->mixer.muted)
|
2011-08-07 02:58:12 +02:00
|
|
|
mixer_mute(&mpctx->mixer);
|
2007-02-22 22:05:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Execute EDL command for the current position if one exists
|
|
|
|
static void edl_update(MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!next_edl_record)
|
2011-08-07 02:58:12 +02:00
|
|
|
return;
|
2007-02-22 22:05:31 +01:00
|
|
|
|
|
|
|
if (!mpctx->sh_video) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Cannot use EDL without video, disabling.\n");
|
|
|
|
free_edl(edl_records);
|
|
|
|
next_edl_record = NULL;
|
|
|
|
edl_records = NULL;
|
|
|
|
return;
|
2007-02-22 22:05:31 +01:00
|
|
|
}
|
|
|
|
|
2010-12-14 21:31:39 +01:00
|
|
|
if (get_current_time(mpctx) >= next_edl_record->start_sec) {
|
2011-08-07 02:58:12 +02:00
|
|
|
if (next_edl_record->action == EDL_SKIP) {
|
|
|
|
mpctx->osd_function = OSD_FFW;
|
2010-12-18 09:13:45 +01:00
|
|
|
queue_seek(mpctx, MPSEEK_RELATIVE, next_edl_record->length_sec, 0);
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_SKIP: start [%f], stop "
|
|
|
|
"[%f], length [%f]\n", next_edl_record->start_sec,
|
|
|
|
next_edl_record->stop_sec, next_edl_record->length_sec);
|
|
|
|
} else if (next_edl_record->action == EDL_MUTE) {
|
|
|
|
mpctx->edl_muted = !mpctx->edl_muted;
|
|
|
|
if ((mpctx->user_muted | mpctx->edl_muted) != mpctx->mixer.muted)
|
|
|
|
mixer_mute(&mpctx->mixer);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_MUTE: [%f]\n",
|
|
|
|
next_edl_record->start_sec);
|
|
|
|
}
|
|
|
|
next_edl_record = next_edl_record->next;
|
2007-02-22 22:05:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-29 21:45:06 +02:00
|
|
|
static void reinit_decoders(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
reinit_video_chain(mpctx);
|
|
|
|
reinit_audio_chain(mpctx);
|
2011-01-16 20:16:47 +01:00
|
|
|
mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos}, mpctx);
|
2009-03-29 21:45:06 +02:00
|
|
|
}
|
|
|
|
|
2011-04-08 04:15:43 +02:00
|
|
|
static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
2010-04-24 17:50:19 +02:00
|
|
|
{
|
|
|
|
if (mpctx->sh_video) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "seek_video_reset";
|
|
|
|
resync_video_stream(mpctx->sh_video);
|
2010-04-24 17:50:19 +02:00
|
|
|
mpctx->sh_video->timer = 0;
|
|
|
|
vo_seek_reset(mpctx->video_out);
|
|
|
|
mpctx->sh_video->timer = 0;
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->sh_video->num_buffered_pts = 0;
|
|
|
|
mpctx->sh_video->last_pts = MP_NOPTS_VALUE;
|
|
|
|
mpctx->delay = 0;
|
2010-04-24 17:50:19 +02:00
|
|
|
mpctx->time_frame = 0;
|
2010-11-11 11:29:14 +01:00
|
|
|
mpctx->restart_playback = true;
|
2011-08-07 02:58:12 +02:00
|
|
|
// Not all demuxers set d_video->pts during seek, so this value
|
|
|
|
// (which is used by at least vobsub and edl code below) may
|
|
|
|
// be completely wrong (probably 0).
|
|
|
|
mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset;
|
2010-12-14 21:31:39 +01:00
|
|
|
mpctx->video_pts = mpctx->sh_video->pts;
|
2011-08-07 02:58:12 +02:00
|
|
|
update_subtitles(mpctx, mpctx->sh_video->pts, mpctx->video_offset,
|
2011-01-12 00:37:02 +01:00
|
|
|
true);
|
2011-08-07 02:58:12 +02:00
|
|
|
update_teletext(mpctx->sh_video, mpctx->demuxer, 1);
|
2010-04-24 17:50:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mpctx->sh_audio) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "seek_audio_reset";
|
2010-04-24 17:50:19 +02:00
|
|
|
resync_audio_stream(mpctx->sh_audio);
|
2011-04-08 04:15:43 +02:00
|
|
|
if (reset_ao)
|
2011-04-09 02:03:22 +02:00
|
|
|
ao_reset(mpctx->ao);
|
2011-07-02 08:22:32 +02:00
|
|
|
mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size;
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->sh_audio->a_buffer_len = 0;
|
|
|
|
if (!mpctx->sh_video)
|
|
|
|
update_subtitles(mpctx, mpctx->sh_audio->pts,
|
2011-01-12 00:37:02 +01:00
|
|
|
mpctx->video_offset, true);
|
2010-04-24 17:50:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vo_vobsub && mpctx->sh_video) {
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "seek_vobsub_reset";
|
|
|
|
vobsub_seek(vo_vobsub, mpctx->sh_video->pts);
|
2010-04-24 17:50:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
edl_seek_reset(mpctx);
|
|
|
|
|
2010-12-15 00:09:47 +01:00
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
mpctx->hrseek_framedrop = false;
|
2010-04-24 17:50:19 +02:00
|
|
|
mpctx->total_avsync_change = 0;
|
2011-08-07 02:58:12 +02:00
|
|
|
audio_time_usage = 0;
|
|
|
|
video_time_usage = 0;
|
|
|
|
vout_time_usage = 0;
|
2010-04-24 17:50:19 +02:00
|
|
|
drop_frame_cnt = 0;
|
|
|
|
|
|
|
|
current_module = NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-29 21:45:06 +02:00
|
|
|
static bool timeline_set_part(struct MPContext *mpctx, int i)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
if (n->source == p->source)
|
|
|
|
return false;
|
2011-04-08 04:15:43 +02: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
|
|
|
|
uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts.fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts.gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB);
|
|
|
|
mpctx->stop_play = orig_stop_play;
|
2009-03-29 21:45:06 +02:00
|
|
|
mpctx->demuxer = n->source->demuxer;
|
|
|
|
mpctx->d_video = mpctx->demuxer->video;
|
|
|
|
mpctx->d_audio = mpctx->demuxer->audio;
|
|
|
|
mpctx->d_sub = mpctx->demuxer->sub;
|
|
|
|
mpctx->sh_video = mpctx->d_video->sh;
|
|
|
|
mpctx->sh_audio = mpctx->d_audio->sh;
|
|
|
|
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 02:58:12 +02:00
|
|
|
if (pts < (p + 1)->start) {
|
2009-03-29 21:45:06 +02:00
|
|
|
*need_reset = timeline_set_part(mpctx, i);
|
|
|
|
return pts - p->start + p->source_start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-02-22 22:05:31 +01:00
|
|
|
|
2007-02-23 02:49:51 +01:00
|
|
|
// return -1 if seek failed (non-seekable stream?), 0 otherwise
|
2011-04-08 04:15:43 +02:00
|
|
|
static int seek(MPContext *mpctx, struct seek_params seek,
|
|
|
|
bool timeline_fallthrough)
|
2007-02-23 02:49:51 +01:00
|
|
|
{
|
2010-12-15 00:09:47 +01:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
|
2007-02-23 02:49:51 +01:00
|
|
|
current_module = "seek";
|
2009-03-29 21:45:06 +02:00
|
|
|
if (mpctx->stop_play == AT_END_OF_FILE)
|
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
2010-12-15 00:09:47 +01:00
|
|
|
bool hr_seek = mpctx->demuxer->accurate_seek && opts->correct_pts;
|
|
|
|
hr_seek &= seek.exact >= 0 && seek.type != MPSEEK_FACTOR;
|
|
|
|
hr_seek &= opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE
|
2011-08-07 02:58:12 +02:00
|
|
|
|| opts->hr_seek > 0 || seek.exact > 0;
|
2010-12-18 09:13:45 +01:00
|
|
|
if (seek.type == MPSEEK_FACTOR
|
|
|
|
|| seek.type == MPSEEK_ABSOLUTE
|
|
|
|
&& seek.amount < mpctx->last_chapter_pts
|
|
|
|
|| seek.amount < 0)
|
2011-08-04 22:14:44 +02:00
|
|
|
mpctx->last_chapter_seek = -2;
|
2010-12-18 09:13:45 +01:00
|
|
|
if (mpctx->timeline && seek.type == MPSEEK_FACTOR) {
|
|
|
|
seek.amount *= mpctx->timeline[mpctx->num_timeline_parts].start;
|
|
|
|
seek.type = MPSEEK_ABSOLUTE;
|
2009-03-29 21:45:06 +02:00
|
|
|
}
|
2010-12-14 21:33:55 +01:00
|
|
|
if ((mpctx->demuxer->accurate_seek || mpctx->timeline)
|
2010-12-18 09:13:45 +01: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 04:25:12 +01:00
|
|
|
}
|
|
|
|
|
2009-03-29 21:45:06 +02: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 09:13:45 +01:00
|
|
|
double demuxer_amount = seek.amount;
|
2009-03-29 21:45:06 +02:00
|
|
|
if (mpctx->timeline) {
|
2010-12-18 09:13:45 +01:00
|
|
|
demuxer_amount = timeline_set_from_time(mpctx, seek.amount,
|
|
|
|
&need_reset);
|
2010-12-15 00:02:14 +01:00
|
|
|
if (demuxer_amount == -1) {
|
2009-03-29 21:45:06 +02:00
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
|
|
|
// Clear audio from current position
|
|
|
|
if (mpctx->sh_audio) {
|
2011-04-09 02:03:22 +02:00
|
|
|
ao_reset(mpctx->ao);
|
2009-03-29 21:45:06 +02:00
|
|
|
mpctx->sh_audio->a_buffer_len = 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2010-12-18 09:13:45 +01:00
|
|
|
int demuxer_style = 0;
|
|
|
|
switch (seek.type) {
|
|
|
|
case MPSEEK_FACTOR:
|
|
|
|
demuxer_style |= SEEK_FACTOR; // fallthrough
|
|
|
|
case MPSEEK_ABSOLUTE:
|
|
|
|
demuxer_style |= SEEK_ABSOLUTE;
|
|
|
|
}
|
2010-12-15 00:09:47 +01:00
|
|
|
if (hr_seek || seek.direction < 0)
|
2010-12-18 09:13:45 +01:00
|
|
|
demuxer_style |= SEEK_BACKWARD;
|
|
|
|
else if (seek.direction > 0)
|
|
|
|
demuxer_style |= SEEK_FORWARD;
|
|
|
|
|
2011-11-06 15:54:57 +01:00
|
|
|
if (hr_seek)
|
|
|
|
demuxer_amount -= opts->hr_seek_demuxer_offset;
|
2010-12-15 00:02:14 +01:00
|
|
|
int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, audio_delay,
|
2010-12-18 09:13:45 +01:00
|
|
|
demuxer_style);
|
2009-03-29 21:45:06 +02:00
|
|
|
if (need_reset)
|
|
|
|
reinit_decoders(mpctx);
|
|
|
|
if (seekresult == 0)
|
2011-08-07 02:58:12 +02:00
|
|
|
return -1;
|
2007-02-23 02:49:51 +01:00
|
|
|
|
2011-04-08 04:15:43 +02:00
|
|
|
seek_reset(mpctx, !timeline_fallthrough);
|
2010-12-15 00:02:14 +01:00
|
|
|
|
|
|
|
/* Use the target time as "current position" for further relative
|
|
|
|
* seeks etc until a new video frame has been decoded */
|
2011-07-31 00:05:17 +02:00
|
|
|
if (seek.type == MPSEEK_ABSOLUTE) {
|
2010-12-18 09:13:45 +01:00
|
|
|
mpctx->video_pts = seek.amount;
|
2011-07-31 00:05:17 +02:00
|
|
|
mpctx->last_seek_pts = seek.amount;
|
|
|
|
} else
|
|
|
|
mpctx->last_seek_pts = MP_NOPTS_VALUE;
|
2010-12-15 00:02:14 +01:00
|
|
|
|
2010-12-15 00:09:47 +01:00
|
|
|
if (hr_seek) {
|
|
|
|
mpctx->hrseek_active = true;
|
|
|
|
mpctx->hrseek_framedrop = true;
|
|
|
|
mpctx->hrseek_pts = seek.amount;
|
|
|
|
}
|
|
|
|
|
2010-12-15 00:02:14 +01:00
|
|
|
mpctx->start_timestamp = GetTimerMS();
|
|
|
|
|
2007-02-23 02:49:51 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-18 09:13:45 +01: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 02:58:12 +02:00
|
|
|
return; // Well... not common enough to bother doing better
|
2010-12-18 09:13:45 +01: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 02:58:12 +02:00
|
|
|
*seek = (struct seek_params){ 0 };
|
2010-12-18 09:13:45 +01: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 02:58:12 +02:00
|
|
|
*seek = (struct seek_params){ 0 };
|
2010-12-18 09:13:45 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2010-11-07 23:54:32 +01:00
|
|
|
|
|
|
|
double get_time_length(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->timeline)
|
|
|
|
return mpctx->timeline[mpctx->num_timeline_parts].start;
|
|
|
|
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
double get_time_ans;
|
|
|
|
// <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW
|
|
|
|
if (demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH,
|
|
|
|
(void *) &get_time_ans) > 0)
|
|
|
|
return get_time_ans;
|
|
|
|
|
|
|
|
struct sh_video *sh_video = mpctx->d_video->sh;
|
|
|
|
struct sh_audio *sh_audio = mpctx->d_audio->sh;
|
|
|
|
if (sh_video && sh_video->i_bps && sh_audio && sh_audio->i_bps)
|
|
|
|
return (double) (demuxer->movi_end - demuxer->movi_start) /
|
2011-08-07 02:58:12 +02:00
|
|
|
(sh_video->i_bps + sh_audio->i_bps);
|
2010-11-07 23:54:32 +01:00
|
|
|
if (sh_video && sh_video->i_bps)
|
|
|
|
return (double) (demuxer->movi_end - demuxer->movi_start) /
|
2011-08-07 02:58:12 +02:00
|
|
|
sh_video->i_bps;
|
2010-11-07 23:54:32 +01:00
|
|
|
if (sh_audio && sh_audio->i_bps)
|
|
|
|
return (double) (demuxer->movi_end - demuxer->movi_start) /
|
2011-08-07 02:58:12 +02:00
|
|
|
sh_audio->i_bps;
|
2010-11-07 23:54:32 +01: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;
|
|
|
|
if (demuxer->stream_pts != MP_NOPTS_VALUE)
|
|
|
|
return demuxer->stream_pts;
|
|
|
|
struct sh_video *sh_video = demuxer->video->sh;
|
|
|
|
if (sh_video)
|
2010-12-14 21:31:39 +01:00
|
|
|
return mpctx->video_pts;
|
2011-07-31 00:05:17 +02:00
|
|
|
double apts = playing_audio_pts(mpctx);
|
|
|
|
if (apts != MP_NOPTS_VALUE)
|
|
|
|
return apts;
|
|
|
|
return mpctx->last_seek_pts;
|
2010-11-07 23:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int get_percent_pos(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
int ans = 0;
|
|
|
|
if (mpctx->timeline)
|
|
|
|
ans = get_current_time(mpctx) * 100 /
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->timeline[mpctx->num_timeline_parts].start;
|
2010-11-07 23:54:32 +01:00
|
|
|
else if (demux_control(demuxer, DEMUXER_CTRL_GET_PERCENT_POS, &ans) > 0)
|
|
|
|
;
|
|
|
|
else {
|
|
|
|
int len = (demuxer->movi_end - demuxer->movi_start) / 100;
|
|
|
|
off_t pos = demuxer->filepos > 0 ?
|
2011-08-07 02:58:12 +02:00
|
|
|
demuxer->filepos : stream_tell(demuxer->stream);
|
2010-11-07 23:54:32 +01:00
|
|
|
if (len > 0)
|
|
|
|
ans = (pos - demuxer->movi_start) / len;
|
|
|
|
else
|
|
|
|
ans = 0;
|
|
|
|
}
|
|
|
|
if (ans < 0)
|
|
|
|
ans = 0;
|
|
|
|
if (ans > 100)
|
|
|
|
ans = 100;
|
|
|
|
return ans;
|
|
|
|
}
|
|
|
|
|
2010-04-24 19:46:54 +02:00
|
|
|
// -2 is no chapters, -1 is before first chapter
|
2009-04-02 04:00:22 +02:00
|
|
|
int get_current_chapter(struct MPContext *mpctx)
|
|
|
|
{
|
2010-11-13 14:48:26 +01:00
|
|
|
double current_pts = get_current_time(mpctx);
|
|
|
|
if (!mpctx->chapters)
|
2010-04-25 22:20:34 +02:00
|
|
|
return FFMAX(mpctx->last_chapter_seek,
|
2010-11-13 14:48:26 +01:00
|
|
|
demuxer_get_current_chapter(mpctx->demuxer, current_pts));
|
2009-04-02 04:00:22 +02:00
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < mpctx->num_chapters; i++)
|
|
|
|
if (current_pts < mpctx->chapters[i].start)
|
|
|
|
break;
|
2010-04-25 22:20:34 +02:00
|
|
|
return FFMAX(mpctx->last_chapter_seek, i - 1);
|
2009-04-02 04:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
char *chapter_display_name(struct MPContext *mpctx, int chapter)
|
2011-10-23 04:51:44 +02:00
|
|
|
{
|
|
|
|
char *name = chapter_name(mpctx, chapter);
|
|
|
|
if (name) {
|
|
|
|
name = talloc_asprintf(name, "(%d) %s", chapter + 1, name);
|
|
|
|
} else {
|
|
|
|
int chapter_count = get_chapter_count(mpctx);
|
|
|
|
if (chapter_count <= 0)
|
|
|
|
name = talloc_asprintf(NULL, "(%d)", chapter + 1);
|
|
|
|
else
|
|
|
|
name = talloc_asprintf(NULL, "(%d) of %d", chapter + 1,
|
|
|
|
chapter_count);
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns NULL if chapter name unavailable
|
|
|
|
char *chapter_name(struct MPContext *mpctx, int chapter)
|
2009-04-02 04:00:22 +02:00
|
|
|
{
|
2011-08-08 06:42:28 +02:00
|
|
|
if (!mpctx->chapters)
|
2011-10-23 04:51:44 +02:00
|
|
|
return demuxer_chapter_name(mpctx->demuxer, chapter);
|
2011-07-03 19:04:21 +02:00
|
|
|
return talloc_strdup(NULL, mpctx->chapters[chapter].name);
|
2009-04-02 04:00:22 +02:00
|
|
|
}
|
|
|
|
|
2011-10-23 04:51:44 +02:00
|
|
|
// returns the start of the chapter in seconds
|
|
|
|
double chapter_start_time(struct MPContext *mpctx, int chapter)
|
|
|
|
{
|
|
|
|
if (!mpctx->chapters)
|
|
|
|
return demuxer_chapter_time(mpctx->demuxer, chapter, NULL);
|
|
|
|
return mpctx->chapters[chapter].start;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_chapter_count(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->chapters)
|
|
|
|
return demuxer_chapter_count(mpctx->demuxer);
|
|
|
|
return mpctx->num_chapters;
|
|
|
|
}
|
|
|
|
|
|
|
|
int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts)
|
2009-04-02 04:00:22 +02:00
|
|
|
{
|
2011-08-04 22:14:44 +02:00
|
|
|
mpctx->last_chapter_seek = -2;
|
2011-08-08 06:42:28 +02:00
|
|
|
if (!mpctx->chapters) {
|
2011-10-23 04:51:44 +02:00
|
|
|
int res = demuxer_seek_chapter(mpctx->demuxer, chapter, seek_pts);
|
2010-04-25 22:20:34 +02:00
|
|
|
if (res >= 0) {
|
|
|
|
if (*seek_pts == -1)
|
2011-04-08 04:15:43 +02:00
|
|
|
seek_reset(mpctx, true);
|
2010-04-25 22:20:34 +02:00
|
|
|
else {
|
|
|
|
mpctx->last_chapter_seek = res;
|
|
|
|
mpctx->last_chapter_pts = *seek_pts;
|
|
|
|
}
|
|
|
|
}
|
2010-04-23 19:14:59 +02:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-04-02 04:00:22 +02:00
|
|
|
if (chapter >= mpctx->num_chapters)
|
|
|
|
return -1;
|
|
|
|
if (chapter < 0)
|
|
|
|
chapter = 0;
|
|
|
|
*seek_pts = mpctx->chapters[chapter].start;
|
2010-04-25 22:20:34 +02:00
|
|
|
mpctx->last_chapter_seek = chapter;
|
|
|
|
mpctx->last_chapter_pts = *seek_pts;
|
2009-04-02 04:00:22 +02:00
|
|
|
return chapter;
|
|
|
|
}
|
|
|
|
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
static void run_playloop(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
float aq_sleep_time = 0;
|
2011-04-07 20:17:51 +02:00
|
|
|
bool full_audio_buffers = false;
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
if (opts->chapterrange[1] > 0) {
|
|
|
|
int cur_chapter = get_current_chapter(mpctx);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (cur_chapter != -1 && cur_chapter + 1 > opts->chapterrange[1])
|
2010-12-13 20:12:49 +01:00
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mpctx->sh_audio && mpctx->d_audio->sh) {
|
|
|
|
mpctx->sh_audio = mpctx->d_audio->sh;
|
|
|
|
mpctx->sh_audio->ds = mpctx->d_audio;
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
}
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
/*========================== PLAY AUDIO ============================*/
|
2010-12-13 20:12:49 +01:00
|
|
|
|
2011-07-31 00:05:17 +02:00
|
|
|
if (!mpctx->sh_video)
|
|
|
|
mpctx->restart_playback = false;
|
|
|
|
|
|
|
|
if (mpctx->sh_audio && !mpctx->restart_playback) {
|
2011-04-07 20:17:51 +02:00
|
|
|
int status = fill_audio_out_buffers(mpctx);
|
2011-05-05 20:34:17 +02:00
|
|
|
full_audio_buffers = status >= 0 && !mpctx->ao->untimed;
|
2011-04-07 20:17:51 +02:00
|
|
|
if (status == -2)
|
2010-12-13 20:12:49 +01:00
|
|
|
// at eof, all audio at least written to ao
|
|
|
|
if (!mpctx->sh_video)
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
2011-04-07 20:17:51 +02:00
|
|
|
}
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
if (!mpctx->sh_video) {
|
2010-12-20 18:38:54 +01:00
|
|
|
if (mpctx->step_frames) {
|
|
|
|
mpctx->step_frames = 0;
|
|
|
|
pause_player(mpctx);
|
|
|
|
}
|
2010-12-13 20:12:49 +01:00
|
|
|
// handle audio-only case:
|
2011-04-22 06:07:54 +02:00
|
|
|
double a_pos = 0, a_buf = 0;
|
2010-12-13 20:12:49 +01:00
|
|
|
// sh_audio can be NULL due to video stream switching
|
|
|
|
// TODO: handle this better
|
2011-04-22 06:07:54 +02:00
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
a_buf = ao_get_delay(mpctx->ao);
|
|
|
|
a_pos = written_audio_pts(mpctx) - mpctx->opts.playback_speed *
|
2011-08-07 02:58:12 +02:00
|
|
|
a_buf;
|
2011-04-22 06:07:54 +02:00
|
|
|
}
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
print_status(mpctx, a_pos, false);
|
|
|
|
|
2011-01-12 00:37:02 +01:00
|
|
|
update_subtitles(mpctx, a_pos, mpctx->video_offset, false);
|
2010-12-13 20:12:49 +01:00
|
|
|
update_osd_msg(mpctx);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (end_at.type == END_AT_TIME && end_at.pos < a_pos)
|
2011-04-07 22:05:16 +02:00
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
2011-08-07 02:58:12 +02:00
|
|
|
else if (mpctx->timeline && mpctx->stop_play == AT_END_OF_FILE
|
|
|
|
&& mpctx->timeline_part + 1 < mpctx->num_timeline_parts
|
|
|
|
&& mpctx->sh_audio) {
|
2011-04-08 04:15:43 +02:00
|
|
|
struct timeline_part *p = mpctx->timeline + mpctx->timeline_part;
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!opts->gapless_audio && p->source != (p + 1)->source
|
2011-04-22 06:07:54 +02:00
|
|
|
&& a_buf > 0.05) {
|
2011-04-08 04:15:43 +02:00
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_input_get_cmd(mpctx->input, (a_buf - .05) * 1000, true);
|
2011-04-08 04:15:43 +02:00
|
|
|
} else {
|
|
|
|
seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE,
|
2011-08-07 02:58:12 +02:00
|
|
|
.amount = (p + 1)->start },
|
2011-04-08 04:15:43 +02:00
|
|
|
true);
|
|
|
|
}
|
2011-04-07 22:05:16 +02:00
|
|
|
} else if (!mpctx->stop_play) {
|
2011-04-22 06:07:54 +02:00
|
|
|
int sleep_time = 100;
|
|
|
|
if (mpctx->sh_audio) {
|
2011-05-05 20:34:17 +02:00
|
|
|
if (mpctx->ao->untimed)
|
|
|
|
sleep_time = 0;
|
|
|
|
else if (full_audio_buffers)
|
2011-04-22 06:07:54 +02:00
|
|
|
sleep_time = FFMAX(20, a_buf * 1000 - 50);
|
|
|
|
else
|
|
|
|
sleep_time = 20;
|
|
|
|
sleep_time = FFMIN(sleep_time, 100);
|
|
|
|
}
|
2011-04-07 22:05:16 +02:00
|
|
|
mp_input_get_cmd(mpctx->input, sleep_time, true);
|
|
|
|
}
|
2010-12-13 20:12:49 +01:00
|
|
|
} else {
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
/*========================== PLAY VIDEO ============================*/
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
vo_pts = mpctx->sh_video->timer * 90000.0;
|
|
|
|
vo_fps = mpctx->sh_video->fps;
|
|
|
|
|
|
|
|
bool blit_frame = mpctx->video_out->frame_loaded;
|
2011-12-06 07:27:49 +01:00
|
|
|
if (!blit_frame) {
|
2010-12-13 20:12:49 +01:00
|
|
|
double frame_time = update_video(mpctx);
|
|
|
|
blit_frame = mpctx->video_out->frame_loaded;
|
|
|
|
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");
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (frame_time < 0)
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
|
|
|
else if (!mpctx->restart_playback) {
|
|
|
|
mpctx->time_frame += frame_time / opts->playback_speed;
|
|
|
|
adjust_sync(mpctx, frame_time);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mpctx->timeline) {
|
|
|
|
struct timeline_part *next =
|
|
|
|
mpctx->timeline + mpctx->timeline_part + 1;
|
|
|
|
if (mpctx->sh_video->pts >= next->start
|
|
|
|
|| mpctx->stop_play == AT_END_OF_FILE
|
|
|
|
&& mpctx->timeline_part + 1 < mpctx->num_timeline_parts) {
|
2010-12-18 09:13:45 +01:00
|
|
|
seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE,
|
2011-04-08 04:15:43 +02:00
|
|
|
.amount = next->start },
|
|
|
|
true);
|
2010-12-13 20:12:49 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
// ================================================================
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
current_module = "vo_check_events";
|
|
|
|
vo_check_events(mpctx->video_out);
|
|
|
|
|
|
|
|
#ifdef CONFIG_X11
|
|
|
|
if (stop_xscreensaver) {
|
|
|
|
current_module = "stop_xscreensaver";
|
|
|
|
xscreensaver_heartbeat(mpctx->x11_state);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (heartbeat_cmd) {
|
|
|
|
static unsigned last_heartbeat;
|
|
|
|
unsigned now = GetTimerMS();
|
|
|
|
if (now - last_heartbeat > 30000) {
|
|
|
|
last_heartbeat = now;
|
|
|
|
system(heartbeat_cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 19:20:30 +01:00
|
|
|
bool frame_time_remaining = sleep_until_near_frame(mpctx,
|
|
|
|
&mpctx->time_frame,
|
2011-04-07 20:17:51 +02:00
|
|
|
full_audio_buffers,
|
2010-12-14 19:20:30 +01:00
|
|
|
&aq_sleep_time);
|
2010-12-13 20:12:49 +01:00
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
//=================== FLIP PAGE (VIDEO BLT): ======================
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
current_module = "flip_page";
|
|
|
|
if (!frame_time_remaining && blit_frame) {
|
2011-12-04 17:10:17 +01:00
|
|
|
vo_new_frame_imminent(mpctx->video_out);
|
2010-12-14 19:20:30 +01:00
|
|
|
struct sh_video *sh_video = mpctx->sh_video;
|
2010-12-14 21:31:39 +01:00
|
|
|
mpctx->video_pts = sh_video->pts;
|
2011-01-12 00:37:02 +01:00
|
|
|
update_subtitles(mpctx, sh_video->pts, mpctx->video_offset, false);
|
2010-12-14 19:20:30 +01:00
|
|
|
update_teletext(sh_video, mpctx->demuxer, 0);
|
|
|
|
update_osd_msg(mpctx);
|
|
|
|
struct vf_instance *vf = sh_video->vfilter;
|
2011-12-05 04:24:18 +01:00
|
|
|
mpctx->osd->pts = mpctx->video_pts;
|
2011-01-12 14:15:02 +01:00
|
|
|
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
|
2010-12-14 19:20:30 +01:00
|
|
|
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
|
|
|
|
vo_osd_changed(0);
|
|
|
|
|
|
|
|
mpctx->time_frame -= mpctx->video_out->flip_queue_offset;
|
|
|
|
aq_sleep_time += mpctx->time_frame;
|
|
|
|
// flag 256 means: libvo driver does its timing (dvb card)
|
|
|
|
if (mpctx->time_frame > 0.001
|
2011-01-17 15:31:45 +01:00
|
|
|
&& !(mpctx->sh_video->output_flags & VFCAP_TIMER))
|
2010-12-14 19:20:30 +01:00
|
|
|
mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame);
|
|
|
|
mpctx->time_frame += mpctx->video_out->flip_queue_offset;
|
|
|
|
|
2010-12-13 20:12:49 +01:00
|
|
|
unsigned int t2 = GetTimer();
|
2011-01-28 19:15:22 +01: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);
|
|
|
|
unsigned int pts_us = mpctx->last_time + time_frame * 1e6;
|
2010-12-13 20:12:49 +01:00
|
|
|
int duration = -1;
|
|
|
|
double pts2 = mpctx->video_out->next_pts2;
|
|
|
|
if (pts2 != MP_NOPTS_VALUE && opts->correct_pts
|
|
|
|
&& !mpctx->restart_playback) {
|
|
|
|
// expected A/V sync correction is ignored
|
2010-12-14 21:31:39 +01:00
|
|
|
double diff = (pts2 - mpctx->video_pts);
|
2010-12-13 20:12:49 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
vo_flip_page(mpctx->video_out, pts_us | 1, duration);
|
|
|
|
|
|
|
|
mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001;
|
|
|
|
vout_time_usage += mpctx->last_vo_flip_duration;
|
|
|
|
if (mpctx->video_out->driver->flip_page_timed) {
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
if (mpctx->restart_playback) {
|
|
|
|
mpctx->syncing_audio = true;
|
2011-07-31 00:05:17 +02:00
|
|
|
if (mpctx->sh_audio)
|
2010-12-13 20:12:49 +01:00
|
|
|
fill_audio_out_buffers(mpctx);
|
|
|
|
mpctx->restart_playback = false;
|
|
|
|
mpctx->time_frame = 0;
|
|
|
|
get_relative_time(mpctx);
|
|
|
|
}
|
|
|
|
print_status(mpctx, MP_NOPTS_VALUE, true);
|
2011-10-06 20:46:01 +02:00
|
|
|
screenshot_flip(mpctx);
|
2010-12-13 20:12:49 +01:00
|
|
|
} else
|
|
|
|
print_status(mpctx, MP_NOPTS_VALUE, false);
|
|
|
|
|
|
|
|
if (opts->auto_quality > 0) {
|
|
|
|
current_module = "autoq";
|
|
|
|
if (output_quality < opts->auto_quality && aq_sleep_time > 0)
|
|
|
|
++output_quality;
|
2011-08-07 02:58:12 +02:00
|
|
|
else if (output_quality > 1 && aq_sleep_time < 0)
|
2010-12-13 20:12:49 +01:00
|
|
|
--output_quality;
|
2011-08-07 02:58:12 +02:00
|
|
|
else if (output_quality > 0 && aq_sleep_time < -0.050f) // 50ms
|
2010-12-13 20:12:49 +01:00
|
|
|
output_quality = 0;
|
|
|
|
set_video_quality(mpctx->sh_video, output_quality);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!frame_time_remaining && blit_frame) {
|
|
|
|
if (play_n_frames >= 0) {
|
|
|
|
--play_n_frames;
|
|
|
|
if (play_n_frames <= 0)
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
}
|
|
|
|
if (mpctx->step_frames > 0) {
|
|
|
|
mpctx->step_frames--;
|
|
|
|
if (mpctx->step_frames == 0)
|
|
|
|
pause_player(mpctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
// FIXME: add size based support for -endpos
|
2010-12-13 20:12:49 +01:00
|
|
|
if (end_at.type == END_AT_TIME &&
|
|
|
|
!frame_time_remaining && end_at.pos <= mpctx->sh_video->pts)
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
|
|
|
|
} // end if(mpctx->sh_video)
|
|
|
|
|
|
|
|
#ifdef CONFIG_DVDNAV
|
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
|
|
|
nav_highlight_t hl;
|
|
|
|
mp_dvdnav_get_highlight(mpctx->stream, &hl);
|
|
|
|
if (!vo_spudec || !spudec_apply_palette_crop(vo_spudec, hl.palette, hl.sx, hl.sy, hl.ex, hl.ey)) {
|
|
|
|
osd_set_nav_box(hl.sx, hl.sy, hl.ex, hl.ey);
|
|
|
|
vo_osd_changed(OSDTYPE_DVDNAV);
|
|
|
|
} else {
|
|
|
|
osd_set_nav_box(0, 0, 0, 0);
|
|
|
|
vo_osd_changed(OSDTYPE_DVDNAV);
|
|
|
|
vo_osd_changed(OSDTYPE_SPU);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp_dvdnav_stream_has_changed(mpctx->stream)) {
|
|
|
|
double ar = -1.0;
|
|
|
|
if (mpctx->sh_video &&
|
|
|
|
stream_control(mpctx->demuxer->stream,
|
|
|
|
STREAM_CTRL_GET_ASPECT_RATIO, &ar)
|
|
|
|
!= STREAM_UNSUPPORTED)
|
|
|
|
mpctx->sh_video->stream_aspect = ar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
//================= Keyboard events, SEEKing ====================
|
2010-12-13 20:12:49 +01:00
|
|
|
|
|
|
|
current_module = "key_events";
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
mp_cmd_t *cmd;
|
2010-12-15 00:02:14 +01:00
|
|
|
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). */
|
2010-12-18 09:13:45 +01:00
|
|
|
if (mpctx->seek.type && cmd->id != MP_CMD_SEEK
|
2010-12-15 00:02:14 +01:00
|
|
|
|| mpctx->restart_playback && cmd->id == MP_CMD_SEEK
|
|
|
|
&& GetTimerMS() - mpctx->start_timestamp < 300)
|
|
|
|
break;
|
|
|
|
cmd = mp_input_get_cmd(mpctx->input, 0, 0);
|
2010-12-13 20:12:49 +01:00
|
|
|
run_command(mpctx, cmd);
|
|
|
|
mp_cmd_free(cmd);
|
|
|
|
if (mpctx->stop_play)
|
|
|
|
break;
|
|
|
|
}
|
2011-12-05 05:36:20 +01:00
|
|
|
bool slow_video = mpctx->sh_video && mpctx->video_out->frame_loaded;
|
|
|
|
if (!(mpctx->paused || slow_video) || mpctx->stop_play
|
|
|
|
|| mpctx->seek.type || mpctx->restart_playback)
|
2010-12-13 20:12:49 +01:00
|
|
|
break;
|
|
|
|
if (mpctx->sh_video) {
|
|
|
|
update_osd_msg(mpctx);
|
|
|
|
int hack = vo_osd_changed(0);
|
|
|
|
vo_osd_changed(hack);
|
2011-12-05 05:36:20 +01:00
|
|
|
if (hack || mpctx->video_out->want_redraw) {
|
2011-12-05 04:24:18 +01:00
|
|
|
if (redraw_osd(mpctx) < 0) {
|
2011-12-05 05:36:20 +01:00
|
|
|
if (mpctx->paused)
|
|
|
|
add_step_frame(mpctx);
|
2010-12-13 20:12:49 +01:00
|
|
|
break;
|
|
|
|
} else
|
|
|
|
vo_osd_changed(0);
|
|
|
|
}
|
|
|
|
}
|
2011-12-05 05:36:20 +01:00
|
|
|
if (!mpctx->paused)
|
|
|
|
break;
|
2010-12-13 20:12:49 +01:00
|
|
|
pause_loop(mpctx);
|
|
|
|
}
|
|
|
|
|
2011-09-04 21:08:26 +02:00
|
|
|
// handle -sstep
|
2011-09-04 21:12:51 +02:00
|
|
|
if (step_sec > 0 && !mpctx->paused && !mpctx->restart_playback) {
|
2010-12-13 20:12:49 +01:00
|
|
|
mpctx->osd_function = OSD_FFW;
|
2010-12-18 09:13:45 +01:00
|
|
|
queue_seek(mpctx, MPSEEK_RELATIVE, step_sec, 0);
|
2010-12-13 20:12:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
edl_update(mpctx);
|
|
|
|
|
|
|
|
/* Looping. */
|
2011-07-28 10:39:43 +02:00
|
|
|
if (opts->loop_times >= 0 && (mpctx->stop_play == AT_END_OF_FILE ||
|
|
|
|
mpctx->stop_play == PT_NEXT_ENTRY)) {
|
2010-12-13 20:12:49 +01:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "loop_times = %d\n", opts->loop_times);
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->loop_times > 1)
|
2010-12-13 20:12:49 +01:00
|
|
|
opts->loop_times--;
|
2011-08-07 02:58:12 +02:00
|
|
|
else if (opts->loop_times == 1)
|
2010-12-13 20:12:49 +01:00
|
|
|
opts->loop_times = -1;
|
|
|
|
play_n_frames = play_n_frames_mf;
|
|
|
|
mpctx->stop_play = 0;
|
2011-07-28 10:39:43 +02:00
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->seek_to_sec, 0);
|
2010-12-13 20:12:49 +01:00
|
|
|
}
|
|
|
|
|
2010-12-18 09:13:45 +01:00
|
|
|
if (mpctx->seek.type) {
|
2011-04-08 04:15:43 +02:00
|
|
|
seek(mpctx, mpctx->seek, false);
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->seek = (struct seek_params){ 0 };
|
2010-12-13 20:12:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-29 11:55:23 +02:00
|
|
|
static int read_keys(void *ctx, int fd)
|
2008-04-04 02:26:33 +02:00
|
|
|
{
|
2008-04-29 14:12:19 +02:00
|
|
|
getch2(ctx);
|
2011-07-17 03:47:50 +02:00
|
|
|
return MP_INPUT_NOTHING;
|
2008-04-04 02:26:33 +02:00
|
|
|
}
|
|
|
|
|
2011-01-12 00:37:02 +01: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;
|
|
|
|
}
|
|
|
|
|
2011-02-18 15:32:40 +01:00
|
|
|
static int select_audio(demuxer_t *demuxer, int audio_id, char **audio_lang)
|
2011-01-12 00:37:02 +01:00
|
|
|
{
|
|
|
|
if (audio_id == -1)
|
|
|
|
audio_id = demuxer_audio_track_by_lang_and_default(demuxer, audio_lang);
|
|
|
|
if (audio_id != -1) // -1 (automatic) is the default behaviour of demuxers
|
|
|
|
demuxer_switch_audio(demuxer, audio_id);
|
|
|
|
if (audio_id == -2) { // some demuxers don't yet know how to switch to no sound
|
|
|
|
demuxer->audio->id = -2;
|
|
|
|
demuxer->audio->sh = NULL;
|
|
|
|
}
|
|
|
|
return demuxer->audio->id;
|
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
static void print_version(const char *name)
|
2011-01-12 00:37:02 +01:00
|
|
|
{
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, MP_TITLE, name);
|
|
|
|
|
|
|
|
/* Test for CPU capabilities (and corresponding OS support) for optimizing */
|
|
|
|
GetCpuCaps(&gCpuCaps);
|
|
|
|
#if ARCH_X86
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V,
|
2011-08-07 02:58:12 +02:00
|
|
|
"CPUflags: MMX: %d MMX2: %d 3DNow: %d 3DNowExt: %d SSE: %d SSE2: %d SSSE3: %d\n",
|
|
|
|
gCpuCaps.hasMMX, gCpuCaps.hasMMX2,
|
|
|
|
gCpuCaps.has3DNow, gCpuCaps.has3DNowExt,
|
|
|
|
gCpuCaps.hasSSE, gCpuCaps.hasSSE2, gCpuCaps.hasSSSE3);
|
2011-01-12 00:37:02 +01:00
|
|
|
#if CONFIG_RUNTIME_CPUDETECT
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "Compiled with runtime CPU detection.\n");
|
2011-01-12 00:37:02 +01:00
|
|
|
#else
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "Compiled for x86 CPU with extensions:");
|
|
|
|
if (HAVE_MMX)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " MMX");
|
|
|
|
if (HAVE_MMX2)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " MMX2");
|
|
|
|
if (HAVE_AMD3DNOW)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " 3DNow");
|
|
|
|
if (HAVE_AMD3DNOWEXT)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " 3DNowExt");
|
|
|
|
if (HAVE_SSE)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " SSE");
|
|
|
|
if (HAVE_SSE2)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " SSE2");
|
|
|
|
if (HAVE_SSSE3)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " SSSE3");
|
|
|
|
if (HAVE_CMOV)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, " CMOV");
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "\n");
|
2011-01-12 00:37:02 +01:00
|
|
|
#endif /* CONFIG_RUNTIME_CPUDETECT */
|
|
|
|
#endif /* ARCH_X86 */
|
2011-10-01 19:03:49 +02:00
|
|
|
print_libav_versions();
|
2011-01-12 00:37:02 +01:00
|
|
|
}
|
2008-04-04 02:26:33 +02:00
|
|
|
|
2011-03-27 17:38:01 +02:00
|
|
|
#ifdef PTW32_STATIC_LIB
|
|
|
|
static void detach_ptw32(void)
|
|
|
|
{
|
|
|
|
pthread_win32_thread_detach_np();
|
|
|
|
pthread_win32_process_detach_np();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-06-06 15:28:25 +02:00
|
|
|
/* This preprocessor directive is a hack to generate a mplayer-nomain.o object
|
|
|
|
* file for some tools to link against. */
|
|
|
|
#ifndef DISABLE_MAIN
|
2011-08-07 02:58:12 +02:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2011-03-27 17:38:01 +02:00
|
|
|
#ifdef PTW32_STATIC_LIB
|
|
|
|
pthread_win32_process_attach_np();
|
|
|
|
pthread_win32_thread_attach_np();
|
|
|
|
atexit(detach_ptw32);
|
|
|
|
#endif
|
2011-08-08 06:58:21 +02:00
|
|
|
if (argc > 1 && (!strcmp(argv[1], "-leak-report")
|
|
|
|
|| !strcmp(argv[1], "--leak-report")))
|
2011-01-15 21:26:27 +01:00
|
|
|
talloc_enable_leak_report();
|
2006-12-28 16:31:48 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
char *mem_ptr;
|
2006-12-28 16:31:48 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// movie info:
|
2006-12-28 16:31:48 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/* Flag indicating whether MPlayer should exit without playing anything. */
|
|
|
|
int opt_exit = 0;
|
|
|
|
int i;
|
2006-12-28 16:31:48 +01:00
|
|
|
|
2011-10-06 20:46:01 +02:00
|
|
|
struct MPContext *mpctx = talloc(NULL, MPContext);
|
|
|
|
*mpctx = (struct MPContext){
|
2008-04-21 05:07:22 +02:00
|
|
|
.osd_function = OSD_PLAY,
|
|
|
|
.begin_skip = MP_NOPTS_VALUE,
|
|
|
|
.play_tree_step = 1,
|
|
|
|
.global_sub_pos = -1,
|
|
|
|
.set_of_sub_pos = -1,
|
|
|
|
.file_format = DEMUXER_TYPE_UNKNOWN,
|
|
|
|
.last_dvb_step = 1,
|
|
|
|
};
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
InitTimer();
|
|
|
|
srand(GetTimerMS());
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg_init();
|
|
|
|
init_libav();
|
2002-04-12 12:40:38 +02:00
|
|
|
|
2008-08-08 01:04:43 +02:00
|
|
|
#ifdef CONFIG_X11
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->x11_state = vo_x11_init_state();
|
2008-04-20 06:36:34 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
struct MPOpts *opts = &mpctx->opts;
|
|
|
|
set_default_mplayer_options(opts);
|
|
|
|
// Create the config context and register the options
|
|
|
|
mpctx->mconfig = m_config_new(opts, cfg_include);
|
|
|
|
m_config_register_options(mpctx->mconfig, mplayer_opts);
|
|
|
|
m_config_register_options(mpctx->mconfig, common_opts);
|
|
|
|
mp_input_register_options(mpctx->mconfig);
|
2008-04-13 21:18:51 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// Preparse the command line
|
|
|
|
m_config_preparse_command_line(mpctx->mconfig, argc, argv);
|
2007-01-17 01:27:41 +01:00
|
|
|
|
2008-10-13 18:23:55 +02:00
|
|
|
#if (defined(__MINGW32__) || defined(__CYGWIN__)) && defined(CONFIG_WIN32DLL)
|
2011-08-07 02:58:12 +02:00
|
|
|
set_path_env();
|
2008-10-13 18:23:55 +02:00
|
|
|
#endif
|
2003-08-17 21:18:29 +02:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_TV
|
2011-08-07 02:58:12 +02:00
|
|
|
stream_tv_defaults.immediate = 1;
|
2002-08-22 23:30:31 +02:00
|
|
|
#endif
|
2001-10-19 04:00:45 +02:00
|
|
|
|
2008-04-26 09:44:59 +02:00
|
|
|
parse_cfgfiles(mpctx, mpctx->mconfig);
|
2002-01-15 00:38:49 +01:00
|
|
|
|
2008-04-26 09:44:59 +02:00
|
|
|
mpctx->playtree = m_config_parse_mp_command_line(mpctx->mconfig, argc, argv);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->playtree == NULL)
|
|
|
|
opt_exit = 1;
|
2005-09-02 10:29:30 +02:00
|
|
|
else {
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->playtree = play_tree_cleanup(mpctx->playtree);
|
|
|
|
if (mpctx->playtree) {
|
|
|
|
mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree,
|
|
|
|
mpctx->mconfig);
|
|
|
|
if (mpctx->playtree_iter) {
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, 0, 0) !=
|
|
|
|
PLAY_TREE_ITER_ENTRY) {
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
}
|
|
|
|
mpctx->filename = play_tree_iter_get_file(mpctx->playtree_iter,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
}
|
2005-09-02 10:29:30 +02:00
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
print_version("MPlayer2");
|
2009-02-21 18:35:48 +01:00
|
|
|
|
2008-10-13 18:23:55 +02:00
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
2011-08-07 02:58:12 +02:00
|
|
|
{
|
|
|
|
HMODULE kernel32 = GetModuleHandle("Kernel32.dll");
|
|
|
|
BOOL WINAPI (*setDEP)(DWORD) = NULL;
|
|
|
|
BOOL WINAPI (*setDllDir)(LPCTSTR) = NULL;
|
|
|
|
if (kernel32) {
|
|
|
|
setDEP = GetProcAddress(kernel32, "SetProcessDEPPolicy");
|
|
|
|
setDllDir = GetProcAddress(kernel32, "SetDllDirectoryA");
|
|
|
|
}
|
|
|
|
if (setDEP)
|
|
|
|
setDEP(3);
|
|
|
|
if (setDllDir)
|
|
|
|
setDllDir("");
|
|
|
|
}
|
|
|
|
// stop Windows from showing all kinds of annoying error dialogs
|
|
|
|
SetErrorMode(0x8003);
|
|
|
|
// request 1ms timer resolution
|
|
|
|
timeBeginPeriod(1);
|
2009-07-07 01:26:13 +02:00
|
|
|
#endif
|
2009-02-10 16:34:44 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_PRIORITY
|
|
|
|
set_priority();
|
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->video_driver_list &&
|
|
|
|
strcmp(opts->video_driver_list[0], "help") == 0) {
|
|
|
|
list_video_out();
|
|
|
|
opt_exit = 1;
|
2001-06-03 02:24:49 +02:00
|
|
|
}
|
2002-03-14 22:31:25 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->audio_driver_list &&
|
|
|
|
strcmp(opts->audio_driver_list[0], "help") == 0) {
|
|
|
|
list_audio_out();
|
|
|
|
opt_exit = 1;
|
2001-06-03 02:24:49 +02:00
|
|
|
}
|
2001-08-25 21:11:24 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/* Check codecs.conf. */
|
|
|
|
if (!codecs_file || !parse_codec_cfg(codecs_file)) {
|
|
|
|
if (!parse_codec_cfg(mem_ptr = get_path("codecs.conf"))) {
|
|
|
|
if (!parse_codec_cfg(MPLAYER_CONFDIR "/codecs.conf")) {
|
|
|
|
if (!parse_codec_cfg(NULL))
|
|
|
|
exit_player_with_rc(mpctx, EXIT_NONE, 0);
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V,
|
|
|
|
"Using built-in default codecs.conf.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(mem_ptr); // release the buffer created by get_path()
|
2002-12-16 00:45:19 +01:00
|
|
|
}
|
2001-09-27 14:59:35 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (audio_codec_list && strcmp(audio_codec_list[0], "help") == 0) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Available audio codecs:\n");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_CODECS\n");
|
|
|
|
list_codecs(1);
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
if (video_codec_list && strcmp(video_codec_list[0], "help") == 0) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Available video codecs:\n");
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_CODECS\n");
|
|
|
|
list_codecs(0);
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
if (video_fm_list && strcmp(video_fm_list[0], "help") == 0) {
|
|
|
|
vfm_help();
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
if (audio_fm_list && strcmp(audio_fm_list[0], "help") == 0) {
|
|
|
|
afm_help();
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
|
|
|
}
|
|
|
|
if (af_cfg.list && strcmp(af_cfg.list[0], "help") == 0) {
|
|
|
|
af_help();
|
|
|
|
printf("\n");
|
|
|
|
opt_exit = 1;
|
2004-09-07 00:27:08 +02:00
|
|
|
}
|
2008-08-04 08:16:23 +02:00
|
|
|
#ifdef CONFIG_X11
|
2011-08-07 02:58:12 +02:00
|
|
|
if (vo_fstype_list && strcmp(vo_fstype_list[0], "help") == 0) {
|
|
|
|
fstype_help();
|
|
|
|
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
|
|
|
|
opt_exit = 1;
|
2003-02-07 20:38:39 +01:00
|
|
|
}
|
2003-02-08 10:08:11 +01:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
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;
|
2005-08-05 21:57:47 +02:00
|
|
|
}
|
2010-11-13 22:10:58 +01:00
|
|
|
if (opts->list_properties) {
|
2011-08-07 02:58:12 +02:00
|
|
|
property_print_help();
|
|
|
|
opt_exit = 1;
|
2006-03-22 17:35:17 +01:00
|
|
|
}
|
2001-09-27 14:59:35 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opt_exit)
|
|
|
|
exit_player(mpctx, EXIT_NONE);
|
2005-09-02 10:29:30 +02:00
|
|
|
|
2010-11-13 22:10:58 +01:00
|
|
|
if (!mpctx->filename && !opts->player_idle_mode) {
|
2011-08-07 02:58:12 +02:00
|
|
|
// no file/vcd/dvd -> show HELP:
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", mp_gtext(help_text));
|
2008-12-04 00:55:52 +01:00
|
|
|
exit_player_with_rc(mpctx, EXIT_NONE, 0);
|
2001-08-25 21:11:24 +02:00
|
|
|
}
|
|
|
|
|
2006-11-28 19:29:24 +01:00
|
|
|
/* Display what configure line was used */
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Configuration: " CONFIGURATION "\n");
|
|
|
|
|
2001-08-25 21:11:24 +02:00
|
|
|
// Many users forget to include command line in bugreports...
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mp_msg_test(MSGT_CPLAYER, MSGL_V)) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "CommandLine:");
|
|
|
|
for (i = 1; i < argc; i++)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", argv[i]);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
|
2001-08-25 21:11:24 +02:00
|
|
|
}
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
//------ load global data first ------
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->osd = osd_create();
|
2008-06-24 07:29:36 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// check font
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_FREETYPE
|
2011-08-07 02:58:12 +02:00
|
|
|
init_freetype();
|
2003-12-08 13:14:32 +01:00
|
|
|
#endif
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_FONTCONFIG
|
2011-08-07 02:58:12 +02:00
|
|
|
if (font_fontconfig <= 0) {
|
2003-12-08 13:14:32 +01:00
|
|
|
#endif
|
2008-08-07 10:58:07 +02:00
|
|
|
#ifdef CONFIG_BITMAP_FONT
|
2011-08-07 02:58:12 +02:00
|
|
|
if (font_name) {
|
|
|
|
vo_font = read_font_desc(font_name, font_factor, verbose > 1);
|
|
|
|
if (!vo_font)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Cannot load bitmap font: %s\n",
|
|
|
|
filename_recode(font_name));
|
|
|
|
} else {
|
|
|
|
// try default:
|
|
|
|
vo_font = read_font_desc(mem_ptr = get_path("font/font.desc"),
|
|
|
|
font_factor, verbose > 1);
|
|
|
|
free(mem_ptr); // release the buffer created by get_path()
|
|
|
|
if (!vo_font)
|
|
|
|
vo_font = read_font_desc(MPLAYER_DATADIR "/font/font.desc",
|
|
|
|
font_factor, verbose > 1);
|
|
|
|
}
|
|
|
|
if (sub_font_name)
|
|
|
|
mpctx->osd->sub_font = read_font_desc(sub_font_name, font_factor,
|
|
|
|
verbose > 1);
|
|
|
|
else
|
|
|
|
mpctx->osd->sub_font = vo_font;
|
2006-08-22 01:19:11 +02:00
|
|
|
#endif
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_FONTCONFIG
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2002-08-28 22:52:02 +02:00
|
|
|
#endif
|
2006-11-22 20:21:16 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-09-03 12:47:56 +02:00
|
|
|
mpctx->ass_library = mp_ass_init(opts);
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->osd->ass_library = mpctx->ass_library;
|
2006-10-28 17:07:18 +02:00
|
|
|
#endif
|
|
|
|
|
2001-11-20 08:53:20 +01:00
|
|
|
#ifdef HAVE_RTC
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->rtc) {
|
|
|
|
char *rtc_device = opts->rtc_device;
|
|
|
|
// seteuid(0); /* Can't hurt to try to get root here */
|
|
|
|
if ((rtc_fd = open(rtc_device ? rtc_device : "/dev/rtc", O_RDONLY)) < 0)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "Failed to open %s: %s "
|
|
|
|
"(it should be readable by the user.)\n",
|
|
|
|
rtc_device ? rtc_device : "/dev/rtc", strerror(errno));
|
|
|
|
else {
|
|
|
|
unsigned long irqp = 1024; /* 512 seemed OK. 128 is jerky. */
|
|
|
|
|
|
|
|
if (ioctl(rtc_fd, RTC_IRQP_SET, irqp) < 0) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "Linux RTC init error in "
|
|
|
|
"ioctl (rtc_irqp_set %lu): %s\n",
|
|
|
|
irqp, strerror(errno));
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_HINT, "Try adding \"echo %lu > /proc/sys/dev/rtc/max-user-freq\" to your system startup scripts.\n", irqp);
|
|
|
|
close(rtc_fd);
|
|
|
|
rtc_fd = -1;
|
|
|
|
} else if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) {
|
|
|
|
/* variable only by the root */
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC init error in "
|
|
|
|
"ioctl (rtc_pie_on): %s\n", strerror(errno));
|
|
|
|
close(rtc_fd);
|
|
|
|
rtc_fd = -1;
|
|
|
|
} else
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V,
|
|
|
|
"Using Linux hardware RTC timing (%ldHz).\n", irqp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rtc_fd < 0)
|
2005-09-15 05:08:35 +02:00
|
|
|
#endif /* HAVE_RTC */
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Using %s timing\n",
|
|
|
|
opts->softsleep ? "software" : timer_name);
|
2001-11-14 01:26:28 +01:00
|
|
|
|
2008-08-01 22:28:58 +02:00
|
|
|
#ifdef HAVE_TERMCAP
|
2011-08-07 02:58:12 +02:00
|
|
|
load_termcap(NULL); // load key-codes
|
2001-08-22 23:35:44 +02:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// ========== Init keyboard FIFO (connection to libvo) ============
|
2001-08-25 23:05:23 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// Init input system
|
|
|
|
current_module = "init_input";
|
|
|
|
mpctx->input = mp_input_init(&opts->input);
|
|
|
|
mpctx->key_fifo = mp_fifo_create(mpctx->input, opts);
|
|
|
|
if (slave_mode)
|
|
|
|
mp_input_add_cmd_fd(mpctx->input, 0, USE_FD0_CMD_SELECT, MP_INPUT_SLAVE_CMD_FUNC, NULL);
|
|
|
|
else if (opts->consolecontrols)
|
|
|
|
mp_input_add_key_fd(mpctx->input, 0, 1, read_keys, NULL, mpctx->key_fifo);
|
|
|
|
// Set the libstream interrupt callback
|
|
|
|
stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input);
|
2003-04-04 22:02:53 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = NULL;
|
2007-12-19 09:42:18 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/// Catch signals
|
2004-09-19 20:45:11 +02:00
|
|
|
#ifndef __MINGW32__
|
2011-08-07 02:58:12 +02:00
|
|
|
signal(SIGCHLD, child_sighandler);
|
2004-09-19 20:45:11 +02:00
|
|
|
#endif
|
2002-01-30 13:46:03 +01:00
|
|
|
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_CRASH_DEBUG
|
2011-08-07 02:58:12 +02:00
|
|
|
prog_path = argv[0];
|
2004-10-29 01:25:18 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
//========= Catch terminate signals: ================
|
|
|
|
// terminate requests:
|
|
|
|
signal(SIGTERM, exit_sighandler); // kill
|
|
|
|
signal(SIGHUP, exit_sighandler); // kill -HUP / xterm closed
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
signal(SIGINT, exit_sighandler); // Interrupt from keyboard
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
signal(SIGQUIT, exit_sighandler); // Quit from keyboard
|
|
|
|
signal(SIGPIPE, exit_sighandler); // Some window managers cause this
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_SIGHANDLER
|
2011-08-07 02:58:12 +02:00
|
|
|
// fatal errors:
|
|
|
|
signal(SIGBUS, exit_sighandler); // bus error
|
|
|
|
signal(SIGSEGV, exit_sighandler); // segfault
|
|
|
|
signal(SIGILL, exit_sighandler); // illegal instruction
|
|
|
|
signal(SIGFPE, exit_sighandler); // floating point exc.
|
|
|
|
signal(SIGABRT, exit_sighandler); // abort()
|
2008-08-07 14:20:50 +02:00
|
|
|
#ifdef CONFIG_CRASH_DEBUG
|
2011-08-07 02:58:12 +02:00
|
|
|
if (crash_debug)
|
|
|
|
signal(SIGTRAP, exit_sighandler);
|
2004-10-29 01:25:18 +02:00
|
|
|
#endif
|
2002-03-27 04:45:55 +01:00
|
|
|
#endif
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// ***************** Now, let's see the per-file stuff ******************
|
2002-05-05 19:22:38 +02:00
|
|
|
|
2001-08-22 23:35:44 +02:00
|
|
|
play_next_file:
|
2001-08-30 23:14:34 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// init global sub numbers
|
|
|
|
mpctx->global_sub_size = 0;
|
|
|
|
memset(mpctx->sub_counts, 0, sizeof(mpctx->sub_counts));
|
2002-02-23 22:22:55 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->filename) {
|
|
|
|
load_per_protocol_config(mpctx->mconfig, mpctx->filename);
|
|
|
|
load_per_extension_config(mpctx->mconfig, mpctx->filename);
|
|
|
|
load_per_file_config(mpctx->mconfig, mpctx->filename);
|
2005-09-02 10:44:55 +02:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
|
|
|
|
if (opts->video_driver_list)
|
|
|
|
load_per_output_config(mpctx->mconfig, PROFILE_CFG_VO,
|
|
|
|
opts->video_driver_list[0]);
|
|
|
|
if (opts->audio_driver_list)
|
|
|
|
load_per_output_config(mpctx->mconfig, PROFILE_CFG_AO,
|
|
|
|
opts->audio_driver_list[0]);
|
|
|
|
|
|
|
|
// We must enable getch2 here to be able to interrupt network connection
|
|
|
|
// or cache filling
|
|
|
|
if (opts->consolecontrols && !slave_mode) {
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
// ================= GUI idle loop (STOP state) =========================
|
|
|
|
while (opts->player_idle_mode && !mpctx->filename) {
|
|
|
|
play_tree_t *entry = NULL;
|
|
|
|
mp_cmd_t *cmd;
|
|
|
|
if (mpctx->video_out && mpctx->video_out->config_ok)
|
|
|
|
vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL);
|
|
|
|
while (!(cmd = mp_input_get_cmd(mpctx->input, 0, 0))) {
|
|
|
|
if (mpctx->video_out)
|
|
|
|
vo_check_events(mpctx->video_out);
|
|
|
|
usec_sleep(20000);
|
|
|
|
}
|
|
|
|
switch (cmd->id) {
|
2005-09-02 10:44:55 +02:00
|
|
|
case MP_CMD_LOADFILE:
|
|
|
|
// prepare a tree entry with the new filename
|
|
|
|
entry = play_tree_new();
|
|
|
|
play_tree_add_file(entry, cmd->args[0].v.s);
|
2006-04-27 15:22:23 +02:00
|
|
|
// The entry is added to the main playtree after the switch().
|
2005-09-02 10:44:55 +02:00
|
|
|
break;
|
|
|
|
case MP_CMD_LOADLIST:
|
2011-07-29 06:24:09 +02:00
|
|
|
entry = parse_playlist_file(mpctx->mconfig, bstr(cmd->args[0].v.s));
|
2005-09-02 10:44:55 +02:00
|
|
|
break;
|
|
|
|
case MP_CMD_QUIT:
|
2011-08-07 02:58:12 +02:00
|
|
|
exit_player_with_rc(mpctx, EXIT_QUIT,
|
|
|
|
(cmd->nargs > 0) ? cmd->args[0].v.i : 0);
|
2005-09-02 10:44:55 +02:00
|
|
|
break;
|
2010-07-14 23:36:06 +02:00
|
|
|
case MP_CMD_VO_FULLSCREEN:
|
2008-03-28 09:30:53 +01:00
|
|
|
case MP_CMD_GET_PROPERTY:
|
|
|
|
case MP_CMD_SET_PROPERTY:
|
|
|
|
case MP_CMD_STEP_PROPERTY:
|
|
|
|
run_command(mpctx, cmd);
|
|
|
|
break;
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_cmd_free(cmd);
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (entry) { // user entered a command that gave a valid entry
|
|
|
|
if (mpctx->playtree)
|
|
|
|
// the playtree is always a node with one child. let's clear it
|
|
|
|
play_tree_free_list(mpctx->playtree->child, 1);
|
|
|
|
else
|
|
|
|
// .. or make a brand new playtree
|
|
|
|
mpctx->playtree = play_tree_new();
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->playtree)
|
|
|
|
continue; // couldn't make playtree! wait for next command
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
play_tree_set_child(mpctx->playtree, entry);
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/* Make iterator start at the top the of tree. */
|
|
|
|
mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree,
|
|
|
|
mpctx->mconfig);
|
|
|
|
if (!mpctx->playtree_iter)
|
|
|
|
continue;
|
2005-09-02 10:44:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// find the first real item in the tree
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, 0, 0) !=
|
|
|
|
PLAY_TREE_ITER_ENTRY) {
|
|
|
|
// no items!
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
continue; // wait for next command
|
|
|
|
}
|
|
|
|
mpctx->filename = play_tree_iter_get_file(mpctx->playtree_iter, 1);
|
2005-09-02 10:44:55 +02:00
|
|
|
}
|
|
|
|
}
|
2011-09-04 21:08:26 +02:00
|
|
|
|
2011-07-23 04:13:25 +02:00
|
|
|
#ifdef CONFIG_ASS
|
|
|
|
ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
|
|
|
|
#endif
|
2010-03-09 23:13:11 +01:00
|
|
|
if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
|
2011-08-07 02:58:12 +02:00
|
|
|
vo_control(mpctx->video_out, VOCTRL_RESUME, NULL);
|
2010-02-20 00:35:21 +01:00
|
|
|
|
2009-11-16 05:54:22 +01:00
|
|
|
if (mpctx->filename) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "\nPlaying %s.\n",
|
|
|
|
filename_recode(mpctx->filename));
|
|
|
|
if (use_filename_title && opts->vo_wintitle == NULL)
|
2011-07-10 02:20:47 +02:00
|
|
|
opts->vo_wintitle = talloc_strdup(NULL,
|
|
|
|
mp_basename(mpctx->filename));
|
2009-11-12 10:21:53 +01:00
|
|
|
}
|
2001-08-23 13:33:58 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (edl_filename) {
|
|
|
|
if (edl_records)
|
|
|
|
free_edl(edl_records);
|
|
|
|
next_edl_record = edl_records = edl_parse_file();
|
|
|
|
}
|
|
|
|
if (edl_output_filename) {
|
|
|
|
if (edl_fd)
|
|
|
|
fclose(edl_fd);
|
|
|
|
if ((edl_fd = fopen(edl_output_filename, "w")) == NULL) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
|
|
|
"Can't open EDL file [%s] for writing.\n",
|
|
|
|
filename_recode(edl_output_filename));
|
|
|
|
}
|
2005-12-06 09:52:18 +01:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
//==================== Open VOB-Sub ============================
|
2002-05-05 19:22:38 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "vobsub";
|
|
|
|
if (opts->vobsub_name) {
|
|
|
|
vo_vobsub = vobsub_open(opts->vobsub_name, spudec_ifo, 1, &vo_spudec);
|
|
|
|
if (vo_vobsub == NULL)
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Cannot load subtitles: %s\n",
|
|
|
|
filename_recode(opts->vobsub_name));
|
|
|
|
} else if (opts->sub_auto && mpctx->filename) {
|
2011-03-03 11:31:12 +01:00
|
|
|
char **vob = find_vob_subtitles(opts, mpctx->filename);
|
2011-04-09 04:14:55 +02:00
|
|
|
for (int i = 0; i < MP_TALLOC_ELEMS(vob); i++) {
|
|
|
|
vo_vobsub = vobsub_open(vob[i], spudec_ifo, 0, &vo_spudec);
|
|
|
|
if (vo_vobsub)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
talloc_free(vob);
|
2002-02-20 23:41:06 +01:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
if (vo_vobsub) {
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_VOBSUB;
|
|
|
|
vobsub_set_from_lang(vo_vobsub, opts->sub_lang);
|
|
|
|
mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only,
|
|
|
|
mpctx);
|
|
|
|
|
|
|
|
// setup global sub numbering
|
|
|
|
mpctx->sub_counts[SUB_SOURCE_VOBSUB] =
|
|
|
|
vobsub_get_indexes_count(vo_vobsub);
|
|
|
|
}
|
|
|
|
|
|
|
|
//============ Open & Sync STREAM --- fork cache2 ====================
|
|
|
|
|
|
|
|
mpctx->stream = NULL;
|
|
|
|
mpctx->demuxer = NULL;
|
|
|
|
mpctx->d_audio = NULL;
|
|
|
|
mpctx->d_video = NULL;
|
|
|
|
mpctx->d_sub = NULL;
|
|
|
|
mpctx->sh_audio = NULL;
|
|
|
|
mpctx->sh_video = NULL;
|
|
|
|
|
|
|
|
current_module = "open_stream";
|
|
|
|
mpctx->stream = open_stream(mpctx->filename, opts, &mpctx->file_format);
|
|
|
|
if (!mpctx->stream) { // error...
|
|
|
|
mpctx->stop_play = libmpdemux_was_interrupted(mpctx, PT_NEXT_ENTRY);
|
|
|
|
goto goto_next_file;
|
|
|
|
}
|
|
|
|
mpctx->initialized_flags |= INITIALIZED_STREAM;
|
|
|
|
|
|
|
|
if (mpctx->file_format == DEMUXER_TYPE_PLAYLIST) {
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "\nThis looks like a playlist, but "
|
|
|
|
"playlist support will not be used automatically.\n"
|
|
|
|
"MPlayer's playlist code is unsafe and should only be used with "
|
|
|
|
"trusted sources.\nPlayback will probably fail.\n\n");
|
2008-04-26 12:56:15 +02:00
|
|
|
#if 0
|
2011-08-07 02:58:12 +02:00
|
|
|
play_tree_t *entry;
|
|
|
|
// Handle playlist
|
|
|
|
current_module = "handle_playlist";
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Parsing playlist %s...\n",
|
|
|
|
filename_recode(mpctx->filename));
|
|
|
|
entry = parse_playtree(mpctx->stream, mpctx->mconfig, 0);
|
|
|
|
mpctx->eof = playtree_add_playlist(mpctx, entry);
|
|
|
|
goto goto_next_file;
|
2008-04-26 12:56:15 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
mpctx->stream->start_pos += seek_to_byte;
|
|
|
|
|
|
|
|
if (stream_dump_type == 5) {
|
|
|
|
unsigned char buf[4096];
|
|
|
|
int len;
|
|
|
|
FILE *f;
|
|
|
|
current_module = "dumpstream";
|
|
|
|
stream_reset(mpctx->stream);
|
|
|
|
stream_seek(mpctx->stream, mpctx->stream->start_pos);
|
|
|
|
f = fopen(opts->stream_dump_name, "wb");
|
|
|
|
if (!f) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Cannot open dump file.\n");
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
2003-05-31 23:50:21 +02:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->chapterrange[0] > 1) {
|
|
|
|
int chapter = opts->chapterrange[0] - 1;
|
|
|
|
stream_control(mpctx->stream, STREAM_CTRL_SEEK_TO_CHAPTER, &chapter);
|
|
|
|
}
|
|
|
|
struct stream_dump_progress info;
|
|
|
|
stream_dump_progress_start(&info);
|
|
|
|
while (!mpctx->stream->eof && !async_quit_request) {
|
|
|
|
len = stream_read(mpctx->stream, buf, 4096);
|
|
|
|
if (len > 0) {
|
|
|
|
if (fwrite(buf, len, 1, f) != 1) {
|
|
|
|
mp_tmsg(MSGT_GLOBAL, MSGL_FATAL, "%s: Error writing file.\n", opts->stream_dump_name);
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stream_dump_progress(&info, len, mpctx->stream);
|
|
|
|
if (opts->chapterrange[1] > 0) {
|
|
|
|
int chapter = -1;
|
|
|
|
if (stream_control(mpctx->stream,
|
|
|
|
STREAM_CTRL_GET_CURRENT_CHAPTER, &chapter) == STREAM_OK
|
|
|
|
&& chapter + 1 > opts->chapterrange[1])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fclose(f)) {
|
|
|
|
mp_tmsg(MSGT_GLOBAL, MSGL_FATAL, "%s: Error writing file.\n",
|
|
|
|
opts->stream_dump_name);
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
|
|
|
stream_dump_progress_end(&info, opts->stream_dump_name);
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Stream dump complete.\n");
|
|
|
|
exit_player_with_rc(mpctx, EXIT_EOF, 0);
|
|
|
|
}
|
2001-12-01 23:54:17 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDREAD
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVD) {
|
|
|
|
current_module = "dvd lang->id";
|
|
|
|
if (opts->audio_lang && opts->audio_id == -1)
|
|
|
|
opts->audio_id = dvd_aid_from_lang(mpctx->stream, opts->audio_lang);
|
|
|
|
if (opts->sub_lang && opts->sub_id == -1)
|
|
|
|
opts->sub_id = dvd_sid_from_lang(mpctx->stream, opts->sub_lang);
|
|
|
|
// setup global sub numbering
|
|
|
|
mpctx->sub_counts[SUB_SOURCE_DEMUX] = dvd_number_of_subs(mpctx->stream);
|
|
|
|
current_module = NULL;
|
|
|
|
}
|
2002-01-20 01:39:54 +01:00
|
|
|
#endif
|
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
|
|
|
current_module = "dvdnav lang->id";
|
|
|
|
if (opts->audio_lang && opts->audio_id == -1)
|
|
|
|
opts->audio_id = mp_dvdnav_aid_from_lang(mpctx->stream,
|
|
|
|
opts->audio_lang);
|
|
|
|
dvdsub_lang_id = -3;
|
|
|
|
if (opts->sub_lang && opts->sub_id == -1)
|
|
|
|
dvdsub_lang_id = opts->sub_id = mp_dvdnav_sid_from_lang(
|
|
|
|
mpctx->stream, opts->sub_lang);
|
|
|
|
// setup global sub numbering
|
|
|
|
mpctx->sub_counts[SUB_SOURCE_DEMUX] = mp_dvdnav_number_of_subs(
|
|
|
|
mpctx->stream);
|
|
|
|
current_module = NULL;
|
|
|
|
}
|
2006-11-25 15:46:21 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// CACHE2: initial prefill: 20% later: 5% (should be set by -cacheopts)
|
2003-11-01 16:17:01 +01:00
|
|
|
goto_enable_cache:
|
2011-08-07 02:58:12 +02:00
|
|
|
if (stream_cache_size > 0) {
|
|
|
|
int res;
|
|
|
|
float stream_cache_min_percent = opts->stream_cache_min_percent;
|
|
|
|
float stream_cache_seek_min_percent = opts->stream_cache_seek_min_percent;
|
|
|
|
current_module = "enable_cache";
|
|
|
|
res = stream_enable_cache(mpctx->stream, stream_cache_size * 1024,
|
|
|
|
stream_cache_size * 1024 * (stream_cache_min_percent / 100.0),
|
|
|
|
stream_cache_size * 1024 * (stream_cache_seek_min_percent / 100.0));
|
|
|
|
if (res == 0)
|
|
|
|
if ((mpctx->stop_play = libmpdemux_was_interrupted(mpctx, PT_NEXT_ENTRY)))
|
|
|
|
goto goto_next_file;
|
|
|
|
}
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
//============ Open DEMUXERS --- DETECT file type =======================
|
|
|
|
current_module = "demux_open";
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->demuxer = demux_open(opts, mpctx->stream, mpctx->file_format,
|
|
|
|
opts->audio_id, opts->video_id, opts->sub_id,
|
|
|
|
mpctx->filename);
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// HACK to get MOV Reference Files working
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->demuxer && mpctx->demuxer->type == DEMUXER_TYPE_PLAYLIST) {
|
|
|
|
unsigned char *playlist_entry;
|
|
|
|
play_tree_t *list = NULL, *entry = NULL;
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "handle_demux_playlist";
|
|
|
|
while (ds_get_packet(mpctx->demuxer->video, &playlist_entry) > 0) {
|
|
|
|
char *temp;
|
|
|
|
const char *bname;
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Adding file %s to element entry.\n",
|
|
|
|
filename_recode(playlist_entry));
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
bname = mp_basename(playlist_entry);
|
|
|
|
if ((strlen(bname) > 10) && !strncmp(bname, "qt", 2) &&
|
|
|
|
!strncmp(bname + 3, "gateQT", 6))
|
|
|
|
continue;
|
2003-01-27 23:27:54 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!strcmp(playlist_entry, mpctx->filename)) // self-reference
|
|
|
|
continue;
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
entry = play_tree_new();
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->filename && !strcmp(mp_basename(playlist_entry),
|
|
|
|
playlist_entry)) { // add reference path of current file
|
|
|
|
temp = malloc((strlen(mpctx->filename) - strlen(mp_basename(
|
|
|
|
mpctx->filename)) + strlen(playlist_entry) + 1));
|
|
|
|
if (temp) {
|
|
|
|
strncpy(temp, mpctx->filename, strlen(mpctx->filename) -
|
|
|
|
strlen(mp_basename(mpctx->filename)));
|
|
|
|
temp[strlen(mpctx->filename) - strlen(mp_basename(
|
|
|
|
mpctx->filename))] = '\0';
|
|
|
|
strcat(temp, playlist_entry);
|
|
|
|
if (!strcmp(temp, mpctx->filename)) {
|
|
|
|
free(temp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
play_tree_add_file(entry, temp);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V,
|
|
|
|
"Resolving reference to %s.\n", temp);
|
|
|
|
free(temp);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
play_tree_add_file(entry, playlist_entry);
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!list)
|
|
|
|
list = entry;
|
|
|
|
else
|
|
|
|
play_tree_append_entry(list, entry);
|
|
|
|
}
|
|
|
|
free_demuxer(mpctx->demuxer);
|
|
|
|
mpctx->demuxer = NULL;
|
2003-01-13 03:42:06 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (list) {
|
|
|
|
entry = play_tree_new();
|
|
|
|
play_tree_set_child(entry, list);
|
|
|
|
mpctx->stop_play = playtree_add_playlist(mpctx, entry);
|
|
|
|
goto goto_next_file;
|
|
|
|
}
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->demuxer) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Failed to recognize file format.\n");
|
|
|
|
goto goto_next_file;
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
|
2011-08-07 02:58:12 +02: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 12:05:35 +01:00
|
|
|
|
2011-08-07 02:58:12 +02: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 12:05:35 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->timeline) {
|
|
|
|
mpctx->timeline_part = 0;
|
|
|
|
mpctx->demuxer = mpctx->timeline[0].source->demuxer;
|
|
|
|
|
|
|
|
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,
|
|
|
|
filename_recode(mpctx->sources[i].demuxer->filename));
|
|
|
|
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;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "%3d %9.3f %9.3f %3td\n", i, p->start,
|
|
|
|
p->source_start, p->source - mpctx->sources);
|
|
|
|
}
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "END %9.3f\n",
|
|
|
|
mpctx->timeline[part_count].start);
|
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 12:05:35 +01:00
|
|
|
}
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->sources) {
|
|
|
|
mpctx->sources = talloc_ptrtype(NULL, mpctx->sources);
|
|
|
|
*mpctx->sources = (struct content_source){
|
|
|
|
.stream = mpctx->stream,
|
|
|
|
.demuxer = mpctx->demuxer
|
|
|
|
};
|
|
|
|
mpctx->num_sources = 1;
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->initialized_flags |= INITIALIZED_DEMUXER;
|
2002-08-21 23:31:20 +02:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
if (opts->ass_enabled && mpctx->ass_library) {
|
|
|
|
for (int j = 0; j < mpctx->num_sources; j++) {
|
|
|
|
struct demuxer *d = mpctx->sources[j].demuxer;
|
|
|
|
for (int i = 0; i < d->num_attachments; i++) {
|
|
|
|
struct demux_attachment *att = d->attachments + i;
|
2011-09-03 12:47:56 +02:00
|
|
|
if (opts->use_embedded_fonts && attachment_is_font(att))
|
2011-08-07 02:58:12 +02:00
|
|
|
ass_add_font(mpctx->ass_library, att->name, att->data,
|
|
|
|
att->data_size);
|
|
|
|
}
|
2009-03-29 21:45:06 +02:00
|
|
|
}
|
|
|
|
}
|
2008-01-12 02:12:39 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "demux_open2";
|
2002-03-18 02:49:42 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->d_audio = mpctx->demuxer->audio;
|
|
|
|
mpctx->d_video = mpctx->demuxer->video;
|
|
|
|
mpctx->d_sub = mpctx->demuxer->sub;
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (ts_prog) {
|
|
|
|
int tmp = ts_prog;
|
|
|
|
mp_property_do("switch_program", M_PROPERTY_SET, &tmp, mpctx);
|
|
|
|
}
|
|
|
|
// select audio stream
|
2011-04-02 21:26:03 +02:00
|
|
|
for (int i = 0; i < mpctx->num_sources; i++)
|
|
|
|
select_audio(mpctx->sources[i].demuxer->audio->demuxer, opts->audio_id,
|
|
|
|
opts->audio_lang);
|
2008-02-29 18:25:50 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// DUMP STREAMS:
|
|
|
|
if ((stream_dump_type) && (stream_dump_type < 4)) {
|
|
|
|
FILE *f;
|
|
|
|
demux_stream_t *ds = NULL;
|
|
|
|
current_module = "dump";
|
|
|
|
// select stream to dump
|
|
|
|
switch (stream_dump_type) {
|
|
|
|
case 1: ds = mpctx->d_audio;
|
|
|
|
break;
|
|
|
|
case 2: ds = mpctx->d_video;
|
|
|
|
break;
|
|
|
|
case 3: ds = mpctx->d_sub;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!ds) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
|
|
|
"dump: FATAL: Selected stream missing!\n");
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
|
|
|
// disable other streams:
|
|
|
|
if (mpctx->d_audio && mpctx->d_audio != ds) {
|
|
|
|
ds_free_packs(mpctx->d_audio);
|
|
|
|
mpctx->d_audio->id = -2;
|
|
|
|
}
|
|
|
|
if (mpctx->d_video && mpctx->d_video != ds) {
|
|
|
|
ds_free_packs(mpctx->d_video);
|
|
|
|
mpctx->d_video->id = -2;
|
|
|
|
}
|
|
|
|
if (mpctx->d_sub && mpctx->d_sub != ds) {
|
|
|
|
ds_free_packs(mpctx->d_sub);
|
|
|
|
mpctx->d_sub->id = -2;
|
|
|
|
}
|
|
|
|
// let's dump it!
|
|
|
|
f = fopen(opts->stream_dump_name, "wb");
|
|
|
|
if (!f) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Cannot open dump file.\n");
|
|
|
|
exit_player(mpctx, EXIT_ERROR);
|
|
|
|
}
|
|
|
|
struct stream_dump_progress info;
|
|
|
|
stream_dump_progress_start(&info);
|
|
|
|
while (!ds->eof) {
|
|
|
|
unsigned char *start;
|
|
|
|
int in_size = ds_get_packet(ds, &start);
|
|
|
|
if ((mpctx->demuxer->file_format == DEMUXER_TYPE_AVI || mpctx->demuxer->file_format == DEMUXER_TYPE_ASF || mpctx->demuxer->file_format == DEMUXER_TYPE_MOV)
|
|
|
|
&& stream_dump_type == 2)
|
|
|
|
fwrite(&in_size, 1, 4, f);
|
|
|
|
if (in_size > 0) {
|
|
|
|
fwrite(start, in_size, 1, f);
|
|
|
|
stream_dump_progress(&info, in_size, mpctx->stream);
|
|
|
|
}
|
|
|
|
if (opts->chapterrange[1] > 0) {
|
|
|
|
int cur_chapter = demuxer_get_current_chapter(mpctx->demuxer, 0);
|
|
|
|
if (cur_chapter != -1 && cur_chapter + 1 > opts->chapterrange[1])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
stream_dump_progress_end(&info, opts->stream_dump_name);
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Stream dump complete.\n");
|
|
|
|
exit_player_with_rc(mpctx, EXIT_EOF, 0);
|
2008-11-23 13:45:07 +01:00
|
|
|
}
|
2001-05-14 00:45:21 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->sh_audio = mpctx->d_audio->sh;
|
|
|
|
mpctx->sh_video = mpctx->d_video->sh;
|
2001-05-14 00:45:21 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_video) {
|
|
|
|
current_module = "video_read_properties";
|
|
|
|
if (!video_read_properties(mpctx->sh_video)) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Video: Cannot read properties.\n");
|
|
|
|
mpctx->sh_video = mpctx->d_video->sh = NULL;
|
|
|
|
} else {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_V, "[V] filefmt:%d fourcc:0x%X "
|
|
|
|
"size:%dx%d fps:%5.3f ftime:=%6.4f\n",
|
|
|
|
mpctx->demuxer->file_format, mpctx->sh_video->format,
|
|
|
|
mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
|
2011-08-25 01:45:13 +02:00
|
|
|
mpctx->sh_video->fps, mpctx->sh_video->frametime);
|
2011-08-07 02:58:12 +02:00
|
|
|
if (force_fps) {
|
|
|
|
mpctx->sh_video->fps = force_fps;
|
|
|
|
mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
|
|
|
|
}
|
|
|
|
vo_fps = mpctx->sh_video->fps;
|
2002-02-08 17:00:14 +01:00
|
|
|
|
2011-08-25 01:45:13 +02:00
|
|
|
if (!mpctx->sh_video->fps && !force_fps && !opts->correct_pts) {
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "FPS not specified in the "
|
|
|
|
"header or invalid, use the -fps option.\n");
|
|
|
|
}
|
|
|
|
}
|
2002-02-09 02:29:11 +01:00
|
|
|
|
2002-02-08 17:00:14 +01:00
|
|
|
}
|
2001-07-22 00:37:55 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->sh_video && !mpctx->sh_audio) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "No stream found.\n");
|
2008-08-03 17:21:40 +02:00
|
|
|
#ifdef CONFIG_DVBIN
|
2011-08-07 02:58:12 +02:00
|
|
|
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;
|
2001-05-12 03:57:18 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (dvb_step_channel(mpctx->stream, dir)) {
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
mpctx->dvbin_reopen = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
goto goto_next_file; // exit_player(_("Fatal error"));
|
|
|
|
}
|
2002-03-17 01:47:15 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
/* display clip info */
|
|
|
|
demux_info_print(mpctx->demuxer);
|
|
|
|
|
|
|
|
//================= Read SUBTITLES (DVD & TEXT) =========================
|
|
|
|
if (vo_spudec == NULL && (mpctx->stream->type == STREAMTYPE_DVD
|
|
|
|
|| mpctx->stream->type == STREAMTYPE_DVDNAV))
|
|
|
|
init_vo_spudec(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
|
|
|
|
current_module = "read_subtitles_file";
|
|
|
|
double sub_fps = mpctx->sh_video ? mpctx->sh_video->fps : 25;
|
|
|
|
if (opts->sub_name) {
|
|
|
|
for (i = 0; opts->sub_name[i] != NULL; ++i)
|
|
|
|
add_subtitles(mpctx, opts->sub_name[i], sub_fps, 0);
|
|
|
|
}
|
|
|
|
if (opts->sub_auto) { // auto load sub file ...
|
|
|
|
char **tmp = find_text_subtitles(opts, mpctx->filename);
|
|
|
|
int nsub = MP_TALLOC_ELEMS(tmp);
|
|
|
|
for (int i = 0; i < nsub; i++)
|
|
|
|
add_subtitles(mpctx, tmp[i], sub_fps, 1);
|
|
|
|
talloc_free(tmp);
|
|
|
|
}
|
|
|
|
if (mpctx->set_of_sub_size > 0)
|
|
|
|
mpctx->sub_counts[SUB_SOURCE_SUBS] = mpctx->set_of_sub_size;
|
2002-05-18 01:47:27 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (select_subtitle(mpctx)) {
|
|
|
|
if (mpctx->subdata)
|
|
|
|
switch (stream_dump_type) {
|
|
|
|
case 3: list_sub_file(mpctx->subdata);
|
|
|
|
break;
|
|
|
|
case 4: dump_mpsub(mpctx->subdata, mpctx->sh_video->fps);
|
|
|
|
break;
|
|
|
|
case 6: dump_srt(mpctx->subdata, mpctx->sh_video->fps);
|
|
|
|
break;
|
|
|
|
case 7: dump_microdvd(mpctx->subdata, mpctx->sh_video->fps);
|
|
|
|
break;
|
|
|
|
case 8: dump_jacosub(mpctx->subdata, mpctx->sh_video->fps);
|
|
|
|
break;
|
|
|
|
case 9: dump_sami(mpctx->subdata, mpctx->sh_video->fps);
|
|
|
|
break;
|
|
|
|
}
|
2006-03-24 20:32:23 +01:00
|
|
|
}
|
2004-09-28 15:51:20 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
print_file_properties(mpctx, mpctx->filename);
|
2002-10-23 17:48:56 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
reinit_video_chain(mpctx);
|
|
|
|
if (mpctx->sh_video) {
|
|
|
|
if (mpctx->sh_video->output_flags & VFCAP_SPU && vo_spudec)
|
|
|
|
spudec_set_hw_spu(vo_spudec, mpctx->video_out);
|
2008-08-07 12:36:07 +02:00
|
|
|
#ifdef CONFIG_FREETYPE
|
2011-08-07 02:58:12 +02:00
|
|
|
force_load_font = 1;
|
2002-08-28 22:52:02 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
} else if (!mpctx->sh_audio)
|
|
|
|
goto goto_next_file;
|
2002-08-28 22:52:02 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
//================== MAIN: ==========================
|
|
|
|
current_module = "main";
|
2002-09-26 01:45:34 +02:00
|
|
|
|
2010-11-13 22:10:58 +01:00
|
|
|
if (opts->playing_msg) {
|
2011-08-07 02:58:12 +02:00
|
|
|
char *msg = property_expand_string(mpctx, opts->playing_msg);
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
|
2006-03-22 01:19:02 +01:00
|
|
|
free(msg);
|
|
|
|
}
|
2009-07-07 01:26:13 +02:00
|
|
|
|
2006-03-22 01:19:02 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// Disable the term OSD in verbose mode
|
|
|
|
if (verbose)
|
|
|
|
opts->term_osd = 0;
|
2002-03-17 01:47:15 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// Make sure old OSD does not stay around,
|
|
|
|
// e.g. with -fixed-vo and same-resolution files
|
|
|
|
clear_osd_msgs();
|
|
|
|
update_osd_msg(mpctx);
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
//================ SETUP AUDIO ==========================
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
reinit_audio_chain(mpctx);
|
|
|
|
if (mpctx->sh_audio && mpctx->sh_audio->codec)
|
|
|
|
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
|
|
|
"ID_AUDIO_CODEC=%s\n", mpctx->sh_audio->codec->name);
|
|
|
|
}
|
2002-05-25 13:40:29 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
current_module = "av_init";
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->sh_video) {
|
|
|
|
mpctx->sh_video->timer = 0;
|
|
|
|
if (!ignore_start)
|
|
|
|
audio_delay += mpctx->sh_video->stream_delay;
|
|
|
|
}
|
|
|
|
if (mpctx->sh_audio) {
|
|
|
|
if (start_volume >= 0)
|
|
|
|
mixer_setvolume(&mpctx->mixer, start_volume, start_volume);
|
|
|
|
if (!ignore_start)
|
|
|
|
audio_delay -= mpctx->sh_audio->stream_delay;
|
|
|
|
mpctx->delay = -audio_delay;
|
|
|
|
}
|
2007-06-07 20:06:53 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->sh_audio) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Audio: no sound\n");
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Freeing %d unused audio chunks.\n",
|
|
|
|
mpctx->d_audio->packs);
|
|
|
|
ds_free_packs(mpctx->d_audio); // free buffered chunks
|
|
|
|
}
|
|
|
|
if (!mpctx->sh_video) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Video: no video\n");
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_V, "Freeing %d unused video chunks.\n",
|
|
|
|
mpctx->d_video->packs);
|
|
|
|
ds_free_packs(mpctx->d_video);
|
|
|
|
mpctx->d_video->id = -2;
|
|
|
|
}
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->sh_video && !mpctx->sh_audio)
|
|
|
|
goto goto_next_file;
|
2003-01-26 17:11:08 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (force_fps && mpctx->sh_video) {
|
|
|
|
vo_fps = mpctx->sh_video->fps = force_fps;
|
|
|
|
mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
|
|
|
|
"FPS forced to be %5.3f (ftime: %5.3f).\n",
|
|
|
|
mpctx->sh_video->fps, mpctx->sh_video->frametime);
|
|
|
|
}
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_input_set_section(mpctx->input, NULL);
|
|
|
|
//TODO: add desired (stream-based) sections here
|
|
|
|
if (mpctx->stream->type == STREAMTYPE_TV)
|
|
|
|
mp_input_set_section(mpctx->input, "tv");
|
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVDNAV)
|
|
|
|
mp_input_set_section(mpctx->input, "dvdnav");
|
|
|
|
|
|
|
|
//==================== START PLAYING =======================
|
|
|
|
|
|
|
|
if (opts->loop_times > 1)
|
|
|
|
opts->loop_times--;
|
|
|
|
else if (opts->loop_times == 1)
|
|
|
|
opts->loop_times = -1;
|
|
|
|
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Starting playback...\n");
|
|
|
|
|
|
|
|
total_time_usage_start = GetTimer();
|
|
|
|
audio_time_usage = 0;
|
|
|
|
video_time_usage = 0;
|
|
|
|
vout_time_usage = 0;
|
|
|
|
total_frame_cnt = 0;
|
|
|
|
drop_frame_cnt = 0; // fix for multifile fps benchmark
|
|
|
|
play_n_frames = play_n_frames_mf;
|
|
|
|
|
|
|
|
if (play_n_frames == 0) {
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
|
|
|
goto goto_next_file;
|
|
|
|
}
|
2002-06-28 19:13:18 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->time_frame = 0;
|
|
|
|
mpctx->drop_message_shown = 0;
|
|
|
|
mpctx->restart_playback = true;
|
|
|
|
mpctx->video_pts = 0;
|
|
|
|
mpctx->last_seek_pts = 0;
|
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
mpctx->hrseek_framedrop = false;
|
|
|
|
mpctx->step_frames = 0;
|
|
|
|
mpctx->total_avsync_change = 0;
|
|
|
|
mpctx->last_chapter_seek = -2;
|
2002-06-28 19:13:18 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// If there's a timeline force an absolute seek to initialize state
|
|
|
|
if (opts->seek_to_sec || mpctx->timeline) {
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->seek_to_sec, 0);
|
2011-04-08 04:15:43 +02:00
|
|
|
seek(mpctx, mpctx->seek, false);
|
2011-08-07 02:58:12 +02:00
|
|
|
end_at.pos += opts->seek_to_sec;
|
|
|
|
}
|
|
|
|
if (opts->chapterrange[0] > 0) {
|
|
|
|
double pts;
|
2011-10-23 04:51:44 +02:00
|
|
|
if (seek_chapter(mpctx, opts->chapterrange[0] - 1, &pts) >= 0
|
2011-08-07 02:58:12 +02:00
|
|
|
&& pts > -1.0) {
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0);
|
|
|
|
seek(mpctx, mpctx->seek, false);
|
|
|
|
}
|
2010-12-18 09:13:45 +01:00
|
|
|
}
|
2007-02-27 02:16:59 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (end_at.type == END_AT_SIZE) {
|
|
|
|
mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
|
|
|
|
"Option -endpos in MPlayer does not yet support size units.\n");
|
|
|
|
end_at.type = END_AT_NONE;
|
|
|
|
}
|
2007-02-27 02:16:59 +01:00
|
|
|
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_DVDNAV
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_dvdnav_context_free(mpctx);
|
|
|
|
if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
|
|
|
|
mp_dvdnav_read_wait(mpctx->stream, 0, 1);
|
|
|
|
mp_dvdnav_cell_has_changed(mpctx->stream, 1);
|
|
|
|
}
|
2008-01-26 12:51:34 +01:00
|
|
|
#endif
|
2007-02-27 02:16:59 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->seek = (struct seek_params){ 0 };
|
|
|
|
get_relative_time(mpctx); // reset current delta
|
|
|
|
// Make sure VO knows current pause state
|
|
|
|
if (mpctx->sh_video)
|
|
|
|
vo_control(mpctx->video_out,
|
|
|
|
mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
|
2008-04-28 11:09:31 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
while (!mpctx->stop_play)
|
|
|
|
run_playloop(mpctx);
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_GLOBAL, MSGL_V, "EOF code: %d \n", mpctx->stop_play);
|
2001-07-29 23:07:34 +02:00
|
|
|
|
2008-08-03 17:21:40 +02:00
|
|
|
#ifdef CONFIG_DVBIN
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->dvbin_reopen) {
|
|
|
|
mpctx->stop_play = 0;
|
|
|
|
uninit_player(mpctx, INITIALIZED_ALL - (INITIALIZED_STREAM | INITIALIZED_GETCH2 | (opts->fixed_vo ? INITIALIZED_VO : 0)));
|
|
|
|
cache_uninit(mpctx->stream);
|
|
|
|
mpctx->dvbin_reopen = 0;
|
|
|
|
goto goto_enable_cache;
|
|
|
|
}
|
2006-04-25 22:32:46 +02:00
|
|
|
#endif
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2001-12-19 17:55:32 +01:00
|
|
|
goto_next_file: // don't jump here after ao/vo/getch initialization!
|
2001-08-23 01:48:18 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
|
|
|
|
|
|
|
|
if (opts->benchmark) {
|
|
|
|
double tot = video_time_usage + vout_time_usage + audio_time_usage;
|
|
|
|
double total_time_usage;
|
|
|
|
total_time_usage_start = GetTimer() - total_time_usage_start;
|
|
|
|
total_time_usage = (float)total_time_usage_start * 0.000001;
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\nBENCHMARKs: VC:%8.3fs VO:%8.3fs "
|
|
|
|
"A:%8.3fs Sys:%8.3fs = %8.3fs\n",
|
|
|
|
video_time_usage, vout_time_usage, audio_time_usage,
|
|
|
|
total_time_usage - tot, total_time_usage);
|
|
|
|
if (total_time_usage > 0.0)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "BENCHMARK%%: VC:%8.4f%% "
|
|
|
|
"VO:%8.4f%% A:%8.4f%% Sys:%8.4f%% = %8.4f%%\n",
|
|
|
|
100.0 * video_time_usage / total_time_usage,
|
|
|
|
100.0 * vout_time_usage / total_time_usage,
|
|
|
|
100.0 * audio_time_usage / total_time_usage,
|
|
|
|
100.0 * (total_time_usage - tot) / total_time_usage,
|
|
|
|
100.0);
|
|
|
|
if (total_frame_cnt && frame_dropping)
|
|
|
|
mp_msg(MSGT_CPLAYER, MSGL_INFO, "BENCHMARKn: disp: %d (%3.2f fps)"
|
|
|
|
" drop: %d (%d%%) total: %d (%3.2f fps)\n",
|
|
|
|
total_frame_cnt - drop_frame_cnt,
|
|
|
|
(total_time_usage > 0.5) ? ((total_frame_cnt -
|
|
|
|
drop_frame_cnt) / total_time_usage) : 0,
|
|
|
|
drop_frame_cnt,
|
|
|
|
100 * drop_frame_cnt / total_frame_cnt,
|
|
|
|
total_frame_cnt,
|
|
|
|
(total_time_usage > 0.5) ?
|
|
|
|
(total_frame_cnt / total_time_usage) : 0);
|
|
|
|
}
|
2002-01-17 21:25:55 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
// time to uninit all, except global stuff:
|
|
|
|
int uninitialize_parts = INITIALIZED_ALL;
|
|
|
|
if (opts->fixed_vo)
|
|
|
|
uninitialize_parts -= INITIALIZED_VO;
|
|
|
|
if (opts->gapless_audio && mpctx->stop_play == AT_END_OF_FILE)
|
|
|
|
uninitialize_parts -= INITIALIZED_AO;
|
|
|
|
uninit_player(mpctx, uninitialize_parts);
|
|
|
|
|
|
|
|
if (mpctx->set_of_sub_size > 0) {
|
|
|
|
current_module = "sub_free";
|
|
|
|
for (i = 0; i < mpctx->set_of_sub_size; ++i) {
|
|
|
|
sub_free(mpctx->set_of_subtitles[i]);
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->set_of_ass_tracks[i])
|
|
|
|
ass_free_track(mpctx->set_of_ass_tracks[i]);
|
2006-07-07 20:26:51 +02:00
|
|
|
#endif
|
2011-08-07 02:58:12 +02:00
|
|
|
}
|
|
|
|
mpctx->set_of_sub_size = 0;
|
2006-07-07 20:26:51 +02:00
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->vo_sub_last = vo_sub = NULL;
|
|
|
|
mpctx->subdata = NULL;
|
2008-07-30 14:01:30 +02:00
|
|
|
#ifdef CONFIG_ASS
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->osd->ass_track = NULL;
|
|
|
|
if (mpctx->ass_library)
|
|
|
|
ass_clear_fonts(mpctx->ass_library);
|
2006-07-07 20:26:51 +02:00
|
|
|
#endif
|
2003-01-20 23:49:50 +01:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (!mpctx->stop_play) // In case some goto jumped here...
|
|
|
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
2008-08-13 07:06:26 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
int playtree_direction = 1;
|
2008-08-13 07:06:26 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->stop_play == PT_NEXT_ENTRY
|
|
|
|
|| mpctx->stop_play == PT_PREV_ENTRY) {
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, mpctx->play_tree_step, 0)
|
|
|
|
!= PLAY_TREE_ITER_ENTRY) {
|
2008-03-07 18:16:10 +01:00
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
}
|
2011-08-07 02:58:12 +02:00
|
|
|
mpctx->play_tree_step = 1;
|
|
|
|
} else if (mpctx->stop_play == PT_UP_NEXT ||
|
|
|
|
mpctx->stop_play == PT_UP_PREV) {
|
|
|
|
int direction = mpctx->stop_play == PT_UP_NEXT ? 1 : -1;
|
|
|
|
if (mpctx->playtree_iter) {
|
|
|
|
if (play_tree_iter_up_step(mpctx->playtree_iter, direction, 0) !=
|
|
|
|
PLAY_TREE_ITER_ENTRY) {
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (mpctx->stop_play == PT_STOP) {
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
} else // NEXT PREV SRC
|
|
|
|
playtree_direction = mpctx->stop_play == PT_PREV_SRC ? -1 : 1;
|
|
|
|
|
|
|
|
while (mpctx->playtree_iter != NULL) {
|
|
|
|
mpctx->filename = play_tree_iter_get_file(mpctx->playtree_iter,
|
|
|
|
playtree_direction);
|
|
|
|
if (mpctx->filename == NULL) {
|
|
|
|
if (play_tree_iter_step(mpctx->playtree_iter, playtree_direction,
|
|
|
|
0) != PLAY_TREE_ITER_ENTRY) {
|
|
|
|
play_tree_iter_free(mpctx->playtree_iter);
|
|
|
|
mpctx->playtree_iter = NULL;
|
|
|
|
}
|
|
|
|
;
|
|
|
|
} else
|
|
|
|
break;
|
2003-02-09 14:17:12 +01:00
|
|
|
}
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
if (mpctx->playtree_iter != NULL || opts->player_idle_mode) {
|
|
|
|
if (!mpctx->playtree_iter)
|
|
|
|
mpctx->filename = NULL;
|
|
|
|
mpctx->stop_play = 0;
|
|
|
|
goto play_next_file;
|
|
|
|
}
|
2002-08-28 22:52:02 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
exit_player_with_rc(mpctx, EXIT_EOF, 0);
|
2001-08-22 23:35:44 +02:00
|
|
|
|
2011-08-07 02:58:12 +02:00
|
|
|
return 1;
|
2001-03-15 20:38:34 +01:00
|
|
|
}
|
2008-06-06 15:28:25 +02:00
|
|
|
#endif /* DISABLE_MAIN */
|