1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-26 09:02:38 +00:00
mpv/video/out/vo.c

621 lines
18 KiB
C
Raw Normal View History

/*
* libvo common functions, variables used by many/all drivers.
*
* 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 <string.h>
#include <assert.h>
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
#include <stdbool.h>
#include <unistd.h>
2013-06-15 17:23:53 +00:00
#include <libavutil/common.h>
#include "talloc.h"
#include "config.h"
#include "osdep/timer.h"
#include "mpvcore/options.h"
#include "mpvcore/bstr.h"
#include "vo.h"
#include "aspect.h"
#include "mpvcore/input/input.h"
#include "mpvcore/m_config.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mpv_global.h"
#include "video/mp_image.h"
#include "video/vfcap.h"
#include "sub/osd.h"
//
// Externally visible list of all vo drivers
//
extern struct vo_driver video_out_x11;
extern struct vo_driver video_out_vdpau;
extern struct vo_driver video_out_xv;
extern struct vo_driver video_out_opengl;
extern struct vo_driver video_out_opengl_hq;
extern struct vo_driver video_out_opengl_old;
extern struct vo_driver video_out_null;
extern struct vo_driver video_out_image;
extern struct vo_driver video_out_lavc;
extern struct vo_driver video_out_caca;
2008-11-22 17:16:43 +00:00
extern struct vo_driver video_out_direct3d;
extern struct vo_driver video_out_direct3d_shaders;
extern struct vo_driver video_out_sdl;
2009-05-08 20:50:26 +00:00
extern struct vo_driver video_out_corevideo;
video: add vaapi decode and output support This is based on the MPlayer VA API patches. To be exact it's based on a very stripped down version of commit f1ad459a263f8537f6c from git://gitorious.org/vaapi/mplayer.git. This doesn't contain useless things like benchmarking hacks and the demo code for GLX interop. Also, unlike in the original patch, decoding and video output are split into separate source files (the separation between decoding and display also makes pixel format hacks unnecessary). On the other hand, some features not present in the original patch were added, like screenshot support. VA API is rather bad for actual video output. Dealing with older libva versions or the completely broken vdpau backend doesn't help. OSD is low quality and should be rather slow. In some cases, only either OSD or subtitles can be shown at the same time (because OSD is drawn first, OSD is prefered). Also, libva can't decide whether it accepts straight or premultiplied alpha for OSD sub-pictures: the vdpau backend seems to assume premultiplied, while a native vaapi driver uses straight. So I picked straight alpha. It doesn't matter much, because the blending code for straight alpha I added to img_convert.c is probably buggy, and ASS subtitles might be blended incorrectly. Really good video output with VA API would probably use OpenGL and the GL interop features, but at this point you might just use vo_opengl. (Patches for making HW decoding with vo_opengl have a chance of being accepted.) Despite these issues, decoding seems to work ok. I still got tearing on the Intel system I tested (Intel(R) Core(TM) i3-2350M). It was also tested with the vdpau vaapi wrapper on a nvidia system; however this was rather broken. (Fortunately, there is no reason to use mpv's VAAPI support over native VDPAU.)
2013-08-09 12:01:30 +00:00
extern struct vo_driver video_out_vaapi;
extern struct vo_driver video_out_wayland;
const struct vo_driver *video_out_drivers[] =
{
#if HAVE_VDPAU
&video_out_vdpau,
#endif
#if HAVE_GL
&video_out_opengl,
#endif
#if HAVE_DIRECT3D
&video_out_direct3d_shaders,
&video_out_direct3d,
#endif
#if HAVE_COREVIDEO
&video_out_corevideo,
#endif
#if HAVE_XV
&video_out_xv,
#endif
#if HAVE_SDL2
&video_out_sdl,
#endif
#if HAVE_GL
&video_out_opengl_old,
#endif
#if HAVE_VAAPI
video: add vaapi decode and output support This is based on the MPlayer VA API patches. To be exact it's based on a very stripped down version of commit f1ad459a263f8537f6c from git://gitorious.org/vaapi/mplayer.git. This doesn't contain useless things like benchmarking hacks and the demo code for GLX interop. Also, unlike in the original patch, decoding and video output are split into separate source files (the separation between decoding and display also makes pixel format hacks unnecessary). On the other hand, some features not present in the original patch were added, like screenshot support. VA API is rather bad for actual video output. Dealing with older libva versions or the completely broken vdpau backend doesn't help. OSD is low quality and should be rather slow. In some cases, only either OSD or subtitles can be shown at the same time (because OSD is drawn first, OSD is prefered). Also, libva can't decide whether it accepts straight or premultiplied alpha for OSD sub-pictures: the vdpau backend seems to assume premultiplied, while a native vaapi driver uses straight. So I picked straight alpha. It doesn't matter much, because the blending code for straight alpha I added to img_convert.c is probably buggy, and ASS subtitles might be blended incorrectly. Really good video output with VA API would probably use OpenGL and the GL interop features, but at this point you might just use vo_opengl. (Patches for making HW decoding with vo_opengl have a chance of being accepted.) Despite these issues, decoding seems to work ok. I still got tearing on the Intel system I tested (Intel(R) Core(TM) i3-2350M). It was also tested with the vdpau vaapi wrapper on a nvidia system; however this was rather broken. (Fortunately, there is no reason to use mpv's VAAPI support over native VDPAU.)
2013-08-09 12:01:30 +00:00
&video_out_vaapi,
#endif
#if HAVE_X11
&video_out_x11,
#endif
&video_out_null,
// should not be auto-selected
&video_out_image,
#if HAVE_CACA
&video_out_caca,
#endif
#if HAVE_ENCODING
&video_out_lavc,
#endif
#if HAVE_GL
&video_out_opengl_hq,
#endif
#if HAVE_WAYLAND
&video_out_wayland,
#endif
NULL
};
static bool get_desc(struct m_obj_desc *dst, int index)
{
if (index >= MP_ARRAY_SIZE(video_out_drivers) - 1)
return false;
const struct vo_driver *vo = video_out_drivers[index];
*dst = (struct m_obj_desc) {
.name = vo->name,
.description = vo->description,
.priv_size = vo->priv_size,
.priv_defaults = vo->priv_defaults,
.options = vo->options,
.hidden = vo->encode,
.p = vo,
};
return true;
}
// For the vo option
const struct m_obj_list vo_obj_list = {
.get_desc = get_desc,
.description = "video outputs",
.aliases = {
{"gl", "opengl"},
{"gl3", "opengl-hq"},
{0}
},
.allow_unknown_entries = true,
.allow_trailer = true,
};
static struct vo *vo_create(struct mpv_global *global,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
char *name, char **args)
{
struct mp_log *log = mp_log_new(NULL, global->log, "vo");
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &vo_obj_list, bstr0(name))) {
mp_msg_log(log, MSGL_ERR, "Video output %s not found!\n", name);
talloc_free(log);
return NULL;
};
struct vo *vo = talloc_ptrtype(NULL, vo);
*vo = (struct vo) {
.vo_log = { .log = talloc_steal(vo, log) },
.log = mp_log_new(vo, log, name),
.driver = desc.p,
.opts = &global->opts->vo,
.encode_lavc_ctx = encode_lavc_ctx,
.input_ctx = input_ctx,
.event_fd = -1,
.registered_fd = -1,
.aspdat = { .monitor_par = 1 },
.next_pts = MP_NOPTS_VALUE,
.next_pts2 = MP_NOPTS_VALUE,
};
if (vo->driver->encode != !!vo->encode_lavc_ctx)
goto error;
struct m_config *config = m_config_from_obj_desc(vo, &desc);
if (m_config_apply_defaults(config, name, vo->opts->vo_defs) < 0)
goto error;
if (m_config_set_obj_params(config, args) < 0)
goto error;
vo->priv = config->optstruct;
if (vo->driver->preinit(vo))
goto error;
return vo;
error:
talloc_free(vo);
return NULL;
}
int vo_control(struct vo *vo, uint32_t request, void *data)
{
return vo->driver->control(vo, request, data);
}
void vo_queue_image(struct vo *vo, struct mp_image *mpi)
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
{
if (!vo->config_ok)
return;
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
if (vo->driver->buffer_frames) {
vo->driver->draw_image(vo, mpi);
return;
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
}
vo->frame_loaded = true;
vo->next_pts = mpi->pts;
vo->next_pts2 = MP_NOPTS_VALUE;
assert(!vo->waiting_mpi);
vo->waiting_mpi = mp_image_new_ref(mpi);
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
}
int vo_redraw_frame(struct vo *vo)
{
if (!vo->config_ok)
return -1;
if (vo_control(vo, VOCTRL_REDRAW_FRAME, NULL) == true) {
vo->want_redraw = false;
vo->redrawing = true;
return 0;
}
return -1;
}
bool vo_get_want_redraw(struct vo *vo)
{
if (!vo->config_ok)
return false;
return vo->want_redraw;
}
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
int vo_get_buffered_frame(struct vo *vo, bool eof)
{
if (!vo->config_ok)
return -1;
if (vo->frame_loaded)
return 0;
if (!vo->driver->buffer_frames)
return -1;
vo->driver->get_buffered_frame(vo, eof);
return vo->frame_loaded ? 0 : -1;
}
void vo_skip_frame(struct vo *vo)
{
vo_control(vo, VOCTRL_SKIPFRAME, NULL);
vo->frame_loaded = false;
mp_image_unrefp(&vo->waiting_mpi);
}
void vo_new_frame_imminent(struct vo *vo)
{
if (vo->driver->buffer_frames)
vo_control(vo, VOCTRL_NEWFRAME, NULL);
else {
assert(vo->frame_loaded);
assert(vo->waiting_mpi);
assert(vo->waiting_mpi->pts == vo->next_pts);
vo->driver->draw_image(vo, vo->waiting_mpi);
mp_image_unrefp(&vo->waiting_mpi);
}
}
void vo_draw_osd(struct vo *vo, struct osd_state *osd)
{
if (vo->config_ok && vo->driver->draw_osd)
vo->driver->draw_osd(vo, osd);
}
Implement vsync-aware frame timing for VDPAU Main things added are custom frame dropping for VDPAU to work around the display FPS limit, frame timing adjustment to avoid jitter when video frame times keep falling near vsyncs, and use of VDPAU's timing feature to keep one future frame queued in advance. NVIDIA's VDPAU implementation refuses to change the displayed frame more than once per vsync. This set a limit on how much video could be sped up, and caused problems for nearly all videos on low-FPS video projectors (playing 24 FPS video on a 24 FPS projector would not work reliably as MPlayer may need to slightly speed up the video for AV sync). This commit adds a framedrop mechanism that drops some frames so that no more than one is sent for display per vsync. The code tries to select the dropped frames smartly, selecting the best one to show for each vsync. Because of the timing features needed the drop functionality currently does not work if the correct-pts option is disabled. The code also adjusts frame timing slightly to avoid jitter. If you for example play 24 FPS video content on a 72 FPS display then normally a frame would be shown for 3 vsyncs, but if the frame times happen to fall near vsyncs and change between just before and just after then there could be frames alternating between 2 and 4 vsyncs. The code changes frame timing by up to one quarter vsync interval to avoid this. The above functionality depends on having reliable vsync timing information available. The display refresh rate is not directly provided by the VDPAU API. The current code uses information from the XF86VidMode extension if available; I'm not sure how common cases where that is inaccurate are. The refresh rate can be specified manually if necessary. After the changes in this commit MPlayer now always tries to keep one frame queued for future display using VDPAU's internal timing mechanism (however no more than 50 ms to the future). This should make video playback somewhat more robust against timing inaccuracies caused by system load.
2009-11-15 02:39:22 +00:00
void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration)
{
if (!vo->config_ok)
return;
if (!vo->redrawing) {
vo->frame_loaded = false;
vo->next_pts = MP_NOPTS_VALUE;
vo->next_pts2 = MP_NOPTS_VALUE;
}
vo->want_redraw = false;
vo->redrawing = false;
Implement vsync-aware frame timing for VDPAU Main things added are custom frame dropping for VDPAU to work around the display FPS limit, frame timing adjustment to avoid jitter when video frame times keep falling near vsyncs, and use of VDPAU's timing feature to keep one future frame queued in advance. NVIDIA's VDPAU implementation refuses to change the displayed frame more than once per vsync. This set a limit on how much video could be sped up, and caused problems for nearly all videos on low-FPS video projectors (playing 24 FPS video on a 24 FPS projector would not work reliably as MPlayer may need to slightly speed up the video for AV sync). This commit adds a framedrop mechanism that drops some frames so that no more than one is sent for display per vsync. The code tries to select the dropped frames smartly, selecting the best one to show for each vsync. Because of the timing features needed the drop functionality currently does not work if the correct-pts option is disabled. The code also adjusts frame timing slightly to avoid jitter. If you for example play 24 FPS video content on a 72 FPS display then normally a frame would be shown for 3 vsyncs, but if the frame times happen to fall near vsyncs and change between just before and just after then there could be frames alternating between 2 and 4 vsyncs. The code changes frame timing by up to one quarter vsync interval to avoid this. The above functionality depends on having reliable vsync timing information available. The display refresh rate is not directly provided by the VDPAU API. The current code uses information from the XF86VidMode extension if available; I'm not sure how common cases where that is inaccurate are. The refresh rate can be specified manually if necessary. After the changes in this commit MPlayer now always tries to keep one frame queued for future display using VDPAU's internal timing mechanism (however no more than 50 ms to the future). This should make video playback somewhat more robust against timing inaccuracies caused by system load.
2009-11-15 02:39:22 +00:00
if (vo->driver->flip_page_timed)
vo->driver->flip_page_timed(vo, pts_us, duration);
else
vo->driver->flip_page(vo);
vo->hasframe = true;
}
void vo_check_events(struct vo *vo)
{
if (!vo->config_ok) {
if (vo->registered_fd != -1)
mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd);
vo->registered_fd = -1;
return;
}
vo_control(vo, VOCTRL_CHECK_EVENTS, NULL);
}
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
void vo_seek_reset(struct vo *vo)
{
vo_control(vo, VOCTRL_RESET, NULL);
vo->frame_loaded = false;
vo->next_pts = MP_NOPTS_VALUE;
vo->next_pts2 = MP_NOPTS_VALUE;
vo->hasframe = false;
mp_image_unrefp(&vo->waiting_mpi);
core/VO: Allow VO drivers to add/modify frames Add interfaces to allow VO drivers to add or remove frames from the video stream and to alter timestamps. Currently this functionality only works with in correct-pts mode. Use the new functionality in vo_vdpau to properly support frame-adding deinterlace modes. Frames added by the VDPAU deinterlacing code are now properly timed. Before every second frame was always shown immediately (probably next monitor refresh) after the previous one, even if you were watching things in slow motion, and framestepping didn't stop at them at all. When seeking the deinterlace algorithm is no longer fed a mix of frames from old and new positions. As a side effect of the changes a problem with resize events was also fixed. Resizing calls video_to_output_surface() to render the frame at the new resolution, but before this function also changed the list of history frames, so resizing could give an image different from the original one, and also corrupt next frames due to them seeing the wrong history. Now the function has no such side effects. There are more resize-related problems though that will be fixed in a later commit. The deint_mpi[] list of reserved frames is increased from 2 to 3 entries for reasons related to the above. Having 2 entries is enough when you initially get a new frame in draw_image() because then you'll have those two entries plus the new one for a total of 3 (the code relied on the oldest mpi implicitly staying reserved for the duration of the call even after usage count was decreased). However if you want to be able to reproduce the rendering outside draw_image(), relying on the explicitly reserved list only, then it needs to store 3 entries.
2009-09-18 13:27:55 +00:00
}
void vo_destroy(struct vo *vo)
{
if (vo->registered_fd != -1)
mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd);
mp_image_unrefp(&vo->waiting_mpi);
vo->driver->uninit(vo);
talloc_free(vo);
}
struct vo *init_best_video_out(struct mpv_global *global,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx)
{
struct m_obj_settings *vo_list = global->opts->vo.video_driver_list;
// first try the preferred drivers, with their optional subdevice param:
if (vo_list && vo_list[0].name) {
for (int n = 0; vo_list[n].name; n++) {
// Something like "-vo name," allows fallback to autoprobing.
if (strlen(vo_list[n].name) == 0)
goto autoprobe;
struct vo *vo = vo_create(global, input_ctx, encode_lavc_ctx,
vo_list[n].name, vo_list[n].attribs);
if (vo)
return vo;
}
return NULL;
}
autoprobe:
// now try the rest...
for (int i = 0; video_out_drivers[i]; i++) {
struct vo *vo = vo_create(global, input_ctx, encode_lavc_ctx,
(char *)video_out_drivers[i]->name, NULL);
if (vo)
return vo;
}
return NULL;
}
// Fit *w/*h into the size specified by geo.
static void apply_autofit(int *w, int *h, int scr_w, int scr_h,
struct m_geometry *geo, bool allow_upscale)
{
if (!geo->wh_valid)
return;
int dummy;
int n_w = *w, n_h = *h;
m_geometry_apply(&dummy, &dummy, &n_w, &n_h, scr_w, scr_h, geo);
if (!allow_upscale && *w <= n_w && *h <= n_h)
return;
// If aspect mismatches, always make the window smaller than the fit box
double asp = (double)*w / *h;
double n_asp = (double)n_w / n_h;
if (n_asp <= asp) {
*w = n_w;
*h = n_w / asp;
} else {
*w = n_h * asp;
*h = n_h;
}
}
// Set window size (vo->dwidth/dheight) and position (vo->dx/dy) according to
// the video display size d_w/d_h.
// NOTE: currently, all GUI backends do their own handling of window geometry
// additional to this code. This is to deal with initial window placement,
// fullscreen handling, avoiding resize on config() with no size change,
// multi-monitor stuff, and possibly more.
static void determine_window_geometry(struct vo *vo, int d_w, int d_h)
{
struct mp_vo_opts *opts = vo->opts;
int scr_w = opts->screenwidth;
int scr_h = opts->screenheight;
aspect_calc_monitor(vo, &d_w, &d_h);
apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit, true);
apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_larger, false);
vo->dx = (int)(opts->screenwidth - d_w) / 2;
vo->dy = (int)(opts->screenheight - d_h) / 2;
m_geometry_apply(&vo->dx, &vo->dy, &d_w, &d_h, scr_w, scr_h,
&opts->geometry);
vo->dx += vo->xinerama_x;
vo->dy += vo->xinerama_y;
vo->dwidth = d_w;
vo->dheight = d_h;
}
static int event_fd_callback(void *ctx, int fd)
{
struct vo *vo = ctx;
vo_check_events(vo);
input: rework event reading and command queuing Rework much of the logic related to reading from event sources and queuing commands. The two biggest architecture changes are: - The code buffering keycodes in mp_fifo.c is gone. Instead key input is now immediately fed to input.c and interpreted as commands, and then the commands are buffered instead. - mp_input_get_cmd() now always tries to read every available event from every event source and convert them to (buffered) commands. Before it would only process new events until one new command became available. Some relevant behavior changes: - Before commands could be lost when stream code called mp_input_check_interrupt() which read commands (to see if they were of types that triggered aborts during slow IO tasks) and then threw them away. This was especially an issue if cache was enabled and slow to read. Fixed - now it's possible to check whether there are queued commands which will abort playback of the current file without throwing other commands away. - mp_input_check_interrupt() now prints a message if it returns true. This is especially useful because the failures caused by aborted stream reads can trigger error messages from other code that was doing the read; the new message makes it more obvious what the cause of the subsequent error messages is. - It's now possible to again avoid making stdin non-blocking (which caused some issues) without reintroducing extra latency. The change will be done in a subsequent commit. - Event sources that do not support select() should now have somewhat lower latency in certain situations as they will be checked both before and after select()/sleep in input reading; before the sleep always happened first even if such sources already had queued input. Before the key fifo was also handled in this manner (first key triggered select, but if multiple were read then rest could be delayed; however in most cases this didn't add latency in practice as after central code started doing command handling it queried for further commands with a max sleep time of 0). - Key fifo limiting is more accurate now: it now counts actual commands intead of keycodes, and all queued keys are read immediately from input devices so they can be counted correctly. - Since keypresses are now interpreted immediately, commands which change keybindings will no longer affect following keypresses that have already been read before the command is executed. This should not be an issue in practice with current keybinding behavior.
2011-07-17 01:47:50 +00:00
return MP_INPUT_NOTHING;
}
int vo_reconfig(struct vo *vo, struct mp_image_params *params, int flags)
{
int d_width = params->d_w;
int d_height = params->d_h;
aspect_save_videores(vo, params->w, params->h, d_width, d_height);
if (vo_control(vo, VOCTRL_UPDATE_SCREENINFO, NULL) == VO_TRUE) {
determine_window_geometry(vo, params->d_w, params->d_h);
d_width = vo->dwidth;
d_height = vo->dheight;
}
vo->dwidth = d_width;
vo->dheight = d_height;
talloc_free(vo->params);
vo->params = NULL;
struct mp_image_params p2 = *params;
int ret;
if (vo->driver->reconfig) {
ret = vo->driver->reconfig(vo, &p2, flags);
} else {
// Old config() takes window size, while reconfig() takes aspect (!)
ret = vo->driver->config(vo, p2.w, p2.h, d_width, d_height, flags,
p2.imgfmt);
ret = ret ? -1 : 0;
}
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);
vo->registered_fd = vo->event_fd;
}
vo->frame_loaded = false;
vo->waiting_mpi = NULL;
vo->redrawing = false;
vo->hasframe = false;
if (vo->config_ok) {
// Legacy
struct mp_csp_details csp;
if (vo_control(vo, VOCTRL_GET_YUV_COLORSPACE, &csp) > 0) {
csp.levels_in = params->colorlevels;
csp.levels_out = params->outputlevels;
csp.format = params->colorspace;
vo_control(vo, VOCTRL_SET_YUV_COLORSPACE, &csp);
}
}
return ret;
}
/**
* \brief lookup an integer in a table, table must have 0 as the last key
* \param key key to search for
* \result translation corresponding to key or "to" value of last mapping
* if not found.
*/
int lookup_keymap_table(const struct mp_keymap *map, int key) {
while (map->from && map->from != key) map++;
return map->to;
}
static void print_video_rect(struct vo *vo, struct mp_rect src,
struct mp_rect dst, struct mp_osd_res osd)
{
int sw = src.x1 - src.x0, sh = src.y1 - src.y0;
int dw = dst.x1 - dst.x0, dh = dst.y1 - dst.y0;
MP_VERBOSE(&vo->vo_log, "Window size: %dx%d\n",
vo->dwidth, vo->dheight);
MP_VERBOSE(&vo->vo_log, "Video source: %dx%d (%dx%d)\n",
vo->aspdat.orgw, vo->aspdat.orgh,
vo->aspdat.prew, vo->aspdat.preh);
MP_VERBOSE(&vo->vo_log, "Video display: (%d, %d) %dx%d -> (%d, %d) %dx%d\n",
src.x0, src.y0, sw, sh, dst.x0, dst.y0, dw, dh);
MP_VERBOSE(&vo->vo_log, "Video scale: %f/%f\n",
(double)dw / sw, (double)dh / sh);
MP_VERBOSE(&vo->vo_log, "OSD borders: l=%d t=%d r=%d b=%d\n",
osd.ml, osd.mt, osd.mr, osd.mb);
MP_VERBOSE(&vo->vo_log, "Video borders: l=%d t=%d r=%d b=%d\n",
dst.x0, dst.y0, vo->dwidth - dst.x1, vo->dheight - dst.y1);
}
// Clamp [start, end) to range [0, size) with various fallbacks.
static void clamp_size(int size, int *start, int *end)
{
*start = FFMAX(0, *start);
*end = FFMIN(size, *end);
if (*start >= *end) {
*start = 0;
*end = 1;
}
}
// Round source to a multiple of 2, this is at least needed for vo_direct3d
// and ATI cards.
#define VID_SRC_ROUND_UP(x) (((x) + 1) & ~1)
static void src_dst_split_scaling(int src_size, int dst_size,
int scaled_src_size, bool unscaled,
float zoom, float align, float pan,
int *src_start, int *src_end,
int *dst_start, int *dst_end,
int *osd_margin_a, int *osd_margin_b)
{
if (unscaled) {
scaled_src_size = src_size;
zoom = 0.0;
}
scaled_src_size += zoom * scaled_src_size;
align = (align + 1) / 2;
*src_start = 0;
*src_end = src_size;
*dst_start = (dst_size - scaled_src_size) * align + pan * scaled_src_size;
*dst_end = *dst_start + scaled_src_size;
// Distance of screen frame to video
*osd_margin_a = *dst_start;
*osd_margin_b = dst_size - *dst_end;
// Clip to screen
int s_src = *src_end - *src_start;
int s_dst = *dst_end - *dst_start;
if (*dst_start < 0) {
int border = -(*dst_start) * s_src / s_dst;
*src_start += VID_SRC_ROUND_UP(border);
*dst_start = 0;
}
if (*dst_end > dst_size) {
int border = (*dst_end - dst_size) * s_src / s_dst;
*src_end -= VID_SRC_ROUND_UP(border);
*dst_end = dst_size;
}
if (unscaled) {
// Force unscaled by reducing the range for src or dst
int src_s = *src_end - *src_start;
int dst_s = *dst_end - *dst_start;
if (src_s > dst_s) {
*src_end = *src_start + dst_s;
} else if (src_s < dst_s) {
*dst_end = *dst_start + src_s;
}
}
// For sanity: avoid bothering VOs with corner cases
clamp_size(src_size, src_start, src_end);
clamp_size(dst_size, dst_start, dst_end);
}
// Calculate the appropriate source and destination rectangle to
// get a correctly scaled picture, including pan-scan.
// out_src: visible part of the video
// out_dst: area of screen covered by the video source rectangle
// out_osd: OSD size, OSD margins, etc.
void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
struct mp_rect *out_dst, struct mp_osd_res *out_osd)
{
struct mp_vo_opts *opts = vo->opts;
int src_w = vo->aspdat.orgw;
int src_h = vo->aspdat.orgh;
struct mp_rect dst = {0, 0, vo->dwidth, vo->dheight};
struct mp_rect src = {0, 0, src_w, src_h};
struct mp_osd_res osd = {
.w = vo->dwidth,
.h = vo->dheight,
.display_par = vo->aspdat.monitor_par,
};
if (opts->keepaspect) {
int scaled_width, scaled_height;
aspect_calc_panscan(vo, &scaled_width, &scaled_height);
src_dst_split_scaling(src_w, vo->dwidth, scaled_width, opts->unscaled,
opts->zoom, opts->align_x, opts->pan_x,
&src.x0, &src.x1, &dst.x0, &dst.x1,
&osd.ml, &osd.mr);
src_dst_split_scaling(src_h, vo->dheight, scaled_height, opts->unscaled,
opts->zoom, opts->align_y, opts->pan_y,
&src.y0, &src.y1, &dst.y0, &dst.y1,
&osd.mt, &osd.mb);
}
*out_src = src;
*out_dst = dst;
*out_osd = osd;
print_video_rect(vo, src, dst, osd);
}
// Return the window title the VO should set. Always returns a null terminated
// string. The string is valid until frontend code is invoked again. Copy it if
// you need to keep the string for an extended period of time.
const char *vo_get_window_title(struct vo *vo)
{
if (!vo->window_title)
vo->window_title = talloc_strdup(vo, "");
return vo->window_title;
}
/**
* Generates a mouse movement message if those are enable and sends it
* to the "main" MPlayer.
*
* \param posx new x position of mouse
* \param posy new y position of mouse
*/
2010-04-26 16:22:56 +00:00
void vo_mouse_movement(struct vo *vo, int posx, int posy)
{
input: handle mouse movement differently Before this commit, mouse movement events emitted a special command ("set_mouse_pos"), which was specially handled in command.c. This was once special-cased to the dvdnav and menu code, and did nothing after libmenu and dvdnav were removed. Change it so that mouse movement triggers a pseudo-key ("MOUSE_MOVE"), which then can be bound to an arbitrary command. The mouse position is now managed in input.c. A command which actually needs the mouse position can use either mp_input_get_mouse_pos() or mp_get_osd_mouse_pos() to query it. The former returns raw window-space coordinates, while the latter returns coordinates transformed to OSD- space. (Both are the same for most VOs, except vo_xv and vo_x11, which can't render OSD in window-space. These require extra code for mapping mouse position.) As of this commit, there is still nothing that uses mouse movement, so MOUSE_MOVE is mapped to "ignore" to silence warnings when moving the mouse (much like MOUSE_BTN0). Extend the concept of input sections. Allow multiple sections to be active at once, and organize them as stack. Bindings from the top of the stack are preferred to lower ones. Each section has a mouse input section associated, inside which mouse events are associated with the bindings. If the mouse pointer is outside of a section's mouse area, mouse events will be dispatched to an input section lower on the stack of active sections. This is intended for scripting, which is to be added later. Two scripts could occupy different areas of the screen without conflicting with each other. (If it turns out that this mechanism is useless, we'll just remove it again.)
2013-04-26 00:13:30 +00:00
if (!vo->opts->enable_mouse_movements)
return;
float p[2] = {posx, posy};
vo_control(vo, VOCTRL_WINDOW_TO_OSD_COORDS, p);
mp_input_set_mouse_pos(vo->input_ctx, p[0], p[1]);
}