2013-10-29 21:38:29 +00:00
|
|
|
/*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
2013-10-29 21:38:29 +00:00
|
|
|
*
|
player: change license of most core files to LGPL
These files have all in common that they were fully or mostly taken from
mplayer.c. (mplayer.c was a huge file that contains almost all of the
playback core, until it was split into multiple parts.) This was
probably the hardest part to relicense, because so much code was moved
around all the time.
player/audio.c still does not compile. We'll have to redo audio
filtering. Once that is done, we can probably actually provide an
actual LGPL configure switch.
Here is a relatively detailed list of potential issues:
8d190244: author did not reply, parts were made GPL-only in a previous
commit.
7882ea9b: author could not be reached, but the code is gone. wscript
still has --datadir switch, but I don't think this is relevant to
copyright.
f197efd5: unclear origin, but I consider the code gone anyway (replaced
with generic OSD mechanisms).
8337d9c2: author did not reply, but only the option still exists (under
a different name), other code was removed.
d8fd7131: did not reply. Disabled in a previous commit.
05258251: same author as above. Both fields actually seem to have
vanished (even when tracking renames), so no action taken.
d459e644, 268b2c1a: author did not reply, but we reuse only the options
(with different names and slightly or fully different semantics, and
completely different implementations), so I don't think this is relevant
for copyright.
09e742fe, 17c39c4e: same as above.
e8a173de, bff4b3ee: author could not be reached. The commands were
reworked to properties, and the code outside of the TV code were moved
back to the TV code. So I don't think copyright applies to the current
command.c parts (mp_property_tv_color, mp_property_tv_freq,
mp_property_tv_scan). The TV parts remain GPL.
0810e427: could not be reached. Disabled in a previous commit.
43744a2d: unknown author, but this was replaced by dynamic alloc (if the
change is even copyrightable).
116ca0c7: unknown author; reasoning see input.c relicensing commit.
e7e4d1d8: these semantics still exist, but as generic code, and this
code was fully removed.
f1175cd9: the author of the cited patch is unknown, and upon inspection
it turns out that I was only using the idea to pause the player on EOF,
so I claim it's not copyright relevant.
25affdcc: author could not be reached (yet) - but it's only a function
rename, not copyrightable.
5728504c was committed by Arpi (who agreed), but hints that it might be
by a different author. In fact it seems to be mostly this patch:
http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2001-November/002041.html
The author did not respond, but it all seems to have been removed later.
It's a terrible mess though. Arpi reverted the A-V sync code at first,
but left the RTC code for a while. The following commits remove these
changes 100%: 14b35442, 7181a091, 31482783, 614f8475, df58e822.
cehoyos did explicitly not agree to LGPL, but was involved in the
following changes:
c99d8fc8: applied a patch and didn't modify it, the original author
agreed.
40ac0d31: author could not be reached, but all code is gone anyway. The
"af" command has a similar function, but works completely different and
actually reuses a mechanism older than this patch.
54350436: applied a patch, but didn't modify it, except for adding a
German translation, which was removed later.
a2dda036: same situation as above
240b743e: this was made GPL-only in a previous commit
7b25afd7: same as above (for now)
kirijua could not be reached, but was a regular patch contributor:
c2c997fd: video equalizer code move; probably not copyrightable. Is GPL
due to Nick anyway.
be54f481: technically, this became the audio track property later. But
all what is left is the fact that you pass a track ID to it, so consider
the original coypright non-relevant.
2f376d1b: this was rewritten in b7052b43, but for now we can afford to
be careful, so this was marked as GPL only in a previous commit.
43844d09: remaining parts in main.c were reverted in a previous commit.
anders has mostly disagreed with the LGPL relicensing. Does not want
libaf to become LGPL, but made some concessions. In particular, he
granted us permission to relicense 4943e9c52c and 242aa6ebd4. We also
consider some of his changes remaining in mpv not relevant for copyright
(such as 735de602 - we won't remove the this option completely). We will
completely remove his other contributions, including the entire audio
filter chain. For now, this stuff is marked as GPL only. The remaining
question is how much code in player/audio.c (based on the former
mplayer.c and dec_audio.c) is under his copyright. I made claims about
this in a previous commit.
Nick(ols) Kurshev, svn username "nick" and "nickols_k", could not be
reached. He had a lot of changes in early MPlayer. It seems all of that
was removed, at least in mpv. His main work, like VIDIX or libswscale
work, does not exist in mpv anymore, but the changes to mplayer.c and
other core parts still deserve attention:
a4119f6b, fb927549, ad3529b8, e11b23dc, 5f2178be, 93c371d5: removed in
b43d67e0, d1628d12, 24ed01fe, df58e822.
0a83c6ec, 104c125e, 4e067f62, aec5dcc8, b587a3d6, f3de6e6b: DR, VAA, and
"tune" stuff was fully removed later on or replaced with other
mechanisms.
340183b0: screenshots were redone later (the VOCTRL was even removed,
with an independent implementation using the same VOCTRL a few years
later), so not relevant anymore. Basically only the 's' shortcut remains
(but not its implementation).
92c5c274, bffd4007, 555c6766: for now marked as GPL only in a previous
commit.
Might contain some trace amounts of "michael"'s copyright, who agreed to
LGPL only once the core is relicensed. This will still be respected, but
I don't think it matters at this in this case. (Some code touched by him
was merged into mplayer.c, and then disappeared after heavy
refactoring.)
I tried to be as careful and as complete as possible. It can't be
excluded that amends to this will be made later.
This does not make the player LGPL yet.
2017-06-23 13:53:41 +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.
|
2013-10-29 21:38:29 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2013-10-29 21:38:29 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
player: change license of most core files to LGPL
These files have all in common that they were fully or mostly taken from
mplayer.c. (mplayer.c was a huge file that contains almost all of the
playback core, until it was split into multiple parts.) This was
probably the hardest part to relicense, because so much code was moved
around all the time.
player/audio.c still does not compile. We'll have to redo audio
filtering. Once that is done, we can probably actually provide an
actual LGPL configure switch.
Here is a relatively detailed list of potential issues:
8d190244: author did not reply, parts were made GPL-only in a previous
commit.
7882ea9b: author could not be reached, but the code is gone. wscript
still has --datadir switch, but I don't think this is relevant to
copyright.
f197efd5: unclear origin, but I consider the code gone anyway (replaced
with generic OSD mechanisms).
8337d9c2: author did not reply, but only the option still exists (under
a different name), other code was removed.
d8fd7131: did not reply. Disabled in a previous commit.
05258251: same author as above. Both fields actually seem to have
vanished (even when tracking renames), so no action taken.
d459e644, 268b2c1a: author did not reply, but we reuse only the options
(with different names and slightly or fully different semantics, and
completely different implementations), so I don't think this is relevant
for copyright.
09e742fe, 17c39c4e: same as above.
e8a173de, bff4b3ee: author could not be reached. The commands were
reworked to properties, and the code outside of the TV code were moved
back to the TV code. So I don't think copyright applies to the current
command.c parts (mp_property_tv_color, mp_property_tv_freq,
mp_property_tv_scan). The TV parts remain GPL.
0810e427: could not be reached. Disabled in a previous commit.
43744a2d: unknown author, but this was replaced by dynamic alloc (if the
change is even copyrightable).
116ca0c7: unknown author; reasoning see input.c relicensing commit.
e7e4d1d8: these semantics still exist, but as generic code, and this
code was fully removed.
f1175cd9: the author of the cited patch is unknown, and upon inspection
it turns out that I was only using the idea to pause the player on EOF,
so I claim it's not copyright relevant.
25affdcc: author could not be reached (yet) - but it's only a function
rename, not copyrightable.
5728504c was committed by Arpi (who agreed), but hints that it might be
by a different author. In fact it seems to be mostly this patch:
http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2001-November/002041.html
The author did not respond, but it all seems to have been removed later.
It's a terrible mess though. Arpi reverted the A-V sync code at first,
but left the RTC code for a while. The following commits remove these
changes 100%: 14b35442, 7181a091, 31482783, 614f8475, df58e822.
cehoyos did explicitly not agree to LGPL, but was involved in the
following changes:
c99d8fc8: applied a patch and didn't modify it, the original author
agreed.
40ac0d31: author could not be reached, but all code is gone anyway. The
"af" command has a similar function, but works completely different and
actually reuses a mechanism older than this patch.
54350436: applied a patch, but didn't modify it, except for adding a
German translation, which was removed later.
a2dda036: same situation as above
240b743e: this was made GPL-only in a previous commit
7b25afd7: same as above (for now)
kirijua could not be reached, but was a regular patch contributor:
c2c997fd: video equalizer code move; probably not copyrightable. Is GPL
due to Nick anyway.
be54f481: technically, this became the audio track property later. But
all what is left is the fact that you pass a track ID to it, so consider
the original coypright non-relevant.
2f376d1b: this was rewritten in b7052b43, but for now we can afford to
be careful, so this was marked as GPL only in a previous commit.
43844d09: remaining parts in main.c were reverted in a previous commit.
anders has mostly disagreed with the LGPL relicensing. Does not want
libaf to become LGPL, but made some concessions. In particular, he
granted us permission to relicense 4943e9c52c and 242aa6ebd4. We also
consider some of his changes remaining in mpv not relevant for copyright
(such as 735de602 - we won't remove the this option completely). We will
completely remove his other contributions, including the entire audio
filter chain. For now, this stuff is marked as GPL only. The remaining
question is how much code in player/audio.c (based on the former
mplayer.c and dec_audio.c) is under his copyright. I made claims about
this in a previous commit.
Nick(ols) Kurshev, svn username "nick" and "nickols_k", could not be
reached. He had a lot of changes in early MPlayer. It seems all of that
was removed, at least in mpv. His main work, like VIDIX or libswscale
work, does not exist in mpv anymore, but the changes to mplayer.c and
other core parts still deserve attention:
a4119f6b, fb927549, ad3529b8, e11b23dc, 5f2178be, 93c371d5: removed in
b43d67e0, d1628d12, 24ed01fe, df58e822.
0a83c6ec, 104c125e, 4e067f62, aec5dcc8, b587a3d6, f3de6e6b: DR, VAA, and
"tune" stuff was fully removed later on or replaced with other
mechanisms.
340183b0: screenshots were redone later (the VOCTRL was even removed,
with an independent implementation using the same VOCTRL a few years
later), so not relevant anymore. Basically only the 's' shortcut remains
(but not its implementation).
92c5c274, bffd4007, 555c6766: for now marked as GPL only in a previous
commit.
Might contain some trace amounts of "michael"'s copyright, who agreed to
LGPL only once the core is relicensed. This will still be respected, but
I don't think it matters at this in this case. (Some code touched by him
was merged into mplayer.c, and then disappeared after heavy
refactoring.)
I tried to be as careful and as complete as possible. It can't be
excluded that amends to this will be made later.
This does not make the player LGPL yet.
2017-06-23 13:53:41 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2013-10-29 21:38:29 +00:00
|
|
|
*
|
player: change license of most core files to LGPL
These files have all in common that they were fully or mostly taken from
mplayer.c. (mplayer.c was a huge file that contains almost all of the
playback core, until it was split into multiple parts.) This was
probably the hardest part to relicense, because so much code was moved
around all the time.
player/audio.c still does not compile. We'll have to redo audio
filtering. Once that is done, we can probably actually provide an
actual LGPL configure switch.
Here is a relatively detailed list of potential issues:
8d190244: author did not reply, parts were made GPL-only in a previous
commit.
7882ea9b: author could not be reached, but the code is gone. wscript
still has --datadir switch, but I don't think this is relevant to
copyright.
f197efd5: unclear origin, but I consider the code gone anyway (replaced
with generic OSD mechanisms).
8337d9c2: author did not reply, but only the option still exists (under
a different name), other code was removed.
d8fd7131: did not reply. Disabled in a previous commit.
05258251: same author as above. Both fields actually seem to have
vanished (even when tracking renames), so no action taken.
d459e644, 268b2c1a: author did not reply, but we reuse only the options
(with different names and slightly or fully different semantics, and
completely different implementations), so I don't think this is relevant
for copyright.
09e742fe, 17c39c4e: same as above.
e8a173de, bff4b3ee: author could not be reached. The commands were
reworked to properties, and the code outside of the TV code were moved
back to the TV code. So I don't think copyright applies to the current
command.c parts (mp_property_tv_color, mp_property_tv_freq,
mp_property_tv_scan). The TV parts remain GPL.
0810e427: could not be reached. Disabled in a previous commit.
43744a2d: unknown author, but this was replaced by dynamic alloc (if the
change is even copyrightable).
116ca0c7: unknown author; reasoning see input.c relicensing commit.
e7e4d1d8: these semantics still exist, but as generic code, and this
code was fully removed.
f1175cd9: the author of the cited patch is unknown, and upon inspection
it turns out that I was only using the idea to pause the player on EOF,
so I claim it's not copyright relevant.
25affdcc: author could not be reached (yet) - but it's only a function
rename, not copyrightable.
5728504c was committed by Arpi (who agreed), but hints that it might be
by a different author. In fact it seems to be mostly this patch:
http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2001-November/002041.html
The author did not respond, but it all seems to have been removed later.
It's a terrible mess though. Arpi reverted the A-V sync code at first,
but left the RTC code for a while. The following commits remove these
changes 100%: 14b35442, 7181a091, 31482783, 614f8475, df58e822.
cehoyos did explicitly not agree to LGPL, but was involved in the
following changes:
c99d8fc8: applied a patch and didn't modify it, the original author
agreed.
40ac0d31: author could not be reached, but all code is gone anyway. The
"af" command has a similar function, but works completely different and
actually reuses a mechanism older than this patch.
54350436: applied a patch, but didn't modify it, except for adding a
German translation, which was removed later.
a2dda036: same situation as above
240b743e: this was made GPL-only in a previous commit
7b25afd7: same as above (for now)
kirijua could not be reached, but was a regular patch contributor:
c2c997fd: video equalizer code move; probably not copyrightable. Is GPL
due to Nick anyway.
be54f481: technically, this became the audio track property later. But
all what is left is the fact that you pass a track ID to it, so consider
the original coypright non-relevant.
2f376d1b: this was rewritten in b7052b43, but for now we can afford to
be careful, so this was marked as GPL only in a previous commit.
43844d09: remaining parts in main.c were reverted in a previous commit.
anders has mostly disagreed with the LGPL relicensing. Does not want
libaf to become LGPL, but made some concessions. In particular, he
granted us permission to relicense 4943e9c52c and 242aa6ebd4. We also
consider some of his changes remaining in mpv not relevant for copyright
(such as 735de602 - we won't remove the this option completely). We will
completely remove his other contributions, including the entire audio
filter chain. For now, this stuff is marked as GPL only. The remaining
question is how much code in player/audio.c (based on the former
mplayer.c and dec_audio.c) is under his copyright. I made claims about
this in a previous commit.
Nick(ols) Kurshev, svn username "nick" and "nickols_k", could not be
reached. He had a lot of changes in early MPlayer. It seems all of that
was removed, at least in mpv. His main work, like VIDIX or libswscale
work, does not exist in mpv anymore, but the changes to mplayer.c and
other core parts still deserve attention:
a4119f6b, fb927549, ad3529b8, e11b23dc, 5f2178be, 93c371d5: removed in
b43d67e0, d1628d12, 24ed01fe, df58e822.
0a83c6ec, 104c125e, 4e067f62, aec5dcc8, b587a3d6, f3de6e6b: DR, VAA, and
"tune" stuff was fully removed later on or replaced with other
mechanisms.
340183b0: screenshots were redone later (the VOCTRL was even removed,
with an independent implementation using the same VOCTRL a few years
later), so not relevant anymore. Basically only the 's' shortcut remains
(but not its implementation).
92c5c274, bffd4007, 555c6766: for now marked as GPL only in a previous
commit.
Might contain some trace amounts of "michael"'s copyright, who agreed to
LGPL only once the core is relicensed. This will still be respected, but
I don't think it matters at this in this case. (Some code touched by him
was merged into mplayer.c, and then disappeared after heavy
refactoring.)
I tried to be as careful and as complete as possible. It can't be
excluded that amends to this will be made later.
This does not make the player LGPL yet.
2017-06-23 13:53:41 +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/>.
|
2013-10-29 21:38:29 +00:00
|
|
|
*/
|
|
|
|
|
2020-08-31 20:45:04 +00:00
|
|
|
#include <assert.h>
|
2013-10-29 21:38:29 +00:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <math.h>
|
2020-08-31 20:45:04 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "client.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "core.h"
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "screenshot.h"
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "audio/out/ao.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/common.h"
|
|
|
|
#include "common/encode.h"
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "common/msg.h"
|
|
|
|
#include "common/playlist.h"
|
2020-04-09 22:55:39 +00:00
|
|
|
#include "common/stats.h"
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "demux/demux.h"
|
video: make decoder wrapper a filter
Move dec_video.c to filters/f_decoder_wrapper.c. It essentially becomes
a source filter. vd.h mostly disappears, because mp_filter takes care of
the dataflow, but its remains are in struct mp_decoder_fns.
One goal is to simplify dataflow by letting the filter framework handle
it (or more accurately, using its conventions). One result is that the
decode calls disappear from video.c, because we simply connect the
decoder wrapper and the filter chain with mp_pin_connect().
Another goal is to eventually remove the code duplication between the
audio and video paths for this. This commit prepares for this by trying
to make f_decoder_wrapper.c extensible, so it can be used for audio as
well later.
Decoder framedropping changes a bit. It doesn't seem to be worse than
before, and it's an obscure feature, so I'm content with its new state.
Some special code that was apparently meant to avoid dropping too many
frames in a row is removed, though.
I'm not sure how the source code tree should be organized. For one,
video/decode/vd_lavc.c is the only file in its directory, which is a bit
annoying.
2018-01-28 09:08:45 +00:00
|
|
|
#include "filters/f_decoder_wrapper.h"
|
audio: refactor how data is passed to AO
This replaces the two buffers (ao_chain.ao_buffer in the core, and
buffer_state.buffers in the AO) with a single queue. Instead of having a
byte based buffer, the queue is simply a list of audio frames, as output
by the decoder. This should make dataflow simpler and reduce copying.
It also attempts to simplify fill_audio_out_buffers(), the function I
always hated most, because it's full of subtle and buggy logic.
Unfortunately, I got assaulted by corner cases, dumb features (attempt
at seamless looping, really?), and other crap, so it got pretty
complicated again. fill_audio_out_buffers() is still full of subtle and
buggy logic. Maybe it got worse. On the other hand, maybe there really
is some progress. Who knows.
Originally, the data flow parts was meant to be in f_output_chain, but
due to tricky interactions with the playloop code, it's now in the dummy
filter in audio.c.
At least this improves the way the audio PTS is passed to the encoder in
encoding mode. Now it attempts to pass frames directly, along with the
pts, which should minimize timestamp problems. But to be honest, encoder
mode is one big kludge that shouldn't exist in this way.
This commit should be considered pre-alpha code. There are lots of bugs
still hiding.
2020-08-28 18:23:54 +00:00
|
|
|
#include "filters/filter_internal.h"
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/input.h"
|
2014-04-23 18:37:57 +00:00
|
|
|
#include "misc/dispatch.h"
|
2020-08-31 20:45:04 +00:00
|
|
|
#include "options/m_config_frontend.h"
|
|
|
|
#include "options/m_property.h"
|
|
|
|
#include "options/options.h"
|
2013-12-19 20:31:27 +00:00
|
|
|
#include "osdep/terminal.h"
|
2013-10-29 21:38:29 +00:00
|
|
|
#include "osdep/timer.h"
|
|
|
|
#include "stream/stream.h"
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
#include "sub/dec_sub.h"
|
2013-11-24 11:58:06 +00:00
|
|
|
#include "sub/osd.h"
|
2013-10-29 21:38:29 +00:00
|
|
|
#include "video/out/vo.h"
|
|
|
|
|
2016-09-16 12:23:54 +00:00
|
|
|
// Wait until mp_wakeup_core() is called, since the last time
|
2016-09-16 12:25:50 +00:00
|
|
|
// mp_wait_events() was called.
|
|
|
|
void mp_wait_events(struct MPContext *mpctx)
|
2014-09-06 14:57:46 +00:00
|
|
|
{
|
client API: rewrite property observation (again)
I intend to rewrite this code approximately every 2 months.
Last time, I did this in commit d66eb93e5d4 (and 065c307e8e7 and
b2006eeb74f). It was intended to remove the roundabout synchronous
thread "ping pong" when observing properties. At first, the original
async. code was replaced with some nice mostly synchronous code. But
then an async. code path had to be added for vo_libmpv, and finally the
sync. code was dropped because it broke in other obscure cases (like the
Objective-C Cocoa backend).
Try again. This time, update properties entirely on the main thread.
Updates get batched out on every playloop iteration. (At first I wanted
it to make it every time the player goes to sleep, but that might starve
API clients if the playloop get saturated.) One nice thing is that
clients only get woken up once all changed events have been sent, which
might reduce overhead.
While this sounds simple, it's not. The main problem is that reading
properties must not block the client API, i.e. no client API locks can
be held while reading the property. Maybe eventually we can avoid this
requirement, but currently it's just a fact. This means we have to
iterate over all clients and then over all properties (of each client),
all while releasing all locks when updating a property. Solve this by
rechecking on each iteration whether the list changed, and if so,
aborting the iteration and redo it "next time".
High risk change, expect bugs such as crashes and missing property
updates.
2019-12-19 10:11:51 +00:00
|
|
|
mp_client_send_property_changes(mpctx);
|
|
|
|
|
2020-04-09 22:55:39 +00:00
|
|
|
stats_event(mpctx->stats, "iterations");
|
|
|
|
|
2017-02-21 14:16:38 +00:00
|
|
|
bool sleeping = mpctx->sleeptime > 0;
|
|
|
|
if (sleeping)
|
2016-09-16 12:25:50 +00:00
|
|
|
MP_STATS(mpctx, "start sleep");
|
|
|
|
|
|
|
|
mp_dispatch_queue_process(mpctx->dispatch, mpctx->sleeptime);
|
|
|
|
|
|
|
|
mpctx->sleeptime = INFINITY;
|
|
|
|
|
2017-02-21 14:16:38 +00:00
|
|
|
if (sleeping)
|
2016-09-16 12:25:50 +00:00
|
|
|
MP_STATS(mpctx, "end sleep");
|
2014-09-06 14:57:46 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 12:24:15 +00:00
|
|
|
// Set the timeout used when the playloop goes to sleep. This means the
|
|
|
|
// playloop will re-run as soon as the timeout elapses (or earlier).
|
|
|
|
// mp_set_timeout(c, 0) is essentially equivalent to mp_wakeup_core(c).
|
|
|
|
void mp_set_timeout(struct MPContext *mpctx, double sleeptime)
|
|
|
|
{
|
2018-04-15 09:43:49 +00:00
|
|
|
if (mpctx->sleeptime > sleeptime) {
|
|
|
|
mpctx->sleeptime = sleeptime;
|
2023-10-11 19:32:48 +00:00
|
|
|
int64_t abstime = mp_time_ns_add(mp_time_ns(), sleeptime);
|
2018-04-15 09:43:49 +00:00
|
|
|
mp_dispatch_adjust_timeout(mpctx->dispatch, abstime);
|
|
|
|
}
|
2016-09-16 12:24:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 12:23:54 +00:00
|
|
|
// Cause the playloop to run. This can be called from any thread. If called
|
|
|
|
// from within the playloop itself, it will be run immediately again, instead
|
|
|
|
// of going to sleep in the next mp_wait_events().
|
|
|
|
void mp_wakeup_core(struct MPContext *mpctx)
|
|
|
|
{
|
2016-09-16 12:25:50 +00:00
|
|
|
mp_dispatch_interrupt(mpctx->dispatch);
|
2016-09-16 12:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Opaque callback variant of mp_wakeup_core().
|
|
|
|
void mp_wakeup_core_cb(void *ctx)
|
|
|
|
{
|
|
|
|
struct MPContext *mpctx = ctx;
|
|
|
|
mp_wakeup_core(mpctx);
|
|
|
|
}
|
|
|
|
|
2018-05-06 16:27:18 +00:00
|
|
|
void mp_core_lock(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
mp_dispatch_lock(mpctx->dispatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_core_unlock(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
mp_dispatch_unlock(mpctx->dispatch);
|
|
|
|
}
|
|
|
|
|
2019-12-14 13:18:40 +00:00
|
|
|
// Process any queued user input.
|
2020-04-03 10:56:50 +00:00
|
|
|
static void mp_process_input(struct MPContext *mpctx)
|
2014-09-06 14:57:46 +00:00
|
|
|
{
|
2020-07-23 12:35:56 +00:00
|
|
|
int processed = 0;
|
2014-09-07 18:44:54 +00:00
|
|
|
for (;;) {
|
|
|
|
mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input);
|
|
|
|
if (!cmd)
|
|
|
|
break;
|
2018-05-12 16:46:37 +00:00
|
|
|
run_command(mpctx, cmd, NULL, NULL, NULL);
|
2020-07-23 12:35:56 +00:00
|
|
|
processed = 1;
|
2014-09-06 14:57:46 +00:00
|
|
|
}
|
2016-09-16 12:25:50 +00:00
|
|
|
mp_set_timeout(mpctx, mp_input_get_delay(mpctx->input));
|
2020-07-23 12:35:56 +00:00
|
|
|
if (processed)
|
|
|
|
mp_notify(mpctx, MP_EVENT_INPUT_PROCESSED, NULL);
|
2014-09-06 14:57:46 +00:00
|
|
|
}
|
|
|
|
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
double get_relative_time(struct MPContext *mpctx)
|
|
|
|
{
|
2023-09-18 01:05:16 +00:00
|
|
|
int64_t new_time = mp_time_ns();
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
int64_t delta = new_time - mpctx->last_time;
|
|
|
|
mpctx->last_time = new_time;
|
2023-09-18 01:05:16 +00:00
|
|
|
return delta * 1e-9;
|
Relicense some non-MPlayer source files to LGPL 2.1 or later
This covers source files which were added in mplayer2 and mpv times
only, and where all code is covered by LGPL relicensing agreements.
There are probably more files to which this applies, but I'm being
conservative here.
A file named ao_sdl.c exists in MPlayer too, but the mpv one is a
complete rewrite, and was added some time after the original ao_sdl.c
was removed. The same applies to vo_sdl.c, for which the SDL2 API is
radically different in addition (MPlayer supports SDL 1.2 only).
common.c contains only code written by me. But common.h is a strange
case: although it originally was named mp_common.h and exists in MPlayer
too, by now it contains only definitions written by uau and me. The
exceptions are the CONTROL_ defines - thus not changing the license of
common.h yet.
codec_tags.c contained once large tables generated from MPlayer's
codecs.conf, but all of these tables were removed.
From demux_playlist.c I'm removing a code fragment from someone who was
not asked; this probably could be done later (see commit 15dccc37).
misc.c is a bit complicated to reason about (it was split off mplayer.c
and thus contains random functions out of this file), but actually all
functions have been added post-MPlayer. Except get_relative_time(),
which was written by uau, but looks similar to 3 different versions of
something similar in each of the Unix/win32/OSX timer source files. I'm
not sure what that means in regards to copyright, so I've just moved it
into another still-GPL source file for now.
screenshot.c once had some minor parts of MPlayer's vf_screenshot.c, but
they're all gone.
2016-01-19 17:36:06 +00:00
|
|
|
}
|
|
|
|
|
2017-04-14 16:56:03 +00:00
|
|
|
void update_core_idle_state(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
bool eof = mpctx->video_status == STATUS_EOF &&
|
|
|
|
mpctx->audio_status == STATUS_EOF;
|
2018-05-19 16:25:54 +00:00
|
|
|
bool active = !mpctx->paused && mpctx->restart_complete &&
|
2019-03-11 17:15:55 +00:00
|
|
|
!mpctx->stop_play && mpctx->in_playloop && !eof;
|
2017-04-14 16:56:03 +00:00
|
|
|
|
|
|
|
if (mpctx->playback_active != active) {
|
|
|
|
mpctx->playback_active = active;
|
|
|
|
|
|
|
|
update_screensaver_state(mpctx);
|
|
|
|
|
|
|
|
mp_notify(mpctx, MP_EVENT_CORE_IDLE, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 09:43:06 +00:00
|
|
|
bool get_internal_paused(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
return mpctx->opts->pause || mpctx->paused_for_cache;
|
|
|
|
}
|
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
// The value passed here is the new value for mpctx->opts->pause
|
|
|
|
void set_pause_state(struct MPContext *mpctx, bool user_pause)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
2017-04-14 16:22:45 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
opts->pause = user_pause;
|
2016-09-16 12:24:15 +00:00
|
|
|
|
2020-08-27 09:43:06 +00:00
|
|
|
bool internal_paused = get_internal_paused(mpctx);
|
2017-04-14 16:22:45 +00:00
|
|
|
if (internal_paused != mpctx->paused) {
|
|
|
|
mpctx->paused = internal_paused;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2023-08-10 03:50:05 +00:00
|
|
|
if (mpctx->ao) {
|
|
|
|
bool eof = mpctx->audio_status == STATUS_EOF;
|
|
|
|
ao_set_paused(mpctx->ao, internal_paused, eof);
|
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
if (mpctx->video_out)
|
|
|
|
vo_set_paused(mpctx->video_out, internal_paused);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
mpctx->osd_function = 0;
|
|
|
|
mpctx->osd_force_update = true;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
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
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
if (internal_paused) {
|
|
|
|
mpctx->step_frames = 0;
|
|
|
|
mpctx->time_frame -= get_relative_time(mpctx);
|
|
|
|
} else {
|
|
|
|
(void)get_relative_time(mpctx); // ignore time that passed during pause
|
|
|
|
}
|
|
|
|
}
|
2016-09-16 12:24:15 +00:00
|
|
|
|
2017-04-14 16:56:03 +00:00
|
|
|
update_core_idle_state(mpctx);
|
|
|
|
|
player: change m_config to use new option handling mechanisms
Instead of making m_config a special-case, it more or less uses the
underlying m_config_cache/m_config_shadow APIs properly. This makes the
player core a (relatively) equivalent user of the core option API. In
particular, this means that other threads can change core options with
m_config_cache_write_opt() calls (before this commit, this merely led to
diverging option values).
An important change is that before this commit, mpctx->opts contained
the "master copy" of all option data. Now it's just another copy of the
option data, and the shadow copy is considered the master. This is why
whenever mpctx->opts is written, the change needs to be copied to the
master (thus why this commits add a bunch of m_config_notify... calls).
If another thread (e.g. a VO) changes an option, async_change_cb is now
invoked, which funnels the change notification through the player's
layers.
The new self_notification parameter on mp_option_change_callback is so
that m_config_notify... doesn't trigger recursion, and it's used in
cases where the change was already "processed". It's still needed to
trigger libmpv property updates. (I considered using an extra
m_config_cache for that, but it'd only cause problems with no
advantages.)
I think the recent changes actually forgot to send libmpv property
updates in some cases. This should fix this anyway. In some cases,
property updates are reworked, and the potential for bugs should be
lower (probably).
The primary point of this change is to allow external updates, for
example by a VO writing the fullscreen option if the window state is
changed by the window manager (rather than mpv changing it). This is not
used yet, but the following commits will.
2019-11-29 11:49:15 +00:00
|
|
|
m_config_notify_change_opt_ptr(mpctx->mconfig, &opts->pause);
|
2017-04-14 16:22:45 +00:00
|
|
|
}
|
2014-02-24 21:49:07 +00:00
|
|
|
|
2017-04-14 16:22:45 +00:00
|
|
|
void update_internal_pause_state(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
set_pause_state(mpctx, mpctx->opts->pause);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-10-02 10:33:34 +00:00
|
|
|
void update_screensaver_state(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (!mpctx->video_out)
|
|
|
|
return;
|
|
|
|
|
2022-02-18 20:39:25 +00:00
|
|
|
bool saver_state = (!mpctx->playback_active || !mpctx->opts->stop_screensaver) &&
|
|
|
|
mpctx->opts->stop_screensaver != 2;
|
2017-02-21 13:18:30 +00:00
|
|
|
vo_control_async(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
|
|
|
|
: VOCTRL_KILL_SCREENSAVER, NULL);
|
2016-10-02 10:33:34 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
void add_step_frame(struct MPContext *mpctx, int dir)
|
|
|
|
{
|
2016-01-17 17:07:50 +00:00
|
|
|
if (!mpctx->vo_chain)
|
2013-10-29 21:38:29 +00:00
|
|
|
return;
|
|
|
|
if (dir > 0) {
|
|
|
|
mpctx->step_frames += 1;
|
2017-04-14 16:22:45 +00:00
|
|
|
set_pause_state(mpctx, false);
|
2013-10-29 21:38:29 +00:00
|
|
|
} else if (dir < 0) {
|
2016-04-27 15:01:17 +00:00
|
|
|
if (!mpctx->hrseek_active) {
|
2016-08-15 19:07:32 +00:00
|
|
|
queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, 0);
|
2017-04-14 16:22:45 +00:00
|
|
|
set_pause_state(mpctx, true);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-30 21:01:55 +00:00
|
|
|
// Clear some playback-related fields on file loading or after seeks.
|
|
|
|
void reset_playback_state(struct MPContext *mpctx)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
mp_filter_reset(mpctx->filter_root);
|
|
|
|
|
2014-07-30 21:01:55 +00:00
|
|
|
reset_video_state(mpctx);
|
|
|
|
reset_audio_state(mpctx);
|
|
|
|
reset_subtitle_state(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *t = mpctx->tracks[n];
|
2019-06-03 00:05:52 +00:00
|
|
|
// (Often, but not always, this is redundant and also done elsewhere.)
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
if (t->dec)
|
f_decoder_wrapper: replace most public fields with setters/getters
I may (optionally) move decoding to a separate thread in a future
change. It's a bit attractive to move the entire decoder wrapper to
there, so if the demuxer has a new packet, it doesn't have to wake up
the main thread, and can directly wake up the decoder. (Although that's
bullshit, since there's a queue in between, and libavcodec's
multi-threaded decoding plays cross-threads ping pong with packets
anyway. On the other hand, the main thread would still have to shuffle
the packets around, so whatever, just seems like better design.)
As preparation, there shouldn't be any mutable state exposed by the
wrapper. But there's still a large number of corner-caseish crap, so
just use setters/getters for them. This recorder thing will inherently
not work, so it'll have to be disabled if threads are used.
This is a bit painful, but probably still the right thing. Like
speculatively pulling teeth.
2020-02-28 20:17:55 +00:00
|
|
|
mp_decoder_wrapper_set_play_dir(t->dec, mpctx->play_dir);
|
2020-02-04 19:26:35 +00:00
|
|
|
if (t->d_sub)
|
|
|
|
sub_set_play_dir(t->d_sub, mpctx->play_dir);
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-07-18 10:11:29 +00:00
|
|
|
// May need unpause first
|
|
|
|
if (mpctx->paused_for_cache)
|
|
|
|
update_internal_pause_state(mpctx);
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->hrseek_active = false;
|
2014-12-07 01:47:09 +00:00
|
|
|
mpctx->hrseek_lastframe = false;
|
2016-01-12 22:49:00 +00:00
|
|
|
mpctx->hrseek_backstep = false;
|
2017-08-14 12:02:13 +00:00
|
|
|
mpctx->current_seek = (struct seek_params){0};
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->playback_pts = MP_NOPTS_VALUE;
|
2014-11-28 23:24:51 +00:00
|
|
|
mpctx->step_frames = 0;
|
2016-08-18 18:40:23 +00:00
|
|
|
mpctx->ab_loop_clip = true;
|
2014-07-28 18:40:43 +00:00
|
|
|
mpctx->restart_complete = false;
|
2018-01-06 14:49:32 +00:00
|
|
|
mpctx->paused_for_cache = false;
|
|
|
|
mpctx->cache_buffer = 100;
|
2020-03-05 21:23:43 +00:00
|
|
|
mpctx->cache_update_pts = MP_NOPTS_VALUE;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
|
|
|
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
2017-04-14 16:56:03 +00:00
|
|
|
|
2018-01-06 14:49:32 +00:00
|
|
|
update_internal_pause_state(mpctx);
|
2017-04-14 16:56:03 +00:00
|
|
|
update_core_idle_state(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-06-08 10:04:56 +00:00
|
|
|
static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
|
2018-01-17 06:07:15 +00:00
|
|
|
if (!mpctx->demuxer || !seek.type || seek.amount == MP_NOPTS_VALUE)
|
2016-06-08 10:04:56 +00:00
|
|
|
return;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2023-09-27 12:00:14 +00:00
|
|
|
if (seek.type == MPSEEK_CHAPTER) {
|
|
|
|
mpctx->last_chapter_flag = false;
|
|
|
|
seek.type = MPSEEK_ABSOLUTE;
|
|
|
|
} else {
|
|
|
|
mpctx->last_chapter_seek = -2;
|
|
|
|
}
|
|
|
|
|
2015-03-04 16:21:05 +00:00
|
|
|
bool hr_seek_very_exact = seek.exact == MPSEEK_VERY_EXACT;
|
player: clamp relative seek base time to nominal duration
Since b74c09efbf7, audio-only files let you seek to arbitrary points
beyond the end of the file (but still displayed the time clamped to the
nominal file duration). This was confusing and just not wanted. The
reason is probably that the commit removed setting the audio PTS for
data before the seek target, so if you seek past the end of the file,
the audio PTS is never set. This in turn means the logic to determine
the current playback time has no PTS at all, and thus falls back to the
seek PTS.
This happened in the past for other reasons (like efe43d768f). I have
enough of this, so I'm just changing the code to clamp the seek
timestamp to a "known" range. Do this when seeking ends, because in the
fallback case, the playback time shouldn't be stuck at e.g. "end +
seek_argument". Also do it when initiating a new seek (mp_seek), because
if the previous seek hasn't finished yet, it shouldn't add them up and
allow it to go "out of range" either. The latter is especially relevant
for hr-seeks.
Doing this clamping is problematic because the duration is a possibly
invalid value from the demuxer, or just missing. Especially with
timestamp resets, fun sometimes happens, and in these situations it
might be better not to clamp.
One could argue you should just use the last audio timestamp returned by
the decoder or demuxer (even if that directly conflicts with --end), but
that sounds even more hairy.
In summary: what a dumb waste of time, what the fuck.
2020-09-10 21:24:35 +00:00
|
|
|
double current_time = get_playback_time(mpctx);
|
2016-10-20 18:43:17 +00:00
|
|
|
if (current_time == MP_NOPTS_VALUE && seek.type == MPSEEK_RELATIVE)
|
|
|
|
return;
|
2016-02-28 18:43:07 +00:00
|
|
|
if (current_time == MP_NOPTS_VALUE)
|
|
|
|
current_time = 0;
|
|
|
|
double seek_pts = MP_NOPTS_VALUE;
|
|
|
|
int demux_flags = 0;
|
2015-08-21 13:37:07 +00:00
|
|
|
|
|
|
|
switch (seek.type) {
|
|
|
|
case MPSEEK_ABSOLUTE:
|
2016-02-28 18:43:07 +00:00
|
|
|
seek_pts = seek.amount;
|
2015-08-21 13:37:07 +00:00
|
|
|
break;
|
2016-01-12 22:49:00 +00:00
|
|
|
case MPSEEK_BACKSTEP:
|
2016-02-28 18:43:07 +00:00
|
|
|
seek_pts = current_time;
|
|
|
|
hr_seek_very_exact = true;
|
2016-01-12 22:49:00 +00:00
|
|
|
break;
|
2015-08-21 13:37:07 +00:00
|
|
|
case MPSEEK_RELATIVE:
|
2017-10-23 17:05:39 +00:00
|
|
|
demux_flags = seek.amount > 0 ? SEEK_FORWARD : 0;
|
2016-02-28 18:43:07 +00:00
|
|
|
seek_pts = current_time + seek.amount;
|
2015-08-21 13:37:07 +00:00
|
|
|
break;
|
|
|
|
case MPSEEK_FACTOR: ;
|
|
|
|
double len = get_time_length(mpctx);
|
|
|
|
if (len >= 0)
|
2016-02-28 18:43:07 +00:00
|
|
|
seek_pts = seek.amount * len;
|
2015-08-21 13:37:07 +00:00
|
|
|
break;
|
2023-01-10 18:26:51 +00:00
|
|
|
default: MP_ASSERT_UNREACHABLE();
|
2015-08-21 13:37:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-28 18:43:07 +00:00
|
|
|
double demux_pts = seek_pts;
|
|
|
|
|
2020-02-29 23:28:09 +00:00
|
|
|
bool hr_seek = seek.exact != MPSEEK_KEYFRAME && seek_pts != MP_NOPTS_VALUE &&
|
player: dumb seeking related stuff, make audio hr-seek default
Try to deal with various corner cases. But when I fix one thing, another
thing breaks. (And it's 50/50 whether I find the breakage immediately or
a few months later.) So results may vary.
The default for--hr-seek is changed to "default" (not creative enough to
find a better name). In this mode, audio seeking is exact if there is no
video, or if the video has only a single frame. This change is actually
pretty dumb, since audio frames are usually small enough that exact
seeking does not really add much. But it gets rid of some weird special
cases.
Internally, the most important change is that is_coverart and is_sparse
handling is merged. is_sparse was originally just a special case for
weird .ts streams that have the corresponding low-level flag set. The
idea is that they're pretty similar anyway, so this would reduce the
number of corner cases. But I'm not sure if this doesn't break the
original intended use case for it (I don't have a sample anyway).
This changes last-frame handling, and respects the duration of the last
frame only if audio is disabled. This is mostly "coincidental" due to
the need to make seeking past EOF trigger player exit, and is caused by
setting STATUS_EOF early. On the other hand, this might have been this
way before (see removed chunk close to it).
2020-02-28 16:15:07 +00:00
|
|
|
(seek.exact >= MPSEEK_EXACT || opts->hr_seek == 1 ||
|
|
|
|
(opts->hr_seek >= 0 && seek.type == MPSEEK_ABSOLUTE) ||
|
|
|
|
(opts->hr_seek == 2 && (!mpctx->vo_chain || mpctx->vo_chain->is_sparse)));
|
2016-02-28 18:43:07 +00:00
|
|
|
|
|
|
|
// Under certain circumstances, prefer SEEK_FACTOR.
|
|
|
|
if (seek.type == MPSEEK_FACTOR && !hr_seek &&
|
|
|
|
(mpctx->demuxer->ts_resets_possible || seek_pts == MP_NOPTS_VALUE))
|
2015-08-21 13:37:07 +00:00
|
|
|
{
|
2016-02-28 18:43:07 +00:00
|
|
|
demux_pts = seek.amount;
|
|
|
|
demux_flags |= SEEK_FACTOR;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
2015-08-21 13:37:07 +00:00
|
|
|
|
2019-05-24 00:30:44 +00:00
|
|
|
int play_dir = opts->play_dir;
|
|
|
|
if (play_dir < 0)
|
|
|
|
demux_flags |= SEEK_SATAN;
|
|
|
|
|
2016-02-28 18:43:07 +00:00
|
|
|
if (hr_seek) {
|
|
|
|
double hr_seek_offset = opts->hr_seek_demuxer_offset;
|
|
|
|
// Always try to compensate for possibly bad demuxers in "special"
|
|
|
|
// situations where we need more robustness from the hr-seek code, even
|
|
|
|
// if the user doesn't use --hr-seek-demuxer-offset.
|
|
|
|
// The value is arbitrary, but should be "good enough" in most situations.
|
|
|
|
if (hr_seek_very_exact)
|
|
|
|
hr_seek_offset = MPMAX(hr_seek_offset, 0.5); // arbitrary
|
2016-08-04 09:26:57 +00:00
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
double offset = 0;
|
|
|
|
if (!mpctx->tracks[n]->is_external)
|
|
|
|
offset += get_track_seek_offset(mpctx, mpctx->tracks[n]);
|
|
|
|
hr_seek_offset = MPMAX(hr_seek_offset, -offset);
|
|
|
|
}
|
2019-05-25 15:33:48 +00:00
|
|
|
demux_pts -= hr_seek_offset * play_dir;
|
2017-10-23 17:05:39 +00:00
|
|
|
demux_flags = (demux_flags | SEEK_HR) & ~SEEK_FORWARD;
|
2019-05-24 00:30:44 +00:00
|
|
|
// For HR seeks in backward playback mode, the correct seek rounding
|
|
|
|
// direction is forward instead of backward.
|
|
|
|
if (play_dir < 0)
|
|
|
|
demux_flags |= SEEK_FORWARD;
|
2015-01-19 19:45:31 +00:00
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
|
player: allow seeking in cached parts of unseekable streams
Before this change and before the seekable stream cache became a thing,
we could possibly seek using the stream cache. But we couldn't know
whether the seek would succeed. We knew the available byte range, but
could in general not tell whether a demuxer would stay within the range
when trying to seek to a specific time position. We preferred to have
safe defaults, so seeking in streams that were detected as unseekable
were not honored. We allowed overriding this via --force-seekable=yes,
in which case it depended on your luck whether the seek would work, or
the player crapped its pants.
With the demuxer packet cache, we can tell exactly whether a seek will
work (at least if there's only 1 seek range). We can just let seeks go
through. Everything to allow this is already in place, and this commit
just moves around some minor things.
Note that the demux_seek() return value was not used before, because low
level (i.e. network level) seeks are usually asynchronous, and if they
fail, the state is pretty much undefined. We simply repurpose the return
value to signal whether cache seeking worked. If it didn't, we can just
resume playback normally, because demuxing continues unaffected, and no
decoder are reset.
This should be particularly helpful to people who for some reason stream
data into stdin via streamlink and such.
2017-12-23 21:28:08 +00:00
|
|
|
if (!mpctx->demuxer->seekable)
|
|
|
|
demux_flags |= SEEK_CACHED;
|
|
|
|
|
player: add optional separate video decoding thread
See manpage additions. This has been a topic in MPlayer/mplayer2/mpv
since forever. But since libavcodec multi-threaded decoding was added,
I've always considered this pointless. libavcodec requires you to
"preload" it with packets, and then you can pretty much avoid blocking
on it, if decoding is fast enough.
But in some cases, a decoupled decoder thread _might_ help. Users have
for example come up with cases where decoding video in a separate
process and piping it as raw video to mpv helped. (Or my memory is
false, and it was about vapoursynth filtering, who knows.) So let's just
see whether this helps with anything.
Note that this would have been _much_ easier if libavcodec had an
asynchronous (or rather, non-blocking) API. It could probably have
easily gained that with a small change to its multi-threading code and a
small extension to its API, but I guess not.
Unfortunately, this uglifies f_decoder_wrapper quite a lot. Part of this
is due to annoying corner cases like legacy frame dropping and hardware
decoder state. These could probably be prettified later on.
There is also a change in playloop.c: this is because there is a need to
coordinate playback resets between demuxer thread, decoder thread, and
playback logic. I think this SEEK_BLOCK idea worked out reasonably well.
There are still a number of problems. For example, if the demuxer cache
is full, the decoder thread will simply block hard until the output
queue is full, which interferes with seeking. Could also be improved
later. Hardware decoding will probably die in a fire, because it will
run out of surfaces quickly. We could reduce the queue to size 1...
maybe later. We could update the queue options at runtime easily, but
currently I'm not going to bother.
I could only have put the lavc wrapper itself on a separate thread. But
there is some annoying interaction with EDL and backward playback shit,
and also you would have had to loop demuxer packets through the
playloop, so this sounded less annoying.
The food my mother made for us today was delicious.
Because audio uses the same code, also for audio (even if completely
pointless).
Fixes: #6926
2020-02-29 20:40:52 +00:00
|
|
|
demux_flags |= SEEK_BLOCK;
|
|
|
|
|
player: allow seeking in cached parts of unseekable streams
Before this change and before the seekable stream cache became a thing,
we could possibly seek using the stream cache. But we couldn't know
whether the seek would succeed. We knew the available byte range, but
could in general not tell whether a demuxer would stay within the range
when trying to seek to a specific time position. We preferred to have
safe defaults, so seeking in streams that were detected as unseekable
were not honored. We allowed overriding this via --force-seekable=yes,
in which case it depended on your luck whether the seek would work, or
the player crapped its pants.
With the demuxer packet cache, we can tell exactly whether a seek will
work (at least if there's only 1 seek range). We can just let seeks go
through. Everything to allow this is already in place, and this commit
just moves around some minor things.
Note that the demux_seek() return value was not used before, because low
level (i.e. network level) seeks are usually asynchronous, and if they
fail, the state is pretty much undefined. We simply repurpose the return
value to signal whether cache seeking worked. If it didn't, we can just
resume playback normally, because demuxing continues unaffected, and no
decoder are reset.
This should be particularly helpful to people who for some reason stream
data into stdin via streamlink and such.
2017-12-23 21:28:08 +00:00
|
|
|
if (!demux_seek(mpctx->demuxer, demux_pts, demux_flags)) {
|
|
|
|
if (!mpctx->demuxer->seekable) {
|
2018-01-17 06:07:15 +00:00
|
|
|
MP_ERR(mpctx, "Cannot seek in this stream.\n");
|
player: allow seeking in cached parts of unseekable streams
Before this change and before the seekable stream cache became a thing,
we could possibly seek using the stream cache. But we couldn't know
whether the seek would succeed. We knew the available byte range, but
could in general not tell whether a demuxer would stay within the range
when trying to seek to a specific time position. We preferred to have
safe defaults, so seeking in streams that were detected as unseekable
were not honored. We allowed overriding this via --force-seekable=yes,
in which case it depended on your luck whether the seek would work, or
the player crapped its pants.
With the demuxer packet cache, we can tell exactly whether a seek will
work (at least if there's only 1 seek range). We can just let seeks go
through. Everything to allow this is already in place, and this commit
just moves around some minor things.
Note that the demux_seek() return value was not used before, because low
level (i.e. network level) seeks are usually asynchronous, and if they
fail, the state is pretty much undefined. We simply repurpose the return
value to signal whether cache seeking worked. If it didn't, we can just
resume playback normally, because demuxing continues unaffected, and no
decoder are reset.
This should be particularly helpful to people who for some reason stream
data into stdin via streamlink and such.
2017-12-23 21:28:08 +00:00
|
|
|
MP_ERR(mpctx, "You can force it with '--force-seekable=yes'.\n");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
mpctx->play_dir = play_dir;
|
|
|
|
|
2013-12-24 10:08:26 +00:00
|
|
|
// Seek external, extra files too:
|
2013-12-23 19:14:54 +00:00
|
|
|
for (int t = 0; t < mpctx->num_tracks; t++) {
|
|
|
|
struct track *track = mpctx->tracks[t];
|
2013-12-24 10:08:26 +00:00
|
|
|
if (track->selected && track->is_external && track->demuxer) {
|
2016-02-28 18:43:07 +00:00
|
|
|
double main_new_pos = demux_pts;
|
2016-08-04 09:26:57 +00:00
|
|
|
if (!hr_seek || track->is_external)
|
|
|
|
main_new_pos += get_track_seek_offset(mpctx, track);
|
2016-02-28 18:43:07 +00:00
|
|
|
if (demux_flags & SEEK_FACTOR)
|
|
|
|
main_new_pos = seek_pts;
|
player: add optional separate video decoding thread
See manpage additions. This has been a topic in MPlayer/mplayer2/mpv
since forever. But since libavcodec multi-threaded decoding was added,
I've always considered this pointless. libavcodec requires you to
"preload" it with packets, and then you can pretty much avoid blocking
on it, if decoding is fast enough.
But in some cases, a decoupled decoder thread _might_ help. Users have
for example come up with cases where decoding video in a separate
process and piping it as raw video to mpv helped. (Or my memory is
false, and it was about vapoursynth filtering, who knows.) So let's just
see whether this helps with anything.
Note that this would have been _much_ easier if libavcodec had an
asynchronous (or rather, non-blocking) API. It could probably have
easily gained that with a small change to its multi-threading code and a
small extension to its API, but I guess not.
Unfortunately, this uglifies f_decoder_wrapper quite a lot. Part of this
is due to annoying corner cases like legacy frame dropping and hardware
decoder state. These could probably be prettified later on.
There is also a change in playloop.c: this is because there is a need to
coordinate playback resets between demuxer thread, decoder thread, and
playback logic. I think this SEEK_BLOCK idea worked out reasonably well.
There are still a number of problems. For example, if the demuxer cache
is full, the decoder thread will simply block hard until the output
queue is full, which interferes with seeking. Could also be improved
later. Hardware decoding will probably die in a fire, because it will
run out of surfaces quickly. We could reduce the queue to size 1...
maybe later. We could update the queue options at runtime easily, but
currently I'm not going to bother.
I could only have put the lavc wrapper itself on a separate thread. But
there is some annoying interaction with EDL and backward playback shit,
and also you would have had to loop demuxer packets through the
playloop, so this sounded less annoying.
The food my mother made for us today was delicious.
Because audio uses the same code, also for audio (even if completely
pointless).
Fixes: #6926
2020-02-29 20:40:52 +00:00
|
|
|
demux_seek(track->demuxer, main_new_pos,
|
|
|
|
demux_flags & (SEEK_SATAN | SEEK_BLOCK));
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-18 18:40:23 +00:00
|
|
|
if (!(seek.flags & MPSEEK_FLAG_NOFLUSH))
|
|
|
|
clear_audio_output_buffers(mpctx);
|
|
|
|
|
2014-07-30 21:01:55 +00:00
|
|
|
reset_playback_state(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
player: add optional separate video decoding thread
See manpage additions. This has been a topic in MPlayer/mplayer2/mpv
since forever. But since libavcodec multi-threaded decoding was added,
I've always considered this pointless. libavcodec requires you to
"preload" it with packets, and then you can pretty much avoid blocking
on it, if decoding is fast enough.
But in some cases, a decoupled decoder thread _might_ help. Users have
for example come up with cases where decoding video in a separate
process and piping it as raw video to mpv helped. (Or my memory is
false, and it was about vapoursynth filtering, who knows.) So let's just
see whether this helps with anything.
Note that this would have been _much_ easier if libavcodec had an
asynchronous (or rather, non-blocking) API. It could probably have
easily gained that with a small change to its multi-threading code and a
small extension to its API, but I guess not.
Unfortunately, this uglifies f_decoder_wrapper quite a lot. Part of this
is due to annoying corner cases like legacy frame dropping and hardware
decoder state. These could probably be prettified later on.
There is also a change in playloop.c: this is because there is a need to
coordinate playback resets between demuxer thread, decoder thread, and
playback logic. I think this SEEK_BLOCK idea worked out reasonably well.
There are still a number of problems. For example, if the demuxer cache
is full, the decoder thread will simply block hard until the output
queue is full, which interferes with seeking. Could also be improved
later. Hardware decoding will probably die in a fire, because it will
run out of surfaces quickly. We could reduce the queue to size 1...
maybe later. We could update the queue options at runtime easily, but
currently I'm not going to bother.
I could only have put the lavc wrapper itself on a separate thread. But
there is some annoying interaction with EDL and backward playback shit,
and also you would have had to loop demuxer packets through the
playloop, so this sounded less annoying.
The food my mother made for us today was delicious.
Because audio uses the same code, also for audio (even if completely
pointless).
Fixes: #6926
2020-02-29 20:40:52 +00:00
|
|
|
demux_block_reading(mpctx->demuxer, false);
|
|
|
|
for (int t = 0; t < mpctx->num_tracks; t++) {
|
|
|
|
struct track *track = mpctx->tracks[t];
|
|
|
|
if (track->selected && track->demuxer)
|
|
|
|
demux_block_reading(track->demuxer, false);
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
/* Use the target time as "current position" for further relative
|
|
|
|
* seeks etc until a new video frame has been decoded */
|
2016-02-28 18:43:07 +00:00
|
|
|
mpctx->last_seek_pts = seek_pts;
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2016-02-15 20:03:51 +00:00
|
|
|
if (hr_seek) {
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->hrseek_active = true;
|
2016-02-28 18:43:07 +00:00
|
|
|
mpctx->hrseek_backstep = seek.type == MPSEEK_BACKSTEP;
|
Implement backwards playback
See manpage additions. This is a huge hack. You can bet there are shit
tons of bugs. It's literally forcing square pegs into round holes.
Hopefully, the manpage wall of text makes it clear enough that the whole
shit can easily crash and burn. (Although it shouldn't literally crash.
That would be a bug. It possibly _could_ start a fire by entering some
sort of endless loop, not a literal one, just something where it tries
to do work without making progress.)
(Some obvious bugs I simply ignored for this initial version, but
there's a number of potential bugs I can't even imagine. Normal playback
should remain completely unaffected, though.)
How this works is also described in the manpage. Basically, we demux in
reverse, then we decode in reverse, then we render in reverse.
The decoding part is the simplest: just reorder the decoder output. This
weirdly integrates with the timeline/ordered chapter code, which also
has special requirements on feeding the packets to the decoder in a
non-straightforward way (it doesn't conflict, although a bugmessmass
breaks correct slicing of segments, so EDL/ordered chapter playback is
broken in backward direction).
Backward demuxing is pretty involved. In theory, it could be much
easier: simply iterating the usual demuxer output backward. But this
just doesn't fit into our code, so there's a cthulhu nightmare of shit.
To be specific, each stream (audio, video) is reversed separately. At
least this means we can do backward playback within cached content (for
example, you could play backwards in a live stream; on that note, it
disables prefetching, which would lead to losing new live video, but
this could be avoided).
The fuckmess also meant that I didn't bother trying to support
subtitles. Subtitles are a problem because they're "sparse" streams.
They need to be "passively" demuxed: you don't try to read a subtitle
packet, you demux audio and video, and then look whether there was a
subtitle packet. This means to get subtitles for a time range, you need
to know that you demuxed video and audio over this range, which becomes
pretty messy when you demux audio and video backwards separately.
Backward display is the most weird (and potentially buggy) part. To
avoid that we need to touch a LOT of timing code, we negate all
timestamps. The basic idea is that due to the navigation, all
comparisons and subtractions of timestamps keep working, and you don't
need to touch every single of them to "reverse" them.
E.g.:
bool before = pts_a < pts_b;
would need to be:
bool before = forward
? pts_a < pts_b
: pts_a > pts_b;
or:
bool before = pts_a * dir < pts_b * dir;
or if you, as it's implemented now, just do this after decoding:
pts_a *= dir;
pts_b *= dir;
and then in the normal timing/renderer code:
bool before = pts_a < pts_b;
Consequently, we don't need many changes in the latter code. But some
assumptions inhererently true for forward playback may have been broken
anyway. What is mainly needed is fixing places where values are passed
between positive and negative "domains". For example, seeking and
timestamp user display always uses positive timestamps. The main mess is
that it's not obvious which domain a given variable should or does use.
Well, in my tests with a single file, it suddenly started to work when I
did this. I'm honestly surprised that it did, and that I didn't have to
change a single line in the timing code past decoder (just something
minor to make external/cached text subtitles display). I committed it
immediately while avoiding thinking about it. But there really likely
are subtle problems of all sorts.
As far as I'm aware, gstreamer also supports backward playback. When I
looked at this years ago, I couldn't find a way to actually try this,
and I didn't revisit it now. Back then I also read talk slides from the
person who implemented it, and I'm not sure if and which ideas I might
have taken from it. It's possible that the timestamp reversal is
inspired by it, but I didn't check. (I think it claimed that it could
avoid large changes by changing a sign?)
VapourSynth has some sort of reverse function, which provides a backward
view on a video. The function itself is trivial to implement, as
VapourSynth aims to provide random access to video by frame numbers (so
you just request decreasing frame numbers). From what I remember, it
wasn't exactly fluid, but it worked. It's implemented by creating an
index, and seeking to the target on demand, and a bunch of caching. mpv
could use it, but it would either require using VapourSynth as demuxer
and decoder for everything, or replacing the current file every time
something is supposed to be played backwards.
FFmpeg's libavfilter has reversal filters for audio and video. These
require buffering the entire media data of the file, and don't really
fit into mpv's architecture. It could be used by playing a libavfilter
graph that also demuxes, but that's like VapourSynth but worse.
2019-05-18 00:10:51 +00:00
|
|
|
mpctx->hrseek_pts = seek_pts * mpctx->play_dir;
|
2015-06-18 20:31:55 +00:00
|
|
|
|
video: make decoder wrapper a filter
Move dec_video.c to filters/f_decoder_wrapper.c. It essentially becomes
a source filter. vd.h mostly disappears, because mp_filter takes care of
the dataflow, but its remains are in struct mp_decoder_fns.
One goal is to simplify dataflow by letting the filter framework handle
it (or more accurately, using its conventions). One result is that the
decode calls disappear from video.c, because we simply connect the
decoder wrapper and the filter chain with mp_pin_connect().
Another goal is to eventually remove the code duplication between the
audio and video paths for this. This commit prepares for this by trying
to make f_decoder_wrapper.c extensible, so it can be used for audio as
well later.
Decoder framedropping changes a bit. It doesn't seem to be worse than
before, and it's an obscure feature, so I'm content with its new state.
Some special code that was apparently meant to avoid dropping too many
frames in a row is removed, though.
I'm not sure how the source code tree should be organized. For one,
video/decode/vd_lavc.c is the only file in its directory, which is a bit
annoying.
2018-01-28 09:08:45 +00:00
|
|
|
// allow decoder to drop frames before hrseek_pts
|
|
|
|
bool hrseek_framedrop = !hr_seek_very_exact && opts->hr_seek_framedrop;
|
|
|
|
|
2016-01-12 22:49:00 +00:00
|
|
|
MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s%s\n", mpctx->hrseek_pts,
|
video: make decoder wrapper a filter
Move dec_video.c to filters/f_decoder_wrapper.c. It essentially becomes
a source filter. vd.h mostly disappears, because mp_filter takes care of
the dataflow, but its remains are in struct mp_decoder_fns.
One goal is to simplify dataflow by letting the filter framework handle
it (or more accurately, using its conventions). One result is that the
decode calls disappear from video.c, because we simply connect the
decoder wrapper and the filter chain with mp_pin_connect().
Another goal is to eventually remove the code duplication between the
audio and video paths for this. This commit prepares for this by trying
to make f_decoder_wrapper.c extensible, so it can be used for audio as
well later.
Decoder framedropping changes a bit. It doesn't seem to be worse than
before, and it's an obscure feature, so I'm content with its new state.
Some special code that was apparently meant to avoid dropping too many
frames in a row is removed, though.
I'm not sure how the source code tree should be organized. For one,
video/decode/vd_lavc.c is the only file in its directory, which is a bit
annoying.
2018-01-28 09:08:45 +00:00
|
|
|
hrseek_framedrop ? "" : " (no framedrop)",
|
2016-01-12 22:49:00 +00:00
|
|
|
mpctx->hrseek_backstep ? " (backstep)" : "");
|
video: make decoder wrapper a filter
Move dec_video.c to filters/f_decoder_wrapper.c. It essentially becomes
a source filter. vd.h mostly disappears, because mp_filter takes care of
the dataflow, but its remains are in struct mp_decoder_fns.
One goal is to simplify dataflow by letting the filter framework handle
it (or more accurately, using its conventions). One result is that the
decode calls disappear from video.c, because we simply connect the
decoder wrapper and the filter chain with mp_pin_connect().
Another goal is to eventually remove the code duplication between the
audio and video paths for this. This commit prepares for this by trying
to make f_decoder_wrapper.c extensible, so it can be used for audio as
well later.
Decoder framedropping changes a bit. It doesn't seem to be worse than
before, and it's an obscure feature, so I'm content with its new state.
Some special code that was apparently meant to avoid dropping too many
frames in a row is removed, though.
I'm not sure how the source code tree should be organized. For one,
video/decode/vd_lavc.c is the only file in its directory, which is a bit
annoying.
2018-01-28 09:08:45 +00:00
|
|
|
|
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *track = mpctx->tracks[n];
|
|
|
|
struct mp_decoder_wrapper *dec = track->dec;
|
|
|
|
if (dec && hrseek_framedrop)
|
|
|
|
mp_decoder_wrapper_set_start_pts(dec, mpctx->hrseek_pts);
|
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-02-28 18:43:07 +00:00
|
|
|
if (mpctx->stop_play == AT_END_OF_FILE)
|
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->start_timestamp = mp_time_sec();
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2014-07-30 21:01:55 +00:00
|
|
|
mp_notify(mpctx, MPV_EVENT_SEEK, NULL);
|
2014-07-28 22:07:54 +00:00
|
|
|
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
|
player: gross hack to improve non-hr seeking with external audio tracks
Relative seeks backwards with external audio tracks does not always work
well: it tends to happen that video seek back further than audio, so
audio will remain silent until the audio's after-seek position is
reached. This happens because we strictly seek both video and audio
demuxer to the approximate desirted target PTS, and then start decoding
from that.
Commit 81358380 removes an older method that was supposed to deal with
this. It was sort of bad, because it could lead to the playback core
freezing by waiting on network.
Ideally, the demuxer layer would probably somehow deal with such seeks,
and do them in a way the audio is seeked after video. Currently this is
infeasible, because the demuxer layer assumes a single demuxer, and
external tracks simply use separate demuxer layers. (MPlayer actually
had a pseudo-demuxer that joined external tracks into a single demuxer,
but this is not flexible enough - and also, the demuxer layer as it
currently exists can't deal with dynamically removing external tracks
either. Maybe some time in the future.)
Instead, add a gross hack, that essentially reseeks the audio if it
detects that it's too far off. The result is actually not too bad,
because we can reuse the mechanism that is used for instant track
switching. This way we can make sure of the right position, without
having to care about certain other issues.
It should be noted that if the audio demuxer is used for other tracks
too, and the demuxer does not support refresh seeking, audio will
probably be off by even a higher amount. But this should be rare.
2016-08-07 14:29:13 +00:00
|
|
|
|
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it
makes it work with backward playback mode.
The most obvious change is that the both the A and B point need to be
set now before any looping happens. Unlike before, unset points don't
implicitly use the start or end of the file. I think the old behavior
was a feature that was explicitly added/wanted. Well, it's gone now.
This is because of 2 reasons:
1. I never liked this feature, and it always got in my way (as user).
2. It's inherently annoying with backward playback mode.
In backward playback mode, the user wants to set A/B in the wrong order.
The ab-loop command will first set A, then B, so if you use this command
during backward playback, A will be set to a higher timestamps than B.
If you switch back to forward playback mode, the loop would stop
working. I want the loop to just continue to work, and the chosen
solution conflicts with the removed feature.
The order issue above _could_ be fixed by also switching the AB-loop
user option values around on direction switch. But there are no other
instances of option changes magically affecting other options, and doing
this would probably lead to unexpected misery (dying from corner cases
and such).
Another solution is sorting the A/B points by timestamps after copying
them from the user options. Then A/B options set in backward mode will
work in forward mode. This is the chosen solution. If you sort the
points, you don't know anymore whether the unset point is supposed to
signify the end or the start of the file.
The AB-loop code is slightly better abstracted now, so it should be easy
to restore the removed feature. It would still require coming up with a
solution for backwards playback, though.
A minor change is that if one point is set and the other is unset, I'm
rendering both the chapter markers and the marker for the set point.
Why? I don't know. My test file had chapters, and I guess I decided this
looked better.
This commit also fixes some subtle and obvious issues that I already
forgot about when I wrote this commit message. It cleans up some minor
code duplication and nonsense too.
Regarding backward playback, the code uses an unsanitary mix of internal
("transformed") and user timestamps. So the play_dir variable appears
more than usual.
To mention one unfixed issue: if you set an AB-loop that is completely
past the end of the file, it will get stuck in an infinite seeking loop
once playback reaches the end of the file. Fixing this reliably seemed
annoying, so the fix is "just don't do this". It's not a hard freeze
anyway.
2019-05-26 23:24:22 +00:00
|
|
|
update_ab_loop_clip(mpctx);
|
2017-08-12 21:08:48 +00:00
|
|
|
|
2017-08-14 12:02:13 +00:00
|
|
|
mpctx->current_seek = seek;
|
2024-01-21 23:14:39 +00:00
|
|
|
redraw_subs(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
player: handle seek delays differently
The code removed from handle_input_and_seek_coalesce() did two things:
1. If there's a queued seek, stop accepting non-seek commands, and delay
them to the next playloop iteration.
2. If a seek is executing (i.e. the seek was unqueued, and now it's
trying to decode and display the first video frame), stop accepting
seek commands (and in fact all commands that were queued after the
first seek command). This logic is disabled if seeking started longer
than 300ms ago. (To avoid starvation.)
I'm not sure why 1. would be needed. It's still possible that a command
immediately executed after a seek command sees a "seeking in progress"
state, because it affects queued seeks only, and not seeks in progress.
Drop this code, since it can easily lead to input starvation, and I'm
not aware of any disadvantages.
The logic in 2. is good to make seeking behave much better, as it
guarantees that the video display is updated frequently. Keep the core
idea, but implement it differently. Now this logic is applied to seeks
only. Commands after the seek can execute freely, and like with 1., I
don't see a reason why they couldn't. However, in some cases, seeks are
supposed to be executed instantly, so queue_seek() needs an additional
parameter to signal the need for immediate update.
One nice thing is that commands like sub_seek automatically profit from
the seek delay logic. On the other hand, hitting chapter seek multiple
times still does not update the video on chapter boundaries (as it
should be).
Note that the main goal of this commit is actually simplification of the
input processing logic and to allow all commands to be executed
immediately.
2014-02-07 21:29:50 +00:00
|
|
|
// This combines consecutive seek requests.
|
2013-10-29 21:38:29 +00:00
|
|
|
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
|
2016-08-15 19:07:32 +00:00
|
|
|
enum seek_precision exact, int flags)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
|
|
|
struct seek_params *seek = &mpctx->seek;
|
2015-06-16 21:07:46 +00:00
|
|
|
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
switch (type) {
|
|
|
|
case MPSEEK_RELATIVE:
|
2016-08-15 19:07:32 +00:00
|
|
|
seek->flags |= flags;
|
2013-10-29 21:38:29 +00:00
|
|
|
if (seek->type == MPSEEK_FACTOR)
|
|
|
|
return; // Well... not common enough to bother doing better
|
|
|
|
seek->amount += amount;
|
|
|
|
seek->exact = MPMAX(seek->exact, exact);
|
|
|
|
if (seek->type == MPSEEK_NONE)
|
|
|
|
seek->exact = exact;
|
|
|
|
if (seek->type == MPSEEK_ABSOLUTE)
|
|
|
|
return;
|
|
|
|
seek->type = MPSEEK_RELATIVE;
|
|
|
|
return;
|
|
|
|
case MPSEEK_ABSOLUTE:
|
|
|
|
case MPSEEK_FACTOR:
|
2016-01-12 22:49:00 +00:00
|
|
|
case MPSEEK_BACKSTEP:
|
2023-09-27 12:00:14 +00:00
|
|
|
case MPSEEK_CHAPTER:
|
2013-10-29 21:38:29 +00:00
|
|
|
*seek = (struct seek_params) {
|
|
|
|
.type = type,
|
|
|
|
.amount = amount,
|
|
|
|
.exact = exact,
|
2016-08-15 19:07:32 +00:00
|
|
|
.flags = flags,
|
2013-10-29 21:38:29 +00:00
|
|
|
};
|
|
|
|
return;
|
|
|
|
case MPSEEK_NONE:
|
|
|
|
*seek = (struct seek_params){ 0 };
|
|
|
|
return;
|
|
|
|
}
|
2023-01-10 18:26:51 +00:00
|
|
|
MP_ASSERT_UNREACHABLE();
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void execute_queued_seek(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->seek.type) {
|
2019-12-14 13:15:57 +00:00
|
|
|
bool queued_hr_seek = mpctx->seek.exact != MPSEEK_KEYFRAME;
|
2014-07-29 17:59:56 +00:00
|
|
|
// Let explicitly imprecise seeks cancel precise seeks:
|
2019-12-14 13:15:57 +00:00
|
|
|
if (mpctx->hrseek_active && !queued_hr_seek)
|
2014-07-29 17:59:56 +00:00
|
|
|
mpctx->start_timestamp = -1e9;
|
2019-12-14 13:15:57 +00:00
|
|
|
// If the user seeks continuously (keeps arrow key down) try to finish
|
|
|
|
// showing a frame from one location before doing another seek (instead
|
|
|
|
// of never updating the screen).
|
|
|
|
if ((mpctx->seek.flags & MPSEEK_FLAG_DELAY) &&
|
player: handle seek delays differently
The code removed from handle_input_and_seek_coalesce() did two things:
1. If there's a queued seek, stop accepting non-seek commands, and delay
them to the next playloop iteration.
2. If a seek is executing (i.e. the seek was unqueued, and now it's
trying to decode and display the first video frame), stop accepting
seek commands (and in fact all commands that were queued after the
first seek command). This logic is disabled if seeking started longer
than 300ms ago. (To avoid starvation.)
I'm not sure why 1. would be needed. It's still possible that a command
immediately executed after a seek command sees a "seeking in progress"
state, because it affects queued seeks only, and not seeks in progress.
Drop this code, since it can easily lead to input starvation, and I'm
not aware of any disadvantages.
The logic in 2. is good to make seeking behave much better, as it
guarantees that the video display is updated frequently. Keep the core
idea, but implement it differently. Now this logic is applied to seeks
only. Commands after the seek can execute freely, and like with 1., I
don't see a reason why they couldn't. However, in some cases, seeks are
supposed to be executed instantly, so queue_seek() needs an additional
parameter to signal the need for immediate update.
One nice thing is that commands like sub_seek automatically profit from
the seek delay logic. On the other hand, hitting chapter seek multiple
times still does not update the video on chapter boundaries (as it
should be).
Note that the main goal of this commit is actually simplification of the
input processing logic and to allow all commands to be executed
immediately.
2014-02-07 21:29:50 +00:00
|
|
|
mp_time_sec() - mpctx->start_timestamp < 0.3)
|
2019-12-14 13:15:57 +00:00
|
|
|
{
|
|
|
|
// Wait until a video frame is available and has been shown.
|
|
|
|
if (mpctx->video_status < STATUS_PLAYING)
|
|
|
|
return;
|
player: make repeated hr-seeks past EOF trigger EOF as expected
If you have a normal file with audio and video, and keep "spamming"
forward hr-seeks, the player just kept showing the last video frame
instead of exiting or playing the next file. This started happening
since commit 6bcda94cb. Although not a bug per se, it was odd, and very
user-noticable.
The main problem was that the pending seek command was processed before
the EOF was "noticed". Processing the command reset everything, so the
player did not terminate playback, but repeated the seek.
This commit restores the old behavior.
For one, it makes video return the correct status (video.c). The
parameter is a bit ugly, but better than duplicating the logic or having
another MPContext field. (As a minor detail, setting r=VD_EOF makes sure
have_new_frame() returns true, rather than going through another
iteration or whatever the hell will happen instead, which would clobber
logical_eof.)
Another thing is making the seek logic actually wait until the seek
outcome has been determined if audio is also active. Audio needs to wait
for video in order to get the video seek target position. (Which in turn
is because hr-seek still "snaps" to video frames. You can't seek in
between two frames, so audio can't just use the seek target, but always
has to wait on the timestamp of the video frame. This has other
disadvantages and is a misdesign, but not something I'll fix today.)
In theory, this might make hr-seeks less responsive, because it needs to
fully decode/filter the audio too, but in practice most time is spent on
video, which had to be fully decoded before this change. (In general,
hr-seek could probably just show a random frame when a queued hr-seek
overrides the current hr-seek, which would probably lead to a better
user experience, but that's out of scope.)
Fixes: #7206
2019-12-14 13:17:16 +00:00
|
|
|
// On A/V hr-seeks, always wait for the full result, to avoid corner
|
|
|
|
// cases when seeking past EOF (we want it to determine that EOF
|
|
|
|
// actually happened, instead of overwriting it with the new seek).
|
|
|
|
if (mpctx->hrseek_active && queued_hr_seek && mpctx->vo_chain &&
|
|
|
|
mpctx->ao_chain && !mpctx->restart_complete)
|
|
|
|
return;
|
2019-12-14 13:15:57 +00:00
|
|
|
}
|
2016-02-15 20:03:51 +00:00
|
|
|
mp_seek(mpctx, mpctx->seek);
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->seek = (struct seek_params){0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-24 12:58:57 +00:00
|
|
|
// NOPTS (i.e. <0) if unknown
|
2013-10-29 21:38:29 +00:00
|
|
|
double get_time_length(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
2017-11-24 12:58:57 +00:00
|
|
|
return demuxer && demuxer->duration >= 0 ? demuxer->duration : MP_NOPTS_VALUE;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 23:46:34 +00:00
|
|
|
// Return approximate PTS of first frame played. This can be completely wrong
|
|
|
|
// for a number of reasons in a number of situations.
|
|
|
|
double get_start_time(struct MPContext *mpctx, int dir)
|
|
|
|
{
|
|
|
|
double res = 0;
|
|
|
|
if (mpctx->demuxer) {
|
|
|
|
if (!mpctx->opts->rebase_start_time)
|
|
|
|
res += mpctx->demuxer->start_time;
|
|
|
|
if (dir < 0)
|
|
|
|
res += MPMAX(mpctx->demuxer->duration, 0);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
double get_current_time(struct MPContext *mpctx)
|
|
|
|
{
|
2020-09-10 21:47:59 +00:00
|
|
|
if (!mpctx->demuxer)
|
|
|
|
return MP_NOPTS_VALUE;
|
|
|
|
if (mpctx->playback_pts != MP_NOPTS_VALUE)
|
|
|
|
return mpctx->playback_pts * mpctx->play_dir;
|
|
|
|
return mpctx->last_seek_pts;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-29 17:27:46 +00:00
|
|
|
double get_playback_time(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
double cur = get_current_time(mpctx);
|
2015-04-28 20:03:18 +00:00
|
|
|
// During seeking, the time corresponds to the last seek time - apply some
|
|
|
|
// cosmetics to it.
|
2020-09-10 21:47:59 +00:00
|
|
|
if (cur != MP_NOPTS_VALUE && mpctx->playback_pts == MP_NOPTS_VALUE) {
|
2015-04-28 20:03:18 +00:00
|
|
|
double length = get_time_length(mpctx);
|
|
|
|
if (length >= 0)
|
2015-11-16 21:47:17 +00:00
|
|
|
cur = MPCLAMP(cur, 0, length);
|
2015-04-28 20:03:18 +00:00
|
|
|
}
|
2024-05-25 19:43:23 +00:00
|
|
|
// Force to 0 if this is not MP_NOPTS_VALUE.
|
|
|
|
if (cur != MP_NOPTS_VALUE && cur < 0)
|
|
|
|
cur = 0.0;
|
2015-11-16 21:47:17 +00:00
|
|
|
return cur;
|
2014-06-29 17:27:46 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
// Return playback position in 0.0-1.0 ratio, or -1 if unknown.
|
|
|
|
double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
|
|
|
|
{
|
|
|
|
struct demuxer *demuxer = mpctx->demuxer;
|
|
|
|
if (!demuxer)
|
|
|
|
return -1;
|
|
|
|
double ans = -1;
|
2015-11-16 21:47:17 +00:00
|
|
|
double start = 0;
|
2013-10-29 21:38:29 +00:00
|
|
|
double len = get_time_length(mpctx);
|
|
|
|
if (use_range) {
|
2017-12-04 01:26:42 +00:00
|
|
|
double startpos = get_play_start_pts(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
double endpos = get_play_end_pts(mpctx);
|
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it
makes it work with backward playback mode.
The most obvious change is that the both the A and B point need to be
set now before any looping happens. Unlike before, unset points don't
implicitly use the start or end of the file. I think the old behavior
was a feature that was explicitly added/wanted. Well, it's gone now.
This is because of 2 reasons:
1. I never liked this feature, and it always got in my way (as user).
2. It's inherently annoying with backward playback mode.
In backward playback mode, the user wants to set A/B in the wrong order.
The ab-loop command will first set A, then B, so if you use this command
during backward playback, A will be set to a higher timestamps than B.
If you switch back to forward playback mode, the loop would stop
working. I want the loop to just continue to work, and the chosen
solution conflicts with the removed feature.
The order issue above _could_ be fixed by also switching the AB-loop
user option values around on direction switch. But there are no other
instances of option changes magically affecting other options, and doing
this would probably lead to unexpected misery (dying from corner cases
and such).
Another solution is sorting the A/B points by timestamps after copying
them from the user options. Then A/B options set in backward mode will
work in forward mode. This is the chosen solution. If you sort the
points, you don't know anymore whether the unset point is supposed to
signify the end or the start of the file.
The AB-loop code is slightly better abstracted now, so it should be easy
to restore the removed feature. It would still require coming up with a
solution for backwards playback, though.
A minor change is that if one point is set and the other is unset, I'm
rendering both the chapter markers and the marker for the set point.
Why? I don't know. My test file had chapters, and I guess I decided this
looked better.
This commit also fixes some subtle and obvious issues that I already
forgot about when I wrote this commit message. It cleans up some minor
code duplication and nonsense too.
Regarding backward playback, the code uses an unsanitary mix of internal
("transformed") and user timestamps. So the play_dir variable appears
more than usual.
To mention one unfixed issue: if you set an AB-loop that is completely
past the end of the file, it will get stuck in an infinite seeking loop
once playback reaches the end of the file. Fixing this reliably seemed
annoying, so the fix is "just don't do this". It's not a hard freeze
anyway.
2019-05-26 23:24:22 +00:00
|
|
|
if (endpos > MPMAX(0, len))
|
2015-11-16 21:47:17 +00:00
|
|
|
endpos = MPMAX(0, len);
|
2013-10-29 21:38:29 +00:00
|
|
|
if (endpos < startpos)
|
|
|
|
endpos = startpos;
|
|
|
|
start = startpos;
|
|
|
|
len = endpos - startpos;
|
|
|
|
}
|
|
|
|
double pos = get_current_time(mpctx);
|
2015-01-14 21:14:20 +00:00
|
|
|
if (len > 0)
|
2013-10-29 21:38:29 +00:00
|
|
|
ans = MPCLAMP((pos - start) / len, 0, 1);
|
2019-09-21 15:03:57 +00:00
|
|
|
if (ans < 0) {
|
2018-09-07 19:58:46 +00:00
|
|
|
int64_t size = demuxer->filesize;
|
|
|
|
if (size > 0 && demuxer->filepos >= 0)
|
|
|
|
ans = MPCLAMP(demuxer->filepos / (double)size, 0, 1);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
if (use_range) {
|
|
|
|
if (mpctx->opts->play_frames > 0)
|
|
|
|
ans = MPMAX(ans, 1.0 -
|
|
|
|
mpctx->max_frames / (double) mpctx->opts->play_frames);
|
|
|
|
}
|
|
|
|
return ans;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -2 is no chapters, -1 is before first chapter
|
|
|
|
int get_current_chapter(struct MPContext *mpctx)
|
|
|
|
{
|
2014-03-25 01:10:24 +00:00
|
|
|
if (!mpctx->num_chapters)
|
|
|
|
return -2;
|
2013-10-29 21:38:29 +00:00
|
|
|
double current_pts = get_current_time(mpctx);
|
2014-03-25 01:10:24 +00:00
|
|
|
int i;
|
2014-07-31 20:54:57 +00:00
|
|
|
for (i = 0; i < mpctx->num_chapters; i++)
|
2014-11-02 16:20:04 +00:00
|
|
|
if (current_pts < mpctx->chapters[i].pts)
|
2014-03-25 01:10:24 +00:00
|
|
|
break;
|
2023-09-27 12:00:14 +00:00
|
|
|
return mpctx->last_chapter_flag ?
|
|
|
|
mpctx->last_chapter_seek : MPMAX(mpctx->last_chapter_seek, i - 1);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *chapter_display_name(struct MPContext *mpctx, int chapter)
|
|
|
|
{
|
|
|
|
char *name = chapter_name(mpctx, chapter);
|
2014-08-27 22:16:03 +00:00
|
|
|
char *dname = NULL;
|
2013-10-29 21:38:29 +00:00
|
|
|
if (name) {
|
|
|
|
dname = talloc_asprintf(NULL, "(%d) %s", chapter + 1, name);
|
|
|
|
} else if (chapter < -1) {
|
|
|
|
dname = talloc_strdup(NULL, "(unavailable)");
|
|
|
|
} else {
|
|
|
|
int chapter_count = get_chapter_count(mpctx);
|
|
|
|
if (chapter_count <= 0)
|
|
|
|
dname = talloc_asprintf(NULL, "(%d)", chapter + 1);
|
|
|
|
else
|
|
|
|
dname = talloc_asprintf(NULL, "(%d) of %d", chapter + 1,
|
|
|
|
chapter_count);
|
|
|
|
}
|
|
|
|
return dname;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns NULL if chapter name unavailable
|
|
|
|
char *chapter_name(struct MPContext *mpctx, int chapter)
|
|
|
|
{
|
2014-03-25 01:10:24 +00:00
|
|
|
if (chapter < 0 || chapter >= mpctx->num_chapters)
|
|
|
|
return NULL;
|
2015-08-12 09:10:45 +00:00
|
|
|
return mp_tags_get_str(mpctx->chapters[chapter].metadata, "title");
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 10:11:14 +00:00
|
|
|
// returns the start of the chapter in seconds (NOPTS if unavailable)
|
2013-10-29 21:38:29 +00:00
|
|
|
double chapter_start_time(struct MPContext *mpctx, int chapter)
|
|
|
|
{
|
|
|
|
if (chapter == -1)
|
2015-11-16 21:47:17 +00:00
|
|
|
return 0;
|
2014-03-25 01:10:24 +00:00
|
|
|
if (chapter >= 0 && chapter < mpctx->num_chapters)
|
2014-11-02 16:20:04 +00:00
|
|
|
return mpctx->chapters[chapter].pts;
|
2014-03-25 01:18:12 +00:00
|
|
|
return MP_NOPTS_VALUE;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int get_chapter_count(struct MPContext *mpctx)
|
|
|
|
{
|
2014-03-25 01:05:48 +00:00
|
|
|
return mpctx->num_chapters;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it
makes it work with backward playback mode.
The most obvious change is that the both the A and B point need to be
set now before any looping happens. Unlike before, unset points don't
implicitly use the start or end of the file. I think the old behavior
was a feature that was explicitly added/wanted. Well, it's gone now.
This is because of 2 reasons:
1. I never liked this feature, and it always got in my way (as user).
2. It's inherently annoying with backward playback mode.
In backward playback mode, the user wants to set A/B in the wrong order.
The ab-loop command will first set A, then B, so if you use this command
during backward playback, A will be set to a higher timestamps than B.
If you switch back to forward playback mode, the loop would stop
working. I want the loop to just continue to work, and the chosen
solution conflicts with the removed feature.
The order issue above _could_ be fixed by also switching the AB-loop
user option values around on direction switch. But there are no other
instances of option changes magically affecting other options, and doing
this would probably lead to unexpected misery (dying from corner cases
and such).
Another solution is sorting the A/B points by timestamps after copying
them from the user options. Then A/B options set in backward mode will
work in forward mode. This is the chosen solution. If you sort the
points, you don't know anymore whether the unset point is supposed to
signify the end or the start of the file.
The AB-loop code is slightly better abstracted now, so it should be easy
to restore the removed feature. It would still require coming up with a
solution for backwards playback, though.
A minor change is that if one point is set and the other is unset, I'm
rendering both the chapter markers and the marker for the set point.
Why? I don't know. My test file had chapters, and I guess I decided this
looked better.
This commit also fixes some subtle and obvious issues that I already
forgot about when I wrote this commit message. It cleans up some minor
code duplication and nonsense too.
Regarding backward playback, the code uses an unsanitary mix of internal
("transformed") and user timestamps. So the play_dir variable appears
more than usual.
To mention one unfixed issue: if you set an AB-loop that is completely
past the end of the file, it will get stuck in an infinite seeking loop
once playback reaches the end of the file. Fixing this reliably seemed
annoying, so the fix is "just don't do this". It's not a hard freeze
anyway.
2019-05-26 23:24:22 +00:00
|
|
|
// If the current playback position (or seek target) falls before the B
|
|
|
|
// position, actually make playback loop when reaching the B point. The
|
|
|
|
// intention is that you can seek out of the ab-loop range.
|
|
|
|
void update_ab_loop_clip(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
double pts = get_current_time(mpctx);
|
|
|
|
double ab[2];
|
|
|
|
mpctx->ab_loop_clip = pts != MP_NOPTS_VALUE &&
|
|
|
|
get_ab_loop_times(mpctx, ab) &&
|
|
|
|
pts * mpctx->play_dir <= ab[1] * mpctx->play_dir;
|
|
|
|
}
|
|
|
|
|
2014-08-03 17:09:22 +00:00
|
|
|
static void handle_osd_redraw(struct MPContext *mpctx)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
2024-05-19 21:19:17 +00:00
|
|
|
if (!mpctx->video_out || !mpctx->video_out->config_ok || (mpctx->playing && mpctx->stop_play))
|
2014-08-03 17:09:22 +00:00
|
|
|
return;
|
|
|
|
// If we're playing normally, let OSD be redrawn naturally as part of
|
|
|
|
// video display.
|
2014-08-10 11:11:46 +00:00
|
|
|
if (!mpctx->paused) {
|
|
|
|
if (mpctx->sleeptime < 0.1 && mpctx->video_status == STATUS_PLAYING)
|
|
|
|
return;
|
|
|
|
}
|
2014-08-03 17:09:22 +00:00
|
|
|
// Don't redraw immediately during a seek (makes it significantly slower).
|
player: dumb seeking related stuff, make audio hr-seek default
Try to deal with various corner cases. But when I fix one thing, another
thing breaks. (And it's 50/50 whether I find the breakage immediately or
a few months later.) So results may vary.
The default for--hr-seek is changed to "default" (not creative enough to
find a better name). In this mode, audio seeking is exact if there is no
video, or if the video has only a single frame. This change is actually
pretty dumb, since audio frames are usually small enough that exact
seeking does not really add much. But it gets rid of some weird special
cases.
Internally, the most important change is that is_coverart and is_sparse
handling is merged. is_sparse was originally just a special case for
weird .ts streams that have the corresponding low-level flag set. The
idea is that they're pretty similar anyway, so this would reduce the
number of corner cases. But I'm not sure if this doesn't break the
original intended use case for it (I don't have a sample anyway).
This changes last-frame handling, and respects the duration of the last
frame only if audio is disabled. This is mostly "coincidental" due to
the need to make seeking past EOF trigger player exit, and is caused by
setting STATUS_EOF early. On the other hand, this might have been this
way before (see removed chunk close to it).
2020-02-28 16:15:07 +00:00
|
|
|
bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_sparse;
|
2016-04-23 14:39:46 +00:00
|
|
|
if (use_video && mp_time_sec() - mpctx->start_timestamp < 0.1) {
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_set_timeout(mpctx, 0.1);
|
2014-08-03 17:09:22 +00:00
|
|
|
return;
|
2014-10-03 19:57:16 +00:00
|
|
|
}
|
2014-10-03 19:53:32 +00:00
|
|
|
bool want_redraw = osd_query_and_reset_want_redraw(mpctx->osd) ||
|
|
|
|
vo_want_redraw(mpctx->video_out);
|
2014-06-15 18:46:57 +00:00
|
|
|
if (!want_redraw)
|
2014-08-03 17:09:22 +00:00
|
|
|
return;
|
2014-06-15 18:46:57 +00:00
|
|
|
vo_redraw(mpctx->video_out);
|
2024-01-21 23:14:39 +00:00
|
|
|
// Even though we just redrew, it may need to be done again for certain
|
|
|
|
// cases of subtitles on an image.
|
|
|
|
redraw_subs(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
static void clear_underruns(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->ao_chain && mpctx->ao_chain->underrun) {
|
|
|
|
mpctx->ao_chain->underrun = false;
|
|
|
|
mp_wakeup_core(mpctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpctx->vo_chain && mpctx->vo_chain->underrun) {
|
|
|
|
mpctx->vo_chain->underrun = false;
|
|
|
|
mp_wakeup_core(mpctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-17 19:59:52 +00:00
|
|
|
static void handle_update_cache(struct MPContext *mpctx)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
2016-04-19 20:01:30 +00:00
|
|
|
bool force_update = false;
|
2013-10-29 21:38:29 +00:00
|
|
|
struct MPOpts *opts = mpctx->opts;
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
|
player: consider audio buffer if AO driver does not report underruns
AOs can report audio underruns, but only ao_alsa and ao_sdl (???)
currently do so. If the AO was marked as not reporting it, the cache
state was used to determine whether playback was interrupted due to slow
input.
This caused problems in some cases, such as video with very low video
frame rate: when a new frame is displayed, a new frame has to be
decoded, and since there it's so much further into the file (long frame
durations), the cache gets into an underrun state for a short moment,
even though both audio and video are playing fine. Enlarging the audio
buffer didn't help.
Fix this by making all AOs report underruns. If the AO driver does not
report underruns, fall back to using the buffer state.
pull.c behavior is slightly changed. Pull AOs are normally intended to
be used by pseudo-realtime audio APIs that fetch an audio buffer from
the API user via callback. I think it makes no sense to consider a
buffer underflow not an underrun in any situation, since we return
silence to the reader. (OK, maybe the reader could check the return
value? But let's not go there as long as there's no implementation.)
Remove the flag from ao_sdl.c, since it just worked via the generic
mechanism. Make the redundant underrun message verbose only.
push.c seems to log a redundant underflow message when resuming (because
somehow ao_play_data() is called when there's still no new data in the
buffer). But since ao_alsa does its own underrun reporting, and I only
use ao_alsa, I don't really care.
Also in all my tests, there seemed to be a rather high delay until the
underflow was logged (with audio only). I have no idea why this happened
and didn't try to debug this, but there's probably something wrong
somewhere.
This commit may cause random regressions.
See: #7440
2020-02-13 00:28:59 +00:00
|
|
|
if (!mpctx->demuxer || mpctx->encode_lavc_ctx) {
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
clear_underruns(mpctx);
|
2014-05-19 21:27:09 +00:00
|
|
|
return;
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
}
|
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of
--demuxer-readahead-secs if the stream cache is active. The default
value is very high (10 seconds), which means it can act as network
cache.
Remove the old behavior of trying to pause once the byte cache runs
low. Instead, do something similar wit the demuxer cache. The nice
thing is that we can guess how many seconds of video it has cached,
and we can make better decisions. But for now, apply a relatively
naive heuristic: if the cache is below 0.5 secs, pause, and wait
until at least 2 secs are available.
Note that due to timestamp reordering, the estimated cached duration
of video might be inaccurate, depending on the file format. If the
file format has DTS, it's easy, otherwise the duration will seemingly
jump back and forth.
2014-08-26 23:13:20 +00:00
|
|
|
|
2016-04-20 08:50:22 +00:00
|
|
|
double now = mp_time_sec();
|
|
|
|
|
2018-09-07 20:26:48 +00:00
|
|
|
struct demux_reader_state s;
|
|
|
|
demux_get_reader_state(mpctx->demuxer, &s);
|
2014-08-27 20:42:28 +00:00
|
|
|
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
mpctx->demux_underrun |= s.underrun;
|
|
|
|
|
2016-04-19 20:01:30 +00:00
|
|
|
int cache_buffer = 100;
|
2019-11-14 12:51:47 +00:00
|
|
|
bool use_pause_on_low_cache = opts->cache_pause && mpctx->play_dir > 0;
|
2018-01-06 14:49:32 +00:00
|
|
|
|
|
|
|
if (!mpctx->restart_complete) {
|
|
|
|
// Audio or video is restarting, and initial buffering is enabled. Make
|
|
|
|
// sure we actually restart them in paused mode, so no audio gets
|
|
|
|
// dropped and video technically doesn't start yet.
|
|
|
|
use_pause_on_low_cache &= opts->cache_pause_initial &&
|
|
|
|
(mpctx->video_status == STATUS_READY ||
|
|
|
|
mpctx->audio_status == STATUS_READY);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_low = use_pause_on_low_cache && !s.idle &&
|
2021-10-20 01:16:22 +00:00
|
|
|
s.ts_info.duration < opts->cache_pause_wait;
|
2018-01-06 14:49:32 +00:00
|
|
|
|
|
|
|
// Enter buffering state only if there actually was an underrun (or if
|
|
|
|
// initial caching before playback restart is used).
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
bool need_wait = is_low;
|
|
|
|
if (is_low && !mpctx->paused_for_cache && mpctx->restart_complete) {
|
|
|
|
// Wait only if an output underrun was registered. (Or if there is no
|
|
|
|
// underrun detection.)
|
|
|
|
bool output_underrun = false;
|
|
|
|
|
player: consider audio buffer if AO driver does not report underruns
AOs can report audio underruns, but only ao_alsa and ao_sdl (???)
currently do so. If the AO was marked as not reporting it, the cache
state was used to determine whether playback was interrupted due to slow
input.
This caused problems in some cases, such as video with very low video
frame rate: when a new frame is displayed, a new frame has to be
decoded, and since there it's so much further into the file (long frame
durations), the cache gets into an underrun state for a short moment,
even though both audio and video are playing fine. Enlarging the audio
buffer didn't help.
Fix this by making all AOs report underruns. If the AO driver does not
report underruns, fall back to using the buffer state.
pull.c behavior is slightly changed. Pull AOs are normally intended to
be used by pseudo-realtime audio APIs that fetch an audio buffer from
the API user via callback. I think it makes no sense to consider a
buffer underflow not an underrun in any situation, since we return
silence to the reader. (OK, maybe the reader could check the return
value? But let's not go there as long as there's no implementation.)
Remove the flag from ao_sdl.c, since it just worked via the generic
mechanism. Make the redundant underrun message verbose only.
push.c seems to log a redundant underflow message when resuming (because
somehow ao_play_data() is called when there's still no new data in the
buffer). But since ao_alsa does its own underrun reporting, and I only
use ao_alsa, I don't really care.
Also in all my tests, there seemed to be a rather high delay until the
underflow was logged (with audio only). I have no idea why this happened
and didn't try to debug this, but there's probably something wrong
somewhere.
This commit may cause random regressions.
See: #7440
2020-02-13 00:28:59 +00:00
|
|
|
if (mpctx->ao_chain)
|
|
|
|
output_underrun |= mpctx->ao_chain->underrun;
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
if (mpctx->vo_chain)
|
|
|
|
output_underrun |= mpctx->vo_chain->underrun;
|
|
|
|
|
|
|
|
// Output underruns could be sporadic (unrelated to demuxer buffer state
|
|
|
|
// and for example caused by slow decoding), so use a past demuxer
|
|
|
|
// underrun as indication that the underrun was possibly due to a
|
|
|
|
// demuxer underrun.
|
|
|
|
need_wait = mpctx->demux_underrun && output_underrun;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let the underrun flag "stick" around until the cache has fully recovered.
|
|
|
|
// See logic where demux_underrun is used.
|
|
|
|
if (!is_low)
|
|
|
|
mpctx->demux_underrun = false;
|
2018-01-06 14:49:32 +00:00
|
|
|
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
if (mpctx->paused_for_cache != need_wait) {
|
|
|
|
mpctx->paused_for_cache = need_wait;
|
2018-01-06 14:49:32 +00:00
|
|
|
update_internal_pause_state(mpctx);
|
|
|
|
force_update = true;
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
if (mpctx->paused_for_cache)
|
2018-01-06 14:49:32 +00:00
|
|
|
mpctx->cache_stop_time = now;
|
|
|
|
}
|
|
|
|
|
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a
while if network runs out of data. If this is not done, then playback
will go on frame-wise (as packets are slowly read from the network and
then instantly decoded and displayed). This feature is actually useless,
as you won't get nice playback no matter what if network is too slow,
but I guess I still prefer this behavior for some reason.
This commit changes this behavior from using the demuxer cache state
only, to trying to use underrun information from the AO/VO. This means
if you have a very large audio buffer, then cache-pausing will trigger
once that buffer is depleted, which will be some time _after_ the
demuxer cache has run out.
This requires explicit support from the AO. Otherwise, the behavior
should be mostly the same as before this commit.
This does not care about the AO buffer. In theory, the AO may underrun,
then the player will write some data to the AO buffer, then the AO will
recover and play this bit of data, then the player will probably trigger
the cache-pause behavior. The probability of this happening should be
pretty low, so I will hold off fixing this until the next refactor of
the AO chain (if ever).
The VO underflow detection was devised and tested in 5 minutes, and may
not be correct. At least I'm fairly sure that the combination of all the
factors should make incorrect behavior relatively unlikely, but problems
are possible.
Also, the demux_reader_state.underrun field may be inaccurate. It's only
the present state at the time demux_get_reader_state() was called, and
may exclude past underruns. In theory, this could cause "close" cases to
be missed. Then you might get an audio underrun without cache-pausing
acting on it. If the stars align, this could happen multiple times in
the row, effectively making this feature not work.
The most user-visible consequence of this change is that the user
will now see an AO underrun warning every time the cache runs out.
Maybe this cache-pause feature should just be removed...
2019-10-11 17:34:04 +00:00
|
|
|
if (!mpctx->paused_for_cache)
|
|
|
|
clear_underruns(mpctx);
|
|
|
|
|
2018-01-06 14:49:32 +00:00
|
|
|
if (mpctx->paused_for_cache) {
|
|
|
|
cache_buffer =
|
2021-10-20 01:16:22 +00:00
|
|
|
100 * MPCLAMP(s.ts_info.duration / opts->cache_pause_wait, 0, 0.99);
|
2018-01-06 14:49:32 +00:00
|
|
|
mp_set_timeout(mpctx, 0.2);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of
--demuxer-readahead-secs if the stream cache is active. The default
value is very high (10 seconds), which means it can act as network
cache.
Remove the old behavior of trying to pause once the byte cache runs
low. Instead, do something similar wit the demuxer cache. The nice
thing is that we can guess how many seconds of video it has cached,
and we can make better decisions. But for now, apply a relatively
naive heuristic: if the cache is below 0.5 secs, pause, and wait
until at least 2 secs are available.
Note that due to timestamp reordering, the estimated cached duration
of video might be inaccurate, depending on the file format. If the
file format has DTS, it's easy, otherwise the duration will seemingly
jump back and forth.
2014-08-26 23:13:20 +00:00
|
|
|
|
2014-07-31 02:19:41 +00:00
|
|
|
// Also update cache properties.
|
2018-08-31 10:48:36 +00:00
|
|
|
bool busy = !s.idle;
|
2020-03-05 21:23:43 +00:00
|
|
|
if (fabs(mpctx->cache_update_pts - mpctx->playback_pts) >= 1.0)
|
|
|
|
busy = true;
|
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of
--demuxer-readahead-secs if the stream cache is active. The default
value is very high (10 seconds), which means it can act as network
cache.
Remove the old behavior of trying to pause once the byte cache runs
low. Instead, do something similar wit the demuxer cache. The nice
thing is that we can guess how many seconds of video it has cached,
and we can make better decisions. But for now, apply a relatively
naive heuristic: if the cache is below 0.5 secs, pause, and wait
until at least 2 secs are available.
Note that due to timestamp reordering, the estimated cached duration
of video might be inaccurate, depending on the file format. If the
file format has DTS, it's easy, otherwise the duration will seemingly
jump back and forth.
2014-08-26 23:13:20 +00:00
|
|
|
if (busy || mpctx->next_cache_update > 0) {
|
2014-07-31 02:19:41 +00:00
|
|
|
if (mpctx->next_cache_update <= now) {
|
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of
--demuxer-readahead-secs if the stream cache is active. The default
value is very high (10 seconds), which means it can act as network
cache.
Remove the old behavior of trying to pause once the byte cache runs
low. Instead, do something similar wit the demuxer cache. The nice
thing is that we can guess how many seconds of video it has cached,
and we can make better decisions. But for now, apply a relatively
naive heuristic: if the cache is below 0.5 secs, pause, and wait
until at least 2 secs are available.
Note that due to timestamp reordering, the estimated cached duration
of video might be inaccurate, depending on the file format. If the
file format has DTS, it's easy, otherwise the duration will seemingly
jump back and forth.
2014-08-26 23:13:20 +00:00
|
|
|
mpctx->next_cache_update = busy ? now + 0.25 : 0;
|
2016-04-19 20:01:30 +00:00
|
|
|
force_update = true;
|
2014-07-31 02:19:41 +00:00
|
|
|
}
|
2016-09-16 12:24:15 +00:00
|
|
|
if (mpctx->next_cache_update > 0)
|
|
|
|
mp_set_timeout(mpctx, mpctx->next_cache_update - now);
|
2014-07-31 02:19:41 +00:00
|
|
|
}
|
2016-04-19 20:01:30 +00:00
|
|
|
|
|
|
|
if (mpctx->cache_buffer != cache_buffer) {
|
2018-01-06 14:49:32 +00:00
|
|
|
if ((mpctx->cache_buffer == 100) != (cache_buffer == 100)) {
|
2016-04-19 20:01:30 +00:00
|
|
|
if (cache_buffer < 100) {
|
2018-05-02 19:34:43 +00:00
|
|
|
MP_VERBOSE(mpctx, "Enter buffering (buffer went from %d%% -> %d%%) [%fs].\n",
|
2021-10-20 01:16:22 +00:00
|
|
|
mpctx->cache_buffer, cache_buffer, s.ts_info.duration);
|
2016-04-19 20:01:30 +00:00
|
|
|
} else {
|
2016-04-20 08:50:22 +00:00
|
|
|
double t = now - mpctx->cache_stop_time;
|
2018-05-02 19:34:43 +00:00
|
|
|
MP_VERBOSE(mpctx, "End buffering (waited %f secs) [%fs].\n",
|
2021-10-20 01:16:22 +00:00
|
|
|
t, s.ts_info.duration);
|
2016-04-19 20:01:30 +00:00
|
|
|
}
|
2018-05-02 19:34:43 +00:00
|
|
|
} else {
|
|
|
|
MP_VERBOSE(mpctx, "Still buffering (buffer went from %d%% -> %d%%) [%fs].\n",
|
2021-10-20 01:16:22 +00:00
|
|
|
mpctx->cache_buffer, cache_buffer, s.ts_info.duration);
|
2016-04-19 20:01:30 +00:00
|
|
|
}
|
|
|
|
mpctx->cache_buffer = cache_buffer;
|
|
|
|
force_update = true;
|
|
|
|
}
|
|
|
|
|
2017-01-18 18:02:50 +00:00
|
|
|
if (s.eof && !busy)
|
|
|
|
prefetch_next(mpctx);
|
|
|
|
|
2020-03-05 21:23:43 +00:00
|
|
|
if (force_update) {
|
|
|
|
mpctx->cache_update_pts = mpctx->playback_pts;
|
2016-04-19 20:01:30 +00:00
|
|
|
mp_notify(mpctx, MP_EVENT_CACHE_UPDATE, NULL);
|
2020-03-05 21:23:43 +00:00
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 20:01:30 +00:00
|
|
|
int get_cache_buffering_percentage(struct MPContext *mpctx)
|
2014-10-07 20:07:07 +00:00
|
|
|
{
|
2016-04-19 20:01:30 +00:00
|
|
|
return mpctx->demuxer ? mpctx->cache_buffer : -1;
|
2014-10-07 20:07:07 +00:00
|
|
|
}
|
|
|
|
|
2024-04-14 21:37:32 +00:00
|
|
|
static void handle_update_subtitles(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
if (mpctx->video_status == STATUS_EOF) {
|
|
|
|
update_subtitles(mpctx, mpctx->playback_pts);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = 0; n < mpctx->num_tracks; n++) {
|
|
|
|
struct track *track = mpctx->tracks[n];
|
|
|
|
if (track->type == STREAM_SUB && !track->demuxer_ready) {
|
|
|
|
update_subtitles(mpctx, mpctx->playback_pts);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
static void handle_cursor_autohide(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
|
|
|
struct vo *vo = mpctx->video_out;
|
|
|
|
|
|
|
|
if (!vo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool mouse_cursor_visible = mpctx->mouse_cursor_visible;
|
2014-07-18 13:04:46 +00:00
|
|
|
double now = mp_time_sec();
|
2013-10-29 21:38:29 +00:00
|
|
|
|
|
|
|
unsigned mouse_event_ts = mp_input_get_mouse_event_counter(mpctx->input);
|
|
|
|
if (mpctx->mouse_event_ts != mouse_event_ts) {
|
|
|
|
mpctx->mouse_event_ts = mouse_event_ts;
|
2020-01-12 00:32:13 +00:00
|
|
|
mpctx->mouse_timer = now + opts->cursor_autohide_delay / 1000.0;
|
2013-10-29 21:38:29 +00:00
|
|
|
mouse_cursor_visible = true;
|
|
|
|
}
|
|
|
|
|
2014-07-18 13:04:46 +00:00
|
|
|
if (mpctx->mouse_timer > now) {
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_set_timeout(mpctx, mpctx->mouse_timer - now);
|
2014-07-18 13:04:46 +00:00
|
|
|
} else {
|
2013-10-29 21:38:29 +00:00
|
|
|
mouse_cursor_visible = false;
|
2014-07-18 13:04:46 +00:00
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2020-01-12 00:32:13 +00:00
|
|
|
if (opts->cursor_autohide_delay == -1)
|
2013-10-29 21:38:29 +00:00
|
|
|
mouse_cursor_visible = true;
|
|
|
|
|
2020-01-12 00:32:13 +00:00
|
|
|
if (opts->cursor_autohide_delay == -2)
|
2013-10-29 21:38:29 +00:00
|
|
|
mouse_cursor_visible = false;
|
|
|
|
|
2020-01-12 00:32:13 +00:00
|
|
|
if (opts->cursor_autohide_fs && !opts->vo->fullscreen)
|
2013-10-29 21:38:29 +00:00
|
|
|
mouse_cursor_visible = true;
|
|
|
|
|
|
|
|
if (mouse_cursor_visible != mpctx->mouse_cursor_visible)
|
|
|
|
vo_control(vo, VOCTRL_SET_CURSOR_VISIBILITY, &mouse_cursor_visible);
|
|
|
|
mpctx->mouse_cursor_visible = mouse_cursor_visible;
|
|
|
|
}
|
|
|
|
|
2014-11-02 19:26:51 +00:00
|
|
|
static void handle_vo_events(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct vo *vo = mpctx->video_out;
|
2014-11-09 09:00:21 +00:00
|
|
|
int events = vo ? vo_query_and_reset_events(vo, VO_EVENTS_USER) : 0;
|
2014-11-02 19:26:51 +00:00
|
|
|
if (events & VO_EVENT_RESIZE)
|
|
|
|
mp_notify(mpctx, MP_EVENT_WIN_RESIZE, NULL);
|
2014-11-02 19:48:45 +00:00
|
|
|
if (events & VO_EVENT_WIN_STATE)
|
|
|
|
mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL);
|
2020-01-08 19:23:12 +00:00
|
|
|
if (events & VO_EVENT_DPI)
|
|
|
|
mp_notify(mpctx, MP_EVENT_WIN_STATE2, NULL);
|
2020-09-07 16:22:25 +00:00
|
|
|
if (events & VO_EVENT_FOCUS)
|
|
|
|
mp_notify(mpctx, MP_EVENT_FOCUS, NULL);
|
2014-11-02 19:26:51 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
static void handle_sstep(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2014-08-03 18:25:03 +00:00
|
|
|
if (mpctx->stop_play || !mpctx->restart_complete)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (opts->step_sec > 0 && !mpctx->paused) {
|
2013-10-29 21:38:29 +00:00
|
|
|
set_osd_function(mpctx, OSD_FFW);
|
2016-08-15 19:07:32 +00:00
|
|
|
queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, MPSEEK_DEFAULT, 0);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
2014-08-03 18:25:03 +00:00
|
|
|
|
2014-08-26 18:45:41 +00:00
|
|
|
if (mpctx->video_status >= STATUS_EOF) {
|
2015-07-08 19:31:31 +00:00
|
|
|
if (mpctx->max_frames >= 0 && !mpctx->stop_play)
|
2014-10-14 11:29:38 +00:00
|
|
|
mpctx->stop_play = AT_END_OF_FILE; // force EOF even if audio left
|
2014-08-03 18:25:03 +00:00
|
|
|
if (mpctx->step_frames > 0 && !mpctx->paused)
|
2017-04-14 16:22:45 +00:00
|
|
|
set_pause_state(mpctx, true);
|
2014-08-03 18:25:03 +00:00
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2014-04-17 21:55:04 +00:00
|
|
|
static void handle_loop_file(struct MPContext *mpctx)
|
|
|
|
{
|
2019-05-26 23:33:35 +00:00
|
|
|
if (mpctx->stop_play != AT_END_OF_FILE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
double target = MP_NOPTS_VALUE;
|
|
|
|
enum seek_precision prec = MPSEEK_DEFAULT;
|
|
|
|
|
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it
makes it work with backward playback mode.
The most obvious change is that the both the A and B point need to be
set now before any looping happens. Unlike before, unset points don't
implicitly use the start or end of the file. I think the old behavior
was a feature that was explicitly added/wanted. Well, it's gone now.
This is because of 2 reasons:
1. I never liked this feature, and it always got in my way (as user).
2. It's inherently annoying with backward playback mode.
In backward playback mode, the user wants to set A/B in the wrong order.
The ab-loop command will first set A, then B, so if you use this command
during backward playback, A will be set to a higher timestamps than B.
If you switch back to forward playback mode, the loop would stop
working. I want the loop to just continue to work, and the chosen
solution conflicts with the removed feature.
The order issue above _could_ be fixed by also switching the AB-loop
user option values around on direction switch. But there are no other
instances of option changes magically affecting other options, and doing
this would probably lead to unexpected misery (dying from corner cases
and such).
Another solution is sorting the A/B points by timestamps after copying
them from the user options. Then A/B options set in backward mode will
work in forward mode. This is the chosen solution. If you sort the
points, you don't know anymore whether the unset point is supposed to
signify the end or the start of the file.
The AB-loop code is slightly better abstracted now, so it should be easy
to restore the removed feature. It would still require coming up with a
solution for backwards playback, though.
A minor change is that if one point is set and the other is unset, I'm
rendering both the chapter markers and the marker for the set point.
Why? I don't know. My test file had chapters, and I guess I decided this
looked better.
This commit also fixes some subtle and obvious issues that I already
forgot about when I wrote this commit message. It cleans up some minor
code duplication and nonsense too.
Regarding backward playback, the code uses an unsanitary mix of internal
("transformed") and user timestamps. So the play_dir variable appears
more than usual.
To mention one unfixed issue: if you set an AB-loop that is completely
past the end of the file, it will get stuck in an infinite seeking loop
once playback reaches the end of the file. Fixing this reliably seemed
annoying, so the fix is "just don't do this". It's not a hard freeze
anyway.
2019-05-26 23:24:22 +00:00
|
|
|
double ab[2];
|
2019-05-26 23:33:35 +00:00
|
|
|
if (get_ab_loop_times(mpctx, ab) && mpctx->ab_loop_clip) {
|
2024-07-04 20:07:10 +00:00
|
|
|
if (mpctx->remaining_ab_loops > 0) {
|
|
|
|
mpctx->remaining_ab_loops--;
|
|
|
|
mp_notify_property(mpctx, "remaining-ab-loops");
|
2020-02-08 13:57:45 +00:00
|
|
|
}
|
2019-05-26 23:33:35 +00:00
|
|
|
target = ab[0];
|
|
|
|
prec = MPSEEK_EXACT;
|
2024-06-04 19:51:03 +00:00
|
|
|
} else if (mpctx->remaining_file_loops) {
|
|
|
|
if (mpctx->remaining_file_loops > 0) {
|
|
|
|
mpctx->remaining_file_loops--;
|
|
|
|
mp_notify_property(mpctx, "remaining-file-loops");
|
player: change m_config to use new option handling mechanisms
Instead of making m_config a special-case, it more or less uses the
underlying m_config_cache/m_config_shadow APIs properly. This makes the
player core a (relatively) equivalent user of the core option API. In
particular, this means that other threads can change core options with
m_config_cache_write_opt() calls (before this commit, this merely led to
diverging option values).
An important change is that before this commit, mpctx->opts contained
the "master copy" of all option data. Now it's just another copy of the
option data, and the shadow copy is considered the master. This is why
whenever mpctx->opts is written, the change needs to be copied to the
master (thus why this commits add a bunch of m_config_notify... calls).
If another thread (e.g. a VO) changes an option, async_change_cb is now
invoked, which funnels the change notification through the player's
layers.
The new self_notification parameter on mp_option_change_callback is so
that m_config_notify... doesn't trigger recursion, and it's used in
cases where the change was already "processed". It's still needed to
trigger libmpv property updates. (I considered using an extra
m_config_cache for that, but it'd only cause problems with no
advantages.)
I think the recent changes actually forgot to send libmpv property
updates in some cases. This should fix this anyway. In some cases,
property updates are reworked, and the potential for bugs should be
lower (probably).
The primary point of this change is to allow external updates, for
example by a VO writing the fullscreen option if the window state is
changed by the window manager (rather than mpv changing it). This is not
used yet, but the following commits will.
2019-11-29 11:49:15 +00:00
|
|
|
}
|
2019-05-26 23:46:34 +00:00
|
|
|
target = get_start_time(mpctx, mpctx->play_dir);
|
2016-08-18 18:40:23 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 23:33:35 +00:00
|
|
|
if (target != MP_NOPTS_VALUE) {
|
2020-08-22 18:43:15 +00:00
|
|
|
if (!mpctx->shown_aframes && !mpctx->shown_vframes) {
|
|
|
|
MP_WARN(mpctx, "No media data to loop.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-28 15:23:12 +00:00
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
|
|
|
set_osd_function(mpctx, OSD_FFW);
|
2019-05-26 23:33:35 +00:00
|
|
|
mark_seek(mpctx);
|
|
|
|
|
|
|
|
// Assumes execute_queued_seek() happens before next audio/video is
|
|
|
|
// attempted to be decoded or filtered.
|
|
|
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, target, prec, MPSEEK_FLAG_NOFLUSH);
|
2014-04-17 21:55:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-08 16:27:07 +00:00
|
|
|
void seek_to_last_frame(struct MPContext *mpctx)
|
2014-12-07 01:47:09 +00:00
|
|
|
{
|
2016-01-17 17:07:50 +00:00
|
|
|
if (!mpctx->vo_chain)
|
2014-12-07 01:47:09 +00:00
|
|
|
return;
|
player: hack against --keep-open misbehaving with broken files
If a file (or a demuxer) is broken, seeking close to the end of the file
doesn't work, and seek_to_last_frame() will be called over and over
again, burning CPU for no reason.
Observed with incomplete mp4 files. That this can happen was already
mentioned in commit 090f6cfc, but I guess now I'll do something against
it.
hrseek_lastframe is cleared by reset_playback_state(), so it's only set
if seek_to_last_frame() was called, and no other seek happened since
then. If finding the last frame succeeds, no EOF will happen (unless the
user unpauses, but then it will simply remain at the last frame). If it
fails, then it will return immediately, without retrying.
2014-12-24 14:12:11 +00:00
|
|
|
if (mpctx->hrseek_lastframe) // exit if we already tried this
|
|
|
|
return;
|
2014-12-07 01:47:09 +00:00
|
|
|
MP_VERBOSE(mpctx, "seeking to last frame...\n");
|
|
|
|
// Approximately seek close to the end of the file.
|
|
|
|
// Usually, it will seek some seconds before end.
|
player: partially fix seek_to_last_frame in backward mode
Another shitty obscure feature that usually nobody notices.
Unsurprisingly, it doesn't go well with backward playback mode.
If you use --keep-open in forward playback mode, and seek past the end
of the file, it tries to seek to the very last frame. The demuxer will
seek to the last "keyframe" before the end (i.e. some frames to go in
most cases), and trying to hr-seek to the file duration often won't cut
it, so this requires some special code. The function at hand seeks
"close" to the end, and then stops hr-seek when the last frame us
encountered (simple enough and very effective).
In backward playback mode, start and end are reversed, and we need to
seek "close" to the start of the file instead. Simple enough to do, and
it works.
One problem is that command.c has some weird logic to make going beyond
the last chapter either end playback (--keep-open=no), or jump to the
last frame. Now this will jump to the first frame, which is weird, but
let's ignore this.
Another problem is that seeking before playback start position hits EOF
in backward playback mode, which is a demuxer bug, and has nothing to do
with this code. But it triggers this code, so seeking before the start
will show the "last" frame. (My description is a mess with directions.
Figure it out yourself.)
2019-05-27 00:12:26 +00:00
|
|
|
double end = MP_NOPTS_VALUE;
|
|
|
|
if (mpctx->play_dir > 0) {
|
|
|
|
end = get_play_end_pts(mpctx);
|
|
|
|
if (end == MP_NOPTS_VALUE)
|
|
|
|
end = get_time_length(mpctx);
|
|
|
|
} else {
|
|
|
|
end = get_start_time(mpctx, 1);
|
|
|
|
}
|
2014-12-07 01:47:09 +00:00
|
|
|
mp_seek(mpctx, (struct seek_params){
|
|
|
|
.type = MPSEEK_ABSOLUTE,
|
|
|
|
.amount = end,
|
2015-03-04 16:21:05 +00:00
|
|
|
.exact = MPSEEK_VERY_EXACT,
|
2016-02-15 20:03:51 +00:00
|
|
|
});
|
2014-12-07 01:47:09 +00:00
|
|
|
// Make it exact: stop seek only if last frame was reached.
|
|
|
|
if (mpctx->hrseek_active) {
|
player: partially fix seek_to_last_frame in backward mode
Another shitty obscure feature that usually nobody notices.
Unsurprisingly, it doesn't go well with backward playback mode.
If you use --keep-open in forward playback mode, and seek past the end
of the file, it tries to seek to the very last frame. The demuxer will
seek to the last "keyframe" before the end (i.e. some frames to go in
most cases), and trying to hr-seek to the file duration often won't cut
it, so this requires some special code. The function at hand seeks
"close" to the end, and then stops hr-seek when the last frame us
encountered (simple enough and very effective).
In backward playback mode, start and end are reversed, and we need to
seek "close" to the start of the file instead. Simple enough to do, and
it works.
One problem is that command.c has some weird logic to make going beyond
the last chapter either end playback (--keep-open=no), or jump to the
last frame. Now this will jump to the first frame, which is weird, but
let's ignore this.
Another problem is that seeking before playback start position hits EOF
in backward playback mode, which is a demuxer bug, and has nothing to do
with this code. But it triggers this code, so seeking before the start
will show the "last" frame. (My description is a mess with directions.
Figure it out yourself.)
2019-05-27 00:12:26 +00:00
|
|
|
mpctx->hrseek_pts = INFINITY * mpctx->play_dir;
|
2014-12-07 01:47:09 +00:00
|
|
|
mpctx->hrseek_lastframe = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
static void handle_keep_open(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2014-09-23 23:56:53 +00:00
|
|
|
if (opts->keep_open && mpctx->stop_play == AT_END_OF_FILE &&
|
2022-01-17 15:02:19 +00:00
|
|
|
(opts->keep_open == 2 ||
|
|
|
|
(!playlist_get_next(mpctx->playlist, 1) && opts->loop_times == 1)))
|
2014-09-23 23:56:53 +00:00
|
|
|
{
|
2013-10-29 21:38:29 +00:00
|
|
|
mpctx->stop_play = KEEP_PLAYING;
|
2016-01-17 17:07:50 +00:00
|
|
|
if (mpctx->vo_chain) {
|
2022-05-13 02:58:12 +00:00
|
|
|
if (!vo_has_frame(mpctx->video_out)) { // EOF not reached normally
|
2014-12-07 01:47:09 +00:00
|
|
|
seek_to_last_frame(mpctx);
|
2022-05-13 02:58:12 +00:00
|
|
|
mpctx->audio_status = STATUS_EOF;
|
|
|
|
mpctx->video_status = STATUS_EOF;
|
|
|
|
}
|
2014-12-07 01:47:09 +00:00
|
|
|
}
|
2019-08-30 18:11:39 +00:00
|
|
|
if (opts->keep_open_pause) {
|
audio: refactor how data is passed to AO
This replaces the two buffers (ao_chain.ao_buffer in the core, and
buffer_state.buffers in the AO) with a single queue. Instead of having a
byte based buffer, the queue is simply a list of audio frames, as output
by the decoder. This should make dataflow simpler and reduce copying.
It also attempts to simplify fill_audio_out_buffers(), the function I
always hated most, because it's full of subtle and buggy logic.
Unfortunately, I got assaulted by corner cases, dumb features (attempt
at seamless looping, really?), and other crap, so it got pretty
complicated again. fill_audio_out_buffers() is still full of subtle and
buggy logic. Maybe it got worse. On the other hand, maybe there really
is some progress. Who knows.
Originally, the data flow parts was meant to be in f_output_chain, but
due to tricky interactions with the playloop code, it's now in the dummy
filter in audio.c.
At least this improves the way the audio PTS is passed to the encoder in
encoding mode. Now it attempts to pass frames directly, along with the
pts, which should minimize timestamp problems. But to be honest, encoder
mode is one big kludge that shouldn't exist in this way.
This commit should be considered pre-alpha code. There are lots of bugs
still hiding.
2020-08-28 18:23:54 +00:00
|
|
|
if (mpctx->ao && ao_is_playing(mpctx->ao))
|
|
|
|
return;
|
2017-04-14 16:22:45 +00:00
|
|
|
set_pause_state(mpctx, true);
|
2019-08-30 18:11:39 +00:00
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-27 20:28:07 +00:00
|
|
|
static void handle_chapter_change(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
int chapter = get_current_chapter(mpctx);
|
|
|
|
if (chapter != mpctx->last_chapter) {
|
|
|
|
mpctx->last_chapter = chapter;
|
2021-12-02 16:19:39 +00:00
|
|
|
mp_notify(mpctx, MP_EVENT_CHAPTER_CHANGE, NULL);
|
2014-04-27 20:28:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-21 19:22:20 +00:00
|
|
|
// Execute a forceful refresh of the VO window. This clears the window from
|
|
|
|
// the previous video. It also creates/destroys the VO on demand.
|
|
|
|
// It tries to make the change only in situations where the window is
|
|
|
|
// definitely needed or not needed, or if the force parameter is set (the
|
|
|
|
// latter also decides whether to clear an existing window, because there's
|
|
|
|
// no way to know if this has already been done or not).
|
|
|
|
int handle_force_window(struct MPContext *mpctx, bool force)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
2015-09-21 19:22:20 +00:00
|
|
|
// True if we're either in idle mode, or loading of the file has finished.
|
|
|
|
// It's also set via force in some stages during file loading.
|
2018-05-19 16:25:54 +00:00
|
|
|
bool act = mpctx->stop_play || mpctx->playback_initialized || force;
|
2015-09-21 19:22:20 +00:00
|
|
|
|
2016-02-24 21:19:00 +00:00
|
|
|
// On the other hand, if a video track is selected, but no video is ever
|
|
|
|
// decoded on it, then create the window.
|
|
|
|
bool stalled_video = mpctx->playback_initialized && mpctx->restart_complete &&
|
|
|
|
mpctx->video_status == STATUS_EOF && mpctx->vo_chain &&
|
2016-02-25 10:21:54 +00:00
|
|
|
!mpctx->video_out->config_ok;
|
2016-02-24 21:19:00 +00:00
|
|
|
|
|
|
|
// Don't interfere with real video playback
|
|
|
|
if (mpctx->vo_chain && !stalled_video)
|
|
|
|
return 0;
|
|
|
|
|
2015-09-20 15:58:02 +00:00
|
|
|
if (!mpctx->opts->force_vo) {
|
2016-02-25 10:21:54 +00:00
|
|
|
if (act && !mpctx->vo_chain)
|
2015-09-20 21:03:00 +00:00
|
|
|
uninit_video_out(mpctx);
|
2015-09-20 15:58:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2015-09-21 19:22:20 +00:00
|
|
|
if (mpctx->opts->force_vo != 2 && !act)
|
|
|
|
return 0;
|
|
|
|
|
2015-09-20 15:58:02 +00:00
|
|
|
if (!mpctx->video_out) {
|
|
|
|
struct vo_extra ex = {
|
|
|
|
.input_ctx = mpctx->input,
|
|
|
|
.osd = mpctx->osd,
|
|
|
|
.encode_lavc_ctx = mpctx->encode_lavc_ctx,
|
2016-09-16 12:23:54 +00:00
|
|
|
.wakeup_cb = mp_wakeup_core_cb,
|
|
|
|
.wakeup_ctx = mpctx,
|
2015-09-20 15:58:02 +00:00
|
|
|
};
|
|
|
|
mpctx->video_out = init_best_video_out(mpctx->global, &ex);
|
|
|
|
if (!mpctx->video_out)
|
|
|
|
goto err;
|
|
|
|
mpctx->mouse_cursor_visible = true;
|
|
|
|
}
|
|
|
|
|
2015-09-21 19:22:20 +00:00
|
|
|
if (!mpctx->video_out->config_ok || force) {
|
2014-10-03 17:57:49 +00:00
|
|
|
struct vo *vo = mpctx->video_out;
|
2013-10-29 21:38:29 +00:00
|
|
|
// Pick whatever works
|
|
|
|
int config_format = 0;
|
2015-01-03 16:23:01 +00:00
|
|
|
uint8_t fmts[IMGFMT_END - IMGFMT_START] = {0};
|
|
|
|
vo_query_formats(vo, fmts);
|
2013-10-29 21:38:29 +00:00
|
|
|
for (int fmt = IMGFMT_START; fmt < IMGFMT_END; fmt++) {
|
2015-01-03 16:23:01 +00:00
|
|
|
if (fmts[fmt - IMGFMT_START]) {
|
2013-10-29 21:38:29 +00:00
|
|
|
config_format = fmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-11-29 08:05:18 +00:00
|
|
|
|
|
|
|
// Use a 16:9 aspect ratio so that fullscreen on a 16:9 screen will not
|
|
|
|
// have vertical margins, which can lead to a different size or position
|
|
|
|
// of subtitles than with 16:9 videos.
|
2013-10-29 21:38:29 +00:00
|
|
|
int w = 960;
|
2023-11-29 08:05:18 +00:00
|
|
|
int h = 540;
|
2013-10-29 21:38:29 +00:00
|
|
|
struct mp_image_params p = {
|
|
|
|
.imgfmt = config_format,
|
|
|
|
.w = w, .h = h,
|
2015-12-19 19:04:31 +00:00
|
|
|
.p_w = 1, .p_h = 1,
|
2023-09-20 18:18:26 +00:00
|
|
|
.force_window = true,
|
2013-10-29 21:38:29 +00:00
|
|
|
};
|
2015-10-03 16:20:16 +00:00
|
|
|
if (vo_reconfig(vo, &p) < 0)
|
2015-09-20 15:58:02 +00:00
|
|
|
goto err;
|
2022-11-15 21:50:38 +00:00
|
|
|
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
|
|
|
|
update_content_type(mpctx, track);
|
2016-10-26 18:35:12 +00:00
|
|
|
update_screensaver_state(mpctx);
|
2014-09-08 22:54:34 +00:00
|
|
|
vo_set_paused(vo, true);
|
2014-06-15 18:46:57 +00:00
|
|
|
vo_redraw(vo);
|
2014-02-17 01:52:26 +00:00
|
|
|
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
2015-09-20 15:58:02 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
mpctx->opts->force_vo = 0;
|
player: change m_config to use new option handling mechanisms
Instead of making m_config a special-case, it more or less uses the
underlying m_config_cache/m_config_shadow APIs properly. This makes the
player core a (relatively) equivalent user of the core option API. In
particular, this means that other threads can change core options with
m_config_cache_write_opt() calls (before this commit, this merely led to
diverging option values).
An important change is that before this commit, mpctx->opts contained
the "master copy" of all option data. Now it's just another copy of the
option data, and the shadow copy is considered the master. This is why
whenever mpctx->opts is written, the change needs to be copied to the
master (thus why this commits add a bunch of m_config_notify... calls).
If another thread (e.g. a VO) changes an option, async_change_cb is now
invoked, which funnels the change notification through the player's
layers.
The new self_notification parameter on mp_option_change_callback is so
that m_config_notify... doesn't trigger recursion, and it's used in
cases where the change was already "processed". It's still needed to
trigger libmpv property updates. (I considered using an extra
m_config_cache for that, but it'd only cause problems with no
advantages.)
I think the recent changes actually forgot to send libmpv property
updates in some cases. This should fix this anyway. In some cases,
property updates are reworked, and the potential for bugs should be
lower (probably).
The primary point of this change is to allow external updates, for
example by a VO writing the fullscreen option if the window state is
changed by the window manager (rather than mpv changing it). This is not
used yet, but the following commits will.
2019-11-29 11:49:15 +00:00
|
|
|
m_config_notify_change_opt_ptr(mpctx->mconfig, &mpctx->opts->force_vo);
|
2015-09-20 15:58:02 +00:00
|
|
|
uninit_video_out(mpctx);
|
|
|
|
MP_FATAL(mpctx, "Error opening/initializing the VO window.\n");
|
|
|
|
return -1;
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 18:25:03 +00:00
|
|
|
// Potentially needed by some Lua scripts, which assume TICK always comes.
|
|
|
|
static void handle_dummy_ticks(struct MPContext *mpctx)
|
|
|
|
{
|
2019-05-17 19:53:55 +00:00
|
|
|
if ((mpctx->video_status != STATUS_PLAYING &&
|
|
|
|
mpctx->video_status != STATUS_DRAINING) ||
|
|
|
|
mpctx->paused)
|
|
|
|
{
|
2015-06-23 17:21:20 +00:00
|
|
|
if (mp_time_sec() - mpctx->last_idle_tick > 0.050) {
|
2014-08-03 18:25:03 +00:00
|
|
|
mpctx->last_idle_tick = mp_time_sec();
|
|
|
|
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-09 20:54:49 +00:00
|
|
|
// Update current playback time.
|
|
|
|
static void handle_playback_time(struct MPContext *mpctx)
|
|
|
|
{
|
2018-05-03 02:29:11 +00:00
|
|
|
if (mpctx->vo_chain &&
|
|
|
|
!mpctx->vo_chain->is_sparse &&
|
2016-05-09 20:54:49 +00:00
|
|
|
mpctx->video_status >= STATUS_PLAYING &&
|
|
|
|
mpctx->video_status < STATUS_EOF)
|
|
|
|
{
|
|
|
|
mpctx->playback_pts = mpctx->video_pts;
|
|
|
|
} else if (mpctx->audio_status >= STATUS_PLAYING &&
|
|
|
|
mpctx->audio_status < STATUS_EOF)
|
|
|
|
{
|
|
|
|
mpctx->playback_pts = playing_audio_pts(mpctx);
|
player: set playback_pts in hr-seek past EOF case
Hr-seek past the last frame instantly enters EOF, which means
handle_playback_time() will not set playback_pts to the video PTS (as
all video frames are skipped), which leads to the playback time being
taken from the last seek target. This results in confusing behavior,
especially since the seek time will be clipped to the file duration for
display, but not for further relative seeks.
Obviously, the time should be set to the last video frame, so use the
last video frame as fallback if both audio and video have ended. Also,
since the same problem exists with audio-only playback, add a fallback
for audio PTS too. We don't know which was the "last" fragment of media
played (to decide whether to use the audio or video PTS as the
fallback), but it doesn't matter since the maximum works.
This could lead to some undesired effects. In particular the audio PTS
is basically a bad guess, and is for example not clipped against --end.
(But the ridiculous way audio syncing and clamping currently works, I'm
not going to touch that shit unless I rewrite it completely.) The cover
art case is slightly broken: using --keep-open with keyframe seeks will
result in 0 as playback PTS (the video PTS). OK, who cares, it got late.
Also casually get rid of last_vo_pts, since that barely made any sense
at all.
Fixes: #7487
2020-02-28 00:37:41 +00:00
|
|
|
} else if (mpctx->video_status == STATUS_EOF &&
|
|
|
|
mpctx->audio_status == STATUS_EOF)
|
|
|
|
{
|
2023-08-25 21:45:14 +00:00
|
|
|
double apts = playing_audio_pts(mpctx);
|
player: set playback_pts in hr-seek past EOF case
Hr-seek past the last frame instantly enters EOF, which means
handle_playback_time() will not set playback_pts to the video PTS (as
all video frames are skipped), which leads to the playback time being
taken from the last seek target. This results in confusing behavior,
especially since the seek time will be clipped to the file duration for
display, but not for further relative seeks.
Obviously, the time should be set to the last video frame, so use the
last video frame as fallback if both audio and video have ended. Also,
since the same problem exists with audio-only playback, add a fallback
for audio PTS too. We don't know which was the "last" fragment of media
played (to decide whether to use the audio or video PTS as the
fallback), but it doesn't matter since the maximum works.
This could lead to some undesired effects. In particular the audio PTS
is basically a bad guess, and is for example not clipped against --end.
(But the ridiculous way audio syncing and clamping currently works, I'm
not going to touch that shit unless I rewrite it completely.) The cover
art case is slightly broken: using --keep-open with keyframe seeks will
result in 0 as playback PTS (the video PTS). OK, who cares, it got late.
Also casually get rid of last_vo_pts, since that barely made any sense
at all.
Fixes: #7487
2020-02-28 00:37:41 +00:00
|
|
|
double vpts = mpctx->video_pts;
|
|
|
|
double mpts = MP_PTS_MAX(apts, vpts);
|
|
|
|
if (mpts != MP_NOPTS_VALUE)
|
|
|
|
mpctx->playback_pts = mpts;
|
2016-05-09 20:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-29 11:37:10 +00:00
|
|
|
// We always make sure audio and video buffers are filled before actually
|
|
|
|
// starting playback. This code handles starting them at the same time.
|
2016-02-27 19:02:51 +00:00
|
|
|
static void handle_playback_restart(struct MPContext *mpctx)
|
2013-10-29 21:38:29 +00:00
|
|
|
{
|
|
|
|
struct MPOpts *opts = mpctx->opts;
|
2015-01-29 11:37:10 +00:00
|
|
|
|
|
|
|
if (mpctx->audio_status < STATUS_READY ||
|
|
|
|
mpctx->video_status < STATUS_READY)
|
|
|
|
return;
|
|
|
|
|
2019-05-17 19:59:52 +00:00
|
|
|
handle_update_cache(mpctx);
|
2018-01-03 20:48:42 +00:00
|
|
|
|
2015-01-29 11:37:10 +00:00
|
|
|
if (mpctx->video_status == STATUS_READY) {
|
|
|
|
mpctx->video_status = STATUS_PLAYING;
|
|
|
|
get_relative_time(mpctx);
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
player: dumb seeking related stuff, make audio hr-seek default
Try to deal with various corner cases. But when I fix one thing, another
thing breaks. (And it's 50/50 whether I find the breakage immediately or
a few months later.) So results may vary.
The default for--hr-seek is changed to "default" (not creative enough to
find a better name). In this mode, audio seeking is exact if there is no
video, or if the video has only a single frame. This change is actually
pretty dumb, since audio frames are usually small enough that exact
seeking does not really add much. But it gets rid of some weird special
cases.
Internally, the most important change is that is_coverart and is_sparse
handling is merged. is_sparse was originally just a special case for
weird .ts streams that have the corresponding low-level flag set. The
idea is that they're pretty similar anyway, so this would reduce the
number of corner cases. But I'm not sure if this doesn't break the
original intended use case for it (I don't have a sample anyway).
This changes last-frame handling, and respects the duration of the last
frame only if audio is disabled. This is mostly "coincidental" due to
the need to make seeking past EOF trigger player exit, and is caused by
setting STATUS_EOF early. On the other hand, this might have been this
way before (see removed chunk close to it).
2020-02-28 16:15:07 +00:00
|
|
|
MP_DBG(mpctx, "starting video playback\n");
|
2015-01-29 11:37:10 +00:00
|
|
|
}
|
|
|
|
|
2016-05-09 20:54:49 +00:00
|
|
|
if (mpctx->audio_status == STATUS_READY) {
|
|
|
|
// If a new seek is queued while the current one finishes, don't
|
|
|
|
// actually play the audio, but resume seeking immediately.
|
|
|
|
if (mpctx->seek.type && mpctx->video_status == STATUS_PLAYING) {
|
|
|
|
handle_playback_time(mpctx);
|
player: make repeated hr-seeks past EOF trigger EOF as expected
If you have a normal file with audio and video, and keep "spamming"
forward hr-seeks, the player just kept showing the last video frame
instead of exiting or playing the next file. This started happening
since commit 6bcda94cb. Although not a bug per se, it was odd, and very
user-noticable.
The main problem was that the pending seek command was processed before
the EOF was "noticed". Processing the command reset everything, so the
player did not terminate playback, but repeated the seek.
This commit restores the old behavior.
For one, it makes video return the correct status (video.c). The
parameter is a bit ugly, but better than duplicating the logic or having
another MPContext field. (As a minor detail, setting r=VD_EOF makes sure
have_new_frame() returns true, rather than going through another
iteration or whatever the hell will happen instead, which would clobber
logical_eof.)
Another thing is making the seek logic actually wait until the seek
outcome has been determined if audio is also active. Audio needs to wait
for video in order to get the video seek target position. (Which in turn
is because hr-seek still "snaps" to video frames. You can't seek in
between two frames, so audio can't just use the seek target, but always
has to wait on the timestamp of the video frame. This has other
disadvantages and is a misdesign, but not something I'll fix today.)
In theory, this might make hr-seeks less responsive, because it needs to
fully decode/filter the audio too, but in practice most time is spent on
video, which had to be fully decoded before this change. (In general,
hr-seek could probably just show a random frame when a queued hr-seek
overrides the current hr-seek, which would probably lead to a better
user experience, but that's out of scope.)
Fixes: #7206
2019-12-14 13:17:16 +00:00
|
|
|
mpctx->seek.flags &= ~MPSEEK_FLAG_DELAY; // immediately
|
2016-05-09 20:54:49 +00:00
|
|
|
execute_queued_seek(mpctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
audio: refactor how data is passed to AO
This replaces the two buffers (ao_chain.ao_buffer in the core, and
buffer_state.buffers in the AO) with a single queue. Instead of having a
byte based buffer, the queue is simply a list of audio frames, as output
by the decoder. This should make dataflow simpler and reduce copying.
It also attempts to simplify fill_audio_out_buffers(), the function I
always hated most, because it's full of subtle and buggy logic.
Unfortunately, I got assaulted by corner cases, dumb features (attempt
at seamless looping, really?), and other crap, so it got pretty
complicated again. fill_audio_out_buffers() is still full of subtle and
buggy logic. Maybe it got worse. On the other hand, maybe there really
is some progress. Who knows.
Originally, the data flow parts was meant to be in f_output_chain, but
due to tricky interactions with the playloop code, it's now in the dummy
filter in audio.c.
At least this improves the way the audio PTS is passed to the encoder in
encoding mode. Now it attempts to pass frames directly, along with the
pts, which should minimize timestamp problems. But to be honest, encoder
mode is one big kludge that shouldn't exist in this way.
This commit should be considered pre-alpha code. There are lots of bugs
still hiding.
2020-08-28 18:23:54 +00:00
|
|
|
audio_start_ao(mpctx);
|
2016-05-09 20:54:49 +00:00
|
|
|
}
|
2015-01-29 11:37:10 +00:00
|
|
|
|
|
|
|
if (!mpctx->restart_complete) {
|
|
|
|
mpctx->hrseek_active = false;
|
|
|
|
mpctx->restart_complete = true;
|
2017-08-14 12:02:13 +00:00
|
|
|
mpctx->current_seek = (struct seek_params){0};
|
2016-08-18 18:40:23 +00:00
|
|
|
handle_playback_time(mpctx);
|
2015-01-29 11:37:10 +00:00
|
|
|
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
|
2017-04-14 16:56:03 +00:00
|
|
|
update_core_idle_state(mpctx);
|
2015-01-29 11:37:10 +00:00
|
|
|
if (!mpctx->playing_msg_shown) {
|
2015-02-18 20:10:08 +00:00
|
|
|
if (opts->playing_msg && opts->playing_msg[0]) {
|
2015-01-29 11:37:10 +00:00
|
|
|
char *msg =
|
|
|
|
mp_property_expand_escaped_string(mpctx, opts->playing_msg);
|
2015-07-13 11:06:09 +00:00
|
|
|
struct mp_log *log = mp_log_new(NULL, mpctx->log, "!term-msg");
|
|
|
|
mp_info(log, "%s\n", msg);
|
|
|
|
talloc_free(log);
|
2015-01-29 11:37:10 +00:00
|
|
|
talloc_free(msg);
|
|
|
|
}
|
2015-02-18 20:10:08 +00:00
|
|
|
if (opts->osd_playing_msg && opts->osd_playing_msg[0]) {
|
2015-01-29 11:37:10 +00:00
|
|
|
char *msg =
|
|
|
|
mp_property_expand_escaped_string(mpctx, opts->osd_playing_msg);
|
2022-04-07 22:00:00 +00:00
|
|
|
set_osd_msg(mpctx, 1, opts->osd_playing_msg_duration ?
|
|
|
|
opts->osd_playing_msg_duration : opts->osd_duration,
|
|
|
|
"%s", msg);
|
2015-01-29 11:37:10 +00:00
|
|
|
talloc_free(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mpctx->playing_msg_shown = true;
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it
makes it work with backward playback mode.
The most obvious change is that the both the A and B point need to be
set now before any looping happens. Unlike before, unset points don't
implicitly use the start or end of the file. I think the old behavior
was a feature that was explicitly added/wanted. Well, it's gone now.
This is because of 2 reasons:
1. I never liked this feature, and it always got in my way (as user).
2. It's inherently annoying with backward playback mode.
In backward playback mode, the user wants to set A/B in the wrong order.
The ab-loop command will first set A, then B, so if you use this command
during backward playback, A will be set to a higher timestamps than B.
If you switch back to forward playback mode, the loop would stop
working. I want the loop to just continue to work, and the chosen
solution conflicts with the removed feature.
The order issue above _could_ be fixed by also switching the AB-loop
user option values around on direction switch. But there are no other
instances of option changes magically affecting other options, and doing
this would probably lead to unexpected misery (dying from corner cases
and such).
Another solution is sorting the A/B points by timestamps after copying
them from the user options. Then A/B options set in backward mode will
work in forward mode. This is the chosen solution. If you sort the
points, you don't know anymore whether the unset point is supposed to
signify the end or the start of the file.
The AB-loop code is slightly better abstracted now, so it should be easy
to restore the removed feature. It would still require coming up with a
solution for backwards playback, though.
A minor change is that if one point is set and the other is unset, I'm
rendering both the chapter markers and the marker for the set point.
Why? I don't know. My test file had chapters, and I guess I decided this
looked better.
This commit also fixes some subtle and obvious issues that I already
forgot about when I wrote this commit message. It cleans up some minor
code duplication and nonsense too.
Regarding backward playback, the code uses an unsanitary mix of internal
("transformed") and user timestamps. So the play_dir variable appears
more than usual.
To mention one unfixed issue: if you set an AB-loop that is completely
past the end of the file, it will get stuck in an infinite seeking loop
once playback reaches the end of the file. Fixing this reliably seemed
annoying, so the fix is "just don't do this". It's not a hard freeze
anyway.
2019-05-26 23:24:22 +00:00
|
|
|
update_ab_loop_clip(mpctx);
|
2020-09-21 17:36:25 +00:00
|
|
|
MP_VERBOSE(mpctx, "playback restart complete @ %f, audio=%s, video=%s%s\n",
|
2020-08-27 09:34:53 +00:00
|
|
|
mpctx->playback_pts, mp_status_str(mpctx->audio_status),
|
2020-09-21 17:36:25 +00:00
|
|
|
mp_status_str(mpctx->video_status),
|
|
|
|
get_internal_paused(mpctx) ? " (paused)" : "");
|
2020-02-28 14:41:45 +00:00
|
|
|
|
player: clamp relative seek base time to nominal duration
Since b74c09efbf7, audio-only files let you seek to arbitrary points
beyond the end of the file (but still displayed the time clamped to the
nominal file duration). This was confusing and just not wanted. The
reason is probably that the commit removed setting the audio PTS for
data before the seek target, so if you seek past the end of the file,
the audio PTS is never set. This in turn means the logic to determine
the current playback time has no PTS at all, and thus falls back to the
seek PTS.
This happened in the past for other reasons (like efe43d768f). I have
enough of this, so I'm just changing the code to clamp the seek
timestamp to a "known" range. Do this when seeking ends, because in the
fallback case, the playback time shouldn't be stuck at e.g. "end +
seek_argument". Also do it when initiating a new seek (mp_seek), because
if the previous seek hasn't finished yet, it shouldn't add them up and
allow it to go "out of range" either. The latter is especially relevant
for hr-seeks.
Doing this clamping is problematic because the duration is a possibly
invalid value from the demuxer, or just missing. Especially with
timestamp resets, fun sometimes happens, and in these situations it
might be better not to clamp.
One could argue you should just use the last audio timestamp returned by
the decoder or demuxer (even if that directly conflicts with --end), but
that sounds even more hairy.
In summary: what a dumb waste of time, what the fuck.
2020-09-10 21:24:35 +00:00
|
|
|
// To avoid strange effects when using relative seeks, especially if
|
|
|
|
// there are no proper audio & video timestamps (seeks after EOF).
|
|
|
|
double length = get_time_length(mpctx);
|
|
|
|
if (mpctx->last_seek_pts != MP_NOPTS_VALUE && length >= 0)
|
|
|
|
mpctx->last_seek_pts = MPCLAMP(mpctx->last_seek_pts, 0, length);
|
|
|
|
|
2020-02-28 14:41:45 +00:00
|
|
|
// Continuous seeks past EOF => treat as EOF instead of repeating seek.
|
|
|
|
if (mpctx->seek.type == MPSEEK_RELATIVE && mpctx->seek.amount > 0 &&
|
|
|
|
mpctx->video_status == STATUS_EOF &&
|
|
|
|
mpctx->audio_status == STATUS_EOF)
|
|
|
|
mpctx->seek = (struct seek_params){0};
|
2015-01-29 11:37:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-15 20:03:51 +00:00
|
|
|
static void handle_eof(struct MPContext *mpctx)
|
2015-01-29 11:37:10 +00:00
|
|
|
{
|
2020-06-10 09:43:19 +00:00
|
|
|
if (mpctx->seek.type)
|
|
|
|
return; // for proper keep-open operation
|
|
|
|
|
2015-01-29 11:37:10 +00:00
|
|
|
/* Don't quit while paused and we're displaying the last video frame. On the
|
|
|
|
* other hand, if we don't have a video frame, then the user probably seeked
|
|
|
|
* outside of the video, and we do want to quit. */
|
|
|
|
bool prevent_eof =
|
2024-05-25 21:26:50 +00:00
|
|
|
mpctx->paused && mpctx->video_out && vo_has_frame(mpctx->video_out) &&
|
|
|
|
!mpctx->vo_chain->is_coverart;
|
2015-01-29 11:37:10 +00:00
|
|
|
/* It's possible for the user to simultaneously switch both audio
|
|
|
|
* and video streams to "disabled" at runtime. Handle this by waiting
|
|
|
|
* rather than immediately stopping playback due to EOF.
|
|
|
|
*/
|
2016-01-21 21:24:20 +00:00
|
|
|
if ((mpctx->ao_chain || mpctx->vo_chain) && !prevent_eof &&
|
2015-01-29 11:37:10 +00:00
|
|
|
mpctx->audio_status == STATUS_EOF &&
|
2016-02-15 20:03:51 +00:00
|
|
|
mpctx->video_status == STATUS_EOF &&
|
|
|
|
!mpctx->stop_play)
|
2015-01-29 11:37:10 +00:00
|
|
|
{
|
2016-02-15 20:03:51 +00:00
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
2015-01-29 11:37:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_playloop(struct MPContext *mpctx)
|
|
|
|
{
|
2013-10-29 21:38:29 +00:00
|
|
|
if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) {
|
2020-09-03 13:44:35 +00:00
|
|
|
mpctx->stop_play = PT_ERROR;
|
2013-10-29 21:38:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-16 20:40:21 +00:00
|
|
|
update_demuxer_properties(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2014-07-30 21:24:08 +00:00
|
|
|
handle_cursor_autohide(mpctx);
|
2014-11-02 19:26:51 +00:00
|
|
|
handle_vo_events(mpctx);
|
2015-02-14 14:13:58 +00:00
|
|
|
handle_command_updates(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2018-01-26 03:36:47 +00:00
|
|
|
if (mpctx->lavfi && mp_filter_has_failed(mpctx->lavfi))
|
|
|
|
mpctx->stop_play = AT_END_OF_FILE;
|
2016-02-05 22:19:56 +00:00
|
|
|
|
2016-05-09 20:54:49 +00:00
|
|
|
fill_audio_out_buffers(mpctx);
|
|
|
|
write_video(mpctx);
|
|
|
|
|
2016-02-27 19:02:51 +00:00
|
|
|
handle_playback_restart(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2016-05-09 20:54:49 +00:00
|
|
|
handle_playback_time(mpctx);
|
|
|
|
|
2014-08-03 18:25:03 +00:00
|
|
|
handle_dummy_ticks(mpctx);
|
|
|
|
|
2014-07-28 18:40:43 +00:00
|
|
|
update_osd_msg(mpctx);
|
2024-04-14 21:37:32 +00:00
|
|
|
|
|
|
|
handle_update_subtitles(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
player: make screenshot each-frame mode more accurate
Due to asynchronicity, we generally can't guarantee that a video frame
matches up with other events such as playback time change exactly (since
decoding, presentation, and property update all happen at different
times). This is a complaint in the referenced bug report, where
screenshot filenames in each-frame screenshot did not use the correct
timestamp, and instead was lagging behind by 1 frame.
But in this case, synchronicity was already pretty much forced with wait
calls. The only problem was that the playback time was updated at a
later time, which results in the observed 1 frame lag. Fix this by
moving the place where the screenshot is triggered in this mode.
Normal screenshots may still have the old problem. There is no effort
made to guarantee the timestamps absolutely line up, same as with the
OSD. (If you want a guarantee, you need to use a video filter, such as
libavfilter's drawtext. These will obviously use the proper timestamp,
instead of going through the somewhat asynchronous property etc. system
in the player frontend.)
Fixes: #7433
2020-02-07 12:32:07 +00:00
|
|
|
handle_each_frame_screenshot(mpctx);
|
|
|
|
|
2016-02-15 20:03:51 +00:00
|
|
|
handle_eof(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2014-09-23 23:31:45 +00:00
|
|
|
handle_loop_file(mpctx);
|
|
|
|
|
2014-07-29 22:22:25 +00:00
|
|
|
handle_keep_open(mpctx);
|
|
|
|
|
2014-08-03 18:25:03 +00:00
|
|
|
handle_sstep(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2017-04-14 16:56:03 +00:00
|
|
|
update_core_idle_state(mpctx);
|
|
|
|
|
2019-12-14 13:18:10 +00:00
|
|
|
execute_queued_seek(mpctx);
|
|
|
|
|
2014-07-18 13:04:46 +00:00
|
|
|
if (mpctx->stop_play)
|
2014-08-03 17:14:30 +00:00
|
|
|
return;
|
2014-07-18 13:04:46 +00:00
|
|
|
|
2014-08-03 17:09:22 +00:00
|
|
|
handle_osd_redraw(mpctx);
|
2014-07-18 13:04:46 +00:00
|
|
|
|
2020-03-08 18:37:20 +00:00
|
|
|
if (mp_filter_graph_run(mpctx->filter_root))
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
2019-12-14 13:18:10 +00:00
|
|
|
|
2016-09-16 12:25:50 +00:00
|
|
|
mp_wait_events(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2019-05-17 19:59:52 +00:00
|
|
|
handle_update_cache(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2014-09-06 14:57:46 +00:00
|
|
|
mp_process_input(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
|
2014-04-27 20:28:07 +00:00
|
|
|
handle_chapter_change(mpctx);
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
handle_force_window(mpctx, false);
|
|
|
|
}
|
|
|
|
|
2014-10-06 19:20:38 +00:00
|
|
|
void mp_idle(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
handle_dummy_ticks(mpctx);
|
2016-09-16 12:25:50 +00:00
|
|
|
mp_wait_events(mpctx);
|
2014-10-06 19:20:38 +00:00
|
|
|
mp_process_input(mpctx);
|
2015-05-02 15:53:25 +00:00
|
|
|
handle_command_updates(mpctx);
|
2019-05-17 19:59:52 +00:00
|
|
|
handle_update_cache(mpctx);
|
2014-10-10 16:51:44 +00:00
|
|
|
handle_cursor_autohide(mpctx);
|
2014-11-02 19:26:51 +00:00
|
|
|
handle_vo_events(mpctx);
|
2014-10-06 19:20:38 +00:00
|
|
|
update_osd_msg(mpctx);
|
|
|
|
handle_osd_redraw(mpctx);
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:38:29 +00:00
|
|
|
// Waiting for the slave master to send us a new file to play.
|
|
|
|
void idle_loop(struct MPContext *mpctx)
|
|
|
|
{
|
|
|
|
// ================= idle loop (STOP state) =========================
|
|
|
|
bool need_reinit = true;
|
player: fix subtle idle mode differences on early program start
If the user manages to run a "loadfile x append" command before the loop
in mp_play_files() is entered, then the player could start playing
these. This isn't expected, because appending files to the playlist in
idle mode does not normally start playback. It could happen because
there is a short time window where commands are processed before the
loop is entered (such as running the command when a script is loaded).
The idle mode semantics are pretty weird: if files were provided in
advance (on the command line), then these should be played immediately.
But if idle mode was already entered, and something is appended to the
playlist using "append", i.e. without explicitly triggering playback,
then it should remain in idle mode.
Try to follow this by redefining PT_STOP to strictly mean idle mode.
Remove the playlist->current check from idle_loop(), since only the
stop_play field counts now (cf. what mp_set_playlist_entry() does).
This actually introduces the possibility that playlist->current, and
with it playlist-pos, are set to something, even though playback is not
active or being started. Previously, this was only possible during state
transitions, such as when changing playlist entries.
Very annoyingly, this means the current way MPV_EVENT_IDLE was sent
doesn't work anymore. Logically, idle mode can be "active" even if
idle_loop() was not entered yet (between the time after mp_initialize()
and before the loop in mp_play_files()). Instead of worrying about this,
redo the "idle-active" property, and deprecate the event.
See: #7543
2020-03-21 13:01:38 +00:00
|
|
|
while (mpctx->opts->player_idle_mode && mpctx->stop_play == PT_STOP) {
|
2014-02-26 19:45:24 +00:00
|
|
|
if (need_reinit) {
|
2014-10-03 17:57:49 +00:00
|
|
|
uninit_audio_out(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
handle_force_window(mpctx, true);
|
2016-09-16 12:24:15 +00:00
|
|
|
mp_wakeup_core(mpctx);
|
2015-08-20 13:25:32 +00:00
|
|
|
mp_notify(mpctx, MPV_EVENT_IDLE, NULL);
|
2014-09-12 23:13:36 +00:00
|
|
|
need_reinit = false;
|
2014-02-26 19:45:24 +00:00
|
|
|
}
|
2014-10-06 19:20:38 +00:00
|
|
|
mp_idle(mpctx);
|
2013-10-29 21:38:29 +00:00
|
|
|
}
|
|
|
|
}
|