Compare commits

...
47 Commits
Author SHA1 Message Date
Martin Herkt c226bc7616 Release 0.20.0 2016-08-26 19:40:58 +02:00
Martin Herkt e60a36bd4e Merge branch 'master' into release/current 2016-08-26 19:15:57 +02:00
wm4 bc97d60542 demux: close underlying stream if it's fully read anyway
This is for text subtitles. libavformat currently always reads text
subtitles completely on init. This means the underlying stream is
useless and will consume resources for various reasons (network
connection, file handles, cache memory).

Take care of this by closing the underlying stream if we think the
demuxer has read everything. Since libavformat doesn't export whether it
did (or whether it may access the stream again in the future), we rely
on a whitelist. Also, instead of setting the stream to NULL or so, set
it to an empty dummy stream. This way we don't have to litter the code
with NULL checks.

demux_lavf.c needs extra changes, because it tries to do clever things
for the sake of subtitle charset conversion.

The main reason we keep the demuxer etc. open is because we fell for
libavformat being so generic, and we tried to remove corresponding
special-cases in the higher-level player code. Some of this is forced
due to ass/srt mkv/mp4 demuxing being very similar to external text
files. In the future it might be better to do this in a more
straight-forward way, such as reading text subtitles into libass and
then discarding the demuxer entirely, but for aforementioned reasons
this could be more of a mess than the solution introduced by this
commit.

Probably fixes #3456.
2016-08-26 13:34:52 +02:00
wm4 4121016689 player: don't directly access demuxer->stream
Cleaner and makes it easier to change the underlying stream.

mp_property_stream_capture() still directly accesses it directly via
demux_run_on_thread(). This is evil, but still somewhat sane and is not
getting into the way here.

Not sure if I got all field accesses.
2016-08-26 13:33:38 +02:00
wm4 b636b19058 cache: don't use a backbuffer if the cache is as large as the file
It's just wasted memory.

One corner case is when a file grows during playback, but this is rare
and usually happens on-disk only. The cache size was generally limited
before this change already, so no reason to care.

As an unrelated change, move the cache size info to the resize_cache()
function. There's really no reason not to do this, and it's slightly
more informative if the user changes the cache size at runtime.
2016-08-26 13:33:38 +02:00
wm4 cb9b60ef19 stream_memory: disable stream cache
Obviously makes no sense and just wastes resources.
2016-08-26 13:33:38 +02:00
James Ross-Gowan 31f28da0f3 w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.

Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.

Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.

This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-26 20:02:58 +10:00
Avi Halachmi (:avih) ef0b2e33b1 vo_opengl: angle: new opengl flag to control DirectComposition
On some systems DirectComposition might behave poorly. Add an opengl
suboption flag 'dcomposition' (default=yes) which can disable it.
2016-08-25 23:47:37 +10:00
wm4 2a5b61244f x11: work around mutter fullscreen issue
If the video has the same size as the screen, starting with --fs and
then leaving fullscreen doesn't actually leave fullscreen.

The reason is that mpv tries to restore the previous window size if
necessary (otherwise, you'd end up with a Window of nearly the same size
as the screen with some WMs). It will typically restore with the
rectangle set exactly to the screen if no other position or size is
forced. This triggers pre-EWMH fullscreen mode, which WMs detect using
various heuristics.

Apparently we triggered this with mutter (but strangely no other WMs).
It's possible that pre-EWMH fullscreen mode actually requires removing
decorations, and mutter either ignores this. But this is speculation and
I haven't checked.

Work this around by reducing the requested size by 1 pixel if it
happens.

This was observed with mutter 3.18.2.

Fixes #2072.
2016-08-25 14:16:10 +02:00
wm4 c4ba600832 audio: avoid missed wakeups with ab-loops
Could get "stuck".
2016-08-24 12:14:48 +02:00
wm4 c218d9e960 vd_lavc: minor simplification
The timebase is now always valid.
2016-08-23 12:07:46 +02:00
wm4 a47d849df7 ad_lavc: actually tell decoder about the timebase
Essentially forgotten in commit 05e4df3f.
2016-08-23 12:06:47 +02:00
wm4 6980575e15 ao_alsa: log if retrieving supported channel maps fails
It's a sign that the driver doesn't implement the channel map API.
2016-08-22 20:05:34 +02:00
wm4 724f60bf9a audio: do not apply --audio-channels if spdif passthrough is in use
If spdif is enabled, the channel layout has no meaning other than
setting the number of channels. The number of channels must be fixed to
achieve the exact bitrate required.

Fixes #3445.
2016-08-22 12:12:10 +02:00
wm4 74d4073771 demux: demote packet queue overflow to a warning
It doesn't necessarily have to mean anything bad.

We're still too lazy to provide any more detailed information (e.g.
whether this happened to likely bad interleaving, excessive amount of
packets like with some ASS subs, or that the readahead user option is
limitted by the packet queue size).
2016-08-22 12:10:55 +02:00
wm4 93104142f4 player: log if video is considered an image
It's a heristic that can fail, so better log it.
2016-08-21 12:27:48 +02:00
wm4 9a5e062a04 manpage: fix typo 2016-08-20 18:15:24 +02:00
wm4 6b676d82fd vo: be more trusting to estimated display FPS
This should actually be rather safe - we already check whether the
estimated value jitters less than the (possibly untrustworthy) nominal
one. Remove a "safety" check that disabled this code for small
deviations, and make it trigger sooner into playback. Also lower the log
level of messages about using the estimated display FPS down to verbose.

Normally there's another mechanism for smoothing out minor estimation
differences, but that is not good enough here.

This possibly improves behavior as reported in #3433, which can be
reproduced with --vo=null:fps=48.426 --display-fps=48 (though it doesn't
consider the jitter introduced by a real VO).
2016-08-20 18:15:17 +02:00
wm4 af103aebd7 player: update Windows playback state asynchronously
Doing this required synchronizing with the VO thread, which could lead
to audio dropouts if the VO was frozen (which can happen in practice if
e.g. an opengl_cb user is not doing what the API demands).

Add a way to send asynchronous VOCTRLs, and use that for the playback
state. In theory, it would be better to make this status update a
several function and to "merge" several queued update, but that would be
slightly more effort/code, and the update is so infrequent that the
merging would never happen anyway.

The change to vo_destroy() is to make sure all queued asynchronous
reuqests are finished before making the vo_thread exit.

Even though it's only used on MS Windows, it's run on any platform with
any VO, which makes this worse.
2016-08-20 14:46:38 +02:00
wm4 969c011522 ta: add a helper macro 2016-08-20 14:41:12 +02:00
wm4 1a8af89b7d vo: fix mismatching types in pointer operation
run_control() dereferences an uint32_t as int. Whether this is allowed
depends on what uint32_t is typedefed to (dereferencing an unsigned int
as int should be fine). Fix it by always using int. The uint32_t type
never really made sense.
2016-08-20 14:11:35 +02:00
Paul B Mahol e057629493 af_lavrresample: better swr reinitialization 2016-08-20 11:37:06 +02:00
rr- cb31e72589 manpage: info about --panscan vs. --video-unscaled 2016-08-19 22:53:17 +02:00
rr- ed644b0d33 aspect: add --video-unscaled=downscale-big 2016-08-19 22:51:46 +02:00
wm4 23993e91f3 af_lavrresample: fix error if resampler could not be recreated
There are situations where the resampler is destroyed and recreated
during playback. If recreating the resampler unexpectedly fails, the
filter function is supposed to return an error. This wasn't done
correctly, because get_out_samples() accessed the resampler before the
check. Move the check up to fix this.
2016-08-19 22:27:15 +02:00
James Ross-Gowan 68dc869d6a command: prevent O(n^2) behaviour for playlist property
When fetching the playlist property, playlist_entry_from_index would be
called for each playlist entry, which traversed a linked list to get the
entry corresponding to the specified index. This was very slow for large
playlists. Since get_playlist_entry is called for each index in order,
it can avoid a full traversal of the linked list by using the next
pointer on the previously requested entry.
2016-08-20 00:07:32 +10:00
wm4 e5f61c2bd5 vd_lavc: remove unnecessary initialization
This is already the default value.
2016-08-19 15:00:58 +02:00
wm4 2fbd1d3610 demux: change fps field to double
Because why not.
2016-08-19 15:00:58 +02:00
wm4 05e4df3f0c video/audio: always provide "proper" timestamps to libavcodec
Instead of passing through double float timestamps opaquely, pass real
timestamps. Do so by always setting a valid timebase on the
AVCodecContext for audio and video decoding.

Specifically try not to round timestamps to a too coarse timebase, which
could round off small adjustments to timestamps (such as for start time
rebasing or demux_timeline). If the timebase is considered too coarse,
make it finer.

This gets rid of the need to do this specifically for some hardware
decoding wrapper. The old method of passing through double timestamps
was also a bit questionable. While libavcodec is not supposed to
interpret timestamps at all if no timebase is provided, it was
needlessly tricky. Also, it actually does compare them with
AV_NOPTS_VALUE. This change will probably also reduce confusion in the
future.
2016-08-19 14:59:30 +02:00
wm4 4aaa83339c av_common: improve rounding for float->int timestamp conversions 2016-08-19 14:11:32 +02:00
wm4 5d2dfbdfca player: refresh very low framerate video on filter changes
Limit the max. time the refresh is delayed. Make it refresh at all if
image mode is enabled.

Fixes #3435.
2016-08-19 09:38:45 +02:00
wm4 4e3663a2da vf_rotate: allow arbitrary rotation
vf_rotate selects the correct filter for 90° rotation, but it can be
extended to use lavfi's vf_rotate as fallback.

See #3434.
2016-08-19 09:37:52 +02:00
wm4 e6952c7053 github: fix typo 2016-08-18 21:17:25 +02:00
wm4 0f83caf96a github: encourage bug reports
Word this sentence slightly more positively because we are a positive
project.
2016-08-18 21:05:20 +02:00
wm4 1e53fc3a15 demux_lavf: don't report start time for ogg
Better with ogg shoutcast streams. These have PTS resets on each
playlist item, so the PTS would usually reset to some negative value.
2016-08-18 21:03:01 +02:00
wm4 a1dec6f54a player: make looping slightly more seamless
This affects A-B loops and --loop-file, and audio. Instead of dropping
audio by resetting the AO, try to make it seamless by not sending data
after the loop point, and after the seek send new data without a reset.
2016-08-18 20:40:23 +02:00
wm4 bbcd0b6a03 audio: improve aspects of EOF handling
The code actually kept going out of EOF mode into resync mode back into
EOF mode when the playloop had to wait after an audio EOF caused by the
endpts. This would break seamless looping (as added by the next commit).

Apply endpts earlier, to ensure the filter_audio() function always
returns AD_EOF in this case.

The idiotic ao_buffer makes this an amazing pain in the ass.
2016-08-18 20:38:09 +02:00
wm4 7bba97b301 video: don't discard video frames after endpts
Instead of letting it keep decoding by trying to find a new frame,
"plug" the frame queue by not removing it. (Or actually, by putting
it back instead of discarding it.)

Matters for seamless looping (following commits), and possibly some
other corner cases.

The added function vf_unread_output_frame() is a bit of a sin, but still
reasonable, since its implementation is trivial.
2016-08-18 20:37:25 +02:00
wm4 0a0967f48b build: make avutil-mastering-metadata check slightly more robust
Fixes the specific scenario of compiling against a local Libav build,
while the system has FFmpeg installed.
2016-08-18 14:55:43 +02:00
jaseg e02cb674ce manpage: input: fix define-section syntax
Source says "force", manpage said "forced". Now both say "force".
2016-08-17 23:33:20 +02:00
wm4 f5bbb5aed2 player: add option to control duration of image display
The --image-display-duration option controls how long an image is
displayed. It's also possible to display the image forever (until manual
user interaction stops playback).

With this, the core drops the old method to "drain" video (i.e. waiting
for the last frame duration on end of playback). Instead, we reuse
MPContext.time_frame. The old mechanism was disabled for non-images
anyway.

Fixes #3425.
2016-08-17 22:46:48 +02:00
wm4 07f8b64754 m_option: add mechanism to allow inf/-inf float options
Used by the next commit.
2016-08-17 21:48:56 +02:00
wm4 3a7e86ff6e m_option: simplify float value range handling
Use clamp_double() to handle all value restriction/verification. The
error messages become a bit less nice, but they were kind of incomplete
before.
2016-08-17 21:44:05 +02:00
wm4 12e251c29e demux: fix undefined behavior with ogg metadata update
When an ogg track upodates metadata, we have to perform a complicated
runtime update due to the demux.c architecture. A detail was broken and
an array was allocated with the previous number of streams, which
usually led to invalid memory write accesses at least on the first
update.

See github commit comment on commit b9ba9a89.
2016-08-16 10:48:54 +02:00
wm4 814dacdd7d af_lavrresample: work around libswresample misbehavior
The touched code is for seek resets and such - we simply want to reset
the entire resample state. But I noticed after a seek a tiny bit of
audio is missing (mpv's audio sync code inserted silence to compensate).

It turns out swr_drop_output() either does not reset some internal state
as we expect, or it's designed to drop not only buffered samples, but
also future samples.

On the other hand, libavresample's avresample_read(), does not have this
problem. (It is also pretty explicit in what it does - return/skip
buffered data, nothing else.)

Is the libswresample behavior a bug? Or a feature? Does nobody even
know? Who cares - use the hammer to unfuck the situation. Destroy and
deallocate the libswresample context and recreate it. On every seek.
2016-08-16 00:05:34 +02:00
wm4 86fa1e6129 player: allow passing flags to queue_seek()
Change the last parameter from a bool to an int, which is supposed to
take bit-flags. The at this point only flag is MPSEEK_FLAG_DELAY, which
replaces the previous bool parameter. The old false parameter becomes 0,
the old true parameter becomes MPSEEK_FLAG_DELAY.

Since the old "immediate" parameter is now essentially inverted, two
coalesced immediate and delayed seeks end up as delayed instead of
immediate. This change doesn't matter, since there are no relative
immediate seeks anyway.
2016-08-15 21:07:32 +02:00
Martin Herkt 795b65b2ff DOCS: Update version 2016-08-15 16:16:17 +02:00
48 changed files with 628 additions and 257 deletions

No files matched your search

+3 -1
View File
@@ -13,7 +13,9 @@ If you're not using git master or the latest release, update.
Make a log file made with -v or --log-file=output.txt, paste it to
http://sprunge.us or a similar site, and replace this text with a link to it.
Do not bother reporting a bug if you do not provide the required information.
Providing a log file is strongly encouraged. It is very helpful for reproducing
bugs or getting important technical context. Without log it might not be
possible to analyze and fix certain bugs.
### Sample files
+1
View File
@@ -32,6 +32,7 @@ API changes
::
--- mpv 0.20.0
--- mpv 0.19.0
1.22 - add stream_cb API for custom protocols
--- mpv 0.18.1 ---
+4
View File
@@ -19,6 +19,10 @@ Interface changes
::
--- mpv 0.20.0 ---
- add --image-display-duration option - this also means that image duration
is not influenced by --mf-fps anymore in the general case (this is an
incompatible change)
--- mpv 0.19.0 ---
- deprecate "balance" option/property (no replacement)
--- mpv 0.18.1 ---
+2 -2
View File
@@ -526,7 +526,7 @@ Input Commands that are Possibly Subject to Change
``disable-section "<section>"``
Disable the named input section. Undoes ``enable-section``.
``define-section "<section>" "<contents>" [default|forced]``
``define-section "<section>" "<contents>" [default|force]``
Create a named input section, or replace the contents of an already existing
input section. The ``contents`` parameter uses the same syntax as the
``input.conf`` file (except that using the section syntax in it is not
@@ -544,7 +544,7 @@ Input Commands that are Possibly Subject to Change
<default> (also used if parameter omitted)
Use a key binding defined by this section only if the user hasn't
already bound this key to a command.
<forced>
<force>
Always bind a key. (The input section that was made active most recently
wins if there are ambiguities.)
+25 -4
View File
@@ -701,6 +701,8 @@ Video
controls how much of the image is cropped. May not work with all video
output drivers.
This option has no effect if ``--video-unscaled`` option is used.
``--video-aspect=<ratio>``
Override video aspect ratio, in case aspect information is incorrect or
missing in the file being played. See also ``--no-video-aspect``.
@@ -738,10 +740,12 @@ Video
choices if you encounter video that has the wrong aspect ratio in mpv,
but seems to be correct in other players.
``--video-unscaled``
``--video-unscaled=<no|yes|downscale-big>``
Disable scaling of the video. If the window is larger than the video,
black bars are added. Otherwise, the video is cropped. The video still
can be influenced by the other ``--video-...`` options.
black bars are added. Otherwise, the video is cropped, unless the option
is set to ``downscale-big``, in which case the video is fit to window. The
video still can be influenced by the other ``--video-...`` options. This
option disables the effect of ``--panscan``.
Note that the scaler algorithm may still be used, even if the video isn't
scaled. For example, this can influence chroma conversion. The video will
@@ -1873,6 +1877,23 @@ Window
file.mkv normally, then fail to open ``/dev/null``, then exit). (In
mpv 0.8.0, ``always`` was introduced, which restores the old behavior.)
``--image-display-duration=<seconds|inf>``
If the current file is an image, play the image for the given amount of
seconds (default: 1). ``inf`` means the file is kept open forever (until
the user stops playback manually).
Unlike ``--keep-open``, the player is not paused, but simply continues
playback until the time has elapsed. (It should not use any resources
during "playback".)
This affects image files, which are defined as having only 1 video frame
and no audio. The player may recognize certain non-images as images, for
example if ``--length`` is used to reduce the length to 1 frame, or if
you seek to the last frame.
This option does not affect the framerate used for ``mf://`` or
``--merge-files``. For that, use ``--mf-fps`` instead.
``--force-window=<yes|no|immediate>``
Create a video output window even if there is no video. This can be useful
when pretending that mpv is a GUI application. Currently, the window
@@ -3650,7 +3671,7 @@ Miscellaneous
``--video-sync-max-audio-change=<value>``
Maximum *additional* speed difference in percent that is applied to audio
with ``--video-sync=display-...`` (default: 0.125). Normally, the player
play the audio at the speed of the video. But if the difference between
plays the audio at the speed of the video. But if the difference between
audio and video position is too high, e.g. due to drift or other timing
errors, it will attempt to speed up or slow down audio by this additional
factor. Too low values could lead to video frame dropping or repeating if
+10
View File
@@ -881,6 +881,16 @@ Available video output drivers are:
Windows only.
``dcomposition=<yes|no>``
Allows DirectComposition when using the ANGLE backend (default: yes).
DirectComposition implies flip-model presentation, which can improve
rendering efficiency on Windows 8+ by avoiding a copy of the video frame.
mpv uses it by default where possible, but it can cause poor behaviour
with some drivers, such as a black screen or graphical corruption when
leaving full-screen mode. Use "no" to disable it.
Windows with ANGLE only.
``sw``
Continue even if a software renderer is detected.
+43
View File
@@ -1,3 +1,46 @@
Release 0.20.0
==============
Options and Commands
--------------------
Added
~~~~~
- aspect: add --video-unscaled=downscale-big
- player: add --image-display-duration option to control duration of image display (#3425)
- vo_opengl: angle: new flag (dcomposition) to control DirectComposition
Fixes and Minor Enhancements
----------------------------
- af_lavrresample: fix error if resampler could not be recreated
- audio: avoid missed wakeups with ab-loops
- audio: do not apply --audio-channels if spdif passthrough is in use (#3445)
- cache: don't use a backbuffer if the cache is as large as the file
- command: prevent O(n^2) behaviour for playlist property
- demux: close underlying stream if it's fully read anyway (#3456)
- demux: fix undefined behavior with ogg metadata update (#3451)
- player: make looping slightly more seamless
- player: refresh very low framerate video on filter changes (#3435)
- stream_memory: disable stream cache
- vf_rotate: allow arbitrary rotation (#3434)
- vo: be more trusting to estimated display FPS (#3433)
- w32_common: use hooks to detect parent window resize
- x11: work around mutter fullscreen issue (#2072)
This listing is not complete. Check DOCS/client-api-changes.rst for a history
of changes to the client API, and DOCS/interface-changes.rst for a history
of changes to other user-visible interfaces.
A complete changelog can be seen by running `git log v0.19.0..v0.20.0`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.19.0...v0.20.0 or
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.20.0..v0.20.0
Release 0.19.0
==============
+1 -1
View File
@@ -1 +1 @@
0.19.0
0.20.0
+2 -1
View File
@@ -86,7 +86,7 @@ static int init(struct dec_audio *da, const char *decoder)
struct priv *ctx = talloc_zero(NULL, struct priv);
da->priv = ctx;
ctx->codec_timebase = (AVRational){0};
ctx->codec_timebase = mp_get_codec_timebase(da->codec);
ctx->force_channel_map = c->force_channels;
@@ -103,6 +103,7 @@ static int init(struct dec_audio *da, const char *decoder)
lavc_context->refcounted_frames = 1;
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
lavc_context->time_base = ctx->codec_timebase;
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
lavc_context->request_channel_layout =
+6
View File
@@ -779,6 +779,12 @@ struct mp_audio *af_read_output_frame(struct af_stream *s)
return af_dequeue_output_frame(s->last);
}
void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame)
{
struct af_instance *af = s->last;
MP_TARRAY_INSERT_AT(af, af->out_queued, af->num_out_queued, 0, frame);
}
// Make sure the caller can change data referenced by the frame.
// Return negative error code on failure (i.e. you can't write).
int af_make_writeable(struct af_instance *af, struct mp_audio *frame)
+1
View File
@@ -148,6 +148,7 @@ void af_add_output_frame(struct af_instance *af, struct mp_audio *frame);
int af_filter_frame(struct af_stream *s, struct mp_audio *frame);
int af_output_frame(struct af_stream *s, bool eof);
struct mp_audio *af_read_output_frame(struct af_stream *s);
void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame);
int af_make_writeable(struct af_instance *af, struct mp_audio *frame);
double af_calc_delay(struct af_stream *s);
+16 -14
View File
@@ -99,10 +99,6 @@ static double get_delay(struct af_resample *s)
return avresample_get_delay(s->avrctx) / (double)s->in_rate +
avresample_available(s->avrctx) / (double)s->out_rate;
}
static void drop_all_output(struct af_resample *s)
{
while (avresample_read(s->avrctx, NULL, 1000) > 0) {}
}
static int get_out_samples(struct af_resample *s, int in_samples)
{
return avresample_get_out_samples(s->avrctx, in_samples);
@@ -113,10 +109,6 @@ static double get_delay(struct af_resample *s)
int64_t base = s->in_rate * (int64_t)s->out_rate;
return swr_get_delay(s->avrctx, base) / (double)base;
}
static void drop_all_output(struct af_resample *s)
{
while (swr_drop_output(s->avrctx, 1000) > 0) {}
}
static int get_out_samples(struct af_resample *s, int in_samples)
{
#if LIBSWRESAMPLE_VERSION_MAJOR > 1 || LIBSWRESAMPLE_VERSION_MINOR >= 2
@@ -406,8 +398,17 @@ static int control(struct af_instance *af, int cmd, void *arg)
return AF_OK;
}
case AF_CONTROL_RESET:
if (s->avrctx)
drop_all_output(s);
if (s->avrctx) {
#if HAVE_LIBSWRESAMPLE
swr_close(s->avrctx);
if (swr_init(s->avrctx) < 0) {
close_lavrr(af);
return AF_ERROR;
}
#else
while (avresample_read(s->avrctx, NULL, 1000) > 0) {}
#endif
}
return AF_OK;
}
return AF_UNKNOWN;
@@ -479,19 +480,20 @@ static void reorder_planes(struct mp_audio *mpa, int *reorder,
static int filter_resample(struct af_instance *af, struct mp_audio *in)
{
struct af_resample *s = af->priv;
struct mp_audio *out = NULL;
if (!s->avrctx)
goto error;
int samples = get_out_samples(s, in ? in->samples : 0);
struct mp_audio out_format = s->pool_fmt;
struct mp_audio *out = mp_audio_pool_get(af->out_pool, &out_format, samples);
out = mp_audio_pool_get(af->out_pool, &out_format, samples);
if (!out)
goto error;
if (in)
mp_audio_copy_attributes(out, in);
if (!s->avrctx)
goto error;
if (out->samples) {
out->samples = resample_frame(s->avrctx, out, in);
if (out->samples < 0)
+3 -1
View File
@@ -337,8 +337,10 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
struct mp_chmap_sel chmap_sel = {.tmp = p};
snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(p->alsa);
if (!maps)
if (!maps) {
MP_VERBOSE(ao, "snd_pcm_query_chmaps() returned NULL\n");
return false;
}
for (int i = 0; maps[i] != NULL; i++) {
char aname[128];
+29 -2
View File
@@ -16,6 +16,7 @@
*/
#include <assert.h>
#include <math.h>
#include <libavutil/common.h>
#include <libavutil/log.h>
@@ -84,6 +85,30 @@ void mp_set_lav_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c)
#endif
}
// Pick a "good" timebase, which will be used to convert double timestamps
// back to fractions for passing them through libavcodec.
AVRational mp_get_codec_timebase(struct mp_codec_params *c)
{
AVRational tb = {c->native_tb_num, c->native_tb_den};
if (tb.num < 1 || tb.den < 1) {
if (c->reliable_fps)
tb = av_inv_q(av_d2q(c->fps, 1000000));
if (tb.num < 1 || tb.den < 1)
tb = AV_TIME_BASE_Q;
}
// If the timebase is too coarse, raise its precision, or small adjustments
// to timestamps done between decoder and demuxer could be lost.
if (av_q2d(tb) > 0.001) {
AVRational r = av_div_q(tb, (AVRational){1, 1000});
tb.den *= (r.num + r.den - 1) / r.den;
}
av_reduce(&tb.num, &tb.den, tb.num, tb.den, 1000000);
return tb;
}
// We merely pass-through our PTS/DTS as an int64_t; libavcodec won't use it.
union pts { int64_t i; double d; };
@@ -97,8 +122,10 @@ union pts { int64_t i; double d; };
int64_t mp_pts_to_av(double mp_pts, AVRational *tb)
{
assert(sizeof(int64_t) >= sizeof(double));
if (tb && tb->num > 0 && tb->den > 0)
return mp_pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : mp_pts / av_q2d(*tb);
if (tb && tb->num > 0 && tb->den > 0) {
return mp_pts == MP_NOPTS_VALUE ?
AV_NOPTS_VALUE : llrint(mp_pts / av_q2d(*tb));
}
// The + 0.0 is to squash possible negative zero mp_pts, which would
// happen to end up as AV_NOPTS_VALUE.
return (union pts){.d = mp_pts + 0.0}.i;
+1
View File
@@ -33,6 +33,7 @@ struct mp_log;
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size);
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
void mp_set_lav_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c);
AVRational mp_get_codec_timebase(struct mp_codec_params *c);
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb);
int64_t mp_pts_to_av(double mp_pts, AVRational *tb);
double mp_pts_from_av(int64_t av_pts, AVRational *tb);
+27 -5
View File
@@ -580,12 +580,12 @@ static bool read_packet(struct demux_internal *in)
if (packs >= in->max_packs || bytes >= in->max_bytes) {
if (!in->warned_queue_overflow) {
in->warned_queue_overflow = true;
MP_ERR(in, "Too many packets in the demuxer packet queues:\n");
MP_WARN(in, "Too many packets in the demuxer packet queues:\n");
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
if (ds->selected) {
MP_ERR(in, " %s/%d: %zd packets, %zd bytes\n",
stream_type_name(ds->type), n, ds->packs, ds->bytes);
MP_WARN(in, " %s/%d: %zd packets, %zd bytes\n",
stream_type_name(ds->type), n, ds->packs, ds->bytes);
}
}
}
@@ -1031,6 +1031,7 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->ts_resets_possible = src->ts_resets_possible;
dst->fully_read = src->fully_read;
dst->start_time = src->start_time;
dst->is_network = src->is_network;
dst->priv = src->priv;
}
@@ -1039,10 +1040,10 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->metadata = mp_tags_dup(dst, src->metadata);
if (dst->num_update_stream_tags != src->num_update_stream_tags) {
dst->num_update_stream_tags = src->num_update_stream_tags;
talloc_free(dst->update_stream_tags);
dst->update_stream_tags =
talloc_zero_array(dst, struct mp_tags *, dst->num_update_stream_tags);
dst->num_update_stream_tags = src->num_update_stream_tags;
}
for (int n = 0; n < dst->num_update_stream_tags; n++) {
talloc_free(dst->update_stream_tags[n]);
@@ -1151,6 +1152,23 @@ static void demux_init_cuesheet(struct demuxer *demuxer)
}
}
static void demux_maybe_replace_stream(struct demuxer *demuxer)
{
struct demux_internal *in = demuxer->in;
assert(!in->threading && demuxer == in->d_user);
if (demuxer->fully_read) {
MP_VERBOSE(demuxer, "assuming demuxer read all data; closing stream\n");
free_stream(demuxer->stream);
demuxer->stream = open_memory_stream(NULL, 0); // dummy
in->d_thread->stream = demuxer->stream;
in->d_buffer->stream = demuxer->stream;
if (demuxer->desc->control)
demuxer->desc->control(in->d_thread, DEMUXER_CTRL_REPLACE_STREAM, NULL);
}
}
static struct demuxer *open_given_type(struct mpv_global *global,
struct mp_log *log,
const struct demuxer_desc *desc,
@@ -1172,6 +1190,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.log = mp_log_new(demuxer, log, desc->name),
.glog = log,
.filename = talloc_strdup(demuxer, stream->url),
.is_network = stream->is_network,
.events = DEMUX_EVENT_ALL,
};
demuxer->seekable = stream->seekable;
@@ -1311,6 +1330,7 @@ done:
// Convenience function: open the stream, enable the cache (according to params
// and global opts.), open the demuxer.
// (use free_demuxer_and_stream() to free the underlying stream too)
// Also for some reason may close the opened stream if it's not needed.
struct demuxer *demux_open_url(const char *url,
struct demuxer_params *params,
struct mp_cancel *cancel,
@@ -1329,7 +1349,9 @@ struct demuxer *demux_open_url(const char *url,
if (!params->disable_cache)
stream_enable_cache(&s, &opts->stream_cache);
struct demuxer *d = demux_open(s, params, global);
if (!d) {
if (d) {
demux_maybe_replace_stream(d);
} else {
params->demuxer_failed = true;
free_stream(s);
}
+3
View File
@@ -43,6 +43,7 @@ enum demux_ctrl {
DEMUXER_CTRL_STREAM_CTRL,
DEMUXER_CTRL_GET_READER_STATE,
DEMUXER_CTRL_GET_BITRATE_STATS, // double[STREAM_TYPE_COUNT]
DEMUXER_CTRL_REPLACE_STREAM,
};
struct demux_ctrl_reader_state {
@@ -183,6 +184,7 @@ typedef struct demuxer {
// packets is not slow either (unlike e.g. libavdevice pseudo-demuxers).
// Typical examples: text subtitles, playlists
bool fully_read;
bool is_network; // opened directly from a network stream
// Bitmask of DEMUX_EVENT_*
int events;
@@ -219,6 +221,7 @@ typedef struct demuxer {
// thread-safe, only the demuxer is allowed to access the stream directly.
// You can freely use demux_stream_control() to send STREAM_CTRLs, or use
// demux_pause() to get exclusive access to the stream.
// Also note that the stream can get replaced if fully_read is set.
struct stream *stream;
} demuxer_t;
+19 -4
View File
@@ -114,6 +114,7 @@ struct format_hack {
// Do not confuse player's position estimation (position is into external
// segment, with e.g. HLS, player knows about the playlist main file only).
bool clear_filepos : 1;
bool ignore_start : 1;
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
@@ -137,6 +138,9 @@ static const struct format_hack format_hacks[] = {
{"h264", .if_flags = AVFMT_NOTIMESTAMPS },
{"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
// Rebasing start time to 0 is very weird with ogg shoutcast streams.
{"ogg", .ignore_start = true},
TEXTSUB("aqtitle"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
TEXTSUB("mpl2"), TEXTSUB("mpsub"), TEXTSUB("pjs"), TEXTSUB("realtext"),
TEXTSUB("sami"), TEXTSUB("srt"), TEXTSUB("stl"), TEXTSUB("subviewer"),
@@ -158,6 +162,7 @@ static const struct format_hack format_hacks[] = {
typedef struct lavf_priv {
struct stream *stream;
bool own_stream;
char *filename;
struct format_hack format_hack;
AVInputFormat *avif;
@@ -286,8 +291,10 @@ static void convert_charset(struct demuxer *demuxer)
if (conv.start)
data = conv;
}
if (data.start)
if (data.start) {
priv->stream = open_memory_stream(data.start, data.len);
priv->own_stream = true;
}
talloc_free(alloc);
}
@@ -666,6 +673,8 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
if (sh->codec->lav_headers)
mp_copy_lav_codec_headers(sh->codec->lav_headers, codec);
#endif
sh->codec->native_tb_num = st->time_base.num;
sh->codec->native_tb_den = st->time_base.den;
if (st->disposition & AV_DISPOSITION_DEFAULT)
sh->default_track = true;
@@ -855,8 +864,8 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->ts_resets_possible =
priv->avif_flags & (AVFMT_TS_DISCONT | AVFMT_NOTIMESTAMPS);
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
if (avfc->start_time != AV_NOPTS_VALUE && !priv->format_hack.ignore_start)
demuxer->start_time = avfc->start_time / (double)AV_TIME_BASE;
demuxer->fully_read = priv->format_hack.fully_read;
@@ -1062,6 +1071,12 @@ redo:
av_seek_frame(priv->avfc, 0, stream_tell(priv->stream),
AVSEEK_FLAG_BYTE);
return DEMUXER_CTRL_OK;
case DEMUXER_CTRL_REPLACE_STREAM:
if (priv->own_stream)
free_stream(priv->stream);
priv->own_stream = false;
priv->stream = demuxer->stream;
return DEMUXER_CTRL_OK;
default:
return DEMUXER_CTRL_NOTIMPL;
}
@@ -1086,7 +1101,7 @@ static void demux_close_lavf(demuxer_t *demuxer)
#endif
}
}
if (priv->stream != demuxer->stream)
if (priv->own_stream)
free_stream(priv->stream);
talloc_free(priv);
demuxer->priv = NULL;
+1
View File
@@ -321,6 +321,7 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
c->disp_w = 0;
c->disp_h = 0;
c->fps = demuxer->opts->mf_fps;
c->reliable_fps = true;
demux_add_sh_stream(demuxer, sh);
+4
View File
@@ -145,6 +145,9 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
c->force_channels = true;
c->samplerate = opts->samplerate;
c->native_tb_num = 1;
c->native_tb_den = c->samplerate;
int f = opts->aformat;
// See PCM(): sign float bits endian
mp_set_pcm_codec(sh->codec, f & 1, f & 2, f >> 3, f & 4);
@@ -233,6 +236,7 @@ static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
c->codec = decoder;
c->codec_tag = imgfmt;
c->fps = opts->fps;
c->reliable_fps = true;
c->disp_w = width;
c->disp_h = height;
demux_add_sh_stream(demuxer, sh);
+5 -1
View File
@@ -74,6 +74,9 @@ struct mp_codec_params {
struct AVCodecContext *lav_headers;
struct AVCodecParameters *lav_codecpar;
// Timestamp granularity for converting double<->rational timestamps.
int native_tb_num, native_tb_den;
// STREAM_AUDIO
int samplerate;
struct mp_chmap channels;
@@ -84,7 +87,8 @@ struct mp_codec_params {
// STREAM_VIDEO
bool avi_dts; // use DTS timing; first frame and DTS is 0
float fps; // frames per second (set only if constant fps)
double fps; // frames per second (set only if constant fps)
bool reliable_fps; // the fps field is definitely not broken
int par_w, par_h; // pixel aspect ratio (0 if unknown/square)
int disp_w, disp_h; // display size
int rotate; // intended display rotation, in degrees, [0, 359]
+5 -22
View File
@@ -894,7 +894,8 @@ static int clamp_double(const m_option_t *opt, void *val)
v = opt->min;
r = M_OPT_OUT_OF_RANGE;
}
if (!isfinite(v)) {
// (setting max/min to INFINITY/-INFINITY is allowed)
if (!isfinite(v) && v != opt->max && v != opt->min) {
v = opt->min;
r = M_OPT_OUT_OF_RANGE;
}
@@ -921,22 +922,8 @@ static int parse_double(struct mp_log *log, const m_option_t *opt,
return M_OPT_INVALID;
}
if (opt->flags & M_OPT_MIN)
if (tmp_float < opt->min) {
mp_err(log, "The %.*s option must be >= %f: %.*s\n",
BSTR_P(name), opt->min, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if (opt->flags & M_OPT_MAX)
if (tmp_float > opt->max) {
mp_err(log, "The %.*s option must be <= %f: %.*s\n",
BSTR_P(name), opt->max, BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
if (!isfinite(tmp_float)) {
mp_err(log, "The %.*s option must be a finite number: %.*s\n",
if (clamp_double(opt, &tmp_float) < 0) {
mp_err(log, "The %.*s option is out of range: %.*s\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_OUT_OF_RANGE;
}
@@ -990,11 +977,7 @@ static int double_set(const m_option_t *opt, void *dst, struct mpv_node *src)
} else {
return M_OPT_UNKNOWN;
}
if ((opt->flags & M_OPT_MIN) && val < opt->min)
return M_OPT_OUT_OF_RANGE;
if ((opt->flags & M_OPT_MAX) && val > opt->max)
return M_OPT_OUT_OF_RANGE;
if (!isfinite(val))
if (clamp_double(opt, &val) < 0)
return M_OPT_OUT_OF_RANGE;
*(double *)dst = val;
return 1;
+6 -1
View File
@@ -25,6 +25,7 @@
#include <stddef.h>
#include <sys/types.h>
#include <limits.h>
#include <math.h>
#include "config.h"
@@ -214,6 +215,8 @@ const m_option_t mp_opts[] = {
({"no", 0},
{"yes", 1},
{"always", 2})),
OPT_DOUBLE("image-display-duration", image_display_duration,
M_OPT_RANGE, 0, INFINITY),
OPT_CHOICE("index", index_mode, 0, ({"default", 1}, {"recreate", 0})),
@@ -451,7 +454,8 @@ const m_option_t mp_opts[] = {
OPT_FLOATRANGE("video-pan-y", vo.pan_y, 0, -3.0, 3.0),
OPT_FLOATRANGE("video-align-x", vo.align_x, 0, -1.0, 1.0),
OPT_FLOATRANGE("video-align-y", vo.align_y, 0, -1.0, 1.0),
OPT_FLAG("video-unscaled", vo.unscaled, 0),
OPT_CHOICE("video-unscaled", vo.unscaled, 0,
({"no", 0}, {"yes", 1}, {"downscale-big", 2})),
OPT_FLAG("force-rgba-osd-rendering", force_rgba_osd, 0),
OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 359,
({"no", -1})),
@@ -796,6 +800,7 @@ const struct MPOpts mp_default_opts = {
.play_frames = -1,
.rebase_start_time = 1,
.keep_open = 0,
.image_display_duration = 1.0,
.stream_id = { { [STREAM_AUDIO] = -1,
[STREAM_VIDEO] = -1,
[STREAM_SUB] = -1, },
+1
View File
@@ -190,6 +190,7 @@ typedef struct MPOpts {
int ignore_path_in_watch_later_config;
int pause;
int keep_open;
double image_display_duration;
char *lavfi_complex;
int stream_id[2][STREAM_TYPE_COUNT];
int stream_id_ff[STREAM_TYPE_COUNT];
+55 -26
View File
@@ -229,7 +229,7 @@ int reinit_audio_filters(struct MPContext *mpctx)
mpctx->playback_pts != MP_NOPTS_VALUE && delay > 0.2)
{
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->playback_pts,
MPSEEK_EXACT, true);
MPSEEK_EXACT, 0);
}
return 1;
}
@@ -379,14 +379,18 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
if (opts->ao_null_fallback && !spdif_fallback)
ao_flags |= AO_INIT_NULL_FALLBACK;
if (!opts->audio_output_channels.set || opts->audio_output_channels.auto_safe)
ao_flags |= AO_INIT_SAFE_MULTICHANNEL_ONLY;
if (opts->audio_stream_silence)
ao_flags |= AO_INIT_STREAM_SILENCE;
mp_chmap_sel_list(&afs->output.channels, opts->audio_output_channels.chmaps,
opts->audio_output_channels.num_chmaps);
if (af_fmt_is_pcm(afs->output.format)) {
if (!opts->audio_output_channels.set ||
opts->audio_output_channels.auto_safe)
ao_flags |= AO_INIT_SAFE_MULTICHANNEL_ONLY;
mp_chmap_sel_list(&afs->output.channels,
opts->audio_output_channels.chmaps,
opts->audio_output_channels.num_chmaps);
}
mp_audio_set_channels(&afs->output, &afs->output.channels);
@@ -722,15 +726,40 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
}
static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
int minsamples, bool eof)
static bool copy_output(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
int minsamples, double endpts, bool eof, bool *seteof)
{
struct af_stream *afs = mpctx->ao_chain->af;
while (mp_audio_buffer_samples(outbuf) < minsamples) {
if (af_output_frame(afs, eof) < 0)
return true; // error, stop doing stuff
int cursamples = mp_audio_buffer_samples(outbuf);
int maxsamples = INT_MAX;
if (endpts != MP_NOPTS_VALUE) {
double rate = afs->output.rate / mpctx->audio_speed;
double curpts = written_audio_pts(mpctx);
if (curpts != MP_NOPTS_VALUE)
maxsamples = (endpts - curpts - mpctx->opts->audio_delay) * rate;
}
struct mp_audio *mpa = af_read_output_frame(afs);
if (!mpa)
return false; // out of data
if (cursamples + mpa->samples > maxsamples) {
if (cursamples < maxsamples) {
struct mp_audio pre = *mpa;
pre.samples = maxsamples - cursamples;
mp_audio_buffer_append(outbuf, &pre);
mp_audio_skip_samples(mpa, pre.samples);
}
af_unread_output_frame(afs, mpa);
*seteof = true;
return true;
}
mp_audio_buffer_append(outbuf, mpa);
talloc_free(mpa);
}
@@ -764,20 +793,24 @@ static int decode_new_frame(struct ao_chain *ao_c)
* Return 0 on success, or negative AD_* error code.
* In the former case outbuf has at least minsamples buffered on return.
* In case of EOF/error it might or might not be. */
static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf,
static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
int minsamples)
{
struct ao_chain *ao_c = mpctx->ao_chain;
struct af_stream *afs = ao_c->af;
if (afs->initialized < 1)
return AD_ERR;
MP_STATS(ao_c, "start audio");
double endpts = get_play_end_pts(mpctx);
bool eof = false;
int res;
while (1) {
res = 0;
if (copy_output(afs, outbuf, minsamples, false))
if (copy_output(mpctx, outbuf, minsamples, endpts, false, &eof))
break;
res = decode_new_frame(ao_c);
@@ -785,13 +818,13 @@ static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf,
break;
if (res < 0) {
// drain filters first (especially for true EOF case)
copy_output(afs, outbuf, minsamples, true);
copy_output(mpctx, outbuf, minsamples, endpts, true, &eof);
break;
}
// On format change, make sure to drain the filter chain.
if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) {
copy_output(afs, outbuf, minsamples, true);
copy_output(mpctx, outbuf, minsamples, endpts, true, &eof);
res = AD_NEW_FMT;
break;
}
@@ -817,6 +850,9 @@ static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf,
return AD_ERR;
}
if (res == 0 && mp_audio_buffer_samples(outbuf) < minsamples && eof)
res = AD_EOF;
MP_STATS(ao_c, "end audio");
return res;
@@ -826,6 +862,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct ao_chain *ao_c = mpctx->ao_chain;
bool was_eof = mpctx->audio_status == STATUS_EOF;
dump_audio_stats(mpctx);
@@ -918,10 +955,10 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
playsize = playsize / align * align;
int status = AD_OK;
int status = mpctx->audio_status >= STATUS_DRAINING ? AD_EOF : AD_OK;
bool working = false;
if (playsize > mp_audio_buffer_samples(ao_c->ao_buffer)) {
status = filter_audio(mpctx->ao_chain, ao_c->ao_buffer, playsize);
status = filter_audio(mpctx, ao_c->ao_buffer, playsize);
if (status == AD_WAIT)
return;
if (status == AD_NO_PROGRESS) {
@@ -1015,17 +1052,6 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
bool partial_fill = false;
int playflags = 0;
double endpts = get_play_end_pts(mpctx);
if (endpts != MP_NOPTS_VALUE) {
double samples = (endpts - written_audio_pts(mpctx) - opts->audio_delay)
* play_samplerate;
if (playsize > samples) {
playsize = MPMAX((int)samples / align * align, 0);
audio_eof = true;
partial_fill = true;
}
}
if (playsize > mp_audio_buffer_samples(ao_c->ao_buffer)) {
playsize = mp_audio_buffer_samples(ao_c->ao_buffer);
partial_fill = true;
@@ -1057,8 +1083,11 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
mpctx->audio_status = STATUS_DRAINING;
// Wait until the AO has played all queued data. In the gapless case,
// we trigger EOF immediately, and let it play asynchronously.
if (ao_eof_reached(mpctx->ao) || opts->gapless_audio)
if (ao_eof_reached(mpctx->ao) || opts->gapless_audio) {
mpctx->audio_status = STATUS_EOF;
if (!was_eof)
mpctx->sleeptime = 0;
}
}
}
+45 -55
View File
@@ -75,8 +75,6 @@ struct command_ctx {
double last_seek_pts;
double marked_pts;
double prev_pts;
char **warned_deprecated;
int num_warned_deprecated;
@@ -220,7 +218,7 @@ static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
}
// Call before a seek, in order to allow revert-seek to undo the seek.
static void mark_seek(struct MPContext *mpctx)
void mark_seek(struct MPContext *mpctx)
{
struct command_ctx *cmd = mpctx->command_ctx;
double now = mp_time_sec();
@@ -431,11 +429,9 @@ static int mp_property_stream_path(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
// demuxer->stream as well as stream->url are immutable -> ok to access
struct stream *stream = mpctx->demuxer ? mpctx->demuxer->stream : NULL;
if (!stream || !stream->url)
if (!mpctx->demuxer || !mpctx->demuxer->filename)
return M_PROPERTY_UNAVAILABLE;
return m_property_strdup_ro(action, arg, stream->url);
return m_property_strdup_ro(action, arg, mpctx->demuxer->filename);
}
struct change_stream_capture_args {
@@ -649,7 +645,7 @@ static int mp_property_percent_pos(void *ctx, struct m_property *prop,
switch (action) {
case M_PROPERTY_SET: {
double pos = *(double *)arg;
queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, MPSEEK_DEFAULT, 0);
return M_PROPERTY_OK;
}
case M_PROPERTY_GET: {
@@ -694,7 +690,7 @@ static int mp_property_time_pos(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, 0);
return M_PROPERTY_OK;
}
return property_time(action, arg, get_current_time(mpctx));
@@ -743,7 +739,7 @@ static int mp_property_playback_time(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, 0);
return M_PROPERTY_OK;
}
return property_time(action, arg, get_playback_time(mpctx));
@@ -844,7 +840,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
} else {
double pts = chapter_start_time(mpctx, chapter);
if (pts != MP_NOPTS_VALUE) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, MPSEEK_DEFAULT, 0);
mpctx->last_chapter_seek = chapter;
mpctx->last_chapter_pts = pts;
}
@@ -2210,7 +2206,7 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
video_vd_control(vd, VDCTRL_REINIT, NULL);
double last_pts = mpctx->last_vo_pts;
if (last_pts != MP_NOPTS_VALUE)
queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, 0);
}
return M_PROPERTY_OK;
}
@@ -3194,10 +3190,30 @@ static int mp_property_playlist_pos_1(void *ctx, struct m_property *prop,
return mp_property_playlist_pos_x(ctx, prop, action, arg, 1);
}
struct get_playlist_ctx {
struct MPContext *mpctx;
int last_index;
struct playlist_entry *last_entry;
};
static int get_playlist_entry(int item, int action, void *arg, void *ctx)
{
struct MPContext *mpctx = ctx;
struct playlist_entry *e = playlist_entry_from_index(mpctx->playlist, item);
struct get_playlist_ctx *p = ctx;
struct MPContext *mpctx = p->mpctx;
struct playlist_entry *e;
// This is an optimization that prevents O(n^2) behaviour when the entire
// playlist is requested. If a request is made for the last requested entry
// or the entry immediately following it, it can be found without a full
// traversal of the linked list.
if (p->last_entry && item == p->last_index)
e = p->last_entry;
else if (p->last_entry && item == p->last_index + 1)
e = p->last_entry->next;
else
e = playlist_entry_from_index(mpctx->playlist, item);
p->last_index = item;
p->last_entry = e;
if (!e)
return M_PROPERTY_ERROR;
@@ -3237,8 +3253,10 @@ static int mp_property_playlist(void *ctx, struct m_property *prop,
*(char **)arg = res;
return M_PROPERTY_OK;
}
struct get_playlist_ctx p = { .mpctx = mpctx };
return m_property_read_list(action, arg, playlist_entry_count(mpctx->playlist),
get_playlist_entry, mpctx);
get_playlist_entry, &p);
}
static char *print_obj_osd_list(struct m_obj_settings *list)
@@ -3301,9 +3319,11 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
}
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (r > 0 && action == M_PROPERTY_SET) {
mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
if (strcmp(prop->name, "ab-loop-b") == 0) {
struct command_ctx *cctx = mpctx->command_ctx;
cctx->prev_pts = opts->ab_loop[0];
if (opts->ab_loop[1] != MP_NOPTS_VALUE &&
mpctx->playback_pts <= opts->ab_loop[1])
mpctx->ab_loop_clip = true;
}
// Update if visible
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
@@ -4622,19 +4642,19 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
mark_seek(mpctx);
switch (abs) {
case 0: { // Relative seek
queue_seek(mpctx, MPSEEK_RELATIVE, v, precision, false);
queue_seek(mpctx, MPSEEK_RELATIVE, v, precision, MPSEEK_FLAG_DELAY);
set_osd_function(mpctx, (v > 0) ? OSD_FFW : OSD_REW);
break;
}
case 1: { // Absolute seek by percentage
double ratio = v / 100.0;
double cur_pos = get_current_pos_ratio(mpctx, false);
queue_seek(mpctx, MPSEEK_FACTOR, ratio, precision, false);
queue_seek(mpctx, MPSEEK_FACTOR, ratio, precision, MPSEEK_FLAG_DELAY);
set_osd_function(mpctx, cur_pos < ratio ? OSD_FFW : OSD_REW);
break;
}
case 2: { // Absolute seek to a timestamp in seconds
queue_seek(mpctx, MPSEEK_ABSOLUTE, v, precision, false);
queue_seek(mpctx, MPSEEK_ABSOLUTE, v, precision, MPSEEK_FLAG_DELAY);
set_osd_function(mpctx,
v > get_current_time(mpctx) ? OSD_FFW : OSD_REW);
break;
@@ -4642,7 +4662,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
case 3: { // Relative seek by percentage
queue_seek(mpctx, MPSEEK_FACTOR,
get_current_pos_ratio(mpctx, false) + v / 100.0,
precision, false);
precision, MPSEEK_FLAG_DELAY);
set_osd_function(mpctx, v > 0 ? OSD_FFW : OSD_REW);
break;
}}
@@ -4664,7 +4684,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
} else if (oldpts != MP_NOPTS_VALUE) {
cmdctx->last_seek_pts = get_current_time(mpctx);
cmdctx->marked_pts = MP_NOPTS_VALUE;
queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, MPSEEK_EXACT, false);
queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, MPSEEK_EXACT,
MPSEEK_FLAG_DELAY);
set_osd_function(mpctx, OSD_REW);
if (bar_osd)
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR;
@@ -4850,7 +4871,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
// rounding for the mess of it.
a[0] += 0.01 * (a[1] >= 0 ? 1 : -1);
mark_seek(mpctx);
queue_seek(mpctx, MPSEEK_RELATIVE, a[0], MPSEEK_EXACT, false);
queue_seek(mpctx, MPSEEK_RELATIVE, a[0], MPSEEK_EXACT,
MPSEEK_FLAG_DELAY);
set_osd_function(mpctx, (a[0] > 0) ? OSD_FFW : OSD_REW);
if (bar_osd)
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR;
@@ -5373,7 +5395,6 @@ void command_init(struct MPContext *mpctx)
mpctx->command_ctx = talloc(NULL, struct command_ctx);
*mpctx->command_ctx = (struct command_ctx){
.last_seek_pts = MP_NOPTS_VALUE,
.prev_pts = MP_NOPTS_VALUE,
};
}
@@ -5386,8 +5407,6 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
ctx->marked_pts = MP_NOPTS_VALUE;
}
if (event == MPV_EVENT_SEEK)
ctx->prev_pts = MP_NOPTS_VALUE;
if (event == MPV_EVENT_IDLE)
ctx->is_idle = true;
if (event == MPV_EVENT_START_FILE)
@@ -5398,35 +5417,6 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
}
}
void handle_ab_loop(struct MPContext *mpctx)
{
struct command_ctx *ctx = mpctx->command_ctx;
struct MPOpts *opts = mpctx->opts;
if (opts->pause)
return;
double now = mpctx->restart_complete ? mpctx->playback_pts : MP_NOPTS_VALUE;
if (now != MP_NOPTS_VALUE && (opts->ab_loop[0] != MP_NOPTS_VALUE ||
opts->ab_loop[1] != MP_NOPTS_VALUE))
{
double start = opts->ab_loop[0];
if (start == MP_NOPTS_VALUE)
start = 0;
double end = opts->ab_loop[1];
if (end == MP_NOPTS_VALUE)
end = INFINITY;
if (ctx->prev_pts >= start && ctx->prev_pts < end &&
(now >= end || mpctx->stop_play == AT_END_OF_FILE))
{
mark_seek(mpctx);
queue_seek(mpctx, MPSEEK_ABSOLUTE, start,
MPSEEK_EXACT, false);
}
}
ctx->prev_pts = now;
}
void handle_command_updates(struct MPContext *mpctx)
{
struct command_ctx *ctx = mpctx->command_ctx;
+1 -1
View File
@@ -57,6 +57,6 @@ enum {
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
void handle_ab_loop(struct MPContext *mpctx);
void mark_seek(struct MPContext *mpctx);
#endif /* MPLAYER_COMMAND_H */
+8 -2
View File
@@ -80,6 +80,11 @@ enum seek_precision {
MPSEEK_VERY_EXACT,
};
enum seek_flags {
MPSEEK_FLAG_DELAY = 1 << 0, // give player chance to coalesce multiple seeks
MPSEEK_FLAG_NOFLUSH = 1 << 1, // keeping remaining data for seamless loops
};
enum video_sync {
VS_DEFAULT = 0,
VS_DISP_RESAMPLE,
@@ -322,6 +327,7 @@ typedef struct MPContext {
bool hrseek_lastframe; // drop everything until last frame reached
bool hrseek_backstep; // go to frame before seek target
double hrseek_pts;
bool ab_loop_clip; // clip to the "b" part of an A-B loop if available
// AV sync: the next frame should be shown when the audio out has this
// much (in seconds) buffered data left. Increased when more data is
// written to the ao, decreased when moving to the next video frame.
@@ -380,7 +386,7 @@ typedef struct MPContext {
enum seek_type type;
enum seek_precision exact;
double amount;
bool immediate; // disable seek delay logic
unsigned flags; // MPSEEK_FLAG_*
} seek;
// Allow audio to issue a second seek if audio is too far ahead (for non-hr
@@ -512,7 +518,7 @@ void pause_player(struct MPContext *mpctx);
void unpause_player(struct MPContext *mpctx);
void add_step_frame(struct MPContext *mpctx, int dir);
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
enum seek_precision exact, bool immediate);
enum seek_precision exact, int flags);
double get_time_length(struct MPContext *mpctx);
double get_current_time(struct MPContext *mpctx);
double get_playback_time(struct MPContext *mpctx);
+3 -3
View File
@@ -634,7 +634,7 @@ void autoload_external_files(struct MPContext *mpctx)
char *lang = list[i].lang;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *t = mpctx->tracks[n];
if (t->demuxer && strcmp(t->demuxer->stream->url, filename) == 0)
if (t->demuxer && strcmp(t->demuxer->filename, filename) == 0)
goto skip;
}
if (list[i].type == STREAM_SUB && !sc[STREAM_VIDEO] && !sc[STREAM_AUDIO])
@@ -1029,7 +1029,7 @@ reopen_file:
int entry_stream_flags = 0;
if (!pl->disable_safety) {
entry_stream_flags = STREAM_SAFE_ONLY;
if (mpctx->demuxer->stream->is_network)
if (mpctx->demuxer->is_network)
entry_stream_flags |= STREAM_NETWORK_ONLY;
}
for (struct playlist_entry *e = pl->first; e; e = e->next)
@@ -1140,7 +1140,7 @@ reopen_file:
startpos = start;
}
if (startpos != MP_NOPTS_VALUE) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, MPSEEK_DEFAULT, 0);
execute_queued_seek(mpctx);
}
+8 -2
View File
@@ -90,6 +90,12 @@ double get_play_end_pts(struct MPContext *mpctx)
if (cend != MP_NOPTS_VALUE && (end == MP_NOPTS_VALUE || cend < end))
end = cend;
}
if (mpctx->ab_loop_clip && opts->ab_loop[1] != MP_NOPTS_VALUE &&
opts->ab_loop[1] > opts->ab_loop[0])
{
if (end == MP_NOPTS_VALUE || end > opts->ab_loop[1])
end = opts->ab_loop[1];
}
return end;
}
@@ -143,8 +149,8 @@ void update_vo_playback_state(struct MPContext *mpctx)
if ((oldstate.playing && oldstate.taskbar_progress) ||
(newstate.playing && newstate.taskbar_progress))
{
vo_control(mpctx->video_out,
VOCTRL_UPDATE_PLAYBACK_STATE, &newstate);
vo_control_async(mpctx->video_out,
VOCTRL_UPDATE_PLAYBACK_STATE, &newstate);
}
mpctx->vo_playback_state = newstate;
}
+31 -10
View File
@@ -141,7 +141,7 @@ void add_step_frame(struct MPContext *mpctx, int dir)
unpause_player(mpctx);
} else if (dir < 0) {
if (!mpctx->hrseek_active) {
queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, true);
queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, 0);
pause_player(mpctx);
}
}
@@ -172,6 +172,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->last_seek_pts = MP_NOPTS_VALUE;
mpctx->cache_wait_time = 0;
mpctx->step_frames = 0;
mpctx->ab_loop_clip = true;
mpctx->restart_complete = false;
#if HAVE_ENCODING
@@ -272,7 +273,9 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
}
}
clear_audio_output_buffers(mpctx);
if (!(seek.flags & MPSEEK_FLAG_NOFLUSH))
clear_audio_output_buffers(mpctx);
reset_playback_state(mpctx);
/* Use the target time as "current position" for further relative
@@ -301,11 +304,13 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
mpctx->audio_allow_second_chance_seek =
!hr_seek && !(demux_flags & SEEK_FORWARD);
mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1];
}
// This combines consecutive seek requests.
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
enum seek_precision exact, bool immediate)
enum seek_precision exact, int flags)
{
struct seek_params *seek = &mpctx->seek;
@@ -314,7 +319,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
switch (type) {
case MPSEEK_RELATIVE:
seek->immediate |= immediate;
seek->flags |= flags;
if (seek->type == MPSEEK_FACTOR)
return; // Well... not common enough to bother doing better
seek->amount += amount;
@@ -332,7 +337,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
.type = type,
.amount = amount,
.exact = exact,
.immediate = immediate,
.flags = flags,
};
return;
case MPSEEK_NONE:
@@ -351,7 +356,8 @@ void execute_queued_seek(struct MPContext *mpctx)
/* If the user seeks continuously (keeps arrow key down)
* try to finish showing a frame from one location before doing
* another seek (which could lead to unchanging display). */
if (!mpctx->seek.immediate && mpctx->video_status < STATUS_PLAYING &&
bool delay = mpctx->seek.flags & MPSEEK_FLAG_DELAY;
if (delay && mpctx->video_status < STATUS_PLAYING &&
mp_time_sec() - mpctx->start_timestamp < 0.3)
return;
mp_seek(mpctx, mpctx->seek);
@@ -682,7 +688,7 @@ static void handle_sstep(struct MPContext *mpctx)
if (opts->step_sec > 0 && !mpctx->paused) {
set_osd_function(mpctx, OSD_FFW);
queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, MPSEEK_DEFAULT, 0);
}
if (mpctx->video_status >= STATUS_EOF) {
@@ -696,10 +702,25 @@ static void handle_sstep(struct MPContext *mpctx)
static void handle_loop_file(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
if (mpctx->stop_play == AT_END_OF_FILE &&
(opts->ab_loop[0] != MP_NOPTS_VALUE || opts->ab_loop[1] != MP_NOPTS_VALUE))
{
// Assumes execute_queued_seek() happens before next audio/video is
// attempted to be decoded or filtered.
mpctx->stop_play = KEEP_PLAYING;
double start = 0;
if (opts->ab_loop[0] != MP_NOPTS_VALUE)
start = opts->ab_loop[0];
mark_seek(mpctx);
queue_seek(mpctx, MPSEEK_ABSOLUTE, start, MPSEEK_EXACT,
MPSEEK_FLAG_NOFLUSH);
}
if (opts->loop_file && mpctx->stop_play == AT_END_OF_FILE) {
mpctx->stop_play = KEEP_PLAYING;
set_osd_function(mpctx, OSD_FFW);
queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, MPSEEK_DEFAULT, MPSEEK_FLAG_NOFLUSH);
if (opts->loop_file > 0)
opts->loop_file--;
}
@@ -893,6 +914,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->hrseek_active = false;
mpctx->restart_complete = true;
mpctx->audio_allow_second_chance_seek = false;
handle_playback_time(mpctx);
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
if (!mpctx->playing_msg_shown) {
if (opts->playing_msg && opts->playing_msg[0]) {
@@ -912,6 +934,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
}
mpctx->playing_msg_shown = true;
mpctx->sleeptime = 0;
mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
MP_VERBOSE(mpctx, "playback restart complete\n");
}
}
@@ -1012,8 +1035,6 @@ void run_playloop(struct MPContext *mpctx)
handle_loop_file(mpctx);
handle_ab_loop(mpctx);
handle_keep_open(mpctx);
handle_sstep(mpctx);
+34 -19
View File
@@ -217,8 +217,8 @@ static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
if (mpctx->opts->deinterlace >= 0)
video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &(int){0});
if (params.rotate && (params.rotate % 90 == 0)) {
if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90)) {
if (params.rotate) {
if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90) || params.rotate % 90) {
// Try to insert a rotation filter.
char *args[] = {"angle", "auto", NULL};
if (try_filter(vo_c, "rotate", "autorotate", args) < 0)
@@ -539,11 +539,13 @@ void mp_force_video_refresh(struct MPContext *mpctx)
return;
// If not paused, the next frame should come soon enough.
if (opts->pause && mpctx->video_status == STATUS_PLAYING &&
if ((opts->pause || mpctx->time_frame >= 0.5) &&
(mpctx->video_status >= STATUS_PLAYING ||
mpctx->video_status <= STATUS_DRAINING) &&
mpctx->last_vo_pts != MP_NOPTS_VALUE)
{
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts,
MPSEEK_VERY_EXACT, true);
MPSEEK_VERY_EXACT, 0);
}
}
@@ -840,9 +842,11 @@ static int video_output_image(struct MPContext *mpctx)
struct mp_image *img = vf_read_output_frame(vo_c->vf);
if (img) {
double endpts = get_play_end_pts(mpctx);
if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) {
r = VD_EOF;
} else if (mpctx->max_frames == 0) {
if ((endpts != MP_NOPTS_VALUE && img->pts >= endpts) ||
mpctx->max_frames == 0)
{
vf_unread_output_frame(vo_c->vf, img);
img = NULL;
r = VD_EOF;
} else if (hrseek && mpctx->hrseek_lastframe) {
mp_image_setrefp(&mpctx->saved_frame, img);
@@ -1283,10 +1287,6 @@ static void calculate_frame_duration(struct MPContext *mpctx)
double pts1 = mpctx->next_frames[1]->pts;
if (pts0 != MP_NOPTS_VALUE && pts1 != MP_NOPTS_VALUE && pts1 >= pts0)
duration = pts1 - pts0;
} else {
// E.g. last frame on EOF. Only use it if it's significant.
if (demux_duration >= 0.1)
duration = demux_duration;
}
// The following code tries to compensate for rounded Matroska timestamps
@@ -1350,17 +1350,32 @@ void write_video(struct MPContext *mpctx)
return;
if (r == VD_EOF) {
int prev_state = mpctx->video_status;
mpctx->video_status = STATUS_EOF;
if (mpctx->num_past_frames > 0 && mpctx->past_frames[0].duration > 0) {
if (vo_still_displaying(vo))
mpctx->video_status = STATUS_DRAINING;
}
mpctx->delay = 0;
mpctx->last_av_difference = 0;
if (mpctx->video_status <= STATUS_PLAYING) {
mpctx->video_status = STATUS_DRAINING;
get_relative_time(mpctx);
if (mpctx->num_past_frames == 1 && mpctx->past_frames[0].pts == 0 &&
!mpctx->ao_chain)
{
MP_VERBOSE(mpctx, "assuming this is an image\n");
mpctx->time_frame += opts->image_display_duration;
} else {
mpctx->time_frame = 0;
}
}
if (mpctx->video_status == STATUS_DRAINING) {
mpctx->time_frame -= get_relative_time(mpctx);
mpctx->sleeptime = MPMIN(mpctx->sleeptime, mpctx->time_frame);
if (mpctx->time_frame <= 0) {
MP_VERBOSE(mpctx, "video EOF reached\n");
mpctx->video_status = STATUS_EOF;
}
}
MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
if (prev_state != mpctx->video_status)
mpctx->sleeptime = 0;
return;
}
+14 -10
View File
@@ -325,6 +325,14 @@ static int resize_cache(struct priv *s, int64_t size)
int64_t min_size = FILL_LIMIT * 2;
int64_t max_size = ((size_t)-1) / 8;
if (s->stream_size > 0) {
size = MPMIN(size, s->stream_size);
if (size >= s->stream_size) {
MP_VERBOSE(s, "no backbuffer needed\n");
s->back_size = 0;
}
}
int64_t buffer_size = MPCLAMP(size, min_size, max_size);
s->back_size = MPCLAMP(s->back_size, min_size, max_size);
buffer_size += s->back_size;
@@ -373,6 +381,10 @@ static int resize_cache(struct priv *s, int64_t size)
if (s->seek_limit > s->buffer_size - FILL_LIMIT)
s->seek_limit = s->buffer_size - FILL_LIMIT;
MP_VERBOSE(s, "Cache size set to %lld KiB (%lld KiB backbuffer)\n",
(long long)(s->buffer_size / 1024),
(long long)(s->back_size / 1024));
assert(s->back_size < s->buffer_size);
return STREAM_OK;
@@ -675,22 +687,14 @@ int stream_cache_init(stream_t *cache, stream_t *stream,
s->seek_limit = opts->seek_min * 1024ULL;
s->back_size = opts->back_buffer * 1024ULL;
int64_t cache_size = opts->size * 1024ULL;
s->stream_size = stream_get_size(stream);
int64_t file_size = stream_get_size(stream);
if (file_size >= 0)
cache_size = MPMIN(cache_size, file_size);
if (resize_cache(s, cache_size) != STREAM_OK) {
if (resize_cache(s, opts->size * 1024ULL) != STREAM_OK) {
MP_ERR(s, "Failed to allocate cache buffer.\n");
talloc_free(s);
return -1;
}
MP_VERBOSE(cache, "Cache size set to %lld KiB (%lld KiB backbuffer)\n",
(long long)(s->buffer_size / 1024),
(long long)(s->back_size / 1024));
pthread_mutex_init(&s->mutex, NULL);
pthread_cond_init(&s->wakeup, NULL);
+1
View File
@@ -88,6 +88,7 @@ static int open_f(stream_t *stream)
stream->seekable = true;
stream->control = control;
stream->read_chunk = 1024 * 1024;
stream->allow_caching = false;
struct priv *p = talloc_zero(stream, struct priv);
stream->priv = p;
+4
View File
@@ -94,6 +94,9 @@ bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) TA_PRF(
#define ta_steal(ta_parent, ptr) (TA_TYPEOF(ptr))ta_steal_(ta_parent, ptr)
#define ta_dup_ptrtype(ta_parent, ptr) \
(TA_TYPEOF(ptr))ta_memdup(ta_parent, ptr, sizeof(*(ptr)))
// Ugly macros that crash on OOM.
// All of these mirror real functions (with a 'x' added after the 'ta_'
// prefix), and the only difference is that they will call abort() on allocation
@@ -121,6 +124,7 @@ bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) TA_PRF(
#define ta_xnew_array_size(...) ta_oom_p(ta_new_array_size(__VA_ARGS__))
#define ta_xnew_ptrtype(...) ta_oom_g(ta_new_ptrtype(__VA_ARGS__))
#define ta_xnew_array_ptrtype(...) ta_oom_g(ta_new_array_ptrtype(__VA_ARGS__))
#define ta_xdup_ptrtype(...) ta_oom_g(ta_dup_ptrtype(__VA_ARGS__))
#define ta_xsteal(ta_parent, ptr) (TA_TYPEOF(ptr))ta_xsteal_(ta_parent, ptr)
#define ta_xrealloc(ta_parent, ptr, type, count) \
+2 -8
View File
@@ -449,10 +449,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
if (!lavc_codec)
return;
ctx->codec_timebase = (AVRational){0};
if (strstr(decoder, "_mmal") || strstr(decoder, "_mediacodec"))
ctx->codec_timebase = (AVRational){1, 1000000};
ctx->codec_timebase = mp_get_codec_timebase(vd->codec);
ctx->pix_fmt = AV_PIX_FMT_NONE;
ctx->hwdec = hwdec;
ctx->hwdec_fmt = 0;
@@ -463,9 +460,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
avctx->opaque = vd;
avctx->codec_type = AVMEDIA_TYPE_VIDEO;
avctx->codec_id = lavc_codec->id;
if (ctx->codec_timebase.num)
avctx->time_base = ctx->codec_timebase;
avctx->time_base = ctx->codec_timebase;
avctx->refcounted_frames = 1;
ctx->pic = av_frame_alloc();
@@ -828,7 +823,6 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
struct demux_packet *cc = new_demux_packet_from(sd->data, sd->size);
cc->pts = vd->codec_pts;
cc->dts = vd->codec_dts;
cc->pos = -1;
demuxer_feed_caption(vd->header, cc);
}
+7
View File
@@ -458,6 +458,13 @@ struct mp_image *vf_read_output_frame(struct vf_chain *c)
return vf_dequeue_output_frame(c->last);
}
// Undo the previous vf_read_output_frame().
void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img)
{
struct vf_instance *vf = c->last;
MP_TARRAY_INSERT_AT(vf, vf->out_queued, vf->num_out_queued, 0, img);
}
// Some filters (vf_vapoursynth) filter on separate threads, and may need new
// input from the decoder, even though the core does not need a new output image
// yet (this is required to get proper pipelining in the filter). If the filter
+1
View File
@@ -157,6 +157,7 @@ int vf_filter_frame(struct vf_chain *c, struct mp_image *img);
int vf_output_frame(struct vf_chain *c, bool eof);
int vf_needs_input(struct vf_chain *c);
struct mp_image *vf_read_output_frame(struct vf_chain *c);
void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img);
void vf_seek_reset(struct vf_chain *c);
struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
char **args);
+9 -2
View File
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <inttypes.h>
#include "common/msg.h"
@@ -46,11 +47,17 @@ static int lavfi_reconfig(struct vf_instance *vf,
struct vf_priv_s *p = vf_lw_old_priv(vf);
if (p->angle == 4) { // "auto"
int r = in->rotate;
if (r < 0 || r >= 360 || (r % 90) != 0) {
if (r < 0 || r >= 360) {
MP_ERR(vf, "Can't apply rotation of %d degrees.\n", r);
return -1;
}
vf_lw_update_graph(vf, NULL, "%s", rot[(r / 90) % 360]);
if (r % 90) {
double a = r / 180.0 * M_PI;
vf_lw_update_graph(vf, NULL, "rotate=%f:ow=rotw(%f):oh=roth(%f)",
a, a, a);
} else {
vf_lw_update_graph(vf, NULL, "%s", rot[(r / 90) % 360]);
}
out->rotate = 0;
}
return 0;
+7 -3
View File
@@ -27,10 +27,12 @@
#include "sub/osd.h"
static void aspect_calc_panscan(struct mp_vo_opts *opts,
int w, int h, int d_w, int d_h, bool unscaled,
int w, int h, int d_w, int d_h, int unscaled,
int window_w, int window_h, double monitor_par,
int *out_w, int *out_h)
{
w *= monitor_par;
int fwidth = window_w;
int fheight = (float)window_w / d_w * d_h / monitor_par;
if (fheight > window_h || fheight < h) {
@@ -51,9 +53,11 @@ static void aspect_calc_panscan(struct mp_vo_opts *opts,
}
if (unscaled) {
fwidth = w * monitor_par;
fheight = h;
vo_panscan_area = 0;
if (unscaled != 2 || (w <= window_w && h <= window_h)) {
fwidth = w;
fheight = h;
}
}
*out_w = fwidth + vo_panscan_area * opts->panscan * f_w;
+1
View File
@@ -32,6 +32,7 @@ enum {
VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context
VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer
VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
VOFLAG_ANGLE_DCOMP = 1 << 5, // Whether DirectComposition is allowed
};
extern const int mpgl_preferred_gl_versions[];
+7 -2
View File
@@ -306,8 +306,13 @@ static int angle_init(struct MPGLContext *ctx, int flags)
// EGL_DIRECT_COMPOSITION_ANGLE enables the use of flip-mode present, which
// avoids a copy of the video image and lowers vsync jitter, though the
// extension is only present on Windows 8 and up.
if (strstr(exts, "EGL_ANGLE_direct_composition")) {
// extension is only present on Windows 8 and up, and might have subpar
// behavior with some drivers (Intel? symptom - whole desktop is black for
// some seconds after spending some minutes in fullscreen and then leaving
// fullscreen).
if ((flags & VOFLAG_ANGLE_DCOMP) &&
strstr(exts, "EGL_ANGLE_direct_composition"))
{
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len,
EGL_DIRECT_COMPOSITION_ANGLE);
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_TRUE);
+36 -15
View File
@@ -297,12 +297,17 @@ autoprobe:
return NULL;
}
static void terminate_vo(void *p)
{
struct vo *vo = p;
struct vo_internal *in = vo->in;
in->terminate = true;
}
void vo_destroy(struct vo *vo)
{
struct vo_internal *in = vo->in;
mp_dispatch_lock(in->dispatch);
vo->in->terminate = true;
mp_dispatch_unlock(in->dispatch);
mp_dispatch_run(in->dispatch, terminate_vo, vo);
pthread_join(vo->in->thread, NULL);
dealloc_vo(vo);
}
@@ -339,9 +344,7 @@ static void check_estimated_display_fps(struct vo *vo)
struct vo_internal *in = vo->in;
bool use_estimated = false;
if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES * 2 &&
fabs((in->nominal_vsync_interval - in->estimated_vsync_interval))
>= 0.01 * in->nominal_vsync_interval &&
if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES / 2 &&
in->estimated_vsync_interval <= 1e6 / 20.0 &&
in->estimated_vsync_interval >= 1e6 / 99.0)
{
@@ -358,12 +361,11 @@ static void check_estimated_display_fps(struct vo *vo)
}
if (use_estimated == (in->vsync_interval == in->nominal_vsync_interval)) {
if (use_estimated) {
MP_WARN(vo, "Reported display FPS seems incorrect.\n"
"Assuming a value closer to %.3f Hz.\n",
1e6 / in->estimated_vsync_interval);
MP_VERBOSE(vo, "adjusting display FPS to a value closer to %.3f Hz\n",
1e6 / in->estimated_vsync_interval);
} else {
MP_WARN(vo, "Switching back to assuming %.3f Hz.\n",
1e6 / in->nominal_vsync_interval);
MP_VERBOSE(vo, "switching back to assuming display fps = %.3f Hz\n",
1e6 / in->nominal_vsync_interval);
}
}
in->vsync_interval = use_estimated ? (int64_t)in->estimated_vsync_interval
@@ -537,20 +539,39 @@ static void run_control(void *p)
{
void **pp = p;
struct vo *vo = pp[0];
uint32_t request = *(int *)pp[1];
int request = (intptr_t)pp[1];
void *data = pp[2];
int ret = vo->driver->control(vo, request, data);
*(int *)pp[3] = ret;
if (pp[3])
*(int *)pp[3] = ret;
}
int vo_control(struct vo *vo, uint32_t request, void *data)
int vo_control(struct vo *vo, int request, void *data)
{
int ret;
void *p[] = {vo, &request, data, &ret};
void *p[] = {vo, (void *)(intptr_t)request, data, &ret};
mp_dispatch_run(vo->in->dispatch, run_control, p);
return ret;
}
// Run vo_control() without waiting for a reply.
// (Only works for some VOCTRLs.)
void vo_control_async(struct vo *vo, int request, void *data)
{
void *p[4] = {vo, (void *)(intptr_t)request, NULL, NULL};
void **d = talloc_memdup(NULL, p, sizeof(p));
switch (request) {
case VOCTRL_UPDATE_PLAYBACK_STATE:
d[2] = ta_xdup_ptrtype(d, (struct voctrl_playback_state *)data);
break;
default:
abort(); // requires explicit support
}
mp_dispatch_enqueue_autofree(vo->in->dispatch, run_control, d);
}
// must be called locked
static void forget_frames(struct vo *vo)
{
+2 -1
View File
@@ -341,7 +341,8 @@ struct mpv_global;
struct vo *init_best_video_out(struct mpv_global *global, struct vo_extra *ex);
int vo_reconfig(struct vo *vo, struct mp_image_params *p);
int vo_control(struct vo *vo, uint32_t request, void *data);
int vo_control(struct vo *vo, int request, void *data);
void vo_control_async(struct vo *vo, int request, void *data);
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts);
void vo_queue_frame(struct vo *vo, struct vo_frame *frame);
void vo_wait_frame(struct vo *vo);
+5
View File
@@ -70,6 +70,7 @@ struct gl_priv {
int allow_sw;
int swap_interval;
int dwm_flush;
int allow_direct_composition;
int opt_vsync_fences;
char *backend;
@@ -409,6 +410,9 @@ static int preinit(struct vo *vo)
if (p->allow_sw)
vo_flags |= VOFLAG_SW;
if (p->allow_direct_composition)
vo_flags |= VOFLAG_ANGLE_DCOMP;
p->glctx = mpgl_init(vo, p->backend, vo_flags);
if (!p->glctx)
goto err_out;
@@ -460,6 +464,7 @@ static const struct m_option options[] = {
OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)),
OPT_CHOICE("dwmflush", dwm_flush, 0,
({"no", -1}, {"auto", 0}, {"windowed", 1}, {"yes", 2})),
OPT_FLAG("dcomposition", allow_direct_composition, 0, OPTDEF_INT(1)),
OPT_FLAG("debug", use_gl_debug, 0),
OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt),
OPT_FLAG("sw", allow_sw, 0),
+117 -35
View File
@@ -48,8 +48,6 @@
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
static const wchar_t classname[] = L"mpv";
static __thread struct vo_w32_state *w32_thread_context;
struct vo_w32_state {
@@ -64,6 +62,8 @@ struct vo_w32_state {
HWND window;
HWND parent; // 0 normally, set in embedding mode
HHOOK parent_win_hook;
HWINEVENTHOOK parent_evt_hook;
HMONITOR monitor; // Handle of the current screen
struct mp_rect screenrc; // Size and virtual position of the current screen
@@ -747,11 +747,19 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
// is that will make us lose WM_USER wakeups.
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
return 0;
case WM_NCDESTROY: // Sometimes only WM_NCDESTROY is received in --wid mode
case WM_DESTROY:
if (w32->destroyed)
break;
// If terminate is not set, something else destroyed the window. This
// can also happen in --wid mode when the parent window is destroyed.
if (!w32->terminate)
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
RevokeDragDrop(w32->window);
w32->destroyed = true;
w32->window = NULL;
PostQuitMessage(0);
return 0;
break;
case WM_SYSCOMMAND:
switch (wParam) {
case SC_SCREENSAVE:
@@ -901,27 +909,109 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
return DefWindowProcW(hWnd, message, wParam, lParam);
}
static pthread_once_t window_class_init_once = PTHREAD_ONCE_INIT;
static ATOM window_class;
static void register_window_class(void)
{
window_class = RegisterClassExW(&(WNDCLASSEXW) {
.cbSize = sizeof(WNDCLASSEXW),
.style = CS_HREDRAW | CS_VREDRAW,
.lpfnWndProc = WndProc,
.hInstance = HINST_THISCOMPONENT,
.hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"),
.hCursor = LoadCursor(NULL, IDC_ARROW),
.lpszClassName = L"mpv",
});
}
static ATOM get_window_class(void)
{
pthread_once(&window_class_init_once, register_window_class);
return window_class;
}
static void resize_child_win(HWND parent)
{
// Check if an mpv window is a child of this window. This will not
// necessarily be the case because the hook functions will run for all
// windows on the parent window's thread.
ATOM cls = get_window_class();
HWND child = FindWindowExW(parent, NULL, (LPWSTR)MAKEINTATOM(cls), NULL);
if (!child)
return;
// Make sure the window was created by this instance
if (GetWindowLongPtrW(child, GWLP_HINSTANCE) != (LONG_PTR)HINST_THISCOMPONENT)
return;
// Resize the mpv window to match its parent window's size
RECT rm, rp;
if (!GetClientRect(child, &rm))
return;
if (!GetClientRect(parent, &rp))
return;
if (EqualRect(&rm, &rp))
return;
SetWindowPos(child, NULL, 0, 0, rp.right, rp.bottom, SWP_ASYNCWINDOWPOS |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
static LRESULT CALLBACK parent_win_hook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION)
goto done;
CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
if (cwp->message != WM_WINDOWPOSCHANGED)
goto done;
resize_child_win(cwp->hwnd);
done:
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
static void CALLBACK parent_evt_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread,
DWORD dwmsEventTime)
{
if (event != EVENT_OBJECT_LOCATIONCHANGE)
return;
if (!hwnd || idObject != OBJID_WINDOW || idChild != CHILDID_SELF)
return;
resize_child_win(hwnd);
}
static void install_parent_hook(struct vo_w32_state *w32)
{
DWORD pid;
DWORD tid = GetWindowThreadProcessId(w32->parent, &pid);
// If the parent lives inside the current process, install a Windows hook
if (pid == GetCurrentProcessId()) {
w32->parent_win_hook = SetWindowsHookExW(WH_CALLWNDPROC,
parent_win_hook, NULL, tid);
} else {
// Otherwise, use a WinEvent hook. These don't seem to be as smooth as
// Windows hooks, but they can be delivered across process boundaries.
w32->parent_evt_hook = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
NULL, parent_evt_hook, pid, tid, WINEVENT_OUTOFCONTEXT);
}
}
static void remove_parent_hook(struct vo_w32_state *w32)
{
if (w32->parent_win_hook)
UnhookWindowsHookEx(w32->parent_win_hook);
if (w32->parent_evt_hook)
UnhookWinEvent(w32->parent_evt_hook);
}
// Dispatch incoming window events and handle them.
// This returns only when the thread is asked to terminate.
static void run_message_loop(struct vo_w32_state *w32)
{
MSG msg;
while (GetMessageW(&msg, 0, 0, 0) > 0) {
while (GetMessageW(&msg, 0, 0, 0) > 0)
DispatchMessageW(&msg);
if (w32->parent) {
RECT r, rp;
BOOL res = GetClientRect(w32->window, &r);
res = res && GetClientRect(w32->parent, &rp);
if (res && (r.right != rp.right || r.bottom != rp.bottom))
MoveWindow(w32->window, 0, 0, rp.right, rp.bottom, FALSE);
// Window has probably been closed, e.g. due to parent program crash
if (!IsWindow(w32->parent))
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
}
}
// Even if the message loop somehow exits, we still have to respond to
// external requests until termination is requested.
while (!w32->terminate)
@@ -1222,33 +1312,26 @@ static void *gui_thread(void *ptr)
thread_disable_ime();
WNDCLASSEXW wcex = {
.cbSize = sizeof wcex,
.style = CS_HREDRAW | CS_VREDRAW,
.lpfnWndProc = WndProc,
.hInstance = HINST_THISCOMPONENT,
.hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"),
.hCursor = LoadCursor(NULL, IDC_ARROW),
.lpszClassName = classname,
};
RegisterClassExW(&wcex);
w32_thread_context = w32;
if (w32->opts->WinID >= 0)
w32->parent = (HWND)(intptr_t)(w32->opts->WinID);
ATOM cls = get_window_class();
if (w32->parent) {
RECT r;
GetClientRect(w32->parent, &r);
w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname,
classname,
w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY,
(LPWSTR)MAKEINTATOM(cls), L"mpv",
WS_CHILD | WS_VISIBLE,
0, 0, r.right, r.bottom,
w32->parent, 0, HINST_THISCOMPONENT, NULL);
// Install a hook to get notifications when the parent changes size
if (w32->window)
install_parent_hook(w32);
} else {
w32->window = CreateWindowExW(0, classname,
classname,
w32->window = CreateWindowExW(0, (LPWSTR)MAKEINTATOM(cls), L"mpv",
update_style(w32, 0),
CW_USEDEFAULT, SW_HIDE, 100, 100,
0, 0, HINST_THISCOMPONENT, NULL);
@@ -1323,10 +1406,9 @@ done:
MP_VERBOSE(w32, "uninit\n");
if (w32->window) {
RevokeDragDrop(w32->window);
remove_parent_hook(w32);
if (w32->window && !w32->destroyed)
DestroyWindow(w32->window);
}
if (w32->taskbar_list)
ITaskbarList2_Release(w32->taskbar_list);
if (w32->taskbar_list3)
+11 -3
View File
@@ -1748,17 +1748,25 @@ static void vo_x11_fullscreen(struct vo *vo)
x11->nofsrc = x11->winrc;
}
struct mp_rect rc = x11->nofsrc;
if (x11->wm_type & vo_wm_FULLSCREEN) {
x11_set_ewmh_state(x11, "_NET_WM_STATE_FULLSCREEN", x11->fs);
if (!x11->fs && (x11->pos_changed_during_fs ||
x11->size_changed_during_fs))
{
if (x11->screenrc.x0 == rc.x0 && x11->screenrc.x1 == rc.x1 &&
x11->screenrc.y0 == rc.y0 && x11->screenrc.y1 == rc.y1)
{
// Workaround for some WMs switching back to FS in this case.
MP_VERBOSE(x11, "avoiding triggering old-style fullscreen\n");
rc.x1 -= 1;
rc.y1 -= 1;
}
vo_x11_move_resize(vo, x11->pos_changed_during_fs,
x11->size_changed_during_fs,
x11->nofsrc);
x11->size_changed_during_fs, rc);
}
} else {
struct mp_rect rc = x11->nofsrc;
if (x11->fs) {
vo_x11_update_screeninfo(vo);
rc = x11->screenrc;
+1 -1
View File
@@ -508,7 +508,7 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
}, {
'name': 'avutil-mastering-metadata',
'desc': 'libavutil mastering display metadata struct',
'func': check_statement('libavutil/mastering_display_metadata.h',
'func': check_statement('libavutil/frame.h',
'AV_FRAME_DATA_MASTERING_DISPLAY_METADATA',
use='libav'),
}