2009-02-08 03:27:30 +00:00
|
|
|
/*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
vo.c, vo.h, vo_null.c: change license to LGPL
Most contributors have agreed. vo.c requires a "driver" entry for each
video output - we assume that if someone who didn't agree to LGPL added
a line, it's fine for vo.c to be LGPL anyway. If the affected video
output is not disabled at compilation time, the resulting binary will be
GPL anyway.
One problem are the changes by Nick Kurshev (usually using "nick" as SVN
username). He could not be reached. I believe all changes to his files
are actually gone, but here is a detailed listing:
fa1d5742bc: nick introduces a new VO API. It was removed in 64bedd9683.
Some of this was replaced by VOCTRLs are introduced in 7c51652a1b,
obviously replacing at least some functionality by his API.
b587a3d642: nick adds a vo_tune_info_t struct. Removed in 64bedd9683
too.
9caad2c29a: nick adds some VOCTRLs, which were silently removed in
8cc5ba5ab8 (they became unused probably with the VIDIX removal).
340183b0e9: nick adds VO-based screenshots, which got removed in
2f4b840f62. Strangely the same name was introduced in 01cf896a2f again,
but this is a coincidence and worked differently (also it was removed
yet again in 2858232220).
104c125e6d: nick adds an option for "direct rendering". It was renamed
in 6403904ae9 and fully removed in e48b21dd87.
5ddd8e92a1: nick adds code to check the VO driver preinit arg to every
single VO driver. The argument itself and any possibly remaining code
associated with it was removed in 1f5ffe7d30.
f6878753fb: nick adds header inclusion guards. We assume this is not
relevant for copyright.
Some of nick's code was merely moved to other files, such as the
equalizer stuff added in 555c676683 and moved in 4db72f6a80 and
12579136ff, and don't affect copyright of these files anymore.
Other notes:
fef7b17c34: a patch by someone who wasn't asked for relicensing added a
symbol that was removed again in 1b09f4633.
4a8a46fafd: author probably didn't agree to LGPL, but the function
signature was changed later on anyway, and nothing of this is left.
7b25afd742: the same author adds a symbol to what is vo.h today, which
this relicensing commit removes, as it was unused. (It's not clear
whether the mere symbol is copyrightable, but no need to take a risk.)
3a406e94d7, 9dd8f241ac: slave mode things by someone who couldn't be
reached. This aspect of the old slave mode was completely removed.
bbeb54d80a: patch by someone who was not asked, but the added code was
completely removed again.
2017-05-10 12:32:34 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2009-02-08 03:27:30 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
vo.c, vo.h, vo_null.c: change license to LGPL
Most contributors have agreed. vo.c requires a "driver" entry for each
video output - we assume that if someone who didn't agree to LGPL added
a line, it's fine for vo.c to be LGPL anyway. If the affected video
output is not disabled at compilation time, the resulting binary will be
GPL anyway.
One problem are the changes by Nick Kurshev (usually using "nick" as SVN
username). He could not be reached. I believe all changes to his files
are actually gone, but here is a detailed listing:
fa1d5742bc: nick introduces a new VO API. It was removed in 64bedd9683.
Some of this was replaced by VOCTRLs are introduced in 7c51652a1b,
obviously replacing at least some functionality by his API.
b587a3d642: nick adds a vo_tune_info_t struct. Removed in 64bedd9683
too.
9caad2c29a: nick adds some VOCTRLs, which were silently removed in
8cc5ba5ab8 (they became unused probably with the VIDIX removal).
340183b0e9: nick adds VO-based screenshots, which got removed in
2f4b840f62. Strangely the same name was introduced in 01cf896a2f again,
but this is a coincidence and worked differently (also it was removed
yet again in 2858232220).
104c125e6d: nick adds an option for "direct rendering". It was renamed
in 6403904ae9 and fully removed in e48b21dd87.
5ddd8e92a1: nick adds code to check the VO driver preinit arg to every
single VO driver. The argument itself and any possibly remaining code
associated with it was removed in 1f5ffe7d30.
f6878753fb: nick adds header inclusion guards. We assume this is not
relevant for copyright.
Some of nick's code was merely moved to other files, such as the
equalizer stuff added in 555c676683 and moved in 4db72f6a80 and
12579136ff, and don't affect copyright of these files anymore.
Other notes:
fef7b17c34: a patch by someone who wasn't asked for relicensing added a
symbol that was removed again in 1b09f4633.
4a8a46fafd: author probably didn't agree to LGPL, but the function
signature was changed later on anyway, and nothing of this is left.
7b25afd742: the same author adds a symbol to what is vo.h today, which
this relicensing commit removes, as it was unused. (It's not clear
whether the mere symbol is copyrightable, but no need to take a risk.)
3a406e94d7, 9dd8f241ac: slave mode things by someone who couldn't be
reached. This aspect of the old slave mode was completely removed.
bbeb54d80a: patch by someone who was not asked, but the added code was
completely removed again.
2017-05-10 12:32:34 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
vo.c, vo.h, vo_null.c: change license to LGPL
Most contributors have agreed. vo.c requires a "driver" entry for each
video output - we assume that if someone who didn't agree to LGPL added
a line, it's fine for vo.c to be LGPL anyway. If the affected video
output is not disabled at compilation time, the resulting binary will be
GPL anyway.
One problem are the changes by Nick Kurshev (usually using "nick" as SVN
username). He could not be reached. I believe all changes to his files
are actually gone, but here is a detailed listing:
fa1d5742bc: nick introduces a new VO API. It was removed in 64bedd9683.
Some of this was replaced by VOCTRLs are introduced in 7c51652a1b,
obviously replacing at least some functionality by his API.
b587a3d642: nick adds a vo_tune_info_t struct. Removed in 64bedd9683
too.
9caad2c29a: nick adds some VOCTRLs, which were silently removed in
8cc5ba5ab8 (they became unused probably with the VIDIX removal).
340183b0e9: nick adds VO-based screenshots, which got removed in
2f4b840f62. Strangely the same name was introduced in 01cf896a2f again,
but this is a coincidence and worked differently (also it was removed
yet again in 2858232220).
104c125e6d: nick adds an option for "direct rendering". It was renamed
in 6403904ae9 and fully removed in e48b21dd87.
5ddd8e92a1: nick adds code to check the VO driver preinit arg to every
single VO driver. The argument itself and any possibly remaining code
associated with it was removed in 1f5ffe7d30.
f6878753fb: nick adds header inclusion guards. We assume this is not
relevant for copyright.
Some of nick's code was merely moved to other files, such as the
equalizer stuff added in 555c676683 and moved in 4db72f6a80 and
12579136ff, and don't affect copyright of these files anymore.
Other notes:
fef7b17c34: a patch by someone who wasn't asked for relicensing added a
symbol that was removed again in 1b09f4633.
4a8a46fafd: author probably didn't agree to LGPL, but the function
signature was changed later on anyway, and nothing of this is left.
7b25afd742: the same author adds a symbol to what is vo.h today, which
this relicensing commit removes, as it was unused. (It's not clear
whether the mere symbol is copyrightable, but no need to take a risk.)
3a406e94d7, 9dd8f241ac: slave mode things by someone who couldn't be
reached. This aspect of the old slave mode was completely removed.
bbeb54d80a: patch by someone who was not asked, but the added code was
completely removed again.
2017-05-10 12:32:34 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2009-02-08 03:27:30 +00:00
|
|
|
*/
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2023-10-19 14:26:26 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdatomic.h>
|
|
|
|
#include <stdbool.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2001-02-24 20:28:24 +00:00
|
|
|
|
|
|
|
#include "config.h"
|
2013-03-02 21:50:09 +00:00
|
|
|
#include "osdep/timer.h"
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
#include "osdep/threads.h"
|
|
|
|
#include "misc/dispatch.h"
|
|
|
|
#include "misc/rendezvous.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
2014-08-29 10:09:04 +00:00
|
|
|
#include "misc/bstr.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "vo.h"
|
2007-02-17 20:58:55 +00:00
|
|
|
#include "aspect.h"
|
2018-04-20 15:48:44 +00:00
|
|
|
#include "dr_helper.h"
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/input.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/m_config.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
|
|
|
#include "common/global.h"
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
#include "common/stats.h"
|
2016-05-09 17:42:03 +00:00
|
|
|
#include "video/hwdec.h"
|
video/filter: change filter API, use refcounting, remove filter DR
Change the entire filter API to use reference counted images instead
of vf_get_image().
Remove filter "direct rendering". This was useful for vf_expand and (in
rare cases) vf_sub: DR allowed these filters to pass a cropped image to
the filters before them. Then, on filtering, the image was "uncropped",
so that black bars could be added around the image without copying. This
means that in some cases, vf_expand will be slower (-vf gradfun,expand
for example).
Note that another form of DR used for in-place filters has been replaced
by simpler logic. Instead of trying to do DR, filters can check if the
image is writeable (with mp_image_is_writeable()), and do true in-place
if that's the case. This affects filters like vf_gradfun and vf_sub.
Everything has to support strides now. If something doesn't, making a
copy of the image data is required.
2012-11-05 13:25:04 +00:00
|
|
|
#include "video/mp_image.h"
|
2013-11-24 11:58:06 +00:00
|
|
|
#include "sub/osd.h"
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
#include "osdep/io.h"
|
2014-10-19 21:32:34 +00:00
|
|
|
#include "osdep/threads.h"
|
2002-09-29 21:53:05 +00:00
|
|
|
|
2017-10-09 19:45:26 +00:00
|
|
|
extern const struct vo_driver video_out_mediacodec_embed;
|
2015-09-30 20:52:22 +00:00
|
|
|
extern const struct vo_driver video_out_x11;
|
2014-06-10 21:56:05 +00:00
|
|
|
extern const struct vo_driver video_out_vdpau;
|
|
|
|
extern const struct vo_driver video_out_xv;
|
vo_opengl: refactor into vo_gpu
This is done in several steps:
1. refactor MPGLContext -> struct ra_ctx
2. move GL-specific stuff in vo_opengl into opengl/context.c
3. generalize context creation to support other APIs, and add --gpu-api
4. rename all of the --opengl- options that are no longer opengl-specific
5. move all of the stuff from opengl/* that isn't GL-specific into gpu/
(note: opengl/gl_utils.h became opengl/utils.h)
6. rename vo_opengl to vo_gpu
7. to handle window screenshots, the short-term approach was to just add
it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to
ra itself (and vo_gpu altered to compensate), but this was a stop-gap
measure to prevent this commit from getting too big
8. move ra->fns->flush to ra_gl_ctx instead
9. some other minor changes that I've probably already forgotten
Note: This is one half of a major refactor, the other half of which is
provided by rossy's following commit. This commit enables support for
all linux platforms, while his version enables support for all non-linux
platforms.
Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the
--opengl- options like --opengl-early-flush, --opengl-finish etc. Should
be a strict superset of the old functionality.
Disclaimer: Since I have no way of compiling mpv on all platforms, some
of these ports were done blindly. Specifically, the blind ports included
context_mali_fbdev.c and context_rpi.c. Since they're both based on
egl_helpers, the port should have gone smoothly without any major
changes required. But if somebody complains about a compile error on
those platforms (assuming anybody actually uses them), you know where to
complain.
2017-09-14 06:04:55 +00:00
|
|
|
extern const struct vo_driver video_out_gpu;
|
2021-04-09 07:14:54 +00:00
|
|
|
extern const struct vo_driver video_out_gpu_next;
|
2018-02-20 12:30:18 +00:00
|
|
|
extern const struct vo_driver video_out_libmpv;
|
2014-06-10 21:56:05 +00:00
|
|
|
extern const struct vo_driver video_out_null;
|
|
|
|
extern const struct vo_driver video_out_image;
|
|
|
|
extern const struct vo_driver video_out_lavc;
|
|
|
|
extern const struct vo_driver video_out_caca;
|
2015-04-15 16:14:14 +00:00
|
|
|
extern const struct vo_driver video_out_drm;
|
2014-06-10 21:56:05 +00:00
|
|
|
extern const struct vo_driver video_out_direct3d;
|
|
|
|
extern const struct vo_driver video_out_sdl;
|
|
|
|
extern const struct vo_driver video_out_vaapi;
|
2022-09-27 16:12:54 +00:00
|
|
|
extern const struct vo_driver video_out_dmabuf_wayland;
|
2018-08-27 20:06:38 +00:00
|
|
|
extern const struct vo_driver video_out_wlshm;
|
2016-10-12 23:30:45 +00:00
|
|
|
extern const struct vo_driver video_out_tct;
|
2020-11-01 11:13:31 +00:00
|
|
|
extern const struct vo_driver video_out_sixel;
|
2022-12-13 14:58:13 +00:00
|
|
|
extern const struct vo_driver video_out_kitty;
|
2008-04-03 03:25:41 +00:00
|
|
|
|
2023-01-14 22:35:54 +00:00
|
|
|
static const struct vo_driver *const video_out_drivers[] =
|
2001-02-24 20:28:24 +00:00
|
|
|
{
|
2017-07-06 17:58:26 +00:00
|
|
|
#if HAVE_ANDROID
|
2017-10-09 19:45:26 +00:00
|
|
|
&video_out_mediacodec_embed,
|
2016-12-08 09:59:50 +00:00
|
|
|
#endif
|
vo_opengl: refactor into vo_gpu
This is done in several steps:
1. refactor MPGLContext -> struct ra_ctx
2. move GL-specific stuff in vo_opengl into opengl/context.c
3. generalize context creation to support other APIs, and add --gpu-api
4. rename all of the --opengl- options that are no longer opengl-specific
5. move all of the stuff from opengl/* that isn't GL-specific into gpu/
(note: opengl/gl_utils.h became opengl/utils.h)
6. rename vo_opengl to vo_gpu
7. to handle window screenshots, the short-term approach was to just add
it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to
ra itself (and vo_gpu altered to compensate), but this was a stop-gap
measure to prevent this commit from getting too big
8. move ra->fns->flush to ra_gl_ctx instead
9. some other minor changes that I've probably already forgotten
Note: This is one half of a major refactor, the other half of which is
provided by rossy's following commit. This commit enables support for
all linux platforms, while his version enables support for all non-linux
platforms.
Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the
--opengl- options like --opengl-early-flush, --opengl-finish etc. Should
be a strict superset of the old functionality.
Disclaimer: Since I have no way of compiling mpv on all platforms, some
of these ports were done blindly. Specifically, the blind ports included
context_mali_fbdev.c and context_rpi.c. Since they're both based on
egl_helpers, the port should have gone smoothly without any major
changes required. But if somebody complains about a compile error on
those platforms (assuming anybody actually uses them), you know where to
complain.
2017-09-14 06:04:55 +00:00
|
|
|
&video_out_gpu,
|
2021-04-09 07:14:54 +00:00
|
|
|
&video_out_gpu_next,
|
2014-08-17 00:54:50 +00:00
|
|
|
#if HAVE_VDPAU
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_vdpau,
|
2014-08-17 00:54:50 +00:00
|
|
|
#endif
|
2013-07-16 11:28:28 +00:00
|
|
|
#if HAVE_DIRECT3D
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_direct3d,
|
2008-11-18 13:18:55 +00:00
|
|
|
#endif
|
2019-10-17 11:03:14 +00:00
|
|
|
#if HAVE_WAYLAND && HAVE_MEMFD_CREATE
|
2018-08-27 20:06:38 +00:00
|
|
|
&video_out_wlshm,
|
|
|
|
#endif
|
2013-07-16 11:28:28 +00:00
|
|
|
#if HAVE_XV
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_xv,
|
2009-09-18 21:25:36 +00:00
|
|
|
#endif
|
2019-10-22 14:41:19 +00:00
|
|
|
#if HAVE_SDL2_VIDEO
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_sdl,
|
2012-12-28 07:07:14 +00:00
|
|
|
#endif
|
2022-09-27 16:12:54 +00:00
|
|
|
#if HAVE_DMABUF_WAYLAND
|
|
|
|
&video_out_dmabuf_wayland,
|
|
|
|
#endif
|
2022-05-26 19:49:26 +00:00
|
|
|
#if HAVE_VAAPI_X11 && HAVE_GPL
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_vaapi,
|
2015-09-30 20:52:22 +00:00
|
|
|
#endif
|
|
|
|
#if HAVE_X11
|
|
|
|
&video_out_x11,
|
2004-10-04 19:36:12 +00:00
|
|
|
#endif
|
2024-02-18 16:43:30 +00:00
|
|
|
&video_out_libmpv,
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_null,
|
|
|
|
// should not be auto-selected
|
|
|
|
&video_out_image,
|
2016-10-12 23:30:45 +00:00
|
|
|
&video_out_tct,
|
2013-07-16 11:28:28 +00:00
|
|
|
#if HAVE_CACA
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_caca,
|
2013-02-06 21:29:21 +00:00
|
|
|
#endif
|
2015-04-15 16:14:14 +00:00
|
|
|
#if HAVE_DRM
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_drm,
|
2019-07-14 13:56:10 +00:00
|
|
|
#endif
|
2020-11-01 11:13:31 +00:00
|
|
|
#if HAVE_SIXEL
|
|
|
|
&video_out_sixel,
|
2015-04-15 16:14:14 +00:00
|
|
|
#endif
|
2022-12-13 14:58:13 +00:00
|
|
|
&video_out_kitty,
|
2015-04-16 20:29:25 +00:00
|
|
|
&video_out_lavc,
|
2001-02-24 20:28:24 +00:00
|
|
|
};
|
2002-05-13 13:15:40 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal {
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_thread thread;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct mp_dispatch_queue *dispatch;
|
2018-04-20 15:48:44 +00:00
|
|
|
struct dr_helper *dr_helper;
|
2017-07-23 07:41:51 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// --- The following fields are protected by lock
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex lock;
|
|
|
|
mp_cond wakeup;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
|
|
|
bool need_wakeup;
|
|
|
|
bool terminate;
|
|
|
|
|
video: fix and simplify video format changes and last frame display
The previous commit broke these things, and fixing them is separate in
this commit in order to reduce the volume of changes.
Move the image queue from the VO to the playback core. The image queue
is a remnant of the old way how vdpau was implemented, and increasingly
became more and more an artifact. In the end, it did only one thing:
computing the duration of the current frame. This was done by taking the
PTS difference between the current and the future frame. We keep this,
but by moving it out of the VO, we don't have to special-case format
changes anymore. This simplifies the code a lot.
Since we need the queue to compute the duration only, a queue size
larger than 2 makes no sense, and we can hardcode that.
Also change how the last frame is handled. The last frame is a bit of a
problem, because video timing works by showing one frame after another,
which makes it a special case. Make the VO provide a function to notify
us when the frame is done, instead. The frame duration is used for that.
This is not perfect. For example, changing playback speed during the
last frame doesn't update the end time. Pausing will not stop the clock
that times the last frame. But I don't think this matters for such a
corner case.
2014-08-12 21:17:35 +00:00
|
|
|
bool hasframe;
|
2014-08-23 10:43:09 +00:00
|
|
|
bool hasframe_rendered;
|
2014-10-03 19:53:32 +00:00
|
|
|
bool request_redraw; // redraw request from player to VO
|
|
|
|
bool want_redraw; // redraw request from VO to player
|
2015-01-12 04:14:41 +00:00
|
|
|
bool send_reset; // send VOCTRL_RESET
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
bool paused;
|
2024-02-04 15:35:13 +00:00
|
|
|
bool wakeup_on_done;
|
2015-03-12 22:35:38 +00:00
|
|
|
int queued_events; // event mask for the user
|
|
|
|
int internal_events; // event mask for us
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2023-09-07 23:15:01 +00:00
|
|
|
double nominal_vsync_interval;
|
2015-08-04 17:23:07 +00:00
|
|
|
|
2023-09-07 23:15:01 +00:00
|
|
|
double vsync_interval;
|
2015-11-25 21:07:56 +00:00
|
|
|
int64_t *vsync_samples;
|
|
|
|
int num_vsync_samples;
|
2015-11-28 17:40:25 +00:00
|
|
|
int64_t num_total_vsync_samples;
|
2023-09-16 02:45:24 +00:00
|
|
|
int64_t prev_vsync;
|
2023-09-07 23:15:01 +00:00
|
|
|
double base_vsync;
|
2015-11-25 21:11:58 +00:00
|
|
|
int drop_point;
|
2015-11-25 21:07:56 +00:00
|
|
|
double estimated_vsync_interval;
|
|
|
|
double estimated_vsync_jitter;
|
|
|
|
bool expecting_vsync;
|
2016-04-06 20:56:06 +00:00
|
|
|
int64_t num_successive_vsyncs;
|
2015-11-25 21:07:56 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
int64_t flip_queue_offset; // queue flip events at most this much in advance
|
2018-03-13 10:23:23 +00:00
|
|
|
int64_t timing_offset; // same (but from options; not VO configured)
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2015-11-13 21:41:41 +00:00
|
|
|
int64_t delayed_count;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
int64_t drop_count;
|
|
|
|
bool dropped_frame; // the previous frame was dropped
|
2015-01-24 21:56:02 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
struct vo_frame *current_frame; // last frame queued to the VO
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
int64_t wakeup_pts; // time at which to pull frame from decoder
|
|
|
|
|
|
|
|
bool rendering; // true if an image is being rendered
|
2015-07-01 17:24:28 +00:00
|
|
|
struct vo_frame *frame_queued; // should be drawn next
|
|
|
|
int req_frames; // VO's requested value of num_frames
|
2016-09-22 18:16:44 +00:00
|
|
|
uint64_t current_frame_id;
|
2014-08-17 00:50:59 +00:00
|
|
|
|
2015-03-13 12:14:11 +00:00
|
|
|
double display_fps;
|
2018-03-13 11:34:26 +00:00
|
|
|
double reported_display_fps;
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
|
|
|
|
struct stats_ctx *stats;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
};
|
|
|
|
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
extern const struct m_sub_options gl_video_conf;
|
|
|
|
|
2014-04-29 13:19:03 +00:00
|
|
|
static void forget_frames(struct vo *vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
static MP_THREAD_VOID vo_thread(void *ptr);
|
2014-04-29 13:19:03 +00:00
|
|
|
|
2013-07-21 19:17:48 +00:00
|
|
|
static bool get_desc(struct m_obj_desc *dst, int index)
|
|
|
|
{
|
2023-01-14 22:37:10 +00:00
|
|
|
if (index >= MP_ARRAY_SIZE(video_out_drivers))
|
2013-07-21 19:17:48 +00:00
|
|
|
return false;
|
|
|
|
const struct vo_driver *vo = video_out_drivers[index];
|
|
|
|
*dst = (struct m_obj_desc) {
|
2013-10-23 17:06:14 +00:00
|
|
|
.name = vo->name,
|
|
|
|
.description = vo->description,
|
2013-07-21 19:17:48 +00:00
|
|
|
.priv_size = vo->priv_size,
|
|
|
|
.priv_defaults = vo->priv_defaults,
|
|
|
|
.options = vo->options,
|
2016-11-25 20:00:39 +00:00
|
|
|
.options_prefix = vo->options_prefix,
|
2016-09-05 19:04:17 +00:00
|
|
|
.global_opts = vo->global_opts,
|
2018-02-20 12:30:18 +00:00
|
|
|
.hidden = vo->encode,
|
2013-07-21 19:17:48 +00:00
|
|
|
.p = vo,
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For the vo option
|
|
|
|
const struct m_obj_list vo_obj_list = {
|
|
|
|
.get_desc = get_desc,
|
|
|
|
.description = "video outputs",
|
|
|
|
.aliases = {
|
2017-10-09 16:55:44 +00:00
|
|
|
{"gl", "gpu"},
|
2016-09-05 19:05:31 +00:00
|
|
|
{"direct3d_shaders", "direct3d"},
|
2017-10-09 16:55:44 +00:00
|
|
|
{"opengl", "gpu"},
|
2018-02-20 12:30:18 +00:00
|
|
|
{"opengl-cb", "libmpv"},
|
2013-07-21 19:17:48 +00:00
|
|
|
{0}
|
|
|
|
},
|
|
|
|
.allow_trailer = true,
|
2016-09-01 12:21:32 +00:00
|
|
|
.disallow_positional_parameters = true,
|
2016-11-25 20:00:39 +00:00
|
|
|
.use_global_options = true,
|
2013-07-21 19:17:48 +00:00
|
|
|
};
|
2008-04-03 03:25:41 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void dispatch_wakeup_cb(void *ptr)
|
|
|
|
{
|
|
|
|
struct vo *vo = ptr;
|
|
|
|
vo_wakeup(vo);
|
|
|
|
}
|
|
|
|
|
2018-03-13 10:23:23 +00:00
|
|
|
// Initialize or update options from vo->opts
|
|
|
|
static void read_opts(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2023-11-09 04:08:57 +00:00
|
|
|
in->timing_offset = (uint64_t)(MP_TIME_S_TO_NS(vo->opts->timing_offset));
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2018-03-13 10:23:23 +00:00
|
|
|
}
|
|
|
|
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
static void update_opts(void *p)
|
|
|
|
{
|
|
|
|
struct vo *vo = p;
|
|
|
|
|
|
|
|
if (m_config_cache_update(vo->opts_cache)) {
|
2018-03-13 10:23:23 +00:00
|
|
|
read_opts(vo);
|
2019-11-29 11:50:50 +00:00
|
|
|
if (vo->driver->control) {
|
|
|
|
vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL);
|
|
|
|
// "Legacy" update of video position related options.
|
|
|
|
// Unlike VOCTRL_VO_OPTS_CHANGED, often not propagated to backends.
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
|
2019-11-29 11:50:50 +00:00
|
|
|
}
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Does not include thread- and VO uninit.
|
|
|
|
static void dealloc_vo(struct vo *vo)
|
2014-04-22 19:06:23 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
forget_frames(vo); // implicitly synchronized
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
|
|
|
|
// These must be free'd before vo->in->dispatch.
|
|
|
|
talloc_free(vo->opts_cache);
|
|
|
|
talloc_free(vo->gl_opts_cache);
|
video: redo video equalizer option handling
I really wouldn't care much about this, but some parts of the core code
are under HAVE_GPL, so there's some need to get rid of it. Simply turn
the video equalizer from its current fine-grained handling with vf/vo
fallbacks into global options. This makes updating them much simpler.
This removes any possibility of applying video equalizers in filters,
which affects vf_scale, and the previously removed vf_eq. Not a big
loss, since the preferred VOs have this builtin.
Remove video equalizer handling from vo_direct3d, vo_sdl, vo_vaapi, and
vo_xv. I'm not going to waste my time on these legacy VOs.
vo.eq_opts_cache exists _only_ to send a VOCTRL_SET_EQUALIZER, which
exists _only_ to trigger a redraw. This seems silly, but for now I feel
like this is less of a pain. The rest of the equalizer using code is
self-updating.
See commit 96b906a51d5 for how some video equalizer code was GPL only.
Some command line option names and ranges can probably be traced back to
a GPL only committer, but we don't consider these copyrightable.
2017-08-22 15:01:35 +00:00
|
|
|
talloc_free(vo->eq_opts_cache);
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_destroy(&vo->params_mutex);
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_destroy(&vo->in->lock);
|
|
|
|
mp_cond_destroy(&vo->in->wakeup);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
talloc_free(vo);
|
2014-04-22 19:06:23 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 17:37:16 +00:00
|
|
|
static struct vo *vo_create(bool probing, struct mpv_global *global,
|
2016-11-25 20:00:39 +00:00
|
|
|
struct vo_extra *ex, char *name)
|
2008-04-03 03:25:41 +00:00
|
|
|
{
|
2016-09-16 12:23:54 +00:00
|
|
|
assert(ex->wakeup_cb);
|
|
|
|
|
2013-07-31 19:44:21 +00:00
|
|
|
struct mp_log *log = mp_log_new(NULL, global->log, "vo");
|
2013-07-21 19:17:48 +00:00
|
|
|
struct m_obj_desc desc;
|
|
|
|
if (!m_obj_list_find(&desc, &vo_obj_list, bstr0(name))) {
|
2013-12-21 20:49:13 +00:00
|
|
|
mp_msg(log, MSGL_ERR, "Video output %s not found!\n", name);
|
2013-07-31 19:44:21 +00:00
|
|
|
talloc_free(log);
|
2013-07-21 19:17:48 +00:00
|
|
|
return NULL;
|
|
|
|
};
|
|
|
|
struct vo *vo = talloc_ptrtype(NULL, vo);
|
|
|
|
*vo = (struct vo) {
|
2013-07-31 19:44:21 +00:00
|
|
|
.log = mp_log_new(vo, log, name),
|
2013-07-21 19:17:48 +00:00
|
|
|
.driver = desc.p,
|
2013-12-21 16:51:20 +00:00
|
|
|
.global = global,
|
2014-12-31 18:01:28 +00:00
|
|
|
.encode_lavc_ctx = ex->encode_lavc_ctx,
|
|
|
|
.input_ctx = ex->input_ctx,
|
|
|
|
.osd = ex->osd,
|
2014-01-11 17:44:27 +00:00
|
|
|
.monitor_par = 1,
|
2014-12-31 18:01:28 +00:00
|
|
|
.extra = *ex,
|
2014-12-19 17:37:16 +00:00
|
|
|
.probing = probing,
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
.in = talloc(vo, struct vo_internal),
|
2013-07-21 19:17:48 +00:00
|
|
|
};
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_init(&vo->params_mutex);
|
2014-01-21 23:26:01 +00:00
|
|
|
talloc_steal(vo, log);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
*vo->in = (struct vo_internal) {
|
|
|
|
.dispatch = mp_dispatch_create(vo),
|
2015-07-01 17:24:28 +00:00
|
|
|
.req_frames = 1,
|
2016-04-06 20:56:06 +00:00
|
|
|
.estimated_vsync_jitter = -1,
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
.stats = stats_ctx_create(vo, global, "vo"),
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
};
|
|
|
|
mp_dispatch_set_wakeup_fn(vo->in->dispatch, dispatch_wakeup_cb, vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_init(&vo->in->lock);
|
|
|
|
mp_cond_init(&vo->in->wakeup);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
vo->opts_cache = m_config_cache_alloc(NULL, global, &vo_sub_opts);
|
2016-09-02 13:50:54 +00:00
|
|
|
vo->opts = vo->opts_cache->opts;
|
|
|
|
|
options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.
This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).
This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.
As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.
An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.
Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 13:50:33 +00:00
|
|
|
m_config_cache_set_dispatch_change_cb(vo->opts_cache, vo->in->dispatch,
|
|
|
|
update_opts, vo);
|
|
|
|
|
|
|
|
vo->gl_opts_cache = m_config_cache_alloc(NULL, global, &gl_video_conf);
|
video: redo video equalizer option handling
I really wouldn't care much about this, but some parts of the core code
are under HAVE_GPL, so there's some need to get rid of it. Simply turn
the video equalizer from its current fine-grained handling with vf/vo
fallbacks into global options. This makes updating them much simpler.
This removes any possibility of applying video equalizers in filters,
which affects vf_scale, and the previously removed vf_eq. Not a big
loss, since the preferred VOs have this builtin.
Remove video equalizer handling from vo_direct3d, vo_sdl, vo_vaapi, and
vo_xv. I'm not going to waste my time on these legacy VOs.
vo.eq_opts_cache exists _only_ to send a VOCTRL_SET_EQUALIZER, which
exists _only_ to trigger a redraw. This seems silly, but for now I feel
like this is less of a pain. The rest of the equalizer using code is
self-updating.
See commit 96b906a51d5 for how some video equalizer code was GPL only.
Some command line option names and ranges can probably be traced back to
a GPL only committer, but we don't consider these copyrightable.
2017-08-22 15:01:35 +00:00
|
|
|
vo->eq_opts_cache = m_config_cache_alloc(NULL, global, &mp_csp_equalizer_conf);
|
|
|
|
|
2014-07-27 19:33:11 +00:00
|
|
|
mp_input_set_mouse_transform(vo->input_ctx, NULL, NULL);
|
2013-02-06 21:54:03 +00:00
|
|
|
if (vo->driver->encode != !!vo->encode_lavc_ctx)
|
2013-07-21 19:17:48 +00:00
|
|
|
goto error;
|
2016-11-25 20:00:39 +00:00
|
|
|
vo->priv = m_config_group_from_desc(vo, vo->log, global, &desc, name);
|
|
|
|
if (!vo->priv)
|
2013-07-21 19:17:48 +00:00
|
|
|
goto error;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
if (mp_thread_create(&vo->in->thread, vo_thread, vo))
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
goto error;
|
|
|
|
if (mp_rendezvous(vo, 0) < 0) { // init barrier
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_thread_join(vo->in->thread);
|
2013-07-21 19:17:48 +00:00
|
|
|
goto error;
|
2014-04-22 19:06:23 +00:00
|
|
|
}
|
2013-07-21 19:17:48 +00:00
|
|
|
return vo;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2013-07-21 19:17:48 +00:00
|
|
|
error:
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
dealloc_vo(vo);
|
2013-07-21 19:17:48 +00:00
|
|
|
return NULL;
|
2008-04-03 03:25:41 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 18:01:28 +00:00
|
|
|
struct vo *init_best_video_out(struct mpv_global *global, struct vo_extra *ex)
|
2008-04-03 01:58:32 +00:00
|
|
|
{
|
2018-05-21 14:25:52 +00:00
|
|
|
struct mp_vo_opts *opts = mp_get_config_group(NULL, global, &vo_sub_opts);
|
|
|
|
struct m_obj_settings *vo_list = opts->video_driver_list;
|
|
|
|
struct vo *vo = NULL;
|
2002-09-29 21:53:05 +00:00
|
|
|
// first try the preferred drivers, with their optional subdevice param:
|
2013-07-21 19:17:48 +00:00
|
|
|
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;
|
2014-12-19 17:37:16 +00:00
|
|
|
bool p = !!vo_list[n + 1].name;
|
2018-05-21 14:25:52 +00:00
|
|
|
vo = vo_create(p, global, ex, vo_list[n].name);
|
2013-07-21 19:17:48 +00:00
|
|
|
if (vo)
|
2018-05-21 14:25:52 +00:00
|
|
|
goto done;
|
2013-07-21 19:17:48 +00:00
|
|
|
}
|
2018-05-21 14:25:52 +00:00
|
|
|
goto done;
|
2013-07-21 19:17:48 +00:00
|
|
|
}
|
|
|
|
autoprobe:
|
2002-09-29 21:53:05 +00:00
|
|
|
// now try the rest...
|
2023-01-14 22:37:10 +00:00
|
|
|
for (int i = 0; i < MP_ARRAY_SIZE(video_out_drivers); i++) {
|
2015-01-21 18:39:58 +00:00
|
|
|
const struct vo_driver *driver = video_out_drivers[i];
|
|
|
|
if (driver == &video_out_null)
|
|
|
|
break;
|
2018-05-21 14:25:52 +00:00
|
|
|
vo = vo_create(true, global, ex, (char *)driver->name);
|
2013-07-21 19:17:48 +00:00
|
|
|
if (vo)
|
2018-05-21 14:25:52 +00:00
|
|
|
goto done;
|
2002-09-29 21:53:05 +00:00
|
|
|
}
|
2018-05-21 14:25:52 +00:00
|
|
|
done:
|
|
|
|
talloc_free(opts);
|
|
|
|
return vo;
|
2002-09-29 21:53:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-20 12:46:38 +00:00
|
|
|
static void terminate_vo(void *p)
|
|
|
|
{
|
|
|
|
struct vo *vo = p;
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
in->terminate = true;
|
|
|
|
}
|
|
|
|
|
2014-04-22 19:03:52 +00:00
|
|
|
void vo_destroy(struct vo *vo)
|
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
2016-08-20 12:46:38 +00:00
|
|
|
mp_dispatch_run(in->dispatch, terminate_vo, vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_thread_join(vo->in->thread);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
dealloc_vo(vo);
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 12:23:54 +00:00
|
|
|
// Wakeup the playloop to queue new video frames etc.
|
|
|
|
static void wakeup_core(struct vo *vo)
|
|
|
|
{
|
|
|
|
vo->extra.wakeup_cb(vo->extra.wakeup_ctx);
|
|
|
|
}
|
|
|
|
|
2015-11-25 21:07:56 +00:00
|
|
|
// Drop timing information on discontinuities like seeking.
|
|
|
|
// Always called locked.
|
|
|
|
static void reset_vsync_timings(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2015-11-25 21:11:58 +00:00
|
|
|
in->drop_point = 0;
|
2015-11-25 21:11:30 +00:00
|
|
|
in->base_vsync = 0;
|
2015-11-25 21:07:56 +00:00
|
|
|
in->expecting_vsync = false;
|
2016-04-06 20:56:06 +00:00
|
|
|
in->num_successive_vsyncs = 0;
|
2015-11-25 21:07:56 +00:00
|
|
|
}
|
|
|
|
|
2023-09-07 23:15:01 +00:00
|
|
|
static double vsync_stddef(struct vo *vo, double ref_vsync)
|
2015-11-27 13:36:13 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
double jitter = 0;
|
|
|
|
for (int n = 0; n < in->num_vsync_samples; n++) {
|
|
|
|
double diff = in->vsync_samples[n] - ref_vsync;
|
|
|
|
jitter += diff * diff;
|
|
|
|
}
|
|
|
|
return sqrt(jitter / in->num_vsync_samples);
|
|
|
|
}
|
|
|
|
|
2023-10-08 16:31:28 +00:00
|
|
|
#define MAX_VSYNC_SAMPLES 1000
|
2023-11-12 21:31:29 +00:00
|
|
|
#define DELAY_VSYNC_SAMPLES 10
|
2015-11-29 16:49:08 +00:00
|
|
|
|
|
|
|
// Check if we should switch to measured average display FPS if it seems
|
|
|
|
// "better" then the system-reported one. (Note that small differences are
|
|
|
|
// handled as drift instead.)
|
|
|
|
static void check_estimated_display_fps(struct vo *vo)
|
2015-11-25 21:07:56 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2015-11-27 20:56:29 +00:00
|
|
|
bool use_estimated = false;
|
2016-08-20 16:12:12 +00:00
|
|
|
if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES / 2 &&
|
2023-09-16 02:45:24 +00:00
|
|
|
in->estimated_vsync_interval <= 1e9 / 20.0 &&
|
2023-09-16 03:14:20 +00:00
|
|
|
in->estimated_vsync_interval >= 1e9 / 400.0)
|
2015-11-27 13:36:13 +00:00
|
|
|
{
|
2015-11-29 16:41:01 +00:00
|
|
|
for (int n = 0; n < in->num_vsync_samples; n++) {
|
|
|
|
if (fabs(in->vsync_samples[n] - in->estimated_vsync_interval)
|
|
|
|
>= in->estimated_vsync_interval / 4)
|
|
|
|
goto done;
|
|
|
|
}
|
2015-11-27 13:36:13 +00:00
|
|
|
double mjitter = vsync_stddef(vo, in->estimated_vsync_interval);
|
|
|
|
double njitter = vsync_stddef(vo, in->nominal_vsync_interval);
|
2015-11-27 20:56:29 +00:00
|
|
|
if (mjitter * 1.01 < njitter)
|
|
|
|
use_estimated = true;
|
2015-11-29 16:41:01 +00:00
|
|
|
done: ;
|
2015-11-27 20:56:29 +00:00
|
|
|
}
|
2023-09-16 02:45:24 +00:00
|
|
|
if (use_estimated == (fabs(in->vsync_interval - in->nominal_vsync_interval) < 1e9)) {
|
2015-11-28 17:41:11 +00:00
|
|
|
if (use_estimated) {
|
2023-10-08 16:32:21 +00:00
|
|
|
MP_TRACE(vo, "adjusting display FPS to a value closer to %.3f Hz\n",
|
2023-09-16 02:45:24 +00:00
|
|
|
1e9 / in->estimated_vsync_interval);
|
2015-11-28 17:41:11 +00:00
|
|
|
} else {
|
2023-10-08 16:32:21 +00:00
|
|
|
MP_TRACE(vo, "switching back to assuming display fps = %.3f Hz\n",
|
2023-09-16 02:45:24 +00:00
|
|
|
1e9 / in->nominal_vsync_interval);
|
2015-11-28 17:41:11 +00:00
|
|
|
}
|
2015-11-25 21:07:56 +00:00
|
|
|
}
|
2023-09-07 23:15:01 +00:00
|
|
|
in->vsync_interval = use_estimated ? in->estimated_vsync_interval
|
2015-11-27 20:56:29 +00:00
|
|
|
: in->nominal_vsync_interval;
|
2015-11-29 16:49:08 +00:00
|
|
|
}
|
2015-11-25 21:07:56 +00:00
|
|
|
|
2015-11-29 16:49:08 +00:00
|
|
|
// Attempt to detect vsyncs delayed/skipped by the driver. This tries to deal
|
|
|
|
// with strong jitter too, because some drivers have crap vsync timing.
|
|
|
|
static void vsync_skip_detection(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2015-11-25 21:11:30 +00:00
|
|
|
|
2015-11-27 13:35:08 +00:00
|
|
|
int window = 4;
|
2023-09-07 23:15:01 +00:00
|
|
|
double t_r = in->prev_vsync, t_e = in->base_vsync, diff = 0.0, desync_early = 0.0;
|
2015-11-27 13:35:08 +00:00
|
|
|
for (int n = 0; n < in->drop_point; n++) {
|
|
|
|
diff += t_r - t_e;
|
|
|
|
t_r -= in->vsync_samples[n];
|
|
|
|
t_e -= in->vsync_interval;
|
|
|
|
if (n == window + 1)
|
|
|
|
desync_early = diff / window;
|
|
|
|
}
|
2023-09-07 23:15:01 +00:00
|
|
|
double desync = diff / in->num_vsync_samples;
|
2015-11-27 13:35:08 +00:00
|
|
|
if (in->drop_point > window * 2 &&
|
2023-09-07 23:15:01 +00:00
|
|
|
fabs(desync - desync_early) >= in->vsync_interval * 3 / 4)
|
2015-11-27 13:35:08 +00:00
|
|
|
{
|
2015-11-25 21:11:30 +00:00
|
|
|
// Assume a drop. An underflow can technically speaking not be a drop
|
|
|
|
// (it's up to the driver what this is supposed to mean), but no reason
|
|
|
|
// to treat it differently.
|
2015-11-29 16:49:08 +00:00
|
|
|
in->base_vsync = in->prev_vsync;
|
2015-11-25 21:11:30 +00:00
|
|
|
in->delayed_count += 1;
|
2015-11-25 21:11:58 +00:00
|
|
|
in->drop_point = 0;
|
2015-11-25 21:11:30 +00:00
|
|
|
MP_STATS(vo, "vo-delayed");
|
|
|
|
}
|
2015-11-25 21:11:58 +00:00
|
|
|
if (in->drop_point > 10)
|
2015-11-29 16:49:08 +00:00
|
|
|
in->base_vsync += desync / 10; // smooth out drift
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always called locked.
|
2018-08-31 18:08:08 +00:00
|
|
|
static void update_vsync_timing_after_swap(struct vo *vo,
|
|
|
|
struct vo_vsync_info *vsync)
|
2015-11-29 16:49:08 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
vo, vo_gpu, glx: correct GLX_OML_sync_control usage
I misunderstood how this extension works. If I understand it correctly
now, it's worse than I thought. They key thing is that the (ust, msc,
sbc) tripple is not for a single swap event. Instead, (ust, msc) run
independently from sbc. Assuming a CFR display/compositor, this means
you can at best know the vsync phase and frequency, but not the exact
time a sbc changed value.
There is GLX_INTEL_swap_event, which might work as expected, but it has
no EGL equivalent (while GLX_OML_sync_control does, in theory).
Redo the context_glx sync code. Now it's either more correct or less
correct. I wanted to add proper skip detection (if a vsync gets skipped
due to rendering taking too long and other problems), but it turned out
to be too complex, so only some unused fields in vo.h are left of it.
The "generic" skip detection has to do.
The vsync_duration field is also unused by vo.c.
Actually this seems to be an improvement. In cases where the flip call
timing is off, but the real driver-level timing apparently still works,
this will not report vsync skips or higher vsync jitter anymore. I could
observe this with screenshots and fullscreen switching. On the other
hand, maybe it just introduces an A/V offset or so.
Why the fuck can't there be a proper API for retrieving these
statistics? I'm not even asking for much.
2018-09-21 13:42:37 +00:00
|
|
|
int64_t vsync_time = vsync->last_queue_display_time;
|
2016-04-06 20:56:06 +00:00
|
|
|
int64_t prev_vsync = in->prev_vsync;
|
2018-08-31 18:08:08 +00:00
|
|
|
in->prev_vsync = vsync_time;
|
2015-11-29 16:49:08 +00:00
|
|
|
|
|
|
|
if (!in->expecting_vsync) {
|
|
|
|
reset_vsync_timings(vo);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-06 20:56:06 +00:00
|
|
|
in->num_successive_vsyncs++;
|
2023-11-12 21:31:29 +00:00
|
|
|
if (in->num_successive_vsyncs <= DELAY_VSYNC_SAMPLES)
|
2016-04-06 20:56:06 +00:00
|
|
|
return;
|
|
|
|
|
2023-10-08 16:32:42 +00:00
|
|
|
if (vsync_time <= 0 || vsync_time <= prev_vsync) {
|
|
|
|
in->prev_vsync = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prev_vsync <= 0)
|
|
|
|
return;
|
|
|
|
|
2015-11-29 16:49:08 +00:00
|
|
|
if (in->num_vsync_samples >= MAX_VSYNC_SAMPLES)
|
|
|
|
in->num_vsync_samples -= 1;
|
|
|
|
MP_TARRAY_INSERT_AT(in, in->vsync_samples, in->num_vsync_samples, 0,
|
2018-08-31 18:08:08 +00:00
|
|
|
vsync_time - prev_vsync);
|
2015-11-29 16:49:08 +00:00
|
|
|
in->drop_point = MPMIN(in->drop_point + 1, in->num_vsync_samples);
|
|
|
|
in->num_total_vsync_samples += 1;
|
|
|
|
if (in->base_vsync) {
|
|
|
|
in->base_vsync += in->vsync_interval;
|
|
|
|
} else {
|
2018-08-31 18:08:08 +00:00
|
|
|
in->base_vsync = vsync_time;
|
2015-11-29 16:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double avg = 0;
|
2023-10-08 16:32:42 +00:00
|
|
|
for (int n = 0; n < in->num_vsync_samples; n++) {
|
|
|
|
assert(in->vsync_samples[n] > 0);
|
2015-11-29 16:49:08 +00:00
|
|
|
avg += in->vsync_samples[n];
|
2023-10-08 16:32:42 +00:00
|
|
|
}
|
2015-11-29 16:49:08 +00:00
|
|
|
in->estimated_vsync_interval = avg / in->num_vsync_samples;
|
|
|
|
in->estimated_vsync_jitter =
|
|
|
|
vsync_stddef(vo, in->vsync_interval) / in->vsync_interval;
|
|
|
|
|
|
|
|
check_estimated_display_fps(vo);
|
|
|
|
vsync_skip_detection(vo);
|
|
|
|
|
|
|
|
MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter);
|
2023-11-09 04:08:57 +00:00
|
|
|
MP_STATS(vo, "value %f vsync-diff", MP_TIME_NS_TO_S(in->vsync_samples[0]));
|
2015-11-25 21:07:56 +00:00
|
|
|
}
|
|
|
|
|
2014-08-17 00:50:59 +00:00
|
|
|
// to be called from VO thread only
|
|
|
|
static void update_display_fps(struct vo *vo)
|
|
|
|
{
|
2015-03-12 22:35:38 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-03-12 22:35:38 +00:00
|
|
|
if (in->internal_events & VO_EVENT_WIN_STATE) {
|
|
|
|
in->internal_events &= ~(unsigned)VO_EVENT_WIN_STATE;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-03-12 22:35:38 +00:00
|
|
|
|
2018-03-13 11:34:26 +00:00
|
|
|
double fps = 0;
|
|
|
|
vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &fps);
|
2015-03-12 22:35:38 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-03-13 12:14:11 +00:00
|
|
|
|
2018-03-13 11:34:26 +00:00
|
|
|
in->reported_display_fps = fps;
|
|
|
|
}
|
2015-03-13 17:48:14 +00:00
|
|
|
|
2023-10-15 15:18:49 +00:00
|
|
|
double display_fps = vo->opts->display_fps_override;
|
2018-03-13 11:34:26 +00:00
|
|
|
if (display_fps <= 0)
|
|
|
|
display_fps = in->reported_display_fps;
|
2015-11-27 13:36:13 +00:00
|
|
|
|
2018-03-13 11:34:26 +00:00
|
|
|
if (in->display_fps != display_fps) {
|
2023-09-16 02:45:24 +00:00
|
|
|
in->nominal_vsync_interval = display_fps > 0 ? 1e9 / display_fps : 0;
|
2015-11-27 13:36:13 +00:00
|
|
|
in->vsync_interval = MPMAX(in->nominal_vsync_interval, 1);
|
2018-03-13 11:34:26 +00:00
|
|
|
in->display_fps = display_fps;
|
|
|
|
|
|
|
|
MP_VERBOSE(vo, "Assuming %f FPS for display sync.\n", display_fps);
|
|
|
|
|
|
|
|
// make sure to update the player
|
|
|
|
in->queued_events |= VO_EVENT_WIN_STATE;
|
|
|
|
wakeup_core(vo);
|
2014-08-17 00:50:59 +00:00
|
|
|
}
|
2018-03-13 11:34:26 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-08-17 00:50:59 +00:00
|
|
|
}
|
|
|
|
|
2014-04-21 20:53:51 +00:00
|
|
|
static void check_vo_caps(struct vo *vo)
|
|
|
|
{
|
|
|
|
int rot = vo->params->rotate;
|
|
|
|
if (rot) {
|
|
|
|
bool ok = rot % 90 ? false : (vo->driver->caps & VO_CAP_ROTATE90);
|
|
|
|
if (!ok) {
|
|
|
|
MP_WARN(vo, "Video is flagged as rotated by %d degrees, but the "
|
|
|
|
"video output does not support this.\n", rot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void run_reconfig(void *p)
|
2008-04-03 01:58:32 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
void **pp = p;
|
|
|
|
struct vo *vo = pp[0];
|
2018-04-22 17:36:16 +00:00
|
|
|
struct mp_image *img = pp[1];
|
2015-10-03 16:20:16 +00:00
|
|
|
int *ret = pp[2];
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2018-04-22 17:36:16 +00:00
|
|
|
struct mp_image_params *params = &img->params;
|
|
|
|
|
2015-01-24 21:56:02 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2018-01-16 10:47:20 +00:00
|
|
|
MP_VERBOSE(vo, "reconfig to %s\n", mp_image_params_to_str(params));
|
|
|
|
|
vo: fix missed option updates under rare circumstances
Dear diary,
today I fixed a shitty bug that was all my fault because I made a
horrible mess. (Except it was a horrible mess before I even touched
this shit, but let's not blame others.)
Sometimes, updates to VO option that control video sizing (like panscan)
didn't update the screen correctly. They were delayed until the next
option change or so.
It turns out that if the option update happens at the "same" time as a
VOCTRL, update_opts() doesn't actually notify the vo_driver of the
change. This in turn happened because run_control() called
m_config_cache_update(). The latter function returns true if the options
changed since the last call, and update_opts() also calls it (on the
same config cache) for the same purpose. The update_opts() call, which
is triggered by a third mechanism, comes later, but the cache update
call will return false (as it should). Basically, given the config API,
you can't act differently on multiple update calls and expect it to
work. The skipped handling in update_opts() meant that the notification
required to apply the changed option wasn't run.
Fix this by simply calling update_opts() directly instead. Now there's
only 1 m_config_cache_update() call on this specific instance. Fix the
call in run_reconfig() too, so the previous sentence isn't a lie (but it
probably doesn't make a difference in practice due to certain details).
I'm not sure how I even ran into this sort-of race condition. The VOCTRL
that messed up the option update was VOCTRL_UPDATE_PLAYBACK_STATE, which
happens semi-regularly.
Why this config cache shit and all the other shit? Rediscovering this
crap wasn't pleasant. It's a bunch of hacks that became necessary when
the ancient MPlayer architecture made it hard to move the VO to a
separate thread.
All the VO code typically accesses vo->opts (whose fields all used to be
global variables in MPlayer). The frontend changes these on user input.
Putting locking around all the options would be a nightmare, and keeping
a copy of the options in the thread was much simpler. You need a way to
propagate option changes, notify the thread, and update the local copy
too. And the result of these thoughts was the config cache mechanism.
In this specific case, the relevant cache update call in update_opts()
triggers a VOCTRL_SET_PANSCAN to the VO driver, which isn't related to
its former function anymore. Instead, it causes the VO driver to update
the video sizing/placing options, which the generic VO code can't do.
(Mostly because the VO driver includes the windowing stuff and is
responsible for resizing etc. itself.)
VOCTRLs sent by the frontend are even worse. MPlayer had no real runtime
option change mechanism. Some options were vaguely duplicated by
properties, so you could effectively change those options at runtime.
Each of these options had its own VOCTRL, which still exist today, e.g.
VOCTRL_FULLSCREEN, or VOCTRL_ONTOP. I tried to make all options runtime
changeable, and to unify properties with options. But I couldn't be
bothered with updating all VO drivers to listen to option changes
directly, because that would be pretty tedious. So the property code is
still all there and sends the old VOCTRLs. But of course you need to
sync up the options, which is why the run_control() code did that.
(Unrelated: VO_EVENT_FULLSCREEN_STATE is the worst shithack of them all.
Currently, only the frontend can actually write to options (for awful
reasons), so if the fullscreen state changes due to outside interaction,
the VO driver can't update the corresponding option fields. So the VO
notifies the frontend with said VO_EVENT_, and the frontend then sends
VOCTRL_GET_FULLSCREEN, and updates the global copy of the option with
the value returned by that. I still like to think the situation is not
that bad considering the monstrous effort of converting single-threaded
code that had hundreds of options in global variables to multi-threaded
code with no global variables at all.)
2019-07-12 01:30:52 +00:00
|
|
|
update_opts(vo);
|
2016-09-02 13:50:54 +00:00
|
|
|
|
2015-12-19 19:04:31 +00:00
|
|
|
mp_image_params_get_dsize(params, &vo->dwidth, &vo->dheight);
|
2007-02-17 20:58:55 +00:00
|
|
|
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_lock(&vo->params_mutex);
|
2014-01-21 23:26:01 +00:00
|
|
|
talloc_free(vo->params);
|
2018-01-18 09:08:48 +00:00
|
|
|
vo->params = talloc_dup(vo, params);
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_unlock(&vo->params_mutex);
|
2014-01-21 23:26:01 +00:00
|
|
|
|
2018-04-22 17:36:16 +00:00
|
|
|
if (vo->driver->reconfig2) {
|
|
|
|
*ret = vo->driver->reconfig2(vo, img);
|
|
|
|
} else {
|
|
|
|
*ret = vo->driver->reconfig(vo, vo->params);
|
|
|
|
}
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
vo->config_ok = *ret >= 0;
|
2014-04-21 20:53:51 +00:00
|
|
|
if (vo->config_ok) {
|
|
|
|
check_vo_caps(vo);
|
|
|
|
} else {
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_lock(&vo->params_mutex);
|
2014-01-21 23:26:01 +00:00
|
|
|
talloc_free(vo->params);
|
|
|
|
vo->params = NULL;
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_unlock(&vo->params_mutex);
|
2014-01-21 23:26:01 +00:00
|
|
|
}
|
2015-01-24 21:56:02 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-07-01 17:24:28 +00:00
|
|
|
talloc_free(in->current_frame);
|
|
|
|
in->current_frame = NULL;
|
2015-01-24 21:56:02 +00:00
|
|
|
forget_frames(vo);
|
2015-11-25 21:07:56 +00:00
|
|
|
reset_vsync_timings(vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
2014-08-17 00:50:59 +00:00
|
|
|
update_display_fps(vo);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
|
2015-10-03 16:20:16 +00:00
|
|
|
int vo_reconfig(struct vo *vo, struct mp_image_params *params)
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2018-04-22 17:36:16 +00:00
|
|
|
struct mp_image dummy = {0};
|
|
|
|
mp_image_set_params(&dummy, params);
|
|
|
|
void *p[] = {vo, &dummy, &ret};
|
|
|
|
mp_dispatch_run(vo->in->dispatch, run_reconfig, p);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vo_reconfig2(struct vo *vo, struct mp_image *img)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
void *p[] = {vo, img, &ret};
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
mp_dispatch_run(vo->in->dispatch, run_reconfig, p);
|
2008-04-18 03:28:47 +00:00
|
|
|
return ret;
|
2007-02-17 20:58:55 +00:00
|
|
|
}
|
2002-09-29 21:53:05 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void run_control(void *p)
|
|
|
|
{
|
|
|
|
void **pp = p;
|
|
|
|
struct vo *vo = pp[0];
|
2016-08-20 12:46:38 +00:00
|
|
|
int request = (intptr_t)pp[1];
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
void *data = pp[2];
|
vo: fix missed option updates under rare circumstances
Dear diary,
today I fixed a shitty bug that was all my fault because I made a
horrible mess. (Except it was a horrible mess before I even touched
this shit, but let's not blame others.)
Sometimes, updates to VO option that control video sizing (like panscan)
didn't update the screen correctly. They were delayed until the next
option change or so.
It turns out that if the option update happens at the "same" time as a
VOCTRL, update_opts() doesn't actually notify the vo_driver of the
change. This in turn happened because run_control() called
m_config_cache_update(). The latter function returns true if the options
changed since the last call, and update_opts() also calls it (on the
same config cache) for the same purpose. The update_opts() call, which
is triggered by a third mechanism, comes later, but the cache update
call will return false (as it should). Basically, given the config API,
you can't act differently on multiple update calls and expect it to
work. The skipped handling in update_opts() meant that the notification
required to apply the changed option wasn't run.
Fix this by simply calling update_opts() directly instead. Now there's
only 1 m_config_cache_update() call on this specific instance. Fix the
call in run_reconfig() too, so the previous sentence isn't a lie (but it
probably doesn't make a difference in practice due to certain details).
I'm not sure how I even ran into this sort-of race condition. The VOCTRL
that messed up the option update was VOCTRL_UPDATE_PLAYBACK_STATE, which
happens semi-regularly.
Why this config cache shit and all the other shit? Rediscovering this
crap wasn't pleasant. It's a bunch of hacks that became necessary when
the ancient MPlayer architecture made it hard to move the VO to a
separate thread.
All the VO code typically accesses vo->opts (whose fields all used to be
global variables in MPlayer). The frontend changes these on user input.
Putting locking around all the options would be a nightmare, and keeping
a copy of the options in the thread was much simpler. You need a way to
propagate option changes, notify the thread, and update the local copy
too. And the result of these thoughts was the config cache mechanism.
In this specific case, the relevant cache update call in update_opts()
triggers a VOCTRL_SET_PANSCAN to the VO driver, which isn't related to
its former function anymore. Instead, it causes the VO driver to update
the video sizing/placing options, which the generic VO code can't do.
(Mostly because the VO driver includes the windowing stuff and is
responsible for resizing etc. itself.)
VOCTRLs sent by the frontend are even worse. MPlayer had no real runtime
option change mechanism. Some options were vaguely duplicated by
properties, so you could effectively change those options at runtime.
Each of these options had its own VOCTRL, which still exist today, e.g.
VOCTRL_FULLSCREEN, or VOCTRL_ONTOP. I tried to make all options runtime
changeable, and to unify properties with options. But I couldn't be
bothered with updating all VO drivers to listen to option changes
directly, because that would be pretty tedious. So the property code is
still all there and sends the old VOCTRLs. But of course you need to
sync up the options, which is why the run_control() code did that.
(Unrelated: VO_EVENT_FULLSCREEN_STATE is the worst shithack of them all.
Currently, only the frontend can actually write to options (for awful
reasons), so if the fullscreen state changes due to outside interaction,
the VO driver can't update the corresponding option fields. So the VO
notifies the frontend with said VO_EVENT_, and the frontend then sends
VOCTRL_GET_FULLSCREEN, and updates the global copy of the option with
the value returned by that. I still like to think the situation is not
that bad considering the monstrous effort of converting single-threaded
code that had hundreds of options in global variables to multi-threaded
code with no global variables at all.)
2019-07-12 01:30:52 +00:00
|
|
|
update_opts(vo);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
int ret = vo->driver->control(vo, request, data);
|
2016-08-20 12:46:38 +00:00
|
|
|
if (pp[3])
|
|
|
|
*(int *)pp[3] = ret;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
|
2016-08-20 12:11:35 +00:00
|
|
|
int vo_control(struct vo *vo, int request, void *data)
|
2014-04-22 19:03:52 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
int ret;
|
2016-08-20 12:46:38 +00:00
|
|
|
void *p[] = {vo, (void *)(intptr_t)request, data, &ret};
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
mp_dispatch_run(vo->in->dispatch, run_control, p);
|
|
|
|
return ret;
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
2016-08-20 12:46:38 +00:00
|
|
|
// Run vo_control() without waiting for a reply.
|
|
|
|
// (Only works for some VOCTRLs.)
|
|
|
|
void vo_control_async(struct vo *vo, int request, void *data)
|
|
|
|
{
|
|
|
|
void *p[4] = {vo, (void *)(intptr_t)request, NULL, NULL};
|
|
|
|
void **d = talloc_memdup(NULL, p, sizeof(p));
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_UPDATE_PLAYBACK_STATE:
|
2018-01-18 09:08:48 +00:00
|
|
|
d[2] = talloc_dup(d, (struct voctrl_playback_state *)data);
|
2016-08-20 12:46:38 +00:00
|
|
|
break;
|
2017-02-21 13:18:30 +00:00
|
|
|
case VOCTRL_KILL_SCREENSAVER:
|
|
|
|
case VOCTRL_RESTORE_SCREENSAVER:
|
|
|
|
break;
|
2016-08-20 12:46:38 +00:00
|
|
|
default:
|
|
|
|
abort(); // requires explicit support
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_dispatch_enqueue_autofree(vo->in->dispatch, run_control, d);
|
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// must be called locked
|
2014-04-29 13:19:03 +00:00
|
|
|
static void forget_frames(struct vo *vo)
|
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
video: fix and simplify video format changes and last frame display
The previous commit broke these things, and fixing them is separate in
this commit in order to reduce the volume of changes.
Move the image queue from the VO to the playback core. The image queue
is a remnant of the old way how vdpau was implemented, and increasingly
became more and more an artifact. In the end, it did only one thing:
computing the duration of the current frame. This was done by taking the
PTS difference between the current and the future frame. We keep this,
but by moving it out of the VO, we don't have to special-case format
changes anymore. This simplifies the code a lot.
Since we need the queue to compute the duration only, a queue size
larger than 2 makes no sense, and we can hardcode that.
Also change how the last frame is handled. The last frame is a bit of a
problem, because video timing works by showing one frame after another,
which makes it a special case. Make the VO provide a function to notify
us when the frame is done, instead. The frame duration is used for that.
This is not perfect. For example, changing playback speed during the
last frame doesn't update the end time. Pausing will not stop the clock
that times the last frame. But I don't think this matters for such a
corner case.
2014-08-12 21:17:35 +00:00
|
|
|
in->hasframe = false;
|
2014-08-23 10:43:09 +00:00
|
|
|
in->hasframe_rendered = false;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
in->drop_count = 0;
|
2015-11-13 21:41:41 +00:00
|
|
|
in->delayed_count = 0;
|
2015-07-01 17:24:28 +00:00
|
|
|
talloc_free(in->frame_queued);
|
|
|
|
in->frame_queued = NULL;
|
2016-09-22 18:16:44 +00:00
|
|
|
in->current_frame_id += VO_MAX_REQ_FRAMES + 1;
|
2015-01-24 21:56:02 +00:00
|
|
|
// don't unref current_frame; we always want to be able to redraw it
|
2015-08-10 16:43:25 +00:00
|
|
|
if (in->current_frame) {
|
|
|
|
in->current_frame->num_vsyncs = 0; // but reset future repeats
|
|
|
|
in->current_frame->display_synced = false; // mark discontinuity
|
|
|
|
}
|
2014-04-29 13:19:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 18:42:30 +00:00
|
|
|
// VOs which have no special requirements on UI event loops etc. can set the
|
|
|
|
// vo_driver.wait_events callback to this (and leave vo_driver.wakeup unset).
|
|
|
|
// This function must not be used or called for other purposes.
|
|
|
|
void vo_wait_default(struct vo *vo, int64_t until_time)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
|
|
|
if (!in->need_wakeup)
|
|
|
|
mp_cond_timedwait_until(&in->wakeup, &in->lock, until_time);
|
|
|
|
mp_mutex_unlock(&in->lock);
|
2016-07-20 18:42:30 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Called unlocked.
|
|
|
|
static void wait_vo(struct vo *vo, int64_t until_time)
|
2014-04-22 19:03:52 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2016-07-21 12:48:30 +00:00
|
|
|
if (vo->driver->wait_events) {
|
|
|
|
vo->driver->wait_events(vo, until_time);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
} else {
|
2016-07-21 12:48:30 +00:00
|
|
|
vo_wait_default(vo, until_time);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2016-07-21 12:48:30 +00:00
|
|
|
in->need_wakeup = false;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void wakeup_locked(struct vo *vo)
|
2014-04-29 13:19:03 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_cond_broadcast(&in->wakeup);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
if (vo->driver->wakeup)
|
|
|
|
vo->driver->wakeup(vo);
|
|
|
|
in->need_wakeup = true;
|
2014-04-29 13:19:03 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Wakeup VO thread, and make it check for new events with VOCTRL_CHECK_EVENTS.
|
|
|
|
// To be used by threaded VO backends.
|
|
|
|
void vo_wakeup(struct vo *vo)
|
2014-04-22 19:03:52 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
wakeup_locked(vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
|
2024-02-04 17:31:18 +00:00
|
|
|
static int64_t get_current_frame_end(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
if (!in->current_frame)
|
|
|
|
return -1;
|
|
|
|
return in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
|
|
|
|
}
|
|
|
|
|
2024-02-04 15:35:13 +00:00
|
|
|
static bool still_displaying(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2024-02-04 17:31:18 +00:00
|
|
|
bool working = in->rendering || in->frame_queued;
|
|
|
|
if (working)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
int64_t frame_end = get_current_frame_end(vo);
|
|
|
|
if (frame_end < 0)
|
|
|
|
goto done;
|
|
|
|
working = mp_time_ns() < frame_end;
|
|
|
|
|
|
|
|
done:
|
|
|
|
return working && in->hasframe;
|
2024-02-04 15:35:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if there is still a frame being displayed (or queued).
|
|
|
|
bool vo_still_displaying(struct vo *vo)
|
|
|
|
{
|
|
|
|
mp_mutex_lock(&vo->in->lock);
|
|
|
|
bool res = still_displaying(vo);
|
|
|
|
mp_mutex_unlock(&vo->in->lock);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-02-04 17:31:18 +00:00
|
|
|
// Make vo issue a wakeup once vo_still_displaying() becomes false.
|
2024-02-04 15:35:13 +00:00
|
|
|
void vo_request_wakeup_on_done(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
mp_mutex_lock(&vo->in->lock);
|
|
|
|
if (still_displaying(vo)) {
|
|
|
|
in->wakeup_on_done = true;
|
|
|
|
} else {
|
|
|
|
wakeup_core(vo);
|
|
|
|
}
|
|
|
|
mp_mutex_unlock(&vo->in->lock);
|
|
|
|
}
|
|
|
|
|
video: fix and simplify video format changes and last frame display
The previous commit broke these things, and fixing them is separate in
this commit in order to reduce the volume of changes.
Move the image queue from the VO to the playback core. The image queue
is a remnant of the old way how vdpau was implemented, and increasingly
became more and more an artifact. In the end, it did only one thing:
computing the duration of the current frame. This was done by taking the
PTS difference between the current and the future frame. We keep this,
but by moving it out of the VO, we don't have to special-case format
changes anymore. This simplifies the code a lot.
Since we need the queue to compute the duration only, a queue size
larger than 2 makes no sense, and we can hardcode that.
Also change how the last frame is handled. The last frame is a bit of a
problem, because video timing works by showing one frame after another,
which makes it a special case. Make the VO provide a function to notify
us when the frame is done, instead. The frame duration is used for that.
This is not perfect. For example, changing playback speed during the
last frame doesn't update the end time. Pausing will not stop the clock
that times the last frame. But I don't think this matters for such a
corner case.
2014-08-12 21:17:35 +00:00
|
|
|
// Whether vo_queue_frame() can be called. If the VO is not ready yet, the
|
|
|
|
// function will return false, and the VO will call the wakeup callback once
|
|
|
|
// it's ready.
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// next_pts is the exact time when the next frame should be displayed. If the
|
|
|
|
// VO is ready, but the time is too "early", return false, and call the wakeup
|
|
|
|
// callback once the time is right.
|
2015-08-10 16:43:25 +00:00
|
|
|
// If next_pts is negative, disable any timing and draw the frame as fast as
|
|
|
|
// possible.
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2018-04-29 17:42:18 +00:00
|
|
|
bool blocked = vo->driver->initially_blocked &&
|
|
|
|
!(in->internal_events & VO_EVENT_INITIAL_UNBLOCK);
|
|
|
|
bool r = vo->config_ok && !in->frame_queued && !blocked &&
|
2015-08-10 16:43:25 +00:00
|
|
|
(!in->current_frame || in->current_frame->num_vsyncs < 1);
|
|
|
|
if (r && next_pts >= 0) {
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Don't show the frame too early - it would basically freeze the
|
|
|
|
// display by disallowing OSD redrawing or VO interaction.
|
2018-03-13 10:23:23 +00:00
|
|
|
// Actually render the frame at earliest the given offset before target
|
|
|
|
// time.
|
|
|
|
next_pts -= in->timing_offset;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
next_pts -= in->flip_queue_offset;
|
2023-09-18 01:44:05 +00:00
|
|
|
int64_t now = mp_time_ns();
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
if (next_pts > now)
|
|
|
|
r = false;
|
|
|
|
if (!in->wakeup_pts || next_pts < in->wakeup_pts) {
|
2015-11-25 21:10:55 +00:00
|
|
|
in->wakeup_pts = next_pts;
|
2016-08-26 18:19:39 +00:00
|
|
|
// If we have to wait, update the vo thread's timer.
|
|
|
|
if (!r)
|
|
|
|
wakeup_locked(vo);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Direct the VO thread to put the currently queued image on the screen.
|
|
|
|
// vo_is_ready_for_frame() must have returned true before this call.
|
2015-07-01 17:24:28 +00:00
|
|
|
// Ownership of frame is handed to the vo.
|
|
|
|
void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-08-10 16:43:25 +00:00
|
|
|
assert(vo->config_ok && !in->frame_queued &&
|
|
|
|
(!in->current_frame || in->current_frame->num_vsyncs < 1));
|
video: fix and simplify video format changes and last frame display
The previous commit broke these things, and fixing them is separate in
this commit in order to reduce the volume of changes.
Move the image queue from the VO to the playback core. The image queue
is a remnant of the old way how vdpau was implemented, and increasingly
became more and more an artifact. In the end, it did only one thing:
computing the duration of the current frame. This was done by taking the
PTS difference between the current and the future frame. We keep this,
but by moving it out of the VO, we don't have to special-case format
changes anymore. This simplifies the code a lot.
Since we need the queue to compute the duration only, a queue size
larger than 2 makes no sense, and we can hardcode that.
Also change how the last frame is handled. The last frame is a bit of a
problem, because video timing works by showing one frame after another,
which makes it a special case. Make the VO provide a function to notify
us when the frame is done, instead. The frame duration is used for that.
This is not perfect. For example, changing playback speed during the
last frame doesn't update the end time. Pausing will not stop the clock
that times the last frame. But I don't think this matters for such a
corner case.
2014-08-12 21:17:35 +00:00
|
|
|
in->hasframe = true;
|
2016-09-22 18:16:44 +00:00
|
|
|
frame->frame_id = ++(in->current_frame_id);
|
2015-07-01 17:24:28 +00:00
|
|
|
in->frame_queued = frame;
|
2015-11-25 21:10:55 +00:00
|
|
|
in->wakeup_pts = frame->display_synced
|
2015-08-10 16:43:25 +00:00
|
|
|
? 0 : frame->pts + MPMAX(frame->duration, 0);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
wakeup_locked(vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// If a frame is currently being rendered (or queued), wait until it's done.
|
|
|
|
// Otherwise, return immediately.
|
|
|
|
void vo_wait_frame(struct vo *vo)
|
2014-04-22 19:03:52 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
while (in->frame_queued || in->rendering)
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_cond_wait(&in->wakeup, &in->lock);
|
|
|
|
mp_mutex_unlock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
|
2015-05-12 20:30:45 +00:00
|
|
|
// Wait until realtime is >= ts
|
|
|
|
// called without lock
|
|
|
|
static void wait_until(struct vo *vo, int64_t target)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2023-09-18 01:44:05 +00:00
|
|
|
while (target > mp_time_ns()) {
|
2015-05-12 20:31:22 +00:00
|
|
|
if (in->queued_events & VO_EVENT_LIVE_RESIZING)
|
|
|
|
break;
|
2023-10-21 02:55:41 +00:00
|
|
|
if (mp_cond_timedwait_until(&in->wakeup, &in->lock, target))
|
2015-05-12 20:30:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-05-12 20:30:45 +00:00
|
|
|
}
|
|
|
|
|
2019-10-07 12:48:34 +00:00
|
|
|
static bool render_frame(struct vo *vo)
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2015-07-01 17:24:28 +00:00
|
|
|
struct vo_frame *frame = NULL;
|
2020-05-09 20:32:28 +00:00
|
|
|
bool more_frames = false;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2014-08-17 00:50:59 +00:00
|
|
|
update_display_fps(vo);
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
if (in->frame_queued) {
|
|
|
|
talloc_free(in->current_frame);
|
|
|
|
in->current_frame = in->frame_queued;
|
|
|
|
in->frame_queued = NULL;
|
|
|
|
} else if (in->paused || !in->current_frame || !in->hasframe ||
|
2015-11-03 12:33:38 +00:00
|
|
|
(in->current_frame->display_synced && in->current_frame->num_vsyncs < 1) ||
|
2015-11-25 21:10:55 +00:00
|
|
|
!in->current_frame->display_synced)
|
2015-07-01 17:24:28 +00:00
|
|
|
{
|
|
|
|
goto done;
|
|
|
|
}
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
frame = vo_frame_ref(in->current_frame);
|
|
|
|
assert(frame);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
2015-08-10 16:43:25 +00:00
|
|
|
if (frame->display_synced) {
|
|
|
|
frame->pts = 0;
|
|
|
|
frame->duration = -1;
|
|
|
|
}
|
|
|
|
|
2023-09-18 01:44:05 +00:00
|
|
|
int64_t now = mp_time_ns();
|
2015-07-01 17:24:28 +00:00
|
|
|
int64_t pts = frame->pts;
|
|
|
|
int64_t duration = frame->duration;
|
|
|
|
int64_t end_time = pts + duration;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2015-01-23 10:30:43 +00:00
|
|
|
// Time at which we should flip_page on the VO.
|
2015-08-10 16:43:25 +00:00
|
|
|
int64_t target = frame->display_synced ? 0 : pts - in->flip_queue_offset;
|
2015-01-23 10:30:43 +00:00
|
|
|
|
2015-06-07 18:57:44 +00:00
|
|
|
// "normal" strict drop threshold.
|
2015-11-27 20:52:06 +00:00
|
|
|
in->dropped_frame = duration >= 0 && end_time < now;
|
2015-06-07 18:57:44 +00:00
|
|
|
|
2015-08-10 16:43:25 +00:00
|
|
|
in->dropped_frame &= !frame->display_synced;
|
2014-09-20 13:14:43 +00:00
|
|
|
in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP);
|
2018-03-13 11:54:48 +00:00
|
|
|
in->dropped_frame &= frame->can_drop;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
// Even if we're hopelessly behind, rather degrade to 10 FPS playback,
|
|
|
|
// instead of just freezing the display forever.
|
2023-11-09 04:08:57 +00:00
|
|
|
in->dropped_frame &= now - in->prev_vsync < MP_TIME_MS_TO_NS(100);
|
2015-07-01 17:24:28 +00:00
|
|
|
in->dropped_frame &= in->hasframe_rendered;
|
2015-01-23 10:30:43 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
// Setup parameters for the next time this frame is drawn. ("frame" is the
|
|
|
|
// frame currently drawn, while in->current_frame is the potentially next.)
|
|
|
|
in->current_frame->repeat = true;
|
2015-10-29 21:17:43 +00:00
|
|
|
if (frame->display_synced) {
|
2023-11-14 14:40:02 +00:00
|
|
|
// Increment the offset only if it's not the last vsync. The current_frame
|
|
|
|
// can still be reused. This is mostly important for redraws that might
|
|
|
|
// overshoot the target vsync point.
|
|
|
|
if (in->current_frame->num_vsyncs > 1) {
|
|
|
|
in->current_frame->vsync_offset += in->current_frame->vsync_interval;
|
|
|
|
in->current_frame->ideal_frame_vsync += in->current_frame->ideal_frame_vsync_duration;
|
|
|
|
}
|
2015-10-29 21:17:43 +00:00
|
|
|
in->dropped_frame |= in->current_frame->num_vsyncs < 1;
|
|
|
|
}
|
2015-08-10 16:43:25 +00:00
|
|
|
if (in->current_frame->num_vsyncs > 0)
|
|
|
|
in->current_frame->num_vsyncs -= 1;
|
2015-07-01 17:24:28 +00:00
|
|
|
|
vo: redraw dropped frame if paused between queuing and drawing frame
When frame-stepping with display-sync mode enabled in high framerate
video, the frame was sometimes not redrawn correctly. Only the first OSD
interaction (or something similar) made it visible.
In this case, the core schedules many frames as dropped (because it's
ignorant of pausing/frame-stepping, as in theory the player is _not_
paused during frame-stepping, only at the end of it). There's a race
between the VO rendering the queued frame, and the core calling
vo_set_paused() after it has queued the frame. If the latter happens
first, the existing logic to redraw the previous dropped frame does
things correctly. If the former happens, the frame is not redrawn
automatically, but will be redrawn on the next user input (or if OSD is
enabled, and the pause state change updates it, which leads to an
immediate redraw).
Fix this by never actually dropping a frame in paused mode. The request
by the core to drop it is simply ignored.
Maybe this could be done slightly nicer by updating the pause state with
the VO atomically. Then we wouldn't have the frame drop counter going up
either (it's actually dropped, but then redrawn; but I doubt any user,
or me in a few weeks, would understand this). But I'm not really
interested in polishing this by increasing the complexity of the
frame-step code.
2019-12-04 22:53:38 +00:00
|
|
|
// Always render when paused (it's typically the last frame for a while).
|
|
|
|
in->dropped_frame &= !in->paused;
|
|
|
|
|
2016-04-06 20:56:06 +00:00
|
|
|
bool use_vsync = in->current_frame->display_synced && !in->paused;
|
|
|
|
if (use_vsync && !in->expecting_vsync) // first DS frame in a row
|
2023-09-18 01:44:05 +00:00
|
|
|
in->prev_vsync = now;
|
2016-04-06 20:56:06 +00:00
|
|
|
in->expecting_vsync = use_vsync;
|
2015-11-25 21:07:56 +00:00
|
|
|
|
vo: fix race condition with redraw requests
There is a very subtle race in vo that can manifest itself on pause
events. In the renderloop, render_frame, unsurprisingly, does the heavy
lifting of actually queuing up and flipping the frames. This is called
during normal playback. Sometimes various parts of the player can make a
redraw request which will latter trigger another render of the frame
later down in the loop (do_redraw). Because these requests can happen at
essentially anytime, sometimes the redraw request will happen *before*
do_redraw and it'll be caught in render_frame. When this happens,
the existing render_frame run works perfectly fine as a redraw so it
clears out the request which is sensible. Normally this is all locked of
course, but there's one catch. render_frame has to unlock itself when
propagating down into specific VOs/backends. That's what causes this
bug.
While render_frame is unlocked, other parts of the player can send
redraw requests which will cause in->request_redraw to become true. The
logic in the code always clears out this parameter after a successful
render, but this isn't correct. When in->request_become becomes true in
the middle of render_frame, there needs to be one more draw afterwards
to reflect whatever actually changed (usually the OSD). Instead, this
gets simply discarded. If you rapidly spam pause while rendering things
to the OSD at the same time, it's possible to for the last render to
be behind a frame and appear as if your osd event was ignored.
Once you realize what is happening, the fix is quite simple. Just store
the initial value of in->request_redraw before the unlock step. After we
do the render step and unlock again, only set in->request_redraw to
false if there was an initial redraw request. We just finished doing a
redraw, so it is safe to clear. Otherwise, leave in->request_redraw
alone. If it is initially false, then it will still be false and nothing
changes. However if it updated to true in the middle of the rendering,
this value is now preserved so we can go and call do_redraw later and
show what that last frame was meant to be when you pause. One
unfortunate thing about this design is that it is technically possible
for other internal things in vo to update during that unlocked period.
Hopefully, that doesn't actually happen and only redraw requests work
like this.
Fixes #8172 and #8350.
2023-03-02 00:46:59 +00:00
|
|
|
// Store the initial value before we unlock.
|
|
|
|
bool request_redraw = in->request_redraw;
|
|
|
|
|
2015-08-01 20:16:52 +00:00
|
|
|
if (in->dropped_frame) {
|
|
|
|
in->drop_count += 1;
|
2024-02-04 15:35:13 +00:00
|
|
|
wakeup_core(vo);
|
2015-08-01 20:16:52 +00:00
|
|
|
} else {
|
2015-02-07 20:14:43 +00:00
|
|
|
in->rendering = true;
|
2014-08-23 10:43:09 +00:00
|
|
|
in->hasframe_rendered = true;
|
2015-07-01 17:24:28 +00:00
|
|
|
int64_t prev_drop_count = vo->in->drop_count;
|
2020-04-09 23:45:45 +00:00
|
|
|
// Can the core queue new video now? Non-display-sync uses a separate
|
2020-06-01 13:08:24 +00:00
|
|
|
// timer instead, but possibly benefits from preparing a frame early.
|
|
|
|
bool can_queue = !in->frame_queued &&
|
|
|
|
(in->current_frame->num_vsyncs < 1 || !use_vsync);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2020-04-09 23:45:45 +00:00
|
|
|
|
|
|
|
if (can_queue)
|
|
|
|
wakeup_core(vo);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
stats_time_start(in->stats, "video-draw");
|
2014-08-22 14:21:58 +00:00
|
|
|
|
2023-09-26 19:26:23 +00:00
|
|
|
vo->driver->draw_frame(vo, frame);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
stats_time_end(in->stats, "video-draw");
|
2017-01-18 11:04:17 +00:00
|
|
|
|
2015-05-12 20:30:45 +00:00
|
|
|
wait_until(vo, target);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
stats_time_start(in->stats, "video-flip");
|
2017-01-18 11:04:17 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
vo->driver->flip_page(vo);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
2018-08-31 18:08:08 +00:00
|
|
|
struct vo_vsync_info vsync = {
|
vo, vo_gpu, glx: correct GLX_OML_sync_control usage
I misunderstood how this extension works. If I understand it correctly
now, it's worse than I thought. They key thing is that the (ust, msc,
sbc) tripple is not for a single swap event. Instead, (ust, msc) run
independently from sbc. Assuming a CFR display/compositor, this means
you can at best know the vsync phase and frequency, but not the exact
time a sbc changed value.
There is GLX_INTEL_swap_event, which might work as expected, but it has
no EGL equivalent (while GLX_OML_sync_control does, in theory).
Redo the context_glx sync code. Now it's either more correct or less
correct. I wanted to add proper skip detection (if a vsync gets skipped
due to rendering taking too long and other problems), but it turned out
to be too complex, so only some unused fields in vo.h are left of it.
The "generic" skip detection has to do.
The vsync_duration field is also unused by vo.c.
Actually this seems to be an improvement. In cases where the flip call
timing is off, but the real driver-level timing apparently still works,
this will not report vsync skips or higher vsync jitter anymore. I could
observe this with screenshots and fullscreen switching. On the other
hand, maybe it just introduces an A/V offset or so.
Why the fuck can't there be a proper API for retrieving these
statistics? I'm not even asking for much.
2018-09-21 13:42:37 +00:00
|
|
|
.last_queue_display_time = -1,
|
|
|
|
.skipped_vsyncs = -1,
|
2018-08-31 18:08:08 +00:00
|
|
|
};
|
|
|
|
if (vo->driver->get_vsync)
|
|
|
|
vo->driver->get_vsync(vo, &vsync);
|
|
|
|
|
vo, vo_gpu, glx: correct GLX_OML_sync_control usage
I misunderstood how this extension works. If I understand it correctly
now, it's worse than I thought. They key thing is that the (ust, msc,
sbc) tripple is not for a single swap event. Instead, (ust, msc) run
independently from sbc. Assuming a CFR display/compositor, this means
you can at best know the vsync phase and frequency, but not the exact
time a sbc changed value.
There is GLX_INTEL_swap_event, which might work as expected, but it has
no EGL equivalent (while GLX_OML_sync_control does, in theory).
Redo the context_glx sync code. Now it's either more correct or less
correct. I wanted to add proper skip detection (if a vsync gets skipped
due to rendering taking too long and other problems), but it turned out
to be too complex, so only some unused fields in vo.h are left of it.
The "generic" skip detection has to do.
The vsync_duration field is also unused by vo.c.
Actually this seems to be an improvement. In cases where the flip call
timing is off, but the real driver-level timing apparently still works,
this will not report vsync skips or higher vsync jitter anymore. I could
observe this with screenshots and fullscreen switching. On the other
hand, maybe it just introduces an A/V offset or so.
Why the fuck can't there be a proper API for retrieving these
statistics? I'm not even asking for much.
2018-09-21 13:42:37 +00:00
|
|
|
// Make up some crap if presentation feedback is missing.
|
2023-10-08 16:32:42 +00:00
|
|
|
if (vsync.last_queue_display_time <= 0)
|
2023-09-16 02:45:24 +00:00
|
|
|
vsync.last_queue_display_time = mp_time_ns();
|
2018-08-31 14:33:15 +00:00
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
stats_time_end(in->stats, "video-flip");
|
2014-08-22 14:21:58 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-07-01 17:24:28 +00:00
|
|
|
in->dropped_frame = prev_drop_count < vo->in->drop_count;
|
2015-02-07 20:14:43 +00:00
|
|
|
in->rendering = false;
|
2015-08-10 16:43:25 +00:00
|
|
|
|
2018-08-31 18:08:08 +00:00
|
|
|
update_vsync_timing_after_swap(vo, &vsync);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
}
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2018-02-16 03:56:52 +00:00
|
|
|
if (vo->driver->caps & VO_CAP_NORETAIN) {
|
2017-07-06 17:49:54 +00:00
|
|
|
talloc_free(in->current_frame);
|
|
|
|
in->current_frame = NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-23 17:44:46 +00:00
|
|
|
if (in->dropped_frame) {
|
|
|
|
MP_STATS(vo, "drop-vo");
|
|
|
|
} else {
|
2023-09-19 18:11:30 +00:00
|
|
|
// If the initial redraw request was true or mpv is still playing,
|
|
|
|
// then we can clear it here since we just performed a redraw, or the
|
|
|
|
// next loop will draw what we need. However if there initially is
|
vo: fix race condition with redraw requests
There is a very subtle race in vo that can manifest itself on pause
events. In the renderloop, render_frame, unsurprisingly, does the heavy
lifting of actually queuing up and flipping the frames. This is called
during normal playback. Sometimes various parts of the player can make a
redraw request which will latter trigger another render of the frame
later down in the loop (do_redraw). Because these requests can happen at
essentially anytime, sometimes the redraw request will happen *before*
do_redraw and it'll be caught in render_frame. When this happens,
the existing render_frame run works perfectly fine as a redraw so it
clears out the request which is sensible. Normally this is all locked of
course, but there's one catch. render_frame has to unlock itself when
propagating down into specific VOs/backends. That's what causes this
bug.
While render_frame is unlocked, other parts of the player can send
redraw requests which will cause in->request_redraw to become true. The
logic in the code always clears out this parameter after a successful
render, but this isn't correct. When in->request_become becomes true in
the middle of render_frame, there needs to be one more draw afterwards
to reflect whatever actually changed (usually the OSD). Instead, this
gets simply discarded. If you rapidly spam pause while rendering things
to the OSD at the same time, it's possible to for the last render to
be behind a frame and appear as if your osd event was ignored.
Once you realize what is happening, the fix is quite simple. Just store
the initial value of in->request_redraw before the unlock step. After we
do the render step and unlock again, only set in->request_redraw to
false if there was an initial redraw request. We just finished doing a
redraw, so it is safe to clear. Otherwise, leave in->request_redraw
alone. If it is initially false, then it will still be false and nothing
changes. However if it updated to true in the middle of the rendering,
this value is now preserved so we can go and call do_redraw later and
show what that last frame was meant to be when you pause. One
unfortunate thing about this design is that it is technically possible
for other internal things in vo to update during that unlocked period.
Hopefully, that doesn't actually happen and only redraw requests work
like this.
Fixes #8172 and #8350.
2023-03-02 00:46:59 +00:00
|
|
|
// no redraw request, then something can change this (i.e. the OSD)
|
2023-09-19 18:11:30 +00:00
|
|
|
// while the vo was unlocked. If we are paused, don't touch
|
|
|
|
// in->request_redraw in that case.
|
|
|
|
if (request_redraw || !in->paused)
|
vo: fix race condition with redraw requests
There is a very subtle race in vo that can manifest itself on pause
events. In the renderloop, render_frame, unsurprisingly, does the heavy
lifting of actually queuing up and flipping the frames. This is called
during normal playback. Sometimes various parts of the player can make a
redraw request which will latter trigger another render of the frame
later down in the loop (do_redraw). Because these requests can happen at
essentially anytime, sometimes the redraw request will happen *before*
do_redraw and it'll be caught in render_frame. When this happens,
the existing render_frame run works perfectly fine as a redraw so it
clears out the request which is sensible. Normally this is all locked of
course, but there's one catch. render_frame has to unlock itself when
propagating down into specific VOs/backends. That's what causes this
bug.
While render_frame is unlocked, other parts of the player can send
redraw requests which will cause in->request_redraw to become true. The
logic in the code always clears out this parameter after a successful
render, but this isn't correct. When in->request_become becomes true in
the middle of render_frame, there needs to be one more draw afterwards
to reflect whatever actually changed (usually the OSD). Instead, this
gets simply discarded. If you rapidly spam pause while rendering things
to the OSD at the same time, it's possible to for the last render to
be behind a frame and appear as if your osd event was ignored.
Once you realize what is happening, the fix is quite simple. Just store
the initial value of in->request_redraw before the unlock step. After we
do the render step and unlock again, only set in->request_redraw to
false if there was an initial redraw request. We just finished doing a
redraw, so it is safe to clear. Otherwise, leave in->request_redraw
alone. If it is initially false, then it will still be false and nothing
changes. However if it updated to true in the middle of the rendering,
this value is now preserved so we can go and call do_redraw later and
show what that last frame was meant to be when you pause. One
unfortunate thing about this design is that it is technically possible
for other internal things in vo to update during that unlocked period.
Hopefully, that doesn't actually happen and only redraw requests work
like this.
Fixes #8172 and #8350.
2023-03-02 00:46:59 +00:00
|
|
|
in->request_redraw = false;
|
2015-02-07 20:14:43 +00:00
|
|
|
}
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
2020-05-09 20:32:28 +00:00
|
|
|
if (in->current_frame && in->current_frame->num_vsyncs &&
|
|
|
|
in->current_frame->display_synced)
|
|
|
|
more_frames = true;
|
|
|
|
|
|
|
|
if (in->frame_queued && in->frame_queued->display_synced)
|
|
|
|
more_frames = true;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_cond_broadcast(&in->wakeup); // for vo_wait_frame()
|
2015-01-23 10:30:43 +00:00
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
done:
|
2024-01-27 04:56:50 +00:00
|
|
|
if (!vo->driver->frame_owner || in->dropped_frame)
|
2023-10-01 06:19:40 +00:00
|
|
|
talloc_free(frame);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2020-04-09 23:33:38 +00:00
|
|
|
|
2020-05-09 20:32:28 +00:00
|
|
|
return more_frames;
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
static void do_redraw(struct vo *vo)
|
2014-06-15 18:46:57 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
|
|
|
|
2018-02-16 03:56:52 +00:00
|
|
|
if (!vo->config_ok || (vo->driver->caps & VO_CAP_NORETAIN))
|
2015-07-01 17:24:28 +00:00
|
|
|
return;
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
in->request_redraw = false;
|
vo: generic redraw support
Usually, a VO must react to VOCTRL_REDRAW_FRAME in order to redraw the
current screen correctly if video is paused (this is done to update
OSD). But if it's not supported, we can just draw the current image
again in the generic vo.c code.
Unfortunately, this turned out pretty useless, because the VOs which
would benefit from this need to redraw even if there is no image, in
order to draw a black screen in --idle --force-window mode. The way
redrawing is handled in the X11 common code and in vo_x11 and vo_xv is
in the way, and I'm not sure what exactly vo_wayland requires. Other VOs
have a non-trivial implementation of VOCTRL_REDRAW_FRAME, which
(probably) makes redrawing slightly more efficient, e.g. by skipping
texture upload. So for now, no VO uses this new functionality, but since
it's trivial, commit it anyway.
The vo_driver->untimed case is for forcibly disabling redraw for vo_lavc
and vo_image always.
2015-01-24 22:28:38 +00:00
|
|
|
bool full_redraw = in->dropped_frame;
|
2015-07-01 17:24:28 +00:00
|
|
|
struct vo_frame *frame = NULL;
|
|
|
|
if (!vo->driver->untimed)
|
|
|
|
frame = vo_frame_ref(in->current_frame);
|
|
|
|
if (frame)
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
in->dropped_frame = false;
|
2015-07-01 17:24:28 +00:00
|
|
|
struct vo_frame dummy = {0};
|
|
|
|
if (!frame)
|
|
|
|
frame = &dummy;
|
|
|
|
frame->redraw = !full_redraw; // unconditionally redraw if it was dropped
|
2016-11-01 12:08:25 +00:00
|
|
|
frame->repeat = false;
|
2015-07-01 17:24:28 +00:00
|
|
|
frame->still = true;
|
2015-07-03 17:34:33 +00:00
|
|
|
frame->pts = 0;
|
|
|
|
frame->duration = -1;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2023-09-26 19:26:23 +00:00
|
|
|
vo->driver->draw_frame(vo, frame);
|
2015-07-01 17:24:28 +00:00
|
|
|
vo->driver->flip_page(vo);
|
|
|
|
|
2023-10-01 06:19:40 +00:00
|
|
|
if (frame != &dummy && !vo->driver->frame_owner)
|
2015-07-01 17:24:28 +00:00
|
|
|
talloc_free(frame);
|
2014-06-15 18:46:57 +00:00
|
|
|
}
|
|
|
|
|
2018-04-20 15:48:44 +00:00
|
|
|
static struct mp_image *get_image_vo(void *ctx, int imgfmt, int w, int h,
|
2023-01-21 14:21:49 +00:00
|
|
|
int stride_align, int flags)
|
2018-04-20 15:48:44 +00:00
|
|
|
{
|
|
|
|
struct vo *vo = ctx;
|
2023-01-21 14:21:49 +00:00
|
|
|
return vo->driver->get_image(vo, imgfmt, w, h, stride_align, flags);
|
2018-04-20 15:48:44 +00:00
|
|
|
}
|
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
static MP_THREAD_VOID vo_thread(void *ptr)
|
2014-04-22 19:03:52 +00:00
|
|
|
{
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
struct vo *vo = ptr;
|
|
|
|
struct vo_internal *in = vo->in;
|
2017-02-21 13:31:51 +00:00
|
|
|
bool vo_paused = false;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_thread_set_name("vo");
|
2014-10-19 21:32:34 +00:00
|
|
|
|
client API: fix potential deadlock problems by throwing more shit at it
The render API (vo_libmpv) had potential deadlock problems with
MPV_RENDER_PARAM_ADVANCED_CONTROL. This required vd-lavc-dr to be
enabled (the default). I never observed these deadlocks in the wild
(doesn't mean they didn't happen), although I could specifically provoke
them with some code changes.
The problem was mostly about DR (direct rendering, letting the video
decoder write to OpenGL buffer memory). Allocating/freeing a DR image
needs to be done on the OpenGL thread, even though _lots_ of threads are
involved with handling images. Freeing a DR image is a special case that
can happen any time. dr_helper.c does most of the evil magic of
achieving this. Unfortunately, there was a (sort of) circular lock
dependency: freeing an image while certain internal locks are held would
trigger the user's context update callback, which in turn would call
mpv_render_context_update(), which processed all pending free requests,
and then acquire an internal lock - which the caller might not release
until a further DR image could be freed.
"Solve" this by making freeing DR images asynchronous. This is slightly
risky, but actually not much. The DR images will be free'd eventually.
The biggest disadvantage is probably that debugging might get trickier.
Any solution to this problem will probably add images to free to some
sort of queue, and then process it later. I considered making this more
explicit (so there'd be a point where the caller forcibly waits for all
queued items to be free'd), but discarded these ideas as this probably
would only increase complexity.
Another consequence is that freeing DR images on the GL thread is not
synchronous anymore. Instead, it mpv_render_context_update() will do it
with a delay. This seems roundabout, but doesn't actually change
anything, and avoids additional code.
This also fixes that the render API required the render API user to
remain on the same thread, even though this wasn't documented. As such,
it was a bug. OpenGL essentially forces you to do all GL usage on a
single thread, but in theory the API user could for example move the GL
context to another thread.
The API bump is because I think you can't make enough noise about this.
Since we don't backport fixes to old versions, I'm specifically stating
that old versions are broken, and I'm supplying workarounds.
Internally, dr_helper_create() does not use pthread_self() anymore, thus
the vo.c change. I think it's better to make binding to the current
thread as explicit as possible.
Of course it's not sure that this fixes all deadlocks (probably not).
2019-09-26 12:07:33 +00:00
|
|
|
if (vo->driver->get_image) {
|
2018-04-20 15:48:44 +00:00
|
|
|
in->dr_helper = dr_helper_create(in->dispatch, get_image_vo, vo);
|
client API: fix potential deadlock problems by throwing more shit at it
The render API (vo_libmpv) had potential deadlock problems with
MPV_RENDER_PARAM_ADVANCED_CONTROL. This required vd-lavc-dr to be
enabled (the default). I never observed these deadlocks in the wild
(doesn't mean they didn't happen), although I could specifically provoke
them with some code changes.
The problem was mostly about DR (direct rendering, letting the video
decoder write to OpenGL buffer memory). Allocating/freeing a DR image
needs to be done on the OpenGL thread, even though _lots_ of threads are
involved with handling images. Freeing a DR image is a special case that
can happen any time. dr_helper.c does most of the evil magic of
achieving this. Unfortunately, there was a (sort of) circular lock
dependency: freeing an image while certain internal locks are held would
trigger the user's context update callback, which in turn would call
mpv_render_context_update(), which processed all pending free requests,
and then acquire an internal lock - which the caller might not release
until a further DR image could be freed.
"Solve" this by making freeing DR images asynchronous. This is slightly
risky, but actually not much. The DR images will be free'd eventually.
The biggest disadvantage is probably that debugging might get trickier.
Any solution to this problem will probably add images to free to some
sort of queue, and then process it later. I considered making this more
explicit (so there'd be a point where the caller forcibly waits for all
queued items to be free'd), but discarded these ideas as this probably
would only increase complexity.
Another consequence is that freeing DR images on the GL thread is not
synchronous anymore. Instead, it mpv_render_context_update() will do it
with a delay. This seems roundabout, but doesn't actually change
anything, and avoids additional code.
This also fixes that the render API required the render API user to
remain on the same thread, even though this wasn't documented. As such,
it was a bug. OpenGL essentially forces you to do all GL usage on a
single thread, but in theory the API user could for example move the GL
context to another thread.
The API bump is because I think you can't make enough noise about this.
Since we don't backport fixes to old versions, I'm specifically stating
that old versions are broken, and I'm supplying workarounds.
Internally, dr_helper_create() does not use pthread_self() anymore, thus
the vo.c change. I think it's better to make binding to the current
thread as explicit as possible.
Of course it's not sure that this fixes all deadlocks (probably not).
2019-09-26 12:07:33 +00:00
|
|
|
dr_helper_acquire_thread(in->dr_helper);
|
|
|
|
}
|
2018-04-20 15:48:44 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
int r = vo->driver->preinit(vo) ? -1 : 0;
|
|
|
|
mp_rendezvous(vo, r); // init barrier
|
|
|
|
if (r < 0)
|
2018-04-20 15:48:44 +00:00
|
|
|
goto done;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
|
2018-03-13 10:23:23 +00:00
|
|
|
read_opts(vo);
|
2015-03-13 12:14:11 +00:00
|
|
|
update_display_fps(vo);
|
2015-04-16 20:31:09 +00:00
|
|
|
vo_event(vo, VO_EVENT_WIN_STATE);
|
2015-03-13 12:14:11 +00:00
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
while (1) {
|
|
|
|
mp_dispatch_queue_process(vo->in->dispatch, 0);
|
|
|
|
if (in->terminate)
|
|
|
|
break;
|
2020-05-09 20:11:34 +00:00
|
|
|
stats_event(in->stats, "iterations");
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
vo->driver->control(vo, VOCTRL_CHECK_EVENTS, NULL);
|
2020-05-10 14:59:40 +00:00
|
|
|
bool working = render_frame(vo);
|
2023-09-18 02:34:32 +00:00
|
|
|
int64_t now = mp_time_ns();
|
2023-11-09 04:08:57 +00:00
|
|
|
int64_t wait_until = now + MP_TIME_S_TO_NS(working ? 0 : 1000);
|
2024-02-04 17:31:18 +00:00
|
|
|
bool wakeup_on_done = false;
|
|
|
|
int64_t wakeup_core_after = 0;
|
2017-02-21 13:31:51 +00:00
|
|
|
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
if (in->wakeup_pts) {
|
|
|
|
if (in->wakeup_pts > now) {
|
|
|
|
wait_until = MPMIN(wait_until, in->wakeup_pts);
|
|
|
|
} else {
|
|
|
|
in->wakeup_pts = 0;
|
2016-09-16 12:23:54 +00:00
|
|
|
wakeup_core(vo);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-07 12:48:34 +00:00
|
|
|
if (vo->want_redraw && !in->want_redraw) {
|
2014-10-03 19:53:32 +00:00
|
|
|
in->want_redraw = true;
|
2016-09-16 12:23:54 +00:00
|
|
|
wakeup_core(vo);
|
2014-10-03 19:53:32 +00:00
|
|
|
}
|
2024-02-04 17:31:18 +00:00
|
|
|
if ((!working && !in->rendering && !in->frame_queued) && in->wakeup_on_done) {
|
|
|
|
// At this point we know VO is going to sleep
|
|
|
|
int64_t frame_end = get_current_frame_end(vo);
|
|
|
|
if (frame_end >= 0)
|
|
|
|
wakeup_core_after = frame_end;
|
|
|
|
wakeup_on_done = true;
|
|
|
|
in->wakeup_on_done = false;
|
|
|
|
}
|
2020-05-09 20:11:34 +00:00
|
|
|
vo->want_redraw = false;
|
2014-10-03 19:53:32 +00:00
|
|
|
bool redraw = in->request_redraw;
|
2015-01-12 04:14:41 +00:00
|
|
|
bool send_reset = in->send_reset;
|
|
|
|
in->send_reset = false;
|
2017-02-21 13:18:30 +00:00
|
|
|
bool send_pause = in->paused != vo_paused;
|
|
|
|
vo_paused = in->paused;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2017-02-21 13:31:51 +00:00
|
|
|
|
2015-01-12 04:14:41 +00:00
|
|
|
if (send_reset)
|
|
|
|
vo->driver->control(vo, VOCTRL_RESET, NULL);
|
2017-02-21 13:18:30 +00:00
|
|
|
if (send_pause)
|
|
|
|
vo->driver->control(vo, vo_paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
|
2014-10-03 19:53:32 +00:00
|
|
|
if (wait_until > now && redraw) {
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
do_redraw(vo); // now is a good time
|
|
|
|
continue;
|
|
|
|
}
|
2017-02-21 13:31:51 +00:00
|
|
|
if (vo->want_redraw) // might have been set by VOCTRLs
|
|
|
|
wait_until = 0;
|
|
|
|
|
2023-09-17 22:47:14 +00:00
|
|
|
if (wait_until <= now)
|
|
|
|
continue;
|
|
|
|
|
2024-02-04 17:31:18 +00:00
|
|
|
if (wakeup_on_done) {
|
|
|
|
// At this point wait_until should be longer than frame duration
|
|
|
|
if (wakeup_core_after >= 0 && wait_until >= wakeup_core_after) {
|
|
|
|
wait_vo(vo, wakeup_core_after);
|
|
|
|
mp_mutex_lock(&in->lock);
|
|
|
|
in->need_wakeup = true;
|
|
|
|
mp_mutex_unlock(&in->lock);
|
|
|
|
}
|
|
|
|
wakeup_core(vo);
|
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
wait_vo(vo, wait_until);
|
|
|
|
}
|
2015-07-01 17:24:28 +00:00
|
|
|
forget_frames(vo); // implicitly synchronized
|
|
|
|
talloc_free(in->current_frame);
|
|
|
|
in->current_frame = NULL;
|
2015-07-04 15:27:13 +00:00
|
|
|
vo->driver->uninit(vo);
|
2018-04-20 15:48:44 +00:00
|
|
|
done:
|
|
|
|
TA_FREEP(&in->dr_helper);
|
2023-10-21 02:55:41 +00:00
|
|
|
MP_THREAD_RETURN();
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
}
|
|
|
|
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
void vo_set_paused(struct vo *vo, bool paused)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
if (in->paused != paused) {
|
2014-09-21 08:06:06 +00:00
|
|
|
in->paused = paused;
|
2017-02-21 13:31:51 +00:00
|
|
|
if (in->paused && in->dropped_frame) {
|
2015-11-15 12:17:54 +00:00
|
|
|
in->request_redraw = true;
|
2017-02-21 13:31:51 +00:00
|
|
|
wakeup_core(vo);
|
|
|
|
}
|
2015-11-25 21:07:56 +00:00
|
|
|
reset_vsync_timings(vo);
|
2017-02-21 13:18:30 +00:00
|
|
|
wakeup_locked(vo);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int64_t vo_get_drop_count(struct vo *vo)
|
|
|
|
{
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&vo->in->lock);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
int64_t r = vo->in->drop_count;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&vo->in->lock);
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-01-08 19:16:49 +00:00
|
|
|
void vo_increment_drop_count(struct vo *vo, int64_t n)
|
|
|
|
{
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&vo->in->lock);
|
2015-01-08 19:16:49 +00:00
|
|
|
vo->in->drop_count += n;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&vo->in->lock);
|
2015-01-08 19:16:49 +00:00
|
|
|
}
|
|
|
|
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Make the VO redraw the OSD at some point in the future.
|
|
|
|
void vo_redraw(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
if (!in->request_redraw) {
|
|
|
|
in->request_redraw = true;
|
2017-02-21 13:31:51 +00:00
|
|
|
in->want_redraw = false;
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
wakeup_locked(vo);
|
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-04-22 19:03:52 +00:00
|
|
|
}
|
|
|
|
|
2014-10-03 19:53:32 +00:00
|
|
|
bool vo_want_redraw(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2014-10-03 19:53:32 +00:00
|
|
|
bool r = in->want_redraw;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-10-03 19:53:32 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-04-22 19:03:52 +00:00
|
|
|
void vo_seek_reset(struct vo *vo)
|
|
|
|
{
|
2015-11-14 20:44:59 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2014-04-29 13:19:03 +00:00
|
|
|
forget_frames(vo);
|
2015-11-25 21:07:56 +00:00
|
|
|
reset_vsync_timings(vo);
|
2015-11-14 20:44:59 +00:00
|
|
|
in->send_reset = true;
|
2015-01-12 04:14:41 +00:00
|
|
|
wakeup_locked(vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2008-12-20 11:52:11 +00:00
|
|
|
}
|
|
|
|
|
video: fix and simplify video format changes and last frame display
The previous commit broke these things, and fixing them is separate in
this commit in order to reduce the volume of changes.
Move the image queue from the VO to the playback core. The image queue
is a remnant of the old way how vdpau was implemented, and increasingly
became more and more an artifact. In the end, it did only one thing:
computing the duration of the current frame. This was done by taking the
PTS difference between the current and the future frame. We keep this,
but by moving it out of the VO, we don't have to special-case format
changes anymore. This simplifies the code a lot.
Since we need the queue to compute the duration only, a queue size
larger than 2 makes no sense, and we can hardcode that.
Also change how the last frame is handled. The last frame is a bit of a
problem, because video timing works by showing one frame after another,
which makes it a special case. Make the VO provide a function to notify
us when the frame is done, instead. The frame duration is used for that.
This is not perfect. For example, changing playback speed during the
last frame doesn't update the end time. Pausing will not stop the clock
that times the last frame. But I don't think this matters for such a
corner case.
2014-08-12 21:17:35 +00:00
|
|
|
// Whether at least 1 frame was queued or rendered since last seek or reconfig.
|
|
|
|
bool vo_has_frame(struct vo *vo)
|
|
|
|
{
|
|
|
|
return vo->in->hasframe;
|
|
|
|
}
|
|
|
|
|
2014-08-20 19:35:45 +00:00
|
|
|
static void run_query_format(void *p)
|
|
|
|
{
|
|
|
|
void **pp = p;
|
|
|
|
struct vo *vo = pp[0];
|
2015-01-03 16:23:01 +00:00
|
|
|
uint8_t *list = pp[1];
|
|
|
|
for (int format = IMGFMT_START; format < IMGFMT_END; format++)
|
|
|
|
list[format - IMGFMT_START] = vo->driver->query_format(vo, format);
|
2014-08-20 19:35:45 +00:00
|
|
|
}
|
|
|
|
|
2015-01-03 16:23:01 +00:00
|
|
|
// For each item in the list (allocated as uint8_t[IMGFMT_END - IMGFMT_START]),
|
|
|
|
// set the supported format flags.
|
|
|
|
void vo_query_formats(struct vo *vo, uint8_t *list)
|
2014-08-20 19:35:45 +00:00
|
|
|
{
|
2015-01-03 16:23:01 +00:00
|
|
|
void *p[] = {vo, list};
|
2014-08-20 19:35:45 +00:00
|
|
|
mp_dispatch_run(vo->in->dispatch, run_query_format, p);
|
|
|
|
}
|
|
|
|
|
2014-01-21 23:26:01 +00:00
|
|
|
// 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.
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// Must be called from the VO thread only.
|
2014-01-21 23:26:01 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-06-01 15:24:19 +00:00
|
|
|
if (!vo->params) {
|
|
|
|
*out_src = *out_dst = (struct mp_rect){0};
|
|
|
|
*out_osd = (struct mp_osd_res){0};
|
2014-05-05 22:33:59 +00:00
|
|
|
return;
|
2014-06-01 15:24:19 +00:00
|
|
|
}
|
2014-04-20 19:36:56 +00:00
|
|
|
mp_get_src_dst_rects(vo->log, vo->opts, vo->driver->caps, vo->params,
|
|
|
|
vo->dwidth, vo->dheight, vo->monitor_par,
|
|
|
|
out_src, out_dst, out_osd);
|
2014-01-21 23:26:01 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 01:44:05 +00:00
|
|
|
// flip_page[_timed] will be called offset_us nanoseconds too early.
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
// (For vo_vdpau, which does its own timing.)
|
2015-07-20 19:12:46 +00:00
|
|
|
// num_req_frames set the requested number of requested vo_frame.frames.
|
2018-01-20 15:10:42 +00:00
|
|
|
// (For vo_gpu interpolation.)
|
2023-09-18 01:44:05 +00:00
|
|
|
void vo_set_queue_params(struct vo *vo, int64_t offset_ns, int num_req_frames)
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2023-09-18 01:44:05 +00:00
|
|
|
in->flip_queue_offset = offset_ns;
|
2015-07-20 19:12:46 +00:00
|
|
|
in->req_frames = MPCLAMP(num_req_frames, 1, VO_MAX_REQ_FRAMES);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-07-01 17:22:40 +00:00
|
|
|
}
|
|
|
|
|
2015-07-20 19:12:46 +00:00
|
|
|
int vo_get_num_req_frames(struct vo *vo)
|
2015-07-01 17:22:40 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-07-20 19:12:46 +00:00
|
|
|
int res = in->req_frames;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-07-01 17:22:40 +00:00
|
|
|
return res;
|
2011-12-06 19:23:54 +00:00
|
|
|
}
|
|
|
|
|
2023-09-07 23:15:01 +00:00
|
|
|
double vo_get_vsync_interval(struct vo *vo)
|
2014-08-17 00:50:59 +00:00
|
|
|
{
|
2015-03-12 22:35:38 +00:00
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2023-09-07 23:15:01 +00:00
|
|
|
double res = vo->in->vsync_interval > 1 ? vo->in->vsync_interval : -1;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-03-12 22:35:38 +00:00
|
|
|
return res;
|
2014-08-17 00:50:59 +00:00
|
|
|
}
|
|
|
|
|
2015-11-25 21:07:56 +00:00
|
|
|
double vo_get_estimated_vsync_interval(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2023-09-18 00:45:21 +00:00
|
|
|
double res = in->estimated_vsync_interval;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-11-25 21:07:56 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
double vo_get_estimated_vsync_jitter(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-11-25 21:07:56 +00:00
|
|
|
double res = in->estimated_vsync_jitter;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-11-25 21:07:56 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-11-14 20:44:59 +00:00
|
|
|
// Get the time in seconds at after which the currently rendering frame will
|
|
|
|
// end. Returns positive values if the frame is yet to be finished, negative
|
|
|
|
// values if it already finished.
|
2015-08-10 16:43:25 +00:00
|
|
|
// This can only be called while no new frame is queued (after
|
|
|
|
// vo_is_ready_for_frame). Returns 0 for non-display synced frames, or if the
|
|
|
|
// deadline for continuous display was missed.
|
2015-11-14 20:44:59 +00:00
|
|
|
double vo_get_delay(struct vo *vo)
|
2015-08-10 16:43:25 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-08-10 16:43:25 +00:00
|
|
|
assert (!in->frame_queued);
|
|
|
|
int64_t res = 0;
|
2015-11-25 21:11:58 +00:00
|
|
|
if (in->base_vsync && in->vsync_interval > 1 && in->current_frame) {
|
|
|
|
res = in->base_vsync;
|
2015-08-10 16:43:25 +00:00
|
|
|
int extra = !!in->rendering;
|
|
|
|
res += (in->current_frame->num_vsyncs + extra) * in->vsync_interval;
|
|
|
|
if (!in->current_frame->display_synced)
|
|
|
|
res = 0;
|
|
|
|
}
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2023-11-09 04:08:57 +00:00
|
|
|
return res ? MP_TIME_NS_TO_S(res - mp_time_ns()) : 0;
|
2015-08-10 16:43:25 +00:00
|
|
|
}
|
|
|
|
|
player: fix display-sync timing if audio take long on resume
In display-sync mode, the very first video frame is idiotically fully
timed, even though audio has not been synced yet at this point, and the
video frame is more like a "preview" frame. But since it's fully timed,
an underflow is detected if audio takes longer than the display time of
the frame (we send the second frame only after audio is done).
The timing code will try to compensate for the determined desync, but it
really shouldn't. So explicitly discard the timing info in this specific
case. On the other hand, if the first frame still hasn't finished
display, we can pretend everything is ok.
This is a hack - ideally, we either would send a frame without timing
info (and then send it again or so when playback starts properly), or we
would add real pause support to the VO, and pause it during syncing.
2016-08-07 12:06:54 +00:00
|
|
|
void vo_discard_timing_info(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
player: fix display-sync timing if audio take long on resume
In display-sync mode, the very first video frame is idiotically fully
timed, even though audio has not been synced yet at this point, and the
video frame is more like a "preview" frame. But since it's fully timed,
an underflow is detected if audio takes longer than the display time of
the frame (we send the second frame only after audio is done).
The timing code will try to compensate for the determined desync, but it
really shouldn't. So explicitly discard the timing info in this specific
case. On the other hand, if the first frame still hasn't finished
display, we can pretend everything is ok.
This is a hack - ideally, we either would send a frame without timing
info (and then send it again or so when playback starts properly), or we
would add real pause support to the VO, and pause it during syncing.
2016-08-07 12:06:54 +00:00
|
|
|
reset_vsync_timings(vo);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
player: fix display-sync timing if audio take long on resume
In display-sync mode, the very first video frame is idiotically fully
timed, even though audio has not been synced yet at this point, and the
video frame is more like a "preview" frame. But since it's fully timed,
an underflow is detected if audio takes longer than the display time of
the frame (we send the second frame only after audio is done).
The timing code will try to compensate for the determined desync, but it
really shouldn't. So explicitly discard the timing info in this specific
case. On the other hand, if the first frame still hasn't finished
display, we can pretend everything is ok.
This is a hack - ideally, we either would send a frame without timing
info (and then send it again or so when playback starts properly), or we
would add real pause support to the VO, and pause it during syncing.
2016-08-07 12:06:54 +00:00
|
|
|
}
|
|
|
|
|
2015-11-13 21:41:41 +00:00
|
|
|
int64_t vo_get_delayed_count(struct vo *vo)
|
2015-08-10 16:43:25 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-11-13 21:41:41 +00:00
|
|
|
int64_t res = vo->in->delayed_count;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-08-10 16:43:25 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-03-13 12:14:11 +00:00
|
|
|
double vo_get_display_fps(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-03-13 12:14:11 +00:00
|
|
|
double res = vo->in->display_fps;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-03-13 12:14:11 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-11-02 19:26:51 +00:00
|
|
|
// Set specific event flags, and wakeup the playback core if needed.
|
2014-11-09 09:00:21 +00:00
|
|
|
// vo_query_and_reset_events() can retrieve the events again.
|
2014-11-02 19:26:51 +00:00
|
|
|
void vo_event(struct vo *vo, int event)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2014-11-02 19:26:51 +00:00
|
|
|
if ((in->queued_events & event & VO_EVENTS_USER) != (event & VO_EVENTS_USER))
|
2016-09-16 12:23:54 +00:00
|
|
|
wakeup_core(vo);
|
2015-05-13 07:28:59 +00:00
|
|
|
if (event)
|
|
|
|
wakeup_locked(vo);
|
2014-11-02 19:26:51 +00:00
|
|
|
in->queued_events |= event;
|
2015-03-12 22:35:38 +00:00
|
|
|
in->internal_events |= event;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-11-02 19:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check event flags set with vo_event(). Return the mask of events that was
|
2014-11-09 09:00:21 +00:00
|
|
|
// set and included in the events parameter. Clear the returned events.
|
|
|
|
int vo_query_and_reset_events(struct vo *vo, int events)
|
2014-11-02 19:26:51 +00:00
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2014-11-02 19:26:51 +00:00
|
|
|
int r = in->queued_events & events;
|
2014-11-09 09:00:21 +00:00
|
|
|
in->queued_events &= ~(unsigned)r;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2014-11-02 19:26:51 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-01-24 21:56:02 +00:00
|
|
|
struct mp_image *vo_get_current_frame(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2015-07-01 17:24:28 +00:00
|
|
|
struct mp_image *r = NULL;
|
|
|
|
if (vo->in->current_frame)
|
|
|
|
r = mp_image_new_ref(vo->in->current_frame->current);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2015-01-24 21:56:02 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:18:36 +00:00
|
|
|
struct vo_frame *vo_get_current_vo_frame(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_internal *in = vo->in;
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_lock(&in->lock);
|
2018-02-07 19:18:36 +00:00
|
|
|
struct vo_frame *r = vo_frame_ref(vo->in->current_frame);
|
2023-10-21 02:55:41 +00:00
|
|
|
mp_mutex_unlock(&in->lock);
|
2018-02-07 19:18:36 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-04-20 15:48:44 +00:00
|
|
|
struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h,
|
2023-01-21 14:21:49 +00:00
|
|
|
int stride_align, int flags)
|
2018-04-20 15:48:44 +00:00
|
|
|
{
|
|
|
|
if (vo->driver->get_image_ts)
|
2023-01-21 14:21:49 +00:00
|
|
|
return vo->driver->get_image_ts(vo, imgfmt, w, h, stride_align, flags);
|
2018-04-20 15:48:44 +00:00
|
|
|
if (vo->in->dr_helper)
|
2023-01-21 14:21:49 +00:00
|
|
|
return dr_helper_get_image(vo->in->dr_helper, imgfmt, w, h, stride_align, flags);
|
2018-04-20 15:48:44 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-01 17:24:28 +00:00
|
|
|
static void destroy_frame(void *p)
|
|
|
|
{
|
|
|
|
struct vo_frame *frame = p;
|
|
|
|
for (int n = 0; n < frame->num_frames; n++)
|
|
|
|
talloc_free(frame->frames[n]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a new reference to the given frame. The image pointers are also new
|
|
|
|
// references. Calling talloc_free() on the frame unrefs all currently set
|
|
|
|
// image references. (Assuming current==frames[0].)
|
|
|
|
struct vo_frame *vo_frame_ref(struct vo_frame *frame)
|
|
|
|
{
|
|
|
|
if (!frame)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
struct vo_frame *new = talloc_ptrtype(NULL, new);
|
|
|
|
talloc_set_destructor(new, destroy_frame);
|
|
|
|
*new = *frame;
|
2023-06-28 05:42:22 +00:00
|
|
|
for (int n = 0; n < frame->num_frames; n++)
|
2015-07-01 17:24:28 +00:00
|
|
|
new->frames[n] = mp_image_new_ref(frame->frames[n]);
|
|
|
|
new->current = new->num_frames ? new->frames[0] : NULL;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2015-04-16 20:29:25 +00:00
|
|
|
/*
|
|
|
|
* lookup an integer in a table, table must have 0 as the last key
|
|
|
|
* param: key key to search for
|
|
|
|
* returns translation corresponding to key or "to" value of last mapping
|
2014-04-22 19:03:52 +00:00
|
|
|
* if not found.
|
|
|
|
*/
|
|
|
|
int lookup_keymap_table(const struct mp_keymap *map, int key)
|
|
|
|
{
|
|
|
|
while (map->from && map->from != key)
|
|
|
|
map++;
|
|
|
|
return map->to;
|
|
|
|
}
|
2023-11-06 23:52:56 +00:00
|
|
|
|
|
|
|
struct mp_image_params vo_get_current_params(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct mp_image_params p = {0};
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_lock(&vo->params_mutex);
|
2023-11-06 23:52:56 +00:00
|
|
|
if (vo->params)
|
|
|
|
p = *vo->params;
|
2023-11-09 02:18:48 +00:00
|
|
|
mp_mutex_unlock(&vo->params_mutex);
|
2023-11-06 23:52:56 +00:00
|
|
|
return p;
|
|
|
|
}
|
2023-08-31 14:59:14 +00:00
|
|
|
|
|
|
|
struct mp_image_params vo_get_target_params(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct mp_image_params p = {0};
|
|
|
|
mp_mutex_lock(&vo->params_mutex);
|
|
|
|
if (vo->target_params)
|
|
|
|
p = *vo->target_params;
|
|
|
|
mp_mutex_unlock(&vo->params_mutex);
|
|
|
|
return p;
|
|
|
|
}
|