mpv/mpvcore/player/main.c

446 lines
12 KiB
C

/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include "config.h"
#include "talloc.h"
#include "osdep/io.h"
#include "osdep/getch2.h"
#include "osdep/priority.h"
#include "osdep/timer.h"
#include "mpvcore/av_log.h"
#include "mpvcore/codecs.h"
#include "mpvcore/cpudetect.h"
#include "mpvcore/encode.h"
#include "mpvcore/m_config.h"
#include "mpvcore/m_option.h"
#include "mpvcore/m_property.h"
#include "mpvcore/mp_common.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mpv_global.h"
#include "mpvcore/resolve.h"
#include "mpvcore/parser-cfg.h"
#include "mpvcore/parser-mpcmd.h"
#include "mpvcore/playlist.h"
#include "mpvcore/playlist_parser.h"
#include "mpvcore/options.h"
#include "mpvcore/input/input.h"
#include "audio/decode/dec_audio.h"
#include "audio/out/ao.h"
#include "audio/mixer.h"
#include "demux/demux.h"
#include "stream/stream.h"
#include "sub/ass_mp.h"
#include "sub/osd.h"
#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "mp_core.h"
#include "mp_lua.h"
#include "command.h"
#include "screenshot.h"
#if HAVE_X11
#include "video/out/x11_common.h"
#endif
#if HAVE_COCOA
#include "osdep/macosx_application.h"
#endif
#ifdef PTW32_STATIC_LIB
#include <pthread.h>
#endif
#if defined(__MINGW32__) || defined(__CYGWIN__)
#include <windows.h>
#endif
const char mp_help_text[] = _(
"Usage: mpv [options] [url|path/]filename\n"
"\n"
"Basic options:\n"
" --start=<time> seek to given (percent, seconds, or hh:mm:ss) position\n"
" --no-audio do not play sound\n"
" --no-video do not play video\n"
" --fs fullscreen playback\n"
" --sub=<file> specify subtitle file to use\n"
" --playlist=<file> specify playlist file\n"
"\n"
" --list-options list all mpv options\n"
"\n");
void mp_print_version(int always)
{
int v = always ? MSGL_INFO : MSGL_V;
mp_msg(MSGT_CPLAYER, v,
"%s (C) 2000-2013 mpv/MPlayer/mplayer2 projects\n built on %s\n", mplayer_version, mplayer_builddate);
print_libav_versions(v);
mp_msg(MSGT_CPLAYER, v, "\n");
}
static MP_NORETURN void exit_player(struct MPContext *mpctx,
enum exit_reason how)
{
int rc;
uninit_player(mpctx, INITIALIZED_ALL);
#if HAVE_ENCODING
encode_lavc_finish(mpctx->encode_lavc_ctx);
encode_lavc_free(mpctx->encode_lavc_ctx);
#endif
mpctx->encode_lavc_ctx = NULL;
#if HAVE_LUA
mp_lua_uninit(mpctx);
#endif
#if defined(__MINGW32__) || defined(__CYGWIN__)
timeEndPeriod(1);
#endif
#if HAVE_COCOA
cocoa_set_input_context(NULL);
#endif
command_uninit(mpctx);
mp_input_uninit(mpctx->input);
osd_free(mpctx->osd);
#if HAVE_LIBASS
ass_library_done(mpctx->ass_library);
mpctx->ass_library = NULL;
#endif
if (how != EXIT_NONE) {
const char *reason;
switch (how) {
case EXIT_SOMENOTPLAYED:
case EXIT_PLAYED:
reason = "End of file";
break;
case EXIT_NOTPLAYED:
reason = "No files played";
break;
case EXIT_ERROR:
reason = "Fatal error";
break;
default:
reason = "Quit";
}
MP_INFO(mpctx, "\nExiting... (%s)\n", reason);
}
if (mpctx->has_quit_custom_rc) {
rc = mpctx->quit_custom_rc;
} else {
switch (how) {
case EXIT_ERROR:
rc = 1; break;
case EXIT_NOTPLAYED:
rc = 2; break;
case EXIT_SOMENOTPLAYED:
rc = 3; break;
default:
rc = 0;
}
}
// must be last since e.g. mp_msg uses option values
// that will be freed by this.
mp_msg_uninit(mpctx->global);
talloc_free(mpctx);
#if HAVE_COCOA
terminate_cocoa_application();
// never reach here:
// terminate calls exit itself, just silence compiler warning
exit(0);
#else
exit(rc);
#endif
}
static bool handle_help_options(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
int opt_exit = 0;
if (opts->audio_decoders && strcmp(opts->audio_decoders, "help") == 0) {
struct mp_decoder_list *list = audio_decoder_list();
mp_print_decoders(MSGT_CPLAYER, MSGL_INFO, "Audio decoders:", list);
talloc_free(list);
opt_exit = 1;
}
if (opts->video_decoders && strcmp(opts->video_decoders, "help") == 0) {
struct mp_decoder_list *list = video_decoder_list();
mp_print_decoders(MSGT_CPLAYER, MSGL_INFO, "Video decoders:", list);
talloc_free(list);
opt_exit = 1;
}
#if HAVE_X11
if (opts->vo.fstype_list && strcmp(opts->vo.fstype_list[0], "help") == 0) {
fstype_help();
mp_msg(MSGT_FIXME, MSGL_FIXME, "\n");
opt_exit = 1;
}
#endif
if ((opts->demuxer_name && strcmp(opts->demuxer_name, "help") == 0) ||
(opts->audio_demuxer_name && strcmp(opts->audio_demuxer_name, "help") == 0) ||
(opts->sub_demuxer_name && strcmp(opts->sub_demuxer_name, "help") == 0)) {
demuxer_help();
MP_INFO(mpctx, "\n");
opt_exit = 1;
}
if (opts->list_properties) {
property_print_help();
opt_exit = 1;
}
#if HAVE_ENCODING
if (encode_lavc_showhelp(mpctx->opts))
opt_exit = 1;
#endif
return opt_exit;
}
#ifdef PTW32_STATIC_LIB
static void detach_ptw32(void)
{
pthread_win32_thread_detach_np();
pthread_win32_process_detach_np();
}
#endif
static void osdep_preinit(int *p_argc, char ***p_argv)
{
char *enable_talloc = getenv("MPV_LEAK_REPORT");
if (*p_argc > 1 && (strcmp((*p_argv)[1], "-leak-report") == 0 ||
strcmp((*p_argv)[1], "--leak-report") == 0))
enable_talloc = "1";
if (enable_talloc && strcmp(enable_talloc, "1") == 0)
talloc_enable_leak_report();
#ifdef __MINGW32__
mp_get_converted_argv(p_argc, p_argv);
#endif
#ifdef PTW32_STATIC_LIB
pthread_win32_process_attach_np();
pthread_win32_thread_attach_np();
atexit(detach_ptw32);
#endif
#if defined(__MINGW32__) || defined(__CYGWIN__)
// stop Windows from showing all kinds of annoying error dialogs
SetErrorMode(0x8003);
#endif
load_termcap(NULL); // load key-codes
mp_time_init();
}
static int read_keys(void *ctx, int fd)
{
if (getch2(ctx))
return MP_INPUT_NOTHING;
return MP_INPUT_DEAD;
}
static void init_input(struct MPContext *mpctx)
{
mpctx->input = mp_input_init(mpctx->global);
if (mpctx->opts->slave_mode)
mp_input_add_cmd_fd(mpctx->input, 0, USE_FD0_CMD_SELECT, MP_INPUT_SLAVE_CMD_FUNC, NULL);
else if (mpctx->opts->consolecontrols)
mp_input_add_key_fd(mpctx->input, 0, 1, read_keys, NULL, mpctx->input);
// Set the libstream interrupt callback
stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input);
#if HAVE_COCOA
cocoa_set_input_context(mpctx->input);
#endif
}
static int cfg_include(struct m_config *conf, char *filename, int flags)
{
return m_config_parse_config_file(conf, filename, flags);
}
static int mpv_main(int argc, char *argv[])
{
osdep_preinit(&argc, &argv);
if (argc >= 1) {
argc--;
argv++;
}
struct MPContext *mpctx = talloc(NULL, MPContext);
*mpctx = (struct MPContext){
.last_dvb_step = 1,
.terminal_osd_text = talloc_strdup(mpctx, ""),
.playlist = talloc_struct(mpctx, struct playlist, {0}),
};
// Create the config context and register the options
mpctx->mconfig = m_config_new(mpctx, sizeof(struct MPOpts),
&mp_default_opts, mp_opts);
mpctx->opts = mpctx->mconfig->optstruct;
mpctx->mconfig->includefunc = cfg_include;
mpctx->mconfig->use_profiles = true;
mpctx->mconfig->is_toplevel = true;
struct MPOpts *opts = mpctx->opts;
mpctx->global = talloc_zero(mpctx, struct mpv_global);
mpctx->global->opts = opts;
// Nothing must call mp_msg() before this
mp_msg_init(mpctx->global);
mpctx->log = mp_log_new(mpctx, mpctx->global->log, "!cplayer");
init_libav();
GetCpuCaps(&gCpuCaps);
screenshot_init(mpctx);
mpctx->mixer = mixer_init(mpctx, opts);
command_init(mpctx);
// Preparse the command line
m_config_preparse_command_line(mpctx->mconfig, argc, argv);
mp_print_version(false);
if (!mp_parse_cfgfiles(mpctx))
exit_player(mpctx, EXIT_ERROR);
int r = m_config_parse_mp_command_line(mpctx->mconfig, mpctx->playlist,
argc, argv);
if (r < 0) {
if (r <= M_OPT_EXIT) {
exit_player(mpctx, EXIT_NONE);
} else {
exit_player(mpctx, EXIT_ERROR);
}
}
if (handle_help_options(mpctx))
exit_player(mpctx, EXIT_NONE);
MP_VERBOSE(mpctx, "Configuration: " CONFIGURATION "\n");
MP_VERBOSE(mpctx, "Command line:");
for (int i = 0; i < argc; i++)
MP_VERBOSE(mpctx, " '%s'", argv[i]);
MP_VERBOSE(mpctx, "\n");
if (!mpctx->playlist->first && !opts->player_idle_mode) {
mp_print_version(true);
MP_INFO(mpctx, "%s", mp_gtext(mp_help_text));
exit_player(mpctx, EXIT_NONE);
}
#if HAVE_PRIORITY
set_priority();
#endif
init_input(mpctx);
#if HAVE_ENCODING
if (opts->encode_output.file && *opts->encode_output.file) {
mpctx->encode_lavc_ctx = encode_lavc_init(&opts->encode_output);
if(!mpctx->encode_lavc_ctx) {
mp_msg(MSGT_VO, MSGL_INFO, "Encoding initialization failed.");
exit_player(mpctx, EXIT_ERROR);
}
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
m_config_set_option0(mpctx->mconfig, "force-window", "no");
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
}
#endif
#if HAVE_LIBASS
mpctx->ass_library = mp_ass_init(opts);
#else
MP_WARN(mpctx, "Compiled without libass.\n");
MP_WARN(mpctx, "There will be no OSD and no text subtitles.\n");
#endif
mpctx->osd = osd_create(opts, mpctx->ass_library);
if (opts->force_vo) {
opts->fixed_vo = 1;
mpctx->video_out = init_best_video_out(mpctx->global, mpctx->input,
mpctx->encode_lavc_ctx);
if (!mpctx->video_out) {
MP_FATAL(mpctx, "Error opening/initializing "
"the selected video_out (-vo) device.\n");
exit_player(mpctx, EXIT_ERROR);
}
mpctx->mouse_cursor_visible = true;
mpctx->initialized_flags |= INITIALIZED_VO;
}
#if HAVE_LUA
// Lua user scripts can call arbitrary functions. Load them at a point
// where this is safe.
mp_lua_init(mpctx);
#endif
if (opts->shuffle)
playlist_shuffle(mpctx->playlist);
if (opts->merge_files)
merge_playlist_files(mpctx->playlist);
mpctx->playlist->current = mp_resume_playlist(mpctx->playlist, opts);
if (!mpctx->playlist->current)
mpctx->playlist->current = mpctx->playlist->first;
mp_play_files(mpctx);
exit_player(mpctx, mpctx->stop_play == PT_QUIT ? EXIT_QUIT : mpctx->quit_player_rc);
return 1;
}
int main(int argc, char *argv[])
{
#if HAVE_COCOA
return cocoa_main(mpv_main, argc, argv);
#else
return mpv_main(argc, argv);
#endif
}