mirror of
https://github.com/mpv-player/mpv
synced 2024-12-25 00:02:13 +00:00
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
|
||||
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``
|
||||
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
|
||||
|
@ -1556,7 +1556,7 @@ static void update_osd_msg(struct MPContext *mpctx)
|
||||
// Look if we have a msg
|
||||
mp_osd_msg_t *msg = get_osd_msg(mpctx);
|
||||
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);
|
||||
} else if (opts->term_osd) {
|
||||
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)
|
||||
osd_level = 3;
|
||||
|
||||
if (mpctx->sh_video && opts->term_osd != 1) {
|
||||
if (mpctx->video_out && opts->term_osd != 1) {
|
||||
// fallback on the timer
|
||||
char *text = NULL;
|
||||
|
||||
@ -2031,8 +2031,10 @@ void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
|
||||
return;
|
||||
|
||||
if (type == STREAM_VIDEO) {
|
||||
uninit_player(mpctx, INITIALIZED_VCODEC |
|
||||
(mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO));
|
||||
int uninit = INITIALIZED_VCODEC;
|
||||
if (!mpctx->opts->force_vo)
|
||||
uninit |= mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO;
|
||||
uninit_player(mpctx, uninit);
|
||||
} else if (type == STREAM_AUDIO) {
|
||||
uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
|
||||
} else if (type == STREAM_SUB) {
|
||||
@ -2380,7 +2382,8 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||
init_demux_stream(mpctx, STREAM_VIDEO);
|
||||
sh_video_t *sh_video = mpctx->sh_video;
|
||||
if (!sh_video) {
|
||||
uninit_player(mpctx, INITIALIZED_VO);
|
||||
if (!opts->force_vo)
|
||||
uninit_player(mpctx, INITIALIZED_VO);
|
||||
goto no_video;
|
||||
}
|
||||
|
||||
@ -2453,7 +2456,8 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||
return 1;
|
||||
|
||||
err_out:
|
||||
uninit_player(mpctx, INITIALIZED_VO);
|
||||
if (!opts->force_vo)
|
||||
uninit_player(mpctx, INITIALIZED_VO);
|
||||
cleanup_demux_stream(mpctx, STREAM_VIDEO);
|
||||
no_video:
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (mpctx->video_out) {
|
||||
vo_check_events(mpctx->video_out);
|
||||
handle_cursor_autohide(mpctx);
|
||||
}
|
||||
|
||||
double buffered_audio = -1;
|
||||
while (mpctx->sh_video) { // never loops, for "break;" only
|
||||
struct vo *vo = mpctx->video_out;
|
||||
@ -3606,12 +3650,16 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
|
||||
"\nFATAL: Could not initialize video filters (-vf) "
|
||||
"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);
|
||||
mpctx->current_track[STREAM_VIDEO] = NULL;
|
||||
if (!mpctx->current_track[STREAM_AUDIO])
|
||||
mpctx->stop_play = PT_NEXT_ENTRY;
|
||||
mpctx->error_playing = true;
|
||||
handle_force_window(mpctx);
|
||||
break;
|
||||
}
|
||||
video_left = frame_time >= 0;
|
||||
@ -3628,11 +3676,6 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
if (endpts != MP_NOPTS_VALUE)
|
||||
video_left &= mpctx->sh_video->pts < endpts;
|
||||
|
||||
// ================================================================
|
||||
vo_check_events(vo);
|
||||
|
||||
handle_cursor_autohide(mpctx);
|
||||
|
||||
handle_heartbeat_cmd(mpctx);
|
||||
|
||||
if (!video_left || (mpctx->paused && !mpctx->restart_playback))
|
||||
@ -3857,7 +3900,7 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
audio_sleep = 0.020;
|
||||
}
|
||||
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);
|
||||
if (mpctx->video_out->driver->draw_osd)
|
||||
want_redraw |= mpctx->osd->want_redraw;
|
||||
@ -3888,6 +3931,8 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
|
||||
handle_keep_open(mpctx);
|
||||
|
||||
handle_force_window(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
|
||||
&& 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;
|
||||
while (!(cmd = mp_input_get_cmd(mpctx->input,
|
||||
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, "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);
|
||||
}
|
||||
@ -4936,6 +4986,19 @@ static int mpv_main(int argc, char *argv[])
|
||||
|
||||
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
|
||||
// Lua user scripts can call arbitrary functions. Load them at a point
|
||||
// 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("ao", audio_driver_list, 0, &ao_obj_list),
|
||||
OPT_FLAG("fixed-vo", fixed_vo, CONF_GLOBAL),
|
||||
OPT_FLAG("force-window", force_vo, CONF_GLOBAL),
|
||||
OPT_FLAG("ontop", vo.ontop, 0),
|
||||
OPT_FLAG("border", vo.border, 0),
|
||||
|
||||
|
@ -50,6 +50,7 @@ typedef struct MPOpts {
|
||||
|
||||
struct m_obj_settings *audio_driver_list;
|
||||
int fixed_vo;
|
||||
int force_vo;
|
||||
int softvol;
|
||||
float mixer_init_volume;
|
||||
int mixer_init_mute;
|
||||
|
@ -316,8 +316,10 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
|
||||
struct voctrl_screenshot_args args =
|
||||
{ .full_window = (mode == MODE_FULL_WINDOW) };
|
||||
|
||||
struct vf_instance *vfilter = mpctx->sh_video->vfilter;
|
||||
vfilter->control(vfilter, VFCTRL_SCREENSHOT, &args);
|
||||
if (mpctx->sh_video && mpctx->sh_video->vfilter) {
|
||||
struct vf_instance *vfilter = mpctx->sh_video->vfilter;
|
||||
vfilter->control(vfilter, VFCTRL_SCREENSHOT, &args);
|
||||
}
|
||||
|
||||
if (!args.out_image)
|
||||
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.
|
||||
if (ctx->software_fallback_decoder) {
|
||||
uninit_avctx(sh);
|
||||
sh->vf_initialized = 0;
|
||||
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Error using hardware "
|
||||
"decoding, falling back to software decoding.\n");
|
||||
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->dheight = d_height;
|
||||
|
||||
talloc_free(vo->params);
|
||||
vo->params = NULL;
|
||||
|
||||
struct mp_image_params p2 = *params;
|
||||
|
||||
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_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) {
|
||||
mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback,
|
||||
NULL, vo);
|
||||
|
@ -238,6 +238,7 @@ struct vo {
|
||||
struct mp_log *log; // Using e.g. "[vo/vdpau]" as prefix
|
||||
int config_ok; // Last config call was successful?
|
||||
int config_count; // Total number of successful config calls
|
||||
struct mp_image_params *params; // Configured parameters (as in vo_reconfig)
|
||||
|
||||
bool probing;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user