Compare commits

..
248 Commits
Author SHA1 Message Date
Martin Herkt d18f7bb8cd Release 0.27.0 2017-09-13 03:40:14 +02:00
Niklas Haas 0bb67b1055 vo_opengl: always initialize uniforms on first use
Even if the contents are entirely zero. In the current code, these
entries were left uninitialized. (Which always worked for nvidia - but
randomly blew up for AMD)
2017-09-12 03:00:47 +02:00
Niklas Haas 0c2cb69597 vo_opengl: generalize UBO packing/handling
This is simultaneously generalized into two directions:
1. Support more sc_uniform types (needed for SC_UNIFORM_TYPE_PUSHC)
2. Support more flexible packing (needed for both PUSHC and ra_d3d11)
2017-09-12 02:57:45 +02:00
Niklas Haas 3faf1fb0a4 vo: avoid putting large voctrl_performance_data on stack
This is around 512 kB, which is just way too much. Heap-allocate it
instead. Also cut down the max pass count to 64, since 128 was
unrealistically high even for vo_opengl.
2017-09-11 18:20:18 +02:00
Niklas Haas 71c25df5e6 vo_opengl: refactor timer_pool_measure (again)
Instead of relying on power-of-two buffer sizes and unsigned overflow,
make this code more robust (and also cleaner).

Why can't C get a real modulo operator?
2017-09-11 02:42:50 +02:00
Niklas Haas 8a4f2f0ac0 vo_opengl: fix out-of-bounds access in timer_pool_measure
This was there even before the refactor, but the refactor exposed the
bug. I hate C's useless fucking modulo operator so much. I've gotten hit
by this exact bug way too many times.
2017-09-11 02:07:04 +02:00
Niklas Haas 0fe4a492c4 vo_opengl: fix out-of-bounds read in update_uniform
Since the addition of UBOs, the assumption that the uniform index
corresponds to the pass->params.inputs index is no longer true. Also,
there's no reason it would even need this - since the `input` is also
available directly in sc_uniform.

I have no idea how I've been using this code for as long as I have
without any segfaults until earlier today.
2017-09-11 01:04:08 +02:00
Niklas Haas 1da53248ab vo_opengl: refactor/fix mp_pass_perf code
This was needlessly complicated and prone to breakage, because even the
references to the ring buffer could end up getting invalidated and
containing garbage data on e.g. shader cache flush. For much the same
reason why we can't keep around the *timer_pool, we're also forced to
hard-copy the entire sample buffer per pass per frame.

Not a huge deal, though. This is, what, a few kB per frame? We have more
pressing CPU performance concerns anyway.

Also simplified/fixed some other code.
2017-09-11 00:35:23 +02:00
Niklas Haas d0c87dd579 vo_opengl: add a gamut warning feature
This clearly highlights all out-of-gamut/clipped pixels. (Either too
bright or too saturated)

Has some (documented) caveats. Also make TONE_MAPPING_CLIP stop actually
clamping the value range (it's unnecessary and breaks this feature).
2017-09-10 18:19:46 +02:00
Martin Herkt b19b0869d6 man: correct default for --audio-file-auto
Fixes #4851
2017-09-10 07:05:33 +02:00
Akemi 9cb7f87b86 osx: fix media keys input when other Apps steal the priority
other Apps do the same as mpv and tap into the global event chain.
events that are handled are not being propagated down the event chain.
that can lead to mpv not getting any media key events anymore when they
are held back by other Apps. we will just move mpv back to the top of
the event list when mpv is refocused and is not at the top of the list
any more.

Fixes #4834
2017-09-07 20:42:26 +02:00
Niklas Haas 5771f7abf4 vo_opengl: add support for vulkan GLSL dialect
Redefining texture1D / texture3D seems to be illegal, they are already
built-in macros or something. So just use tex1D and tex3D instead.

Additionally, GL_KHR_vulkan_glsl requires using explicit vertex
locations and bindings, so make some changes to facilitate this. (It
also requires explicitly setting location=0 for the color attachment
output)
2017-09-04 13:53:23 +02:00
Niklas Haas 62f0677614 vo_opengl: use rgba16 for 3DLUTs instead of rgb16
Vulkan compat. rgb16 doesn't exist on hardware anyway, might as well
just generate the 3DLUT against rgba16 as well. We've decided this is
the simplest way to do vulkan compatibility: just make sure we never
actually need 3-component textures.
2017-09-04 13:53:14 +02:00
Niklas Haas 8cf5799ab1 vo_opengl: refactor scaler LUT weight packing/loading
This is mostly done so we can support using textures with more
components than the scaler LUTs have entries. But while we're at it,
also change the way the weights are packed so that they're always
sequential with no gaps. This allows us to simplify
pass_sample_separated_get_weights as well.
2017-09-04 13:53:14 +02:00
Niklas Haas f589a3bd78 vo_opengl: scale deband-grain to the signal range
This prevents blowing up for high dynamic range sources, where a noise
level of 48 can suddenly mean 4800.
2017-09-03 21:51:48 +02:00
James Ross-Gowan 9a28088e74 filter_kernels: correct spline64 kernel
This seems to have had some copy/paste errors. It should now match the
implementation in fmtconv:
https://github.com/EleonoreMizo/fmtconv/blob/00453a86dd73/src/fmtcl/ContFirSpline64.cpp#L58-L76
2017-09-03 21:18:06 +02:00
Ricardo Constantino d280b3db93 ytdl_hook: resolve relative paths when joining segment urls
FFmpeg/mpv don't do it automatically.
See #4827
2017-09-03 13:42:51 +01:00
wm4 181b071431 audio: fix format change regression
Ever since the mp_aframe change, audio mid-stream format changes crash.
I have no idea why the recent mp_aframe change triggers this. Didn't
look too deeply into it either. It appears to work now, though.

Fixes #4828.
2017-09-03 13:09:49 +02:00
James Ross-Gowan 650d4e3401 input: fix error in MP_KEY_IS_WHEEL
Whoops.
2017-09-03 20:46:06 +10:00
James Ross-Gowan 7897f79217 input: merge mouse wheel and axis keycodes
Mouse wheel bindings have always been a cause of user confusion.
Previously, on Wayland and macOS, precise touchpads would generate AXIS
keycodes and notched mouse wheels would generate mouse button keycodes.
On Windows, both types of device would generate AXIS keycodes and on
X11, both types of device would generate mouse button keycodes. This
made it pretty difficult for users to modify their mouse-wheel bindings,
since it differed between platforms and in some cases, between devices.

To make it more confusing, the keycodes used on Windows were changed in
18a45a42d5 without a deprecation period or adequate communication to
users.

This change aims to make mouse wheel binds less confusing. Both the
mouse button and AXIS keycodes are now deprecated aliases of the new
WHEEL keycodes. This will technically break input configs on Wayland and
macOS that assign different commands to precise and non-precise scroll
events, but this is probably uncommon (if anyone does it at all) and I
think it's a fair tradeoff for finally fixing mouse wheel-related
confusion on other platforms.
2017-09-03 20:31:44 +10:00
James Ross-Gowan 8fe4aa94ee cocoa: fix button numbering for back/forward
It seems like the Cocoa backend used to return the same mpv keycodes for
mouse back/forward as it did for scrolling up and down. Fix this by
explicitly mapping all Cocoa button numbers to the right mpv keycodes.
2017-09-03 20:31:44 +10:00
James Ross-Gowan 957e9a37db input: use mnemonic names for mouse buttons
mpv's mouse button numbering is based on X11 button numbering, which
allows for an arbitrary number of buttons and includes mouse wheel input
as buttons 3-6. This button numbering was used throughout the codebase
and exposed in input.conf, and it was difficult to remember which
physical button each number actually referred to and which referred to
the scroll wheel.

In practice, PC mice only have between two and five buttons and one or
two scroll wheel axes, which are more or less in the same location and
have more or less the same function. This allows us to use names to
refer to the buttons instead of numbers, which makes input.conf syntax a
lot easier to remember. It also makes the syntax robust to changes in
mpv's underlying numbering. The old MOUSE_BTNx names are still
understood as deprecated aliases of the named buttons.

This changes both the input.conf syntax and the MP_MOUSE_BTNx symbols in
the codebase, since I think both would benefit from using names over
numbers, especially since some platforms don't use X11 button numbering
and handle different mouse buttons in different windowing system events.

This also makes the names shorter, since otherwise they would be pretty
long, and it removes the high-numbered MOUSE_BTNx_DBL names, since they
weren't used.

Names are the same as used in Qt:
https://doc.qt.io/qt-5/qt.html#MouseButton-enum
2017-09-03 20:31:44 +10:00
wm4 449d9725c9 stream_lavf: use avio_read_partial()
Possibly improves latency and such things.
2017-09-01 17:59:03 +02:00
wm4 9f0e358827 vo_opengl: fix overlay mode (again)
Did I mention yet that I regret this overlay mode thing?
2017-08-30 12:19:32 +02:00
wm4 96462040ec vd_lavc: change auto-probe order to prefer cuda over vdpau-copy
This aims at making --opengl-hwdec-interop=cuda --hwdec=yes select the
correct decoding mode: cuda instead of vdpau-copy.
2017-08-30 12:08:40 +02:00
wm4 93d40ac480 DOCS/contribute.md: coding style exception 2017-08-30 10:34:16 +02:00
wm4 a9571fcc0f vo_opengl: don't discard buffered video on redundant resize calls
If a VO-area option changes, gl_video_resize() is called
unconditionally. This function does something even if the size does not
change (at least it discards buffered frames for interpolation), which
can lead to stutter when you keep firing option change events during
playback.

Check for an actual resize, and if nothing changes, exit early.
2017-08-29 15:15:34 +02:00
wm4 a46500a2c8 vo_opengl: don't assume imgfmt=0 is valid
Could cause a crash if anything called ra_get_imgfmt_desc(imgfmt=0). Let
it fail correctly. This can happen if a hwdec backend does not set
hw_subfmt correctly.
2017-08-29 15:05:32 +02:00
Niklas Haas cc79d48d22 vo_opengl: fix the renderpass target format at creation time
Required for vulkan.
2017-08-27 14:37:47 +02:00
Niklas Haas 7baa18d5f8 vo_opengl: fix misleading comment in ra.h
tex_upload is not just for buffers
2017-08-27 14:36:28 +02:00
Niklas Haas 1d47473a7b vo_opengl: use UBOs where supported/required
This also introduces RA_CAP_GLOBAL_UNIFORM. If this is not set, UBOs
*must* be used for non-bindings. Currently the cap is ignored though,
and the shader_cache *always* generates UBO-using code where it can.
Could be made an option in principle.

Only enabled for drivers new enough to support explicit UBO offsets,
just in case...

No change to performance, which is probably what we expect.
2017-08-27 14:36:04 +02:00
Niklas Haas 136cf2b770 vo_opengl: add support for UBOs
Not actually used by anything yet, but straightforward enough to add to
the RA API for starters.
2017-08-27 14:36:00 +02:00
Niklas Haas 8404a354e5 vo_opengl: clarify RA_CAP_DIRECT_UPLOAD
This no longer concerns the API user except in as much as the API user
probably wants to know whether or not PBOs are active, so keep around
the CAP field even though it's mostly useless now.
2017-08-27 14:36:00 +02:00
Niklas Haas 7684fda6ac vo_opengl: refactor shader_cache binding
There's no reason to be needlessly wasteful with our binding points
here. Just add a CAP for it.
2017-08-27 14:36:00 +02:00
Niklas Haas 45bae90f4d vo_opengl: be explicit about IMG_RW
Both vulkan and opengl distinguish between rendering to an image and
using an image as a storage attachment. So make this an explicit
capability instead of lumping it in with render_dst. (That way we could
support, for example, using an image as a storage attachment without
requiring a framebuffer)

The real reason for this change is that you can directly use the output
FBO as a storage attachment on vulkan but you can't on opengl, which
makes this param structly separate from render_dst.
2017-08-27 14:36:00 +02:00
Niklas Haas f40717a664 vo_opengl: use size_t offset for vertex offsets
I don't like the feeling of "reusing" the int binding for this. It
feels... wrong, somehow. I'd prefer to use an explicit "offset" field.
(Plus, I might re-use this for uniform buffers or something)

YMMV
2017-08-27 14:36:00 +02:00
Akemi d355cb618c osx: fix build on macOS 10.11 and earlier
98f5579 added a safeguard for the deactivation of the automatic tab bar
but it still needs a compile time guard. add a forward declaration for
the setAllowsAutomaticWindowTabbing method of NSWindow.

Fixes #4783
2017-08-26 11:42:50 +02:00
James Ross-Gowan eacd97d536 mp_image: include config.h directly
This is needed for HAVE_SSE4_INTRINSICS. config.h used to be included as
a transitive dependency of vf.h, but the include statement was removed
from vf.h in 8f2ccba71b.

Also silence an unused variable warning that was introduced in the same
commit.
2017-08-26 00:41:04 +10:00
James Ross-Gowan 9aa091615e video: increase --monitorpixelaspect range
Some people use very wide display modes such as 3840x240 with their CRT
televisions because it lessens scaling artifacts in video game
emulators. When using a 3840x240 display mode on a 4:3 CRT television,
the pixel aspect ratio is 1:12, so in order to watch a video with mpv on
the same television without changing the display mode, the user should
use --monitorpixelaspect=1:12.

Unfortunately, 1:12 (or 0.083) was out of range for the
--monitorpixelaspect option. There was no good reason for this, so
extend the range of the option to 1:32-32:1 (0.03125-32,) which should
be more than enough to support "super-wide" display modes like these.

This is related to #4483, but it doesn't fix the issue (which was to do
with subtitle rendering.)
2017-08-26 00:40:11 +10:00
wm4 68dc7d1695 vo_opengl: allow selection of true 32 bit float if float16 unavailable
Shouldn't make a difference for OpenGL (even with the weird duplication
of these functions removed). Might be useful for the WIP vulkan backend.
2017-08-24 22:44:41 +02:00
wm4 ecef999d19 vd_lavc: work around some more idiotic FFmpeg nonsense
Like in commit bd356333c7, but for the other hwaccels as well.

Fixes #4794.
2017-08-24 11:35:17 +02:00
wm4 b62150bd6b command: restore OSD marker for video equalizer properties
Commit 03cf150ff3 accidentally dropped these. Readd them in a simpler
way (so only a property_osd_display[] entry is enough). This commit
doesn't actually touch the video equalizer properties, because the
default value of 0 for the marker is what they require anyway.
2017-08-23 14:23:37 +02:00
wm4 60ba7eebd7 vd_lavc: fix mid-stream hwdec fallback
Not resetting hwdec_request_reinit caused it to flush on every packet,
which not only caused it to fail triggering the actual fallback, and let
it never decode a new frame, but also to get stuck on EOF.
2017-08-23 14:02:44 +02:00
wm4 997e1fb621 audio: fix spdif mode
Not sure how this was not caught before. It crashed when trying to use
spdif mode.
2017-08-23 12:14:11 +02:00
wm4 f6c4e4ab76 DOCS/interface-changes.rst: don't suggest @deinterlace label
--vf-defaults always applies, and the label is ignored. So don't suggest
using it, it would be misleading.
2017-08-23 12:11:56 +02:00
wm4 8f2ccba71b video: change --deinterlace behavior
This removes all GPL only code from it, and that's the whole purpose.
Also happens to be much simpler.

The "deinterlace" option still sort of exists, but only as runtime
changeable option. The main change in behavior is that the property will
not report back the actual deint state. Or in other words, if inserting
or initializing the filter fails, the deinterlace property will still
return "yes". This is in line with most recent behavior changes to
properties and options.
2017-08-22 19:08:07 +02:00
wm4 0c88b661f9 manpage: video equalizer properties now behave differently
No more special behavior.
2017-08-22 18:28:21 +02:00
wm4 ab39518719 manpage: remove removed "osd" command
Someone pointed this out on github.
2017-08-22 17:33:30 +02:00
wm4 1b2185657f vo_direct3d: fix build
Broken by previous commit. Fix completely untested.
2017-08-22 17:32:05 +02:00
wm4 03cf150ff3 video: redo video equalizer option handling
I really wouldn't care much about this, but some parts of the core code
are under HAVE_GPL, so there's some need to get rid of it. Simply turn
the video equalizer from its current fine-grained handling with vf/vo
fallbacks into global options. This makes updating them much simpler.

This removes any possibility of applying video equalizers in filters,
which affects vf_scale, and the previously removed vf_eq. Not a big
loss, since the preferred VOs have this builtin.

Remove video equalizer handling from vo_direct3d, vo_sdl, vo_vaapi, and
vo_xv. I'm not going to waste my time on these legacy VOs.

vo.eq_opts_cache exists _only_ to send a VOCTRL_SET_EQUALIZER, which
exists _only_ to trigger a redraw. This seems silly, but for now I feel
like this is less of a pain. The rest of the equalizer using code is
self-updating.

See commit 96b906a51d for how some video equalizer code was GPL only.
Some command line option names and ranges can probably be traced back to
a GPL only committer, but we don't consider these copyrightable.
2017-08-22 17:01:35 +02:00
wm4 d67aa6da6b vf_eq: remove this filter
Both the video equalizer command/option glue, which drives this filter,
as well as the filter itself are slightly GPL contaminated. So it goes.

After this commit, "--vf=eq" will actually use libavfilter's vf_eq (if
FFmpeg was compiled in GPL mode), but it has different options and will
not listen to the equalizer VOCTRLs.
2017-08-22 15:58:49 +02:00
wm4 d2bdb72b69 options: add a thread-safe way to notify option updates
So far, we had a thread-safe way to read options, but no option update
notification mechanism. Everything was funneled though the main thread's
central mp_option_change_callback() function. For example, if the
panscan options were changed, the function called vo_control() with
VOCTRL_SET_PANSCAN to manually notify the VO thread of updates. This
worked, but's pretty inconvenient. Most of these problems come from the
fact that MPlayer was written as a single-threaded program.

This commit works towards a more flexible mechanism. It adds an update
callback to m_config_cache (the thing that is already used for
thread-safe access of global options).

This alone would still be rather inconvenient, at least in context of
VOs. Add another mechanism on top of it that uses mp_dispatch_queue, and
takes care of some annoying synchronization issues. We extend
mp_dispatch_queue itself to make this easier and slightly more
efficient.

As a first application, use this to reimplement certain VO scaling and
renderer options. The update_opts() function translates these to the
"old" VOCTRLs, though.

An annoyingly subtle issue is that m_config_cache's destructor now
releases pending notifications, and must be released before the
associated dispatch queue. Otherwise, it could happen that option
updates during e.g. VO destruction queue or run stale entries, which is
not expected.

Rather untested. The singly-linked list code in dispatch.c is probably
buggy, and I bet some aspects about synchronization are not entirely
sane.
2017-08-22 15:50:33 +02:00
wm4 5361c0b5d8 build: pick a main-fn source file only if cplayer is built
libmpv does not need this.
2017-08-22 11:40:39 +02:00
Niklas Haas 09c501a40e vo_opengl: refactor tex_upload to ra_buf_pool
Also refactors the usage of tex_upload to make ra_tex_upload_pbo a
RA-internal thing again.

ra_buf_pool has the main advantage of being dynamically sized depending
on buf_poll, so for OpenGL we'll end up only using one buffer (when not
persistently mapping) - while for vulkan we'll use as many as necessary,
which depends on the swapchain depth anyway.
2017-08-22 09:55:49 +02:00
Ricardo Constantino 371000108a TOOLS/lua/autodeint: update to lavfi-bridge
Also use lavfi setfield instead of removed field-dominance.
Remove missing remainder of field-dominance in docs.
2017-08-21 18:12:10 +01:00
wm4 0bfeba2d9a win32: fix massive memory corruption (take 2)
As pointed out by uau on IRC, the pointer to info is still used outside
of the lock. An extremely small race condition window, but still a race
condition.
2017-08-21 18:42:04 +02:00
wm4 d431111b06 win32: fix massive memory corruption
The struct m_thread_info pointer is part of an array, that will be
reallocated if another thread is created while the run_thread is just
being called. In previous versions of this code, the pointer was stable
(as long as the thread existed), so this was overlooked.

Fixes #4770. I'm not sure why this triggers it so reliably, while it
remained undetected otherwise.
2017-08-21 17:47:00 +02:00
wm4 b21e0746f6 ao_rsound: allow setting the host
Completely untested (rsound dev libs unavailable on my system). Trivial
enough that it's very likely that it'll just work. No port selection,
but could be added by parsing it as part of the device name.

Should fix #4714.
2017-08-21 15:46:00 +02:00
wm4 437469c103 x11: fix that window could be resized when using embedding
Somewhat lazy fix. The code isn't particularly robust or correct wrt.
window embedding.

Fixes #4784.
2017-08-21 15:15:55 +02:00
wm4 028faacff5 video: add metadata handling for spherical video
This adds handling of spherical video metadata: retrieving it from
demux_lavf and demux_mkv, passing it through filters, and adjusting it
with vf_format. This does not include support for rendering this type of
video.

We don't expect we need/want to support the other projection types like
cube maps, so we don't include that for now. They can be added later as
needed.

Also raise the maximum sizes of stringified image params, since they
can get really long.
2017-08-21 14:56:07 +02:00
Martin Herkt 82d9419f62 Revert "x11: drop xscrnsaver use"
This broke screensaver/powersave inhibition with at least KDE and
LXDE. This is a release blocker.

Since fdo, KDE and GNOME idiots seem to be unable to reach
a consensus on a simple protocol, this seems unlikely to get
fixed upstream this year, so revert this change.

Fixes #4752.
Breaks #4706 but I don’t give a damn.

This reverts commit 3f75b3c343.
2017-08-20 09:18:39 +02:00
Martin Herkt a82007dd1e Revert "x11: use xdg-screensaver suspend/resume"
This reverts commit 6694048272.
2017-08-20 09:11:07 +02:00
James Ross-Gowan 2d78705b73 context_angle: remove unused variable
Unused since 16e0a39482.
2017-08-20 16:46:02 +10:00
James Ross-Gowan 08ec444ba5 context_angle: replace hard-coded array size 2017-08-19 22:05:29 +10:00
Akemi 98f5579c02 osx: remove the standard "Show Tab Bar" menu item
it's unused and added automatically. we need to guard the deactivation
since it's a 10.12+ feature.
2017-08-18 21:44:33 +02:00
Akemi 344b75f52d osx: code cleanups and cosmetic fixes
silence build warnings, clean up code style and remove unused code.
2017-08-18 19:47:47 +02:00
Akemi e0a2d15216 osx: refactor menu bar creation
make the menu bar look more like a standard macOS menu bar, with all the
standard menu items like About, Help, Open, etc.
2017-08-18 19:20:41 +02:00
Akemi 48ab72b478 osx: move menu bar creation into its own file 2017-08-18 19:20:41 +02:00
wm4 1f7fe1597d audio: fix uninitialized data access
dst was not supposed to be initialized, the mp_audio_ setters (which
initialize dst's fields) assume it is -> shit happens. Regression from
recent changes. Was probably harmless.
2017-08-18 17:53:38 +02:00
Niklas Haas 01058b16f9 vo_opengl: allow texture uploads to fail
Surprisingly makes the code shorter, not longer
2017-08-18 02:33:29 +02:00
Frédéric Brière be05c9eb58 options: correct two remaining instances of singular file-path options
These were left behind by e4bc563fd2.
2017-08-18 00:51:52 +01:00
Niklas Haas abb7e88e3c vo_opengl: clarify the ra_fns.debug_marker 2017-08-18 01:10:37 +02:00
Niklas Haas 46d86da630 vo_opengl: refactor RA texture and buffer updates
- tex_uploads args are moved to a struct
- the ability to directly upload texture data without going through a
  buffer is made explicit
- the concept of buffer updates and buffer polling is made more explicit
  and generalized to buf_update as well (not just mapped buffers)
- the ability to call tex_upload/buf_update on a tex/buf is made
  explicit during tex/buf creation
- uploading from buffers now uses an explicit offset instead of
  implicitly comparing *src against buf->data, because not all buffers
  may actually be persistently mapped
- the initial_data = immutable requirement is dropped. (May be re-added
  later for D3D11 if that ever becomes a thing)

This change helps the vulkan abstraction immensely and also helps move
common code (like the PBO pooling) out of ra_gl and into the
opengl/utils.c

This also technically has the side-benefit / side-constraint of using
PBOs for OSD texture uploads as well, which actually seems to help
performance on machines where --opengl-pbo is faster than the naive code
path. Because of this, I decided to hook up the OSD code to the
opengl-pbo option as well.

One drawback of this refactor is that the GL_STREAM_COPY hack for
texture uploads "got lost", but I think I'm happy with that going away
anyway since DR almost fully deprecates it, and it's not the "right
thing" anyway - but instead an nvidia-only hack to make this stuff work
somewhat better on NUMA systems with discrete GPUs.

Another change is that due to the way fencing works with ra_buf (we get
one fence per ra_buf per upload) we have to use multiple ra_bufs instead
of offsets into a shared buffer. But for OpenGL this is probably better
anyway. It's possible that in future, we could support having
independent “buffer slices” (each with their own fence/sync object), but
this would be an optimization more than anything. I also think that we
could address the underlying problem (memory closeness) differently by
making the ra_vk memory allocator smart enough to chunk together
allocations under the hood.
2017-08-18 00:34:34 +02:00
Niklas Haas 9ca5a2a5d8 vo_opengl: make blitting an explicit capability
Instead of merging it into render_dst. This is better for vulkan,
because blitting in vulkan both does not require a FBO *and* requires a
different image layout.

Also less "hacky" for OpenGL, since now the weird blit=FBO requirement
is an implementation detail of ra_gl
2017-08-18 00:19:14 +02:00
Niklas Haas 8209376468 vo_opengl: make ra_fns.timer_create optional 2017-08-18 00:19:14 +02:00
Ricardo Constantino 1171f92beb TOOLS/autocrop.lua: fix cropdetect black limit for 10-bit videos
Also update to use newer lavfi-bridges for the relevant filters to
shut up warnings about deprecated crop filter.
2017-08-17 20:43:25 +01:00
wm4 b9430309ec stream: add an assert() to an obscure seek case
This affects small seeks backwards while within the buffer. Demuxers
usually avoid this, so it's probably not triggered very often. (Although
demux_mkv.c potentially triggers it often, and it uses stream_peek() to
explicitly guarantee that it can use this code to seek back.) The
condition is complex enough to warrant an assertion.
2017-08-17 17:38:07 +02:00
wm4 79a39aeebd demux_lavf: use partial read for AVIOContext.read_packet
More betterer.
2017-08-17 17:28:01 +02:00
wm4 d7b893cf35 manpage: update --lavfi-complex description 2017-08-17 10:56:53 +02:00
Niklas Haas b74067bc74 vo_opengl: remove redundant #defines in unsharp_hook
These are no longer valid anyway, and the code doesn't use them.
2017-08-17 09:12:28 +02:00
wm4 158768513c audio: fix build on Libav
Sigh...
2017-08-16 21:26:16 +02:00
wm4 1f593beeb4 audio: introduce a new type to hold audio frames
This is pretty pointless, but I believe it allows us to claim that the
new code is not affected by the copyright of the old code. This is
needed, because the original mp_audio struct was written by someone who
has disagreed with LGPL relicensing (it was called af_data at the time,
and was defined in af.h).

The "GPL'ed" struct contents that surive are pretty trivial: just the
data pointer, and some metadata like the format, samplerate, etc. - but
at least in this case, any new code would be extremely similar anyway,
and I'm not really sure whether it's OK to claim different copyright. So
what we do is we just use AVFrame (which of course is LGPL with 100%
certainty), and add some accessors around it to adapt it to mpv
conventions.

Also, this gets rid of some annoying conventions of mp_audio, like the
struct fields that require using an accessor to write to them anyway.

For the most part, this change is only dumb replacements of mp_audio
related functions and fields. One minor actual change is that you can't
allocate the new type on the stack anymore.

Some code still uses mp_audio. All audio filter code will be deleted, so
it makes no sense to convert this code. (Audio filters which are LGPL
and which we keep will have to be ported to a new filter infrastructure
anyway.) player/audio.c uses it because it interacts with the old filter
code. push.c has some complex use of mp_audio and mp_audio_buffer, but
this and pull.c will most likely be rewritten to do something else.
2017-08-16 21:10:54 +02:00
James Ross-Gowan 16e0a39482 vo_opengl: extract non-ANGLE specific D3D11 code
This extracts non-ANGLE specific code to d3d11_helpers.c, which is
modeled after egl_helpers.c. Currently the only consumer is
context_angle.c, but in future this may allow the D3D11 device and
swapchain creation logic to be reused in other backends.

Also includes small improvements to D3D11 device creation. It is now
possible to create feature level 11_1 devices (though ANGLE does not
support these,) and BGRA swapchains, which might be slightly more
efficient than ARGB, since its the same format used by the compositor.
2017-08-17 00:28:38 +10:00
wm4 f24612db44 img_format: #if -> #ifdef
Oops.
2017-08-16 11:15:52 +02:00
Ricardo Constantino 257dbdf06f TOOLS/autoload.lua: implement natural sorting 2017-08-16 10:00:27 +01:00
wm4 6694048272 x11: use xdg-screensaver suspend/resume
If it doesn't work this time, I'll remove all X11 screensaver code.

Fixes #4763.
2017-08-15 20:32:44 +02:00
wm4 34ab0386cb vo_rpi: fix operation
Commit 697c4389a9 worked "almost". I couldn't test it at the time.
2017-08-15 19:41:23 +02:00
wm4 935df644af vo_opengl: fix incorrect glBindFramebuffer() call
Used the wrong binding.
2017-08-15 19:08:54 +02:00
wm4 b44e81d9c3 vo_opengl: fix dangling pointers when VAOs are not available
This is for legacy GL: if VAOs are not available, the helper has to
specify vertex attributes again on every rendering. gl_vao_init() keeps
the vertex array for this purpose. Unfortunately, a temporary argument
was passed to the function, instead of the permanent copy.

Also, it didn't use num_entries (instead expected the array being
terminated by a {0} entry). Fix that source code indentation too.
2017-08-15 18:59:59 +02:00
wm4 72a8120daa Revert "build: rpi: rely on pkgconfig for compiler flags"
This reverts commit ea40fa36ee.

This caused strange runtime failure on Raspbian (when running mpv,
vc_dispmanx_display_open() returned 0, while other dispmanx using
programs were fine). The problem must have been something about the
compiler flags, maybe linking order or set of include paths.
2017-08-15 18:41:16 +02:00
wm4 16d22ea02a img_format: better exclusion of bayer formats
These are useless and shouldn't be confused with normal RGB formats.
Replace the earlier hack checking the format name with a proper check.
(Not sure when this flag was added. Libav won't have it anyway, but also
no bayer formats.)
2017-08-15 17:03:35 +02:00
wm4 63b1031ca2 vo_opengl: support float pixel formats
Like AV_PIX_FMT_GBRPF32LE.
2017-08-15 17:00:35 +02:00
wm4 817bb2bbbe options: change --loop semantics
As announced by the previous deprecation.
2017-08-14 20:01:08 +02:00
wm4 df8cc84f47 vo_opengl: remove DR image layouting code to renderer
No reason to have it in a higher level.
2017-08-14 19:57:44 +02:00
wm4 68201f4591 player: make refresh seeks slightly more robust
Refresh seeks are automatically issued when changing filters, which
improves user experience if these filters change buffering or such.

The refresh seek could actually overwrite a previously ongoing seek:

	set pause yes
	set time-pos 10
	set vf ""

Here, the video code issued a refresh seek to the previous video
position, which could be different from the previously triggered (and
still ongoing) seek, this overwriting the seek.

Factor all refresh seek handling into a new function, and make it handle
ongoing seeks correctly.

Remove the weird new canonical_pts field, which actually had no use.

Fixes #4757.
2017-08-14 14:02:13 +02:00
wm4 b6d79deebb player: do not destroy VO immediately if there is no video track
Commit f1d161d55f accidentally added the handle_force_window() call if
no track is selected.

This was OK, but breaks something like "mpv *", where some files are not
playable (like subtitle files) - the unplayable files would remove and
recreate the VO window, which is annoying.

Just drop the call again.
2017-08-14 12:19:54 +02:00
wm4 cacc6db2a3 vo_opengl: hwdec_vdpau: use correct source texture size
In commit c6fafbffac we accidentally set the logical texture size to
the cropped video size, which is not correct. This caused rendering
artifacts in some cases.

Use the video surfaces size instead. Since the current mp_image_params
contains the cropped size only, wrapper texture creation has to be moved
to the _map function. Move the same code for the mixer case (strictly
speaking this is not needed, but seems more symmetric).

(Also there is no need to clear gl_textures on uninit - leftover from
the old hwdec mapper API. So we just drop that part.)

Fixes #4760.
2017-08-14 12:17:39 +02:00
wm4 c6628a5fb6 player: add --track-auto-selection option
I imagine this is useful. Or maybe it isn't.
2017-08-12 23:44:47 +02:00
wm4 b531332835 player: fix another audio resync issue
This oddly triggers bogus EOF when switching filter graphs between two
audio files (in this case, "[vid1]f[vo];[aid2]f[ao]"->"[aid1]f[ao]",
with aid2 being an external audio source).

This commit also fixes desync when seeking with an external file
connected via --lavfi-complex.

(Yes, the audio resync code is cursed.)
2017-08-12 23:35:12 +02:00
wm4 f1d161d55f player: make --lavfi-complex changeable at runtime
Tends to be somewhat glitchy if subtitles are enabled, and you enable
and disable tracks.

On error, this will disable --lavfi-complex, which will result in
whatever behavior.
2017-08-12 23:10:40 +02:00
wm4 baead23ea0 af_lavrresample: don't call swr_set_compensation() unless necessary
This was _always_ called, even if the resampling was static, or the
filter was inserted for format conversion only. This should have been
fine, as I expected the function not to enable resampling when the
compensation is unset, and the source/target rates are the same. But
this is not the case, and it always enables resampling.

So explicitly avoid the call. If we have already called it successfully,
it's better not do avoid it (to overwrite the previous compensation
value), but it will also be cheap/no-op then.

Probably fixes #4716.
2017-08-12 12:12:52 +02:00
wm4 c20df5b3e1 vo_opengl: hwdec_ios: fix build 2017-08-11 22:00:44 +02:00
wm4 c391640645 player: fix --lavfi-complex freeze
Commit 0e0b87b6f3 fixed that dropped packets did not trigger further
work correctly. But it also made trivial --lavfi-complex freeze. The
reason is that the meaning if DATA_AGAIN was overloaded: the decoders
meant that they should be called again, while lavfi.c meant that other
outputs needed to be checked again. Rename the latter meaning to
DATA_STARVE, which means that the current input will deliver no more
data, until "other" work has been done (like reading other outputs, or
feeding input).

The decoders never return DATA_STARVE, because they don't get input from
the player core (instead, they get it from the demuxer directly, which
is why they still can return DATA_WAIT).

Also document the DATA_* semantics in the enum.

Fixes #4746.
2017-08-11 21:29:35 +02:00
wm4 8b1d4b978d vo_opengl: remove some dead code
These were replaced by ra equivalents, and with the recent changes, all
of them became fully unused.
2017-08-11 21:29:35 +02:00
wm4 1d0bf4073b vo_opengl: handle probing GL texture formats better
Retrieve the depth for each component and internal texture format
separately. Only for 8 bit per component textures we assume that all
bits are used (or else we would in my opinion create too many probe
textures).

Assuming 8 bit components are always correct also fixes operation in
GLES3, where we assumed that each component had -1 bits depth, and this
all UNORM formats were considered unusable. On GLES, the function to
check the real bit depth is not available. Since GLES has no 16 bit
UNORM textures at all, except with the MPGL_CAP_EXT16 extension, just
drop the special condition for it. (Of course GLES still manages to
introduce a funny special case by allowing GL_LUMINANCE , but not
defining GL_TEXTURE_LUMINANCE_SIZE.)

Should fix #4749.
2017-08-11 21:29:35 +02:00
wm4 e7a9bd6937 vo_opengl: remove another unneeded GL include
Getting mp_pass_perf seriously requires including vo.h???
2017-08-11 21:29:35 +02:00
wm4 697c4389a9 rpi: fix build
Runtime untested, because I get this:

  [vo/rpi] Could not get DISPMANX objects.

This happened even when building older git versions, and on a RPI image
that hasn't changed in the recent years. I don't know how to make this
POS work again, so I guess if there's a bug in the new code, it will
remain broken.
2017-08-11 21:29:35 +02:00
wm4 de3eecce7f vo_opengl: move strictly private ra_gl structs to .c file
So that nothing accidentally accesses these.
2017-08-11 21:29:35 +02:00
Ricardo Constantino 1a1f6e8581 man/mpv: document bd:// expected titles and title list 2017-08-11 19:37:01 +01:00
wm4 fba4e8aa40 vo_opengl: remove some indirect GL header inclusions from core renderer 2017-08-10 21:36:57 +02:00
wm4 c6fafbffac vo_opengl: separate hwdec context and mapping, port it to use ra
This does two separate rather intrusive things:

 1. Make the hwdec context (which does initialization, provides the
    device to the decoder, and other basic state) and frame mapping
    (getting textures from a mp_image) separate. This is more
    flexible, and you could map multiple images at once. It will
    help removing some hwdec special-casing from video.c.
 2. Switch all hwdec API use to ra. Of course all code is still
    GL specific, but in theory it would be possible to support other
    backends. The most important change is that the hwdec interop
    returns ra objects, instead of anything GL specific. This removes
    the last dependency on GL-specific header files from video.c.

I'm mixing these separate changes because both requires essentially
rewriting all the glue code, so better do them at once. For the same
reason, this change isn't done incrementally.

hwdec_ios.m is untested, since I can't test it. Apart from superficial
mistakes, this also requires dealing with Apple's texture format
fuckups: they force you to use GL_LUMINANCE[_ALPHA] instead of GL_RED
and GL_RG. We also need to report the correct format via ra_tex to
the renderer, which is done by find_la_variant(). It's unknown whether
this works correctly.

hwdec_rpi.c as well as vo_rpi.c are still broken. (I need to pull my
RPI out of a dusty pile of devices and cables, so, later.)
2017-08-10 21:24:31 +02:00
wm4 b2fb3f1340 vo_opengl: hwdec_cuda: fix filtering mode
Probably explains quality issues in some cases.
2017-08-09 21:02:23 +02:00
wm4 9c5dcf9398 vo_opengl: shrink the hwdec overlay API
Just remove one callback, and fold the functionality into the other one.
RPI will still not compile, so the hwdec_rpi.c changes are untested.
2017-08-09 20:57:37 +02:00
wm4 de6d3f8ca1 vd_lavc: fix device leak with copy-mode hwaccels
Apparently this was broken by the "ctx->hwdec" check in the if condition
guarding the destroy call, and "ctx->hwdec = NULL;" was moved up
earlier, making this always dead code.

This should probably be refcounted or so, although that could make it
worse as well. For now, add a flag whether the device should be
destroyed.

Fixes #4735.
2017-08-09 12:00:56 +02:00
wm4 7397e8ab42 vo_opengl: add a hack for Apple's broken iOS hwdec stuff
As seen in hwdec_ios.m, it insists on using the legacy luminance alpha
formats for mapped textures.
2017-08-08 17:53:19 +02:00
Niklas Haas 6087f63003 vo_opengl: go back to using GL_TIME_ELAPSED
Less flexible than GL_TIMESTAMP but supported by more platforms. This
will mean that nested queries have to be detected and silently omitted,
but oh well. Not much use for them anyway.

Fixes #4721.
2017-08-08 17:08:25 +02:00
wm4 7f9193b353 Revert "mp_image_pool: disallow adding read only frames"
This reverts commit 0ce3dce03a.

We actually explicitly add read-only frames in some of the hwaccel code
via mp_image_new_custom_ref(), which sets AV_BUFFER_FLAG_READONLY. It's
probably better to keep it this way.

Fixes #4652 (and some related issues with D3D).
2017-08-08 16:16:55 +02:00
wm4 f7fb6e186a options: fix --include
This is really obnoxious. --include parses into the default profile, but
when used on the command line, it did never get applied. So we have to
apply it when the exact conditions for this are met.

Fixes #4673.
2017-08-08 15:54:51 +02:00
wm4 0e36b77aae demux_mkv: avoid an error message in a corner case
If --demuxer-mkv-probe-start-time=no is used, and a seek is triggered on
start, then cluster_start will be 0, and the packet reading code will
print an error message about not finding valid data. This fixes itself
since it invokes the resync code, but it's still pretty ugly. Avoid this
by always initializing cluster_start.
2017-08-08 15:19:50 +02:00
wm4 2ad79785cc build: use main-fn-cocoa.c on OSX
Broken by a434892208.
2017-08-08 14:56:44 +02:00
wm4 0e0b87b6f3 player: fix confusion in audio resync code
Just the audio resync code in its normal state: buggy. This time,
AD_NO_PROGRESS was handled about the same as AD_WAIT. But it means the
decoder didn't output data, even though input is still readily
available.

This happened in particular when the timeline code was used (potentially
skipping many packets), and thus should fix #4688.
2017-08-08 14:55:02 +02:00
wm4 8ef50016be player: fix --end with large values
Causea a simple integer overflow.

Fixes #4650.
2017-08-08 14:11:58 +02:00
wm4 0b10a07b63 vo_opengl: don't call glGetProgramBinary if GL_PROGRAM_BINARY_LENGTH==0
Noticed in #4717, although the issue might be about something else.
2017-08-08 13:16:37 +02:00
wm4 3f75b3c343 x11: drop xscrnsaver use
It's an ancient X11 protocol extension that apparently nobody uses
anymore (desktop environments in particular have replaced it with
equally bad protocols that require tons of dependencies). Users keep
complaining about it being a required dependency.

The impact is likely minimal to none.

Fixes #4706 and other annoying people.
2017-08-08 12:55:41 +02:00
feixm1 a434892208 build: fix dependencies for Cygwin environment
This replaces previous commit with same intentions. This time, with
proper formating (no tabs in code).
2017-08-08 11:41:56 +02:00
wm4 c1bcd30b09 vo_opengl: cosmetics to comments 2017-08-08 11:38:29 +02:00
wm4 fd135cf73e player: readd smi subtitle extension
Fixes #4626. Previously removed because the original smi entry was added
by someone who did not agree to LGPL relicensing. I'm not sure if the
original change was copyrightable, but this commit for sure does not
fall under that author's copyright.
2017-08-08 11:37:04 +02:00
Kevin Mitchell 12cafdc868 ao_wasapi: remove old comment 2017-08-07 16:33:29 -07:00
Kevin Mitchell 6f40c211a5 ao_wasapi: reorganize wasapi.h
Remove dead declarations. Move macro only used in wasapi_utils.c closer to use.
Rearrange declaration order.
2017-08-07 14:33:03 -07:00
Kevin Mitchell 434d3d4976 ao_wasapi: deduplicate wasapi sample format selection 2017-08-07 14:33:03 -07:00
Kevin Mitchell 15eb1e1ad3 ao_wasapi: clean up find_formats logic
There were too many functions within functions, too much going on in if
clauses and duplicated code. Fix it.
2017-08-07 14:33:03 -07:00
Kevin Mitchell bee602da82 ao_wasapi: return bool instead of HRESULT from thread_init
Any bad HRESULTs should have been printed already and lots of failure modes
don't have an HRESULT leading to awkward hr = E_FAIL business.

This also checks the exit status of GetBufferSize in the align hack. A final
fatal message is added if either of the retry hacks fail.
2017-08-07 14:33:03 -07:00
wm4 61c8a147b5 vo_opengl: call ra_free() in the correct context
This also fixes a double free in vo_opengl_cb.c.
2017-08-07 19:57:15 +02:00
wm4 bbb57694b0 vf_vapoursynth: fix inverted sign and restore 10 bit support
Fixes #4720, I think.
2017-08-07 19:31:15 +02:00
wm4 168ffbaf23 client API: more opengl_cb clarifications
Also fix a typo in ra_gl.c. Too greedy for a separate commit.
2017-08-07 19:24:25 +02:00
wm4 d45fbecbb5 vo_opengl: add another ra_format field to exclude insane formats
Generic description of pixel formats is hard. In this case, the Apple
special format for packed YUV could have been interpreted as a RGB
format with funny packing.
2017-08-07 19:18:58 +02:00
wm4 47ea771b7a vo_opengl: further GL API use separation
Move multiple GL-specific things from the renderer to other places like
vo_opengl.c, vo_opengl_cb.c, and ra_gl.c.

The vp_w/vp_h parameters to gl_video_resize() make no sense anymore, and
are implicitly part of struct fbodst.

Checking the main framebuffer depth is moved to vo_opengl.c. For
vo_opengl_cb.c it always assumes 8. The API user now has to override
this manually. The previous heuristic didn't make much sense anyway.

The only remaining dependency on GL is the hwdec stuff, which is harder
to change.
2017-08-07 19:17:28 +02:00
wm4 1adf324d8b vo_opengl: fix minor memory leak
Don't leak the buffer if glGetProgramBinary() fails.
2017-08-07 18:46:40 +02:00
Niklas Haas bed421d483 vo_opengl: nuke ra_gl->first_run
Completely unnecessary, we can just update the uniforms immediately
after creating the program. In theory, for GLSL 4.20+, we could even
skip this, but oh well.
2017-08-07 17:47:04 +02:00
Niklas Haas ecbb02148b vo_opengl: better formatting for enum RA_CAP
Also fixes an issue where 1 << 5 was used twice, probably because of the
terrible formatting obscuring this bug
2017-08-07 17:46:04 +02:00
Niklas Haas 01a40bb1ee vo_opengl: also support RA_VARTYPE_INT vertex attribs
No reason not to.
2017-08-07 17:46:04 +02:00
wm4 346ac1e09f vo_opengl: simplify mirroring and fix it if glBlitFramebuffer is used
The vp_w/vp_h variables and parameters were not really used anymore
(they were redundant with ra_tex w/h) - but vp_h was still used to
identify whether rendering should be done mirrored.

Simplify this by adding a fbodst struct (some bad naming), which
contains the render target texture, and some parameters how it should be
rendered to (for now only flipping). It would not be appropriate to make
this a member of ra_tex, so it's a separate struct.

Introduces a weird regression for the first frame rendered after
interpolation is toggled at runtime, but seems to work otherwise. This
is possibly due to the change that blit() now mirrors, instead of just
copying. (This is also why ra_fns.blit is changed.)

Fixes #4719.
2017-08-07 16:44:15 +02:00
Ricardo Constantino 46f7a4352e travis: install missing x11 deps 2017-08-07 13:53:52 +01:00
Ricardo Constantino 5fb0d7dbef travis: print config.log on failure like appveyor 2017-08-07 13:53:51 +01:00
wm4 41ee66d566 vo_opengl: drop pointless fbotex_init() function 2017-08-07 14:34:18 +02:00
Niklas Haas 9581fbe569 vo_opengl: generalize ra_buf to support other buffer objects
This allows us to integrate PBOs and SSBOs into the same abstraction,
with the potential to easily add UBOs if the need arises.
2017-08-07 12:46:30 +02:00
Jan Janssen 207458c7a9 acompressor.lua: Convert to use mp.options and lavfi filter bridge 2017-08-07 11:42:20 +02:00
Akemi f550fdaa91 cocoa: add an option to disable the native macOS fullscreen
Fixes #4014
2017-08-06 22:48:26 +02:00
Ricardo Constantino 2b83f7e391 ytdl_hook: support fragments with relative paths
Unbreaks segmented DASH with the change in
https://github.com/rg3/youtube-dl/commit/1141e9104 which made each
segment URL only use relative path from fragment_base_url with a
different key.
2017-08-06 13:27:53 +01:00
Niklas Haas 494aa0f651 vo_opengl: only mark frames as fresh if they contain a new image
When using dumb mode, we can actually redraw a frame without uploading
it. Marking this as fresh as well results in unpredictable pass
behavior, which is confusing and makes debugging harder. So mark it as a
redraw instead, in that case.
2017-08-06 02:51:11 +02:00
Niklas Haas 988d188d96 vo_opengl: drop ra_gl.h from shader_cache.c
Since the GL *gl is no longer needed for the timers, we can get rid of
the sc->gl dependency. This requires moving a utility function (which is
not GL-specific anyway) out of gl_utils.h and into utils.h
2017-08-06 00:10:22 +02:00
Niklas Haas e5748e891f vo_opengl: measure pass_draw_osd as a whole
In the past, this always measured the per-shader execution times of the
individual OSD parts, which was thrown off because the shader was reused
anyway. (And apparently recording the OSD shader execution times was
removed completely, probably because of them being so unrealiably
anyway)

Since ra_timer no longer has the restriction of not allowing timers to
run concurrently, we can just wrap the entire OSD block inside a single
osd_timer now, and record that. (Technically, this can still be off when
using --blend-subtitles=video/yes and showing a full-screen OSD at the
same time. Maybe this can be done better?)
2017-08-06 00:10:20 +02:00
Niklas Haas f2298f394e vo_opengl: move timers to struct ra
In order to prevent code duplication and keep the ra abstraction as
small as possible, `ra` only implements the actual timer queries,
it does not do pooling/averaging of the results. This is instead moved
to a ra-neutral struct timer_pool in utils.c.
2017-08-06 00:10:20 +02:00
Aman Gupta a680c643eb wscript: fix build of videotoolbox hwaccel for iOS 2017-08-05 22:10:14 +03:00
wm4 56742ecdc9 vo_opengl: ra_gl: make getting GL ptr slightly less tedious 2017-08-05 17:09:25 +02:00
wm4 dddda6e4a5 vo_opengl: move GL state resetting to vo_opengl_cb
This code is pretty much for the sake of vo_opengl_cb API users. It
resets certain state that either the user or our code doesn't reset
correctly. This is somewhat outdated. With GL implicit state being
so awfully large, it seems more reasonable require that any code
restores the default state when returning to the caller. Some
exceptions are defined in opengl_cb.h.
2017-08-05 16:27:09 +02:00
wm4 333cae74ef vo_opengl: move shader handling to ra
Now all GL-specifics of shader compilation are abstracted through ra.
Of course we still have everything hardcoded to GLSL - that isn't going
to change.

Some things will probably change later - in particular, the way we pass
uniforms and textures to the shader. Currently, there is a confusing
mismatch between "primitive" uniforms like floats, and others like
textures.

Also, SSBOs are not abstracted yet.
2017-08-05 16:27:09 +02:00
wm4 f72a33d2cb vo_opengl: organize ra PBO flag slightly differently
Instead of having a mutable ra_tex field (and the only one), move the
flag to struct ra, since we have only 2 tex_upload user calls anyway,
and both want the same PBO behavior. (At first I considered making it
a RA_TEX_UPLOAD_ flag, but why bother. PBOs are a terribly GL-specific
thing, so we can't expect a reasonable abstraction of it anyway.)
2017-08-05 13:48:46 +02:00
wm4 dd096863fa vo_opengl: make OSD code use ra for textures
This requires a silly extension to ra_fns.tex_upload: since the OSD
texture can be much larger than the actual OSD image data to upload, a
mechanism for uploading only to a small part of the texture is needed.
Otherwise, we'd have to realloc/copy the data, just to pad it, and then
pay for uploading the padding too.

The RA_TEX_UPLOAD_DISCARD flag is not interpreted by GL (not sure how
you'd tell GL about this), but it clarifies the API and might be
helpful if we support other backend APIs in the future.
2017-08-05 13:44:30 +02:00
wm4 8dd4ae13ff vo_opengl: restore OSX "old" hwdec
Probably. Untested.
2017-08-05 13:09:05 +02:00
wm4 aac04c0d64 vo_opengl: split utils.c/h
Actually GL-specific parts go into gl_utils.c/h, the shader cache
(gl_sc*) into shader_cache.c/h.

No semantic changes of any kind, except that the VAO helper is made
public again as part of gl_utils.c (all while the goal for gl_utils.c
itself is to be included by GL-specific code).
2017-08-05 13:09:05 +02:00
wm4 fa4a1c4675 vo_opengl: always use GL_TRIANGLES for all primitives
Will make the ra layer _slightly_ simpler.
2017-08-05 13:09:05 +02:00
wm4 0206efa94a vo_opengl: pass ra objects during rendering instead of GL objects
Another "small" step towards removing GL dependencies from the renderer.
This commit generally passes ra_tex objects instead of GL FBO integer
IDs to various rendering functions. video.c still manually binds the
FBOs when calling shaders.

This also happens to fix a memory leak with output_fbo.
2017-08-05 13:09:05 +02:00
wm4 a796745fd2 vo_opengl: make fbotex helper use ra
Further work removing GL dependencies from the actual video renderer,
and moving them into ra backends.

Use of glInvalidateFramebuffer() falls away. I'd like to keep this, but
it's better to readd it once shader runs are in ra.
2017-08-05 13:09:05 +02:00
wm4 90b53fede6 vo_opengl: drop unused custom texture filter for FBO helper 2017-08-05 13:09:05 +02:00
Jan Ekström 0389852db0 {demux_mf,osdep/io}: disable glob usage when it is not available
This currently is only limited to Android. Its stdlib contains the
things that mpv's POSIX check checks for, but unfortunately not
glob().

This fixes Android compilation broken in 70a70b9da .
2017-08-05 01:51:29 +03:00
Jan Ekström 1a0d92aeca build: re-add and re-structurize the glob() checks
* If we have glob() supported, we have `HAVE_GLOB = 1'.
* If we have specifically POSIX glob(), we have
  `HAVE_GLOB_POSIX = 1`.
* If we have specifically Win32 glob(), we have
  `HAVE_GLOB_WIN32 = 1`
2017-08-05 01:51:29 +03:00
Jan Ekström 69650851f8 build: move Android environment check to main dependencies
Additionally change the description to better match what Android is,
which is an "environment". The original positioning of this check
was unfortunate.
2017-08-05 01:51:29 +03:00
James Ross-Gowan 037c7a9279 w32_common: handle media keys
This was attempted before in fc9695e63b, but it was reverted in
1b7ce759b1 because it caused conflicts with other software watching
the same keys (See #2041.) It seems like some PCs ship with OEM software
that watches the volume keys without consuming key events and this
causes them to be handled twice, once by mpv and once by the other
software.

In order to prevent conflicts like this, use the WM_APPCOMMAND message
to handle media keys. Returning TRUE from the WM_APPCOMMAND handler
should indicate to the operating system that we consumed the key event
and it should not be propogated to the shell. Also, we now only listen
for keys that are directly related to multimedia playback (eg. the
APPCOMMAND_MEDIA_* keys.) Keys like APPCOMMAND_VOLUME_* are ignored, so
they can be handled by the shell, or by other mixer software.
2017-08-05 02:38:44 +10:00
Rostislav Pehlivanov e406e81477 vo_opengl: always print when getting embedded ICC profile data
The printout in get_vid_profile() gets skipped if icc caching has
been enabled, so always print if an embedded ICC profile has been
provided.
2017-08-04 09:50:13 +01:00
Rostislav Pehlivanov 5ea390c07f TOOLS/lua/status-line: improve and update
Updates the line once per second rather than once per frame, saving
quite a bit of CPU. Also completely stops the script while paused.
That aside, fixes the swapped checks for video and audio-only files and
adds bitrate printing.
2017-08-04 09:07:37 +01:00
Niklas Haas 779031ad8f bstr: short-circuit bstr_equals on pointer equality
More efficient in cases where we're comparing a bstr against itself,
which can happen in e.g. the ICC profile code.
2017-08-03 21:48:25 +02:00
Niklas Haas fee6b287a5 vo_opengl: support embedded ICC profiles
This currently only works when using lcms-based color management
(--icc-profile-*).

In principle, we could also support using lcms even when the user has
not specified an ICC profile, by generating the profile against a fixed
reference (--target-prim/--target-trc) instead. I still might do that
some day, simply because 3dlut provides a higher quality conversion than
our simple gamut mapping does for stuff like BT.2020, and also because
it's now needed to enable embedded ICC profiles. But that would be a
separate change, so preserve the status quo for now.

(Besides, my opinion is still that you should be using an ICC profile if
you care about colors being accurate _at all_)
2017-08-03 21:48:25 +02:00
Niklas Haas 1aab037601 vd_lavc: decode embedded ICC profiles
Since these need to be refcounted, we throw them directly into struct
mp_image instead of being part of mp_colorspace. Even though they would
semantically make more sense in mp_colorspace, having them there is
really awkward because mp_colorspace is passed around and stored a lot,
and this way their lifetime is exactly tied to the lifetime of the
mp_image associated with it.
2017-08-03 21:48:25 +02:00
Niklas Haas 0f956f0929 vo_opengl: use GL_CLIENT_STORAGE_BIT for DR
mesa won't pick client storage unless this bit is set, and we
*absolutely* want to be using client storage for our DR PBOs.
Performance is shit on AMD otherwise. (Nvidia always uses client storage
for persistent coherent buffers whether you tell it it or not, probably
because it's way faster and nvidia doesn't trust users to figure that
out on their own)
2017-08-03 20:06:58 +02:00
wm4 7625bcc716 vo_opengl: remove unused ra_mapped_buffer.preferred_align field
It makes no sense to have this on an already created buffer.

If anything, the ra backend would have to export this as a global value
(e.g. struct ra field), so that whatever allocates the buffer can
account for the required alignment. Since this code is in vo_opengl.c in
the first place, and since GL doesn't dictate any special alignment
here, it doesn't make sense in the first place to export this. (Maybe
something like this will be required later.)
2017-08-03 18:59:43 +02:00
Niklas Haas 2bf094cd55 vo_opengl: don't hardcode texmap0 for polar compute
This was an oversight. The ID shouldn't be hard-coded here, so add it to
sampler_prelude instead.
2017-08-03 18:55:52 +02:00
Niklas Haas 5e89aed934 vo_opengl: don't precompute texcoord in global scope
Breaks on mesa for whatever reason... even though it doesn't generate a
GLSL shader compiler error

Shouldn't make a performance difference for us because we cache `pos`
anyway, and most compute shaders will probably cache all of their
samples to shmem. Might have to re-visit this when we have an actual use
case for repeated sampling inside CS though. (RAVU + anti-ringing is a
possible candidate for that)
2017-08-03 18:50:07 +02:00
Niklas Haas 83f3910398 vo_opengl: make compute shaders more flexible
This allows users to do their own custom sample writing, mainly meant to
address use cases such as RAVU. Also clean up the compute shader code a
bit.
2017-08-03 18:27:36 +02:00
wm4 e7d31d12be vo_opengl: add legend for texture format debug dump 2017-08-03 16:19:57 +02:00
wm4 1479c7bd0d vo_opengl: give special Apple name a more appropriate name
Or less appropriate, as some would argue. The new name is short for
"Apple YUV packed".

(This format is needed only for hardware decoding on rather old Apple
hardware, and a very annoying special case.)
2017-08-03 16:19:56 +02:00
wm4 ffe0526064 vo_opengl: simplify/fix user shader textures
This broke float textures, which were actually used by some shaders.
There were probably some other bugs as well.

Lots of code can be avoided by using ra_tex_params directly, so do that.

The main change is that COMPONENT/FORMAT are replaced by a single FORMAT
directive, which takes different parameters now. Due to the mess with
16/32 bit float textures, and because we want to support other APIs than
just GL in the future, it's not really clear how this should be handled,
and the nice component/type separation makes things actually harder. So
just jump the gun and use the ra_format.name names, which were
originally meant mostly for debugging. (This is probably something that
will be regretted later.)

Still only superficially tested, but seems to work.

Fixes #4708.
2017-08-03 16:19:49 +02:00
wm4 7ec8bd168e travis: drop libav-stable support
For libav-stable, we download the Libav tarball, which is failing,
because their certificate is broken:

ERROR: cannot verify libav.org's certificate, issued by `/C=US/O=Let\'s Encrypt/CN=Let\'s Encrypt Authority X3':
  Issued certificate has expired.

I don't intend to support Libav's overly old releases anymore anyway,
so if you want to use Libav, use its git master.
2017-08-03 14:43:04 +02:00
Niklas Haas 2bcf04a7bd vo_opengl: fix constexprs on ANGLE
I hate GLES
2017-08-03 14:27:38 +02:00
Niklas Haas 8f484567fc vo_opengl: fix HLG OOTF inverse
Got the "sign" of the second multiplication wrong.
2017-08-03 14:26:35 +02:00
Niklas Haas 5e1e7d32e8 vo_opengl: generalize HDR tone mapping to gamut mapping
Since this code was already written for HDR, and is now per-channel
(because it works better for HDR as well), we can actually reuse this to
get very high quality gamut mapping without clipping. The only required
change is to move the tone mapping from before the gamut map to after
the gamut map. Additonally, we need to also account for changes in the
signal range as a result of applying the CMS when we compute ref_peak,
which is fortunately pretty easy because we only need to consider the
case of primaries mapping to themselves.

Since `HDR` no longer really makes sense as a label, rename it to
`--tone-mapping` in general. Also fits better with
`--tone-mapping-desat` etc.

Arguably we could also rename `--hdr-compute-peak`, but that option is
basically only useful for HDR content anyway because we don't need
information about the signal range for gamut mapping.

This (finally!) gives us reasonably high quality gamut mapping even in
the absence of an ICC profile / 3DLUT.
2017-08-03 12:46:57 +02:00
Niklas Haas 6074cfdfd4 vo_opengl: implement HLG OOTF inverse
Huge thanks to @rusxg for finding this solution, which was previously
believed not to exist. Of course, we still don't actually need it, but I
don't want to leave this half-implemented in case somebody does in the
future.
2017-08-03 12:05:37 +02:00
wm4 955cc50e1b options: --priority can be LGPL
Original author has agreed now.

Also fix the notice in dec_video.c - all GPL-only code is gone
(unrelated to --priority/its author).
2017-08-03 09:44:36 +02:00
Akemi 12ee13101b osx: load the proper profiles and configs in the bundle
different shells need different args to load the expected profiles and
configs, so we added a small heuristic to decide those args. also don't
always load the profiles for a bash login shell and instead only use the
standard shell without any args.
2017-07-31 20:23:58 +02:00
Akemi 67506f21b8 osx: fix bundle for paths that need escaping
this fixes the mpv binary call in our bundle wrapper script, in the case
that the path to the binary needs escaping. examples are white spaces or
special chars.
2017-07-31 20:23:58 +02:00
Alex Notes bda32d99d7 cocoa: fix the support of multiple renderers (GPU switch)
So far, switching between integrated and discrete GPU would cause the
kernel to kill mpv due to an indecipherable buffer error. The technical
note TN2229 from Apple recommends to enable OpenGL Offline Renderers for
every Mac with more GPUs than displays to handle the switch between GPU.

By ordering the array from the least commonly rejected to the most,
we can sequentially remove PixelFormat attributes to fit the host.

Fixes #2371
2017-07-31 20:23:58 +02:00
Akemi 80758eda17 cocoa: remove usage of FFABS and the dependency on libavutil/common.h 2017-07-31 20:22:33 +02:00
Akemi b726f1eb90 cocoa: distinguish between horizontal and vertical scroll
we need to switch the x and y deltas when Shift is being held because
macOS switches them around. otherwise we would get a horizontal scroll
on a vertical one and vice versa.

additional we switch from deltaX/Y to scrollingDeltaX/Y since the Apple
docs suggest it's the preferred way now. in my tests both reported the
same values on imprecise scrolls though.
2017-07-31 20:22:33 +02:00
wm4 53188a14bf vo_opengl: manage user shader textures with ra
Drops some features I guess, no idea if those were needed. Untested due
to lack of test cases.
2017-07-30 11:38:52 +02:00
wm4 5429dbf2a2 vo_opengl: fix dither texture filter
Should be GL_NEAREST, not GL_LINEAR.
2017-07-30 09:43:41 +02:00
wm4 ab1ffa1382 vo_opengl: manage ICC LUT texture via ra
Also move the capability check to gl_video_get_lut3d(), because it
seems more convenient (ra won't have a _CAP_EXT16).
2017-07-29 21:23:31 +02:00
wm4 37b7b32d61 vo_opengl: manage scaler LUT textures via ra
Also fix the RA_CAP_ bitmask nonsense.
2017-07-29 20:15:59 +02:00
wm4 8494fdadae vo_opengl: manage dither texture via ra
Also add some more helpers.

Fix the broken math.h include statement.

utils.c uses ra_gl.h internals, which it shouldn't, and which will be
removed again as soon as this code gets converted to ra fully.
2017-07-29 20:14:48 +02:00
wm4 0f9fcf0ed4 vo_opengl: do not use GL format conversion on texture upload
The dither texture data is created as a float array, but uploaded to a
texture with GL_R16 as internal format. We relied on GL to do the
conversion from float to uint16_t. Not all GL variants even support
this: GLES does not provide this conversion (one of the reasons why this
code has a float16 code path). Also, ra is not going to do this. So just
convert on the fly.

Still keep the float16 texture format fallback, because not all GLES
implementations provide GL_R16.

There is some possibility that we'll need to provide some kind of upload
conversion anyway for float->float16. We still rely on GL doing this
implicitly, and all GL variants support it, but with RA there might be
the need for explicit conversion. Even then, it might be best to reduce
the number of conversion cases. I'll worry about this later.
2017-07-29 20:12:43 +02:00
wm4 6fcc09ff3d vo_opengl: use ra_* for format negotiation too
Format handling via ra_* was added earlier, but the format negotiation
part was forgotten.

Actually move some aspects of it to ra_get_imgfmt_desc(). Also make sure
the unorm and float formats selected by the common format lookup
functions are linear filterable. (For OpenGL, this is implicitly
guaranteed, so it wasn't done before.) Whether these assumptions should
be checked/enforced in the ra code at all is a bit fuzzy, but with ra
being helper code only for the actual video renderer, it's probably
justified.
2017-07-29 20:11:51 +02:00
Martin Herkt 67cda60da0 options: fix --external-file alias typo 2017-07-29 16:40:08 +02:00
James Ross-Gowan 28e3618e79 subprocess-win: fix swprintf usage
The count parameter refers to the number of wide-characters to write to
the buffer (including the terminating NUL character, which is always
added,) not the number of bytes. See C99 7.24.2.3 or
https://msdn.microsoft.com/en-us/library/ybk95axf.aspx
2017-07-29 21:13:07 +10:00
James Ross-Gowan df12f76910 appveyor: use a clean %PATH%
AppVeyor build workers have Git for Windows on the %PATH%, which
includes some MSYS2 binaries. Having multiple MSYS2 installations on the
%PATH% causes cygheap errors, so remove them and make sure %PATH% only
has stuff we need.
2017-07-28 23:33:30 +10:00
James Ross-Gowan 5453604606 subprocess-win: don't leak pipe handle on error 2017-07-28 23:00:46 +10:00
Niklas Haas 345bb193fe vo_opengl: support loading custom user textures
Parsing the texture data as raw strings makes the textures the most
portable and self-contained. In order to facilitate different types of
shaders, the parse_user_shader interaction has been changed to instead
have it loop through blocks and call the passed functions for each valid
block parsed. This is more modular and also cleaner, with better code
separation.

Closes #4586.
2017-07-27 23:51:05 +02:00
Niklas Haas f1af6e53f0 vo_opengl: slightly refactor user_shaders code
- Each struct tex_hook now stores multiple hooks, this allows us to
  avoid the awkward way of the current code has to add the same pass
  multiple times.

- As a consequence, SHADER_MAX_HOOKS was split up into SHADER_MAX_PASSES
  (number of tex_hooks) and SHADER_MAX_HOOKS (number of hooked textures
  per tex_hook), and both numbers decreased correspondingly.

- Instead of having a weird free() callback, we can just leverage
  talloc's recursive free behavior. The only user is the user shaders code
  anyway.
2017-07-27 23:45:17 +02:00
Niklas Haas ea76f79e5d vo_opengl: tone map on the maximum signal component
This actually makes sure we don't decolor due to clipping even when the
signal itself exceeds the luma by a significant factor, which was pretty
common for saturated blues (and to a lesser degree, reds) - most
noticeable in skies etc.

This prevents the turn-the-sky-cyan effect of mobius tone mapping, and
should also improve the other tone mapping modes in quality.
2017-07-27 09:40:12 +02:00
Niklas Haas e1cc43182c vo_opengl: fix mpgl_caps bit check
As pointed out by @bjin, this would match if _any_ of the reqs are set.
Need to test for explicit equality.
2017-07-27 00:38:54 +02:00
wm4 81851febc4 vo_opengl: start work on rendering API abstraction
This starts work on moving OpenGL-specific code out of the general
renderer code, so that we can support other other GPU APIs. This is in
a very early stage and it's only a proof of concept. It's unknown
whether this will succeed or result in other backends.

For now, the GL rendering API ("ra") and its only provider (ra_gl) does
texture creation/upload/destruction only. And it's used for the main
video texture only. All other code is still hardcoded to GL.

There is some duplication with ra_format and gl_format handling. In the
end, only the ra variants will be needed (plus the gl_format table of
course). For now, this is simpler, because for some reason lots of hwdec
code still requires the GL variants, and would have to be updated to
use the ra ones.

Currently, the video.c code accesses private ra_gl fields. In the end,
it should not do that of course, and it would not include ra_gl.h.

Probably adds bugs, but you can keep them.
2017-07-26 11:31:43 +02:00
Niklas Haas 5904eddb38 vo_opengl: describe the texture uploading mode
Be a bit more transparent here, which is especially helpful when people
are sending me screenshots of stats pages.
2017-07-26 02:42:23 +02:00
Niklas Haas b31020b193 vo_opengl: check against shmem limits
The radius check was not strict enough, especially not for all
platforms. To fix this, actually check the hardware capabilities instead
of relying on a hard-coded maximum radius.
2017-07-26 01:54:33 +02:00
James Ross-Gowan 9875f14ad4 vo_opengl: fix image uniforms for older OpenGL
This explicitly enables the GL_ARB_shader_image_load_store extension,
which seems to fix compute shaders for Intel/GL 3.0.
2017-07-26 08:02:03 +10:00
Niklas Haas 49a648447f vo_opengl: cosmetic change 2017-07-25 20:14:03 +02:00
James Ross-Gowan 7097b6f81c subprocess-win: remove VLA usage
This only ever waits on four handles at most anyway.
2017-07-26 00:20:11 +10:00
Niklas Haas f2809e19f0 vo_opengl: add PRINTF_ATTRIBUTE to gl_sc_ssbo
Doesn't uncover any bugs, but apparently we're getting in the habit of
this anyway.
2017-07-25 06:35:10 +02:00
Niklas Haas 62de84cbe3 vo_opengl: kill off FBOTEX_COMPUTE again
The textures not having an FBO actually caused regressions when trying
to render the subtitles on top of this texture (--blend-subtitles),
which still relied on an FBO.

So just kill off the logic entirely. Why worry about a single FBO wasted
when we're allocating like 10 anyway.

Fixes #4657.
2017-07-25 06:32:29 +02:00
Niklas Haas d099e037ef vo_opengl: fix incoherent SSBO usage
According to the OpenGL spec, atomic access to SSBO variables is *not*
guaranteed to be coherent, even when reusing the same SSBO attached to
the same shader across different frames. So we actually need a
glMemoryBarrier here, at least in theory.
2017-07-25 06:11:57 +02:00
Niklas Haas 6c06e7e2a0 vo_opengl: cosmetic fix 2017-07-25 05:23:52 +02:00
Niklas Haas cd226bdfd8 vo_opengl: fix incoherent texture usage
This bug slipped past my attention because nvidia ignores memory
barriers, but this is not necessarily always the case. Since
image_load_store is incoherent (specifically, writing to images from
compute shaders is incoherent) we need to insert a memory barrier to
make it coherent again. Since we only care about texture fetches, that's
the only barrier we need.
2017-07-25 05:22:29 +02:00
Niklas Haas 241d5ebc46 vo_opengl: adjust the rules for linearization
Two changes, compounded into one since they affect the same logic:

1. Never use linearization for HDR downscaling
2. Always use linearization for interpolation

Instead of fixing p->use_linear at the beginning of pass_render_frame,
we flip it on "dynamically" as needed. I plan on killing this
p->use_linear frame (along with other per-pass metadata) and moving them
into their own struct for tracking the "current" state of the video, but
that's a separate/upcoming refactor.

As a small bonus, reduce some code duplication in the interpolation
logic.

Fixes #4631
2017-07-24 23:26:15 +02:00
Bin Jin 13ef6bcf6f vo_opengl: enable compute shader for mesa
Mesa 17.1 supports compute shader but not full specs of OpenGL 4.3.
Change the code to detect OpenGL extension "GL_ARB_compute_shader"
rather than OpenGL version 4.3.

HDR peak detection requires SSBO, and polar scaler requires 2D array
extension. Add these extensions as requirement as well.
2017-07-25 04:07:26 +08:00
Niklas Haas dbef5b737e manpage: expand --hwdec warnings
I've found more test cases where hwdec=cuda shits itself, even
hwdec=cuda-copy. So the whole “copyback is no worse than swdec” is
simply not true. Also, in the light of 10 bit media files and APIs
silently truncating to 8 bit, the warnings need to be generalized a bit.
It's no longer safe to say that “doesn't convert to RGB” means “perfect
playback”.

I've also added a very strong disclaimer to the whole hwdec scenario
clarifying why hwdec is usually a bad idea unless absolutely needed,
because I've seen issue after issue that is resolved by disabling hwdec.
2017-07-24 19:16:30 +02:00
Niklas Haas 0c84ee01d5 vo_opengl: support user compute shaders
These are identical to regular fragment shader hooks, but with extra
metadata indicating the preferred block size.
2017-07-24 17:19:34 +02:00
Niklas Haas f338ec4591 vo_opengl: implement compute shader based EWA kernel
This performs almost 50% faster on my machine (!!), from 4650μs down to
about 3176μs for ewa_lanczossharp.

It's possible we could use a similar approach to speed up the separable
scalers, although with vastly simpler code. For separable scalers we'd
also have the additional huge benefit of only needing padding in one
direction, so we could potentially use a big 256x1 kernel or something
to essentially compute an entire row at once.
2017-07-24 17:19:31 +02:00
Niklas Haas b196cadf9f vo_opengl: support HDR peak detection
This is done via compute shaders. As a consequence, the tone mapping
algorithms had to be rewritten to compute their known constants in GLSL
(ahead of time), instead of doing it once. Didn't affect performance.

Using shmem/SSBO atomics in this way is extremely fast on nvidia, but it
might be slow on other platforms. Needs testing.

Unfortunately, setting up the SSBO still requires OpenGL calls, which
means I can't have it in video_shaders.c, where it belongs. But I'll
defer worrying about that until the backend refactor, since then I'll be
breaking up the video/video_shaders structure anyway.
2017-07-24 17:19:31 +02:00
Niklas Haas aad6ba018a vo_opengl: support compute shaders
These can either be invoked as dispatch_compute to do a single
computation, or finish_pass_fbo (after setting compute_size_minimum) to
render to a new texture using a compute shader. To make this stuff all
work transparently, we try really, really hard to make compute shaders
as identical to fragment shaders as possible in their behavior.
2017-07-24 17:19:31 +02:00
Niklas Haas eb54d2ad4d vo_opengl: cut down on FBOTEX_FUZZY abuse
Don't use FBOTEX_FUZZY where the FBO is sized according to
p->texture_w/h, since this changes infrequently (and when it does, we
need to reset everything anyway). No real reason to make this change
other than that it possibly prevents nasty surprises in the future, so I
feel more comfortable about it.
2017-07-24 16:41:38 +02:00
wm4 38b367c039 build: warn against VLA
Although C99 supports them, they are optional in C11, and we don't
like/use them anyway.
2017-07-24 08:12:42 +02:00
wm4 24dc91907a common, vo_opengl: add/use helper for formatted strings on the stack
Seems like I really like this C99 idiom. No reason not to generalize it
do snprintf(). Introduce mp_tprintf(), which basically this idiom to
snprintf(). This macro looks like it returns a string that was allocated
with alloca() on the caller site, except it's portable C99/C11. (And
unlike alloca(), the result is valid only within block scope.)

Use it in 2 places in the vo_opengl code. But it has the potential to
make a whole bunch of weird looking code look slightly nicer.
2017-07-24 08:12:42 +02:00
wm4 3d0f86145c vo_opengl: check format on some printf-like calls
Fix 1 incorrect use.
2017-07-24 08:08:02 +02:00
wm4 64d56114ed vo_opengl: add direct rendering support
Can be enabled via --vd-lavc-dr=yes. See manpage additions for what it
does.

This reminds of the MPlayer -dr flag, but the implementation is
completely different. It's the same basic concept: letting the decoder
render into a GPU buffer to avoid a copy. Unlike MPlayer, this doesn't
try to go through filters (libavfilter doesn't support this anyway).
Unless a filter can work in-place, DR will be silently disabled. MPlayer
had very complex semantics about buffer types and management (which
apparently nobody ever understood) and weird restrictions that mostly
limited it to mpeg2 style codecs. The mpv code does not do any of this,
and just lets the decoder allocate an arbitrary number of untyped
images. (No MPlayer code was used.)

Parts of the code based on work by atomnuker (starting point for the
generic code) and haasn (some GL definitions, some basic PBO code, and
correct fencing).
2017-07-24 04:32:55 +02:00
wm4 9e7665b21b mp_image: expose some image allocation code as helpers, refactor
Refactor the image allocation code, and expose part of it as helper
code. This aims towards allowing callers to easily allocate mp_image
references from custom-allocated linear buffers. This is exposing only
as much as what should be actually required.
2017-07-23 09:31:27 +02:00
wm4 0ce3dce03a mp_image_pool: disallow adding read only frames
Remove the feature of adding read-only frames to mp_image_pool_add().
This makes no sense, because an image pool is an allocator, and must
always return writable images. Also check these assumptions earlier.
2017-07-23 09:28:36 +02:00
wm4 bbfd9b5a29 vo_opengl: osd: remove stale declaration
Was missed in the previous changes.
2017-07-23 00:02:02 +02:00
wm4 87e9cd13aa vo_opengl: add printf format checking to pass_describe() 2017-07-22 22:08:23 +02:00
wm4 95885bbaa2 vo_opengl: make VAO helper private, remove old VAO mechanism
The struct describing vertex attributes is still public, of course.
2017-07-22 21:56:23 +02:00
wm4 d3dfffdf02 vo_opengl: osd: use new VAO mechanism
In addition to using the new VAO mechanism introduced in the previous
commit, this tries to keep the OSD code self-contained. This doesn't
work all too well (because of the pass and CMS stuff), but it's still
better than before.
2017-07-22 21:55:38 +02:00
wm4 a24df94fed vo_opengl: add mechanism to create/cache VAO on the fly
This removes VAO handling from video.c. Instead the shader cache will
create the VAO as needed. The consequence is that this creates a VAO
per shader, which might be a bit wasteful, but doesn't matter anyway.
2017-07-22 21:51:32 +02:00
wm4 1c81311673 vo_opengl: osd: refactor and simplify
Reduce this to 1 draw call per OSD pass. This removes the need for some
annoying special handling regarding 3D video support (we supported
duplicating the OSD/subtitles for side-by-side 3D output etc.).

Remove the unneeded texture sampler uniform thing.
2017-07-22 21:46:59 +02:00
wm4 2378acc3b3 options: drop --video-aspect-method=hybrid
Remove this code because it could be argued that it contains GPL-only
code (see commit 642e963c86 for details).

The remaining aspect methods appear to work just as well, are
potentially more compatible to other players, and the code becomes much
simpler.
2017-07-21 20:19:39 +02:00
wm4 533ff28574 client API: drop some compatibility handling
See commit e4bc563fd2 for more information.
2017-07-21 20:09:22 +02:00
wm4 a22ff44f7b input: drop deprecated "osd" command
Complicated situation due to changes by GPL-only author, but also
unnecessary due to newer mechanisms.
2017-07-21 20:02:58 +02:00
wm4 4bc29c1730 options: kill --field-dominance
GPL-only author, no chance of relicensing.
2017-07-21 20:00:09 +02:00
wm4 7a51271d3d demux_lavf: don't free AVFormatContext.key field
We don't even set it anymore. This really should be up to libavformat.
2017-07-21 19:54:26 +02:00
wm4 8c82555e41 ao_oss: fix a dumb calculation
period_size used the wrong unit, and even if the unit had been correct,
was assigned the wrong value.

Probably fixes #4642.
2017-07-21 19:45:59 +02:00
James Cowgill f676f6d2b9 manpage: fix minor spelling mistake in mpv.rst
Found by https://lintian.debian.org/
2017-07-19 19:05:25 +01:00
Ricardo Constantino e0739378dd travis: trigger website rebuild on VERSION changes too
Needed to update the stable manual if no DOCS were changed.
2017-07-19 19:03:08 +01:00
Martin Herkt 9e124933e5 Update VERSION 2017-07-18 21:15:58 +02:00
186 changed files with 11378 additions and 6595 deletions

No files matched your search

+1 -3
View File
@@ -5,7 +5,6 @@ os:
#- osx
env:
matrix:
- LIBAV=libav-stable
- LIBAV=libav-git
- LIBAV=ffmpeg-stable
- LIBAV=ffmpeg-git
@@ -26,8 +25,6 @@ branches:
matrix:
exclude:
- os: osx
env: LIBAV=libav-stable
- os: osx
env: LIBAV=libav-git
- os: osx
@@ -44,6 +41,7 @@ script:
- ./bootstrap.py
- ./waf configure
- ./waf build
after_failure: cat ./build/config.log
after_script: TOOLS/travis-rebuild-website
notifications:
-1
View File
@@ -283,7 +283,6 @@ x player/audio.c LGPL (dysfunctional due to libaf)
video/filter/vf_crop.c will be deleted
video/filter/vf_d3d11vpp.c LGPL
video/filter/vf_dsize.c will be deleted
video/filter/vf_eq.c will be deleted
video/filter/vf_expand.c will be deleted
video/filter/vf_flip.c will be deleted
video/filter/vf_format.c will be deleted
+1 -1
View File
@@ -32,7 +32,7 @@ API changes
::
--- mpv 0.26.0 ---
--- mpv 0.27.0 ---
1.25 - remove setting "no-" options via mpv_set_option*(). (See corresponding
deprecation in 0.23.0.)
--- mpv 0.25.0 ---
+2
View File
@@ -140,6 +140,8 @@ mpv uses C99 with K&R formatting, with some exceptions.
...
}
```
(If the if body is simple enough, this rule can be skipped.)
- Remove any trailing whitespace.
General coding
+47
View File
@@ -19,6 +19,53 @@ Interface changes
::
--- mpv 0.27.0 ---
- drop previously deprecated --field-dominance option
- drop previously deprecated "osd" command
- remove client API compatibility handling for "script", "sub-file",
"audio-file", "external-file" (these cases used to log a deprecation
warning)
- drop deprecated --video-aspect-method=hybrid option choice
- rename --hdr-tone-mapping to --tone-mapping (and generalize it)
- --opengl-fbo-format changes from a choice to a string. Also, its value
will be checked only on renderer initialization, rather than when the
option is set.
- Using opengl-cb now always assumes 8 bit per component depth, and dithers
to this size. Before, it tried to figure out the depth of the first
framebuffer that was ever passed to the renderer. Having GL framebuffers
with a size larger than 8 bit per component is quite rare. If you need
it, set the --dither-depth option instead.
- --lavfi-complex can now be set during runtime. If you set this in
expectation it would be applied only after a reload, you might observe
weird behavior.
- add --track-auto-selection to help with scripts/applications that
make exclusive use of --lavfi-complex.
- undeprecate --loop, and map it from --loop-playlist to --loop-file (the
deprecation was to make sure no API user gets broken by a sudden behavior
change)
- remove previously deprecated vf_eq
- remove that hardware deinterlace filters (vavpp, d3d11vpp, vdpaupp)
changed their deinterlacing-enabled setting depending on what the
--deinterlace option or property was set to. Now, a filter always does
what its filter options and defaults imply. The --deinterlace option and
property strictly add/remove its own filters. For example, if you run
"mpv --vf=vavpp --deinterlace=yes", this will insert another, redundant
filter, which is probably not what you want. For toggling a deinterlace
filter manually, use the "vf toggle" command, and do not set the
deinterlace option/property. To customize the filter that will be
inserted automatically, use --vf-defaults. Details how this works will
probably change in the future.
- remove deinterlace=auto (this was not deprecated, but had only a very
obscure use that stopped working with the change above. It was also
prone to be confused with a feature not implemented by it: auto did _not_
mean that deinterlacing was enabled on demand.)
- add shortened mnemonic names for mouse button bindings, eg. mbtn_left
the old numeric names (mouse_btn0) are deprecated
- remove mouse_btn3_dbl and up, since they are only generated for buttons
0-2 (these now print an error when sent from the 'mouse' command)
- rename the axis bindings to wheel_up/down/etc. axis scrolling and mouse
wheel scrolling are now conceptually the same thing
the old axis_up/down names remain as deprecated aliases
--- mpv 0.26.0 ---
- remove remaining deprecated audio device options, like --alsa-device
Some of them were removed in earlier releases.
+2 -1
View File
@@ -216,7 +216,8 @@ Available audio output drivers are:
it will write a WAVE header every time the file is opened.
``rsound``
Audio output to an RSound daemon
Audio output to an RSound daemon. Use ``--audio-device=rsound/<hostname>``
to set the host name (with ``<hostname>`` replaced, without the ``< >``).
.. note:: Completely useless, unless you intend to run RSound. Not to be
confused with RoarAudio, which is something completely
-14
View File
@@ -346,10 +346,6 @@ List of Input Commands
events that have already been displayed, or are within a short prefetch
range.
``osd [<level>]``
Toggle OSD level. If ``<level>`` is specified, set the OSD mode
(see ``--osd-level`` for valid values).
``print-text "<string>"``
Print text to stdout. The string can contain properties (see
`Property Expansion`_).
@@ -2171,22 +2167,12 @@ caveats with some properties (due to historical reasons):
Option changes at runtime are affected by this as well.
``deinterlace``
While video is active, this behaves differently from the option. It will
never return the ``auto`` value (but the state as observed by the video
chain). If you set ``auto``, the property will set this as the option value,
and will return the actual video chain state as observed instead of auto.
``video-aspect``
While video is active, always returns the effective aspect ratio. Setting
a special value (like ``no``, values ``<= 0``) will make the property
set this as option, and return whatever actual aspect was derived from the
option setting.
``brightness`` (and other color options)
If ``--vo=xv`` is used, these properties may return the adapter's current
values instead of the option values.
``display-fps``
If a VO is created, this will return either the actual display FPS, or
an invalid value, instead of the option value.
+6 -1
View File
@@ -437,7 +437,7 @@ Suffix Meaning
-set Set a list of items
============= ===============================================
Although some operations allow specifiying multiple ``,``-separated items, using
Although some operations allow specifying multiple ``,``-separated items, using
this is strongly discouraged and deprecated, except for ``-set``.
Without suffix, the action taken is normally ``-set``.
@@ -728,6 +728,11 @@ PROTOCOLS
you must mount the ISO file as filesystem, and point ``--bluray-device``
to the mounted directory directly.
``title`` can be: ``longest`` or ``first`` (selects the default
playlist); ``mpls/<number>`` (selects <number>.mpls playlist);
``<number>`` (select playlist with the same index). You can list
the available playlists with ``--msg-level=bd=v``.
``dvd://[title|[starttitle]-endtitle][/device]`` ``--dvd-device=PATH``
Play a DVD. DVD menus are not supported. If no title is given, the longest
+202 -76
View File
@@ -76,6 +76,18 @@ Track Selection
to ``auto`` (the default), mpv will choose the first edition declared as a
default, or if there is no default, the first edition defined.
``--track-auto-selection=<yes|no>``
Enable the default track auto-selection (default: yes). Enabling this will
make the player select streams according to ``--aid``, ``--alang``, and
others. If it is disabled, no tracks are selected. In addition, the player
will not exit if no tracks are selected, and wait instead (this wait mode
is similar to pausing, but the pause option is not set).
This is useful with ``--lavfi-complex``: you can start playback in this
mode, and then set select tracks at runtime by setting the filter graph.
Note that if ``--lavfi-complex`` is set before playback is started, the
referenced tracks are always selected.
Playback Control
----------------
@@ -287,11 +299,7 @@ Playback Control
time trying to loop a file that doesn't exist. But it might be useful for
playing webradios under very bad network conditions.
``--loop``
Currently a deprecated alias to ``--loop-playlist``. After a deprecation
period, it will be undeprecated, but changed to alias ``--loop-file``.
``--loop-file=<N|inf|no>``
``--loop-file=<N|inf|no>``, ``--loop=<N|inf|no>``
Loop a single file N times. ``inf`` means forever, ``no`` means normal
playback. For compatibility, ``--loop-file`` and ``--loop-file=yes`` are
also accepted, and are the same as ``--loop-file=inf``.
@@ -301,6 +309,8 @@ Playback Control
difference between the two option is that this option performs a seek on
loop, instead of reloading the file.
``--loop`` is an alias for this option.
``--ab-loop-a=<time>``, ``--ab-loop-b=<time>``
Set loop points. If playback passes the ``b`` timestamp, it will seek to
the ``a`` timestamp. Seeking past the ``b`` point doesn't loop (this is
@@ -712,7 +722,8 @@ Video
The ``...-copy`` modes (e.g. ``dxva2-copy``) allow you to use hardware
decoding with any VO, backend or filter. Because these copy the decoded
video back to system RAM, they're likely less efficient than the direct
modes (like e.g. ``dxva2``).
modes (like e.g. ``dxva2``), and probably not more efficient than software
decoding except for some codecs (e.g. HEVC).
.. note::
@@ -722,15 +733,19 @@ Video
.. admonition:: Quality reduction with hardware decoding
Normally, hardware decoding does not reduce video quality (at least for
the codecs h264 and HEVC). However, due to restrictions in video output
APIs, there can be some loss, or blatantly incorrect results.
In theory, hardware decoding does not reduce video quality (at least
for the codecs h264 and HEVC). However, due to restrictions in video
output APIs, as well as bugs in the actual hardware decoders, there can
be some loss, or even blatantly incorrect results.
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, not certain filtering (such as debanding)
cannot be applied in an ideal way.
used by ``--vo=opengl``. This means certain colorspaces may not display
correctly, and certain filtering (such as debanding) cannot be applied
in an ideal way. This will also usually force the use of low quality
chroma scalers instead of the one specified by ``--cscale``. In other
cases, hardware decoding can also reduce the bit depth of the decoded
image, which can introduce banding or precision loss for 10-bit files.
``vdpau`` is usually safe. If deinterlacing enabled (or the ``vdpaupp``
video filter is active in general), it forces RGB conversion. The latter
@@ -739,14 +754,15 @@ 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
or BT.709, a forced but correct RGB conversion is performed. Otherwise,
the result will be incorrect.
``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 or BT.709, a forced, low-quality but correct RGB conversion is
performed. Otherwise, the result will be totally incorrect.
``d3d11va`` is usually safe (if used with ANGLE builds that support
``EGL_KHR_stream path`` - otherwise, it converts to RGB), except that
10 bit input (HEVC main 10 profiles) will be rounded down to 8 bits.
10 bit input (HEVC main 10 profiles) will be rounded down to 8 bits,
which results in reduced quality.
``dxva2`` is not safe. It appears to always use BT.601 for forced RGB
conversion, but actual behavior depends on the GPU drivers. Some drivers
@@ -758,17 +774,30 @@ Video
``rpi`` always uses the hardware overlay renderer, even with
``--vo=opengl``.
``cuda`` should be safe, but it has been reported to corrupt the
timestamps causing glitched, flashing frames on some files. It can also
sometimes cause massive framedrops for unknown reasons. Caution is
advised.
``crystalhd`` is not safe. It always converts to 4:2:2 YUV, which
may be lossy, depending on how chroma sub-sampling is done during
conversion. It also discards the top left pixel of each frame for
some reason.
All other methods, in particular the copy-back methods (like
``dxva2-copy`` etc.) are either fully safe, or not worse than software
decoding.
``dxva2-copy`` etc.) should hopefully be safe, although they can still
cause random decoding issues. At the very least, they shouldn't affect
the colors of the image.
In particular, ``auto-copy`` will only select safe modes
(although potentially slower than other methods).
In particular, ``auto-copy`` will only select "safe" modes
(although potentially slower than other methods), but there's still no
guarantee the chosen hardware decoder will actually work correctly.
In general, it's very strongly advised to avoid hardware decoding
unless **absolutely** necessary, i.e. if your CPU is insufficient to
decode the file in questions. If you run into any weird decoding issues,
frame glitches or discoloration, and you have ``--hwdec`` turned on,
the first thing you should try is disabling it.
``--opengl-hwdec-interop=<name>``
This is useful for the ``opengl`` and ``opengl-cb`` VOs for creating the
@@ -835,13 +864,10 @@ Video
- ``--video-aspect=16:9`` or ``--video-aspect=1.7777``
- ``--no-video-aspect`` or ``--video-aspect=no``
``--video-aspect-method=<hybrid|bitstream|container>``
``--video-aspect-method=<bitstream|container>``
This sets the default video aspect determination method (if the aspect is
_not_ overridden by the user with ``--video-aspect`` or others).
:hybrid: Prefer the container aspect ratio. If the bitstream aspect
switches mid-stream, switch to preferring the bitstream aspect.
This was the default in older mpv and mplayer2. Deprecated.
:container: Strictly prefer the container aspect ratio. This is apparently
the default behavior with VLC, at least with Matroska. Note that
if the container has no aspect ratio set, the behavior is the
@@ -940,8 +966,8 @@ Video
Works in ``--no-correct-pts`` mode only.
``--deinterlace=<yes|no|auto>``
Enable or disable interlacing (default: auto, which usually means no).
``--deinterlace=<yes|no>``
Enable or disable interlacing (default: no).
Interlaced video shows ugly comb-like artifacts, which are visible on
fast movement. Enabling this typically inserts the yadif video filter in
order to deinterlace the video, or lets the video output apply deinterlacing
@@ -950,22 +976,11 @@ Video
This behaves exactly like the ``deinterlace`` input property (usually
mapped to ``d``).
``auto`` is a technicality. Strictly speaking, the default for this option
is deinterlacing disabled, but the ``auto`` case is needed if ``yadif`` was
added to the filter chain manually with ``--vf``. Then the core shouldn't
disable deinterlacing just because the ``--deinterlace`` was not set.
``--field-dominance=<auto|top|bottom>``
Set first field for interlaced content.
:auto: (default) If the decoder does not export the appropriate
information, it falls back on ``top`` (top field first).
:top: top field first
:bottom: bottom field first
.. note::
Setting either ``top`` or ``bottom`` will flag all frames as interlaced.
Keep in mind that this **will** conflict with manually inserted
deinterlacing filters, unless you take care. (Since mpv 0.27.0, even the
hardware deinterlace filters will conflict. Also since that version,
``--deinterlace=auto`` was removed, which used to mean that the default
interlacing option of possibly inserted video filters was used.)
``--frames=<number>``
Play/convert only first ``<number>`` video frames, then quit.
@@ -1028,6 +1043,23 @@ Video
(default: 3). If this is a number, then fallback will be triggered if
N frames fail to decode in a row. 1 is equivalent to ``yes``.
``--vd-lavc-dr=<yes|no>``
Enable direct rendering (default: no). If this is set to ``yes``, the
video will be decoded directly to GPU video memory (or staging buffers).
This can speed up video upload, and may help with large resolutions or
slow hardware. This works only with the following VOs:
- ``opengl``: requires at least OpenGL 4.4.
(In particular, this can't be made work with ``opengl-cb``.)
Using video filters of any kind that write to the image data (or output
newly allocated frames) will silently disable the DR code path.
There are some corner cases that will result in undefined behavior (crashes
and other strange behavior) if this option is enabled. These are pending
towards being fixed properly at a later point.
``--vd-lavc-bitexact``
Only use bit-exact algorithms in all decoding steps (for codec testing).
@@ -1450,13 +1482,12 @@ Audio
``--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
default.
specifies how external audio files are matched.
:no: Don't automatically load external audio files.
:exact: Load the media filename with audio file extension (default).
:no: Don't automatically load external audio files (default).
:exact: Load the media filename with audio file extension.
:fuzzy: Load all audio files containing media filename.
:all: Load all audio files in the current and ``--audio-file-path``
:all: Load all audio files in the current and ``--audio-file-paths``
directories.
``--audio-file-paths=<path1:path2:...>``
@@ -1811,7 +1842,7 @@ Subtitles
:no: Don't automatically load external subtitle files.
:exact: Load the media filename with subtitle file extension (default).
:fuzzy: Load all subs containing media filename.
:all: Load all subs in the current and ``--sub-file-path`` directories.
:all: Load all subs in the current and ``--sub-file-paths`` directories.
``--sub-codepage=<codepage>``
You can use this option to specify the subtitle codepage. uchardet will be
@@ -2456,6 +2487,10 @@ Window
as having the same size as on none-HiDPI resolutions. This is the default OS X
behavior.
``--native-fs``, ``--no-native-fs``
(OS X only)
Uses the native fullscreen mechanism of the OS (default: yes).
``--monitorpixelaspect=<ratio>``
Set the aspect of a single pixel of your monitor or TV screen (default:
1). A value of 1 means square pixels (correct for (almost?) all LCDs). See
@@ -2965,7 +3000,7 @@ Input
Support depends on the VO in use.
``--input-media-keys=<yes|no>``
(OS X only)
(OS X and Windows only)
Enable/disable media keys support. Enabled by default (except for libmpv).
``--input-right-alt-gr``, ``--no-input-right-alt-gr``
@@ -4089,7 +4124,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
``--linear-scaling``
Scale in linear light. It should only be used with a
``--opengl-fbo-format`` that has at least 16 bit precision.
``--opengl-fbo-format`` that has at least 16 bit precision. This option
has no effect on HDR content.
``--correct-downscaling``
When using convolution based filters, extend the filter size when
@@ -4112,7 +4148,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
the ``--tscale`` setting.
Note that this relies on vsync to work, see ``--opengl-swapinterval`` for
more information.
more information. It should also only be used with an ``--opengl-fbo-format``
that has at least 16 bit precision.
``--interpolation-threshold=<0..1,-1>``
Threshold below which frame ratio interpolation gets disabled (default:
@@ -4216,24 +4253,61 @@ The following video options are currently all specific to ``--vo=opengl`` and
...
Each block of metadata, along with the non-metadata lines after it, defines
a single pass. Each pass can set the following metadata:
Each section of metadata, along with the non-metadata lines after it,
defines a single block. There are currently two types of blocks, HOOKs and
TEXTUREs.
DESC <title>
User-friendly description of the pass. This is the name used when
representing this shader in the list of passes for property
`vo-passes`.
A ``TEXTURE`` block can set the following options:
TEXTURE <name> (required)
The name of this texture. Hooks can then bind the texture under this
name using BIND. This must be the first option of the texture block.
SIZE <width> [<height>] [<depth>] (required)
The dimensions of the texture. The height and depth are optional. The
type of texture (1D, 2D or 3D) depends on the number of components
specified.
FORMAT <name> (required)
The texture format for the samples. Supported texture formats are listed
in debug logging when the ``opengl`` VO is initialized (look for
``Texture formats:``). Usually, this follows OpenGL naming conventions.
For example, ``rgb16`` provides 3 channels with normalized 16 bit
components. One oddity are float formats: for example, ``rgba16f`` has
16 bit internal precision, but the texture data is provided as 32 bit
floats, and the driver converts the data on texture upload.
Although format names follow a common naming convention, not all of them
are available on all hardware, drivers, GL versions, and so on.
FILTER <LINEAR|NEAREST>
The min/magnification filter used when sampling from this texture.
BORDER <CLAMP|REPEAT|MIRROR>
The border wrapping mode used when sampling from this texture.
Following the metadata is a string of bytes in hexadecimal notation that
define the raw texture data, corresponding to the format specified by
`FORMAT`, on a single line with no extra whitespace.
A ``HOOK`` block can set the following options:
HOOK <name> (required)
The texture which to hook into. May occur multiple times within a
metadata block, up to a predetermined limit. See below for a list of
hookable textures.
DESC <title>
User-friendly description of the pass. This is the name used when
representing this shader in the list of passes for property
`vo-passes`.
BIND <name>
Loads a texture and makes it available to the pass, and sets up macros
to enable accessing it. See below for a list of set macros. By default,
no textures are bound. The special name HOOKED can be used to refer to
the texture that triggered this pass.
Loads a texture (either coming from mpv or from a ``TEXTURE`` block)
and makes it available to the pass. When binding textures from mpv,
this will also set up macros to facilitate accessing it properly. See
below for a list. By default, no textures are bound. The special name
HOOKED can be used to refer to the texture that triggered this pass.
SAVE <name>
Gives the name of the texture to save the result of this pass into. By
@@ -4257,19 +4331,40 @@ The following video options are currently all specific to ``--vo=opengl`` and
hook point can still cause that hook point to be saved, which has some
minor overhead)
OFFSET ox oy
OFFSET <ox> <oy>
Indicates a pixel shift (offset) introduced by this pass. These pixel
offsets will be accumulated and corrected during the next scaling pass
(``cscale`` or ``scale``). The default values are 0 0 which correspond
to no shift. Note that offsets are ignored when not overwriting the
hooked texture.
COMPONENTS n
COMPONENTS <n>
Specifies how many components of this pass's output are relevant and
should be stored in the texture, up to 4 (rgba). By default, this value
is equal to the number of components in HOOKED.
Each bound texture (via ``BIND``) will make available the following
COMPUTE <bw> <bh> [<tw> <th>]
Specifies that this shader should be treated as a compute shader, with
the block size bw and bh. The compute shader will be dispatched with
however many blocks are necessary to completely tile over the output.
Within each block, there will bw tw*th threads, forming a single work
group. In other words: tw and th specify the work group size, which can
be different from the block size. So for example, a compute shader with
bw, bh = 32 and tw, th = 8 running on a 500x500 texture would dispatch
16x16 blocks (rounded up), each with 8x8 threads.
Compute shaders in mpv are treated a bit different from fragment
shaders. Instead of defining a ``vec4 hook`` that produces an output
sample, you directly define ``void hook`` which writes to a fixed
writeonly image unit named ``out_image`` (this is bound by mpv) using
`imageStore`. To help translate texture coordinates in the absence of
vertices, mpv provides a special function ``NAME_map(id)`` to map from
the texel space of the output image to the texture coordinates for all
bound textures. In particular, ``NAME_pos`` is equivalent to
``NAME_map(gl_GlobalInvocationID)``, although using this only really
makes sense if (tw,th) == (bw,bh).
Each bound mpv texture (via ``BIND``) will make available the following
definitions to that shader pass, where NAME is the name of the bound
texture:
@@ -4696,9 +4791,11 @@ The following video options are currently all specific to ``--vo=opengl`` and
The user should independently guarantee this before using these signal
formats for display.
``--hdr-tone-mapping=<value>``
Specifies the algorithm used for tone-mapping HDR images onto the target
display. Valid values are:
``--tone-mapping=<value>``
Specifies the algorithm used for tone-mapping images onto the target
display. This is relevant for both HDR->SDR conversion as well as gamut
reduction (e.g. playing back BT.2020 content on a standard gamut display).
Valid values are:
clip
Hard-clip any out-of-range values. Use this when you care about
@@ -4717,10 +4814,10 @@ The following video options are currently all specific to ``--vo=opengl`` and
results in flattening of details and degradation in color accuracy.
hable
Similar to ``reinhard`` but preserves both dark and bright details
better (slightly sigmoidal), at the cost of slightly darkening
everything. Developed by John Hable for use in video games. Use this
when you care about detail preservation more than color/brightness
accuracy. This is roughly equivalent to
better (slightly sigmoidal), at the cost of slightly darkening /
desaturating everything. Developed by John Hable for use in video
games. Use this when you care about detail preservation more than
color/brightness accuracy. This is roughly equivalent to
``--hdr-tone-mapping=reinhard --tone-mapping-param=0.24``.
gamma
Fits a logarithmic transfer between the tone curves.
@@ -4750,6 +4847,14 @@ The following video options are currently all specific to ``--vo=opengl`` and
linear
Specifies the scale factor to use while stretching. Defaults to 1.0.
``--hdr-compute-peak``
Compute the HDR peak per-frame of relying on tagged metadata. These values
are averaged over local regions as well as over several frames to prevent
the value from jittering around too much. This option basically gives you
dynamic, per-scene tone mapping. Requires compute shaders, which is a
fairly recent OpenGL feature, and will probably also perform horribly on
some drivers, so enable at your own risk.
``--tone-mapping-desaturate=<value>``
Apply desaturation for highlights that exceed this level of brightness. The
higher the parameter, the more color information will be preserved. This
@@ -4760,6 +4865,24 @@ The following video options are currently all specific to ``--vo=opengl`` and
The default of 2.0 is somewhat conservative and will mostly just apply to
skies or directly sunlit surfaces. A setting of 0.0 disables this option.
``--gamut-warning``
If enabled, mpv will mark all clipped/out-of-gamut pixels that exceed a
given threshold (currently hard-coded to 101%). The affected pixels will be
inverted to make them stand out. Note: This option applies after the
effects of all of mpv's color space transformation / tone mapping options,
so it's a good idea to combine this with ``--tone-mapping=clip`` and use
``--target-gamut`` to set the gamut to simulate. For example,
``--target-gamut=bt.709`` would make mpv highlight all pixels that exceed the
gamut of a standard gamut (sRGB) display. This option also does not work
well with ICC profiles, since the 3DLUTs are always generated against the
source color space and have chromatically-accurate clipping built in.
``--use-embedded-icc-profile``
Load the embedded ICC profile contained in media files such as PNG images.
(Default: yes). Note that this option only works when also using a display
ICC profile (``--icc-profile`` or ``--icc-profile-auto``), and also
requires LittleCMS 2 support.
``--icc-profile=<file>``
Load an ICC profile and use it to transform video RGB to screen output.
Needs LittleCMS 2 support compiled in. This option overrides the
@@ -5122,11 +5245,14 @@ Miscellaneous
video or audio outputs are not possible, but you can use filters to merge
them into one.
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
It's not possible to change the tracks connected to the filter at runtime,
unless you explicitly change the ``lavfi-complex`` property and set new
track assignments. When the graph is changed, the track selection is changed
according to the used labels as well.
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
changed.
changed with the normal methods.
Note that the normal filter chains (``--af``, ``--vf``) are applied between
the complex graphs (e.g. ``ao`` label) and the actual output.
+11 -28
View File
@@ -414,6 +414,16 @@ Available mpv-only filters are:
but values such as ``[16:9]`` can be passed too (``[...]`` for quoting
to prevent the option parser from interpreting the ``:`` character).
``<spherical-type>``
Type of the spherical projection:
:auto: As indicated by the file (default)
:none: Normal video
:equirect: Equirectangular
:unknown: Unknown projection
``<spherical-yaw>``, ``<spherical-pitch>``, ``<spherical-roll>``
Reference angle in degree, if spherical video is used.
``noformat[=fmt]``
Restricts the color space for the next filter without doing any conversion.
@@ -474,32 +484,6 @@ Available mpv-only filters are:
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
forces a specific threading configuration.
``eq[=gamma:contrast:brightness:saturation:rg:gg:bg:weight]``
Software equalizer that uses lookup tables (slow), allowing gamma correction
in addition to simple brightness and contrast adjustment. The parameters are
given as floating point values.
``<0.1-10>``
initial gamma value (default: 1.0)
``<-2-2>``
initial contrast, where negative values result in a negative image
(default: 1.0)
``<-1-1>``
initial brightness (default: 0.0)
``<0-3>``
initial saturation (default: 1.0)
``<0.1-10>``
gamma value for the red component (default: 1.0)
``<0.1-10>``
gamma value for the green component (default: 1.0)
``<0.1-10>``
gamma value for the blue component (default: 1.0)
``<0-1>``
The weight parameter can be used to reduce the effect of a high gamma
value on bright image areas, e.g. keep them from getting overamplified
and just plain white. A value of 0.0 turns the gamma correction all
the way down while 1.0 leaves it at its full strength (default: 1.0).
``pullup[=jl:jr:jt:jb:sb:mp]``
Pulldown reversal (inverse telecine) filter, capable of handling mixed
hard-telecine, 24000/1001 fps progressive, and 30000/1001 fps progressive
@@ -787,7 +771,7 @@ Available mpv-only filters are:
no
Don't perform deinterlacing.
first-field
Show only first field (going by ``--field-dominance``).
Show only first field.
bob
bob deinterlacing (default).
weave, motion-adaptive, motion-compensated
@@ -828,7 +812,6 @@ Available mpv-only filters are:
the mode selected with ``deint-mode``.
``deint-mode=<first-field|bob|temporal|temporal-spatial>``
Select deinterlacing mode (default: temporal).
All modes respect ``--field-dominance``.
Note that there's currently a mechanism that allows the ``vdpau`` VO to
change the ``deint-mode`` of auto-inserted ``vdpaupp`` filters. To avoid
+1 -1
View File
@@ -356,7 +356,7 @@ Available video output drivers are:
no
Don't allow deinterlacing (default for newer libva).
first-field
Show only first field (going by ``--field-dominance``).
Show only first field.
bob
bob deinterlacing (default for older libva).
+101
View File
@@ -1,3 +1,104 @@
Release 0.27.0
==============
Features
--------
Added
~~~~~
- Windows: handle media keys
- libmpv: options: add a thread-safe way to notify option updates
- vd_lavc/vo_opengl: support embedded ICC profiles
- vo: rendering API abstraction for future non-GL video outputs
- vo_opengl: add a gamut warning feature to highlight out-of-gamut colors (--gamut-warning)
- vo_opengl: add direct rendering support (--vd-lavc-dr)
- vo_opengl: implement (faster) compute shader based EWA kernel
- vo_opengl: implement HLG OOTF inverse
- vo_opengl: support HDR peak detection (--hdr-compute-peak)
- vo_opengl: support float input pixel formats
- vo_opengl: support loading custom user textures (#4586)
- vo_opengl: support user compute shaders
Removed
~~~~~~~
- Remove video equalizer handling from vo_direct3d, vo_sdl, vo_vaapi, and vo_xv (GPL, not worth the effort to support legacy VOs)
Options and Commands
--------------------
Added
~~~~~
- macOS: add --no-native-fs option to disable the native macOS fullscreen (#4014)
- player: add --track-auto-selection option
Changed
~~~~~~~
- input: use mnemonic names for mouse buttons, same as Qt: https://doc.qt.io/qt-5/qt.html#MouseButton-enum
- options: change --loop semantics
- player: make --lavfi-complex changeable at runtime
- vf_eq: remove this filter (GPL; uses libavfilter’s eq filter now, with changed semantics)
- video: change --deinterlace behavior
- vo_opengl: generalize HDR tone mapping to gamut mapping, --hdr-tone-mapping → --tone-mapping
Removed
~~~~~~~
- --field-dominance (GPL-only author, no chance of relicensing)
- input: drop deprecated "osd" command
- options: drop --video-aspect-method=hybrid (GPL-only)
Fixes and Minor Enhancements
----------------------------
- TOOLS/autocrop.lua: fix cropdetect black limit for 10-bit videos
- TOOLS/lua/autodeint: update to lavfi-bridge
- TOOLS/lua/status-line: improve and update
- af_lavrresample: don't call swr_set_compensation() unless necessary (#4716)
- ao_oss: fix period_size calculation (#4642)
- ao_rsound: allow setting the host
- audio: fix spdif mode
- filter_kernels: correct spline64 kernel
- macOS: fix media keys input when other Apps steal the priority (#4834)
- macOS: fix the support of multiple renderers (GPU switch) (#2371)
- macOS: remove the standard "Show Tab Bar" menu item
- options: fix --include (#4673)
- player: fix --end with large values (#4650)
- player: fix confusion in audio resync code (#4688)
- player: make refresh seeks slightly more robust (#4757)
- player: readd smi subtitle extension (#4626)
- vd_lavc: change auto-probe order to prefer cuda over vdpau-copy
- vd_lavc: fix device leak with copy-mode hwaccels (#4735)
- vd_lavc: fix hwdec compatibility with yuvj420p formats
- vd_lavc: fix mid-stream hwdec fallback
- vf_vapoursynth: fix inverted sign and restore 10 bit support (#4720)
- video: increase --monitorpixelaspect range
- vo_opengl: adjust the rules for linearization (#4631)
- vo_opengl: scale deband-grain to the signal range
- vo_opengl: tone map on the maximum signal component
- x11: fix that window could be resized when using embedding (#4784)
- ytdl_hook: resolve relative paths when joining segment urls (#4827)
- ytdl_hook: support fragments with relative paths, fixes segmented DASH
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.26.0..v0.27.0`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.26.0...v0.27.0 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.26.0..v0.27.0
Release 0.26.0
==============
+108 -79
View File
@@ -3,48 +3,80 @@
--
-- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation
-- of the parameteres.
--
-- Default key bindings:
-- n: Toggle dynamic range compression on or off
-- F1/Shift+F1: Increase/Decrease threshold parameter
-- F2/Shift+F2: Increase/Decrease ratio parameter
-- F3/Shift+F3: Increase/Decrease knee parameter
-- F4/Shift+F4: Increase/Decrease makeup gain parameter
-- F5/Shift+F5: Increase/Decrease attack parameter
-- F6/Shift+F6: Increase/Decrease release parameter
--
-- To change key bindings in input.conf use:
-- BINDING script-message-to acompressor toggle-acompressor
-- BINDING script-message-to acompressor update-param PARAM INCREMENT
-- BINDING is the key binding to use, PARAM is either 'attack', 'release',
-- 'threshold', 'ratio', 'knee' or 'makeup' and INCREMENT is a signed floating
-- point value to add to the current parameter value.
--
-- You may also just adjust default parameters to your liking in this table.
local mp = require 'mp'
local options = require 'mp.options'
local o = {
default_enable = false,
show_osd = true,
osd_timeout = 4000,
filter_label = mp.get_script_name(),
key_toggle = 'n',
key_increase_threshold = 'F1',
key_decrease_threshold = 'Shift+F1',
key_increase_ratio = 'F2',
key_decrease_ratio = 'Shift+F2',
key_increase_knee = 'F3',
key_decrease_knee = 'Shift+F3',
key_increase_makeup = 'F4',
key_decrease_makeup = 'Shift+F4',
key_increase_attack = 'F5',
key_decrease_attack = 'Shift+F5',
key_increase_release = 'F6',
key_decrease_release = 'Shift+F6',
default_threshold = -25.0,
default_ratio = 3.0,
default_knee = 2.0,
default_makeup = 8.0,
default_attack = 20.0,
default_release = 250.0,
step_threshold = -2.5,
step_ratio = 1.0,
step_knee = 1.0,
step_makeup = 1.0,
step_attack = 10.0,
step_release = 10.0,
}
options.read_options(o)
local params = {
-- 'hide' defines wether the parameter should be hidden from OSD display (it will still be included in ffmpeg filter graph).
-- 'input_format' is used to parse the value back from a obtained filter graph.
-- 'output_format' defines how the value shall be formatted for creating the filter graph.
{ name = 'Attack', value= 20, min=0.01, max=2000, hide= 20, input_format='attack=(%d+[.%d+]*)', output_format='%g' },
{ name = 'Release', value=250, min=0.01, max=9000, hide=250, input_format='release=(%d+[.%d+]*)', output_format='%g' },
{ name = 'Threshold', value=-25, min= -30, max= 0, hide=nil, input_format='threshold=(-%d+[.%d+]*)dB', output_format='%gdB' },
{ name = 'Ratio', value= 3, min= 1, max= 20, hide=nil, input_format='ratio=(%d+[.%d+]*)', output_format='%g' },
{ name = 'Knee', value= 2, min= 1, max= 10, hide= 2, input_format='knee=(%d+[.%d+]*)dB', output_format='%gdB' },
{ name = 'Makeup', value= 8, min= 0, max= 24, hide=nil, input_format='makeup=(%d+[.%d+]*)dB', output_format='%gdB' }
{ name = 'attack', min=0.01, max=2000, hide_default=true, dB='' },
{ name = 'release', min=0.01, max=9000, hide_default=true, dB='' },
{ name = 'threshold', min= -30, max= 0, hide_default=false, dB='dB' },
{ name = 'ratio', min= 1, max= 20, hide_default=false, dB='' },
{ name = 'knee', min= 1, max= 10, hide_default=true, dB='dB' },
{ name = 'makeup', min= 0, max= 24, hide_default=false, dB='dB' },
}
-- Defines the mpv filter label to be used. This allows us to easily add/replace/remove it, so
-- it should be left so something meaningful and unique.
local filter_label = 'acompressor'
local function parse_value(value)
-- Using nil here because tonumber differs between lua 5.1 and 5.2 when parsing fractions in combination with explicit base argument set to 10.
-- And we can't omit it because gsub returns 2 values which would get unpacked and cause more problems. Gotta love scripting languages.
return tonumber(value:gsub('dB$', ''), nil)
end
local function format_value(value, dB)
return string.format('%g%s', value, dB)
end
local function show_osd(filter)
if not o.show_osd then
return
end
if not filter.enabled then
mp.commandv('show-text', 'Dynamic range compressor: disabled', o.osd_timeout)
return
end
local function update_filter()
local graph = {}
local pretty = {}
for _,param in pairs(params) do
graph[#graph+1] = string.format('%s=' .. param.output_format, string.lower(param.name), param.value)
if param.hide ~= param.value then
pretty[#pretty+1] = string.format('%s: ' .. param.output_format, param.name, param.value)
for _,param in ipairs(params) do
local value = parse_value(filter.params[param.name])
if not (param.hide_default and value == o['default_' .. param.name]) then
pretty[#pretty+1] = string.format('%s: %g%s', param.name:gsub("^%l", string.upper), value, param.dB)
end
end
@@ -54,49 +86,49 @@ local function update_filter()
pretty = '\n(' .. table.concat(pretty, ', ') .. ')'
end
mp.command(string.format('no-osd af add @%s:lavfi=[acompressor=%s]; show-text "Dynamic range compressor: enabled%s" 4000', filter_label, table.concat(graph, ':'), pretty))
mp.commandv('show-text', 'Dynamic range compressor: enabled' .. pretty, o.osd_timeout)
end
local function read_filter()
local graph = nil
local function get_filter()
local af = mp.get_property_native('af', {})
for i = 1, #af do
if af[i]['name'] == 'lavfi' and af[i]['label'] == 'acompressor' then
graph = af[i]['params']['graph']
break
if af[i].label == o.filter_label then
return af, i
end
end
if graph == nil then
return false
end
af[#af+1] = {
name = 'acompressor',
label = o.filter_label,
enabled = false,
params = {},
}
for _,param in pairs(params) do
local value = tonumber(string.match(graph, param.input_format))
if value ~= nil and value >= param.min and value <= param.max then
param.value = value
end
af[#af].params[param.name] = format_value(o['default_' .. param.name], param.dB)
end
return true
return af, #af
end
local function toggle_acompressor()
if read_filter() then
mp.command(string.format('no-osd af del @%s; show-text "Dynamic range compressor: disabled"', filter_label))
else
update_filter()
end
local af, i = get_filter()
af[i].enabled = not af[i].enabled
mp.set_property_native('af', af)
show_osd(af[i])
end
local function update_param(name, increment)
-- The current filter values could come from a watch_later config.
-- read_filter() will parse them, so that we don't clobber those with our default values.
read_filter()
for _,param in pairs(params) do
if string.lower(param.name) == string.lower(name) then
param.value = math.max(param.min, math.min(param.value + increment, param.max))
update_filter()
if param.name == string.lower(name) then
local af, i = get_filter()
local value = parse_value(af[i].params[param.name])
value = math.max(param.min, math.min(value + increment, param.max))
af[i].params[param.name] = format_value(value, param.dB)
af[i].enabled = true
mp.set_property_native('af', af)
show_osd(af[i])
return
end
end
@@ -104,23 +136,20 @@ local function update_param(name, increment)
mp.msg.error('Unknown parameter "' .. name .. '"')
end
mp.add_key_binding("n", "toggle-acompressor", toggle_acompressor)
mp.add_key_binding(o.key_toggle, "toggle-acompressor", toggle_acompressor)
mp.register_script_message('update-param', update_param)
mp.add_key_binding("F1", 'acompressor-inc-threshold', function() update_param('threshold', -5); end, { repeatable = true })
mp.add_key_binding("Shift+F1", 'acompressor-dec-threshold', function() update_param('Threshold', 5); end, { repeatable = true })
for _,param in pairs(params) do
for direction,step in pairs({increase=1, decrease=-1}) do
mp.add_key_binding(o['key_' .. direction .. '_' .. param.name],
'acompressor-' .. direction .. '-' .. param.name,
function() update_param(param.name, step*o['step_' .. param.name]); end,
{ repeatable = true })
end
end
mp.add_key_binding("F2", 'acompressor-inc-ratio', function() update_param('Ratio', 1); end, { repeatable = true })
mp.add_key_binding("Shift+F2", 'acompressor-dec-ratio', function() update_param('Ratio', -1); end, { repeatable = true })
mp.add_key_binding("F3", 'acompressor-inc-knee', function() update_param('Knee', 1); end, { repeatable = true })
mp.add_key_binding("Shift+F3", 'acompressor-dec-knee', function() update_param('Knee', -1); end, { repeatable = true })
mp.add_key_binding("F4", 'acompressor-inc-makeup', function() update_param('Makeup', 1); end, { repeatable = true })
mp.add_key_binding("Shift+F4", 'acompressor-dec-makeup', function() update_param('Makeup', -1); end, { repeatable = true })
mp.add_key_binding("F5", 'acompressor-inc-attack', function() update_param('Attack', 10); end, { repeatable = true })
mp.add_key_binding("Shift+F5", 'acompressor-dec-attack', function() update_param('Attack', -10); end, { repeatable = true })
mp.add_key_binding("F6", 'acompressor-inc-release', function() update_param('Release', 10); end, { repeatable = true })
mp.add_key_binding("Shift+F6", 'acompressor-dec-release', function() update_param('Release', -10); end, { repeatable = true })
if o.default_enable then
local af, i = get_filter()
af[i].enabled = true
mp.set_property_native('af', af)
end
+4 -3
View File
@@ -66,8 +66,8 @@ function autocrop_start()
-- insert the cropdetect filter
ret=mp.command(
string.format(
'vf add @%s:lavfi=graph="cropdetect=limit=24:round=2:reset=0"',
cropdetect_label
'vf add @%s:cropdetect=limit=%f:round=2:reset=0',
cropdetect_label, 24/255
)
)
-- wait to gather data
@@ -79,6 +79,7 @@ function do_crop()
local cropdetect_metadata = mp.get_property_native(
string.format("vf-metadata/%s", cropdetect_label)
)
-- use it to crop if its valid
if cropdetect_metadata then
if cropdetect_metadata["lavfi.cropdetect.w"]
@@ -86,7 +87,7 @@ function do_crop()
and cropdetect_metadata["lavfi.cropdetect.x"]
and cropdetect_metadata["lavfi.cropdetect.y"]
then
mp.command(string.format("vf add @%s:crop=%s:%s:%s:%s",
mp.command(string.format("vf add @%s:lavfi-crop=w=%s:h=%s:x=%s:y=%s",
crop_label,
cropdetect_metadata["lavfi.cropdetect.w"],
cropdetect_metadata["lavfi.cropdetect.h"],
+38 -27
View File
@@ -3,13 +3,13 @@
-- currently playing video.
--
-- It registers the key-binding ctrl+d, which when pressed, inserts the filters
-- ``vf=lavfi=idet,pullup,vf=lavfi=idet``. After 4 seconds, it removes these
-- ``vf=idet,lavfi-pullup,idet``. After 4 seconds, it removes these
-- filters and decides whether the content is progressive, interlaced, or
-- telecined and the interlacing field dominance.
--
-- Based on this information, it may set mpv's ``deinterlace`` property (which
-- usually inserts the yadif filter), or insert the ``pullup`` filter if the
-- content is telecined. It also sets mpv's ``field-dominance`` property.
-- content is telecined. It also sets field dominance with lavfi setfield.
--
-- OPTIONS:
-- The default detection time may be overridden by adding
@@ -22,16 +22,14 @@
-- To see counts of the various types of frames for each detection phase,
-- the verbosity can be increased with
--
-- --msg-level autodeint=v
--
-- This script requires a recent version of ffmpeg for which the idet
-- filter adds the required metadata.
-- --msg-level=autodeint=v
require "mp.msg"
script_name = mp.get_script_name()
detect_label = string.format("%s-detect", script_name)
pullup_label = string.format("%s", script_name)
dominance_label = string.format("%s-dominance", script_name)
ivtc_detect_label = string.format("%s-ivtc-detect", script_name)
-- number of seconds to gather cropdetect data
@@ -55,6 +53,10 @@ function del_filter_if_present(label)
return false
end
local function add_vf(label, filter)
return mp.command(('vf add @%s:%s'):format(label, filter))
end
function start_detect()
-- exit if detection is already in progress
if timer then
@@ -64,11 +66,13 @@ function start_detect()
mp.set_property("deinterlace","no")
del_filter_if_present(pullup_label)
del_filter_if_present(dominance_label)
-- insert the detection filter
local cmd = string.format('vf add @%s:lavfi=graph="idet",@%s:pullup,@%s:lavfi=graph="idet"',
detect_label, pullup_label, ivtc_detect_label)
if not mp.command(cmd) then
-- insert the detection filters
if not (add_vf(detect_label, 'idet') and
add_vf(dominance_label, 'setfield=mode=auto') and
add_vf(pullup_label, 'lavfi-pullup') and
add_vf(ivtc_detect_label, 'idet')) then
mp.msg.error("failed to insert detection filters")
return
end
@@ -88,12 +92,12 @@ progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3, 4
function judge(label)
-- get the metadata
local result = mp.get_property_native(string.format("vf-metadata/%s", label))
num_tff = tonumber(result["lavfi.idet.multiple.tff"])
num_bff = tonumber(result["lavfi.idet.multiple.bff"])
num_progressive = tonumber(result["lavfi.idet.multiple.progressive"])
num_undetermined = tonumber(result["lavfi.idet.multiple.undetermined"])
num_interlaced = num_tff + num_bff
num_determined = num_interlaced + num_progressive
local num_tff = tonumber(result["lavfi.idet.multiple.tff"])
local num_bff = tonumber(result["lavfi.idet.multiple.bff"])
local num_progressive = tonumber(result["lavfi.idet.multiple.progressive"])
local num_undetermined = tonumber(result["lavfi.idet.multiple.undetermined"])
local num_interlaced = num_tff + num_bff
local num_determined = num_interlaced + num_progressive
mp.msg.verbose(label.." progressive = "..num_progressive)
mp.msg.verbose(label.." interlaced-tff = "..num_tff)
@@ -116,26 +120,33 @@ end
function select_filter()
-- handle the first detection filter results
verdict = judge(detect_label)
local verdict = judge(detect_label)
local ivtc_verdict = judge(ivtc_detect_label)
local dominance = "auto"
if verdict == progressive then
mp.msg.info("progressive: doing nothing")
stop_detect()
del_filter_if_present(dominance_label)
del_filter_if_present(pullup_label)
return
elseif verdict == interlaced_tff then
mp.set_property("field-dominance", "top")
elseif verdict == interlaced_bff then
mp.set_property("field-dominance", "bottom")
elseif verdict == interlaced then
mp.set_property("field-dominance", "auto")
else
if verdict == interlaced_tff then
dominance = "tff"
add_vf(dominance_label, 'setfield=mode='..dominance)
elseif verdict == interlaced_bff then
dominance = "bff"
add_vf(dominance_label, 'setfield=mode='..dominance)
else
del_filter_if_present(dominance_label)
end
end
-- handle the ivtc detection filter results
verdict = judge(ivtc_detect_label)
if verdict == progressive then
mp.msg.info(string.format("telecinied with %s field dominance: using pullup", mp.get_property("field-dominance")))
if ivtc_verdict == progressive then
mp.msg.info(string.format("telecined with %s field dominance: using pullup", dominance))
stop_detect()
else
mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", mp.get_property("field-dominance")))
mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", dominance))
del_filter_if_present(pullup_label)
mp.set_property("deinterlace","yes")
stop_detect()
+32 -9
View File
@@ -53,6 +53,37 @@ table.filter = function(t, iter)
end
end
-- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus
-- Released under the MIT License
-- http://www.davekoelle.com/files/alphanum.lua
-- split a string into a table of number and string values
function splitbynum(s)
local result = {}
for x, y in (s or ""):gmatch("(%d*)(%D*)") do
if x ~= "" then table.insert(result, tonumber(x)) end
if y ~= "" then table.insert(result, y) end
end
return result
end
function clean_key(k)
k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower()
return splitbynum(k)
end
-- compare two strings
function alnumcomp(x, y)
local xt, yt = clean_key(x), clean_key(y)
for i = 1, math.min(#xt, #yt) do
local xe, ye = xt[i], yt[i]
if type(xe) == "string" then ye = tostring(ye)
elseif type(ye) == "string" then xe = tostring(xe) end
if xe ~= ye then return xe < ye end
end
return #xt < #yt
end
function find_and_add_entries()
local path = mp.get_property("path", "")
local dir, filename = mputils.split_path(path)
@@ -81,15 +112,7 @@ function find_and_add_entries()
end
return EXTENSIONS[string.lower(ext)]
end)
table.sort(files, function (a, b)
local len = string.len(a) - string.len(b)
if len ~= 0 then -- case for ordering filename ending with such as X.Y.Z
local ext = string.len(get_extension(a)) + 1
a = string.sub(a, 1, -ext)
b = string.sub(b, 1, -ext)
end
return string.lower(a) < string.lower(b)
end)
table.sort(files, alnumcomp)
if dir == "." then
dir = ""
+27 -6
View File
@@ -18,10 +18,10 @@ function update_status_line()
atsl("(Buffering) ")
end
if mp.get_property("vid") ~= "no" then
if mp.get_property("aid") ~= "no" then
atsl("A")
end
if mp.get_property("aid") ~= "no" then
if mp.get_property("vid") ~= "no" then
atsl("V")
end
@@ -43,7 +43,7 @@ function update_status_line()
r = mp.get_property_number("avsync", nil)
if r ~= nil then
atsl(string.format(" A-V: %7.3f", r))
atsl(string.format(" A-V: %f", r))
end
r = mp.get_property("total-avsync-change", 0)
@@ -51,12 +51,24 @@ function update_status_line()
atsl(string.format(" ct:%7.3f", r))
end
r = mp.get_property_number("drop-frame-count", -1)
r = mp.get_property_number("decoder-drop-frame-count", -1)
if r > 0 then
atsl(" Late: ")
atsl(r)
end
r = mp.get_property_osd("video-bitrate")
if r ~= nil and r ~= "" then
atsl(" Vb: ")
atsl(r)
end
r = mp.get_property_osd("audio-bitrate")
if r ~= nil and r ~= "" then
atsl(" Ab: ")
atsl(r)
end
r = mp.get_property_number("cache", 0)
if r > 0 then
atsl(string.format(" Cache: %d%% ", r))
@@ -66,6 +78,15 @@ function update_status_line()
mp.set_property("options/term-status-msg", newStatus)
end
-- Register the event
mp.register_event("tick", update_status_line)
timer = mp.add_periodic_timer(1, update_status_line)
function on_pause_change(name, value)
if value == false then
timer:resume()
else
timer:stop()
end
mp.add_timeout(0.1, update_status_line)
end
mp.observe_property("pause", "bool", on_pause_change)
mp.register_event("seek", update_status_line)
+7
View File
@@ -137,6 +137,13 @@ elements_matroska = (
'LuminanceMin, 55DA, float',
),
),
'Projection, 7670, sub', (
'ProjectionType, 7671, uint',
'ProjectionPrivate, 7672, binary',
'ProjectionPoseYaw, 7673, float',
'ProjectionPosePitch, 7674, float',
'ProjectionPoseRoll, 7675, float',
),
),
'Audio, e1, sub', (
'SamplingFrequency, b5, float',
@@ -1,3 +1,13 @@
#!/bin/bash -l
#!/bin/sh
export MPVBUNDLE="true"
$SHELL -c "$(dirname "$0")/mpv --player-operation-mode=pseudo-gui"
# set the right args for the user specified standard shell
# to load the expected profiles and configs
args="-c"
case "$SHELL" in
*bash) args="-l $args";;
*zsh) args="-l -i $args";;
esac
cd "$(dirname "$0")"
$SHELL $args "./mpv --player-operation-mode=pseudo-gui"
+1 -5
View File
@@ -87,10 +87,6 @@ end
class Libav < TravisDepsBuilder
def build_map
{
"libav-stable" => {
:action => :stable,
:url => 'http://libav.org/releases/libav-12.tar.gz'
},
"libav-git" => {
:action => :git,
:url => "git://git.libav.org/libav.git"
@@ -133,7 +129,7 @@ end
class Dependencies < TravisDepsBuilder
def deps
packages = {
'linux' => 'pkg-config fontconfig libfribidi-dev yasm',
'linux' => 'pkg-config fontconfig libfribidi-dev yasm libxinerama-dev libxrandr-dev',
'osx' => 'pkg-config fontconfig freetype fribidi yasm'
}
package_manager_update
+1 -1
View File
@@ -11,7 +11,7 @@ if [ "x$TRAVIS_BRANCH" != "xmaster" ]; then
fi
TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE:-HEAD^..}
if git diff --quiet --name-only --exit-code "$TRAVIS_COMMIT_RANGE" DOCS/; then
if git diff --quiet --name-only --exit-code "$TRAVIS_COMMIT_RANGE" DOCS/ VERSION; then
exit;
fi
+1 -1
View File
@@ -1 +1 @@
0.26.0
0.27.0
+2
View File
@@ -4,6 +4,8 @@ branches:
- appveyor
environment:
global:
PATH: C:\msys64\usr\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
matrix:
- MSYSTEM: MINGW64
- MSYSTEM: MINGW32
+445
View File
@@ -0,0 +1,445 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include "common/common.h"
#include "chmap.h"
#include "fmt-conversion.h"
#include "format.h"
#include "aframe.h"
struct mp_aframe {
AVFrame *av_frame;
// We support channel layouts different from AVFrame channel masks
struct mp_chmap chmap;
// We support spdif formats, which are allocated as AV_SAMPLE_FMT_S16.
int format;
double pts;
};
static void free_frame(void *ptr)
{
struct mp_aframe *frame = ptr;
av_frame_free(&frame->av_frame);
}
struct mp_aframe *mp_aframe_create(void)
{
struct mp_aframe *frame = talloc_zero(NULL, struct mp_aframe);
frame->pts = MP_NOPTS_VALUE;
frame->av_frame = av_frame_alloc();
if (!frame->av_frame)
abort();
talloc_set_destructor(frame, free_frame);
return frame;
}
struct mp_aframe *mp_aframe_new_ref(struct mp_aframe *frame)
{
if (!frame)
return NULL;
struct mp_aframe *dst = mp_aframe_create();
dst->chmap = frame->chmap;
dst->format = frame->format;
dst->pts = frame->pts;
if (mp_aframe_is_allocated(frame)) {
if (av_frame_ref(dst->av_frame, frame->av_frame) < 0)
abort();
} else {
// av_frame_ref() would fail.
mp_aframe_config_copy(dst, frame);
}
return dst;
}
// Revert to state after mp_aframe_create().
void mp_aframe_reset(struct mp_aframe *frame)
{
av_frame_unref(frame->av_frame);
frame->chmap.num = 0;
frame->format = 0;
frame->pts = MP_NOPTS_VALUE;
}
// Remove all actual audio data and leave only the metadata.
void mp_aframe_unref_data(struct mp_aframe *frame)
{
// In a fucked up way, this is less complex than just unreffing the data.
struct mp_aframe *tmp = mp_aframe_create();
MPSWAP(struct mp_aframe, *tmp, *frame);
mp_aframe_reset(frame);
mp_aframe_config_copy(frame, tmp);
talloc_free(tmp);
}
// Return a new reference to the data in av_frame. av_frame itself is not
// touched. Returns NULL if not representable, or if input is NULL.
// Does not copy the timestamps.
struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame)
{
if (!av_frame || av_frame->width > 0 || av_frame->height > 0)
return NULL;
int format = af_from_avformat(av_frame->format);
if (!format && av_frame->format != AV_SAMPLE_FMT_NONE)
return NULL;
struct mp_aframe *frame = mp_aframe_create();
// This also takes care of forcing refcounting.
if (av_frame_ref(frame->av_frame, av_frame) < 0)
abort();
frame->format = format;
mp_chmap_from_lavc(&frame->chmap, frame->av_frame->channel_layout);
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being a stupid POS again
if (frame->chmap.num != frame->av_frame->channels)
mp_chmap_from_channels(&frame->chmap, av_frame->channels);
#endif
return frame;
}
// Return a new reference to the data in frame. Returns NULL is not
// representable (), or if input is NULL.
// Does not copy the timestamps.
struct AVFrame *mp_aframe_to_avframe(struct mp_aframe *frame)
{
if (!frame)
return NULL;
if (af_to_avformat(frame->format) != frame->av_frame->format)
return NULL;
if (!mp_chmap_is_lavc(&frame->chmap))
return NULL;
return av_frame_clone(frame->av_frame);
}
struct AVFrame *mp_aframe_to_avframe_and_unref(struct mp_aframe *frame)
{
AVFrame *av = mp_aframe_to_avframe(frame);
talloc_free(frame);
return av;
}
// You must not use this.
struct AVFrame *mp_aframe_get_raw_avframe(struct mp_aframe *frame)
{
return frame->av_frame;
}
// Return whether it has associated audio data. (If not, metadata only.)
bool mp_aframe_is_allocated(struct mp_aframe *frame)
{
return frame->av_frame->buf[0] || frame->av_frame->extended_data[0];
}
// Clear dst, and then copy the configuration to it.
void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src)
{
mp_aframe_reset(dst);
dst->chmap = src->chmap;
dst->format = src->format;
dst->pts = src->pts;
if (av_frame_copy_props(dst->av_frame, src->av_frame) < 0)
abort();
dst->av_frame->format = src->av_frame->format;
dst->av_frame->channel_layout = src->av_frame->channel_layout;
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being a stupid POS again
dst->av_frame->channels = src->av_frame->channels;
#endif
}
// Return whether a and b use the same physical audio format. Extra metadata
// such as PTS, per-frame signalling, and AVFrame side data is not compared.
bool mp_aframe_config_equals(struct mp_aframe *a, struct mp_aframe *b)
{
struct mp_chmap ca = {0}, cb = {0};
mp_aframe_get_chmap(a, &ca);
mp_aframe_get_chmap(b, &cb);
return mp_chmap_equals(&ca, &cb) &&
mp_aframe_get_rate(a) == mp_aframe_get_rate(b) &&
mp_aframe_get_format(a) == mp_aframe_get_format(b);
}
// Return whether all required format fields have been set.
bool mp_aframe_config_is_valid(struct mp_aframe *frame)
{
return frame->format && frame->chmap.num && frame->av_frame->sample_rate;
}
// Return the pointer to the first sample for each plane. The pointers stay
// valid until the next call that mutates frame somehow. You must not write to
// the audio data. Returns NULL if no frame allocated.
uint8_t **mp_aframe_get_data_ro(struct mp_aframe *frame)
{
return mp_aframe_is_allocated(frame) ? frame->av_frame->extended_data : NULL;
}
// Like mp_aframe_get_data_ro(), but you can write to the audio data.
// Additionally, it will return NULL if copy-on-write fails.
uint8_t **mp_aframe_get_data_rw(struct mp_aframe *frame)
{
if (!mp_aframe_is_allocated(frame))
return NULL;
if (av_frame_make_writable(frame->av_frame) < 0)
return NULL;
return frame->av_frame->extended_data;
}
int mp_aframe_get_format(struct mp_aframe *frame)
{
return frame->format;
}
bool mp_aframe_get_chmap(struct mp_aframe *frame, struct mp_chmap *out)
{
if (!mp_chmap_is_valid(&frame->chmap))
return false;
*out = frame->chmap;
return true;
}
int mp_aframe_get_channels(struct mp_aframe *frame)
{
return frame->chmap.num;
}
int mp_aframe_get_rate(struct mp_aframe *frame)
{
return frame->av_frame->sample_rate;
}
int mp_aframe_get_size(struct mp_aframe *frame)
{
return frame->av_frame->nb_samples;
}
double mp_aframe_get_pts(struct mp_aframe *frame)
{
return frame->pts;
}
bool mp_aframe_set_format(struct mp_aframe *frame, int format)
{
if (mp_aframe_is_allocated(frame))
return false;
enum AVSampleFormat av_format = af_to_avformat(format);
if (av_format == AV_SAMPLE_FMT_NONE && format) {
if (!af_fmt_is_spdif(format))
return false;
av_format = AV_SAMPLE_FMT_S16;
}
frame->format = format;
frame->av_frame->format = av_format;
return true;
}
bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in)
{
if (!mp_chmap_is_valid(in) && !mp_chmap_is_empty(in))
return false;
if (mp_aframe_is_allocated(frame) && in->num != frame->chmap.num)
return false;
uint64_t lavc_layout = mp_chmap_to_lavc_unchecked(in);
if (!lavc_layout && in->num)
return false;
frame->chmap = *in;
frame->av_frame->channel_layout = lavc_layout;
#if LIBAVUTIL_VERSION_MICRO >= 100
// FFmpeg being a stupid POS again
frame->av_frame->channels = frame->chmap.num;
#endif
return true;
}
bool mp_aframe_set_rate(struct mp_aframe *frame, int rate)
{
if (rate < 1 && rate > 10000000)
return false;
frame->av_frame->sample_rate = rate;
return true;
}
bool mp_aframe_set_size(struct mp_aframe *frame, int samples)
{
if (!mp_aframe_is_allocated(frame) || mp_aframe_get_size(frame) < samples)
return false;
frame->av_frame->nb_samples = MPMAX(samples, 0);
return true;
}
void mp_aframe_set_pts(struct mp_aframe *frame, double pts)
{
frame->pts = pts;
}
// Return number of data pointers.
int mp_aframe_get_planes(struct mp_aframe *frame)
{
return af_fmt_is_planar(mp_aframe_get_format(frame))
? mp_aframe_get_channels(frame) : 1;
}
// Return number of bytes between 2 consecutive samples on the same plane.
size_t mp_aframe_get_sstride(struct mp_aframe *frame)
{
int format = mp_aframe_get_format(frame);
return af_fmt_to_bytes(format) *
(af_fmt_is_planar(format) ? 1 : mp_aframe_get_channels(frame));
}
// Set data to the audio after the given number of samples (i.e. slice it).
void mp_aframe_skip_samples(struct mp_aframe *f, int samples)
{
assert(samples >= 0 && samples <= mp_aframe_get_size(f));
int num_planes = mp_aframe_get_planes(f);
size_t sstride = mp_aframe_get_sstride(f);
for (int n = 0; n < num_planes; n++)
f->av_frame->extended_data[n] += samples * sstride;
f->av_frame->nb_samples -= samples;
if (f->pts != MP_NOPTS_VALUE)
f->pts += samples / (double)mp_aframe_get_rate(f);
}
// Return the timestamp of the sample just after the end of this frame.
double mp_aframe_end_pts(struct mp_aframe *f)
{
int rate = mp_aframe_get_rate(f);
if (f->pts == MP_NOPTS_VALUE || rate < 1)
return MP_NOPTS_VALUE;
return f->pts + f->av_frame->nb_samples / (double)rate;
}
// Return the duration in seconds of the frame (0 if invalid).
double mp_aframe_duration(struct mp_aframe *f)
{
int rate = mp_aframe_get_rate(f);
if (rate < 1)
return 0;
return f->av_frame->nb_samples / (double)rate;
}
// Clip the given frame to the given timestamp range. Adjusts the frame size
// and timestamp.
void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end)
{
double f_end = mp_aframe_end_pts(f);
int rate = mp_aframe_get_rate(f);
if (f_end == MP_NOPTS_VALUE)
return;
if (end != MP_NOPTS_VALUE) {
if (f_end >= end) {
if (f->pts >= end) {
f->av_frame->nb_samples = 0;
} else {
int new = (end - f->pts) * rate;
f->av_frame->nb_samples = MPCLAMP(new, 0, f->av_frame->nb_samples);
}
}
}
if (start != MP_NOPTS_VALUE) {
if (f->pts < start) {
if (f_end <= start) {
f->av_frame->nb_samples = 0;
f->pts = f_end;
} else {
int skip = (start - f->pts) * rate;
skip = MPCLAMP(skip, 0, f->av_frame->nb_samples);
mp_aframe_skip_samples(f, skip);
}
}
}
}
struct mp_aframe_pool {
AVBufferPool *avpool;
int element_size;
};
struct mp_aframe_pool *mp_aframe_pool_create(void *ta_parent)
{
return talloc_zero(ta_parent, struct mp_aframe_pool);
}
static void mp_aframe_pool_destructor(void *p)
{
struct mp_aframe_pool *pool = p;
av_buffer_pool_uninit(&pool->avpool);
}
// Like mp_aframe_allocate(), but use the pool to allocate data.
int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame,
int samples)
{
int planes = mp_aframe_get_planes(frame);
size_t sstride = mp_aframe_get_sstride(frame);
int plane_size = MP_ALIGN_UP(sstride * MPMAX(samples, 1), 32);
int size = plane_size * planes;
if (size <= 0 || mp_aframe_is_allocated(frame))
return -1;
if (!pool->avpool || size > pool->element_size) {
size_t alloc = ta_calc_prealloc_elems(size);
if (alloc >= INT_MAX)
return -1;
av_buffer_pool_uninit(&pool->avpool);
pool->element_size = alloc;
pool->avpool = av_buffer_pool_init(pool->element_size, NULL);
if (!pool->avpool)
return -1;
talloc_set_destructor(pool, mp_aframe_pool_destructor);
}
// Yes, you have to do all this shit manually.
// At least it's less stupid than av_frame_get_buffer(), which just wipes
// the entire frame struct on error for no reason.
AVFrame *av_frame = frame->av_frame;
if (av_frame->extended_data != av_frame->data)
av_freep(&av_frame->extended_data); // sigh
av_frame->extended_data =
av_mallocz_array(planes, sizeof(av_frame->extended_data[0]));
if (!av_frame->extended_data)
abort();
av_frame->buf[0] = av_buffer_pool_get(pool->avpool);
if (!av_frame->buf[0])
return -1;
av_frame->linesize[0] = samples * sstride;
for (int n = 0; n < planes; n++)
av_frame->extended_data[n] = av_frame->buf[0]->data + n * plane_size;
av_frame->nb_samples = samples;
return 0;
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include <stdint.h>
struct mp_aframe;
struct AVFrame;
struct mp_chmap;
struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame);
struct mp_aframe *mp_aframe_create(void);
struct mp_aframe *mp_aframe_new_ref(struct mp_aframe *frame);
void mp_aframe_reset(struct mp_aframe *frame);
void mp_aframe_unref_data(struct mp_aframe *frame);
struct AVFrame *mp_aframe_to_avframe(struct mp_aframe *frame);
struct AVFrame *mp_aframe_to_avframe_and_unref(struct mp_aframe *frame);
struct AVFrame *mp_aframe_get_raw_avframe(struct mp_aframe *frame);
bool mp_aframe_is_allocated(struct mp_aframe *frame);
void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src);
bool mp_aframe_config_equals(struct mp_aframe *a, struct mp_aframe *b);
bool mp_aframe_config_is_valid(struct mp_aframe *frame);
uint8_t **mp_aframe_get_data_ro(struct mp_aframe *frame);
uint8_t **mp_aframe_get_data_rw(struct mp_aframe *frame);
int mp_aframe_get_format(struct mp_aframe *frame);
bool mp_aframe_get_chmap(struct mp_aframe *frame, struct mp_chmap *out);
int mp_aframe_get_channels(struct mp_aframe *frame);
int mp_aframe_get_rate(struct mp_aframe *frame);
int mp_aframe_get_size(struct mp_aframe *frame);
double mp_aframe_get_pts(struct mp_aframe *frame);
bool mp_aframe_set_format(struct mp_aframe *frame, int format);
bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in);
bool mp_aframe_set_rate(struct mp_aframe *frame, int rate);
bool mp_aframe_set_size(struct mp_aframe *frame, int samples);
void mp_aframe_set_pts(struct mp_aframe *frame, double pts);
int mp_aframe_get_planes(struct mp_aframe *frame);
size_t mp_aframe_get_sstride(struct mp_aframe *frame);
void mp_aframe_skip_samples(struct mp_aframe *f, int samples);
double mp_aframe_end_pts(struct mp_aframe *f);
double mp_aframe_duration(struct mp_aframe *f);
void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end);
struct mp_aframe_pool;
struct mp_aframe_pool *mp_aframe_pool_create(void *ta_parent);
int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame,
int samples);
+25
View File
@@ -29,6 +29,7 @@
#include "common/common.h"
#include "fmt-conversion.h"
#include "audio.h"
#include "aframe.h"
static void update_redundant_info(struct mp_audio *mpa)
{
@@ -403,6 +404,30 @@ fail:
return NULL;
}
struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe)
{
struct AVFrame *av = mp_aframe_get_raw_avframe(aframe);
struct mp_audio *res = mp_audio_from_avframe(av);
if (!res)
return NULL;
struct mp_chmap chmap = {0};
mp_aframe_get_chmap(aframe, &chmap);
mp_audio_set_channels(res, &chmap);
mp_audio_set_format(res, mp_aframe_get_format(aframe));
res->pts = mp_aframe_get_pts(aframe);
return res;
}
void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src)
{
*dst = (struct mp_audio){0};
struct mp_chmap chmap = {0};
mp_aframe_get_chmap(src, &chmap);
mp_audio_set_channels(dst, &chmap);
mp_audio_set_format(dst, mp_aframe_get_format(src));
dst->rate = mp_aframe_get_rate(src);
}
int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe)
{
av_frame_unref(avframe);
+4
View File
@@ -83,6 +83,10 @@ 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_aframe;
struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe);
void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src);
struct mp_audio_pool;
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
+2 -2
View File
@@ -23,7 +23,7 @@
#include "demux/demux.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "audio/aframe.h"
#include "dec_audio.h"
struct mp_decoder_list;
@@ -39,7 +39,7 @@ struct ad_functions {
bool (*send_packet)(struct dec_audio *da, struct demux_packet *pkt);
// Return whether decoding is still going on (false if EOF was reached).
// Never returns false & *out set, but can return true with !*out.
bool (*receive_frame)(struct dec_audio *da, struct mp_audio **out);
bool (*receive_frame)(struct dec_audio *da, struct mp_aframe **out);
};
enum ad_ctrl {
+12 -21
View File
@@ -40,7 +40,6 @@
struct priv {
AVCodecContext *avctx;
AVFrame *avframe;
struct mp_audio frame;
bool force_channel_map;
uint32_t skip_samples, trim_samples;
bool preroll_done;
@@ -191,7 +190,7 @@ static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
return true;
}
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
static bool receive_frame(struct dec_audio *da, struct mp_aframe **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
@@ -217,25 +216,18 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
double out_pts = mp_pts_from_av(priv->avframe->pts, &priv->codec_timebase);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
struct mp_aframe *mpframe = mp_aframe_from_avframe(priv->avframe);
if (!mpframe)
return true;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
if (priv->force_channel_map) {
if (lavc_chmap.num == da->codec->channels.num)
lavc_chmap = da->codec->channels;
}
mp_audio_set_channels(mpframe, &lavc_chmap);
if (priv->force_channel_map)
mp_aframe_set_chmap(mpframe, &da->codec->channels);
mpframe->pts = out_pts;
if (out_pts == MP_NOPTS_VALUE)
out_pts = priv->next_pts;
mp_aframe_set_pts(mpframe, out_pts);
if (mpframe->pts == MP_NOPTS_VALUE)
mpframe->pts = priv->next_pts;
if (mpframe->pts != MP_NOPTS_VALUE)
priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
priv->next_pts = mp_aframe_end_pts(mpframe);
#if LIBAVCODEC_VERSION_MICRO >= 100
AVFrameSideData *sd =
@@ -254,14 +246,14 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
priv->preroll_done = true;
}
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
uint32_t skip = MPMIN(priv->skip_samples, mp_aframe_get_size(mpframe));
if (skip) {
mp_audio_skip_samples(mpframe, skip);
mp_aframe_skip_samples(mpframe, skip);
priv->skip_samples -= skip;
}
uint32_t trim = MPMIN(priv->trim_samples, mpframe->samples);
uint32_t trim = MPMIN(priv->trim_samples, mp_aframe_get_size(mpframe));
if (trim) {
mpframe->samples -= trim;
mp_aframe_set_size(mpframe, mp_aframe_get_size(mpframe) - trim);
priv->trim_samples -= trim;
}
@@ -269,7 +261,6 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
av_frame_unref(priv->avframe);
MP_DBG(da, "Decoded %d samples\n", mpframe->samples);
return true;
}
+29 -12
View File
@@ -40,8 +40,9 @@ struct spdifContext {
uint8_t out_buffer[OUTBUF_SIZE];
bool need_close;
bool use_dts_hd;
struct mp_audio fmt;
struct mp_audio_pool *pool;
struct mp_aframe *fmt;
int sstride;
struct mp_aframe_pool *pool;
bool got_eof;
struct demux_packet *queued_packet;
};
@@ -84,7 +85,7 @@ static int init(struct dec_audio *da, const char *decoder)
da->priv = spdif_ctx;
spdif_ctx->log = da->log;
spdif_ctx->use_dts_hd = da->opts->dtshd;
spdif_ctx->pool = mp_audio_pool_create(spdif_ctx);
spdif_ctx->pool = mp_aframe_pool_create(spdif_ctx);
if (strcmp(decoder, "spdif_dts_hd") == 0)
spdif_ctx->use_dts_hd = true;
@@ -198,6 +199,9 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
AVDictionary *format_opts = NULL;
spdif_ctx->fmt = mp_aframe_create();
talloc_steal(spdif_ctx, spdif_ctx->fmt);
int num_channels = 0;
int sample_format = 0;
int samplerate = 0;
@@ -246,9 +250,14 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
default:
abort();
}
mp_audio_set_num_channels(&spdif_ctx->fmt, num_channels);
mp_audio_set_format(&spdif_ctx->fmt, sample_format);
spdif_ctx->fmt.rate = samplerate;
struct mp_chmap chmap;
mp_chmap_from_channels(&chmap, num_channels);
mp_aframe_set_chmap(spdif_ctx->fmt, &chmap);
mp_aframe_set_format(spdif_ctx->fmt, sample_format);
mp_aframe_set_rate(spdif_ctx->fmt, samplerate);
spdif_ctx->sstride = mp_aframe_get_sstride(spdif_ctx->fmt);
if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
MP_FATAL(da, "libavformat spdif initialization failed.\n");
@@ -279,7 +288,7 @@ static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
return true;
}
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
static bool receive_frame(struct dec_audio *da, struct mp_aframe **out)
{
struct spdifContext *spdif_ctx = da->priv;
@@ -308,13 +317,21 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
goto done;
}
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
*out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
if (!*out)
*out = mp_aframe_new_ref(spdif_ctx->fmt);
int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
if (mp_aframe_pool_allocate(spdif_ctx->pool, *out, samples) < 0) {
TA_FREEP(out);
goto done;
}
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
(*out)->pts = pts;
uint8_t **data = mp_aframe_get_data_rw(*out);
if (!data) {
TA_FREEP(out);
goto done;
}
memcpy(data[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
mp_aframe_set_pts(*out, pts);
done:
talloc_free(spdif_ctx->queued_packet);
+13 -20
View File
@@ -38,8 +38,6 @@
#include "dec_audio.h"
#include "ad.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/filter/af.h"
@@ -179,25 +177,24 @@ static void fix_audio_pts(struct dec_audio *da)
if (!da->current_frame)
return;
if (da->current_frame->pts != MP_NOPTS_VALUE) {
double newpts = da->current_frame->pts;
double frame_pts = mp_aframe_get_pts(da->current_frame);
if (frame_pts != MP_NOPTS_VALUE) {
if (da->pts != MP_NOPTS_VALUE)
MP_STATS(da, "value %f audio-pts-err", da->pts - newpts);
MP_STATS(da, "value %f audio-pts-err", da->pts - frame_pts);
// Keep the interpolated timestamp if it doesn't deviate more
// than 1 ms from the real one. (MKV rounded timestamps.)
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
da->pts = newpts;
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - frame_pts) > 0.001)
da->pts = frame_pts;
}
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
da->pts = 0;
da->current_frame->pts = da->pts;
mp_aframe_set_pts(da->current_frame, da->pts);
if (da->pts != MP_NOPTS_VALUE)
da->pts += da->current_frame->samples / (double)da->current_frame->rate;
da->pts += mp_aframe_duration(da->current_frame);
}
void audio_work(struct dec_audio *da)
@@ -228,11 +225,6 @@ void audio_work(struct dec_audio *da)
bool progress = da->ad_driver->receive_frame(da, &da->current_frame);
if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
talloc_free(da->current_frame);
da->current_frame = NULL;
}
da->current_state = da->current_frame ? DATA_OK : DATA_AGAIN;
if (!progress)
da->current_state = DATA_EOF;
@@ -242,10 +234,11 @@ void audio_work(struct dec_audio *da)
bool segment_end = da->current_state == DATA_EOF;
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->end;
if (da->current_frame->samples == 0) {
mp_aframe_clip_timestamps(da->current_frame, da->start, da->end);
double frame_pts = mp_aframe_get_pts(da->current_frame);
if (frame_pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
segment_end = frame_pts >= da->end;
if (mp_aframe_get_size(da->current_frame) == 0) {
talloc_free(da->current_frame);
da->current_frame = NULL;
}
@@ -280,7 +273,7 @@ void audio_work(struct dec_audio *da)
// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
// DATA_EOF: end of file, no more frames to be expected
// DATA_AGAIN: dropped frame or something similar
int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame)
int audio_get_frame(struct dec_audio *da, struct mp_aframe **out_frame)
{
*out_frame = NULL;
if (da->current_frame) {
+3 -4
View File
@@ -19,11 +19,10 @@
#define MPLAYER_DEC_AUDIO_H
#include "audio/chmap.h"
#include "audio/audio.h"
#include "audio/aframe.h"
#include "demux/demux.h"
#include "demux/stheader.h"
struct mp_audio_buffer;
struct mp_decoder_list;
struct dec_audio {
@@ -48,7 +47,7 @@ struct dec_audio {
double start, end;
struct demux_packet *packet;
struct demux_packet *new_segment;
struct mp_audio *current_frame;
struct mp_aframe *current_frame;
int current_state;
};
@@ -57,7 +56,7 @@ int audio_init_best_codec(struct dec_audio *d_audio);
void audio_uninit(struct dec_audio *d_audio);
void audio_work(struct dec_audio *d_audio);
int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame);
int audio_get_frame(struct dec_audio *d_audio, struct mp_aframe **out_frame);
void audio_reset_decoding(struct dec_audio *d_audio);
+9 -5
View File
@@ -76,6 +76,7 @@ struct af_resample {
int allow_detach;
char **avopts;
double playback_speed;
bool is_resampling;
struct AVAudioResampleContext *avrctx;
struct mp_audio avrctx_fmt; // output format of avrctx
struct mp_audio pool_fmt; // format used to allocate frames for avrctx output
@@ -332,6 +333,8 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
// * Also, the input channel layout must have already been set.
avresample_set_channel_mapping(s->avrctx, s->reorder_in);
s->is_resampling = false;
if (avresample_open(s->avrctx) < 0 || avresample_open(s->avrctx_out) < 0) {
MP_ERR(af, "Cannot open Libavresample Context. \n");
goto error;
@@ -518,9 +521,7 @@ static int filter(struct af_instance *af, struct mp_audio *in)
struct af_resample *s = af->priv;
int new_rate = rate_from_speed(s->in_rate_af, s->playback_speed);
bool need_reinit = fabs(new_rate / (double)s->in_rate - 1) > 0.01;
if (s->avrctx) {
if (s->avrctx && !(!s->is_resampling && new_rate == s->in_rate)) {
AVRational r = av_d2q(s->playback_speed * s->in_rate_af / s->in_rate,
INT_MAX / 2);
// Essentially, swr/avresample_set_compensation() does 2 things:
@@ -531,10 +532,13 @@ static int filter(struct af_instance *af, struct mp_audio *in)
// feeding it, until the next filter() call.
int mult = INT_MAX / 2 / MPMAX(MPMAX(abs(r.num), abs(r.den)), 1);
r = (AVRational){ r.num * mult, r.den * mult };
if (avresample_set_compensation(s->avrctx, r.den - r.num, r.den) < 0)
need_reinit = true;
if (avresample_set_compensation(s->avrctx, r.den - r.num, r.den) >= 0) {
new_rate = s->in_rate;
s->is_resampling = true;
}
}
bool need_reinit = fabs(new_rate / (double)s->in_rate - 1) > 0.01;
if (need_reinit && new_rate != s->in_rate) {
// Before reconfiguring, drain the audio that is still buffered
// in the resampler.
+5 -6
View File
@@ -26,7 +26,6 @@
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "options/options.h"
#include "options/m_config.h"
@@ -480,12 +479,12 @@ bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
// --- The following functions just return immutable information.
void ao_get_format(struct ao *ao, struct mp_audio *format)
void ao_get_format(struct ao *ao,
int *samplerate, int *format, struct mp_chmap *channels)
{
*format = (struct mp_audio){0};
mp_audio_set_format(format, ao->format);
mp_audio_set_channels(format, &ao->channels);
format->rate = ao->samplerate;
*samplerate = ao->samplerate;
*format = ao->format;
*channels = ao->channels;
}
const char *ao_get_name(struct ao *ao)
+2 -2
View File
@@ -81,7 +81,6 @@ struct ao;
struct mpv_global;
struct input_ctx;
struct encode_lavc_context;
struct mp_audio;
struct ao *ao_init_best(struct mpv_global *global,
int init_flags,
@@ -89,7 +88,8 @@ struct ao *ao_init_best(struct mpv_global *global,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
void ao_uninit(struct ao *ao);
void ao_get_format(struct ao *ao, struct mp_audio *format);
void ao_get_format(struct ao *ao,
int *samplerate, int *format, struct mp_chmap *channels);
const char *ao_get_name(struct ao *ao);
const char *ao_get_description(struct ao *ao);
bool ao_untimed(struct ao *ao);
+3 -2
View File
@@ -387,8 +387,9 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
}
}
ao->period_size = channels.num * af_fmt_to_bytes(format);
p->outburst -= p->outburst % ao->period_size; // round down
int sstride = channels.num * af_fmt_to_bytes(format);
p->outburst -= p->outburst % sstride; // round down
ao->period_size = p->outburst / sstride;
return 0;
+3
View File
@@ -64,6 +64,9 @@ static int init(struct ao *ao)
if (rsd_init(&priv->rd) < 0)
return -1;
if (ao->device)
rsd_set_param(priv->rd, RSD_HOST, ao->device);
// Actual channel layout unknown.
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext_def(&sel);
+4 -4
View File
@@ -201,9 +201,9 @@ static DWORD __stdcall AudioThread(void *lpParameter)
mpthread_set_name("wasapi event");
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
state->init_ret = wasapi_thread_init(ao);
state->init_ok = wasapi_thread_init(ao);
SetEvent(state->hInitDone);
if (FAILED(state->init_ret))
if (!state->init_ok)
goto exit_label;
MP_DBG(ao, "Entering dispatch loop\n");
@@ -302,7 +302,7 @@ static int init(struct ao *ao)
state->dispatch = mp_dispatch_create(state);
mp_dispatch_set_wakeup_fn(state->dispatch, thread_wakeup, ao);
state->init_ret = E_FAIL;
state->init_ok = false;
state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL);
if (!state->hAudioThread) {
MP_FATAL(ao, "Failed to create audio thread\n");
@@ -312,7 +312,7 @@ static int init(struct ao *ao)
WaitForSingleObject(state->hInitDone, INFINITE); // wait on init complete
SAFE_DESTROY(state->hInitDone,CloseHandle(state->hInitDone));
if (FAILED(state->init_ret)) {
if (!state->init_ok) {
if (!ao->probing)
MP_FATAL(ao, "Received failure from audio thread\n");
uninit(ao);
+6 -14
View File
@@ -45,15 +45,6 @@ typedef struct change_notify {
HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug);
void wasapi_change_uninit(struct ao* ao);
#define EXIT_ON_ERROR(hres) \
do { if (FAILED(hres)) { goto exit_label; } } while(0)
#define SAFE_DESTROY(unk, release) \
do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0)
#define mp_format_res_str(hres) \
(SUCCEEDED(hres) ? "ok" : ((hres) == AUDCLNT_E_UNSUPPORTED_FORMAT) \
? "unsupported" : mp_HRESULT_to_str(hres))
enum wasapi_thread_state {
WASAPI_THREAD_FEED = 0,
WASAPI_THREAD_RESUME,
@@ -64,8 +55,8 @@ enum wasapi_thread_state {
typedef struct wasapi_state {
struct mp_log *log;
bool init_ok; // status of init phase
// Thread handles
HRESULT init_ret; // status of init phase
HANDLE hInitDone; // set when init is complete in audio thread
HANDLE hAudioThread; // the audio thread itself
HANDLE hWake; // thread wakeup event
@@ -113,11 +104,12 @@ void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
bstr wasapi_get_specified_device_string(struct ao *ao);
LPWSTR wasapi_find_deviceID(struct ao *ao);
void wasapi_dispatch(struct ao *ao);
HRESULT wasapi_thread_init(struct ao *ao);
bool wasapi_thread_init(struct ao *ao);
void wasapi_thread_uninit(struct ao *ao);
void wasapi_receive_proxies(wasapi_state *state);
void wasapi_release_proxies(wasapi_state *state);
#define EXIT_ON_ERROR(hres) \
do { if (FAILED(hres)) { goto exit_label; } } while(0)
#define SAFE_DESTROY(unk, release) \
do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0)
#endif
+130 -155
View File
@@ -81,7 +81,7 @@ static const struct wasapi_sample_fmt wasapi_formats[] = {
{AF_FORMAT_S32, 32, 32, &KSDATAFORMAT_SUBTYPE_PCM},
// compatible, assume LSBs are ignored
{AF_FORMAT_S32, 32, 24, &KSDATAFORMAT_SUBTYPE_PCM},
// aka S24 (with conversion) - untested, thus commented.
// aka S24 (with conversion on output)
{AF_FORMAT_S32, 24, 24, &KSDATAFORMAT_SUBTYPE_PCM},
{AF_FORMAT_FLOAT, 32, 32, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT},
{AF_FORMAT_S_AC3, 16, 16, &mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL},
@@ -94,6 +94,20 @@ static const struct wasapi_sample_fmt wasapi_formats[] = {
{0},
};
static void wasapi_get_best_sample_formats(
int src_format, struct wasapi_sample_fmt *out_formats)
{
int mp_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(src_format, mp_formats);
for (int n = 0; mp_formats[n]; n++) {
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (wasapi_formats[i].mp_format == mp_formats[n])
*out_formats++ = wasapi_formats[i];
}
}
*out_formats = (struct wasapi_sample_fmt) {0};
}
static const GUID *format_to_subtype(int format)
{
for (int i = 0; wasapi_formats[i].mp_format; i++) {
@@ -103,18 +117,6 @@ static const GUID *format_to_subtype(int format)
return &KSDATAFORMAT_SPECIFIER_NONE;
}
// "solve" the under-determined inverse of format_to_subtype by assuming the
// input subtype is "special" (i.e. IEC61937)
static int special_subtype_to_format(const GUID *subtype)
{
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (IsEqualGUID(subtype, wasapi_formats[i].subtype) &&
af_fmt_is_spdif(wasapi_formats[i].mp_format))
return wasapi_formats[i].mp_format;
}
return 0;
}
char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
{
buf = mp_GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
@@ -132,54 +134,21 @@ static void update_waveformat_datarate(WAVEFORMATEXTENSIBLE *wformat)
}
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
struct wasapi_sample_fmt format,
struct wasapi_sample_fmt *format,
DWORD samplerate, struct mp_chmap *channels)
{
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
wformat->Format.wBitsPerSample = format.bits;
wformat->Format.wBitsPerSample = format->bits;
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wformat->SubFormat = *format_to_subtype(format.mp_format);
wformat->Samples.wValidBitsPerSample = format.used_msb;
wformat->SubFormat = *format_to_subtype(format->mp_format);
wformat->Samples.wValidBitsPerSample = format->used_msb;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate(wformat);
}
// This implicitly transforms all pcm formats to: interleaved / signed (except
// 8-bit is unsigned) / waveext channel order. "Special" formats should be
// exempt as they should already satisfy these properties.
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
{
struct mp_chmap channels = ao->channels;
if (mp_chmap_is_unknown(&channels))
mp_chmap_from_channels(&channels, channels.num);
mp_chmap_reorder_to_waveext(&channels);
if (!mp_chmap_is_valid(&channels))
mp_chmap_from_channels(&channels, 2);
// First find a format that is actually representable.
// (Notably excludes AF_FORMAT_DOUBLE.)
struct wasapi_sample_fmt format =
{AF_FORMAT_S16, 16, 16, &KSDATAFORMAT_SUBTYPE_PCM};
int alt_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(ao->format, alt_formats);
for (int n = 0; alt_formats[n]; n++) {
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (wasapi_formats[i].mp_format == alt_formats[n]) {
format = wasapi_formats[i];
goto found_format;
}
}
}
found_format:
set_waveformat(wformat, format, ao->samplerate, &channels);
}
// other wformat parameters must already be set with set_waveformat
static void change_waveformat_samplerate(WAVEFORMATEXTENSIBLE *wformat,
DWORD samplerate)
@@ -319,9 +288,20 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf,
}
waveformat_copy(&state->format, wf);
state->share_mode = share_mode;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz -> %s (%s)\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate,
waveformat_to_str(wf),
state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE
? "exclusive" : "shared");
return true;
}
#define mp_format_res_str(hres) \
(SUCCEEDED(hres) ? "ok" : ((hres) == AUDCLNT_E_UNSUPPORTED_FORMAT) \
? "unsupported" : mp_HRESULT_to_str(hres))
static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
struct wasapi_state *state = ao->priv;
@@ -333,36 +313,15 @@ static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
return SUCCEEDED(hr);
}
// This works like try_format_exclusive(), but will try to fallback to the AC3
// format if the format is a non-AC3 passthrough format. *wformat will be
// adjusted accordingly.
static bool try_format_exclusive_with_spdif_fallback(struct ao *ao,
WAVEFORMATEXTENSIBLE *wformat)
{
if (try_format_exclusive(ao, wformat))
return true;
int special_format = special_subtype_to_format(&wformat->SubFormat);
if (special_format && special_format != AF_FORMAT_S_AC3) {
MP_VERBOSE(ao, "Retrying as AC3.\n");
wformat->SubFormat = *format_to_subtype(AF_FORMAT_S_AC3);
return try_format_exclusive(ao, wformat);
}
return false;
}
static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
int samplerate, struct mp_chmap *channels)
{
int alt_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(ao->format, alt_formats);
for (int n = 0; alt_formats[n]; n++) {
for (int i = 0; wasapi_formats[i].mp_format; i++) {
if (wasapi_formats[i].mp_format == alt_formats[n]) {
set_waveformat(wformat, wasapi_formats[i], samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
}
struct wasapi_sample_fmt alt_formats[MP_ARRAY_SIZE(wasapi_formats)];
wasapi_get_best_sample_formats(ao->format, alt_formats);
for (int n = 0; alt_formats[n].mp_format; n++) {
set_waveformat(wformat, &alt_formats[n], samplerate, channels);
if (try_format_exclusive(ao, wformat))
return true;
}
wformat->Format.wBitsPerSample = 0;
@@ -441,40 +400,39 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
return false;
}
static bool find_formats_exclusive(struct ao *ao, bool do_search)
static bool find_formats_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
// Try the specified format as is
if (try_format_exclusive(ao, wformat))
return true;
// Try the requested format as is. If that doesn't work, and the do_search
// argument is set, do the pcm format search.
if (!try_format_exclusive_with_spdif_fallback(ao, &wformat) &&
(!do_search || !search_channels(ao, &wformat)))
if (af_fmt_is_spdif(ao->format)) {
if (ao->format != AF_FORMAT_S_AC3) {
// If the requested format failed and it is passthrough, but not
// AC3, try lying and saying it is.
MP_VERBOSE(ao, "Retrying as AC3.\n");
wformat->SubFormat = *format_to_subtype(AF_FORMAT_S_AC3);
if (try_format_exclusive(ao, wformat))
return true;
}
return false;
}
if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_EXCLUSIVE))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (exclusive) -> %s\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate,
waveformat_to_str(&wformat.Format));
return true;
// Fallback on the PCM format search
return search_channels(ao, wformat);
}
static bool find_formats_shared(struct ao *ao)
static bool find_formats_shared(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
struct wasapi_state *state = ao->priv;
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
WAVEFORMATEX *closestMatch;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_SHARED,
&wformat.Format, &closestMatch);
&wformat->Format,
&closestMatch);
MP_VERBOSE(ao, "Trying %s (shared) -> %s\n",
waveformat_to_str(&wformat.Format), mp_format_res_str(hr));
waveformat_to_str(&wformat->Format), mp_format_res_str(hr));
if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
EXIT_ON_ERROR(hr);
@@ -482,27 +440,20 @@ static bool find_formats_shared(struct ao *ao)
case S_OK:
break;
case S_FALSE:
waveformat_copy(&wformat, closestMatch);
waveformat_copy(wformat, closestMatch);
CoTaskMemFree(closestMatch);
MP_VERBOSE(ao, "Closest match is %s\n",
waveformat_to_str(&wformat.Format));
waveformat_to_str(&wformat->Format));
break;
default:
hr = IAudioClient_GetMixFormat(state->pAudioClient, &closestMatch);
EXIT_ON_ERROR(hr);
waveformat_copy(&wformat, closestMatch);
waveformat_copy(wformat, closestMatch);
MP_VERBOSE(ao, "Fallback to mix format %s\n",
waveformat_to_str(&wformat.Format));
waveformat_to_str(&wformat->Format));
CoTaskMemFree(closestMatch);
}
if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_SHARED))
return false;
MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (shared) -> %s\n",
mp_chmap_to_str(&ao->channels),
af_fmt_to_str(ao->format), ao->samplerate,
waveformat_to_str(&wformat.Format));
return true;
exit_label:
MP_ERR(state, "Error finding shared mode format: %s\n",
@@ -513,22 +464,36 @@ exit_label:
static bool find_formats(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
struct mp_chmap channels = ao->channels;
if (state->opt_exclusive) {
// If exclusive is requested, try the requested format (which
// might be passthrough). If that fails, do a pcm format
// search.
return find_formats_exclusive(ao, true);
} else if (af_fmt_is_spdif(ao->format)) {
// If a passthrough format is requested, but exclusive mode
// was not explicitly set, try only the requested passthrough
// format in exclusive mode. Fall back on shared mode if that
// fails without doing the exclusive pcm format search.
if (find_formats_exclusive(ao, false))
return true;
if (mp_chmap_is_unknown(&channels))
mp_chmap_from_channels(&channels, channels.num);
mp_chmap_reorder_to_waveext(&channels);
if (!mp_chmap_is_valid(&channels))
mp_chmap_from_channels(&channels, 2);
struct wasapi_sample_fmt alt_formats[MP_ARRAY_SIZE(wasapi_formats)];
wasapi_get_best_sample_formats(ao->format, alt_formats);
struct wasapi_sample_fmt wasapi_format =
{AF_FORMAT_S16, 16, 16, &KSDATAFORMAT_SUBTYPE_PCM};;
if (alt_formats[0].mp_format)
wasapi_format = alt_formats[0];
AUDCLNT_SHAREMODE share_mode;
WAVEFORMATEXTENSIBLE wformat;
set_waveformat(&wformat, &wasapi_format, ao->samplerate, &channels);
if (state->opt_exclusive || af_fmt_is_spdif(ao->format)) {
share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
if(!find_formats_exclusive(ao, &wformat))
return false;
} else {
share_mode = AUDCLNT_SHAREMODE_SHARED;
if(!find_formats_shared(ao, &wformat))
return false;
}
// Default is to use shared mode
return find_formats_shared(ao);
return set_ao_format(ao, &wformat.Format, share_mode);
}
static HRESULT init_clock(struct wasapi_state *state) {
@@ -833,7 +798,7 @@ exit_label:
destroy_enumerator(enumerator);
}
static HRESULT load_device(struct mp_log *l,
static bool load_device(struct mp_log *l,
IMMDevice **ppDevice, LPWSTR deviceID)
{
IMMDeviceEnumerator *pEnumerator = NULL;
@@ -849,7 +814,7 @@ exit_label:
if (FAILED(hr))
mp_err(l, "Error loading selected device: %s\n", mp_HRESULT_to_str(hr));
SAFE_RELEASE(pEnumerator);
return hr;
return SUCCEEDED(hr);
}
static LPWSTR select_device(struct mp_log *l, struct device_desc *d)
@@ -934,7 +899,7 @@ exit_label:
return deviceID;
}
HRESULT wasapi_thread_init(struct ao *ao)
bool wasapi_thread_init(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
MP_DBG(ao, "Init wasapi thread\n");
@@ -946,14 +911,18 @@ HRESULT wasapi_thread_init(struct ao *ao)
retry:
if (state->deviceID) {
hr = load_device(ao->log, &state->pDevice, state->deviceID);
EXIT_ON_ERROR(hr);
if (!load_device(ao->log, &state->pDevice, state->deviceID))
return false;
MP_DBG(ao, "Activating pAudioClient interface\n");
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
CLSCTX_ALL, NULL,
(void **)&state->pAudioClient);
EXIT_ON_ERROR(hr);
if (FAILED(hr)) {
MP_FATAL(ao, "Error activating device: %s\n",
mp_HRESULT_to_str(hr));
return false;
}
} else {
MP_VERBOSE(ao, "Trying UWP wrapper.\n");
@@ -961,67 +930,73 @@ retry:
HANDLE lib = LoadLibraryW(L"wasapiuwp2.dll");
if (!lib) {
MP_ERR(ao, "Wrapper not found: %d\n", (int)GetLastError());
hr = E_FAIL;
EXIT_ON_ERROR(hr);
}
if (lib) {
wuCreateDefaultAudioRenderer =
(void*)GetProcAddress(lib, "wuCreateDefaultAudioRenderer");
return false;
}
wuCreateDefaultAudioRenderer =
(void*)GetProcAddress(lib, "wuCreateDefaultAudioRenderer");
if (!wuCreateDefaultAudioRenderer) {
MP_ERR(ao, "Function not found.\n");
hr = E_FAIL;
EXIT_ON_ERROR(hr);
return false;
}
IUnknown *res = NULL;
hr = wuCreateDefaultAudioRenderer(&res);
MP_VERBOSE(ao, "Device: %s %p\n", mp_HRESULT_to_str(hr), res);
EXIT_ON_ERROR(hr);
if (FAILED(hr)) {
MP_FATAL(ao, "Error activating device: %s\n",
mp_HRESULT_to_str(hr));
return false;
}
hr = IUnknown_QueryInterface(res, &IID_IAudioClient,
(void **)&state->pAudioClient);
IUnknown_Release(res);
EXIT_ON_ERROR(hr);
if (FAILED(hr)) {
MP_FATAL(ao, "Failed to get UWP audio client: %s\n",
mp_HRESULT_to_str(hr));
return false;
}
}
// In the event of an align hack, we've already done this.
if (!align_hack) {
MP_DBG(ao, "Probing formats\n");
if (!find_formats(ao)) {
hr = E_FAIL;
EXIT_ON_ERROR(hr);
}
if (!find_formats(ao))
return false;
}
MP_DBG(ao, "Fixing format\n");
hr = fix_format(ao, align_hack);
switch (hr) {
case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:
if (align_hack)
if (align_hack) {
MP_FATAL(ao, "Align hack failed\n");
break;
}
// According to MSDN, we must use this as base after the failure.
IAudioClient_GetBufferSize(state->pAudioClient,
&state->bufferFrameCount);
hr = IAudioClient_GetBufferSize(state->pAudioClient,
&state->bufferFrameCount);
if (FAILED(hr)) {
MP_FATAL(ao, "Error getting buffer size for align hack: %s\n",
mp_HRESULT_to_str(hr));
return false;
}
wasapi_thread_uninit(ao);
align_hack = true;
MP_WARN(ao, "This appears to require a weird Windows 7 hack. Retrying.\n");
goto retry;
case AUDCLNT_E_DEVICE_IN_USE:
case AUDCLNT_E_DEVICE_INVALIDATED:
if (retry_wait > 8)
break;
if (retry_wait > 8) {
MP_FATAL(ao, "Bad device retry failed\n");
return false;
}
wasapi_thread_uninit(ao);
MP_WARN(ao, "Retrying in %"PRId64" us\n", retry_wait);
mp_sleep_us(retry_wait);
retry_wait *= 2;
goto retry;
}
EXIT_ON_ERROR(hr);
MP_DBG(ao, "Init wasapi thread done\n");
return S_OK;
exit_label:
MP_FATAL(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr));
return hr;
return SUCCEEDED(hr);
}
void wasapi_thread_uninit(struct ao *ao)
+15
View File
@@ -115,6 +115,12 @@ bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2)
return rc->x1 > rc->x0 && rc->y1 > rc->y0;
}
bool mp_rect_equals(struct mp_rect *rc1, struct mp_rect *rc2)
{
return rc1->x0 == rc2->x0 && rc1->y0 == rc2->y0 &&
rc1->x1 == rc2->x1 && rc1->y1 == rc2->y1;
}
// This works like snprintf(), except that it starts writing the first output
// character to str[strlen(str)]. This returns the number of characters the
// string would have *appended* assuming a large enough buffer, will make sure
@@ -287,3 +293,12 @@ char *mp_tag_str_buf(char *buf, size_t buf_size, uint32_t tag)
}
return buf;
}
char *mp_tprintf_buf(char *buf, size_t buf_size, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vsnprintf(buf, buf_size, format, ap);
va_end(ap);
return buf;
}
+14 -4
View File
@@ -59,10 +59,11 @@ enum stream_type {
};
enum {
DATA_OK = 1,
DATA_WAIT = 0,
DATA_AGAIN = -1,
DATA_EOF = -2,
DATA_OK = 1, // data is actually being returned
DATA_WAIT = 0, // async wait: check state again after next wakeup
DATA_AGAIN = -2, // repeat request (internal progress was made)
DATA_STARVE = -1, // need input (might require to drain other outputs)
DATA_EOF = -3, // no more data available
};
extern const char mpv_version[];
@@ -82,6 +83,7 @@ struct mp_rect {
void mp_rect_union(struct mp_rect *rc, const struct mp_rect *src);
bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2);
bool mp_rect_contains(struct mp_rect *rc, int x, int y);
bool mp_rect_equals(struct mp_rect *rc1, struct mp_rect *rc2);
int mp_snprintf_cat(char *str, size_t size, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
@@ -101,4 +103,12 @@ char *mp_strerror_buf(char *buf, size_t buf_size, int errnum);
char *mp_tag_str_buf(char *buf, size_t buf_size, uint32_t tag);
#define mp_tag_str(t) mp_tag_str_buf((char[22]){0}, 22, t)
// Return a printf(format, ...) formatted string of the given SIZE. SIZE must
// be a compile time constant. The result is allocated on the stack and valid
// only within the current block scope.
#define mp_tprintf(SIZE, format, ...) \
mp_tprintf_buf((char[SIZE]){0}, (SIZE), (format), __VA_ARGS__)
char *mp_tprintf_buf(char *buf, size_t buf_size, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
#endif /* MPLAYER_MPCOMMON_H */
+18 -3
View File
@@ -38,6 +38,10 @@
#include <libavutil/display.h>
#include <libavutil/opt.h>
#if HAVE_AVUTIL_SPHERICAL
#include <libavutil/spherical.h>
#endif
#include "common/msg.h"
#include "common/tags.h"
#include "common/av_common.h"
@@ -234,7 +238,7 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
memcpy(buf, priv->init_fragment.start + priv->stream_pos, ret);
priv->stream_pos += ret;
} else {
ret = stream_read(stream, buf, size);
ret = stream_read_partial(stream, buf, size);
priv->stream_pos = priv->init_fragment.len + stream_tell(stream);
}
@@ -641,6 +645,19 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
sh->codec->rotate = (((int)(-r) % 360) + 360) % 360;
}
#if HAVE_AVUTIL_SPHERICAL
sd = av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, NULL);
if (sd) {
AVSphericalMapping *sp = (void *)sd;
struct mp_spherical_params *mpsp = &sh->codec->spherical;
mpsp->type = sp->projection == AV_SPHERICAL_EQUIRECTANGULAR ?
MP_SPHERICAL_EQUIRECTANGULAR : MP_SPHERICAL_UNKNOWN;
mpsp->ref_angles[0] = sp->yaw / (float)(1 << 16);
mpsp->ref_angles[1] = sp->pitch / (float)(1 << 16);
mpsp->ref_angles[2] = sp->roll / (float)(1 << 16);
}
#endif
// This also applies to vfw-muxed mkv, but we can't detect these easily.
sh->codec->avi_dts = matches_avinputformat_name(priv, "avi");
@@ -1146,8 +1163,6 @@ static void demux_close_lavf(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
if (priv) {
if (priv->avfc)
av_freep(&priv->avfc->key);
avformat_close_input(&priv->avfc);
if (priv->pb)
av_freep(&priv->pb->buffer);
+2
View File
@@ -115,6 +115,7 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam
char *fname = talloc_size(mf, strlen(filename) + 32);
#if HAVE_GLOB
if (!strchr(filename, '%')) {
strcpy(fname, filename);
if (!strchr(filename, '*'))
@@ -137,6 +138,7 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam
globfree(&gg);
goto exit_mf;
}
#endif
mp_info(log, "search expr: %s\n", filename);
+48
View File
@@ -109,6 +109,7 @@ typedef struct mkv_track {
uint32_t colorspace;
int stereo_mode;
struct mp_colorspace color;
struct mp_spherical_params spherical;
uint32_t a_channels, a_bps;
float a_sfreq;
@@ -584,6 +585,49 @@ static void parse_trackcolour(struct demuxer *demuxer, struct mkv_track *track,
}
}
static void parse_trackprojection(struct demuxer *demuxer, struct mkv_track *track,
struct ebml_projection *projection)
{
if (projection->n_projection_type) {
const char *name;
switch (projection->projection_type) {
case 0:
name = "rectangular";
track->spherical.type = MP_SPHERICAL_NONE;
break;
case 1:
name = "equirectangular";
track->spherical.type = MP_SPHERICAL_EQUIRECTANGULAR;
break;
default:
name = "unknown";
track->spherical.type = MP_SPHERICAL_UNKNOWN;
}
MP_VERBOSE(demuxer, "| + ProjectionType: %s (%"PRIu64")\n", name,
projection->projection_type);
}
if (projection->n_projection_private) {
MP_VERBOSE(demuxer, "| + ProjectionPrivate: %zd bytes\n",
projection->projection_private.len);
MP_WARN(demuxer, "Unknown ProjectionPrivate element.\n");
}
if (projection->n_projection_pose_yaw) {
track->spherical.ref_angles[0] = projection->projection_pose_yaw;
MP_VERBOSE(demuxer, "| + ProjectionPoseYaw: %f\n",
projection->projection_pose_yaw);
}
if (projection->n_projection_pose_pitch) {
track->spherical.ref_angles[1] = projection->projection_pose_pitch;
MP_VERBOSE(demuxer, "| + ProjectionPosePitch: %f\n",
projection->projection_pose_pitch);
}
if (projection->n_projection_pose_roll) {
track->spherical.ref_angles[2] = projection->projection_pose_roll;
MP_VERBOSE(demuxer, "| + ProjectionPoseRoll: %f\n",
projection->projection_pose_roll);
}
}
static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
struct ebml_video *video)
{
@@ -628,6 +672,8 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
}
if (video->n_colour)
parse_trackcolour(demuxer, track, &video->colour);
if (video->n_projection)
parse_trackprojection(demuxer, track, &video->projection);
}
/**
@@ -1454,6 +1500,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
sh_v->stereo_mode = track->stereo_mode;
sh_v->color = track->color;
sh_v->spherical = track->spherical;
done:
demux_add_sh_stream(demuxer, sh);
@@ -1965,6 +2012,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
}
if (id == MATROSKA_ID_CLUSTER) {
MP_VERBOSE(demuxer, "|+ found cluster\n");
mkv_d->cluster_start = start_pos;
break;
}
int res = read_header_element(demuxer, id, start_pos);
+2 -1
View File
@@ -22,7 +22,7 @@
#include "common/common.h"
#include "audio/chmap.h"
#include "video/csputils.h"
#include "video/mp_image.h"
struct MPOpts;
struct demuxer;
@@ -93,6 +93,7 @@ struct mp_codec_params {
int rotate; // intended display rotation, in degrees, [0, 359]
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
struct mp_colorspace color; // colorspace info where available
struct mp_spherical_params spherical;
// STREAM_VIDEO + STREAM_AUDIO
int bits_per_coded_sample;
+7 -11
View File
@@ -28,21 +28,17 @@
# If this is enabled, treat all the following bindings as default.
#default-bindings start
#MOUSE_BTN0 ignore # don't do anything
#MOUSE_BTN0_DBL cycle fullscreen # toggle fullscreen on/off
#MOUSE_BTN2 cycle pause # toggle pause on/off
#MOUSE_BTN3 seek 10
#MOUSE_BTN4 seek -10
#MOUSE_BTN5 add volume -2
#MOUSE_BTN6 add volume 2
#MBTN_LEFT ignore # don't do anything
#MBTN_LEFT_DBL cycle fullscreen # toggle fullscreen on/off
#MBTN_RIGHT cycle pause # toggle pause on/off
# Mouse wheels, touchpad or other input devices that have axes
# if the input devices supports precise scrolling it will also scale the
# numeric value accordingly
#AXIS_UP seek 10
#AXIS_DOWN seek -10
#AXIS_LEFT seek 5
#AXIS_RIGHT seek -5
#WHEEL_UP seek 10
#WHEEL_DOWN seek -10
#WHEEL_LEFT add volume -2
#WHEEL_RIGHT add volume 2
## Seek units are in seconds, but note that these are limited by keyframes
#RIGHT seek 5
-3
View File
@@ -98,9 +98,6 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_PLAYLIST_SHUFFLE, "playlist-shuffle", },
{ MP_CMD_SUB_STEP, "sub-step", { ARG_INT }, .allow_auto_repeat = true },
{ MP_CMD_SUB_SEEK, "sub-seek", { ARG_INT }, .allow_auto_repeat = true },
#if HAVE_GPL
{ MP_CMD_OSD, "osd", { OARG_INT(-1) } },
#endif
{ MP_CMD_PRINT_TEXT, "print-text", { ARG_STRING }, .allow_auto_repeat = true },
{ MP_CMD_SHOW_TEXT, "show-text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) },
.allow_auto_repeat = true},
-1
View File
@@ -46,7 +46,6 @@ enum mp_command_type {
MP_CMD_QUIT_WATCH_LATER,
MP_CMD_PLAYLIST_NEXT,
MP_CMD_PLAYLIST_PREV,
MP_CMD_OSD,
MP_CMD_SCREENSHOT,
MP_CMD_SCREENSHOT_TO_FILE,
MP_CMD_SCREENSHOT_RAW,
+46 -38
View File
@@ -95,7 +95,7 @@ struct cmd_queue {
struct mp_cmd *first;
};
struct axis_state {
struct wheel_state {
double dead_zone_accum;
double unit_accum;
};
@@ -135,11 +135,11 @@ struct input_ctx {
bool mouse_mangle, mouse_src_mangle;
struct mp_rect mouse_src, mouse_dst;
// Axis state (MP_AXIS_*)
struct axis_state axis_state_y; // MP_AXIS_UP/MP_AXIS_DOWN
struct axis_state axis_state_x; // MP_AXIS_LEFT/MP_AXIS_RIGHT
struct axis_state *axis_current; // Points to axis currently being scrolled
double last_axis_time; // mp_time_sec() of the last axis event
// Wheel state (MP_WHEEL_*)
struct wheel_state wheel_state_y; // MP_WHEEL_UP/MP_WHEEL_DOWN
struct wheel_state wheel_state_x; // MP_WHEEL_LEFT/MP_WHEEL_RIGHT
struct wheel_state *wheel_current; // The direction currently being scrolled
double last_wheel_time; // mp_time_sec() of the last wheel event
// List of command binding sections
struct cmd_bind_section *cmd_bind_sections;
@@ -200,9 +200,9 @@ const struct m_sub_options input_config = {
OPT_INTRANGE("input-key-fifo-size", key_fifo_size, 0, 2, 65000),
OPT_FLAG("input-cursor", enable_mouse_movements, 0),
OPT_FLAG("input-vo-keyboard", vo_key_input, 0),
OPT_FLAG("input-media-keys", use_media_keys, 0),
#if HAVE_COCOA
OPT_FLAG("input-appleremote", use_appleremote, 0),
OPT_FLAG("input-media-keys", use_media_keys, 0),
#endif
OPT_FLAG("window-dragging", allow_win_drag, 0),
OPT_REPLACED("input-x11-keyboard", "input-vo-keyboard"),
@@ -216,9 +216,9 @@ const struct m_sub_options input_config = {
.ar_rate = 40,
.use_alt_gr = 1,
.enable_mouse_movements = 1,
.use_media_keys = 1,
#if HAVE_COCOA
.use_appleremote = 1,
.use_media_keys = 1,
#endif
.default_bindings = 1,
.vo_key_input = 1,
@@ -634,10 +634,10 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale,
}
}
// Pre-processing for MP_AXIS_* events. If this returns false, the caller
// Pre-processing for MP_WHEEL_* events. If this returns false, the caller
// should discard the event.
static bool process_axis(struct input_ctx *ictx, int code, double *scale,
int *scale_units)
static bool process_wheel(struct input_ctx *ictx, int code, double *scale,
int *scale_units)
{
// Size of the deadzone in scroll units. The user must scroll at least this
// much in any direction before their scroll is registered.
@@ -650,45 +650,45 @@ static bool process_axis(struct input_ctx *ictx, int code, double *scale,
// sent when the user scrolls slowly.
static const double UNIT_SCROLL_TIME = 0.5;
// Determine which axis is being scrolled
// Determine which direction is being scrolled
double dir;
struct axis_state *state;
struct wheel_state *state;
switch (code) {
case MP_AXIS_UP: dir = -1; state = &ictx->axis_state_y; break;
case MP_AXIS_DOWN: dir = +1; state = &ictx->axis_state_y; break;
case MP_AXIS_LEFT: dir = -1; state = &ictx->axis_state_x; break;
case MP_AXIS_RIGHT: dir = +1; state = &ictx->axis_state_x; break;
case MP_WHEEL_UP: dir = -1; state = &ictx->wheel_state_y; break;
case MP_WHEEL_DOWN: dir = +1; state = &ictx->wheel_state_y; break;
case MP_WHEEL_LEFT: dir = -1; state = &ictx->wheel_state_x; break;
case MP_WHEEL_RIGHT: dir = +1; state = &ictx->wheel_state_x; break;
default:
return true;
}
// Reset accumulators if it's determined that the user finished scrolling
double now = mp_time_sec();
if (now > ictx->last_axis_time + DEADZONE_SCROLL_TIME) {
ictx->axis_current = NULL;
ictx->axis_state_y.dead_zone_accum = 0;
ictx->axis_state_x.dead_zone_accum = 0;
if (now > ictx->last_wheel_time + DEADZONE_SCROLL_TIME) {
ictx->wheel_current = NULL;
ictx->wheel_state_y.dead_zone_accum = 0;
ictx->wheel_state_x.dead_zone_accum = 0;
}
if (now > ictx->last_axis_time + UNIT_SCROLL_TIME) {
ictx->axis_state_y.unit_accum = 0;
ictx->axis_state_x.unit_accum = 0;
if (now > ictx->last_wheel_time + UNIT_SCROLL_TIME) {
ictx->wheel_state_y.unit_accum = 0;
ictx->wheel_state_x.unit_accum = 0;
}
ictx->last_axis_time = now;
ictx->last_wheel_time = now;
// Process axis deadzone. A lot of touchpad drivers don't filter scroll
// input, which makes it difficult for the user to send AXIS_UP/DOWN
// without accidentally triggering AXIS_LEFT/RIGHT. We try to fix this by
// implementing a deadzone. When the value of either axis breaks out of the
// deadzone, events from the other axis will be ignored until the user
// finishes scrolling.
if (ictx->axis_current == NULL) {
// Process wheel deadzone. A lot of touchpad drivers don't filter scroll
// input, which makes it difficult for the user to send WHEEL_UP/DOWN
// without accidentally triggering WHEEL_LEFT/RIGHT. We try to fix this by
// implementing a deadzone. When the value of either direction breaks out
// of the deadzone, events from the other direction will be ignored until
// the user finishes scrolling.
if (ictx->wheel_current == NULL) {
state->dead_zone_accum += *scale * dir;
if (state->dead_zone_accum * dir > DEADZONE_DIST) {
ictx->axis_current = state;
ictx->wheel_current = state;
*scale = state->dead_zone_accum * dir;
}
}
if (ictx->axis_current != state)
if (ictx->wheel_current != state)
return false;
// Determine scale_units. This is incremented every time the accumulated
@@ -724,7 +724,7 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale,
if (!force_mouse && opts->doubleclick_time && MP_KEY_IS_MOUSE_BTN_DBL(unmod))
return;
int units = 1;
if (MP_KEY_IS_AXIS(unmod) && !process_axis(ictx, unmod, &scale, &units))
if (MP_KEY_IS_WHEEL(unmod) && !process_wheel(ictx, unmod, &scale, &units))
return;
interpret_key(ictx, code, scale, units);
if (code & MP_KEY_STATE_DOWN) {
@@ -732,8 +732,8 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale,
if (ictx->last_doubleclick_key_down == code &&
now - ictx->last_doubleclick_time < opts->doubleclick_time / 1000.0)
{
if (code >= MP_MOUSE_BTN0 && code <= MP_MOUSE_BTN2) {
interpret_key(ictx, code - MP_MOUSE_BTN0 + MP_MOUSE_BTN0_DBL,
if (code >= MP_MBTN_LEFT && code <= MP_MBTN_RIGHT) {
interpret_key(ictx, code - MP_MBTN_BASE + MP_MBTN_DBL_BASE,
1, 1);
}
}
@@ -766,7 +766,7 @@ void mp_input_put_key_utf8(struct input_ctx *ictx, int mods, struct bstr t)
}
}
void mp_input_put_axis(struct input_ctx *ictx, int direction, double value)
void mp_input_put_wheel(struct input_ctx *ictx, int direction, double value)
{
if (value == 0.0)
return;
@@ -1441,6 +1441,14 @@ bool mp_input_use_alt_gr(struct input_ctx *ictx)
return r;
}
bool mp_input_use_media_keys(struct input_ctx *ictx)
{
input_lock(ictx);
bool r = ictx->opts->use_media_keys;
input_unlock(ictx);
return r;
}
struct mp_cmd *mp_input_parse_cmd(struct input_ctx *ictx, bstr str,
const char *location)
{
+4 -1
View File
@@ -143,7 +143,7 @@ void mp_input_put_key_utf8(struct input_ctx *ictx, int mods, struct bstr t);
// Process scrolling input. Support for precise scrolling. Scales the given
// scroll amount add multiplies it with the command (seeking, sub-delay, etc)
void mp_input_put_axis(struct input_ctx *ictx, int direction, double value);
void mp_input_put_wheel(struct input_ctx *ictx, int direction, double value);
// Update mouse position (in window coordinates).
void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y);
@@ -250,6 +250,9 @@ void mp_input_set_cancel(struct input_ctx *ictx, void (*cb)(void *c), void *c);
// characters. If false, count Right Alt as the modifier Alt key.
bool mp_input_use_alt_gr(struct input_ctx *ictx);
// Return true if mpv should intercept keyboard media keys
bool mp_input_use_media_keys(struct input_ctx *ictx);
// Like mp_input_parse_cmd_strv, but also run the command.
void mp_input_run_cmd(struct input_ctx *ictx, const char **cmd);
+55 -45
View File
@@ -78,46 +78,29 @@ static const struct key_name key_names[] = {
{ MP_KEY_KPDEC, "KP_DEC" },
{ MP_KEY_KPINS, "KP_INS" },
{ MP_KEY_KPENTER, "KP_ENTER" },
{ MP_MOUSE_BTN0, "MOUSE_BTN0" },
{ MP_MOUSE_BTN1, "MOUSE_BTN1" },
{ MP_MOUSE_BTN2, "MOUSE_BTN2" },
{ MP_MOUSE_BTN3, "MOUSE_BTN3" },
{ MP_MOUSE_BTN4, "MOUSE_BTN4" },
{ MP_MOUSE_BTN5, "MOUSE_BTN5" },
{ MP_MOUSE_BTN6, "MOUSE_BTN6" },
{ MP_MOUSE_BTN7, "MOUSE_BTN7" },
{ MP_MOUSE_BTN8, "MOUSE_BTN8" },
{ MP_MOUSE_BTN9, "MOUSE_BTN9" },
{ MP_MOUSE_BTN10, "MOUSE_BTN10" },
{ MP_MOUSE_BTN11, "MOUSE_BTN11" },
{ MP_MOUSE_BTN12, "MOUSE_BTN12" },
{ MP_MOUSE_BTN13, "MOUSE_BTN13" },
{ MP_MOUSE_BTN14, "MOUSE_BTN14" },
{ MP_MOUSE_BTN15, "MOUSE_BTN15" },
{ MP_MOUSE_BTN16, "MOUSE_BTN16" },
{ MP_MOUSE_BTN17, "MOUSE_BTN17" },
{ MP_MOUSE_BTN18, "MOUSE_BTN18" },
{ MP_MOUSE_BTN19, "MOUSE_BTN19" },
{ MP_MOUSE_BTN0_DBL, "MOUSE_BTN0_DBL" },
{ MP_MOUSE_BTN1_DBL, "MOUSE_BTN1_DBL" },
{ MP_MOUSE_BTN2_DBL, "MOUSE_BTN2_DBL" },
{ MP_MOUSE_BTN3_DBL, "MOUSE_BTN3_DBL" },
{ MP_MOUSE_BTN4_DBL, "MOUSE_BTN4_DBL" },
{ MP_MOUSE_BTN5_DBL, "MOUSE_BTN5_DBL" },
{ MP_MOUSE_BTN6_DBL, "MOUSE_BTN6_DBL" },
{ MP_MOUSE_BTN7_DBL, "MOUSE_BTN7_DBL" },
{ MP_MOUSE_BTN8_DBL, "MOUSE_BTN8_DBL" },
{ MP_MOUSE_BTN9_DBL, "MOUSE_BTN9_DBL" },
{ MP_MOUSE_BTN10_DBL, "MOUSE_BTN10_DBL" },
{ MP_MOUSE_BTN11_DBL, "MOUSE_BTN11_DBL" },
{ MP_MOUSE_BTN12_DBL, "MOUSE_BTN12_DBL" },
{ MP_MOUSE_BTN13_DBL, "MOUSE_BTN13_DBL" },
{ MP_MOUSE_BTN14_DBL, "MOUSE_BTN14_DBL" },
{ MP_MOUSE_BTN15_DBL, "MOUSE_BTN15_DBL" },
{ MP_MOUSE_BTN16_DBL, "MOUSE_BTN16_DBL" },
{ MP_MOUSE_BTN17_DBL, "MOUSE_BTN17_DBL" },
{ MP_MOUSE_BTN18_DBL, "MOUSE_BTN18_DBL" },
{ MP_MOUSE_BTN19_DBL, "MOUSE_BTN19_DBL" },
{ MP_MBTN_LEFT, "MBTN_LEFT" },
{ MP_MBTN_MID, "MBTN_MID" },
{ MP_MBTN_RIGHT, "MBTN_RIGHT" },
{ MP_WHEEL_UP, "WHEEL_UP" },
{ MP_WHEEL_DOWN, "WHEEL_DOWN" },
{ MP_WHEEL_LEFT, "WHEEL_LEFT" },
{ MP_WHEEL_RIGHT, "WHEEL_RIGHT" },
{ MP_MBTN_BACK, "MBTN_BACK" },
{ MP_MBTN_FORWARD, "MBTN_FORWARD" },
{ MP_MBTN9, "MBTN9" },
{ MP_MBTN10, "MBTN10" },
{ MP_MBTN11, "MBTN11" },
{ MP_MBTN12, "MBTN12" },
{ MP_MBTN13, "MBTN13" },
{ MP_MBTN14, "MBTN14" },
{ MP_MBTN15, "MBTN15" },
{ MP_MBTN16, "MBTN16" },
{ MP_MBTN17, "MBTN17" },
{ MP_MBTN18, "MBTN18" },
{ MP_MBTN19, "MBTN19" },
{ MP_MBTN_LEFT_DBL, "MBTN_LEFT_DBL" },
{ MP_MBTN_MID_DBL, "MBTN_MID_DBL" },
{ MP_MBTN_RIGHT_DBL, "MBTN_RIGHT_DBL" },
{ MP_AR_PLAY, "AR_PLAY" },
{ MP_AR_PLAY_HOLD, "AR_PLAY_HOLD" },
@@ -134,11 +117,6 @@ static const struct key_name key_names[] = {
{ MP_AR_VDOWN, "AR_VDOWN" },
{ MP_AR_VDOWN_HOLD, "AR_VDOWN_HOLD" },
{ MP_AXIS_UP, "AXIS_UP" },
{ MP_AXIS_DOWN, "AXIS_DOWN" },
{ MP_AXIS_LEFT, "AXIS_LEFT" },
{ MP_AXIS_RIGHT, "AXIS_RIGHT" },
{ MP_KEY_POWER, "POWER" },
{ MP_KEY_MENU, "MENU" },
{ MP_KEY_PLAY, "PLAY" },
@@ -159,6 +137,9 @@ static const struct key_name key_names[] = {
{ MP_KEY_SEARCH, "SEARCH" },
{ MP_KEY_SLEEP, "SLEEP" },
{ MP_KEY_CANCEL, "CANCEL" },
{ MP_KEY_RECORD, "RECORD" },
{ MP_KEY_CHANNEL_UP, "CHANNEL_UP" },
{ MP_KEY_CHANNEL_DOWN,"CHANNEL_DOWN" },
// These are kept for backward compatibility
{ MP_KEY_PAUSE, "XF86_PAUSE" },
@@ -166,6 +147,35 @@ static const struct key_name key_names[] = {
{ MP_KEY_PREV, "XF86_PREV" },
{ MP_KEY_NEXT, "XF86_NEXT" },
// Deprecated numeric aliases for the mouse buttons
{ MP_MBTN_LEFT, "MOUSE_BTN0" },
{ MP_MBTN_MID, "MOUSE_BTN1" },
{ MP_MBTN_RIGHT, "MOUSE_BTN2" },
{ MP_WHEEL_UP, "MOUSE_BTN3" },
{ MP_WHEEL_DOWN, "MOUSE_BTN4" },
{ MP_WHEEL_LEFT, "MOUSE_BTN5" },
{ MP_WHEEL_RIGHT, "MOUSE_BTN6" },
{ MP_MBTN_BACK, "MOUSE_BTN7" },
{ MP_MBTN_FORWARD, "MOUSE_BTN8" },
{ MP_MBTN9, "MOUSE_BTN9" },
{ MP_MBTN10, "MOUSE_BTN10" },
{ MP_MBTN11, "MOUSE_BTN11" },
{ MP_MBTN12, "MOUSE_BTN12" },
{ MP_MBTN13, "MOUSE_BTN13" },
{ MP_MBTN14, "MOUSE_BTN14" },
{ MP_MBTN15, "MOUSE_BTN15" },
{ MP_MBTN16, "MOUSE_BTN16" },
{ MP_MBTN17, "MOUSE_BTN17" },
{ MP_MBTN18, "MOUSE_BTN18" },
{ MP_MBTN19, "MOUSE_BTN19" },
{ MP_MBTN_LEFT_DBL, "MOUSE_BTN0_DBL" },
{ MP_MBTN_MID_DBL, "MOUSE_BTN1_DBL" },
{ MP_MBTN_RIGHT_DBL, "MOUSE_BTN2_DBL" },
{ MP_WHEEL_UP, "AXIS_UP" },
{ MP_WHEEL_DOWN, "AXIS_DOWN" },
{ MP_WHEEL_LEFT, "AXIS_LEFT" },
{ MP_WHEEL_RIGHT, "AXIS_RIGHT" },
{ MP_KEY_CLOSE_WIN, "CLOSE_WIN" },
{ MP_KEY_MOUSE_MOVE, "MOUSE_MOVE" },
{ MP_KEY_MOUSE_LEAVE, "MOUSE_LEAVE" },
+36 -61
View File
@@ -73,6 +73,9 @@
#define MP_KEY_SEARCH (MP_KEY_MM_BASE+17)
#define MP_KEY_SLEEP (MP_KEY_MM_BASE+18)
#define MP_KEY_CANCEL (MP_KEY_MM_BASE+19)
#define MP_KEY_RECORD (MP_KEY_MM_BASE+20)
#define MP_KEY_CHANNEL_UP (MP_KEY_MM_BASE+21)
#define MP_KEY_CHANNEL_DOWN (MP_KEY_MM_BASE+22)
/* Function keys */
#define MP_KEY_F (MP_KEY_BASE+0x40)
@@ -95,57 +98,42 @@
#define MP_KEY_KPENTER (MP_KEY_KEYPAD+13)
// Mouse events from VOs
#define MP_MOUSE_BASE ((MP_KEY_BASE+0xA0)|MP_NO_REPEAT_KEY|MP_KEY_EMIT_ON_UP)
#define MP_MOUSE_BTN0 (MP_MOUSE_BASE+0)
#define MP_MOUSE_BTN1 (MP_MOUSE_BASE+1)
#define MP_MOUSE_BTN2 (MP_MOUSE_BASE+2)
#define MP_MOUSE_BTN3 (MP_MOUSE_BASE+3)
#define MP_MOUSE_BTN4 (MP_MOUSE_BASE+4)
#define MP_MOUSE_BTN5 (MP_MOUSE_BASE+5)
#define MP_MOUSE_BTN6 (MP_MOUSE_BASE+6)
#define MP_MOUSE_BTN7 (MP_MOUSE_BASE+7)
#define MP_MOUSE_BTN8 (MP_MOUSE_BASE+8)
#define MP_MOUSE_BTN9 (MP_MOUSE_BASE+9)
#define MP_MOUSE_BTN10 (MP_MOUSE_BASE+10)
#define MP_MOUSE_BTN11 (MP_MOUSE_BASE+11)
#define MP_MOUSE_BTN12 (MP_MOUSE_BASE+12)
#define MP_MOUSE_BTN13 (MP_MOUSE_BASE+13)
#define MP_MOUSE_BTN14 (MP_MOUSE_BASE+14)
#define MP_MOUSE_BTN15 (MP_MOUSE_BASE+15)
#define MP_MOUSE_BTN16 (MP_MOUSE_BASE+16)
#define MP_MOUSE_BTN17 (MP_MOUSE_BASE+17)
#define MP_MOUSE_BTN18 (MP_MOUSE_BASE+18)
#define MP_MOUSE_BTN19 (MP_MOUSE_BASE+19)
#define MP_MOUSE_BTN_END (MP_MOUSE_BASE+20)
#define MP_MBTN_BASE ((MP_KEY_BASE+0xA0)|MP_NO_REPEAT_KEY|MP_KEY_EMIT_ON_UP)
#define MP_MBTN_LEFT (MP_MBTN_BASE+0)
#define MP_MBTN_MID (MP_MBTN_BASE+1)
#define MP_MBTN_RIGHT (MP_MBTN_BASE+2)
#define MP_WHEEL_UP (MP_MBTN_BASE+3)
#define MP_WHEEL_DOWN (MP_MBTN_BASE+4)
#define MP_WHEEL_LEFT (MP_MBTN_BASE+5)
#define MP_WHEEL_RIGHT (MP_MBTN_BASE+6)
#define MP_MBTN_BACK (MP_MBTN_BASE+7)
#define MP_MBTN_FORWARD (MP_MBTN_BASE+8)
#define MP_MBTN9 (MP_MBTN_BASE+9)
#define MP_MBTN10 (MP_MBTN_BASE+10)
#define MP_MBTN11 (MP_MBTN_BASE+11)
#define MP_MBTN12 (MP_MBTN_BASE+12)
#define MP_MBTN13 (MP_MBTN_BASE+13)
#define MP_MBTN14 (MP_MBTN_BASE+14)
#define MP_MBTN15 (MP_MBTN_BASE+15)
#define MP_MBTN16 (MP_MBTN_BASE+16)
#define MP_MBTN17 (MP_MBTN_BASE+17)
#define MP_MBTN18 (MP_MBTN_BASE+18)
#define MP_MBTN19 (MP_MBTN_BASE+19)
#define MP_MBTN_END (MP_MBTN_BASE+20)
#define MP_KEY_IS_MOUSE_BTN_SINGLE(code) \
((code) >= MP_MOUSE_BASE && (code) < MP_MOUSE_BTN_END)
((code) >= MP_MBTN_BASE && (code) < MP_MBTN_END)
#define MP_KEY_IS_WHEEL(code) \
((code) >= MP_WHEEL_UP && (code) <= MP_WHEEL_RIGHT)
#define MP_MOUSE_BASE_DBL ((MP_KEY_BASE+0xC0)|MP_NO_REPEAT_KEY)
#define MP_MOUSE_BTN0_DBL (MP_MOUSE_BASE_DBL+0)
#define MP_MOUSE_BTN1_DBL (MP_MOUSE_BASE_DBL+1)
#define MP_MOUSE_BTN2_DBL (MP_MOUSE_BASE_DBL+2)
#define MP_MOUSE_BTN3_DBL (MP_MOUSE_BASE_DBL+3)
#define MP_MOUSE_BTN4_DBL (MP_MOUSE_BASE_DBL+4)
#define MP_MOUSE_BTN5_DBL (MP_MOUSE_BASE_DBL+5)
#define MP_MOUSE_BTN6_DBL (MP_MOUSE_BASE_DBL+6)
#define MP_MOUSE_BTN7_DBL (MP_MOUSE_BASE_DBL+7)
#define MP_MOUSE_BTN8_DBL (MP_MOUSE_BASE_DBL+8)
#define MP_MOUSE_BTN9_DBL (MP_MOUSE_BASE_DBL+9)
#define MP_MOUSE_BTN10_DBL (MP_MOUSE_BASE_DBL+10)
#define MP_MOUSE_BTN11_DBL (MP_MOUSE_BASE_DBL+11)
#define MP_MOUSE_BTN12_DBL (MP_MOUSE_BASE_DBL+12)
#define MP_MOUSE_BTN13_DBL (MP_MOUSE_BASE_DBL+13)
#define MP_MOUSE_BTN14_DBL (MP_MOUSE_BASE_DBL+14)
#define MP_MOUSE_BTN15_DBL (MP_MOUSE_BASE_DBL+15)
#define MP_MOUSE_BTN16_DBL (MP_MOUSE_BASE_DBL+16)
#define MP_MOUSE_BTN17_DBL (MP_MOUSE_BASE_DBL+17)
#define MP_MOUSE_BTN18_DBL (MP_MOUSE_BASE_DBL+18)
#define MP_MOUSE_BTN19_DBL (MP_MOUSE_BASE_DBL+19)
#define MP_MOUSE_BTN_DBL_END (MP_MOUSE_BASE_DBL+20)
#define MP_MBTN_DBL_BASE ((MP_KEY_BASE+0xC0)|MP_NO_REPEAT_KEY)
#define MP_MBTN_LEFT_DBL (MP_MBTN_DBL_BASE+0)
#define MP_MBTN_MID_DBL (MP_MBTN_DBL_BASE+1)
#define MP_MBTN_RIGHT_DBL (MP_MBTN_DBL_BASE+2)
#define MP_MBTN_DBL_END (MP_MBTN_DBL_BASE+20)
#define MP_KEY_IS_MOUSE_BTN_DBL(code) \
((code) >= MP_MOUSE_BTN0_DBL && (code) < MP_MOUSE_BTN_DBL_END)
((code) >= MP_MBTN_DBL_BASE && (code) < MP_MBTN_DBL_END)
// Apple Remote input module
#define MP_AR_BASE (MP_KEY_BASE+0xE0)
@@ -164,17 +152,6 @@
#define MP_AR_VDOWN (MP_AR_BASE + 12)
#define MP_AR_VDOWN_HOLD (MP_AR_BASE + 13)
// Mouse wheels or touchpad input
#define MP_AXIS_BASE (MP_KEY_BASE+0x100)
#define MP_AXIS_UP (MP_AXIS_BASE+0)
#define MP_AXIS_DOWN (MP_AXIS_BASE+1)
#define MP_AXIS_LEFT (MP_AXIS_BASE+2)
#define MP_AXIS_RIGHT (MP_AXIS_BASE+3)
#define MP_AXIS_END (MP_AXIS_BASE+4)
#define MP_KEY_IS_AXIS(code) \
((code) >= MP_AXIS_BASE && (code) < MP_AXIS_END)
// Reserved area. Can be used for keys that have no explicit names assigned,
// but should be mappable by the user anyway.
#define MP_KEY_UNKNOWN_RESERVED_START (MP_KEY_BASE+0x10000)
@@ -197,12 +174,10 @@
// Whether to dispatch the key binding by current mouse position.
#define MP_KEY_DEPENDS_ON_MOUSE_POS(code) \
(MP_KEY_IS_MOUSE_CLICK(code) || MP_KEY_IS_AXIS(code) || \
(code) == MP_KEY_MOUSE_MOVE)
(MP_KEY_IS_MOUSE_CLICK(code) || (code) == MP_KEY_MOUSE_MOVE)
#define MP_KEY_IS_MOUSE(code) \
(MP_KEY_IS_MOUSE_CLICK(code) || MP_KEY_IS_AXIS(code) || \
MP_KEY_IS_MOUSE_MOVE(code))
(MP_KEY_IS_MOUSE_CLICK(code) || MP_KEY_IS_MOUSE_MOVE(code))
// No input source should generate this.
#define MP_KEY_UNMAPPED (MP_KEY_INTERN+4)
+8 -2
View File
@@ -68,14 +68,20 @@ extern "C" {
* defaults. Likewise, mpv will attempt to leave the OpenGL context with
* standard defaults. The following state is excluded from this:
*
* - the current viewport (can have/is set to an arbitrary value)
* - the glViewport state
* - the glScissor state (but GL_SCISSOR_TEST is in its default value)
* - glBlendFuncSeparate() state (but GL_BLEND is in its default value)
* - glClearColor() state
* - mpv may overwrite the callback set with glDebugMessageCallback()
* - mpv always disables GL_DITHER at init
*
* Messing with the state could be avoided by creating shared OpenGL contexts,
* but this is avoided for the sake of compatibility and interoperability.
*
* On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to
* create OpenGL objects. You will have to do the same. This ensures that
* objects created by mpv and the API users don't clash.
* objects created by mpv and the API users don't clash. Also, legacy state
* must be either in its defaults, or not interfere with core state.
*
* Threading
* ---------
+37
View File
@@ -454,3 +454,40 @@ struct bstr bstr_get_ext(struct bstr s)
return (struct bstr){NULL, 0};
return bstr_splice(s, dotpos + 1, s.len);
}
static int h_to_i(unsigned char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1; // invalid char
}
bool bstr_decode_hex(void *talloc_ctx, struct bstr hex, struct bstr *out)
{
if (!out)
return false;
char *arr = talloc_array(talloc_ctx, char, hex.len / 2);
int len = 0;
while (hex.len >= 2) {
int a = h_to_i(hex.start[0]);
int b = h_to_i(hex.start[1]);
hex = bstr_splice(hex, 2, hex.len);
if (a < 0 || b < 0) {
talloc_free(arr);
return false;
}
arr[len++] = (a << 4) | b;
}
*out = (struct bstr){ .start = arr, .len = len };
return true;
}
+8 -1
View File
@@ -80,6 +80,10 @@ double bstrtod(struct bstr str, struct bstr *rest);
void bstr_lower(struct bstr str);
int bstr_sscanf(struct bstr str, const char *format, ...);
// Decode a string containing hexadecimal data. All whitespace will be silently
// ignored. When successful, this allocates a new array to store the output.
bool bstr_decode_hex(void *talloc_ctx, struct bstr hex, struct bstr *out);
// Decode the UTF-8 code point at the start of the string, and return the
// character.
// After calling this function, *out_next will point to the next character.
@@ -189,7 +193,10 @@ static inline int bstrcmp0(struct bstr str1, const char *str2)
static inline bool bstr_equals(struct bstr str1, struct bstr str2)
{
return str1.len == str2.len && bstrcmp(str1, str2) == 0;
if (str1.len != str2.len)
return false;
return str1.start == str2.start || bstrcmp(str1, str2) == 0;
}
static inline bool bstr_equals0(struct bstr str1, const char *str2)
+56
View File
@@ -59,6 +59,7 @@ struct mp_dispatch_item {
mp_dispatch_fn fn;
void *fn_data;
bool asynchronous;
bool mergeable;
bool completed;
struct mp_dispatch_item *next;
};
@@ -113,12 +114,25 @@ static void mp_dispatch_append(struct mp_dispatch_queue *queue,
struct mp_dispatch_item *item)
{
pthread_mutex_lock(&queue->lock);
if (item->mergeable) {
for (struct mp_dispatch_item *cur = queue->head; cur; cur = cur->next) {
if (cur->mergeable && cur->fn == item->fn &&
cur->fn_data == item->fn_data)
{
talloc_free(item);
pthread_mutex_unlock(&queue->lock);
return;
}
}
}
if (queue->tail) {
queue->tail->next = item;
} else {
queue->head = item;
}
queue->tail = item;
// Wake up the main thread; note that other threads might wait on this
// condition for reasons, so broadcast the condition.
pthread_cond_broadcast(&queue->cond);
@@ -127,6 +141,7 @@ static void mp_dispatch_append(struct mp_dispatch_queue *queue,
if (!queue->wakeup_fn)
queue->interrupted = true;
pthread_mutex_unlock(&queue->lock);
if (queue->wakeup_fn)
queue->wakeup_fn(queue->wakeup_ctx);
}
@@ -165,6 +180,47 @@ void mp_dispatch_enqueue_autofree(struct mp_dispatch_queue *queue,
mp_dispatch_append(queue, item);
}
// Like mp_dispatch_enqueue(), but
void mp_dispatch_enqueue_notify(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data)
{
struct mp_dispatch_item *item = talloc_ptrtype(NULL, item);
*item = (struct mp_dispatch_item){
.fn = fn,
.fn_data = fn_data,
.mergeable = true,
.asynchronous = true,
};
mp_dispatch_append(queue, item);
}
// Remove already queued item. Only items enqueued with the following functions
// can be canceled:
// - mp_dispatch_enqueue()
// - mp_dispatch_enqueue_notify()
// Items which were enqueued, and which are currently executing, can not be
// canceled anymore. This function is mostly for being called from the same
// context as mp_dispatch_queue_process(), where the "currently executing" case
// can be excluded.
void mp_dispatch_cancel_fn(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data)
{
pthread_mutex_lock(&queue->lock);
struct mp_dispatch_item **pcur = &queue->head;
queue->tail = NULL;
while (*pcur) {
struct mp_dispatch_item *cur = *pcur;
if (cur->fn == fn && cur->fn_data == fn_data) {
*pcur = cur->next;
talloc_free(cur);
} else {
queue->tail = cur;
pcur = &cur->next;
}
}
pthread_mutex_unlock(&queue->lock);
}
// Run fn(fn_data) on the target thread synchronously. This function enqueues
// the callback and waits until the target thread is done doing this.
// This is redundant to calling the function inside mp_dispatch_[un]lock(),
+4
View File
@@ -12,6 +12,10 @@ void mp_dispatch_enqueue(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_enqueue_autofree(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_enqueue_notify(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_cancel_fn(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_run(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout);
+94 -14
View File
@@ -39,6 +39,7 @@
#include "common/global.h"
#include "common/msg.h"
#include "common/msg_control.h"
#include "misc/dispatch.h"
#include "misc/node.h"
#include "osdep/atomic.h"
@@ -57,6 +58,8 @@ struct m_config_shadow {
pthread_mutex_t lock;
struct m_config *root;
char *data;
struct m_config_cache **listeners;
int num_listeners;
};
// Represents a sub-struct (OPT_SUBSTRUCT()).
@@ -156,8 +159,11 @@ static void config_destroy(void *p)
m_option_free(co->opt, config->shadow->data + co->shadow_offset);
}
if (config->shadow)
if (config->shadow) {
// must all have been unregistered
assert(config->shadow->num_listeners == 0);
pthread_mutex_destroy(&config->shadow->lock);
}
}
struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
@@ -703,6 +709,8 @@ static int m_config_handle_special_options(struct m_config *config,
config->recursion_depth += 1;
config->includefunc(config->includefunc_ctx, param, flags);
config->recursion_depth -= 1;
if (config->recursion_depth == 0 && config->profile_depth == 0)
m_config_finish_default_profile(config, flags);
return 1;
}
@@ -910,19 +918,8 @@ int m_config_set_option_node(struct m_config *config, bstr name,
int r;
struct m_config_option *co = m_config_get_co(config, name);
if (!co) {
co = m_config_get_co_raw(config, name);
if (co && co->opt->type == &m_option_type_cli_alias) {
/*bstr old_name = name;
co = m_config_mogrify_cli_opt(config, &name, &(bool){0}, &(int){0});
*/
name = bstr0((char *)co->opt->priv);
MP_WARN(config, "Setting %.*s via API is deprecated, set %s instead.\n",
BSTR_P(name), co->opt->name);
} else {
return M_OPT_UNKNOWN;
}
}
if (!co)
return M_OPT_UNKNOWN;
// Do this on an "empty" type to make setting the option strictly overwrite
// the old value, as opposed to e.g. appending to lists.
@@ -1199,6 +1196,16 @@ static bool is_group_included(struct m_config *config, int group, int parent)
return false;
}
static void cache_destroy(void *p)
{
struct m_config_cache *cache = p;
// (technically speaking, being able to call them both without anything
// breaking is a feature provided by these functions)
m_config_cache_set_wakeup_cb(cache, NULL, NULL);
m_config_cache_set_dispatch_change_cb(cache, NULL, NULL, NULL);
}
struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct mpv_global *global,
const struct m_sub_options *group)
@@ -1207,6 +1214,7 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct m_config *root = shadow->root;
struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache);
talloc_set_destructor(cache, cache_destroy);
cache->shadow = shadow;
cache->shadow_config = m_config_new(cache, mp_null_log, root->size,
root->defaults, root->options);
@@ -1301,12 +1309,84 @@ void m_config_notify_change_co(struct m_config *config,
group = g->parent_group;
}
if (shadow) {
pthread_mutex_lock(&shadow->lock);
for (int n = 0; n < shadow->num_listeners; n++) {
struct m_config_cache *cache = shadow->listeners[n];
if (cache->wakeup_cb)
cache->wakeup_cb(cache->wakeup_cb_ctx);
}
pthread_mutex_unlock(&shadow->lock);
}
if (config->option_change_callback) {
config->option_change_callback(config->option_change_callback_ctx, co,
changed);
}
}
void m_config_cache_set_wakeup_cb(struct m_config_cache *cache,
void (*cb)(void *ctx), void *cb_ctx)
{
struct m_config_shadow *shadow = cache->shadow;
pthread_mutex_lock(&shadow->lock);
if (cache->in_list) {
for (int n = 0; n < shadow->num_listeners; n++) {
if (shadow->listeners[n] == cache)
MP_TARRAY_REMOVE_AT(shadow->listeners, shadow->num_listeners, n);
}
if (!shadow->num_listeners) {
talloc_free(shadow->listeners);
shadow->listeners = NULL;
}
}
if (cb) {
MP_TARRAY_APPEND(NULL, shadow->listeners, shadow->num_listeners, cache);
cache->in_list = true;
cache->wakeup_cb = cb;
cache->wakeup_cb_ctx = cb_ctx;
}
pthread_mutex_unlock(&shadow->lock);
}
static void dispatch_notify(void *p)
{
struct m_config_cache *cache = p;
assert(cache->wakeup_dispatch_queue);
mp_dispatch_enqueue_notify(cache->wakeup_dispatch_queue,
cache->wakeup_dispatch_cb,
cache->wakeup_dispatch_cb_ctx);
}
void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache,
struct mp_dispatch_queue *dispatch,
void (*cb)(void *ctx), void *cb_ctx)
{
// Remove the old one is tricky. Firts make sure no new notifications will
// come.
m_config_cache_set_wakeup_cb(cache, NULL, NULL);
// Remove any pending notifications (assume we're on the same thread as
// any potential mp_dispatch_queue_process() callers).
if (cache->wakeup_dispatch_queue) {
mp_dispatch_cancel_fn(cache->wakeup_dispatch_queue,
cache->wakeup_dispatch_cb,
cache->wakeup_dispatch_cb_ctx);
}
cache->wakeup_dispatch_queue = NULL;
cache->wakeup_dispatch_cb = NULL;
cache->wakeup_dispatch_cb_ctx = NULL;
if (cb) {
cache->wakeup_dispatch_queue = dispatch;
cache->wakeup_dispatch_cb = cb;
cache->wakeup_dispatch_cb_ctx = cb_ctx;
m_config_cache_set_wakeup_cb(cache, dispatch_notify, cache);
}
}
bool m_config_is_in_group(struct m_config *config,
const struct m_sub_options *group,
struct m_config_option *co)
+29 -2
View File
@@ -275,9 +275,20 @@ struct m_config_cache {
struct m_config *shadow_config;
long long ts;
int group;
bool in_list;
// --- Implicitly synchronized by setting/unsetting wakeup_cb.
struct mp_dispatch_queue *wakeup_dispatch_queue;
void (*wakeup_dispatch_cb)(void *ctx);
void *wakeup_dispatch_cb_ctx;
// --- Protected by shadow->lock
void (*wakeup_cb)(void *ctx);
void *wakeup_cb_ctx;
};
// Create a mirror copy from the global options.
// Keep in mind that a m_config_cache object is not thread-safe; it merely
// provides thread-safe access to the global options. All API functions for
// the same m_config_cache object must synchronized, unless otherwise noted.
// ta_parent: parent for the returned allocation
// global: option data source
// group: the option group to return. This can be NULL for the global option
@@ -287,6 +298,22 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct mpv_global *global,
const struct m_sub_options *group);
// If any of the options in the group possibly changes, call this callback. The
// callback must not actually access the cache or anything option related.
// Instead, it must wake up the thread that normally accesses the cache.
void m_config_cache_set_wakeup_cb(struct m_config_cache *cache,
void (*cb)(void *ctx), void *cb_ctx);
// If any of the options in the group change, call this callback on the given
// dispatch queue. This is higher level than m_config_cache_set_wakeup_cb(),
// and you can do anything you want in the callback (assuming the dispatch
// queue is processed in the same thread that accesses m_config_cache API).
// To ensure clean shutdown, you must destroy the m_config_cache (or unset the
// callback) before the dispatch queue is destroyed.
void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache,
struct mp_dispatch_queue *dispatch,
void (*cb)(void *ctx), void *cb_ctx);
// Update the options in cache->opts to current global values. Return whether
// there was an update notification at all (which may or may not indicate that
// some options have changed).
@@ -295,13 +322,13 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
bool m_config_cache_update(struct m_config_cache *cache);
// Like m_config_cache_alloc(), but return the struct (m_config_cache->opts)
// directly, with no way to update the config.
// directly, with no way to update the config. Basically this returns a copy
// with a snapshot of the current option values.
// Warning: does currently not set the child as its own talloc root, which
// means the only way to free the struct is by freeing ta_parent.
void *mp_get_config_group(void *ta_parent, struct mpv_global *global,
const struct m_sub_options *group);
// Read a single global option in a thread-safe way. For multiple options,
// use m_config_cache. The option must exist and match the provided type (the
// type is used as a sanity check only). Performs semi-expensive lookup.
+4 -4
View File
@@ -397,9 +397,8 @@ struct m_option {
// The following are also part of the M_OPT_* flags, and are used to update
// certain groups of options.
#define UPDATE_OPT_FIRST (1 << 7)
#define UPDATE_TERM (1 << 7) // terminal options
#define UPDATE_RENDERER (1 << 8) // mainly vo_opengl options
#define UPDATE_VIDEOPOS (1 << 9) // video position (panscan etc.)
#define UPDATE_TERM (1 << 7) // terminal options
#define UPDATE_DEINT (1 << 8) // --deinterlace
#define UPDATE_OSD (1 << 10) // related to OSD rendering
#define UPDATE_BUILTIN_SCRIPTS (1 << 11) // osc/ytdl
#define UPDATE_IMGPAR (1 << 12) // video image params overrides
@@ -408,7 +407,8 @@ struct m_option {
#define UPDATE_PRIORITY (1 << 15) // --priority (Windows-only)
#define UPDATE_SCREENSAVER (1 << 16) // --stop-screensaver
#define UPDATE_VOL (1 << 17) // softvol related options
#define UPDATE_OPT_LAST (1 << 17)
#define UPDATE_LAVFI_COMPLEX (1 << 18) // --lavfi-complex
#define UPDATE_OPT_LAST (1 << 18)
// All bits between _FIRST and _LAST (inclusive)
#define UPDATE_OPTS_MASK \
-5
View File
@@ -82,11 +82,6 @@ enum mp_property_action {
// Pass down an action to a sub-property.
// arg: struct m_property_action_arg*
M_PROPERTY_KEY_ACTION,
// Get the (usually constant) value that indicates no change. Obscure
// special functionality for things like the volume property.
// Otherwise works like M_PROPERTY_GET.
M_PROPERTY_GET_NEUTRAL,
};
// Argument for M_PROPERTY_SWITCH
+21 -42
View File
@@ -142,26 +142,27 @@ static const m_option_t mp_vo_opt_list[] = {
OPT_FLAG("force-window-position", force_window_position, 0),
OPT_STRING("x11-name", winname, 0),
OPT_FLOATRANGE("monitoraspect", force_monitor_aspect, 0, 0.0, 9.0),
OPT_FLOATRANGE("monitorpixelaspect", monitor_pixel_aspect, 0, 0.2, 9.0),
OPT_FLOATRANGE("monitorpixelaspect", monitor_pixel_aspect, 0, 1.0/32.0, 32.0),
OPT_FLAG("fullscreen", fullscreen, 0),
OPT_ALIAS("fs", "fullscreen"),
OPT_FLAG("native-keyrepeat", native_keyrepeat, 0),
OPT_FLOATRANGE("panscan", panscan, UPDATE_VIDEOPOS, 0.0, 1.0),
OPT_FLOATRANGE("video-zoom", zoom, UPDATE_VIDEOPOS, -20.0, 20.0),
OPT_FLOATRANGE("video-pan-x", pan_x, UPDATE_VIDEOPOS, -3.0, 3.0),
OPT_FLOATRANGE("video-pan-y", pan_y, UPDATE_VIDEOPOS, -3.0, 3.0),
OPT_FLOATRANGE("video-align-x", align_x, UPDATE_VIDEOPOS, -1.0, 1.0),
OPT_FLOATRANGE("video-align-y", align_y, UPDATE_VIDEOPOS, -1.0, 1.0),
OPT_CHOICE("video-unscaled", unscaled, UPDATE_VIDEOPOS,
OPT_FLOATRANGE("panscan", panscan, 0, 0.0, 1.0),
OPT_FLOATRANGE("video-zoom", zoom, 0, -20.0, 20.0),
OPT_FLOATRANGE("video-pan-x", pan_x, 0, -3.0, 3.0),
OPT_FLOATRANGE("video-pan-y", pan_y, 0, -3.0, 3.0),
OPT_FLOATRANGE("video-align-x", align_x, 0, -1.0, 1.0),
OPT_FLOATRANGE("video-align-y", align_y, 0, -1.0, 1.0),
OPT_CHOICE("video-unscaled", unscaled, 0,
({"no", 0}, {"yes", 1}, {"downscale-big", 2})),
OPT_INT64("wid", WinID, 0),
OPT_CHOICE_OR_INT("screen", screen_id, 0, 0, 32,
({"default", -1})),
OPT_CHOICE_OR_INT("fs-screen", fsscreen_id, 0, 0, 32,
({"all", -2}, {"current", -1})),
OPT_FLAG("keepaspect", keepaspect, UPDATE_VIDEOPOS),
OPT_FLAG("keepaspect", keepaspect, 0),
OPT_FLAG("keepaspect-window", keepaspect_window, 0),
OPT_FLAG("hidpi-window-scale", hidpi_window_scale, 0),
OPT_FLAG("native-fs", native_fs, 0),
#if HAVE_X11
OPT_CHOICE("x11-netwm", x11_netwm, 0,
({"auto", 0}, {"no", -1}, {"yes", 1})),
@@ -178,7 +179,7 @@ static const m_option_t mp_vo_opt_list[] = {
#endif
#if HAVE_GL
OPT_STRING_VALIDATE("opengl-hwdec-interop", gl_hwdec_interop, 0,
gl_hwdec_validate_opt),
ra_hwdec_validate_opt),
OPT_REPLACED("hwdec-preload", "opengl-hwdec-interop"),
#endif
{0}
@@ -196,6 +197,7 @@ const struct m_sub_options vo_sub_opts = {
.keepaspect = 1,
.keepaspect_window = 1,
.hidpi_window_scale = 1,
.native_fs = 1,
.taskbar_progress = 1,
.snap_window = 0,
.border = 1,
@@ -273,7 +275,7 @@ const m_option_t mp_opts[] = {
OPT_STRING("log-file", log_file, CONF_PRE_PARSE | M_OPT_FILE | UPDATE_TERM),
OPT_FLAG("msg-module", msg_module, UPDATE_TERM),
OPT_FLAG("msg-time", msg_time, UPDATE_TERM),
#if HAVE_WIN32_DESKTOP && HAVE_GPL
#if HAVE_WIN32_DESKTOP
OPT_CHOICE("priority", w32_priority, UPDATE_PRIORITY,
({"no", 0},
{"realtime", REALTIME_PRIORITY_CLASS},
@@ -359,8 +361,9 @@ const m_option_t mp_opts[] = {
OPT_ALIAS("audio", "aid"),
OPT_STRINGLIST("alang", stream_lang[STREAM_AUDIO], 0),
OPT_STRINGLIST("slang", stream_lang[STREAM_SUB], 0),
OPT_FLAG("track-auto-selection", stream_auto_sel, 0),
OPT_STRING("lavfi-complex", lavfi_complex, 0),
OPT_STRING("lavfi-complex", lavfi_complex, UPDATE_LAVFI_COMPLEX),
OPT_CHOICE("audio-display", audio_display, 0,
({"no", 0}, {"attachment", 1})),
@@ -420,10 +423,7 @@ const m_option_t mp_opts[] = {
OPT_SETTINGSLIST("vf-defaults", vf_defs, 0, &vf_obj_list, ),
OPT_SETTINGSLIST("vf", vf_settings, 0, &vf_obj_list, ),
OPT_CHOICE("deinterlace", deinterlace, 0,
({"auto", -1},
{"no", 0},
{"yes", 1})),
OPT_FLAG("deinterlace", deinterlace, UPDATE_DEINT),
OPT_STRING("ad", audio_decoders, 0),
OPT_STRING("vd", video_decoders, 0),
@@ -443,13 +443,7 @@ const m_option_t mp_opts[] = {
// 0 means square pixels
OPT_ASPECT("video-aspect", movie_aspect, UPDATE_IMGPAR, -1.0, 10.0),
OPT_CHOICE("video-aspect-method", aspect_method, UPDATE_IMGPAR,
({"hybrid", 0}, {"bitstream", 1}, {"container", 2})),
#if HAVE_GPL
OPT_CHOICE("field-dominance", field_dominance, UPDATE_IMGPAR,
({"auto", -1}, {"top", 0}, {"bottom", 1}),
.deprecation_message = "use --vf=setfield=bff or tff"),
#endif
({"bitstream", 1}, {"container", 2})),
OPT_SUBSTRUCT("vd-lavc", vd_lavc_params, vd_lavc_conf, 0),
OPT_SUBSTRUCT("ad-lavc", ad_lavc_params, ad_lavc_conf, 0),
@@ -466,7 +460,7 @@ const m_option_t mp_opts[] = {
OPT_PATHLIST("sub-file-paths", sub_paths, 0),
OPT_PATHLIST("audio-file-paths", audiofile_paths, 0),
OPT_PATHLIST("external-files", external_files, 0),
OPT_CLI_ALIAS("external-file", "external-file-append"),
OPT_CLI_ALIAS("external-file", "external-files-append"),
OPT_FLAG("autoload-files", autoload_files, 0),
OPT_FLOAT("sub-delay", sub_delay, UPDATE_OSD),
OPT_FLOAT("sub-fps", sub_fps, UPDATE_OSD),
@@ -575,15 +569,7 @@ const m_option_t mp_opts[] = {
.deprecation_message = "use Lua scripting instead"),
OPT_FLOAT("heartbeat-interval", heartbeat_interval, CONF_MIN, 0),
#if HAVE_GPL
OPT_INTRANGE("brightness", gamma_brightness, 0, -100, 100),
OPT_INTRANGE("saturation", gamma_saturation, 0, -100, 100),
OPT_INTRANGE("contrast", gamma_contrast, 0, -100, 100),
OPT_INTRANGE("hue", gamma_hue, 0, -100, 100),
OPT_INTRANGE("gamma", gamma_gamma, 0, -100, 100),
OPT_CHOICE_C("video-output-levels", video_output_levels, 0,
mp_csp_levels_names),
#endif
OPT_SUBSTRUCT("", video_equalizer, mp_csp_equalizer_conf, 0),
OPT_FLAG("use-filedir-conf", use_filedir_conf, 0),
OPT_CHOICE("osd-level", osd_level, 0,
@@ -617,6 +603,7 @@ const m_option_t mp_opts[] = {
({"no", 0},
{"yes", -1},
{"inf", -1})),
OPT_ALIAS("loop", "loop-file"),
OPT_FLAG("resume-playback", position_resume, 0),
OPT_FLAG("save-position-on-quit", position_save_on_quit, 0),
@@ -834,8 +821,6 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("ass-scale-with-window", "sub-ass-scale-with-window"),
OPT_REPLACED("sub-ass-style-override", "sub-ass-override"),
OPT_REMOVED("fs-black-out-screens", NULL),
OPT_REPLACED_MSG("loop", "loop-playlist", "--loop will be changed to map to"
" --loop-file in future releases."),
OPT_REPLACED("sub-paths", "sub-file-paths"),
{0}
@@ -847,7 +832,6 @@ const struct MPOpts mp_default_opts = {
.audio_driver_list = NULL,
.audio_decoders = NULL,
.video_decoders = NULL,
.deinterlace = -1,
.softvol = SOFTVOL_AUTO,
.softvol_max = 130,
.softvol_volume = 100,
@@ -860,11 +844,6 @@ const struct MPOpts mp_default_opts = {
.heartbeat_interval = 30.0,
.stop_screensaver = 1,
.cursor_autohide_delay = 1000,
.gamma_gamma = 0,
.gamma_brightness = 0,
.gamma_contrast = 0,
.gamma_saturation = 0,
.gamma_hue = 0,
.video_osd = 1,
.osd_level = 1,
.osd_duration = 1000,
@@ -925,6 +904,7 @@ const struct MPOpts mp_default_opts = {
.stream_id_ff = { [STREAM_AUDIO] = -1,
[STREAM_VIDEO] = -1,
[STREAM_SUB] = -1, },
.stream_auto_sel = 1,
.audio_display = 1,
.sub_visibility = 1,
.sub_pos = 100,
@@ -934,7 +914,6 @@ const struct MPOpts mp_default_opts = {
.pitch_correction = 1,
.movie_aspect = -1.,
.aspect_method = 2,
.field_dominance = -1,
.sub_auto = 0,
.audiofile_auto = -1,
.osd_bar_visible = 1,
+3 -8
View File
@@ -40,6 +40,7 @@ typedef struct mp_vo_opts {
int keepaspect;
int keepaspect_window;
int hidpi_window_scale;
int native_fs;
int64_t WinID;
@@ -118,13 +119,7 @@ typedef struct MPOpts {
char *media_title;
int force_rgba_osd;
// ranges -100 - 100, 1000 if the vo default should be used
int gamma_gamma;
int gamma_brightness;
int gamma_contrast;
int gamma_saturation;
int gamma_hue;
int video_output_levels;
struct mp_csp_equalizer_opts *video_equalizer;
int stop_screensaver;
int cursor_autohide_delay;
@@ -211,6 +206,7 @@ typedef struct MPOpts {
int stream_id[2][STREAM_TYPE_COUNT];
int stream_id_ff[STREAM_TYPE_COUNT];
char **stream_lang[STREAM_TYPE_COUNT];
int stream_auto_sel;
int audio_display;
char **display_tags;
int sub_visibility;
@@ -253,7 +249,6 @@ typedef struct MPOpts {
int deinterlace;
float movie_aspect;
int aspect_method;
int field_dominance;
char **sub_name;
char **sub_paths;
char **audiofile_paths;
+1 -1
View File
@@ -28,7 +28,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#if HAVE_POSIX
#if HAVE_GLOB_POSIX
#include <glob.h>
#endif
+1 -8
View File
@@ -18,14 +18,7 @@
#ifndef MPV_MACOSX_APPLICATION
#define MPV_MACOSX_APPLICATION
// Menu Keys identifing menu items
typedef enum {
MPM_H_SIZE,
MPM_N_SIZE,
MPM_D_SIZE,
MPM_MINIMIZE,
MPM_ZOOM,
} MPMenuKey;
#include "osdep/macosx_menubar.h"
// multithreaded wrapper for mpv_main
int cocoa_main(int argc, char *argv[]);
+15 -131
View File
@@ -47,21 +47,6 @@ static pthread_t playback_thread_id;
EventsResponder *_eventsResponder;
}
- (NSMenuItem *)menuItemWithParent:(NSMenu *)parent
title:(NSString *)title
action:(SEL)selector
keyEquivalent:(NSString*)key;
- (NSMenuItem *)mainMenuItemWithParent:(NSMenu *)parent
child:(NSMenu *)child;
- (void)registerMenuItem:(NSMenuItem*)menuItem forKey:(MPMenuKey)key;
- (NSMenu *)appleMenuWithMainMenu:(NSMenu *)mainMenu;
- (NSMenu *)videoMenu;
- (NSMenu *)windowMenu;
@end
@interface NSApplication (NiblessAdditions)
- (void)setAppleMenu:(NSMenu *)aMenu;
@end
static Application *mpv_shared_app(void)
@@ -76,12 +61,12 @@ static void terminate_cocoa_application(void)
}
@implementation Application
@synthesize menuItems = _menu_items;
@synthesize menuBar = _menu_Bar;
@synthesize openCount = _open_count;
- (void)sendEvent:(NSEvent *)event
{
if (![_eventsResponder processKeyEvent:event])
if ([self modalWindow] || ![_eventsResponder processKeyEvent:event])
[super sendEvent:event];
[_eventsResponder wakeup];
}
@@ -89,7 +74,6 @@ static void terminate_cocoa_application(void)
- (id)init
{
if (self = [super init]) {
self.menuItems = [[[NSMutableDictionary alloc] init] autorelease];
_eventsResponder = [EventsResponder sharedInstance];
NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
@@ -125,11 +109,6 @@ static void terminate_cocoa_application(void)
currentPosition, timeLeft];
return tBar;
}
- (void)toggleTouchBarMenu
{
[NSApp toggleTouchBarCustomizationPalette:self];
}
#endif
- (void)processEvent:(struct mpv_event *)event
@@ -145,117 +124,12 @@ static void terminate_cocoa_application(void)
[_eventsResponder queueCommand:cmd];
}
#define _R(P, T, E, K) \
{ \
NSMenuItem *tmp = [self menuItemWithParent:(P) title:(T) \
action:nil keyEquivalent:(E)]; \
[self registerMenuItem:tmp forKey:(K)]; \
}
- (NSMenu *)appleMenuWithMainMenu:(NSMenu *)mainMenu
{
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Apple Menu"];
[self mainMenuItemWithParent:mainMenu child:menu];
[self menuItemWithParent:menu title:@"Hide mpv"
action:@selector(hide:) keyEquivalent: @"h"];
[menu addItem:[NSMenuItem separatorItem]];
[self menuItemWithParent:menu title:@"Quit mpv"
action:@selector(stopPlayback) keyEquivalent: @"q"];
return [menu autorelease];
}
- (NSMenu *)videoMenu
{
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Video"];
_R(menu, @"Half Size", @"0", MPM_H_SIZE)
_R(menu, @"Normal Size", @"1", MPM_N_SIZE)
_R(menu, @"Double Size", @"2", MPM_D_SIZE)
return [menu autorelease];
}
- (NSMenu *)windowMenu
{
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Window"];
_R(menu, @"Minimize", @"m", MPM_MINIMIZE)
_R(menu, @"Zoom", @"z", MPM_ZOOM)
#if HAVE_MACOS_TOUCHBAR
if ([self respondsToSelector:@selector(touchBar)]) {
[menu addItem:[NSMenuItem separatorItem]];
[self menuItemWithParent:menu title:@"Customize Touch Bar…"
action:@selector(toggleTouchBarMenu) keyEquivalent: @""];
}
#endif
return [menu autorelease];
}
- (void)initialize_menu
{
NSMenu *main_menu = [[NSMenu new] autorelease];
[NSApp setMainMenu:main_menu];
[NSApp setAppleMenu:[self appleMenuWithMainMenu:main_menu]];
[NSApp mainMenuItemWithParent:main_menu child:[self videoMenu]];
[NSApp mainMenuItemWithParent:main_menu child:[self windowMenu]];
}
#undef _R
- (void)stopPlayback
{
[self stopMPV:"quit"];
}
- (void)stopPlaybackAndRememberPosition
{
[self stopMPV:"quit-watch-later"];
}
- (void)stopMPV:(char *)cmd
{
if (![_eventsResponder queueCommand:cmd])
terminate_cocoa_application();
}
- (void)registerMenuItem:(NSMenuItem*)menuItem forKey:(MPMenuKey)key
{
[self.menuItems setObject:menuItem forKey:[NSNumber numberWithInt:key]];
}
- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key
{
NSNumber *boxedKey = [NSNumber numberWithInt:key];
NSMenuItem *item = [self.menuItems objectForKey:boxedKey];
if (item) {
[item setAction:action];
}
}
- (NSMenuItem *)menuItemWithParent:(NSMenu *)parent
title:(NSString *)title
action:(SEL)action
keyEquivalent:(NSString*)key
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
action:action
keyEquivalent:key];
[parent addItem:item];
return [item autorelease];
}
- (NSMenuItem *)mainMenuItemWithParent:(NSMenu *)parent
child:(NSMenu *)child
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@""
action:nil
keyEquivalent:@""];
[item setSubmenu:child];
[parent addItem:item];
return [item autorelease];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
@@ -265,10 +139,15 @@ static void terminate_cocoa_application(void)
andEventID:kAEQuitApplication];
}
- (void)applicationWillBecomeActive:(NSNotification *)notification
{
[_eventsResponder setHighestPriotityMediaKeysTap];
}
- (void)handleQuitEvent:(NSAppleEventDescriptor *)event
withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[self stopPlayback];
[self stopMPV:"quit"];
}
- (void)getUrl:(NSAppleEventDescriptor *)event
@@ -292,6 +171,11 @@ static void terminate_cocoa_application(void)
mpv_shared_app().openCount--;
return;
}
[self openFiles:filenames];
}
- (void)openFiles:(NSArray *)filenames
{
SEL cmpsel = @selector(localizedStandardCompare:);
NSArray *files = [filenames sortedArrayUsingSelector:cmpsel];
[_eventsResponder handleFilesArray:files];
@@ -325,14 +209,14 @@ static void *playback_thread(void *ctx_obj)
void cocoa_register_menu_item_action(MPMenuKey key, void* action)
{
if (application_instantiated)
[NSApp registerSelector:(SEL)action forKey:key];
[[NSApp menuBar] registerSelector:(SEL)action forKey:key];
}
static void init_cocoa_application(bool regular)
{
NSApp = mpv_shared_app();
[NSApp setDelegate:NSApp];
[NSApp initialize_menu];
[NSApp setMenuBar:[[MenuBar alloc] init]];
// Will be set to Regular from cocoa_common during UI creation so that we
// don't create an icon when playing audio only files.
+4 -4
View File
@@ -17,18 +17,18 @@
#import <Cocoa/Cocoa.h>
#include "osdep/macosx_application.h"
#import "osdep/macosx_menubar_objc.h"
struct mpv_event;
@interface Application : NSApplication
- (void)initialize_menu;
- (void)registerSelector:(SEL)selector forKey:(MPMenuKey)key;
- (void)stopPlayback;
- (void)processEvent:(struct mpv_event *)event;
- (void)queueCommand:(char *)cmd;
- (void)stopMPV:(char *)cmd;
- (void)openFiles:(NSArray *)filenames;
@property(nonatomic, retain) NSMutableDictionary *menuItems;
@property(nonatomic, retain) MenuBar *menuBar;
@property(nonatomic, retain) NSArray *files;
@property(nonatomic, assign) size_t openCount;
@end
+6 -1
View File
@@ -25,6 +25,11 @@
#include "osdep/macosx_versions.h"
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)
@interface NSWindow (macOS10_12_SDK)
+ (void)setAllowsAutomaticWindowTabbing:(BOOL)allow;
@end
typedef NSUInteger NSWindowStyleMask;
static const NSWindowStyleMask NSWindowStyleMaskClosable = NSClosableWindowMask;
static const NSWindowStyleMask NSWindowStyleMaskTitled = NSTitledWindowMask;
@@ -45,7 +50,7 @@ typedef NSUInteger NSEventModifierFlags;
static const NSEventModifierFlags NSEventModifierFlagShift = NSShiftKeyMask;
static const NSEventModifierFlags NSEventModifierFlagControl = NSControlKeyMask;
static const NSEventModifierFlags NSEventModifierFlagCommand = NSCommandKeyMask;
static const NSEventModifierFlags NSEventModifierFlagCommand = NSCommandKeyMask;
static const NSEventModifierFlags NSEventModifierFlagOption = NSAlternateKeyMask;
#endif
+26 -4
View File
@@ -142,7 +142,7 @@ static int mk_flags(NSEvent *event)
return ([event data1] & 0x0000FFFF);
}
static int mk_down(NSEvent *event)
static int mk_down(NSEvent *event)
{
return (((mk_flags(event) & 0xFF00) >> 8)) == 0xA;
}
@@ -254,7 +254,7 @@ void cocoa_set_mpv_handle(struct mpv_handle *ctx)
[_input_lock unlock];
}
- (void)setInputContext:(struct input_ctx *)ctx;
- (void)setInputContext:(struct input_ctx *)ctx
{
[_input_lock lock];
_inputContext = ctx;
@@ -346,7 +346,6 @@ void cocoa_set_mpv_handle(struct mpv_handle *ctx)
- (void)startAppleRemote
{
#if HAVE_APPLE_REMOTE
dispatch_async(dispatch_get_main_queue(), ^{
self->_remote = [[HIDRemote alloc] init];
@@ -356,8 +355,8 @@ void cocoa_set_mpv_handle(struct mpv_handle *ctx)
}
});
#endif
}
- (void)stopAppleRemote
{
#if HAVE_APPLE_REMOTE
@@ -367,10 +366,31 @@ void cocoa_set_mpv_handle(struct mpv_handle *ctx)
});
#endif
}
- (void)restartMediaKeys
{
CGEventTapEnable(self->_mk_tap_port, true);
}
- (void)setHighestPriotityMediaKeysTap
{
if (self->_mk_tap_port == nil)
return;
CGEventTapInformation *taps = ta_alloc_size(nil, sizeof(CGEventTapInformation));
uint32_t numTaps = 0;
CGError err = CGGetEventTapList(1, taps, &numTaps);
if (err == kCGErrorSuccess && numTaps > 0) {
pid_t processID = [NSProcessInfo processInfo].processIdentifier;
if (taps[0].tappingProcess != processID) {
[self stopMediaKeys];
[self startMediaKeys];
}
}
talloc_free(taps);
}
- (void)startMediaKeys
{
dispatch_async(dispatch_get_main_queue(), ^{
@@ -390,10 +410,12 @@ void cocoa_set_mpv_handle(struct mpv_handle *ctx)
[[NSRunLoop mainRunLoop] addPort:port forMode:NSRunLoopCommonModes];
});
}
- (void)stopMediaKeys
{
dispatch_async(dispatch_get_main_queue(), ^{
NSMachPort *port = (NSMachPort *)self->_mk_tap_port;
CGEventTapEnable(self->_mk_tap_port, false);
[[NSRunLoop mainRunLoop] removePort:port forMode:NSRunLoopCommonModes];
CFRelease(self->_mk_tap_port);
self->_mk_tap_port = nil;
+2
View File
@@ -40,6 +40,8 @@ struct input_ctx;
- (void)putKey:(int)keycode;
- (void)setHighestPriotityMediaKeysTap;
- (void)handleFilesArray:(NSArray *)files;
- (bool)processKeyEvent:(NSEvent *)event;
+30
View File
@@ -0,0 +1,30 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPV_MACOSX_MENU
#define MPV_MACOSX_MENU
// Menu Keys identifing menu items
typedef enum {
MPM_H_SIZE,
MPM_N_SIZE,
MPM_D_SIZE,
MPM_MINIMIZE,
MPM_ZOOM,
} MPMenuKey;
#endif /* MPV_MACOSX_MENU */
+751
View File
@@ -0,0 +1,751 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "common/common.h"
#import "macosx_menubar_objc.h"
#import "osdep/macosx_application_objc.h"
#include "osdep/macosx_compat.h"
@implementation MenuBar
{
NSArray *menuTree;
}
- (id)init
{
if (self = [super init]) {
NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];
[userDefaults setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
[userDefaults setBool:YES forKey:@"NSDisabledDictationMenuItem"];
[userDefaults setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"];
if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)])
[NSWindow setAllowsAutomaticWindowTabbing: NO];
menuTree = @[
@{
@"name": @"Apple",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"About mpv",
@"action" : @"about",
@"key" : @"",
@"target" : self
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Preferences…",
@"action" : @"preferences:",
@"key" : @",",
@"target" : self,
@"file" : @"mpv.conf",
@"alertTitle1": @"No Application found to open your config file.",
@"alertText1" : @"Please open the mpv.conf file with "
"your preferred text editor in the now "
"open folder to edit your config.",
@"alertTitle2": @"No config file found.",
@"alertText2" : @"Please create a mpv.conf file with your "
"preferred text editor in the now open folder.",
@"alertTitle3": @"No config path or file found.",
@"alertText3" : @"Please create the following path ~/.config/mpv/ "
"and a mpv.conf file within with your preferred "
"text editor."
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Keyboard Shortcuts Config…",
@"action" : @"preferences:",
@"key" : @"",
@"target" : self,
@"file" : @"input.conf",
@"alertTitle1": @"No Application found to open your config file.",
@"alertText1" : @"Please open the input.conf file with "
"your preferred text editor in the now "
"open folder to edit your config.",
@"alertTitle2": @"No config file found.",
@"alertText2" : @"Please create a input.conf file with your "
"preferred text editor in the now open folder.",
@"alertTitle3": @"No config path or file found.",
@"alertText3" : @"Please create the following path ~/.config/mpv/ "
"and a input.conf file within with your preferred "
"text editor."
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Hide mpv",
@"action" : @"hide:",
@"key" : @"h",
@"target" : NSApp
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Quit and Remember Position",
@"action" : @"quit:",
@"key" : @"",
@"target" : self,
@"cmd" : @"quit-watch-later"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Quit mpv",
@"action" : @"quit:",
@"key" : @"q",
@"target" : self,
@"cmd" : @"quit"
}]
]
},
@{
@"name": @"File",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Open File…",
@"action" : @"openFile",
@"key" : @"o",
@"target" : self
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Open URL…",
@"action" : @"openURL",
@"key" : @"O",
@"target" : self
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Save Screenshot",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"async screenshot"
}]
]
},
@{
@"name": @"Edit",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Undo",
@"action" : @"undo:",
@"key" : @"z"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Redo",
@"action" : @"redo:",
@"key" : @"Z"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Cut",
@"action" : @"cut:",
@"key" : @"x"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Copy",
@"action" : @"copy:",
@"key" : @"c"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Paste",
@"action" : @"paste:",
@"key" : @"v"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Select All",
@"action" : @"selectAll:",
@"key" : @"a"
}]
]
},
@{
@"name": @"View",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Fullscreen",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle fullscreen"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Float on Top",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle ontop"
}],
#if HAVE_MACOS_TOUCHBAR
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Customize Touch Bar…",
@"action" : @"toggleTouchBarCustomizationPalette:",
@"key" : @"",
@"target" : NSApp
}]
#endif
]
},
@{
@"name": @"Video",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Zoom Out",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add panscan -0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Zoom In",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add panscan 0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Reset Zoom",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set panscan 0"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Aspect Ratio 4:3",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set video-aspect \"4:3\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Aspect Ratio 16:9",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set video-aspect \"16:9\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Aspect Ratio 1.85:1",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set video-aspect \"1.85:1\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Aspect Ratio 2.35:1",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set video-aspect \"2.35:1\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Reset Aspect Ratio",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set video-aspect \"-1\""
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Half Size",
@"key" : @"0",
@"cmdSpecial" : [NSNumber numberWithInt:MPM_H_SIZE]
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Normal Size",
@"key" : @"1",
@"cmdSpecial" : [NSNumber numberWithInt:MPM_N_SIZE]
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Double Size",
@"key" : @"2",
@"cmdSpecial" : [NSNumber numberWithInt:MPM_D_SIZE]
}]
]
},
@{
@"name": @"Audio",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Next Audio Track",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle audio"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Previous Audio Track",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle audio down"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Mute",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle mute"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Play Audio Later",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add audio-delay 0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Play Audio Earlier",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add audio-delay -0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Reset Audio Delay",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set audio-delay 0.0 "
}]
]
},
@{
@"name": @"Subtitle",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Next Subtitle Track",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle sub"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Previous Subtitle Track",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle sub down"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Force Style",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle-values sub-ass-override \"force\" \"no\""
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Display Subtitles Later",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add sub-delay 0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Display Subtitles Earlier",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add sub-delay -0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Reset Subtitle Delay",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set sub-delay 0.0"
}]
]
},
@{
@"name": @"Playback",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Pause",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle pause"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Increase Speed",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add speed 0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Decrease Speed",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add speed -0.1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Reset Speed",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"set speed 1.0"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Show Playlist",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"script-message osc-playlist"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Show Chapters",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"script-message osc-chapterlist"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Show Tracks",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"script-message osc-tracklist"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Next File",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"playlist-next"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Previous File",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"playlist-prev"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Loop File",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle-values loop-file \"inf\" \"no\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Toggle Loop Playlist",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"cycle-values loop-playlist \"inf\" \"no\""
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Shuffle",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"playlist-shuffle"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Next Chapter",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add chapter 1"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Previous Chapter",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"add chapter -1"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Step Forward",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"frame-step"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Step Backward",
@"action" : @"cmd:",
@"key" : @"",
@"target" : self,
@"cmd" : @"frame-back-step"
}]
]
},
@{
@"name": @"Window",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Minimize",
@"key" : @"m",
@"cmdSpecial" : [NSNumber numberWithInt:MPM_MINIMIZE]
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Zoom",
@"key" : @"z",
@"cmdSpecial" : [NSNumber numberWithInt:MPM_ZOOM]
}]
]
},
@{
@"name": @"Help",
@"menu": @[
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"mpv Website…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://mpv.io"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"mpv on github…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://github.com/mpv-player/mpv"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Online Manual…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://mpv.io/manual/master/"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Online Wiki…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://github.com/mpv-player/mpv/wiki"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Release Notes…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://github.com/mpv-player/mpv/blob/master/RELEASE_NOTES"
}],
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Keyboard Shortcuts…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://github.com/mpv-player/mpv/blob/master/etc/input.conf"
}],
@{ @"name": @"separator" },
[NSMutableDictionary dictionaryWithDictionary:@{
@"name" : @"Report Issue…",
@"action" : @"url:",
@"key" : @"",
@"target" : self,
@"url" : @"https://github.com/mpv-player/mpv/issues/new"
}]
]
}
];
[NSApp setMainMenu:[self mainMenu]];
}
return self;
}
- (NSMenu *)mainMenu
{
NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"];
for(id mMenu in menuTree) {
NSMenu *menu = [[NSMenu alloc] initWithTitle:mMenu[@"name"]];
NSMenuItem *mItem = [mainMenu addItemWithTitle:mMenu[@"name"]
action:nil
keyEquivalent:@""];
[mainMenu setSubmenu:menu forItem:mItem];
for(id subMenu in mMenu[@"menu"]) {
#if HAVE_MACOS_TOUCHBAR
if ([subMenu[@"action"] isEqual:@"toggleTouchBarCustomizationPalette:"]) {
if (![NSApp respondsToSelector:@selector(touchBar)])
continue;
}
#endif
if ([subMenu[@"name"] isEqual:@"separator"]) {
[menu addItem:[NSMenuItem separatorItem]];
} else {
NSMenuItem *iItem = [menu addItemWithTitle:subMenu[@"name"]
action:NSSelectorFromString(subMenu[@"action"])
keyEquivalent:subMenu[@"key"]];
[iItem setTarget:subMenu[@"target"]];
[subMenu setObject:iItem forKey:@"menuItem"];
}
}
}
return mainMenu;
}
- (void)about
{
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
@"mpv", @"ApplicationName",
[self getMPVIcon], @"ApplicationIcon",
@"Copyright © 2000-2017 mpv/MPlayer/mplayer2 projects", @"Copyright",
[NSString stringWithUTF8String:mpv_version], @"ApplicationVersion",
nil];
[NSApp orderFrontStandardAboutPanelWithOptions:options];
}
- (void)preferences:(NSMenuItem *)menuItem
{
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *mItemDict = [self getDictFromMenuItem:menuItem];
NSArray *configPaths = @[
[NSString stringWithFormat:@"%@/.mpv/", NSHomeDirectory()],
[NSString stringWithFormat:@"%@/.config/mpv/", NSHomeDirectory()]];
for (id path in configPaths) {
NSString *fileP = [path stringByAppendingString:mItemDict[@"file"]];
if ([fileManager fileExistsAtPath:fileP]){
if ([workspace openFile:fileP])
return;
[workspace openFile:path];
[self alertWithTitle:mItemDict[@"alertTitle1"]
andText:mItemDict[@"alertText1"]];
return;
}
if ([workspace openFile:path]) {
[self alertWithTitle:mItemDict[@"alertTitle2"]
andText:mItemDict[@"alertText2"]];
return;
}
}
[self alertWithTitle:mItemDict[@"alertTitle3"]
andText:mItemDict[@"alertText3"]];
}
- (void)quit:(NSMenuItem *)menuItem
{
NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"];
[(Application *)NSApp stopMPV:(char *)[cmd UTF8String]];
}
- (void)openFile
{
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:YES];
if ([panel runModal] == NSFileHandlingPanelOKButton){
NSMutableArray *fileArray = [[NSMutableArray alloc] init];
for (id url in [panel URLs])
[fileArray addObject:[url path]];
[(Application *)NSApp openFiles:fileArray];
}
}
- (void)openURL
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Open URL"];
[alert addButtonWithTitle:@"Ok"];
[alert addButtonWithTitle:@"Cancel"];
[alert setIcon:[self getMPVIcon]];
NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)];
[input setPlaceholderString:@"URL"];
[alert setAccessoryView:input];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[input becomeFirstResponder];
});
if ([alert runModal] == NSAlertFirstButtonReturn && [input stringValue].length > 0) {
NSArray *url = [NSArray arrayWithObjects:[input stringValue], nil];
[(Application *)NSApp openFiles:url];
}
}
- (void)cmd:(NSMenuItem *)menuItem
{
NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"];
[(Application *)NSApp queueCommand:(char *)[cmd UTF8String]];
}
- (void)url:(NSMenuItem *)menuItem
{
NSString *url = [self getDictFromMenuItem:menuItem][@"url"];
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
}
static const char macosx_icon[] =
#include "osdep/macosx_icon.inc"
;
- (NSImage *)getMPVIcon
{
NSData *icon_data = [NSData dataWithBytesNoCopy:(void *)macosx_icon
length:sizeof(macosx_icon)
freeWhenDone:NO];
return [[NSImage alloc] initWithData:icon_data];
}
- (void)alertWithTitle:(NSString *)title andText:(NSString *)text
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:title];
[alert setInformativeText:text];
[alert addButtonWithTitle:@"Ok"];
[alert setIcon:[self getMPVIcon]];
[alert runModal];
}
- (NSMutableDictionary *)getDictFromMenuItem:(NSMenuItem *)menuItem
{
for(id mMenu in menuTree) {
for(id subMenu in mMenu[@"menu"]) {
if([subMenu[@"menuItem"] isEqual:menuItem])
return subMenu;
}
}
return nil;
}
- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key
{
for(id mMenu in menuTree) {
for(id subMenu in mMenu[@"menu"]) {
if([subMenu[@"cmdSpecial"] isEqual:[NSNumber numberWithInt:key]]) {
[subMenu[@"menuItem"] setAction:action];
return;
}
}
}
}
@end
+25
View File
@@ -0,0 +1,25 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#include "osdep/macosx_menubar.h"
@interface MenuBar : NSObject
- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key;
@end
+9 -4
View File
@@ -112,7 +112,8 @@ static int create_overlapped_pipe(HANDLE *read, HANDLE *write)
unsigned long id = atomic_fetch_add(&counter, 1);
unsigned pid = GetCurrentProcessId();
wchar_t buf[36];
swprintf(buf, sizeof(buf), L"\\\\.\\pipe\\mpv-anon-%08x-%08lx", pid, id);
swprintf(buf, MP_ARRAY_SIZE(buf), L"\\\\.\\pipe\\mpv-anon-%08x-%08lx",
pid, id);
// The function for creating anonymous pipes (CreatePipe) can't create
// overlapped pipes, so instead, use a named pipe with a unique name
@@ -126,8 +127,10 @@ static int create_overlapped_pipe(HANDLE *read, HANDLE *write)
// Open the write end of the pipe as a synchronous handle
*write = CreateFileW(buf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (*write == INVALID_HANDLE_VALUE)
if (*write == INVALID_HANDLE_VALUE) {
CloseHandle(*read);
goto error;
}
return 0;
error:
@@ -173,8 +176,10 @@ error:
static int sparse_wait(HANDLE *handles, unsigned num_handles)
{
unsigned w_num_handles = 0;
HANDLE w_handles[num_handles];
int map[num_handles];
HANDLE w_handles[10];
int map[10];
if (num_handles > MP_ARRAY_SIZE(w_handles))
return -1;
for (unsigned i = 0; i < num_handles; i++) {
if (!handles[i])
+20
View File
@@ -69,6 +69,21 @@ static const struct keymap vk_map[] = {
{0, 0}
};
static const struct keymap appcmd_map[] = {
{APPCOMMAND_MEDIA_NEXTTRACK, MP_KEY_NEXT},
{APPCOMMAND_MEDIA_PREVIOUSTRACK, MP_KEY_PREV},
{APPCOMMAND_MEDIA_STOP, MP_KEY_STOP},
{APPCOMMAND_MEDIA_PLAY_PAUSE, MP_KEY_PLAYPAUSE},
{APPCOMMAND_MEDIA_PLAY, MP_KEY_PLAY},
{APPCOMMAND_MEDIA_PAUSE, MP_KEY_PAUSE},
{APPCOMMAND_MEDIA_RECORD, MP_KEY_RECORD},
{APPCOMMAND_MEDIA_FAST_FORWARD, MP_KEY_FORWARD},
{APPCOMMAND_MEDIA_REWIND, MP_KEY_REWIND},
{APPCOMMAND_MEDIA_CHANNEL_UP, MP_KEY_CHANNEL_UP},
{APPCOMMAND_MEDIA_CHANNEL_DOWN, MP_KEY_CHANNEL_DOWN},
{0, 0}
};
static int lookup_keymap(const struct keymap *map, int key)
{
while (map->from && map->from != key) map++;
@@ -89,3 +104,8 @@ int mp_w32_vkey_to_mpkey(UINT vkey, bool extended)
return mpkey;
}
int mp_w32_appcmd_to_mpkey(UINT appcmd)
{
return lookup_keymap(appcmd_map, appcmd);
}
+3
View File
@@ -23,4 +23,7 @@
/* Convert a Windows virtual key code to an mpv key */
int mp_w32_vkey_to_mpkey(UINT vkey, bool extended);
/* Convert a WM_APPCOMMAND value to an mpv key */
int mp_w32_appcmd_to_mpkey(UINT appcmd);
#endif
+8 -3
View File
@@ -205,8 +205,13 @@ int pthread_detach(pthread_t thread)
static DWORD WINAPI run_thread(LPVOID lpParameter)
{
struct m_thread_info *info = lpParameter;
pthread_exit(info->user_fn(info->user_arg));
pthread_mutex_lock(&pthread_table_lock);
struct m_thread_info *pinfo = find_thread_info(pthread_self());
assert(pinfo);
struct m_thread_info info = *pinfo;
pthread_mutex_unlock(&pthread_table_lock);
pthread_exit(info.user_fn(info.user_arg));
abort(); // not reached
}
@@ -228,7 +233,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
.user_fn = start_routine,
.user_arg = arg,
};
info->handle = CreateThread(NULL, 0, run_thread, info, CREATE_SUSPENDED,
info->handle = CreateThread(NULL, 0, run_thread, NULL, CREATE_SUSPENDED,
&info->id);
if (!info->handle) {
remove_thread_info(info);
+68 -54
View File
@@ -51,6 +51,7 @@ enum {
AD_NEW_FMT = -3,
AD_WAIT = -4,
AD_NO_PROGRESS = -5,
AD_STARVE = -6,
};
// Use pitch correction only for speed adjustments by the user, not minor sync
@@ -278,12 +279,8 @@ int reinit_audio_filters(struct MPContext *mpctx)
// Only force refresh if the amount of dropped buffered data is going to
// cause "issues" for the A/V sync logic.
if (mpctx->audio_status == STATUS_PLAYING &&
mpctx->playback_pts != MP_NOPTS_VALUE && delay > 0.2)
{
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->playback_pts,
MPSEEK_EXACT, 0);
}
if (mpctx->audio_status == STATUS_PLAYING && delay > 0.2)
issue_refresh_seek(mpctx, MPSEEK_EXACT);
return 1;
}
@@ -355,6 +352,7 @@ static void ao_chain_uninit(struct ao_chain *ao_c)
af_destroy(ao_c->af);
talloc_free(ao_c->input_frame);
talloc_free(ao_c->input_format);
talloc_free(ao_c->ao_buffer);
talloc_free(ao_c);
}
@@ -371,6 +369,18 @@ void uninit_audio_chain(struct MPContext *mpctx)
}
}
static void get_ao_format(struct ao *ao, struct mp_audio *aformat)
{
int samplerate;
int format;
struct mp_chmap channels;
ao_get_format(ao, &samplerate, &format, &channels);
*aformat = (struct mp_audio){0};
mp_audio_set_format(aformat, format);
mp_audio_set_channels(aformat, &channels);
aformat->rate = samplerate;
}
static void reinit_audio_filters_and_output(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
@@ -379,12 +389,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
struct track *track = ao_c->track;
struct af_stream *afs = ao_c->af;
if (ao_c->input_frame)
mp_audio_copy_config(&ao_c->input_format, ao_c->input_frame);
struct mp_audio in_format = ao_c->input_format;
if (!mp_audio_config_valid(&in_format)) {
if (!mp_aframe_config_is_valid(ao_c->input_format)) {
// We don't know the audio format yet - so configure it later as we're
// resyncing. fill_audio_buffers() will call this function again.
mp_wakeup_core(mpctx);
@@ -393,18 +398,20 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
// Weak gapless audio: drain AO on decoder format changes
if (mpctx->ao_decoder_fmt && mpctx->ao && opts->gapless_audio < 0 &&
!mp_audio_config_equals(mpctx->ao_decoder_fmt, &in_format))
!mp_aframe_config_equals(mpctx->ao_decoder_fmt, ao_c->input_format))
{
uninit_audio_out(mpctx);
}
struct mp_audio in_format;
mp_audio_config_from_aframe(&in_format, ao_c->input_format);
if (mpctx->ao && mp_audio_config_equals(&in_format, &afs->input))
return;
afs->output = (struct mp_audio){0};
if (mpctx->ao) {
ao_get_format(mpctx->ao, &afs->output);
} else if (af_fmt_is_pcm(in_format.format)) {
get_ao_format(mpctx->ao, &afs->output);
} else if (af_fmt_is_pcm(mp_aframe_get_format(ao_c->input_format))) {
afs->output.rate = opts->force_srate;
mp_audio_set_format(&afs->output, opts->audio_output_format);
if (opts->audio_output_channels.num_chmaps == 1) {
@@ -458,7 +465,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
struct mp_audio fmt = {0};
if (mpctx->ao)
ao_get_format(mpctx->ao, &fmt);
get_ao_format(mpctx->ao, &fmt);
// Verify passthrough format was not changed.
if (mpctx->ao && af_fmt_is_spdif(afs->output.format)) {
@@ -480,7 +487,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
if (!audio_init_best_codec(ao_c->audio_src))
goto init_error;
reset_audio_state(mpctx);
ao_c->input_format = (struct mp_audio){0};
mp_aframe_reset(ao_c->input_format);
mp_wakeup_core(mpctx); // reinit with new format next time
return;
}
@@ -495,8 +502,8 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
if (!mp_audio_config_equals(&afs->output, &afs->filter_output))
afs->initialized = 0;
mpctx->ao_decoder_fmt = talloc(NULL, struct mp_audio);
*mpctx->ao_decoder_fmt = in_format;
mpctx->ao_decoder_fmt = mp_aframe_create();
mp_aframe_config_copy(mpctx->ao_decoder_fmt, ao_c->input_format);
MP_INFO(mpctx, "AO: [%s] %s\n", ao_get_name(mpctx->ao),
mp_audio_config_to_str(&fmt));
@@ -556,25 +563,19 @@ init_error:
void reinit_audio_chain(struct MPContext *mpctx)
{
reinit_audio_chain_src(mpctx, NULL);
struct track *track = NULL;
track = mpctx->current_track[0][STREAM_AUDIO];
if (!track || !track->stream) {
uninit_audio_out(mpctx);
error_on_track(mpctx, track);
return;
}
reinit_audio_chain_src(mpctx, track);
}
void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
// (track=NULL creates a blank chain, used for lavfi-complex)
void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track)
{
struct track *track = NULL;
struct sh_stream *sh = NULL;
if (!src) {
track = mpctx->current_track[0][STREAM_AUDIO];
if (!track) {
uninit_audio_out(mpctx);
return;
}
sh = track->stream;
if (!sh) {
uninit_audio_out(mpctx);
goto no_audio;
}
}
assert(!mpctx->ao_chain);
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
@@ -583,15 +584,15 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
mpctx->ao_chain = ao_c;
ao_c->log = mpctx->log;
ao_c->af = af_new(mpctx->global);
if (sh)
ao_c->af->replaygain_data = sh->codec->replaygain_data;
if (track && track->stream)
ao_c->af->replaygain_data = track->stream->codec->replaygain_data;
ao_c->spdif_passthrough = true;
ao_c->pts = MP_NOPTS_VALUE;
ao_c->ao_buffer = mp_audio_buffer_create(NULL);
ao_c->ao = mpctx->ao;
ao_c->input_format = mp_aframe_create();
ao_c->filter_src = src;
if (!ao_c->filter_src) {
if (track) {
ao_c->track = track;
track->ao_c = ao_c;
if (!init_audio_decoder(mpctx, track))
@@ -603,7 +604,7 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
if (mpctx->ao) {
struct mp_audio fmt;
ao_get_format(mpctx->ao, &fmt);
get_ao_format(mpctx->ao, &fmt);
mp_audio_buffer_reinit(ao_c->ao_buffer, &fmt);
}
@@ -613,7 +614,6 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
init_error:
uninit_audio_chain(mpctx);
uninit_audio_out(mpctx);
no_audio:
error_on_track(mpctx, track);
}
@@ -625,9 +625,7 @@ double written_audio_pts(struct MPContext *mpctx)
if (!ao_c)
return MP_NOPTS_VALUE;
struct mp_audio in_format = ao_c->input_format;
if (!mp_audio_config_valid(&in_format) || ao_c->af->initialized < 1)
if (ao_c->af->initialized < 1)
return MP_NOPTS_VALUE;
// first calculate the end pts of audio that has been output by decoder
@@ -664,7 +662,7 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags
return 0;
struct ao *ao = mpctx->ao;
struct mp_audio out_format;
ao_get_format(ao, &out_format);
get_ao_format(ao, &out_format);
#if HAVE_ENCODING
encode_lavc_set_audio_pts(mpctx->encode_lavc_ctx, playing_audio_pts(mpctx));
#endif
@@ -717,7 +715,7 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
return true;
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
get_ao_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / mpctx->audio_speed;
if (!opts->initial_audio_sync) {
@@ -800,8 +798,11 @@ static bool copy_output(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
if (endpts != MP_NOPTS_VALUE) {
double rate = afs->output.rate / mpctx->audio_speed;
double curpts = written_audio_pts(mpctx);
if (curpts != MP_NOPTS_VALUE)
maxsamples = (endpts - curpts - mpctx->opts->audio_delay) * rate;
if (curpts != MP_NOPTS_VALUE) {
double remaining =
(endpts - curpts - mpctx->opts->audio_delay) * rate;
maxsamples = MPCLAMP(remaining, 0, INT_MAX);
}
}
struct mp_audio *mpa = af_read_output_frame(afs);
@@ -839,10 +840,14 @@ static int decode_new_frame(struct ao_chain *ao_c)
res = audio_get_frame(ao_c->audio_src, &ao_c->input_frame);
}
if (ao_c->input_frame)
mp_aframe_config_copy(ao_c->input_format, ao_c->input_frame);
switch (res) {
case DATA_OK: return AD_OK;
case DATA_WAIT: return AD_WAIT;
case DATA_AGAIN: return AD_NO_PROGRESS;
case DATA_STARVE: return AD_STARVE;
case DATA_EOF: return AD_EOF;
default: abort();
}
@@ -875,7 +880,9 @@ static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
break;
res = decode_new_frame(ao_c);
if (res == AD_NO_PROGRESS || res == AD_WAIT)
if (res == AD_NO_PROGRESS)
continue;
if (res == AD_WAIT || res == AD_STARVE)
break;
if (res < 0) {
// drain filters first (especially for true EOF case)
@@ -884,14 +891,19 @@ static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
}
// On format change, make sure to drain the filter chain.
if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) {
struct mp_audio in_format;
mp_audio_config_from_aframe(&in_format, ao_c->input_format);
if (!mp_audio_config_equals(&afs->input, &in_format)) {
copy_output(mpctx, outbuf, minsamples, endpts, true, &eof);
res = AD_NEW_FMT;
break;
}
struct mp_audio *mpa = ao_c->input_frame;
struct mp_audio *mpa = mp_audio_from_aframe(ao_c->input_frame);
talloc_free(ao_c->input_frame);
ao_c->input_frame = NULL;
if (!mpa)
abort();
if (mpa->pts == MP_NOPTS_VALUE) {
ao_c->pts = MP_NOPTS_VALUE;
} else {
@@ -967,7 +979,9 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
if (ao_c->af->initialized < 1 || !mpctx->ao) {
// Probe the initial audio format. Returns AD_OK (and does nothing) if
// the format is already known.
int r = decode_new_frame(mpctx->ao_chain);
int r = AD_NO_PROGRESS;
while (r == AD_NO_PROGRESS)
r = decode_new_frame(mpctx->ao_chain);
if (r == AD_WAIT)
return; // continue later when new data is available
if (r == AD_EOF) {
@@ -993,7 +1007,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
}
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
get_ao_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / mpctx->audio_speed;
int align = af_format_sample_alignment(out_format.format);
@@ -1038,7 +1052,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
status = filter_audio(mpctx, ao_c->ao_buffer, playsize);
if (status == AD_WAIT)
return;
if (status == AD_NO_PROGRESS) {
if (status == AD_NO_PROGRESS || status == AD_STARVE) {
mp_wakeup_core(mpctx);
return;
}
+66 -134
View File
@@ -56,7 +56,7 @@
#include "video/decode/vd.h"
#include "video/out/vo.h"
#include "video/csputils.h"
#include "audio/audio_buffer.h"
#include "audio/aframe.h"
#include "audio/out/ao.h"
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
@@ -1805,9 +1805,6 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
.max = opts->softvol_max,
};
return M_PROPERTY_OK;
case M_PROPERTY_GET_NEUTRAL:
*(float *)arg = 100;
return M_PROPERTY_OK;
case M_PROPERTY_PRINT:
*(char **)arg = talloc_asprintf(NULL, "%i", (int)opts->softvol_volume);
return M_PROPERTY_OK;
@@ -2019,17 +2016,20 @@ static int mp_property_audio_codec(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, c);
}
static int property_audiofmt(struct mp_audio a, int action, void *arg)
static int property_audiofmt(struct mp_aframe *fmt, int action, void *arg)
{
if (!mp_audio_config_valid(&a))
if (!fmt || !mp_aframe_config_is_valid(fmt))
return M_PROPERTY_UNAVAILABLE;
struct mp_chmap chmap = {0};
mp_aframe_get_chmap(fmt, &chmap);
struct m_sub_property props[] = {
{"samplerate", SUB_PROP_INT(a.rate)},
{"channel-count", SUB_PROP_INT(a.channels.num)},
{"channels", SUB_PROP_STR(mp_chmap_to_str(&a.channels))},
{"hr-channels", SUB_PROP_STR(mp_chmap_to_str_hr(&a.channels))},
{"format", SUB_PROP_STR(af_fmt_to_str(a.format))},
{"samplerate", SUB_PROP_INT(mp_aframe_get_rate(fmt))},
{"channel-count", SUB_PROP_INT(chmap.num)},
{"channels", SUB_PROP_STR(mp_chmap_to_str(&chmap))},
{"hr-channels", SUB_PROP_STR(mp_chmap_to_str_hr(&chmap))},
{"format", SUB_PROP_STR(af_fmt_to_str(mp_aframe_get_format(fmt)))},
{0}
};
@@ -2040,20 +2040,28 @@ static int mp_property_audio_params(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct mp_audio fmt = {0};
if (mpctx->ao_chain)
fmt = mpctx->ao_chain->input_format;
return property_audiofmt(fmt, action, arg);
return property_audiofmt(mpctx->ao_chain ? mpctx->ao_chain->input_format : NULL,
action, arg);
}
static int mp_property_audio_out_params(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct mp_audio fmt = {0};
if (mpctx->ao)
ao_get_format(mpctx->ao, &fmt);
return property_audiofmt(fmt, action, arg);
struct mp_aframe *frame = NULL;
if (mpctx->ao) {
frame = mp_aframe_create();
int samplerate;
int format;
struct mp_chmap channels;
ao_get_format(mpctx->ao, &samplerate, &format, &channels);
mp_aframe_set_rate(frame, samplerate);
mp_aframe_set_format(frame, format);
mp_aframe_set_chmap(frame, &channels);
}
int r = property_audiofmt(frame, action, arg);
talloc_free(frame);
return r;
}
/// Balance (RW)
@@ -2470,29 +2478,6 @@ static int mp_property_hwdec_interop(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, name);
}
#if HAVE_GPL
// Possibly GPL due to 7b25afd7423e9056782993cbd1b32ead64ac1462.
static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (!mpctx->vo_chain)
return mp_property_generic_option(mpctx, prop, action, arg);
switch (action) {
case M_PROPERTY_GET:
*(int *)arg = get_deinterlacing(mpctx) > 0;
return M_PROPERTY_OK;
case M_PROPERTY_GET_CONSTRICTED_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
case M_PROPERTY_SET:
set_deinterlacing(mpctx, *(int *)arg);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
}
#endif
/// Helper to set vo flags.
/** \ingroup PropertyImplHelper
*/
@@ -2602,35 +2587,6 @@ static int mp_property_frame_count(void *ctx, struct m_property *prop,
return m_property_int_ro(action, arg, frames);
}
#if HAVE_GPL
static int mp_property_video_color(void *ctx, struct m_property *prop,
int action, void *arg)
{
const char *name = prop->priv ? prop->priv : prop->name;
MPContext *mpctx = ctx;
if (!mpctx->vo_chain)
return mp_property_generic_option(mpctx, prop, action, arg);
switch (action) {
case M_PROPERTY_SET: {
if (video_set_colors(mpctx->vo_chain, name, *(int *) arg) <= 0)
return M_PROPERTY_UNAVAILABLE;
break;
}
case M_PROPERTY_GET:
if (video_get_colors(mpctx->vo_chain, name, (int *)arg) <= 0)
return M_PROPERTY_UNAVAILABLE;
// Write new value to option variable
mp_property_generic_option(mpctx, prop, M_PROPERTY_SET, arg);
return M_PROPERTY_OK;
case M_PROPERTY_GET_NEUTRAL:
*(int *)arg = 0;
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
}
#endif
/// Video codec tag (RO)
static int mp_property_video_format(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -2926,12 +2882,8 @@ static void get_frame_perf(struct mpv_node *node, struct mp_frame_perf *perf)
node_map_add(pass, "peak", MPV_FORMAT_INT64)->u.int64 = data->peak;
node_map_add(pass, "count", MPV_FORMAT_INT64)->u.int64 = data->count;
struct mpv_node *samples = node_map_add(pass, "samples", MPV_FORMAT_NODE_ARRAY);
int idx = data->index;
for (int n = 0; n < data->count; n++) {
node_array_add(samples, MPV_FORMAT_INT64)->u.int64 = data->samples[idx];
idx = (idx + 1) % PERF_SAMPLE_COUNT;
}
for (int n = 0; n < data->count; n++)
node_array_add(samples, MPV_FORMAT_INT64)->u.int64 = data->samples[n];
}
}
@@ -2961,19 +2913,21 @@ static int mp_property_vo_passes(void *ctx, struct m_property *prop,
return M_PROPERTY_OK;
}
struct voctrl_performance_data data = {0};
if (vo_control(mpctx->video_out, VOCTRL_PERFORMANCE_DATA, &data) <= 0)
return M_PROPERTY_UNAVAILABLE;
int ret = M_PROPERTY_UNAVAILABLE;
struct voctrl_performance_data *data = talloc_ptrtype(NULL, data);
if (vo_control(mpctx->video_out, VOCTRL_PERFORMANCE_DATA, data) <= 0)
goto out;
switch (action) {
case M_PROPERTY_PRINT: {
char *res = NULL;
res = talloc_asprintf_append(res, "fresh:\n");
res = asprint_perf(res, &data.fresh);
res = asprint_perf(res, &data->fresh);
res = talloc_asprintf_append(res, "\nredraw:\n");
res = asprint_perf(res, &data.redraw);
res = asprint_perf(res, &data->redraw);
*(char **)arg = res;
return M_PROPERTY_OK;
ret = M_PROPERTY_OK;
goto out;
}
case M_PROPERTY_GET: {
@@ -2981,14 +2935,19 @@ static int mp_property_vo_passes(void *ctx, struct m_property *prop,
node_init(&node, MPV_FORMAT_NODE_MAP, NULL);
struct mpv_node *fresh = node_map_add(&node, "fresh", MPV_FORMAT_NODE_ARRAY);
struct mpv_node *redraw = node_map_add(&node, "redraw", MPV_FORMAT_NODE_ARRAY);
get_frame_perf(fresh, &data.fresh);
get_frame_perf(redraw, &data.redraw);
get_frame_perf(fresh, &data->fresh);
get_frame_perf(redraw, &data->redraw);
*(struct mpv_node *)arg = node;
return M_PROPERTY_OK;
ret = M_PROPERTY_OK;
goto out;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
ret = M_PROPERTY_NOT_IMPLEMENTED;
out:
talloc_free(data);
return ret;
}
static int mp_property_vo(void *ctx, struct m_property *p, int action, void *arg)
@@ -4023,22 +3982,10 @@ static const struct m_property mp_properties_base[] = {
// Video
{"fullscreen", mp_property_fullscreen},
#if HAVE_GPL
{"deinterlace", mp_property_deinterlace},
#endif
{"taskbar-progress", mp_property_taskbar_progress},
{"ontop", mp_property_ontop},
{"border", mp_property_border},
{"on-all-workspaces", mp_property_all_workspaces},
#if HAVE_GPL
{"gamma", mp_property_video_color},
{"brightness", mp_property_video_color},
{"contrast", mp_property_video_color},
{"saturation", mp_property_video_color},
{"hue", mp_property_video_color},
{"video-output-levels", mp_property_video_color,
.priv = (void *)"output-levels"},
#endif
{"video-out-params", mp_property_vo_imgparams},
{"video-dec-params", mp_property_dec_imgparams},
{"video-params", mp_property_vd_imgparams},
@@ -4354,6 +4301,8 @@ static const struct property_osd_display {
int osd_progbar;
// Needs special ways to display the new value (seeks are delayed)
int seek_msg, seek_bar;
// Show a marker thing on OSD bar. Ignored if osd_progbar==0.
float marker;
// Free-form message (if NULL, osd_name or the property name is used)
const char *msg;
} property_osd_display[] = {
@@ -4368,10 +4317,10 @@ static const struct property_osd_display {
// audio
{ "volume", "Volume",
.msg = "Volume: ${?volume:${volume}% ${?mute==yes:(Muted)}}${!volume:${volume}}",
.osd_progbar = OSD_VOLUME },
.osd_progbar = OSD_VOLUME, .marker = 100 },
{ "ao-volume", "AO Volume",
.msg = "AO Volume: ${?ao-volume:${ao-volume}% ${?ao-mute==yes:(Muted)}}${!ao-volume:${ao-volume}}",
.osd_progbar = OSD_VOLUME },
.osd_progbar = OSD_VOLUME, .marker = 100 },
{ "mute", "Mute" },
{ "ao-mute", "AO Mute" },
{ "audio-delay", "A-V delay" },
@@ -4469,13 +4418,15 @@ static void show_property_osd(MPContext *mpctx, const char *name, int osd_mode)
if ((osd_mode & MP_ON_OSD_BAR) && (prop.flags & CONF_RANGE) == CONF_RANGE) {
if (prop.type == CONF_TYPE_INT) {
int n = prop.min;
mp_property_do(name, M_PROPERTY_GET_NEUTRAL, &n, mpctx);
if (disp.osd_progbar)
n = disp.marker;
int i;
if (mp_property_do(name, M_PROPERTY_GET, &i, mpctx) > 0)
set_osd_bar(mpctx, disp.osd_progbar, prop.min, prop.max, n, i);
} else if (prop.type == CONF_TYPE_FLOAT) {
float n = prop.min;
mp_property_do(name, M_PROPERTY_GET_NEUTRAL, &n, mpctx);
if (disp.osd_progbar)
n = disp.marker;
float f;
if (mp_property_do(name, M_PROPERTY_GET, &f, mpctx) > 0)
set_osd_bar(mpctx, disp.osd_progbar, prop.min, prop.max, n, f);
@@ -5194,28 +5145,6 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
break;
}
#if HAVE_GPL
// Potentially GPL due to 8d190244d21a4d40bb9e8f7d51aa09ca1888de09.
case MP_CMD_OSD: {
MP_WARN(mpctx, "The 'osd' command is deprecated. "
"Use 'cycle osd-level' instead.\n");
int v = cmd->args[0].v.i;
#define MAX_OSD_LEVEL 3
if (opts->osd_level > MAX_OSD_LEVEL)
opts->osd_level = MAX_OSD_LEVEL;
if (v < 0)
opts->osd_level = (opts->osd_level + 1) % (MAX_OSD_LEVEL + 1);
else
opts->osd_level = MPCLAMP(v, 0, MAX_OSD_LEVEL);
if (opts->osd_level > 0 && (on_osd & MP_ON_OSD_MSG))
set_osd_msg(mpctx, osdl, osd_duration, "OSD level: %d", opts->osd_level);
if (opts->osd_level == 0)
set_osd_msg(mpctx, 0, 0, "");
mp_wakeup_core(mpctx);
break;
}
#endif
case MP_CMD_PRINT_TEXT: {
MP_INFO(mpctx, "%s\n", cmd->args[0].v.s);
break;
@@ -5675,7 +5604,12 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
return -1;
}
const bool dbc = cmd->args[3].v.i;
button += dbc ? MP_MOUSE_BASE_DBL : MP_MOUSE_BASE;
if (dbc && button > (MP_MBTN_RIGHT - MP_MBTN_BASE)) {
MP_ERR(mpctx, "%d is not a valid mouse button for double-clicks.\n",
button);
return -1;
}
button += dbc ? MP_MBTN_DBL_BASE : MP_MBTN_BASE;
mp_input_set_mouse_pos_artificial(mpctx->input, x, y);
mp_input_put_key_artificial(mpctx->input, button);
break;
@@ -5850,7 +5784,7 @@ void mp_notify(struct MPContext *mpctx, int event, void *arg)
static void update_priority(struct MPContext *mpctx)
{
#if HAVE_WIN32_DESKTOP && HAVE_GPL
#if HAVE_WIN32_DESKTOP
struct MPOpts *opts = mpctx->opts;
if (opts->w32_priority > 0)
SetPriorityClass(GetCurrentProcess(), opts->w32_priority);
@@ -5865,13 +5799,8 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
if (flags & UPDATE_TERM)
mp_update_logging(mpctx, false);
if (mpctx->video_out) {
if (flags & UPDATE_VIDEOPOS)
vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
if (flags & UPDATE_RENDERER)
vo_control(mpctx->video_out, VOCTRL_UPDATE_RENDER_OPTS, NULL);
}
if (flags & UPDATE_DEINT)
recreate_auto_filters(mpctx);
if (flags & UPDATE_OSD) {
osd_changed(mpctx->osd);
@@ -5923,6 +5852,9 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
if (flags & UPDATE_VOL)
audio_update_volume(mpctx);
if (flags & UPDATE_LAVFI_COMPLEX)
update_lavfi_complex(mpctx);
}
void mp_notify_property(struct MPContext *mpctx, const char *property)
+21 -19
View File
@@ -28,7 +28,7 @@
#include "common/common.h"
#include "options/options.h"
#include "sub/osd.h"
#include "audio/audio.h"
#include "audio/aframe.h"
#include "video/mp_image.h"
#include "video/out/vo.h"
@@ -73,8 +73,8 @@ enum seek_type {
};
enum seek_precision {
MPSEEK_DEFAULT = 0,
// The following values are numerically sorted by increasing precision
MPSEEK_DEFAULT = 0,
MPSEEK_KEYFRAME,
MPSEEK_EXACT,
MPSEEK_VERY_EXACT,
@@ -85,6 +85,13 @@ enum seek_flags {
MPSEEK_FLAG_NOFLUSH = 1 << 1, // keeping remaining data for seamless loops
};
struct seek_params {
enum seek_type type;
enum seek_precision exact;
double amount;
unsigned flags; // MPSEEK_FLAG_*
};
enum video_sync {
VS_DEFAULT = 0,
VS_DISP_RESAMPLE,
@@ -198,10 +205,10 @@ struct ao_chain {
double ao_resume_time;
// 1-element input frame queue.
struct mp_audio *input_frame;
struct mp_aframe *input_frame;
// Last known input_mpi format (so vf can be reinitialized any time).
struct mp_audio input_format;
// Last known input_mpi format (so af can be reinitialized any time).
struct mp_aframe *input_format;
struct track *track;
struct lavfi_pad *filter_src;
@@ -306,7 +313,7 @@ typedef struct MPContext {
struct lavfi *lavfi;
struct ao *ao;
struct mp_audio *ao_decoder_fmt; // for weak gapless audio check
struct mp_aframe *ao_decoder_fmt; // for weak gapless audio check
struct ao_chain *ao_chain;
struct vo_chain *vo_chain;
@@ -339,6 +346,7 @@ typedef struct MPContext {
bool hrseek_lastframe; // drop everything until last frame reached
bool hrseek_backstep; // go to frame before seek target
double hrseek_pts;
struct seek_params current_seek;
bool ab_loop_clip; // clip to the "b" part of an A-B loop if available
// AV sync: the next frame should be shown when the audio out has this
// much (in seconds) buffered data left. Increased when more data is
@@ -396,13 +404,7 @@ typedef struct MPContext {
// Used to turn a new time value to a delta from last time.
int64_t last_time;
// Used to communicate the parameters of a seek between parts
struct seek_params {
enum seek_type type;
enum seek_precision exact;
double amount;
unsigned flags; // MPSEEK_FLAG_*
} seek;
struct seek_params seek;
// Allow audio to issue a second seek if audio is too far ahead (for non-hr
// seeks with external audio tracks).
@@ -478,7 +480,7 @@ void update_playback_speed(struct MPContext *mpctx);
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 reinit_audio_chain_src(struct MPContext *mpctx, struct track *track);
void audio_update_volume(struct MPContext *mpctx);
void audio_update_balance(struct MPContext *mpctx);
void reload_audio_output(struct MPContext *mpctx);
@@ -522,6 +524,7 @@ void prefetch_next(struct MPContext *mpctx);
void close_recorder(struct MPContext *mpctx);
void close_recorder_and_error(struct MPContext *mpctx);
void open_recorder(struct MPContext *mpctx, bool on_init);
void update_lavfi_complex(struct MPContext *mpctx);
// main.c
int mp_initialize(struct MPContext *mpctx, char **argv);
@@ -529,6 +532,7 @@ struct MPContext *mp_create(void);
void mp_destroy(struct MPContext *mpctx);
void mp_print_version(struct mp_log *log, int always);
void mp_update_logging(struct MPContext *mpctx, bool preinit);
void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec);
// misc.c
double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t);
@@ -608,11 +612,10 @@ bool update_subtitles(struct MPContext *mpctx, double video_pts);
// video.c
int video_get_colors(struct vo_chain *vo_c, const char *item, int *value);
int video_set_colors(struct vo_chain *vo_c, const char *item, int value);
int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data);
void reset_video_state(struct MPContext *mpctx);
int init_video_decoder(struct MPContext *mpctx, struct track *track);
int reinit_video_chain(struct MPContext *mpctx);
int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src);
void reinit_video_chain(struct MPContext *mpctx);
void reinit_video_chain_src(struct MPContext *mpctx, struct track *track);
int reinit_video_filters(struct MPContext *mpctx);
void write_video(struct MPContext *mpctx);
void mp_force_video_refresh(struct MPContext *mpctx);
@@ -620,8 +623,7 @@ 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, int opt_val);
void recreate_auto_filters(struct MPContext *mpctx);
// Values of MPOpts.softvol
enum {
+1 -1
View File
@@ -34,7 +34,7 @@
static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub",
"srt", "rt", "ssa", "ass", "mks", "vtt",
"sup", "scc",
"sup", "scc", "smi",
NULL};
static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
+51 -29
View File
@@ -37,7 +37,8 @@
#include "common/av_common.h"
#include "common/msg.h"
#include "audio/audio.h"
#include "audio/format.h"
#include "audio/aframe.h"
#include "video/mp_image.h"
#include "audio/fmt-conversion.h"
#include "video/fmt-conversion.h"
@@ -96,7 +97,7 @@ struct lavfi_pad {
// 1-frame queue (used for both input and output)
struct mp_image *pending_v;
struct mp_audio *pending_a;
struct mp_aframe *pending_a;
// -- dir==LAVFI_IN
@@ -107,7 +108,7 @@ struct lavfi_pad {
// used to check for format changes manually
struct mp_image *in_fmt_v;
struct mp_audio in_fmt_a;
struct mp_aframe *in_fmt_a;
// -- dir==LAVFI_OUT
@@ -199,7 +200,7 @@ static void free_graph(struct lavfi *c)
pad->filter_pad = -1;
pad->buffer = NULL;
TA_FREEP(&pad->in_fmt_v);
pad->in_fmt_a = (struct mp_audio){0};
TA_FREEP(&pad->in_fmt_a);
pad->buffer_is_eof = false;
pad->input_needed = false;
pad->input_waiting = false;
@@ -249,12 +250,19 @@ struct lavfi *lavfi_create(struct mp_log *log, char *graph_string)
void lavfi_destroy(struct lavfi *c)
{
if (!c)
return;
free_graph(c);
clear_data(c);
av_frame_free(&c->tmp_frame);
talloc_free(c);
}
const char *lavfi_get_graph(struct lavfi *c)
{
return c->graph_string;
}
struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name)
{
for (int n = 0; n < c->num_pads; n++) {
@@ -305,9 +313,14 @@ static void send_global_eof(struct lavfi *c)
// libavfilter allows changing some parameters on the fly, but not
// others.
static bool is_aformat_ok(struct mp_audio *a, struct mp_audio *b)
static bool is_aformat_ok(struct mp_aframe *a, struct mp_aframe *b)
{
return mp_audio_config_equals(a, b);
struct mp_chmap ca = {0}, cb = {0};
mp_aframe_get_chmap(a, &ca);
mp_aframe_get_chmap(b, &cb);
return mp_chmap_equals(&ca, &cb) &&
mp_aframe_get_rate(a) == mp_aframe_get_rate(b) &&
mp_aframe_get_format(a) == mp_aframe_get_format(b);
}
static bool is_vformat_ok(struct mp_image *a, struct mp_image *b)
{
@@ -324,9 +337,9 @@ static void check_format_changes(struct lavfi *c)
if (!pad->buffer || pad->dir != LAVFI_IN)
continue;
if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a.format) {
if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a) {
c->draining_new_format |= !is_aformat_ok(pad->pending_a,
&pad->in_fmt_a);
pad->in_fmt_a);
}
if (pad->type == STREAM_VIDEO && pad->pending_v && pad->in_fmt_v) {
c->draining_new_format |= !is_vformat_ok(pad->pending_v,
@@ -382,7 +395,10 @@ static bool init_pads(struct lavfi *c)
if (pad->pending_a) {
assert(pad->type == STREAM_AUDIO);
mp_audio_copy_config(&pad->in_fmt_a, pad->pending_a);
pad->in_fmt_a = mp_aframe_new_ref(pad->pending_a);
if (!pad->in_fmt_a)
goto error;
mp_aframe_unref_data(pad->in_fmt_a);
} else if (pad->pending_v) {
assert(pad->type == STREAM_VIDEO);
pad->in_fmt_v = mp_image_new_ref(pad->pending_v);
@@ -393,9 +409,11 @@ static bool init_pads(struct lavfi *c)
// libavfilter makes this painful. Init it with a dummy config,
// just so we can tell it the stream is EOF.
if (pad->type == STREAM_AUDIO) {
mp_audio_set_format(&pad->in_fmt_a, AF_FORMAT_FLOAT);
mp_audio_set_num_channels(&pad->in_fmt_a, 2);
pad->in_fmt_a.rate = 48000;
pad->in_fmt_a = mp_aframe_create();
mp_aframe_set_format(pad->in_fmt_a, AF_FORMAT_FLOAT);
mp_aframe_set_chmap(pad->in_fmt_a,
&(struct mp_chmap)MP_CHMAP_INIT_STEREO);
mp_aframe_set_rate(pad->in_fmt_a, 48000);
} else if (pad->type == STREAM_VIDEO) {
pad->in_fmt_v = talloc_zero(NULL, struct mp_image);
mp_image_setfmt(pad->in_fmt_v, IMGFMT_420P);
@@ -414,11 +432,13 @@ static bool init_pads(struct lavfi *c)
char *filter_name = NULL;
if (pad->type == STREAM_AUDIO) {
params->time_base = pad->timebase =
(AVRational){1, pad->in_fmt_a.rate};
params->format = af_to_avformat(pad->in_fmt_a.format);
params->sample_rate = pad->in_fmt_a.rate;
params->channel_layout =
mp_chmap_to_lavc(&pad->in_fmt_a.channels);
(AVRational){1, mp_aframe_get_rate(pad->in_fmt_a)};
params->format =
af_to_avformat(mp_aframe_get_format(pad->in_fmt_a));
params->sample_rate = mp_aframe_get_rate(pad->in_fmt_a);
struct mp_chmap chmap = {0};
mp_aframe_get_chmap(pad->in_fmt_a, &chmap);
params->channel_layout = mp_chmap_to_lavc(&chmap);
filter_name = "abuffer";
} else if (pad->type == STREAM_VIDEO) {
params->time_base = pad->timebase = AV_TIME_BASE_Q;
@@ -484,9 +504,11 @@ static void dump_graph(struct lavfi *c)
#endif
}
void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs)
void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad,
struct mp_hwdec_devices *hwdevs)
{
c->hwdec_devs = hwdevs;
// We don't actually treat this per-pad.
pad->main->hwdec_devs = hwdevs;
}
// Initialize the graph if all inputs have formats set. If it's already
@@ -556,8 +578,8 @@ static void feed_input_pads(struct lavfi *c)
frame = mp_image_to_av_frame_and_unref(pad->pending_v);
pad->pending_v = NULL;
} else if (pad->pending_a) {
pts = pad->pending_a->pts;
frame = mp_audio_to_avframe_and_unref(pad->pending_a);
pts = mp_aframe_get_pts(pad->pending_a);
frame = mp_aframe_to_avframe_and_unref(pad->pending_a);
pad->pending_a = NULL;
} else {
if (!pad->input_eof) {
@@ -612,9 +634,9 @@ static void read_output_pads(struct lavfi *c)
pad->output_needed = false;
double pts = mp_pts_from_av(c->tmp_frame->pts, &pad->timebase);
if (pad->type == STREAM_AUDIO) {
pad->pending_a = mp_audio_from_avframe(c->tmp_frame);
pad->pending_a = mp_aframe_from_avframe(c->tmp_frame);
if (pad->pending_a)
pad->pending_a->pts = pts;
mp_aframe_set_pts(pad->pending_a, pts);
} else if (pad->type == STREAM_VIDEO) {
pad->pending_v = mp_image_from_av_frame(c->tmp_frame);
if (pad->pending_v)
@@ -716,15 +738,15 @@ static int lavfi_request_frame(struct lavfi_pad *pad)
} else if (pad->main->all_waiting) {
return DATA_WAIT;
}
return DATA_AGAIN;
return DATA_STARVE;
}
// Try to read a new frame from an output pad. Returns one of the following:
// DATA_OK: a frame is returned
// DATA_AGAIN: needs more input data
// DATA_STARVE: needs more input data
// DATA_WAIT: needs more input data, and all inputs in LAVFI_WAIT state
// DATA_EOF: no more data
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe)
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe)
{
int r = lavfi_request_frame(pad);
*out_aframe = pad->pending_a;
@@ -750,7 +772,7 @@ bool lavfi_needs_input(struct lavfi_pad *pad)
// A filter user is supposed to call lavfi_needs_input(), and if that returns
// true, send either a new status or a frame. A status can be one of:
// DATA_AGAIN: a new frame/status will come, caller will retry
// DATA_STARVE: a new frame/status will come, caller will retry
// DATA_WAIT: a new frame/status will come, but caller goes to sleep
// DATA_EOF: no more input possible (in near time)
// If you have a new frame, use lavfi_send_frame_ instead.
@@ -764,7 +786,7 @@ void lavfi_send_status(struct lavfi_pad *pad, int status)
assert(!pad->pending_v && !pad->pending_a);
pad->input_waiting = status == DATA_WAIT || status == DATA_EOF;
pad->input_again = status == DATA_AGAIN;
pad->input_again = status == DATA_STARVE;
pad->input_eof = status == DATA_EOF;
}
@@ -778,7 +800,7 @@ static void lavfi_sent_frame(struct lavfi_pad *pad)
}
// See lavfi_send_status() for remarks.
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe)
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe)
{
assert(pad->type == STREAM_AUDIO);
assert(!pad->pending_a);
+6 -4
View File
@@ -5,7 +5,7 @@ struct mp_log;
struct lavfi;
struct lavfi_pad;
struct mp_image;
struct mp_audio;
struct mp_aframe;
enum lavfi_direction {
LAVFI_IN = 1,
@@ -13,6 +13,7 @@ enum lavfi_direction {
};
struct lavfi *lavfi_create(struct mp_log *log, char *graph_string);
const char *lavfi_get_graph(struct lavfi *c);
void lavfi_destroy(struct lavfi *c);
struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name);
enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad);
@@ -22,12 +23,13 @@ bool lavfi_get_connected(struct lavfi_pad *pad);
bool lavfi_process(struct lavfi *c);
bool lavfi_has_failed(struct lavfi *c);
void lavfi_seek_reset(struct lavfi *c);
void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs);
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe);
void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad,
struct mp_hwdec_devices *hwdevs);
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe);
int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe);
bool lavfi_needs_input(struct lavfi_pad *pad);
void lavfi_send_status(struct lavfi_pad *pad, int status);
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe);
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe);
void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe);
#endif
+126 -53
View File
@@ -43,8 +43,6 @@
#include "common/recorder.h"
#include "input/input.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/decode/dec_audio.h"
#include "audio/out/ao.h"
#include "demux/demux.h"
@@ -945,21 +943,83 @@ void prefetch_next(struct MPContext *mpctx)
}
}
static bool init_complex_filters(struct MPContext *mpctx)
// Destroy the complex filter, and remove the references to the filter pads.
// (Call cleanup_deassociated_complex_filters() to close decoders/VO/AO
// that are not connected anymore due to this.)
static void deassociate_complex_filters(struct MPContext *mpctx)
{
assert(!mpctx->lavfi);
for (int n = 0; n < mpctx->num_tracks; n++)
mpctx->tracks[n]->sink = NULL;
if (mpctx->vo_chain)
mpctx->vo_chain->filter_src = NULL;
if (mpctx->ao_chain)
mpctx->ao_chain->filter_src = NULL;
lavfi_destroy(mpctx->lavfi);
mpctx->lavfi = NULL;
}
// Close all decoders and sinks (AO/VO) that are not connected to either
// a track or a filter pad.
static void cleanup_deassociated_complex_filters(struct MPContext *mpctx)
{
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (!(track->sink || track->vo_c || track->ao_c)) {
if (track->d_video && !track->vo_c) {
video_uninit(track->d_video);
track->d_video = NULL;
}
if (track->d_audio && !track->ao_c) {
audio_uninit(track->d_audio);
track->d_audio = NULL;
}
track->selected = false;
}
}
if (mpctx->vo_chain && !mpctx->vo_chain->video_src &&
!mpctx->vo_chain->filter_src)
{
uninit_video_chain(mpctx);
}
if (mpctx->ao_chain && !mpctx->ao_chain->audio_src &&
!mpctx->ao_chain->filter_src)
{
uninit_audio_chain(mpctx);
}
}
// >0: changed, 0: no change, -1: error
static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit)
{
char *graph = mpctx->opts->lavfi_complex;
bool have_graph = graph && graph[0] && !force_uninit;
if (have_graph && mpctx->lavfi &&
strcmp(graph, lavfi_get_graph(mpctx->lavfi)) == 0 &&
!lavfi_has_failed(mpctx->lavfi))
return 0;
if (!mpctx->lavfi && !have_graph)
return 0;
if (!graph || !graph[0])
return true;
// Deassociate the old filter pads. We leave both sources (tracks) and
// sinks (AO/VO) "dangling", connected to neither track or filter pad.
// Later, we either reassociate them with new pads, or uninit them if
// they are still dangling. This avoids too interruptive actions like
// recreating the VO.
deassociate_complex_filters(mpctx);
bool success = false;
if (!have_graph) {
success = true; // normal full removal of graph
goto done;
}
mpctx->lavfi = lavfi_create(mpctx->log, graph);
if (!mpctx->lavfi)
return false;
goto done;
if (lavfi_has_failed(mpctx->lavfi))
return false;
goto done;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
@@ -983,6 +1043,12 @@ static bool init_complex_filters(struct MPContext *mpctx)
if (lavfi_get_connected(pad))
continue;
assert(!track->sink);
if (track->vo_c || track->ao_c) {
MP_ERR(mpctx, "Pad %s tries to connect to already selected track.\n",
label);
goto done;
}
track->sink = pad;
lavfi_set_connected(pad, true);
track->selected = true;
@@ -992,66 +1058,76 @@ static bool init_complex_filters(struct MPContext *mpctx)
if (pad && lavfi_pad_type(pad) == STREAM_VIDEO &&
lavfi_pad_direction(pad) == LAVFI_OUT)
{
if (mpctx->vo_chain) {
if (mpctx->vo_chain->video_src) {
MP_ERR(mpctx, "Pad vo tries to connected to already used VO.\n");
goto done;
}
} else {
reinit_video_chain_src(mpctx, NULL);
if (!mpctx->vo_chain)
goto done;
}
lavfi_set_connected(pad, true);
reinit_video_chain_src(mpctx, pad);
struct vo_chain *vo_c = mpctx->vo_chain;
vo_c->filter_src = pad;
lavfi_pad_set_hwdec_devs(vo_c->filter_src, vo_c->hwdec_devs);
}
pad = lavfi_find_pad(mpctx->lavfi, "ao");
if (pad && lavfi_pad_type(pad) == STREAM_AUDIO &&
lavfi_pad_direction(pad) == LAVFI_OUT)
{
if (mpctx->ao_chain) {
if (mpctx->ao_chain->audio_src) {
MP_ERR(mpctx, "Pad ao tries to connected to already used AO.\n");
goto done;
}
} else {
reinit_audio_chain_src(mpctx, NULL);
if (!mpctx->ao_chain)
goto done;
}
lavfi_set_connected(pad, true);
reinit_audio_chain_src(mpctx, pad);
mpctx->ao_chain->filter_src = pad;
}
return true;
}
static bool init_complex_filter_decoders(struct MPContext *mpctx)
{
if (!mpctx->lavfi)
return true;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->sink && track->type == STREAM_VIDEO) {
if (!init_video_decoder(mpctx, track))
return false;
if (!track->d_video && !init_video_decoder(mpctx, track))
goto done;
}
if (track->sink && track->type == STREAM_AUDIO) {
if (!init_audio_decoder(mpctx, track))
return false;
if (!track->d_audio && !init_audio_decoder(mpctx, track))
goto done;
}
}
return true;
success = true;
done:
if (!success)
deassociate_complex_filters(mpctx);
cleanup_deassociated_complex_filters(mpctx);
if (mpctx->playback_initialized) {
for (int n = 0; n < mpctx->num_tracks; n++)
reselect_demux_stream(mpctx, mpctx->tracks[n]);
}
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
return success ? 1 : -1;
}
static void uninit_complex_filters(struct MPContext *mpctx)
void update_lavfi_complex(struct MPContext *mpctx)
{
if (!mpctx->lavfi)
return;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->d_video && !track->vo_c) {
video_uninit(track->d_video);
track->d_video = NULL;
}
if (track->d_audio && !track->ao_c) {
audio_uninit(track->d_audio);
track->d_audio = NULL;
}
if (mpctx->playback_initialized) {
if (reinit_complex_filters(mpctx, false) != 0)
issue_refresh_seek(mpctx, MPSEEK_EXACT);
}
if (mpctx->vo_chain && mpctx->vo_chain->filter_src)
uninit_video_chain(mpctx);
if (mpctx->ao_chain && mpctx->ao_chain->filter_src)
uninit_audio_chain(mpctx);
lavfi_destroy(mpctx->lavfi);
mpctx->lavfi = NULL;
}
// Start playing the current playlist entry.
@@ -1176,7 +1252,7 @@ reopen_file:
if (process_preloaded_hooks(mpctx))
goto terminate_playback;
if (!init_complex_filters(mpctx))
if (reinit_complex_filters(mpctx, false) < 0)
goto terminate_playback;
assert(NUM_PTRACKS == 2); // opts->stream_id is hardcoded to 2
@@ -1185,7 +1261,7 @@ reopen_file:
struct track *sel = NULL;
bool taken = (t == STREAM_VIDEO && mpctx->vo_chain) ||
(t == STREAM_AUDIO && mpctx->ao_chain);
if (!taken)
if (!taken && opts->stream_auto_sel)
sel = select_default_track(mpctx, i, t);
mpctx->current_track[i][t] = sel;
}
@@ -1223,14 +1299,11 @@ reopen_file:
update_playback_speed(mpctx);
if (!init_complex_filter_decoders(mpctx))
goto terminate_playback;
reinit_video_chain(mpctx);
reinit_audio_chain(mpctx);
reinit_sub_all(mpctx);
if (!mpctx->vo_chain && !mpctx->ao_chain) {
if (!mpctx->vo_chain && !mpctx->ao_chain && opts->stream_auto_sel) {
MP_FATAL(mpctx, "No video or audio streams selected.\n");
mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
goto terminate_playback;
@@ -1307,7 +1380,7 @@ terminate_playback:
close_recorder(mpctx);
// time to uninit all, except global stuff:
uninit_complex_filters(mpctx);
reinit_complex_filters(mpctx, true);
uninit_audio_chain(mpctx);
uninit_video_chain(mpctx);
uninit_sub_all(mpctx);
+46 -48
View File
@@ -1000,7 +1000,7 @@ layouts["box"] = function ()
lo.style = osc_styles.smallButtonsR
lo = add_layout("volume")
lo.geometry =
lo.geometry =
{x = posX+pos_offsetX - (25 * 2) - osc_geo.p,
y = bigbtnrowY, an = 4, w = 25, h = 25}
lo.style = osc_styles.smallButtonsR
@@ -1550,7 +1550,7 @@ function osc_init()
return not (title == "") and title or "mpv"
end
ne.eventresponder["mouse_btn0_up"] = function ()
ne.eventresponder["mbtn_left_up"] = function ()
local title = mp.get_property_osd("media-title")
if (have_pl) then
title = string.format("[%d/%d] %s", countone(pl_pos - 1),
@@ -1559,7 +1559,7 @@ function osc_init()
show_message(title)
end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () show_message(mp.get_property_osd("filename")) end
-- playlist buttons
@@ -1569,14 +1569,14 @@ function osc_init()
ne.content = "\238\132\144"
ne.enabled = (pl_pos > 1) or (loop ~= "no")
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function ()
mp.commandv("playlist-prev", "weak")
show_message(get_playlist(), 3)
end
ne.eventresponder["shift+mouse_btn0_up"] =
ne.eventresponder["shift+mbtn_left_up"] =
function () show_message(get_playlist(), 3) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () show_message(get_playlist(), 3) end
--next
@@ -1584,14 +1584,14 @@ function osc_init()
ne.content = "\238\132\129"
ne.enabled = (have_pl and (pl_pos < pl_count)) or (loop ~= "no")
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function ()
mp.commandv("playlist-next", "weak")
show_message(get_playlist(), 3)
end
ne.eventresponder["shift+mouse_btn0_up"] =
ne.eventresponder["shift+mbtn_left_up"] =
function () show_message(get_playlist(), 3) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () show_message(get_playlist(), 3) end
@@ -1607,7 +1607,7 @@ function osc_init()
return ("\238\128\130")
end
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () mp.commandv("cycle", "pause") end
--skipback
@@ -1615,11 +1615,11 @@ function osc_init()
ne.softrepeat = true
ne.content = "\238\128\132"
ne.eventresponder["mouse_btn0_down"] =
ne.eventresponder["mbtn_left_down"] =
function () mp.commandv("seek", -5, "relative", "keyframes") end
ne.eventresponder["shift+mouse_btn0_down"] =
ne.eventresponder["shift+mbtn_left_down"] =
function () mp.commandv("frame-back-step") end
ne.eventresponder["mouse_btn2_down"] =
ne.eventresponder["mbtn_right_down"] =
function () mp.commandv("seek", -30, "relative", "keyframes") end
--skipfrwd
@@ -1627,11 +1627,11 @@ function osc_init()
ne.softrepeat = true
ne.content = "\238\128\133"
ne.eventresponder["mouse_btn0_down"] =
ne.eventresponder["mbtn_left_down"] =
function () mp.commandv("seek", 10, "relative", "keyframes") end
ne.eventresponder["shift+mouse_btn0_down"] =
ne.eventresponder["shift+mbtn_left_down"] =
function () mp.commandv("frame-step") end
ne.eventresponder["mouse_btn2_down"] =
ne.eventresponder["mbtn_right_down"] =
function () mp.commandv("seek", 60, "relative", "keyframes") end
--ch_prev
@@ -1639,14 +1639,14 @@ function osc_init()
ne.enabled = have_ch
ne.content = "\238\132\132"
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function ()
mp.commandv("add", "chapter", -1)
show_message(get_chapterlist(), 3)
end
ne.eventresponder["shift+mouse_btn0_up"] =
ne.eventresponder["shift+mbtn_left_up"] =
function () show_message(get_chapterlist(), 3) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () show_message(get_chapterlist(), 3) end
--ch_next
@@ -1654,14 +1654,14 @@ function osc_init()
ne.enabled = have_ch
ne.content = "\238\132\133"
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function ()
mp.commandv("add", "chapter", 1)
show_message(get_chapterlist(), 3)
end
ne.eventresponder["shift+mouse_btn0_up"] =
ne.eventresponder["shift+mbtn_left_up"] =
function () show_message(get_chapterlist(), 3) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () show_message(get_chapterlist(), 3) end
--
@@ -1679,11 +1679,11 @@ function osc_init()
return ("\238\132\134" .. osc_styles.smallButtonsLlabel
.. " " .. aid .. "/" .. #tracks_osc.audio)
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () set_track("audio", 1) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () set_track("audio", -1) end
ne.eventresponder["shift+mouse_btn0_down"] =
ne.eventresponder["shift+mbtn_left_down"] =
function () show_message(get_tracklist("audio"), 2) end
--cy_sub
@@ -1698,11 +1698,11 @@ function osc_init()
return ("\238\132\135" .. osc_styles.smallButtonsLlabel
.. " " .. sid .. "/" .. #tracks_osc.sub)
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () set_track("sub", 1) end
ne.eventresponder["mouse_btn2_up"] =
ne.eventresponder["mbtn_right_up"] =
function () set_track("sub", -1) end
ne.eventresponder["shift+mouse_btn0_down"] =
ne.eventresponder["shift+mbtn_left_down"] =
function () show_message(get_tracklist("sub"), 2) end
--tog_fs
@@ -1714,7 +1714,7 @@ function osc_init()
return ("\238\132\136")
end
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () mp.commandv("cycle", "fullscreen") end
@@ -1760,7 +1760,7 @@ function osc_init()
end
end
ne.eventresponder["mouse_btn0_down"] = --exact seeks on single clicks
ne.eventresponder["mbtn_left_down"] = --exact seeks on single clicks
function (element) mp.commandv("seek", get_slider_value(element),
"absolute-percent", "exact") end
ne.eventresponder["reset"] =
@@ -1777,7 +1777,7 @@ function osc_init()
return (mp.get_property_osd("playback-time"))
end
end
ne.eventresponder["mouse_btn0_up"] = function ()
ne.eventresponder["mbtn_left_up"] = function ()
state.tc_ms = not state.tc_ms
request_init()
end
@@ -1801,7 +1801,7 @@ function osc_init()
end
end
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () state.rightTC_trem = not state.rightTC_trem end
-- cache
@@ -1848,12 +1848,12 @@ function osc_init()
return volicon[math.min(4,math.ceil(volume / (100/3)))]
end
end
ne.eventresponder["mouse_btn0_up"] =
ne.eventresponder["mbtn_left_up"] =
function () mp.commandv("cycle", "mute") end
ne.eventresponder["mouse_wheel_up_press"] =
ne.eventresponder["wheel_up_press"] =
function () mp.commandv("osd-auto", "add", "volume", 5) end
ne.eventresponder["mouse_wheel_down_press"] =
ne.eventresponder["wheel_down_press"] =
function () mp.commandv("osd-auto", "add", "volume", -5) end
@@ -2296,19 +2296,17 @@ do_enable_keybindings()
--mouse input bindings
mp.set_key_bindings({
{"mouse_btn0", function(e) process_event("mouse_btn0", "up") end,
function(e) process_event("mouse_btn0", "down") end},
{"shift+mouse_btn0", function(e) process_event("shift+mouse_btn0", "up") end,
function(e) process_event("shift+mouse_btn0", "down") end},
{"mouse_btn2", function(e) process_event("mouse_btn2", "up") end,
function(e) process_event("mouse_btn2", "down") end},
{"mouse_btn3", function(e) process_event("mouse_wheel_up", "press") end},
{"mouse_btn4", function(e) process_event("mouse_wheel_down", "press") end},
{"axis_up", function(e) process_event("mouse_wheel_up", "press") end},
{"axis_down", function(e) process_event("mouse_wheel_down", "press") end},
{"mouse_btn0_dbl", "ignore"},
{"shift+mouse_btn0_dbl", "ignore"},
{"mouse_btn2_dbl", "ignore"},
{"mbtn_left", function(e) process_event("mbtn_left", "up") end,
function(e) process_event("mbtn_left", "down") end},
{"shift+mbtn_left", function(e) process_event("shift+mbtn_left", "up") end,
function(e) process_event("shift+mbtn_left", "down") end},
{"mbtn_right", function(e) process_event("mbtn_right", "up") end,
function(e) process_event("mbtn_right", "down") end},
{"wheel_up", function(e) process_event("wheel_up", "press") end},
{"wheel_down", function(e) process_event("wheel_down", "press") end},
{"mbtn_left_dbl", "ignore"},
{"shift+mbtn_left_dbl", "ignore"},
{"mbtn_right_dbl", "ignore"},
}, "input", "force")
mp.enable_key_bindings("input")
+36 -5
View File
@@ -122,7 +122,37 @@ local function is_blacklisted(url)
return false
end
local function edl_track_joined(fragments, protocol, is_live)
local function make_absolute_url(base_url, url)
if url:find("https?://") == 1 then return url end
local proto, domain, rest =
base_url:match("(https?://)([^/]+/)(.*)/?")
local segs = {}
rest:gsub("([^/]+)", function(c) table.insert(segs, c) end)
url:gsub("([^/]+)", function(c) table.insert(segs, c) end)
local resolved_url = {}
for i, v in ipairs(segs) do
if v == ".." then
table.remove(resolved_url)
elseif v ~= "." then
table.insert(resolved_url, v)
end
end
return proto .. domain ..
table.concat(resolved_url, "/")
end
local function join_url(base_url, fragment)
local res = ""
if base_url and fragment.path then
res = make_absolute_url(base_url, fragment.path)
elseif fragment.url then
res = fragment.url
end
return res
end
local function edl_track_joined(fragments, protocol, is_live, base)
if not (type(fragments) == "table") or not fragments[1] then
msg.debug("No fragments to join into EDL")
return nil
@@ -136,7 +166,7 @@ local function edl_track_joined(fragments, protocol, is_live)
not fragments[1].duration and not is_live then
-- assume MP4 DASH initialization segment
table.insert(parts,
"!mp4_dash,init=" .. edl_escape(fragments[1].url))
"!mp4_dash,init=" .. edl_escape(join_url(base, fragments[1])))
offset = 2
-- Check remaining fragments for duration;
@@ -152,7 +182,7 @@ local function edl_track_joined(fragments, protocol, is_live)
for i = offset, #fragments do
local fragment = fragments[i]
table.insert(parts, edl_escape(fragment.url))
table.insert(parts, edl_escape(join_url(base, fragment)))
if fragment.duration then
parts[#parts] =
parts[#parts] .. ",length="..fragment.duration
@@ -169,7 +199,8 @@ local function add_single_video(json)
for _, track in pairs(json.requested_formats) do
local edl_track = nil
edl_track = edl_track_joined(track.fragments,
track.protocol, json.is_live)
track.protocol, json.is_live,
track.fragment_base_url)
if track.acodec and track.acodec ~= "none" then
-- audio track
mp.commandv("audio-add",
@@ -184,7 +215,7 @@ local function add_single_video(json)
elseif not (json.url == nil) then
local edl_track = nil
edl_track = edl_track_joined(json.fragments, json.protocol,
json.is_live)
json.is_live, json.fragment_base_url)
-- normal video or single track
streamurl = edl_track or json.url
+19
View File
@@ -111,6 +111,25 @@ double get_track_seek_offset(struct MPContext *mpctx, struct track *track)
return 0;
}
void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec)
{
// let queued seeks execute at a slightly later point
if (mpctx->seek.type) {
mp_wakeup_core(mpctx);
return;
}
// repeat currently ongoing seeks
if (mpctx->current_seek.type) {
mpctx->seek = mpctx->current_seek;
mp_wakeup_core(mpctx);
return;
}
// maybe happens when changing filters while file is loaded - ignore for now
if (mpctx->playback_pts == MP_NOPTS_VALUE)
return;
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->playback_pts, min_prec, 0);
}
float mp_get_cache_percent(struct MPContext *mpctx)
{
struct stream_cache_info info = {0};
+5 -1
View File
@@ -232,6 +232,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->hrseek_framedrop = false;
mpctx->hrseek_lastframe = false;
mpctx->hrseek_backstep = false;
mpctx->current_seek = (struct seek_params){0};
mpctx->playback_pts = MP_NOPTS_VALUE;
mpctx->last_seek_pts = MP_NOPTS_VALUE;
mpctx->cache_wait_time = 0;
@@ -376,6 +377,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
!hr_seek && !(demux_flags & SEEK_FORWARD);
mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1];
mpctx->current_seek = seek;
}
// This combines consecutive seek requests.
@@ -989,6 +992,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
if (!mpctx->restart_complete) {
mpctx->hrseek_active = false;
mpctx->restart_complete = true;
mpctx->current_seek = (struct seek_params){0};
mpctx->audio_allow_second_chance_seek = false;
handle_playback_time(mpctx);
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
@@ -1049,7 +1053,7 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx)
continue;
if (track->d_audio) {
audio_work(track->d_audio);
struct mp_audio *fr;
struct mp_aframe *fr;
int res = audio_get_frame(track->d_audio, &fr);
if (res == DATA_OK) {
lavfi_send_frame_a(track->sink, fr);
+28 -165
View File
@@ -13,8 +13,6 @@
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*
* Parts under HAVE_GPL are licensed under GNU General Public License.
*/
#include <stddef.h>
@@ -70,69 +68,6 @@ static const char av_desync_help_text[] =
"position will not match to the video (see A-V status field).\n"
"\n";
#if HAVE_GPL
int video_set_colors(struct vo_chain *vo_c, const char *item, int value)
{
vf_equalizer_t data;
data.item = item;
data.value = value;
MP_VERBOSE(vo_c, "set video colors %s=%d \n", item, value);
if (video_vf_vo_control(vo_c, VFCTRL_SET_EQUALIZER, &data) == CONTROL_TRUE)
return 1;
MP_VERBOSE(vo_c, "Video attribute '%s' is not supported by selected vo.\n",
item);
return 0;
}
int video_get_colors(struct vo_chain *vo_c, const char *item, int *value)
{
vf_equalizer_t data;
data.item = item;
MP_VERBOSE(vo_c, "get video colors %s \n", item);
if (video_vf_vo_control(vo_c, VFCTRL_GET_EQUALIZER, &data) == CONTROL_TRUE) {
*value = data.value;
return 1;
}
return 0;
}
#endif
// Send a VCTRL, or if it doesn't work, translate it to a VOCTRL and try the VO.
int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data)
{
if (vo_c->vf->initialized > 0) {
int r = vf_control_any(vo_c->vf, vf_cmd, data);
if (r != CONTROL_UNKNOWN)
return r;
}
switch (vf_cmd) {
case VFCTRL_SET_EQUALIZER: {
vf_equalizer_t *eq = data;
if (!vo_c->vo->config_ok)
return CONTROL_FALSE; // vo not configured?
struct voctrl_set_equalizer_args param = {
eq->item, eq->value
};
return vo_control(vo_c->vo, VOCTRL_SET_EQUALIZER, &param) == VO_TRUE;
}
case VFCTRL_GET_EQUALIZER: {
vf_equalizer_t *eq = data;
if (!vo_c->vo->config_ok)
return CONTROL_FALSE; // vo not configured?
struct voctrl_get_equalizer_args param = {
eq->item, &eq->value
};
return vo_control(vo_c->vo, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
}
}
return CONTROL_UNKNOWN;
}
static void set_allowed_vo_formats(struct vo_chain *vo_c)
{
vo_query_formats(vo_c->vo, vo_c->vf->allowed_output_formats);
@@ -162,16 +97,6 @@ static bool check_output_format(struct vo_chain *vo_c, int imgfmt)
static int probe_deint_filters(struct vo_chain *vo_c)
{
#if HAVE_GPL
// Usually, we prefer inserting/removing deint filters. But If there's VO
// support, or the user inserted a filter that supports swichting deint and
// that has no VF_DEINTERLACE_LABEL, or if the filter was auto-inserted
// for other reasons and supports switching deint (like vf_d3d11vpp), then
// use the runtime switching method.
if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &(int){1}) == CONTROL_OK)
return 0;
#endif
if (check_output_format(vo_c, IMGFMT_VDPAU)) {
char *args[5] = {"deint", "yes"};
int pref = 0;
@@ -216,12 +141,6 @@ static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
return;
}
#if HAVE_GPL
// Make sure to reset this even if runtime deint switching is used.
if (mpctx->opts->deinterlace >= 0)
video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &(int){0});
#endif
if (params.rotate) {
if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90) || params.rotate % 90) {
// Try to insert a rotation filter.
@@ -242,12 +161,15 @@ static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
}
}
if (mpctx->opts->deinterlace == 1)
if (mpctx->opts->deinterlace)
probe_deint_filters(vo_c);
}
static void recreate_auto_filters(struct MPContext *mpctx)
void recreate_auto_filters(struct MPContext *mpctx)
{
if (!mpctx->vo_chain)
return;
filter_reconfig(mpctx, mpctx->vo_chain);
mp_force_video_refresh(mpctx);
@@ -255,34 +177,6 @@ static void recreate_auto_filters(struct MPContext *mpctx)
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
int get_deinterlacing(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
int enabled = 0;
#if HAVE_GPL
if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
enabled = -1;
#endif
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 set_deinterlacing(struct MPContext *mpctx, int opt_val)
{
if ((opt_val < 0 && mpctx->opts->deinterlace == opt_val) ||
(opt_val == (get_deinterlacing(mpctx) > 0)))
return;
mpctx->opts->deinterlace = opt_val;
recreate_auto_filters(mpctx);
if (opt_val >= 0)
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
}
static void recreate_video_filters(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
@@ -425,6 +319,7 @@ int init_video_decoder(struct MPContext *mpctx, struct track *track)
d_video->header = track->stream;
d_video->codec = track->stream->codec;
d_video->fps = d_video->header->codec->fps;
d_video->vo = mpctx->vo_chain->vo;
// Note: at least mpv_opengl_cb_uninit_gl() relies on being able to get
// rid of all references to the VO by destroying the VO chain. Thus,
@@ -455,23 +350,19 @@ err_out:
return 0;
}
int reinit_video_chain(struct MPContext *mpctx)
void reinit_video_chain(struct MPContext *mpctx)
{
return reinit_video_chain_src(mpctx, NULL);
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
if (!track || !track->stream) {
error_on_track(mpctx, track);
return;
}
reinit_video_chain_src(mpctx, track);
}
int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
// (track=NULL creates a blank chain, used for lavfi-complex)
void reinit_video_chain_src(struct MPContext *mpctx, struct track *track)
{
struct track *track = NULL;
struct sh_stream *sh = NULL;
if (!src) {
track = mpctx->current_track[0][STREAM_VIDEO];
if (!track)
return 0;
sh = track->stream;
if (!sh)
goto no_video;
}
assert(!mpctx->vo_chain);
if (!mpctx->video_out) {
@@ -503,11 +394,7 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
vo_c->hwdec_devs = vo_c->vo->hwdec_devs;
if (mpctx->lavfi)
lavfi_set_hwdec_devs(mpctx->lavfi, vo_c->hwdec_devs);
vo_c->filter_src = src;
if (!vo_c->filter_src) {
if (track) {
vo_c->track = track;
track->vo_c = vo_c;
if (!init_video_decoder(mpctx, track))
@@ -515,7 +402,7 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
vo_c->video_src = track->d_video;
vo_c->container_fps = vo_c->video_src->fps;
vo_c->is_coverart = !!sh->attached_picture;
vo_c->is_coverart = !!track->stream->attached_picture;
track->vo_c = vo_c;
vo_c->track = track;
@@ -539,14 +426,12 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
reset_video_state(mpctx);
reset_subtitle_state(mpctx);
return 1;
return;
err_out:
no_video:
uninit_video_chain(mpctx);
error_on_track(mpctx, track);
handle_force_window(mpctx, true);
return 0;
}
// Try to refresh the video by doing a precise seek to the currently displayed
@@ -560,13 +445,10 @@ void mp_force_video_refresh(struct MPContext *mpctx)
return;
// If not paused, the next frame should come soon enough.
if ((opts->pause || mpctx->time_frame >= 0.5) &&
(mpctx->video_status >= STATUS_PLAYING ||
mpctx->video_status <= STATUS_DRAINING) &&
mpctx->last_vo_pts != MP_NOPTS_VALUE)
if (opts->pause || mpctx->time_frame >= 0.5 ||
mpctx->video_status == STATUS_EOF)
{
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts,
MPSEEK_VERY_EXACT, 0);
issue_refresh_seek(mpctx, MPSEEK_VERY_EXACT);
}
}
@@ -616,6 +498,7 @@ static int decode_image(struct MPContext *mpctx)
switch (res) {
case DATA_WAIT: return VD_WAIT;
case DATA_OK:
case DATA_STARVE:
case DATA_AGAIN: return VD_PROGRESS;
case DATA_EOF: return VD_EOF;
default: abort();
@@ -991,28 +874,6 @@ static void update_av_diff(struct MPContext *mpctx, double offset)
}
}
static void init_vo(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct vo_chain *vo_c = mpctx->vo_chain;
#if HAVE_GPL
if (opts->gamma_gamma != 0)
video_set_colors(vo_c, "gamma", opts->gamma_gamma);
if (opts->gamma_brightness != 0)
video_set_colors(vo_c, "brightness", opts->gamma_brightness);
if (opts->gamma_contrast != 0)
video_set_colors(vo_c, "contrast", opts->gamma_contrast);
if (opts->gamma_saturation != 0)
video_set_colors(vo_c, "saturation", opts->gamma_saturation);
if (opts->gamma_hue != 0)
video_set_colors(vo_c, "hue", opts->gamma_hue);
video_set_colors(vo_c, "output-levels", opts->video_output_levels);
#endif
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
double calc_average_frame_duration(struct MPContext *mpctx)
{
double total = 0;
@@ -1064,9 +925,11 @@ static double find_best_speed(struct MPContext *mpctx, double vsync)
static bool using_spdif_passthrough(struct MPContext *mpctx)
{
if (mpctx->ao_chain && mpctx->ao_chain->ao) {
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao_chain->ao, &out_format);
return !af_fmt_is_pcm(out_format.format);
int samplerate;
int format;
struct mp_chmap channels;
ao_get_format(mpctx->ao_chain->ao, &samplerate, &format, &channels);
return !af_fmt_is_pcm(format);
}
return false;
}
@@ -1454,7 +1317,7 @@ void write_video(struct MPContext *mpctx)
mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED;
goto error;
}
init_vo(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
mpctx->time_frame -= get_relative_time(mpctx);
+1
View File
@@ -563,6 +563,7 @@ bool stream_seek(stream_t *s, int64_t pos)
int64_t x = pos - (s->pos - (int)s->buf_len);
if (x >= 0) {
s->buf_pos = x;
assert(s->buf_pos <= s->buf_len);
return true;
}
}
+4
View File
@@ -79,7 +79,11 @@ static int fill_buffer(stream_t *s, char *buffer, int max_len)
AVIOContext *avio = s->priv;
if (!avio)
return -1;
#if LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 81, 100)
int r = avio_read_partial(avio, buffer, max_len);
#else
int r = avio_read(avio, buffer, max_len);
#endif
return (r <= 0) ? -1 : r;
}
+1 -27
View File
@@ -55,32 +55,6 @@ static int control(stream_t *s, int cmd, void *arg)
return STREAM_UNSUPPORTED;
}
static int h_to_i(unsigned char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static bool bstr_to_hex_inplace(bstr *h)
{
if (h->len % 2)
return false;
for (int n = 0; n < h->len / 2; n++) {
int hi = h_to_i(h->start[n * 2 + 0]);
int lo = h_to_i(h->start[n * 2 + 1]);
if (hi < 0 || lo < 0)
return false;
h->start[n] = (hi << 4) | lo;
}
h->len /= 2;
return true;
}
static int open_f(stream_t *stream)
{
stream->fill_buffer = fill_buffer;
@@ -100,7 +74,7 @@ static int open_f(stream_t *stream)
bstr_eatstart0(&data, "memory://");
stream_control(stream, STREAM_CTRL_SET_CONTENTS, &data);
if (use_hex && !bstr_to_hex_inplace(&p->data)) {
if (use_hex && !bstr_decode_hex(stream, p->data, &p->data)) {
MP_FATAL(stream, "Invalid data.\n");
return STREAM_ERROR;
}
+1 -1
View File
@@ -104,7 +104,7 @@ const struct m_sub_options sub_style_conf = {
.change_flags = UPDATE_OSD,
};
static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
{
return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt
&& a.mr == b.mr && a.mb == b.mb
+2
View File
@@ -82,6 +82,8 @@ struct mp_osd_res {
double display_par;
};
bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b);
// 0 <= sub_bitmaps.render_index < MAX_OSD_PARTS
#define MAX_OSD_PARTS 5
+33 -34
View File
@@ -31,6 +31,7 @@
#include "mp_image.h"
#include "csputils.h"
#include "options/m_config.h"
#include "options/m_option.h"
const struct m_opt_choice_alternatives mp_csp_names[] = {
@@ -96,15 +97,6 @@ const struct m_opt_choice_alternatives mp_csp_light_names[] = {
{0}
};
const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
"brightness",
"contrast",
"hue",
"saturation",
"gamma",
"output-levels",
};
const struct m_opt_choice_alternatives mp_chroma_names[] = {
{"unknown", MP_CHROMA_AUTO},
{"mpeg2/4/h264",MP_CHROMA_LEFT},
@@ -817,9 +809,25 @@ bool mp_colorspace_equal(struct mp_colorspace c1, struct mp_colorspace c2)
c1.sig_peak == c2.sig_peak;
}
#define OPT_BASE_STRUCT struct mp_csp_equalizer_opts
const struct m_sub_options mp_csp_equalizer_conf = {
.opts = (const m_option_t[]) {
OPT_INTRANGE("brightness", values[MP_CSP_EQ_BRIGHTNESS], 0, -100, 100),
OPT_INTRANGE("saturation", values[MP_CSP_EQ_SATURATION], 0, -100, 100),
OPT_INTRANGE("contrast", values[MP_CSP_EQ_CONTRAST], 0, -100, 100),
OPT_INTRANGE("hue", values[MP_CSP_EQ_HUE], 0, -100, 100),
OPT_INTRANGE("gamma", values[MP_CSP_EQ_GAMMA], 0, -100, 100),
OPT_CHOICE_C("video-output-levels", values[MP_CSP_EQ_OUTPUT_LEVELS], 0,
mp_csp_levels_names),
{0}
},
.size = sizeof(struct mp_csp_equalizer_opts),
};
// Copy settings from eq into params.
void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
const struct mp_csp_equalizer *eq)
const struct mp_csp_equalizer_opts *eq)
{
params->brightness = eq->values[MP_CSP_EQ_BRIGHTNESS] / 100.0;
params->contrast = (eq->values[MP_CSP_EQ_CONTRAST] + 100) / 100.0;
@@ -829,37 +837,28 @@ void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
params->levels_out = eq->values[MP_CSP_EQ_OUTPUT_LEVELS];
}
static int find_eq(int capabilities, const char *name)
struct mp_csp_equalizer_state *mp_csp_equalizer_create(void *ta_parent,
struct mpv_global *global)
{
for (int i = 0; i < MP_CSP_EQ_COUNT; i++) {
if (strcmp(name, mp_csp_equalizer_names[i]) == 0)
return ((1 << i) & capabilities) ? i : -1;
}
return -1;
struct m_config_cache *c = m_config_cache_alloc(ta_parent, global,
&mp_csp_equalizer_conf);
// The terrible, terrible truth.
return (struct mp_csp_equalizer_state *)c;
}
int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property,
int *out_value)
bool mp_csp_equalizer_state_changed(struct mp_csp_equalizer_state *state)
{
int index = find_eq(eq->capabilities, property);
if (index < 0)
return -1;
*out_value = eq->values[index];
return 0;
struct m_config_cache *c = (struct m_config_cache *)state;
return m_config_cache_update(c);
}
int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property,
int value)
void mp_csp_equalizer_state_get(struct mp_csp_equalizer_state *state,
struct mp_csp_params *params)
{
int index = find_eq(eq->capabilities, property);
if (index < 0)
return 0;
eq->values[index] = value;
return 1;
struct m_config_cache *c = (struct m_config_cache *)state;
m_config_cache_update(c);
struct mp_csp_equalizer_opts *opts = c->opts;
mp_csp_copy_equalizer_values(params, opts);
}
void mp_invert_cmat(struct mp_cmat *out, struct mp_cmat *in)
+13 -23
View File
@@ -185,6 +185,8 @@ enum mp_chroma_location {
extern const struct m_opt_choice_alternatives mp_chroma_names[];
extern const struct m_sub_options mp_csp_equalizer_conf;
enum mp_csp_equalizer_param {
MP_CSP_EQ_BRIGHTNESS,
MP_CSP_EQ_CONTRAST,
@@ -195,28 +197,23 @@ enum mp_csp_equalizer_param {
MP_CSP_EQ_COUNT,
};
#define MP_CSP_EQ_CAPS_COLORMATRIX \
( (1 << MP_CSP_EQ_BRIGHTNESS) \
| (1 << MP_CSP_EQ_CONTRAST) \
| (1 << MP_CSP_EQ_HUE) \
| (1 << MP_CSP_EQ_SATURATION) \
| (1 << MP_CSP_EQ_OUTPUT_LEVELS) )
#define MP_CSP_EQ_CAPS_GAMMA (1 << MP_CSP_EQ_GAMMA)
#define MP_CSP_EQ_CAPS_BRIGHTNESS (1 << MP_CSP_EQ_BRIGHTNESS)
extern const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT];
// Default initialization with 0 is enough, except for the capabilities field
struct mp_csp_equalizer {
// Bit field of capabilities. For example (1 << MP_CSP_EQ_HUE) means hue
// support is available.
int capabilities;
struct mp_csp_equalizer_opts {
// Value for each property is in the range [-100, 100].
// 0 is default, meaning neutral or no change.
int values[MP_CSP_EQ_COUNT];
};
void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
const struct mp_csp_equalizer_opts *eq);
struct mpv_global;
struct mp_csp_equalizer_state *mp_csp_equalizer_create(void *ta_parent,
struct mpv_global *global);
bool mp_csp_equalizer_state_changed(struct mp_csp_equalizer_state *state);
void mp_csp_equalizer_state_get(struct mp_csp_equalizer_state *state,
struct mp_csp_params *params);
struct mp_csp_col_xy {
float x, y;
};
@@ -233,13 +230,6 @@ struct mp_csp_primaries {
struct mp_csp_col_xy red, green, blue, white;
};
void mp_csp_copy_equalizer_values(struct mp_csp_params *params,
const struct mp_csp_equalizer *eq);
int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property,
int value);
int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property,
int *out_value);
enum mp_csp avcol_spc_to_mp_csp(int avcolorspace);
enum mp_csp_levels avcol_range_to_mp_csp_levels(int avrange);
enum mp_csp_prim avcol_pri_to_mp_csp_prim(int avpri);
+7 -39
View File
@@ -15,8 +15,6 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*
* Almost LGPL.
*
* Parts under HAVE_GPL are licensed under GNU General Public License forever.
*/
#include <stdio.h>
@@ -205,35 +203,10 @@ static void fix_image_params(struct dec_video *d_video,
// While mp_image_params normally always have to have d_w/d_h set, the
// decoder signals unknown bitstream aspect ratio with both set to 0.
float dec_aspect = p.p_w > 0 && p.p_h > 0 ? p.p_w / (float)p.p_h : 0;
#if HAVE_GPL
if (d_video->initial_decoder_aspect == 0)
d_video->initial_decoder_aspect = dec_aspect;
#endif
bool use_container = true;
switch (opts->aspect_method) {
case 0:
#if HAVE_GPL
// We normally prefer the container aspect, unless the decoder aspect
// changes at least once.
if (dec_aspect > 0 && d_video->initial_decoder_aspect != dec_aspect) {
MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
// Even if the aspect switches back, don't use container aspect again.
d_video->initial_decoder_aspect = -1;
use_container = false;
}
break;
#else
/* fall through, behave as "bitstream" */
#endif
case 1:
if (dec_aspect) {
MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
use_container = false;
}
break;
if (opts->aspect_method == 1 && p.p_w > 0 && p.p_h > 0) {
MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
use_container = false;
}
if (use_container && c->par_w > 0 && c->par_h) {
@@ -271,6 +244,10 @@ static void fix_image_params(struct dec_video *d_video,
p.color.sig_peak = 0.0;
}
p.spherical = c->spherical;
if (p.spherical.type == MP_SPHERICAL_AUTO)
p.spherical.type = MP_SPHERICAL_NONE;
// Guess missing colorspace fields from metadata. This guarantees all
// fields are at least set to legal values afterwards.
mp_image_params_guess_csp(&p);
@@ -327,15 +304,6 @@ static bool receive_frame(struct dec_video *d_video, struct mp_image **out_image
if (!mpi)
return progress;
#if HAVE_GPL
if (opts->field_dominance == 0) {
mpi->fields |= MP_IMGFIELD_TOP_FIRST | MP_IMGFIELD_INTERLACED;
} else if (opts->field_dominance == 1) {
mpi->fields &= ~MP_IMGFIELD_TOP_FIRST;
mpi->fields |= MP_IMGFIELD_INTERLACED;
}
#endif
// Note: the PTS is reordered, but the DTS is not. Both should be monotonic.
double pts = mpi->pts;
double dts = mpi->dts;
+1 -1
View File
@@ -37,6 +37,7 @@ struct dec_video {
struct mp_hwdec_devices *hwdec_devs; // video output hwdec handles
struct sh_stream *header;
struct mp_codec_params *codec;
struct vo *vo; // required for direct rendering into video memory
char *decoder_desc;
@@ -72,7 +73,6 @@ struct dec_video {
double decoded_pts;
struct mp_image_params dec_format, last_format, fixed_format;
float initial_decoder_aspect;
double start_pts;
double start, end;
+8
View File
@@ -2,6 +2,7 @@
#define MPV_LAVC_H
#include <stdbool.h>
#include <pthread.h>
#include <libavcodec/avcodec.h>
@@ -60,6 +61,7 @@ typedef struct lavc_ctx {
// Set by generic hwaccels.
struct mp_hwdec_ctx *hwdec_dev;
bool owns_hwdec_dev;
int hwdec_fmt;
int hwdec_w;
@@ -72,6 +74,12 @@ typedef struct lavc_ctx {
struct mp_image_pool *hwdec_swpool;
AVBufferRef *cached_hw_frames_ctx;
// --- The following fields are protected by dr_lock.
pthread_mutex_t dr_lock;
bool dr_failed;
struct mp_image_pool *dr_pool;
int dr_imgfmt, dr_w, dr_h, dr_stride_align;
} vd_ffmpeg_ctx;
struct vd_lavc_hwdec {
Loaded 100 of 186 files, more files were not shown because too many files have changed in this diff. Show more