diff options
Diffstat (limited to 'mplayer.c')
-rw-r--r-- | mplayer.c | 2598 |
1 files changed, 1490 insertions, 1108 deletions
@@ -21,7 +21,9 @@ #include <stdio.h> #include <stdlib.h> +#include <stdbool.h> #include "config.h" +#include "talloc.h" #if defined(__MINGW32__) || defined(__CYGWIN__) #define _UWIN 1 /*disable Non-underscored versions of non-ANSI functions as otherwise int eof would conflict with eof()*/ @@ -54,21 +56,23 @@ #include <errno.h> #include "mp_msg.h" +#include "av_log.h" -#define HELP_MP_DEFINE_STATIC -#include "help_mp.h" #include "m_option.h" #include "m_config.h" +#include "mplayer.h" +#include "access_mpcontext.h" #include "m_property.h" #include "cfg-mplayer-def.h" -#include "libavutil/intreadwrite.h" +#include "ffmpeg_files/intreadwrite.h" #include "libavutil/avstring.h" #include "subreader.h" +#include "mp_osd.h" #include "libvo/video_out.h" #include "libvo/font_load.h" @@ -83,18 +87,17 @@ #include "codec-cfg.h" #include "edl.h" -#include "mplayer.h" + #include "spudec.h" #include "vobsub.h" -#include "access_mpcontext.h" #include "osdep/getch2.h" #include "osdep/timer.h" - -#include "gui/interface.h" +#include "osdep/findfiles.h" #include "input/input.h" +const int under_mencoder = 0; int slave_mode=0; int player_idle_mode=0; int quiet=0; @@ -136,18 +139,12 @@ char *heartbeat_cmd; #include "parser-cfg.h" #include "parser-mpcmd.h" -m_config_t* mconfig; - //**************************************************************************// // Config file //**************************************************************************// static int cfg_inc_verbose(m_option_t *conf){ ++verbose; return 0;} -static int cfg_include(m_option_t *conf, char *filename){ - return m_config_parse_config_file(mconfig, filename); -} - #include "path.h" //**************************************************************************// @@ -175,6 +172,95 @@ static int max_framesize=0; #include "mixer.h" #include "mp_core.h" +#include "options.h" +#include "defaultopts.h" + +static const char help_text[]=_( +"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" +"\n" +" * * * SEE THE MAN PAGE FOR DETAILS, FURTHER (ADVANCED) OPTIONS AND KEYS * * *\n" +"\n"); + + +#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") + //**************************************************************************// //**************************************************************************// @@ -184,24 +270,6 @@ static int max_framesize=0; int noconsolecontrols=0; //**************************************************************************// -// Not all functions in mplayer.c take the context as an argument yet -static MPContext mpctx_s = { - .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, - .loop_times = -1, -#ifdef CONFIG_DVBIN - .last_dvb_step = 1, -#endif -}; - -static MPContext *mpctx = &mpctx_s; - -int fixed_vo=0; - // benchmark: double video_time_usage=0; double vout_time_usage=0; @@ -212,25 +280,11 @@ static int drop_frame_cnt=0; // total number of dropped frames int benchmark=0; // options: -#define DEFAULT_STARTUP_DECODE_RETRY 8 int auto_quality=0; static int output_quality=0; -float playback_speed=1.0; - -int use_gui=0; - -#ifdef CONFIG_GUI -int enqueue=0; -#endif - static int list_properties = 0; -int osd_level=1; -// if nonzero, hide current OSD contents when GetTimerMS() reaches this -unsigned int osd_visible; -int osd_duration = 1000; - int term_osd = 1; static char* term_osd_esc = "\x1b[A\r\x1b[K"; static char* playing_msg = NULL; @@ -238,17 +292,12 @@ static char* playing_msg = NULL; static double seek_to_sec; static off_t seek_to_byte=0; static off_t step_sec=0; -static int loop_seek=0; static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 }; // A/V sync: int autosync=0; // 30 might be a good default value. -// may be changed by GUI: (FIXME!) -float rel_seek_secs=0; -int abs_seek_pos=0; - // codecs: char **audio_codec_list=NULL; // override audio codec char **video_codec_list=NULL; // override video codec @@ -260,10 +309,6 @@ extern char *demuxer_name; // override demuxer extern char *audio_demuxer_name; // override audio demuxer extern char *sub_demuxer_name; // override sub demuxer -// streaming: -int audio_id=-1; -int video_id=-1; -int dvdsub_id=-1; // 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 @@ -272,7 +317,6 @@ int vobsub_id=-1; char* audio_lang=NULL; char* dvdsub_lang=NULL; static char* spudec_ifo=NULL; -char* filename=NULL; //"MI2-Trailer.avi"; int forced_subs_only=0; int file_filter=1; @@ -293,8 +337,6 @@ static char *stream_dump_name="stream.dump"; // A-V sync: static float default_max_pts_correction=-1;//0.01f; -static float max_pts_correction=0;//default_max_pts_correction; -static float c_total=0; float audio_delay=0; static int ignore_start=0; @@ -307,10 +349,6 @@ static int audio_output_format=-1; // AF_FORMAT_UNKNOWN static int play_n_frames=-1; static int play_n_frames_mf=-1; -// screen info: -char** video_driver_list=NULL; -char** audio_driver_list=NULL; - // sub: char *font_name=NULL; char *sub_font_name=NULL; @@ -325,7 +363,7 @@ char *vobsub_name=NULL; int subcc_enabled=0; int suboverlap_enabled = 1; -#include "libass/ass_mp.h" +#include "ass_mp.h" char* current_module=NULL; // for debugging @@ -336,7 +374,7 @@ char* current_module=NULL; // for debugging #include "m_struct.h" #include "libmenu/menu.h" extern vf_info_t vf_info_menu; -static vf_info_t* libmenu_vfs[] = { +static const vf_info_t* const libmenu_vfs[] = { &vf_info_menu, NULL }; @@ -354,12 +392,10 @@ static char* rtc_device; edl_record_ptr edl_records = NULL; ///< EDL entries memory area edl_record_ptr next_edl_record = NULL; ///< only for traversing edl_records -short edl_decision = 0; ///< 1 when an EDL operation has been made. FILE* edl_fd = NULL; ///< fd to write to when in -edlout mode. int use_filedir_conf; int use_filename_title; -static unsigned int initialized_flags=0; #include "mpcommon.h" #include "command.h" @@ -402,7 +438,15 @@ int mpctx_get_osd_function(MPContext *mpctx) return mpctx->osd_function; } -static int is_valid_metadata_type (metadata_t type) { +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; +} + +static int is_valid_metadata_type(struct MPContext *mpctx, metadata_t type) { switch (type) { /* check for valid video stream */ @@ -446,7 +490,7 @@ static int is_valid_metadata_type (metadata_t type) { return 1; } -static char *get_demuxer_info (char *tag) { +static char *get_demuxer_info(struct MPContext *mpctx, char *tag) { char **info = mpctx->demuxer->info; int n; @@ -460,19 +504,20 @@ static char *get_demuxer_info (char *tag) { return info[2*n+1] ? strdup (info[2*n+1]) : NULL; } -char *get_metadata (metadata_t type) { +char *get_metadata(struct MPContext *mpctx, metadata_t type) +{ char *meta = NULL; sh_audio_t * const sh_audio = mpctx->sh_audio; sh_video_t * const sh_video = mpctx->sh_video; - if (!is_valid_metadata_type (type)) + if (!is_valid_metadata_type(mpctx, type)) return NULL; switch (type) { case META_NAME: { - return strdup (mp_basename2 (filename)); + return strdup (mp_basename2 (mpctx->filename)); } case META_VIDEO_CODEC: @@ -535,25 +580,25 @@ char *get_metadata (metadata_t type) { /* check for valid demuxer */ case META_INFO_TITLE: - return get_demuxer_info ("Title"); + return get_demuxer_info(mpctx, "Title"); case META_INFO_ARTIST: - return get_demuxer_info ("Artist"); + return get_demuxer_info(mpctx, "Artist"); case META_INFO_ALBUM: - return get_demuxer_info ("Album"); + return get_demuxer_info(mpctx, "Album"); case META_INFO_YEAR: - return get_demuxer_info ("Year"); + return get_demuxer_info(mpctx, "Year"); case META_INFO_COMMENT: - return get_demuxer_info ("Comment"); + return get_demuxer_info(mpctx, "Comment"); case META_INFO_TRACK: - return get_demuxer_info ("Track"); + return get_demuxer_info(mpctx, "Track"); case META_INFO_GENRE: - return get_demuxer_info ("Genre"); + return get_demuxer_info(mpctx, "Genre"); default: break; @@ -576,24 +621,21 @@ static void mp_dvdnav_context_free(MPContext *ctx){ } #endif -void uninit_player(unsigned int mask){ - mask &= initialized_flags; +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){ - initialized_flags&=~INITIALIZED_ACODEC; + mpctx->initialized_flags&=~INITIALIZED_ACODEC; current_module="uninit_acodec"; if(mpctx->sh_audio) uninit_audio(mpctx->sh_audio); -#ifdef CONFIG_GUI - if (use_gui) guiGetEvent(guiSetAfilter, (char *)NULL); -#endif mpctx->sh_audio=NULL; mpctx->mixer.afilter = NULL; } if(mask&INITIALIZED_VCODEC){ - initialized_flags&=~INITIALIZED_VCODEC; + mpctx->initialized_flags&=~INITIALIZED_VCODEC; current_module="uninit_vcodec"; if(mpctx->sh_video) uninit_video(mpctx->sh_video); mpctx->sh_video=NULL; @@ -603,8 +645,25 @@ void uninit_player(unsigned int mask){ } if(mask&INITIALIZED_DEMUXER){ - initialized_flags&=~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++) { + free_stream(mpctx->sources[i].stream); + free_demuxer(mpctx->sources[i].demuxer); + } + } + 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; free_demuxer(mpctx->demuxer); @@ -614,16 +673,16 @@ void uninit_player(unsigned int mask){ // kill the cache process: if(mask&INITIALIZED_STREAM){ - initialized_flags&=~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){ - initialized_flags&=~INITIALIZED_VO; + mpctx->initialized_flags&=~INITIALIZED_VO; current_module="uninit_vo"; - mpctx->video_out->uninit(); + vo_destroy(mpctx->video_out); mpctx->video_out=NULL; #ifdef CONFIG_DVDNAV mp_dvdnav_context_free(mpctx); @@ -632,7 +691,7 @@ void uninit_player(unsigned int mask){ // Must be after libvo uninit, as few vo drivers (svgalib) have tty code. if(mask&INITIALIZED_GETCH2){ - initialized_flags&=~INITIALIZED_GETCH2; + mpctx->initialized_flags&=~INITIALIZED_GETCH2; current_module="uninit_getch2"; mp_msg(MSGT_CPLAYER,MSGL_DBG2,"\n[[[uninit getch2]]]\n"); // restore terminal: @@ -640,72 +699,58 @@ void uninit_player(unsigned int mask){ } if(mask&INITIALIZED_VOBSUB){ - initialized_flags&=~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){ - initialized_flags&=~INITIALIZED_SPUDEC; + mpctx->initialized_flags&=~INITIALIZED_SPUDEC; current_module="uninit_spudec"; spudec_free(vo_spudec); vo_spudec=NULL; } if(mask&INITIALIZED_AO){ - initialized_flags&=~INITIALIZED_AO; + mpctx->initialized_flags&=~INITIALIZED_AO; current_module="uninit_ao"; if (mpctx->edl_muted) mixer_mute(&mpctx->mixer); - if (mpctx->audio_out) mpctx->audio_out->uninit(mpctx->eof?0:1); + if (mpctx->audio_out) + mpctx->audio_out->uninit(mpctx->stop_play != AT_END_OF_FILE); mpctx->audio_out=NULL; } -#ifdef CONFIG_GUI - if(mask&INITIALIZED_GUI){ - initialized_flags&=~INITIALIZED_GUI; - current_module="uninit_gui"; - guiDone(); - } -#endif - - if(mask&INITIALIZED_INPUT){ - initialized_flags&=~INITIALIZED_INPUT; - current_module="uninit_input"; - mp_input_uninit(); -#ifdef CONFIG_MENU - if (use_menu) - menu_uninit(); -#endif - } - current_module=NULL; } -void exit_player_with_rc(enum exit_reason how, int rc) +void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc) { - if (mpctx->user_muted && !mpctx->edl_muted) mixer_mute(&mpctx->mixer); - uninit_player(INITIALIZED_ALL); + uninit_player(mpctx, INITIALIZED_ALL); #if defined(__MINGW32__) || defined(__CYGWIN__) timeEndPeriod(1); #endif #ifdef CONFIG_X11 -#ifdef CONFIG_GUI - if ( !use_gui ) + vo_uninit(mpctx->x11_state); // Close the X11 connection (if any is open). #endif - vo_uninit(); // Close the X11 connection (if any is open). + + current_module="uninit_input"; + mp_input_uninit(mpctx->input); +#ifdef CONFIG_MENU + if (use_menu) + menu_uninit(); #endif #ifdef CONFIG_FREETYPE current_module="uninit_font"; - if (sub_font && sub_font != vo_font) free_font_desc(sub_font); - sub_font = NULL; - if (vo_font) free_font_desc(vo_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(); #endif - free_osd_list(); + osd_free(mpctx->osd); #ifdef CONFIG_ASS ass_library_done(ass_library); @@ -715,9 +760,9 @@ void exit_player_with_rc(enum exit_reason how, int rc) current_module="exit_player"; // free mplayer config - if(mconfig) - m_config_free(mconfig); - mconfig = NULL; + if(mpctx->mconfig) + m_config_free(mpctx->mconfig); + mpctx->mconfig = NULL; if(mpctx->playtree_iter) play_tree_iter_free(mpctx->playtree_iter); @@ -726,20 +771,21 @@ void exit_player_with_rc(enum exit_reason how, int rc) play_tree_free(mpctx->playtree, 1); mpctx->playtree = NULL; + talloc_free(mpctx->key_fifo); if(edl_records != NULL) free(edl_records); // free mem allocated for EDL edl_records = NULL; switch(how) { case EXIT_QUIT: - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_ExitingHow,MSGTR_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_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_ExitingHow,MSGTR_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_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_ExitingHow,MSGTR_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: @@ -750,9 +796,9 @@ void exit_player_with_rc(enum exit_reason how, int rc) exit(rc); } -void exit_player(enum exit_reason how) +static void exit_player(struct MPContext *mpctx, enum exit_reason how) { - exit_player_with_rc(how, 1); + exit_player_with_rc(mpctx, how, 1); } #ifndef __MINGW32__ @@ -773,7 +819,6 @@ static void exit_sighandler(int x){ if (!crash_debug || x != SIGTRAP) #endif ++sig_count; - if(initialized_flags==0 && sig_count>1) exit(1); if(sig_count==5) { /* We're crashing bad and can't uninit cleanly :( @@ -790,9 +835,10 @@ static void exit_sighandler(int x){ kill(getpid(),SIGKILL); #endif } - mp_msg(MSGT_CPLAYER,MSGL_FATAL,"\n" MSGTR_IntBySignal,x, - current_module?current_module:"unknown" - ); + 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){ @@ -804,15 +850,15 @@ static void exit_sighandler(int x){ return; // killed from keyboard (^C) or killed [-9] case SIGILL: #if CONFIG_RUNTIME_CPUDETECT - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_Exit_SIGILL_RTCpuSel); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,Exit_SIGILL_RTCpuSel); #else - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_Exit_SIGILL); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,Exit_SIGILL); #endif case SIGFPE: case SIGSEGV: - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_Exit_SIGSEGV_SIGFPE); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,Exit_SIGSEGV_SIGFPE); default: - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_Exit_SIGCRASH); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,Exit_SIGCRASH); #ifdef CONFIG_CRASH_DEBUG if (crash_debug) { int gdb_pid; @@ -840,15 +886,20 @@ static void exit_sighandler(int x){ #include "cfg-mplayer.h" -static void parse_cfgfiles( m_config_t* conf ) +static int cfg_include(m_option_t *conf, char *filename) +{ + return m_config_parse_config_file(conf->priv, filename); +} + +static void parse_cfgfiles(struct MPContext *mpctx, m_config_t* conf) { char *conffile; int conffile_fd; if (!disable_system_conf && m_config_parse_config_file(conf, MPLAYER_CONFDIR "/mplayer.conf") < 0) - exit_player(EXIT_NONE); + exit_player(mpctx, EXIT_NONE); if ((conffile = get_path("")) == NULL) { - mp_msg(MSGT_CPLAYER,MSGL_WARN,MSGTR_NoHomeDir); + mp_tmsg(MSGT_CPLAYER,MSGL_WARN,"Cannot find HOME directory.\n"); } else { #ifdef __MINGW32__ mkdir(conffile); @@ -857,16 +908,16 @@ if ((conffile = get_path("")) == NULL) { #endif free(conffile); if ((conffile = get_path("config")) == NULL) { - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_GetpathProblem); + 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_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_CreatingCfgFile, conffile); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Creating config file: %s\n", conffile); write(conffile_fd, default_config, strlen(default_config)); close(conffile_fd); } if (!disable_user_conf && m_config_parse_config_file(conf, conffile) < 0) - exit_player(EXIT_NONE); + exit_player(mpctx, EXIT_NONE); free(conffile); } } @@ -890,7 +941,7 @@ static void load_per_protocol_config (m_config_t* conf, const char *const file) p = m_config_get_profile (conf, protocol); if (p) { - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_LoadingProtocolProfile, protocol); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Loading protocol-related profile '%s'\n", protocol); m_config_set_profile(conf,p); } } @@ -904,7 +955,7 @@ static void load_per_extension_config (m_config_t* conf, const char *const file) m_profile_t *p; /* does filename actually have an extension ? */ - str = strrchr (filename, '.'); + str = strrchr (file, '.'); if (!str) return; @@ -913,7 +964,7 @@ static void load_per_extension_config (m_config_t* conf, const char *const file) p = m_config_get_profile (conf, extension); if (p) { - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_LoadingExtensionProfile, extension); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Loading extension-related profile '%s'\n", extension); m_config_set_profile(conf,p); } } @@ -930,7 +981,7 @@ static void load_per_output_config (m_config_t* conf, char *cfg, char *out) p = m_config_get_profile (conf, profile); if (p) { - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_LoadingExtensionProfile, profile); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Loading extension-related profile '%s'\n", profile); m_config_set_profile(conf,p); } } @@ -944,7 +995,7 @@ static int try_load_config(m_config_t *conf, const char *file) struct stat st; if (stat(file, &st)) return 0; - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_LoadingConfig, file); + mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Loading config '%s'\n", file); m_config_parse_config_file (conf, file); return 1; } @@ -997,42 +1048,35 @@ static void load_per_file_config (m_config_t* conf, const char *const file) * 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. */ -static int libmpdemux_was_interrupted(int eof) { +static int libmpdemux_was_interrupted(struct MPContext *mpctx, int stop_play) +{ mp_cmd_t* cmd; - if((cmd = mp_input_get_cmd(0,0,0)) != NULL) { + if((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { switch(cmd->id) { case MP_CMD_QUIT: - exit_player_with_rc(EXIT_QUIT, (cmd->nargs > 0)? cmd->args[0].v.i : 0); + exit_player_with_rc(mpctx, EXIT_QUIT, (cmd->nargs > 0)? cmd->args[0].v.i : 0); case MP_CMD_PLAY_TREE_STEP: { - eof = (cmd->args[0].v.i > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY; + 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: { - eof = (cmd->args[0].v.i > 0) ? PT_UP_NEXT : PT_UP_PREV; + stop_play = (cmd->args[0].v.i > 0) ? PT_UP_NEXT : PT_UP_PREV; } break; case MP_CMD_PLAY_ALT_SRC_STEP: { - eof = (cmd->args[0].v.i > 0) ? PT_NEXT_SRC : PT_PREV_SRC; + stop_play = (cmd->args[0].v.i > 0) ? PT_NEXT_SRC : PT_PREV_SRC; } break; } mp_cmd_free(cmd); } - return eof; + return stop_play; } #define mp_basename(s) (strrchr(s,'\\')==NULL?(mp_basename2(s)):(strrchr(s,'\\')+1)) -static int playtree_add_playlist(play_tree_t* entry) +static int playtree_add_playlist(struct MPContext *mpctx, play_tree_t* entry) { - play_tree_add_bpf(entry,filename); + play_tree_add_bpf(entry,mpctx->filename); -#ifdef CONFIG_GUI - if (use_gui) { - if (entry) { - import_playtree_playlist_into_gui(entry, mconfig); - play_tree_free_list(entry,1); - } - } else -#endif { if(!entry) { entry = mpctx->playtree_iter->tree; @@ -1058,76 +1102,59 @@ static int playtree_add_playlist(play_tree_t* entry) return PT_NEXT_SRC; } -void add_subtitles(char *filename, float fps, int noerr) +void add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr) { - sub_data *subd; -#ifdef CONFIG_ASS - ass_track_t *asst = 0; -#endif + struct MPOpts *opts = &mpctx->opts; + sub_data *subd = NULL; + struct ass_track *asst = NULL; if (filename == NULL || mpctx->set_of_sub_size >= MAX_SUBTITLE_FILES) { return; } - subd = sub_read_file(filename, fps); #ifdef CONFIG_ASS - if (ass_enabled) + if (opts->ass_enabled) { #ifdef CONFIG_ICONV asst = ass_read_stream(ass_library, filename, sub_cp); #else asst = ass_read_stream(ass_library, filename, 0); #endif - if (ass_enabled && subd && !asst) - asst = ass_read_subdata(ass_library, subd, fps); - - if (!asst && !subd) -#else - if(!subd) + if (!asst) { + subd = sub_read_file(filename, fps); + if (subd) { + asst = ass_read_subdata(ass_library, subd, fps); + if (asst) { + sub_free(subd); + subd = NULL; + } + } + } + } else #endif - mp_msg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR, MSGTR_CantLoadSub, - filename_recode(filename)); + subd = sub_read_file(filename, fps); + + + if (!asst && !subd) { + mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR, + "Cannot load subtitles: %s\n", filename_recode(filename)); + return; + } -#ifdef CONFIG_ASS - if (!asst && !subd) return; mpctx->set_of_ass_tracks[mpctx->set_of_sub_size] = asst; -#else - if (!subd) return; -#endif mpctx->set_of_subtitles[mpctx->set_of_sub_size] = subd; mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILE_SUB_ID=%d\n", mpctx->set_of_sub_size); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILE_SUB_FILENAME=%s\n", filename_recode(filename)); ++mpctx->set_of_sub_size; - mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_AddedSubtitleFile, mpctx->set_of_sub_size, + mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "SUB: Added subtitle file (%d): %s\n", mpctx->set_of_sub_size, filename_recode(filename)); } -// FIXME: if/when the GUI calls this, global sub numbering gets (potentially) broken. -void update_set_of_subtitles(void) - // subdata was changed, set_of_sub... have to be updated. +void init_vo_spudec(struct MPContext *mpctx) { - sub_data ** const set_of_subtitles = mpctx->set_of_subtitles; - int i; - if (mpctx->set_of_sub_size > 0 && subdata == NULL) { // *subdata was deleted - for (i = mpctx->set_of_sub_pos + 1; i < mpctx->set_of_sub_size; ++i) - set_of_subtitles[i-1] = set_of_subtitles[i]; - set_of_subtitles[mpctx->set_of_sub_size-1] = NULL; - --mpctx->set_of_sub_size; - if (mpctx->set_of_sub_size > 0) subdata = set_of_subtitles[mpctx->set_of_sub_pos=0]; - } - else if (mpctx->set_of_sub_size > 0 && subdata != NULL) { // *subdata was changed - set_of_subtitles[mpctx->set_of_sub_pos] = subdata; - } - else if (mpctx->set_of_sub_size <= 0 && subdata != NULL) { // *subdata was added - set_of_subtitles[mpctx->set_of_sub_pos=mpctx->set_of_sub_size] = subdata; - ++mpctx->set_of_sub_size; - } -} - -void init_vo_spudec(void) { if (vo_spudec) spudec_free(vo_spudec); - initialized_flags &= ~INITIALIZED_SPUDEC; + mpctx->initialized_flags &= ~INITIALIZED_SPUDEC; vo_spudec = NULL; // we currently can't work without video stream @@ -1166,7 +1193,7 @@ void init_vo_spudec(void) { } if (vo_spudec!=NULL) { - initialized_flags|=INITIALIZED_SPUDEC; + mpctx->initialized_flags|=INITIALIZED_SPUDEC; mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only, mpctx); } } @@ -1227,15 +1254,27 @@ static void sadd_hhmmssf(char *buf, unsigned *pos, int len, float time) { saddf(buf, pos, len, "%02d.%1d", ss, f1); } -/** - * \brief print the status line - * \param a_pos audio position - * \param a_v A-V desynchronization - * \param corr amount out A-V synchronization - */ -static void print_status(float a_pos, float a_v, float corr) +static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) { + struct MPOpts *opts = &mpctx->opts; 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 - sh_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 (quiet) + return; + + int width; char *line; unsigned pos = 0; @@ -1270,7 +1309,8 @@ static void print_status(float a_pos, float a_v, float corr) // A-V sync if (mpctx->sh_audio && sh_video) - saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ", a_v, corr); + saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ", + mpctx->last_av_difference, mpctx->total_avsync_change); // Video stats if (sh_video) @@ -1282,9 +1322,9 @@ static void print_status(float a_pos, float a_v, float corr) if (sh_video) { if (sh_video->timer > 0.5) saddf(line, &pos, width, "%2d%% %2d%% %4.1f%% ", - (int)(100.0*video_time_usage*playback_speed/(double)sh_video->timer), - (int)(100.0*vout_time_usage*playback_speed/(double)sh_video->timer), - (100.0*audio_time_usage*playback_speed/(double)sh_video->timer)); + (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) { @@ -1306,8 +1346,8 @@ static void print_status(float a_pos, float a_v, float corr) #endif // other - if (playback_speed != 1) - saddf(line, &pos, width, "%4.2fx ", playback_speed); + if (opts->playback_speed != 1) + saddf(line, &pos, width, "%4.2fx ", opts->playback_speed); // end if (erase_to_end_of_line) { @@ -1327,39 +1367,34 @@ static void print_status(float a_pos, float a_v, float corr) * \param sh_audio describes the requested input format of the chain. * \param ao_data describes the requested output format of the chain. */ -int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data) +int build_afilter_chain(struct MPContext *mpctx, sh_audio_t *sh_audio, ao_data_t *ao_data) { + struct MPOpts *opts = &mpctx->opts; int new_srate; int result; if (!sh_audio) { -#ifdef CONFIG_GUI - if (use_gui) guiGetEvent(guiSetAfilter, (char *)NULL); -#endif mpctx->mixer.afilter = NULL; return 0; } if(af_control_any_rev(sh_audio->afilter, AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET, - &playback_speed)) { + &opts->playback_speed)) { new_srate = sh_audio->samplerate; } else { - new_srate = sh_audio->samplerate * playback_speed; + new_srate = sh_audio->samplerate * opts->playback_speed; if (new_srate != ao_data->samplerate) { // limits are taken from libaf/af_resample.c if (new_srate < 8000) new_srate = 8000; if (new_srate > 192000) new_srate = 192000; - playback_speed = (float)new_srate / (float)sh_audio->samplerate; + opts->playback_speed = (float)new_srate / (float)sh_audio->samplerate; } } result = init_audio_filters(sh_audio, new_srate, &ao_data->samplerate, &ao_data->channels, &ao_data->format); mpctx->mixer.afilter = sh_audio->afilter; -#ifdef CONFIG_GUI - if (use_gui) guiGetEvent(guiSetAfilter, (char *)sh_audio->afilter); -#endif return result; } @@ -1385,10 +1420,10 @@ static mp_osd_msg_t* osd_msg_stack = NULL; * it is pulled on top of the stack, otherwise a new message is created. * */ - -void set_osd_msg(int id, int level, int time, const char* fmt, ...) { +static void set_osd_msg_va(int id, int level, int time, const char *fmt, + va_list ap) +{ mp_osd_msg_t *msg,*last=NULL; - va_list va; int r; // look if the id is already in the stack @@ -1405,9 +1440,7 @@ void set_osd_msg(int id, int level, int time, const char* fmt, ...) { osd_msg_stack = msg; } // write the msg - va_start(va,fmt); - r = vsnprintf(msg->msg, 128, fmt, va); - va_end(va); + r = vsnprintf(msg->msg, 128, fmt, ap); if(r >= 128) msg->msg[127] = 0; // set id and time msg->id = id; @@ -1416,6 +1449,23 @@ void set_osd_msg(int id, int level, int time, const char* fmt, ...) { } +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); +} + + /** * \brief Remove a message from the OSD stack * @@ -1462,24 +1512,27 @@ static void clear_osd_msgs(void) { * */ -static mp_osd_msg_t* get_osd_msg(void) { +static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg,*prev,*last = NULL; static unsigned last_update = 0; unsigned now = GetTimerMS(); unsigned diff; char hidden_dec_done = 0; - if (osd_visible) { + if (mpctx->osd_visible) { // 36000000 means max timed visibility is 1 hour into the future, if // the difference is greater assume it's wrapped around from below 0 - if (osd_visible - now > 36000000) { - osd_visible = 0; + if (mpctx->osd_visible - now > 36000000) { + mpctx->osd_visible = 0; vo_osd_progbar_type = -1; // disable vo_osd_changed(OSDTYPE_PROGBAR); - if (mpctx->osd_function != OSD_PAUSE) - mpctx->osd_function = OSD_PLAY; + mpctx->osd_function = mpctx->paused ? OSD_PAUSE : OSD_PLAY; } } + if (mpctx->osd_show_percentage_until - now > 36000000) + mpctx->osd_show_percentage_until = 0; if(!last_update) last_update = now; diff = now >= last_update ? now - last_update : 0; @@ -1489,14 +1542,16 @@ static mp_osd_msg_t* get_osd_msg(void) { // Look for the first message in the stack with high enough level. for(msg = osd_msg_stack ; msg ; last = msg, msg = prev) { prev = msg->prev; - if(msg->level > osd_level && hidden_dec_done) continue; + if (msg->level > opts->osd_level && hidden_dec_done) + continue; // The message has a high enough level or it is the first hidden one // in both cases we decrement the timer or kill it. if(!msg->started || msg->time > diff) { if(msg->started) msg->time -= diff; else msg->started = 1; // display it - if(msg->level <= osd_level) return msg; + if (msg->level <= opts->osd_level) + return msg; hidden_dec_done = 1; continue; } @@ -1521,26 +1576,28 @@ static mp_osd_msg_t* get_osd_msg(void) { * */ -void set_osd_bar(int type,const char* name,double min,double max,double val) { - - if(osd_level < 1) return; +void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,double max,double val) { + struct MPOpts *opts = &mpctx->opts; + if (opts->osd_level < 1) + return; if(mpctx->sh_video) { - osd_visible = (GetTimerMS() + 1000) | 1; + mpctx->osd_visible = (GetTimerMS() + 1000) | 1; vo_osd_progbar_type = type; vo_osd_progbar_value = 256*(val-min)/(max-min); vo_osd_changed(OSDTYPE_PROGBAR); return; } - set_osd_msg(OSD_MSG_BAR,1,osd_duration,"%s: %d %%", - name,ROUND(100*(val-min)/(max-min))); + set_osd_msg(OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%", + name, ROUND(100*(val-min)/(max-min))); } /** * \brief Display text subtitles on the OSD */ -void set_osd_subtitle(subtitle *subs) { +void set_osd_subtitle(struct MPContext *mpctx, subtitle *subs) +{ int i; vo_sub = subs; vo_osd_changed(OSDTYPE_SUBTITLE); @@ -1567,18 +1624,30 @@ void set_osd_subtitle(subtitle *subs) { * */ -static void update_osd_msg(void) { +static void update_osd_msg(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg; - static char osd_text[128] = ""; - static char osd_text_timer[128]; - - // we need some mem for vo_osd_text - vo_osd_text = (unsigned char*)osd_text; + struct osd_state *osd = mpctx->osd; + char osd_text_timer[128]; + + if (mpctx->add_osd_seek_info) { + double percentage; + if (mpctx->timeline && mpctx->sh_video) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); + set_osd_bar(mpctx, 0, "Position", 0, 100, percentage); + if (mpctx->sh_video) + mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1; + mpctx->add_osd_seek_info = false; + } // Look if we have a msg - if((msg = get_osd_msg())) { - if(strcmp(osd_text,msg->msg)) { - strncpy((char*)osd_text, msg->msg, 127); + if((msg = get_osd_msg(mpctx))) { + if (strcmp(osd->osd_text, msg->msg)) { + strncpy(osd->osd_text, msg->msg, 127); if(mpctx->sh_video) vo_osd_changed(OSDTYPE_OSD); else if(term_osd) mp_msg(MSGT_CPLAYER,MSGL_STATUS,"%s%s\n",term_osd_esc,msg->msg); } @@ -1587,21 +1656,30 @@ static void update_osd_msg(void) { if(mpctx->sh_video) { // fallback on the timer - if(osd_level>=2) { - int len = demuxer_get_time_length(mpctx->demuxer); + if (opts->osd_level >= 2) { + int len; + if (mpctx->timeline) + len = mpctx->timeline[mpctx->num_timeline_parts].start; + else + len = demuxer_get_time_length(mpctx->demuxer); int percentage = -1; char percentage_text[10]; int pts = demuxer_get_current_time(mpctx->demuxer); - if (mpctx->osd_show_percentage) - percentage = demuxer_get_percent_pos(mpctx->demuxer); + if (mpctx->osd_show_percentage_until) { + if (mpctx->timeline) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); + } if (percentage >= 0) snprintf(percentage_text, 9, " (%d%%)", percentage); else percentage_text[0] = 0; - if (osd_level == 3) + if (opts->osd_level == 3) snprintf(osd_text_timer, 63, "%c %02d:%02d:%02d / %02d:%02d:%02d%s", mpctx->osd_function,pts/3600,(pts/60)%60,pts%60, @@ -1613,20 +1691,16 @@ static void update_osd_msg(void) { } else osd_text_timer[0]=0; - // always decrement the percentage timer - if(mpctx->osd_show_percentage) - mpctx->osd_show_percentage--; - - if(strcmp(osd_text,osd_text_timer)) { - strncpy(osd_text, osd_text_timer, 63); + if (strcmp(osd->osd_text, osd_text_timer)) { + strncpy(osd->osd_text, osd_text_timer, 63); vo_osd_changed(OSDTYPE_OSD); } return; } // Clear the term osd line - if(term_osd && osd_text[0]) { - osd_text[0] = 0; + if (term_osd && osd->osd_text[0]) { + osd->osd_text[0] = 0; printf("%s\n",term_osd_esc); } } @@ -1635,7 +1709,9 @@ static void update_osd_msg(void) { // OSDMsgStack -void reinit_audio_chain(void) { +void reinit_audio_chain(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_audio) return; current_module="init_audio_codec"; @@ -1643,7 +1719,7 @@ void reinit_audio_chain(void) { if(!init_best_audio_codec(mpctx->sh_audio,audio_codec_list,audio_fm_list)){ goto init_error; } - initialized_flags|=INITIALIZED_ACODEC; + mpctx->initialized_flags|=INITIALIZED_ACODEC; mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); @@ -1657,20 +1733,21 @@ void reinit_audio_chain(void) { mpctx->sh_audio->samplerate, // output: &ao_data.samplerate, &ao_data.channels, &ao_data.format)){ - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_AudioFilterChainPreinitError); - exit_player(EXIT_ERROR); + mp_tmsg(MSGT_CPLAYER,MSGL_ERR, "Error at audio filter chain " + "pre-init!\n"); + exit_player(mpctx, EXIT_ERROR); } current_module="ao2_init"; - mpctx->audio_out = init_best_audio_out(audio_driver_list, + mpctx->audio_out = init_best_audio_out(opts->audio_driver_list, 0, // plugin flag ao_data.samplerate, ao_data.channels, ao_data.format, 0); if(!mpctx->audio_out){ - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CannotInitAO); + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Could not open/initialize audio device -> no sound.\n"); goto init_error; } - initialized_flags|=INITIALIZED_AO; + mpctx->initialized_flags|=INITIALIZED_AO; mp_msg(MSGT_CPLAYER,MSGL_INFO,"AO: [%s] %dHz %dch %s (%d bytes per sample)\n", mpctx->audio_out->info->short_name, ao_data.samplerate, ao_data.channels, @@ -1682,8 +1759,8 @@ void reinit_audio_chain(void) { mp_msg(MSGT_CPLAYER,MSGL_V,"AO: Comment: %s\n", mpctx->audio_out->info->comment); // init audio filters: current_module="af_init"; - if(!build_afilter_chain(mpctx->sh_audio, &ao_data)) { - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_NoMatchingFilter); + if(!build_afilter_chain(mpctx, mpctx->sh_audio, &ao_data)) { + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Couldn't find matching filter/ao format!\n"); goto init_error; } mpctx->mixer.audio_out = mpctx->audio_out; @@ -1691,7 +1768,7 @@ void reinit_audio_chain(void) { return; init_error: - uninit_player(INITIALIZED_ACODEC|INITIALIZED_AO); // close codec and possibly AO + uninit_player(mpctx, INITIALIZED_ACODEC|INITIALIZED_AO); // close codec and possibly AO mpctx->sh_audio=mpctx->d_audio->sh=NULL; // -> nosound mpctx->d_audio->id = -2; } @@ -1703,8 +1780,10 @@ init_error: // Return pts value corresponding to the end point of audio written to the // ao so far. -static double written_audio_pts(sh_audio_t *sh_audio, demux_stream_t *d_audio) +static double written_audio_pts(struct MPContext *mpctx) { + sh_audio_t *sh_audio = mpctx->sh_audio; + demux_stream_t *d_audio = mpctx->d_audio; double buffered_output; // first calculate the end pts of audio that has been output by decoder double a_pts = sh_audio->pts; @@ -1747,31 +1826,31 @@ static double written_audio_pts(sh_audio_t *sh_audio, demux_stream_t *d_audio) // Filters divide audio length by playback_speed, so multiply by it // to get the length in original units without speedup or slowdown - a_pts -= buffered_output * playback_speed / ao_data.bps; + a_pts -= buffered_output * mpctx->opts.playback_speed / ao_data.bps; - return a_pts; + return a_pts + mpctx->video_offset; } // Return pts value corresponding to currently playing audio. -double playing_audio_pts(sh_audio_t *sh_audio, demux_stream_t *d_audio, - const ao_functions_t *audio_out) +double playing_audio_pts(struct MPContext *mpctx) { - return written_audio_pts(sh_audio, d_audio) - playback_speed * - audio_out->get_delay(); + return written_audio_pts(mpctx) - mpctx->opts.playback_speed * + mpctx->audio_out->get_delay(); } -static int check_framedrop(double frame_time) { +static int check_framedrop(struct MPContext *mpctx, double frame_time) { + struct MPOpts *opts = &mpctx->opts; // check for frame-drop: current_module = "check_framedrop"; if (mpctx->sh_audio && !mpctx->d_audio->eof) { static int dropped_frames; - float delay = playback_speed*mpctx->audio_out->get_delay(); + float delay = opts->playback_speed*mpctx->audio_out->get_delay(); 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->osd_function != OSD_PAUSE) { + if (d < -dropped_frames*frame_time-0.100 && !mpctx->paused + && !mpctx->update_video_immediately) { ++drop_frame_cnt; ++dropped_frames; return frame_dropping; @@ -1781,53 +1860,12 @@ static int check_framedrop(double frame_time) { return 0; } -static int generate_video_frame(sh_video_t *sh_video, demux_stream_t *d_video) -{ - unsigned char *start; - int in_size; - int hit_eof=0; - double pts; - - while (1) { - int drop_frame = check_framedrop(sh_video->frametime); - void *decoded_frame; - current_module = "decode video"; - // XXX Time used in this call is not counted in any performance - // timer now, OSD is not updated correctly for filter-added frames - if (vf_output_queued_frame(sh_video->vfilter)) - break; - current_module = "video_read_frame"; - in_size = ds_get_packet_pts(d_video, &start, &pts); - if (in_size < 0) { - // try to extract last frames in case of decoder lag - in_size = 0; - pts = 1e300; - hit_eof = 1; - } - if (in_size > max_framesize) - max_framesize = in_size; - current_module = "decode video"; - decoded_frame = decode_video(sh_video, start, in_size, drop_frame, pts); - if (decoded_frame) { - update_subtitles(sh_video, sh_video->pts, mpctx->d_sub, 0); - update_teletext(sh_video, mpctx->demuxer, 0); - update_osd_msg(); - current_module = "filter video"; - if (filter_video(sh_video, decoded_frame, sh_video->pts)) - break; - } else if (drop_frame) - return -1; - if (hit_eof) - return 0; - } - return 1; -} #ifdef HAVE_RTC int rtc_fd = -1; #endif -static float timing_sleep(float time_frame) +static float timing_sleep(struct MPContext *mpctx, float time_frame) { #ifdef HAVE_RTC if (rtc_fd >= 0){ @@ -1836,8 +1874,8 @@ static float timing_sleep(float time_frame) while (time_frame > 0.000) { unsigned long rtc_ts; if (read(rtc_fd, &rtc_ts, sizeof(rtc_ts)) <= 0) - mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_LinuxRTCReadError, strerror(errno)); - time_frame -= GetRelativeTime(); + mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Linux RTC read error: %s\n", strerror(errno)); + time_frame -= get_relative_time(mpctx); } } else #endif @@ -1848,40 +1886,42 @@ static float timing_sleep(float time_frame) current_module = "sleep_timer"; while (time_frame > margin) { usec_sleep(1000000 * (time_frame - margin)); - time_frame -= GetRelativeTime(); + time_frame -= get_relative_time(mpctx); } if (softsleep){ current_module = "sleep_soft"; if (time_frame < 0) - mp_msg(MSGT_AVSYNC, MSGL_WARN, MSGTR_SoftsleepUnderflow); + mp_tmsg(MSGT_AVSYNC, MSGL_WARN, "Warning! Softsleep underflow!\n"); while (time_frame > 0) - time_frame-=GetRelativeTime(); // burn the CPU + time_frame -= get_relative_time(mpctx); // burn the CPU } } return time_frame; } -static void select_subtitle(MPContext *mpctx) { +static void select_subtitle(MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; // find the best sub to use int vobsub_index_id = vobsub_get_index_by_id(vo_vobsub, vobsub_id); mpctx->global_sub_pos = -1; // no subs by default if (vobsub_index_id >= 0) { // if user asks for a vobsub id, use that first. mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_VOBSUB] + vobsub_index_id; - } else if (dvdsub_id >= 0 && mpctx->global_sub_indices[SUB_SOURCE_DEMUX] >= 0) { + } else if (opts->sub_id >= 0 && mpctx->global_sub_indices[SUB_SOURCE_DEMUX] >= 0) { // if user asks for a dvd sub id, use that next. - mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvdsub_id; + mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + opts->sub_id; } else if (mpctx->global_sub_indices[SUB_SOURCE_SUBS] >= 0) { // if there are text subs to use, use those. (autosubs come last here) mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_SUBS]; - } else if (dvdsub_id == -1 && mpctx->global_sub_indices[SUB_SOURCE_DEMUX] >= 0) { + } else if (opts->sub_id == -1 && mpctx->global_sub_indices[SUB_SOURCE_DEMUX] >= 0) { // finally select subs by language and container hints - if (dvdsub_id == -1 && dvdsub_lang) - dvdsub_id = demuxer_sub_track_by_lang(mpctx->demuxer, dvdsub_lang); - if (dvdsub_id == -1) - dvdsub_id = demuxer_default_sub_track(mpctx->demuxer); - if (dvdsub_id >= 0) - mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvdsub_id; + if (opts->sub_id == -1 && dvdsub_lang) + opts->sub_id = demuxer_sub_track_by_lang(mpctx->demuxer, dvdsub_lang); + if (opts->sub_id == -1) + opts->sub_id = demuxer_default_sub_track(mpctx->demuxer); + if (opts->sub_id >= 0) + mpctx->global_sub_pos = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + opts->sub_id; } // rather than duplicate code, use the SUB_SELECT handler to init the right one. mpctx->global_sub_pos--; @@ -1919,6 +1959,7 @@ static mp_image_t * mp_dvdnav_copy_mpi(mp_image_t *to_mpi, } static void mp_dvdnav_reset_stream (MPContext *ctx) { + struct MPOpts *opts = &ctx->opts; if (ctx->sh_video) { /// clear video pts ctx->d_video->pts = 0.0f; @@ -1944,11 +1985,11 @@ static void mp_dvdnav_reset_stream (MPContext *ctx) { } audio_delay = 0.0f; - mpctx->global_sub_size = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + mp_dvdnav_number_of_subs(mpctx->stream); - if (dvdsub_lang && dvdsub_id == dvdsub_lang_id) { + ctx->global_sub_size = ctx->global_sub_indices[SUB_SOURCE_DEMUX] + mp_dvdnav_number_of_subs(ctx->stream); + if (dvdsub_lang && opts->sub_id == dvdsub_lang_id) { dvdsub_lang_id = mp_dvdnav_sid_from_lang(ctx->stream, dvdsub_lang); - if (dvdsub_lang_id != dvdsub_id) { - dvdsub_id = dvdsub_lang_id; + if (dvdsub_lang_id != opts->sub_id) { + opts->sub_id = dvdsub_lang_id; select_subtitle(ctx); } } @@ -1958,7 +1999,8 @@ static void mp_dvdnav_reset_stream (MPContext *ctx) { } /// Restore last decoded DVDNAV (still frame) -static mp_image_t *mp_dvdnav_restore_smpi(int *in_size, +static mp_image_t *mp_dvdnav_restore_smpi(struct MPContext *mpctx, + int *in_size, unsigned char **start, mp_image_t *decoded_frame) { @@ -2002,7 +2044,7 @@ static mp_image_t *mp_dvdnav_restore_smpi(int *in_size, } /// Save last decoded DVDNAV (still frame) -static void mp_dvdnav_save_smpi(int in_size, +static void mp_dvdnav_save_smpi(struct MPContext *mpctx, int in_size, unsigned char *start, mp_image_t *decoded_frame) { @@ -2023,78 +2065,48 @@ static void mp_dvdnav_save_smpi(int in_size, } #endif /* CONFIG_DVDNAV */ -static void adjust_sync_and_print_status(int between_frames, float timing_error) +/* 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) { - current_module="av_sync"; - - if(mpctx->sh_audio){ - double a_pts, v_pts; - - if (autosync) - /* - * If autosync is enabled, the value for delay must be calculated - * a bit differently. It is set only to the difference between - * the audio and video timers. Any attempt to include the real - * or corrected delay causes the pts_correction code below to - * try to correct for the changes in delay which autosync is - * trying to measure. This keeps the two from competing, but still - * allows the code to correct for PTS drift *only*. (Using a delay - * value here, even a "corrected" one, would be incompatible with - * autosync mode.) - */ - a_pts = written_audio_pts(mpctx->sh_audio, mpctx->d_audio) - mpctx->delay; - else - a_pts = playing_audio_pts(mpctx->sh_audio, mpctx->d_audio, mpctx->audio_out); - - v_pts = mpctx->sh_video->pts; + current_module = "av_sync"; - { - static int drop_message=0; - double AV_delay = a_pts - audio_delay - v_pts; - double x; - if (AV_delay>0.5 && drop_frame_cnt>50 && drop_message==0){ - ++drop_message; - mp_msg(MSGT_AVSYNC,MSGL_WARN,MSGTR_SystemTooSlow); - } - if (autosync) - x = AV_delay*0.1f; - else - /* Do not correct target time for the next frame if this frame - * was late not because of wrong target time but because the - * target time could not be met */ - x = (AV_delay + timing_error * playback_speed) * 0.1f; - if (x < -max_pts_correction) - x = -max_pts_correction; - else if (x> max_pts_correction) - x = max_pts_correction; - if (default_max_pts_correction >= 0) - max_pts_correction = default_max_pts_correction; - else - max_pts_correction = mpctx->sh_video->frametime*0.10; // +-10% of time - if (!between_frames) { - mpctx->delay+=x; - c_total+=x; - } - if(!quiet) - print_status(a_pts - audio_delay, AV_delay, c_total); - } - - } else { - // No audio: + if (!mpctx->sh_audio) + return; - if (!quiet) - print_status(0, 0, 0); - } + 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 + + double change = av_delay * 0.1; + double max_change = default_max_pts_correction >= 0 ? + default_max_pts_correction : frame_time * 0.1; + if (change < -max_change) + change = -max_change; + else if (change > max_change) + change = max_change; + mpctx->delay += change; + mpctx->total_avsync_change += change; } -static int fill_audio_out_buffers(void) +static int fill_audio_out_buffers(struct MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; unsigned int t; double tt; int playsize; int playflags=0; int audio_eof=0; - int bytes_to_write; sh_audio_t * const sh_audio = mpctx->sh_audio; current_module="play_audio"; @@ -2105,74 +2117,70 @@ static int fill_audio_out_buffers(void) // sync completely wrong; there should be no need to use ao_data.pts // in get_space() ao_data.pts = ((mpctx->sh_video?mpctx->sh_video->timer:0)+mpctx->delay)*90000.0; - bytes_to_write = mpctx->audio_out->get_space(); - if (mpctx->sh_video || bytes_to_write >= ao_data.outburst) + playsize = mpctx->audio_out->get_space(); + if (mpctx->sh_video || playsize >= ao_data.outburst) break; // handle audio-only case: // this is where mplayer sleeps during audio-only playback // to avoid 100% CPU use - sleep_time = (ao_data.outburst - bytes_to_write) * 1000 / ao_data.bps; + sleep_time = (ao_data.outburst - playsize) * 1000 / ao_data.bps; if (sleep_time < 10) sleep_time = 10; // limit to 100 wakeups per second usec_sleep(sleep_time * 1000); } - while (bytes_to_write) { - playsize = bytes_to_write; - if (playsize > MAX_OUTBURST) - playsize = MAX_OUTBURST; - bytes_to_write -= playsize; - - // Fill buffer if needed: - current_module="decode_audio"; - t = GetTimer(); - if (decode_audio(sh_audio, playsize) < 0) // EOF or error - if (mpctx->d_audio->eof) { - audio_eof = 1; - if (sh_audio->a_out_buffer_len == 0) - return 0; - } - t = GetTimer() - t; - tt = t*0.000001f; audio_time_usage+=tt; - if (playsize > sh_audio->a_out_buffer_len) { - playsize = sh_audio->a_out_buffer_len; - if (audio_eof) - playflags |= AOPLAY_FINAL_CHUNK; - } - if (!playsize) - break; - - // play audio: - current_module="play_audio"; + // Fill buffer if needed: + current_module="decode_audio"; + t = GetTimer(); + if (decode_audio(sh_audio, playsize) < 0) // EOF or error + if (mpctx->d_audio->eof) { + audio_eof = 1; + if (sh_audio->a_out_buffer_len == 0) + return 0; + } + t = GetTimer() - t; + tt = t*0.000001f; audio_time_usage+=tt; + if (playsize > sh_audio->a_out_buffer_len) { + playsize = sh_audio->a_out_buffer_len; + if (audio_eof) + playflags |= AOPLAY_FINAL_CHUNK; + } + if (!playsize) + return 1; - // Is this pts value actually useful for the aos that access it? - // They're obviously badly broken in the way they handle av sync; - // would not having access to this make them more broken? - ao_data.pts = ((mpctx->sh_video?mpctx->sh_video->timer:0)+mpctx->delay)*90000.0; - playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags); + // play audio: + current_module="play_audio"; - if (playsize > 0) { - sh_audio->a_out_buffer_len -= playsize; - memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize], - sh_audio->a_out_buffer_len); - mpctx->delay += playback_speed*playsize/(double)ao_data.bps; - } - else if (audio_eof && mpctx->audio_out->get_delay() < .04) { - // Sanity check to avoid hanging in case current ao doesn't output - // partial chunks and doesn't check for AOPLAY_FINAL_CHUNK - mp_msg(MSGT_CPLAYER, MSGL_WARN, "Audio output truncated at end.\n"); - sh_audio->a_out_buffer_len = 0; - } + // Is this pts value actually useful for the aos that access it? + // They're obviously badly broken in the way they handle av sync; + // would not having access to this make them more broken? + ao_data.pts = ((mpctx->sh_video?mpctx->sh_video->timer:0)+mpctx->delay)*90000.0; + playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags); + + if (playsize > 0) { + sh_audio->a_out_buffer_len -= playsize; + memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize], + sh_audio->a_out_buffer_len); + mpctx->delay += opts->playback_speed*playsize/(double)ao_data.bps; } + else if (audio_eof && mpctx->audio_out->get_delay() < .04) { + // Sanity check to avoid hanging in case current ao doesn't output + // partial chunks and doesn't check for AOPLAY_FINAL_CHUNK + mp_msg(MSGT_CPLAYER, MSGL_WARN, "Audio output truncated at end.\n"); + sh_audio->a_out_buffer_len = 0; + } + return 1; } -static int sleep_until_update(float *time_frame, float *aq_sleep_time) +static int sleep_until_update(struct MPContext *mpctx, float *time_frame, + float *aq_sleep_time) { + struct MPOpts *opts = &mpctx->opts; int frame_time_remaining = 0; current_module="calc_sleep_time"; - *time_frame -= GetRelativeTime(); // reset timer + *time_frame -= get_relative_time(mpctx); // reset timer if (mpctx->sh_audio && !mpctx->d_audio->eof) { float delay = mpctx->audio_out->get_delay(); @@ -2188,12 +2196,12 @@ static int sleep_until_update(float *time_frame, float *aq_sleep_time) * 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 / playback_speed + *time_frame; + float predicted = mpctx->delay / opts->playback_speed + *time_frame; float difference = delay - predicted; delay = predicted + difference / (float)autosync; } - *time_frame = delay - mpctx->delay / playback_speed; + *time_frame = delay - mpctx->delay / opts->playback_speed; // delay = amount of audio buffered in soundcard/driver if (delay > 0.25) delay=0.25; else @@ -2217,27 +2225,30 @@ static int sleep_until_update(float *time_frame, float *aq_sleep_time) //============================== SLEEP: =================================== + *time_frame -= mpctx->video_out->flip_queue_offset; // flag 256 means: libvo driver does its timing (dvb card) - if (*time_frame > 0.001 && !(vo_flags&256)) - *time_frame = timing_sleep(*time_frame); + if (*time_frame > 0.001 && !(mpctx->sh_video->output_flags&256)) + *time_frame = timing_sleep(mpctx, *time_frame); + *time_frame += mpctx->video_out->flip_queue_offset; return frame_time_remaining; } -int reinit_video_chain(void) { +int reinit_video_chain(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; sh_video_t * const sh_video = mpctx->sh_video; double ar=-1.0; //================== Init VIDEO (codec & libvo) ========================== - if(!fixed_vo || !(initialized_flags&INITIALIZED_VO)){ + if (!opts->fixed_vo || !(mpctx->initialized_flags & INITIALIZED_VO)) { current_module="preinit_libvo"; //shouldn't we set dvideo->id=-2 when we fail? - vo_config_count=0; //if((mpctx->video_out->preinit(vo_subdevice))!=0){ - if(!(mpctx->video_out=init_best_video_out(video_driver_list))){ - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_ErrorInitializingVODevice); + 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; } - initialized_flags|=INITIALIZED_VO; + mpctx->initialized_flags|=INITIALIZED_VO; } if(stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar) != STREAM_UNSUPPORTED) @@ -2245,28 +2256,28 @@ int reinit_video_chain(void) { current_module="init_video_filters"; { char* vf_arg[] = { "_oldargs_", (char*)mpctx->video_out , NULL }; - sh_video->vfilter=(void*)vf_open_filter(NULL,"vo",vf_arg); + sh_video->vfilter = vf_open_filter(opts, NULL,"vo",vf_arg); } #ifdef CONFIG_MENU if(use_menu) { char* vf_arg[] = { "_oldargs_", menu_root, NULL }; - vf_menu = vf_open_plugin(libmenu_vfs,sh_video->vfilter,"menu",vf_arg); + vf_menu = vf_open_plugin(opts,libmenu_vfs,sh_video->vfilter,"menu",vf_arg); if(!vf_menu) { - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CantOpenLibmenuFilterWithThisRootMenu,menu_root); + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Can't open libmenu video filter with root menu %s.\n",menu_root); use_menu = 0; } } if(vf_menu) - sh_video->vfilter=(void*)vf_menu; + sh_video->vfilter = vf_menu; #endif #ifdef CONFIG_ASS - if(ass_enabled) { + if(opts->ass_enabled) { int i; int insert = 1; - if (vf_settings) - for (i = 0; vf_settings[i].name; ++i) - if (strcmp(vf_settings[i].name, "ass") == 0) { + 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; } @@ -2274,20 +2285,26 @@ int reinit_video_chain(void) { extern vf_info_t vf_info_ass; const vf_info_t* libass_vfs[] = {&vf_info_ass, NULL}; char* vf_arg[] = {"auto", "1", NULL}; - vf_instance_t* vf_ass = vf_open_plugin(libass_vfs,sh_video->vfilter,"ass",vf_arg); + 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=(void*)vf_ass; + sh_video->vfilter = vf_ass; + else if (retcode == -1) // vf_ass open() returns -1 if there's VO 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"); } } #endif - sh_video->vfilter=(void*)append_filters(sh_video->vfilter); + sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings); #ifdef CONFIG_ASS - if (ass_enabled) - ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter, VFCTRL_INIT_EOSD, ass_library); + if (opts->ass_enabled) + sh_video->vfilter->control(sh_video->vfilter, VFCTRL_INIT_EOSD, ass_library); #endif current_module="init_video_codec"; @@ -2297,11 +2314,11 @@ int reinit_video_chain(void) { mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); if(!sh_video->initialized){ - if(!fixed_vo) uninit_player(INITIALIZED_VO); + if(!opts->fixed_vo) uninit_player(mpctx, INITIALIZED_VO); goto err_out; } - initialized_flags|=INITIALIZED_VCODEC; + mpctx->initialized_flags|=INITIALIZED_VCODEC; if (sh_video->codec) mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_CODEC=%s\n", sh_video->codec->name); @@ -2330,158 +2347,254 @@ err_out: return 0; } -static double update_video(int *blit_frame) +static double update_video_nocorrect_pts(struct MPContext *mpctx) { - sh_video_t * const sh_video = mpctx->sh_video; - //-------------------- Decode a frame: ----------------------- - double frame_time; - *blit_frame = 0; // Don't blit if we hit EOF - if (!correct_pts) { - unsigned char* start=NULL; - void *decoded_frame = NULL; - int drop_frame=0; - int in_size; - - current_module = "video_read_frame"; - frame_time = sh_video->next_frame_time; - in_size = video_read_frame(sh_video, &sh_video->next_frame_time, - &start, force_fps); + struct sh_video *sh_video = mpctx->sh_video; + double frame_time = 0; + struct vo *video_out = mpctx->video_out; + while (!video_out->frame_loaded) { + current_module = "filter_video"; + // In nocorrect-pts mode there is no way to properly time these frames + if (vo_get_buffered_frame(video_out, 0) >= 0) + break; + if (vf_output_queued_frame(sh_video->vfilter)) + continue; + unsigned char *packet = NULL; + frame_time = sh_video->next_frame_time; + if (mpctx->update_video_immediately) + frame_time = 0; + int in_size = video_read_frame(sh_video, &sh_video->next_frame_time, + &packet, force_fps); + if (in_size < 0) { #ifdef CONFIG_DVDNAV - /// wait, still frame or EOF - if (mpctx->stream->type == STREAMTYPE_DVDNAV && in_size < 0) { - 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 + 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 #endif - if (in_size < 0) - return -1; - if (in_size > max_framesize) - max_framesize = in_size; // stats - 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; - drop_frame = check_framedrop(frame_time); - update_subtitles(sh_video, sh_video->pts, mpctx->d_sub, 0); - update_teletext(sh_video, mpctx->demuxer, 0); - update_osd_msg(); - current_module = "decode_video"; + 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; #ifdef CONFIG_DVDNAV - decoded_frame = mp_dvdnav_restore_smpi(&in_size,&start,decoded_frame); - /// still frame has been reached, no need to decode - if (in_size > 0 && !decoded_frame) + decoded_frame = mp_dvdnav_restore_smpi(mpctx, &in_size, &packet, NULL); + if (in_size >= 0 && !decoded_frame) #endif - decoded_frame = decode_video(sh_video, start, in_size, drop_frame, - sh_video->pts); + decoded_frame = decode_video(sh_video, packet, in_size, framedrop_type, + sh_video->pts); #ifdef CONFIG_DVDNAV - /// save back last still frame for future display - mp_dvdnav_save_smpi(in_size,start,decoded_frame); + // Save last still frame for future display + mp_dvdnav_save_smpi(mpctx, in_size, packet, decoded_frame); #endif - current_module = "filter_video"; - *blit_frame = (decoded_frame && filter_video(sh_video, decoded_frame, - sh_video->pts)); + if (decoded_frame) { + current_module = "filter video"; + if (filter_video(sh_video, decoded_frame, sh_video->pts)) + if (!video_out->config_ok) + break; + } } - else { - int res = generate_video_frame(sh_video, mpctx->d_video); - if (!res) - return -1; - ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter, - VFCTRL_GET_PTS, &sh_video->pts); - if (sh_video->pts == MP_NOPTS_VALUE) { - mp_msg(MSGT_CPLAYER, MSGL_ERR, "pts after filters MISSING\n"); - sh_video->pts = sh_video->last_pts; - } - if (sh_video->last_pts == MP_NOPTS_VALUE) - sh_video->last_pts= sh_video->pts; - else if (sh_video->last_pts > sh_video->pts) { - sh_video->last_pts = sh_video->pts; - mp_msg(MSGT_CPLAYER, MSGL_INFO, "pts value < previous\n"); - } - 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; - *blit_frame = res > 0; + return frame_time; +} + +static void determine_frame_pts(struct MPContext *mpctx) +{ + struct sh_video *sh_video = mpctx->sh_video; + struct MPOpts *opts = &mpctx->opts; + + if (opts->user_pts_assoc_mode) { + sh_video->pts_assoc_mode = opts->user_pts_assoc_mode; + } else if (sh_video->pts_assoc_mode == 0) { + if (sh_video->codec_reordered_pts != MP_NOPTS_VALUE) + 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 ? + sh_video->codec_reordered_pts : sh_video->sorted_pts; +} + +static double update_video(struct MPContext *mpctx) +{ + struct sh_video *sh_video = mpctx->sh_video; + struct vo *video_out = mpctx->video_out; + sh_video->vfilter->control(sh_video->vfilter, VFCTRL_SET_OSD_OBJ, + mpctx->osd); // hack for vf_expand + if (!mpctx->opts.correct_pts) + return update_video_nocorrect_pts(mpctx); + + double pts; + + bool hit_eof = false; + while (!video_out->frame_loaded) { + current_module = "filter_video"; + if (vo_get_buffered_frame(video_out, hit_eof) >= 0) + break; + // XXX Time used in this call is not counted in any performance + // timer now, OSD time is not updated correctly for filter-added frames + if (vf_output_queued_frame(sh_video->vfilter)) + continue; + if (hit_eof) + return -1; + unsigned char *packet = NULL; + int in_size = ds_get_packet_pts(mpctx->d_video, &packet, &pts); + if (pts != MP_NOPTS_VALUE) + pts += mpctx->video_offset; + if (in_size < 0) { + // try to extract last frames in case of decoder lag + in_size = 0; + pts = 1e300; + hit_eof = true; + } + if (in_size > max_framesize) + max_framesize = in_size; + current_module = "decode video"; + int framedrop_type = check_framedrop(mpctx, sh_video->frametime); + void *decoded_frame = decode_video(sh_video, packet, in_size, + framedrop_type, pts); + if (decoded_frame) { + determine_frame_pts(mpctx); + current_module = "filter video"; + if (filter_video(sh_video, decoded_frame, sh_video->pts)) + if (!video_out->config_ok) + break; // We'd likely hang in this loop otherwise + } + } + + pts = video_out->next_pts; + 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; + if (pts == MP_NOPTS_VALUE) + pts = sh_video->last_pts; + } + sh_video->pts = pts; + if (sh_video->last_pts == MP_NOPTS_VALUE) + sh_video->last_pts = sh_video->pts; + else if (sh_video->last_pts > sh_video->pts) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Decreasing video pts: %f < %f\n", + 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; return frame_time; } -static void pause_loop(void) +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) + vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL); + + if (mpctx->audio_out && mpctx->sh_audio) + mpctx->audio_out->pause(); // pause audio, keep data if possible +} + +void unpause_player(struct MPContext *mpctx) +{ + if (!mpctx->paused) + return; + mpctx->paused = 0; + + if (mpctx->audio_out && mpctx->sh_audio) + mpctx->audio_out->resume(); // resume audio + if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok + && !mpctx->step_frames) + vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); // resume video + (void)get_relative_time(mpctx); // ignore time that passed during pause +} + +void add_step_frame(struct MPContext *mpctx) +{ + mpctx->step_frames++; + if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok) + vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL); + unpause_player(mpctx); +} + +static void pause_loop(struct MPContext *mpctx) { mp_cmd_t* cmd; if (!quiet) { // Small hack to display the pause message on the OSD line. // The pause string is: "\n == PAUSE == \r" so we need to // take the first and the last char out - if (term_osd && !mpctx->sh_video) { - char msg[128] = MSGTR_Paused; - int mlen = strlen(msg); - msg[mlen-1] = '\0'; - set_osd_msg(OSD_MSG_PAUSE, 1, 0, "%s", msg+1); - update_osd_msg(); - } else - mp_msg(MSGT_CPLAYER,MSGL_STATUS,MSGTR_Paused); + if (term_osd && !mpctx->sh_video) { + char msg[128] = _("\n ===== PAUSE =====\r"); + int mlen = strlen(msg); + msg[mlen-1] = '\0'; + set_osd_msg(OSD_MSG_PAUSE, 1, 0, "%s", msg+1); + update_osd_msg(mpctx); + } else + mp_tmsg(MSGT_CPLAYER,MSGL_STATUS,"\n ===== PAUSE =====\r"); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_PAUSED\n"); } -#ifdef CONFIG_GUI - if (use_gui) - guiGetEvent(guiCEvent, (char *)guiSetPause); -#endif - if (mpctx->video_out && mpctx->sh_video && vo_config_count) - mpctx->video_out->control(VOCTRL_PAUSE, NULL); - if (mpctx->audio_out && mpctx->sh_audio) - mpctx->audio_out->pause(); // pause audio, keep data if possible - - while ( (cmd = mp_input_get_cmd(20, 1, 1)) == NULL || cmd->pausing == 4) { - if (cmd) { - cmd = mp_input_get_cmd(0,1,0); - run_command(mpctx, cmd); - mp_cmd_free(cmd); - continue; - } - if (mpctx->sh_video && mpctx->video_out && vo_config_count) - mpctx->video_out->check_events(); -#ifdef CONFIG_GUI - if (use_gui) { - guiEventHandling(); - guiGetEvent(guiReDraw, NULL); - if (guiIntfStruct.Playing!=2 || (rel_seek_secs || abs_seek_pos)) - break; - } -#endif + while ( (cmd = mp_input_get_cmd(mpctx->input, 20, 1)) == NULL + || cmd->id == MP_CMD_SET_MOUSE_POS || cmd->pausing == 4) { + if (cmd) { + cmd = mp_input_get_cmd(mpctx->input, 0, 0); + run_command(mpctx, cmd); + mp_cmd_free(cmd); + continue; + } + if (mpctx->sh_video && mpctx->video_out) + vo_check_events(mpctx->video_out); #ifdef CONFIG_MENU if (vf_menu) vf_menu_pause_update(vf_menu); #endif - usec_sleep(20000); - } - if (cmd && cmd->id == MP_CMD_PAUSE) { - cmd = mp_input_get_cmd(0,1,0); - mp_cmd_free(cmd); - } - mpctx->osd_function=OSD_PLAY; - if (mpctx->audio_out && mpctx->sh_audio) { - if (mpctx->eof) // do not play remaining audio if we e.g. switch to the next file - mpctx->audio_out->reset(); - else - mpctx->audio_out->resume(); // resume audio - } - if (mpctx->video_out && mpctx->sh_video && vo_config_count) - mpctx->video_out->control(VOCTRL_RESUME, NULL); // resume video - (void)GetRelativeTime(); // ignore time that passed during pause -#ifdef CONFIG_GUI - if (use_gui) { - if (guiIntfStruct.Playing == guiSetStop) - mpctx->eof = 1; - else - guiGetEvent(guiCEvent, (char *)guiSetPlay); + usec_sleep(20000); + update_osd_msg(mpctx); + int hack = vo_osd_changed(0); + vo_osd_changed(hack); + if (hack) + break; } -#endif } @@ -2511,7 +2624,7 @@ static void edl_update(MPContext *mpctx) return; if (!mpctx->sh_video) { - mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_EdlNOsh_video); + mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Cannot use EDL without video, disabling.\n"); free_edl(edl_records); next_edl_record = NULL; edl_records = NULL; @@ -2521,12 +2634,11 @@ static void edl_update(MPContext *mpctx) if (mpctx->sh_video->pts >= next_edl_record->start_sec) { if (next_edl_record->action == EDL_SKIP) { mpctx->osd_function = OSD_FFW; - abs_seek_pos = 0; - rel_seek_secs = next_edl_record->length_sec; + mpctx->abs_seek_pos = 0; + mpctx->rel_seek_secs = next_edl_record->length_sec; 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); - edl_decision = 1; } else if (next_edl_record->action == EDL_MUTE) { mpctx->edl_muted = !mpctx->edl_muted; @@ -2539,37 +2651,45 @@ static void edl_update(MPContext *mpctx) } } - -// style & SEEK_ABSOLUTE == 0 means seek relative to current position, == 1 means absolute -// style & SEEK_FACTOR == 0 means amount in seconds, == 2 means fraction of file length -// return -1 if seek failed (non-seekable stream?), 0 otherwise -static int seek(MPContext *mpctx, double amount, int style) +static void reinit_decoders(struct MPContext *mpctx) { - current_module = "seek"; - if (demux_seek(mpctx->demuxer, amount, audio_delay, style) == 0) - return -1; + reinit_video_chain(mpctx); + reinit_audio_chain(mpctx); + mp_property_do("sub", M_PROPERTY_SET, &mpctx->global_sub_pos, mpctx); +} - mpctx->startup_decode_retry = DEFAULT_STARTUP_DECODE_RETRY; +static void seek_reset(struct MPContext *mpctx) +{ if (mpctx->sh_video) { current_module = "seek_video_reset"; - if (vo_config_count) - mpctx->video_out->control(VOCTRL_RESET, NULL); - mpctx->num_buffered_frames = 0; + resync_video_stream(mpctx->sh_video); + mpctx->sh_video->timer = 0; + vo_seek_reset(mpctx->video_out); + mpctx->sh_video->timer = 0; + mpctx->sh_video->num_buffered_pts = 0; + mpctx->sh_video->last_pts = MP_NOPTS_VALUE; mpctx->delay = 0; - mpctx->time_frame = 0; + mpctx->time_frame = 0; + mpctx->update_video_immediately = true; // 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; - update_subtitles(mpctx->sh_video, mpctx->sh_video->pts, mpctx->d_sub, 1); + mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset; + update_subtitles(mpctx, &mpctx->opts, mpctx->sh_video, + mpctx->sh_video->pts, mpctx->video_offset, + mpctx->d_sub, 1); update_teletext(mpctx->sh_video, mpctx->demuxer, 1); } if (mpctx->sh_audio) { current_module = "seek_audio_reset"; + resync_audio_stream(mpctx->sh_audio); mpctx->audio_out->reset(); // stop audio, throwing away buffered data + mpctx->sh_audio->a_buffer_len = 0; + mpctx->sh_audio->a_out_buffer_len = 0; if (!mpctx->sh_video) - update_subtitles(NULL, mpctx->sh_audio->pts, mpctx->d_sub, 1); + update_subtitles(mpctx, &mpctx->opts, NULL, mpctx->sh_audio->pts, + mpctx->video_offset, mpctx->d_sub, 1); } if (vo_vobsub && mpctx->sh_video) { @@ -2579,15 +2699,355 @@ static int seek(MPContext *mpctx, double amount, int style) edl_seek_reset(mpctx); - c_total = 0; - max_pts_correction = 0.1; + mpctx->total_avsync_change = 0; audio_time_usage = 0; video_time_usage = 0; vout_time_usage = 0; drop_frame_cnt = 0; current_module = NULL; +} + +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; + uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts.fixed_vo && mpctx->opts.video_id != -2 ? 0 : INITIALIZED_VO) | INITIALIZED_AO | INITIALIZED_ACODEC); + 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; + if (pts < (p+1)->start) { + *need_reset = timeline_set_part(mpctx, i); + return pts - p->start + p->source_start; + } + } + return -1; +} + + +// style & SEEK_ABSOLUTE == 0 means seek relative to current position, == 1 means absolute +// style & SEEK_FACTOR == 0 means amount in seconds, == 2 means fraction of file length +// return -1 if seek failed (non-seekable stream?), 0 otherwise +static int seek(MPContext *mpctx, double amount, int style) +{ + current_module = "seek"; + if (mpctx->stop_play == AT_END_OF_FILE) + mpctx->stop_play = KEEP_PLAYING; + if (style & SEEK_FACTOR + || style & SEEK_ABSOLUTE && amount < mpctx->last_chapter_pts + || amount < 0) + mpctx->last_chapter_seek = -1; + if (mpctx->timeline && style & SEEK_FACTOR) { + amount *= mpctx->timeline[mpctx->num_timeline_parts].start; + style &= ~SEEK_FACTOR; + } + if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && mpctx->sh_video + && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { + style |= SEEK_ABSOLUTE; + if (amount > 0) + style |= SEEK_FORWARD; + else + style |= SEEK_BACKWARD; + amount += mpctx->sh_video->pts; + } + + /* 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; + if (mpctx->timeline) { + amount = timeline_set_from_time(mpctx, amount, &need_reset); + if (amount == -1) { + mpctx->stop_play = AT_END_OF_FILE; + // Clear audio from current position + if (mpctx->sh_audio) { + mpctx->audio_out->reset(); + mpctx->sh_audio->a_buffer_len = 0; + mpctx->sh_audio->a_out_buffer_len = 0; + } + return -1; + } + } + int seekresult = demux_seek(mpctx->demuxer, amount, audio_delay, style); + if (need_reset) + reinit_decoders(mpctx); + if (seekresult == 0) + return -1; + + seek_reset(mpctx); return 0; } +// -2 is no chapters, -1 is before first chapter +int get_current_chapter(struct MPContext *mpctx) +{ + if (!mpctx->chapters || !mpctx->sh_video) + return FFMAX(mpctx->last_chapter_seek, + demuxer_get_current_chapter(mpctx->demuxer)); + + int i; + double current_pts = mpctx->sh_video->pts; + for (i = 1; i < mpctx->num_chapters; i++) + if (current_pts < mpctx->chapters[i].start) + break; + return FFMAX(mpctx->last_chapter_seek, i - 1); +} + +// currently returns a string allocated with malloc, not talloc +char *chapter_display_name(struct MPContext *mpctx, int chapter) +{ + if (!mpctx->chapters || !mpctx->sh_video) + return demuxer_chapter_display_name(mpctx->demuxer, chapter); + return strdup(mpctx->chapters[chapter].name); +} + +int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts, + char **chapter_name) +{ + mpctx->last_chapter_seek = -1; + if (!mpctx->chapters || !mpctx->sh_video) { + int res = demuxer_seek_chapter(mpctx->demuxer, chapter, seek_pts, + chapter_name); + if (res >= 0) { + if (*seek_pts == -1) + seek_reset(mpctx); + else { + mpctx->last_chapter_seek = res; + mpctx->last_chapter_pts = *seek_pts; + } + } + return res; + } + + if (chapter >= mpctx->num_chapters) + return -1; + if (chapter < 0) + chapter = 0; + *seek_pts = mpctx->chapters[chapter].start; + mpctx->last_chapter_seek = chapter; + mpctx->last_chapter_pts = *seek_pts; + if (chapter_name) + *chapter_name = talloc_strdup(NULL, mpctx->chapters[chapter].name); + return chapter; +} + +static int find_ordered_chapter_sources(struct MPContext *mpctx, + struct content_source *sources, + int num_sources, + unsigned char uid_map[][16]) +{ + int num_filenames = 0; + char **filenames = NULL; + if (num_sources > 1) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "This file references data from " + "other sources.\n"); + if (mpctx->stream->type != STREAMTYPE_FILE) { + mp_msg(MSGT_CPLAYER, MSGL_WARN, "Playback source is not a " + "normal disk file. Will not search for related files.\n"); + } else { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Will scan other files in the " + "same directory to find referenced sources.\n"); + filenames = find_files(mpctx->demuxer->filename, ".mkv", + &num_filenames); + } + } + + int num_left = num_sources - 1; + for (int i = 0; i < num_filenames && num_left > 0; i++) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "Checking file %s\n", + filename_recode(filenames[i])); + int format; + struct stream *s = open_stream(filenames[i], &mpctx->opts, &format); + if (!s) + continue; + struct demuxer *d = demux_open(&mpctx->opts, s, DEMUXER_TYPE_MATROSKA, + mpctx->opts.audio_id, + mpctx->opts.video_id, + mpctx->opts.sub_id, filenames[i]); + if (!d) { + free_stream(s); + continue; + } + if (d->file_format == DEMUXER_TYPE_MATROSKA) { + for (int i = 1; i < num_sources; i++) { + if (sources[i].demuxer) + continue; + if (!memcmp(uid_map[i], d->matroska_data.segment_uid, 16)) { + mp_msg(MSGT_CPLAYER, MSGL_INFO,"Match for source %d: %s\n", + i, filename_recode(d->filename)); + sources[i].stream = s; + sources[i].demuxer = d; + num_left--; + goto match; + } + } + } + free_demuxer(d); + free_stream(s); + continue; + match: + ; + } + talloc_free(filenames); + if (num_left) { + mp_msg(MSGT_CPLAYER, MSGL_ERR, "Failed to find ordered chapter part!\n" + "There will be parts MISSING from the video!\n"); + for (int i = 1, j = 1; i < num_sources; i++) + if (sources[i].demuxer) { + sources[j] = sources[i]; + memcpy(uid_map[j], uid_map[i], 16); + j++; + } + } + return num_sources - num_left; +} + +static void build_ordered_chapter_timeline(struct MPContext *mpctx) +{ + if (!mpctx->opts.ordered_chapters) { + mp_msg(MSGT_CPLAYER, MSGL_INFO, "File uses ordered chapters, but " + "you have disabled support for them. Ignoring.\n"); + return; + } + + mp_msg(MSGT_CPLAYER, MSGL_INFO, "File uses ordered chapters, will build " + "edit timeline.\n"); + + struct demuxer *demuxer = mpctx->demuxer; + struct matroska_data *m = &demuxer->matroska_data; + + // +1 because sources/uid_map[0] is original file even if all chapters + // actually use other sources and need separate entries + struct content_source *sources = talloc_array_ptrtype(NULL, sources, + m->num_ordered_chapters+1); + sources[0].stream = mpctx->stream; + sources[0].demuxer = mpctx->demuxer; + unsigned char uid_map[m->num_ordered_chapters+1][16]; + int num_sources = 1; + memcpy(uid_map[0], m->segment_uid, 16); + + for (int i = 0; i < m->num_ordered_chapters; i++) { + struct matroska_chapter *c = m->ordered_chapters + i; + if (!c->has_segment_uid) + memcpy(c->segment_uid, m->segment_uid, 16); + + for (int j = 0; j < num_sources; j++) + if (!memcmp(c->segment_uid, uid_map[j], 16)) + goto found1; + memcpy(uid_map[num_sources], c->segment_uid, 16); + sources[num_sources] = (struct content_source){}; + num_sources++; + found1: + ; + } + + num_sources = find_ordered_chapter_sources(mpctx, sources, num_sources, + uid_map); + + + // +1 for terminating chapter with start time marking end of last real one + struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline, + m->num_ordered_chapters + 1); + struct chapter *chapters = talloc_array_ptrtype(NULL, chapters, + m->num_ordered_chapters); + uint64_t starttime = 0; + uint64_t missing_time = 0; + int part_count = 0; + int num_chapters = 0; + uint64_t prev_part_offset = 0; + for (int i = 0; i < m->num_ordered_chapters; i++) { + struct matroska_chapter *c = m->ordered_chapters + i; + + int j; + for (j = 0; j < num_sources; j++) { + if (!memcmp(c->segment_uid, uid_map[j], 16)) + goto found2; + } + missing_time += c->end - c->start; + continue; + found2:; + chapters[num_chapters].start = starttime / 1000.; + chapters[num_chapters].name = talloc_strdup(chapters, c->name); + /* Only add a separate part if the time or file actually changes. + * Matroska files have chapter divisions that are redundant from + * timeline point of view because the same chapter structure is used + * both to specify the timeline and for normal chapter information. + * Removing a missing inserted external chapter can also cause this. */ + if (part_count == 0 || c->start != starttime + prev_part_offset + || sources + j != timeline[part_count - 1].source) { + timeline[part_count].source = sources + j; + timeline[part_count].start = chapters[num_chapters].start; + timeline[part_count].source_start = c->start / 1000.; + prev_part_offset = c->start - starttime; + part_count++; + } + starttime += c->end - c->start; + num_chapters++; + } + timeline[part_count].start = starttime / 1000.; + + if (!part_count) { + // None of the parts come from the file itself??? + talloc_free(sources); + talloc_free(timeline); + talloc_free(chapters); + return; + } + + mp_msg(MSGT_CPLAYER, MSGL_V, "Timeline contains %d parts from %d " + "sources. Total length %.3f seconds.\n", part_count, num_sources, + timeline[part_count].start); + if (missing_time) + mp_msg(MSGT_CPLAYER, MSGL_ERR, "There are %.3f seconds missing " + "from the timeline!\n", missing_time / 1000.); + mp_msg(MSGT_CPLAYER, MSGL_V, "Source files:\n"); + for (int i = 0; i < num_sources; i++) + mp_msg(MSGT_CPLAYER, MSGL_V, "%d: %s\n", i, + filename_recode(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 = timeline + i; + mp_msg(MSGT_CPLAYER, MSGL_V, "%3d %9.3f %9.3f %3td\n", i, p->start, + p->source_start, p->source - sources); + } + mp_msg(MSGT_CPLAYER, MSGL_V, "END %9.3f\n", timeline[part_count].start); + mpctx->sources = sources; + mpctx->num_sources = num_sources; + mpctx->timeline = timeline; + mpctx->num_timeline_parts = part_count; + mpctx->num_chapters = num_chapters; + mpctx->chapters = chapters; + + mpctx->timeline_part = 0; + mpctx->demuxer = timeline[0].source->demuxer; +} + + +static int read_keys(void *ctx, int fd) +{ + getch2(ctx); + return mplayer_get_key(ctx, 0); +} + + /* This preprocessor directive is a hack to generate a mplayer-nomain.o object * file for some tools to link against. */ #ifndef DISABLE_MAIN @@ -2605,20 +3065,34 @@ int opt_exit = 0; int i; -int gui_no_filename=0; + struct MPContext *mpctx = &(struct MPContext){ + .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, + }; InitTimer(); srand(GetTimerMS()); mp_msg_init(); + set_av_log_callback(); +#ifdef CONFIG_X11 + mpctx->x11_state = vo_x11_init_state(); +#endif + struct MPOpts *opts = &mpctx->opts; + set_default_mplayer_options(opts); // Create the config context and register the options - mconfig = m_config_new(); - m_config_register_options(mconfig,mplayer_opts); - mp_input_register_options(mconfig); + mpctx->mconfig = m_config_new(opts, cfg_include); + m_config_register_options(mpctx->mconfig,mplayer_opts); + mp_input_register_options(mpctx->mconfig); // Preparse the command line - m_config_preparse_command_line(mconfig,argc,argv); + m_config_preparse_command_line(mpctx->mconfig,argc,argv); #if (defined(__MINGW32__) || defined(__CYGWIN__)) && defined(CONFIG_WIN32DLL) set_path_env(); @@ -2628,62 +3102,29 @@ int gui_no_filename=0; stream_tv_defaults.immediate = 1; #endif - if (argc > 1 && argv[1] && - (!strcmp(argv[1], "-gui") || !strcmp(argv[1], "-nogui"))) { - use_gui = !strcmp(argv[1], "-gui"); - } else - if ( argv[0] ) - { - char *base = strrchr(argv[0], '/'); - if (!base) - base = strrchr(argv[0], '\\'); - if (!base) - base = argv[0]; - if(strstr(base, "gmplayer")) - use_gui=1; - } - - parse_cfgfiles(mconfig); + parse_cfgfiles(mpctx, mpctx->mconfig); -#ifdef CONFIG_GUI - if ( use_gui ) cfg_read(); -#endif - - mpctx->playtree = m_config_parse_mp_command_line(mconfig, argc, argv); + mpctx->playtree = m_config_parse_mp_command_line(mpctx->mconfig, argc, argv); if(mpctx->playtree == NULL) opt_exit = 1; else { mpctx->playtree = play_tree_cleanup(mpctx->playtree); if(mpctx->playtree) { - mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree,mconfig); + 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; } - filename = play_tree_iter_get_file(mpctx->playtree_iter,1); + mpctx->filename = play_tree_iter_get_file(mpctx->playtree_iter,1); } } } + mpctx->key_fifo = mp_fifo_create(opts); print_version("MPlayer"); #if defined(__MINGW32__) || defined(__CYGWIN__) -#ifdef CONFIG_GUI - void *runningmplayer = FindWindow("MPlayer GUI for Windows", "MPlayer for Windows"); - if(runningmplayer && filename && use_gui){ - COPYDATASTRUCT csData; - char file[MAX_PATH]; - char *filepart = filename; - if(GetFullPathName(filename, MAX_PATH, file, &filepart)){ - csData.dwData = 0; - csData.cbData = strlen(file)*2; - csData.lpData = file; - SendMessage(runningmplayer, WM_COPYDATA, (WPARAM)runningmplayer, (LPARAM)&csData); - } - } -#endif - { HMODULE kernel32 = GetModuleHandle("Kernel32.dll"); BOOL WINAPI (*setDEP)(DWORD) = NULL; @@ -2704,41 +3145,12 @@ int gui_no_filename=0; if (codec_path) set_codec_path(codec_path); -#ifndef CONFIG_GUI - if(use_gui){ - mp_msg(MSGT_CPLAYER,MSGL_WARN,MSGTR_NoGui); - use_gui=0; - } -#else -#if !defined(__MINGW32__) && !defined(__CYGWIN__) - if(use_gui && !vo_init()){ - mp_msg(MSGT_CPLAYER,MSGL_WARN,MSGTR_GuiNeedsX); - use_gui=0; - } -#endif - if (use_gui && mpctx->playtree_iter){ - char cwd[PATH_MAX+2]; - // Free Playtree and Playtree-Iter as it's not used by the GUI. - play_tree_iter_free(mpctx->playtree_iter); - mpctx->playtree_iter=NULL; - - if (getcwd(cwd, PATH_MAX) != (char *)NULL) - { - strcat(cwd, "/"); - // Prefix relative paths with current working directory - play_tree_add_bpf(mpctx->playtree, cwd); - } - // Import initital playtree into GUI. - import_initial_playtree_into_gui(mpctx->playtree, mconfig, enqueue); - } -#endif /* CONFIG_GUI */ - - if(video_driver_list && strcmp(video_driver_list[0],"help")==0){ + if(opts->video_driver_list && strcmp(opts->video_driver_list[0],"help")==0){ list_video_out(); opt_exit = 1; } - if(audio_driver_list && strcmp(audio_driver_list[0],"help")==0){ + if(opts->audio_driver_list && strcmp(opts->audio_driver_list[0],"help")==0){ list_audio_out(); opt_exit = 1; } @@ -2748,9 +3160,9 @@ 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(EXIT_NONE, 0); + exit_player_with_rc(mpctx, EXIT_NONE, 0); } - mp_msg(MSGT_CPLAYER,MSGL_V,MSGTR_BuiltinCodecsConf); + mp_tmsg(MSGT_CPLAYER,MSGL_V,"Using built-in default codecs.conf.\n"); } } free( mem_ptr ); // release the buffer created by get_path() @@ -2765,14 +3177,14 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ } #endif if(audio_codec_list && strcmp(audio_codec_list[0],"help")==0){ - mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_AvailableAudioCodecs); + 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_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_AvailableVideoCodecs); + 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"); @@ -2813,19 +3225,12 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ } if(opt_exit) - exit_player(EXIT_NONE); - - if (player_idle_mode && use_gui) { - mp_msg(MSGT_CPLAYER, MSGL_FATAL, MSGTR_NoIdleAndGui); - exit_player_with_rc(EXIT_NONE, 1); - } + exit_player(mpctx, EXIT_NONE); - if(!filename && !player_idle_mode){ - if(!use_gui){ + if(!mpctx->filename && !player_idle_mode){ // no file/vcd/dvd -> show HELP: - mp_msg(MSGT_CPLAYER, MSGL_INFO, help_text); - exit_player_with_rc(EXIT_NONE, 0); - } else gui_no_filename=1; + mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", mp_gtext(help_text)); + exit_player_with_rc(mpctx, EXIT_NONE, 0); } /* Display what configure line was used */ @@ -2833,13 +3238,15 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ // Many users forget to include command line in bugreports... if( mp_msg_test(MSGT_CPLAYER,MSGL_V) ){ - mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_CommandLine); + 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"); } //------ load global data first ------ + mpctx->osd = osd_create(); + // check font #ifdef CONFIG_FREETYPE init_freetype(); @@ -2851,7 +3258,7 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ #ifdef CONFIG_BITMAP_FONT if(font_name){ vo_font=read_font_desc(font_name,font_factor,verbose>1); - if(!vo_font) mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CantLoadFont, + if(!vo_font) mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Cannot load bitmap font: %s\n", filename_recode(font_name)); } else { // try default: @@ -2861,16 +3268,14 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ vo_font=read_font_desc(MPLAYER_DATADIR "/font/font.desc",font_factor,verbose>1); } if (sub_font_name) - sub_font = read_font_desc(sub_font_name, font_factor, verbose>1); + mpctx->osd->sub_font = read_font_desc(sub_font_name, font_factor, verbose>1); else - sub_font = vo_font; + mpctx->osd->sub_font = vo_font; #endif #ifdef CONFIG_FONTCONFIG } #endif - vo_init_osd(); - #ifdef CONFIG_ASS ass_library = ass_init(); #endif @@ -2880,65 +3285,61 @@ if(!codecs_file || !parse_codec_cfg(codecs_file)){ { // 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_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_RTCDeviceNotOpenable, + 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_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_LinuxRTCInitErrorIrqpSet, irqp, strerror(errno)); - mp_msg(MSGT_CPLAYER, MSGL_HINT, MSGTR_IncreaseRTCMaxUserFreq, irqp); + 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_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_LinuxRTCInitErrorPieOn, strerror(errno)); + 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_msg(MSGT_CPLAYER, MSGL_V, MSGTR_UsingRTCTiming, irqp); + mp_tmsg(MSGT_CPLAYER, MSGL_V, "Using Linux hardware RTC timing (%ldHz).\n", irqp); } } -#ifdef CONFIG_GUI -// breaks DGA and SVGAlib and VESA drivers: --A'rpi -// and now ? -- Pontscho - if(use_gui) setuid( getuid() ); // strongly test, please check this. -#endif if(rtc_fd<0) #endif /* HAVE_RTC */ mp_msg(MSGT_CPLAYER, MSGL_V, "Using %s timing\n", softsleep?"software":timer_name); #ifdef HAVE_TERMCAP - if ( !use_gui ) load_termcap(NULL); // load key-codes + load_termcap(NULL); // load key-codes #endif // ========== Init keyboard FIFO (connection to libvo) ============ // Init input system current_module = "init_input"; -mp_input_init(use_gui); - mp_input_add_key_fd(-1,0,mplayer_get_key,NULL); + mpctx->input = mp_input_init(&opts->input); + mp_input_add_key_fd(mpctx->input, -1,0,mplayer_get_key,NULL, mpctx->key_fifo); if(slave_mode) - mp_input_add_cmd_fd(0,USE_SELECT,MP_INPUT_SLAVE_CMD_FUNC,NULL); + mp_input_add_cmd_fd(mpctx->input, 0,USE_SELECT,MP_INPUT_SLAVE_CMD_FUNC,NULL); else if(!noconsolecontrols) - mp_input_add_event_fd(0, getch2); + 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); +stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input); #ifdef CONFIG_MENU if(use_menu) { - if(menu_cfg && menu_init(mpctx, menu_cfg)) - mp_msg(MSGT_CPLAYER, MSGL_V, MSGTR_MenuInitialized, menu_cfg); + if(menu_cfg && menu_init(mpctx, mpctx->mconfig, mpctx->input, menu_cfg)) + mp_tmsg(MSGT_CPLAYER, MSGL_V, "Menu initialized: %s\n", menu_cfg); else { menu_cfg = get_path("menu.conf"); - if(menu_init(mpctx, menu_cfg)) - mp_msg(MSGT_CPLAYER, MSGL_V, MSGTR_MenuInitialized, menu_cfg); + if(menu_init(mpctx, mpctx->mconfig, mpctx->input, menu_cfg)) + mp_tmsg(MSGT_CPLAYER, MSGL_V, "Menu initialized: %s\n", menu_cfg); else { - if(menu_init(mpctx, MPLAYER_CONFDIR "/menu.conf")) - mp_msg(MSGT_CPLAYER, MSGL_V, MSGTR_MenuInitialized, MPLAYER_CONFDIR"/menu.conf"); + if(menu_init(mpctx, mpctx->mconfig, mpctx->input, + MPLAYER_CONFDIR "/menu.conf")) + mp_tmsg(MSGT_CPLAYER, MSGL_V, "Menu initialized: %s\n", MPLAYER_CONFDIR"/menu.conf"); else { - mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_MenuInitFailed); + mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Menu init failed.\n"); use_menu = 0; } } @@ -2946,7 +3347,6 @@ stream_set_interrupt_callback(mp_input_check_interrupt); } #endif -initialized_flags|=INITIALIZED_INPUT; current_module = NULL; /// Catch signals @@ -2979,15 +3379,6 @@ current_module = NULL; #endif #endif -#ifdef CONFIG_GUI - if(use_gui){ - guiInit(); - guiGetEvent(guiSetContext, mpctx); - initialized_flags|=INITIALIZED_GUI; - guiGetEvent( guiCEvent,(char *)((gui_no_filename) ? 0 : 1) ); - } -#endif - // ******************* Now, let's see the per-file stuff ******************** play_next_file: @@ -2996,76 +3387,37 @@ play_next_file: mpctx->global_sub_size = 0; { int i; for (i = 0; i < SUB_SOURCES; i++) mpctx->global_sub_indices[i] = -1; } - if (filename) { - load_per_protocol_config (mconfig, filename); - load_per_extension_config (mconfig, filename); - load_per_file_config (mconfig, filename); + 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); } - if (video_driver_list) - load_per_output_config (mconfig, PROFILE_CFG_VO, video_driver_list[0]); - if (audio_driver_list) - load_per_output_config (mconfig, PROFILE_CFG_AO, audio_driver_list[0]); + 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(!noconsolecontrols && !slave_mode){ - if(initialized_flags&INITIALIZED_GETCH2) - mp_msg(MSGT_CPLAYER,MSGL_WARN,MSGTR_Getch2InitializedTwice); + 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... - initialized_flags|=INITIALIZED_GETCH2; + mpctx->initialized_flags|=INITIALIZED_GETCH2; mp_msg(MSGT_CPLAYER,MSGL_DBG2,"\n[[[init getch2]]]\n"); } // =================== GUI idle loop (STOP state) =========================== -#ifdef CONFIG_GUI - if ( use_gui ) { - mpctx->file_format=DEMUXER_TYPE_UNKNOWN; - guiGetEvent( guiSetDefaults,0 ); - while ( guiIntfStruct.Playing != 1 ) - { - mp_cmd_t* cmd; - usec_sleep(20000); - guiEventHandling(); - guiGetEvent( guiReDraw,NULL ); - if ( (cmd = mp_input_get_cmd(0,0,0)) != NULL) { - guiGetEvent(guiIEvent, (char *)cmd->id); - mp_cmd_free(cmd); - } - } - guiGetEvent( guiSetParameters,NULL ); - if ( guiIntfStruct.StreamType == STREAMTYPE_STREAM ) - { - play_tree_t * entry = play_tree_new(); - play_tree_add_file( entry,guiIntfStruct.Filename ); - if ( mpctx->playtree ) play_tree_free_list( mpctx->playtree->child,1 ); - else mpctx->playtree=play_tree_new(); - play_tree_set_child( mpctx->playtree,entry ); - if(mpctx->playtree) - { - mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree,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; - } - filename = play_tree_iter_get_file(mpctx->playtree_iter,1); - } - } - } - } -#endif /* CONFIG_GUI */ - -while (player_idle_mode && !filename) { +while (player_idle_mode && !mpctx->filename) { play_tree_t * entry = NULL; mp_cmd_t * cmd; - if (mpctx->video_out && vo_config_count) - mpctx->video_out->control(VOCTRL_PAUSE, NULL); - while (!(cmd = mp_input_get_cmd(0,1,0))) { // wait for command - if (mpctx->video_out && vo_config_count) mpctx->video_out->check_events(); + 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))) { // wait for command + if (mpctx->video_out) + vo_check_events(mpctx->video_out); usec_sleep(20000); } switch (cmd->id) { @@ -3076,10 +3428,10 @@ while (player_idle_mode && !filename) { // The entry is added to the main playtree after the switch(). break; case MP_CMD_LOADLIST: - entry = parse_playlist_file(cmd->args[0].v.s); + entry = parse_playlist_file(mpctx->mconfig, cmd->args[0].v.s); break; case MP_CMD_QUIT: - exit_player_with_rc(EXIT_QUIT, (cmd->nargs > 0)? cmd->args[0].v.i : 0); + exit_player_with_rc(mpctx, EXIT_QUIT, (cmd->nargs > 0)? cmd->args[0].v.i : 0); break; case MP_CMD_GET_PROPERTY: case MP_CMD_SET_PROPERTY: @@ -3100,7 +3452,7 @@ while (player_idle_mode && !filename) { play_tree_set_child(mpctx->playtree, entry); /* Make iterator start at the top the of tree. */ - mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree, mconfig); + mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree, mpctx->mconfig); if (!mpctx->playtree_iter) continue; // find the first real item in the tree @@ -3110,19 +3462,19 @@ while (player_idle_mode && !filename) { mpctx->playtree_iter = NULL; continue; // wait for next command } - filename = play_tree_iter_get_file(mpctx->playtree_iter, 1); + mpctx->filename = play_tree_iter_get_file(mpctx->playtree_iter, 1); } } //--------------------------------------------------------------------------- - if (mpctx->video_out && vo_config_count) - mpctx->video_out->control(VOCTRL_RESUME, NULL); + if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok) + vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); - if(filename) { - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_Playing, - filename_recode(filename)); + if (mpctx->filename) { + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"\nPlaying %s.\n", + filename_recode(mpctx->filename)); if(use_filename_title && vo_wintitle == NULL) - vo_wintitle = strdup ( mp_basename2 (filename)); + vo_wintitle = strdup(mp_basename2(mpctx->filename)); } if (edl_filename) { @@ -3133,7 +3485,7 @@ if (edl_output_filename) { if (edl_fd) fclose(edl_fd); if ((edl_fd = fopen(edl_output_filename, "w")) == NULL) { - mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_EdlCantOpenForWrite, + mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Can't open EDL file [%s] for writing.\n", filename_recode(edl_output_filename)); } } @@ -3144,11 +3496,11 @@ if (edl_output_filename) { if (vobsub_name){ vo_vobsub=vobsub_open(vobsub_name,spudec_ifo,1,&vo_spudec); if(vo_vobsub==NULL) - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CantLoadSub, + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Cannot load subtitles: %s\n", filename_recode(vobsub_name)); - } else if (sub_auto && filename){ + } else if (sub_auto && mpctx->filename){ /* try to autodetect vobsub from movie filename ::atmos */ - char *buf = strdup(filename), *psub; + char *buf = strdup(mpctx->filename), *psub; char *pdot = strrchr(buf, '.'); char *pslash = strrchr(buf, '/'); #if defined(__MINGW32__) || defined(__CYGWIN__) @@ -3176,7 +3528,7 @@ if (edl_output_filename) { free(buf); } if(vo_vobsub){ - initialized_flags|=INITIALIZED_VOBSUB; + mpctx->initialized_flags|=INITIALIZED_VOBSUB; vobsub_set_from_lang(vo_vobsub, dvdsub_lang); mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only, mpctx); @@ -3201,26 +3553,28 @@ if (edl_output_filename) { mpctx->sh_video=NULL; current_module="open_stream"; - mpctx->stream=open_stream(filename,0,&mpctx->file_format); + mpctx->stream = open_stream(mpctx->filename, opts, &mpctx->file_format); if(!mpctx->stream) { // error... - mpctx->eof = libmpdemux_was_interrupted(PT_NEXT_ENTRY); + mpctx->stop_play = libmpdemux_was_interrupted(mpctx, PT_NEXT_ENTRY); goto goto_next_file; } - initialized_flags|=INITIALIZED_STREAM; - -#ifdef CONFIG_GUI - if ( use_gui ) guiGetEvent( guiSetStream,(char *)mpctx->stream ); -#endif + 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"); +#if 0 play_tree_t* entry; // Handle playlist current_module="handle_playlist"; mp_msg(MSGT_CPLAYER,MSGL_V,"Parsing playlist %s...\n", - filename_recode(filename)); - entry = parse_playtree(mpctx->stream,0); - mpctx->eof=playtree_add_playlist(entry); + filename_recode(mpctx->filename)); + entry = parse_playtree(mpctx->stream, mpctx->mconfig, 0); + mpctx->eof=playtree_add_playlist(mpctx, entry); goto goto_next_file; +#endif } mpctx->stream->start_pos+=seek_to_byte; @@ -3233,41 +3587,42 @@ if(stream_dump_type==5){ stream_seek(mpctx->stream,mpctx->stream->start_pos); f=fopen(stream_dump_name,"wb"); if(!f){ - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_CantOpenDumpfile); - exit_player(EXIT_ERROR); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,"Cannot open dump file.\n"); + exit_player(mpctx, EXIT_ERROR); } - if (dvd_chapter > 1) { - int chapter = dvd_chapter - 1; + if (opts->chapterrange[0] > 1) { + int chapter = opts->chapterrange[0] - 1; stream_control(mpctx->stream, STREAM_CTRL_SEEK_TO_CHAPTER, &chapter); } 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_msg(MSGT_MENCODER,MSGL_FATAL,MSGTR_ErrorWritingFile,stream_dump_name); - exit_player(EXIT_ERROR); + mp_tmsg(MSGT_MENCODER,MSGL_FATAL,"%s: Error writing file.\n",stream_dump_name); + exit_player(mpctx, EXIT_ERROR); } } - if(dvd_last_chapter > 0) { + if (opts->chapterrange[1] > 0) { int chapter = -1; if (stream_control(mpctx->stream, STREAM_CTRL_GET_CURRENT_CHAPTER, - &chapter) == STREAM_OK && chapter + 1 > dvd_last_chapter) + &chapter) == STREAM_OK + && chapter + 1 > opts->chapterrange[1]) break; } } if(fclose(f)) { - mp_msg(MSGT_MENCODER,MSGL_FATAL,MSGTR_ErrorWritingFile,stream_dump_name); - exit_player(EXIT_ERROR); + mp_tmsg(MSGT_MENCODER,MSGL_FATAL,"%s: Error writing file.\n",stream_dump_name); + exit_player(mpctx, EXIT_ERROR); } - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_CoreDumped); - exit_player_with_rc(EXIT_EOF, 0); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Core dumped ;)\n"); + exit_player_with_rc(mpctx, EXIT_EOF, 0); } #ifdef CONFIG_DVDREAD if(mpctx->stream->type==STREAMTYPE_DVD){ current_module="dvd lang->id"; - if(audio_id==-1) audio_id=dvd_aid_from_lang(mpctx->stream,audio_lang); - if(dvdsub_lang && dvdsub_id==-1) dvdsub_id=dvd_sid_from_lang(mpctx->stream,dvdsub_lang); + if(opts->audio_id==-1) opts->audio_id=dvd_aid_from_lang(mpctx->stream,audio_lang); + if(dvdsub_lang && opts->sub_id==-1) opts->sub_id=dvd_sid_from_lang(mpctx->stream,dvdsub_lang); // setup global sub numbering mpctx->global_sub_indices[SUB_SOURCE_DEMUX] = mpctx->global_sub_size; // the global # of the first demux-specific sub. mpctx->global_sub_size += dvd_number_of_subs(mpctx->stream); @@ -3278,10 +3633,10 @@ if(mpctx->stream->type==STREAMTYPE_DVD){ #ifdef CONFIG_DVDNAV if(mpctx->stream->type==STREAMTYPE_DVDNAV){ current_module="dvdnav lang->id"; - if(audio_id==-1) audio_id=mp_dvdnav_aid_from_lang(mpctx->stream,audio_lang); + if(opts->audio_id==-1) opts->audio_id=mp_dvdnav_aid_from_lang(mpctx->stream,audio_lang); dvdsub_lang_id = -3; - if(dvdsub_lang && dvdsub_id==-1) - dvdsub_lang_id=dvdsub_id=mp_dvdnav_sid_from_lang(mpctx->stream,dvdsub_lang); + if(dvdsub_lang && opts->sub_id==-1) + dvdsub_lang_id = opts->sub_id = mp_dvdnav_sid_from_lang(mpctx->stream,dvdsub_lang); // setup global sub numbering mpctx->global_sub_indices[SUB_SOURCE_DEMUX] = mpctx->global_sub_size; // the global # of the first demux-specific sub. mpctx->global_sub_size += mp_dvdnav_number_of_subs(mpctx->stream); @@ -3298,13 +3653,13 @@ if(stream_cache_size>0){ 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->eof = libmpdemux_was_interrupted(PT_NEXT_ENTRY))) goto goto_next_file; + if((mpctx->stop_play = libmpdemux_was_interrupted(mpctx, PT_NEXT_ENTRY))) goto goto_next_file; } //============ Open DEMUXERS --- DETECT file type ======================= current_module="demux_open"; -mpctx->demuxer=demux_open(mpctx->stream,mpctx->file_format,audio_id,video_id,dvdsub_id,filename); +mpctx->demuxer=demux_open(opts, mpctx->stream,mpctx->file_format,opts->audio_id,opts->video_id,opts->sub_id,mpctx->filename); // HACK to get MOV Reference Files working @@ -3325,20 +3680,20 @@ if (mpctx->demuxer && mpctx->demuxer->type==DEMUXER_TYPE_PLAYLIST) if ((strlen(bname)>10) && !strncmp(bname,"qt",2) && !strncmp(bname+3,"gateQT",6)) continue; - if (!strcmp(playlist_entry,filename)) // ignoring self-reference + if (!strcmp(playlist_entry, mpctx->filename)) // ignoring self-reference continue; entry = play_tree_new(); - if (filename && !strcmp(mp_basename(playlist_entry),playlist_entry)) // add reference path of current file + if (mpctx->filename && !strcmp(mp_basename(playlist_entry),playlist_entry)) // add reference path of current file { - temp=malloc((strlen(filename)-strlen(mp_basename(filename))+strlen(playlist_entry)+1)); + temp=malloc((strlen(mpctx->filename)-strlen(mp_basename(mpctx->filename))+strlen(playlist_entry)+1)); if (temp) { - strncpy(temp, filename, strlen(filename)-strlen(mp_basename(filename))); - temp[strlen(filename)-strlen(mp_basename(filename))]='\0'; + 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, filename)) { + if (!strcmp(temp, mpctx->filename)) { free(temp); continue; } @@ -3362,20 +3717,25 @@ if (mpctx->demuxer && mpctx->demuxer->type==DEMUXER_TYPE_PLAYLIST) { entry = play_tree_new(); play_tree_set_child(entry,list); - mpctx->eof=playtree_add_playlist(entry); + mpctx->stop_play = playtree_add_playlist(mpctx, entry); goto goto_next_file; } } if(!mpctx->demuxer) goto goto_next_file; -if(dvd_chapter>1) { - float pts; - if (demuxer_seek_chapter(mpctx->demuxer, dvd_chapter-1, 1, &pts, NULL, NULL) >= 0 && pts > -1.0) - seek(mpctx, pts, SEEK_ABSOLUTE); -} -initialized_flags|=INITIALIZED_DEMUXER; + if (mpctx->demuxer->matroska_data.ordered_chapters) + build_ordered_chapter_timeline(mpctx); + + 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; + } + +mpctx->initialized_flags|=INITIALIZED_DEMUXER; if (mpctx->stream->type != STREAMTYPE_DVD && mpctx->stream->type != STREAMTYPE_DVDNAV) { int i; @@ -3384,23 +3744,26 @@ if (mpctx->stream->type != STREAMTYPE_DVD && mpctx->stream->type != STREAMTYPE_D mpctx->global_sub_indices[SUB_SOURCE_DEMUX] = mpctx->global_sub_size; // the global # of the first demux-specific sub. for (i = 0; i < MAX_S_STREAMS; i++) if (mpctx->demuxer->s_streams[i]) - maxid = FFMAX(maxid, ((sh_sub_t *)mpctx->demuxer->s_streams[i])->sid); + maxid = FFMAX(maxid, mpctx->demuxer->s_streams[i]->sid); mpctx->global_sub_size += maxid + 1; } -// Make dvdsub_id always selectable if set. -if (mpctx->global_sub_size <= mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvdsub_id) - mpctx->global_sub_size = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + dvdsub_id + 1; +// Make opts->sub_id always selectable if set. +if (mpctx->global_sub_size <= mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + opts->sub_id) + mpctx->global_sub_size = mpctx->global_sub_indices[SUB_SOURCE_DEMUX] + opts->sub_id + 1; #ifdef CONFIG_ASS -if (ass_enabled && ass_library) { - for (i = 0; i < mpctx->demuxer->num_attachments; ++i) { - demux_attachment_t* att = mpctx->demuxer->attachments + i; - if (extract_embedded_fonts && - att->name && att->type && att->data && att->data_size && - (strcmp(att->type, "application/x-truetype-font") == 0 || - strcmp(att->type, "application/x-font") == 0)) - ass_add_font(ass_library, att->name, att->data, att->data_size); - } +if (opts->ass_enabled && 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; + if (use_embedded_fonts + && att->name && att->type && att->data && att->data_size + && (strcmp(att->type, "application/x-truetype-font") == 0 + || strcmp(att->type, "application/x-font") == 0)) + ass_add_font(ass_library, att->name, att->data, att->data_size); + } + } } #endif @@ -3417,7 +3780,7 @@ if (ts_prog) { mp_property_do("switch_program", M_PROPERTY_SET, &tmp, mpctx); } // select audio stream -select_audio(mpctx->demuxer, audio_id, audio_lang); +select_audio(mpctx->demuxer, opts->audio_id, audio_lang); // DUMP STREAMS: if((stream_dump_type)&&(stream_dump_type<4)){ @@ -3431,8 +3794,8 @@ if((stream_dump_type)&&(stream_dump_type<4)){ case 3: ds=mpctx->d_sub;break; } if(!ds){ - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_DumpSelectedStreamMissing); - exit_player(EXIT_ERROR); + 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; } @@ -3441,8 +3804,8 @@ if((stream_dump_type)&&(stream_dump_type<4)){ // let's dump it! f=fopen(stream_dump_name,"wb"); if(!f){ - mp_msg(MSGT_CPLAYER,MSGL_FATAL,MSGTR_CantOpenDumpfile); - exit_player(EXIT_ERROR); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL,"Cannot open dump file.\n"); + exit_player(mpctx, EXIT_ERROR); } while(!ds->eof){ unsigned char* start; @@ -3450,15 +3813,15 @@ if((stream_dump_type)&&(stream_dump_type<4)){ 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); - if(dvd_last_chapter>0) { + if (opts->chapterrange[1] > 0) { int cur_chapter = demuxer_get_current_chapter(mpctx->demuxer); - if(cur_chapter!=-1 && cur_chapter+1>dvd_last_chapter) + if(cur_chapter!=-1 && cur_chapter+1 > opts->chapterrange[1]) break; } } fclose(f); - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_CoreDumped); - exit_player_with_rc(EXIT_EOF, 0); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"Core dumped ;)\n"); + exit_player_with_rc(mpctx, EXIT_EOF, 0); } mpctx->sh_audio=mpctx->d_audio->sh; @@ -3468,10 +3831,10 @@ if(mpctx->sh_video){ current_module="video_read_properties"; if(!video_read_properties(mpctx->sh_video)) { - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_CannotReadVideoProperties); + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Video: Cannot read properties.\n"); mpctx->sh_video=mpctx->d_video->sh=NULL; } else { - mp_msg(MSGT_CPLAYER,MSGL_V,MSGTR_FilefmtFourccSizeFpsFtime, + 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, mpctx->sh_video->fps,mpctx->sh_video->frametime ); @@ -3484,7 +3847,7 @@ if(mpctx->sh_video){ vo_fps = mpctx->sh_video->fps; if(!mpctx->sh_video->fps && !force_fps){ - mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_FPSnotspecified); + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"FPS not specified in the header or invalid, use the -fps option.\n"); mpctx->sh_video=mpctx->d_video->sh=NULL; } } @@ -3492,7 +3855,7 @@ if(mpctx->sh_video){ } if(!mpctx->sh_video && !mpctx->sh_audio){ - mp_msg(MSGT_CPLAYER,MSGL_FATAL, MSGTR_NoStreamFound); + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL, "No stream found.\n"); #ifdef CONFIG_DVBIN if(mpctx->stream->type == STREAMTYPE_DVB) { @@ -3503,11 +3866,13 @@ if(!mpctx->sh_video && !mpctx->sh_audio){ else dir = DVB_CHANNEL_LOWER; - if(dvb_step_channel(mpctx->stream, dir)) - mpctx->eof = mpctx->dvbin_reopen = 1; + if(dvb_step_channel(mpctx->stream, dir)) { + mpctx->stop_play = PT_NEXT_ENTRY; + mpctx->dvbin_reopen = 1; + } } #endif - goto goto_next_file; // exit_player(MSGTR_Exit_error); + goto goto_next_file; // exit_player(_("Fatal error")); } /* display clip info */ @@ -3516,26 +3881,25 @@ 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(); + init_vo_spudec(mpctx); } -if(1 || mpctx->sh_video) { // 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 - double fps = mpctx->sh_video ? mpctx->sh_video->fps : 25; current_module="read_subtitles_file"; + double sub_fps = mpctx->sh_video ? mpctx->sh_video->fps : 25; if(sub_name){ for (i = 0; sub_name[i] != NULL; ++i) - add_subtitles (sub_name[i], fps, 0); + add_subtitles(mpctx, sub_name[i], sub_fps, 0); } if(sub_auto) { // auto load sub file ... char *psub = get_path( "sub/" ); - char **tmp = sub_filenames((psub ? psub : ""), filename); + char **tmp = sub_filenames((psub ? psub : ""), mpctx->filename); int i = 0; free(psub); // release the buffer created by get_path() above while (tmp[i]) { - add_subtitles (tmp[i], fps, 1); + add_subtitles(mpctx, tmp[i], sub_fps, 1); free(tmp[i++]); } free(tmp); @@ -3545,7 +3909,7 @@ if(1 || mpctx->sh_video) { mpctx->global_sub_indices[SUB_SOURCE_SUBS] = mpctx->global_sub_size; // the global # of the first sub. mpctx->global_sub_size += mpctx->set_of_sub_size; } -} + if (mpctx->global_sub_size) { select_subtitle(mpctx); @@ -3561,7 +3925,7 @@ if (mpctx->global_sub_size) { } mp_msg(MSGT_IDENTIFY,MSGL_INFO,"ID_FILENAME=%s\n", - filename_recode(filename)); + filename_recode(mpctx->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 (' ') */ @@ -3585,7 +3949,9 @@ if (mpctx->global_sub_size) { 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); } - mp_msg(MSGT_IDENTIFY,MSGL_INFO,"ID_LENGTH=%.2lf\n", demuxer_get_time_length(mpctx->demuxer)); + mp_msg(MSGT_IDENTIFY,MSGL_INFO,"ID_LENGTH=%.2f\n", mpctx->timeline ? + mpctx->timeline[mpctx->num_timeline_parts].start : + demuxer_get_time_length(mpctx->demuxer)); mp_msg(MSGT_IDENTIFY,MSGL_INFO,"ID_SEEKABLE=%d\n", mpctx->stream->seek && (!mpctx->demuxer || mpctx->demuxer->seekable)); if (mpctx->demuxer) { @@ -3596,14 +3962,14 @@ if (mpctx->global_sub_size) { if(!mpctx->sh_video) goto main; // audio-only -if(!reinit_video_chain()) { +if(!reinit_video_chain(mpctx)) { if(!mpctx->sh_video){ if(!mpctx->sh_audio) goto goto_next_file; - goto main; // exit_player(MSGTR_Exit_error); + goto main; // exit_player(_("Fatal error")); } } - if(vo_flags & 0x08 && vo_spudec) + if(mpctx->sh_video->output_flags & 0x08 && vo_spudec) spudec_set_hw_spu(vo_spudec,mpctx->video_out); #ifdef CONFIG_FREETYPE @@ -3631,17 +3997,16 @@ if(verbose) term_osd = 0; int frame_time_remaining=0; // flag int blit_frame=0; -mpctx->num_buffered_frames=0; // Make sure old OSD does not stay around, // e.g. with -fixed-vo and same-resolution files clear_osd_msgs(); -update_osd_msg(); +update_osd_msg(mpctx); //================ SETUP AUDIO ========================== if(mpctx->sh_audio){ - reinit_audio_chain(); + 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); } @@ -3662,18 +4027,18 @@ if(mpctx->sh_audio){ } if(!mpctx->sh_audio){ - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_NoSound); + 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 //mpctx->d_audio->id=-2; // do not read audio chunks - //uninit_player(INITIALIZED_AO); // close device + //uninit_player(mpctx, INITIALIZED_AO); // close device } if(!mpctx->sh_video){ - mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_Video_NoVideo); + 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; - //if(!fixed_vo) uninit_player(INITIALIZED_VO); + //if(!fixed_vo) uninit_player(mpctx, INITIALIZED_VO); } if (!mpctx->sh_video && !mpctx->sh_audio) @@ -3683,48 +4048,44 @@ if (!mpctx->sh_video && !mpctx->sh_audio) 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_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_FPSforced,mpctx->sh_video->fps,mpctx->sh_video->frametime); -} - -#ifdef CONFIG_GUI -if ( use_gui ) { - if ( mpctx->sh_audio ) guiIntfStruct.AudioType=mpctx->sh_audio->channels; else guiIntfStruct.AudioType=0; - if ( !mpctx->sh_video && mpctx->sh_audio ) guiGetEvent( guiSetAudioOnly,(char *)1 ); else guiGetEvent( guiSetAudioOnly,(char *)0 ); - guiGetEvent( guiSetFileFormat,(char *)mpctx->demuxer->file_format ); - if ( guiGetEvent( guiSetValues,(char *)mpctx->sh_video ) ) goto goto_next_file; - guiGetEvent( guiSetDemuxer,(char *)mpctx->demuxer ); + mp_tmsg(MSGT_CPLAYER,MSGL_INFO,"FPS forced to be %5.3f (ftime: %5.3f).\n",mpctx->sh_video->fps,mpctx->sh_video->frametime); } -#endif -mp_input_set_section(NULL); + mp_input_set_section(mpctx->input, NULL); //TODO: add desired (stream-based) sections here -if (mpctx->stream->type==STREAMTYPE_TV) mp_input_set_section("tv"); -if (mpctx->stream->type==STREAMTYPE_DVDNAV) mp_input_set_section("dvdnav"); + 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(mpctx->loop_times>1) mpctx->loop_times--; else -if(mpctx->loop_times==1) mpctx->loop_times = -1; +if(opts->loop_times>1) opts->loop_times--; else +if(opts->loop_times==1) opts->loop_times = -1; -mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_StartPlaying); +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; -mpctx->startup_decode_retry = DEFAULT_STARTUP_DECODE_RETRY; if(play_n_frames==0){ - mpctx->eof=PT_NEXT_ENTRY; goto goto_next_file; + mpctx->stop_play=PT_NEXT_ENTRY; goto goto_next_file; } -if (seek_to_sec) { +// If there's a timeline force an absolute seek to initialize state +if (seek_to_sec || mpctx->timeline) { seek(mpctx, seek_to_sec, SEEK_ABSOLUTE); end_at.pos += seek_to_sec; } +if (opts->chapterrange[0] > 0) { + double pts; + if (seek_chapter(mpctx, opts->chapterrange[0]-1, &pts, NULL) >= 0 + && pts > -1.0) + seek(mpctx, pts, SEEK_ABSOLUTE); +} if (end_at.type == END_AT_SIZE) { - mp_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_MPEndposNoSizeBased); + mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "Option -endpos in MPlayer does not yet support size units.\n"); end_at.type = END_AT_NONE; } @@ -3736,28 +4097,39 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { } #endif -while(!mpctx->eof){ + get_relative_time(mpctx); // reset current delta + mpctx->time_frame = 0; + mpctx->drop_message_shown = 0; + mpctx->update_video_immediately = true; + mpctx->total_avsync_change = 0; + mpctx->last_chapter_seek = -1; + // Make sure VO knows current pause state + if (mpctx->sh_video) + vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, + NULL); + +while(!mpctx->stop_play){ float aq_sleep_time=0; -if(dvd_last_chapter>0) { - int cur_chapter = demuxer_get_current_chapter(mpctx->demuxer); - if(cur_chapter!=-1 && cur_chapter+1>dvd_last_chapter) +if (opts->chapterrange[1] > 0) { + int cur_chapter = get_current_chapter(mpctx); + if(cur_chapter!=-1 && cur_chapter+1 > opts->chapterrange[1]) goto goto_next_file; } 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(); + reinit_audio_chain(mpctx); } /*========================== PLAY AUDIO ============================*/ -if (mpctx->sh_audio) - if (!fill_audio_out_buffers()) +if (mpctx->sh_audio && !mpctx->paused) + if (!fill_audio_out_buffers(mpctx)) // at eof, all audio at least written to ao if (!mpctx->sh_video) - mpctx->eof = PT_NEXT_ENTRY; + mpctx->stop_play = AT_END_OF_FILE; if(!mpctx->sh_video) { @@ -3765,16 +4137,16 @@ if(!mpctx->sh_video) { double a_pos=0; // sh_audio can be NULL due to video stream switching // TODO: handle this better - if((!quiet || end_at.type == END_AT_TIME) && mpctx->sh_audio) - a_pos = playing_audio_pts(mpctx->sh_audio, mpctx->d_audio, mpctx->audio_out); + if (mpctx->sh_audio) + a_pos = playing_audio_pts(mpctx); - if(!quiet) - print_status(a_pos, 0, 0); + print_status(mpctx, a_pos, false); if(end_at.type == END_AT_TIME && end_at.pos < a_pos) - mpctx->eof = PT_NEXT_ENTRY; - update_subtitles(NULL, a_pos, mpctx->d_sub, 0); - update_osd_msg(); + mpctx->stop_play = PT_NEXT_ENTRY; + update_subtitles(mpctx, &mpctx->opts, NULL, a_pos, mpctx->video_offset, + mpctx->d_sub, 0); + update_osd_msg(mpctx); } else { @@ -3783,28 +4155,46 @@ if(!mpctx->sh_video) { vo_pts=mpctx->sh_video->timer*90000.0; vo_fps=mpctx->sh_video->fps; - if (!mpctx->num_buffered_frames) { - double frame_time = update_video(&blit_frame); - while (!blit_frame && mpctx->startup_decode_retry > 0) { - double delay = mpctx->delay; - // these initial decode failures are probably due to codec delay, - // ignore them and also their probably nonsense durations - update_video(&blit_frame); - mpctx->delay = delay; - mpctx->startup_decode_retry--; - } - mpctx->startup_decode_retry = 0; + blit_frame = mpctx->video_out->frame_loaded; + if (!blit_frame) { + 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_msg(MSGT_CPLAYER,MSGL_FATAL, MSGTR_NotInitializeVOPorVO); - mpctx->eof = 1; goto goto_next_file; + mp_tmsg(MSGT_CPLAYER,MSGL_FATAL, "\nFATAL: Could not initialize video filters (-vf) or video output (-vo).\n"); + mpctx->stop_play = PT_NEXT_ENTRY; + goto goto_next_file; + } + if (blit_frame) { + struct sh_video *sh_video = mpctx->sh_video; + update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, + mpctx->video_offset, mpctx->d_sub, 0); + update_teletext(sh_video, mpctx->demuxer, 0); + update_osd_msg(mpctx); + struct vf_instance *vf = mpctx->sh_video->vfilter; + vf->control(vf, VFCTRL_DRAW_EOSD, NULL); + vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); + vo_osd_changed(0); } if (frame_time < 0) - mpctx->eof = 1; + mpctx->stop_play = AT_END_OF_FILE; else { - // might return with !eof && !blit_frame if !correct_pts - mpctx->num_buffered_frames += blit_frame; - mpctx->time_frame += frame_time / playback_speed; // for nosound + if (mpctx->update_video_immediately) { + // Show this frame immediately, rest normally + mpctx->update_video_immediately = false; + } else { + 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) { + seek(mpctx, next->start, SEEK_ABSOLUTE); + continue; } } @@ -3813,17 +4203,13 @@ if(!mpctx->sh_video) { // current_module="draw_osd"; // if(vo_config_count) mpctx->video_out->draw_osd(); -#ifdef CONFIG_GUI - if(use_gui) guiEventHandling(); -#endif - current_module="vo_check_events"; - if (vo_config_count) mpctx->video_out->check_events(); + vo_check_events(mpctx->video_out); #ifdef CONFIG_X11 if (stop_xscreensaver) { current_module = "stop_xscreensaver"; - xscreensaver_heartbeat(); + xscreensaver_heartbeat(mpctx->x11_state); } #endif if (heartbeat_cmd) { @@ -3835,22 +4221,42 @@ if(!mpctx->sh_video) { } } - frame_time_remaining = sleep_until_update(&mpctx->time_frame, &aq_sleep_time); + frame_time_remaining = sleep_until_update(mpctx, &mpctx->time_frame, &aq_sleep_time); //====================== FLIP PAGE (VIDEO BLT): ========================= current_module="flip_page"; if (!frame_time_remaining && blit_frame) { unsigned int t2=GetTimer(); - - if(vo_config_count) mpctx->video_out->flip_page(); - mpctx->num_buffered_frames--; - - vout_time_usage += (GetTimer() - t2) * 0.000001; + unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; + int duration = -1; + double pts2 = mpctx->video_out->next_pts2; + if (pts2 != MP_NOPTS_VALUE && opts->correct_pts) { + // expected A/V sync correction is ignored + double diff = (pts2 - mpctx->sh_video->pts); + diff /= opts->playback_speed; + if (mpctx->time_frame < 0) + diff += mpctx->time_frame; + if (diff < 0) + diff = 0; + if (diff > 10) + diff = 10; + duration = diff * 1e6; + } + 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); + } + print_status(mpctx, MP_NOPTS_VALUE, true); } -//====================== A-V TIMESTAMP CORRECTION: ========================= - - adjust_sync_and_print_status(frame_time_remaining, mpctx->time_frame); + else + print_status(mpctx, MP_NOPTS_VALUE, false); //============================ Auto QUALITY ============================ @@ -3872,16 +4278,24 @@ if(auto_quality>0){ set_video_quality(mpctx->sh_video,output_quality); } - if (play_n_frames >= 0 && !frame_time_remaining && blit_frame) { - --play_n_frames; - if (play_n_frames <= 0) mpctx->eof = PT_NEXT_ENTRY; + 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); + } } // FIXME: add size based support for -endpos if (end_at.type == END_AT_TIME && !frame_time_remaining && end_at.pos <= mpctx->sh_video->pts) - mpctx->eof = PT_NEXT_ENTRY; + mpctx->stop_play = PT_NEXT_ENTRY; } // end if(mpctx->sh_video) @@ -3902,102 +4316,81 @@ if(auto_quality>0){ } #endif -//============================ Handle PAUSE =============================== - - current_module="pause"; - - if (mpctx->osd_function == OSD_PAUSE) { - mpctx->was_paused = 1; - pause_loop(); - } - -// handle -sstep -if(step_sec>0) { - mpctx->osd_function=OSD_FFW; - rel_seek_secs+=step_sec; -} - - edl_update(mpctx); - //================= Keyboard events, SEEKing ==================== current_module="key_events"; { + while (1) { mp_cmd_t* cmd; - int brk_cmd = 0; - while( !brk_cmd && (cmd = mp_input_get_cmd(0,0,0)) != NULL) { - brk_cmd = run_command(mpctx, cmd); + while ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { + run_command(mpctx, cmd); mp_cmd_free(cmd); - if (brk_cmd == 2) - goto goto_enable_cache; + if (mpctx->stop_play) + break; + if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { + cmd = mp_input_get_cmd(mpctx->input, 0, 1); + /* Allow seek commands to be combined, but execute the real seek + * before processing other commands */ + if (!cmd || cmd->id != MP_CMD_SEEK) + break; + } + } + if (!mpctx->paused || mpctx->stop_play || mpctx->rel_seek_secs + || mpctx->abs_seek_pos) + break; + if (mpctx->sh_video) { + update_osd_msg(mpctx); + int hack = vo_osd_changed(0); + vo_osd_changed(hack); + if (hack) { + if (redraw_osd(mpctx->sh_video, mpctx->osd) < 0) { + add_step_frame(mpctx); + break; + } + else + vo_osd_changed(0); + } + } + pause_loop(mpctx); } } - mpctx->was_paused = 0; + +// handle -sstep +if (step_sec > 0 && !mpctx->paused) { + mpctx->osd_function=OSD_FFW; + mpctx->rel_seek_secs+=step_sec; +} + + edl_update(mpctx); /* Looping. */ - if(mpctx->eof==1 && mpctx->loop_times>=0) { - mp_msg(MSGT_CPLAYER,MSGL_V,"loop_times = %d, eof = %d\n", mpctx->loop_times,mpctx->eof); + if(mpctx->stop_play==AT_END_OF_FILE && opts->loop_times>=0) { + mp_msg(MSGT_CPLAYER,MSGL_V,"loop_times = %d\n", opts->loop_times); - if(mpctx->loop_times>1) mpctx->loop_times--; else - if(mpctx->loop_times==1) mpctx->loop_times=-1; + if(opts->loop_times>1) opts->loop_times--; else + if(opts->loop_times==1) opts->loop_times=-1; play_n_frames=play_n_frames_mf; - mpctx->eof=0; - abs_seek_pos=SEEK_ABSOLUTE; rel_seek_secs=seek_to_sec; - loop_seek = 1; + mpctx->stop_play=0; + mpctx->abs_seek_pos=SEEK_ABSOLUTE; mpctx->rel_seek_secs=seek_to_sec; } -if(rel_seek_secs || abs_seek_pos){ - if (seek(mpctx, rel_seek_secs, abs_seek_pos) >= 0) { - // Set OSD: - if(!loop_seek){ - if( !edl_decision ) - set_osd_bar(0,"Position",0,100,demuxer_get_percent_pos(mpctx->demuxer)); - } - } +if(mpctx->rel_seek_secs || mpctx->abs_seek_pos){ + seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); - rel_seek_secs=0; - abs_seek_pos=0; - loop_seek=0; - edl_decision = 0; + mpctx->rel_seek_secs=0; + mpctx->abs_seek_pos=0; } -#ifdef CONFIG_GUI - if(use_gui){ - guiEventHandling(); - if(mpctx->demuxer->file_format==DEMUXER_TYPE_AVI && mpctx->sh_video && mpctx->sh_video->video.dwLength>2){ - // get pos from frame number / total frames - guiIntfStruct.Position=(float)mpctx->d_video->pack_no*100.0f/mpctx->sh_video->video.dwLength; - } else { - guiIntfStruct.Position=demuxer_get_percent_pos(mpctx->demuxer); - } - if ( mpctx->sh_video ) guiIntfStruct.TimeSec=mpctx->sh_video->pts; - else if ( mpctx->sh_audio ) guiIntfStruct.TimeSec=playing_audio_pts(mpctx->sh_audio, mpctx->d_audio, mpctx->audio_out); - guiIntfStruct.LengthInSec=demuxer_get_time_length(mpctx->demuxer); - guiGetEvent( guiReDraw,NULL ); - guiGetEvent( guiSetVolume,NULL ); - if(guiIntfStruct.Playing==0) break; // STOP - if(guiIntfStruct.Playing==2) mpctx->osd_function=OSD_PAUSE; - if ( guiIntfStruct.DiskChanged || guiIntfStruct.NewPlay ) goto goto_next_file; -#ifdef CONFIG_DVDREAD - if ( mpctx->stream->type == STREAMTYPE_DVD ) - { - dvd_priv_t * dvdp = mpctx->stream->priv; - guiIntfStruct.DVD.current_chapter=dvd_chapter_from_cell(dvdp,guiIntfStruct.DVD.current_title-1, dvdp->cur_cell)+1; - } -#endif - } -#endif /* CONFIG_GUI */ - -} // while(!mpctx->eof) +} // while(!mpctx->stop_play) -mp_msg(MSGT_GLOBAL,MSGL_V,"EOF code: %d \n",mpctx->eof); +mp_msg(MSGT_GLOBAL,MSGL_V,"EOF code: %d \n",mpctx->stop_play); #ifdef CONFIG_DVBIN if(mpctx->dvbin_reopen) { - mpctx->eof = 0; - uninit_player(INITIALIZED_ALL-(INITIALIZED_GUI|INITIALIZED_STREAM|INITIALIZED_INPUT|INITIALIZED_GETCH2|(fixed_vo?INITIALIZED_VO:0))); + 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; @@ -4035,7 +4428,7 @@ if(benchmark){ } // time to uninit all, except global stuff: -uninit_player(INITIALIZED_ALL-(INITIALIZED_GUI+INITIALIZED_INPUT+(fixed_vo?INITIALIZED_VO:0))); +uninit_player(mpctx, INITIALIZED_ALL-(opts->fixed_vo?INITIALIZED_VO:0)); if(mpctx->set_of_sub_size > 0) { current_module="sub_free"; @@ -4056,38 +4449,36 @@ if(ass_library) ass_clear_fonts(ass_library); #endif -if(mpctx->eof == PT_NEXT_ENTRY || mpctx->eof == PT_PREV_ENTRY) { - mpctx->eof = mpctx->eof == PT_NEXT_ENTRY ? 1 : -1; - if(play_tree_iter_step(mpctx->playtree_iter,mpctx->play_tree_step,0) == PLAY_TREE_ITER_ENTRY) { - mpctx->eof = 1; - } else { + if (!mpctx->stop_play) // In case some goto jumped here... + mpctx->stop_play = PT_NEXT_ENTRY; + +int playtree_direction = 1; + +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) { play_tree_iter_free(mpctx->playtree_iter); mpctx->playtree_iter = NULL; } mpctx->play_tree_step = 1; -} else if(mpctx->eof == PT_UP_NEXT || mpctx->eof == PT_UP_PREV) { - mpctx->eof = mpctx->eof == PT_UP_NEXT ? 1 : -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,mpctx->eof,0) == PLAY_TREE_ITER_ENTRY) { - mpctx->eof = 1; - } else { + 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->eof == PT_STOP) { +} else if (mpctx->stop_play == PT_STOP) { play_tree_iter_free(mpctx->playtree_iter); mpctx->playtree_iter = NULL; } else { // NEXT PREV SRC - mpctx->eof = mpctx->eof == PT_PREV_SRC ? -1 : 1; + playtree_direction = mpctx->stop_play == PT_PREV_SRC ? -1 : 1; } -if(mpctx->eof == 0) mpctx->eof = 1; - while(mpctx->playtree_iter != NULL) { - filename = play_tree_iter_get_file(mpctx->playtree_iter,mpctx->eof); - if(filename == NULL) { - if(play_tree_iter_step(mpctx->playtree_iter,mpctx->eof,0) != PLAY_TREE_ITER_ENTRY) { + 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; }; @@ -4095,23 +4486,14 @@ while(mpctx->playtree_iter != NULL) { break; } -#ifdef CONFIG_GUI -if(use_gui && !mpctx->playtree_iter) { -#ifdef CONFIG_DVDREAD - if(!guiIntfStruct.DiskChanged) -#endif - mplEnd(); -} -#endif - -if(use_gui || mpctx->playtree_iter != NULL || player_idle_mode){ - if(!mpctx->playtree_iter) filename = NULL; - mpctx->eof = 0; +if(mpctx->playtree_iter != NULL || player_idle_mode){ + if(!mpctx->playtree_iter) mpctx->filename = NULL; + mpctx->stop_play = 0; goto play_next_file; } -exit_player_with_rc(EXIT_EOF, 0); +exit_player_with_rc(mpctx, EXIT_EOF, 0); return 1; } |