mirror of https://github.com/mpv-player/mpv
core: add --force-window
This commit adds the --force-window option, which will cause mpv always to create a window when started. This can be useful when pretending that mpv is a GUI application (which it isn't, but users pretend anyway), and playing audio files would run mpv in the background without giving a window to control it. This doesn't actually create the window immediately: it only does so only after initializing playback and when it is clear that there won't be any actual video. This could be a problem when starting slow or completely stuck network streams (mpv would remain frozen in the background), or if video initialization somehow is stuck forever in an in-between state (like when the decoder doesn't output a video frame, but doesn't return an error either). Well, we can pretend only so much that mpv is a GUI application.
This commit is contained in:
parent
3c0333978e
commit
f01744ac4e
|
@ -804,6 +804,20 @@
|
||||||
depending on GPU drivers and hardware. For other VOs, this just makes
|
depending on GPU drivers and hardware. For other VOs, this just makes
|
||||||
rendering slower.
|
rendering slower.
|
||||||
|
|
||||||
|
``--force-window``
|
||||||
|
Create a video output window even if there is no video. This can be useful
|
||||||
|
when pretending that mpv is a GUI application. Currently, the window
|
||||||
|
always has the size 640x480, and is subject to ``--geometry``,
|
||||||
|
``--autofit``, and similar options.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The window is created only after initialization (to make sure default
|
||||||
|
window placement still works if the video size is different from the
|
||||||
|
``--force-window`` default window size). This can be a problem if
|
||||||
|
initialization doesn't work perfectly, such as when opening URLs with
|
||||||
|
bad network connection, or opening broken video files.
|
||||||
|
|
||||||
``--force-window-position``
|
``--force-window-position``
|
||||||
Forcefully move mpv's video output window to default location whenever
|
Forcefully move mpv's video output window to default location whenever
|
||||||
there is a change in video parameters, video stream or file. This used to
|
there is a change in video parameters, video stream or file. This used to
|
||||||
|
|
|
@ -1556,7 +1556,7 @@ static void update_osd_msg(struct MPContext *mpctx)
|
||||||
// Look if we have a msg
|
// Look if we have a msg
|
||||||
mp_osd_msg_t *msg = get_osd_msg(mpctx);
|
mp_osd_msg_t *msg = get_osd_msg(mpctx);
|
||||||
if (msg && !msg->show_position) {
|
if (msg && !msg->show_position) {
|
||||||
if (mpctx->sh_video && opts->term_osd != 1) {
|
if (mpctx->video_out && opts->term_osd != 1) {
|
||||||
osd_set_text(osd, msg->msg);
|
osd_set_text(osd, msg->msg);
|
||||||
} else if (opts->term_osd) {
|
} else if (opts->term_osd) {
|
||||||
if (strcmp(mpctx->terminal_osd_text, msg->msg)) {
|
if (strcmp(mpctx->terminal_osd_text, msg->msg)) {
|
||||||
|
@ -1576,7 +1576,7 @@ static void update_osd_msg(struct MPContext *mpctx)
|
||||||
if (msg && msg->show_position)
|
if (msg && msg->show_position)
|
||||||
osd_level = 3;
|
osd_level = 3;
|
||||||
|
|
||||||
if (mpctx->sh_video && opts->term_osd != 1) {
|
if (mpctx->video_out && opts->term_osd != 1) {
|
||||||
// fallback on the timer
|
// fallback on the timer
|
||||||
char *text = NULL;
|
char *text = NULL;
|
||||||
|
|
||||||
|
@ -2031,8 +2031,10 @@ void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (type == STREAM_VIDEO) {
|
if (type == STREAM_VIDEO) {
|
||||||
uninit_player(mpctx, INITIALIZED_VCODEC |
|
int uninit = INITIALIZED_VCODEC;
|
||||||
(mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO));
|
if (!mpctx->opts->force_vo)
|
||||||
|
uninit |= mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO;
|
||||||
|
uninit_player(mpctx, uninit);
|
||||||
} else if (type == STREAM_AUDIO) {
|
} else if (type == STREAM_AUDIO) {
|
||||||
uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
|
uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
|
||||||
} else if (type == STREAM_SUB) {
|
} else if (type == STREAM_SUB) {
|
||||||
|
@ -2380,7 +2382,8 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||||
init_demux_stream(mpctx, STREAM_VIDEO);
|
init_demux_stream(mpctx, STREAM_VIDEO);
|
||||||
sh_video_t *sh_video = mpctx->sh_video;
|
sh_video_t *sh_video = mpctx->sh_video;
|
||||||
if (!sh_video) {
|
if (!sh_video) {
|
||||||
uninit_player(mpctx, INITIALIZED_VO);
|
if (!opts->force_vo)
|
||||||
|
uninit_player(mpctx, INITIALIZED_VO);
|
||||||
goto no_video;
|
goto no_video;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2453,7 +2456,8 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
uninit_player(mpctx, INITIALIZED_VO);
|
if (!opts->force_vo)
|
||||||
|
uninit_player(mpctx, INITIALIZED_VO);
|
||||||
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
||||||
no_video:
|
no_video:
|
||||||
mpctx->current_track[STREAM_VIDEO] = NULL;
|
mpctx->current_track[STREAM_VIDEO] = NULL;
|
||||||
|
@ -3528,6 +3532,41 @@ static void handle_keep_open(struct MPContext *mpctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute a forceful refresh of the VO window, if it hasn't had a valid frame
|
||||||
|
// for a while. The problem is that a VO with no valid frame (vo->hasframe==0)
|
||||||
|
// doesn't redraw video and doesn't OSD interaction. So screw it, hard.
|
||||||
|
static void handle_force_window(struct MPContext *mpctx)
|
||||||
|
{
|
||||||
|
// Don't interfere with real video playback
|
||||||
|
if (mpctx->sh_video)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct vo *vo = mpctx->video_out;
|
||||||
|
if (!vo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!vo->config_ok) {
|
||||||
|
MP_INFO(mpctx, "Creating non-video VO window.\n");
|
||||||
|
// Pick whatever works
|
||||||
|
int config_format = 0;
|
||||||
|
for (int fmt = IMGFMT_START; fmt < IMGFMT_END; fmt++) {
|
||||||
|
if (vo->driver->query_format(vo, fmt)) {
|
||||||
|
config_format = fmt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int w = 640;
|
||||||
|
int h = 480;
|
||||||
|
struct mp_image_params p = {
|
||||||
|
.imgfmt = config_format,
|
||||||
|
.w = w, .h = h,
|
||||||
|
.d_w = w, .d_h = h,
|
||||||
|
};
|
||||||
|
vo_reconfig(vo, &p, 0);
|
||||||
|
redraw_osd(mpctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static double get_wakeup_period(struct MPContext *mpctx)
|
static double get_wakeup_period(struct MPContext *mpctx)
|
||||||
{
|
{
|
||||||
/* Even if we can immediately wake up in response to most input events,
|
/* Even if we can immediately wake up in response to most input events,
|
||||||
|
@ -3593,6 +3632,11 @@ static void run_playloop(struct MPContext *mpctx)
|
||||||
audio_left = status > -2;
|
audio_left = status > -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mpctx->video_out) {
|
||||||
|
vo_check_events(mpctx->video_out);
|
||||||
|
handle_cursor_autohide(mpctx);
|
||||||
|
}
|
||||||
|
|
||||||
double buffered_audio = -1;
|
double buffered_audio = -1;
|
||||||
while (mpctx->sh_video) { // never loops, for "break;" only
|
while (mpctx->sh_video) { // never loops, for "break;" only
|
||||||
struct vo *vo = mpctx->video_out;
|
struct vo *vo = mpctx->video_out;
|
||||||
|
@ -3606,12 +3650,16 @@ static void run_playloop(struct MPContext *mpctx)
|
||||||
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
||||||
"\nFATAL: Could not initialize video filters (-vf) "
|
"\nFATAL: Could not initialize video filters (-vf) "
|
||||||
"or video output (-vo).\n");
|
"or video output (-vo).\n");
|
||||||
uninit_player(mpctx, INITIALIZED_VCODEC | INITIALIZED_VO);
|
int uninit = INITIALIZED_VCODEC;
|
||||||
|
if (!opts->force_vo)
|
||||||
|
uninit |= INITIALIZED_VO;
|
||||||
|
uninit_player(mpctx, uninit);
|
||||||
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
||||||
mpctx->current_track[STREAM_VIDEO] = NULL;
|
mpctx->current_track[STREAM_VIDEO] = NULL;
|
||||||
if (!mpctx->current_track[STREAM_AUDIO])
|
if (!mpctx->current_track[STREAM_AUDIO])
|
||||||
mpctx->stop_play = PT_NEXT_ENTRY;
|
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||||
mpctx->error_playing = true;
|
mpctx->error_playing = true;
|
||||||
|
handle_force_window(mpctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
video_left = frame_time >= 0;
|
video_left = frame_time >= 0;
|
||||||
|
@ -3628,11 +3676,6 @@ static void run_playloop(struct MPContext *mpctx)
|
||||||
if (endpts != MP_NOPTS_VALUE)
|
if (endpts != MP_NOPTS_VALUE)
|
||||||
video_left &= mpctx->sh_video->pts < endpts;
|
video_left &= mpctx->sh_video->pts < endpts;
|
||||||
|
|
||||||
// ================================================================
|
|
||||||
vo_check_events(vo);
|
|
||||||
|
|
||||||
handle_cursor_autohide(mpctx);
|
|
||||||
|
|
||||||
handle_heartbeat_cmd(mpctx);
|
handle_heartbeat_cmd(mpctx);
|
||||||
|
|
||||||
if (!video_left || (mpctx->paused && !mpctx->restart_playback))
|
if (!video_left || (mpctx->paused && !mpctx->restart_playback))
|
||||||
|
@ -3857,7 +3900,7 @@ static void run_playloop(struct MPContext *mpctx)
|
||||||
audio_sleep = 0.020;
|
audio_sleep = 0.020;
|
||||||
}
|
}
|
||||||
sleeptime = FFMIN(sleeptime, audio_sleep);
|
sleeptime = FFMIN(sleeptime, audio_sleep);
|
||||||
if (sleeptime > 0 && mpctx->sh_video) {
|
if (sleeptime > 0 && mpctx->video_out && mpctx->video_out->config_ok) {
|
||||||
bool want_redraw = vo_get_want_redraw(mpctx->video_out);
|
bool want_redraw = vo_get_want_redraw(mpctx->video_out);
|
||||||
if (mpctx->video_out->driver->draw_osd)
|
if (mpctx->video_out->driver->draw_osd)
|
||||||
want_redraw |= mpctx->osd->want_redraw;
|
want_redraw |= mpctx->osd->want_redraw;
|
||||||
|
@ -3888,6 +3931,8 @@ static void run_playloop(struct MPContext *mpctx)
|
||||||
|
|
||||||
handle_keep_open(mpctx);
|
handle_keep_open(mpctx);
|
||||||
|
|
||||||
|
handle_force_window(mpctx);
|
||||||
|
|
||||||
execute_queued_seek(mpctx);
|
execute_queued_seek(mpctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4227,7 +4272,11 @@ static void idle_loop(struct MPContext *mpctx)
|
||||||
while (mpctx->opts->player_idle_mode && !mpctx->playlist->current
|
while (mpctx->opts->player_idle_mode && !mpctx->playlist->current
|
||||||
&& mpctx->stop_play != PT_QUIT)
|
&& mpctx->stop_play != PT_QUIT)
|
||||||
{
|
{
|
||||||
uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_VO);
|
int uninit = INITIALIZED_AO;
|
||||||
|
if (!mpctx->opts->force_vo)
|
||||||
|
uninit |= INITIALIZED_VO;
|
||||||
|
uninit_player(mpctx, uninit);
|
||||||
|
handle_force_window(mpctx);
|
||||||
mp_cmd_t *cmd;
|
mp_cmd_t *cmd;
|
||||||
while (!(cmd = mp_input_get_cmd(mpctx->input,
|
while (!(cmd = mp_input_get_cmd(mpctx->input,
|
||||||
get_wakeup_period(mpctx) * 1000,
|
get_wakeup_period(mpctx) * 1000,
|
||||||
|
@ -4925,6 +4974,7 @@ static int mpv_main(int argc, char *argv[])
|
||||||
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
|
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
|
||||||
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
|
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
|
||||||
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
|
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");
|
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
|
||||||
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
|
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
|
@ -4936,6 +4986,19 @@ static int mpv_main(int argc, char *argv[])
|
||||||
|
|
||||||
mpctx->osd = osd_create(opts, mpctx->ass_library);
|
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_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
|
||||||
|
"the selected video_out (-vo) device.\n");
|
||||||
|
exit_player(mpctx, EXIT_ERROR);
|
||||||
|
}
|
||||||
|
mpctx->mouse_cursor_visible = true;
|
||||||
|
mpctx->initialized_flags |= INITIALIZED_VO;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_LUA
|
#ifdef CONFIG_LUA
|
||||||
// Lua user scripts can call arbitrary functions. Load them at a point
|
// Lua user scripts can call arbitrary functions. Load them at a point
|
||||||
// where this is safe.
|
// where this is safe.
|
||||||
|
|
|
@ -567,6 +567,7 @@ const m_option_t mp_opts[] = {
|
||||||
OPT_SETTINGSLIST("vo", vo.video_driver_list, 0, &vo_obj_list),
|
OPT_SETTINGSLIST("vo", vo.video_driver_list, 0, &vo_obj_list),
|
||||||
OPT_SETTINGSLIST("ao", audio_driver_list, 0, &ao_obj_list),
|
OPT_SETTINGSLIST("ao", audio_driver_list, 0, &ao_obj_list),
|
||||||
OPT_FLAG("fixed-vo", fixed_vo, CONF_GLOBAL),
|
OPT_FLAG("fixed-vo", fixed_vo, CONF_GLOBAL),
|
||||||
|
OPT_FLAG("force-window", force_vo, CONF_GLOBAL),
|
||||||
OPT_FLAG("ontop", vo.ontop, 0),
|
OPT_FLAG("ontop", vo.ontop, 0),
|
||||||
OPT_FLAG("border", vo.border, 0),
|
OPT_FLAG("border", vo.border, 0),
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ typedef struct MPOpts {
|
||||||
|
|
||||||
struct m_obj_settings *audio_driver_list;
|
struct m_obj_settings *audio_driver_list;
|
||||||
int fixed_vo;
|
int fixed_vo;
|
||||||
|
int force_vo;
|
||||||
int softvol;
|
int softvol;
|
||||||
float mixer_init_volume;
|
float mixer_init_volume;
|
||||||
int mixer_init_mute;
|
int mixer_init_mute;
|
||||||
|
|
|
@ -316,8 +316,10 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
|
||||||
struct voctrl_screenshot_args args =
|
struct voctrl_screenshot_args args =
|
||||||
{ .full_window = (mode == MODE_FULL_WINDOW) };
|
{ .full_window = (mode == MODE_FULL_WINDOW) };
|
||||||
|
|
||||||
struct vf_instance *vfilter = mpctx->sh_video->vfilter;
|
if (mpctx->sh_video && mpctx->sh_video->vfilter) {
|
||||||
vfilter->control(vfilter, VFCTRL_SCREENSHOT, &args);
|
struct vf_instance *vfilter = mpctx->sh_video->vfilter;
|
||||||
|
vfilter->control(vfilter, VFCTRL_SCREENSHOT, &args);
|
||||||
|
}
|
||||||
|
|
||||||
if (!args.out_image)
|
if (!args.out_image)
|
||||||
vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args);
|
vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args);
|
||||||
|
|
|
@ -778,7 +778,6 @@ static struct mp_image *decode_with_fallback(struct sh_video *sh,
|
||||||
// Failed hardware decoding? Try again in software.
|
// Failed hardware decoding? Try again in software.
|
||||||
if (ctx->software_fallback_decoder) {
|
if (ctx->software_fallback_decoder) {
|
||||||
uninit_avctx(sh);
|
uninit_avctx(sh);
|
||||||
sh->vf_initialized = 0;
|
|
||||||
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Error using hardware "
|
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Error using hardware "
|
||||||
"decoding, falling back to software decoding.\n");
|
"decoding, falling back to software decoding.\n");
|
||||||
const char *decoder = ctx->software_fallback_decoder;
|
const char *decoder = ctx->software_fallback_decoder;
|
||||||
|
|
|
@ -406,6 +406,9 @@ int vo_reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
||||||
vo->dwidth = d_width;
|
vo->dwidth = d_width;
|
||||||
vo->dheight = d_height;
|
vo->dheight = d_height;
|
||||||
|
|
||||||
|
talloc_free(vo->params);
|
||||||
|
vo->params = NULL;
|
||||||
|
|
||||||
struct mp_image_params p2 = *params;
|
struct mp_image_params p2 = *params;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -419,6 +422,8 @@ int vo_reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
||||||
}
|
}
|
||||||
vo->config_ok = (ret >= 0);
|
vo->config_ok = (ret >= 0);
|
||||||
vo->config_count += vo->config_ok;
|
vo->config_count += vo->config_ok;
|
||||||
|
if (vo->config_ok)
|
||||||
|
vo->params = talloc_memdup(vo, &p2, sizeof(p2));
|
||||||
if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) {
|
if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) {
|
||||||
mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback,
|
mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback,
|
||||||
NULL, vo);
|
NULL, vo);
|
||||||
|
|
|
@ -238,6 +238,7 @@ struct vo {
|
||||||
struct mp_log *log; // Using e.g. "[vo/vdpau]" as prefix
|
struct mp_log *log; // Using e.g. "[vo/vdpau]" as prefix
|
||||||
int config_ok; // Last config call was successful?
|
int config_ok; // Last config call was successful?
|
||||||
int config_count; // Total number of successful config calls
|
int config_count; // Total number of successful config calls
|
||||||
|
struct mp_image_params *params; // Configured parameters (as in vo_reconfig)
|
||||||
|
|
||||||
bool probing;
|
bool probing;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue