Compare commits

...
215 Commits
Author SHA1 Message Date
Martin Herkt ef2d6ed125 Release 0.19.0 2016-08-15 16:10:59 +02:00
Martin Herkt fd1bc95b48 Merge branch 'master' into release/current 2016-08-15 15:19:29 +02:00
Paul B Mahol d7a7e9a8c8 vo_direct3d: add missing header
Fixes build under cygwin.

Signed-off-by: wm4 <wm4@nowhere>
2016-08-14 21:51:59 +02:00
wm4 4a8647b6b8 sub: don't potentially discard too many subtitles on seek
The accepts_packet packet callback is supposed to deal with subtitle
decoders which have only a small queue of current subtitle events (i.e.
sd_lavc.c), in case feeding it too many packets would discard events
that are still needed.

Normally, the number of subtitles that need to be preserved is estimated
by the rendering pts (get_bitmaps() argument). Rendering lags behind
decoding, so normally the rendering pts is smaller than the next video
frame pts, and we simply discard all subtitle events until the rendering
pts.

This breaks down in some annoying corner cases. One of them is seeking
backwards: the VO will still try to render the old PTS during seeks,
which passes a high PTS to the subtitle renderer, which in turn would
discard more subtitles than it should. There is a similar issue with
forward seeks. Add hacks to deal with those issues.

There should be a better way to deal with the essentially unknown
"rendering position", which is made worse by screenshots or rendering
with vf_sub. At the very least, we could handle seeks better, and e.g.
either force the VO not to re-render subs after seeks (ugly), or
introduce seek sequence numbers to distinguish attempts to render
earlier subtitles when a seek is done.
2016-08-14 20:27:37 +02:00
wm4 4b5de33e89 command: add replaygain information properties to track-list 2016-08-13 15:21:09 +02:00
wm4 9d2bcd2ef9 demux: minor cleanup to replaygain error handling
If the PEAK tag is invalid, return an error.

Make the error signalling conventions more uniform by strictly returning
a negative value on error, and treating >=0 as success.
2016-08-13 15:09:03 +02:00
wm4 e392d6610d demux: make ALBUM replaygain tags optional
IF they're missing, use the TRACK ones instead. See #3405.
2016-08-13 15:06:45 +02:00
wm4 78d808c5bd audio: log replaygain values in af_volume instead demuxer
The demuxer layer usually doesn't log per-stream information, and even
the replaygain information was logged only if it came from tags.

So log it in af_volume instead.
2016-08-13 15:06:07 +02:00
wm4 b9ba9a898a demux: add per-track metadata
...and ignore it. The main purpose is for retrieving per-track
replaygain tags. Other than that per-track tags are not used or accessed
by the playback core yet.

The demuxer infrastructure is still not really good with that whole
synchronization thing (at least in part due to being inherited from
mplayer's single-threaded architecture). A convoluted mechanism is
needed to transport the tags from demuxer thread to user thread. Two
factors contribute to the complexity: tags can change during playback,
and tracks (i.e. struct sh_stream) are not duplicated per thread.

In particular, we update the way replaygain tags are retrieved. We first
try to use per-track tags (common in Matroska) and global tags
(effectively formats like mp3). This part fixes #3405.
2016-08-12 21:39:32 +02:00
Paul B Mahol e2a54bb1ca audio/filter: remove delay audio filter
Similar filter is available in libavfilter.
2016-08-12 19:45:39 +02:00
wm4 ec4a8bdb3d github: encourage providing sample files
Also, try to save users from trying to use the github file upload
function. While we're at it, also make the sprunge.us hint a link.
2016-08-11 22:55:36 +02:00
wm4 dc6f8d4a0a command: add a property that returns filename without extension
Requested. Fixes #3404.
2016-08-11 22:40:00 +02:00
wm4 9024cb1685 manpage: remove the word "slave"
It's discriminatory or something.
2016-08-11 22:29:18 +02:00
Ricardo Constantino 5d74ba98ce stream/stream_bluray: display list of available titles on verbose
Based on similar code on FFmpeg and libbluray's list_titles example.
2016-08-11 10:02:46 +02:00
wm4 87190969a7 player: add --no-autoload-files option
Allt his auto-loading is getting annoying especially for testing.
2016-08-10 22:22:50 +02:00
wm4 367e9fb7f1 ao_alsa: make pause state more robust, reduce minor code duplication
With the previous commit, ao_alsa.c now has 3 possible ways to pause
playback. Actually all 3 of them need get_delay() to fake its return
value, so don't duplicate that code.

Also much of the code looks a bit questionable when considering
inconsistent pause/resume calls from outside, so ignore redundant calls.
2016-08-09 17:09:29 +02:00
wm4 2ded41d2be ao_alsa: handle --audio-stream-silence
push.c does not handle this automatically, and AOs using push.c have to
handle it themselves. Also, ALSA is low-level enough that it needs
explicit support in user code. At least I haven't found any option that
does this.

We still can get away relatively cheaply by abusing underflow-handling
for this. ao_alsa.c already configures ALSA to handle underflows by
playing silence. So we purposely induce an underflow when opening the
device, as well as when pausing or resetting the device.

This introduces minor misbehavior: it doesn't account for the additional
delay the initial silence adds, unless the device has fully played the
fragment of silence when the player starts sending data to it. But
nobody cares.
2016-08-09 17:09:29 +02:00
wm4 062349ff5b player: add --audio-wait-open options
Complements the option added in the previous commit.
2016-08-09 17:09:29 +02:00
wm4 eab92cec60 player: add --audio-stream-silence
Completely insane that this has to be done. Crap for compensating HDMI
crap.
2016-08-09 17:09:29 +02:00
wm4 3759a3f40b ao_coreaudio: actually use stop callback
The .pause callback is never used for pull.c-based AOs.

This means this always streamed silence instead of deactivating audio.
2016-08-09 17:09:29 +02:00
wm4 d81b5690df af_lavcac3enc: allow passing options to libavcodec 2016-08-09 17:09:29 +02:00
Thomas Petazzoni 8c2e299b84 wscript: improve stdatomic check
The current stdatomic check verifies the availability of the function by
calling atomic_load(). It also uses this test to check if linking
against libatomic is needed or not.

Unfortunately, on specific architectures (namely SPARC), using
atomic_load() does *not* require linking against libatomic, while other
atomic operations do. Due to this, mpv's wscript concludes that
stdatomic is available, and that linking against libatomic is not
needed, causing the following link failure:

[190/190] Linking build/mpv
audio/out/ao.c.13.o: In function `ao_query_and_reset_events':
/home/peko/autobuild/instance-0/output/build/mpv-0.18.1/build/../audio/out/ao.c:399: undefined reference to `__atomic_fetch_and_4'

In order to fix this, the stdatomic check is adjusted to call
atomic_fetch_add() instead, which does require libatomic. Thanks to
this, the wscript realizes that linking against libatomic is needed, and
the build works fine.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
2016-08-09 17:09:29 +02:00
wm4 469666b75b stream: fix double-free if cache init fails
If the normal stream cache init fails, and a file cache was initialized
before, we free the file cache as well. But since the file cache is
chained to the real stream, the real stream will also be freed. This has
to be prevented by clearing the pointer to the original stream in the
uncached_stream field.

This could in particular be triggered by using --cache-initial=1000 and
aborting playback during loading. (Without that option, stream cache
init failure is far less likely.)
2016-08-08 11:32:55 +02:00
wm4 896a97c2e2 demux: do not add packets between refresh seek requested and done
Could cause strange issues on seeks or track switches, was only visible
as race condition.
2016-08-08 11:13:43 +02:00
wm4 56c52bd2e1 stream_cb: fix stale comment
Oops.
2016-08-08 10:34:12 +02:00
wm4 355a145a48 DOCS/release-policy.md: reminder to update certain version numbers 2016-08-07 19:43:57 +02:00
wm4 3a21d71ad0 client API: bump API for stream_cb
Forgotten in previous commit.

Also minor semi-related change: remove the extra "," from the
mpv_sub_api enum, which I accidentally added in the previous commit.
(C99 is fine with trailing ",", C89 strictly speaking not. So do
this for maximum compatibility.)
2016-08-07 19:42:20 +02:00
Aman Gupta 7ca4a453e0 client API: add stream_cb API for user-defined stream implementations
Based on #2630. Some heavy changes by committer.

Signed-off-by: wm4 <wm4@nowhere>
2016-08-07 19:33:20 +02:00
wm4 52a0cbe456 client API: fix indentation 2016-08-07 19:33:20 +02:00
wm4 5640ceb838 client API: add MPV_ERROR_GENERIC
Because why not.
2016-08-07 19:33:20 +02:00
wm4 cfed84af2b w32_common: update window size on resize events
Fixes a regression introduced by commit 088a0075.

Fixes #3400.
2016-08-07 19:33:20 +02:00
wm4 259f219438 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 16:29:13 +02:00
wm4 c7b73edc65 player: fix mid-stream audio sync condition
This code is for resyncing audio-only streams (e.g. switching between
audio tracks if no video track is active). This must not be run if the
video PTS just isn't known yet. (Although the case in which this changes
anything is probably very obscure, if it can even happen. Still, it's a
bit more correct.)

This is a correction to commit 91a3bda6.
2016-08-07 16:00:14 +02:00
wm4 32cc190a55 player: fix display-sync timing if audio take long on resume
In display-sync mode, the very first video frame is idiotically fully
timed, even though audio has not been synced yet at this point, and the
video frame is more like a "preview" frame. But since it's fully timed,
an underflow is detected if audio takes longer than the display time of
the frame (we send the second frame only after audio is done).

The timing code will try to compensate for the determined desync, but it
really shouldn't. So explicitly discard the timing info in this specific
case. On the other hand, if the first frame still hasn't finished
display, we can pretend everything is ok.

This is a hack - ideally, we either would send a frame without timing
info (and then send it again or so when playback starts properly), or we
would add real pause support to the VO, and pause it during syncing.
2016-08-07 14:14:32 +02:00
wm4 39ae261cc5 demux_timeline: enable refresh seeks in some situations
Play a trick to make the packet pos field monotonically increasing over
segment boundaries if the source demuxers return monotonically
increasing pos values. This allows the demuxer to uniquely identify
packets with the pos field, and can do refresh seeks using that.

Normally, the packet pos field is used as a fallback for determining the
playback position if the demuxer returns no proper duration. But
demux_timeline.c always will, and the packet pos fields usually make no
sense in relation to the returned file size anyway if the timeline
source demuxers originate from separate streams.
2016-08-07 13:53:34 +02:00
wm4 78bcbe289e demux: make refresh seek handling more generic
Remove the explicit whitelisting of formats for refresh seeks. Instead,
check whether the packet position is somewhat reliable during demuxing.
If there are packets without position, or the packet position is not
monotonically increasing, then do not use them for refresh seeks.

This does not make sure of some requirements, such as deterministic
seeks. If that happens, mpv will mess up a bit on stream switching.

Also, add another method that uses DTS to identify packets, and prefer
it to the packet position method. Even if there's a demuxer which
randomizes packet positions, it hardly can do that with DTS. The DTS
method is not always available either, though. Some formats do not have
a DTS, and others are not always strictly monotonic (possibly due to
libavformat codec parsing and timestamp determination issues).
2016-08-06 19:28:41 +02:00
wm4 91a3bda6f5 player: sync audio as well when enabling it mid-stream
If an audio track is enabled during playback, then make it resume at the
exact "current position", instead of playing audio before that position.
This was already done for video.
2016-08-06 17:48:45 +02:00
wm4 7fd3dbcd52 demux: fix a minor race condition
If the packet read function returns, and EOF was detected, and a seek
was issued in the meantime, then don't use the EOF result. The seek will
be processed later, and reset the EOF state anyway.

The main effect is probably that we don't return EOF to the decoders
(which the playback core resets before issuing the seek), and that we
won't log an EOF message.

Not important, but slightly more correct.
2016-08-06 17:48:45 +02:00
wm4 d41f0a54b0 player: improve instant track switching
When switching tracks, we normally have the problem that data gets lost
due to readahead buffering. (Which in turn is because we're stubborn and
instruct the demuxers to discard data on unselected streams.) The
demuxer layer has a hack that re-reads discarded buffered data if a
stream is enabled mid-stream, so track switching will seem instant.

A somewhat similar problem is when all tracks of an external files were
disabled - when enabling the first track, we have to seek to the target
position.

Handle these with the same mechanism. Pass the "current time" to the
demuxer's stream switch function, and let the demuxer figure out what to
do. The demuxer will issue a refresh seek (if possible) to update the
new stream, or will issue a "normal" seek if there was no active stream
yet.

One case that changes is when a video/audio stream is enabled on an
external file with only a subtitle stream active, and the demuxer does
not support rrefresh seeks. This is a fuzzy case, because subtitles are
sparse, and the demuxer might have skipped large amounts of data. We
used to seek (and send the subtitle decoder some subtitle packets
twice). This case is sort of obscure and insane, and the fix would be
questionable, so we simply don't care.

Should mostly fix #3392.
2016-08-06 15:47:04 +02:00
wm4 d4ee5e5a8a build: always require atomics
Always require them, instead of just for some components which have hard
requirements on correct atomic semantics. They should be widely
available, and are supported by all recent gcc and clang compiler
versions. We even have the fallbacks builtins, which should keep this
working on very old gcc releases.

In particular, w32_common.c recently added a hard requirement on
atomics, but checking this properly in the build system would have been
messy. This commit makes sure it always works.

The fallback where weak atomic semantics are always fine is in theory
rather questionable as well.
2016-08-05 17:10:22 +02:00
wm4 b2e5eb13bc ao_wasapi: in exclusive mode do not output multichannel by default
Exactly the same situation as with ao_alsa in commit 0b144eac (except
that we can detect the situation better under wasapi).

Essentially, wasapi will allow us to output any sample format, and not
just the one configured by the user in the audio system settings.
2016-08-05 16:11:42 +02:00
wm4 088a0075c5 w32_common: don't wait for GUI thread when polling for events
VOCTRL_CHECK_EVENTS is called on every frame. This is by design, and is
supposed to check the event queue of the windowing API.

With the decoupled GUI thread in w32_common.c this doesn't make too much
sense, and the purpose of VOCTRL_CHECK_EVENTS is really reduced to
checking event flags. Even worse, waiting on the GUI thread can
interfere with playback, since win32 sometimes blocks the event loop
(e.g. clicking the window title bar).

Change the code such that we really only query the event flags. Use
atomics to avoid having to add a new mutex. (We assume we always have
real atomics available. The build system doesn't check this properly,
and it could fall back to dummy atomics, which are not atomic.)

Should help with #3393. Doesn't help if the core happens to send a
synchronous request, most commonly via VOCTRL_SET_CURSOR_VISIBILITY or
VOCTRL_UPDATE_PLAYBACK_STATE.
2016-08-05 16:04:57 +02:00
wm4 9f70117233 ao_null: use channel list option type for channel-layouts suboption 2016-08-05 12:23:42 +02:00
wm4 eccabeb81c options: fix channels options copy/free operations
For some fucked up reason the arguments can be NULL. Makes no sense to
me, but ok.
2016-08-05 12:23:15 +02:00
Ben Boeckel 0c4a73fa21 tvi_v4l2: fix style in the uninit function 2016-08-05 10:08:57 +02:00
Ben Boeckel fa7beaad1f tvi_v4l2: explicitly brace the code
It's fine either way, but this code is weirdly formatted. Make it more
explicit.
2016-08-05 10:08:52 +02:00
wm4 00091411ec github: discourage bug reports 2016-08-05 00:41:08 +02:00
wm4 0b144eac39 audio: use --audio-channels=auto behavior, except on ALSA
This commit adds an --audio-channel=auto-safe mode, and makes it the
default. This mode behaves like "auto" with most AOs, except with
ao_alsa. The intention is to allow multichannel output by default on
sane APIs. ALSA is not sane as in it's so low level that it will e.g.
configure any layout over HDMI, even if the connected A/V receiver does
not support it. The HDMI fuckup is of course not ALSA's fault, but other
audio APIs normally isolate applications from dealing with this and
require the user to globally configure the correct output layout.

This will help with other AOs too. ao_lavc (encoding) is changed to the
new semantics as well, because it used to force stereo (perhaps because
encoding mode is supposed to produce safe files for crap devices?).
Exclusive mode output on Windows might need to be adjusted accordingly,
as it grants the same kind of low level access as ALSA (requires more
research).

In addition to the things mentioned above, the --audio-channels option
is extended to accept a set of channel layouts. This is supposed to be
the correct way to configure mpv ALSA multichannel output. You need to
put a list of channel layouts that your A/V receiver supports.
2016-08-04 20:49:20 +02:00
wm4 c30aa23401 player: remove special-case for DL/DR speakers
Pointless anyway. With superficial checking I couldn't find any decoder
which actually outputs this, and AO chmap negotiation would properly
ignore them anyway in most cases.
2016-08-04 19:14:35 +02:00
wm4 efa9fe8edf options: un-restrict --audio-delay
Not a real reason to restrict its value range.
2016-08-04 11:27:15 +02:00
wm4 3c3361217c player: offset demuxer on start/seek properly with audio/sub delay
Assume you use a large value like --audio-delay=20. Then until now the
player would just have seeked normally to a "too late" position, and
played silence for about 20 seconds until audio in the correct time
range is coming again.

Change this by offsetting seeks by the right amount. This works for both
external and muxed files. If a seek isn't precise, then it works only
for external files.

This might cause issues with very large delay options. Hr-seek skipping
could take a lot of time (especially because it affects video too), the
demuxer queue could overflow, and other weird corner cases could appear.
But we just try this on best-effort basis, and if the user uses extreme
values we don't guarantee good behavior.
2016-08-04 11:26:57 +02:00
wm4 356e703510 vo_opengl_cb: log better error message if OpenGL not initialized by user
Otherwise opengl/video.c would confusingly complain about a wrong
version, which in this case makes no sense.
2016-08-03 19:18:00 +02:00
wm4 f3c35d8108 af_lavcac3enc: skip output if there was no input frame
Unrealistic corner case: drainning was initiated right after a seek.
2016-08-02 22:06:22 +02:00
Rostislav Pehlivanov dea738a245 wayland_common: check for NULL current_output on fs switching
Prevents segfaults when a fullscreen switch is issued before fully
initializing the VO.
Doesn't change anything since the schedule_resize is only there to
resize in case the image size switches, which happens long after init.
2016-08-01 22:16:40 +02:00
wm4 251299da4f af_lavcac3enc: fix buffering timestamps calculations
In theory, an encoder could buffer some data.
2016-08-01 19:59:59 +02:00
wm4 2e3db648b5 af_lavcac3enc: fix memory leak
A major one. Oops.
2016-08-01 17:59:37 +02:00
Rostislav Pehlivanov 08a4af5e6e wayland_common: provide the real scaled window resolution
It makes more sense to completely abstract this scaling inside the
backend so that internally the player only works with real actual
drawn pixels.
2016-08-01 00:31:34 +02:00
wm4 0432ab8f09 af_lavcac3enc: fix a debug message 2016-07-31 18:51:10 +02:00
wm4 0a1c87464b af_lavcac3enc: error out properly if encoding fails 2016-07-31 18:51:08 +02:00
wm4 48f60e182a af_lavcac3enc: fix aspects of AVFrame handling
We send a refcounted frame to the encoder, but then disrespect
refcounting rules and write to the frame data without making sure the
buffer is really writeable.

In theory this can lead to reallocation on every frame is the encoder
really keeps a reference. If we really cared, we could fix this by
providing a buffer pool. But then again, we don't care.
2016-07-31 18:51:05 +02:00
wm4 98af572484 audio: make mp_audio_realloc[_min] ensure frame is writeable
This is logical: the function makes sense only in situations where you
are going to write to the audio data. To make it worse,
av_buffer_realloc() also handles this situation, but only if the buffer
size changes (simply because it can't realloc memory in use), so we have
to explicitly force reallocation by unreffing the buffers first.
2016-07-31 18:51:02 +02:00
Ricardo Constantino 5bcb22beea ytdl: Error out with http_dash_segments
Unsupported for now.
2016-07-30 19:02:39 +02:00
wm4 546f7472fb options: add vp9 to --hwdec-codecs 2016-07-30 15:47:51 +02:00
Rostislav Pehlivanov 45687c17f7 wayland_common: fix fullscreen image switching bug
The problem was that when in fullscreen, switching between images did
not issue a resize event, causing none of the images to be rendered
correctly.
This fixes the problem by issuing a resize event with the screen width
and height.
This commit also moves the zeroing of the events field to when it gets
retrieved by mpv rather than randomly after a resize in the vo/backend
code.
2016-07-30 15:33:00 +02:00
Rostislav Pehlivanov 2f986a5980 wayland_common: prevent black bars on most non-native aspect ratios
ssurface_handle_configure()'s width and height are just hints given by
the compositor, the application's free to not respect those strictly and
to compensate for e.g. aspect ratio.
This prevents crazy scenarios in which pictures with portrait aspect
ratios have a huge black area to make them 16:9 or whatever the
compositor feels like.
2016-07-30 15:32:57 +02:00
Eric Toombs 27b59e1940 demux_raw: s16be support was missing due to small typo
Signed-off-by: wm4 <wm4@nowhere>
2016-07-30 00:05:40 +02:00
Jan Ekström aaf6e3b58d wscript: add proper non-version'd SONAME for Android
This seems to have become a requirement since API target 23+, and
matches what FFmpeg does.
2016-07-30 00:02:40 +02:00
Chris Mayo f95cde60ff build: add --htmldir option
Defaults to docdir but makes it possible to install html documentation
separately.
2016-07-30 00:02:40 +02:00
Rostislav Pehlivanov 3fe5ff99c6 wayland_common: clip window size to the display output size
With X11 it was usually left up to the window manager to prevent huge
windows from being out of range, but no Wayland compositor will do
this right now.

Hugely improves usability when using mpv as an image viewer.
2016-07-30 00:02:40 +02:00
Rostislav Pehlivanov f918ec4b2b wayland_common: flush wakeup_pipe on a wakeup
Missed during the recent changes.
Also simplify error checking code and check for POLLNVAL
as well (the display fd was never actually checked to be valid).
2016-07-30 00:02:39 +02:00
Rostislav Pehlivanov c3e11f7b7c osdep/io: introduce mp_flush_wakeup_pipe()
Makes a fairly common occurence with wakeup_pipes easier to handle.
2016-07-30 00:02:39 +02:00
Rostislav Pehlivanov f3f4e048d8 wayland_common: remove untested/unusable wayland dnd code
Not worth keeping 200 lines of untestable as of today code which might
be broken, if it hasn't been already.
2016-07-30 00:02:39 +02:00
Niklas Haas 46b60a3e72 vo_opengl: remove the 3dlut-size npot2 restriction
This requires changing the pixel upload alignment because the odd sizes
might not be aligned to multiples of 4.

Anyway, the restriction has no real benefit and the sizes in between 32
and 64 might be worth using, so just drop it.
2016-07-25 12:50:11 +02:00
Niklas Haas f127869037 vo_opengl: reduce default 3dlut-size to 64x64x64
Following testing after ebe798a, this is a more than sufficient size to
cover our use case.

The old default was a drop of about 58 dB PSNR using the old code, and
this new default is about 65 dB PSNR, so it's actually an improvement
despite resulting in a smaller size.

There was no outlier whatsoever when comparing sizes around the 64
neighbourhood (with every step corresponding to a PSNR drop of about
0.07 dB), so I picked this since it's a power of two and requires no
change to the current 3dlut-size parsing logic.

I also tested smaller sizes such as 32x32x32 which performed almost as
well on colorful samples, but this results in noticeable black boost in
the dark regions, which is pretty undesirable. Therefore, we should
avoid going much further below 64x64x64.

Either way, this new size is so fast to compute that the 3dlut cache is
almost useless on my end. In fact, it might even be slower to load the
profile from the cache than to recompute it from scratch. (For caches on
a disk. For cache on a tmpfs, it makes no difference)
2016-07-25 12:50:04 +02:00
wm4 cb4342dda4 x11: do not accidentally discard event flags
It seems vo_x11_check_events() was supposed to return the currently
flagged events and reset them. But there are many places where
vo_x11_check_events() is called without checking its return value. This
could lead to forgotten events.

Change the code such that they can't get lost.
2016-07-25 11:28:06 +02:00
Niklas Haas ebe798a333 vo_opengl: increase 3DLUT accuracy at lower LUT sizes
This code had the exact same texture indexing bug that the original
scaler code had before the introduction of the LUT_POS macro to fix it.

We can re-use this same macro here, and the performance drop is
virtually entirely negligible. The benefit is greatly improved LUT
accuracy as the 3DLUT size decreases - in particular, the old LUT
started introducing more and more black crush the lower your LUT size is
(because the error was essentially an over-contrast bias, with a
magnitude linearly related to the lut size).

The new code improves black stability as the LUT size decreases, and
only at very low values (16 and below) do black levels start noticeably
getting affected (due to crude linearization of the nonlinear response
curve).

The default value of 3dlut-size is definitely generous enough for this
to make no difference out of the box, but it also causes no performance
drop at all on my machine so I see no harm in improving the logic.
Furthermore, this means we could easily decrease the default 3dlut size
in a future commit, perhaps even down to 64x64x64 as a default. (But
more testing is warranted here)
2016-07-25 10:26:45 +02:00
wm4 dcfde2934d audio: use idiotic FFmpeg ABI rules for public-except-not-public fields
The FFmpeg API is incredibly weird and inconsistent about this. This is
also a FFmpeg-only issue and nothing like this is in Libav - which
doesn't really show FFmpeg in a very positive light.

(To make it even worse: this is a full-blown Libav API incompatibility,
even though this crap was added for Libav ABI-compatibility. It's
absurd.)

Quoting the FFmpeg header for the AVFrame.channels field:

    /**
     * number of audio channels, only used for audio.
     * Code outside libavutil should access this field using:
     * av_frame_get_channels(frame)
     * - encoding: unused
     * - decoding: Read by user.
     */
    int channels;

It says "should" not must, and it doesn't even mention
av_frame_set_channels(). It's also in the section for public fields (not
below a marker that indicates private fields in a public struct, like
it's done e.g. in AVCodecContext).

But not using the accessor will cause silent failures on ABI changes.
The failure that happened due to this code didn't even make it apparent
what was wrong. So just use the idiotic accessor.

Also harmonize the FFmpeg-cursing in the code. (It's fully justified.)

Fixes #3295.

Note that mpv will still check the exact library version numbers, and
reject mismatches - to protect itself from such issues in the future.
2016-07-24 19:33:20 +02:00
wm4 6bb48a10db player: disable DS with spdif transcoding too
Otherwise it behaves dumb. (Although you could argue it shouldn't try to
guess whether speed changes work, but instead simply disable DS if they
don't work.)
2016-07-24 19:19:24 +02:00
wm4 3623cec7d2 af_lavcac3enc: use common code for AVFrame setup 2016-07-24 19:06:00 +02:00
wm4 d60db967bd demux_lavf: remove subtitle seeking special-case
It used not to work - but now it apparently does. Not sure when that got
fixed in FFmpeg, but there's no longer a reason to keep this hack.

This also gets rid of the check for the read_seek2 field, which is not
part of the public API.
2016-07-24 18:41:55 +02:00
wm4 99d9921f39 vo_opengl: glctx can be NULL during init
This fixes a crash that can happen with the Cocoa backend: it calls
vo_wakeup() during init, which calls vo_opengl.c/wakeup().

Fixes #3360.
2016-07-21 16:07:15 +02:00
wm4 16d276308a x11, wayland: do not accidentally close FD 0
Both backends have code to close each FD of their wakeup_pipe array.
This array is default-initialized with 0, which means if the backends
exit before the wakeup pipe is created (e.g. when probing), they would
close FD 0.

Initialize the FDs with -1. Then we call close(-1) in these situations,
which is perfectly allowed and has no bad consequences.
2016-07-21 15:18:32 +02:00
wm4 b4acfcc8aa vo: remove now unused event_fd handling 2016-07-21 14:48:30 +02:00
Rostislav Pehlivanov c6b83fec7d wayland_common: make function declaration consistent
Half the code had a space before the arguments and half didn't.
2016-07-21 14:45:40 +02:00
Rostislav Pehlivanov c0ef3cf9c2 wayland: port to the new wakeup/wait_events framework
This fits natively into the vo/backend and allows to simplify the
polling code.
One new change is the fact that surface_handle_enter flags VO_EVENT_WIN_STATE
and VO_EVENT_RESIZE instead of only VO_EVENT_WIN_STATE. Before this, the code
hackily relied on the timeout and the loop in the wait_frame function to track
and set the scaling factor. Instead, this triggers mpv to run a schedule_resize
and adjust the new VO output dimensions immediately. This is also more accurate
since surface_handle_enter() gets called when a surface is created, moved and
resized, which is exactly what the rest of the player might be interested in.
2016-07-21 14:45:38 +02:00
wm4 8f51418ca4 manpage: not-document tscale=linear 2016-07-21 12:26:05 +02:00
Niklas Haas 7d01a16f64 vo_opengl: add a tscale=linear direct implementation
This uses GLSL mix() instead of going through an indirect texture
access. Easy to implement and might require less resources on some
devices, since the oversample code was already essentially just a
special case of this.

Could be made the new default (as per issue #2685), but that should be
done in a separate commit.
2016-07-21 11:26:19 +02:00
Akemi f53c7f1a78 cocoa: remove unused icc profile variables 2016-07-21 08:48:45 +02:00
Akemi 7531275f62 cocoa: update screen fps only if necessary
we don't need to update the display refresh rate
when going fullscreen or updating the colour profile
2016-07-21 08:48:37 +02:00
wm4 fde784129f x11: stop using vo.event_fd
Instead let it do its own event loop wakeup handling.
2016-07-20 20:52:08 +02:00
wm4 bd9c0a10e5 vo_opengl: allow backends to provide callbacks for custom event loops
Until now, this has been either handled over vo.event_fd (which should
go away), or by putting event handling on a separate thread. The
backends which do the latter do it for a reason and won't need this, but
X11 and Wayland will, in order to get rid of event_fd.
2016-07-20 20:42:30 +02:00
Rostislav Pehlivanov e11a20a812 vo_wayland: fix high CPU usage due to busy polling
There's no need to call wl_display_flush() since all the client-side
buffered data has already been flushed prior to polling the fd.
Instead only check for POLLIN and the usual ERR+HUP.
2016-07-19 10:01:51 +02:00
wm4 64cd0f4292 x11: skip ICC update on every window move
Don't just cause vo_opengl to update the ICC profile every time the
window is moved. Instead, explicitly check if the screen was changed.

Mostly untested.
2016-07-18 13:06:38 +02:00
wm4 510c6a1be4 libarchive: sanitize non-UTF8 archive entries
Some client API users simply don't like such filenames. For their sake,
don't return them, but return a dummy filename instead. (Returning a
latin1-ized version would work too, but is slightly more work.)

Also remove the "\n" from the replacement dummy filename. This was
accidental.
2016-07-18 12:52:59 +02:00
wm4 fb8deb69a6 libarchive: unify entry iteration between stream/demux layers
No really good reason to duplicate this.
2016-07-18 12:44:56 +02:00
wm4 77e1e8e38e audio: refactor mixer code and delete mixer.c
mixer.c didn't really deserve to be separate anymore, as half of its
contents were unnecessary glue code after recent changes. It also
created a weird split between audio.c and af.c due to the fact that
mixer.c could insert audio filters. With the code being in audio.c
directly, together with other code that unserts filters during runtime,
it will be possible to cleanup this code a bit and make it work like the
video filter code.

As part of this change, make the balance code work like the volume code,
and add an option to back the current balance value. Also, since the
balance semantics are unexpected for most users (panning between the
audio channels, instead of just changing the relative volume), and there
are some other volumes, formally deprecate both the old property and the
new option.
2016-07-17 19:21:28 +02:00
wm4 79974e7ad9 audio: fix crashes due to broken uninit check
Since mixer->ao is always NULL now (it was really just forgotten to be
removed), the uninit call never actually cleared the af field, leaving
a dangling pointer that could be accessed by volume control.
2016-07-15 23:11:25 +02:00
wm4 ea18cb99ba vf_vavpp: get rid of mp_refqueue_is_interlaced()
This makes the difference between passing VA_FRAME_PICTURE or
VA_BOTTOM_FIELD for progressive frames (that should be force-
deinterlaced) to VAProcPipelineParameterBuffer.flags. VA-VPP doesn't
really seem to care, and we can get rid of mp_refqueue_is_interlaced()
entirely. It could be argued it's better to pass field flags instead of
the progressive flag.
2016-07-15 20:37:46 +02:00
wm4 c5361d12d8 vf_d3d11vpp: fix interlaced-only=no mode
"Real" frame flag vs. what we pretend it to be. It always used the real
flag, and thus never deinterlaced unflagged frames, even if the
suboption was set to "no".
2016-07-15 20:21:03 +02:00
wm4 358932a109 vf_d3d11vpp: add video processor selection
Unfortunately completely useless. I still don't know how to force a
video processor to use a specific algorithm, if it's even possible.
2016-07-15 19:48:58 +02:00
wm4 831fc4f012 player: do not cut off terminal status line if it contains newlines
Just a heuristic to preserve the status line in odd corner cases.
Probably a crap idea.

Fixes #3340.
2016-07-15 18:58:36 +02:00
wm4 f29bba1123 af: avoid rebuilding filter chain in another minor case
No need to create additional noise of we can trivially see that
rebuiding the chain won't change anything.
2016-07-15 13:04:17 +02:00
wm4 48f9ea7104 videotoolbox: add yuv420p to --videotoolbox-format 2016-07-15 13:04:17 +02:00
wm4 ae94f329a9 vo_opengl: hwdec: reset hw_subfmt field
In theory, mp_image_params with hw_subfmt set to non-0 if imgfmt is not
a hwaccel format is invalid. (It worked fine because nothing checks this
yet.)
2016-07-15 13:04:17 +02:00
wm4 85488f6892 video: change hw_subfmt meaning
The hw_subfmt field roughly corresponds to the field
AVHWFramesContext.sw_format in ffmpeg. The ffmpeg one is of the type
AVPixelFormat (instead of the underlying hardware format), so it's a
good idea to switch to this too for preparation.

Now the hw_subfmt field is an mp_imgfmt instead of an opaque/API-
specific number. VDPAU and Direct3D11 already used mp_imgfmt, but
Videotoolbox and VAAPI had to be switched.

One somewhat user-visible change is that the verbose log will now always
show the hw_subfmt as image format, instead of as nonsensical number.

(In the end it would be good if we could switch to AVHWFramesContext
completely, but the upstream API is incomplete and doesn't cover
Direct3D11 and Videotoolbox.)
2016-07-15 13:04:17 +02:00
Martin Herkt 4a4a9f3302 man: fix PDF build error
ReportLab really doesn’t like breaking inline literals, so insert an
explicit line break.

Fixes #3338
2016-07-15 12:59:26 +02:00
Aman Gupta 588b2f48e5 videotoolbox: add --hwdec=videotoolbox-copy for h/w accelerated decoding with video filters 2016-07-15 01:01:17 +02:00
wm4 e656889624 DOCS/compile-windows.md: MSVC DLL usage instructions 2016-07-14 22:40:10 +02:00
Timotej Lazar 91a1b17104 Use - as command-name separator everywhere
Old-style commands using _ as separator (e.g. show_progress) were still
used in some places, including documentation and configuration files.
This commit updates all such instances to the new style (show-progress)
so that commands are easier to find in the manual.
2016-07-14 22:37:42 +02:00
wm4 6e45e1de77 demux_timeline: restore mkv edition switching 2016-07-14 18:26:58 +02:00
wm4 d191d76e52 ao_pulse: fix some volume control rounding issues
Volume could get easily "stuck" or making too huge steps when doing
things like "add ao-volume 1".
2016-07-14 18:11:14 +02:00
wm4 f53d73b9dc ao_creoaudio: print OSStatus as decimal signed integer too
OSStatus is quite inconsistent. Sometimes it's a FourCC, sometimes it
reads as decimal signed number.
2016-07-13 17:07:06 +02:00
wm4 79f48500e2 ao_coreaudio: use correct free function on errors 2016-07-13 16:34:00 +02:00
wm4 b5bc93d009 manpage: add some missing color management related sub-properties 2016-07-13 11:05:27 +02:00
Niklas Haas a14f9249c4 command: add properties for HDR metadata
Since it turns out that knowing what exactly a file was tagged with can
be useful for debugging purposes, expose this as a property so I can
check it more easily.

This is mostly useful for sig-peak (since nom-peak is currently entirely
calculated by us), but I added both for consistency.
2016-07-13 11:03:42 +02:00
wm4 8b47e5daa4 video: respect --deinterlace=auto
--deinterlace=auto is the default, and has the obscure semantics that
deinterlacing is disabled, unless the user has manually inserted a
deinterlacing filter.

While in software decoding this doesn't matter, and we will happily
insert 2 yadif filters (if the user has already added one), or not
remove the yadif filter (if deinterlacing is disabled, but the user has
added the filter manually), this is different with hardware deinterlacer
filters. These support VFCTRL_SET_DEINTERLACE for toggling deinterlacing
filtering at runtime. It exists mainly for legacy reasons, and possibly
because it makes switching deinterlacing modes more efficient. It might
also gives us an entry-point for VO deinterlacing, maybe. For whatever
reasons this mechanism exists, we still support and use it.

This commit fixes that video.c always used VFCTRL_SET_DEINTERLACE to
disable deinterlacing, even if --deinterlace=auto was set. Fix this by
checking the value of the option directly.
2016-07-12 19:37:09 +02:00
James Ross-Gowan bf2e2bf3e8 win32: mpv.rc: re-add version info
For some reason, the lack of version info was preventing mpv from
appearing in the Default Programs dialog. Re-add it, but don't set the
string version numbers from version.h, because that's what was causing
trouble when the version info was removed. Like the binary version
numbers, these are now hardcoded to 2.0.0.0, which probably doesn't
matter.

The new version info block is also slightly different to the old one. It
fills out all the binary VERSIONINFO fields and makes better use of
macros. It also removes the \000 line terminators from the string
version info, since as far as I can tell, this was just cargo-culting
for an old broken version of the Microsoft resource compiler, and
binutils' windres terminates the strings properly without them.
2016-07-12 21:05:33 +10:00
James Ross-Gowan 43e811cb4b vo_opengl: angle: use WARP if there are no hw adapters
This should get mpv working on Windows 7 machines without hardware
accelerated graphics adapters. It already worked on Windows 8 and up
because those systems would silently fall back to WARP if there was no
graphics hardware installed.

The normal MPGL_CAP_SW flag is not set, so unlike other opengl backends,
this will choose a software adapter even if opengl:sw is not specified.
The reason for this is, unlike on Linux, where vo_xv and vo_x11 can be
used, mpv on Windows does not have any VO to fall back on when hardware
acceleration isn't available, so if software adapters are rejected, the
user won't see any video output when using the default settings. WARP
seems to perform quite well, so it should be used in this case.
2016-07-12 20:26:41 +10:00
wm4 2d44dfaba9 cache: minor simplification
Every cache_wakeup_and_wait() caller has to deal with asynchronous
stream abort, so why not make it somehow part of the function.
2016-07-11 22:20:23 +02:00
wm4 f4d62dc4a0 cache: fix previous commit
The cache reader thread actually unlocks the mutex protecting the
underlying stream while reading from it. That's why other code goes out
of its way to run certain stream operations on the cache thread. Do the
same.

We could have this simpler by creating a mechanism that would "park" the
cache thread and make it wait for the lock (while we have it) in order
to gain exclusive access. This could be done in the future.
2016-07-11 22:16:06 +02:00
wm4 374600cec0 cache: propagate seek failures
Eagerly execute seeks to the underlying stream in the cache seek
entrypoint itself. While asynchronous execution is a goal of the cache,
it doesn't matter too much for seeks. They always were executed within
the lock, so the reader was blocked anyway. It's not necessary to ensure
async. execution here either, because seeks are relatively rare, and the
demuxer can just stay blocked for a while.

Fixes: mpv http://samples.mplayerhq.hu/V-codecs/DIV5/ayaneshk-test.avi
2016-07-11 20:52:30 +02:00
James Ross-Gowan 5a6ba2a4fd vo_opengl: angle: try D3D9 when D3D11 fails eglInitialize
This will happen when D3D11 is present on the machine but the supported
feature level is too low.
2016-07-11 22:07:13 +10:00
wm4 e246c3f060 audio: fix code for adjusting conversion filters
This code was supposed to adjust existing conversion filters (to make
them output a different format). But the code was just broken,
apparently a refactoring accident. It accessed af instead of af->prev.

The bug tended to add new conversion filters, even if an existing one
could have been used. (Can be tested by inserting a dummy lavrresample
filter followed by a format filter which forces conversion.)

In addition, it's probably better to return the actual error code if
reinitializing the filter fails. It would then respect an AF_FALSE
return value, which means format negotiation failed, instead of a
generic error.
2016-07-11 12:23:32 +02:00
wm4 61afe3820a af_volume: don't let softvol overwrite af_volume volumedb sub-option
af_volume has a volumedb sub-option, which allows the user to set an
explicit volume. Until recently, the player read back this value and
used it as initial softvol volume. But now it just overwrites it.

Instead of overwriting it, multiply the different gain values. Above
all, this will do the right thing if only softvol is used, or if the
user only adds the af_volume filter manually.
2016-07-11 11:03:36 +02:00
wm4 60048b7eb9 audio: add heuristic to move auto-downmixing before other filters
Normally, you want downmixing to happen first thing in the filter chain.
This is reflected in codec downmixing, which feeds the filter chain
downmixed audio in the first place. Doing this has the advantage of
needing less data to process. But the main motivation is that if there
is a drc filter in the chain, you want to process it the downmixed
audio.

Add an idiotic heuristic to achieve this. It tries to detect whether the
audio was indeed automatically downmixed (or upmixed). To detect what
the output format is going to be, it builds the filter chain normally,
and then retries with the heuristic applied (and for extra paranoia,
retries without the heuristic again if it fails to successfully rebuild
the filter chain for unknown reasons). This is simple and will work in
almost all cases.

Doing it in a more complete way is rather hard, because filters are so
generic. For example, we know absolutely nothing about the behavior of
af_lavfi, which creates an opaque filter graph with libavfilter. We
don't know why a filter would e.g. change the channel layout on its
output. (Our heuristic bails out in this case.) We're also slave to the
lowest common denominator of how our format negotiation works, and how
libavfilter's works.

In theory, we could make this mechanism explicit by introducing a
special dummy filter. The filter chain would then try to convert between
input and output formats at the dummy filter, which would give the user
more control over how downmix happens. On the other hand, the user could
just insert explicit conversion filters instead, so this would probably
have questionable value.
2016-07-10 19:53:53 +02:00
wm4 7be98ef1b2 audio: add auto-inserted flag to filter list logging
Like the video filter chain.
2016-07-10 19:51:09 +02:00
wm4 2eac58eaa9 audio: cleanup audio filter format negotiation
The algorithm and functionality is the same, but the code becomes much
simpler and easier to follow.

The assumption that there is only 1 conversion filter (lavrresample)
helps with the simplification, but the main change is to use the same
code for format/channels/rate. Get rid of the different AF_CONTROL_SET_*
controls, and change the af->data parameters directly. (af->data is
badly named, but essentially is a placeholder for the output format.)

Also, instead of trying to use the af_reinit() loop to init inserted
conversion filters or filters with changed output formats, do it inline,
and move the common code to a filter_reinit() function. This gets rid of
the awful retry variable.

In general, this should not change any runtime behavior.
2016-07-10 19:51:09 +02:00
wm4 065bb635d9 manpage: minor fix
Also fix some other type in interface-changes.rst.
2016-07-10 19:51:09 +02:00
Uros Vampl c5827387fd audio: show an osd bar when changing ao-volume
also, make the osd msg prettier
2016-07-10 19:49:28 +02:00
Martin Herkt 1e00bcc14c Release 0.18.1 2016-07-10 02:20:08 +02:00
Martin Herkt 7635438592 Merge branch 'master' into release/current 2016-07-10 01:06:17 +02:00
wm4 e518bf2c72 audio: insert audio-inserted filters at end of chain
This happens to be better for the af_volume filter (for softvol), and
saves some code too. It's "better" because you want to affect the
final filtered audio, such as after a manually inserted drc filter.
2016-07-09 20:23:15 +02:00
wm4 d47b708f00 audio: don't crash when changing volume if no audio is initialized
Oversight.
2016-07-09 19:34:45 +02:00
wm4 2fb2f9b93a man: fix typo 2016-07-09 18:55:14 +02:00
wm4 995c47da9a audio: drop --softvol=no and --softvol=auto
Drop the code for switching the volume options and properties between
af_volume and AO volume controls. interface-changes.rst mentions the
changes in detail.

Do this because this was exceedingly complex and had other problems as
well. It was also very hard to test. It's just not worth the trouble.

Some leftovers like AOCONTROL_HAS_PER_APP_VOLUME will be removed at a
later point.

Fixes #3322.
2016-07-09 18:31:18 +02:00
Jakub Wilk da9590d368 man: fix typos 2016-07-09 16:27:42 +02:00
Jakub Wilk 34a33ebd4d vo_xv, vo_x11: fix typos in warnings 2016-07-09 14:35:41 +02:00
wm4 e58ecc3630 man: fix botched sentence
Fixes #3323.
2016-07-08 19:34:52 +02:00
wm4 6d83455a95 vd_d3d11vpp: remove nonsensical flush call
I made this call up because I sort of thought this makes senssssse. I'm
now convinced that it does not, and even is actively harmful. I'm still
quite in the dark how the DirectD 11 video API is supposed to work with
synchronization, but at least for normal graphics this call would not
make much sense.
2016-07-08 19:34:24 +02:00
wm4 35be389031 x11: add missing FocusChangeMask
So we actually get FocusOut events. Disables key repeat when losing
focus while a key is down.
2016-07-08 16:36:53 +02:00
wm4 885e991312 ao_coreaudio: error out when selecting invalid device
When selecting a device that simply doesn't exist with --audio-device,
AudioUnit will still initialize and start playback without complaining.
But it will never call the audio render callback, which leads to audio
playback simply not progressing.

I couldn't find a way to get AudioUnit to report an error at all, so
here's a crappy hack that takes care of this in most cases. We assume
that all devices have a kAudioDevicePropertyDeviceIsAlive property.
Invalid devices will error when querying the property (with 'obj!' as
status code).

This is not the correct fix, because we try to double-guess AudioUnit's
behavior by accessing a lower label API. Suggestions welcome.
2016-07-08 16:11:03 +02:00
wm4 26b6873ffd client API: remove "status" log level from mpv_request_event docs
Although it appears to be accepted by the function, MSGL_STATUS messages
are never passed to the client API. Consequently "status" has the same
meaning as "v" and is useless.
2016-07-08 16:10:57 +02:00
wm4 71a8aa2e9a command: don't delay progress updates to next video frame 2016-07-08 13:46:15 +02:00
wm4 16b48a429d video: fix midstream video configuration changes
Commit 771a8bf5 added code to avoid unnecessary vf_reconfig() calls for
unrelated reasons, but forget to consider that it has to be called at
least once if the input format changes. As a consequence it got "stuck"
due to not being able to decode more frames.
2016-07-08 08:22:51 +02:00
wm4 f2110f582d vo_vdpau: minor simplification
Remove some indirections that aren't needed anymore.
2016-07-07 16:10:53 +02:00
wm4 8660b4c9f0 video: limit number of frames sent to VO to the VO requested amount
vo_frame can have more than 1 frame - the extra frames are future
references, which are sometimes useful for filtering (vo_opengl
interpolation). There's no harm in reducing the number of frames sent to
the VO requested amount of future frames, so do that.

Doesn't actually reduce the number of concurrently in use frames in
practice.
2016-07-07 16:10:13 +02:00
wm4 ab6fac43b4 player: cut off status line on terminal width
If the status line is wider than the reported terminal size, then cut it
off instead of causing the terminal to scroll down for the next line.

This is done in the most primitive way possible, assuming ASCII.

This was actually done in the past as far as I'm aware; do it again.
(Probably differently.)
2016-07-06 19:52:09 +02:00
wm4 5d2f1da7c5 vf, af: print filter labels in verbose mode 2016-07-06 14:13:03 +02:00
wm4 771a8bf5c6 video: fix deinterlace filter handling for VFCTRL_SET_DEINTERLACE filters
Some filters support VFCTRL_SET_DEINTERLACE. This affects most hardware
deinterlace filters. They can be inserted by the user manually, or auto-
inserted by vf.c itself as conversion filter (vf_d3d11vpp). In these
cases, we shouldn't insert or remove filters outselves, and instead
VFCTRL_SET_DEINTERLACE should be invoked to switch the mode.

This wasn't done correctly in the recently refactored code and could
have broken with --deinterlace. (The refactor only considered switching
via property in this case.) Fix it by making it a proper part of the
filter_reconfig() function, and making set_deinterlacing() (which is
called by the property handler) merely call filter_reconfig() in all
cases to do the real work.

We can even avoid rebuilding the filter chain - though only if no other
auto-filters are inserted. It probably also provides a slightly cleaner
way to implement functionality in the VO while still inserting video
filter fallbacks correctly if required.
2016-07-06 14:08:47 +02:00
wm4 08cd50b84b vf: mark filter chain as uninitialized when mutating it
Sounds fair. Can be used to determine if the filter chain was mutated at
all, and avoiding unconditional reinit if it wasn't.
2016-07-06 13:47:58 +02:00
wm4 0b1ef81498 video: fix deinterlace filter handling on pixel format changes
The test scenario at hand was hardware decoding a file with d3d11 and
with deinterlacing enabled. The file switches to a non-hardware
dedocdeable format mid-stream. This failed, because it tried to call
vf_reconfig() with the old filters inserted, with was fatal due to
vf_d3d11vpp accepting only hardware input formats.

Fix this by always strictly removing all auto-inserted filters
(including the deinterlacing one), and reconfiguring only after that.

Note that this change is good for other situations too, because we
generally don't want to use a hardware deinterlacer for software
decoding by default. They're not necessarily optimal, and VAAPI VPP even
has incomprehensible deinterlacer bugs specifically with software frames
not coming from a hardware decoder.
2016-07-06 13:38:43 +02:00
wm4 fc76966d9e vf: don't clobber input params on reconfigure failure
I think this is more robust, and future commits will rely on it.
2016-07-06 13:27:20 +02:00
wm4 0fe62bc1d2 player: it's not --force-seeking but --force-seekable
Oops. Fuck.
2016-07-06 00:11:57 +02:00
wm4 d72bcc8041 player: rewrite deinterlace filter auto-insertion
Instead of using the "vf" command code (which changes filters at runtime
on user input), use the general filter-insertion code. The latter was
added later, and is more suitable for automatically inserted filters.

The old code failed in particular when using watch-later saving, which
stored the filter list in the resume config file. If a user changed the
hardware decoding mode via command line, the stored filter chain was out
of date and could cause failure due to not working with hardware or
software decoding mode. Storing the deinterlace filter in the filter
list was unavoidable, because it was part of the user state. (The new
code only edits the actually instantiated filters.)
2016-07-05 21:10:26 +02:00
Niklas Haas 329a7147d0 csp: document deviations from the references where they occur
These mostly happen in situations where the correct behavior is
relatively new and not found in the wild (therefore not worth
implementing) and/or extremely complicated (and thus not worth worrying
about the potential edge cases and UI changes).

Still, it's best to document these where they happen to guide the poor
souls maintaining these files in the future.
2016-07-05 19:16:49 +02:00
Ben Boeckel 4f4eea6b44 x11_common: silence xdg-screensaver
Some screen lockers have a habit of dumping output to the terminal when
their output is reset. Ignore its output to keep the TTY output clean.
2016-07-05 19:16:01 +02:00
Ben Boeckel 33e72ed451 subprocess: add a callback to ignore output
This callback is equivalent to shoving data into /dev/null.
2016-07-05 19:15:59 +02:00
James Ross-Gowan 6a3da439cd vo_opengl: angle: update the swapchain on resize
This uses eglPostSubBufferNV to trigger ANGLE to check the window size
and update the size of the swapchain to match, which is recommended
here: https://groups.google.com/d/msg/angleproject/RvyVkjRCQGU/gfKfT64IAgAJ

With the D3D11 backend, using eglPostSubBufferNV with a 0-sized update
region will even skip the Present() call, meaning it won't block for a
vsync period. Hopefully ANGLE will have a less hacky way of doing this
in future. See the relevant ANGLE issue: http://anglebug.com/1438

Fixes #3301
2016-07-04 22:04:37 +10:00
wm4 f98e1b0b96 vo_opengl: error out gracefully when trying to use FBOs without FBO API
This can for example happen with vo_opengl_cb, if it is used with a GL
implementation that does not supports FBOs. (mpv itself should never
attempt to use FBOs if they're not available.)

Without this check it would trigger an assert() in our dummy
glBindFramebuffer wrapper.

Suspected cause of #3308, although it's still unlikely.
2016-07-04 13:34:31 +02:00
wm4 7cd1a3c766 vf_d3d11vpp: fix output image format if not doing any filtering
For example it should be set to IMGFMT_D3D11NV12 if it isn't already.
Otherwise, an assertion in vf.c could trigger.

This probably couldn't be provoked yet.
2016-07-04 11:54:02 +02:00
wm4 46f1ce1e2f input: remove redundant log message 2016-07-04 10:50:00 +02:00
Niklas Haas be230d16e5 vo_opengl: move eval_szexpr to user_shaders.c
This moves some of the bulky user-shader specific logic into the file
dedicated to it. Rather than expose video.c state, variable lookup is
now done via a simulated closure.
2016-07-03 19:42:52 +02:00
Niklas Haas 5b6cce2b73 vd_lavc: expose mastering display side data reference peak
This greatly improves the result when decoding typical (ST.2084) HDR
content, since the job of tone mapping gets significantly easier when
you're only mapping from 1000 to 250, rather than 10000 to 250.

The difference is so drastic that we can now even reasonably use
`hdr-tone-mapping=linear` and get a very perceptually uniform result
that is only slightly darker than normal. (To compensate for the extra
dynamic range)

Due to weird implementation details, this only seems to be present on
keyframes (or something like that), so we have to cache the last seen
value for the frames in between.

Also, in some files the metadata is just completely broken /
nonsensical, so I decided to apply a simple heuristic to detect
completely broken metadata.
2016-07-03 19:42:52 +02:00
Niklas Haas 923e3c7b20 vo_opengl: generalize HDR tone mapping mechanism
This involves multiple changes:

1. Brightness metadata is split into nominal peak and signal peak.

For a quick and dirty explanation: nominal peak is the brightest value
that your color space can represent (i.e. the brightness of an encoded
1.0), and signal peak is the brightest value that actually occurs in
the video (i.e. the brightest thing that's displayed).

2. vo_opengl uses a new decision logic to figure out the right nom_peak
and sig_peak for all situations. It also does a better job of picking
the right target gamut/colorspace to use for the OSD. (Which still is
and still should be treated as sRGB). This change in logic also
fixes #3293 en passant.

3. Since it was growing rapidly, the logic for auto-guessing / inferring
the right colorimetry configuration (in pass_colormanage) was split from
the logic for actually performing the adaptation (now pass_color_map).

Right now, the new logic doesn't do a whole lot since HDR metadata is
still ignored (but not for long).
2016-07-03 19:42:52 +02:00
Niklas Haas d81fb97f45 mp_image: split colorimetry metadata into its own struct
This has two reasons:

1. I tend to add new fields to this metadata, and every time I've done
so I've consistently forgotten to update all of the dozens of places in
which this colorimetry metadata might end up getting used. While most
usages don't really care about most of the metadata, sometimes the
intend was simply to “copy” the colorimetry metadata from one struct to
another. With this being inside a substruct, those lines of code can now
simply read a.color = b.color without having to care about added or
removed fields.

2. It makes the type definitions nicer for upcoming refactors.

In going through all of the usages, I also expanded a few where I felt
that omitting the “young” fields was a bug.
2016-07-03 19:42:52 +02:00
Niklas Haas 3abf9c9204 vo_opengl: don't constantly resize the output FBO
Commit 883d3114 seems to have (accidentally?) dropped the FBOTEX_FUZZY
from the output_fbo resize, which means that current master will keep
resizing and resizing the FBO as you change the window size, introducing
severe memory leaking after a while. (Not sure why that would cause
memory leaks, but I blame nvidia)

Either way, it's bad for performance too, so it's worth fixing.
2016-07-03 19:42:52 +02:00
wm4 ea23a86f49 sub: remove unused sub_bitmaps.scaled field 2016-07-03 19:32:43 +02:00
wm4 8ed32e90c9 sub: move RGBA scaling to vo_vaapi
vo_vaapi is the only thing which can't scale RGBA on the GPU. (Other
cases of RGBA scaling are handled in draw_bmp.c for some reason.)

Move this code and get rid of the osd_conv_cache thing.

Functionally, nothing changes.
2016-07-03 19:32:43 +02:00
wm4 37cf92c07a sub: change how libass output is converted to RGBA in some cases
This affects VOs (or other code which render OSD) which does not support
the LIBASS format, but only RGBA. Instead of having a converter stage in
osd.c, make mp_ass_packer_pack() output directly in RGBA.

In general, this is work towards refcounted subtitle images.

Although we could keep the "converter" design, doing it this way seems
simpler, at least considering the current situation with only 2 OSD
formats. It also prevents copying & packing the data twice, which will
lead to better performance. (Although I guess this case is not important
at all.)

It also fixes --force-rgba-osd-rendering when used with vo_opengl,
vo_vdpau, and vo_direct3d.
2016-07-03 19:32:31 +02:00
wm4 59972fbfe1 sub: move around some code
Put the packing code into separate functions. Preparation for the
following commit.
2016-07-03 19:32:00 +02:00
wm4 f110552898 sub: pass preferred OSD format to subtitle renderers
The intention is to let mp_ass_packer_pack() produce different output
for the RGBA and LIBASS formats. VOs (or whatever generates the OSD)
currently do not signal a preferred format, and this mechanism just
exists to switch between RGBA and LIBASS formats correctly, preferring
LIBASS if the VO supports it.
2016-07-03 19:31:56 +02:00
wm4 315e2e3da8 vo_opengl: remove caching GL_MAX_TEXTURE_SIZE value
No real need to cache this, and we need fewer fields in the OSD part
struct.

Also add logging for when the OSD texture is reallocated.
2016-07-03 17:33:59 +02:00
wm4 b57debe5b3 vo_opengl: use ringbuffer of PBOs
This is how PBOs are normally supposed to be used.

Unfortunately I can't see an any absolute improvement on nVidia binary
drivers and playing 4K material. Compared to the "old" PBO path with 1
buffer, the measured GL time decreases significantly, though.
2016-07-03 16:34:32 +02:00
wm4 8c7f9dc1a8 vo_opengl: support inconsistent negative strides per plane
GL generally does not support flipping the image on upload, meaning
negative strides are not supported. vo_opengl handles this by flipping
rendering if the stride is inverted, and gl_pbo_upload() "ignores"
negative strides by uploading without flipping the image.

If individual planes had strides with different signs, this broke. The
flipping affected the entire image, and only the sign of the first plane
was respected.

This is just a crazy corner case that will never happen, but it turns
out this is quite simple to support, and actually improves the code
somewhat.
2016-07-03 16:34:32 +02:00
wm4 823c353faa vo_opengl: move PBO upload handling to shared code
This introduces a gl_pbo_upload_tex() function, which works almost like
our gl_upload_tex() glTexSubImage2D() wrapper, except it takes a struct
which caches the PBO handles. It also takes the full texture size (to
make allocating an ideal buffer size easier), and a parameter to disable
PBOs (so that the caller doesn't have to duplicate the gl_upload_tex()
call if PBOs are disabled or unavailable).

This also removes warnings and fallbacks on PBO failure. We just
silently try using PBOs on every frame, and if that fails at some point,
revert to normal texture uploads. Probably doesn't matter.
2016-07-03 16:34:32 +02:00
James Ross-Gowan 06219c7f88 w32_common: make WM_NCHITTEST simpler and more accurate
This makes the geometry of the sizing borders more like the ones in
Windows 10. It also fixes an off-by-one error that made the right and
bottom borders thinner than the left and top borders, which made it
difficult to resize the window when using the Windows 7 classic theme
(because it has pretty thin sizing borders to begin with.)
2016-07-03 21:58:29 +10:00
wm4 5d47f9a565 manpage: document confusing "vf clr" command usage 2016-07-03 13:45:18 +02:00
wm4 1298c29dc7 player: add missing space to error message 2016-07-02 18:26:55 +02:00
Ben Boeckel 76a73a0a5d vd_lavc: hide structs behind platform flags
Otherwise, warnings about them being unused appear.
2016-07-01 19:12:34 -04:00
Ben Boeckel b2ce00ed56 readme: fix typo 2016-07-01 19:12:26 -04:00
wm4 d057e7a142 player: fatal error if linked and compiled FFmpeg versions mismatch
We don't support this anymore.

This tries to exit in a controlled way after command line options are
applied in order to honor logging options and, in case of libmpv, not to
kill the host. Not sure if it would be better to just vomit text to
stderr and call abort().
2016-07-01 20:56:59 +02:00
wm4 3738bfb451 bitmap_packer: remove some unused functions 2016-07-01 20:29:45 +02:00
wm4 8523e1fd95 sub: set ASS sub bitmap data to correct pointer
Point it to the copied data. Doesn't really matter at this point, but
later it might have left dangling pointers.
2016-07-01 20:28:49 +02:00
wm4 fd5098a409 vo_direct3d: remove bitmap packer
See previous comments.
2016-07-01 20:28:49 +02:00
wm4 c0f272fcd1 vo_vdpau: remove bitmap packer use
See previous commit.
2016-07-01 20:28:49 +02:00
wm4 1b71cfccba vo_opengl: remove OSD bitmap packing
It's packed in the OSD common layer already.
2016-07-01 20:28:49 +02:00
wm4 549a9ea6fa command: pack sub image data in overlay-add command
Working towards refcounted sub images, and also for removing bitmap
packers from VOs.

I'm not sure why we even have this overlay-add command. It was sort of
"needed" before opengl-cb was introduced, and before Lua scripts could
put ASS drawings on OSD without conflicting with the OSC. But now trying
to use it doesn't make too much sense anymore.

Still keep it because we're trying to be nice, but throw performance out
of the window. Now image data is copied 2 more times before displaying
it. This also makes using the command a bit simpler.
2016-07-01 20:22:09 +02:00
wm4 614efea3e6 ad_lavc: work around braindead ffmpeg behavior
The libavcodec wmapro decoder will skip some bytes at the start of the
first packet and return each time. It will not return any audio data in
this state.

Our own code as well as libavcodec's new API handling
(avcodec_send_packet() etc.) discard the PTS on the first return, which
means the PTS is never known for the first packet. This results in a
"Failed audio resync." message.

Fixy it by remember the PTS in next_pts. This field is used only if the
decoder outputs no PTS, and is updated after each frame - and thus
should be safe to set.

(Possibly this should be fixed in libavcodec new API handling by not
setting the PTS to NOPTS as long as no real data has been output. It
could even interpolate the PTS if the timebase is known.)

Fixes the failure message seen in #3297.
2016-07-01 15:51:34 +02:00
wm4 c57304a591 sub: pack libass bitmaps directly in sd_ass.c and osd_libass.c
Change all producer of libass images to packing the bitmaps into a
single larger bitmap directly when they're output. This is supposed to
help working towards refcounted sub bitmaps.

This will reduce performance for VOs like vo_xv, but not for vo_opengl.
vo_opengl simply will pick up the pre-packed sub bitmaps, and skip
packing them again. vo_xv will copy and pack the sub bitmaps
unnecessarily - but if we want sub bitmap refcounting, they'd have to be
copied anyway.

The packing code cannot be removed yet from vo_opengl, because there are
certain corner cases that still produce unpackad other sub bitmaps.
Actual refcounting will also require more work.
2016-06-30 21:38:50 +02:00
wm4 2d6da37b6b README: declare that we do not respect FFmpeg ABI rules 2016-06-29 21:16:42 +02:00
wm4 e40851ea22 options: deprecate --heartbeat-cmd
It's useless. --heartbeat-interval is also considered deprecated, but
this is not made explicit.
2016-06-29 18:10:15 +02:00
wm4 9ed5dae480 options: add a deprecation warning printing mechanism
We have a warning mechanism for removed and for replaced options, but
none yet for options which have been simply deprecated.

For the following commit.

(Fun fact: just adding the m_option field increases binary size by
14KB.)
2016-06-29 18:09:30 +02:00
wm4 c6953bfa8c ao_oss: do not add an entry to audio-device-list if device file missing
This effectively makes it go away on Linux (unless you have OSS
emulation loaded).
2016-06-29 17:40:04 +02:00
wm4 deb1c3c7a8 audio: don't add default entry to audio-device-list if AO support listing
In such cases there isn't really a reason to do so, and using such an
entry would probably fail anyway.

Also convenient for the following commit.
2016-06-29 17:38:57 +02:00
wm4 9ca1592f3f d3d: implement screenshots for --hwdec=d3d11va
No method of taking a screenshot was implemented at all. vo_opengl
lacked window screenshotting, because ANGLE doesn't allow reading the
frontbuffer. There was no way to read back from a D3D11 texture either.

Implement reading image data from D3D11 textures. This is a low-quality
effort to get basic screenshots done. Eventually there will be a better
implementation: once we use AVHWFramesContext natively, the readback
implementation will be in libavcodec, and will be able to cache the
staging texture correctly. Hopefully. (For now it doesn't even have a
AVHWFramesContext for D3D11 yet. But the abstraction is more appropriate
for this purpose.)
2016-06-28 20:38:53 +02:00
wm4 17c5738cb4 d3d: merge angle_common.h into d3d.h
OK, this was dumb. The file didn't have much to do with ANGLE, and the
functionality can simply be moved to d3d.c. That file contains helpers
for decoding, but can always be present (on Windows) since it doesn't
access any D3D specific libavcodec APIs. Thus it doesn't need to be
conditionally built like the actual hwaccel wrappers.
2016-06-28 20:07:56 +02:00
Muhammad Faiz d5615102d5 vo_opengl: add output_size uniform to custom shader
logically, scaler should know its input and output size

Signed-off-by: wm4 <wm4@nowhere>
2016-06-28 19:52:55 +02:00
wm4 40c9b38023 vo_opengl: minor typo and coding style fixes 2016-06-28 19:48:29 +02:00
Niklas Haas dc9a5cbfd7 vo_opengl: revise the transfer curve logic
Instead of hard-coding a big list, move some of the functionality
to csputils. Affects both the auto-guess blacklist and the peak
estimation.

Also update the comments.
2016-06-28 19:48:29 +02:00
Niklas Haas 65499d863a csputils: adjust whitespace
All these blank lines felt sort of weird, especially since the functions
were semantically related.
2016-06-28 19:48:29 +02:00
Niklas Haas 6e6c32ed6c vo_opengl: revise the logic for picking the default color space
Too many "exceptions" these days, it's easier to just hard-code a
whitelist instead of a blacklist. And besides, it only really makes
sense to avoid adaptation for BT.601 specifically, since that's the one
we auto-guess based on the resolution.
2016-06-28 19:48:29 +02:00
Niklas Haas f9fe5d06ad vo_opengl: use image_params instead of *_src for autoconfig
I'm not even sure why we ever consulted *_src to begin with, since that
just describes the current image format - and not the original metadata.
(And in fact, we specifically had logic to work around the impliciations
this had on linear scaling)

image_params is *the* authoritative source on the intended (i.e.
reference) image metadata, whereas *_src may be changed by previous
passes already. So only consult image_params for picking auto-generated
values.

Also, add some more missing "wide gamut" and "non-gamma" curves to the
autoconfig blacklist. (Maybe it would make sense to move this list to
csputils in the future? Or perhaps even auto-detect it based on the
associated primaries)
2016-06-28 19:48:29 +02:00
Niklas Haas f3b6966d14 vo_opengl: implement the Panasonic V-Log function
User request and not that hard. Closes #3157.

Note that FFmpeg doesn't support this and there's no signalling in HEVC
etc., so the only way users can access it is by using vf_format
manually.

Mind: This encoding uses full range values, not TV range.
2016-06-28 19:48:29 +02:00
Niklas Haas 740fdc139f manpage: add missing documentation for vf_format:gamma=dci-p3 2016-06-28 19:48:29 +02:00
Niklas Haas 247ec0cb84 csputils: add Panasonic V-Gamut primaries
This is actually not entirely trivial since it involves negative Yxy
coordinates, so the CMM has to be capable of full floating point
operation. Fortunately, LittleCMS is, so we can just blindly implement
it.
2016-06-28 19:48:29 +02:00
Niklas Haas 13a63331b6 manpage: warn about the use of HDR functions for target-trc
Most devices seems to require special signalling (e.g. via HDMI
metadata) to actually decode HDR signals and treat them as such, so it's
probably worth warning the potential user about the fact that mpv pretty
definitely does *not* set any of this metadata signalling.
2016-06-28 19:48:29 +02:00
Niklas Haas 9278ce98f7 vo_opengl: implement ARIB STD-B68 (HLG) HDR TRC
This HDR function is unique in that it's still display-referred, it just
allows for values above the reference peak (super-highlights). The
official standard doesn't actually document this very well, but the
nominal peak turns out to be exactly 12.0 - so we normalize to this
value internally in mpv. (This lets us preserve the property that the
textures are encoded in the range [0,1], preventing clipping and making
the best use of an integer texture's range)

This was grouped together with SMPTE ST2084 when checking libavutil
compatibility since they were added in the same release window, in a
similar timeframe.
2016-06-28 19:48:29 +02:00
wm4 4ce53025cb audio: add a helper for getting frame end PTS
Although I don't see any use for it yet, why not.
2016-06-27 15:12:21 +02:00
wm4 3e58ce96ac dec_audio: fix segment boudnary switching
Some bugs in this code are exposed by e.g. playing lossless audio files
with --ad-lavc-threads=16. (libavcodec doesn't really support threaded
audio decoding, except for lossless files.) In these cases, a major
amount of audio can be buffered, which makes incorrect handling of this
buffering obvious.

For one, draining the decoder can take a while, so if there's a new
segment, we shouldn't read audio.

The segment end check was completely wrong, and used the start value.
2016-06-27 15:12:21 +02:00
Rudolf Polzer acb74236ac ao_lavc, vo_lavc: Migrate to new encoding API.
Also marked some places for possible later refactoring, as they became
quite similar in this commit.
2016-06-27 08:33:12 -04:00
stepshal c5094206ce Fix misspellings 2016-06-26 13:47:21 +02:00
quilloss 24478a8a72 vo_opengl utils: use gl->main_fb when reading window content
The main framebuffer is not the default framebuffer for the dxinterop
backend. Bind the main framebuffer and use the appropriate attachment
when reading the window content.

Fix #3284
2016-06-26 13:17:39 +02:00
wm4 e081e46950 manpage: fix typo 2016-06-26 12:33:16 +02:00
wm4 22c76e85db vo_xv: fix behavior with odd sizes
The size check introduced in commit d941a57b did not consider that Xv
can round up the image size to the next chroma boundary. Doing that
makes sense, so it can't certainly be considered server misbehavior.

Do 2 things against this: allow if the server returns a larger image (we
just crop it then), and also allocate a properly aligned image in the
first place.
2016-06-25 12:44:42 +02:00
wm4 d1d864546c DOCS: change version references from 0.17.1 to 0.18.0
0.17.1 was never released, so the actual 0.18.0 release takes its place.
2016-06-25 12:39:08 +02:00
198 changed files with 4637 additions and 3584 deletions

No files matched your search

+9 -2
View File
@@ -10,5 +10,12 @@ If you're not using git master or the latest release, update.
### Log files
Make a log file made with -v or --log-file=output.txt, paste it to sprunge.us,
and replace this text with a link to it.
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.
### Sample files
Sample files needed to reproduce this issue can be uploaded to https://0x0.st/
or similar sites. (Only needed if the issue cannot be reproduced without it.)
+7 -1
View File
@@ -32,7 +32,13 @@ API changes
::
--- mpv 0.17.1 ---
--- mpv 0.19.0
1.22 - add stream_cb API for custom protocols
--- mpv 0.18.1 ---
---- - remove "status" log level from mpv_request_log_messages() docs. This
is 100% equivalent to "v". The behavior is still the same, thus no
actual API change.
--- mpv 0.18.0 ---
1.21 - mpv_set_property() changes behavior with MPV_FORMAT_NODE. Before this
change it rejected mpv_nodes with format==MPV_FORMAT_STRING if the
property was not a string or did not have special mechanisms in place
+18
View File
@@ -173,6 +173,24 @@ sed -i 's_/mingw64/bin_/mingw64/lib_' /mingw64/lib/pkgconfig/mpv.pc
rmdir /mingw64/bin/pkgconfig
```
Linking libmpv with MSVC programs
---------------------------------
You can build C++ programs in Visual Studio and link them with libmpv. To do
this, you need a Visual Studio which supports ``stdint.h`` (recent ones do),
and you need to create a import library for the mpv DLL:
```bash
lib /def:mpv.def /name:mpv-1.dll /out:mpv.lib /MACHINE:X64
```
The string in the ``/name:`` parameter must match the filename of the DLL (this
is simply the filename the MSVC linker will use). The ``mpv.def`` can be
retrieved from the mpv build directory, or can be produced by MingGW's
gendef.exe helper from the mpv DLL.
Static linking is not possible.
Running mpv
-----------
+23 -2
View File
@@ -19,7 +19,28 @@ Interface changes
::
--- mpv 0.17.1 ---
--- mpv 0.19.0 ---
- deprecate "balance" option/property (no replacement)
--- mpv 0.18.1 ---
- deprecate --heartbeat-cmd
- remove --softvol=no capability:
- deprecate --softvol, it now does nothing
- --volume, --mute, and the corresponding properties now always control
softvol, and behave as expected without surprises (e.g. you can set
them normally while no audio is initialized)
- rename --softvol-max to --volume-max (deprecated alias is added)
- the --volume-restore-data option and property are removed without
replacement. They were _always_ internal, and used for watch-later
resume/restore. Now --volume/--mute are saved directly instead.
- the previous point means resuming files with older watch-later configs
will print an error about missing --volume-restore-data (which you can
ignore), and will not restore the previous value
- as a consequence, volume controls will no longer control PulseAudio
per-application value, or use the system mixer's per-application
volume processing
- system or per-application volume can still be controlled with the
ao-volume and ao-mute properties (there are no command line options)
--- mpv 0.18.0 ---
- now ab-loops are active even if one of the "ab-loop-a"/"-b" properties is
unset ("no"), in which case the start of the file is used if the A loop
point is unset, and the end of the file for an unset B loop point
@@ -27,7 +48,7 @@ Interface changes
(also needs --embeddedfonts=no)
- add "hwdec-interop" and "hwdec-current" properties
- deprecated "hwdec-active" and "hwdec-detected" properties (to be removed
in mpv 0.19.0)
in mpv 0.20.0)
- choice option/property values that are "yes" or "no" will now be returned
as booleans when using the mpv_node functions in the client API, the
"native" property accessors in Lua, and the JSON API. They can be set as
-28
View File
@@ -312,34 +312,6 @@ Available filters are:
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
remixing audio to 5.1 and output it like this.
``delay[=[ch1,ch2,...]]``
Delays the sound to the loudspeakers such that the sound from the
different channels arrives at the listening position simultaneously. It is
only useful if you have more than 2 loudspeakers.
``[ch1,ch2,...]``
The delay in ms that should be imposed on each channel (floating point
number between 0 and 1000).
To calculate the required delay for the different channels, do as follows:
1. Measure the distance to the loudspeakers in meters in relation to your
listening position, giving you the distances s1 to s5 (for a 5.1
system). There is no point in compensating for the subwoofer (you will
not hear the difference anyway).
2. Subtract the distances s1 to s5 from the maximum distance, i.e.
``s[i] = max(s) - s[i]; i = 1...5``.
3. Calculate the required delays in ms as ``d[i] = 1000*s[i]/342; i =
1...5``.
.. admonition:: Example
``mpv --af=delay=[10.5,10.5,0,0,7,0] media.avi``
Would delay front left and right by 10.5 ms, the two rear channels
and the subwoofer by 0 ms and the center channel by 7 ms.
``drc[=method:target]``
Applies dynamic range compression. This maximizes the volume by compressing
the audio signal's dynamic range. (Formerly called ``volnorm``.)
+70 -45
View File
@@ -459,6 +459,8 @@ Input Commands that are Possibly Subject to Change
Remove all filters. Note that like the other sub-commands, this does
not control automatically inserted filters.
The argument is always needed. E.g. in case of ``clr`` use ``vf clr ""``.
You can assign labels to filter by prefixing them with ``@name:`` (where
``name`` is a user-chosen arbitrary identifier). Labels can be used to
refer to filters by name in all of the filter chain modification commands.
@@ -561,29 +563,20 @@ Input Commands that are Possibly Subject to Change
the resolution is reduced to that of the video's. You can read the
``osd-width`` and ``osd-height`` properties. At least with ``--vo-xv`` and
anamorphic video (such as DVD), ``osd-par`` should be read as well, and the
overlay should be aspect-compensated. (Future directions: maybe mpv should
take care of some of these things automatically, but it's hard to tell
where to draw the line.)
overlay should be aspect-compensated.
``id`` is an integer between 0 and 63 identifying the overlay element. The
ID can be used to add multiple overlay parts, update a part by using this
command with an already existing ID, or to remove a part with
``overlay-remove``. Using a previously unused ID will add a new overlay,
while reusing an ID will update it. (Future directions: there should be
something to ensure different programs wanting to create overlays don't
conflict with each others, should that ever be needed.)
while reusing an ID will update it.
``x`` and ``y`` specify the position where the OSD should be displayed.
``file`` specifies the file the raw image data is read from. It can be
either a numeric UNIX file descriptor prefixed with ``@`` (e.g. ``@4``),
or a filename. The file will be mapped into memory with ``mmap()``. Some VOs
will pass the mapped pointer directly to display APIs (e.g. opengl or
vdpau), so no actual copying is involved. Truncating the source file while
the overlay is active will crash the player. You shouldn't change the data
while the overlay is active, because the data is essentially accessed at
random points. Instead, call ``overlay-add`` again (preferably with a
different memory region to prevent tearing).
or a filename. The file will be mapped into memory with ``mmap()``,
copied, and unmapped before the command returns (changed in mpv 0.18.1).
It is also possible to pass a raw memory address for use as bitmap memory
by passing a memory address as integer prefixed with an ``&`` character.
@@ -616,15 +609,14 @@ Input Commands that are Possibly Subject to Change
(Technically, the minimum size would be ``stride * (h - 1) + w * 4``, but
for simplicity, the player will access all ``stride * h`` bytes.)
.. admonition:: Warning
.. note::
When updating the overlay, you should prepare a second shared memory
region (e.g. make use of the offset parameter) and add this as overlay,
instead of reusing the same memory every time. Otherwise, you might
get the equivalent of tearing, when your application and mpv write/read
the buffer at the same time. Also, keep in mind that mpv might access
an overlay's memory at random times whenever it feels the need to do
so, for example when redrawing the screen.
Before mpv 0.18.1, you had to do manual "double buffering" when updating
an overlay by replacing it with a different memory buffer. Since mpv
0.18.1, the memory is simply copied and doesn't reference any of the
memory indicated by the command's arguments after the commend returns.
If you want to use this command before mpv 0.18.1, reads the old docs
to see how to handle this correctly.
``overlay-remove <id>``
Remove an overlay added with ``overlay-add`` and the same ID. Does nothing
@@ -648,7 +640,7 @@ Input Commands that are Possibly Subject to Change
The argument is the name of the binding.
It can optionally be prefixed with the name of the script, using ``/`` as
separator, e.g. ``script_binding scriptname/bindingname``.
separator, e.g. ``script-binding scriptname/bindingname``.
For completeness, here is how this command works internally. The details
could change any time. On any matching key event, ``script-message-to``
@@ -817,7 +809,7 @@ Input sections group a set of bindings, and enable or disable them at once.
In ``input.conf``, each key binding is assigned to an input section, rather
than actually having explicit text sections.
See also: ``enable_section`` and ``disable_section`` commands.
See also: ``enable-section`` and ``disable-section`` commands.
Predefined bindings:
@@ -877,6 +869,12 @@ Property list
looks better for display purposes. Use the ``path`` property to get an
unmodified filename.)
This has a sub-property:
``filename/no-ext``
Like the ``filename`` property, but if the text contains a ``.``, strip
all text after the last ``.``. Usually this removes the file extension.
``file-size``
Length in bytes of the source file/stream. (This is the same as
``${stream-end}``. For ordered chapters and such, the
@@ -1242,29 +1240,31 @@ Property list
See ``--hr-seek``.
``mixer-active``
Return ``yes`` if the audio mixer is active, ``no`` otherwise. This has
implications for ``--softvol=no`` mode: if the mixer is inactive, changing
``volume`` doesn't actually change anything on the system mixer. If the
``--volume`` or ``--mute`` option are used, these might not be applied
properly until the mixer becomes active either. (The options, if set, will
just overwrite the mixer state at audio initialization.)
Return ``yes`` if the audio mixer is active, ``no`` otherwise.
While the behavior with ``mixer-active==yes`` is relatively well-defined,
the ``no`` case will provide possibly wrong or insignificant values.
Note that an active mixer does not necessarily imply active audio output,
although this is implied in the current implementation.
This option is relatively useless. Before mpv 0.18.1, it could be used to
infer behavior of the ``volume`` property.
``volume`` (RW)
Current volume (see ``--volume`` for details). Also see ``mixer-active``
property.
Current volume (see ``--volume`` for details).
``volume-max``
Current maximum value the volume property can be set to. (This may depend
on the ``--softvol-max`` option.)
``volume-max`` (RW)
Current maximum value the volume property can be set to. (Equivalent to the
``--volume-max`` option.)
``mute`` (RW)
Current mute status (``yes``/``no``). Also see ``mixer-active`` property.
Current mute status (``yes``/``no``).
``ao-volume`` (RW)
System volume. This property is available only if mpv audio output is
currently active, and only if the underlying implementation supports volume
control. What this option does depends on the API. For example, on ALSA
this usually changes system-wide audio, while with PulseAudio this controls
per-application volume.
``ao-mute`` (RW)
Similar to ``ao-volume``, but controls the mute state. May be unimplemented
even if ``ao-volume`` works.
``audio-delay`` (RW)
See ``--audio-delay``.
@@ -1327,6 +1327,8 @@ Property list
It doesn't change the volumes of each channel, but instead sets up a pan
matrix to mix the left and right channels.)
Deprecated.
``fullscreen`` (RW)
See ``--fullscreen``.
@@ -1388,7 +1390,7 @@ Property list
properties to see whether this was successful.
Unlike in mpv 0.9.x and before, this does not return the currently active
hardware decoder. Since mpv 0.17.1, ``hwdec-current`` is available for
hardware decoder. Since mpv 0.18.0, ``hwdec-current`` is available for
this purpose.
``hwdec-current``
@@ -1412,13 +1414,13 @@ Property list
platform and VO.
``hwdec-active``
Deprecated. To be removed in mpv 0.19.0. Use ``hwdec-current`` instead.
Deprecated. To be removed in mpv 0.20.0. Use ``hwdec-current`` instead.
Return ``yes`` or ``no``, depending on whether any type of hardware decoding
is actually in use.
``hwdec-detected``
Deprecated. To be removed in mpv 0.19.0.
Deprecated. To be removed in mpv 0.20.0.
If hardware decoding is active, this returns the hardware decoder in use.
Otherwise, it returns either ``no``, or if applicable, the currently loaded
@@ -1485,6 +1487,12 @@ Property list
``video-params/gamma``
The gamma function in use as string. (Exact values subject to change.)
``video-params/nom-peak``
The video encoding's nominal peak brightness as float.
``video-params/sig-peak``
The video file's tagged signal peak as float.
``video-params/chroma-location``
Chroma location as string. (Exact values subject to change.)
@@ -1511,6 +1519,9 @@ Property list
"colormatrix" MPV_FORMAT_STRING
"colorlevels" MPV_FORMAT_STRING
"primaries" MPV_FORMAT_STRING
"gamma" MPV_FORMAT_STRING
"nom-peak" MPV_FORMAT_DOUBLE
"sig-peak" MPV_FORMAT_DOUBLE
"chroma-location" MPV_FORMAT_STRING
"rotate" MPV_FORMAT_INT64
"stereo-in" MPV_FORMAT_STRING
@@ -1591,7 +1602,7 @@ Property list
``osd-width``, ``osd-height``
Last known OSD width (can be 0). This is needed if you want to use the
``overlay_add`` command. It gives you the actual OSD size, which can be
``overlay-add`` command. It gives you the actual OSD size, which can be
different from the window size in some cases.
``osd-par``
@@ -1802,6 +1813,16 @@ Property list
``track-list/N/audio-channels`` (deprecated)
Deprecated alias for ``track-list/N/demux-channel-count``.
``track-list/N/replaygain-track-peak``, ``track-list/N/replaygain-track-gain``
Per-track replaygain values. Only available for audio tracks with
corresponding information stored in the source file.
``track-list/N/replaygain-album-peak``, ``track-list/N/replaygain-album-gain``
Per-album replaygain values. If the file has per-track but no per-album
information, the per-album values will be copied from the per-track
values currently. It's possible that future mpv versions will make
these properties unavailable instead in this case.
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
@@ -1831,6 +1852,10 @@ Property list
"demux-samplerate" MPV_FORMAT_INT64
"demux-fps" MPV_FORMAT_DOUBLE
"audio-channels" MPV_FORMAT_INT64
"replaygain-track-peak" MPV_FORMAT_DOUBLE
"replaygain-track-gain" MPV_FORMAT_DOUBLE
"replaygain-album-peak" MPV_FORMAT_DOUBLE
"replaygain-album-gain" MPV_FORMAT_DOUBLE
``chapter-list``
List of chapters, current entry marked. Currently, the raw property value
@@ -2134,7 +2159,7 @@ Property list
(Note that if an option is marked as file-local, even ``options/`` will
access the local value, and the ``old`` value, which will be restored on
end of playback, can not be read or written until end of playback.)
end of playback, cannot be read or written until end of playback.)
``option-info/<name>``
Additional per-option information.
+2 -2
View File
@@ -41,7 +41,7 @@ It's also possible to send input.conf style text-only commands:
::
> echo 'show_text ${playback-time}' | socat - /tmp/mpvsocket
> echo 'show-text ${playback-time}' | socat - /tmp/mpvsocket
But you won't get a reply over the socket. (This particular command shows the
playback time on the player's OSD.)
@@ -65,7 +65,7 @@ You can send commands from a command prompt:
::
echo show_text ${playback-time} >\\.\pipe\mpvsocket
echo show-text ${playback-time} >\\.\pipe\mpvsocket
To be able to simultaneously read and write from the IPC pipe, like on Linux,
it's necessary to write an external program that uses overlapped file I/O (or
+3 -3
View File
@@ -175,7 +175,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
Similar to ``mp.set_property``, but set the given property using its native
type.
Since there are several data types which can not represented natively in
Since there are several data types which cannot represented natively in
Lua, this might not always work as expected. For example, while the Lua
wrapper can do some guesswork to decide whether a Lua table is an array
or a map, this would fail with empty tables. Also, there are not many
@@ -541,8 +541,8 @@ Example command-line::
--script-opts=myscript-optionA=TEST,myscript-optionB=0,myscript-optionC=yes
mp.utils options
----------------
mp.utils functions
------------------
This built-in module provides generic helper functions for Lua, and have
strictly speaking nothing to do with mpv or video/audio playback. They are
+1 -1
View File
@@ -946,7 +946,7 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
``~/.config/mpv/watch_later/``
Contains temporary config files needed for resuming playback of files with
the watch later feature. See for example the ``Q`` key binding, or the
``quit_watch_later`` input command.
``quit-watch-later`` input command.
Each file is a small config file which is loaded if the corresponding media
file is loaded. It contains the playback position and some (not necessarily
+127 -63
View File
@@ -196,7 +196,7 @@ Playback Control
position and so can take some time depending on decoding performance. For
some video formats, precise seeks are disabled. This option selects the
default choice to use for seeks; it is possible to explicitly override that
default in the definition of key bindings and in slave mode commands.
default in the definition of key bindings and in input commands.
:no: Never use precise seeks.
:absolute: Use precise seeks if the seek is to an absolute position in the
@@ -374,7 +374,7 @@ Program Behavior
``--idle=<no|yes|once>``
Makes mpv wait idly instead of quitting when there is no file to play.
Mostly useful in slave mode, where mpv can be controlled through input
Mostly useful in input mode, where mpv can be controlled through input
commands.
``once`` will only idle at start and let the player close once the
@@ -404,7 +404,7 @@ Program Behavior
``--no-resume-playback``
Do not restore playback position from the ``watch_later`` configuration
subdirectory (usually ``~/.config/mpv/watch_later/``).
See ``quit_watch_later`` input command.
See ``quit-watch-later`` input command.
``--profile=<profile1,profile2,...>``
Use the given profile(s), ``--profile=help`` displays a list of the
@@ -586,7 +586,9 @@ Video
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:videotoolbox: requires ``--vo=opengl`` (OS X 10.8 and up only)
:videotoolbox-copy: copies video back into system RAM (OS X 10.8 and up only)
:dxva2: requires ``--vo=opengl:backend=angle`` or
``--vo=opengl:backend=dxinterop`` (Windows only)
:dxva2-copy: copies video back to system RAM (Windows only)
:d3d11va: requires ``--vo=opengl:backend=angle`` (Windows only)
@@ -638,15 +640,15 @@ Video
In some cases, RGB conversion is forced, which means the RGB conversion
is performed by the hardware decoding API, instead of the OpenGL code
used by ``--vo=opengl``. This means certain obscure colorspaces may
not display correctly, and not certain filtering (such as debanding)
can not be applied in an ideal way.
not display correctly, not certain filtering (such as debanding)
cannot be applied in an ideal way.
``vdpau`` is usually safe. If deinterlacing enabled (or the ``vdpaupp``
video filter is active in general), it forces RGB conversion. The latter
currently does not treat certain colorspaces like BT.2020 correctly
(which is mostly a mpv-specific restriction). If the ``vdpauprb``
retrieves image data without RGB conversion, but does not work with
postprocessing.
(which is mostly a mpv-specific restriction). The ``vdpauprb`` video
filter retrieves image data without RGB conversion and is safe (but
precludes use of vdpau postprocessing).
``vaapi`` is safe if the ``vaapi-egl`` backend is indicated in the logs.
If ``vaapi-glx`` is indicated, and the video colorspace is either BT.601
@@ -690,8 +692,8 @@ Video
choice of the format can influence performance considerably. On the other
hand, there doesn't appear to be a good way to detect the best format for
the given hardware. ``nv12``, the default, works better on modern hardware,
while ``uyvy422`` appears to be better for old hardware. ``rgb0`` also
works.
while ``uyvy422`` appears to be better for old hardware. ``rgb0`` and
``yuv420p`` also work.
``--panscan=<0.0-1.0>``
Enables pan-and-scan functionality (cropping the sides of e.g. a 16:9
@@ -883,9 +885,9 @@ Video
You can get the list of allowed codecs with ``mpv --vd=help``. Remove the
prefix, e.g. instead of ``lavc:h264`` use ``h264``.
By default, this is set to ``h264,vc1,wmv3,hevc,mpeg2video``. Note that the
hardware acceleration special codecs like ``h264_vdpau`` are not relevant
anymore, and in fact have been removed from Libav in this form.
By default, this is set to ``h264,vc1,wmv3,hevc,mpeg2video,vp9``. Note that
the hardware acceleration special codecs like ``h264_vdpau`` are not
relevant anymore, and in fact have been removed from Libav in this form.
This is usually only needed with broken GPUs, where a codec is reported
as supported, but decoding causes more problems than it solves.
@@ -1072,14 +1074,15 @@ Audio
``--volume=<value>``
Set the startup volume. 0 means silence, 100 means no volume reduction or
amplification. A value of -1 (the default) will not change the volume. See
also ``--softvol``.
amplification. Negative values can be passed for compatibility, but are
treated as 0.
.. note::
Since mpv 0.18.1, this always controls the internal mixer (aka "softvol").
This was changed after the mpv 0.9 release. Before that, 100 actually
meant maximum volume. At the same time, the volume scale was made cubic,
so the old values won't match up with the new ones anyway.
``--balance=<value>``
How much left/right channels contribute to the audio.
Deprecated.
``--audio-delay=<sec>``
Audio delay in seconds (positive or negative float value). Positive values
@@ -1094,20 +1097,17 @@ Audio
See also: ``--volume``.
``--softvol=<mode>``
Control whether to use the volume controls of the audio output driver or
the internal mpv volume filter.
``--softvol=<no|yes|auto>``
Deprecated/unfunctional. Before mpv 0.18.1, this used to control whether
to use the volume controls of the audio output driver or the internal mpv
volume filter.
:no: prefer audio driver controls, use the volume filter only if
absolutely needed
:yes: always use the volume filter
:auto: prefer the volume filter if the audio driver uses the system mixer
(default)
The current behavior is as if this option was set to ``yes``. The other
behaviors are not available anymore, although ``auto`` almost matches
current behavior in most cases.
The intention of ``auto`` is to avoid changing system mixer settings from
within mpv with default settings. mpv is a video player, not a mixer panel.
On the other hand, mixer controls are enabled for sound servers like
PulseAudio, which provide per-application volume.
The ``no`` behavior is still partially available through the ``ao-volume``
and ``ao-mute`` properties. But there are no options to reset these.
``--audio-demuxer=<[+]name>``
Use this audio demuxer type when using ``--audio-file``. Use a '+' before
@@ -1150,30 +1150,51 @@ Audio
This and enabling passthrough via ``--ad`` are deprecated in favor of
using ``--audio-spdif=dts-hd``.
``--audio-channels=<auto|number|layout>``
Request a channel layout for audio output (default: stereo). This will ask
the AO to open a device with the given channel layout. It's up to the AO
to accept this layout, or to pick a fallback or to error out if the
requested layout is not supported.
``--audio-channels=<auto-safe|auto|layouts>``
Control which audio channels are output (e.g. surround vs. stereo). There
are the following possibilities:
The ``--audio-channels`` option either takes a channel number or an explicit
channel layout. Channel numbers refer to default layouts, e.g. 2 channels
refer to stereo, 6 refers to 5.1.
- ``--audio-channels=auto-safe``
Use the system's preferred channel layout. If there is none (such
as when accessing a hardware device instead of the system mixer),
force stereo. Some audio outputs might simply accept any layout and
do downmixing on their own.
This is the default.
- ``--audio-channels=auto``
Send the audio device whatever it accepts, preferring the audio's
original channel layout. Can cause issues with HDMI (see the warning
below).
- ``--audio-channels=layout1,layout2,...``
List of ``,``-separated channel layouts which should be allowed.
Technically, this only adjusts the filter chain output to the best
matching layout in the list, and passes the result to the audio API.
It's possible that the audio API will select a different channel
layout.
Using this mode is recommended for direct hardware output, especially
over HDMI (see HDMI warning below).
- ``--audio-channels=stereo``
Force a plain stereo downmix. This is a special-case of the previous
item. (See paragraphs below for implications.)
If a list of layouts is given, each item can be either an explicit channel
layout name (like ``5.1``), or a channel number. Channel numbers refer to
default layouts, e.g. 2 channels refer to stereo, 6 refers to 5.1.
See ``--audio-channels=help`` output for defined default layouts. This also
lists speaker names, which can be used to express arbitrary channel
layouts (e.g. ``fl-fr-lfe`` is 2.1).
``--audio-channels=auto`` tries to play audio using the input file's
channel layout. There is no guarantee that the audio API handles this
correctly. See the HDMI warning below.
(``empty`` is an accepted obsolete alias for ``auto``.)
This will also request the channel layout from the decoder. If the decoder
does not support the layout, it will fall back to its native channel layout.
(You can use ``--ad-lavc-downmix=no`` to make the decoder always output
its native layout.) Note that only some decoders support remixing audio.
Some that do include AC-3, AAC or DTS audio.
If the list of channel layouts has only 1 item, the decoder is asked to
produce according output. This sometimes triggers decoder-downmix, which
might be different from the normal mpv downmix. (Only some decoders support
remixing audio, like AC-3, AAC or DTS. You can use ``--ad-lavc-downmix=no``
to make the decoder always output its native layout.) One consequence is
that ``--audio-channels=stereo`` triggers decoder downmix, while ``auto``
or ``auto-safe`` never will, even if they end up selecting stereo. This
happens because the decision whether to use decoder downmix happens long
before the audio device is opened.
If the channel layout of the media file (i.e. the decoder) and the AO's
channel layout don't match, mpv will attempt to insert a conversion filter.
@@ -1186,6 +1207,10 @@ Audio
channel layout, random things can happen, such as dropping the
additional channels, or adding noise.
You are recommended to set an explicit whitelist of the layouts you
want. For example, most A/V receivers connected via HDMI and that can
do 7.1 would be served by: ``--audio-channels=7.1,5.1,stereo``
``--audio-normalize-downmix=<yes|no>``
Enable/disable normalization if surround audio is downmixed to stereo
(default: no). If this is disabled, downmix can cause clipping. If it's
@@ -1264,10 +1289,12 @@ Audio
their start timestamps differ, and then video timing is gradually adjusted
if necessary to reach correct synchronization later.
``--softvol-max=<100.0-1000.0>``
``--volume-max=<100.0-1000.0>``, ``--softvol-max=<...>``
Set the maximum amplification level in percent (default: 130). A value of
130 will allow you to adjust the volume up to about double the normal level.
``--softvol-max`` is a deprecated alias and should not be used.
``--audio-file-auto=<no|exact|fuzzy|all>``, ``--no-audio-file-auto``
Load additional audio files matching the video filename. The parameter
specifies how external audio files are matched. ``exact`` is enabled by
@@ -1276,7 +1303,7 @@ Audio
:no: Don't automatically load external audio files.
:exact: Load the media filename with audio file extension (default).
:fuzzy: Load all audio files containing media filename.
:all: Load all aufio files in the current and ``--audio-file-paths``
:all: Load all audio files in the current and ``--audio-file-paths``
directories.
``--audio-file-paths=<path1:path2:...>``
@@ -1288,7 +1315,7 @@ Audio
or to set your own application name when using libmpv.
``--volume-restore-data=<string>``
Used internally for use by playback resume (e.g. with ``quit_watch_later``).
Used internally for use by playback resume (e.g. with ``quit-watch-later``).
Restoring value has to be done carefully, because different AOs as well as
softvol can have different value ranges, and we don't want to restore
volume if setting the volume changes it system wide. The normal options
@@ -1314,13 +1341,32 @@ Audio
Default: 0.2 (200 ms).
``--audio-stream-silence=<yes|no>``
Cash-grab consumer audio hardware (such as A/V receivers) often ignore
initial audio sent over HDMI. This can happen every time audio over HDMI
is stopped and resumed. In order to compensate for this, you can enable
this option to not to stop and restart audio on seeks, and fill the gaps
with silence. Likewise, when pausing playback, audio is not stopped, and
silence is played while paused. Note that if no audio track is selected,
the audio device will still be closed immediately.
Not all AOs support this.
``--audio-wait-open=<secs>``
This makes sense for use with ``--audio-stream-silence=yes``. If this option
is given, the player will wait for the given amount of seconds after opening
the audio device before sending actual audio data to it. Useful if your
expensive hardware discards the first 1 or 2 seconds of audio data sent to
it. If ``--audio-stream-silence=yes`` is not set, this option will likely
just waste time.
Subtitles
---------
.. note::
Changing styling and position does not work with all subtitles. Image-based
subtitles (DVD, Bluray/PGS, DVB) can not changed for fundamental reasons.
subtitles (DVD, Bluray/PGS, DVB) cannot changed for fundamental reasons.
Subtitles in ASS format are normally not changed intentionally, but
overriding them can be controlled with ``--ass-style-override``.
@@ -2019,6 +2065,14 @@ Window
be the default behavior. Currently only affects X11 VOs.
``--heartbeat-cmd=<command>``
.. warning::
This option is redundant with Lua scripting. Further, it shouldn't be
needed for disabling screensaver anyway, since mpv will call
``xdg-screensaver`` when using X11 backend. As a consequence this
option has been deprecated with no direct replacement.
Command that is executed every 30 seconds during playback via *system()* -
i.e. using the shell. The time between the commands can be customized with
the ``--heartbeat-interval`` option. The command is not run while playback
@@ -2438,7 +2492,7 @@ Demuxer
stop reading additional packets as soon as one of the limits is reached.
(The limits still can be slightly overstepped due to technical reasons.)
Set these limits highher if you get a packet queue overflow warning, and
Set these limits higher if you get a packet queue overflow warning, and
you think normal playback would be possible with a larger packet queue.
See ``--list-options`` for defaults and value range.
@@ -2463,7 +2517,7 @@ Demuxer
``--force-seekable=<yes|no>``
If the player thinks that the media is not seekable (e.g. playing from a
pipe, or it's a http stream with a server that doesn't support range
pipe, or it's an http stream with a server that doesn't support range
requests), seeking will be disabled. This option can forcibly enable it.
For seeks within the cache, there's a good chance of success.
@@ -2533,7 +2587,7 @@ Input
automatically enabled when ``-`` is found on the command line. There are
situations where you have to set it manually, e.g. if you open
``/dev/stdin`` (or the equivalent on your system), use stdin in a playlist
or intend to read from stdin later on via the loadfile or loadlist slave
or intend to read from stdin later on via the loadfile or loadlist input
commands.
``--input-ipc-server=<filename>``
@@ -2655,7 +2709,7 @@ OSD
(default), then the playback time, duration, and some more information is
shown.
This is also used for the ``show_progress`` command (by default mapped to
This is also used for the ``show-progress`` command (by default mapped to
``P``), or in some non-default cases when seeking.
``--osd-status-msg`` is a legacy equivalent (but with a minor difference).
@@ -2663,7 +2717,7 @@ OSD
``--osd-status-msg=<string>``
Show a custom string during playback instead of the standard status text.
This overrides the status text used for ``--osd-level=3``, when using the
``show_progress`` command (by default mapped to ``P``), or in some
``show-progress`` command (by default mapped to ``P``), or in some
non-default cases when seeking. Expands properties. See
`Property Expansion`_.
@@ -2924,7 +2978,7 @@ Screenshot
insert the number of the current month as number. You have to use
multiple ``%tX`` specifiers to build a full date/time string.
``%{prop[:fallback text]}``
Insert the value of the slave property 'prop'. E.g. ``%{filename}`` is
Insert the value of the input property 'prop'. E.g. ``%{filename}`` is
the same as ``%f``. If the property does not exist or is not available,
an error text is inserted, unless a fallback is specified.
``%%``
@@ -3173,7 +3227,7 @@ TV
If <chan> is an integer greater than 1000, it will be treated as
frequency (in kHz) rather than channel name from frequency table.
Use _ for spaces in names (or play with quoting ;-) ). The channel
names will then be written using OSD, and the slave commands
names will then be written using OSD, and the input commands
``tv_step_channel``, ``tv_set_channel`` and ``tv_last_channel``
will be usable for a remote control. Not compatible with
the ``frequency`` parameter.
@@ -3290,7 +3344,7 @@ Cache
With ``auto``, the cache will usually be enabled for network streams,
using the size set by ``--cache-default``. With ``yes``, the cache will
always be enabled with the size set by ``--cache-default`` (unless the
stream can not be cached, or ``--cache-default`` disables caching).
stream cannot be cached, or ``--cache-default`` disables caching).
May be useful when playing files from slow media, but can also have
negative effects, especially with file formats that require a lot of
@@ -3622,7 +3676,7 @@ Miscellaneous
``--stream-capture=<filename>``
Allows capturing the primary stream (not additional audio tracks or other
kind of streams) into the given file. Capturing can also be started and
stopped by changing the filename with the ``stream-capture`` slave property.
stopped by changing the filename with the ``stream-capture`` property.
Generally this will not produce usable results for anything else than MPEG
or raw streams, unless capturing includes the file headers and is not
interrupted. Note that, due to cache latencies, captured data may begin and
@@ -3666,6 +3720,16 @@ Miscellaneous
``--audio-file``, this includes all tracks, and does not cause default
stream selection over the "proper" file.
``--autoload-files=<yes|no>``
Automatically load/select external files (default: yes).
If set to ``no``, then do not automatically load external files as specified
by ``--sub-auto`` and ``--audio-file-auto``. If external files are forcibly
added (like with ``--sub-file``), they will not be auto-selected.
This does not affect playlist expansion, redirection, or other loading of
referenced files like with ordered chapters.
``--lavfi-complex=<string>``
Set a "complex" libavfilter filter, which means a single filter graph can
take input from multiple source audio and video tracks. The graph can result
@@ -3685,7 +3749,7 @@ Miscellaneous
video or audio outputs are not possible, but you can use filters to merge
them into one.
The complex filter can not be changed yet during playback. It's also not
The complex filter cannot be changed yet during playback. It's also not
possible to change the tracks connected to the filter at runtime. Other
tracks, as long as they're not connected to the filter, and the
corresponding output is not connected to the filter, can still be freely
+11 -1
View File
@@ -214,7 +214,7 @@ Available filters are:
Format name, e.g. rgb15, bgr24, 420p, etc. (default: don't change).
``<outfmt>``
Format name that should be substituted for the output. If they do not
have the same bytes per pixel and chroma subsamplimg, it will fail.
have the same bytes per pixel and chroma subsampling, it will fail.
``<colormatrix>``
Controls the YUV to RGB color space conversion when playing video. There
are various standards. Normally, BT.601 should be used for SD video, and
@@ -288,6 +288,8 @@ Available filters are:
:adobe: Adobe RGB (1998)
:prophoto: ProPhoto RGB (ROMM)
:cie1931: CIE 1931 RGB
:dci-p3: DCI-P3 (Digital Cinema)
:v-gamut: Panasonic V-Gamut primaries
``<gamma>``
Gamma function the source file was encoded with. Normally this should be set
@@ -311,6 +313,8 @@ Available filters are:
:gamma2.8: Pure power curve (gamma 2.8)
:prophoto: ProPhoto RGB (ROMM) curve
:st2084: SMPTE ST2084 (HDR) curve
:std-b67: ARIB STD-B67 (Hybrid Log-gamma) curve
:v-log: Panasonic V-Log transfer curve
``<peak>``
Reference peak illumination for the video file. This is mostly
@@ -817,6 +821,12 @@ Available filters are:
Whether deinterlacing is enabled (default: no).
``interlaced-only=<yes|no>``
If ``yes`` (default), only deinterlace frames marked as interlaced.
``mode=<blend|bob|adaptive|mocomp|ivctc|none>``
Tries to select a video processor with the given processing capability.
If a video processor supports multiple capabilities, it is not clear
which algorithm is actually selected. ``none`` always falls back. On
most if not all hardware, this option will probably do nothing, because
a video processor usually supports all modes or none.
``buffer=<num>``
Buffer ``<num>`` frames in the filter chain. This filter is probably pretty
+21 -4
View File
@@ -362,6 +362,9 @@ Available video output drivers are:
exchange for adding some blur. This filter is good at temporal
interpolation, and also known as "smoothmotion" (see ``tscale``).
``linear``
A ``tscale`` filter.
``custom``
A user-defined custom shader (see ``scale-shader``).
@@ -465,7 +468,7 @@ Available video output drivers are:
8
Dither to 8 bit output.
Note that the depth of the connected video display device can not be
Note that the depth of the connected video display device cannot be
detected. Often, LCD panels will do dithering on their own, which
conflicts with ``opengl``'s dithering and leads to ugly output.
@@ -629,6 +632,9 @@ Available video output drivers are:
never resets (regardless of seeks).
vec2 image_size
The size in pixels of the input image.
vec2 target_size
The size in pixels of the visible part of the scaled (and possibly
cropped) image.
For example, a shader that inverts the colors could look like this::
@@ -979,6 +985,8 @@ Available video output drivers are:
CIE 1931 RGB (not to be confused with CIE XYZ)
dci-p3
DCI-P3 (Digital Cinema Colorspace), SMPTE RP431-2
v-gamut
Panasonic V-Gamut (VARICAM) primaries
``target-trc=<value>``
Specifies the transfer characteristics (gamma) of the display. Video
@@ -1003,6 +1011,16 @@ Available video output drivers are:
ProPhoto RGB (ROMM)
st2084
SMPTE ST2084 (HDR) curve, PQ OETF
std-b67
ARIB STD-B67 (Hybrid Log-gamma) curve, also known as BBC/NHK HDR
v-log
Panasonic V-Log (VARICAM) curve
NOTE: When using HDR output formats, mpv will encode to the specified
curve but it will not set any HDMI flags or other signalling that
might be required for the target device to correctly display the
HDR signal. The user should independently guarantee this before
using these signal formats for display.
``target-brightness=<1..100000>``
Specifies the display's approximate brightness in cd/m^2. When playing
@@ -1080,8 +1098,7 @@ Available video output drivers are:
``3dlut-size=<r>x<g>x<b>``
Size of the 3D LUT generated from the ICC profile in each dimension.
Default is 128x256x64.
Sizes must be a power of two, and 512 at most.
Default is 64x64x64. Sizes may range from 2 to 512.
``icc-contrast=<0-100000>``
Specifies an upper limit on the target device's contrast ratio.
@@ -1285,7 +1302,7 @@ Available video output drivers are:
This also supports many of the suboptions the ``opengl`` VO has. Run
``mpv --vo=opengl-cb:help`` for a list.
This also supports the ``vo_cmdline`` command.
This also supports the ``vo-cmdline`` command.
``rpi`` (Raspberry Pi)
Native video output on the Raspberry Pi using the MMAL API.
+6 -6
View File
@@ -30,7 +30,7 @@ Player
* Slave mode compatibility broken (see below).
* Re-enable screensaver while the player is paused.
* Allow resuming playback at a later point with ``Shift+q``, also see the
``quit_watch_later`` input command.
``quit-watch-later`` input command.
* ``--keep-open`` option to stop the player from closing the window and
exiting after playback ends.
* A client API, that allows embedding **mpv** into applications
@@ -312,11 +312,11 @@ input.conf and Slave Commands
+--------------------------------+----------------------------------------+
| Old | New |
+================================+========================================+
| ``pt_step 1 [0|1]`` | ``playlist_next [weak|force]`` |
| ``pt_step 1 [0|1]`` | ``playlist-next [weak|force]`` |
| | (translation layer cannot deal with |
| | whitespace) |
+--------------------------------+----------------------------------------+
| ``pt_step -1 [0|1]`` | ``playlist_prev [weak|force] (same)`` |
| ``pt_step -1 [0|1]`` | ``playlist-prev [weak|force] (same)`` |
+--------------------------------+----------------------------------------+
| ``switch_ratio [<ratio>]`` | ``set video-aspect <ratio>`` |
| | |
@@ -331,7 +331,7 @@ input.conf and Slave Commands
| ``<step> <dir>`` | ``no-osd``: ``no-osd cycle <prop>`` |
| | ``<step>`` |
+--------------------------------+----------------------------------------+
| ``osd_show_property_text`` | ``show_text <text>`` |
| ``osd_show_property_text`` | ``show-text <text>`` |
| ``<text>`` | The property expansion format string |
| | syntax slightly changed. |
+--------------------------------+----------------------------------------+
@@ -340,9 +340,9 @@ input.conf and Slave Commands
| | ``raw`` prefix to disable property |
| | expansion. |
+--------------------------------+----------------------------------------+
| ``show_tracks`` | ``show_text ${track-list}`` |
| ``show_tracks`` | ``show-text ${track-list}`` |
+--------------------------------+----------------------------------------+
| ``show_chapters`` | ``show_text ${chapter-list}`` |
| ``show_chapters`` | ``show-text ${chapter-list}`` |
+--------------------------------+----------------------------------------+
| ``af_switch``, ``af_add``, ... | ``af set|add|...`` |
+--------------------------------+----------------------------------------+
+2 -1
View File
@@ -25,7 +25,8 @@ Release procedure
- Create and/or update the `VERSION` file.
- Update `DOCS/client-api-changes.rst` (on major releases).
- Update `DOCS/client-api-changes.rst` and `DOCS/interface-changes.rst`
(in particular, update the last version numbers if necessary)
- Create tag v0.X.Y.
+1 -1
View File
@@ -123,7 +123,7 @@ options/options.h, options/options.c
parser-mpcmd.c, and uses the option table in options.c.
input/input.c:
This translates keyboard input comming from VOs and other sources (such
This translates keyboard input coming from VOs and other sources (such
as remote control devices like Apple IR or client API commands) to the
key bindings listed in the user's (or the builtin) input.conf and turns
them into items of type struct mp_cmd. These commands are queued, and read
+14
View File
@@ -137,6 +137,20 @@ most likely the best maintenance out of all stable releases. Older releases
are for distros, and at best receive basic changes like fixing critical security
issues or build fixes, and at worst are completely abandoned.
## FFmpeg ABI compatibility
mpv does not support linking against FFmpeg versions it was not built with, even
if the linked version is supposedly ABI-compatible with the version it was
compiled against. Expect malfunctions, crashes, and security issues if you
do it anyway.
The reason for not supporting this is because it creates far too much complexity
with little to no benefit, coupled with absurd and unusable FFmpeg API
artifacts.
Newer mpv versions will refuse to start if runtime and compile time FFmpeg
library versions mismatch.
## Release cycle
Every other month, an arbitrary git snapshot is made, and is assigned
+190 -1
View File
@@ -1,3 +1,192 @@
Release 0.19.0
==============
Build System Changes
--------------------
- build: add --htmldir option
- build: always require atomics
- wscript: add proper unversioned SONAME for Android
Features
--------
New
~~~
- client API: add stream_cb API for user-defined stream implementations (bumps client API version to 1.22)
- vf_d3d11vpp: add video processor selection
- videotoolbox: add --hwdec=videotoolbox-copy for h/w accelerated decoding with video filters
- vo_opengl: add a tscale=linear direct implementation
Removed
~~~~~~~
- audio/filter: remove delay audio filter
Options and Commands
--------------------
Added
~~~~~
- command: add filename/no-ext sub-property that returns filename without extension (#3404)
- command: add properties for HDR metadata
- command: add replaygain information properties to track-list
- options: add vp9 to --hwdec-codecs
- player: add --audio-stream-silence
- player: add --audio-wait-open
- player: add --no-autoload-files
- videotoolbox: add yuv420p to --videotoolbox-format
Changed
~~~~~~~
- options: un-restrict --audio-delay
- use - as command-name separator everywhere
- vo_opengl: reduce default 3dlut-size to 64x64x64 (since accuracy is improved)
Deprecated
~~~~~~~~~~
- deprecate "balance" option/property (no replacement)
Fixes and Minor Enhancements
----------------------------
- Windows: don't wait for GUI thread when polling for events (#3393)
- af_lavcac3enc: error out properly if encoding fails
- af_volume: don't let softvol overwrite af_volume volumedb sub-option
- ao_pulse: fix some volume control rounding issues
- ao_wasapi: in exclusive mode, do not output multichannel by default
- audio: add heuristic to move auto-downmixing before other filters
- audio: show an osd bar when changing ao-volume
- demux: make ALBUM replaygain tags optional (#3405)
- demux_raw: fix small typo to add s16be support
- demux_timeline: restore mkv edition switching
- libarchive: sanitize non-UTF8 archive entries
- macOS/vo_opengl: fix crash when glctx is NULL during init (#3360)
- player: disable display-sync with spdif transcoding
- player: do not cut off terminal status line if it contains newlines (#3340)
- player: fix display-sync timing if audio resumes slowly
- player: improve instant track switching (#3392)
- player: improve non-hr seeking with external audio tracks
- player: offset demuxer on start/seek properly with audio/sub delay
- player: sync audio as well when enabling it mid-stream
- stream/stream_bluray: display list of available titles in verbose mode
- sub: don't potentially discard too many subtitles on seek
- video: respect --deinterlace=auto
- vo_direct3d: add missing header (fixes Cygwin build)
- vo_opengl: angle: try D3D9 when D3D11 fails eglInitialize
- vo_opengl: angle: use WARP if there are no hw adapters (makes it work on Windows 7 without hardware-accelerated graphics)
- vo_opengl: increase 3DLUT accuracy at smaller LUT sizes
- vo_opengl: remove the 3dlut-size npot2 restriction
- vo_wayland: fix high CPU usage due to busy polling
- wayland_common: clip window size to the display output size
- wayland_common: fix crashes when switching to fullscreen before the video output is fully initialized
- wayland_common: fix fullscreen image switching bug
- wayland_common: prevent black bars on most non-native aspect ratios
- wayland_common: remove untested/unusable wayland dnd code
- win32: mpv.rc: re-add version info
- x11: skip ICC update on every window move
- ytdl: Error out with http_dash_segments (unsupported for now)
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.18.1..v0.19.0`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.18.1...v0.19.0 or
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.18.1..v0.19.0
Release 0.18.1
==============
Note: Running mpv with different versions of the FFmpeg/Libav libraries than
it was compiled with is no longer supported. Even supposedly ABI-compatible
versions have been a source of trouble, and it creates far too much
complexity with little to no benefit, coupled with absurd and unusable FFmpeg
API artifacts.
Instead, mpv will exit with an error when such a situation is detected.
This simply means that mpv needs to be rebuilt whenever FFmpeg libraries change.
Features
--------
New
~~~
- d3d: implement screenshots for --hwdec=d3d11va
- vo_opengl: add output_size uniform to custom shader
- vo_opengl: implement the Panasonic V-Log function (#3157)
- vo_opengl: implement ARIB STD-B68 (HLG) HDR TRC
Options and Commands
--------------------
Changed
~~~~~~~
- command: pack sub image data in overlay-add command
Deprecated
~~~~~~~~~~
- options: deprecate --heartbeat-cmd
- audio: deprecate --softvol
Removed
~~~~~~~
- audio: drop --softvol=no and --softvol=auto (#3322)
Fixes and Minor Enhancements
----------------------------
- video: fix deinterlace filter handling on pixel format changes
- x11: silence xdg-screensaver
- vo_opengl: angle: update the swapchain on resize (#3301)
- vo_opengl: error out gracefully when trying to use FBOs without FBO API
- vd_lavc: expose mastering display side data reference peak (improves results with HDR content)
- vo_opengl: generalize HDR tone mapping mechanism (#3293)
- vo_opengl: don't constantly resize the output FBO
- vo_opengl: use ringbuffer of PBOs
- Windows: make WM_NCHITTEST simpler and more accurate
- ao_oss: do not add an entry to audio-device-list if device file missing
- dec_audio: fix segment boudnary switching
- ao_lavc, vo_lavc: Migrate to new FFmpeg encoding API
- vo_opengl: explicitly use main framebuffer when reading window content (#3284)
- vo_xv: fix behavior with odd sizes
- audio: insert auto-inserted filters at end of chain
- x11: add missing FocusChangeMask (disables key repeat when losing focus while a key is down)
- ao_coreaudio: error out when selecting invalid device
- ad_lavc: work around misbehavior of some FFmpeg decoders like wmapro (#3297)
- player: cut off status line on terminal width
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.18.0..v0.18.1`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.18.0...v0.18.1 or
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.18.0..v0.18.1
Release 0.18.0
==============
@@ -39,7 +228,7 @@ New
Removed
-------
~~~~~~~
- vo_opengl: remove nnedi3 prescaler (replaced by user shaders)
- vo_opengl: remove prescaling framework with superxbr prescaler (replaced by user shaders)
+1 -1
View File
@@ -25,7 +25,7 @@ function add_files_at(index, files)
local oldcount = mp.get_property_number("playlist-count", 1)
for i = 1, #files do
mp.commandv("loadfile", files[i], "append")
mp.commandv("playlist_move", oldcount + i - 1, index + i - 1)
mp.commandv("playlist-move", oldcount + i - 1, index + i - 1)
end
end
+1 -1
View File
@@ -1 +1 @@
0.18.0
0.19.0
+44 -13
View File
@@ -138,6 +138,9 @@ static void mp_audio_destructor(void *ptr)
* available on every plane. The previous data is kept (for the smallest
* common number of samples before/after resize).
*
* This also makes sure the resulting buffer is writable (even in the case
* the buffer has the correct size).
*
* mpa->samples is not set or used.
*
* This function is flexible enough to handle format and channel layout
@@ -153,6 +156,12 @@ void mp_audio_realloc(struct mp_audio *mpa, int samples)
int size = get_plane_size(mpa, samples);
if (size < 0)
abort(); // oom or invalid parameters
if (!mp_audio_is_writeable(mpa)) {
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
av_buffer_unref(&mpa->allocated[n]);
mpa->planes[n] = NULL;
}
}
for (int n = 0; n < mpa->num_planes; n++) {
if (!mpa->allocated[n] || size != mpa->allocated[n]->size) {
if (av_buffer_realloc(&mpa->allocated[n], size) < 0)
@@ -171,7 +180,7 @@ void mp_audio_realloc(struct mp_audio *mpa, int samples)
// If the buffer is reallocated, also preallocate.
void mp_audio_realloc_min(struct mp_audio *mpa, int samples)
{
if (samples > mp_audio_get_allocated_size(mpa)) {
if (samples > mp_audio_get_allocated_size(mpa) || !mp_audio_is_writeable(mpa)) {
size_t alloc = ta_calc_prealloc_elems(samples);
if (alloc > INT_MAX)
abort(); // oom
@@ -257,13 +266,21 @@ void mp_audio_skip_samples(struct mp_audio *data, int samples)
data->pts += samples / (double)data->rate;
}
// Return the timestamp of the sample just after the end of this frame.
double mp_audio_end_pts(struct mp_audio *f)
{
if (f->pts == MP_NOPTS_VALUE || f->rate < 1)
return MP_NOPTS_VALUE;
return f->pts + f->samples / (double)f->rate;
}
// Clip the given frame to the given timestamp range. Adjusts the frame size
// and timestamp.
void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end)
{
if (f->pts == MP_NOPTS_VALUE || f->rate < 1)
double f_end = mp_audio_end_pts(f);
if (f_end == MP_NOPTS_VALUE)
return;
double f_end = f->pts + f->samples / (double)f->rate;
if (end != MP_NOPTS_VALUE) {
if (f_end >= end) {
if (f->pts >= end) {
@@ -339,9 +356,9 @@ struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe)
mp_chmap_from_lavc(&lavc_chmap, avframe->channel_layout);
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being special again
if (lavc_chmap.num != avframe->channels)
mp_chmap_from_channels(&lavc_chmap, avframe->channels);
// FFmpeg being stupid POS again
if (lavc_chmap.num != av_frame_get_channels(avframe))
mp_chmap_from_channels(&lavc_chmap, av_frame_get_channels(avframe));
#endif
new->rate = avframe->sample_rate;
@@ -386,12 +403,9 @@ fail:
return NULL;
}
// Returns NULL on failure. The input is always unreffed.
struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame)
int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe)
{
struct AVFrame *avframe = av_frame_alloc();
if (!avframe)
goto fail;
av_frame_unref(avframe);
avframe->nb_samples = frame->samples;
avframe->format = af_to_avformat(frame->format);
@@ -402,8 +416,8 @@ struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame)
if (!avframe->channel_layout)
goto fail;
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being a stupid POS (but I respect it)
avframe->channels = frame->channels.num;
// FFmpeg being a stupid POS again
av_frame_set_channels(avframe, frame->channels.num);
#endif
avframe->sample_rate = frame->rate;
@@ -449,6 +463,23 @@ struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame)
avframe = tmp;
}
return 0;
fail:
av_frame_unref(avframe);
return -1;
}
// Returns NULL on failure. The input is always unreffed.
struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame)
{
struct AVFrame *avframe = av_frame_alloc();
if (!avframe)
goto fail;
if (mp_audio_to_avframe(frame, avframe) < 0)
goto fail;
talloc_free(frame);
return avframe;
+2
View File
@@ -73,6 +73,7 @@ void mp_audio_copy(struct mp_audio *dst, int dst_offset,
void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src);
void mp_audio_skip_samples(struct mp_audio *data, int samples);
void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end);
double mp_audio_end_pts(struct mp_audio *data);
bool mp_audio_is_writeable(struct mp_audio *data);
int mp_audio_make_writeable(struct mp_audio *data);
@@ -80,6 +81,7 @@ int mp_audio_make_writeable(struct mp_audio *data);
struct AVFrame;
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe);
struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame);
int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe);
struct mp_audio_pool;
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
-38
View File
@@ -230,44 +230,6 @@ void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
}
}
// Return channel index of the given speaker, or -1.
static int mp_chmap_find_speaker(const struct mp_chmap *map, int speaker)
{
for (int n = 0; n < map->num; n++) {
if (map->speaker[n] == speaker)
return n;
}
return -1;
}
static void mp_chmap_remove_speaker(struct mp_chmap *map, int speaker)
{
int index = mp_chmap_find_speaker(map, speaker);
if (index >= 0) {
for (int n = index; n < map->num - 1; n++)
map->speaker[n] = map->speaker[n + 1];
map->num--;
}
}
// Some decoders output additional, redundant channels, which are usually
// useless and will mess up proper audio output channel handling.
// map: channel map from which the channels should be removed
// requested: if not NULL, and if it contains any of the "useless" channels,
// don't remove them (this is for convenience)
void mp_chmap_remove_useless_channels(struct mp_chmap *map,
const struct mp_chmap *requested)
{
if (requested &&
mp_chmap_find_speaker(requested, MP_SPEAKER_ID_DL) >= 0)
return;
if (map->num > 2) {
mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DL);
mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DR);
}
}
// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
// Speakers not representable by ffmpeg/libav are dropped.
// Warning: this ignores the order of the channels, and will return a channel
-3
View File
@@ -109,9 +109,6 @@ void mp_chmap_fill_na(struct mp_chmap *map, int num);
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels);
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels);
void mp_chmap_remove_useless_channels(struct mp_chmap *map,
const struct mp_chmap *requested);
uint64_t mp_chmap_to_lavc(const struct mp_chmap *src);
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src);
void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src);
+13
View File
@@ -374,3 +374,16 @@ void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev)
if (s->allow_any)
mp_msg(log, lev, " - anything\n");
}
// Select a channel map from the given list that fits best to c. Don't change
// *c if there's no match, or the list is empty.
void mp_chmap_sel_list(struct mp_chmap *c, struct mp_chmap *maps, int num_maps)
{
// This is a separate function to keep messing with mp_chmap_sel internals
// within this source file.
struct mp_chmap_sel sel = {
.chmaps = maps,
.num_chmaps = num_maps,
};
mp_chmap_sel_fallback(&sel, c);
}
+2
View File
@@ -47,4 +47,6 @@ bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
struct mp_log;
void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev);
void mp_chmap_sel_list(struct mp_chmap *c, struct mp_chmap *maps, int num_maps);
#endif
+8 -2
View File
@@ -104,9 +104,9 @@ static int init(struct dec_audio *da, const char *decoder)
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
if (opts->downmix) {
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
lavc_context->request_channel_layout =
mp_chmap_to_lavc(&mpopts->audio_output_channels);
mp_chmap_to_lavc(&mpopts->audio_output_channels.chmaps[0]);
}
// Always try to set - option only exists for AC3 at the moment
@@ -186,6 +186,12 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
// If the decoder discards the timestamp for some reason, we use the
// interpolated PTS. Initialize it so that it works for the initial
// packet as well.
if (mpkt && priv->next_pts == MP_NOPTS_VALUE)
priv->next_pts = mpkt->pts;
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
+6 -3
View File
@@ -200,7 +200,9 @@ void audio_work(struct dec_audio *da)
if (da->current_frame)
return;
if (!da->packet && demux_read_packet_async(da->header, &da->packet) == 0) {
if (!da->packet && !da->new_segment &&
demux_read_packet_async(da->header, &da->packet) == 0)
{
da->current_state = DATA_WAIT;
return;
}
@@ -211,6 +213,7 @@ void audio_work(struct dec_audio *da)
da->packet = NULL;
}
bool had_input_packet = !!da->packet;
bool had_packet = da->packet || da->new_segment;
int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
@@ -233,12 +236,12 @@ void audio_work(struct dec_audio *da)
fix_audio_pts(da);
bool segment_end = true;
bool segment_end = !da->current_frame && !had_input_packet;
if (da->current_frame) {
mp_audio_clip_timestamps(da->current_frame, da->start, da->end);
if (da->current_frame->pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
segment_end = da->current_frame->pts >= da->start;
segment_end = da->current_frame->pts >= da->end;
if (da->current_frame->samples == 0) {
talloc_free(da->current_frame);
da->current_frame = NULL;
+145 -174
View File
@@ -31,7 +31,6 @@
#include "af.h"
// Static list of filters
extern const struct af_info af_info_delay;
extern const struct af_info af_info_channels;
extern const struct af_info af_info_format;
extern const struct af_info af_info_volume;
@@ -46,7 +45,6 @@ extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_rubberband;
static const struct af_info *const filter_list[] = {
&af_info_delay,
&af_info_channels,
&af_info_format,
&af_info_volume,
@@ -210,29 +208,6 @@ static struct af_instance *af_prepend(struct af_stream *s,
return new;
}
/* Create and insert a new filter of type name after the filter in the
argument. This function can be called during runtime, the return
value is the new filter */
static struct af_instance *af_append(struct af_stream *s,
struct af_instance *af,
char *name, char **args)
{
if (!af)
af = s->first;
if (af == s->last)
af = s->last->prev;
// Create the new filter and make sure it is OK
struct af_instance *new = af_create(s, name, args);
if (!new)
return NULL;
// Update pointers
new->prev = af;
new->next = af->next;
af->next = new;
new->next->prev = new;
return new;
}
// Uninit and remove the filter "af"
static void af_remove(struct af_stream *s, struct af_instance *af)
{
@@ -275,8 +250,12 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
while (af) {
char b[128] = {0};
mp_snprintf_cat(b, sizeof(b), " [%s] ", af->info->name);
if (af->label)
mp_snprintf_cat(b, sizeof(b), "\"%s\" ", af->label);
if (af->data)
mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data));
if (af->auto_inserted)
mp_snprintf_cat(b, sizeof(b), " [a]");
if (af == at)
mp_snprintf_cat(b, sizeof(b), " <-");
MP_MSG(s, msg_level, "%s\n", b);
@@ -287,169 +266,156 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output));
}
static bool af_is_conversion_filter(struct af_instance *af)
{
return af && strcmp(af->info->name, "lavrresample") == 0;
}
// in is what af can take as input - insert a conversion filter if the actual
// input format doesn't match what af expects.
// Returns:
// AF_OK: must call af_reinit() or equivalent, format matches (or is closer)
// AF_FALSE: nothing was changed, format matches
// else: error
static int af_fix_format_conversion(struct af_stream *s,
struct af_instance **p_af,
struct mp_audio in)
{
int rv;
struct af_instance *af = *p_af;
struct af_instance *prev = af->prev;
struct mp_audio actual = *prev->data;
if (actual.format == in.format)
return AF_FALSE;
int dstfmt = in.format;
char *filter = "lavrresample";
if (!af_lavrresample_test_conversion(actual.format, dstfmt))
return AF_ERROR;
if (strcmp(filter, prev->info->name) == 0) {
if (prev->control(prev, AF_CONTROL_SET_FORMAT, &dstfmt) == AF_OK) {
*p_af = prev;
return AF_OK;
}
return AF_ERROR;
}
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt))) {
af_remove(s, new);
return rv;
}
*p_af = new;
return AF_OK;
}
// same as af_fix_format_conversion - only wrt. channels
static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
struct mp_audio in)
{
int rv;
struct af_instance *af = *p_af;
struct af_instance *prev = af->prev;
struct mp_audio actual = *prev->data;
if (mp_chmap_equals(&actual.channels, &in.channels))
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_SET_CHANNELS, &in.channels) == AF_OK) {
*p_af = prev;
return AF_OK;
}
char *filter = "lavrresample";
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_CHANNELS, &in.channels)))
return rv;
*p_af = new;
return AF_OK;
}
static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
struct mp_audio in)
{
int rv;
struct af_instance *af = *p_af;
struct af_instance *prev = af->prev;
struct mp_audio actual = *prev->data;
if (actual.rate == in.rate)
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
*p_af = prev;
return AF_OK;
}
char *filter = "lavrresample";
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate)))
return rv;
*p_af = new;
return AF_OK;
}
static void reset_formats(struct af_stream *s)
{
struct mp_audio none = {0};
for (struct af_instance *af = s->first; af; af = af->next) {
af->control(af, AF_CONTROL_SET_RESAMPLE_RATE, &(int){0});
af->control(af, AF_CONTROL_SET_CHANNELS, &(struct mp_chmap){0});
af->control(af, AF_CONTROL_SET_FORMAT, &(int){0});
if (af != s->first && af != s->last)
mp_audio_copy_config(af->data, &none);
}
}
static int filter_reinit(struct af_instance *af)
{
struct af_instance *prev = af->prev;
assert(prev);
// Check if this is the first filter
struct mp_audio in = *prev->data;
// Reset just in case...
mp_audio_set_null_data(&in);
if (!mp_audio_config_valid(&in))
return AF_ERROR;
af->fmt_in = in;
int rv = af->control(af, AF_CONTROL_REINIT, &in);
if (rv == AF_OK && !mp_audio_config_equals(&in, prev->data))
rv = AF_FALSE; // conversion filter needed
if (rv == AF_FALSE)
af->fmt_in = in;
if (rv == AF_OK) {
if (!mp_audio_config_valid(af->data))
return AF_ERROR;
af->fmt_out = *af->data;
}
return rv;
}
static int filter_reinit_with_conversion(struct af_stream *s, struct af_instance *af)
{
int rv = filter_reinit(af);
// Conversion filter is needed
if (rv == AF_FALSE) {
// First try if we can change the output format of the previous
// filter to the input format the current filter is expecting.
struct mp_audio in = af->fmt_in;
if (af->prev != s->first && !mp_audio_config_equals(af->prev->data, &in)) {
// This should have been successful (because it succeeded
// before), even if just reverting to the old output format.
mp_audio_copy_config(af->prev->data, &in);
rv = filter_reinit(af->prev);
if (rv != AF_OK)
return rv;
}
if (!mp_audio_config_equals(af->prev->data, &in)) {
// Retry with conversion filter added.
struct af_instance *new =
af_prepend(s, af, "lavrresample", NULL);
if (!new)
return AF_ERROR;
new->auto_inserted = true;
mp_audio_copy_config(new->data, &in);
rv = filter_reinit(new);
if (rv != AF_OK)
af_remove(s, new);
}
if (rv == AF_OK)
rv = filter_reinit(af);
}
return rv;
}
static int af_find_output_conversion(struct af_stream *s, struct mp_audio *cfg)
{
assert(mp_audio_config_valid(&s->output));
assert(s->initialized > 0);
if (mp_chmap_equals_reordered(&s->input.channels, &s->output.channels))
return AF_ERROR;
// Heuristic to detect point of conversion. If it looks like something
// more complicated is going on, better bail out.
// We expect that the last filter converts channels.
struct af_instance *conv = s->last->prev;
if (!conv->auto_inserted)
return AF_ERROR;
if (!(mp_chmap_equals_reordered(&conv->fmt_in.channels, &s->input.channels) &&
mp_chmap_equals_reordered(&conv->fmt_out.channels, &s->output.channels)))
return AF_ERROR;
// Also, should be the only one which does auto conversion.
for (struct af_instance *af = s->first->next; af != s->last; af = af->next)
{
if (af != conv && af->auto_inserted &&
!mp_chmap_equals_reordered(&af->fmt_in.channels, &af->fmt_out.channels))
return AF_ERROR;
}
// And not if it's the only filter.
if (conv->prev == s->first && conv->next == s->last)
return AF_ERROR;
*cfg = s->output;
return AF_OK;
}
// Return AF_OK on success or AF_ERROR on failure.
// Warning:
// A failed af_reinit() leaves the audio chain behind in a useless, broken
// state (for example, format filters that were tentatively inserted stay
// inserted).
// In that case, you should always rebuild the filter chain, or abort.
static int af_reinit(struct af_stream *s)
static int af_do_reinit(struct af_stream *s, bool second_pass)
{
struct mp_audio convert_early = {0};
if (second_pass) {
// If a channel conversion happens, and it is done by an auto-inserted
// filter, then insert a filter to convert it early. Otherwise, do
// nothing and return immediately.
if (af_find_output_conversion(s, &convert_early) != AF_OK)
return AF_OK;
}
remove_auto_inserted_filters(s);
af_chain_forget_frames(s);
reset_formats(s);
s->first->fmt_in = s->first->fmt_out = s->input;
if (mp_audio_config_valid(&convert_early)) {
struct af_instance *new = af_prepend(s, s->first, "lavrresample", NULL);
if (!new)
return AF_ERROR;
new->auto_inserted = true;
mp_audio_copy_config(new->data, &convert_early);
int rv = filter_reinit(new);
if (rv != AF_DETACH && rv != AF_OK)
return AF_ERROR;
MP_VERBOSE(s, "Moving up output conversion.\n");
}
// Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
struct af_instance *af = s->first->next;
// Up to 4 retries per filter (channel, rate, format conversions)
int max_retry = 4;
int retry = 0;
while (af) {
if (retry >= max_retry)
goto negotiate_error;
int rv = filter_reinit_with_conversion(s, af);
// Check if this is the first filter
struct mp_audio in = *af->prev->data;
// Reset just in case...
mp_audio_set_null_data(&in);
if (!mp_audio_config_valid(&in))
goto error;
af->fmt_in = in;
int rv = af->control(af, AF_CONTROL_REINIT, &in);
if (rv == AF_OK && !mp_audio_config_equals(&in, af->prev->data))
rv = AF_FALSE; // conversion filter needed
switch (rv) {
case AF_OK:
if (!mp_audio_config_valid(af->data))
goto error;
af->fmt_out = *af->data;
af = af->next;
break;
case AF_FALSE: { // Configuration filter is needed
if (af_fix_channels(s, &af, in) == AF_OK) {
retry++;
continue;
}
if (af_fix_rate(s, &af, in) == AF_OK) {
retry++;
continue;
}
// Do this last, to prevent "format->lavrresample" being added to
// the filter chain when output formats not supported by
// af_lavrresample are in use.
if (af_fix_format_conversion(s, &af, in) == AF_OK) {
retry++;
continue;
}
case AF_FALSE: {
// If the format conversion is (probably) caused by spdif, then
// (as a feature) drop the filter, instead of failing hard.
int fmt_in1 = af->prev->data->format;
int fmt_in2 = in.format;
int fmt_in2 = af->fmt_in.format;
if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
bool spd1 = af_fmt_is_spdif(fmt_in1);
bool spd2 = af_fmt_is_spdif(fmt_in2);
@@ -460,7 +426,6 @@ static int af_reinit(struct af_stream *s)
struct af_instance *aft = af->prev;
af_remove(s, af);
af = aft->next;
retry++;
continue;
}
}
@@ -478,8 +443,6 @@ static int af_reinit(struct af_stream *s)
af->info->name, rv);
goto error;
}
if (af && !af->auto_inserted)
retry = 0;
}
/* Set previously unset fields in s->output to those of the filter chain
@@ -503,6 +466,19 @@ error:
return AF_ERROR;
}
static int af_reinit(struct af_stream *s)
{
int r = af_do_reinit(s, false);
if (r == AF_OK && mp_audio_config_valid(&s->output)) {
r = af_do_reinit(s, true);
if (r != AF_OK) {
MP_ERR(s, "Failed second pass filter negotiation.\n");
r = af_do_reinit(s, false);
}
}
return r;
}
// Uninit and remove all filters
void af_uninit(struct af_stream *s)
{
@@ -557,7 +533,7 @@ void af_destroy(struct af_stream *s)
format of the preferred output respectively. The function is
reentrant i.e. if called with an already initialized stream the
stream will be reinitialized.
If one of the prefered output parameters is 0 the one that needs
If one of the preferred output parameters is 0 the one that needs
no conversion is used (i.e. the output format in the last filter).
The return value is 0 if success and -1 if failure */
int af_init(struct af_stream *s)
@@ -602,12 +578,7 @@ struct af_instance *af_add(struct af_stream *s, char *name, char *label,
if (af_find_by_label(s, label))
return NULL;
struct af_instance *new;
// Insert the filter somewhere nice
if (af_is_conversion_filter(s->first->next))
new = af_append(s, s->first->next, name, args);
else
new = af_prepend(s, s->first->next, name, args);
struct af_instance *new = af_prepend(s, s->last, name, args);
if (!new)
return NULL;
new->label = talloc_strdup(new, label);
-5
View File
@@ -112,9 +112,6 @@ struct af_stream {
enum af_control {
AF_CONTROL_REINIT = 1,
AF_CONTROL_RESET,
AF_CONTROL_SET_RESAMPLE_RATE,
AF_CONTROL_SET_FORMAT,
AF_CONTROL_SET_CHANNELS,
AF_CONTROL_SET_VOLUME,
AF_CONTROL_GET_VOLUME,
AF_CONTROL_SET_PAN_LEVEL,
@@ -160,6 +157,4 @@ int af_test_output(struct af_instance *af, struct mp_audio *out);
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
float af_softclip(float a);
bool af_lavrresample_test_conversion(int src_format, int dst_format);
#endif /* MPLAYER_AF_H */
-194
View File
@@ -1,194 +0,0 @@
/*
* This audio filter delays the output signal for the different
* channels and can be used for simple position panning.
* An extension for this filter would be a reverb.
*
* Original author: Anders
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "common/common.h"
#include "af.h"
#define L 65536
#define UPDATEQI(qi) qi=(qi+1)&(L-1)
// Data for specific instances of this filter
typedef struct af_delay_s
{
void* q[AF_NCH]; // Circular queues used for delaying audio signal
int wi[AF_NCH]; // Write index
int ri; // Read index
float d[AF_NCH]; // Delay [ms]
char *delaystr;
}af_delay_t;
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_delay_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
int i;
struct mp_audio *in = arg;
if (in->bps != 1 && in->bps != 2 && in->bps != 4) {
MP_FATAL(af, "Sample format not supported\n");
return AF_ERROR;
}
// Free prevous delay queues
for(i=0;i<af->data->nch;i++)
free(s->q[i]);
mp_audio_force_interleaved_format(in);
mp_audio_copy_config(af->data, in);
// Allocate new delay queues
for(i=0;i<af->data->nch;i++){
s->q[i] = calloc(L,af->data->bps);
if(NULL == s->q[i])
MP_FATAL(af, "Out of memory\n");
}
if(AF_OK != af_from_ms(AF_NCH, s->d, s->wi, af->data->rate, 0.0, 1000.0))
return AF_ERROR;
s->ri = 0;
for(i=0;i<AF_NCH;i++){
MP_DBG(af, "Channel %i delayed by %0.3fms\n",
i,MPCLAMP(s->d[i],0.0,1000.0));
MP_TRACE(af, "Channel %i delayed by %i samples\n",
i,s->wi[i]);
}
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
int i;
for(i=0;i<AF_NCH;i++)
free(((af_delay_t*)(af->priv))->q[i]);
}
static int filter_frame(struct af_instance *af, struct mp_audio *c)
{
if (!c)
return 0;
af_delay_t* s = af->priv; // Setup for this instance
int nch = c->nch; // Number of channels
int len = mp_audio_psize(c)/c->bps; // Number of sample in data chunk
int ri = 0;
int ch,i;
if (af_make_writeable(af, c) < 0) {
talloc_free(c);
return -1;
}
for(ch=0;ch<nch;ch++){
switch(c->bps){
case 1:{
int8_t* a = c->planes[0];
int8_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
for(i=ch;i<len;i+=nch){
q[wi] = a[i];
a[i] = q[ri];
UPDATEQI(wi);
UPDATEQI(ri);
}
s->wi[ch] = wi;
break;
}
case 2:{
int16_t* a = c->planes[0];
int16_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
for(i=ch;i<len;i+=nch){
q[wi] = a[i];
a[i] = q[ri];
UPDATEQI(wi);
UPDATEQI(ri);
}
s->wi[ch] = wi;
break;
}
case 4:{
int32_t* a = c->planes[0];
int32_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
for(i=ch;i<len;i+=nch){
q[wi] = a[i];
a[i] = q[ri];
UPDATEQI(wi);
UPDATEQI(ri);
}
s->wi[ch] = wi;
break;
}
}
}
s->ri = ri;
af_add_output_frame(af, c);
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->filter_frame = filter_frame;
af_delay_t *s = af->priv;
int n = 1;
int i = 0;
char* cl = s->delaystr;
while(cl && n && i < AF_NCH ){
sscanf(cl,"%f%n",&s->d[i],&n);
if(n==0 || cl[n-1] == '\0')
break;
cl=&cl[n];
if (*cl != ',')
break;
cl++;
i++;
}
return AF_OK;
}
#define OPT_BASE_STRUCT af_delay_t
const struct af_info af_info_delay = {
.info = "Delay audio filter",
.name = "delay",
.open = af_open,
.priv_size = sizeof(af_delay_t),
.options = (const struct m_option[]) {
OPT_STRING("delays", delaystr, 0),
{0}
},
};
+8 -8
View File
@@ -29,10 +29,10 @@ struct priv {
int in_format;
int in_srate;
struct mp_chmap in_channels;
struct m_channels in_channels;
int out_format;
int out_srate;
struct mp_chmap out_channels;
struct m_channels out_channels;
int fail;
};
@@ -44,8 +44,8 @@ static void force_in_params(struct af_instance *af, struct mp_audio *in)
if (priv->in_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(in, priv->in_format);
if (priv->in_channels.num)
mp_audio_set_channels(in, &priv->in_channels);
if (priv->in_channels.num_chmaps > 0)
mp_audio_set_channels(in, &priv->in_channels.chmaps[0]);
if (priv->in_srate)
in->rate = priv->in_srate;
@@ -58,8 +58,8 @@ static void force_out_params(struct af_instance *af, struct mp_audio *out)
if (priv->out_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(out, priv->out_format);
if (priv->out_channels.num)
mp_audio_set_channels(out, &priv->out_channels);
if (priv->out_channels.num_chmaps > 0)
mp_audio_set_channels(out, &priv->out_channels.chmaps[0]);
if (priv->out_srate)
out->rate = priv->out_srate;
@@ -124,10 +124,10 @@ const struct af_info af_info_format = {
.options = (const struct m_option[]) {
OPT_AUDIOFORMAT("format", in_format, 0),
OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000),
OPT_CHMAP("channels", in_channels, CONF_MIN, .min = 0),
OPT_CHANNELS("channels", in_channels, 0, .min = 1),
OPT_AUDIOFORMAT("out-format", out_format, 0),
OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000),
OPT_CHMAP("out-channels", out_channels, CONF_MIN, .min = 0),
OPT_CHANNELS("out-channels", out_channels, 0, .min = 1),
OPT_FLAG("fail", fail, 0),
{0}
},
+30 -16
View File
@@ -33,6 +33,7 @@
#include "config.h"
#include "common/av_common.h"
#include "common/common.h"
#include "af.h"
#include "audio/audio_buffer.h"
@@ -57,11 +58,13 @@ typedef struct af_ac3enc_s {
struct mp_audio *pending; // unconsumed input data
int in_samples; // samples of input per AC3 frame
int out_samples; // upper bound on encoded output per AC3 frame
int64_t encoder_buffered;
int cfg_add_iec61937_header;
int cfg_bit_rate;
int cfg_min_channel_num;
char *cfg_encoder;
char **cfg_avopts;
} af_ac3enc_t;
// fmt carries the input format. Change it to the best next-possible format
@@ -168,6 +171,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
s->in_samples = s->lavc_actx->frame_size;
mp_audio_realloc(s->input, s->in_samples);
s->input->samples = 0;
s->encoder_buffered = 0;
return AF_OK;
}
case AF_CONTROL_RESET:
@@ -176,6 +180,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
talloc_free(s->pending);
s->pending = NULL;
s->input->samples = 0;
s->encoder_buffered = 0;
return AF_OK;
}
return AF_UNKNOWN;
@@ -198,8 +203,8 @@ static void uninit(struct af_instance* af)
static void update_delay(struct af_instance *af)
{
af_ac3enc_t *s = af->priv;
af->delay = ((s->pending ? s->pending->samples : 0) + s->input->samples) /
(double)s->input->rate;
af->delay = ((s->pending ? s->pending->samples : 0) + s->input->samples +
s->encoder_buffered) / (double)s->input->rate;
}
static int filter_frame(struct af_instance *af, struct mp_audio *audio)
@@ -232,6 +237,9 @@ static bool fill_buffer(struct af_instance *af)
af->delay = 0;
if (s->pending) {
if (!mp_audio_is_writeable(s->input))
assert(s->input->samples == 0); // we can't have sent a partial frame
mp_audio_realloc_min(s->input, s->in_samples);
int copy = MPMIN(s->in_samples - s->input->samples, s->pending->samples);
s->input->samples += copy;
mp_audio_copy(s->input, s->input->samples - copy, s->pending, 0, copy);
@@ -249,17 +257,8 @@ static int read_input_frame(struct af_instance *af, AVFrame *frame)
if (!fill_buffer(af))
return 0; // need more input
frame->nb_samples = s->in_samples;
frame->format = s->lavc_actx->sample_fmt;
frame->channel_layout = s->lavc_actx->channel_layout;
#if LIBAVUTIL_VERSION_MICRO >= 100
frame->channels = s->lavc_actx->channels;
#endif
assert(s->input->num_planes <= AV_NUM_DATA_POINTERS);
frame->extended_data = frame->data;
for (int n = 0; n < s->input->num_planes; n++)
frame->data[n] = s->input->planes[n];
frame->linesize[0] = s->input->samples * s->input->sstride;
if (mp_audio_to_avframe(s->input, frame) < 0)
return -1;
return 1;
}
@@ -268,6 +267,9 @@ static int filter_out(struct af_instance *af)
{
af_ac3enc_t *s = af->priv;
if (!s->pending)
return 0;
AVFrame *frame = av_frame_alloc();
if (!frame) {
MP_FATAL(af, "Could not allocate memory \n");
@@ -295,6 +297,7 @@ static int filter_out(struct af_instance *af)
MP_FATAL(af, "Encode failed.\n");
goto done;
}
s->encoder_buffered += s->input->samples;
s->input->samples = 0;
}
int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt);
@@ -303,6 +306,10 @@ static int filter_out(struct af_instance *af)
err = 0;
goto done;
}
if (lavc_ret < 0) {
MP_FATAL(af, "Encode failed.\n");
goto done;
}
#else
err = read_input_frame(af, frame);
if (err < 0)
@@ -312,7 +319,6 @@ static int filter_out(struct af_instance *af)
err = -1;
int ok;
int lavc_ret = avcodec_encode_audio2(s->lavc_actx, &pkt, frame, &ok);
av_frame_free(&frame);
s->input->samples = 0;
if (lavc_ret < 0 || !ok) {
MP_FATAL(af, "Encode failed.\n");
@@ -321,7 +327,9 @@ static int filter_out(struct af_instance *af)
#endif
MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n",
pkt.size, s->pending->samples);
pkt.size, s->pending->samples + s->input->samples);
s->encoder_buffered -= AC3_FRAME_SIZE;
struct mp_audio *out =
mp_audio_pool_get(af->out_pool, af->data, s->out_samples);
@@ -358,11 +366,12 @@ static int filter_out(struct af_instance *af)
swap_16((uint16_t *)(buf + header_len), pkt.size / 2);
out->samples = frame_size / out->sstride;
af_add_output_frame(af, out);
update_delay(af);
err = 0;
done:
av_packet_unref(&pkt);
av_frame_free(&frame);
update_delay(af);
return err;
}
@@ -385,6 +394,10 @@ static int af_open(struct af_instance* af){
MP_ERR(af, "Audio LAVC, couldn't allocate context!\n");
return AF_ERROR;
}
if (mp_set_avopts(af->log, s->lavc_actx, s->cfg_avopts) < 0)
return AF_ERROR;
// For this one, we require the decoder to expert lists of all supported
// parameters. (Not all decoders do that, but the ones we're interested
// in do.)
@@ -434,6 +447,7 @@ const struct af_info af_info_lavcac3enc = {
({"auto", 0}, {"default", 0})),
OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6),
OPT_STRING("encoder", cfg_encoder, 0),
OPT_KEYVALUELIST("o", cfg_avopts, 0),
{0}
},
};
-21
View File
@@ -173,12 +173,6 @@ static int check_output_conversion(int mp_format)
return af_to_avformat(mp_format);
}
bool af_lavrresample_test_conversion(int src_format, int dst_format)
{
return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE &&
check_output_conversion(dst_format) != AV_SAMPLE_FMT_NONE;
}
static struct mp_chmap fudge_pairs[][2] = {
{MP_CHMAP2(BL, BR), MP_CHMAP2(SL, SR)},
{MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR)},
@@ -407,21 +401,6 @@ static int control(struct af_instance *af, int cmd, void *arg)
r = configure_lavrr(af, in, out, true);
return r;
}
case AF_CONTROL_SET_FORMAT: {
int format = *(int *)arg;
if (format && check_output_conversion(format) == AV_SAMPLE_FMT_NONE)
return AF_FALSE;
mp_audio_set_format(af->data, format);
return AF_OK;
}
case AF_CONTROL_SET_CHANNELS: {
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
return AF_OK;
}
case AF_CONTROL_SET_RESAMPLE_RATE:
af->data->rate = *(int *)arg;
return AF_OK;
case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
s->playback_speed = *(double *)arg;
return AF_OK;
+12 -8
View File
@@ -71,15 +71,19 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (af_fmt_is_planar(in->format))
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
s->rgain = 1.0;
if ((s->rgain_track || s->rgain_album) && af->replaygain_data) {
float gain, peak;
struct replaygain_data *rg = af->replaygain_data;
if ((s->rgain_track || s->rgain_album) && rg) {
MP_VERBOSE(af, "Replaygain: Track=%f/%f Album=%f/%f\n",
rg->track_gain, rg->track_peak,
rg->album_gain, rg->album_peak);
float gain, peak;
if (s->rgain_track) {
gain = af->replaygain_data->track_gain;
peak = af->replaygain_data->track_peak;
gain = rg->track_gain;
peak = rg->track_peak;
} else {
gain = af->replaygain_data->album_gain;
peak = af->replaygain_data->album_peak;
gain = rg->album_gain;
peak = rg->album_peak;
}
gain += s->rgain_preamp;
@@ -115,7 +119,7 @@ static void filter_plane(struct af_instance *af, struct mp_audio *data, int p)
{
struct priv *s = af->priv;
float level = s->level * s->rgain;
float level = s->level * s->rgain * from_dB(s->cfg_volume, 20.0, -200.0, 60.0);
int num_samples = data->samples * data->spf;
if (af_fmt_from_planar(af->data->format) == AF_FORMAT_S16) {
@@ -158,7 +162,7 @@ static int af_open(struct af_instance *af)
struct priv *s = af->priv;
af->control = control;
af->filter_frame = filter;
s->level = from_dB(s->cfg_volume, 20.0, -200.0, 60.0);
s->level = 1.0;
return AF_OK;
}
-403
View File
@@ -1,403 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <libavutil/common.h>
#include "config.h"
#include "audio/out/ao.h"
#include "audio/filter/af.h"
#include "common/global.h"
#include "common/msg.h"
#include "mpv_talloc.h"
#include "mixer.h"
struct mixer {
struct mp_log *log;
struct MPOpts *opts;
struct ao *ao;
struct af_stream *af;
// Static, dependent on ao/softvol settings
bool softvol; // use AO (false) or af_volume (true)
bool persistent_volume; // volume does not need to be restored
bool emulate_mute; // if true, emulate mute with volume=0
// Last known values (possibly out of sync with reality)
float vol_l, vol_r;
bool muted;
// Used to decide whether we should unmute on uninit
bool muted_by_us;
/* Contains ao driver name or "softvol" if volume is not persistent
* and needs to be restored after the driver is reinitialized. */
const char *driver;
// Other stuff
float balance;
};
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global)
{
struct mixer *mixer = talloc_ptrtype(talloc_ctx, mixer);
*mixer = (struct mixer) {
.log = mp_log_new(mixer, global->log, "mixer"),
.opts = global->opts,
.vol_l = 100,
.vol_r = 100,
.driver = "",
};
return mixer;
}
bool mixer_audio_initialized(struct mixer *mixer)
{
return !!mixer->ao;
}
float mixer_getmaxvolume(struct mixer *mixer)
{
// gain == 1
return mixer->softvol ? mixer->opts->softvol_max : 100;
}
static void checkvolume(struct mixer *mixer)
{
if (!mixer->ao)
return;
ao_control_vol_t vol = {mixer->vol_l, mixer->vol_r};
if (mixer->softvol) {
float gain;
if (!af_control_any_rev(mixer->af, AF_CONTROL_GET_VOLUME, &gain))
gain = 1.0;
vol.left = gain * 100.0;
vol.right = gain * 100.0;
} else {
MP_DBG(mixer, "Reading volume from AO.\n");
// Rely on the values not changing if the query is not supported
ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol);
ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted);
}
float l = mixer->vol_l;
float r = mixer->vol_r;
if (mixer->emulate_mute && mixer->muted)
l = r = 0;
/* Try to detect cases where the volume has been changed by some external
* action (such as something else changing a shared system-wide volume).
* We don't test for exact equality, as some AOs may round the value
* we last set to some nearby supported value. 3 has been the default
* volume step for increase/decrease keys, and is apparently big enough
* to step to the next possible value in most setups.
*/
if (FFABS(vol.left - l) >= 3 || FFABS(vol.right - r) >= 3) {
mixer->vol_l = vol.left;
mixer->vol_r = vol.right;
if (mixer->emulate_mute)
mixer->muted = false;
}
mixer->muted_by_us &= mixer->muted;
}
void mixer_getvolume(struct mixer *mixer, float *l, float *r)
{
checkvolume(mixer);
*l = mixer->vol_l;
*r = mixer->vol_r;
}
static void setvolume_internal(struct mixer *mixer)
{
float l = mixer->vol_l, r = mixer->vol_r;
if (mixer->emulate_mute && mixer->muted)
l = r = 0;
if (!mixer->softvol) {
MP_DBG(mixer, "Setting volume on AO.\n");
struct ao_control_vol vol = {.left = l, .right = r};
if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
MP_ERR(mixer, "Failed to change audio output volume.\n");
return;
}
float gain = (l + r) / 2.0 / 100.0;
if (!af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)) {
if (gain == 1.0)
return;
MP_VERBOSE(mixer, "Inserting volume filter.\n");
if (!(af_add(mixer->af, "volume", "softvol", NULL)
&& af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mixer, "No volume control available.\n");
}
}
void mixer_setvolume(struct mixer *mixer, float l, float r)
{
checkvolume(mixer); // to check mute status
float max = mixer_getmaxvolume(mixer);
mixer->vol_l = MPCLAMP(l, 0, max);
mixer->vol_r = MPCLAMP(r, 0, max);
if (mixer->ao)
setvolume_internal(mixer);
}
void mixer_getbothvolume(struct mixer *mixer, float *b)
{
float mixer_l, mixer_r;
mixer_getvolume(mixer, &mixer_l, &mixer_r);
*b = (mixer_l + mixer_r) / 2;
}
void mixer_setmute(struct mixer *mixer, bool mute)
{
checkvolume(mixer);
if (mute == mixer->muted)
return;
if (mixer->ao) {
mixer->muted = mute;
mixer->muted_by_us = mute;
if (mixer->emulate_mute) {
setvolume_internal(mixer);
} else {
ao_control(mixer->ao, AOCONTROL_SET_MUTE, &mute);
}
checkvolume(mixer);
} else {
mixer->muted = mute;
mixer->muted_by_us = mute;
}
}
bool mixer_getmute(struct mixer *mixer)
{
checkvolume(mixer);
return mixer->muted;
}
void mixer_addvolume(struct mixer *mixer, float step)
{
float vol_l, vol_r;
mixer_getvolume(mixer, &vol_l, &vol_r);
mixer_setvolume(mixer, vol_l + step, vol_r + step);
}
void mixer_getbalance(struct mixer *mixer, float *val)
{
if (mixer->af)
af_control_any_rev(mixer->af, AF_CONTROL_GET_PAN_BALANCE, &mixer->balance);
*val = mixer->balance;
}
/* NOTE: Currently the balance code is seriously buggy: it always changes
* the af_pan mapping between the first two input channels and first two
* output channels to particular values. These values make sense for an
* af_pan instance that was automatically inserted for balance control
* only and is otherwise an identity transform, but if the filter was
* there for another reason, then ignoring and overriding the original
* values is completely wrong.
*/
void mixer_setbalance(struct mixer *mixer, float val)
{
struct af_instance *af_pan_balance;
mixer->balance = val;
if (!mixer->af)
return;
if (af_control_any_rev(mixer->af, AF_CONTROL_SET_PAN_BALANCE, &val))
return;
if (val == 0)
return;
if (!(af_pan_balance = af_add(mixer->af, "pan", "autopan", NULL))) {
MP_ERR(mixer, "No balance control available.\n");
return;
}
/* make all other channels pass through since by default pan blocks all */
for (int i = 2; i < AF_NCH; i++) {
float level[AF_NCH] = {0};
level[i] = 1.f;
af_control_ext_t arg_ext = { .ch = i, .arg = level };
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_LEVEL,
&arg_ext);
}
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_BALANCE, &val);
}
char *mixer_get_volume_restore_data(struct mixer *mixer)
{
if (!mixer->driver[0])
return NULL;
return talloc_asprintf(NULL, "%s:%f:%f:%d", mixer->driver, mixer->vol_l,
mixer->vol_r, mixer->muted_by_us);
}
static void probe_softvol(struct mixer *mixer)
{
bool ao_perapp = ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) == 1;
bool ao_softvol = ao_control(mixer->ao, AOCONTROL_HAS_SOFT_VOLUME, 0) == 1;
assert(!(ao_perapp && ao_softvol));
mixer->persistent_volume = !ao_softvol;
if (mixer->opts->softvol == SOFTVOL_AUTO) {
// No system-wide volume => fine with AO volume control.
mixer->softvol = !ao_softvol && !ao_perapp;
} else {
mixer->softvol = mixer->opts->softvol == SOFTVOL_YES;
}
if (mixer->softvol)
mixer->persistent_volume = false;
MP_DBG(mixer, "Will use af_volume: %s\n", mixer->softvol ? "yes" : "no");
// If we can't use real volume control => force softvol.
if (!mixer->softvol) {
ao_control_vol_t vol;
if (ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK) {
mixer->softvol = true;
MP_WARN(mixer, "Hardware volume control unavailable.\n");
}
}
// Probe native mute support.
mixer->emulate_mute = true;
if (!mixer->softvol) {
if (ao_control(mixer->ao, AOCONTROL_GET_MUTE, &(bool){0}) == CONTROL_OK)
mixer->emulate_mute = false;
}
}
static void restore_volume(struct mixer *mixer)
{
struct MPOpts *opts = mixer->opts;
struct ao *ao = mixer->ao;
float force_vol_l = -1, force_vol_r = -1;
int force_mute = -1;
const char *prev_driver = mixer->driver;
mixer->driver = mixer->softvol ? "softvol" : ao_get_name(ao);
if (!prev_driver[0])
prev_driver = mixer->driver;
// Restore old parameters if volume won't survive reinitialization.
// But not if volume scale is possibly different.
if (!mixer->persistent_volume && strcmp(mixer->driver, prev_driver) == 0) {
force_vol_l = mixer->vol_l;
force_vol_r = mixer->vol_r;
}
// Set mute if we disabled it on uninit last time.
if (mixer->muted_by_us)
force_mute = 1;
// Set parameters from command line.
if (opts->mixer_init_volume >= 0)
force_vol_l = force_vol_r = opts->mixer_init_volume;
if (opts->mixer_init_mute >= 0)
force_mute = opts->mixer_init_mute;
// Set parameters from playback resume.
char *data = mixer->opts->mixer_restore_volume_data;
if (!mixer->persistent_volume && data && data[0]) {
char drv[40];
float v_l, v_r;
int m;
if (sscanf(data, "%39[^:]:%f:%f:%d", drv, &v_l, &v_r, &m) == 4) {
if (strcmp(mixer->driver, drv) == 0) {
force_vol_l = v_l;
force_vol_r = v_r;
force_mute = !!m;
MP_DBG(mixer, "Restoring volume from resume config.\n");
}
}
talloc_free(mixer->opts->mixer_restore_volume_data);
mixer->opts->mixer_restore_volume_data = NULL;
}
// Using --volume should not reset the volume on every file (i.e. reinit),
// OTOH mpv --{ --volume 10 f1.mkv --} --{ --volume 20 f2.mkv --} must work.
// Resetting the option volumes to "auto" (-1) is easiest. If file local
// options (as shown above) are used, the option handler code will reset
// them to other values, and force the volume to be reset as well.
opts->mixer_init_volume = -1;
opts->mixer_init_mute = -1;
checkvolume(mixer);
if (force_vol_l >= 0 && force_vol_r >= 0) {
MP_DBG(mixer, "Restoring previous volume.\n");
mixer_setvolume(mixer, force_vol_l, force_vol_r);
}
if (force_mute >= 0) {
MP_DBG(mixer, "Restoring previous mute toggle.\n");
mixer_setmute(mixer, force_mute);
}
}
// Called after the audio filter chain is built or rebuilt.
// (Can be called multiple times, even without mixer_uninit() in-between.)
void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af)
{
if (!ao || !af)
return;
mixer->ao = ao;
mixer->af = af;
MP_DBG(mixer, "Reinit...\n");
probe_softvol(mixer);
restore_volume(mixer);
if (mixer->balance != 0)
mixer_setbalance(mixer, mixer->balance);
}
/* Called before uninitializing the audio filter chain. The main purpose is to
* turn off mute, in case it's a global/persistent setting which might
* otherwise be left enabled even after this player instance exits.
*/
void mixer_uninit_audio(struct mixer *mixer)
{
if (!mixer->ao)
return;
MP_DBG(mixer, "Uninit...\n");
checkvolume(mixer);
if (mixer->muted_by_us && mixer->persistent_volume) {
MP_DBG(mixer, "Draining.\n");
/* Current audio output API combines playing the remaining buffered
* audio and uninitializing the AO into one operation, even though
* ideally unmute would happen between those two steps. We can't do
* volume changes after uninitialization, but we don't want the
* remaining audio to play at full volume either. Thus this
* workaround to drop remaining audio first. */
ao_reset(mixer->ao);
mixer_setmute(mixer, false);
/* We remember mute status and re-enable it if we play more audio
* in the same process. */
mixer->muted_by_us = true;
}
mixer->ao = NULL;
mixer->af = NULL;
mixer->softvol = false;
}
-50
View File
@@ -1,50 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MIXER_H
#define MPLAYER_MIXER_H
#include <stdbool.h>
// Values of MPOpts.softvol
enum {
SOFTVOL_NO = 0,
SOFTVOL_YES = 1,
SOFTVOL_AUTO = 2,
};
struct mpv_global;
struct ao;
struct af_stream;
struct mixer;
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global);
void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af);
void mixer_uninit_audio(struct mixer *mixer);
bool mixer_audio_initialized(struct mixer *mixer);
void mixer_getvolume(struct mixer *mixer, float *l, float *r);
void mixer_setvolume(struct mixer *mixer, float l, float r);
void mixer_addvolume(struct mixer *mixer, float step);
void mixer_getbothvolume(struct mixer *mixer, float *b);
void mixer_setmute(struct mixer *mixer, bool mute);
bool mixer_getmute(struct mixer *mixer);
void mixer_getbalance(struct mixer *mixer, float *bal);
void mixer_setbalance(struct mixer *mixer, float bal);
float mixer_getmaxvolume(struct mixer *mixer);
char *mixer_get_volume_restore_data(struct mixer *mixer);
#endif /* MPLAYER_MIXER_H */
+33 -8
View File
@@ -158,7 +158,7 @@ error:
static struct ao *ao_init(bool probing, struct mpv_global *global,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
struct encode_lavc_context *encode_lavc_ctx, int flags,
int samplerate, int format, struct mp_chmap channels,
char *dev, char *name, char **args)
{
@@ -169,6 +169,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->channels = channels;
ao->format = format;
ao->encode_lavc_ctx = encode_lavc_ctx;
ao->init_flags = flags;
if (ao->driver->encode != !!ao->encode_lavc_ctx)
goto fail;
@@ -182,6 +183,8 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->api_priv = talloc_zero_size(ao, ao->api->priv_size);
assert(!ao->api->priv_defaults && !ao->api->options);
ao->stream_silence = flags & AO_INIT_STREAM_SILENCE;
int r = ao->driver->init(ao);
if (r < 0) {
// Silly exception for coreaudio spdif redirection
@@ -190,7 +193,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
snprintf(redirect, sizeof(redirect), "%s", ao->redirect);
snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : "");
talloc_free(ao);
return ao_init(probing, global, input_ctx, encode_lavc_ctx,
return ao_init(probing, global, input_ctx, encode_lavc_ctx, flags,
samplerate, format, channels, rdevice, redirect, NULL);
}
goto fail;
@@ -240,7 +243,7 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
}
struct ao *ao_init_best(struct mpv_global *global,
bool ao_null_fallback,
int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels)
@@ -283,7 +286,7 @@ struct ao *ao_init_best(struct mpv_global *global,
}
}
if (ao_null_fallback) {
if (init_flags & AO_INIT_NULL_FALLBACK) {
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
(struct m_obj_settings){.name = "null"});
}
@@ -297,7 +300,7 @@ struct ao *ao_init_best(struct mpv_global *global,
dev = pref_dev;
mp_verbose(log, "Using preferred device '%s'\n", dev);
}
ao = ao_init(probing, global, input_ctx, encode_lavc_ctx,
ao = ao_init(probing, global, input_ctx, encode_lavc_ctx, init_flags,
samplerate, format, channels, dev,
entry->name, entry->attribs);
if (ao)
@@ -429,6 +432,29 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
return r;
}
// safe_multichannel=true behaves like ao_chmap_sel_adjust.
// safe_multichannel=false is a helper for callers which do not support safe
// handling of arbitrary channel layouts. If the multichannel layouts are not
// considered "always safe" (e.g. HDMI), then allow only stereo or mono, if
// they are part of the list in *s.
bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, bool safe_multichannel)
{
if (!safe_multichannel && (ao->init_flags & AO_INIT_SAFE_MULTICHANNEL_ONLY)) {
struct mp_chmap res = *map;
if (mp_chmap_sel_adjust(s, &res)) {
if (!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_MONO) &&
!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_STEREO))
{
MP_WARN(ao, "Disabling multichannel output.\n");
*map = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
}
}
}
return ao_chmap_sel_adjust(ao, s, map);
}
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num)
{
@@ -490,10 +516,9 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
static void get_devices(struct ao *ao, struct ao_device_list *list)
{
int num = list->num_devices;
if (ao->driver->list_devs)
if (ao->driver->list_devs) {
ao->driver->list_devs(ao, list);
// Add at least a default entry
if (list->num_devices == num) {
} else {
char name[80] = "Default";
if (num > 1)
mp_snprintf_cat(name, sizeof(name), " (%s)", ao->driver->name);
+11 -1
View File
@@ -50,6 +50,16 @@ enum {
AO_EVENT_HOTPLUG = 2,
};
enum {
// Allow falling back to ao_null if nothing else works.
AO_INIT_NULL_FALLBACK = 1 << 0,
// Only accept multichannel configurations that are guaranteed to work
// (i.e. not sending arbitrary layouts over HDMI).
AO_INIT_SAFE_MULTICHANNEL_ONLY = 1 << 1,
// Stream silence as long as no audio is playing.
AO_INIT_STREAM_SILENCE = 1 << 2,
};
typedef struct ao_control_vol {
float left;
float right;
@@ -72,7 +82,7 @@ struct encode_lavc_context;
struct mp_audio;
struct ao *ao_init_best(struct mpv_global *global,
bool ao_null_fallback,
int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
+61 -16
View File
@@ -53,6 +53,7 @@ struct priv {
bool device_lost;
snd_pcm_format_t alsa_fmt;
bool can_pause;
bool paused;
snd_pcm_sframes_t prepause_frames;
double delay_before_pause;
snd_pcm_uframes_t buffersize;
@@ -360,7 +361,7 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
snd_pcm_free_chmaps(maps);
return ao_chmap_sel_adjust(ao, &chmap_sel, chmap);
return ao_chmap_sel_adjust2(ao, &chmap_sel, chmap, false);
}
// Map back our selected channel layout to an ALSA one. This is done this way so
@@ -785,6 +786,14 @@ static int init_device(struct ao *ao, int mode)
MP_VERBOSE(ao, "buffersize: %d samples\n", (int)p->buffersize);
MP_VERBOSE(ao, "period size: %d samples\n", (int)p->outburst);
ao->device_buffer = p->buffersize;
// ao_alsa implements this by relying on underrun behavior (no data means
// underrun, during which silence is played). Trigger by playing some
// initial silence.
if (ao->stream_silence)
ao_play_silence(ao, p->outburst);
return 0;
alsa_error:
@@ -874,7 +883,7 @@ static double get_delay(struct ao *ao)
struct priv *p = ao->priv;
snd_pcm_sframes_t delay;
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
if (p->paused)
return p->delay_before_pause;
if (snd_pcm_delay(p->alsa, &delay) < 0)
@@ -888,27 +897,47 @@ static double get_delay(struct ao *ao)
return delay / (double)ao->samplerate;
}
// For stream-silence mode: replace remaining buffer with silence.
// Tries to cause an instant buffer underrun.
static void soft_reset(struct ao *ao)
{
struct priv *p = ao->priv;
snd_pcm_sframes_t frames = snd_pcm_rewindable(p->alsa);
if (frames > 0 && snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
frames = snd_pcm_rewind(p->alsa, frames);
if (frames < 0) {
int err = frames;
CHECK_ALSA_WARN("pcm rewind error");
}
}
}
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
if (p->can_pause) {
if (p->paused)
return;
p->delay_before_pause = get_delay(ao);
p->prepause_frames = p->delay_before_pause * ao->samplerate;
if (ao->stream_silence) {
soft_reset(ao);
} else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
p->delay_before_pause = get_delay(ao);
err = snd_pcm_pause(p->alsa, 1);
CHECK_ALSA_ERROR("pcm pause error");
p->prepause_frames = 0;
}
} else {
if (snd_pcm_delay(p->alsa, &p->prepause_frames) < 0
|| p->prepause_frames < 0)
p->prepause_frames = 0;
p->delay_before_pause = p->prepause_frames / (double)ao->samplerate;
err = snd_pcm_drop(p->alsa);
CHECK_ALSA_ERROR("pcm drop error");
}
p->paused = true;
alsa_error: ;
}
@@ -930,9 +959,15 @@ static void audio_resume(struct ao *ao)
struct priv *p = ao->priv;
int err;
if (!p->paused)
return;
resume_device(ao);
if (p->can_pause) {
if (ao->stream_silence) {
p->paused = false;
get_delay(ao); // recovers from underrun (as a side-effect)
} else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
err = snd_pcm_pause(p->alsa, 0);
CHECK_ALSA_ERROR("pcm resume error");
@@ -941,11 +976,13 @@ static void audio_resume(struct ao *ao)
MP_VERBOSE(ao, "resume not supported by hardware\n");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
if (p->prepause_frames)
ao_play_silence(ao, p->prepause_frames);
}
if (p->prepause_frames)
ao_play_silence(ao, p->prepause_frames);
alsa_error: ;
p->paused = false;
}
static void reset(struct ao *ao)
@@ -953,12 +990,18 @@ static void reset(struct ao *ao)
struct priv *p = ao->priv;
int err;
p->paused = false;
p->prepause_frames = 0;
p->delay_before_pause = 0;
err = snd_pcm_drop(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
if (ao->stream_silence) {
soft_reset(ao);
} else {
err = snd_pcm_drop(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
}
alsa_error: ;
}
@@ -997,6 +1040,8 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
} while (res == 0);
p->paused = false;
return res < 0 ? -1 : res;
alsa_error:
+1 -1
View File
@@ -419,7 +419,7 @@ const struct ao_driver audio_out_coreaudio = {
.uninit = uninit,
.init = init,
.control = control,
.pause = stop,
.reset = stop,
.resume = start,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
+1 -1
View File
@@ -399,7 +399,7 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.name = "coreaudio_exclusive",
.uninit = uninit,
.init = init,
.pause = audio_pause,
.reset = audio_pause,
.resume = audio_resume,
.list_devs = ca_get_device_list,
.priv_size = sizeof(struct priv),
+1 -1
View File
@@ -70,7 +70,7 @@ OSStatus ca_get_ary(AudioObjectID id, ca_scope scope, ca_sel selector,
return err;
coreaudio_error_free:
free(*data);
talloc_free(*data);
coreaudio_error:
return err;
}
+8 -1
View File
@@ -114,6 +114,13 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
kAudioObjectSystemObject, &p_addr, 0, 0, &size, &v);
CFRelease(uid);
CHECK_CA_ERROR("unable to query for device UID");
uint32_t is_alive = 1;
err = CA_GET(*device, kAudioDevicePropertyDeviceIsAlive, &is_alive);
CHECK_CA_ERROR("could not check whether device is alive (invalid device?)");
if (!is_alive)
MP_WARN(ao, "device is not alive!\n");
} else {
// device not set by user, get the default one
err = CA_GET(kAudioObjectSystemObject,
@@ -140,7 +147,7 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
{
if (code == noErr) return true;
mp_msg(ao->log, level, "%s (%s)\n", message, mp_tag_str(code));
mp_msg(ao->log, level, "%s (%s/%d)\n", message, mp_tag_str(code), (int)code);
return false;
}
+122 -77
View File
@@ -39,8 +39,6 @@
#include "common/encode_lavc.h"
struct priv {
uint8_t *buffer;
size_t buffer_size;
AVStream *stream;
AVCodecContext *codec;
int pcmhack;
@@ -125,7 +123,7 @@ static int init(struct ao *ao)
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false))
goto fail;
mp_chmap_reorder_to_lavc(&ao->channels);
ac->codec->channels = ao->channels.num;
@@ -146,18 +144,10 @@ static int init(struct ao *ao)
if (ac->codec->frame_size <= 1)
ac->pcmhack = av_get_bits_per_sample(ac->codec->codec_id) / 8;
if (ac->pcmhack) {
if (ac->pcmhack)
ac->aframesize = 16384; // "enough"
ac->buffer_size =
ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200;
} else {
else
ac->aframesize = ac->codec->frame_size;
ac->buffer_size =
ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200;
}
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
ac->buffer_size = FF_MIN_BUFFER_SIZE;
ac->buffer = talloc_size(ac, ac->buffer_size);
// enough frames for at least 0.25 seconds
ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
@@ -182,7 +172,7 @@ fail:
}
// close audio device
static int encode(struct ao *ao, double apts, void **data);
static void encode(struct ao *ao, double apts, void **data);
static void uninit(struct ao *ao)
{
struct priv *ac = ao->priv;
@@ -199,12 +189,12 @@ static void uninit(struct ao *ao)
return;
}
if (ac->buffer) {
if (ac->stream) {
double outpts = ac->expected_next_pts;
if (!ectx->options->rawts && ectx->options->copyts)
outpts += ectx->discontinuity_pts_offset;
outpts += encode_lavc_getoffset(ectx, ac->codec);
while (encode(ao, outpts, NULL) > 0) ;
encode(ao, outpts, NULL);
}
pthread_mutex_unlock(&ectx->lock);
@@ -220,24 +210,130 @@ static int get_space(struct ao *ao)
return ac->aframesize * ac->framecount;
}
static void write_packet(struct ao *ao, AVPacket *packet)
{
// TODO: Can we unify this with the equivalent video code path?
struct priv *ac = ao->priv;
packet->stream_index = ac->stream->index;
if (packet->pts != AV_NOPTS_VALUE) {
packet->pts = av_rescale_q(packet->pts,
ac->codec->time_base,
ac->stream->time_base);
} else {
// Do we need this at all? Better be safe than sorry...
MP_WARN(ao, "encoder lost pts, why?\n");
if (ac->savepts != MP_NOPTS_VALUE) {
packet->pts = av_rescale_q(ac->savepts,
ac->codec->time_base,
ac->stream->time_base);
}
}
if (packet->dts != AV_NOPTS_VALUE) {
packet->dts = av_rescale_q(packet->dts,
ac->codec->time_base,
ac->stream->time_base);
}
if (packet->duration > 0) {
packet->duration = av_rescale_q(packet->duration,
ac->codec->time_base,
ac->stream->time_base);
}
ac->savepts = AV_NOPTS_VALUE;
if (encode_lavc_write_frame(ao->encode_lavc_ctx,
ac->stream, packet) < 0) {
MP_ERR(ao, "error writing at %d %d/%d\n",
(int) packet->pts,
ac->stream->time_base.num,
ac->stream->time_base.den);
return;
}
}
static void encode_audio_and_write(struct ao *ao, AVFrame *frame)
{
// TODO: Can we unify this with the equivalent video code path?
struct priv *ac = ao->priv;
AVPacket packet = {0};
#if HAVE_AVCODEC_NEW_CODEC_API
int status = avcodec_send_frame(ac->codec, frame);
if (status < 0) {
MP_ERR(ao, "error encoding at %d %d/%d\n",
frame ? (int) frame->pts : -1,
ac->codec->time_base.num,
ac->codec->time_base.den);
return;
}
for (;;) {
av_init_packet(&packet);
status = avcodec_receive_packet(ac->codec, &packet);
if (status == AVERROR(EAGAIN)) { // No more packets for now.
if (frame == NULL) {
MP_ERR(ao, "sent flush frame, got EAGAIN");
}
break;
}
if (status == AVERROR_EOF) { // No more packets, ever.
if (frame != NULL) {
MP_ERR(ao, "sent audio frame, got EOF");
}
break;
}
if (status < 0) {
MP_ERR(ao, "error encoding at %d %d/%d\n",
frame ? (int) frame->pts : -1,
ac->codec->time_base.num,
ac->codec->time_base.den);
break;
}
if (frame) {
if (ac->savepts == AV_NOPTS_VALUE)
ac->savepts = frame->pts;
}
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
write_packet(ao, &packet);
av_packet_unref(&packet);
}
#else
av_init_packet(&packet);
int got_packet = 0;
int status = avcodec_encode_audio2(ac->codec, &packet, frame, &got_packet);
if (status < 0) {
MP_ERR(ao, "error encoding at %d %d/%d\n",
frame ? (int) frame->pts : -1,
ac->codec->time_base.num,
ac->codec->time_base.den);
return;
}
if (!got_packet) {
return;
}
if (frame) {
if (ac->savepts == AV_NOPTS_VALUE)
ac->savepts = frame->pts;
}
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
write_packet(ao, &packet);
av_packet_unref(&packet);
#endif
}
// must get exactly ac->aframesize amount of data
static int encode(struct ao *ao, double apts, void **data)
static void encode(struct ao *ao, double apts, void **data)
{
AVPacket packet;
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
double realapts = ac->aframecount * (double) ac->aframesize /
ao->samplerate;
int status, gotpacket;
ac->aframecount++;
if (data)
ectx->audio_pts_offset = realapts - apts;
av_init_packet(&packet);
packet.data = ac->buffer;
packet.size = ac->buffer_size;
if(data) {
AVFrame *frame = av_frame_alloc();
frame->format = af_to_avformat(ao->format);
@@ -270,64 +366,11 @@ static int encode(struct ao *ao, double apts, void **data)
ac->lastpts = frame_pts;
frame->quality = ac->codec->global_quality;
status = avcodec_encode_audio2(ac->codec, &packet, frame, &gotpacket);
if (!status) {
if (ac->savepts == AV_NOPTS_VALUE)
ac->savepts = frame->pts;
}
encode_audio_and_write(ao, frame);
av_frame_free(&frame);
}
else
{
status = avcodec_encode_audio2(ac->codec, &packet, NULL, &gotpacket);
}
if(status) {
MP_ERR(ao, "error encoding\n");
return -1;
}
if(!gotpacket)
return 0;
MP_DBG(ao, "got pts %f (playback time: %f); out size: %d\n",
apts, realapts, packet.size);
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
packet.stream_index = ac->stream->index;
// Do we need this at all? Better be safe than sorry...
if (packet.pts == AV_NOPTS_VALUE) {
MP_WARN(ao, "encoder lost pts, why?\n");
if (ac->savepts != MP_NOPTS_VALUE)
packet.pts = ac->savepts;
}
if (packet.pts != AV_NOPTS_VALUE)
packet.pts = av_rescale_q(packet.pts, ac->codec->time_base,
ac->stream->time_base);
if (packet.dts != AV_NOPTS_VALUE)
packet.dts = av_rescale_q(packet.dts, ac->codec->time_base,
ac->stream->time_base);
if(packet.duration > 0)
packet.duration = av_rescale_q(packet.duration, ac->codec->time_base,
ac->stream->time_base);
ac->savepts = AV_NOPTS_VALUE;
if (encode_lavc_write_frame(ao->encode_lavc_ctx, ac->stream, &packet) < 0) {
MP_ERR(ao, "error writing at %f %f/%f\n",
realapts, (double) ac->stream->time_base.num,
(double) ac->stream->time_base.den);
return -1;
}
return packet.size;
encode_audio_and_write(ao, NULL);
}
// this should round samples down to frame sizes
@@ -492,3 +535,5 @@ const struct ao_driver audio_out_lavc = {
.play = play,
.drain = drain,
};
// vim: sw=4 ts=4 et tw=80
+5 -11
View File
@@ -57,7 +57,7 @@ struct priv {
// (This is not needed by the AO API, but many AOs behave this way.)
int outburst; // samples
char **channel_layouts;
struct m_channels channel_layouts;
};
static void drain(struct ao *ao)
@@ -91,15 +91,9 @@ static int init(struct ao *ao)
ao->untimed = priv->untimed;
struct mp_chmap_sel sel = {.tmp = ao};
if (priv->channel_layouts) {
for (int n = 0; priv->channel_layouts[n]; n++) {
struct mp_chmap map = {0};
if (!mp_chmap_from_str(&map, bstr0(priv->channel_layouts[n]))) {
MP_FATAL(ao, "Invalid channel map in option.\n");
return -1;
}
mp_chmap_sel_add_map(&sel, &map);
}
if (priv->channel_layouts.num_chmaps) {
for (int n = 0; n < priv->channel_layouts.num_chmaps; n++)
mp_chmap_sel_add_map(&sel, &priv->channel_layouts.chmaps[n]);
} else {
mp_chmap_sel_add_any(&sel);
}
@@ -244,7 +238,7 @@ const struct ao_driver audio_out_null = {
OPT_FLOATRANGE("latency", latency_sec, 0, 0, 100),
OPT_FLAG("broken-eof", broken_eof, 0),
OPT_FLAG("broken-delay", broken_delay, 0),
OPT_STRINGLIST("channel-layouts", channel_layouts, 0),
OPT_CHANNELS("channel-layouts", channel_layouts, 0),
{0}
},
};
+7
View File
@@ -612,6 +612,12 @@ static int audio_wait(struct ao *ao, pthread_mutex_t *lock)
return r;
}
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
if (stat(PATH_DEV_DSP, &(struct stat){0}) == 0)
ao_device_list_add(list, ao, &(struct ao_device_desc){"", "Default"});
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_oss = {
@@ -629,6 +635,7 @@ const struct ao_driver audio_out_oss = {
.drain = drain,
.wait = audio_wait,
.wakeup = ao_wakeup_poll,
.list_devs = list_devs,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.audio_fd = -1,
+3 -2
View File
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
@@ -34,8 +35,8 @@
#include "ao.h"
#include "internal.h"
#define VOL_PA2MP(v) ((v) * 100 / PA_VOLUME_NORM)
#define VOL_MP2PA(v) ((v) * PA_VOLUME_NORM / 100)
#define VOL_PA2MP(v) ((v) * 100.0 / PA_VOLUME_NORM)
#define VOL_MP2PA(v) lrint((v) * PA_VOLUME_NORM / 100)
struct priv {
// PulseAudio playback stream object
+1 -1
View File
@@ -132,7 +132,7 @@ static bool thread_feed(struct ao *ao)
mp_time_us() + (int64_t)llrint(delay_us));
// note, we can't use ao_read_data return value here since we already
// commited to frame_count above in the GetBuffer call
// committed to frame_count above in the GetBuffer call
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
frame_count, 0);
EXIT_ON_ERROR(hr);
+1 -1
View File
@@ -95,7 +95,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded(
return S_OK;
}
// maybe MPV can go over to the prefered device once it is plugged in?
// maybe MPV can go over to the preferred device once it is plugged in?
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
IMMNotificationClient *This,
LPCWSTR pwstrDeviceId)
+1 -1
View File
@@ -407,7 +407,7 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
}
entry = ao->channels;
if (ao_chmap_sel_adjust(ao, &chmap_sel, &entry)){
if (ao_chmap_sel_adjust2(ao, &chmap_sel, &entry, !state->opt_exclusive)){
change_waveformat_channels(wformat, &entry);
return true;
}
+4
View File
@@ -43,6 +43,8 @@ struct ao {
struct encode_lavc_context *encode_lavc_ctx;
struct input_ctx *input_ctx;
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
int init_flags; // AO_INIT_* flags
bool stream_silence; // if audio inactive, just play silence
// The device as selected by the user, usually using ao_device_desc.name
// from an entry from the list returned by driver->list_devices. If the
@@ -191,6 +193,8 @@ void ao_wakeup_poll(struct ao *ao);
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map);
bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, bool safe_multichannel);
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num);
+6 -2
View File
@@ -185,7 +185,7 @@ static double get_delay(struct ao *ao)
static void reset(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
if (ao->driver->reset)
if (!ao->stream_silence && ao->driver->reset)
ao->driver->reset(ao); // assumes the audio callback thread is stopped
set_state(ao, AO_STATE_NONE);
for (int n = 0; n < ao->num_planes; n++)
@@ -195,7 +195,7 @@ static void reset(struct ao *ao)
static void pause(struct ao *ao)
{
if (ao->driver->reset)
if (!ao->stream_silence && ao->driver->reset)
ao->driver->reset(ao);
set_state(ao, AO_STATE_NONE);
}
@@ -244,6 +244,10 @@ static int init(struct ao *ao)
p->buffers[n] = mp_ring_new(ao, ao->buffer * ao->sstride);
atomic_store(&p->state, AO_STATE_NONE);
assert(ao->driver->resume);
if (ao->stream_silence)
ao->driver->resume(ao);
return 0;
}
+2 -4
View File
@@ -497,10 +497,8 @@ int ao_wait_poll(struct ao *ao, struct pollfd *fds, int num_fds,
bool wakeup = false;
if (p_fds[num_fds].revents & POLLIN) {
wakeup = true;
// flush the wakeup pipe contents - might "drown" some wakeups, but
// that's ok for our use-case
char buf[100];
(void)read(p->wakeup_pipe[0], buf, sizeof(buf));
// might "drown" some wakeups, but that's ok for our use-case
mp_flush_wakeup_pipe(p->wakeup_pipe[0]);
}
return (r >= 0 || r == -EINTR) ? wakeup : -1;
}
+2 -9
View File
@@ -189,7 +189,7 @@ struct lib {
unsigned runv;
};
void print_libav_versions(struct mp_log *log, int v)
bool print_libav_versions(struct mp_log *log, int v)
{
const struct lib libs[] = {
{"libavutil", LIBAVUTIL_VERSION_INT, avutil_version()},
@@ -222,14 +222,7 @@ void print_libav_versions(struct mp_log *log, int v)
mp_msg(log, v, "%s version: %s\n", LIB_PREFIX, av_version_info());
#endif
if (mismatch) {
// Using mismatched libraries can be legitimate, but even then it's
// a bad idea. We don't acknowledge its usefulness and stability.
mp_warn(log, "Warning: mpv was compiled against a different version of "
"%s than the shared\nlibrary it is linked against. This is "
"most likely a broken build\nand misbehavior and crashes are "
"to be expected.\n", LIB_PREFIX);
}
return !mismatch;
}
#undef V
+4 -1
View File
@@ -1,8 +1,11 @@
#ifndef MP_AV_LOG_H
#define MP_AV_LOG_H
#include <stdbool.h>
struct mpv_global;
struct mp_log;
void init_libav(struct mpv_global *global);
void uninit_libav(struct mpv_global *global);
void print_libav_versions(struct mp_log *log, int v);
bool print_libav_versions(struct mp_log *log, int v);
#endif
+223 -117
View File
@@ -119,16 +119,20 @@ struct demux_internal {
int max_packs;
int max_bytes;
// Set if we know that we are at the start of the file. This is used to
// avoid a redundant initial seek after enabling streams. We could just
// allow it, but to avoid buggy seeking affecting normal playback, we don't.
bool initial_state;
bool tracks_switched; // thread needs to inform demuxer of this
bool seeking; // there's a seek queued
int seek_flags; // flags for next seek (if seeking==true)
double seek_pts;
bool refresh_seeks_enabled;
bool start_refresh_seek;
double ref_pts; // assumed player position (only for track switches)
double ts_offset; // timestamp offset to apply everything
double ts_offset; // timestamp offset to apply to everything
void (*run_fn)(void *); // if non-NULL, function queued to be run on
void *run_fn_arg; // the thread as run_fn(run_fn_arg)
@@ -152,7 +156,10 @@ struct demux_stream {
// if false, this stream is disabled, or passively
// read (like subtitles)
bool eof; // end of demuxed stream? (true if all buffer empty)
bool need_refresh; // enabled mid-stream
bool refreshing;
bool correct_dts; // packet DTS is strictly monotonically increasing
bool correct_pos; // packet pos is strictly monotonically increasing
size_t packs; // number of packets in buffer
size_t bytes; // total bytes of packets in buffer
double base_ts; // timestamp of the last packet returned to decoder
@@ -161,12 +168,12 @@ struct demux_stream {
size_t last_br_bytes; // summed packet sizes since last bitrate calculation
double bitrate;
int64_t last_pos;
double last_dts;
struct demux_packet *head;
struct demux_packet *tail;
// for closed captions (demuxer_feed_caption)
struct sh_stream *cc;
};
// Return "a", or if that is NOPTS, return "def".
@@ -199,7 +206,10 @@ static void ds_flush(struct demux_stream *ds)
ds->eof = false;
ds->active = false;
ds->refreshing = false;
ds->need_refresh = false;
ds->last_pos = -1;
ds->last_dts = MP_NOPTS_VALUE;
ds->correct_dts = ds->correct_pos = true;
}
void demux_set_ts_offset(struct demuxer *demuxer, double offset)
@@ -222,6 +232,7 @@ struct sh_stream *demux_alloc_sh_stream(enum stream_type type)
.ff_index = -1, // may be overwritten by demuxer
.demuxer_id = -1, // ... same
.codec = talloc_zero(sh, struct mp_codec_params),
.tags = talloc_zero(sh, struct mp_tags),
};
sh->codec->type = type;
return sh;
@@ -266,6 +277,33 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
pthread_mutex_unlock(&in->lock);
}
// Update sh->tags (lazily). This must be called by demuxers which update
// stream tags after init. (sh->tags can be accessed by the playback thread,
// which means the demuxer thread cannot write or read it directly.)
// Before init is finished, sh->tags can still be accessed freely.
// Ownership of tags goes to the function.
void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh,
struct mp_tags *tags)
{
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_thread);
if (sh->ds) {
while (demuxer->num_update_stream_tags <= sh->index) {
MP_TARRAY_APPEND(demuxer, demuxer->update_stream_tags,
demuxer->num_update_stream_tags, NULL);
}
talloc_free(demuxer->update_stream_tags[sh->index]);
demuxer->update_stream_tags[sh->index] = talloc_steal(demuxer, tags);
demux_changed(demuxer, DEMUX_EVENT_METADATA);
} else {
// not added yet
talloc_free(sh->tags);
sh->tags = talloc_steal(sh, tags);
}
}
// Return a stream with the given index. Since streams can only be added during
// the lifetime of the demuxer, it is guaranteed that an index within the valid
// range [0, demux_get_num_stream()) always returns a valid sh_stream pointer,
@@ -389,6 +427,59 @@ void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp)
demux_add_packet(sh, dp);
}
// An obscure mechanism to get stream switching to be executed faster.
// On a switch, it seeks back, and then grabs all packets that were
// "missing" from the packet queue of the newly selected stream.
// Returns MP_NOPTS_VALUE if no seek should happen.
static double get_refresh_seek_pts(struct demux_internal *in)
{
struct demuxer *demux = in->d_thread;
double start_ts = in->ref_pts;
bool needed = false;
bool normal_seek = true;
bool refresh_possible = true;
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
if (!ds->selected)
continue;
if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
needed |= ds->need_refresh;
// If there were no other streams selected, we can use a normal seek.
normal_seek &= ds->need_refresh;
ds->need_refresh = false;
refresh_possible &= ds->correct_dts || ds->correct_pos;
}
if (!needed || start_ts == MP_NOPTS_VALUE || !demux->desc->seek ||
!demux->seekable || demux->partially_seekable)
return MP_NOPTS_VALUE;
if (normal_seek)
return start_ts;
if (!refresh_possible) {
MP_VERBOSE(in, "can't issue refresh seek\n");
return MP_NOPTS_VALUE;
}
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
// Streams which didn't have any packets yet will return all packets,
// other streams return packets only starting from the last position.
if (ds->last_pos != -1 || ds->last_dts != MP_NOPTS_VALUE)
ds->refreshing = true;
}
// Seek back to player's current position, with a small offset added.
return start_ts - 1.0;
}
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
{
struct demux_stream *ds = stream ? stream->ds : NULL;
@@ -399,25 +490,34 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
struct demux_internal *in = ds->in;
pthread_mutex_lock(&in->lock);
bool drop = false;
bool drop = ds->refreshing;
if (ds->refreshing) {
// Resume reading once the old position was reached (i.e. we start
// returning packets where we left off before the refresh).
drop = dp->pos <= ds->last_pos;
if (dp->pos >= ds->last_pos)
ds->refreshing = false;
// If it's the same position, drop, but continue normally next time.
if (ds->correct_dts) {
ds->refreshing = dp->dts < ds->last_dts;
} else if (ds->correct_pos) {
ds->refreshing = dp->pos < ds->last_pos;
} else {
ds->refreshing = false; // should not happen
}
}
if (!ds->selected || in->seeking || drop) {
if (!ds->selected || ds->need_refresh || in->seeking || drop) {
pthread_mutex_unlock(&in->lock);
talloc_free(dp);
return;
}
ds->correct_pos &= dp->pos >= 0 && dp->pos > ds->last_pos;
ds->correct_dts &= dp->dts != MP_NOPTS_VALUE && dp->dts > ds->last_dts;
ds->last_pos = dp->pos;
ds->last_dts = dp->dts;
dp->stream = stream->index;
dp->next = NULL;
ds->last_pos = dp->pos;
ds->packs++;
ds->bytes += dp->len;
if (ds->tail) {
@@ -505,27 +605,40 @@ static bool read_packet(struct demux_internal *in)
if (!read_more)
return false;
double seek_pts = get_refresh_seek_pts(in);
// Actually read a packet. Drop the lock while doing so, because waiting
// for disk or network I/O can take time.
in->idle = false;
in->initial_state = false;
pthread_mutex_unlock(&in->lock);
struct demuxer *demux = in->d_thread;
if (seek_pts != MP_NOPTS_VALUE) {
MP_VERBOSE(in, "refresh seek to %f\n", seek_pts);
demux->desc->seek(demux, seek_pts, SEEK_BACKWARD | SEEK_HR);
}
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
update_cache(in);
pthread_mutex_lock(&in->lock);
if (eof) {
for (int n = 0; n < in->num_streams; n++)
in->streams[n]->ds->eof = true;
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
if (!in->last_eof) {
if (in->wakeup_cb)
in->wakeup_cb(in->wakeup_cb_ctx);
pthread_cond_signal(&in->wakeup);
MP_VERBOSE(in, "EOF reached.\n");
if (!in->seeking) {
if (eof) {
for (int n = 0; n < in->num_streams; n++)
in->streams[n]->ds->eof = true;
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
if (!in->last_eof) {
if (in->wakeup_cb)
in->wakeup_cb(in->wakeup_cb_ctx);
pthread_cond_signal(&in->wakeup);
MP_VERBOSE(in, "EOF reached.\n");
}
}
in->eof = in->last_eof = eof;
}
in->eof = in->last_eof = eof;
return true;
}
@@ -550,42 +663,6 @@ static void ds_get_packets(struct demux_stream *ds)
}
}
// An obscure mechanism to get stream switching to be executed faster.
// On a switch, it seeks back, and then grabs all packets that were
// "missing" from the packet queue of the newly selected stream.
static void start_refreshing(struct demux_internal *in)
{
struct demuxer *demux = in->d_thread;
in->start_refresh_seek = false;
double start_ts = MP_NOPTS_VALUE;
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
}
if (start_ts == MP_NOPTS_VALUE || !demux->desc->seek || !demux->seekable ||
demux->partially_seekable || !demux->allow_refresh_seeks)
return;
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
// Streams which didn't read any packets yet can return all packets,
// or they'd be stuck forever; affects newly selected streams too.
if (ds->last_pos != -1)
ds->refreshing = true;
}
pthread_mutex_unlock(&in->lock);
// Seek back to player's current position, with a small offset added.
in->d_thread->desc->seek(in->d_thread, start_ts - 1.0, SEEK_BACKWARD | SEEK_HR);
pthread_mutex_lock(&in->lock);
}
static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
@@ -603,9 +680,6 @@ static void execute_trackswitch(struct demux_internal *in)
&(int){any_selected});
pthread_mutex_lock(&in->lock);
if (in->start_refresh_seek)
start_refreshing(in);
}
static void execute_seek(struct demux_internal *in)
@@ -613,6 +687,7 @@ static void execute_seek(struct demux_internal *in)
int flags = in->seek_flags;
double pts = in->seek_pts;
in->seeking = false;
in->initial_state = false;
pthread_mutex_unlock(&in->lock);
@@ -853,17 +928,18 @@ static int decode_float(char *str, float *out)
return 0;
}
static int decode_gain(demuxer_t *demuxer, const char *tag, float *out)
static int decode_gain(struct mp_log *log, struct mp_tags *tags,
const char *tag, float *out)
{
char *tag_val = NULL;
float dec_val;
tag_val = mp_tags_get_str(demuxer->metadata, tag);
tag_val = mp_tags_get_str(tags, tag);
if (!tag_val)
return -1;
if (decode_float(tag_val, &dec_val)) {
mp_msg(demuxer->log, MSGL_ERR, "Invalid replaygain value\n");
if (decode_float(tag_val, &dec_val) < 0) {
mp_msg(log, MSGL_ERR, "Invalid replaygain value\n");
return -1;
}
@@ -871,62 +947,68 @@ static int decode_gain(demuxer_t *demuxer, const char *tag, float *out)
return 0;
}
static int decode_peak(demuxer_t *demuxer, const char *tag, float *out)
static int decode_peak(struct mp_log *log, struct mp_tags *tags,
const char *tag, float *out)
{
char *tag_val = NULL;
float dec_val;
*out = 1.0;
tag_val = mp_tags_get_str(demuxer->metadata, tag);
tag_val = mp_tags_get_str(tags, tag);
if (!tag_val)
return 0;
if (decode_float(tag_val, &dec_val))
return 0;
if (dec_val == 0.0)
return 0;
if (decode_float(tag_val, &dec_val) < 0 || dec_val <= 0.0)
return -1;
*out = dec_val;
return 0;
}
static void apply_replaygain(demuxer_t *demuxer, struct replaygain_data *rg)
static struct replaygain_data *decode_rgain(struct mp_log *log,
struct mp_tags *tags)
{
struct replaygain_data rg = {0};
if (decode_gain(log, tags, "REPLAYGAIN_TRACK_GAIN", &rg.track_gain) >= 0 &&
decode_peak(log, tags, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) >= 0)
{
if (decode_gain(log, tags, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) < 0 ||
decode_peak(log, tags, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak) < 0)
{
rg.album_gain = rg.track_gain;
rg.album_peak = rg.track_peak;
}
return talloc_memdup(NULL, &rg, sizeof(rg));
}
if (decode_gain(log, tags, "REPLAYGAIN_GAIN", &rg.track_gain) >= 0 &&
decode_peak(log, tags, "REPLAYGAIN_PEAK", &rg.track_peak) >= 0)
{
rg.album_gain = rg.track_gain;
rg.album_peak = rg.track_peak;
return talloc_memdup(NULL, &rg, sizeof(rg));
}
return NULL;
}
static void demux_update_replaygain(demuxer_t *demuxer)
{
struct demux_internal *in = demuxer->in;
for (int n = 0; n < in->num_streams; n++) {
struct sh_stream *sh = in->streams[n];
if (sh->type == STREAM_AUDIO && !sh->codec->replaygain_data) {
MP_VERBOSE(demuxer, "Replaygain: Track=%f/%f Album=%f/%f\n",
rg->track_gain, rg->track_peak,
rg->album_gain, rg->album_peak);
sh->codec->replaygain_data = talloc_memdup(in, rg, sizeof(*rg));
struct replaygain_data *rg = decode_rgain(demuxer->log, sh->tags);
if (!rg)
rg = decode_rgain(demuxer->log, demuxer->metadata);
if (rg)
sh->codec->replaygain_data = talloc_steal(in, rg);
}
}
}
static void demux_export_replaygain(demuxer_t *demuxer)
{
struct replaygain_data rg = {0};
if (!decode_gain(demuxer, "REPLAYGAIN_TRACK_GAIN", &rg.track_gain) &&
!decode_peak(demuxer, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) &&
!decode_gain(demuxer, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) &&
!decode_peak(demuxer, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak))
{
apply_replaygain(demuxer, &rg);
}
if (!decode_gain(demuxer, "REPLAYGAIN_GAIN", &rg.track_gain) &&
!decode_peak(demuxer, "REPLAYGAIN_PEAK", &rg.track_peak))
{
rg.album_gain = rg.track_gain;
rg.album_peak = rg.track_peak;
apply_replaygain(demuxer, &rg);
}
}
// Copy all fields from src to dst, depending on event flags.
static void demux_copy(struct demuxer *dst, struct demuxer *src)
{
@@ -947,15 +1029,29 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->partially_seekable = src->partially_seekable;
dst->filetype = src->filetype;
dst->ts_resets_possible = src->ts_resets_possible;
dst->allow_refresh_seeks = src->allow_refresh_seeks;
dst->fully_read = src->fully_read;
dst->start_time = src->start_time;
dst->priv = src->priv;
}
if (src->events & DEMUX_EVENT_METADATA) {
talloc_free(dst->metadata);
dst->metadata = mp_tags_dup(dst, src->metadata);
if (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]);
dst->update_stream_tags[n] =
talloc_steal(dst->update_stream_tags, src->update_stream_tags[n]);
src->update_stream_tags[n] = NULL;
}
}
dst->events |= src->events;
src->events = 0;
}
@@ -977,8 +1073,6 @@ void demux_changed(demuxer_t *demuxer, int events)
if (demuxer->events & DEMUX_EVENT_INIT)
demuxer_sort_chapters(demuxer);
if (demuxer->events & (DEMUX_EVENT_METADATA | DEMUX_EVENT_STREAMS))
demux_export_replaygain(demuxer);
demux_copy(in->d_buffer, demuxer);
@@ -1001,8 +1095,28 @@ void demux_update(demuxer_t *demuxer)
demux_copy(demuxer, in->d_buffer);
demuxer->events |= in->events;
in->events = 0;
if (in->stream_metadata && (demuxer->events & DEMUX_EVENT_METADATA))
mp_tags_merge(demuxer->metadata, in->stream_metadata);
if (demuxer->events & DEMUX_EVENT_METADATA) {
int num_streams = MPMIN(in->num_streams, demuxer->num_update_stream_tags);
for (int n = 0; n < num_streams; n++) {
struct mp_tags *tags = demuxer->update_stream_tags[n];
demuxer->update_stream_tags[n] = NULL;
if (tags) {
struct sh_stream *sh = in->streams[n];
talloc_free(sh->tags);
sh->tags = talloc_steal(sh, tags);
}
}
// Often useful audio-only files, which have metadata in the audio track
// metadata instead of the main metadata (especially OGG).
if (in->num_streams == 1)
mp_tags_merge(demuxer->metadata, in->streams[0]->tags);
if (in->stream_metadata)
mp_tags_merge(demuxer->metadata, in->stream_metadata);
}
if (demuxer->events & (DEMUX_EVENT_METADATA | DEMUX_EVENT_STREAMS))
demux_update_replaygain(demuxer);
pthread_mutex_unlock(&in->lock);
}
@@ -1074,6 +1188,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.min_secs = demuxer->opts->demuxer_min_secs,
.max_packs = demuxer->opts->demuxer_max_packs,
.max_bytes = demuxer->opts->demuxer_max_bytes,
.initial_state = true,
};
pthread_mutex_init(&in->lock, NULL);
pthread_cond_init(&in->wakeup, NULL);
@@ -1277,18 +1392,6 @@ int demux_seek(demuxer_t *demuxer, double seek_pts, int flags)
return 1;
}
// Enable doing a "refresh seek" on the next stream switch.
// Note that this by design does not disable ongoing refresh seeks, and
// does not affect previous stream switch commands (even if they were
// asynchronous).
void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled)
{
struct demux_internal *in = demuxer->in;
pthread_mutex_lock(&in->lock);
in->refresh_seeks_enabled = enabled;
pthread_mutex_unlock(&in->lock);
}
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id)
{
@@ -1301,19 +1404,22 @@ struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
return NULL;
}
// Set whether the given stream should return packets.
// ref_pts is used only if the stream is enabled. Then it serves as approximate
// start pts for this stream (in the worst case it is ignored).
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected)
double ref_pts, bool selected)
{
struct demux_internal *in = demuxer->in;
pthread_mutex_lock(&in->lock);
// don't flush buffers if stream is already selected / unselected
if (stream->ds->selected != selected) {
stream->ds->selected = selected;
stream->ds->active = false;
ds_flush(stream->ds);
in->tracks_switched = true;
if (selected && in->refresh_seeks_enabled)
in->start_refresh_seek = true;
stream->ds->need_refresh = selected && !in->initial_state;
if (stream->ds->need_refresh)
in->ref_pts = MP_ADD_PTS(ref_pts, -in->ts_offset);
if (in->threading) {
pthread_cond_signal(&in->wakeup);
} else {
+7 -9
View File
@@ -178,12 +178,6 @@ typedef struct demuxer {
double start_time;
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
// Enable fast track switching hacks. This requires from the demuxer:
// - seeking is somewhat reliable; packet contents must not change
// - packet position (demux_packet.pos) is set, not negative, unique, and
// monotonically increasing
// - seeking leaves packet positions invariant
bool allow_refresh_seeks;
// The file data was fully read, and there is no need to keep the stream
// open, keep the cache active, or to run the demuxer thread. Generating
// packets is not slow either (unlike e.g. libavdevice pseudo-demuxers).
@@ -216,7 +210,10 @@ typedef struct demuxer {
struct mp_log *log, *glog;
struct demuxer_params *params;
struct demux_internal *in; // internal to demux.c
// internal to demux.c
struct demux_internal *in;
struct mp_tags **update_stream_tags;
int num_update_stream_tags;
// Since the demuxer can run in its own thread, and the stream is not
// thread-safe, only the demuxer is allowed to access the stream directly.
@@ -265,13 +262,12 @@ bool demux_cancel_test(struct demuxer *demuxer);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags);
void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled);
void demux_set_ts_offset(struct demuxer *demuxer, double offset);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected);
double ref_pts, bool selected);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
void demuxer_help(struct mp_log *log);
@@ -280,6 +276,8 @@ int demuxer_add_attachment(struct demuxer *demuxer, char *name,
char *type, void *data, size_t data_size);
int demuxer_add_chapter(demuxer_t *demuxer, char *name,
double pts, uint64_t demuxer_id);
void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh,
struct mp_tags *tags);
double demuxer_get_time_length(struct demuxer *demuxer);
+1 -1
View File
@@ -57,7 +57,7 @@ static void reselect_streams(demuxer_t *demuxer)
for (int n = 0; n < MPMIN(num_slave, p->num_streams); n++) {
if (p->streams[n]) {
demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
demux_stream_is_selected(p->streams[n]));
MP_NOPTS_VALUE, demux_stream_is_selected(p->streams[n]));
}
}
}
+22 -45
View File
@@ -168,7 +168,6 @@ typedef struct lavf_priv {
int num_streams;
int cur_program;
char *mime_type;
bool merge_track_metadata;
double seek_delay;
} lavf_priv_t;
@@ -502,7 +501,7 @@ static void select_tracks(struct demuxer *demuxer, int start)
}
}
static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
static void export_replaygain(demuxer_t *demuxer, struct sh_stream *sh,
AVStream *st)
{
for (int i = 0; i < st->nb_side_data; i++) {
@@ -528,7 +527,10 @@ static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
rgain->album_peak = (av_rgain->album_peak != 0.0) ?
av_rgain->album_peak / 100000.0f : 1.0;
c->replaygain_data = rgain;
// This must be run only before the stream was added, otherwise there
// will be race conditions with accesses from the user thread.
assert(!sh->ds);
sh->codec->replaygain_data = rgain;
}
}
@@ -575,7 +577,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
delay = lavc_delay / (double)codec->sample_rate;
priv->seek_delay = MPMAX(priv->seek_delay, delay);
export_replaygain(demuxer, sh->codec, st);
export_replaygain(demuxer, sh, st);
break;
}
@@ -681,6 +683,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
if (!sh->title && sh->hls_bitrate > 0)
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS);
mp_tags_copy_from_av_dictionary(sh->tags, st->metadata);
demux_add_sh_stream(demuxer, sh);
}
@@ -703,14 +706,14 @@ static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
priv->avfc->event_flags = 0;
demux_changed(demuxer, DEMUX_EVENT_METADATA);
}
if (priv->merge_track_metadata) {
for (int n = 0; n < priv->num_streams; n++) {
AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
mp_tags_copy_from_av_dictionary(demuxer->metadata, st->metadata);
st->event_flags = 0;
demux_changed(demuxer, DEMUX_EVENT_METADATA);
}
for (int n = 0; n < priv->num_streams; n++) {
AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
st->event_flags = 0;
struct mp_tags *tags = talloc_zero(NULL, struct mp_tags);
mp_tags_copy_from_av_dictionary(tags, st->metadata);
demux_set_stream_tags(demuxer, priv->streams[n], tags);
}
}
}
@@ -846,16 +849,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
add_new_streams(demuxer);
// Often useful with OGG audio-only files, which have metadata in the audio
// track metadata instead of the main metadata.
if (demux_get_num_stream(demuxer) == 1) {
priv->merge_track_metadata = true;
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->streams[n]->metadata);
}
}
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
update_metadata(demuxer, NULL);
@@ -865,7 +858,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
demuxer->allow_refresh_seeks = matches_avinputformat_name(priv, "mp4");
demuxer->fully_read = priv->format_hack.fully_read;
return 0;
@@ -953,30 +945,15 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
seek_pts_av = seek_pts * AV_TIME_BASE;
}
int r;
if (!priv->avfc->iformat->read_seek2) {
// Normal seeking.
int r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) {
// When seeking before the beginning of the file, and seeking fails,
// try again without the backwards flag to make it seek to the
// beginning.
avsflags &= ~AVSEEK_FLAG_BACKWARD;
r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) {
// When seeking before the beginning of the file, and seeking fails,
// try again without the backwards flag to make it seek to the
// beginning.
avsflags &= ~AVSEEK_FLAG_BACKWARD;
r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
}
} else {
// av_seek_frame() won't work. Use "new" seeking API. We don't use this
// API by default, because there are some major issues.
// Set max_ts==ts, so that demuxing starts from an earlier position in
// the worst case.
r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
seek_pts_av, seek_pts_av, avsflags);
// Similar issue as in the normal seeking codepath.
if (r < 0) {
r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
seek_pts_av, INT64_MAX, avsflags);
}
}
if (r < 0) {
char buf[180];
av_strerror(r, buf, sizeof(buf));
+3 -16
View File
@@ -65,23 +65,10 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
char **files = NULL;
int num_files = 0;
for (;;) {
struct archive_entry *entry;
int r = archive_read_next_header(mpa->arch, &entry);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
MP_ERR(demuxer, "%s\n", archive_error_string(mpa->arch));
if (r < ARCHIVE_WARN)
break;
if (archive_entry_filetype(entry) != AE_IFREG)
continue;
const char *fn = archive_entry_pathname(entry);
// Some archives may have no filenames.
if (!fn)
fn = talloc_asprintf(mpa, "mpv_unknown#%d\n", num_files);
while (mp_archive_next_entry(mpa)) {
// stream_libarchive.c does the real work
char *f = talloc_asprintf(mpa, "archive://%s|%s", prefix, fn);
char *f = talloc_asprintf(mpa, "archive://%s|%s", prefix,
mpa->entry_filename);
MP_TARRAY_APPEND(mpa, files, num_files, f);
}
+16 -5
View File
@@ -93,6 +93,7 @@ typedef struct mkv_content_encoding {
typedef struct mkv_track {
int tnum;
uint64_t uid;
char *name;
struct sh_stream *stream;
@@ -595,6 +596,7 @@ static void parse_trackentry(struct demuxer *demuxer,
} else {
MP_ERR(demuxer, "Missing track number!\n");
}
track->uid = entry->track_uid;
if (entry->name) {
track->name = talloc_strdup(track, entry->name);
@@ -986,9 +988,6 @@ static void process_tags(demuxer_t *demuxer)
for (int i = 0; i < tags->n_tag; i++) {
struct ebml_tag tag = tags->tag[i];
if (tag.targets.target_track_uid || tag.targets.target_attachment_uid)
continue;
struct mp_tags *dst = NULL;
if (tag.targets.target_chapter_uid) {
@@ -1009,6 +1008,19 @@ static void process_tags(demuxer_t *demuxer)
break;
}
}
} else if (tag.targets.target_track_uid) {
for (int n = 0; n < mkv_d->num_tracks; n++) {
if (mkv_d->tracks[n]->uid ==
tag.targets.target_track_uid)
{
struct sh_stream *sh = mkv_d->tracks[n]->stream;
if (sh)
dst = sh->tags;
break;
}
}
} else if (tag.targets.target_attachment_uid) {
/* ignore */
} else {
dst = demuxer->metadata;
}
@@ -1924,10 +1936,9 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
MP_VERBOSE(demuxer, "All headers are parsed!\n");
process_tags(demuxer);
display_create_tracks(demuxer);
add_coverart(demuxer);
demuxer->allow_refresh_seeks = true;
process_tags(demuxer);
probe_first_timestamp(demuxer);
if (opts->demux_mkv->probe_duration)
+14 -5
View File
@@ -36,7 +36,7 @@
#include "osdep/endian.h"
struct demux_rawaudio_opts {
struct mp_chmap channels;
struct m_channels channels;
int samplerate;
int aformat;
};
@@ -49,13 +49,13 @@ struct demux_rawaudio_opts {
#define OPT_BASE_STRUCT struct demux_rawaudio_opts
const struct m_sub_options demux_rawaudio_conf = {
.opts = (const m_option_t[]) {
OPT_CHMAP("channels", channels, CONF_MIN, .min = 1),
OPT_CHANNELS("channels", channels, 0, .min = 1),
OPT_INTRANGE("rate", samplerate, 0, 1000, 8 * 48000),
OPT_CHOICE("format", aformat, 0,
({"u8", PCM(0, 0, 8, 0)},
{"s8", PCM(1, 0, 8, 0)},
{"u16le", PCM(0, 0, 16, 0)}, {"u16be", PCM(0, 0, 16, 1)},
{"s16le", PCM(1, 0, 16, 0)}, {"u16be", PCM(1, 0, 16, 1)},
{"s16le", PCM(1, 0, 16, 0)}, {"s16be", PCM(1, 0, 16, 1)},
{"u24le", PCM(0, 0, 24, 0)}, {"u24be", PCM(0, 0, 24, 1)},
{"s24le", PCM(1, 0, 24, 0)}, {"s24be", PCM(1, 0, 24, 1)},
{"u32le", PCM(0, 0, 32, 0)}, {"u32be", PCM(0, 0, 32, 1)},
@@ -75,7 +75,11 @@ const struct m_sub_options demux_rawaudio_conf = {
.size = sizeof(struct demux_rawaudio_opts),
.defaults = &(const struct demux_rawaudio_opts){
// Note that currently, stream_cdda expects exactly these parameters!
.channels = MP_CHMAP_INIT_STEREO,
.channels = {
.set = 1,
.chmaps = (struct mp_chmap[]){ MP_CHMAP_INIT_STEREO, },
.num_chmaps = 1,
},
.samplerate = 44100,
.aformat = PCM(1, 0, 16, 0), // s16le
},
@@ -130,9 +134,14 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
return -1;
if (opts->channels.num_chmaps != 1) {
MP_ERR(demuxer, "Invalid channels option given.\n");
return -1;
}
struct sh_stream *sh = demux_alloc_sh_stream(STREAM_AUDIO);
struct mp_codec_params *c = sh->codec;
c->channels = opts->channels;
c->channels = opts->channels.chmaps[0];
c->force_channels = true;
c->samplerate = opts->samplerate;
+9 -1
View File
@@ -84,7 +84,7 @@ static void reselect_streams(struct demuxer *demuxer)
// This stops demuxer readahead for inactive segments.
if (!p->current || seg->d != p->current->d)
selected = false;
demuxer_select_track(seg->d, sh, selected);
demuxer_select_track(seg->d, sh, MP_NOPTS_VALUE, selected);
}
}
}
@@ -197,6 +197,11 @@ static int d_fill_buffer(struct demuxer *demuxer)
if (pkt->stream < 0)
goto drop;
// for refresh seeks, demux.c prefers monotonically increasing packet pos
// since the packet pos is meaningless anyway for timeline, use it
if (pkt->pos >= 0)
pkt->pos |= (seg->index & 0x7FFFULL) << 48;
struct virtual_stream *vs = &p->streams[pkt->stream];
if (pkt->pts != MP_NOPTS_VALUE && pkt->pts >= seg->end) {
@@ -301,6 +306,9 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
demuxer->metadata = meta->metadata;
demuxer->attachments = meta->attachments;
demuxer->num_attachments = meta->num_attachments;
demuxer->editions = meta->editions;
demuxer->num_editions = meta->num_editions;
demuxer->edition = meta->edition;
int num_streams = demux_get_num_stream(meta);
for (int n = 0; n < num_streams; n++) {
+2
View File
@@ -46,6 +46,8 @@ struct sh_stream {
bool forced_track; // container forced track flag
int hls_bitrate;
struct mp_tags *tags;
bool missing_timestamps;
// stream is a picture (such as album art)
+1 -1
View File
@@ -79,7 +79,7 @@
#> playlist-next # skip to next file
#ENTER playlist-next # skip to next file
#< playlist-prev # skip to previous file
#O no-osd cycle_values osd-level 3 1 # cycle through OSD mode
#O no-osd cycle-values osd-level 3 1 # cycle through OSD mode
#o show-progress
#P show-progress
#I show-text "${filename}" # display filename in osd
+8 -8
View File
@@ -24,19 +24,19 @@ SHARP cycle audio # switch audio streams
BS set speed 1.0 # reset speed to normal
q quit
ESC quit
ENTER playlist_next force # skip to next file
ENTER playlist-next force # skip to next file
p cycle pause
. frame_step # advance one frame and pause
. frame-step # advance one frame and pause
SPACE cycle pause
HOME set playlist-pos 0 # not the same as MPlayer
#END pt_up_step -1
> playlist_next # skip to next file
< playlist_prev # previous
> playlist-next # skip to next file
< playlist-prev # previous
#INS alt_src_step 1
#DEL alt_src_step -1
o osd
I show_text "${filename}" # display filename in osd
P show_progress
I show-text "${filename}" # display filename in osd
P show-progress
z add sub-delay -0.1 # subtract 100 ms delay from subs
x add sub-delay +0.1 # add
9 add volume -1
@@ -57,8 +57,8 @@ d cycle framedrop
D cycle deinterlace # toggle deinterlacer (auto-inserted filter)
r add sub-pos -1 # move subtitles up
t add sub-pos +1 # down
#? sub_step +1 # immediately display next subtitle
#? sub_step -1 # previous
#? sub-step +1 # immediately display next subtitle
#? sub-step -1 # previous
#? add sub-scale +0.1 # increase subtitle font size
#? add sub-scale -0.1 # decrease subtitle font size
f cycle fullscreen
+2 -2
View File
@@ -17,7 +17,7 @@ d cycle framedrop
# changed in mpv 0.7.0
ENTER playlist_next force
ENTER playlist-next force
# changed in mpv 0.6.0
@@ -38,4 +38,4 @@ TAB cycle program
A cycle angle
U stop
o osd
I show_text "${filename}"
I show-text "${filename}"
+1 -1
View File
@@ -257,7 +257,7 @@ static const struct legacy_cmd legacy_cmds[] = {
{"saturation", "add saturation"},
{"hue", "add hue"},
{"switch_vsync", "cycle vsync"},
{"sub_load", "sub_add"},
{"sub_load", "sub-add"},
{"sub_select", "cycle sub"},
{"sub_pos", "add sub-pos"},
{"sub_delay", "add sub-delay"},
+1 -1
View File
@@ -31,7 +31,7 @@ void mp_event_drop_files(struct input_ctx *ictx, int num_files, char **files,
for (int i = 0; i < num_files; i++) {
const char *cmd[] = {
"osd-auto",
"sub_add",
"sub-add",
files[i],
NULL
};
+1 -4
View File
@@ -1247,12 +1247,9 @@ void mp_input_load(struct input_ctx *ictx)
void *tmp = talloc_new(NULL);
char **files = mp_find_all_config_files(tmp, ictx->global, "input.conf");
for (int n = 0; files && files[n]; n++)
config_ok = config_ok | parse_config_file(ictx, files[n], false);
parse_config_file(ictx, files[n], false);
talloc_free(tmp);
}
if (!config_ok) {
MP_VERBOSE(ictx, "Falling back on default (hardcoded) input config\n");
}
if (input_conf->use_alt_gr) {
ictx->using_alt_gr = true;
+1 -2
View File
@@ -133,8 +133,7 @@ static void *client_thread(void *p)
}
if (fds[0].revents & POLLIN) {
char discard[100];
(void)read(pipe_fd, discard, sizeof(discard));
mp_flush_wakeup_pipe(pipe_fd);
while (1) {
mpv_event *event = mpv_wait_event(arg->client, 0);
+12 -8
View File
@@ -215,7 +215,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 21)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 22)
/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
@@ -248,7 +248,7 @@ typedef enum mpv_error {
* making asynchronous requests. (Bugs in the client API implementation
* could also trigger this, e.g. if events become "lost".)
*/
MPV_ERROR_EVENT_QUEUE_FULL = -1,
MPV_ERROR_EVENT_QUEUE_FULL = -1,
/**
* Memory allocation failed.
*/
@@ -299,7 +299,7 @@ typedef enum mpv_error {
*/
MPV_ERROR_COMMAND = -12,
/**
* Generic error on loading (used with mpv_event_end_file.error).
* Generic error on loading (usually used with mpv_event_end_file.error).
*/
MPV_ERROR_LOADING_FAILED = -13,
/**
@@ -329,7 +329,11 @@ typedef enum mpv_error {
/**
* The API function which was called is a stub only.
*/
MPV_ERROR_NOT_IMPLEMENTED = -19
MPV_ERROR_NOT_IMPLEMENTED = -19,
/**
* Unspecified error.
*/
MPV_ERROR_GENERIC = -20
} mpv_error;
/**
@@ -660,7 +664,7 @@ typedef enum mpv_format {
MPV_FORMAT_NODE_MAP = 8,
/**
* A raw, untyped byte array. Only used only with mpv_node, and only in
* some very special situations. (Currently, only for the screenshot_raw
* some very special situations. (Currently, only for the screenshot-raw
* command.)
*/
MPV_FORMAT_BYTE_ARRAY = 9
@@ -1148,13 +1152,13 @@ typedef enum mpv_event_id {
* @deprecated This was used internally with the internal "script_dispatch"
* command to dispatch keyboard and mouse input for the OSC.
* It was never useful in general and has been completely
* replaced with "script_binding".
* replaced with "script-binding".
* This event never happens anymore, and is included in this
* header only for compatibility.
*/
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
/**
* Triggered by the script_message input command. The command uses the
* Triggered by the script-message input command. The command uses the
* first argument of the command as client name (see mpv_client_name()) to
* dispatch the message, and passes along all arguments starting from the
* second argument as strings.
@@ -1446,7 +1450,7 @@ int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable);
* required log level for a message to be received with MPV_EVENT_LOG_MESSAGE.
*
* @param min_level Minimal log level as string. Valid log levels:
* no fatal error warn info status v debug trace
* no fatal error warn info v debug trace
* The value "no" disables all messages. This is the default.
* An exception is the value "terminal-default", which uses the
* log level as set by the "--msg-level" option. This works
+1
View File
@@ -37,6 +37,7 @@ mpv_set_property
mpv_set_property_async
mpv_set_property_string
mpv_set_wakeup_callback
mpv_stream_cb_add_ro
mpv_suspend
mpv_terminate_destroy
mpv_unobserve_property
+230
View File
@@ -0,0 +1,230 @@
/* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Note: the client API is licensed under ISC (see above) to ease
* interoperability with other licenses. But keep in mind that the
* mpv core is still mostly GPLv2+. It's up to lawyers to decide
* whether applications using this API are affected by the GPL.
* One argument against this is that proprietary applications
* using mplayer in slave mode is apparently tolerated, and this
* API is basically equivalent to slave mode.
*/
#ifndef MPV_CLIENT_API_STREAM_CB_H_
#define MPV_CLIENT_API_STREAM_CB_H_
#include "client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Warning: this API is not stable yet.
*
* Overview
* --------
*
* This API can be used to make mpv read from a stream with a custom
* implementation. This interface is inspired by funopen on BSD and
* fopencookie on linux. The stream is backed by user-defined callbacks
* which can implement customized open, read, seek, size and close behaviors.
*
* Usage
* -----
*
* Register your stream callbacks with the mpv_stream_cb_add_ro() function. You
* have to provide a mpv_stream_cb_open_ro_fn callback to it (open_fn argument).
*
* Once registered, you can `loadfile myprotocol://myfile`. Your open_fn will be
* invoked with the URI and you must fill out the provided mpv_stream_cb_info
* struct. This includes your stream callbacks (like read_fn), and an opaque
* cookie, which will be passed as the first argument to all the remaining
* stream callbacks.
*
* Note that your custom callbacks must not invoke libmpv APIs as that would
* cause a deadlock.
*
* Stream lifetime
* ---------------
*
* A stream remains valid until its close callback has been called. It's up to
* libmpv to call the close callback, and the libmpv user cannot close it
* directly with the stream_cb API.
*
* For example, if you consider your custom stream to become suddenly invalid
* (maybe because the underlying stream died), libmpv will continue using your
* stream. All you can do is returning errors from each callback, until libmpv
* gives up and closes it.
*
* Protocol registration and lifetime
* ----------------------------------
*
* Protocols remain registered until the mpv instance is terminated. This means
* in particular that it can outlive the mpv_handle that was used to register
* it, but once mpv_terminate_destroy() is called, your registered callbacks
* will not be called again.
*
* Protocol unregistration is finished after the mpv core has been destroyed
* (e.g. after mpv_terminate_destroy() has returned).
*
* If you do not call mpv_terminate_destroy() yourself (e.g. plugin-style code),
* you will have to deal with the registration or even streams outliving your
* code. Here are some possible ways to do this:
* - call mpv_terminate_destroy(), which destroys the core, and will make sure
* all streams are closed once this function returns
* - you refcount all resources your stream "cookies" reference, so that it
* doesn't matter if streams live longer than expected
* - create "cancellation" semantics: after your protocol has been unregistered,
* notify all your streams that are still opened, and make them drop all
* referenced resources - then return errors from the stream callbacks as
* long as the stream is still opened
*
*/
/**
* Read callback used to implement a custom stream. The semantics of the
* callback match read(2) in blocking mode. Short reads are allowed (you can
* return less bytes than requested, and libmpv will retry reading the rest
* with a nother call). If no data can be immediately read, the callback must
* block until there is new data. A return of 0 will be interpreted as final
* EOF, although libmpv might retry the read, or seek to a different position.
*
* @param cookie opaque cookie identifying the stream,
* returned from mpv_stream_cb_open_fn
* @param buf buffer to read data into
* @param size of the buffer
* @return number of bytes read into the buffer
* @return 0 on EOF
* @return -1 on error
*/
typedef int64_t (*mpv_stream_cb_read_fn)(void *cookie, char *buf, uint64_t nbytes);
/**
* Seek callback used to implement a custom stream.
*
* Note that mpv will issue a seek to position 0 immediately after opening. This
* is used to test whether the stream is seekable (since seekability might
* depend on the URI contents, not just the protocol). Return
* MPV_ERROR_UNSUPPORTED if seeking is not implemented for this stream. This
* seek also servies to establish the fact that streams start at position 0.
*
* This callback can be NULL, in which it behaves as if always returning
* MPV_ERROR_UNSUPPORTED.
*
* @param cookie opaque cookie identifying the stream,
* returned from mpv_stream_cb_open_fn
* @param offset target absolut stream position
* @return the resulting offset of the stream
* MPV_ERROR_UNSUPPORTED or MPV_ERROR_GENERIC if the seek failed
*/
typedef int64_t (*mpv_stream_cb_seek_fn)(void *cookie, int64_t offset);
/**
* Size callback used to implement a custom stream.
*
* Return MPV_ERROR_UNSUPPORTED if no size is known.
*
* This callback can be NULL, in which it behaves as if always returning
* MPV_ERROR_UNSUPPORTED.
*
* @param cookie opaque cookie identifying the stream,
* returned from mpv_stream_cb_open_fn
* @return the total size in bytes of the stream
*/
typedef int64_t (*mpv_stream_cb_size_fn)(void *cookie);
/**
* Close callback used to implement a custom stream.
*
* @param cookie opaque cookie identifying the stream,
* returned from mpv_stream_cb_open_fn
*/
typedef void (*mpv_stream_cb_close_fn)(void *cookie);
/**
* See mpv_stream_cb_open_ro_fn callback.
*/
typedef struct mpv_stream_cb_info {
/**
* Opaque user-provided value, which will be passed to the other callbacks.
* The close callback will be called to release the cookie. It is not
* interpreted by mpv. It doesn't even need to be a valid pointer.
*
* The user sets this in the mpv_stream_cb_open_ro_fn callback.
*/
void *cookie;
/**
* Callbacks set by the user in the mpv_stream_cb_open_ro_fn callback. Some
* of them are optional, and can be left unset.
*
* The following callbacks are mandatory: read_fn, close_fn
*/
mpv_stream_cb_read_fn read_fn;
mpv_stream_cb_seek_fn seek_fn;
mpv_stream_cb_size_fn size_fn;
mpv_stream_cb_close_fn close_fn;
} mpv_stream_cb_info;
/**
* Open callback used to implement a custom read-only (ro) stream. The user
* must set the callback fields in the passed info struct. The cookie field
* also can be set to store state associated to the stream instance.
*
* Note that the info struct is valid only for the duration of this callback.
* You can't change the callbacks or the pointer to the cookie at a later point.
*
* Each stream instance created by the open callback can have different
* callbacks.
*
* The close_fn callback will terminate the stream instance. The pointers to
* your callbacks and cookie will be discarded, and the callbacks will not be
* called again.
*
* @param user_data opaque user data provided via mpv_stream_cb_add()
* @param uri name of the stream to be opened (with protocol prefix)
* @param info fields which the user should fill
* @return opaque cookie identifing the newly opened stream
* @return 0 on success, MPV_ERROR_LOADING_FAILED if the URI cannot be opened.
*/
typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri,
mpv_stream_cb_info *info);
/**
* Add a custom stream protocol. This will register a protocol handler under
* the given protocol prefix, and invoke the given callbacks if an URI with the
* matching protocol prefix is opened.
*
* The "ro" is for read-only - only read-only streams can be registered with
* this function.
*
* The callback remains registered until the mpv core is registered.
*
* If a custom stream with the same name is already registered, then the
* MPV_ERROR_INVALID_PARAMETER error is returned.
*
* @param protocol protocol prefix, for example "foo" for "foo://" URIs
* @param user_data opaque pointer passed into the mpv_stream_cb_open_fn
* callback.
* @return error code
*/
int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data,
mpv_stream_cb_open_ro_fn open_fn);
#ifdef __cplusplus
}
#endif
#endif
+1 -1
View File
@@ -116,7 +116,7 @@ int bstr_validate_utf8(struct bstr s);
// talloc, with talloc_ctx as parent.
struct bstr bstr_sanitize_utf8_latin1(void *talloc_ctx, struct bstr s);
// Return the text before the occurance of a character, and return it. Change
// Return the text before the occurrence of a character, and return it. Change
// *rest to point to the text following this character. (rest can be NULL.)
struct bstr bstr_splitchar(struct bstr str, struct bstr *rest, const char c);
+7
View File
@@ -499,6 +499,13 @@ struct m_config_option *m_config_get_co(const struct m_config *config,
co->warning_was_printed = true;
}
return NULL;
} else if (co->opt->deprecation_message) {
if (!co->warning_was_printed) {
MP_WARN(config, "Warning: option %s%s is deprecated "
"and might be removed in the future (%s).\n",
prefix, co->name, co->opt->deprecation_message);
co->warning_was_printed = true;
}
}
return co;
}
+85 -25
View File
@@ -2287,51 +2287,111 @@ const m_option_type_t m_option_type_afmt = {
#include "audio/chmap.h"
static int parse_chmap(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
static int parse_channels(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{
// min>0: at least min channels, min=0: empty ok
int min_ch = (opt->flags & M_OPT_MIN) ? opt->min : 1;
assert(min_ch >= 0);
// see OPT_CHANNELS for semantics.
bool limited = opt->min;
struct m_channels res = {0};
if (bstr_equals0(param, "help")) {
mp_chmap_print_help(log);
if (!limited) {
mp_info(log, "\nOther values:\n"
" auto-safe\n");
}
return M_OPT_EXIT - 1;
}
if (param.len == 0 && min_ch >= 1)
return M_OPT_MISSING_PARAM;
struct mp_chmap res = {0};
if (!mp_chmap_from_str(&res, param)) {
mp_err(log, "Error parsing channel layout: %.*s\n", BSTR_P(param));
return M_OPT_INVALID;
bool auto_safe = bstr_equals0(param, "auto-safe");
if (bstr_equals0(param, "auto") || bstr_equals0(param, "empty") || auto_safe) {
if (limited) {
mp_err(log, "Disallowed parameter.\n");
return M_OPT_INVALID;
}
param.len = 0;
res.set = true;
res.auto_safe = auto_safe;
}
if (!mp_chmap_is_valid(&res) && !(min_ch == 0 && mp_chmap_is_empty(&res))) {
mp_err(log, "Invalid channel layout: %.*s\n", BSTR_P(param));
return M_OPT_INVALID;
while (param.len) {
bstr item;
if (limited) {
item = param;
param.len = 0;
} else {
bstr_split_tok(param, ",", &item, &param);
}
struct mp_chmap map = {0};
if (!mp_chmap_from_str(&map, item) || !mp_chmap_is_valid(&map)) {
mp_err(log, "Invalid channel layout: %.*s\n", BSTR_P(item));
talloc_free(res.chmaps);
return M_OPT_INVALID;
}
MP_TARRAY_APPEND(NULL, res.chmaps, res.num_chmaps, map);
res.set = true;
}
if (dst)
*(struct mp_chmap *)dst = res;
if (dst) {
*(struct m_channels *)dst = res;
} else {
talloc_free(res.chmaps);
}
return 1;
}
static char *print_chmap(const m_option_t *opt, const void *val)
static char *print_channels(const m_option_t *opt, const void *val)
{
const struct mp_chmap *chmap = val;
return talloc_strdup(NULL, mp_chmap_to_str(chmap));
const struct m_channels *ch = val;
if (!ch->set)
return talloc_strdup(NULL, "");
if (ch->auto_safe)
return talloc_strdup(NULL, "auto-safe");
if (ch->num_chmaps > 0) {
char *res = talloc_strdup(NULL, "");
for (int n = 0; n < ch->num_chmaps; n++) {
if (n > 0)
res = talloc_strdup_append(res, ",");
res = talloc_strdup_append(res, mp_chmap_to_str(&ch->chmaps[n]));
}
return res;
}
return talloc_strdup(NULL, "auto");
}
static void free_channels(void *src)
{
if (!src)
return;
const m_option_type_t m_option_type_chmap = {
struct m_channels *ch = src;
talloc_free(ch->chmaps);
*ch = (struct m_channels){0};
}
static void copy_channels(const m_option_t *opt, void *dst, const void *src)
{
if (!(dst && src))
return;
struct m_channels *ch = dst;
free_channels(dst);
*ch = *(struct m_channels *)src;
ch->chmaps =
talloc_memdup(NULL, ch->chmaps, sizeof(ch->chmaps[0]) * ch->num_chmaps);
}
const m_option_type_t m_option_type_channels = {
.name = "Audio channels or channel map",
.size = sizeof(struct mp_chmap),
.parse = parse_chmap,
.print = print_chmap,
.copy = copy_opt,
.size = sizeof(struct m_channels),
.parse = parse_channels,
.print = print_channels,
.copy = copy_channels,
.free = free_channels,
};
static int parse_timestring(struct bstr str, double *time, char endchar)
+18 -6
View File
@@ -61,7 +61,7 @@ extern const m_option_type_t m_option_type_afmt;
extern const m_option_type_t m_option_type_color;
extern const m_option_type_t m_option_type_geometry;
extern const m_option_type_t m_option_type_size_box;
extern const m_option_type_t m_option_type_chmap;
extern const m_option_type_t m_option_type_channels;
extern const m_option_type_t m_option_type_node;
// Used internally by m_config.c
@@ -98,6 +98,13 @@ struct m_geometry {
void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh,
int scrw, int scrh, struct m_geometry *gm);
struct m_channels {
bool set : 1;
bool auto_safe : 1;
struct mp_chmap *chmaps;
int num_chmaps;
};
struct m_obj_desc {
// Name which will be used in the option string
const char *name;
@@ -218,7 +225,7 @@ union m_option_value {
struct m_color color;
struct m_geometry geometry;
struct m_geometry size_box;
struct mp_chmap chmap;
struct m_channels channels;
};
////////////////////////////////////////////////////////////////////////////
@@ -333,6 +340,10 @@ struct m_option {
// Initialize variable to given default before parsing options
const void *defval;
// Print a warning when this option is used (for options with no direct
// replacement.)
const char *deprecation_message;
};
@@ -394,7 +405,7 @@ struct m_option {
// Dynamic data type.
/** This flag indicates that the data is dynamically allocated (m_option::p
* points to a pointer). It enables a little hack in the \ref Config wich
* points to a pointer). It enables a little hack in the \ref Config which
* replaces the initial value of such variables with a dynamic copy in case
* the initial value is statically allocated (pretty common with strings).
*/
@@ -622,9 +633,10 @@ extern const char m_option_path_separator;
#define OPT_AUDIOFORMAT(...) \
OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_type_afmt)
#define OPT_CHMAP(...) \
OPT_GENERAL(struct mp_chmap, __VA_ARGS__, .type = &m_option_type_chmap)
// If .min==1, then passing auto is disallowed, but "" is still accepted, and
// limit channel list to 1 item.
#define OPT_CHANNELS(...) \
OPT_GENERAL(struct m_channels, __VA_ARGS__, .type = &m_option_type_channels)
#define M_CHOICES(choices) \
.priv = (void *)&(const struct m_opt_choice_alternatives[]){ \
+18 -12
View File
@@ -40,7 +40,6 @@
#include "video/csputils.h"
#include "video/hwdec.h"
#include "sub/osd.h"
#include "audio/mixer.h"
#include "audio/filter/af.h"
#include "audio/decode/dec_audio.h"
#include "player/core.h"
@@ -85,6 +84,7 @@ const struct m_opt_choice_alternatives mp_hwdec_names[] = {
{"auto-copy", HWDEC_AUTO_COPY},
{"vdpau", HWDEC_VDPAU},
{"videotoolbox",HWDEC_VIDEOTOOLBOX},
{"videotoolbox-copy",HWDEC_VIDEOTOOLBOX_COPY},
{"vaapi", HWDEC_VAAPI},
{"vaapi-copy", HWDEC_VAAPI_COPY},
{"dxva2", HWDEC_DXVA2},
@@ -279,7 +279,7 @@ const m_option_t mp_opts[] = {
// force video/audio rate:
OPT_DOUBLE("fps", force_fps, CONF_MIN, .min = 0),
OPT_INTRANGE("audio-samplerate", force_srate, 0, 1000, 16*48000),
OPT_CHMAP("audio-channels", audio_output_channels, CONF_MIN, .min = 0),
OPT_CHANNELS("audio-channels", audio_output_channels, 0),
OPT_AUDIOFORMAT("audio-format", audio_output_format, 0),
OPT_FLAG("audio-normalize-downmix", audio_normalize, 0),
OPT_DOUBLE("speed", playback_speed, M_OPT_RANGE | M_OPT_FIXED,
@@ -288,7 +288,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("audio-pitch-correction", pitch_correction, 0),
// set a-v distance
OPT_FLOATRANGE("audio-delay", audio_delay, 0, -100.0, 100.0),
OPT_FLOAT("audio-delay", audio_delay, 0),
// ------------------------- codec/vfilter options --------------------
@@ -342,6 +342,7 @@ const m_option_t mp_opts[] = {
OPT_PATHLIST("sub-paths", sub_paths, 0),
OPT_PATHLIST("audio-file-paths", audiofile_paths, 0),
OPT_STRING_APPEND_LIST("external-file", external_files, M_OPT_FILE),
OPT_FLAG("autoload-files", autoload_files, 0),
OPT_STRING("sub-codepage", sub_cp, 0),
OPT_FLOAT("sub-delay", sub_delay, 0),
OPT_FLOAT("sub-fps", sub_fps, 0),
@@ -396,6 +397,8 @@ const m_option_t mp_opts[] = {
OPT_STRING("audio-device", audio_device, 0),
OPT_STRING("audio-client-name", audio_client_name, 0),
OPT_FLAG("audio-fallback-to-null", ao_null_fallback, 0),
OPT_FLAG("audio-stream-silence", audio_stream_silence, 0),
OPT_FLOATRANGE("audio-wait-open", audio_wait_open, 0, 0, 60),
OPT_CHOICE("force-window", force_vo, 0,
({"no", 0}, {"yes", 1}, {"immediate", 2})),
OPT_FLAG("taskbar-progress", vo.taskbar_progress, 0),
@@ -410,19 +413,20 @@ const m_option_t mp_opts[] = {
({"no", SOFTVOL_NO},
{"yes", SOFTVOL_YES},
{"auto", SOFTVOL_AUTO})),
OPT_FLOATRANGE("softvol-max", softvol_max, 0, 100, 1000),
OPT_FLOATRANGE("volume", mixer_init_volume, 0, -1, 1000),
OPT_CHOICE("mute", mixer_init_mute, 0,
OPT_FLOATRANGE("volume-max", softvol_max, 0, 100, 1000),
// values <0 for volume and mute are legacy and ignored
OPT_FLOATRANGE("volume", softvol_volume, 0, -1, 1000),
OPT_CHOICE("mute", softvol_mute, 0,
({"auto", -1},
{"no", 0},
{"yes", 1})),
OPT_STRING("volume-restore-data", mixer_restore_volume_data, 0),
OPT_CHOICE("gapless-audio", gapless_audio, 0,
({"no", 0},
{"yes", 1},
{"weak", -1})),
OPT_DOUBLE("audio-buffer", audio_buffer, M_OPT_MIN | M_OPT_MAX,
.min = 0, .max = 10),
OPT_FLOATRANGE("balance", balance, 0, -1, 1),
OPT_GEOMETRY("geometry", vo.geometry, 0),
OPT_SIZE_BOX("autofit", vo.autofit, 0),
@@ -469,7 +473,8 @@ const m_option_t mp_opts[] = {
OPT_STRING("vo-mmcss-profile", vo.mmcss_profile, M_OPT_FIXED),
#endif
OPT_STRING("heartbeat-cmd", heartbeat_cmd, 0),
OPT_STRING("heartbeat-cmd", heartbeat_cmd, 0,
.deprecation_message = "use Lua scripting instead"),
OPT_FLOAT("heartbeat-interval", heartbeat_interval, CONF_MIN, 0),
OPT_CHOICE_OR_INT("screen", vo.screen_id, 0, 0, 32,
@@ -684,6 +689,7 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("ass-use-margins", "sub-use-margins"),
OPT_REPLACED("media-title", "force-media-title"),
OPT_REPLACED("input-unix-socket", "input-ipc-server"),
OPT_REPLACED("softvol-max", "volume-max"),
{0}
};
@@ -697,8 +703,8 @@ const struct MPOpts mp_default_opts = {
.deinterlace = -1,
.softvol = SOFTVOL_AUTO,
.softvol_max = 130,
.mixer_init_volume = -1,
.mixer_init_mute = -1,
.softvol_volume = 100,
.softvol_mute = 0,
.gapless_audio = -1,
.audio_buffer = 0.2,
.audio_device = "auto",
@@ -758,6 +764,7 @@ const struct MPOpts mp_default_opts = {
.sync_audio_drop_size = 0.020,
.load_config = 1,
.position_resume = 1,
.autoload_files = 1,
.stream_cache = {
.size = -1,
.def_size = 75000,
@@ -802,7 +809,6 @@ const struct MPOpts mp_default_opts = {
.sub_visibility = 1,
.sub_pos = 100,
.sub_speed = 1.0,
.audio_output_channels = MP_CHMAP_INIT_STEREO,
.audio_output_format = 0, // AF_FORMAT_UNKNOWN
.playback_speed = 1.,
.pitch_correction = 1,
@@ -825,7 +831,7 @@ const struct MPOpts mp_default_opts = {
.sub_cp = "auto",
.screenshot_template = "mpv-shot%n",
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video",
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9",
.videotoolbox_format = IMGFMT_NV12,
.index_mode = 1,
+7 -4
View File
@@ -87,11 +87,13 @@ typedef struct MPOpts {
char *audio_device;
char *audio_client_name;
int ao_null_fallback;
int audio_stream_silence;
float audio_wait_open;
int force_vo;
int softvol;
float mixer_init_volume;
int mixer_init_mute;
char *mixer_restore_volume_data;
float softvol_volume;
float balance;
int softvol_mute;
float softvol_max;
int gapless_audio;
double audio_buffer;
@@ -226,7 +228,7 @@ typedef struct MPOpts {
double force_fps;
int index_mode;
struct mp_chmap audio_output_channels;
struct m_channels audio_output_channels;
int audio_output_format;
int audio_normalize;
int force_srate;
@@ -243,6 +245,7 @@ typedef struct MPOpts {
char **sub_paths;
char **audiofile_paths;
char **external_files;
int autoload_files;
int sub_auto;
int audiofile_auto;
int osd_bar_visible;
+2 -2
View File
@@ -74,7 +74,7 @@
typedef enum
{
kHIDRemoteModeNone = 0L,
kHIDRemoteModeShared, // Share the remote with others - let's you listen to the remote control events as long as noone has an exclusive lock on it
kHIDRemoteModeShared, // Share the remote with others - let's you listen to the remote control events as long as no one has an exclusive lock on it
// (RECOMMENDED ONLY FOR SPECIAL PURPOSES)
kHIDRemoteModeExclusive, // Try to acquire an exclusive lock on the remote (NOT RECOMMENDED)
@@ -182,7 +182,7 @@ typedef enum
- (BOOL)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware is inspected
inspectNewHardwareWithService:(io_service_t)service //
prematchResult:(BOOL)prematchResult; // Return YES if HIDRemote should go on with this hardware and try
// to use it, or NO if it should not be persued further.
// to use it, or NO if it should not be pursued further.
// Exlusive lock lending
- (BOOL)hidRemote:(HIDRemote *)hidRemote
+3 -3
View File
@@ -293,7 +293,7 @@ static HIDRemote *sHIDRemote = nil;
}while(0);
// An error occured. Do necessary clean up.
// An error occurred. Do necessary clean up.
if (matchDict!=NULL)
{
CFRelease(matchDict);
@@ -1422,7 +1422,7 @@ static HIDRemote *sHIDRemote = nil;
[((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self failedNewHardwareWithError:error];
}
// An error occured or this device is not of interest .. cleanup ..
// An error occurred or this device is not of interest .. cleanup ..
if (serviceNotification!=0)
{
IOObjectRelease(serviceNotification);
@@ -1615,7 +1615,7 @@ static HIDRemote *sHIDRemote = nil;
switch (buttonCode)
{
case kHIDRemoteButtonCodeIDChanged:
// Do nothing, this is handled seperately
// Do nothing, this is handled separately
break;
case kHIDRemoteButtonCodeUp:
+8 -21
View File
@@ -28,15 +28,14 @@
// Emulate the parts of C11 stdatomic.h needed by mpv.
// Still relies on gcc/clang atomic builtins.
// The t field is a hack to make the non-atomic fallback macro mess work.
typedef struct { volatile unsigned long v, t; } atomic_ulong;
typedef struct { volatile int v, t; } atomic_int;
typedef struct { volatile unsigned int v, t; } atomic_uint;
typedef struct { volatile _Bool v, t; } atomic_bool;
typedef struct { volatile long long v, t; } atomic_llong;
typedef struct { volatile uint_least32_t v, t; } atomic_uint_least32_t;
typedef struct { volatile unsigned long long v, t; } atomic_ullong;
typedef struct { volatile unsigned long v; } atomic_ulong;
typedef struct { volatile int v; } atomic_int;
typedef struct { volatile unsigned int v; } atomic_uint;
typedef struct { volatile _Bool v; } atomic_bool;
typedef struct { volatile long long v; } atomic_llong;
typedef struct { volatile uint_least32_t v; } atomic_uint_least32_t;
typedef struct { volatile unsigned long long v; } atomic_ullong;
#define ATOMIC_VAR_INIT(x) \
{.v = (x)}
@@ -82,19 +81,7 @@ typedef struct { volatile unsigned long long v, t; } atomic_ullong;
ok_; })
#else
// This is extremely wrong. The build system actually disables code that has
// a serious dependency on working atomics, so this is barely ok.
#define atomic_load(p) ((p)->v)
#define atomic_store(p, val) ((p)->v = (val))
#define atomic_fetch_op_(a, b, op) \
((a)->t = (a)->v, (a)->v = (a)->v op (b), (a)->t)
#define atomic_fetch_add(a, b) atomic_fetch_op_(a, b, +)
#define atomic_fetch_and(a, b) atomic_fetch_op_(a, b, &)
#define atomic_fetch_or(a, b) atomic_fetch_op_(a, b, |)
#define atomic_compare_exchange_strong(p, old, new) \
((p)->v == *(old) ? ((p)->v = (new), 1) : (*(old) = (p)->v, 0))
# error "this should have been a configuration error, report a bug please"
#endif /* no atomics */
#endif /* else HAVE_STDATOMIC */
+8
View File
@@ -85,6 +85,14 @@ int mp_make_wakeup_pipe(int pipes[2])
}
#endif
void mp_flush_wakeup_pipe(int pipe_end)
{
#ifndef __MINGW32__
char buf[100];
(void)read(pipe_end, buf, sizeof(buf));
#endif
}
#ifdef _WIN32
#include <windows.h>
+1
View File
@@ -47,6 +47,7 @@
bool mp_set_cloexec(int fd);
int mp_make_cloexec_pipe(int pipes[2]);
int mp_make_wakeup_pipe(int pipes[2]);
void mp_flush_wakeup_pipe(int pipe_end);
#ifdef _WIN32
#include <wchar.h>
+1 -1
View File
@@ -161,7 +161,7 @@ static void terminate_cocoa_application(void)
- (void)stopPlaybackAndRememberPosition
{
[self stopMPV:"quit_watch_later"];
[self stopMPV:"quit-watch-later"];
}
- (void)stopMPV:(char *)cmd
+25 -7
View File
@@ -19,13 +19,31 @@
#include <winver.h>
1 VERSIONINFO
FILEVERSION 2,0,0,0
PRODUCTVERSION 2,0,0,0
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
}
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2, 0, 0, 0
PRODUCTVERSION 2, 0, 0, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0
{
BLOCK "StringFileInfo" {
BLOCK "000004b0" {
VALUE "Comments", "mpv is distributed under the terms of the GNU General Public License Version 2 or later."
VALUE "CompanyName", "mpv"
VALUE "FileDescription", "mpv"
VALUE "FileVersion", "2.0.0.0"
VALUE "LegalCopyright", "(C) 2000-2016 mpv/mplayer2/MPlayer"
VALUE "OriginalFilename", "mpv.exe"
VALUE "ProductName", "mpv"
VALUE "ProductVersion", "2.0.0.0"
}
}
BLOCK "VarFileInfo" {
VALUE "Translation", 0, 1200
}
}
IDI_ICON1 ICON DISCARDABLE "etc/mpv-icon.ico"
+4
View File
@@ -45,6 +45,10 @@ static void *run_subprocess(void *ptr)
return NULL;
}
void mp_devnull(void *ctx, char *data, size_t size)
{
}
void mp_subprocess_detached(struct mp_log *log, char **args)
{
struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
+2
View File
@@ -24,6 +24,8 @@ struct mp_cancel;
typedef void (*subprocess_read_cb)(void *ctx, char *data, size_t size);
void mp_devnull(void *ctx, char *data, size_t size);
// Start a subprocess. Uses callbacks to read from stdout and stderr.
int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
+120 -9
View File
@@ -31,7 +31,6 @@
#include "common/common.h"
#include "osdep/timer.h"
#include "audio/mixer.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/decode/dec_audio.h"
@@ -120,6 +119,69 @@ fail:
mp_notify(mpctx, MP_EVENT_CHANGE_ALL, NULL);
}
// Called when opts->softvol_volume or opts->softvol_mute were changed.
void audio_update_volume(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct ao_chain *ao_c = mpctx->ao_chain;
if (!ao_c || ao_c->af->initialized < 1)
return;
float gain = MPMAX(opts->softvol_volume / 100.0, 0);
if (opts->softvol_mute == 1)
gain = 0.0;
if (!af_control_any_rev(ao_c->af, AF_CONTROL_SET_VOLUME, &gain)) {
if (gain == 1.0)
return;
MP_VERBOSE(mpctx, "Inserting volume filter.\n");
if (!(af_add(ao_c->af, "volume", "softvol", NULL)
&& af_control_any_rev(ao_c->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mpctx, "No volume control available.\n");
}
}
/* NOTE: Currently the balance code is seriously buggy: it always changes
* the af_pan mapping between the first two input channels and first two
* output channels to particular values. These values make sense for an
* af_pan instance that was automatically inserted for balance control
* only and is otherwise an identity transform, but if the filter was
* there for another reason, then ignoring and overriding the original
* values is completely wrong.
*/
void audio_update_balance(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct ao_chain *ao_c = mpctx->ao_chain;
if (!ao_c || ao_c->af->initialized < 1)
return;
float val = opts->balance;
if (af_control_any_rev(ao_c->af, AF_CONTROL_SET_PAN_BALANCE, &val))
return;
if (val == 0)
return;
struct af_instance *af_pan_balance;
if (!(af_pan_balance = af_add(ao_c->af, "pan", "autopan", NULL))) {
MP_ERR(mpctx, "No balance control available.\n");
return;
}
/* make all other channels pass through since by default pan blocks all */
for (int i = 2; i < AF_NCH; i++) {
float level[AF_NCH] = {0};
level[i] = 1.f;
af_control_ext_t arg_ext = { .ch = i, .arg = level };
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_LEVEL,
&arg_ext);
}
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_BALANCE, &val);
}
static int recreate_audio_filters(struct MPContext *mpctx)
{
assert(mpctx->ao_chain);
@@ -132,7 +194,11 @@ static int recreate_audio_filters(struct MPContext *mpctx)
if (afs->initialized < 1 && af_init(afs) < 0)
goto fail;
mixer_reinit_audio(mpctx->mixer, mpctx->ao, afs);
if (mpctx->opts->softvol == SOFTVOL_NO)
MP_ERR(mpctx, "--softvol=no is not supported anymore.\n");
audio_update_volume(mpctx);
audio_update_balance(mpctx);
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
@@ -202,6 +268,7 @@ void reset_audio_state(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->audio_drop_throttle = 0;
mpctx->audio_stat_start = 0;
mpctx->audio_allow_second_chance_seek = false;
}
void uninit_audio_out(struct MPContext *mpctx)
@@ -210,7 +277,6 @@ void uninit_audio_out(struct MPContext *mpctx)
// Note: with gapless_audio, stop_play is not correctly set
if (mpctx->opts->gapless_audio || mpctx->stop_play == AT_END_OF_FILE)
ao_drain(mpctx->ao);
mixer_uninit_audio(mpctx->mixer);
ao_uninit(mpctx->ao);
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
@@ -243,7 +309,6 @@ static void ao_chain_uninit(struct ao_chain *ao_c)
void uninit_audio_chain(struct MPContext *mpctx)
{
if (mpctx->ao_chain) {
mixer_uninit_audio(mpctx->mixer);
ao_chain_uninit(mpctx->ao_chain);
mpctx->ao_chain = NULL;
@@ -289,7 +354,10 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
} else if (af_fmt_is_pcm(in_format.format)) {
afs->output.rate = opts->force_srate;
mp_audio_set_format(&afs->output, opts->audio_output_format);
mp_audio_set_channels(&afs->output, &opts->audio_output_channels);
if (opts->audio_output_channels.num_chmaps == 1) {
mp_audio_set_channels(&afs->output,
&opts->audio_output_channels.chmaps[0]);
}
}
// filter input format: same as codec's output format:
@@ -304,15 +372,25 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
}
if (!mpctx->ao) {
int ao_flags = 0;
bool spdif_fallback = af_fmt_is_spdif(afs->output.format) &&
ao_c->spdif_passthrough;
bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback;
mp_chmap_remove_useless_channels(&afs->output.channels,
&opts->audio_output_channels);
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);
mp_audio_set_channels(&afs->output, &afs->output.channels);
mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input,
mpctx->ao = ao_init_best(mpctx->global, ao_flags, mpctx->input,
mpctx->encode_lavc_ctx, afs->output.rate,
afs->output.format, afs->output.channels);
ao_c->ao = mpctx->ao;
@@ -363,6 +441,9 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
mp_audio_config_to_str(&fmt));
MP_VERBOSE(mpctx, "AO: Description: %s\n", ao_get_description(mpctx->ao));
update_window_title(mpctx, true);
ao_c->ao_resume_time =
opts->audio_wait_open > 0 ? mp_time_sec() + opts->audio_wait_open : 0;
}
if (recreate_audio_filters(mpctx) < 0)
@@ -596,6 +677,9 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
sync_pts = mpctx->video_pts - opts->audio_delay;
} else if (mpctx->hrseek_active) {
sync_pts = mpctx->hrseek_pts;
} else {
// If audio-only is enabled mid-stream during playback, sync accordingly.
sync_pts = mpctx->playback_pts;
}
if (sync_pts == MP_NOPTS_VALUE) {
mpctx->audio_status = STATUS_FILLING;
@@ -611,6 +695,27 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
}
ptsdiff = MPCLAMP(ptsdiff, -3600, 3600);
// Heuristic: if audio is "too far" ahead, and one of them is a separate
// track, allow a refresh seek to the correct position to fix it.
if (ptsdiff > 0.2 && mpctx->audio_allow_second_chance_seek && sync_to_video) {
struct ao_chain *ao_c = mpctx->ao_chain;
if (ao_c && ao_c->track && mpctx->vo_chain && mpctx->vo_chain->track &&
ao_c->track->demuxer != mpctx->vo_chain->track->demuxer)
{
struct track *track = ao_c->track;
double pts = mpctx->video_pts;
if (pts != MP_NOPTS_VALUE)
pts += get_track_seek_offset(mpctx, track);
// (disable it first to make it take any effect)
demuxer_select_track(track->demuxer, track->stream, pts, false);
demuxer_select_track(track->demuxer, track->stream, pts, true);
reset_audio_state(mpctx);
MP_VERBOSE(mpctx, "retrying audio seek\n");
return false;
}
}
mpctx->audio_allow_second_chance_seek = false;
int align = af_format_sample_alignment(out_format.format);
*skip = (int)(-ptsdiff * play_samplerate) / align * align;
return true;
@@ -760,6 +865,12 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
return; // try again next iteration
}
if (ao_c->ao_resume_time > mp_time_sec()) {
double remaining = ao_c->ao_resume_time - mp_time_sec();
mpctx->sleeptime = MPMIN(mpctx->sleeptime, remaining);
return;
}
if (mpctx->vo_chain && ao_c->pts_reset) {
MP_VERBOSE(mpctx, "Reset playback due to audio timestamp reset.\n");
reset_playback_state(mpctx);
+62
View File
@@ -24,6 +24,7 @@
#include "common/global.h"
#include "common/msg.h"
#include "common/msg_control.h"
#include "common/global.h"
#include "input/input.h"
#include "input/cmd_list.h"
#include "misc/ctype.h"
@@ -62,9 +63,13 @@ struct mp_client_api {
pthread_mutex_t lock;
// -- protected by lock
struct mpv_handle **clients;
int num_clients;
uint64_t event_masks; // combined events of all clients, or 0 if unknown
struct mp_custom_protocol *custom_protocols;
int num_custom_protocols;
};
struct observe_property {
@@ -1592,6 +1597,7 @@ static const char *const err_table[] = {
[-MPV_ERROR_UNKNOWN_FORMAT] = "unrecognized file format",
[-MPV_ERROR_UNSUPPORTED] = "not supported",
[-MPV_ERROR_NOT_IMPLEMENTED] = "operation not implemented",
[-MPV_ERROR_GENERIC] = "something happened",
};
const char *mpv_error_string(int error)
@@ -1722,3 +1728,59 @@ void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api)
unlock_core(ctx);
return res;
}
struct mp_custom_protocol {
char *protocol;
void *user_data;
mpv_stream_cb_open_ro_fn open_fn;
};
int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data,
mpv_stream_cb_open_ro_fn open_fn)
{
if (!open_fn)
return MPV_ERROR_INVALID_PARAMETER;
struct mp_client_api *clients = ctx->clients;
int r = 0;
pthread_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_custom_protocols; n++) {
struct mp_custom_protocol *proto = &clients->custom_protocols[n];
if (strcmp(proto->protocol, protocol) == 0) {
r = MPV_ERROR_INVALID_PARAMETER;
break;
}
}
if (stream_has_proto(protocol))
r = MPV_ERROR_INVALID_PARAMETER;
if (r >= 0) {
struct mp_custom_protocol proto = {
.protocol = talloc_strdup(clients, protocol),
.user_data = user_data,
.open_fn = open_fn,
};
MP_TARRAY_APPEND(clients, clients->custom_protocols,
clients->num_custom_protocols, proto);
}
pthread_mutex_unlock(&clients->lock);
return r;
}
bool mp_streamcb_lookup(struct mpv_global *g, const char *protocol,
void **out_user_data, mpv_stream_cb_open_ro_fn *out_fn)
{
struct mp_client_api *clients = g->client_api;
bool found = false;
pthread_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_custom_protocols; n++) {
struct mp_custom_protocol *proto = &clients->custom_protocols[n];
if (strcmp(proto->protocol, protocol) == 0) {
*out_user_data = proto->user_data;
*out_fn = proto->open_fn;
found = true;
break;
}
}
pthread_mutex_unlock(&clients->lock);
return found;
}
+4
View File
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include "libmpv/client.h"
#include "libmpv/stream_cb.h"
struct MPContext;
struct mpv_handle;
@@ -46,4 +47,7 @@ struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
struct mp_client_api *client_api);
void kill_video(struct mp_client_api *client_api);
bool mp_streamcb_lookup(struct mpv_global *g, const char *protocol,
void **out_user_data, mpv_stream_cb_open_ro_fn *out_fn);
#endif
+235 -194
View File
@@ -54,12 +54,12 @@
#include "video/decode/vd.h"
#include "video/out/vo.h"
#include "video/csputils.h"
#include "audio/mixer.h"
#include "audio/audio_buffer.h"
#include "audio/out/ao.h"
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
#include "video/out/bitmap_packer.h"
#include "options/path.h"
#include "screenshot.h"
@@ -88,7 +88,8 @@ struct command_ctx {
// One of these is in use by the OSD; the other one exists so that the
// bitmap list can be manipulated without additional synchronization.
struct sub_bitmaps overlay_osd[2];
struct sub_bitmaps *overlay_osd_current;
int overlay_osd_current;
struct bitmap_packer *overlay_packer;
struct hook_handler **hooks;
int num_hooks;
@@ -98,9 +99,8 @@ struct command_ctx {
};
struct overlay {
void *map_start;
size_t map_size;
struct sub_bitmap osd;
struct mp_image *source;
int x, y;
};
struct hook_handler {
@@ -219,7 +219,7 @@ static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);
}
// Call before a seek, in order to allow revert_seek to undo the seek.
// Call before a seek, in order to allow revert-seek to undo the seek.
static void mark_seek(struct MPContext *mpctx)
{
struct command_ctx *cmd = mpctx->command_ctx;
@@ -346,7 +346,19 @@ static int mp_property_filename(void *ctx, struct m_property *prop,
if (mp_is_url(bstr0(filename)))
mp_url_unescape_inplace(filename);
char *f = (char *)mp_basename(filename);
int r = m_property_strdup_ro(action, arg, f[0] ? f : filename);
if (!f[0])
f = filename;
if (action == M_PROPERTY_KEY_ACTION) {
struct m_property_action_arg *ka = arg;
if (strcmp(ka->key, "no-ext") == 0) {
action = ka->action;
arg = ka->arg;
bstr root;
if (mp_splitext(f, &root))
f = bstrto0(filename, root);
}
}
int r = m_property_strdup_ro(action, arg, f);
talloc_free(filename);
return r;
}
@@ -1560,7 +1572,8 @@ static int mp_property_mixer_active(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
return m_property_flag_ro(action, arg, mixer_audio_initialized(mpctx->mixer));
struct ao_chain *ao_c = mpctx->ao_chain;
return m_property_flag_ro(action, arg, ao_c && ao_c->af->initialized > 0);
}
/// Volume (RW)
@@ -1568,44 +1581,29 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
switch (action) {
case M_PROPERTY_GET:
mixer_getbothvolume(mpctx->mixer, arg);
return M_PROPERTY_OK;
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
.flags = M_OPT_RANGE,
.min = 0,
.max = mixer_getmaxvolume(mpctx->mixer),
.max = opts->softvol_max,
};
return M_PROPERTY_OK;
case M_PROPERTY_GET_NEUTRAL:
*(float *)arg = 100;
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
float val;
mixer_getbothvolume(mpctx->mixer, &val);
*(char **)arg = talloc_asprintf(NULL, "%i", (int)val);
case M_PROPERTY_PRINT:
*(char **)arg = talloc_asprintf(NULL, "%i", (int)opts->softvol_volume);
return M_PROPERTY_OK;
}
case M_PROPERTY_SET:
mixer_setvolume(mpctx->mixer, *(float *) arg, *(float *) arg);
return M_PROPERTY_OK;
case M_PROPERTY_SWITCH: {
struct m_property_switch_arg *sarg = arg;
mixer_addvolume(mpctx->mixer, sarg->inc);
return M_PROPERTY_OK;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_volume_max(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
return m_property_float_ro(action, arg, mixer_getmaxvolume(mpctx->mixer));
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (action == M_PROPERTY_SET)
audio_update_volume(mpctx);
return r;
}
/// Mute (RW)
@@ -1613,13 +1611,83 @@ static int mp_property_mute(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (action == M_PROPERTY_GET_TYPE) {
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
}
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (action == M_PROPERTY_SET)
audio_update_volume(mpctx);
return r;
}
static int mp_property_ao_volume(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct ao *ao = mpctx->ao;
if (!ao)
return M_PROPERTY_NOT_IMPLEMENTED;
switch (action) {
case M_PROPERTY_SET:
mixer_setmute(mpctx->mixer, *(int *) arg);
case M_PROPERTY_SET: {
float value = *(float *)arg;
ao_control_vol_t vol = {value, value};
if (ao_control(ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
return M_PROPERTY_OK;
case M_PROPERTY_GET:
*(int *)arg = mixer_getmute(mpctx->mixer);
}
case M_PROPERTY_GET: {
ao_control_vol_t vol = {0};
if (ao_control(ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
*(float *)arg = (vol.left + vol.right) / 2.0f;
return M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
.flags = M_OPT_RANGE,
.min = 0,
.max = 100,
};
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
ao_control_vol_t vol = {0};
if (ao_control(ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
*(char **)arg = talloc_asprintf(NULL, "%.f", (vol.left + vol.right) / 2.0f);
return M_PROPERTY_OK;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_ao_mute(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct ao *ao = mpctx->ao;
if (!ao)
return M_PROPERTY_NOT_IMPLEMENTED;
switch (action) {
case M_PROPERTY_SET: {
bool value = *(int *)arg;
if (ao_control(ao, AOCONTROL_SET_MUTE, &value) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
return M_PROPERTY_OK;
}
case M_PROPERTY_GET: {
bool value = false;
if (ao_control(ao, AOCONTROL_GET_MUTE, &value) != CONTROL_OK)
return M_PROPERTY_UNAVAILABLE;
*(int *)arg = value;
return M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
@@ -1627,22 +1695,6 @@ static int mp_property_mute(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_volrestore(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
switch (action) {
case M_PROPERTY_GET: {
char *s = mixer_get_volume_restore_data(mpctx->mixer);
*(char **)arg = s;
return s ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE;
}
case M_PROPERTY_SET:
return M_PROPERTY_NOT_IMPLEMENTED;
}
return mp_property_generic_option(mpctx, prop, action, arg);
}
static int get_device_entry(int item, int action, void *arg, void *ctx)
{
struct ao_device_list *list = ctx;
@@ -1801,23 +1853,10 @@ static int mp_property_balance(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
float bal;
switch (action) {
case M_PROPERTY_GET:
mixer_getbalance(mpctx->mixer, arg);
return M_PROPERTY_OK;
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
.flags = M_OPT_RANGE,
.min = -1,
.max = 1,
};
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
if (action == M_PROPERTY_PRINT) {
char **str = arg;
mixer_getbalance(mpctx->mixer, &bal);
float bal = mpctx->opts->balance;
if (bal == 0.f)
*str = talloc_strdup(NULL, "center");
else if (bal == -1.f)
@@ -1831,11 +1870,11 @@ static int mp_property_balance(void *ctx, struct m_property *prop,
}
return M_PROPERTY_OK;
}
case M_PROPERTY_SET:
mixer_setbalance(mpctx->mixer, *(float *)arg);
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (action == M_PROPERTY_SET)
audio_update_balance(mpctx);
return r;
}
static struct track* track_next(struct MPContext *mpctx, enum stream_type type,
@@ -1971,6 +2010,10 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
if (track->d_audio)
decoder_desc = track->d_audio->decoder_desc;
bool has_rg = track->stream->codec->replaygain_data;
struct replaygain_data rg = has_rg ? *track->stream->codec->replaygain_data
: (struct replaygain_data){0};
struct m_sub_property props[] = {
{"id", SUB_PROP_INT(track->user_tid)},
{"type", SUB_PROP_STR(stream_type_name(track->type)),
@@ -2004,6 +2047,14 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
{"demux-samplerate", SUB_PROP_INT(p.samplerate),
.unavailable = !p.samplerate},
{"demux-fps", SUB_PROP_DOUBLE(p.fps), .unavailable = p.fps <= 0},
{"replaygain-track-peak", SUB_PROP_FLOAT(rg.track_peak),
.unavailable = !has_rg},
{"replaygain-track-gain", SUB_PROP_FLOAT(rg.track_gain),
.unavailable = !has_rg},
{"replaygain-album-peak", SUB_PROP_FLOAT(rg.album_peak),
.unavailable = !has_rg},
{"replaygain-album-gain", SUB_PROP_FLOAT(rg.album_gain),
.unavailable = !has_rg},
{0}
};
@@ -2257,88 +2308,6 @@ static int mp_property_detected_hwdec(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
#define VF_DEINTERLACE_LABEL "deinterlace"
static bool probe_deint_filter(struct MPContext *mpctx, const char *filt)
{
char filter[80];
// add a label so that removing the filter is easier
snprintf(filter, sizeof(filter), "@%s:%s", VF_DEINTERLACE_LABEL, filt);
return edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "pre", filter) >= 0;
}
static bool check_output_format(struct MPContext *mpctx, int imgfmt)
{
struct vo_chain *vo_c = mpctx->vo_chain;
if (!vo_c)
return false;
return vo_c->vf->allowed_output_formats[imgfmt - IMGFMT_START];
}
static int probe_deint_filters(struct MPContext *mpctx)
{
if (check_output_format(mpctx, IMGFMT_VDPAU)) {
char filter[80] = "vdpaupp:deint=yes";
int pref = 0;
vo_control(mpctx->video_out, VOCTRL_GET_PREF_DEINT, &pref);
pref = pref < 0 ? -pref : pref;
if (pref > 0 && pref <= 4) {
const char *types[] =
{"", "first-field", "bob", "temporal", "temporal-spatial"};
mp_snprintf_cat(filter, sizeof(filter), ":deint-mode=%s",
types[pref]);
}
probe_deint_filter(mpctx, filter);
return 0;
}
if (check_output_format(mpctx, IMGFMT_VAAPI) &&
probe_deint_filter(mpctx, "vavpp"))
return 0;
if ((check_output_format(mpctx, IMGFMT_D3D11VA) ||
check_output_format(mpctx, IMGFMT_D3D11NV12)) &&
probe_deint_filter(mpctx, "d3d11vpp"))
return 0;
if (probe_deint_filter(mpctx, "yadif"))
return 0;
return -1;
}
static int get_deinterlacing(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
int enabled = 0;
if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
enabled = -1;
if (enabled < 0) {
// vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
enabled = 1;
}
return enabled;
}
void remove_deint_filter(struct MPContext *mpctx)
{
edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "del", "@" VF_DEINTERLACE_LABEL);
}
void set_deinterlacing(struct MPContext *mpctx, bool enable)
{
struct vo_chain *vo_c = mpctx->vo_chain;
if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL)) {
if (!enable)
remove_deint_filter(mpctx);
} else {
if ((get_deinterlacing(mpctx) > 0) != enable) {
int arg = enable;
if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
probe_deint_filters(mpctx);
}
}
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
}
static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2568,13 +2537,15 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
{"aspect", SUB_PROP_FLOAT(d_w / (double)d_h)},
{"par", SUB_PROP_FLOAT(p.p_w / (double)p.p_h)},
{"colormatrix",
SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.colorspace))},
SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.color.space))},
{"colorlevels",
SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.colorlevels))},
SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.color.levels))},
{"primaries",
SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.primaries))},
SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.color.primaries))},
{"gamma",
SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.gamma))},
SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.color.gamma))},
{"nom-peak", SUB_PROP_FLOAT(p.color.nom_peak)},
{"sig-peak", SUB_PROP_FLOAT(p.color.sig_peak)},
{"chroma-location",
SUB_PROP_STR(m_opt_choice_str(mp_chroma_names, p.chroma_location))},
{"stereo-in",
@@ -3751,8 +3722,10 @@ static const struct m_property mp_properties[] = {
// Audio
{"mixer-active", mp_property_mixer_active},
{"volume", mp_property_volume},
{"volume-max", mp_property_volume_max},
{"volume-max", mp_property_generic_option},
{"mute", mp_property_mute},
{"ao-volume", mp_property_ao_volume},
{"ao-mute", mp_property_ao_mute},
{"audio-delay", mp_property_audio_delay},
{"audio-codec-name", mp_property_audio_codec_name},
{"audio-codec", mp_property_audio_codec},
@@ -3762,7 +3735,6 @@ static const struct m_property mp_properties[] = {
M_PROPERTY_DEPRECATED_ALIAS("audio-channels", "audio-params/channel-count"),
{"aid", mp_property_audio},
{"balance", mp_property_balance},
{"volume-restore-data", mp_property_volrestore},
{"audio-device", mp_property_audio_device},
{"audio-device-list", mp_property_audio_devices},
{"current-ao", mp_property_ao},
@@ -3940,7 +3912,7 @@ static const char *const *const mp_event_property_change[] = {
"colormatrix-output-range", "colormatrix-primaries", "video-aspect"),
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio", "volume", "mute", "balance",
"volume-restore-data", "current-ao", "audio-codec-name", "audio-params",
"current-ao", "audio-codec-name", "audio-params",
"audio-out-params", "volume-max", "mixer-active"),
E(MPV_EVENT_SEEK, "seeking", "core-idle", "eof-reached"),
E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle", "eof-reached"),
@@ -4104,7 +4076,11 @@ static const struct property_osd_display {
{ "volume", "Volume",
.msg = "Volume: ${?volume:${volume}% ${?mute==yes:(Muted)}}${!volume:${volume}}",
.osd_progbar = OSD_VOLUME },
{ "ao-volume", "AO Volume",
.msg = "AO Volume: ${?ao-volume:${ao-volume}% ${?ao-mute==yes:(Muted)}}${!ao-volume:${ao-volume}}",
.osd_progbar = OSD_VOLUME },
{ "mute", "Mute" },
{ "ao-mute", "AO Mute" },
{ "audio-delay", "A-V delay" },
{ "audio", "Audio" },
{ "balance", "Balance", .osd_progbar = OSD_BALANCE },
@@ -4314,20 +4290,85 @@ static int edit_filters_osd(struct MPContext *mpctx, enum stream_type mediatype,
static void recreate_overlays(struct MPContext *mpctx)
{
struct command_ctx *cmd = mpctx->command_ctx;
struct sub_bitmaps *new = &cmd->overlay_osd[0];
if (new == cmd->overlay_osd_current)
new += 1; // pick the unused one
int overlay_next = !cmd->overlay_osd_current;
struct sub_bitmaps *new = &cmd->overlay_osd[overlay_next];
new->format = SUBBITMAP_RGBA;
new->change_id = 1;
// overlay array can have unused entries, but parts list must be "packed"
bool valid = false;
new->num_parts = 0;
for (int n = 0; n < cmd->num_overlays; n++) {
struct overlay *o = &cmd->overlays[n];
if (o->osd.bitmap)
MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, o->osd);
if (o->source) {
struct mp_image *s = o->source;
struct sub_bitmap b = {
.bitmap = s->planes[0],
.stride = s->stride[0],
.w = s->w, .dw = s->w,
.h = s->h, .dh = s->h,
.x = o->x,
.y = o->y,
};
MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, b);
}
}
cmd->overlay_osd_current = new;
osd_set_external2(mpctx->osd, cmd->overlay_osd_current);
if (!cmd->overlay_packer)
cmd->overlay_packer = talloc_zero(cmd, struct bitmap_packer);
cmd->overlay_packer->padding = 1; // assume bilinear scaling
packer_set_size(cmd->overlay_packer, new->num_parts);
for (int n = 0; n < new->num_parts; n++)
cmd->overlay_packer->in[n] = (struct pos){new->parts[n].w, new->parts[n].h};
if (packer_pack(cmd->overlay_packer) < 0 || new->num_parts == 0)
goto done;
struct pos bb[2];
packer_get_bb(cmd->overlay_packer, bb);
new->packed_w = bb[1].x;
new->packed_h = bb[1].y;
if (!new->packed || new->packed->w < new->packed_w ||
new->packed->h < new->packed_h)
{
talloc_free(new->packed);
new->packed = mp_image_alloc(IMGFMT_BGRA, cmd->overlay_packer->w,
cmd->overlay_packer->h);
if (!new->packed)
goto done;
}
// clear padding
mp_image_clear(new->packed, 0, 0, new->packed->w, new->packed->h);
for (int n = 0; n < new->num_parts; n++) {
struct sub_bitmap *b = &new->parts[n];
struct pos pos = cmd->overlay_packer->result[n];
int stride = new->packed->stride[0];
void *pdata = (uint8_t *)new->packed->planes[0] + pos.y * stride + pos.x * 4;
memcpy_pic(pdata, b->bitmap, b->w * 4, b->h, stride, b->stride);
b->bitmap = pdata;
b->stride = stride;
b->src_x = pos.x;
b->src_y = pos.y;
}
valid = true;
done:
if (!valid) {
new->format = SUBBITMAP_EMPTY;
new->num_parts = 0;
}
osd_set_external2(mpctx->osd, new);
cmd->overlay_osd_current = overlay_next;
}
// Set overlay with the given ID to the contents as described by "new".
@@ -4342,17 +4383,11 @@ static void replace_overlay(struct MPContext *mpctx, int id, struct overlay *new
}
struct overlay *ptr = &cmd->overlays[id];
struct overlay old = *ptr;
if (!ptr->osd.bitmap && !new->osd.bitmap)
return; // don't need to recreate or unmap
talloc_free(ptr->source);
*ptr = *new;
recreate_overlays(mpctx);
// Do this afterwards, so we never unmap while the OSD is using it.
if (old.map_start && old.map_size)
munmap(old.map_start, old.map_size);
recreate_overlays(mpctx);
}
static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
@@ -4361,25 +4396,24 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
{
int r = -1;
if (strcmp(fmt, "bgra") != 0) {
MP_ERR(mpctx, "overlay_add: unsupported OSD format '%s'\n", fmt);
MP_ERR(mpctx, "overlay-add: unsupported OSD format '%s'\n", fmt);
goto error;
}
if (id < 0 || id >= 64) { // arbitrary upper limit
MP_ERR(mpctx, "overlay_add: invalid id %d\n", id);
MP_ERR(mpctx, "overlay-add: invalid id %d\n", id);
goto error;
}
if (w < 0 || h < 0 || stride < w * 4 || (stride % 4)) {
MP_ERR(mpctx, "overlay_add: inconsistent parameters\n");
if (w <= 0 || h <= 0 || stride < w * 4 || (stride % 4)) {
MP_ERR(mpctx, "overlay-add: inconsistent parameters\n");
goto error;
}
struct overlay overlay = {
.osd = {
.stride = stride,
.x = x, .y = y,
.w = w, .h = h,
.dw = w, .dh = h,
},
.source = mp_image_alloc(IMGFMT_BGRA, w, h),
.x = x,
.y = y,
};
if (!overlay.source)
goto error;
int fd = -1;
bool close_fd = true;
void *p = NULL;
@@ -4398,21 +4432,25 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
} else {
fd = open(file, O_RDONLY | O_BINARY | O_CLOEXEC);
}
int map_size = 0;
if (fd >= 0) {
overlay.map_size = offset + h * stride;
void *m = mmap(NULL, overlay.map_size, PROT_READ, MAP_SHARED, fd, 0);
map_size = offset + h * stride;
void *m = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, 0);
if (close_fd)
close(fd);
if (m && m != MAP_FAILED) {
overlay.map_start = m;
if (m && m != MAP_FAILED)
p = m;
}
}
if (!p) {
MP_ERR(mpctx, "overlay_add: could not open or map '%s'\n", file);
MP_ERR(mpctx, "overlay-add: could not open or map '%s'\n", file);
talloc_free(overlay.source);
goto error;
}
overlay.osd.bitmap = (char *)p + offset;
memcpy_pic(overlay.source->planes[0], (char *)p + offset, w * 4, h,
overlay.source->stride[0], stride);
if (map_size)
munmap(p, map_size);
replace_overlay(mpctx, id, &overlay);
r = 0;
error:
@@ -4434,6 +4472,8 @@ static void overlay_uninit(struct MPContext *mpctx)
for (int id = 0; id < cmd->num_overlays; id++)
overlay_remove(mpctx, id);
osd_set_external2(mpctx->osd, NULL);
for (int n = 0; n < 2; n++)
mp_image_unrefp(&cmd->overlay_osd[n].packed);
}
struct cycle_counter {
@@ -4958,6 +4998,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
mpctx->add_osd_seek_info |=
(msg_osd ? OSD_SEEK_INFO_TEXT : 0) |
(bar_osd ? OSD_SEEK_INFO_BAR : 0);
mpctx->osd_force_update = true;
break;
case MP_CMD_TV_LAST_CHANNEL: {
-3
View File
@@ -59,7 +59,4 @@ void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
void handle_ab_loop(struct MPContext *mpctx);
void remove_deint_filter(struct MPContext *mpctx);
void set_deinterlacing(struct MPContext *mpctx, bool enable);
#endif /* MPLAYER_COMMAND_H */
+2 -1
View File
@@ -208,7 +208,8 @@ static const char *const backup_properties[] = {
"options/speed",
"options/edition",
"options/pause",
"volume-restore-data",
"volume",
"mute",
"options/audio-delay",
//"balance",
"options/fullscreen",
+17 -1
View File
@@ -185,6 +185,7 @@ struct ao_chain {
struct af_stream *af;
struct ao *ao;
struct mp_audio_buffer *ao_buffer;
double ao_resume_time;
// 1-element input frame queue.
struct mp_audio *input_frame;
@@ -287,7 +288,6 @@ typedef struct MPContext {
struct lavfi *lavfi;
struct mixer *mixer;
struct ao *ao;
struct mp_audio *ao_decoder_fmt; // for weak gapless audio check
struct ao_chain *ao_chain;
@@ -383,6 +383,10 @@ typedef struct MPContext {
bool immediate; // disable seek delay logic
} seek;
// Allow audio to issue a second seek if audio is too far ahead (for non-hr
// seeks with external audio tracks).
bool audio_allow_second_chance_seek;
/* Heuristic for relative chapter seeks: keep track which chapter
* the user wanted to go to, even if we aren't exactly within the
* boundaries of that chapter due to an inaccurate seek. */
@@ -429,6 +433,8 @@ void uninit_audio_out(struct MPContext *mpctx);
void uninit_audio_chain(struct MPContext *mpctx);
int init_audio_decoder(struct MPContext *mpctx, struct track *track);
void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src);
void audio_update_volume(struct MPContext *mpctx);
void audio_update_balance(struct MPContext *mpctx);
// configfiles.c
void mp_parse_cfgfiles(struct MPContext *mpctx);
@@ -485,6 +491,7 @@ int stream_dump(struct MPContext *mpctx, const char *source_filename);
int mpctx_run_reentrant(struct MPContext *mpctx, void (*thread_fn)(void *arg),
void *thread_arg);
struct mpv_global *create_sub_global(struct MPContext *mpctx);
double get_track_seek_offset(struct MPContext *mpctx, struct track *track);
// osd.c
void set_osd_bar(struct MPContext *mpctx, int type,
@@ -555,5 +562,14 @@ void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
double calc_average_frame_duration(struct MPContext *mpctx);
int init_video_decoder(struct MPContext *mpctx, struct track *track);
int get_deinterlacing(struct MPContext *mpctx);
void set_deinterlacing(struct MPContext *mpctx, bool enable);
// Values of MPOpts.softvol
enum {
SOFTVOL_NO = 0,
SOFTVOL_YES = 1,
SOFTVOL_AUTO = 2,
};
#endif /* MPLAYER_MP_CORE_H */
+11 -24
View File
@@ -42,7 +42,6 @@
#include "common/encode.h"
#include "input/input.h"
#include "audio/mixer.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/decode/dec_audio.h"
@@ -195,23 +194,10 @@ void reselect_demux_stream(struct MPContext *mpctx, struct track *track)
{
if (!track->stream)
return;
demuxer_select_track(track->demuxer, track->stream, track->selected);
// External files may need an explicit seek to the correct position, if
// they were not implicitly advanced during playback.
if (track->selected && track->demuxer != mpctx->demuxer) {
bool position_ok = false;
for (int n = 0; n < demux_get_num_stream(track->demuxer); n++) {
struct sh_stream *stream = demux_get_stream(track->demuxer, n);
if (stream != track->stream && stream->type != STREAM_SUB)
position_ok |= demux_stream_is_selected(stream);
}
if (!position_ok) {
double pts = get_current_time(mpctx);
if (pts == MP_NOPTS_VALUE)
pts = 0;
demux_seek(track->demuxer, pts, 0);
}
}
double pts = get_current_time(mpctx);
if (pts != MP_NOPTS_VALUE)
pts += get_track_seek_offset(mpctx, track);
demuxer_select_track(track->demuxer, track->stream, pts, track->selected);
}
// Called from the demuxer thread if a new packet is available.
@@ -266,7 +252,7 @@ static struct track *add_stream_track(struct MPContext *mpctx,
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
demuxer_select_track(track->demuxer, stream, false);
demuxer_select_track(track->demuxer, stream, MP_NOPTS_VALUE, false);
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
@@ -310,6 +296,8 @@ static int match_lang(char **langs, char *lang)
static bool compare_track(struct track *t1, struct track *t2, char **langs,
struct MPOpts *opts)
{
if (!opts->autoload_files && t1->is_external != t2->is_external)
return !t1->is_external;
bool ext1 = t1->is_external && !t1->no_default;
bool ext2 = t2->is_external && !t2->no_default;
if (ext1 != ext2)
@@ -368,6 +356,8 @@ struct track *select_default_track(struct MPContext *mpctx, int order,
pick = NULL;
if (pick && pick->attached_picture && !mpctx->opts->audio_display)
pick = NULL;
if (pick && !opts->autoload_files && pick->is_external)
pick = NULL;
return pick;
}
@@ -467,9 +457,6 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
reselect_demux_stream(mpctx, current);
}
if (track && track->demuxer == mpctx->demuxer)
demux_set_enable_refresh_seeks(mpctx->demuxer, true);
mpctx->current_track[order][type] = track;
if (track) {
@@ -477,8 +464,6 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
reselect_demux_stream(mpctx, track);
}
demux_set_enable_refresh_seeks(mpctx->demuxer, false);
if (type == STREAM_VIDEO && order == 0) {
reinit_video_chain(mpctx);
} else if (type == STREAM_AUDIO && order == 0) {
@@ -624,6 +609,8 @@ void autoload_external_files(struct MPContext *mpctx)
{
if (mpctx->opts->sub_auto < 0 && mpctx->opts->audiofile_auto < 0)
return;
if (!mpctx->opts->autoload_files)
return;
void *tmp = talloc_new(NULL);
char *base_filename = mpctx->filename;
+3 -3
View File
@@ -42,8 +42,8 @@ function mp.input_disable_section(section)
mp.commandv("disable-section", section)
end
-- For dispatching script_binding. This is sent as:
-- script_message_to $script_name $binding_name $keystate
-- For dispatching script-binding. This is sent as:
-- script-message-to $script_name $binding_name $keystate
-- The array is indexed by $binding_name, and has functions like this as value:
-- fn($binding_name, $keystate)
local dispatch_key_bindings = {}
@@ -412,7 +412,7 @@ mp.register_event("shutdown", function() mp.keep_running = false end)
mp.register_event("client-message", message_dispatch)
mp.register_event("property-change", property_change)
-- sent by "script_binding"
-- sent by "script-binding"
mp.register_script_message("key-binding", dispatch_key_binding)
mp.msg = {
+4 -4
View File
@@ -1383,7 +1383,7 @@ function osc_init()
ne.content = "\238\132\144"
ne.visible = have_pl
ne.eventresponder["mouse_btn0_up"] =
function () mp.commandv("playlist_prev", "weak") end
function () mp.commandv("playlist-prev", "weak") end
ne.eventresponder["shift+mouse_btn0_up"] =
function () show_message(mp.get_property_osd("playlist"), 3) end
@@ -1393,7 +1393,7 @@ function osc_init()
ne.content = "\238\132\129"
ne.visible = have_pl
ne.eventresponder["mouse_btn0_up"] =
function () mp.commandv("playlist_next", "weak") end
function () mp.commandv("playlist-next", "weak") end
ne.eventresponder["shift+mouse_btn0_up"] =
function () show_message(mp.get_property_osd("playlist"), 3) end
@@ -1421,7 +1421,7 @@ function osc_init()
ne.eventresponder["mouse_btn0_down"] =
function () mp.commandv("seek", -5, "relative", "keyframes") end
ne.eventresponder["shift+mouse_btn0_down"] =
function () mp.commandv("frame_back_step") end
function () mp.commandv("frame-back-step") end
ne.eventresponder["mouse_btn2_down"] =
function () mp.commandv("seek", -30, "relative", "keyframes") end
@@ -1433,7 +1433,7 @@ function osc_init()
ne.eventresponder["mouse_btn0_down"] =
function () mp.commandv("seek", 10, "relative", "keyframes") end
ne.eventresponder["shift+mouse_btn0_down"] =
function () mp.commandv("frame_step") end
function () mp.commandv("frame-step") end
ne.eventresponder["mouse_btn2_down"] =
function () mp.commandv("seek", 60, "relative", "keyframes") end
+4
View File
@@ -222,6 +222,10 @@ mp.add_hook("on_load", 10, function ()
-- DASH?
if not (json["requested_formats"] == nil) then
if (json["requested_formats"][1].protocol == "http_dash_segments") then
msg.error("MPEG-Dash Segments unsupported, add [protocol!=http_dash_segments] to your ytdl-format.")
return
end
-- video url
streamurl = json["requested_formats"][1].url
Loaded 100 of 198 files, more files were not shown because too many files have changed in this diff. Show more