Compare commits

..
221 Commits
Author SHA1 Message Date
Martin Herkt 0f1afc6ba2 Release 0.24.0 2017-02-12 02:31:16 +01:00
Martin Herkt 35aa705c3e Merge branch 'master' into release/current 2017-02-12 01:01:56 +01:00
pavelxdd 3739d1318f win32: update the fullscreen state on restoring
If a maximized window restored from fullscreen,
the fullscreen state needs to be updated manually.
2017-02-12 02:49:25 +11:00
wm4 8432514c5f DOCS/contribute.md: adjust coding style for if/else bracing 2017-02-11 16:48:29 +01:00
James Ross-Gowan 8bc335e3db vo_opengl: angle: log the device/surface implementation
This should be useful for debugging, since otherwise it's hard to tell
which implementation has been auto-detected or if any failed to init.
2017-02-12 02:40:49 +11:00
Ricardo Constantino 6ac3d77e90 ytdl_hook: support livestream segmented DASH VODs
Seen with a VOD of a recently ended livestream on Youtube.

They seem to use segmented DASH but unlike normal Youtube
segmented DASH, the segments don't seem to need the initialization
segment.

The video actually fails to start to play if the init segment is
prepended with a lot of 'Found duplicated MOOV Atom. Skipped it' errors
popping up.
2017-02-10 20:24:03 +00:00
wm4 d0f1d382e6 DOCS/mplayer-changes.rst: fix syntax
The missing newline made the github rst renderer (which is a POS) hide
the entire section.

Also, add some more warning text.
2017-02-10 15:27:45 +01:00
Thomas V 1672204dc5 dvb: move priv allocation to dvb_open
This fixes a crash when changing channels; previously stream->priv would not
be initialized when dvb_get_state reused the existing state.

Signed-off-by: Thomas VanSelus <tvanselus@diospyros.us>
2017-02-10 08:19:28 +01:00
wm4 f89ff14195 lavfi: cosmetics: more consistent variable naming 2017-02-09 11:33:04 +01:00
Kevin Mitchell cc3eb531eb ao_oss: fix mixer channel message 2017-02-08 21:03:40 -08:00
Kevin Mitchell f4d75376fe ao_oss: use --audio-device if --oss-device isn't set.
Fall back on PATH_DEV_DSP if nothing is set.

This mirrors the behaviour of --audio-device / --alsa-device.

There doesn't appear to be a general way to list devices with oss, so
--audio-device=help doesn't list oss devices except for the default one if the
file exists.

Previously --audio-device was ignored entirely by ao_oss.

fixes #4122
2017-02-08 21:03:40 -08:00
wm4 5085cf295f lavfi: slightly better disconnected output handling
If we have a disconnected output, read data only passively (and don't
cause input to be written). Otherwise, we're in danger of making
libavfilter queue too many frames on other outputs which are connected
to the same input, but don't read as quickly.

Also don't set pad->output_needed in this specific case, because it
would nonsensically make lavfi_process() return true, even if nothing is
going on.

This commit breaks if there is a simple filter chain with a connected
input, but a disconnected output, like

   --lavfi-complex='[aid1] copy [ao]'

and the audio output didn't initialize correctly. This will eventually
starve video as the audio packet queue get full (it will print a
warning, and then assume video EOF and exit).

But whatever.

Probably fixes #4118.
2017-02-08 20:29:38 +01:00
wang-bin e285e22143 Use AV_INPUT_BUFFER_PADDING_SIZE instead of deprecated one
Signed-off-by: wm4 <wm4@nowhere>
2017-02-08 16:55:16 +01:00
wm4 c94ede9e62 manpage: fix incorrect option name for --record-file
Also the one in interface-changes.rst.

Fixes #4121.
2017-02-08 06:54:47 +01:00
wm4 6d236bd96e mp_image_tool: guard hw downloading against allocation failures
Small oversight, matters for OOM errors.
2017-02-07 17:06:20 +01:00
wm4 96a45a16af player: add experimental stream recording feature
This is basically a WIP, but it can't remain in a branch forever. A
warning is print when using it as it's still a bit "shaky".
2017-02-07 17:05:17 +01:00
James Ross-Gowan 061b752217 vo_opengl: egl_helpers: fix for non-Windows
Whoops. Fixes #4119
2017-02-08 01:27:17 +11:00
James Ross-Gowan e0250b9604 vo_opengl: angle: rewrite with custom swap chain
This replaces the old backend that exclusively used EGL windowing with
one that can also use ANGLE's ability to render to directly to a
texture. The advantage of this is that it allows mpv to create the swap
chain itself and this allows mpv to use a flip-mode swap chain on a HWND
(which avoids problems with DirectComposition) and to use a longer swap
chain that has six backbuffers by default (which reportedly fixes
problems with rendering 24fps video on 24Hz monitors.)

Also, "screenshot window" should now work on DXGI 1.2 and up (Windows 8
and up.)
2017-02-07 22:45:07 +11:00
Ricardo Constantino 5fbad204a6 ytdl_hook: reenable support for length-less segments in EDL
They're unsupported only in MP4 DASH mode.
2017-02-06 16:50:28 +00:00
Philip Langdale 46a556bd4b manpage: Reorganise discussion of cuda hwdec to be less confusing
The existing discussion has managed to convince people they should
use cuda-copy instead of cuda, which just isn't right.
2017-02-06 06:14:39 +01:00
wm4 c7b72a6afe DOCS/edl-mpv.rst: fix typo 2017-02-05 16:02:58 +01:00
wm4 19551200d5 ytdl_hook: fix EDL syntax
All entries must be separated by ";" or "\n". The parser just doesn't
enforce it if an entry uses quoting.
2017-02-05 16:02:22 +01:00
wm4 92f9747c2b demux_edl: fix behavior with no init segment provided
Not that we use it...
2017-02-05 15:51:36 +01:00
Frédéric Brière aaad2d847e tv: Zero-out newly-allocated handle in tv_new_handle()
Some fields (notably tv_channel_list) were left uninitialized,
potentially causing problems later on.

Fixes #4096
2017-02-05 14:20:27 +01:00
wm4 8362577f8c demux_timeline: more silencing 2017-02-04 23:10:04 +01:00
wm4 58c9f1a822 demux: try not to read packets when cancelled
Essentially, this will make it abort sooner. Especially with DASH it
might avoid confusing error messages, although exact behavior depends
on timing.
2017-02-04 22:53:04 +01:00
Ricardo Constantino 7e6b37be07 ytdl_hook: Add non-dash fallbacks to default formats 2017-02-04 22:34:47 +01:00
wm4 61202bb364 ytdl_hook, edl: implement pseudo-DASH support
We use the metadata provided by youtube-dl to sort-of implement
fragmented DASH streaming.

This is all a bit hacky, but hopefully a makeshift solution until
libavformat has proper mechanisms. (Although in danger of being one
of those temporary hacks that become permanent.)
2017-02-04 22:34:38 +01:00
wm4 97680bf604 demux_timeline: move code around
Cosmetic preparation for later changes.
2017-02-04 22:19:21 +01:00
Ricardo Constantino a46fc5e5eb ytdl: support segmented dash 2017-02-04 22:19:21 +01:00
wm4 50991fac81 sub: remove .txt as text subtitle extension
If used with fuzzy matching, the player tends to pick up random text
files, sometimes with interesting results.

The most interesting interaction is when the user uses
--log-file=something.txt, and mpv tries to open its own log file. It
essentially "freezes" during probing, because every time it reads from
it, it will write some more data, which in turn will cause more data to
be read - until the 2MB max. probing size is slowly reached. This is not
even an obscure corner case, but happened to multiple users.

The .txt extension has been considered a subtitle extension ever since
the code was added to MPlayer's subreader.c, but I'm not seeing many
actual subtitle files with this extension, so just get rid of it.
2017-02-03 11:32:16 +01:00
S2F0amEgS2xvdmVy 1ec6cd89b9 manpage: state that hwdec d3d11va requires Windows 8+
As the manual entry for --hwdec states that d3d11va and d3d11va-copy require Windows, it can be assumed that it also works for Windows 7. Since it doesn't, according to https://github.com/mpv-player/mpv/issues/3285#issuecomment-228593539, and personal testing, updating the manual accordingly and making the hwdec OS requirements for ANGLE in line with videotoolbox, where OS version is stated.
2017-02-03 09:13:36 +01:00
wm4 95d4c2d7f6 player: different way to auto-enable the demuxer cache
Instead of enabling it only when a stream-cache is enabled, also try to
enable it independently from that if the demuxer is marked as
is_network.

Also add some code to the EDL code, so EDLs containing network streams
are automatically cached this way.

Extend the OSD info line so that it shows the demuxer cache in this case
(more or less).

I didn't find where or whether options.rst describes how the demuxer
cache is enabled, so no changes there.
2017-02-02 18:38:16 +01:00
wm4 fb9a32977d stream: get rid of streamtype enum
Because it's kind of dumb. (But not sure if it was worth the trouble.)

For stream_file.c, we add new explicit fields. The rest are rather
special uses and can be killed by comparing the stream impl. name.

The changes to DVD/BD/CD/TV are entirely untested.
2017-02-02 18:26:58 +01:00
wm4 e13a62fc34 stream: better method signal caching, rename weird uncached_stream field
"uncached_stream" is a pretty bad name. It could be mistaken for a
boolean, and then its meaning would be inverted. Rename it.

Also add a "caching" field, which signals that the stream is a cache or
reads from a cache. This is easier to understand and more flexible.
2017-02-02 18:03:29 +01:00
Akemi d11c03faee cocoa: fix color profile retrieval
when the color profile was changed it used the right NSScreen but with
the old colorSpace. this was optimised out by a previous commit because
of a wrong assumption. we need to update the screen so we can get the
new colorSpace. this adds a bit of redundancy since on screen change it
will update screen pointer twice.
2017-02-02 16:31:28 +01:00
Akemi 5f10a415d7 cocoa: fix dropping of files and URLs
the problem here is that dropped files can also be treated as
NSURLPboardType instead of just NSFilenamesPboardType. the 'else if'
could never be reached and was dead code.

this basically reverts ed695ce which tried to fix multiple dropped
URLs, or rather files, and moves the filename check infront of the URL
check. the filename path can handle multiple dropped files, whereas the
URL path can only handle one dropped URL. this assumes that only one URL
can be dropped at a time. it also reverts a603543 because it's not
needed any more.

this also fixes a problem where dropped URLs from Chrome don't conform
to the NSURL class and the readObjectsForClasses method always returned
an empty URL.

Fixes #4036
2017-02-02 16:25:44 +01:00
Akemi 6da224b1dd cocoa: optimise screen event handling
this optimises two things and fix a minor bug.

1. we always updated the display refresh rate on any mode change whether
it was the current screen or not. now we only update the refresh rate
when the mode changed happened on the current screen.
2. the windowDidChangeScreen event doesn't exclusively trigger on screen
changes so we updated the display refresh rate in cases where it wasn't
needed at all. since we manually keep track of the current screen, we
can easily test if the screen really changed.
3. weirdly on initWithContentRect accessing the screen of the window
always returned the main screen instead of the screen the window is
created on. since we already use the window init method with the screen
as argument, overwrite that method instead and use the screen argument.
2017-02-02 16:22:27 +01:00
Akemi f39a1cb1b0 cocoa: fix displaylink refresh rate retrieval
we are dealing with several problems here, which weren't apparent
because we always initialised a new displaylink for the display refresh
rate retrieval, previously to commit 449eb20 and bug 9490b62.

just changing the display with CVDisplayLinkSetCurrentCGDisplay
can cause inconsistent refresh rates and discontinuity in timestamps.
this can either lead to bogus values for the Actual display refresh rate
or retrieving the refresh rate of the previous display if we immediately
try to get a new value. since the Actual refresh rate is computed i
assume that it at least needs one refresh period to actual return
something useful.

furthermore when changing the screen and updating the displaylink, it
seems that the retrieved refresh rates for the screen mpv wasn't opened
on are being estimated in a sub-optimal way. as an example, when moving
my window to my second screen the Actual refresh rate was always a
constant 60Hz, even though it is supposed to fluctuate a little bit.
though if mpv was started on the secondary screen the Actual refresh
rate fluctuated around 59.94Hz like expected. in that case my primary
screen always reported a constant 60Hz instead.

for the first problem we moved the actual retrieval of the refresh rate
to the very last moment when mpv actual requests a new value and not
when the refresh rate changed. we only update the displaylink itself
when a possible refresh rate change is detected. this gives the
displaylink some time to calculate the new refresh rate. for the second
problem, instead of setting the new display we completely uninitialise
the old dislaylink and create a new one for the new screen. this gives
us properly estimated refresh rates.

additionally we also optimised the display refresh rate fallback
heuristic. it will never be 0 anymore and we prevent it from returning
bogus values with a simple threshold for the difference of the Actual
and Nominal refresh rate.
2017-02-02 16:22:27 +01:00
Akemi 8bbdecea83 osx: consistent normalisation when searching for external files
several unicode characters can be encoded in two different ways, either
in a precomposed (NFC) or decomposed (NFD) representation. everywhere
besides on macOS, specifically HFS+, precomposed strings are being used.
furthermore on macOS we can get either precomposed or decomposed
strings, for example when not HFS+ formatted volumes are used. that can
be the case for network mounted devices (SMB, NFS) or optical/removable
devices (UDF). this can lead to an inequality of actual equal strings,
which can happen when comparing strings from different sources, like the
command line or filesystem. this makes it mainly a problem on macOS
systems.

one case that can potential break is the sub-auto option. to prevent
that we convert the search string as well as the string we search in to
the same normalised representation, specifically we use the decomposed
form which is used anywhere else.

this could potentially be a problem on other platforms too, though the
potential of occurring is very minor. for those platforms we don't
convert anything and just fallback to the input.

Fixes #4016
2017-02-02 16:21:04 +01:00
wm4 ce23dfa2fa hw_dxva2: create a IDirect3D9Ex device
This should allow us to create the device in situations when
Direct3DCreate9 normally fails, for example if no user is logged in.
While the later use-case is not very interesting, I hope it to work in
some other situations as well, for example while certain drivers are in
exclusive full screen mode.

This is available since Windows 7, so I'm removing the old call
completely.
2017-02-02 12:58:04 +01:00
wm4 644e23a0f5 tvi_dummy: don't return bad dummy PTS
This makes the player spam timestamp warnings. Return NOPTS instead.
2017-02-02 08:51:40 +01:00
wm4 738de7d3fe demux_tv: free the correct field instead of creating dangling pointer
This could potentially have caused fun crashes if the --tv-channels
option was used, and something more advanced than tv:// was used to open
it. (This code is still untested.)
2017-02-02 08:50:18 +01:00
Dan Oscarsson f1c9032ddf sub: add justify of subtitles
To make it easier for the eyes, multi line subtitles should
be left justified (for most languages).
This adds an option to define how subtitles are to be justified
inpendently of how they are aligned.
Also add option to enable --sub-justify to be applied on ASS subtitles.
2017-02-01 16:42:58 +01:00
Ricardo Constantino bbf01346ff osc: allow playlist buttons when looping
Closes #4092
2017-02-01 13:26:38 +00:00
wm4 2493d91122 TOOLS/matroska.py: use python3
"python" usually maps to Python 2, while all Python 3 installations
provide "python3". And the script requires Python 3.
2017-02-01 07:03:13 +01:00
Ricardo Constantino 9831d3ada6 osc: box: clip with ellipsis after too much stretching 2017-01-31 18:54:40 +00:00
Ricardo Constantino ee000f2133 osc: bottom/topbar: clip title instead of stretching 2017-01-31 18:54:40 +00:00
wm4 ccf2b302cf player: add .scc subtitle extension
Requested. Supposedly "scenarist closed captions".

(The list of getting quite full. But it's probably still better than
trying to probe the files by contents, because the external subtitle
loader code will initially look at _all_ files in the same directory as
the main file.)
2017-01-31 14:50:58 +01:00
wm4 9c12d54afa demux_mkv: passthrough BlockAdditions for libvpx alpha
Dumb but simple thing. Requires the FFmpeg libvpx decoder wrapper, as
its native decoder doesn't support alpha.
2017-01-31 14:48:10 +01:00
wm4 55d6408526 TOOLS/matroska.py: fix some minor things for dumping
TOOLS/matroska.py can be used stand-alone for dumping Matroska file
contents. Fix some related issues.

Elements were treated as unknown if the element hex ID contained
uppercase characters.

Unknown elements stopped parsing. This was intentional, but I don't
really see any reason for it.

Dumping with Python 2 is broken. I don't care, but everytime I hit this,
I find myself trying to find out why. So make it error out explicitly.
2017-01-31 14:47:11 +01:00
wm4 c178920505 demux_mkv: any reference makes a frame not a keyframe
Fixes seeking with:

https://bugs.chromium.org/p/chromium/issues/detail?id=497889

Haali also ignores the element's contents, and interprets its presence
as the block not being a keyframe. FFmpeg is going to have an equivalent
change.

I don't know yet whether the affected sample is valid - a reference
timestamp of 0 doesn't make too much sense to me.
2017-01-31 12:44:49 +01:00
wm4 07494aab16 command: nicer OSd-formatting for loop-file 2017-01-31 11:47:28 +01:00
wm4 9a464f53ef input.conf: change "L" to change loop-file by default 2017-01-31 11:46:43 +01:00
Ricardo Constantino e017c24174 ytdl_hook: refactor edl track joining
Only used once for now.

Also, support switched format strings, like bestaudio+bestvideo.
2017-01-30 16:21:44 +00:00
Ricardo Constantino 6dafc85187 ytdl_hook: respect --no-audio, don't force-select track 2017-01-30 15:01:07 +00:00
wm4 dccb752b5f build: fix --disable-gl if cuda is enabled
video/out/opengl/hwdec_cuda.c is enabled with cuda-hwaccel. But it makes
only sense to build if GL is enabled, and in fact it fails to link
without GL as it calls a specific GL helper function.

In theory it would be perfectly possible to use cuda-copy with GL
disabled. But I'm not bothering with the complexity.
2017-01-30 09:34:10 +01:00
Akemi 954625c165 cocoa: fix unwanted behavior with window level other than the default
setting a window level other than NSNormalWindowLevel always sets
NSWindowCollectionBehaviorTransient, which prevents certain things to
work properly. examples are automatic switching to the active Space when
mpv is made active and (de-)miniaturizing. latter always lead to a
vanishing window.

Fixes #1757 #1884
2017-01-29 19:46:53 +01:00
James Ross-Gowan bed94df521 vo_opengl: dxinterop: use the new SAFE_RELEASE macro 2017-01-30 00:22:30 +11:00
James Ross-Gowan 9692814502 win32: add COM-specific SAFE_RELEASE to windows_utils.h
See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd743946.aspx

Microsoft example code often uses a SAFE_RELEASE macro like the one in
the above link. This makes it easier to avoid errors when releasing COM
interfaces. It also reduces noise in COM-heavy code.

ao_wasapi.h also had a macro called SAFE_RELEASE, though unlike the
version above, its SAFE_RELEASE macro accepted a second parameter which
allowed it to destroy arbitrary objects other than just COM interfaces.
This renames ao_wasapi's SAFE_RELEASE to SAFE_DESTROY, which should more
accurately reflect what it does and prevent confusion with the Microsoft
version.
2017-01-30 00:22:30 +11:00
wm4 a6d29494ca player: print hw format on "VO: " line too
Useful for distinguishing bit depth when hardware decoding. (To the
degree it's useful to show it at all. This just brings the hardware
decoding case on the same level of showing information as the software
decode call.)
2017-01-29 12:53:58 +01:00
Ricardo Constantino 2a7d2d1bc9 ytdl_hook: fix opening hitbox.tv rtmp stream
Worked with librtmp, but ffrtmp needs to set rtmp_swfurl in addition
to rtmp_swfverify, even if they're the same value.
2017-01-29 01:45:13 +00:00
wm4 0aac7533dc vaapi: improve a comment
Try to actually explain what's up with this code.
2017-01-28 18:36:00 +01:00
wm4 443d3a91d3 vaapi: remove central lock around vaapi API calls
The lock was disabled recently. This commit gets rid of the dummied out
calls. The main reason for removing it is that there is no apparent need
for it anymore, and the new FFmpeg vaapi code does not use or provide
such a lock (there are some places which we cannot control and which do
vaapi API calls, like frame destructors).
2017-01-28 18:27:30 +01:00
kwkam d9ef1333c6 waftools/checks/generic: hacks for linking against static/shared libs
When mpv is being linked against static libraries which have shared
libraries as dependencies, linker will throw error because pkg-config
with --static flag will return shared libraries which will be placed
under the -Wl,-Bstatic section, while pkg-config without --static flag
will omit the private libraries required to link the static library.

With this function users can modify the wscript to insert the dependencies
when necessary. For example, linking FFmpeg with shared OpenSSL and zlib:
'func': check_pkg_config_mixed(['crypto','ssl','z'], 'libavcodec')
2017-01-28 14:02:36 +01:00
kwkam d41f25009d wscript: add LIBRARY_PATH for library detection
MinGW GCC seems to ignore LIBRARY_PATH which causes problem
when some libraries not using pkg-config were installed to
local directory
2017-01-28 14:02:34 +01:00
Ilya Tumaykin ea40fa36ee build: rpi: rely on pkgconfig for compiler flags
Upstream provides pkgconfig files for quite some time now [1,2].
Use them to determine the required flags instead of hard coding.

This makes cross-compilation easy, which I dare to say is important for
many raspberry-pi users. This also prevents picking libEGL and libGLESv2
from mesa when they are present, which can happen with the current code.

Good distros should put these pkgconfig files into default pkg-config
search path or populate PKG_CONFIG_PATH for users. However, be nice to
everybody and manually look into '/opt/vc/lib/pkgconfig' just in case.
Hence the PKG_CONFIG_PATH mangling.

[1]: https://github.com/raspberrypi/userland/issues/245
[2]: https://github.com/raspberrypi/userland/commit/05d60a01d53dca363bb4286594db1826ffff8762
2017-01-28 14:02:22 +01:00
pavelxdd 9c90c902c1 win32: snap to screen edges
Disabled by default. The snap sensitivity value depends on
the screen DPI. The default value is 16px on a 96 DPI screen.

Fixes #2248
2017-01-27 12:00:32 +01:00
wm4 cfda696580 build: explicitly check for FFmpeg vs. Libav, and their exact versions
In a first pass, we check whether libavcodec is present.

Then we try to compile a snippet and check for FFmpeg vs. Libav. (This
could probably also be done by somehow checking the pkgconfig version.
But pkg-config can't deal with that idiotic FFmpeg idea that a micro
version number >= 100 identifies FFmpeg vs. Libav.)

After that we check the project-specific version numbers. This means it
can no longer happen that we accidentally allow older, unsupported
versions of FFmpeg, just because the Libav version numbers are somehow
this way.

Also drop the resampler checks. We hardcode which resampler to each with
each project. A user can no longer force use of libavresample with
FFmpeg.
2017-01-27 09:57:01 +01:00
wm4 a9e0f4b279 wscript: merge libavfilter check into the main ffmpeg check
It used to be optional. That's why it was separate. No need for that
anymore.
2017-01-27 09:22:52 +01:00
wm4 c3205d294e atomic: drop __sync builtins
The correctness of the stdatomic.h emulation via the __sync builtins is
questionable, and we've been relying on exact stdatomic semantics for a
while, so just get rid of it. Compilers which support __sync but not
stdatomic.h will use to the slow mutex fallback.

Not sure about the __atomic builtins. It doesn't seem to harm either, so
leave it for now.
2017-01-27 09:15:32 +01:00
wm4 70411f2709 stream: minor cleanup to previous commit
This is almost cosmetic, but removes the duplicated EOF-setting.

Somewhat oddly, this will enter the reconnect path and exit it
immediately again - should be fine.
2017-01-27 09:03:14 +01:00
wm4 4e53f9e5b2 stream: set EOF if stream is canceled
Commit 7be495b3 added the cancellation test, but forgot to set the eof
flag. This could lead to demux_mkv.c not terminating if the stream was
cancelled in some code paths.

This function is what is supposed to set the EOF flag in the first
place, so just add the missing code.
2017-01-26 18:27:30 +01:00
wm4 abc6d130ac command: shorten long playlists on OSD
A hacky, convoluted, half-working mess that attempts to cut off overlong
playlists.

It does so by relying on the ASS formatting rule that the font size is
specified in the virtual PlayResY resolution. This means we can
(normally) easily tell how many lines fit on the screen. On the other
hand, this does not work if the text is wrapped.

This as a kludge until a Better™ solution is available.
2017-01-26 18:24:53 +01:00
wm4 60452f640f osd: move some internal declarations to internal headers 2017-01-26 16:34:31 +01:00
wm4 753dbea83f vd_lavc: allocate 8 ref frames for VP9
Apparently this is the maximum that can be preserved. There is also
something about the decoder being able only to use 3 frames at a time,
and I'm assuming these are part of the 8 frames.
2017-01-26 11:37:47 +01:00
wm4 9980b11058 vo_opengl: egl_helpers: fix variable name
It was basically inverted. Not sure how this even happened. Hopefully
it's more an "I don't know what I was doing" instead of an "I don't know
what I am doing" case.
2017-01-26 11:33:58 +01:00
wm4 bdbb3142ec client API: adjust opengl_cb hardware decoding comments
Replace the reprecated --hwdec-preload option.

Add a comment about cuvid/cuda decoding.
2017-01-26 11:32:09 +01:00
Ricardo Constantino d303ebd9b6 stream_lavf: add support for data URIs
Only FFmpeg supports them and they need to be in the format data://
like other protocols or prefixed with ffmpeg:// or lavf://.

Closes #4058
2017-01-25 15:40:56 +00:00
wm4 39adaf3dcc vf_lavfi: don't crash with VOs without hardware decoding support
When playing with VOs which do not provide mp_hwdec_ctx, vf->hwdec_devs
will remain NULL. This would make it crash on hwdec_devices_get_first(),
even if no hardware decoding or filters using hardware decoding were
involved.

Fixes #4064.
2017-01-25 08:32:35 +01:00
wm4 04376fa024 ad_lavc, vd_lavc: preserve codec_id/codec_type when setting params
avcodec_parameters_to_context() overwrites codec_type and codec_id. But
we already set these by passing the selected AVCodec to
avcodec_alloc_context3(). It's entirely possible that at least codec_id
is different when forcing codecs with --ad/--vd. It's probably better
not to cause confusion by overwriting them. It might even trigger
undefined behavior in libavcodec (how it behaves or whether codec_id is
supposed to be strictly set is unknown, though).
2017-01-25 08:30:14 +01:00
wm4 801fa486b0 ad_lavc, vd_lavc: move mpv->lavc decoder parameter setup to common code
This can be useful in other contexts.

Note that we end up setting AVCodecContext.width/height instead of
coded_width/coded_height now. AVCodecParameters can't set coded_width,
but this is probably more correct anyway.
2017-01-25 08:24:19 +01:00
octos ce803da90d manpage: add "A/V sync" to make it findable with Ctrl+f 2017-01-25 07:59:47 +01:00
Roland Hieber 194b26536b osc: fix crashes related to field eventresponder being nil
I'm still getting some crashes after issue #3210 was fixed in commit
5beb230690. It's hard to reproduce those
crashes, they happen maybe once a month, so I guess it could be a race
condition. But in any case, I don't see anything wrong in applying some
defensive programming here.

For reference, here is what was happening on 0.23.0-1 from Debian
testing:

  Playing: ytdl://usL5CeP_row
   (+) Video --vid=1 (*) (h264)
    (+) Audio --aid=1 --alang=und (*) (aac)
    [osc]
    [osc] stack traceback:
    [osc]	  @osc.lua:2074: in function 'process_event'
    [osc]	  @osc.lua:2246: in function 'cb'
    [osc]	  mp.defaults:107: in function 'fn'
    [osc]	  mp.defaults:60: in function 'handler'
    [osc]	  mp.defaults:339: in function 'handler'
    [osc]	  mp.defaults:448: in function 'call_event_handlers'
    [osc]	  mp.defaults:485: in function 'dispatch_events'
    [osc]	  mp.defaults:441: in function <mp.defaults:440>
    [osc]	  [C]: in ?
    [osc]	  [C]: in ?
    [osc] Lua error: @osc.lua:2074: attempt to index field 'eventresponder' (a nil value)

Note that the location is different from where issue #3210 happens.

Signed-off-by: Roland Hieber <rohieb@rohieb.name>
2017-01-25 07:58:45 +01:00
wm4 3b0e886193 command: fix potential crash for script-binding with multi-commands
"show-text test; script-binding display_stats" can potentially crash. It
sends a message event. None of the string arguments can be NULL, which
fails if cmd->key_name is NULL. This in turn can be due to commands
combined with ";" (basically the key association doesn't consider nested
commands).
2017-01-24 12:39:39 +01:00
wm4 63b34143a5 cue: accept lower-case cue commands
Some files appear to use them. In the sample I've seen, one field was
"Performer" instead of "PERFORMER".

This change is slightly risky, because it increases the chance of
misdetecting other formats as cue files.

Fixes #4057.
2017-01-24 08:57:51 +01:00
wm4 7be495b369 stream: check for playback aborts on reading too
Benefits demux_mkv.c, or demux_lavf.c during probing. In particular
demux_lavf.c can sometimes get "stuck" when reading from a slow/blocking
source, and if probing needs more than a few iterations.

Since this is a read of an atomic variable with relaxed semantics, this
should have no impact on reading speed at all, not even theoretically.
2017-01-24 08:46:17 +01:00
wm4 f762fc4230 av_common: remove old/unused timestamp passthrough code
This attempted to pass through double float timestamps in a bit exact
way by reinterpret casting them to int64_t. This usually worked, because
libavcodec (in decoding mode) is mostly not allowed to interpret
timestamps. libavcodec doesn't even know the unit of the timestamps,
unless the API user sets a timebase.

We've stopped doing this, and always set a timebase. Only ad_spdif.c
still used this (indirectly through mp_set_av_packet()), but doesn't
actually need timestamps on the packet. In fact, it's already explicitly
setting the packet timestamp fields to 0 before passing it to FFmpeg
API.

This code is unused, and the passthrough method wasn't terribly elegant
to begin with. Drop this code. Arbitrarily use AV_TIME_BASE_Q as
fallback in situations the passthrough was used.
2017-01-24 08:36:40 +01:00
wm4 d16ba1f7f6 build: new vaapi hwaccel API does not use av_image_copy_uc_from()
Not even Libav does. Whoops. The developer who wrote the FFmpeg code for
this said he could not find any improvements when using the "GPU memcpy"
; instead, it made it actually slower on some hardware.

It's not clear to what extent the "GPU memcpy" was needed for vaapi, but
hopefully not very much (see #2317).

This commit enables use of the new vaapi API by default with FFmpeg.
2017-01-24 08:22:10 +01:00
wm4 c06fd19d8f av_common: drop unused function
Unused since the AVCodecParameters change.
2017-01-24 08:15:38 +01:00
wm4 b14fac9afa build: replace some FFmpeg API checks with version checks
The FFmpeg versions we support all have the APIs we were checking for.
Only Libav missed them. Simplify this by explicitly checking for FFmpeg
in the code, instead of trying to detect the presence of the API.
2017-01-24 08:11:42 +01:00
wm4 6be58df8d1 ad_lavc: respect AV_FRAME_FLAG_DISCARD
Since we set "skip_manual", we can actually get frames with this set.
Currently, only AV_PKT_FLAG_DISCARD will trigger this flag, and only
mov.c sets the latter flags, so this is related to FFmpeg's half-broken
mp4 edit list support.
2017-01-24 08:04:53 +01:00
wm4 00eadcec8d sub: add option to force using video resolution for image subtitles
Basically for debugging and dealing with broken files.
2017-01-23 15:55:09 +01:00
wm4 3e89e061c2 sd_lavc: remove old broken heuristic
This core of this heuristic was once copied from MPlayer's spudec.c. I
think it was meant for the case when the resolution field was missing or
so.

I couldn't find a file for which this actually does something. On the
other hand, there are samples which actually have a smaller resolution
than 720x576, and which are broken by this old hack.

For subtitles that set no resolution (I'm not sure which codec/container
that would be), there's still the fallback on video resolution.

Just get rid of this hack. Also cleanup a bit. SD_CTRL_GET_RESOLUTION
hasn't been used since DVD menu removal. get_resolution() is left with 1
call site, and would be quite awkward to keep, so un-inline it.
2017-01-23 15:28:12 +01:00
wm4 5c942128d8 player: actually initialize/destroy MPContext.lock
Seems like quite on oversight.

For most of the better pthread implementations, pthread_mutex_init() on
an already 0-initialized memory block is probably a no-op, but of course
we should do things correctly. Also could setup analysis tools.
2017-01-22 15:24:13 +01:00
wm4 06c55ac6c3 charset_conv: fallback to interpreting subs as latin1 if iconv fails
For display purposes, it's better to show scrambled text - at least
that's a more actionable failure mode than spamming the terminal with
FFmpeg nonsense error messages.

This avoids the obnoxious and pointless

  "Invalid UTF-8 in decoded subtitles text; maybe missing -sub_charenc option"

FFmpeg error, which will be spammed on every single subtitle event. We
don't even have a -sub-charenc option, fuck FFmpeg.

Did I mention fuck FFmpeg yet? Because fuck FFmpeg.
2017-01-22 13:10:16 +01:00
wm4 310671e91a charset_conv: support minimum compatibility to utf8:... syntax
Because it's the most commonly used one, and trivial to support.
2017-01-22 13:06:36 +01:00
wm4 73858bb0cc player: remove --stream-capture option/property
This was excessively useless, and I want my time back that was needed to
explain users why they don't want to use it.

It captured the byte stream only, and even for types of streams it was
designed for (like transport streams), it was rather questionable.

As part of the removal, un-inline demux_run_on_thread() (which has only
1 call-site now), and sort of reimplement --stream-dump to write the
data directly instead of using the removed capture code.

(--stream-dump is also very useless, and I struggled coming up with an
explanation for it in the manpage.)
2017-01-21 17:19:01 +01:00
wm4 085dfdea32 command: rename framedrop properties
"drop-frame-count" -> "decoder-frame-drop-count"
"vo-drop-frame-count" -> "frame-drop-count"

This gets rid of the backwards "drop-frame" part in the name.

Maybe calling the new property "frame-drops" would be better, but there
are already a bunch of similar properties that end in "-count".
2017-01-20 17:01:29 +01:00
wm4 fd203ff16a options: refacactor how --opengl-dwmflush is declared
Same deal as previous commit, except this time we just readd it as lone
global option, and read it directly.
2017-01-20 14:03:34 +01:00
wm4 d890e0731c options: refactor how --opengl-dcomposition is declared
vo_opengl used to have it as sub-option, which made it very hard to pass
down option values to backends in a generic way (even if these options
were completely backend-specific). For --opengl-dcomposition we used a
VOFLAG to deal with this. Fortunately, sub-options are gone, and we can
just add it as global option.

Move the option to context_angle.c and add it as global option. I
thought about adding a mechanism to let backends declare options, which
would get magically picked up my m_config instead of having to add them
to the global option list manually (similar to VO vo_driver.options),
but decided against this complexity just for 1 or 2 backends. Likewise,
it could have been added as a single option to avoid the boilerplate of
an option struct, but then again there are probably going to be more
angle suboptions, and it's cleaner.
2017-01-20 13:40:59 +01:00
wm4 a35a5bb5f3 x11: pseudo HiDPI scaling
Scale the window by the assumed DPI scaling factor, using 96 DPI as
base. For example, a screen that reports 192 DPI is assumed to have a
DPI scale factor 2. The window will then be created with twice the size.

For robustness reasons, we accept only integer DPI scales between 1 and
9. We also error out if the X and Y scales are very different, as this
most likely indicates a multiscreen system with botched size reporting.

I'm not sure if reading the X server's DPI is such a good idea - maybe
the Xrdb "Xft.dpi" value should be used instead. The current method
follows what xdpyinfo does.

This can be disabled with --hidpi-window-scale=no.
2017-01-19 16:31:54 +01:00
wm4 4adfde5dd1 options: drop deprecated --sub-codepage syntax 2017-01-19 15:46:59 +01:00
wm4 13add62cf0 options: drop deprecated --vd/--ad codecs selection features
Only simple selection works now. Using "-" to terminate codec selection
remains in the code (might get undeprecated).
2017-01-19 15:38:58 +01:00
Akemi 717ee3ddba cocoa: don't constrain window frame for fullscreen
our constrainFrameRect prevents our window from positioning itself ontop
of the menubar, which is unwanted for a fullscreen window. this always
positioned our window vertically at -22/-23pt when going into fullscreen
because of the menubar. this bug doesn't show on newer versions of OS X
since the various flags we set force the window position. on OS X 10.9
though the fullscreen window was shifted 22pt downwards. even though
this bug doesn't show on newer OS X versions, it should still be fixed
for a possible behaviour changes in future version.

Fixes #4044
2017-01-19 14:59:51 +01:00
Akemi 9490b628c5 cocoa: don't init displaylink on reconfig
everytime we switched to a new video file a new displaylink was
initialised and started, but the old one was not stopped and released
beforehand. this lead to several displaylink callback calls per swap,
depending on how many files were switched beforehand. moving the
displaylink init call to the cocoa init functions will ever only init
one displaylink.

Fixes #4031
2017-01-19 14:58:15 +01:00
Akemi fe02e5023c cocoa: move updateBorder method to the protocol
we are calling the method on a NSWindow object that may not respond to
that call, since its a method of MpvVideoWindow. add the method to our
protocol and rename that protocol to reflect the change.
2017-01-19 14:54:22 +01:00
Akemi 202f9b218a cocoa: properly recover from toggleFullscreen fail
in some circumstances cocoa isn't able to enter or exit fullscreen but
we still set window sizes and flags accordingly. this leaves us in a
hanging state between fullscreen and window. it also prevents the
toggleFullscreen method and its events to work properly afterwards. in
that state it's impossible to enter or exit this 'semi-fullscreen'.
add a proper fallback to recover from this state.

Fixes #4035
2017-01-19 14:52:51 +01:00
Akemi 4fe45fb3a1 cocoa: fix window size in certain circumstances
a combination of starting from bundle and fullscreen used the standard
window size (960x480) from the pseudo GUI instead of the wanted video
size.
2017-01-19 14:51:11 +01:00
wm4 8cbb2b5e9a ad_spdif: log avformat errors 2017-01-19 12:44:28 +01:00
wm4 880bf54d7e player: actually let cache readahead after opening demuxer for prefetch
Disabling cache readahead by default until at least 1 track is selected
is mainly for external files and such, where you don't want them to use
up resources until they're actually used.

It doesn't make sense to disable the cache for the demuxer opened for
prefetch. Also, it's fine to let it do that for the main file too (doing
or not doing it is of little consequence). That saves us from having to
distinguish them.
2017-01-19 08:00:19 +01:00
wm4 06c8ec27f6 player: also log if completely prefetched URL is discarded
Seems like quite an important/interesting case?
2017-01-19 07:56:49 +01:00
wm4 e277fadd60 player: add prefetching of the next playlist entry
Since for mpv CLI, the player state is a singleton, full prefetching is
a bit tricky. We do it only on the demuxer layer.

The implementation reuses the old "open thread". This means there is
significant potential for regressions even if the new option is not
used. This is made worse by the fact that I barely tested this code.

The generic mpctx_run_reentrant() wrapper is also removed - this was its
only user, and its remains become part of the new implementation.
2017-01-18 19:02:50 +01:00
wm4 c54c3b6991 player: restructure cancel callback
As preparation for file prefetching, we basically have to get rid of
using mpctx->playback_abort for the main demuxer (i.e. the thing that
can be prefetched). It can't be changed on a running demuxer, and always
using the same cancel handle would either mean aborting playback would
also abort prefetching, or that playback can't be aborted anymore.

Make this more flexible with some refactoring.

Thi is a quite shitty solution if you ask me, but YOLO.
2017-01-18 17:52:05 +01:00
wm4 04858c0b83 player: move some minor demuxer setup code
In particular, move demux_set_ts_offset() out of the loader thread.
There's no discernible reason for that, probably.
2017-01-18 16:48:47 +01:00
wm4 e7d902bdc0 vo: log timings around flipping/waiting
Found those useful.
2017-01-18 12:14:26 +01:00
wm4 c522d0dfbd ad_spdif: fix obscure cases of AC3 passthrough
Apparently you set the native sample rate when passing through AC3.
This fixes passthrough with 44100 Hz AC3.

Avoid opening a decoder for this and only open the parser. (Hopefully
DTS will also support this some time in the future or so - having to
open a decoder just to get the profile is dumb.)
2017-01-18 10:22:28 +01:00
wm4 da8011c5df vaapi: detect new API on FFmpeg too, and disable it by default
Since the only way to detect the API is by a version check, this had to
wait until the patches were actually pushed to FFmpeg git (which now
happened).

Since this does not include the new magic GPU memcpy libavutil function
yet, the new vaapi code would be slower if copy mode (like vaapi-copy)
is used. This would be quite bad to use by default, so check for the
function, and if not present, disable the new vaapi code. This
effectively disables it by default on FFmpeg.

(We assume that if the new GPU memcpy exists, vaapi's AVHWFramesContext
implementation will use it.)
2017-01-18 08:17:29 +01:00
wm4 398e2d5d42 vaapi: fix va_surface_get_uncropped_size() for libavutil surfaces
Fixes vf_vavpp crashing with the new vaapi decode API.
2017-01-18 08:17:10 +01:00
wm4 a38283d5d9 vaapi: we don't need SSE intrinsics with the new API
libavutil does this for us. Although the new vaapi decode API does not
strictly introduce or even need av_image_copy_uc_from(), it's implied
that it will be present if the new decode API is present - even if it's
not, we can't use our own SSE code with it anyway.
2017-01-17 15:48:56 +01:00
wm4 9d68d8fb0f vo_opengl, vo_opengl_cb: better hwdec interop backend selection
Introduce the --opengl-hwdec-interop option, which replaces
--hwdec-preload. The new option allows explicit selection of the interop
backend.

This is relatively complex, and I would have preferred not to add this,
but it's probably useful to debug certain problems. In exchange, the
"new" option documents that pretty much any but the simplest use of it
will not be forward compatible.
2017-01-17 15:48:56 +01:00
wm4 e94890a5d6 vo_opengl_cb: cleanup messy option synchronization
Replace the old code, that played games to evade thread-safety issues,
with newer thread-safe option access functions.

This also means mp_opengl_create() doesn't need to cache the hwdec
settings anymore. (They're applied in mpv_opengl_cb_init_gl() instead.)
2017-01-17 15:48:56 +01:00
wm4 4b3b3a045a vdpau: reject decoding of non-4:2:0
Tried to decode a High 4:2:2 file, since libavcodec code seemed to
indicate that it's supported. Well, it decodes to garbage.

I couldn't find out why ffmpeg.c actually appears to reject this
correctly. The API seems to be fine with, just that the output is
garbage.

Add a hack for now.
2017-01-17 15:48:56 +01:00
wm4 9d69eae162 vd_lavc: always fail decoding immediately if copying hw surface fails
Successful decoding of a frame resets ctx->hwdec_fail_count to 0 - which
us ok, but prevents fallback if it fails if --vd-lavc-software-fallback
is set to something higher than 1.

Just fail it immediately, since failing here always indicates some real
error (or OOM), not e.g. a video parsing error or such, which we try to
tolerate via the error counter.
2017-01-17 15:48:56 +01:00
wm4 ff9f2c4b6e vdpau: use libavutil for surface allocation during decoding
Use the libavutil vdpau frame allocation code instead of our own "old"
code. This also uses its code for copying a video surface to normal
memory (used by vdpau-copy).

Since vdpau doesn't really have an internal pixel format, 4:2:0 can be
accessed as both nv12 and yuv420p - and libavutil prefers to report
yuv420p. The OpenGL interop has to be adjusted accordingly.

Preemption is a potential problem, but it doesn't break it more than it
already is.

This requires a bug fix to FFmpeg's vdpau code, or vdpau-copy (as well
as taking screenshots) will fail. Libav has fixed this bug ages ago.
2017-01-17 15:48:56 +01:00
wm4 cda31b71de vaapi: move AVHWFramesContext setup code to common code
In a way it can be reused. For now, sw_format and initial_pool_size
determination are still vaapi-specific. I'm hoping this can be eventally
moved to libavcodec in some way. Checking the supported_formats array is
not really vaapi-specific, and could be moved to the generic code path
too, but for now it would make things more complex.

hw_cuda.c can't use this, but hw_vdpau.c will in the following commit.
2017-01-17 15:48:56 +01:00
William Woodruff a4cdd8bb82 lua: close directory after reading its entries
Fixes #4045.
2017-01-17 08:22:13 +01:00
wm4 9850932722 build: prefix hwaccel decoder wrapper filenames with hw_
Should have done this a long time ago.

d3d.c remains as it is, because it's just a bunch of helper functions.
2017-01-17 08:11:12 +01:00
wm4 64041f963e cuda: fix 10 bit decoding
Messy mess. Unsure whether this will be resolved properly in FFmpeg.
2017-01-16 19:58:40 +01:00
Ricardo Constantino fb6481ecb5 stream_bluray: use proper 0-based idx
Even though the title list code was copied from FFmpeg/libbluray,
I didn't check that mpv used 0-based title indexing.

$ mpv bd://1 --bluray-device=. --msg-level=bd=v
[bd] Opening bd://
[bd] List of available titles:
[bd] idx:   1 duration: 00:00:36 (playlist: 00000.mpls)
[bd] idx:   2 duration: 01:31:30 (playlist: 00001.mpls)
[bd] idx:   3 duration: 00:00:50 (playlist: 00003.mpls)

bd://1 actually opens idx 2 from the list, not 1.
bd://mpls/1 opens playlist 00001.mpls as expected.

With this commit:
$ mpv bd://1 --bluray-device=. --msg-level=bd=v
[bd] Opening bd://
[bd] List of available titles:
[bd] idx:   0 duration: 00:00:36 (playlist: 00000.mpls)
[bd] idx:   1 duration: 01:31:30 (playlist: 00001.mpls)
[bd] idx:   2 duration: 00:00:50 (playlist: 00003.mpls)

should play the expected idx 1.
2017-01-16 16:03:15 +00:00
wm4 083e470091 manpage: add comment about --alpha being broken on X11/EGL/Mesa 2017-01-16 16:47:57 +01:00
wm4 1b1771f2a7 video: support filtering hardware frames via libavfilter
Requires a bunch of hacks:
- we access AVFilterLink.hw_frames_ctx. This is not a public API in
  FFmpeg and Libav. Newer FFmpeg provides an accessor
  (av_buffersink_get_hw_frames_ctx), but it's not available in Libav or
  the current FFmpeg release or Libav. We need this value after filter
  graph creation, so We have no choice but to access this.
  One alternative is making filter creation and format negotiation
  fully lazy (i.e. delay it and do it as filters are output), but this
  would be a huge change.
  So for now, we knowingly violate FFmpeg's and Libav's ABI and API
  constraints because they don't provide anything better.
  On newer FFmpeg, we use the (quite ugly) accessor, though.
- mp_image_params doesn't (and can't) have a field for the frames
  context AVBufferRef. So we pass it via vf_set_proto_frame(), and even
  more hacks.
- if a filter needs a hw context, but we haven't created one yet
  (because normally we create them lazily), it will fail at init.
- we allow any hw format now, although this could go horrible wrong.

Why all this effort? We could move hw deinterlacing filters etc. to
FFmpeg, which is a very worthy goal.
2017-01-16 16:10:39 +01:00
wm4 91fb7078e2 vf_lavfi: switch to AVBufferSrcParameters
Instead of using the awful older "API" that passed the parameters
formatted as string. AVBufferSrcParameters is also a prerequisite for
hardware frame filtering support.
2017-01-16 16:10:39 +01:00
wm4 8a23892d04 vo_opengl: hwdec_cuda: add yuv420p support
Because it allows easier testing of filters + hwdec.

Make the texture setup code a bit more generic so it doesn't get too
much of a mess. We also use the GL renderer utility function
gl_find_unorm_format(), which saves us additional work with OpenGL's
semi-redundant format specifiers.
2017-01-16 16:10:39 +01:00
wm4 3a85976f8e cuda: fix AVHWFramesContext initialization
This was partially done. Strangely it worked anyway.
2017-01-16 16:10:39 +01:00
wm4 6b00663755 vo_opengl: hwdec_cuda: export AVHWDeviceContext
So we can use it for filtering later.
2017-01-16 16:10:39 +01:00
wm4 bbdecb792a hwdec: add a AVBufferRef(AVHWDeviceContext) field
This makes "generic" interaction with libav* components easier.
2017-01-16 16:10:22 +01:00
Olivier Perret 348c610b68 lua: allow unregistration of idle handlers 2017-01-15 17:02:21 +01:00
wm4 397705b12c manpage: define stricter rules for C plugin return values
Just in case.
2017-01-14 17:41:04 +01:00
wm4 e91331e683 scripting: don't call dlclose() on C plugins
Can break things quite badly.

Example: reloading a plugin linked against GTK 3.x can cause a segfault
if you call dlclose() on it. According to GTK developers, unloading the
GTK library is unsupported.
2017-01-14 17:14:30 +01:00
wm4 191cdbd31e scripting: minor logging improvements
Give scripting backends a proper name, instead of calling everything
"scripts".

Log client exit directly in client.c, as that is more general (doesn't
change actual output).
2017-01-14 17:13:50 +01:00
wm4 ddaab3349b vd_lavc: demote software decoding message to verbose log level
If hardware decoding is enabled (via --hwdec anything), the player was
printing an informational message that software decdoing is used.
Basically, this confuses users, because they think there is a problem or
such. Just disable the message, it's semi-useless anyway.

This was suggested on IRC, after yet another user was asking why this
message was shown (with a follow up discussion which CPUs can decode
what kind of video codecs).
2017-01-13 18:52:07 +01:00
wm4 e48f1f31cb vf_lavfi: remove pixel format whitelist
Pointless now.
2017-01-13 18:43:35 +01:00
wm4 6c28824a92 vo_opengl: hwdec_vaegl: add a lie for compatibility
EGL rendering + new decode API didn't work due to a certain libva bug
with sort-of legacy API use hitting again. It will report the wrong
vaapi pixel format. It's old code and always nv12 anyway, so stop
worrying about it.
2017-01-13 18:43:35 +01:00
wm4 812128bab7 vo_opengl, vaapi: properly probe 10 bit rendering support
There are going to be users who have a Mesa installation which do not
support 10 bit, but a GPU which can decode to 10 bit. So it's probably
better not to hardcode whether it is supported.

Introduce a more general way to signal supported formats from renderer
to decoder. Obviously this is imperfect, because it still isn't part of
proper format negotation (for example, what if there's a vavpp filter,
which accepts anything). Still slightly better than before.

I don't know any way to probe for vaapi dmabuf/EGL dmabuf support
properly (in particular testing specific formats, not just general
availability). So we stay with the current approach and try to create
and map dummy surfaces on init to probe for support. Overdo it and check
all formats that AVHWFramesConstraints reports, instead of only NV12 and
P010 surfaces.

Since we can support unknown formats now, add explicitly checks to the
EGL/dmabuf mapper code to reject unsupported formats. I also noticed
that libavutil signals support for RGB0/BGR0, but couldn't get it to
work. Remove the DRM formats that are unused/didn't work the way I tried
to use them.

With this, 10 bit decoding + rendering should work, provided you have
a capable CPU and a patched Mesa. The required Mesa patch adds support
for the R16 and GR32 formats. It was sent by a Kodi developer to the
Mesa developer mailing list and was not accepted yet.
2017-01-13 18:43:35 +01:00
wm4 d9376fc86f vaapi: always create AVHWDeviceContext on init
For convenience. Since we still have code that works even if creating a
AVHWDeviceContext fails, failure is ignored. (Although currently, it
succeeds creation even with the stale/abandoned vdpau wrapper driver.)
2017-01-13 13:35:31 +01:00
wm4 b26ab4d08c config: do not resolve default profile during "include" processing
Application of options in the default section is "delayed" until the
whole config file is read in order to allow profile forward references.
This was run at the end of parsing a config file - but because of
"include" options, this means it's not always called at the end of the
main config file.

Use the recursion counter to prevent it from being processed after each
"include" option. This also gets rid of the resulting unintended
infinite recursion (which eventually stopped and failed loading the
config file) due to m_config_finish_default_profile() processing the
"include" option again.

Fixes #4024.
2017-01-13 12:02:46 +01:00
wm4 88dfb9a5e7 vo_opengl: hwdec_vaegl: remove redundant vaapi surface format check
For surfaces allocated by libavutil, we assume that the sw_format (i.e.
in hw_subfmt in mp_image_params) is always correct. The API guarantees
that it explicitly sets the equivalent vaapi format on surface
allocation.

For surfaces allocated by mpv's old vaapi code, we explicitly retrieve
the format right after decoding. Unless the driver magically changes the
format asynchronously, it will still be correct once the surface reaches
the renderer.

In both cases, checking the format again is obviously redundant. In
addition, it doesn't require us to maintain a libva fourcc <-> mpfmt
table and the va_fourcc_to_imgfmt() function. This also unbreaks 10 bit
rendering support (still disabled by default).
2017-01-13 10:28:58 +01:00
wm4 6de00d10c8 vo_opengl: hwdec_vaegl: fix terminology in comment
Bad idea to call a component "pixel" - that's true only for the Y plane.
2017-01-13 10:22:11 +01:00
Mark Thompson 14010e7cf6 vo_opengl: hwdec_vaegl: DRM_FORMAT_GR16 was renamed to DRM_FORMAT_GR32
Signed-off-by: wm4 <wm4@nowhere>
2017-01-13 10:18:54 +01:00
wm4 e4ae62da52 manpage: fix a typo 2017-01-13 10:18:31 +01:00
wm4 44e06b70d5 player: add experimental C plugin interface
This basically reuses the scripting infrastructure.

Note that this needs to be explicitly enabled at compilation. For one,
enabling export for certain symbols from an executable seems to be quite
toolchain-specific. It might not work outside of Linux and cause random
problems within Linux.

If C plugins actually become commonly used and this approach is starting
to turn out as a problem, we can build mpv CLI as a wrapper for libmpv,
which would remove the requirement that plugins pick up host symbols.

I'm being lazy, so implementation/documentation are parked in existing
files, even if that stuff doesn't necessarily belong there. Sue me, or
better send patches.
2017-01-12 17:45:11 +01:00
wm4 ac98ff71dd client API: fix freeze when destroying mpv_handle before mpv_initialize
We have to perform some silly acrobatics to make the playback_thread()
exit, as the mpv_command() will error out with MPV_ERROR_UNINITIALIZED.

Test case: mpv_terminate_destroy(mpv_create())

Reported by a user on IRC.
2017-01-12 16:29:37 +01:00
wm4 e552f5342e vaapi: fix typo 2017-01-12 14:22:46 +01:00
wm4 aecedd9250 mp_image_pool: fix build on Libav
Mismatches between header file symbol visibility in FFmpeg and Libav.
Again.
2017-01-12 14:19:16 +01:00
wm4 5174add43a vo_opengl: hwdec_vaegl: add experimental P010 support
This does not work, because Mesa has no support for the proposed
DRM_FORMAT_R16 and DRM_FORMAT_GR16 formats. It's also untested of
course.

As long as video/decode/vaapi.c doesn't hand down P010 surfaces, this is
fine anyway.

This can be tested by removing the code that disables P010 output:

diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c
--- a/video/decode/vaapi.c
+++ b/video/decode/vaapi.c
@@ -55,13 +55,6 @@ static int init_decoder(struct lavc_ctx *ctx, int w, int h)

     assert(!ctx->avctx->hw_frames_ctx);

-    // If we use direct rendering, disallow 10 bit - it's probably not
-    // implemented yet, and our downstream components can't deal with it.
-    if (!p->own_ctx && required_sw_format != AV_PIX_FMT_NV12) {
-        MP_WARN(ctx, "10 bit surfaces are currently supported.\n");
-        return -1;
-    }
-
2017-01-12 14:01:09 +01:00
wm4 3d7b3996d2 vaapi: explicitly reject 10 bit surfaces outside of copy mode
Rendering support in Mesa probably doesn't exist yet. In theory it might
be possible to use VPP to convert the surfaces to 8 bit (like we do it
with dxva2/d3d11va as ANGLE doesn't support rendering 10 bit surface
either), but that too would require explicit mechanisms. This can't be
implemented either until I have a GPU with actual support.
2017-01-12 14:01:09 +01:00
wm4 e618b1b352 vaapi: handle image copying for vaapi-copy in common code
Other hwdecs will also be able to use this (as soon as they are switched
to use AVHWFramesContext).

As an additional feature, failing to copy back the frame counts as
hardware decoding failure and can trigger fallback. This can be done
easily now, because it needs no way to communicate this from the hwaccel
glue code to the common code.

The old code is still required for the old decode API, until we either
drop or rewrite it. vo_vaapi.c's OSD code (fuck...) also uses these
surface functions to a higher degree.
2017-01-12 14:00:19 +01:00
wm4 fb4ae3c06c cuda: use libavutil functions for copying hw surfaces to memory
mp_image_hw_download() is a libavutil wrapper added in the previous
commit. We drop our own code completely, as everything is provided by
libavutil and our helper wrapper.

This breaks the screenshot code, so that has to be adjusted as well.
2017-01-12 13:59:35 +01:00
wm4 06b30cc81f vaapi: use libavutil functions for copying hw surfaces to memory
Makes va_surface_download() call mp_image_hw_download() for
libavutil-allocated surfaces, which in turn calls
av_hwframe_transfer_data().

mp_image_hw_download() is actually not specific to vaapi, and can be
used for any hw surface allocated by libavutil.
2017-01-12 13:58:28 +01:00
wm4 162c2e2d00 vd_lavc, mp_image: remove code duplication for AVFrame<->mp_image
Mostly affects conversion of the colorimetric parameters.

Not changing AV_FRAME_DATA_MASTERING_DISPLAY_METADATA handling - that's
too messy, as decoders typically output it for keyframes only, and would
require weird caching that can't even be done on the level of the frame
rewrapping functions.
2017-01-12 13:58:28 +01:00
wm4 26d25d567f vaapi: properly set hw_subfmt field with new decode API
This fixes direct rendering with hwdec_vaegl.c.

The code duplication between update_image_params() and
mp_image_copy_fields_from_av_frame() is quite annoying,
bit will have to be resolved in another commit.
2017-01-12 13:58:28 +01:00
wm4 a51e991b56 vaapi: set our own context in AVHWFramesContext not AVHWDeviceContext
AVHWDeviceContext.user_opaque is reserved to libavutil under certain
circumstances, while AVHWFramesContext.user_opaque is truly free for use
by us. It's slightly simpler too.
2017-01-12 13:58:28 +01:00
wm4 fcb970492c wscript: slightly simplify configure check for new vaapi decode API
We can drop the weird acrobatics with the is_ffmpeg. We can distinguish
them directly within the vaapi check, duh.
2017-01-12 13:57:52 +01:00
wm4 32186de367 vaapi: turn all mpv users into a living experiment
Dummy out the locking around all libva calls, which in theory shouldn't
be needed anyway. Two years ago, these were added to prevent crashes
with vaapi decoding and direct rendering (vo_opengl/vo_vaapi) active.

It's not clear whether these are really needed - theory strongly points
towards no. Some developers familiar with vaapi expressed similar
thoughts. But past experience says differently. So let's try
without the locking for a while and see what happens.
2017-01-11 18:47:37 +01:00
wm4 a41bedfce5 vaapi: add missing config.h include
A recent commit added code that checks some HAVE_ symbols in this file.
No config.h include was added, so they could be unavailable and break
compilation (in practice, just --hwdec=vaapi-copy would break).

Not sure how I missed this, maybe waf defined these symbols on the
compiler command line for some reason.
2017-01-11 18:45:04 +01:00
wm4 2afef344fb vaapi: support new libavcodec vaapi API
The old API is deprecated, and libavcodec prints a warning at runtime.
The new API is a bit nicer and does many things for you, such as
managing the underlying hwaccel decoder. libavutil also provides code
for managing surfaces (we use their surface pool).

The new code does not contain any code from the original MPlayer VAAPI
patch (that was used as base for some of the vaapi code in mpv). Thus
the new code is LGPL.

The new API actually does not add any visible symbols, so the only way
to detect it is a version check. Of course, the versions overlap
between FFmpeg and Libav, which requires additional care. The new
API did not get merged into FFmpeg yet, so there's no check for
FFmpeg.
2017-01-11 16:34:18 +01:00
wm4 f4263922c7 vaapi: add hacks to support vaapi surfaces created by libavutil
We usually attach some significant metadata and context to "our"
surfaces. Surfaces created by libavutil (such as we plan to do it when
using the new vaapi decode API in the following commit) don't have this
context, so e.g. copy decoding mode won't work.

Add tons of hacks to make this somehow work.

Eventually we will use libavutil's mechanisms and drop the hacks.
2017-01-11 16:34:18 +01:00
wm4 9fd986b53a vaapi: move standalone display creation code to common code
Preparation for the following commits.
2017-01-11 16:34:18 +01:00
wm4 7dc74ee037 vaapi: rename vaapi.c to vaapi_old.c
vaapi.c will be reintroduced with the new code using the new libavcodec
vaapi API.
2017-01-11 16:34:18 +01:00
wm4 5f449fc11f video: share hwdec extra surface count between backends
It's a quite important and fragile magic number.
2017-01-11 16:34:18 +01:00
Akemi fee5ceccad cocoa: don't change Space on quit in fullscreen
circumvent undefined behavior when quitting in
fullscreen.

Fixes #3957
2017-01-11 15:00:47 +01:00
Akemi 449eb208f4 cocoa: rate limit video output
the display refresh rate can't be estimated
correctly in some cases and just increases till it
turns off display-resample. cases are
off-screen rendering (different space), mpv
being completely hidden behind another window or
the mission control view.
this utilise the unused displaylink callback to
limit the refresh rate to the actual display
refresh rate.
2017-01-11 14:20:32 +01:00
Akemi 46b74a38f1 cocoa: add border cycling
Fixes #2430
2017-01-11 14:17:33 +01:00
Akemi a05c5b4ec6 cocoa: fix handling of geometry option
This flips the y-coordinate to be consistent with
other platforms and the manual. furthermore it
fixes an unwanted behaviour of the cocoa
convertRectFromBacking method, where the x- and
y-coordinate was divided by the same factor as the
width and height instead of placing the new scaled
rectangle at the same relative position as the
original unscaled rectangle, by manually
calculating the new position.

Fixes #3867
2017-01-11 14:15:10 +01:00
wm4 cbd8abcbff audio: restructure decode loop
Same deal as with video. Including the EOF handling.

(It would be nice if this code were not duplicated, but right now we're
not even close to unifying the audio and video code paths.)
2017-01-11 11:58:32 +01:00
wm4 f8baae5854 vd_lavc: inline a function
There's only 1 caller now, so having it as separate function doesn't
make too much sense, and makes the code less readable, if anything.
2017-01-11 11:04:59 +01:00
wm4 77320ad45e video: make decoder EOF reporting explicit
This is simpler and more robust, especially for the hwdec fallback case.
The most annoying issue is that C doesn't support multiple return values
(or sum types), so the decode call gets all awkward.

The hwdec fallback case does not need to try to produce some output
after the fallback anymore. Instead, it can use the normal "replay"
code path.

We invert the "eof" bool that vd_lavc.c used internally. The
receive_frame decoder API returns the inverse of EOF, because
returning "true" from the decode function if EOF was reached
feels awkward.
2017-01-11 11:02:57 +01:00
wm4 902424d065 vd_lavc: fix some leaks and a discarded frame on hwdec fallback
Wasn't a problem in most normal scenarios.
2017-01-11 08:34:08 +01:00
wm4 c2c065913b vd_lavc: move end-of-probing code out of user notification if condition
Usually they happen at the same time, but conflating them is still a bit
unclean and could possibly cause problems in the future. It's also
really unnecessary.
2017-01-11 08:27:32 +01:00
wm4 ee66efeb99 vd_lavc: return proper error codes from get_buffer2 callback
-1 is essentially random and usually the same as AVERROR(EPERM).
2017-01-11 08:26:32 +01:00
wm4 c000b37e9a vd_lavc: complicated improved fallback behavior for --hwdec=cuda
The ffmpeg cuda wrappers need more than 1 packet for determining whether
hw decoding will work. So do something complicated and keep up to 32
packets when trying to do hw decoding, and replay the packets on the
software decoder if it doesn't work.

This code was written in a delirious state, testing for regressions and
determining whether this is worth the trouble will follow later. All mpv
git users are alpha testers as of this moment.

Fixes #3914.
2017-01-10 16:49:06 +01:00
wm4 ed937b6eca video: restructure decode loop
Basically change everything. Why does the code get larger? No idea.
2017-01-10 16:20:02 +01:00
wm4 4e25feda0d player: change aspects of cover art handling
Cover art handling is a disgusting hack that causes a mess in all
components. And this will stay this way. This is the Xth time I've
changed cover art handling, and that will probably also continue.

But change the code such that cover art is injected into the demux
packet stream, instead of having an explicit special case it in the
decoder glue code. (This is somewhat more similar to the cover art hack
in libavformat.)

To avoid that the over art picture is decoded again on each seek, we
need some additional "caching" in player/video.c. Decoding it after each
seek would work as well, but since cover art pictures can be pretty
huge, it's probably ok to invest some lines of code into caching it.

One weird thing is that the cover art packet will remain queued after
seeks, but that is probably not an issue.

In exchange, we can drop the dec_video.c code, which is pretty
convenient for one of the following commits. This code duplicates a
bunch of lower-level decode calls and does icky messing with this weird
state stuff, so I'm glad it goes away.
2017-01-10 15:43:02 +01:00
wm4 334cad57c6 demux: rename a function
Also extend the comment on it.
2017-01-10 12:27:37 +01:00
wm4 0e8b9ed228 demux: uninline ds_get_packets()
It has only 1 caller, and is too far appart within the file. I think it
used to have multiple callers, but now it just doesn't make any sense to
keep it separate anymore.
2017-01-10 11:54:34 +01:00
wm4 9c5cbbf5d7 vo_drm: remove 2 redundant include statements
They're already pulled in by drm_common.h.
2017-01-09 16:23:27 +01:00
wm4 854651f4f5 drm: include <poll.h> instead of <sys/poll.h>
I'm not sure what systems have <sys/poll.h> (maybe there are historical
reasons why some would), but POSIX defines <poll.h>. Although this code
is full of highly OS specific calls (like ioctl()), there's no reason
not to use the more standard include path.
2017-01-09 16:21:28 +01:00
wm4 5ab11016c8 command: remove worthless error handling code
The property calls will always succeed anyway. On the other hand, the
error handling is kind of incomplete (doesn't check setting ab-loop-a
when ab-loop-b is also set), so drop this code.
2017-01-09 13:39:38 +01:00
wm4 0ae753bc3d client API: handle missing MPV_FORMAT_BYTE_ARRAY case in compare_value
Since there's no property yet that uses this type, and the code is used
for property change detection only. this is dead code. Add it anyway for
completeness.
2017-01-09 13:38:17 +01:00
wm4 42a60143c2 demux_mkv: ebml: turn an int shift into int64
This was probably the intention all along. But I honestly have no idea
what this code even does.

Due to what ebml_read_vlen_int() is used for, this is unlikely to have
mattered anyway as it rarely/never reads huge values. Which is probably
why this has worked for over a decade.
2017-01-09 13:37:04 +01:00
wm4 5d7f881bdc audio/out/push: merge if branches with same condition
Cosmetic change.
2017-01-09 13:32:04 +01:00
wm4 afcf3e154a cache: remove redundant free()
This code used to check/free multiple things, so the argument to free()
was not always NULL. After the code was simplified, the free() became
redundant.
2017-01-09 13:18:11 +01:00
wm4 c4ad2732f9 audio: stop being dumb
Obvious mistake: we entered EOF drain mode if the decoder returned
AD_WAIT, which is very wrong. AD_WAIT means we should retry after
waiting for a while (or to be precise, until the demuxer/decoder
have more data). We should just pass down this status, and not
change the audio chain state.

This was exposed by a libavfilter EOF handling bug. Feeding a filter
chain with af_dynaudnorm, and sending an EOF before a frame is returned
makes it stuck and keeps returning EAGAIN, instead of returning the
buffered audio. In combination with the bug at hand, which entered
EOG drain mode, it could happen that it got stuck due to libavfilter
discarding buffered data each time the demuxer ran out of data.

Fixes #3997.
2017-01-08 14:47:53 +01:00
wm4 e0f25010c7 vo_opengl: replace 2 memsets
Cosmetic change.
2017-01-08 11:22:55 +01:00
wm4 c2fdedc2ec client API: correct a comment in the API doxygen 2017-01-08 10:05:07 +01:00
wm4 2ce5523893 vaapi: set libva message callbacks
This is available since the first commit after libva 0.39.4. Since the
version wasn't bumped since, we just check some random other symbol that
was added since (I'd rather not add a configure check).

The libva message callback repeats the endlessly repeated API mistakes
of libraries using global message callback handlers. But it's the only
way to shut up libva's dumb messages to stderr, so add something
complicated and dumb to workaround libva's stupidity.
2017-01-08 07:45:23 +01:00
wm4 d230194f62 ta: remove TA_FREEP NULL check
The NULL check triggers a gcc warning when passing the address of a
variable to it. I was about to silence the warning with some equivalent
code (that just happens to shut up gcc), but then I decided to remove
the NULL check as I don't see a reason why we should allow this. We
don't use it in the existing code anyway (all callers do something like
TA_FREEP(&structptr->member), which is always non-NULL).

Also fix some of the macro argument "quoting".
2017-01-08 07:32:44 +01:00
wm4 f2a24ccfa5 vaapi: rearrange va_initialize() internals and fix double-free on error
Just some minor refactoring within va_initialize() as preparation for
the next commit.

Also, do not call vaTerminate(display) on failures. All callers already
do this, so this would have led to a double-free.
2017-01-08 07:32:44 +01:00
Stefano Pigozzi 68d737bd92 build: always run code generators before compiling 2017-01-07 15:00:22 +01:00
Rostislav Pehlivanov c17c26f404 context_wayland: do not call vo_wayland_request_frame() upon bufferswap
vo_wayland_wait_events() is going to return when its time to swap the
buffers anyway, calling request_frame() before makes no sense.

Fixes the constant high CPU usage by the compositor when mpv is paused
and the window is in view.
2017-01-07 10:29:15 +00:00
wm4 d3eafc976d manpage: document x11probe OpenGL backend
Fixes #3994.
2017-01-06 22:40:59 +01:00
wm4 7aca328eb1 waf: don't discard line endings when using file2string.py 2017-01-05 16:48:27 +01:00
wm4 0b9f1e0323 TOOLS/file2string.py: fix standalone invocation 2017-01-05 16:45:26 +01:00
Stefano Pigozzi 737e3b1758 build: use matroska.py & file2string.py as python modules 2017-01-05 11:25:18 +01:00
pavelxdd 0e7dd6d4ff win32: fix for wm_syscommand
According to MSDN, in WM_SYSCOMMAND messages, the four low-order
bits of the wParam parameter are used internally by the system.
To obtain the correct result when testing the value of wParam,
an application must combine the value 0xFFF0 with the wParam
value by using the bitwise AND operator.
2017-01-05 17:23:03 +11:00
wm4 2ff0745ddb player: remove dysfunctional edition switching OSD code
Was intended to show a "nice" message on edition switching. In practice,
the message was never visible. The OSD code checks whether a demuxer is
loaded, and if not, discards the message - meaning if the OSD code
happened to run before the demuxer was fully loaded, no message was
shown. This is apparently a regression due to extensions to the OSD and
the situations in which it can be used.

Remove the broken code since it's too annoying to fix. Instead, a
default property message will be shown, which is a bit uglier, but
actually not too unuseful.
2017-01-05 00:07:28 +01:00
wm4 f34faca910 client API: fix mpv_set_property() return value before init
Did not return success as success.

Fixes #3988.
2017-01-04 15:08:41 +01:00
wm4 43386a7c92 af_lavfi, vf_lavfi: work around recent libavfilter EOF bug
Looks quite like a bug. If you have a filter chain with only the
dynaudnorm filter, and send call av_buffersrc_add_frame(s, NULL), then
subsequent av_buffersink_get_frame() calls will return EAGAIN instead of
EOF.

This was apparently caused by a recent change in FFmpeg.

Some other circumstances (which I didn't fully analyze and which is due
to the playloop's absurd temporary-EOF behavior on seeks) then led the
decoder loop to send data again, but since libavfilter was stuck in the
EOF state now, it could never recover. It kept sending new input (due to
missing output), until the demuxer refused to return more audio packets.
Each time a filter error was printed.

Fortunately, it's pretty easy to workaround. We just mark the p->eof
flag as we send an EOF frame to libavfilter. The p->eof flag is used
only to recover from temporary EOF: it resets the filter if new data is
available again. We don't care much about av_buffersink_get_frame()
returning a broken EAGAIN state in this situation and essentially ignore
it, meaning if we get EAGAIN after sending EOF, we assume effectively
that EOF was fully reached.
2017-01-02 18:13:08 +01:00
wm4 8e41f314f1 Update copyright year
What kind of bullshit forces you to do this every year anyway.
2017-01-01 19:09:05 +01:00
wm4 0067d1dbef vo_opengl: egl: handle potential eglChooseConfig failures
This is actually a pretty important fix. eglChooseConfig() might be the
first thing that fails when porobing for desktop GL / ES2 / ES3 support,
because EGL_RENDERABLE_TYPE is set values specific to the underlying
APIs.

Not sure how the hell this worked before. EGL 1.4 implementations
certainly could fail the call with EGL_BAD_ATTRIBUTE if
EGL_RENDERABLE_TYPE has EGL_OPENGL_ES3_BIT set. It's quite possible that
many EGL implementations tolerate invalid EGLConfig values steming from
uininitialized EGLConfig values (and eglCreateWindowSurface() even is
specified to return EGL_BAD_CONFIG error code for "not valid"
EGLConfigs).
2016-12-31 14:58:46 +01:00
wm4 5ed4119057 vo_opengl: egl: fix depth size parameter
This was accidentally flipped from 0 to 1 in a previous commit. Actually
simply remove it, because 0 is the default value for this parameter
anyway.
2016-12-30 21:45:55 +01:00
wm4 b14fc38590 vo_opengl: x11egl: fix alpha mode
The way it should (probably) work is that selecting a RGBA framebuffer
format will simply make the compositor use the alpha. It works this way
on Wayland. On X11, this is... not done. Instead, both GLX and EGL
report two FB configs, which are exactly the same, except for the
platform-specific visual. Only the latter (non-default) points to a
visual that actually has alpha. So you can't make the pure GLX and EGL
APIs select alpha mode, and you have to override manually.

Or in other words, alpha was hacked violently into X11, in a way that
doesn't really make sense for the sake of compatibility, and forces API
users to wade through metaphorical cow shit to deal with it.

To be fair, some other platforms actually also require you to enable
alpha explicitly (rather than looking at the framebuffer type), but they
skip the metaphorical cow shit step.
2016-12-30 20:04:47 +01:00
wm4 58a0c43cf4 vo_opengl: x11: move RGBA visual test to x11_common.c
So that the EGL code can use it too.

Also print the actual FB config ID, instead of nonsense. (I _think_ once
in the past a certain GLX implementation just used numeric config IDs
casted to EGLConfig - or at least that would explain this nonsense.)
2016-12-30 20:04:32 +01:00
wm4 7033a4334b vo_opengl: egl_helpers: add a way to override config selection
Preparation for the following commits. Since at least theoretically the
config selection depends on the context type (EGL_RENDERABLE_TYPE has
separate bits for ES 2, ES 3, and desktop GL), doing it any other way
would be too painful.
2016-12-30 20:03:50 +01:00
wm4 d4e7b981bf vo_opengl: egl_helpers: add a way to pass more options
For X11 garbage we have to pass some annoying parameters to EGL context
creation. Add some sort of extensible API, so that adding a new
parameter doesn't break all callers. We still want to keep it as a
single function, because it's so nice isolating all the EGL nonsense API
boilerplate like this. (Did I mention yet that X11 and EGL are garbage?)

Also somewhat simplifies the vo_flags mess in the helper internals.
2016-12-30 20:03:17 +01:00
wm4 b9cebf180b Prefix libavcodec CODEC_FLAG_ constants with AV_
The unprefixed versions are silently deprecated.
2016-12-29 07:37:31 +01:00
wm4 35716b53db charset_conv: fix "auto" fallback with uchardet not compiled
Tried to open iconv with "auto" as source codepage, instead of using
the latin1 fallback. This also neutralizes the libavcodec dumbass
UTF-8 check, which discards subtitles not in UTF-8 and shows an
error message for ffmpeg CLI instead.

Fixes #3954.
2016-12-28 15:31:25 +01:00
Niklas Haas 22a22322cb vo_opengl: partially fix rotation for 4:2:2 content
The chroma alignment renormalization code forgot to account for the fact
that the chroma subsampling ratio has to be rotated.

Unfortunately, doing it this way seems to have somewhat broken the
chroma offset rotation logic for odd-sized subsampled image files. While
this is a bug, it's much, much less noticeable, so it's not nearly as
important as the bug this change fixes. Either way, a future patch needs
to still revise this logic, ideally by redesigning the entire rotation
mechanism.
2016-12-28 15:10:48 +01:00
Akemi a283b35556 cocoa: fix build on OS X 10.9
fixes #3946
2016-12-26 22:53:04 +01:00
Ricardo Constantino 321dd794c8 osc: bottombar/topbar: make chapter markers slightly bigger
1px squares to 2px triangles and layout=box markers from 1px
squares to 2x1px rectangles.

While we're at it, wrap a few lines to 80col again.
2016-12-25 15:44:11 +00:00
174 changed files with 5257 additions and 2378 deletions

No files matched your search

+4 -6
View File
@@ -101,18 +101,16 @@ mpv uses C99 with K&R formatting, with some exceptions.
do_something();
}
```
- If the body of an if statement uses braces, the else branch should also
use braces (and reverse).
- If the if has an else branch, both branches should use braces, even if they're
technically redundant.
Example:
```C
if (a) {
// do something
something();
something_else();
} else {
one_line();
} else {
one_other_line();
}
```
- If an if condition spans multiple physical lines, then put the opening brace
+40 -3
View File
@@ -51,7 +51,8 @@ The rest of the lines belong to one of these classes:
1) An empty or commented line. A comment starts with ``#``, which must be the
first character in the line. The rest of the line (up until the next line
break) is ignored. An empty line has 0 bytes between two line feed bytes.
2) A segment entry in all other cases.
2) A header entry if the line starts with ``!``.
3) A segment entry in all other cases.
Each segment entry consists of a list of named or unnamed parameters.
Parameters are separated with ``,``. Named parameters consist of a name,
@@ -63,8 +64,8 @@ Syntax::
segment_entry ::= <param> ( <param> ',' )*
param ::= [ <name> '=' ] ( <value> | '%' <number> '%' <valuebytes> )
The ``name`` string can consist of any characters, except ``=%,;\n``. The
``value`` string can consist of any characters except of ``,;\n``.
The ``name`` string can consist of any characters, except ``=%,;\n!``. The
``value`` string can consist of any characters except of ``,;\n!``.
The construct starting with ``%`` allows defining any value with arbitrary
contents inline, where ``number`` is an integer giving the number of bytes in
@@ -94,6 +95,42 @@ to ``20``, ``param3`` to ``value,escaped``, ``param4`` to ``value2``.
Instead of line breaks, the character ``;`` can be used. Line feed bytes and
``;`` are treated equally.
Header entries start with ``!`` as first character after a line break. Header
entries affect all other file entries in the EDL file. Their format is highly
implementation specific. They should generally follow the file header, and come
before any file entries.
MP4 DASH
========
This is a header that helps implementing DASH, although it only provides a low
level mechanism.
If this header is set, the given url designates an mp4 init fragment. It's
downloaded, and every URL in the EDL is prefixed with the init fragment on the
byte stream level. This is mostly for use by mpv's internal ytdl support. The
ytdl script will call youtube-dl, which in turn actually processes DASH
manifests. It may work only for this very specific purpose and fail to be
useful in other scenarios. It can be removed or changed in incompatible ways
at any times.
Example::
!mp4_dash,init=url
The ``url`` is encoded as parameter value as defined in the general EDL syntax.
It's expected to point to an "initialization fragment", which will be prefixed
to every entry in the EDL on the byte stream level.
The current implementation will
- ignore stream start times
- use durations as hint for seeking only
- not adjust source timestamps
- open and close segments (i.e. fragments) as needed
- not add segment boundaries as chapter points
- require full compatibility between all segments (same codec etc.)
Timestamp format
================
+18
View File
@@ -19,6 +19,24 @@ Interface changes
::
--- mpv 0.24.0 ---
- deprecate --hwdec-api and replace it with --opengl-hwdec-interop.
The new option accepts both --hwdec values, as well as named backends.
A minor difference is that --hwdec-api=no (which used to be the default)
now actually does not preload any interop layer, while the new default
("") uses the value of --hwdec.
- drop deprecated --ad/--vd features
- drop deprecated --sub-codepage syntax
- rename properties:
- "drop-frame-count" to "decoder-frame-drop-count"
- "vo-drop-frame-count" to "frame-drop-count"
The old names still work, but are deprecated.
- remove the --stream-capture option and property. No replacement.
(--record-file might serve as alternative)
- add --sub-justify
- add --sub-ass-justify
- internally there's a different way to enable the demuxer cache now
it can be auto-enabled even if the stream cache remains disabled
--- mpv 0.23.0 ---
- remove deprecated vf_vdpaurb (use "--hwdec=vdpau-copy" instead)
- the following properties now have new semantics:
+11 -8
View File
@@ -940,15 +940,19 @@ Property list
Total A-V sync correction done. Unavailable if audio or video is
disabled.
``drop-frame-count``
``decoder-frame-drop-count``
Video frames dropped by decoder, because video is too far behind audio (when
using ``--framedrop=decoder``). Sometimes, this may be incremented in other
situations, e.g. when video packets are damaged, or the decoder doesn't
follow the usual rules. Unavailable if video is disabled.
``vo-drop-frame-count``
``drop-frame-count`` is a deprecated alias.
``frame-drop-count``
Frames dropped by VO (when using ``--framedrop=vo``).
``vo-drop-frame-count`` is a deprecated alias.
``mistimed-frame-count``
Number of video frames that were not timed correctly in display-sync mode
for the sake of keeping A/V sync. This does not include external
@@ -1331,8 +1335,8 @@ Property list
This is known only once the VO has opened (and possibly later). With some
VOs (like ``opengl``), this might be never known in advance, but only when
the decoder attempted to create the hw decoder successfully. (Using
``--hwdec-preload`` can load it eagerly.) If there are multiple drivers
loaded, they will be separated by ``,``.
``--opengl-hwdec-interop`` can load it eagerly.) If there are multiple
drivers loaded, they will be separated by ``,``.
If no VO is active or no interop driver is known, this property is
unavailable.
@@ -1341,6 +1345,9 @@ Property list
multiple interop drivers for the same hardware decoder, depending on
platform and VO.
This is somewhat similar to the ``--opengl-hwdec-interop`` option, but
it returns the actually loaded backend, not the value of this option.
``video-format``
Video format as string.
@@ -1545,10 +1552,6 @@ Property list
This property is experimental and might be removed in the future.
``stream-capture`` (RW)
A filename, see ``--stream-capture``. Setting this will start capture using
the given filename. Setting it to an empty string will stop it.
``tv-brightness``, ``tv-contrast``, ``tv-saturation``, ``tv-hue`` (RW)
TV stuff.
+59
View File
@@ -14,3 +14,62 @@ mpv, further documentation is spread over a few places:
- http://mpv.io/manual/master/#list-of-input-commands
- http://mpv.io/manual/master/#properties
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
C PLUGINS
=========
You can write C plugins for mpv. These use the libmpv API, although they do not
use the libmpv library itself.
Currently, they must be explicitly enabled at build time with
``--enable-cplugins``. They are available on Linux/BSD platforms only.
C plugins location
------------------
C plugins are put into the mpv scripts directory in its config directory
(see the `FILES`_ section for details). They must have a ``.so`` file extension.
They can also be explicitly loaded with the ``--script`` option.
API
---
A C plugin must export the following function::
int mpv_open_cplugin(mpv_handle *handle)
The plugin function will be called on loading time. This function does not
return as long as your plugin is loaded (it runs in its own thread). The
``handle`` will be deallocated as soon as the plugin function returns.
The return value is interpreted as error status. A value of ``0`` is
interpreted as success, while ``-1`` signals an error. In the latter case,
the player prints an uninformative error message that loading failed.
Return values other than ``0`` and ``-1`` are reserved, and trigger undefined
behavior.
Within the plugin function, you can call libmpv API functions. The ``handle``
is created by ``mpv_create_client()`` (or actually an internal equivalent),
and belongs to you. You can call ``mpv_wait_event()`` to wait for things
happening, and so on.
Note that the player might block until your plugin calls ``mpv_wait_event()``
for the first time. This gives you a chance to install initial hooks etc.
before playback begins.
The details are quite similar to Lua scripts.
Linkage to libmpv
-----------------
The current implementation requires that your plugins are **not** linked against
libmpv. What your plugins uses are not symbols from a libmpv binary, but
symbols from the mpv host binary.
Examples
--------
See:
- https://github.com/mpv-player/mpv-examples/tree/master/cplugins
+5
View File
@@ -451,6 +451,11 @@ are useful only in special situations.
multiple properties at once, you might not want to act on each property
change, but only when all change notifications have been received.
``mp.unregister_idle(fn)``
Undo ``mp.register_idle(fn)``. This removes all idle handlers that
are equal to the ``fn`` parameter. This uses normal Lua ``==`` comparison,
so be careful when dealing with closures.
``mp.enable_messages(level)``
Set the minimum log level of which mpv message output to receive. These
messages are normally printed to the terminal. By calling this function,
+5 -1
View File
@@ -140,7 +140,7 @@ L
Toggle infinite looping.
Ctrl + and Ctrl -
Adjust audio delay by +/- 0.1 seconds.
Adjust audio delay (A/V sync) by +/- 0.1 seconds.
u
Switch between applying no style overrides to SSA/ASS subtitles, and
@@ -673,6 +673,10 @@ PROTOCOLS
either aliases to documented protocols, or are just redirections to
protocols implemented and documented in FFmpeg.
``data:`` is supported in FFmpeg (not in Libav), but needs to be in the
format ``data://``. This is done to avoid ambiguity with filenames. You
can also prefix it with ``lavf://`` or ``ffmpeg://``.
``ytdl://...``
By default, the youtube-dl hook script (enabled by default for mpv CLI)
only looks at http URLs. Prefixing an URL with ``ytdl://`` forces it to
+166 -53
View File
@@ -640,8 +640,8 @@ Video
``--opengl-backend=dxinterop`` (Windows only)
:dxva2-copy: copies video back to system RAM (Windows only)
:d3d11va: requires ``--vo=opengl`` with ``--opengl-backend=angle``
(Windows only)
:d3d11va-copy: copies video back to system RAM (Windows only)
(Windows 8+ only)
:d3d11va-copy: copies video back to system RAM (Windows 8+ only)
:mediacodec: copies video back to system RAM (Android only)
:rpi: requires ``--vo=opengl`` (Raspberry Pi only - default if available)
:rpi-copy: copies video back to system RAM (Raspberry Pi only)
@@ -669,6 +669,14 @@ Video
forcing it with ``--opengl-backend=x11``, but the vaapi/GLX interop is
said to be slower than ``vaapi-copy``.
The ``cuda`` and ``cuda-copy`` modes provides deinterlacing in the decoder
which is useful as there is no other deinterlacing mechanism in the opengl
output path. To use this deinterlacing you must pass the option:
``vd-lavc-o=deint=[weave|bob|adaptive]``.
Pass ``weave`` (or leave the option unset) to not attempt any
deinterlacing. ``cuda`` should always be preferred unless the ``opengl``
vo is not being used or filters are required.
Most video filters will not work with hardware decoding as they are
primarily implemented on the CPU. Some exceptions are ``vdpaupp``,
``vdpaurb`` and ``vavpp``. See `VIDEO FILTERS`_ for more details.
@@ -719,18 +727,6 @@ Video
affect this additionally. This can give incorrect results even with
completely ordinary video sources.
``cuda`` is usually safe. Interlaced content can be deinterlaced by
the decoder, which is useful as there is no other deinterlacing
mechanism in the opengl output path. To use this deinterlacing you
must pass the option: ``vd-lavc-o=deint=[weave|bob|adaptive]``. Pass
``weave`` to not attempt any deinterlacing.
10 and 12bit HEVC is available if the hardware supports it and a
sufficiently new driver (> 375.xx) is used.
``cuda-copy`` has the same behaviour as ``cuda`` - including the ability
to deinterlace inside the decoder. However, traditional deinterlacing
filters can be used in this case.
``rpi`` always uses the hardware overlay renderer, even with
``--vo=opengl``.
@@ -746,12 +742,12 @@ Video
In particular, ``auto-copy`` will only select safe modes
(although potentially slower than other methods).
``--hwdec-preload=<api>``
``--opengl-hwdec-interop=<name>``
This is useful for the ``opengl`` and ``opengl-cb`` VOs for creating the
hardware decoding OpenGL interop context, but without actually enabling
hardware decoding itself (like ``--hwdec`` does).
If set to ``no`` (default), the ``--hwdec`` option is used.
If set to an empty string (default), the ``--hwdec`` option is used.
For ``opengl``, if set, do not create the interop context on demand, but
when the VO is created.
@@ -762,6 +758,19 @@ Video
to temporarily set the ``hwdec`` option just during OpenGL context
initialization with ``mpv_opengl_cb_init_gl()``.
See ``--opengl-hwdec-interop=help`` for accepted values. This lists the
interop backend, with the ``--hwdec`` alias after it in ``[...]``. Consider
all values except the proper interop backend name, ``auto``, and ``no`` as
silently deprecated and subject to change. Also, if you use this in
application code (e.g. via libmpv), any value other than ``auto`` and ``no``
should be avoided, as backends can change.
Currently the option sets a single value. It is possible that the option
type changes to a list in the future.
The old alias ``--hwdec-preload`` has different behavior if the option value
is ``no``.
``--videotoolbox-format=<name>``
Set the internal pixel format used by ``--hwdec=videotoolbox`` on OSX. The
choice of the format can influence performance considerably. On the other
@@ -1132,41 +1141,31 @@ Audio
multichannel PCM, and mpv supports lossless DTS-HD decoding via
FFmpeg's new DCA decoder (based on libdcadec).
``--ad=<[+|-]family1:(*|decoder1),[+|-]family2:(*|decoder2),...[-]>``
``--ad=<decoder1,decoder2,...[-]>``
Specify a priority list of audio decoders to be used, according to their
decoder name. When determining which decoder to use, the first decoder that
matches the audio format is selected. If that is unavailable, the next
decoder is used. Finally, it tries all other decoders that are not
explicitly selected or rejected by the option.
Specifying family names is deprecated. Entries like ``family:*`` prioritize
all decoders of the given family.
``-`` at the end of the list suppresses fallback on other available
decoders not on the ``--ad`` list. ``+`` in front of an entry forces the
decoder. Both of these should not normally be used, because they break
normal decoder auto-selection! Both of these methods are deprecated.
``-`` in front of an entry disables selection of the decoder. This is
deprecated.
.. admonition:: Examples
``--ad=mp3float``
Prefer the FFmpeg/Libav ``mp3float`` decoder over all other MP3
decoders.
``--ad=lavc:mp3float``
Prefer the FFmpeg/Libav ``mp3float`` decoder over all other MP3
decoders. (Using deprecated family syntax.)
``--ad=help``
List all available decoders.
.. admonition:: Warning
Enabling compressed audio passthrough (AC3 and DTS via SPDIF/HDMI) with
this option is deprecated. Use ``--audio-spdif`` instead.
this option is not possible. Use ``--audio-spdif`` instead.
``--volume=<value>``
Set the startup volume. 0 means silence, 100 means no volume reduction or
@@ -1727,6 +1726,13 @@ Subtitles
Disabled by default.
``--image-subs-video-resolution=<yes|no>``
Override the image subtitle resolution with the video resolution
(default: no). Normally, the subtitle canvas is fit into the video canvas
(e.g. letterboxed). Setting this option uses the video size as subtitle
canvas size. Can be useful to test broken subtitles, which often happen
when the video was trancoded, while attempting to keep the old subtitles.
``--sub-ass``, ``--no-sub-ass``
Render ASS subtitles natively (enabled by default).
@@ -1781,8 +1787,9 @@ Subtitles
subtitles are interpreted as UTF-8 with "Latin 1" as fallback for bytes
which are not valid UTF-8 sequences. iconv is never involved in this mode.
This option changed in mpv 0.23.0. The old syntax is still emulated to some
degree.
This option changed in mpv 0.23.0. Support for the old syntax was fully
removed in mpv 0.24.0.
``--sub-fix-timing``, ``--no-sub-fix-timing``
By default, subtitle timing is adjusted to remove minor gaps or overlaps
@@ -1965,6 +1972,18 @@ Subtitles
Vertical position (default: ``bottom``).
Details see ``--sub-align-x``.
``--sub-justify=<auto|left|center|right>``
Control how multi line subs are justified irrespective of where they
are aligned (default: ``auto`` which justifies as defined by
``--sub-align-y``).
Left justification is recommended to make the subs easier to read
as it is easier for the eyes.
``--sub-ass-justify=<yes|no>``
Applies justification as defined by ``--sub-justify`` on ASS subtitles
if ``--sub-ass-style-override`` is not set to ``no``.
Default: ``no``.
``--sub-shadow-color=<color>``
See ``--sub-color``. Color used for sub text shadow.
@@ -2101,6 +2120,9 @@ Window
Enabled by default.
``--snap-window``
(Windows only) Snap the player window to screen edges.
``--ontop``
Makes the player window stay on top of other windows.
@@ -2339,7 +2361,7 @@ Window
- ``--monitoraspect=16:9`` or ``--monitoraspect=1.7777``
``--hidpi-window-scale``, ``--no-hidpi-window-scale``
(OS X only)
(OS X and X11 only)
Scale the window size according to the backing scale factor (default: yes).
On regular HiDPI resolutions the window opens with double the size but appears
as having the same size as on none-HiDPI resolutions. This is the default OS X
@@ -2724,6 +2746,27 @@ Demuxer
(This value tends to be fuzzy, because many file formats don't store linear
timestamps.)
``--prefetch-playlist=<yes|no>``
Prefetch next playlist entry while playback of the current entry is ending
(default: no). This merely opens the URL of the next playlist entry as soon
as the current URL is fully read.
This does **not** work with URLs resolved by the ``youtube-dl`` wrapper,
and it won't.
This does not affect HLS (``.m3u8`` URLs) - HLS prefetching depends on the
demuxer cache settings and is on by default.
This can give subtly wrong results if per-file options are used, or if
options are changed in the time window between prefetching start and next
file played.
This can occasionally make wrong prefetching decisions. For example, it
can't predict whether you go backwards in the playlist, and assumes you
won't edit the playlist.
Highly experimental.
``--force-seekable=<yes|no>``
If the player thinks that the media is not seekable (e.g. playing from a
pipe, or it's an http stream with a server that doesn't support range
@@ -4288,16 +4331,70 @@ The following video options are currently all specific to ``--vo=opengl`` and
Windows only.
``--opengl-dcomposition=<yes|no>``
Allows DirectComposition when using the ANGLE backend (default: yes).
DirectComposition implies flip-model presentation, which can improve
rendering efficiency on Windows 8+ by avoiding a copy of the video frame.
mpv uses it by default where possible, but it can cause poor behaviour with
some drivers, such as a black screen or graphical corruption when leaving
full-screen mode. Use "no" to disable it.
``--angle-d3d11-feature-level=<11_0|10_1|10_0|9_3>``
Selects a specific feature level when using the ANGLE backend with D3D11.
By default, the highest available feature level is used. This option can be
used to select a lower feature level, which is mainly useful for debugging.
Note that OpenGL ES 3.0 is only supported at feature level 10_1 or higher.
Most extended OpenGL features will not work at lower feature levels
(similar to ``--opengl-dumb-mode``).
Windows with ANGLE only.
``--angle-d3d11-warp=<yes|no|auto>``
Use WARP (Windows Advanced Rasterization Platform) when using the ANGLE
backend with D3D11 (default: auto). This is a high performance software
renderer. By default, it is used when the Direct3D hardware does not
support Direct3D 11 feature level 9_3. While the extended OpenGL features
will work with WARP, they can be very slow.
Windows with ANGLE only.
``--angle-egl-windowing=<yes|no|auto>``
Use ANGLE's built in EGL windowing functions to create a swap chain
(default: auto). If this is set to ``no`` and the D3D11 renderer is in use,
ANGLE's built in swap chain will not be used and a custom swap chain that
is optimized for video rendering will be created instead. If set to
``auto``, a custom swap chain will be used for D3D11 and the built in swap
chain will be used for D3D9. This option is mainly for debugging purposes,
in case the custom swap chain has poor performance or does not work.
If set to ``yes``, the ``--angle-max-frame-latency`` and
``--angle-swapchain-length`` options will have no effect.
Windows with ANGLE only.
``--angle-max-frame-latency=<1-16>``
Sets the maximum number of frames that the system is allowed to queue for
rendering with the ANGLE backend (default: 3). Lower values should make
VSync timing more accurate, but a value of ``1`` requires powerful
hardware, since the CPU will not be able to "render ahead" of the GPU.
Windows with ANGLE only.
``--angle-renderer=<d3d9|d3d11|auto>``
Forces a specific renderer when using the ANGLE backend (default: auto). In
auto mode this will pick D3D11 for systems that support Direct3D 11 feature
level 9_3 or higher, and D3D9 otherwise. This option is mainly for
debugging purposes. Normally there is no reason to force a specific
renderer, though ``--angle-renderer=d3d9`` may give slightly better
performance on old hardware. Note that the D3D9 renderer only supports
OpenGL ES 2.0, so most extended OpenGL features will not work if this
renderer is selected (similar to ``--opengl-dumb-mode``).
Windows with ANGLE only.
``--angle-swapchain-length=<2-16>``
Sets the number of buffers in the D3D11 presentation queue when using the
ANGLE backend (default: 6). At least 2 are required, since one is the back
buffer that mpv renders to and the other is the front buffer that is
presented by the DWM. Additional buffers can improve performance, because
for example, mpv will not have to wait on the DWM to release the front
buffer before rendering a new frame to it. For this reason, Microsoft
recommends at least 4.
Windows 8+ with ANGLE only.
``--opengl-sw``
Continue even if a software renderer is detected.
@@ -4322,6 +4419,9 @@ The following video options are currently all specific to ``--vo=opengl`` and
work.
x11
X11/GLX
x11probe
For internal autoprobing, equivalent to ``x11`` otherwise. Don't use
directly, it could be removed without warning as autoprobing is changed.
wayland
Wayland/EGL
drm
@@ -4561,6 +4661,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
unavailable, it silently falls back on a normal framebuffer. Note that
if you set the ``--opengl-fbo-format`` option to a non-default value, a
format with alpha must be specified, or this won't work.
This does not work on X11 with EGL and Mesa (freedesktop bug 67676).
no
Ignore alpha component.
@@ -4716,21 +4817,10 @@ Miscellaneous
Input file type for ``mf://`` (available: jpeg, png, tga, sgi). By default,
this is guessed from the file extension.
``--stream-capture=<filename>``
Allows capturing the primary stream (not additional audio tracks or other
kind of streams) into the given file. Capturing can also be started and
stopped by changing the filename with the ``stream-capture`` property.
Generally this will not produce usable results for anything else than MPEG
or raw streams, unless capturing includes the file headers and is not
interrupted. Note that, due to cache latencies, captured data may begin and
end somewhat delayed compared to what you see displayed.
The destination file is always appended. (Before mpv 0.8.0, the file was
overwritten.)
``--stream-dump=<filename>``
Same as ``--stream-capture``, but do not start playback. Instead, the entire
file is dumped.
``--stream-dump=<destination-filename>``
Instead of playing a file, read its byte stream and write it to the given
destination file. The destination is overwritten. Can be useful to test
network-related behavior.
``--stream-lavf-o=opt1=value1,opt2=value2,...``
Set AVOptions on streams opened with libavformat. Unknown or misspelled
@@ -4773,6 +4863,29 @@ Miscellaneous
This does not affect playlist expansion, redirection, or other loading of
referenced files like with ordered chapters.
``--record-file=<file>``
Record the current stream to the given target file. The target file will
always be overwritten without asking.
This remuxes the source stream without reencoding, which makes this a
highly fragile and experimental feature. It's entirely possible that this
writes files which are broken, not standards compliant, not playable with
all players (including mpv), or incomplete.
The target file format is determined by the file extension of the target
filename. It is recommended to use the same target container as the source
container if possible, and preferring Matroska as fallback.
Seeking during stream recording, or enabling/disabling stream recording
during playback, can cut off data, or produce "holes" in the output file.
These are technical restrictions. In particular, video data or subtitles
which were read ahead can produce such holes, which might cause playback
problems with various players (including mpv).
The behavior of this option might changed in the future, such as changing
it to a template (similar to ``--screenshot-template``), being renamed,
removed, or anything else, until it is declared semi-stable.
``--lavfi-complex=<string>``
Set a "complex" libavfilter filter, which means a single filter graph can
take input from multiple source audio and video tracks. The graph can result
+4 -1
View File
@@ -8,7 +8,10 @@ behaves. Although there are still many similarities to its ancestors, **mpv**
should generally be treated as a completely different program.
.. admonition:: Warning
This document is not updated anymore, and is incomplete and outdated.
This document is **not updated** anymore, and is **incomplete** and
**outdated**. If you look for old option replacements, always check with
the current mpv manpage, as the options could have changed meanwhile.
General Changes from MPlayer to mpv
-----------------------------------
+121
View File
@@ -1,3 +1,124 @@
Release 0.24.0
==============
Features
--------
Added
~~~~~
- Windows: allow snapping to screen edges (#2248)
- macOS: add border cycling (#2430)
- player: add experimental C plugin interface
- player: add experimental stream recording feature (--record-file)
- player: add prefetching of the next playlist entry (--prefetch-playlist; does NOT work with URLs resolved by youtube-dl)
- stream_lavf: add support for data URIs (#4058)
- vaapi: add support for 10-bit video formats (requires patched Mesa and capable hardware)
- x11: pseudo HiDPI scaling
Options and Commands
--------------------
Added
~~~~~
- sub: add --sub-justify and --sub-ass-justify options
- sub: add option to force using video resolution for image subtitles (--image-subs-video-resolution)
Changed
~~~~~~~
- command: rename framedrop properties
- drop-frame-count → decoder-frame-drop-count
- vo-drop-frame-count → frame-drop-count
- input.conf: change "L" to change loop-file by default
Deprecated
~~~~~~~~~~
- --hwdec-preload (replaced with --opengl-hwdec-interop)
Removed
~~~~~~~
- options: drop deprecated --sub-codepage syntax
- options: drop deprecated --vd/--ad codecs selection features
- player: remove --stream-capture option/property
Fixes and Minor Enhancements
----------------------------
- Windows: update the fullscreen state on restoring
- ad_spdif: fix obscure cases of AC3 passthrough (e.g. 44100 Hz AC3)
- ao_oss: use --audio-device if --oss-device isn't set (#4122)
- build: fix --disable-gl if cuda is enabled
- build: rpi: rely on pkgconfig for compiler flags to ease cross compilation
- charset_conv: fallback to interpreting subs as latin1 if iconv fails
- charset_conv: fix "auto" fallback with uchardet not compiled (#3954)
- client API: fix freeze when destroying mpv_handle before mpv_initialize
- client API: fix mpv_set_property() return value before init (#3988)
- command: fix potential crash for script-binding with multi-commands
- command: nicer OSD formatting for loop-file
- command: shorten long playlists on OSD
- config: do not resolve default profile during "include" processing (#4024)
- cuda: fix 10 bit decoding
- cue: accept lower-case cue commands (#4057)
- demux_mkv: any reference makes a frame not a keyframe (fixes seeking in WebM files with alpha channel)
- lavfi: slightly better disconnected output handling (#4118)
- lua: close directory after reading its entries (#4045)
- macOS: consistent normalization when searching for external files (#4016)
- macOS: don't change Space on quit in fullscreen (#3957)
- macOS: don't constrain window frame for fullscreen (#4044)
- macOS: don't init displaylink on reconfig (#4031)
- macOS: fix build on OS X 10.9 (#3946)
- macOS: fix color profile retrieval
- macOS: fix displaylink refresh rate retrieval
- macOS: fix dropping of files and URLs (#4036)
- macOS: fix handling of geometry option (#3867)
- macOS: fix unwanted behavior with window level other than the default (#1757, #1884)
- macOS: fix window size in certain circumstances
- macOS: properly recover from toggleFullscreen fail (#4035)
- osc: allow playlist buttons when looping (#4092)
- osc: bottom/topbar: clip title instead of stretching
- osc: bottombar/topbar: make chapter markers slightly bigger
- osc: box: clip with ellipsis after too much stretching
- osc: fix crashes related to field eventresponder being nil (#3210)
- player: add .scc subtitle extension
- stream_bluray: use proper 0-based index
- sub: remove .txt as text subtitle extension
- tv: fix segfaults on TV input (#4096)
- vaapi: set libva > 0.39.4 message callbacks to prevent it from printing messages to the terminal
- vd_lavc: improved fallback behavior for --hwdec=cuda (#3914)
- vdpau: reject decoding of non-4:2:0 (would decode to garbage)
- vf_lavfi: don't crash with VOs without hardware decoding support (#4064)
- vo_opengl, vo_opengl_cb: better hwdec interop backend selection
- vo_opengl: angle: rewrite with custom swap chain (fixes problems with e.g. 24 fps video on 24 Hz monitors, avoids problems with DirectComposition, enables "screenshot window" command on Windows 8 and newer)
- vo_opengl: partially fix rotation for 4:2:2 content
- vo_opengl: x11egl: fix alpha mode
- wayland: fix high CPU usage with mpv paused and visible
- ytdl_hook, edl: implement pseudo-DASH support
- ytdl_hook: add non-dash fallbacks to default formats
- ytdl_hook: fix opening hitbox.tv rtmp stream
- ytdl_hook: respect --no-audio, don't force-select track
- ytdl_hook: support livestream segmented DASH VODs
- ytdl_hook: support 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.23.0..v0.24.0`
in the git repository or by visiting either
https://github.com/mpv-player/mpv/compare/v0.23.0...v0.24.0 or
https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.23.0..v0.24.0
Release 0.23.0
==============
View File
Whitespace-only changes.
+9 -5
View File
@@ -5,23 +5,27 @@
# of every string, so code using the string may need to remove that to get
# the exact contents of the original file.
from __future__ import unicode_literals
import sys
# Indexing a byte string yields int on Python 3.x, and a str on Python 2.x
def pord(c):
return ord(c) if type(c) == str else c
def main(infile):
def file2string(infilename, infile, outfile):
outfile.write("// Generated from %s\n\n" % infilename)
conv = ['\\' + ("%03o" % c) for c in range(256)]
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \
"0123456789!#%&'()*+,-./:;<=>?[]^_{|}~ "
for c in safe_chars:
conv[ord(c)] = c
for c, esc in ("\nn", "\tt", r"\\", '""'):
conv[ord(c)] = '\\' + esc
for line in infile:
sys.stdout.write('"' + ''.join(conv[pord(c)] for c in line) + '"\n')
outfile.write('"' + ''.join(conv[pord(c)] for c in line) + '"\n')
with open(sys.argv[1], 'rb') as infile:
sys.stdout.write("// Generated from %s\n\n" % sys.argv[1])
main(infile)
if __name__ == "__main__":
with open(sys.argv[1], 'rb') as infile:
file2string(sys.argv[1], infile, sys.stdout)
+13 -8
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
Generate C definitions for parsing Matroska files.
Can also be used to directly parse Matroska files and display their contents.
@@ -69,6 +69,12 @@ elements_matroska = (
'BlockDuration, 9b, uint',
'ReferenceBlock*, fb, sint',
'DiscardPadding, 75A2, sint',
'BlockAdditions, 75A1, sub', (
'BlockMore*, A6, sub', (
'BlockAddID, EE, uint',
'BlockAdditional, A5, binary',
),
),
),
'SimpleBlock*, a3, binary',
),
@@ -271,6 +277,7 @@ def parse_elems(l, namespace):
for el in l:
if isinstance(el, str):
name, hexid, eltype = [x.strip() for x in el.split(',')]
hexid = hexid.lower()
multiple = name.endswith('*')
name = name.strip('*')
new = MatroskaElement(name, hexid, eltype, namespace)
@@ -285,8 +292,8 @@ parse_elems(elements_ebml, 'EBML')
parse_elems(elements_matroska, 'MATROSKA')
def printf(out, *args):
out.write(' '.join([str(x) for x in args]))
out.write('\n')
out.write(u' '.join([str(x) for x in args]))
out.write(u'\n')
def generate_C_header(out):
printf(out, '// Generated by TOOLS/matroska.py, do not edit manually')
@@ -400,10 +407,6 @@ def read_float(s, length):
def parse_one(s, depth, parent, maxlen):
elid = hexlify(read_id(s)).decode('ascii')
elem = elementd.get(elid)
if parent is not None and elid not in parent.subids and elid not in ('ec', 'bf'):
print('Unexpected:', elid)
if 1:
raise NotImplementedError
size, length = read_vint(s)
this_length = len(elid) / 2 + size + length
if elem is not None:
@@ -442,7 +445,7 @@ def parse_one(s, depth, parent, maxlen):
else:
raise NotImplementedError
else:
print(depth, 'Unknown element:', elid, 'size:', length)
print(" " * depth, '[' + elid + '] Unknown element! size:', length)
read(s, length)
return this_length
@@ -455,6 +458,8 @@ if __name__ == "__main__":
elif sys.argv[1] == '--generate-definitions':
generate_C_definitions(sys.stdout)
else:
if sys.version_info.major < 3:
raise Exception("Dumping requires Python 3.")
s = open(sys.argv[1], "rb")
while 1:
start = s.tell()
+1 -1
View File
@@ -1 +1 @@
0.23.0
0.24.0
+5 -2
View File
@@ -35,8 +35,11 @@ struct ad_functions {
int (*init)(struct dec_audio *da, const char *decoder);
void (*uninit)(struct dec_audio *da);
int (*control)(struct dec_audio *da, int cmd, void *arg);
int (*decode_packet)(struct dec_audio *da, struct demux_packet *pkt,
struct mp_audio **out);
// Return whether or not the packet has been consumed.
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);
};
enum ad_ctrl {
+42 -45
View File
@@ -45,7 +45,6 @@ struct priv {
uint32_t skip_samples, trim_samples;
bool preroll_done;
double next_pts;
bool needs_reset;
AVRational codec_timebase;
};
@@ -116,26 +115,18 @@ static int init(struct dec_audio *da, const char *decoder)
av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
AV_OPT_SEARCH_CHILDREN);
#if HAVE_AVFRAME_SKIP_SAMPLES
#if LIBAVCODEC_VERSION_MICRO >= 100
// Let decoder add AV_FRAME_DATA_SKIP_SAMPLES.
av_opt_set(lavc_context, "flags2", "+skip_manual", AV_OPT_SEARCH_CHILDREN);
#endif
mp_set_avopts(da->log, lavc_context, opts->avopts);
lavc_context->codec_tag = c->codec_tag;
lavc_context->sample_rate = c->samplerate;
lavc_context->bit_rate = c->bitrate;
lavc_context->block_align = c->block_align;
lavc_context->bits_per_coded_sample = c->bits_per_coded_sample;
lavc_context->channels = c->channels.num;
if (!mp_chmap_is_unknown(&c->channels))
lavc_context->channel_layout = mp_chmap_to_lavc(&c->channels);
// demux_mkv
mp_lavc_set_extradata(lavc_context, c->extradata, c->extradata_size);
mp_set_lav_codec_headers(lavc_context, c);
if (mp_set_avctx_codec_headers(lavc_context, c) < 0) {
MP_ERR(da, "Could not set decoder parameters.\n");
uninit(da);
return 0;
}
mp_set_avcodec_threads(da->log, lavc_context, opts->threads);
@@ -177,14 +168,12 @@ static int control(struct dec_audio *da, int cmd, void *arg)
ctx->trim_samples = 0;
ctx->preroll_done = false;
ctx->next_pts = MP_NOPTS_VALUE;
ctx->needs_reset = false;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct mp_audio **out)
static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
@@ -195,41 +184,48 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
if (mpkt && priv->next_pts == MP_NOPTS_VALUE)
priv->next_pts = mpkt->pts;
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase);
int got_frame = 0;
av_frame_unref(priv->avframe);
int ret = avcodec_send_packet(avctx, mpkt ? &pkt : NULL);
if (priv->needs_reset)
control(da, ADCTRL_RESET, NULL);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return false;
int ret = avcodec_send_packet(avctx, &pkt);
if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
if (ret >= 0 && mpkt)
mpkt->len = 0;
ret = avcodec_receive_frame(avctx, priv->avframe);
if (ret >= 0)
got_frame = 1;
if (ret == AVERROR_EOF)
priv->needs_reset = true;
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
}
if (ret < 0) {
if (ret < 0)
MP_ERR(da, "Error decoding audio.\n");
return true;
}
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
int ret = avcodec_receive_frame(avctx, priv->avframe);
if (ret == AVERROR_EOF) {
// If flushing was initialized earlier and has ended now, make it start
// over in case we get new packets at some point in the future.
control(da, ADCTRL_RESET, NULL);
return false;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
MP_ERR(da, "Error decoding audio.\n");
return -1;
}
if (!got_frame)
return 0;
#if LIBAVCODEC_VERSION_MICRO >= 100
if (priv->avframe->flags & AV_FRAME_FLAG_DISCARD)
av_frame_unref(priv->avframe);
#endif
if (!priv->avframe->buf[0])
return true;
double out_pts = mp_pts_from_av(priv->avframe->pts, &priv->codec_timebase);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
return -1;
return true;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
@@ -247,7 +243,7 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
if (mpframe->pts != MP_NOPTS_VALUE)
priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
#if HAVE_AVFRAME_SKIP_SAMPLES
#if LIBAVCODEC_VERSION_MICRO >= 100
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
@@ -279,8 +275,8 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
av_frame_unref(priv->avframe);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, mpframe->samples);
return 0;
MP_DBG(da, "Decoded %d samples\n", mpframe->samples);
return true;
}
static void add_decoders(struct mp_decoder_list *list)
@@ -294,5 +290,6 @@ const struct ad_functions ad_lavc = {
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
.send_packet = send_packet,
.receive_frame = receive_frame,
};
+74 -21
View File
@@ -42,6 +42,8 @@ struct spdifContext {
bool use_dts_hd;
struct mp_audio fmt;
struct mp_audio_pool *pool;
bool got_eof;
struct demux_packet *queued_packet;
};
static int write_packet(void *p, uint8_t *buf, int buf_size)
@@ -71,6 +73,7 @@ static void uninit(struct dec_audio *da)
av_freep(&lavf_ctx->pb->buffer);
av_freep(&lavf_ctx->pb);
avformat_free_context(lavf_ctx);
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->lavf_ctx = NULL;
}
}
@@ -90,13 +93,34 @@ static int init(struct dec_audio *da, const char *decoder)
return spdif_ctx->codec_id != AV_CODEC_ID_NONE;
}
static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt)
static void determine_codec_params(struct dec_audio *da, AVPacket *pkt,
int *out_profile, int *out_rate)
{
struct spdifContext *spdif_ctx = da->priv;
int profile = FF_PROFILE_UNKNOWN;
AVCodecContext *ctx = NULL;
AVFrame *frame = NULL;
AVCodecParserContext *parser = av_parser_init(spdif_ctx->codec_id);
if (parser) {
// Don't make it wait for the next frame.
parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
ctx = avcodec_alloc_context3(NULL);
uint8_t *d = NULL;
int s = 0;
av_parser_parse2(parser, ctx, &d, &s, pkt->data, pkt->size, 0, 0, 0);
*out_profile = profile = ctx->profile;
*out_rate = ctx->sample_rate;
av_free(ctx);
av_parser_close(parser);
}
if (profile != FF_PROFILE_UNKNOWN || spdif_ctx->codec_id != AV_CODEC_ID_DTS)
return;
AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id);
if (!codec)
goto done;
@@ -120,7 +144,8 @@ static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt)
if (avcodec_receive_frame(ctx, frame) < 0)
goto done;
profile = ctx->profile;
*out_profile = profile = ctx->profile;
*out_rate = ctx->sample_rate;
done:
av_frame_free(&frame);
@@ -130,8 +155,6 @@ done:
if (profile == FF_PROFILE_UNKNOWN)
MP_WARN(da, "Failed to parse codec profile.\n");
return profile;
}
static int init_filter(struct dec_audio *da, AVPacket *pkt)
@@ -139,8 +162,9 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
struct spdifContext *spdif_ctx = da->priv;
int profile = FF_PROFILE_UNKNOWN;
if (spdif_ctx->codec_id == AV_CODEC_ID_DTS)
profile = determine_codec_profile(da, pkt);
int c_rate = 0;
determine_codec_params(da, pkt, &profile, &c_rate);
MP_VERBOSE(da, "In: profile=%d samplerate=%d\n", profile, c_rate);
AVFormatContext *lavf_ctx = avformat_alloc_context();
if (!lavf_ctx)
@@ -186,7 +210,7 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
break;
case AV_CODEC_ID_AC3:
sample_format = AF_FORMAT_S_AC3;
samplerate = 48000;
samplerate = c_rate > 0 ? c_rate : 48000;
num_channels = 2;
break;
case AV_CODEC_ID_DTS: {
@@ -243,44 +267,72 @@ fail:
return -1;
}
static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct mp_audio **out)
static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
{
struct spdifContext *spdif_ctx = da->priv;
spdif_ctx->out_buffer_len = 0;
if (spdif_ctx->queued_packet || spdif_ctx->got_eof)
return false;
if (!mpkt)
return 0;
spdif_ctx->queued_packet = mpkt ? demux_copy_packet(mpkt) : NULL;
spdif_ctx->got_eof = !mpkt;
return true;
}
double pts = mpkt->pts;
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
{
struct spdifContext *spdif_ctx = da->priv;
if (spdif_ctx->got_eof) {
spdif_ctx->got_eof = false;
return false;
}
if (!spdif_ctx->queued_packet)
return true;
double pts = spdif_ctx->queued_packet->pts;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
mpkt->len = 0; // will be fully consumed
mp_set_av_packet(&pkt, spdif_ctx->queued_packet, NULL);
pkt.pts = pkt.dts = 0;
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
return -1;
goto done;
}
spdif_ctx->out_buffer_len = 0;
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
if (ret < 0)
return -1;
if (ret < 0) {
MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
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)
return -1;
goto done;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
(*out)->pts = pts;
return 0;
done:
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->queued_packet = NULL;
return true;
}
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct spdifContext *spdif_ctx = da->priv;
switch (cmd) {
case ADCTRL_RESET:
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->queued_packet = NULL;
spdif_ctx->got_eof = false;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
@@ -344,5 +396,6 @@ const struct ad_functions ad_spdif = {
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
.send_packet = send_packet,
.receive_frame = receive_frame,
};
+10 -11
View File
@@ -27,6 +27,7 @@
#include "common/codecs.h"
#include "common/msg.h"
#include "common/recorder.h"
#include "misc/bstr.h"
#include "stream/stream.h"
@@ -201,7 +202,7 @@ static void fix_audio_pts(struct dec_audio *da)
void audio_work(struct dec_audio *da)
{
if (da->current_frame)
if (da->current_frame || !da->ad_driver)
return;
if (!da->packet && !da->new_segment &&
@@ -217,30 +218,28 @@ void audio_work(struct dec_audio *da)
da->packet = NULL;
}
bool had_input_packet = !!da->packet;
bool had_packet = da->packet || da->new_segment;
if (da->ad_driver->send_packet(da, da->packet)) {
if (da->recorder_sink)
mp_recorder_feed_packet(da->recorder_sink, da->packet);
int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
if (ret < 0 || (da->packet && da->packet->len == 0)) {
talloc_free(da->packet);
da->packet = NULL;
}
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 = DATA_OK;
if (!da->current_frame) {
da->current_state = da->current_frame ? DATA_OK : DATA_AGAIN;
if (!progress)
da->current_state = DATA_EOF;
if (had_packet)
da->current_state = DATA_AGAIN;
}
fix_audio_pts(da);
bool segment_end = !da->current_frame && !had_input_packet;
bool segment_end = da->current_state == DATA_EOF;
if (da->current_frame) {
mp_audio_clip_timestamps(da->current_frame, da->start, da->end);
+2
View File
@@ -37,6 +37,8 @@ struct dec_audio {
bool try_spdif;
struct mp_recorder_sink *recorder_sink;
// For free use by the ad_driver
void *priv;
+7 -1
View File
@@ -246,7 +246,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
static void get_metadata_from_av_frame(struct af_instance *af, AVFrame *frame)
{
#if HAVE_AVFRAME_METADATA
#if LIBAVUTIL_VERSION_MICRO >= 100
struct priv *p = af->priv;
if (!p->metadata)
p->metadata = talloc_zero(p, struct mp_tags);
@@ -266,6 +266,12 @@ static int filter_frame(struct af_instance *af, struct mp_audio *data)
if (!p->graph)
goto error;
if (!data) {
if (p->eof)
return 0;
p->eof = true;
}
if (data) {
frame = mp_audio_to_avframe_and_unref(data);
data = NULL;
+3
View File
@@ -36,6 +36,9 @@
#include "common/common.h"
#include "config.h"
#define HAVE_LIBSWRESAMPLE HAVE_IS_FFMPEG
#define HAVE_LIBAVRESAMPLE HAVE_IS_LIBAV
#if HAVE_LIBAVRESAMPLE
#include <libavresample/avresample.h>
#elif HAVE_LIBSWRESAMPLE
+11 -7
View File
@@ -279,14 +279,21 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
int format = ao->format;
struct mp_chmap channels = ao->channels;
const char *device = PATH_DEV_DSP;
if (ao->device)
device = ao->device;
if (p->dsp && p->dsp[0])
device = p->dsp;
MP_VERBOSE(ao, "using '%s' dsp device\n", device);
#ifdef __linux__
p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK);
p->audio_fd = open(device, O_WRONLY | O_NONBLOCK);
#else
p->audio_fd = open(p->dsp, O_WRONLY);
p->audio_fd = open(device, O_WRONLY);
#endif
if (p->audio_fd < 0) {
MP_ERR(ao, "Can't open audio device %s: %s\n",
p->dsp, mp_strerror(errno));
device, mp_strerror(errno));
goto fail;
}
@@ -443,9 +450,8 @@ static int init(struct ao *ao)
p->oss_mixer_channel = SOUND_MIXER_PCM;
}
MP_VERBOSE(ao, "using '%s' dsp device\n", p->dsp);
MP_VERBOSE(ao, "using '%s' mixer device\n", p->oss_mixer_device);
MP_VERBOSE(ao, "using '%s' mixer device\n", mixer_channels[p->oss_mixer_channel]);
MP_VERBOSE(ao, "using '%s' mixer channel\n", mixer_channels[p->oss_mixer_channel]);
ao->format = af_fmt_from_planar(ao->format);
@@ -643,8 +649,6 @@ const struct ao_driver audio_out_oss = {
.buffersize = -1,
.outburst = 512,
.oss_mixer_channel = SOUND_MIXER_PCM,
.dsp = PATH_DEV_DSP,
.oss_mixer_device = PATH_DEV_MIXER,
},
.options = (const struct m_option[]) {
+6 -6
View File
@@ -255,9 +255,9 @@ static void uninit(struct ao *ao)
"while waiting for audio thread to terminate\n");
}
SAFE_RELEASE(state->hInitDone, CloseHandle(state->hInitDone));
SAFE_RELEASE(state->hWake, CloseHandle(state->hWake));
SAFE_RELEASE(state->hAudioThread,CloseHandle(state->hAudioThread));
SAFE_DESTROY(state->hInitDone, CloseHandle(state->hInitDone));
SAFE_DESTROY(state->hWake, CloseHandle(state->hWake));
SAFE_DESTROY(state->hAudioThread,CloseHandle(state->hAudioThread));
wasapi_change_uninit(ao);
@@ -305,7 +305,7 @@ static int init(struct ao *ao)
}
WaitForSingleObject(state->hInitDone, INFINITE); // wait on init complete
SAFE_RELEASE(state->hInitDone,CloseHandle(state->hInitDone));
SAFE_DESTROY(state->hInitDone,CloseHandle(state->hInitDone));
if (FAILED(state->init_ret)) {
if (!ao->probing)
MP_FATAL(ao, "Received failure from audio thread\n");
@@ -418,10 +418,10 @@ static int thread_control(struct ao *ao, enum aocontrol cmd, void *arg)
do {
IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL);
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
IAudioSessionControl_GetDisplayName(state->pSessionControl, &tmp);
} while (lstrcmpW(title, tmp));
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
talloc_free(title);
return CONTROL_OK;
}
+1 -1
View File
@@ -47,7 +47,7 @@ void wasapi_change_uninit(struct ao* ao);
#define EXIT_ON_ERROR(hres) \
do { if (FAILED(hres)) { goto exit_label; } } while(0)
#define SAFE_RELEASE(unk, release) \
#define SAFE_DESTROY(unk, release) \
do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0)
#define mp_format_res_str(hres) \
+1 -1
View File
@@ -242,5 +242,5 @@ void wasapi_change_uninit(struct ao *ao)
change->pEnumerator, (IMMNotificationClient *)change);
}
SAFE_RELEASE(change->pEnumerator, IMMDeviceEnumerator_Release(change->pEnumerator));
SAFE_RELEASE(change->pEnumerator);
}
+19 -22
View File
@@ -539,8 +539,7 @@ static void init_session_display(struct wasapi_state *state) {
return;
exit_label:
// if we got here then the session control is useless - release it
SAFE_RELEASE(state->pSessionControl,
IAudioSessionControl_Release(state->pSessionControl));
SAFE_RELEASE(state->pSessionControl);
MP_WARN(state, "Error setting audio session display name: %s\n",
mp_HRESULT_to_str(hr));
return;
@@ -571,10 +570,8 @@ static void init_volume_control(struct wasapi_state *state)
return;
exit_label:
state->vol_hw_support = 0;
SAFE_RELEASE(state->pEndpointVolume,
IAudioEndpointVolume_Release(state->pEndpointVolume));
SAFE_RELEASE(state->pAudioVolume,
ISimpleAudioVolume_Release(state->pAudioVolume));
SAFE_RELEASE(state->pEndpointVolume);
SAFE_RELEASE(state->pAudioVolume);
MP_WARN(state, "Error setting up volume control: %s\n",
mp_HRESULT_to_str(hr));
}
@@ -707,7 +704,7 @@ exit_label:
if (FAILED(hr))
mp_warn(l, "Failed getting device name: %s\n", mp_HRESULT_to_str(hr));
PropVariantClear(&devname);
SAFE_RELEASE(pProps, IPropertyStore_Release(pProps));
SAFE_RELEASE(pProps);
return namestr ? namestr : talloc_strdup(talloc_ctx, "");
}
@@ -722,7 +719,7 @@ static struct device_desc *get_device_desc(struct mp_log *l, IMMDevice *pDevice)
struct device_desc *d = talloc_zero(NULL, struct device_desc);
d->deviceID = talloc_memdup(d, deviceID,
(wcslen(deviceID) + 1) * sizeof(wchar_t));
SAFE_RELEASE(deviceID, CoTaskMemFree(deviceID));
SAFE_DESTROY(deviceID, CoTaskMemFree(deviceID));
char *full_id = mp_to_utf8(NULL, d->deviceID);
bstr id = bstr0(full_id);
@@ -745,8 +742,8 @@ static void destroy_enumerator(struct enumerator *e)
{
if (!e)
return;
SAFE_RELEASE(e->pDevices, IMMDeviceCollection_Release(e->pDevices));
SAFE_RELEASE(e->pEnumerator, IMMDeviceEnumerator_Release(e->pEnumerator));
SAFE_RELEASE(e->pDevices);
SAFE_RELEASE(e->pEnumerator);
talloc_free(e);
}
@@ -782,7 +779,7 @@ static struct device_desc *device_desc_for_num(struct enumerator *e, UINT i)
return NULL;
}
struct device_desc *d = get_device_desc(e->log, pDevice);
SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
SAFE_RELEASE(pDevice);
return d;
}
@@ -797,7 +794,7 @@ static struct device_desc *default_device_desc(struct enumerator *e)
return NULL;
}
struct device_desc *d = get_device_desc(e->log, pDevice);
SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
SAFE_RELEASE(pDevice);
return d;
}
@@ -834,7 +831,7 @@ static HRESULT load_device(struct mp_log *l,
exit_label:
if (FAILED(hr))
mp_err(l, "Error loading selected device: %s\n", mp_HRESULT_to_str(hr));
SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
SAFE_RELEASE(pEnumerator);
return hr;
}
@@ -908,7 +905,7 @@ LPWSTR wasapi_find_deviceID(struct ao *ao)
BSTR_P(device), d->id, d->name);
}
}
SAFE_RELEASE(d, talloc_free(d));
SAFE_DESTROY(d, talloc_free(d));
}
if (!deviceID)
@@ -969,13 +966,13 @@ void wasapi_thread_uninit(struct ao *ao)
if (state->pAudioClient)
IAudioClient_Stop(state->pAudioClient);
SAFE_RELEASE(state->pRenderClient, IAudioRenderClient_Release(state->pRenderClient));
SAFE_RELEASE(state->pAudioClock, IAudioClock_Release(state->pAudioClock));
SAFE_RELEASE(state->pAudioVolume, ISimpleAudioVolume_Release(state->pAudioVolume));
SAFE_RELEASE(state->pEndpointVolume, IAudioEndpointVolume_Release(state->pEndpointVolume));
SAFE_RELEASE(state->pSessionControl, IAudioSessionControl_Release(state->pSessionControl));
SAFE_RELEASE(state->pAudioClient, IAudioClient_Release(state->pAudioClient));
SAFE_RELEASE(state->pDevice, IMMDevice_Release(state->pDevice));
SAFE_RELEASE(state->hTask, AvRevertMmThreadCharacteristics(state->hTask));
SAFE_RELEASE(state->pRenderClient);
SAFE_RELEASE(state->pAudioClock);
SAFE_RELEASE(state->pAudioVolume);
SAFE_RELEASE(state->pEndpointVolume);
SAFE_RELEASE(state->pSessionControl);
SAFE_RELEASE(state->pAudioClient);
SAFE_RELEASE(state->pDevice);
SAFE_DESTROY(state->hTask, AvRevertMmThreadCharacteristics(state->hTask));
MP_DBG(ao, "Thread uninit done\n");
}
+3 -4
View File
@@ -251,12 +251,11 @@ static int play(struct ao *ao, void **data, int samples, int flags)
if (got_data) {
p->still_playing = true;
p->expected_end_time = 0;
}
// If we don't have new data, the decoder thread basically promises it
// will send new data as soon as it's available.
if (got_data)
// If we don't have new data, the decoder thread basically promises it
// will send new data as soon as it's available.
wakeup_playthread(ao);
}
pthread_mutex_unlock(&p->lock);
return write_samples;
}
+81 -47
View File
@@ -33,6 +33,7 @@
#include "common/msg.h"
#include "demux/packet.h"
#include "demux/stheader.h"
#include "video/fmt-conversion.h"
#include "av_common.h"
#include "codecs.h"
@@ -41,7 +42,7 @@ int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size)
if (size) {
av_free(avctx->extradata);
avctx->extradata_size = 0;
avctx->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return -1;
avctx->extradata_size = size;
@@ -50,35 +51,81 @@ int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size)
return 0;
}
// Copy the codec-related fields from st into avctx. This does not set the
// codec itself, only codec related header data provided by libavformat.
// The goal is to initialize a new decoder with the header data provided by
// libavformat, and unlike avcodec_copy_context(), allow the user to create
// a clean AVCodecContext for a manually selected AVCodec.
// This is strictly for decoding only.
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
enum AVMediaType mp_to_av_stream_type(int type)
{
mp_lavc_set_extradata(avctx, st->extradata, st->extradata_size);
avctx->codec_tag = st->codec_tag;
avctx->bit_rate = st->bit_rate;
avctx->width = st->width;
avctx->height = st->height;
avctx->pix_fmt = st->pix_fmt;
avctx->chroma_sample_location = st->chroma_sample_location;
avctx->sample_rate = st->sample_rate;
avctx->channels = st->channels;
avctx->block_align = st->block_align;
avctx->channel_layout = st->channel_layout;
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
avctx->has_b_frames = st->has_b_frames;
switch (type) {
case STREAM_VIDEO: return AVMEDIA_TYPE_VIDEO;
case STREAM_AUDIO: return AVMEDIA_TYPE_AUDIO;
case STREAM_SUB: return AVMEDIA_TYPE_SUBTITLE;
default: return AVMEDIA_TYPE_UNKNOWN;
}
}
// This only copies ffmpeg-native codec parameters. Parameters produced by
// other demuxers must be handled manually.
void mp_set_lav_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c)
AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c)
{
if (c->lav_codecpar)
avcodec_parameters_to_context(avctx, c->lav_codecpar);
AVCodecParameters *avp = avcodec_parameters_alloc();
if (!avp)
return NULL;
// If we have lavf demuxer params, they overwrite by definition any others.
if (c->lav_codecpar) {
if (avcodec_parameters_copy(avp, c->lav_codecpar) < 0)
goto error;
return avp;
}
avp->codec_type = mp_to_av_stream_type(c->type);
avp->codec_id = mp_codec_to_av_codec_id(c->codec);
avp->codec_tag = c->codec_tag;
if (c->extradata_size) {
avp->extradata =
av_mallocz(c->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avp->extradata)
goto error;
avp->extradata_size = c->extradata_size;
memcpy(avp->extradata, c->extradata, avp->extradata_size);
}
avp->bits_per_coded_sample = c->bits_per_coded_sample;
// Video only
avp->width = c->disp_w;
avp->height = c->disp_h;
if (c->codec && strcmp(c->codec, "mp-rawvideo") == 0) {
avp->format = imgfmt2pixfmt(c->codec_tag);
avp->codec_tag = 0;
}
// Audio only
avp->sample_rate = c->samplerate;
avp->bit_rate = c->bitrate;
avp->block_align = c->block_align;
avp->channels = c->channels.num;
if (!mp_chmap_is_unknown(&c->channels))
avp->channel_layout = mp_chmap_to_lavc(&c->channels);
return avp;
error:
avcodec_parameters_free(&avp);
return NULL;
}
// Set avctx codec headers for decoding. Returns <0 on failure.
int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c)
{
enum AVMediaType codec_type = avctx->codec_type;
enum AVCodecID codec_id = avctx->codec_id;
AVCodecParameters *avp = mp_codec_params_to_av(c);
if (!avp)
return -1;
int r = avcodec_parameters_to_context(avctx, avp) < 0 ? -1 : 0;
avcodec_parameters_free(&avp);
if (avctx->codec_type != AVMEDIA_TYPE_UNKNOWN)
avctx->codec_type = codec_type;
if (avctx->codec_id != AV_CODEC_ID_NONE)
avctx->codec_id = codec_id;
return r;
}
// Pick a "good" timebase, which will be used to convert double timestamps
@@ -108,37 +155,24 @@ AVRational mp_get_codec_timebase(struct mp_codec_params *c)
return tb;
}
// We merely pass-through our PTS/DTS as an int64_t; libavcodec won't use it.
union pts { int64_t i; double d; };
static AVRational get_def_tb(AVRational *tb)
{
return tb && tb->num > 0 && tb->den > 0 ? *tb : AV_TIME_BASE_Q;
}
// Convert the mpv style timestamp (seconds as double) to a libavcodec style
// timestamp (integer units in a given timebase).
//
// If the given timebase is NULL or invalid, pass through the mpv timestamp by
// reinterpret casting them to int64_t. In this case, the timestamps will be
// non-sense for libavcodec, but we expect that it doesn't interpret them,
// and treats them as opaque.
int64_t mp_pts_to_av(double mp_pts, AVRational *tb)
{
assert(sizeof(int64_t) >= sizeof(double));
if (tb && tb->num > 0 && tb->den > 0) {
return mp_pts == MP_NOPTS_VALUE ?
AV_NOPTS_VALUE : llrint(mp_pts / av_q2d(*tb));
}
// The + 0.0 is to squash possible negative zero mp_pts, which would
// happen to end up as AV_NOPTS_VALUE.
return (union pts){.d = mp_pts + 0.0}.i;
AVRational b = get_def_tb(tb);
return mp_pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : llrint(mp_pts / av_q2d(b));
}
// Inverse of mp_pts_to_av(). (The timebases must be exactly the same.)
double mp_pts_from_av(int64_t av_pts, AVRational *tb)
{
assert(sizeof(int64_t) >= sizeof(double));
if (tb && tb->num > 0 && tb->den > 0)
return av_pts == AV_NOPTS_VALUE ? MP_NOPTS_VALUE : av_pts * av_q2d(*tb);
// Should libavcodec set the PTS to AV_NOPTS_VALUE, it would end up as
// non-sense (usually negative zero) when unwrapped to double.
return av_pts == AV_NOPTS_VALUE ? MP_NOPTS_VALUE : (union pts){.i = av_pts}.d;
AVRational b = get_def_tb(tb);
return av_pts == AV_NOPTS_VALUE ? MP_NOPTS_VALUE : av_pts * av_q2d(b);
}
// Set dst from mpkt. Note that dst is not refcountable.
+3 -2
View File
@@ -31,8 +31,9 @@ struct AVDictionary;
struct mp_log;
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size);
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
void mp_set_lav_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c);
enum AVMediaType mp_to_av_stream_type(int type);
AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c);
int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c);
AVRational mp_get_codec_timebase(struct mp_codec_params *c);
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb);
int64_t mp_pts_to_av(double mp_pts, AVRational *tb);
+4 -4
View File
@@ -42,10 +42,10 @@
#include <libavdevice/avdevice.h>
#endif
#if HAVE_LIBAVRESAMPLE
#if HAVE_IS_LIBAV
#include <libavresample/avresample.h>
#endif
#if HAVE_LIBSWRESAMPLE
#if HAVE_IS_FFMPEG
#include <libswresample/swresample.h>
#endif
@@ -197,10 +197,10 @@ bool print_libav_versions(struct mp_log *log, int v)
{"libavformat", LIBAVFORMAT_VERSION_INT, avformat_version()},
{"libswscale", LIBSWSCALE_VERSION_INT, swscale_version()},
{"libavfilter", LIBAVFILTER_VERSION_INT, avfilter_version()},
#if HAVE_LIBAVRESAMPLE
#if HAVE_IS_LIBAV
{"libavresample", LIBAVRESAMPLE_VERSION_INT, avresample_version()},
#endif
#if HAVE_LIBSWRESAMPLE
#if HAVE_IS_FFMPEG
{"libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version()},
#endif
};
+11 -58
View File
@@ -33,26 +33,6 @@ void mp_add_decoder(struct mp_decoder_list *list, const char *family,
MP_TARRAY_APPEND(list, list->entries, list->num_entries, entry);
}
static void mp_add_decoder_entry(struct mp_decoder_list *list,
struct mp_decoder_entry *entry)
{
mp_add_decoder(list, entry->family, entry->codec, entry->decoder,
entry->desc);
}
static struct mp_decoder_entry *find_decoder(struct mp_decoder_list *list,
bstr family, bstr decoder)
{
for (int n = 0; n < list->num_entries; n++) {
struct mp_decoder_entry *cur = &list->entries[n];
if (bstr_equals0(decoder, cur->decoder)) {
if (bstr_equals0(family, "*") || bstr_equals0(family, cur->family))
return cur;
}
}
return NULL;
}
// Add entry, but only if it's not yet on the list, and if the codec matches.
// If codec == NULL, don't compare codecs.
static void add_new(struct mp_decoder_list *to, struct mp_decoder_entry *entry,
@@ -60,8 +40,7 @@ static void add_new(struct mp_decoder_list *to, struct mp_decoder_entry *entry,
{
if (!entry || (codec && strcmp(entry->codec, codec) != 0))
return;
if (!find_decoder(to, bstr0(entry->family), bstr0(entry->decoder)))
mp_add_decoder_entry(to, entry);
mp_add_decoder(to, entry->family, entry->codec, entry->decoder, entry->desc);
}
// Select a decoder from the given list for the given codec. The selection
@@ -71,10 +50,8 @@ static void add_new(struct mp_decoder_list *to, struct mp_decoder_entry *entry,
// The selection string corresponds to --vd/--ad directly, and has the
// following syntax:
// selection = [<entry> ("," <entry>)*]
// entry = [<family> ":"] <decoder> // prefer decoder
// entry = <family> ":*" // prefer all decoders
// entry = "+" [<family> ":"] <decoder> // force a decoder
// entry = "-" [<family> ":"] <decoder> // exclude a decoder
// entry = <decoder> // prefer decoder
// entry = "-" <decoder> // exclude a decoder
// entry = "-" // don't add fallback decoders
// Forcing a decoder means it's added even if the codec mismatches.
struct mp_decoder_list *mp_select_decoders(struct mp_log *log,
@@ -83,7 +60,6 @@ struct mp_decoder_list *mp_select_decoders(struct mp_log *log,
const char *selection)
{
struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
struct mp_decoder_list *remove = talloc_zero(NULL, struct mp_decoder_list);
if (!codec)
codec = "unknown";
bool stop = false;
@@ -96,28 +72,15 @@ struct mp_decoder_list *mp_select_decoders(struct mp_log *log,
stop = true;
break;
}
bool force = bstr_eatstart0(&entry, "+");
bool exclude = !force && bstr_eatstart0(&entry, "-");
if (exclude || force)
mp_warn(log, "Forcing or excluding codecs is deprecated.\n");
struct mp_decoder_list *dest = exclude ? remove : list;
bstr family, decoder;
if (bstr_split_tok(entry, ":", &family, &decoder)) {
mp_warn(log, "Codec family selection is deprecated. "
"Pass the codec name directly.\n");
} else {
family = bstr0("*");
decoder = entry;
if (bstr_find0(entry, ":") >= 0) {
mp_err(log, "Codec family selection was removed. "
"Pass the codec name directly.\n");
break;
}
if (bstr_equals0(decoder, "*")) {
for (int n = 0; n < all->num_entries; n++) {
struct mp_decoder_entry *cur = &all->entries[n];
if (bstr_equals0(family, cur->family))
add_new(dest, cur, codec);
}
} else {
add_new(dest, find_decoder(all, family, decoder),
force ? NULL : codec);
for (int n = 0; n < all->num_entries; n++) {
struct mp_decoder_entry *cur = &all->entries[n];
if (bstr_equals0(entry, cur->decoder))
add_new(list, cur, codec);
}
}
if (!stop) {
@@ -125,16 +88,6 @@ struct mp_decoder_list *mp_select_decoders(struct mp_log *log,
for (int n = 0; n < all->num_entries; n++)
add_new(list, &all->entries[n], codec);
}
for (int n = 0; n < remove->num_entries; n++) {
struct mp_decoder_entry *ex = &remove->entries[n];
struct mp_decoder_entry *del =
find_decoder(list, bstr0(ex->family), bstr0(ex->decoder));
if (del) {
int index = del - &list->entries[0];
MP_TARRAY_REMOVE_AT(list->entries, list->num_entries, index);
}
}
talloc_free(remove);
return list;
}
+1 -1
View File
@@ -473,7 +473,7 @@ static void encode_2pass_prepare(struct encode_lavc_context *ctx,
if (!(*bytebuf = stream_open(buf, ctx->global))) {
MP_WARN(ctx, "%s: could not open '%s', "
"disabling 2-pass encoding at pass 2\n", prefix, buf);
codec->flags &= ~CODEC_FLAG_PASS2;
codec->flags &= ~AV_CODEC_FLAG_PASS2;
set_to_avdictionary(ctx, dictp, "flags", "-pass2");
} else {
struct bstr content = stream_read_complete(*bytebuf, NULL,
+384
View File
@@ -0,0 +1,384 @@
/*
* 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 <math.h>
#include <libavformat/avformat.h>
#include "common/av_common.h"
#include "common/common.h"
#include "common/global.h"
#include "common/msg.h"
#include "demux/packet.h"
#include "demux/stheader.h"
#include "recorder.h"
#define PTS_ADD(a, b) ((a) == MP_NOPTS_VALUE ? (a) : ((a) + (b)))
// Maximum number of packets we buffer at most to attempt to resync streams.
// Essentially, this should be higher than the highest supported keyframe
// interval.
#define QUEUE_MAX_PACKETS 256
// Number of packets we should buffer at least to determine timestamps (due to
// codec delay and frame reordering, and potentially lack of DTS).
// Keyframe flags can trigger this earlier.
#define QUEUE_MIN_PACKETS 16
struct mp_recorder {
struct mpv_global *global;
struct mp_log *log;
struct mp_recorder_sink **streams;
int num_streams;
bool opened; // mux context is valid
bool muxing; // we're currently recording (instead of preparing)
bool muxing_from_start; // no discontinuity at start
bool dts_warning;
// The start timestamp of the currently recorded segment (the timestamp of
// the first packet of the incoming packet stream).
double base_ts;
// The output packet timestamp corresponding to base_ts. It's the timestamp
// of the first packet of the current segment written to the output.
double rebase_ts;
AVFormatContext *mux;
};
struct mp_recorder_sink {
struct mp_recorder *owner;
struct sh_stream *sh;
AVStream *av_stream;
double max_out_pts;
bool discont;
bool proper_eof;
struct demux_packet **packets;
int num_packets;
};
static int add_stream(struct mp_recorder *priv, struct sh_stream *sh)
{
enum AVMediaType av_type = mp_to_av_stream_type(sh->type);
if (av_type == AVMEDIA_TYPE_UNKNOWN)
return -1;
struct mp_recorder_sink *rst = talloc(priv, struct mp_recorder_sink);
*rst = (struct mp_recorder_sink) {
.owner = priv,
.sh = sh,
.av_stream = avformat_new_stream(priv->mux, NULL),
.max_out_pts = MP_NOPTS_VALUE,
};
if (!rst->av_stream)
return -1;
AVCodecParameters *avp = mp_codec_params_to_av(sh->codec);
if (!avp)
return -1;
#if LIBAVCODEC_VERSION_MICRO >= 100
// We don't know the delay, so make something up. If the format requires
// DTS, the result will probably be broken. FFmpeg provides nothing better
// yet (unless you demux with libavformat, which contains tons of hacks
// that try to determine a PTS).
if (!sh->codec->lav_codecpar)
avp->video_delay = 16;
#endif
if (avp->codec_id == AV_CODEC_ID_NONE)
return -1;
if (avcodec_parameters_copy(rst->av_stream->codecpar, avp) < 0)
return -1;
rst->av_stream->time_base = mp_get_codec_timebase(sh->codec);
MP_TARRAY_APPEND(priv, priv->streams, priv->num_streams, rst);
return 0;
}
struct mp_recorder *mp_recorder_create(struct mpv_global *global,
const char *target_file,
struct sh_stream **streams,
int num_streams)
{
struct mp_recorder *priv = talloc_zero(NULL, struct mp_recorder);
priv->global = global;
priv->log = mp_log_new(priv, global->log, "recorder");
if (!num_streams) {
MP_ERR(priv, "No streams.\n");
goto error;
}
priv->mux = avformat_alloc_context();
if (!priv->mux)
goto error;
priv->mux->oformat = av_guess_format(NULL, target_file, NULL);
if (!priv->mux->oformat) {
MP_ERR(priv, "Output format not found.\n");
goto error;
}
if (avio_open2(&priv->mux->pb, target_file, AVIO_FLAG_WRITE, NULL, NULL) < 0) {
MP_ERR(priv, "Failed opening output file.\n");
goto error;
}
for (int n = 0; n < num_streams; n++) {
if (add_stream(priv, streams[n]) < 0) {
MP_ERR(priv, "Can't mux one of the input streams.\n");
goto error;
}
}
// Not sure how to write this in a "standard" way. It appears only mkv
// and mp4 support this directly.
char version[200];
snprintf(version, sizeof(version), "%s experimental stream recording "
"feature (can generate broken files - please report bugs)",
mpv_version);
av_dict_set(&priv->mux->metadata, "encoding_tool", version, 0);
if (avformat_write_header(priv->mux, NULL) < 0) {
MP_ERR(priv, "Write header failed.\n");
goto error;
}
priv->opened = true;
priv->muxing_from_start = true;
priv->base_ts = MP_NOPTS_VALUE;
priv->rebase_ts = 0;
MP_WARN(priv, "This is an experimental feature. Output files might be "
"broken or not play correctly with various players "
"(including mpv itself).\n");
return priv;
error:
mp_recorder_destroy(priv);
return NULL;
}
static void flush_packets(struct mp_recorder *priv)
{
for (int n = 0; n < priv->num_streams; n++) {
struct mp_recorder_sink *rst = priv->streams[n];
for (int i = 0; i < rst->num_packets; i++)
talloc_free(rst->packets[i]);
rst->num_packets = 0;
}
}
static void mux_packet(struct mp_recorder_sink *rst,
struct demux_packet *pkt)
{
struct mp_recorder *priv = rst->owner;
struct demux_packet mpkt = *pkt;
double diff = priv->rebase_ts - priv->base_ts;
mpkt.pts = PTS_ADD(mpkt.pts, diff);
mpkt.dts = PTS_ADD(mpkt.dts, diff);
rst->max_out_pts = MPMAX(rst->max_out_pts, pkt->pts);
AVPacket avpkt;
mp_set_av_packet(&avpkt, &mpkt, &rst->av_stream->time_base);
avpkt.stream_index = rst->av_stream->index;
if (avpkt.duration < 0 && rst->sh->type != STREAM_SUB)
avpkt.duration = 0;
AVPacket *new_packet = av_packet_clone(&avpkt);
if (!new_packet) {
MP_ERR(priv, "Failed to allocate packet.\n");
return;
}
if (av_interleaved_write_frame(priv->mux, new_packet) < 0)
MP_ERR(priv, "Failed writing packet.\n");
}
// Write all packets that currently can be written.
static void mux_packets(struct mp_recorder_sink *rst, bool force)
{
struct mp_recorder *priv = rst->owner;
if (!priv->muxing || !rst->num_packets)
return;
int safe_count = 0;
for (int n = 0; n < rst->num_packets; n++) {
if (rst->packets[n]->keyframe)
safe_count = n;
}
if (force)
safe_count = rst->num_packets;
for (int n = 0; n < safe_count; n++) {
mux_packet(rst, rst->packets[n]);
talloc_free(rst->packets[n]);
}
// Remove packets[0..safe_count]
memmove(&rst->packets[0], &rst->packets[safe_count],
(rst->num_packets - safe_count) * sizeof(rst->packets[0]));
rst->num_packets -= safe_count;
}
// If there was a discontinuity, check whether we can resume muxing (and from
// where).
static void check_restart(struct mp_recorder *priv)
{
if (priv->muxing)
return;
double min_ts = INFINITY;
double rebase_ts = 0;
for (int n = 0; n < priv->num_streams; n++) {
struct mp_recorder_sink *rst = priv->streams[n];
int min_packets = rst->sh->type == STREAM_VIDEO ? QUEUE_MIN_PACKETS : 1;
rebase_ts = MPMAX(rebase_ts, rst->max_out_pts);
if (rst->num_packets < min_packets) {
if (!rst->proper_eof && rst->sh->type != STREAM_SUB)
return;
continue;
}
for (int i = 0; i < min_packets; i++)
min_ts = MPMIN(min_ts, rst->packets[i]->pts);
}
// Subtitle only stream (wait longer) or stream without any PTS (fuck it).
if (!isfinite(min_ts))
return;
priv->rebase_ts = rebase_ts;
priv->base_ts = min_ts;
for (int n = 0; n < priv->num_streams; n++) {
struct mp_recorder_sink *rst = priv->streams[n];
rst->max_out_pts = min_ts;
}
priv->muxing = true;
if (!priv->muxing_from_start)
MP_WARN(priv, "Discontinuity at timestamp %f.\n", priv->rebase_ts);
}
void mp_recorder_destroy(struct mp_recorder *priv)
{
if (priv->opened) {
for (int n = 0; n < priv->num_streams; n++) {
struct mp_recorder_sink *rst = priv->streams[n];
if (!rst->proper_eof)
continue;
mux_packets(rst, true);
}
if (av_write_trailer(priv->mux) < 0)
MP_ERR(priv, "Writing trailer failed.\n");
}
if (priv->mux) {
if (avio_closep(&priv->mux->pb) < 0)
MP_ERR(priv, "Closing file failed\n");
avformat_free_context(priv->mux);
}
flush_packets(priv);
talloc_free(priv);
}
// This is called on a seek, or when recording was started mid-stream.
void mp_recorder_mark_discontinuity(struct mp_recorder *priv)
{
flush_packets(priv);
for (int n = 0; n < priv->num_streams; n++) {
struct mp_recorder_sink *rst = priv->streams[n];
rst->discont = true;
rst->proper_eof = false;
}
priv->muxing = false;
priv->muxing_from_start = false;
}
// Get a stream for writing. The pointer is valid until mp_recorder is
// destroyed. The stream is the index referencing the stream passed to
// mp_recorder_create().
struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r, int stream)
{
assert(stream >= 0 && stream < r->num_streams);
return r->streams[stream];
}
// Pass a packet to the given stream. The function does not own the packet, but
// can create a new reference to it if it needs to retain it. Can be NULL to
// signal proper end of stream.
void mp_recorder_feed_packet(struct mp_recorder_sink *rst,
struct demux_packet *pkt)
{
struct mp_recorder *priv = rst->owner;
if (!pkt) {
rst->proper_eof = true;
check_restart(priv);
mux_packets(rst, false);
return;
}
if (pkt->dts == MP_NOPTS_VALUE && !priv->dts_warning) {
// No, FFmpeg has no actually usable helpers to generate correct DTS.
// No, FFmpeg doesn't tell us which formats need DTS at all.
// No, we can not shut up the FFmpeg warning, which will follow.
MP_WARN(priv, "Source stream misses DTS on at least some packets!\n"
"If the target file format requires DTS, the written\n"
"file will be invalid.\n");
priv->dts_warning = true;
}
if (rst->discont && !pkt->keyframe)
return;
rst->discont = false;
if (rst->num_packets >= QUEUE_MAX_PACKETS) {
MP_ERR(priv, "Stream %d has too many queued packets; dropping.\n",
rst->av_stream->index);
return;
}
pkt = demux_copy_packet(pkt);
if (!pkt)
return;
MP_TARRAY_APPEND(rst, rst->packets, rst->num_packets, pkt);
check_restart(priv);
mux_packets(rst, false);
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef MP_RECORDER_H_
#define MP_RECORDER_H_
struct mp_recorder;
struct mpv_global;
struct demux_packet;
struct sh_stream;
struct mp_recorder_sink;
struct mp_recorder *mp_recorder_create(struct mpv_global *global,
const char *target_file,
struct sh_stream **streams,
int num_streams);
void mp_recorder_destroy(struct mp_recorder *r);
void mp_recorder_mark_discontinuity(struct mp_recorder *r);
struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r, int stream);
void mp_recorder_feed_packet(struct mp_recorder_sink *s,
struct demux_packet *pkt);
#endif
+1 -1
View File
@@ -70,7 +70,7 @@ static enum cue_command read_cmd(struct bstr *data, struct bstr *out_params)
return CUE_EMPTY;
for (int n = 0; cue_command_strings[n].command != -1; n++) {
struct bstr name = bstr0(cue_command_strings[n].text);
if (bstr_startswith(line, name)) {
if (bstr_case_startswith(line, name)) {
struct bstr rest = bstr_cut(line, name.len);
if (rest.len && !strchr(WHITESPACE, rest.start[0]))
continue;
+73 -68
View File
@@ -205,6 +205,9 @@ struct demux_stream {
struct demux_packet *head;
struct demux_packet *tail;
struct demux_packet *attached_picture;
bool attached_picture_added;
// for closed captions (demuxer_feed_caption)
struct sh_stream *cc;
};
@@ -243,6 +246,7 @@ static void ds_flush(struct demux_stream *ds)
ds->last_pos = -1;
ds->last_dts = MP_NOPTS_VALUE;
ds->correct_dts = ds->correct_pos = true;
ds->attached_picture_added = false;
}
void demux_set_ts_offset(struct demuxer *demuxer, double offset)
@@ -291,6 +295,8 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
if (!sh->codec->codec)
sh->codec->codec = "";
sh->ds->attached_picture = sh->attached_picture;
sh->index = in->num_streams;
if (sh->ff_index < 0)
sh->ff_index = sh->index;
@@ -655,7 +661,9 @@ static bool read_packet(struct demux_internal *in)
demux->desc->seek(demux, seek_pts, SEEK_BACKWARD | SEEK_HR);
}
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
bool eof = true;
if (demux->desc->fill_buffer && !demux_cancel_test(demux))
eof = demux->desc->fill_buffer(demux) <= 0;
update_cache(in);
pthread_mutex_lock(&in->lock);
@@ -677,28 +685,6 @@ static bool read_packet(struct demux_internal *in)
return true;
}
// must be called locked; may temporarily unlock
static void ds_get_packets(struct demux_stream *ds)
{
const char *t = stream_type_name(ds->type);
struct demux_internal *in = ds->in;
MP_DBG(in, "reading packet for %s\n", t);
in->eof = false; // force retry
while (ds->selected && !ds->head) {
ds->active = true;
// Note: the following code marks EOF if it can't continue
if (in->threading) {
MP_VERBOSE(in, "waiting for demux thread (%s)\n", t);
pthread_cond_signal(&in->wakeup);
pthread_cond_wait(&in->wakeup, &in->lock);
} else {
read_packet(in);
}
if (ds->eof)
break;
}
}
static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
@@ -777,6 +763,13 @@ static void *demux_thread(void *pctx)
static struct demux_packet *dequeue_packet(struct demux_stream *ds)
{
if (ds->attached_picture) {
ds->eof = true;
if (ds->attached_picture_added)
return NULL;
ds->attached_picture_added = true;
return demux_copy_packet(ds->attached_picture);
}
if (!ds->head)
return NULL;
struct demux_packet *pkt = ds->head;
@@ -820,16 +813,23 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
return pkt;
}
// Whether to avoid actively demuxing new packets to find a new packet on the
// given stream.
// Attached pictures (cover art) should never actively read.
// Sparse packets (Subtitles) interleaved with other non-sparse packets (video,
// audio) should never be read actively, meaning the demuxer thread does not
// try to exceed default readahead in order to find a new packet.
static bool use_lazy_subtitle_reading(struct demux_stream *ds)
static bool use_lazy_packet_reading(struct demux_stream *ds)
{
if (ds->attached_picture)
return true;
if (ds->type != STREAM_SUB)
return false;
// Subtitles are only lazily read if there's at least 1 other actively read
// stream.
for (int n = 0; n < ds->in->num_streams; n++) {
struct demux_stream *s = ds->in->streams[n]->ds;
if (s->type != STREAM_SUB && s->selected && !s->eof)
if (s->type != STREAM_SUB && s->selected && !s->eof && !s->attached_picture)
return true;
}
return false;
@@ -843,12 +843,29 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh)
struct demux_stream *ds = sh ? sh->ds : NULL;
struct demux_packet *pkt = NULL;
if (ds) {
pthread_mutex_lock(&ds->in->lock);
if (!use_lazy_subtitle_reading(ds))
ds_get_packets(ds);
struct demux_internal *in = ds->in;
pthread_mutex_lock(&in->lock);
if (!use_lazy_packet_reading(ds)) {
const char *t = stream_type_name(ds->type);
MP_DBG(in, "reading packet for %s\n", t);
in->eof = false; // force retry
while (ds->selected && !ds->head) {
ds->active = true;
// Note: the following code marks EOF if it can't continue
if (in->threading) {
MP_VERBOSE(in, "waiting for demux thread (%s)\n", t);
pthread_cond_signal(&in->wakeup);
pthread_cond_wait(&in->wakeup, &in->lock);
} else {
read_packet(in);
}
if (ds->eof)
break;
}
}
pkt = dequeue_packet(ds);
pthread_cond_signal(&ds->in->wakeup); // possibly read more
pthread_mutex_unlock(&ds->in->lock);
pthread_cond_signal(&in->wakeup); // possibly read more
pthread_mutex_unlock(&in->lock);
}
return pkt;
}
@@ -874,7 +891,7 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
if (ds->in->threading) {
pthread_mutex_lock(&ds->in->lock);
*out_pkt = dequeue_packet(ds);
if (use_lazy_subtitle_reading(ds)) {
if (use_lazy_packet_reading(ds)) {
r = *out_pkt ? 1 : -1;
} else {
r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0);
@@ -1231,8 +1248,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.events = DEMUX_EVENT_ALL,
};
demuxer->seekable = stream->seekable;
if (demuxer->stream->uncached_stream &&
!demuxer->stream->uncached_stream->seekable)
if (demuxer->stream->underlying && !demuxer->stream->underlying->seekable)
demuxer->seekable = false;
struct demux_internal *in = demuxer->in = talloc_ptrtype(demuxer, in);
@@ -1249,9 +1265,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
pthread_mutex_init(&in->lock, NULL);
pthread_cond_init(&in->wakeup, NULL);
if (stream->uncached_stream)
in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache);
*in->d_thread = *demuxer;
*in->d_buffer = *demuxer;
@@ -1290,7 +1303,8 @@ static struct demuxer *open_given_type(struct mpv_global *global,
demux_init_cache(demuxer);
demux_changed(in->d_thread, DEMUX_EVENT_ALL);
demux_update(demuxer);
stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD, &(int){false});
stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD,
&(int){params ? params->initial_readahead : false});
if (!(params && params->disable_timeline)) {
struct timeline *tl = timeline_load(global, log, demuxer);
if (tl) {
@@ -1299,11 +1313,15 @@ static struct demuxer *open_given_type(struct mpv_global *global,
struct demuxer *sub =
open_given_type(global, log, &demuxer_desc_timeline, stream,
&params2, DEMUX_CHECK_FORCE);
if (sub)
return sub;
timeline_destroy(tl);
if (sub) {
demuxer = sub;
} else {
timeline_destroy(tl);
}
}
}
if (demuxer->is_network || stream->caching)
in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache);
return demuxer;
}
@@ -1382,12 +1400,6 @@ struct demuxer *demux_open_url(const char *url,
cancel, global);
if (!s)
return NULL;
if (params->allow_capture) {
char *f;
mp_read_option_raw(global, "stream-capture", &m_option_type_string, &f);
stream_set_capture_file(s, f);
talloc_free(f);
}
if (!params->disable_cache)
stream_enable_cache_defaults(&s);
struct demuxer *d = demux_open(s, params, global);
@@ -1724,6 +1736,7 @@ static void thread_demux_control(void *p)
int demux_control(demuxer_t *demuxer, int cmd, void *arg)
{
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_user);
if (in->threading) {
pthread_mutex_lock(&in->lock);
@@ -1735,7 +1748,20 @@ int demux_control(demuxer_t *demuxer, int cmd, void *arg)
int r = 0;
struct demux_control_args args = {demuxer, cmd, arg, &r};
demux_run_on_thread(demuxer, thread_demux_control, &args);
if (in->threading) {
MP_VERBOSE(in, "blocking on demuxer thread\n");
pthread_mutex_lock(&in->lock);
while (in->run_fn)
pthread_cond_wait(&in->wakeup, &in->lock);
in->run_fn = thread_demux_control;
in->run_fn_arg = &args;
pthread_cond_signal(&in->wakeup);
while (in->run_fn)
pthread_cond_wait(&in->wakeup, &in->lock);
pthread_mutex_unlock(&in->lock);
} else {
thread_demux_control(&args);
}
return r;
}
@@ -1747,27 +1773,6 @@ int demux_stream_control(demuxer_t *demuxer, int ctrl, void *arg)
return c.res;
}
void demux_run_on_thread(struct demuxer *demuxer, void (*fn)(void *), void *ctx)
{
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_user);
if (in->threading) {
MP_VERBOSE(in, "blocking on demuxer thread\n");
pthread_mutex_lock(&in->lock);
while (in->run_fn)
pthread_cond_wait(&in->wakeup, &in->lock);
in->run_fn = fn;
in->run_fn_arg = ctx;
pthread_cond_signal(&in->wakeup);
while (in->run_fn)
pthread_cond_wait(&in->wakeup, &in->lock);
pthread_mutex_unlock(&in->lock);
} else {
fn(ctx);
}
}
bool demux_cancel_test(struct demuxer *demuxer)
{
return mp_cancel_test(demuxer->stream->cancel);
+3 -3
View File
@@ -162,9 +162,11 @@ struct demuxer_params {
bool *matroska_was_valid;
struct timeline *timeline;
bool disable_timeline;
bool initial_readahead;
bstr init_fragment;
bool skip_lavf_probing;
// -- demux_open_url() only
int stream_flags;
bool allow_capture;
bool disable_cache;
// result
bool demuxer_failed;
@@ -287,8 +289,6 @@ double demuxer_get_time_length(struct demuxer *demuxer);
int demux_stream_control(demuxer_t *demuxer, int ctrl, void *arg);
void demux_run_on_thread(struct demuxer *demuxer, void (*fn)(void *), void *ctx);
void demux_changed(demuxer_t *demuxer, int events);
void demux_update(demuxer_t *demuxer);
+22 -6
View File
@@ -43,6 +43,8 @@ struct priv {
double base_dts; // packet DTS that maps to base_time
double last_dts; // DTS of previously demuxed packet
bool seek_reinit; // needs reinit after seek
bool is_dvd, is_cdda;
};
// If the timestamp difference between subsequent packets is this big, assume
@@ -65,7 +67,7 @@ static void reselect_streams(demuxer_t *demuxer)
static void get_disc_lang(struct stream *stream, struct sh_stream *sh)
{
struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
if (stream->uncached_type == STREAMTYPE_DVD && sh->type == STREAM_SUB)
if (sh->type == STREAM_SUB)
req.id = req.id & 0x1F; // mpeg ID to index
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
if (req.name[0])
@@ -76,7 +78,7 @@ static void add_dvd_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct stream *stream = demuxer->stream;
if (stream->uncached_type != STREAMTYPE_DVD)
if (!p->is_dvd)
return;
struct stream_dvd_info_req info;
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
@@ -162,7 +164,7 @@ static void d_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
struct priv *p = demuxer->priv;
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
if (p->is_cdda) {
demux_seek(p->slave, seek_pts, flags);
return;
}
@@ -222,7 +224,7 @@ static int d_fill_buffer(demuxer_t *demuxer)
return 1;
}
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
if (p->is_cdda) {
demux_add_packet(sh, pkt);
return 1;
}
@@ -285,7 +287,21 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
struct demuxer_params params = {.force_format = "+lavf"};
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA)
struct stream *cur = demuxer->stream;
const char *sname = "";
while (cur) {
if (cur->info)
sname = cur->info->name;
cur = cur->underlying; // down the caching chain
}
p->is_cdda = strcmp(sname, "cdda") == 0;
p->is_dvd = strcmp(sname, "dvd") == 0 ||
strcmp(sname, "ifo") == 0 ||
strcmp(sname, "dvdnav") == 0 ||
strcmp(sname, "ifo_dvdnav") == 0;
if (p->is_cdda)
params.force_format = "+rawaudio";
char *t = NULL;
@@ -311,7 +327,7 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
// (actually libavformat/mpegts.c) to seek sometimes when reading a packet.
// It does this to seek back a bit in case the current file position points
// into the middle of a packet.
if (demuxer->stream->uncached_type != STREAMTYPE_CDDA) {
if (!p->is_cdda) {
demuxer->stream->seekable = false;
// Can be seekable even if the stream isn't.
+115 -39
View File
@@ -44,12 +44,15 @@ struct tl_part {
};
struct tl_parts {
bool dash;
char *init_fragment_url;
struct tl_part *parts;
int num_parts;
};
struct priv {
bstr data;
bool allow_any;
};
// Parse a time (absolute file time or duration). Currently equivalent to a
@@ -64,6 +67,8 @@ static bool parse_time(bstr str, double *out_time)
return true;
}
#define MAX_PARAMS 10
/* Returns a list of parts, or NULL on parse error.
* Syntax (without file header or URI prefix):
* url ::= <entry> ( (';' | '\n') <entry> )*
@@ -78,7 +83,10 @@ static struct tl_parts *parse_edl(bstr str)
bstr_split_tok(str, "\n", &(bstr){0}, &str);
if (bstr_eatstart0(&str, "\n") || bstr_eatstart0(&str, ";"))
continue;
bool is_header = bstr_eatstart0(&str, "!");
struct tl_part p = { .length = -1 };
bstr param_names[MAX_PARAMS];
bstr param_vals[MAX_PARAMS];
int nparam = 0;
while (1) {
bstr name, val;
@@ -116,10 +124,25 @@ static struct tl_parts *parse_edl(bstr str)
if (bstr_equals0(val, "chapters"))
p.chapter_ts = true;
}
if (nparam >= MAX_PARAMS)
goto error;
param_names[nparam] = name;
param_vals[nparam] = val;
nparam++;
if (!bstr_eatstart0(&str, ","))
break;
}
if (is_header) {
if (tl->num_parts)
goto error; // can't have header once an entry was defined
bstr type = param_vals[0]; // value, because no "="
if (bstr_equals0(type, "mp4_dash")) {
tl->dash = true;
if (bstr_equals0(param_names[1], "init"))
tl->init_fragment_url = bstrto0(tl, param_vals[1]);
}
continue;
}
if (!p.filename)
goto error;
MP_TARRAY_APPEND(tl, tl->parts, tl->num_parts, p);
@@ -139,7 +162,10 @@ static struct demuxer *open_source(struct timeline *tl, char *filename)
if (strcmp(d->stream->url, filename) == 0)
return d;
}
struct demuxer *d = demux_open_url(filename, NULL, tl->cancel, tl->global);
struct demuxer_params params = {
.init_fragment = tl->init_fragment,
};
struct demuxer *d = demux_open_url(filename, &params, tl->cancel, tl->global);
if (d) {
MP_TARRAY_APPEND(tl, tl->sources, tl->num_sources, d);
} else {
@@ -202,61 +228,110 @@ static void resolve_timestamps(struct tl_part *part, struct demuxer *demuxer)
static void build_timeline(struct timeline *tl, struct tl_parts *parts)
{
tl->track_layout = NULL;
tl->dash = parts->dash;
if (parts->init_fragment_url && parts->init_fragment_url[0]) {
MP_VERBOSE(tl, "Opening init fragment...\n");
stream_t *s = stream_create(parts->init_fragment_url, STREAM_READ,
tl->cancel, tl->global);
if (s)
tl->init_fragment = stream_read_complete(s, tl, 1000000);
free_stream(s);
if (!tl->init_fragment.len) {
MP_ERR(tl, "Could not read init fragment.\n");
goto error;
}
s = open_memory_stream(tl->init_fragment.start, tl->init_fragment.len);
tl->track_layout = demux_open(s, NULL, tl->global);
if (!tl->track_layout) {
free_stream(s);
MP_ERR(tl, "Could not demux init fragment.\n");
goto error;
}
}
tl->parts = talloc_array_ptrtype(tl, tl->parts, parts->num_parts + 1);
double starttime = 0;
for (int n = 0; n < parts->num_parts; n++) {
struct tl_part *part = &parts->parts[n];
struct demuxer *source = open_source(tl, part->filename);
if (!source)
goto error;
struct demuxer *source = NULL;
resolve_timestamps(part, source);
if (tl->dash) {
part->offset = starttime;
if (part->length <= 0)
MP_WARN(tl, "Segment %d has unknown duration.\n", n);
if (part->offset_set)
MP_WARN(tl, "Offsets are ignored.\n");
tl->demuxer->is_network = true;
double end_time = source_get_length(source);
if (end_time >= 0)
end_time += source->start_time;
// Unknown length => use rest of the file. If duration is unknown, make
// something up.
if (part->length < 0) {
if (end_time < 0) {
MP_WARN(tl, "EDL: source file '%s' has unknown duration.\n",
part->filename);
end_time = 1;
if (!tl->track_layout) {
source = open_source(tl, part->filename);
if (!source)
goto error;
}
part->length = end_time - part->offset;
} else if (end_time >= 0) {
double end_part = part->offset + part->length;
if (end_part > end_time) {
MP_WARN(tl, "EDL: entry %d uses %f "
"seconds, but file has only %f seconds.\n",
n, end_part, end_time);
} else {
MP_VERBOSE(tl, "Opening segment %d...\n", n);
source = open_source(tl, part->filename);
if (!source)
goto error;
resolve_timestamps(part, source);
double end_time = source_get_length(source);
if (end_time >= 0)
end_time += source->start_time;
// Unknown length => use rest of the file. If duration is unknown, make
// something up.
if (part->length < 0) {
if (end_time < 0) {
MP_WARN(tl, "EDL: source file '%s' has unknown duration.\n",
part->filename);
end_time = 1;
}
part->length = end_time - part->offset;
} else if (end_time >= 0) {
double end_part = part->offset + part->length;
if (end_part > end_time) {
MP_WARN(tl, "EDL: entry %d uses %f "
"seconds, but file has only %f seconds.\n",
n, end_part, end_time);
}
}
// Add a chapter between each file.
struct demux_chapter ch = {
.pts = starttime,
.metadata = talloc_zero(tl, struct mp_tags),
};
mp_tags_set_str(ch.metadata, "title", part->filename);
MP_TARRAY_APPEND(tl, tl->chapters, tl->num_chapters, ch);
// Also copy the source file's chapters for the relevant parts
copy_chapters(&tl->chapters, &tl->num_chapters, source, part->offset,
part->length, starttime);
}
// Add a chapter between each file.
struct demux_chapter ch = {
.pts = starttime,
.metadata = talloc_zero(tl, struct mp_tags),
};
mp_tags_set_str(ch.metadata, "title", part->filename);
MP_TARRAY_APPEND(tl, tl->chapters, tl->num_chapters, ch);
// Also copy the source file's chapters for the relevant parts
copy_chapters(&tl->chapters, &tl->num_chapters, source, part->offset,
part->length, starttime);
tl->parts[n] = (struct timeline_part) {
.start = starttime,
.source_start = part->offset,
.source = source,
.url = talloc_strdup(tl, part->filename),
};
starttime += part->length;
if (source) {
tl->demuxer->is_network |= source->is_network;
if (!tl->track_layout)
tl->track_layout = source;
}
}
tl->parts[parts->num_parts] = (struct timeline_part) {.start = starttime};
tl->num_parts = parts->num_parts;
tl->track_layout = tl->parts[0].source;
return;
error:
@@ -286,8 +361,7 @@ static void build_mpv_edl_timeline(struct timeline *tl)
return;
}
MP_TARRAY_APPEND(tl, tl->sources, tl->num_sources, tl->demuxer);
// Source is .edl and not edl:// => don't allow arbitrary paths
if (tl->demuxer->stream->uncached_type != STREAMTYPE_EDL)
if (!p->allow_any)
fix_filenames(parts, tl->demuxer->filename);
build_timeline(tl, parts);
talloc_free(parts);
@@ -303,8 +377,10 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
demuxer->fully_read = true;
struct stream *s = demuxer->stream;
if (s->uncached_type == STREAMTYPE_EDL) {
if (s->info && strcmp(s->info->name, "edl") == 0) {
p->data = bstr0(s->path);
// Source is edl:// and not .edl => allow arbitrary paths
p->allow_any = true;
return 0;
}
if (check >= DEMUX_CHECK_UNSAFE) {
+39 -15
View File
@@ -183,6 +183,8 @@ typedef struct lavf_priv {
AVInputFormat *avif;
int avif_flags;
AVFormatContext *avfc;
bstr init_fragment;
int64_t stream_pos;
AVIOContext *pb;
struct sh_stream **streams; // NULL for unknown streams
int num_streams;
@@ -218,7 +220,14 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
struct stream *stream = priv->stream;
int ret;
ret = stream_read(stream, buf, size);
if (priv->stream_pos < priv->init_fragment.len) {
ret = MPMIN(size, priv->init_fragment.len - priv->stream_pos);
memcpy(buf, priv->init_fragment.start + priv->stream_pos, ret);
priv->stream_pos += ret;
} else {
ret = stream_read(stream, buf, size);
priv->stream_pos = priv->init_fragment.len + stream_tell(stream);
}
MP_TRACE(demuxer, "%d=mp_read(%p, %p, %d), pos: %"PRId64", eof:%d\n",
ret, stream, buf, size, stream_tell(stream), stream->eof);
@@ -230,32 +239,44 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
struct demuxer *demuxer = opaque;
lavf_priv_t *priv = demuxer->priv;
struct stream *stream = priv->stream;
int64_t current_pos;
MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %s)\n", stream, pos,
whence == SEEK_END ? "end" :
whence == SEEK_CUR ? "cur" :
whence == SEEK_SET ? "set" : "size");
if (whence == SEEK_END || whence == AVSEEK_SIZE) {
int64_t end = stream_get_size(stream);
int64_t end = stream_get_size(stream) + priv->init_fragment.len;
if (end < 0)
return -1;
if (whence == AVSEEK_SIZE)
return end;
pos += end;
} else if (whence == SEEK_CUR) {
pos += stream_tell(stream);
pos += priv->stream_pos;
} else if (whence != SEEK_SET) {
return -1;
}
if (pos < 0)
return -1;
current_pos = stream_tell(stream);
if (stream_seek(stream, pos) == 0) {
int64_t stream_target = pos - priv->init_fragment.len;
bool seek_before = stream_target < 0;
if (seek_before)
stream_target = 0; // within init segment - seek real stream to 0
int64_t current_pos = stream_tell(stream);
if (stream_seek(stream, stream_target) == 0) {
stream_seek(stream, current_pos);
return -1;
}
if (seek_before) {
priv->stream_pos = pos;
} else {
priv->stream_pos = priv->init_fragment.len + stream_tell(stream);
}
return pos;
}
@@ -338,7 +359,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
priv->filename = remove_prefix(s->url, prefixes);
char *avdevice_format = NULL;
if (s->uncached_type == STREAMTYPE_AVDEVICE) {
if (s->info && strcmp(s->info->name, "avdevice") == 0) {
// always require filename in the form "format:filename"
char *sep = strchr(priv->filename, ':');
if (!sep) {
@@ -376,7 +397,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
// Disable file-extension matching with normal checks
.filename = check <= DEMUX_CHECK_REQUEST ? priv->filename : "",
.buf_size = 0,
.buf = av_mallocz(PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE),
.buf = av_mallocz(PROBE_BUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE),
};
if (!avpd.buf)
return -1;
@@ -771,6 +792,9 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
if (lavf_check_file(demuxer, check) < 0)
return -1;
if (demuxer->params)
priv->init_fragment = bstrdup(priv, demuxer->params->init_fragment);
avfc = avformat_alloc_context();
if (!avfc)
return -1;
@@ -808,10 +832,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVDictionary *dopts = NULL;
if ((priv->avif_flags & AVFMT_NOFILE) ||
priv->stream->type == STREAMTYPE_AVDEVICE ||
priv->format_hack.no_stream)
{
if ((priv->avif_flags & AVFMT_NOFILE) || priv->format_hack.no_stream) {
mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log);
// This might be incorrect.
demuxer->seekable = true;
@@ -867,9 +888,12 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
av_dict_free(&dopts);
priv->avfc = avfc;
if (avformat_find_stream_info(avfc, NULL) < 0) {
MP_ERR(demuxer, "av_find_stream_info() failed\n");
return -1;
if (!demuxer->params || !demuxer->params->skip_lavf_probing) {
if (avformat_find_stream_info(avfc, NULL) < 0) {
MP_ERR(demuxer, "av_find_stream_info() failed\n");
return -1;
}
}
MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
+3 -2
View File
@@ -294,9 +294,10 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
mf_t *mf;
if (strncmp(demuxer->stream->url, "mf://", 5) == 0 &&
demuxer->stream->type == STREAMTYPE_MF)
demuxer->stream->info && strcmp(demuxer->stream->info->name, "mf") == 0)
{
mf = open_mf_pattern(demuxer, demuxer->log, demuxer->stream->url + 5);
else {
} else {
mf = open_mf_single(demuxer, demuxer->log, demuxer->stream->url);
int bog = 0;
MP_TARRAY_APPEND(mf, mf->streams, bog, demuxer->stream);
+28 -2
View File
@@ -162,6 +162,7 @@ struct block_info {
bstr data;
void *alloc;
int64_t filepos;
struct ebml_block_additions *additions;
};
typedef struct mkv_demuxer {
@@ -2392,6 +2393,8 @@ static void free_block(struct block_info *block)
free(block->alloc);
block->alloc = NULL;
block->data = (bstr){0};
talloc_free(block->additions);
block->additions = NULL;
}
static void index_block(demuxer_t *demuxer, struct block_info *block)
@@ -2552,6 +2555,15 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
block_info->discardpadding / 1e9 * srate);
mkv_d->a_skip_preroll = 0;
}
if (block_info->additions) {
for (int n = 0; n < block_info->additions->n_block_more; n++) {
struct ebml_block_more *add =
&block_info->additions->block_more[n];
int64_t id = add->n_block_add_id ? add->block_add_id : 1;
demux_packet_add_blockadditional(dp, id,
add->block_additional.start, add->block_additional.len);
}
}
mkv_parse_and_add_packet(demuxer, track, dp);
talloc_free_children(track->parser_tmp);
@@ -2604,8 +2616,22 @@ static int read_block_group(demuxer_t *demuxer, int64_t end,
int64_t num = ebml_read_int(s);
if (num == EBML_INT_INVALID)
goto error;
if (num)
block->keyframe = false;
block->keyframe = false;
break;
case MATROSKA_ID_BLOCKADDITIONS:;
struct ebml_block_additions additions = {0};
struct ebml_parse_ctx parse_ctx = {demuxer->log};
if (ebml_read_element(s, &parse_ctx, &additions,
&ebml_block_additions_desc) < 0)
return -1;
if (additions.n_block_more > 0) {
block->additions =
talloc_memdup(NULL, &additions, sizeof(additions));
talloc_steal(block->additions, parse_ctx.talloc_ctx);
parse_ctx.talloc_ctx = NULL;
}
talloc_free(parse_ctx.talloc_ctx);
break;
case MATROSKA_ID_CLUSTER:
+1 -1
View File
@@ -267,7 +267,7 @@ static void find_ordered_chapter_sources(struct tl_ctx *ctx)
talloc_steal(tmp, pl);
for (struct playlist_entry *e = pl ? pl->first : NULL; e; e = e->next)
MP_TARRAY_APPEND(tmp, filenames, num_filenames, e->filename);
} else if (ctx->demuxer->stream->uncached_type != STREAMTYPE_FILE) {
} else if (!ctx->demuxer->stream->is_local_file) {
MP_WARN(ctx, "Playback source is not a "
"normal disk file. Will not search for related files.\n");
} else {
+1 -1
View File
@@ -277,7 +277,7 @@ static int cmp_filename(const void *a, const void *b)
static int parse_dir(struct pl_parser *p)
{
if (p->real_stream->type != STREAMTYPE_DIR)
if (!p->real_stream->is_directory)
return -1;
if (p->probing)
return 0;
+122 -58
View File
@@ -24,11 +24,14 @@
#include "demux.h"
#include "timeline.h"
#include "stheader.h"
#include "stream/stream.h"
struct segment {
int index;
double start, end;
double d_start;
char *url;
bool lazy;
struct demuxer *d;
// stream_map[sh_stream.index] = index into priv.streams, where sh_stream
// is a stream from the source d. It's used to map the streams of the
@@ -51,6 +54,7 @@ struct priv {
struct timeline *tl;
double duration;
bool dash;
struct segment **segments;
int num_segments;
@@ -65,6 +69,50 @@ struct priv {
int eos_packets;
};
static bool target_stream_used(struct segment *seg, int target_index)
{
for (int n = 0; n < seg->num_stream_map; n++) {
if (seg->stream_map[n] == target_index)
return true;
}
return false;
}
// Create mapping from segment streams to virtual timeline streams.
static void associate_streams(struct demuxer *demuxer, struct segment *seg)
{
struct priv *p = demuxer->priv;
if (!seg->d || seg->stream_map)
return;
int counts[STREAM_TYPE_COUNT] = {0};
int num_streams = demux_get_num_stream(seg->d);
for (int n = 0; n < num_streams; n++) {
struct sh_stream *sh = demux_get_stream(seg->d, n);
// Try associating by demuxer ID (supposedly useful for ordered chapters).
struct sh_stream *other =
demuxer_stream_by_demuxer_id(demuxer, sh->type, sh->demuxer_id);
if (!other || !target_stream_used(seg, other->index)) {
// Try to associate the first unused stream with matching media type.
for (int i = 0; i < p->num_streams; i++) {
struct sh_stream *cur = p->streams[i].sh;
if (cur->type == sh->type && !target_stream_used(seg, cur->index))
{
other = cur;
break;
}
}
}
MP_TARRAY_APPEND(seg, seg->stream_map, seg->num_stream_map,
other ? other->index : -1);
counts[sh->type] += 1;
}
}
static void reselect_streams(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
@@ -77,6 +125,9 @@ static void reselect_streams(struct demuxer *demuxer)
for (int n = 0; n < p->num_segments; n++) {
struct segment *seg = p->segments[n];
for (int i = 0; i < seg->num_stream_map; i++) {
if (!seg->d)
continue;
struct sh_stream *sh = demux_get_stream(seg->d, i);
bool selected = false;
if (seg->stream_map[i] >= 0)
@@ -89,8 +140,42 @@ static void reselect_streams(struct demuxer *demuxer)
}
}
static void close_lazy_segments(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
// unload previous segment
for (int n = 0; n < p->num_segments; n++) {
struct segment *seg = p->segments[n];
if (seg != p->current && seg->d && seg->lazy) {
free_demuxer_and_stream(seg->d);
seg->d = NULL;
}
}
}
static void reopen_lazy_segments(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
if (p->current->d)
return;
close_lazy_segments(demuxer);
struct demuxer_params params = {
.init_fragment = p->tl->init_fragment,
.skip_lavf_probing = true,
};
p->current->d = demux_open_url(p->current->url, &params,
demuxer->stream->cancel, demuxer->global);
if (!p->current->d && !demux_cancel_test(demuxer))
MP_ERR(demuxer, "failed to load segment\n");
associate_streams(demuxer, p->current);
}
static void switch_segment(struct demuxer *demuxer, struct segment *new,
double start_pts, int flags)
double start_pts, int flags, bool init)
{
struct priv *p = demuxer->priv;
@@ -100,9 +185,14 @@ static void switch_segment(struct demuxer *demuxer, struct segment *new,
MP_VERBOSE(demuxer, "switch to segment %d\n", new->index);
p->current = new;
reopen_lazy_segments(demuxer);
if (!new->d)
return;
reselect_streams(demuxer);
demux_set_ts_offset(new->d, new->start - new->d_start);
demux_seek(new->d, start_pts, flags);
if (!p->dash)
demux_set_ts_offset(new->d, new->start - new->d_start);
if (!p->dash || !init)
demux_seek(new->d, start_pts, flags);
for (int n = 0; n < p->num_streams; n++) {
struct virtual_stream *vs = &p->streams[n];
@@ -129,7 +219,7 @@ static void d_seek(struct demuxer *demuxer, double seek_pts, int flags)
}
}
switch_segment(demuxer, new, pts, flags);
switch_segment(demuxer, new, pts, flags, false);
}
static int d_fill_buffer(struct demuxer *demuxer)
@@ -137,9 +227,11 @@ static int d_fill_buffer(struct demuxer *demuxer)
struct priv *p = demuxer->priv;
if (!p->current)
switch_segment(demuxer, p->segments[0], 0, 0);
switch_segment(demuxer, p->segments[0], 0, 0, true);
struct segment *seg = p->current;
if (!seg || !seg->d)
return 0;
struct demux_packet *pkt = demux_read_any_packet(seg->d);
if (!pkt || pkt->pts >= seg->end)
@@ -176,20 +268,21 @@ static int d_fill_buffer(struct demuxer *demuxer)
}
if (!next)
return 0;
switch_segment(demuxer, next, next->start, 0);
switch_segment(demuxer, next, next->start, 0, true);
return 1; // reader will retry
}
if (pkt->stream < 0 || pkt->stream > seg->num_stream_map)
goto drop;
if (!pkt->codec)
pkt->codec = demux_get_stream(seg->d, pkt->stream)->codec;
if (pkt->start == MP_NOPTS_VALUE || pkt->start < seg->start)
pkt->start = seg->start;
if (pkt->end == MP_NOPTS_VALUE || pkt->end > seg->end)
pkt->end = seg->end;
if (!p->dash) {
if (!pkt->codec)
pkt->codec = demux_get_stream(seg->d, pkt->stream)->codec;
if (pkt->start == MP_NOPTS_VALUE || pkt->start < seg->start)
pkt->start = seg->start;
if (pkt->end == MP_NOPTS_VALUE || pkt->end > seg->end)
pkt->end = seg->end;
}
pkt->stream = seg->stream_map[pkt->stream];
if (pkt->stream < 0)
@@ -214,7 +307,8 @@ static int d_fill_buffer(struct demuxer *demuxer)
}
}
pkt->new_segment |= vs->new_segment;
if (!p->dash)
pkt->new_segment |= vs->new_segment;
vs->new_segment = false;
demux_add_packet(vs->sh, pkt);
@@ -232,9 +326,9 @@ static void print_timeline(struct demuxer *demuxer)
MP_VERBOSE(demuxer, "Timeline segments:\n");
for (int n = 0; n < p->num_segments; n++) {
struct segment *seg = p->segments[n];
int src_num = -1;
for (int i = 0; i < p->tl->num_sources; i++) {
if (p->tl->sources[i] == seg->d) {
int src_num = n;
for (int i = 0; i < n - 1; i++) {
if (seg->d && p->segments[i]->d == seg->d) {
src_num = i;
break;
}
@@ -242,50 +336,12 @@ static void print_timeline(struct demuxer *demuxer)
MP_VERBOSE(demuxer, " %2d: %12f [%12f] (", n, seg->start, seg->d_start);
for (int i = 0; i < seg->num_stream_map; i++)
MP_VERBOSE(demuxer, "%s%d", i ? " " : "", seg->stream_map[i]);
MP_VERBOSE(demuxer, ") %d:'%s'\n", src_num, seg->d->filename);
MP_VERBOSE(demuxer, ") %d:'%s'\n", src_num, seg->url);
}
MP_VERBOSE(demuxer, "Total duration: %f\n", p->duration);
}
static bool target_stream_used(struct segment *seg, int target_index)
{
for (int n = 0; n < seg->num_stream_map; n++) {
if (seg->stream_map[n] == target_index)
return true;
}
return false;
}
// Create mapping from segment streams to virtual timeline streams.
static void associate_streams(struct demuxer *demuxer, struct segment *seg)
{
struct priv *p = demuxer->priv;
int counts[STREAM_TYPE_COUNT] = {0};
int num_streams = demux_get_num_stream(seg->d);
for (int n = 0; n < num_streams; n++) {
struct sh_stream *sh = demux_get_stream(seg->d, n);
// Try associating by demuxer ID (supposedly useful for ordered chapters).
struct sh_stream *other =
demuxer_stream_by_demuxer_id(demuxer, sh->type, sh->demuxer_id);
if (!other || !target_stream_used(seg, other->index)) {
// Try to associate the first unused stream with matching media type.
for (int i = 0; i < p->num_streams; i++) {
struct sh_stream *cur = p->streams[i].sh;
if (cur->type == sh->type && !target_stream_used(seg, cur->index))
{
other = cur;
break;
}
}
}
MP_TARRAY_APPEND(seg, seg->stream_map, seg->num_stream_map,
other ? other->index : -1);
counts[sh->type] += 1;
}
if (p->dash)
MP_VERBOSE(demuxer, "Durations and offsets are non-authoritative.\n");
}
static int d_open(struct demuxer *demuxer, enum demux_check check)
@@ -334,6 +390,8 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
struct segment *seg = talloc_ptrtype(p, seg);
*seg = (struct segment){
.d = part->source,
.url = part->source ? part->source->filename : part->url,
.lazy = !part->source,
.d_start = part->source_start,
.start = part->start,
.end = next->start,
@@ -345,6 +403,8 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
MP_TARRAY_APPEND(p, p->segments, p->num_segments, seg);
}
p->dash = p->tl->dash;
print_timeline(demuxer);
demuxer->seekable = true;
@@ -352,6 +412,8 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
demuxer->filetype = meta->filetype ? meta->filetype : meta->desc->name;
demuxer->is_network = p->tl->demuxer->is_network;
reselect_streams(demuxer);
return 0;
@@ -361,6 +423,8 @@ static void d_close(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
struct demuxer *master = p->tl->demuxer;
p->current = NULL;
close_lazy_segments(demuxer);
timeline_destroy(p->tl);
free_demuxer(master);
}
+5 -2
View File
@@ -22,7 +22,10 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
tvi_handle_t *tvh;
const tvi_functions_t *funcs;
if (check > DEMUX_CHECK_REQUEST || demuxer->stream->type != STREAMTYPE_TV)
if (check > DEMUX_CHECK_REQUEST)
return -1;
if (!demuxer->stream->info || strcmp(demuxer->stream->info->name, "tv") != 0)
return -1;
tv_param_t *params = mp_get_config_group(demuxer, demuxer->global,
@@ -31,7 +34,7 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
bstr channel, input;
bstr_split_tok(urlparams, "/", &channel, &input);
if (channel.len) {
talloc_free(params->channels);
talloc_free(params->channel);
params->channel = bstrto0(NULL, channel);
}
if (input.len) {
+1 -1
View File
@@ -120,7 +120,7 @@ int64_t ebml_read_vlen_int(bstr *buffer)
return EBML_INT_INVALID;
l = len - buffer->len;
return unum - ((1 << ((7 * l) - 1)) - 1);
return unum - ((1LL << ((7 * l) - 1)) - 1);
}
/*
+21 -3
View File
@@ -94,7 +94,7 @@ void demux_packet_shorten(struct demux_packet *dp, size_t len)
{
assert(len <= dp->len);
dp->len = len;
memset(dp->buffer + dp->len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
memset(dp->buffer + dp->len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
void free_demux_packet(struct demux_packet *dp)
@@ -132,8 +132,8 @@ struct demux_packet *demux_copy_packet(struct demux_packet *dp)
int demux_packet_set_padding(struct demux_packet *dp, int start, int end)
{
#if HAVE_AVFRAME_SKIP_SAMPLES
if (!start && !end)
#if LIBAVCODEC_VERSION_MICRO >= 100
if (!start && !end)
return 0;
if (!dp->avpacket)
return -1;
@@ -146,3 +146,21 @@ int demux_packet_set_padding(struct demux_packet *dp, int start, int end)
#endif
return 0;
}
int demux_packet_add_blockadditional(struct demux_packet *dp, uint64_t id,
void *data, size_t size)
{
#if LIBAVCODEC_VERSION_MICRO >= 100
if (!dp->avpacket)
return -1;
uint8_t *sd = av_packet_new_side_data(dp->avpacket,
AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
8 + size);
if (!sd)
return -1;
AV_WB64(sd, id);
if (size > 0)
memcpy(sd + 8, data, size);
#endif
return 0;
}
+2
View File
@@ -55,5 +55,7 @@ struct demux_packet *demux_copy_packet(struct demux_packet *dp);
void demux_packet_copy_attribs(struct demux_packet *dst, struct demux_packet *src);
int demux_packet_set_padding(struct demux_packet *dp, int start, int end);
int demux_packet_add_blockadditional(struct demux_packet *dp, uint64_t id,
void *data, size_t size);
#endif /* MPLAYER_DEMUX_PACKET_H */
+3 -1
View File
@@ -33,8 +33,10 @@ void timeline_destroy(struct timeline *tl)
return;
for (int n = 0; n < tl->num_sources; n++) {
struct demuxer *d = tl->sources[n];
if (d != tl->demuxer)
if (d != tl->demuxer && d != tl->track_layout)
free_demuxer_and_stream(d);
}
if (tl->track_layout && tl->track_layout != tl->demuxer)
free_demuxer_and_stream(tl->track_layout);
talloc_free(tl);
}
+4
View File
@@ -4,6 +4,7 @@
struct timeline_part {
double start;
double source_start;
char *url;
struct demuxer *source;
};
@@ -15,6 +16,9 @@ struct timeline {
// main source
struct demuxer *demuxer;
bstr init_fragment;
bool dash;
// All referenced files. The source file must be at sources[0].
struct demuxer **sources;
int num_sources;
+1 -1
View File
@@ -143,7 +143,7 @@
#CLOSE_WIN {encode} quit 4
#E cycle edition # next edition
#l ab-loop # Set/clear A-B loop points
#L cycle-values loop "inf" "no" # toggle infinite looping
#L cycle-values loop-file "inf" "no" # toggle infinite looping
#ctrl+c quit 4
# Apple Remote section
+4
View File
@@ -9,6 +9,10 @@
#
# Older installations use ~/.mpv/input.conf instead.
# changed in mpv 0.24.0
L cycle-values loop "inf" "no"
# changed in mpv 0.10.0
O osd
+6 -4
View File
@@ -144,7 +144,8 @@ struct input_ctx {
struct cmd_queue cmd_queue;
struct mp_cancel *cancel;
void (*cancel)(void *cancel_ctx);
void *cancel_ctx;
void (*wakeup_cb)(void *ctx);
void *wakeup_ctx;
@@ -809,7 +810,7 @@ int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
input_lock(ictx);
if (cmd) {
if (ictx->cancel && test_abort_cmd(ictx, cmd))
mp_cancel_trigger(ictx->cancel);
ictx->cancel(ictx->cancel_ctx);
queue_add_tail(&ictx->cmd_queue, cmd);
mp_input_wakeup(ictx);
}
@@ -1335,10 +1336,11 @@ void mp_input_uninit(struct input_ctx *ictx)
talloc_free(ictx);
}
void mp_input_set_cancel(struct input_ctx *ictx, struct mp_cancel *cancel)
void mp_input_set_cancel(struct input_ctx *ictx, void (*cb)(void *c), void *c)
{
input_lock(ictx);
ictx->cancel = cancel;
ictx->cancel = cb;
ictx->cancel_ctx = c;
input_unlock(ictx);
}
+1 -2
View File
@@ -242,8 +242,7 @@ void mp_input_wakeup(struct input_ctx *ictx);
// Used to asynchronously abort playback. Needed because the core still can
// block on network in some situations.
struct mp_cancel;
void mp_input_set_cancel(struct input_ctx *ictx, struct mp_cancel *cancel);
void mp_input_set_cancel(struct input_ctx *ictx, void (*cb)(void *c), void *c);
// If this returns true, use Right Alt key as Alt Gr to produce special
// characters. If false, count Right Alt as the modifier Alt key.
+1 -2
View File
@@ -864,8 +864,7 @@ int mpv_command(mpv_handle *ctx, const char **args);
* function succeeds, this is set to command-specific return
* data. You must call mpv_free_node_contents() to free it
* (again, only if the command actually succeeds).
* Currently, no command uses this, but that can change in
* the future.
* Not many commands actually use this at all.
* @return error code (the result parameter is not set on error)
*/
int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result);
+6 -3
View File
@@ -117,14 +117,17 @@ extern "C" {
*
* While "normal" mpv loads the OpenGL hardware decoding interop on demand,
* this can't be done with opengl_cb for internal technical reasons. Instead,
* make it load the interop at load time by setting the "hwdec-preload"="auto"
* option before calling mpv_opengl_cb_init_gl().
* make it load the interop at load time by setting the
* "opengl-hwdec-interop"="auto" option before calling mpv_opengl_cb_init_gl()
* ("hwdec-preload" in older mpv releases).
*
* There may be certain requirements on the OpenGL implementation:
* - Windows: ANGLE is required (although in theory GL/DX interop could be used)
* - Intel/Linux: EGL is required, and also a glMPGetNativeDisplay() callback
* must be provided (see sections below)
* - nVidia/Linux: GLX is required
* - nVidia/Linux: GLX is required (if you force "cuda", it should work on EGL
* as well, if you have recent enough drivers and the
* "hwaccel" option is set to "cuda" as well)
* - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL)
*
* Once these things are setup, hardware decoding can be enabled/disabled at
+17 -72
View File
@@ -51,28 +51,6 @@ bool mp_charset_is_utf16(const char *user_cp)
bstr_case_startswith(s, bstr0("utf-16"));
}
// Split the string on ':' into components.
// out_arr is at least max entries long.
// Return number of out_arr entries filled.
static int split_colon(const char *user_cp, int max, bstr *out_arr)
{
if (!user_cp || max < 1)
return 0;
int count = 0;
while (1) {
const char *next = strchr(user_cp, ':');
if (next && max - count > 1) {
out_arr[count++] = (bstr){(char *)user_cp, next - user_cp};
user_cp = next + 1;
} else {
out_arr[count++] = (bstr){(char *)user_cp, strlen(user_cp)};
break;
}
}
return count;
}
static const char *const utf_bom[3] = {"\xEF\xBB\xBF", "\xFF\xFE", "\xFE\xFF"};
static const char *const utf_enc[3] = {"utf-8", "utf-16le", "utf-16be"};
@@ -120,59 +98,20 @@ static const char *mp_uchardet(void *talloc_ctx, struct mp_log *log, bstr buf)
// it's a real iconv codepage), user_cp is returned without even looking at
// the buf data.
// The return value may (but doesn't have to) be allocated under talloc_ctx.
static const char *mp_charset_guess_compat(void *talloc_ctx, struct mp_log *log,
bstr buf, const char *user_cp,
int flags)
{
mp_warn(log, "This syntax for the --sub-codepage option is deprecated.\n");
bstr params[3] = {{0}};
split_colon(user_cp, 3, params);
bstr type = params[0];
char lang[100];
snprintf(lang, sizeof(lang), "%.*s", BSTR_P(params[1]));
const char *fallback = params[2].start; // last item, already 0-terminated
const char *res = NULL;
#if HAVE_UCHARDET
if (bstrcasecmp0(type, "uchardet") == 0) {
res = mp_uchardet(talloc_ctx, log, buf);
if (!res && bstr_validate_utf8(buf) >= 0)
res = "utf-8";
}
#endif
if (bstrcasecmp0(type, "utf8") == 0 || bstrcasecmp0(type, "utf-8") == 0) {
if (!fallback)
fallback = params[1].start; // must be already 0-terminated
int r = bstr_validate_utf8(buf);
if (r >= 0 || (r > -8 && (flags & MP_ICONV_ALLOW_CUTOFF)))
res = "utf-8";
}
if (res) {
mp_dbg(log, "%.*s detected charset: '%s'\n", BSTR_P(type), res);
} else {
res = fallback;
mp_dbg(log, "Detection with %.*s failed: fallback to %s\n",
BSTR_P(type), res && res[0] ? res : "broken UTF-8/Latin1");
}
if (!res && !(flags & MP_STRICT_UTF8))
res = "UTF-8-BROKEN";
mp_verbose(log, "Using charset '%s'.\n", res);
return res;
}
const char *mp_charset_guess(void *talloc_ctx, struct mp_log *log, bstr buf,
const char *user_cp, int flags)
{
if (strcasecmp(user_cp, "enca") == 0 || strcasecmp(user_cp, "guess") == 0 ||
strcasecmp(user_cp, "uchardet") == 0 || strchr(user_cp, ':'))
return mp_charset_guess_compat(talloc_ctx, log, buf, user_cp, flags);
{
mp_err(log, "This syntax for the --sub-codepage option was deprecated "
"and has been removed.\n");
if (strncasecmp(user_cp, "utf8:", 5) == 0) {
user_cp = user_cp + 5;
} else {
user_cp = "";
}
}
if (user_cp[0] == '+') {
mp_verbose(log, "Forcing charset '%s'.\n", user_cp + 1);
@@ -191,7 +130,7 @@ const char *mp_charset_guess(void *talloc_ctx, struct mp_log *log, bstr buf,
return "utf-8";
}
const char *res = user_cp;
const char *res = NULL;
if (strcasecmp(user_cp, "auto") == 0) {
#if HAVE_UCHARDET
res = mp_uchardet(talloc_ctx, log, buf);
@@ -200,6 +139,8 @@ const char *mp_charset_guess(void *talloc_ctx, struct mp_log *log, bstr buf,
mp_verbose(log, "Charset auto-detection failed.\n");
res = "UTF-8-BROKEN";
}
} else {
res = user_cp;
}
mp_verbose(log, "Using charset '%s'.\n", res);
@@ -293,5 +234,9 @@ bstr mp_iconv_to_utf8(struct mp_log *log, bstr buf, const char *cp, int flags)
#endif
failure:
return (bstr){0};
if (flags & MP_NO_LATIN1_FALLBACK) {
return buf;
} else {
return bstr_sanitize_utf8_latin1(NULL, buf);
}
}
+1
View File
@@ -10,6 +10,7 @@ enum {
MP_ICONV_VERBOSE = 1, // print errors instead of failing silently
MP_ICONV_ALLOW_CUTOFF = 2, // allow partial input data
MP_STRICT_UTF8 = 4, // don't fall back to UTF-8-BROKEN when guessing
MP_NO_LATIN1_FALLBACK = 8, // fall back to input buffer instead of latin1
};
bool mp_charset_is_utf8(const char *user_cp);
+26 -4
View File
@@ -40,6 +40,7 @@
#include "stream/stream.h"
#include "video/csputils.h"
#include "video/hwdec.h"
#include "video/out/opengl/hwdec.h"
#include "video/image_writer.h"
#include "sub/osd.h"
#include "audio/filter/af.h"
@@ -87,6 +88,8 @@ extern const struct m_obj_list af_obj_list;
extern const struct m_obj_list vo_obj_list;
extern const struct m_obj_list ao_obj_list;
extern const struct m_sub_options angle_conf;
const struct m_opt_choice_alternatives mp_hwdec_names[] = {
{"no", HWDEC_NONE},
{"auto", HWDEC_AUTO},
@@ -150,9 +153,9 @@ const struct m_sub_options stream_cache_conf = {
static const m_option_t mp_vo_opt_list[] = {
OPT_SETTINGSLIST("vo", video_driver_list, 0, &vo_obj_list, ),
OPT_CHOICE_C("hwdec-preload", hwdec_preload_api, 0, mp_hwdec_names),
OPT_SUBSTRUCT("sws", sws_opts, sws_conf, 0),
OPT_FLAG("taskbar-progress", taskbar_progress, 0),
OPT_FLAG("snap-window", snap_window, 0),
OPT_FLAG("ontop", ontop, 0),
OPT_FLAG("border", border, 0),
OPT_FLAG("fit-border", fit_border, 0),
@@ -199,7 +202,11 @@ static const m_option_t mp_vo_opt_list[] = {
0, drm_validate_connector_opt),
OPT_INT("drm-mode", drm_mode_id, 0),
#endif
#if HAVE_GL
OPT_STRING_VALIDATE("opengl-hwdec-interop", gl_hwdec_interop, 0,
gl_hwdec_validate_opt),
OPT_REPLACED("hwdec-preload", "opengl-hwdec-interop"),
#endif
{0}
};
@@ -216,6 +223,7 @@ const struct m_sub_options vo_sub_opts = {
.keepaspect_window = 1,
.hidpi_window_scale = 1,
.taskbar_progress = 1,
.snap_window = 0,
.border = 1,
.fit_border = 1,
.WinID = -1,
@@ -379,6 +387,7 @@ const m_option_t mp_opts[] = {
OPT_STRING("audio-demuxer", audio_demuxer_name, 0),
OPT_STRING("sub-demuxer", sub_demuxer_name, 0),
OPT_FLAG("demuxer-thread", demuxer_thread, 0),
OPT_FLAG("prefetch-playlist", prefetch_open, 0),
OPT_FLAG("cache-pause", cache_pausing, 0),
OPT_DOUBLE("mf-fps", mf_fps, 0),
@@ -466,6 +475,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("sub-forced-only", forced_subs_only, UPDATE_OSD),
OPT_FLAG("stretch-dvd-subs", stretch_dvd_subs, UPDATE_OSD),
OPT_FLAG("stretch-image-subs-to-screen", stretch_image_subs, UPDATE_OSD),
OPT_FLAG("image-subs-video-resolution", image_subs_video_res, UPDATE_OSD),
OPT_FLAG("sub-fix-timing", sub_fix_timing, 0),
OPT_CHOICE("sub-auto", sub_auto, 0,
({"no", -1}, {"exact", 0}, {"fuzzy", 1}, {"all", 2})),
@@ -490,6 +500,7 @@ const m_option_t mp_opts[] = {
({"none", 0}, {"light", 1}, {"normal", 2}, {"native", 3})),
OPT_CHOICE("sub-ass-shaper", ass_shaper, UPDATE_OSD,
({"simple", 0}, {"complex", 1})),
OPT_FLAG("sub-ass-justify", ass_justify, 0),
OPT_CHOICE("sub-ass-style-override", ass_style_override, UPDATE_OSD,
({"no", 0}, {"yes", 1}, {"force", 3}, {"signfs", 4}, {"strip", 5})),
OPT_FLAG("sub-scale-by-window", sub_scale_by_window, UPDATE_OSD),
@@ -583,7 +594,6 @@ const m_option_t mp_opts[] = {
OPT_FLAG("untimed", untimed, 0),
OPT_STRING("stream-capture", stream_capture, M_OPT_FILE),
OPT_STRING("stream-dump", stream_dump, M_OPT_FILE),
OPT_FLAG("stop-playback-on-init-failure", stop_playback_on_init_failure, 0),
@@ -671,6 +681,8 @@ const m_option_t mp_opts[] = {
OPT_STRING("screenshot-template", screenshot_template, 0),
OPT_STRING("screenshot-directory", screenshot_directory, 0),
OPT_STRING("record-file", record_file, M_OPT_FILE),
OPT_SUBSTRUCT("", input_opts, input_config, 0),
OPT_PRINT("list-protocols", stream_print_proto_list),
@@ -686,6 +698,15 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("", gl_video_opts, gl_video_conf, 0),
#endif
#if HAVE_EGL_ANGLE
OPT_SUBSTRUCT("", angle_opts, angle_conf, 0),
#endif
#if HAVE_GL_WIN32
OPT_CHOICE("opengl-dwmflush", wingl_dwm_flush, 0,
({"no", -1}, {"auto", 0}, {"windowed", 1}, {"yes", 2})),
#endif
#if HAVE_ENCODING
OPT_SUBSTRUCT("", encode_opts, encode_config, 0),
#endif
@@ -697,7 +718,8 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("ass", "sub-ass"),
OPT_REPLACED("audiofile", "audio-file"),
OPT_REMOVED("benchmark", "use --untimed (no stats)"),
OPT_REMOVED("capture", "use --stream-capture=<filename>"),
OPT_REMOVED("capture", NULL),
OPT_REMOVED("stream-capture", NULL),
OPT_REMOVED("channels", "use --audio-channels (changed semantics)"),
OPT_REPLACED("cursor-autohide-delay", "cursor-autohide"),
OPT_REPLACED("delay", "audio-delay"),
+9 -2
View File
@@ -10,6 +10,7 @@ typedef struct mp_vo_opts {
struct m_obj_settings *video_driver_list;
int taskbar_progress;
int snap_window;
int ontop;
int fullscreen;
int border;
@@ -50,7 +51,7 @@ typedef struct mp_vo_opts {
// vo_wayland, vo_drm
struct sws_opts *sws_opts;
// vo_opengl, vo_opengl_cb
int hwdec_preload_api;
char *gl_hwdec_interop;
// vo_drm
char *drm_connector_spec;
int drm_mode_id;
@@ -136,8 +137,8 @@ typedef struct MPOpts {
int video_osd;
int untimed;
char *stream_capture;
char *stream_dump;
char *record_file;
int stop_playback_on_init_failure;
int loop_times;
int loop_file;
@@ -213,12 +214,14 @@ typedef struct MPOpts {
int forced_subs_only;
int stretch_dvd_subs;
int stretch_image_subs;
int image_subs_video_res;
int sub_fix_timing;
char **audio_files;
char *demuxer_name;
int demuxer_thread;
int prefetch_open;
char *audio_demuxer_name;
char *sub_demuxer_name;
@@ -279,6 +282,7 @@ typedef struct MPOpts {
int ass_style_override;
int ass_hinting;
int ass_shaper;
int ass_justify;
int sub_clear_on_seek;
int teletext_page;
@@ -318,7 +322,10 @@ typedef struct MPOpts {
char *ipc_path;
char *input_file;
int wingl_dwm_flush;
struct gl_video_opts *gl_video_opts;
struct angle_opts *angle_opts;
struct dvd_opts *dvd_opts;
} MPOpts;
+2 -1
View File
@@ -150,7 +150,8 @@ int m_config_parse(m_config_t *config, const char *location, bstr data,
}
}
m_config_finish_default_profile(config, flags);
if (config->recursion_depth == 0)
m_config_finish_default_profile(config, flags);
talloc_free(tmp);
return 1;
-19
View File
@@ -61,25 +61,6 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
__atomic_compare_exchange_n(&(a)->v, b, c, 0, __ATOMIC_SEQ_CST, \
__ATOMIC_SEQ_CST)
#elif HAVE_SYNC_BUILTINS
#define atomic_load(p) \
__sync_fetch_and_add(&(p)->v, 0)
#define atomic_store(p, val) \
(__sync_synchronize(), (p)->v = (val), __sync_synchronize())
#define atomic_fetch_add(a, b) \
__sync_fetch_and_add(&(a)->v, b)
#define atomic_fetch_and(a, b) \
__sync_fetch_and_and(&(a)->v, b)
#define atomic_fetch_or(a, b) \
__sync_fetch_and_or(&(a)->v, b)
// Assumes __sync_val_compare_and_swap is "strong" (using the C11 meaning).
#define atomic_compare_exchange_strong(p, old, new) \
({ __typeof__((p)->v) val_ = __sync_val_compare_and_swap(&(p)->v, *(old), new); \
bool ok_ = val_ == *(old); \
if (!ok_) *(old) = val_; \
ok_; })
#elif defined(__GNUC__)
#include <pthread.h>
+1 -1
View File
@@ -34,7 +34,7 @@ VS_VERSION_INFO VERSIONINFO
VALUE "CompanyName", "mpv"
VALUE "FileDescription", "mpv"
VALUE "FileVersion", "2.0.0.0"
VALUE "LegalCopyright", "(C) 2000-2016 mpv/mplayer2/MPlayer"
VALUE "LegalCopyright", "(C) 2000-2017 mpv/mplayer2/MPlayer"
VALUE "OriginalFilename", "mpv.exe"
VALUE "ProductName", "mpv"
VALUE "ProductVersion", "2.0.0.0"
+8
View File
@@ -22,6 +22,7 @@
#include <errors.h>
#include <audioclient.h>
#include <d3d9.h>
#include <dxgi1_2.h>
#include "windows_utils.h"
@@ -118,6 +119,13 @@ static char *hresult_to_str(const HRESULT hr)
E(D3DERR_CANNOTPROTECTCONTENT)
E(D3DERR_UNSUPPORTEDCRYPTO)
E(D3DERR_PRESENT_STATISTICS_DISJOINT)
E(DXGI_ERROR_DEVICE_HUNG)
E(DXGI_ERROR_DEVICE_REMOVED)
E(DXGI_ERROR_DEVICE_RESET)
E(DXGI_ERROR_DRIVER_INTERNAL_ERROR)
E(DXGI_ERROR_INVALID_CALL)
E(DXGI_ERROR_WAS_STILL_DRAWING)
E(DXGI_STATUS_OCCLUDED)
default:
return "<Unknown>";
}
+4
View File
@@ -20,6 +20,10 @@
#include <windows.h>
// Conditionally release a COM interface and set the pointer to NULL
#define SAFE_RELEASE(u) \
do { if ((u) != NULL) (u)->lpVtbl->Release(u); (u) = NULL; } while(0)
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid);
#define mp_GUID_to_str(guid) mp_GUID_to_str_buf((char[40]){0}, 40, (guid))
char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr);
+1 -1
View File
@@ -817,7 +817,7 @@ static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
break;
res = decode_new_frame(ao_c);
if (res == AD_NO_PROGRESS)
if (res == AD_NO_PROGRESS || res == AD_WAIT)
break;
if (res < 0) {
// drain filters first (especially for true EOF case)
+17 -2
View File
@@ -359,6 +359,8 @@ void mpv_detach_destroy(mpv_handle *ctx)
if (!ctx)
return;
MP_VERBOSE(ctx, "Exiting...\n");
// reserved_events equals the number of asynchronous requests that weren't
// yet replied. In order to avoid that trying to reply to a removed client
// causes a crash, block until all asynchronous requests were served.
@@ -407,7 +409,13 @@ void mpv_terminate_destroy(mpv_handle *ctx)
if (!ctx)
return;
mpv_command(ctx, (const char*[]){"quit", NULL});
if (ctx->mpctx->initialized) {
mpv_command(ctx, (const char*[]){"quit", NULL});
} else {
mp_dispatch_lock(ctx->mpctx->dispatch);
ctx->mpctx->stop_play = PT_QUIT;
mp_dispatch_unlock(ctx->mpctx->dispatch);
}
if (!ctx->owner) {
mpv_detach_destroy(ctx);
@@ -851,6 +859,12 @@ static bool compare_value(void *a, void *b, mpv_format format)
return false;
return compare_value(&a_n->u, &b_n->u, a_n->format);
}
case MPV_FORMAT_BYTE_ARRAY: {
struct mpv_byte_array *a_r = a, *b_r = b;
if (a_r->size != b_r->size)
return false;
return memcmp(a_r->data, b_r->data, a_r->size) == 0;
}
case MPV_FORMAT_NODE_ARRAY:
case MPV_FORMAT_NODE_MAP:
{
@@ -968,7 +982,7 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res
return MPV_ERROR_INVALID_PARAMETER;
if (mp_input_is_abort_cmd(cmd))
mp_cancel_trigger(ctx->mpctx->playback_abort);
mp_abort_playback_async(ctx->mpctx);
cmd->sender = ctx->name;
@@ -1088,6 +1102,7 @@ int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format,
mp_get_property_id(ctx->mpctx, name) >= 0)
return MPV_ERROR_PROPERTY_UNAVAILABLE;
switch (r) {
case MPV_ERROR_SUCCESS: return MPV_ERROR_SUCCESS;
case MPV_ERROR_OPTION_FORMAT: return MPV_ERROR_PROPERTY_FORMAT;
case MPV_ERROR_OPTION_NOT_FOUND: return MPV_ERROR_PROPERTY_NOT_FOUND;
default: return MPV_ERROR_PROPERTY_ERROR;
+119 -43
View File
@@ -243,6 +243,78 @@ void mark_seek(struct MPContext *mpctx)
cmd->last_seek_time = now;
}
static char *skip_n_lines(char *text, int lines)
{
while (text && lines > 0) {
char *next = strchr(text, '\n');
text = next ? next + 1 : NULL;
lines--;
}
return text;
}
static int count_lines(char *text)
{
int count = 0;
while (text) {
char *next = strchr(text, '\n');
if (!next || (next[0] == '\n' && !next[1]))
break;
text = next + 1;
count++;
}
return count;
}
// Given a huge string separated by new lines, attempts to cut off text above
// the current line to keep the line visible, and below to keep rendering
// performance up. pos gives the current line (0 for the first line).
// "text" might be returned as is, or it can be freed and a new allocation is
// returned.
// This is only a heuristic - we can't deal with line breaking.
static char *cut_osd_list(struct MPContext *mpctx, char *text, int pos)
{
int screen_h, font_h;
osd_get_text_size(mpctx->osd, &screen_h, &font_h);
int max_lines = screen_h / MPMAX(font_h, 1) - 1;
if (!text || max_lines < 5)
return text;
int count = count_lines(text);
if (count <= max_lines)
return text;
char *new = talloc_strdup(NULL, "");
int start = pos - max_lines / 2;
if (start == 1)
start = 0; // avoid weird transition when pad_h becomes visible
int pad_h = start > 0;
if (pad_h)
new = talloc_strdup_append_buffer(new, "\342\206\221 (hidden items)\n");
int space = max_lines - pad_h - 1;
int pad_t = count - start > space;
if (!pad_t)
start = count - space;
char *head = skip_n_lines(text, start);
if (!head) {
talloc_free(new);
return text;
}
char *tail = skip_n_lines(head, max_lines - pad_h - pad_t);
new = talloc_asprintf_append_buffer(new, "%.*s",
(int)(tail ? tail - head : strlen(head)), head);
if (pad_t)
new = talloc_strdup_append_buffer(new, "\342\206\223 (hidden items)\n");
talloc_free(text);
return new;
}
static char *format_file_size(int64_t size)
{
double s = size;
@@ -515,29 +587,6 @@ static int mp_property_stream_path(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, mpctx->demuxer->filename);
}
struct change_stream_capture_args {
char *filename;
struct demuxer *demux;
};
static void do_change_stream_capture(void *p)
{
struct change_stream_capture_args *args = p;
stream_set_capture_file(args->demux->stream, args->filename);
}
static int mp_property_stream_capture(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (mpctx->demuxer && action == M_PROPERTY_SET) {
struct change_stream_capture_args args = {*(char **)arg, mpctx->demuxer};
demux_run_on_thread(mpctx->demuxer, do_change_stream_capture, &args);
// fall through to mp_property_generic_option
}
return mp_property_generic_option(mpctx, prop, action, arg);
}
/// Demuxer name (RO)
static int mp_property_demuxer(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -650,7 +699,7 @@ static int mp_property_total_avsync_change(void *ctx, struct m_property *prop,
return m_property_double_ro(action, arg, mpctx->total_avsync_change);
}
static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop,
static int mp_property_frame_drop_dec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
@@ -692,8 +741,8 @@ static int mp_property_vsync_ratio(void *ctx, struct m_property *prop,
return m_property_double_ro(action, arg, vsyncs / (double)frames);
}
static int mp_property_vo_drop_frame_count(void *ctx, struct m_property *prop,
int action, void *arg)
static int mp_property_frame_drop_vo(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (!mpctx->vo_chain)
@@ -3344,9 +3393,10 @@ static int mp_property_playlist(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
if (action == M_PROPERTY_PRINT) {
struct playlist *pl = mpctx->playlist;
char *res = talloc_strdup(NULL, "");
for (struct playlist_entry *e = mpctx->playlist->first; e; e = e->next)
for (struct playlist_entry *e = pl->first; e; e = e->next)
{
char *p = e->filename;
if (!mp_is_url(bstr0(p))) {
@@ -3354,12 +3404,12 @@ static int mp_property_playlist(void *ctx, struct m_property *prop,
if (s[0])
p = s;
}
const char *m = mpctx->playlist->current == e ?
list_current : list_normal;
const char *m = pl->current == e ? list_current : list_normal;
res = talloc_asprintf_append(res, "%s%s\n", m, p);
}
*(char **)arg = res;
*(char **)arg =
cut_osd_list(mpctx, res, playlist_entry_to_index(pl, pl->current));
return M_PROPERTY_OK;
}
@@ -3499,6 +3549,26 @@ static int mp_property_cwd(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
static int mp_property_record_file(void *ctx, struct m_property *prop,
int action, void *arg)
{
struct MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
if (action == M_PROPERTY_SET) {
char *new = *(char **)arg;
if (!bstr_equals(bstr0(new), bstr0(opts->record_file))) {
talloc_free(opts->record_file);
opts->record_file = talloc_strdup(NULL, new);
open_recorder(mpctx, false);
// open_recorder() unsets it on failure.
if (new && !opts->record_file)
return M_PROPERTY_ERROR;
}
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
}
static int mp_property_protocols(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -3789,7 +3859,6 @@ static const struct m_property mp_properties_base[] = {
{"path", mp_property_path},
{"media-title", mp_property_media_title},
{"stream-path", mp_property_stream_path},
{"stream-capture", mp_property_stream_capture},
{"current-demuxer", mp_property_demuxer},
{"file-format", mp_property_file_format},
{"stream-pos", mp_property_stream_pos},
@@ -3797,10 +3866,10 @@ static const struct m_property mp_properties_base[] = {
{"duration", mp_property_duration},
{"avsync", mp_property_avsync},
{"total-avsync-change", mp_property_total_avsync_change},
{"drop-frame-count", mp_property_drop_frame_cnt},
{"mistimed-frame-count", mp_property_mistimed_frame_count},
{"vsync-ratio", mp_property_vsync_ratio},
{"vo-drop-frame-count", mp_property_vo_drop_frame_count},
{"decoder-frame-drop-count", mp_property_frame_drop_dec},
{"frame-drop-count", mp_property_frame_drop_vo},
{"vo-delayed-frame-count", mp_property_vo_delayed_frame_count},
{"percent-pos", mp_property_percent_pos},
{"time-start", mp_property_time_start},
@@ -3971,6 +4040,8 @@ static const struct m_property mp_properties_base[] = {
{"working-directory", mp_property_cwd},
{"record-file", mp_property_record_file},
{"protocol-list", mp_property_protocols},
{"decoder-list", mp_property_decoders},
{"encoder-list", mp_property_encoders},
@@ -3994,6 +4065,9 @@ static const struct m_property mp_properties_base[] = {
M_PROPERTY_ALIAS("colormatrix-input-range", "video-params/colorlevels"),
M_PROPERTY_ALIAS("colormatrix-primaries", "video-params/primaries"),
M_PROPERTY_ALIAS("colormatrix-gamma", "video-params/gamma"),
M_PROPERTY_DEPRECATED_ALIAS("drop-frame-count", "decoder-frame-drop-count"),
M_PROPERTY_DEPRECATED_ALIAS("vo-drop-frame-count", "frame-drop-count"),
};
// Each entry describes which properties an event (possibly) changes.
@@ -4015,7 +4089,8 @@ static const char *const *const mp_event_property_change[] = {
"total-avsync-change", "audio-speed-correction", "video-speed-correction",
"vo-delayed-frame-count", "mistimed-frame-count", "vsync-ratio",
"estimated-display-fps", "vsync-jitter", "sub-text", "audio-bitrate",
"video-bitrate", "sub-bitrate"),
"video-bitrate", "sub-bitrate", "decoder-frame-drop-count",
"frame-drop-count"),
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
"width", "height", "fps", "aspect", "vo-configured", "current-vo",
@@ -4197,9 +4272,9 @@ static const struct property_osd_display {
} property_osd_display[] = {
// general
{ "loop", "Loop" },
{ "loop-file", "Loop current file" },
{ "chapter", .seek_msg = OSD_SEEK_INFO_CHAPTER_TEXT,
.seek_bar = OSD_SEEK_INFO_BAR },
{ "edition", .seek_msg = OSD_SEEK_INFO_EDITION },
{ "hr-seek", "hr-seek" },
{ "speed", "Speed" },
{ "clock", "Clock" },
@@ -4218,6 +4293,7 @@ static const struct property_osd_display {
// video
{ "panscan", "Panscan", .osd_progbar = OSD_PANSCAN },
{ "taskbar-progress", "Progress in taskbar" },
{ "snap-window", "Snap to screen edges" },
{ "ontop", "Stay on top" },
{ "border", "Border" },
{ "framedrop", "Framedrop" },
@@ -4960,7 +5036,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
int dir = cmd->id == MP_CMD_PLAYLIST_PREV ? -1 : +1;
int force = cmd->args[0].v.i;
struct playlist_entry *e = mp_next_file(mpctx, dir, force);
struct playlist_entry *e = mp_next_file(mpctx, dir, force, true);
if (!e && !force)
return -1;
mp_set_playlist_entry(mpctx, e);
@@ -5309,20 +5385,19 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
case MP_CMD_AB_LOOP: {
double now = get_current_time(mpctx);
int r = 0;
if (opts->ab_loop[0] == MP_NOPTS_VALUE) {
r = mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx);
mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx);
show_property_osd(mpctx, "ab-loop-a", on_osd);
} else if (opts->ab_loop[1] == MP_NOPTS_VALUE) {
r = mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx);
mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx);
show_property_osd(mpctx, "ab-loop-b", on_osd);
} else {
now = MP_NOPTS_VALUE;
r = mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx);
r = mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx);
mp_property_do("ab-loop-a", M_PROPERTY_SET, &now, mpctx);
mp_property_do("ab-loop-b", M_PROPERTY_SET, &now, mpctx);
set_osd_msg(mpctx, osdl, osd_duration, "Clear A-B loop");
}
return r > 0;
break;
}
case MP_CMD_DROP_BUFFERS: {
@@ -5376,7 +5451,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
if (cmd->is_up_down)
state[0] = cmd->repeated ? 'r' : (cmd->is_up ? 'u' : 'd');
event.num_args = 4;
event.args = (const char*[4]){"key-binding", name, state, cmd->key_name};
event.args = (const char*[4]){"key-binding", name, state,
cmd->key_name ? cmd->key_name : ""};
if (mp_client_send_event_dup(mpctx, target,
MPV_EVENT_CLIENT_MESSAGE, &event) < 0)
{
+35 -5
View File
@@ -52,8 +52,7 @@ enum mp_osd_seek_info {
OSD_SEEK_INFO_BAR = 1,
OSD_SEEK_INFO_TEXT = 2,
OSD_SEEK_INFO_CHAPTER_TEXT = 4,
OSD_SEEK_INFO_EDITION = 8,
OSD_SEEK_INFO_CURRENT_FILE = 16,
OSD_SEEK_INFO_CURRENT_FILE = 8,
};
@@ -154,6 +153,9 @@ struct track {
struct vo_chain *vo_c;
struct ao_chain *ao_c;
struct lavfi_pad *sink;
// For stream recording (remuxing mode).
struct mp_recorder_sink *remux_sink;
};
// Summarizes video filtering and output.
@@ -179,6 +181,8 @@ struct vo_chain {
// - video consists of a single picture, which should be shown only once
// - do not sync audio to video in any way
bool is_coverart;
// Just to avoid decoding the coverart picture again after a seek.
struct mp_image *cached_coverart;
};
// Like vo_chain, for audio.
@@ -420,6 +424,8 @@ typedef struct MPContext {
// playback rate. Used to avoid showing it multiple times.
bool drop_message_shown;
struct mp_recorder *recorder;
char *cached_watch_later_configdir;
struct screenshot_ctx *screenshot_ctx;
@@ -429,6 +435,26 @@ typedef struct MPContext {
struct mp_ipc_ctx *ipc_ctx;
struct mpv_opengl_cb_context *gl_cb_ctx;
pthread_mutex_t lock;
// --- The following fields are protected by lock
struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer
// --- Owned by MPContext
pthread_t open_thread;
bool open_active; // open_thread is a valid thread handle, all setup
atomic_bool open_done;
// --- All fields below are immutable while open_active is true.
// Otherwise, they're owned by MPContext.
struct mp_cancel *open_cancel;
char *open_url;
char *open_format;
int open_url_flags;
// --- All fields below are owned by open_thread, unless open_done was set
// to true.
struct demuxer *open_res_demuxer;
int open_res_error;
} MPContext;
// audio.c
@@ -459,6 +485,7 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx,
struct playlist *playlist);
// loadfile.c
void mp_abort_playback_async(struct MPContext *mpctx);
void uninit_player(struct MPContext *mpctx, unsigned int mask);
struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
enum stream_type filter);
@@ -473,7 +500,7 @@ struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer);
bool mp_remove_track(struct MPContext *mpctx, struct track *track);
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
bool force);
bool force, bool mutate);
void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e);
void mp_play_files(struct MPContext *mpctx);
void update_demuxer_properties(struct MPContext *mpctx);
@@ -483,6 +510,10 @@ void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
void autoload_external_files(struct MPContext *mpctx);
struct track *select_default_track(struct MPContext *mpctx, int order,
enum stream_type type);
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);
// main.c
int mp_initialize(struct MPContext *mpctx, char **argv);
@@ -501,8 +532,6 @@ void update_vo_playback_state(struct MPContext *mpctx);
void update_window_title(struct MPContext *mpctx, bool force);
void error_on_track(struct MPContext *mpctx, struct track *track);
int stream_dump(struct MPContext *mpctx, const char *source_filename);
int mpctx_run_reentrant(struct MPContext *mpctx, void (*thread_fn)(void *arg),
void *thread_arg);
double get_track_seek_offset(struct MPContext *mpctx, struct track *track);
// osd.c
@@ -549,6 +578,7 @@ void update_screensaver_state(struct MPContext *mpctx);
// scripting.c
struct mp_scripting {
const char *name; // e.g. "lua script"
const char *file_ext; // e.g. "lua"
int (*load)(struct mpv_handle *client, const char *filename);
};
+16 -5
View File
@@ -10,13 +10,15 @@
#include "common/global.h"
#include "common/msg.h"
#include "misc/ctype.h"
#include "misc/charset_conv.h"
#include "options/options.h"
#include "options/path.h"
#include "external_files.h"
static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "srt",
"smi", "rt", "txt", "ssa", "aqt", "jss",
"js", "ass", "mks", "vtt", "sup", NULL};
"smi", "rt", "ssa", "aqt", "jss",
"js", "ass", "mks", "vtt", "sup", "scc",
NULL};
static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
"ogg", "m4a", "ac3", "opus", "wav",
@@ -97,11 +99,16 @@ static void append_dir_subtitles(struct mpv_global *global,
if (mp_is_url(bstr0(fname)))
goto out;
struct bstr f_fname = bstr0(mp_basename(fname));
struct bstr f_fbname = bstr0(mp_basename(fname));
struct bstr f_fname = mp_iconv_to_utf8(log, f_fbname,
"UTF-8-MAC", MP_NO_LATIN1_FALLBACK);
struct bstr f_fname_noext = bstrdup(tmpmem, bstr_strip_ext(f_fname));
bstr_lower(f_fname_noext);
struct bstr f_fname_trim = bstr_strip(f_fname_noext);
if (f_fbname.start != f_fname.start)
talloc_steal(tmpmem, f_fname.start);
// 0 = nothing
// 1 = any subtitle file
// 2 = any sub file containing movie name
@@ -113,15 +120,19 @@ static void append_dir_subtitles(struct mpv_global *global,
mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path));
struct dirent *de;
while ((de = readdir(d))) {
struct bstr dename = bstr0(de->d_name);
void *tmpmem2 = talloc_new(tmpmem);
struct bstr den = bstr0(de->d_name);
struct bstr dename = mp_iconv_to_utf8(log, den,
"UTF-8-MAC", MP_NO_LATIN1_FALLBACK);
// retrieve various parts of the filename
struct bstr tmp_fname_noext = bstrdup(tmpmem2, bstr_strip_ext(dename));
bstr_lower(tmp_fname_noext);
struct bstr tmp_fname_ext = bstr_get_ext(dename);
struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext);
if (den.start != dename.start)
talloc_steal(tmpmem2, dename.start);
// check what it is (most likely)
int type = test_ext(tmp_fname_ext);
char **langs = NULL;
+17 -9
View File
@@ -43,6 +43,11 @@
#include "lavfi.h"
#if LIBAVFILTER_VERSION_MICRO < 100
#define av_buffersink_get_frame_flags(a, b, c) av_buffersink_get_frame(a, b)
#define AV_BUFFERSINK_FLAG_NO_REQUEST 0
#endif
struct lavfi {
struct mp_log *log;
char *graph_string;
@@ -266,6 +271,10 @@ enum stream_type lavfi_pad_type(struct lavfi_pad *pad)
void lavfi_set_connected(struct lavfi_pad *pad, bool connected)
{
pad->connected = connected;
if (!pad->connected) {
pad->output_needed = false;
drop_pad_data(pad);
}
}
bool lavfi_get_connected(struct lavfi_pad *pad)
@@ -545,19 +554,17 @@ static void read_output_pads(struct lavfi *c)
if (pad->dir != LAVFI_OUT)
continue;
// If disconnected, read and discard everything.
if (!pad->pending_v && !pad->pending_a && !pad->connected)
pad->output_needed = true;
if (!pad->output_needed)
// If disconnected, read and discard everything (passively).
if (pad->connected && !pad->output_needed)
continue;
assert(pad->buffer);
assert(!pad->pending_v && !pad->pending_a);
int flags = pad->output_needed ? 0 : AV_BUFFERSINK_FLAG_NO_REQUEST;
int r = AVERROR_EOF;
if (!pad->buffer_is_eof)
r = av_buffersink_get_frame(pad->buffer, pad->tmp_frame);
r = av_buffersink_get_frame_flags(pad->buffer, pad->tmp_frame, flags);
if (r >= 0) {
pad->output_needed = false;
double pts = mp_pts_from_av(pad->tmp_frame->pts, &pad->timebase);
@@ -583,6 +590,7 @@ static void read_output_pads(struct lavfi *c)
// input pads (via av_buffersrc_get_nb_failed_requests()).
pad->output_eof = false;
} else if (r == AVERROR_EOF) {
pad->output_needed = false;
pad->buffer_is_eof = true;
if (!c->draining_recover_eof && !c->draining_new_format)
pad->output_eof = true;
@@ -611,7 +619,7 @@ bool lavfi_process(struct lavfi *c)
bool all_waiting = true;
bool any_needs_input = false;
bool any_needs_output = false;
bool all_lavfi_eof = true;
bool all_output_eof = true;
bool all_input_eof = true;
// Determine the graph state
@@ -623,12 +631,12 @@ bool lavfi_process(struct lavfi *c)
any_needs_input |= pad->input_needed;
all_input_eof &= pad->input_eof;
} else if (pad->dir == LAVFI_OUT) {
all_lavfi_eof &= pad->buffer_is_eof;
all_output_eof &= pad->buffer_is_eof;
any_needs_output |= pad->output_needed;
}
}
if (all_lavfi_eof && !all_input_eof) {
if (all_output_eof && !all_input_eof) {
free_graph(c);
precreate_graph(c);
all_waiting = false;
+245 -42
View File
@@ -40,6 +40,7 @@
#include "options/m_property.h"
#include "common/common.h"
#include "common/encode.h"
#include "common/recorder.h"
#include "input/input.h"
#include "audio/audio.h"
@@ -57,6 +58,17 @@
#include "command.h"
#include "libmpv/client.h"
// Called by foreign threads when playback should be stopped and such.
void mp_abort_playback_async(struct MPContext *mpctx)
{
mp_cancel_trigger(mpctx->playback_abort);
pthread_mutex_lock(&mpctx->lock);
if (mpctx->demuxer_cancel)
mp_cancel_trigger(mpctx->demuxer_cancel);
pthread_mutex_unlock(&mpctx->lock);
}
static void uninit_demuxer(struct MPContext *mpctx)
{
for (int r = 0; r < NUM_PTRACKS; r++) {
@@ -80,6 +92,11 @@ static void uninit_demuxer(struct MPContext *mpctx)
free_demuxer_and_stream(mpctx->demuxer);
mpctx->demuxer = NULL;
pthread_mutex_lock(&mpctx->lock);
talloc_free(mpctx->demuxer_cancel);
mpctx->demuxer_cancel = NULL;
pthread_mutex_unlock(&mpctx->lock);
}
#define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__)
@@ -453,6 +470,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
uninit_sub(mpctx, current);
if (current) {
if (current->remux_sink)
close_recorder_and_error(mpctx);
current->selected = false;
reselect_demux_stream(mpctx, current);
}
@@ -774,57 +793,144 @@ static void load_per_file_options(m_config_t *conf,
}
}
struct demux_open_args {
int stream_flags;
char *url;
struct mpv_global *global;
struct mp_cancel *cancel;
struct mp_log *log;
// results
struct demuxer *demux;
int err;
};
static void open_demux_thread(void *pctx)
static void *open_demux_thread(void *ctx)
{
struct demux_open_args *args = pctx;
struct mpv_global *global = args->global;
struct MPContext *mpctx = ctx;
struct demuxer_params p = {
.force_format = global->opts->demuxer_name,
.allow_capture = true,
.stream_flags = args->stream_flags,
.force_format = mpctx->open_format,
.stream_flags = mpctx->open_url_flags,
.initial_readahead = true,
};
args->demux = demux_open_url(args->url, &p, args->cancel, global);
if (!args->demux) {
mpctx->open_res_demuxer =
demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global);
if (mpctx->open_res_demuxer) {
MP_VERBOSE(mpctx, "Opening done: %s\n", mpctx->open_url);
} else {
MP_VERBOSE(mpctx, "Opening failed or was aborted: %s\n", mpctx->open_url);
if (p.demuxer_failed) {
args->err = MPV_ERROR_UNKNOWN_FORMAT;
mpctx->open_res_error = MPV_ERROR_UNKNOWN_FORMAT;
} else {
args->err = MPV_ERROR_LOADING_FAILED;
mpctx->open_res_error = MPV_ERROR_LOADING_FAILED;
}
}
if (args->demux && global->opts->rebase_start_time)
demux_set_ts_offset(args->demux, -args->demux->start_time);
atomic_store(&mpctx->open_done, true);
mp_wakeup_core(mpctx);
return NULL;
}
static void cancel_open(struct MPContext *mpctx)
{
if (mpctx->open_cancel)
mp_cancel_trigger(mpctx->open_cancel);
if (mpctx->open_active)
pthread_join(mpctx->open_thread, NULL);
mpctx->open_active = false;
TA_FREEP(&mpctx->open_cancel);
TA_FREEP(&mpctx->open_url);
TA_FREEP(&mpctx->open_format);
if (mpctx->open_res_demuxer)
free_demuxer_and_stream(mpctx->open_res_demuxer);
mpctx->open_res_demuxer = NULL;
atomic_store(&mpctx->open_done, false);
}
// Setup all the field to open this url, and make sure a thread is running.
static void start_open(struct MPContext *mpctx, char *url, int url_flags)
{
cancel_open(mpctx);
assert(!mpctx->open_active);
assert(!mpctx->open_cancel);
assert(!mpctx->open_res_demuxer);
assert(!atomic_load(&mpctx->open_done));
mpctx->open_cancel = mp_cancel_new(NULL);
mpctx->open_url = talloc_strdup(NULL, url);
mpctx->open_format = talloc_strdup(NULL, mpctx->opts->demuxer_name);
mpctx->open_url_flags = url_flags;
if (mpctx->opts->load_unsafe_playlists)
mpctx->open_url_flags = 0;
if (pthread_create(&mpctx->open_thread, NULL, open_demux_thread, mpctx)) {
cancel_open(mpctx);
return;
}
mpctx->open_active = true;
}
static void open_demux_reentrant(struct MPContext *mpctx)
{
struct demux_open_args args = {
.global = mpctx->global,
.cancel = mpctx->playback_abort,
.log = mpctx->log,
.stream_flags = mpctx->playing->stream_flags,
.url = talloc_strdup(NULL, mpctx->stream_open_filename),
};
if (mpctx->opts->load_unsafe_playlists)
args.stream_flags = 0;
mpctx_run_reentrant(mpctx, open_demux_thread, &args);
if (args.demux) {
mpctx->demuxer = args.demux;
enable_demux_thread(mpctx, mpctx->demuxer);
char *url = mpctx->stream_open_filename;
if (mpctx->open_active) {
bool done = atomic_load(&mpctx->open_done);
bool failed = done && !mpctx->open_res_demuxer;
bool correct_url = strcmp(mpctx->open_url, url) == 0;
if (correct_url && !failed) {
MP_VERBOSE(mpctx, "Using prefetched/prefetching URL.\n");
} else if (correct_url && failed) {
MP_VERBOSE(mpctx, "Prefetched URL failed, retrying.\n");
cancel_open(mpctx);
} else {
if (done) {
MP_VERBOSE(mpctx, "Dropping finished prefetch of wrong URL.\n");
} else {
MP_VERBOSE(mpctx, "Aborting onging prefetch of wrong URL.\n");
}
cancel_open(mpctx);
}
}
if (!mpctx->open_active)
start_open(mpctx, url, mpctx->playing->stream_flags);
// User abort should cancel the opener now.
pthread_mutex_lock(&mpctx->lock);
mpctx->demuxer_cancel = mpctx->open_cancel;
pthread_mutex_unlock(&mpctx->lock);
while (!atomic_load(&mpctx->open_done)) {
mp_idle(mpctx);
if (mpctx->stop_play)
mp_abort_playback_async(mpctx);
}
if (mpctx->open_res_demuxer) {
assert(mpctx->demuxer_cancel == mpctx->open_cancel);
mpctx->demuxer = mpctx->open_res_demuxer;
mpctx->open_res_demuxer = NULL;
mpctx->open_cancel = NULL;
} else {
mpctx->error_playing = args.err;
mpctx->error_playing = mpctx->open_res_error;
pthread_mutex_lock(&mpctx->lock);
mpctx->demuxer_cancel = NULL;
pthread_mutex_unlock(&mpctx->lock);
}
cancel_open(mpctx); // cleanup
}
void prefetch_next(struct MPContext *mpctx)
{
if (!mpctx->opts->prefetch_open)
return;
struct playlist_entry *new_entry = mp_next_file(mpctx, +1, false, false);
if (new_entry && !mpctx->open_active && new_entry->filename) {
MP_VERBOSE(mpctx, "Prefetching: %s\n", new_entry->filename);
start_open(mpctx, new_entry->filename, new_entry->stream_flags);
}
talloc_free(args.url);
}
static bool init_complex_filters(struct MPContext *mpctx)
@@ -977,7 +1083,7 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->filename = talloc_strdup(NULL, mpctx->playing->filename);
mpctx->stream_open_filename = mpctx->filename;
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION | OSD_SEEK_INFO_CURRENT_FILE;
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_CURRENT_FILE;
if (opts->reset_options) {
for (int n = 0; opts->reset_options[n]; n++) {
@@ -1038,6 +1144,10 @@ reopen_file:
goto terminate_playback;
}
if (mpctx->opts->rebase_start_time)
demux_set_ts_offset(mpctx->demuxer, -mpctx->demuxer->start_time);
enable_demux_thread(mpctx, mpctx->demuxer);
load_chapters(mpctx);
add_demuxer_tracks(mpctx, mpctx->demuxer);
@@ -1149,6 +1259,8 @@ reopen_file:
if (mpctx->opts->pause)
pause_player(mpctx);
open_recorder(mpctx, true);
playback_start = mp_time_sec();
mpctx->error_playing = 0;
while (!mpctx->stop_play)
@@ -1169,7 +1281,9 @@ terminate_playback:
if (mpctx->step_frames)
opts->pause = 1;
mp_cancel_trigger(mpctx->playback_abort);
mp_abort_playback_async(mpctx);
close_recorder(mpctx);
// time to uninit all, except global stuff:
uninit_complex_filters(mpctx);
@@ -1255,8 +1369,9 @@ terminate_playback:
// it can have side-effects and mutate mpctx.
// direction: -1 (previous) or +1 (next)
// force: if true, don't skip playlist entries marked as failed
// mutate: if true, change loop counters
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
bool force)
bool force, bool mutate)
{
struct playlist_entry *next = playlist_get_next(mpctx->playlist, direction);
if (next && direction < 0 && !force) {
@@ -1316,7 +1431,7 @@ void mp_play_files(struct MPContext *mpctx)
if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR ||
mpctx->stop_play == AT_END_OF_FILE || !mpctx->stop_play)
{
new_entry = mp_next_file(mpctx, +1, false);
new_entry = mp_next_file(mpctx, +1, false, true);
}
mpctx->playlist->current = new_entry;
@@ -1326,6 +1441,8 @@ void mp_play_files(struct MPContext *mpctx)
if (!mpctx->playlist->current && mpctx->opts->player_idle_mode < 2)
break;
}
cancel_open(mpctx);
}
// Abort current playback and set the given entry to play next.
@@ -1339,3 +1456,89 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e)
mpctx->stop_play = PT_CURRENT_ENTRY;
mp_wakeup_core(mpctx);
}
static void set_track_recorder_sink(struct track *track,
struct mp_recorder_sink *sink)
{
if (track->d_sub)
sub_set_recorder_sink(track->d_sub, sink);
if (track->d_video)
track->d_video->recorder_sink = sink;
if (track->d_audio)
track->d_audio->recorder_sink = sink;
track->remux_sink = sink;
}
void close_recorder(struct MPContext *mpctx)
{
if (!mpctx->recorder)
return;
for (int n = 0; n < mpctx->num_tracks; n++)
set_track_recorder_sink(mpctx->tracks[n], NULL);
mp_recorder_destroy(mpctx->recorder);
mpctx->recorder = NULL;
}
// Like close_recorder(), but also unset the option. Intended for use on errors.
void close_recorder_and_error(struct MPContext *mpctx)
{
close_recorder(mpctx);
talloc_free(mpctx->opts->record_file);
mpctx->opts->record_file = NULL;
mp_notify_property(mpctx, "record-file");
MP_ERR(mpctx, "Disabling stream recording.\n");
}
void open_recorder(struct MPContext *mpctx, bool on_init)
{
if (!mpctx->playback_initialized)
return;
close_recorder(mpctx);
char *target = mpctx->opts->record_file;
if (!target || !target[0])
return;
struct sh_stream **streams = NULL;
int num_streams = 0;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->stream && track->selected &&
(track->d_sub || track->d_video || track->d_audio))
{
MP_TARRAY_APPEND(NULL, streams, num_streams, track->stream);
}
}
mpctx->recorder = mp_recorder_create(mpctx->global, mpctx->opts->record_file,
streams, num_streams);
if (!mpctx->recorder) {
talloc_free(streams);
close_recorder_and_error(mpctx);
return;
}
if (!on_init)
mp_recorder_mark_discontinuity(mpctx->recorder);
int n_stream = 0;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (n_stream >= num_streams)
break;
// (We expect track->stream not to be reused on other tracks.)
if (track->stream == streams[n_stream]) {
set_track_recorder_sink(track,
mp_recorder_get_sink(mpctx->recorder, n_stream));
n_stream++;
}
}
talloc_free(streams);
}
+2
View File
@@ -1079,6 +1079,7 @@ static int script_readdir(lua_State *L)
lua_pushstring(L, name); // list index name
lua_settable(L, -3); // list
}
closedir(dir);
talloc_free(fullpath);
return 1;
}
@@ -1331,6 +1332,7 @@ static void add_functions(struct script_ctx *ctx)
}
const struct mp_scripting mp_scripting_lua = {
.name = "lua script",
.file_ext = "lua",
.load = load_lua,
};
+10
View File
@@ -419,6 +419,16 @@ function mp.register_idle(cb)
idle_handlers[#idle_handlers + 1] = cb
end
function mp.unregister_idle(cb)
local new = {}
for _, handler in ipairs(idle_handlers) do
if handler ~= cb then
new[#new + 1] = handler
end
end
idle_handlers = new
end
-- sent by "script-binding"
mp.register_script_message("key-binding", dispatch_key_binding)
+37 -25
View File
@@ -459,20 +459,19 @@ function prepare_elements()
elem_geo.h - slider_lo.border)
end
else -- draw 1px nibbles
else -- draw 2x1px nibbles
--top
if (slider_lo.nibbles_top) then
static_ass:rect_cw(s - 0.5, slider_lo.gap,
s + 0.5, slider_lo.gap*2);
static_ass:rect_cw(s - 1, slider_lo.border,
s + 1, slider_lo.border + slider_lo.gap);
end
--bottom
if (slider_lo.nibbles_bottom) then
static_ass:rect_cw(s - 0.5,
elem_geo.h - slider_lo.gap*2,
s + 0.5,
elem_geo.h - slider_lo.gap);
static_ass:rect_cw(s - 1,
elem_geo.h -slider_lo.border -slider_lo.gap,
s + 1, elem_geo.h - slider_lo.border);
end
end
end
@@ -548,14 +547,12 @@ function render_elements(master_ass)
elem_ass:merge(element.static_ass)
end
if (element.type == "slider") then
local slider_lo = element.layout.slider
local elem_geo = element.layout.geometry
local s_min, s_max = element.slider.min.value, element.slider.max.value
local s_min = element.slider.min.value
local s_max = element.slider.max.value
-- draw pos marker
local pos = element.slider.posF()
@@ -568,7 +565,6 @@ function render_elements(master_ass)
(slider_lo.stype == "knob") then
foH = elem_geo.h / 2
elseif (slider_lo.stype == "bar") then
foV = foV + 1
foH = slider_lo.border + slider_lo.gap
end
@@ -585,10 +581,12 @@ function render_elements(master_ass)
elem_ass:line_to(xp, (innerH)+foV)
elem_ass:line_to(xp-(innerH/2), (innerH/2)+foV)
elseif (slider_lo.stype == "knob") then
elem_ass:rect_cw(xp, (9*innerH/20)+foV, elem_geo.w - foH, (11*innerH/20)+foV)
elem_ass:rect_cw(foH, (3*innerH/8)+foV, xp, (5*innerH/8)+foV)
elem_ass:round_rect_cw(xp - innerH/2, foV, xp + innerH/2,
foV + innerH, innerH/2.0)
elem_ass:rect_cw(xp, (9*innerH/20) + foV,
elem_geo.w - foH, (11*innerH/20) + foV)
elem_ass:rect_cw(foH, (3*innerH/8) + foV,
xp, (5*innerH/8) + foV)
elem_ass:round_rect_cw(xp - innerH/2, foV,
xp + innerH/2, foV + innerH, innerH/2.0)
end
end
@@ -660,8 +658,15 @@ function render_elements(master_ass)
end
local maxchars = element.layout.button.maxchars
if not (maxchars == nil) and (#buttontext > maxchars) then
if (#buttontext > maxchars+20) then
while (#buttontext > maxchars+20) do
buttontext = buttontext:gsub(".[\128-\191]*$", "")
end
buttontext = buttontext .. "..."
end
local _, nchars2 = buttontext:gsub(".[\128-\191]*", "")
local stretch = (maxchars/#buttontext)*100
buttontext = string.format("{\\fscx%f}",
(maxchars/#buttontext)*100) .. buttontext
end
@@ -946,7 +951,7 @@ layouts["box"] = function ()
lo = add_layout("title")
lo.geometry = {x = posX, y = titlerowY, an = 8, w = 496, h = 12}
lo.style = osc_styles.vidtitle
lo.button.maxchars = 90
lo.button.maxchars = 80
lo = add_layout("pl_prev")
lo.geometry =
@@ -1226,8 +1231,9 @@ layouts["bottombar"] = function()
w = t_r - t_l, h = geo.h }
lo = add_layout("title")
lo.geometry = geo
lo.style = osc_styles.vidtitleBar
lo.button.maxchars = math.floor(geo.w/7)
lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
osc_styles.vidtitleBar,
geo.x, geo.y-geo.h/2, geo.w, geo.y+geo.h/2)
-- Playback control buttons
@@ -1296,6 +1302,7 @@ layouts["bottombar"] = function()
lo.geometry = geo
lo.style = osc_styles.timecodes
lo.slider.border = 0
lo.slider.gap = 2
lo.slider.tooltip_style = osc_styles.timePosBar
lo.slider.tooltip_an = 5
lo.slider.stype = user_opts["seekbarstyle"]
@@ -1403,7 +1410,8 @@ layouts["topbar"] = function()
-- Seekbar
geo = { x = sb_l, y = user_opts.barmargin, an = 7, w = math.max(0, sb_r - sb_l), h = geo.h }
geo = { x = sb_l, y = user_opts.barmargin, an = 7,
w = math.max(0, sb_r - sb_l), h = geo.h }
new_element("bgbar1", "box")
lo = add_layout("bgbar1")
@@ -1417,6 +1425,7 @@ layouts["topbar"] = function()
lo.geometry = geo
lo.style = osc_styles.timecodesBar
lo.slider.border = 0
lo.slider.gap = 2
lo.slider.tooltip_style = osc_styles.timePosBar
lo.slider.stype = user_opts["seekbarstyle"]
lo.slider.tooltip_an = 5
@@ -1449,8 +1458,9 @@ layouts["topbar"] = function()
w = t_r - t_l, h = geo.h }
lo = add_layout("title")
lo.geometry = geo
lo.style = osc_styles.vidtitleBar
lo.button.maxchars = math.floor(geo.w/7)
lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
osc_styles.vidtitleBar,
geo.x, geo.y-geo.h/2, geo.w, geo.y+geo.h/2)
end
-- Validate string type user options
@@ -1506,6 +1516,7 @@ function osc_init()
local have_pl = (pl_count > 1)
local pl_pos = mp.get_property_number("playlist-pos", 0) + 1
local have_ch = (mp.get_property_number("chapters", 0) > 0)
local loop = mp.get_property("loop", "no")
local ne
@@ -1539,7 +1550,7 @@ function osc_init()
ne = new_element("pl_prev", "button")
ne.content = "\238\132\144"
ne.enabled = (pl_pos > 1)
ne.enabled = (pl_pos > 1) or (loop ~= "no")
ne.eventresponder["mouse_btn0_up"] =
function ()
mp.commandv("playlist-prev", "weak")
@@ -1554,7 +1565,7 @@ function osc_init()
ne = new_element("pl_next", "button")
ne.content = "\238\132\129"
ne.enabled = (have_pl) and (pl_pos < pl_count)
ne.enabled = (have_pl and (pl_pos < pl_count)) or (loop ~= "no")
ne.eventresponder["mouse_btn0_up"] =
function ()
mp.commandv("playlist-next", "weak")
@@ -2071,6 +2082,7 @@ function process_event(source, what)
if n == 0 then
--click on background (does not work)
elseif n > 0 and not (n > #elements) and
not (elements[n].eventresponder == nil) and
not (elements[n].eventresponder[source .. "_" .. what] == nil) then
if mouse_hit(elements[n]) then
+64 -26
View File
@@ -88,6 +88,42 @@ local function extract_chapters(data, video_length)
return ret
end
local function edl_track_joined(fragments, protocol, is_live)
if not (type(fragments) == "table") or not fragments[1] then
msg.debug("No fragments to join into EDL")
return nil
end
local edl = "edl://"
local offset = 1
if (protocol == "http_dash_segments") and
not fragments[1].duration and not is_live then
-- assume MP4 DASH initialization segment
edl = edl .. "!mp4_dash,init=" .. edl_escape(fragments[1].url) .. ";"
offset = 2
-- Check remaining fragments for duration;
-- if not available in all, give up.
for i = offset, #fragments do
if not fragments[i].duration then
msg.error("EDL doesn't support fragments" ..
"without duration with MP4 DASH")
return nil
end
end
end
for i = offset, #fragments do
local fragment = fragments[i]
edl = edl .. edl_escape(fragment.url)
if fragment.duration then
edl = edl..",length="..fragment.duration
end
edl = edl .. ";"
end
return edl
end
mp.add_hook("on_load", 10, function ()
local url = mp.get_property("stream-open-filename")
@@ -124,15 +160,15 @@ mp.add_hook("on_load", 10, function ()
if (mp.get_property("options/vid") == "no")
and not option_was_set("ytdl-format") then
format = "bestaudio"
format = "bestaudio/best"
msg.verbose("Video disabled. Only using audio")
end
if (format == "") then
format = "bestvideo+bestaudio"
format = "bestvideo+bestaudio/best"
end
table.insert(command, "--format")
table.insert(command, string.format('(%s)[protocol!=http_dash_segments]/best', format))
table.insert(command, format)
for param, arg in pairs(raw_options) do
table.insert(command, "--" .. param)
@@ -190,14 +226,7 @@ mp.add_hook("on_load", 10, function ()
and (json.entries[1]["webpage_url"] == json["webpage_url"])) then
msg.verbose("multi-arc video detected, building EDL")
local playlist = "edl://"
for i, entry in pairs(json.entries) do
playlist = playlist .. edl_escape(entry.url)
if not (entry.duration == nil) then
playlist = playlist..",start=0,length="..entry.duration
end
playlist = playlist .. ";"
end
local playlist = edl_track_joined(json.entries)
msg.debug("EDL: " .. playlist)
@@ -232,7 +261,7 @@ mp.add_hook("on_load", 10, function ()
else
subfile = subfile..edl_escape("memory://WEBVTT")
end
subfile = subfile..",start=0,length="..entry.duration..";"
subfile = subfile..",length="..entry.duration..";"
end
msg.debug(j.." sub EDL: "..subfile)
mp.commandv("sub-add", subfile, "auto", req.ext, j)
@@ -270,23 +299,30 @@ mp.add_hook("on_load", 10, function ()
else -- probably a video
local streamurl = ""
-- DASH?
-- DASH/split tracks
if not (json["requested_formats"] == nil) then
if (json["requested_formats"][1].protocol == "http_dash_segments") then
msg.error("MPEG-Dash Segments unsupported, add [protocol!=http_dash_segments] to your ytdl-format.")
return
for _, track in pairs(json.requested_formats) do
local edl_track = nil
edl_track = edl_track_joined(track.fragments,
track.protocol, json.is_live)
if track.acodec and track.acodec ~= "none" then
-- audio track
mp.commandv("audio-add",
edl_track or track.url, "auto",
track.format_note or "")
elseif track.vcodec and track.vcodec ~= "none" then
-- video track
streamurl = edl_track or track.url
end
end
-- video url
streamurl = json["requested_formats"][1].url
-- audio url
mp.commandv("audio-add", json["requested_formats"][2].url,
"select", json["requested_formats"][2]["format_note"] or "")
elseif not (json.url == nil) then
-- normal video
streamurl = json.url
local edl_track = nil
edl_track = edl_track_joined(json.fragments, json.protocol,
json.is_live)
-- normal video or single track
streamurl = edl_track or json.url
set_http_headers(json.http_headers)
else
msg.error("No URL found in JSON data.")
@@ -295,7 +331,7 @@ mp.add_hook("on_load", 10, function ()
msg.debug("streamurl: " .. streamurl)
mp.set_property("stream-open-filename", streamurl)
mp.set_property("stream-open-filename", streamurl:gsub("^data:", "data://", 1))
mp.set_property("file-local-options/force-media-title", json.title)
@@ -348,6 +384,8 @@ mp.add_hook("on_load", 10, function ()
"rtmp_playpath", json.play_path)
rtmp_prop = append_rtmp_prop(rtmp_prop,
"rtmp_swfverify", json.player_url)
rtmp_prop = append_rtmp_prop(rtmp_prop,
"rtmp_swfurl", json.player_url)
rtmp_prop = append_rtmp_prop(rtmp_prop,
"rtmp_app", json.app)
+11 -2
View File
@@ -136,7 +136,7 @@ void mp_print_version(struct mp_log *log, int always)
{
int v = always ? MSGL_INFO : MSGL_V;
mp_msg(log, v,
"%s (C) 2000-2016 mpv/MPlayer/mplayer2 projects\n built on %s\n",
"%s (C) 2000-2017 mpv/MPlayer/mplayer2 projects\n built on %s\n",
mpv_version, mpv_builddate);
print_libav_versions(log, v);
mp_msg(log, v, "\n");
@@ -199,6 +199,7 @@ void mp_destroy(struct MPContext *mpctx)
pthread_detach(pthread_self());
mp_msg_uninit(mpctx->global);
pthread_mutex_destroy(&mpctx->lock);
talloc_free(mpctx);
}
@@ -311,6 +312,12 @@ static int cfg_include(void *ctx, char *filename, int flags)
return r;
}
static void abort_playback_cb(void *ctx)
{
struct MPContext *mpctx = ctx;
mp_abort_playback_async(mpctx);
}
struct MPContext *mp_create(void)
{
char *enable_talloc = getenv("MPV_LEAK_REPORT");
@@ -329,6 +336,8 @@ struct MPContext *mp_create(void)
.playback_abort = mp_cancel_new(mpctx),
};
pthread_mutex_init(&mpctx->lock, NULL);
mpctx->global = talloc_zero(mpctx, struct mpv_global);
// Nothing must call mp_msg*() and related before this
@@ -361,7 +370,7 @@ struct MPContext *mp_create(void)
cocoa_set_input_context(mpctx->input);
#endif
mp_input_set_cancel(mpctx->input, mpctx->playback_abort);
mp_input_set_cancel(mpctx->input, abort_playback_cb, mpctx);
char *verbose_env = getenv("MPV_VERBOSE");
if (verbose_env)
+18 -53
View File
@@ -17,7 +17,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
#include "config.h"
@@ -213,21 +213,34 @@ int stream_dump(struct MPContext *mpctx, const char *source_filename)
int64_t size = stream_get_size(stream);
stream_set_capture_file(stream, opts->stream_dump);
FILE *dest = fopen(opts->stream_dump, "wb");
if (!dest) {
MP_ERR(mpctx, "Error opening dump file: %s\n", mp_strerror(errno));
return -1;
}
while (mpctx->stop_play == KEEP_PLAYING && !stream->eof) {
bool ok = true;
while (mpctx->stop_play == KEEP_PLAYING && ok) {
if (!opts->quiet && ((stream->pos / (1024 * 1024)) % 2) == 1) {
uint64_t pos = stream->pos;
MP_MSG(mpctx, MSGL_STATUS, "Dumping %lld/%lld...",
(long long int)pos, (long long int)size);
}
stream_fill_buffer(stream);
bstr data = stream_peek(stream, STREAM_MAX_BUFFER_SIZE);
if (data.len == 0) {
ok &= stream->eof;
break;
}
ok &= fwrite(data.start, data.len, 1, dest) == 1;
stream_skip(stream, data.len);
mp_wakeup_core(mpctx); // don't actually sleep
mp_idle(mpctx); // but process input
}
ok &= fclose(dest) == 0;
free_stream(stream);
return 0;
return ok ? 0 : -1;
}
void merge_playlist_files(struct playlist *pl)
@@ -251,51 +264,3 @@ void merge_playlist_files(struct playlist *pl)
playlist_add_file(pl, edl);
talloc_free(edl);
}
struct wrapper_args {
struct MPContext *mpctx;
void (*thread_fn)(void *);
void *thread_arg;
pthread_mutex_t mutex;
bool done;
};
static void *thread_wrapper(void *pctx)
{
struct wrapper_args *args = pctx;
mpthread_set_name("opener");
args->thread_fn(args->thread_arg);
pthread_mutex_lock(&args->mutex);
args->done = true;
pthread_mutex_unlock(&args->mutex);
mp_wakeup_core(args->mpctx); // this interrupts mp_idle()
return NULL;
}
// Run the thread_fn in a new thread. Wait until the thread returns, but while
// waiting, process input and input commands.
int mpctx_run_reentrant(struct MPContext *mpctx, void (*thread_fn)(void *arg),
void *thread_arg)
{
struct wrapper_args args = {mpctx, thread_fn, thread_arg};
pthread_mutex_init(&args.mutex, NULL);
bool success = false;
pthread_t thread;
if (pthread_create(&thread, NULL, thread_wrapper, &args))
goto done;
while (!success) {
mp_idle(mpctx);
if (mpctx->stop_play)
mp_cancel_trigger(mpctx->playback_abort);
pthread_mutex_lock(&args.mutex);
success |= args.done;
pthread_mutex_unlock(&args.mutex);
}
pthread_join(thread, NULL);
done:
pthread_mutex_destroy(&args.mutex);
mp_wakeup_core(mpctx); // avoid lost wakeups during waiting
return success ? 0 : -1;
}
+7 -11
View File
@@ -262,7 +262,7 @@ static void term_osd_print_status_lazy(struct MPContext *mpctx)
if (mpctx->demuxer) {
struct stream_cache_info info = {0};
demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info);
if (info.size > 0) {
if (info.size > 0 || mpctx->demuxer->is_network) {
saddf(&line, " Cache: ");
struct demux_ctrl_reader_state s = {.ts_duration = -1};
@@ -273,10 +273,12 @@ static void term_osd_print_status_lazy(struct MPContext *mpctx)
} else {
saddf(&line, "%2ds", (int)s.ts_duration);
}
if (info.fill >= 1024 * 1024) {
saddf(&line, "+%lldMB", (long long)(info.fill / 1024 / 1024));
} else {
saddf(&line, "+%lldKB", (long long)(info.fill / 1024));
if (info.size > 0) {
if (info.fill >= 1024 * 1024) {
saddf(&line, "+%lldMB", (long long)(info.fill / 1024 / 1024));
} else {
saddf(&line, "+%lldKB", (long long)(info.fill / 1024));
}
}
}
}
@@ -474,12 +476,6 @@ static void add_seek_osd_messages(struct MPContext *mpctx)
"Chapter: %s", chapter);
talloc_free(chapter);
}
if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION) && mpctx->demuxer) {
set_osd_msg(mpctx, 1, mpctx->opts->osd_duration,
"Playing edition %d of %d.",
mpctx->demuxer->edition + 1,
mpctx->demuxer->num_editions);
}
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CURRENT_FILE) {
if (mpctx->filename) {
set_osd_msg(mpctx, 1, mpctx->opts->osd_duration, "%s",
+6
View File
@@ -28,6 +28,7 @@
#include "options/options.h"
#include "common/common.h"
#include "common/encode.h"
#include "common/recorder.h"
#include "options/m_config.h"
#include "options/m_property.h"
#include "common/playlist.h"
@@ -330,6 +331,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
clear_audio_output_buffers(mpctx);
reset_playback_state(mpctx);
if (mpctx->recorder)
mp_recorder_mark_discontinuity(mpctx->recorder);
/* Use the target time as "current position" for further relative
* seeks etc until a new video frame has been decoded */
@@ -663,6 +666,9 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx)
force_update = true;
}
if (s.eof && !busy)
prefetch_next(mpctx);
if (force_update)
mp_notify(mpctx, MP_EVENT_CACHE_UPDATE, NULL);
}
+10 -6
View File
@@ -31,6 +31,7 @@
#include "common/msg.h"
#include "options/path.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "video/image_writer.h"
@@ -346,12 +347,15 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
}
}
if (image && mpctx->vo_chain && mpctx->vo_chain->hwdec_devs) {
struct mp_hwdec_ctx *ctx =
hwdec_devices_get_first(mpctx->vo_chain->hwdec_devs);
struct mp_image *nimage = NULL;
if (ctx && ctx->download_image && (image->fmt.flags & MP_IMGFLAG_HWACCEL))
nimage = ctx->download_image(ctx, image, NULL);
bool hwimage = image && (image->fmt.flags & MP_IMGFLAG_HWACCEL);
if (hwimage) {
struct mp_image *nimage = mp_image_hw_download(image, NULL);
if (!nimage && mpctx->vo_chain && mpctx->vo_chain->hwdec_devs) {
struct mp_hwdec_ctx *ctx =
hwdec_devices_get_first(mpctx->vo_chain->hwdec_devs);
if (ctx && ctx->download_image && hwimage)
nimage = ctx->download_image(ctx, image, NULL);
}
if (nimage) {
talloc_free(image);
image = nimage;
+39 -5
View File
@@ -37,10 +37,14 @@
#include "libmpv/client.h"
extern const struct mp_scripting mp_scripting_lua;
extern const struct mp_scripting mp_scripting_cplugin;
static const struct mp_scripting *const scripting_backends[] = {
#if HAVE_LUA
&mp_scripting_lua,
#endif
#if HAVE_CPLUGINS
&mp_scripting_cplugin,
#endif
NULL
};
@@ -80,13 +84,12 @@ static void *script_thread(void *p)
struct thread_arg *arg = p;
char name[90];
snprintf(name, sizeof(name), "lua (%s)", mpv_client_name(arg->client));
snprintf(name, sizeof(name), "%s (%s)", arg->backend->name,
mpv_client_name(arg->client));
mpthread_set_name(name);
if (arg->backend->load(arg->client, arg->fname) < 0)
MP_ERR(arg, "Could not load script %s\n", arg->fname);
MP_VERBOSE(arg, "Exiting...\n");
MP_ERR(arg, "Could not load %s %s\n", arg->backend->name, arg->fname);
mpv_detach_destroy(arg->client);
talloc_free(arg);
@@ -133,7 +136,7 @@ int mp_load_script(struct MPContext *mpctx, const char *fname)
}
arg->log = mp_client_get_log(arg->client);
MP_VERBOSE(arg, "Loading script %s...\n", fname);
MP_VERBOSE(arg, "Loading %s %s...\n", backend->name, fname);
pthread_t thread;
if (pthread_create(&thread, NULL, script_thread, arg)) {
@@ -228,3 +231,34 @@ void mp_load_scripts(struct MPContext *mpctx)
}
talloc_free(tmp);
}
#if HAVE_CPLUGINS
#include <dlfcn.h>
#define MPV_DLOPEN_FN "mpv_open_cplugin"
typedef int (*mpv_open_cplugin)(mpv_handle *handle);
static int load_cplugin(struct mpv_handle *client, const char *fname)
{
int r = -1;
void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
if (!lib)
goto error;
// Note: once loaded, we never unload, as unloading the libraries linked to
// the plugin can cause random serious problems.
mpv_open_cplugin sym = (mpv_open_cplugin)dlsym(lib, MPV_DLOPEN_FN);
if (!sym)
goto error;
r = sym(client) ? -1 : 0;
error:
return r;
}
const struct mp_scripting mp_scripting_cplugin = {
.name = "SO plugin",
.file_ext = "so",
.load = load_cplugin,
};
#endif
+27 -7
View File
@@ -328,6 +328,10 @@ static void vo_chain_reset_state(struct vo_chain *vo_c)
if (vo_c->video_src)
video_reset(vo_c->video_src);
// Prepare for continued playback after a seek.
if (!vo_c->input_mpi && vo_c->cached_coverart)
vo_c->input_mpi = mp_image_new_ref(vo_c->cached_coverart);
}
void reset_video_state(struct MPContext *mpctx)
@@ -381,6 +385,7 @@ static void vo_chain_uninit(struct vo_chain *vo_c)
lavfi_set_connected(vo_c->filter_src, false);
mp_image_unrefp(&vo_c->input_mpi);
mp_image_unrefp(&vo_c->cached_coverart);
vf_destroy(vo_c->vf);
talloc_free(vo_c);
// this does not free the VO
@@ -682,13 +687,25 @@ static int video_decode_and_filter(struct MPContext *mpctx)
return r;
if (!vo_c->input_mpi) {
// Decode a new image, or at least feed the decoder a packet.
r = decode_image(mpctx);
if (r == VD_WAIT)
return r;
if (vo_c->cached_coverart) {
// Don't ever decode it twice, not even after seek resets.
// (On seek resets, input_mpi is set to the cached image.)
r = VD_EOF;
} else {
// Decode a new image, or at least feed the decoder a packet.
r = decode_image(mpctx);
if (r == VD_WAIT)
return r;
}
}
if (vo_c->input_mpi)
if (vo_c->input_mpi) {
vo_c->input_format = vo_c->input_mpi->params;
vf_set_proto_frame(vo_c->vf, vo_c->input_mpi);
if (vo_c->is_coverart && !vo_c->cached_coverart)
vo_c->cached_coverart = mp_image_new_ref(vo_c->input_mpi);
}
bool eof = !vo_c->input_mpi && (r == VD_EOF || r < 0);
r = video_filter(mpctx, eof);
@@ -1413,8 +1430,11 @@ void write_video(struct MPContext *mpctx)
mp_image_params_get_dsize(&p, &d_w, &d_h);
snprintf(extra, sizeof(extra), " => %dx%d", d_w, d_h);
}
MP_INFO(mpctx, "VO: [%s] %dx%d%s %s\n",
info->name, p.w, p.h, extra, vo_format_name(p.imgfmt));
char sfmt[20] = {0};
if (p.hw_subfmt)
snprintf(sfmt, sizeof(sfmt), "[%s]", mp_imgfmt_to_name(p.hw_subfmt));
MP_INFO(mpctx, "VO: [%s] %dx%d%s %s%s\n",
info->name, p.w, p.h, extra, mp_imgfmt_to_name(p.imgfmt), sfmt);
MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description);
int vo_r = vo_reconfig(vo, &p);
+1 -3
View File
@@ -338,10 +338,8 @@ static int resize_cache(struct priv *s, int64_t size)
buffer_size += s->back_size;
unsigned char *buffer = malloc(buffer_size);
if (!buffer) {
free(buffer);
if (!buffer)
return STREAM_ERROR;
}
if (s->buffer) {
// Copy & free the old ringbuffer data.
+16 -51
View File
@@ -258,8 +258,6 @@ static int open_internal(const stream_info_t *sinfo, const char *url, int flags,
assert(s->seekable == !!s->seek);
s->uncached_type = s->type;
if (s->mime_type)
MP_VERBOSE(s, "Mime-type: '%s'\n", s->mime_type);
@@ -328,7 +326,7 @@ stream_t *open_output_stream(const char *filename, struct mpv_global *global)
static bool stream_reconnect(stream_t *s)
{
if (!s->streaming || s->uncached_stream || !s->seekable || !s->cancel)
if (!s->streaming || s->caching || !s->seekable || !s->cancel)
return false;
int64_t pos = s->pos;
@@ -353,57 +351,25 @@ static bool stream_reconnect(stream_t *s)
return false;
}
static void stream_capture_write(stream_t *s, void *buf, size_t len)
{
if (s->capture_file && len > 0) {
if (fwrite(buf, len, 1, s->capture_file) < 1) {
MP_ERR(s, "Error writing capture file: %s\n", mp_strerror(errno));
stream_set_capture_file(s, NULL);
}
}
}
void stream_set_capture_file(stream_t *s, const char *filename)
{
if (!bstr_equals(bstr0(s->capture_filename), bstr0(filename))) {
if (s->capture_file)
fclose(s->capture_file);
talloc_free(s->capture_filename);
s->capture_file = NULL;
s->capture_filename = NULL;
if (filename) {
s->capture_file = fopen(filename, "ab");
if (s->capture_file) {
s->capture_filename = talloc_strdup(NULL, filename);
if (s->buf_pos < s->buf_len)
stream_capture_write(s, s->buffer, s->buf_len);
} else {
MP_ERR(s, "Error opening capture file: %s\n", mp_strerror(errno));
}
}
}
}
// Read function bypassing the local stream buffer. This will not write into
// s->buffer, but into buf[0..len] instead.
// Returns 0 on error or EOF, and length of bytes read on success.
// Partial reads are possible, even if EOF is not reached.
static int stream_read_unbuffered(stream_t *s, void *buf, int len)
{
int orig_len = len;
int res = 0;
s->buf_pos = s->buf_len = 0;
// we will retry even if we already reached EOF previously.
len = s->fill_buffer ? s->fill_buffer(s, buf, len) : -1;
if (len < 0)
len = 0;
if (len == 0) {
if (s->fill_buffer && !mp_cancel_test(s->cancel))
res = s->fill_buffer(s, buf, len);
if (res <= 0) {
// just in case this is an error e.g. due to network
// timeout reset and retry
// do not retry if this looks like proper eof
int64_t size = stream_get_size(s);
if (!s->eof && s->pos != size && stream_reconnect(s)) {
s->eof = 1; // make sure EOF is set to ensure no endless recursion
return stream_read_unbuffered(s, buf, orig_len);
return stream_read_unbuffered(s, buf, len);
}
s->eof = 1;
@@ -411,9 +377,8 @@ static int stream_read_unbuffered(stream_t *s, void *buf, int len)
}
// When reading succeeded we are obviously not at eof.
s->eof = 0;
s->pos += len;
stream_capture_write(s, buf, len);
return len;
s->pos += res;
return res;
}
static int stream_fill_buffer_by(stream_t *s, int64_t len)
@@ -647,11 +612,9 @@ void free_stream(stream_t *s)
if (!s)
return;
stream_set_capture_file(s, NULL);
if (s->close)
s->close(s);
free_stream(s->uncached_stream);
free_stream(s->underlying);
talloc_free(s);
}
@@ -670,8 +633,8 @@ stream_t *open_memory_stream(void *data, int len)
static stream_t *open_cache(stream_t *orig, const char *name)
{
stream_t *cache = new_stream();
cache->uncached_type = orig->uncached_type;
cache->uncached_stream = orig;
cache->underlying = orig;
cache->caching = true;
cache->seekable = true;
cache->mode = STREAM_READ;
cache->read_chunk = 4 * STREAM_BUFFER_SIZE;
@@ -682,6 +645,8 @@ static stream_t *open_cache(stream_t *orig, const char *name)
cache->lavf_type = talloc_strdup(cache, orig->lavf_type);
cache->streaming = orig->streaming,
cache->is_network = orig->is_network;
cache->is_local_file = orig->is_local_file;
cache->is_directory = orig->is_directory;
cache->cancel = orig->cancel;
cache->global = orig->global;
@@ -722,7 +687,7 @@ static int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts)
stream_t *fcache = open_cache(orig, "file-cache");
if (stream_file_cache_init(fcache, orig, &use_opts) <= 0) {
fcache->uncached_stream = NULL; // don't free original stream
fcache->underlying = NULL; // don't free original stream
free_stream(fcache);
fcache = orig;
}
@@ -731,10 +696,10 @@ static int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts)
int res = stream_cache_init(cache, fcache, &use_opts);
if (res <= 0) {
cache->uncached_stream = NULL; // don't free original stream
cache->underlying = NULL; // don't free original stream
free_stream(cache);
if (fcache != orig) {
fcache->uncached_stream = NULL;
fcache->underlying = NULL;
free_stream(fcache);
}
} else {
+4 -22
View File
@@ -28,20 +28,6 @@
#include "misc/bstr.h"
enum streamtype {
STREAMTYPE_GENERIC = 0,
STREAMTYPE_FILE,
STREAMTYPE_DIR,
STREAMTYPE_DVB,
STREAMTYPE_DVD,
STREAMTYPE_BLURAY,
STREAMTYPE_TV,
STREAMTYPE_MF,
STREAMTYPE_EDL,
STREAMTYPE_AVDEVICE,
STREAMTYPE_CDDA,
};
#define STREAM_BUFFER_SIZE 2048
#define STREAM_MAX_SECTOR_SIZE (8 * 1024)
@@ -178,8 +164,6 @@ typedef struct stream {
// Close
void (*close)(struct stream *s);
enum streamtype type; // see STREAMTYPE_*
enum streamtype uncached_type; // if stream is cache, type of wrapped str.
int sector_size; // sector size (seek will be aligned on this size if non 0)
int read_chunk; // maximum amount of data to read at once to limit latency
unsigned int buf_pos, buf_len;
@@ -197,16 +181,16 @@ typedef struct stream {
bool fast_skip : 1; // consider stream fast enough to fw-seek by skipping
bool is_network : 1; // original stream_info_t.is_network flag
bool allow_caching : 1; // stream cache makes sense
bool caching : 1; // is a cache, or accesses a cache
bool is_local_file : 1; // from the filesystem
bool is_directory : 1; // directory on the filesystem
bool access_references : 1; // open other streams
struct mp_log *log;
struct mpv_global *global;
struct mp_cancel *cancel; // cancellation notification
FILE *capture_file;
char *capture_filename;
struct stream *uncached_stream; // underlying stream for cache wrapper
struct stream *underlying; // e.g. cache wrapper
// Includes additional padding in case sizes get rounded up by sector size.
unsigned char buffer[];
@@ -214,8 +198,6 @@ typedef struct stream {
int stream_fill_buffer(stream_t *s);
void stream_set_capture_file(stream_t *s, const char *filename);
struct mp_cache_opts;
bool stream_wants_cache(stream_t *stream, struct mp_cache_opts *opts);
int stream_enable_cache_defaults(stream_t **stream);
+1 -1
View File
@@ -21,8 +21,8 @@
static int open_f(stream_t *stream)
{
stream->type = STREAMTYPE_AVDEVICE;
stream->demuxer = "lavf";
stream->allow_caching = false;
return STREAM_OK;
}
+1 -2
View File
@@ -425,7 +425,7 @@ static int bluray_stream_open_internal(stream_t *s)
char *time = mp_format_time(ti->duration / 90000, false);
MP_VERBOSE(s, "idx: %3d duration: %s (playlist: %05d.mpls)\n",
i + 1, time, ti->playlist);
i, time, ti->playlist);
talloc_free(time);
/* try to guess which title may contain the main movie */
@@ -450,7 +450,6 @@ static int bluray_stream_open_internal(stream_t *s)
s->fill_buffer = bluray_stream_fill_buffer;
s->close = bluray_stream_close;
s->control = bluray_stream_control;
s->type = STREAMTYPE_BLURAY;
s->sector_size = BLURAY_SECTOR_SIZE;
s->priv = b;
s->demuxer = "+disc";
-1
View File
@@ -383,7 +383,6 @@ static int open_cdda(stream_t *st)
st->streaming = true;
st->type = STREAMTYPE_CDDA;
st->demuxer = "+disc";
print_cdtext(st, 0);
+1 -2
View File
@@ -931,6 +931,7 @@ static int dvb_open(stream_t *stream)
return STREAM_ERROR;
}
stream->priv = mp_get_config_group(stream, stream->global, &stream_dvb_conf);
dvb_state_t* state = dvb_get_state(stream);
dvb_priv_t *p = stream->priv;
@@ -990,7 +991,6 @@ static int dvb_open(stream_t *stream)
return STREAM_ERROR;
}
stream->type = STREAMTYPE_DVB;
stream->fill_buffer = dvb_streaming_read;
stream->close = dvbin_close;
stream->control = dvbin_stream_control;
@@ -1010,7 +1010,6 @@ dvb_state_t *dvb_get_state(stream_t *stream)
}
struct mp_log *log = stream->log;
struct mpv_global *global = stream->global;
stream->priv = mp_get_config_group(stream, stream->global, &stream_dvb_conf);
dvb_priv_t *priv = stream->priv;
int type, size;
char filename[30], *name;
-1
View File
@@ -904,7 +904,6 @@ static int open_s_internal(stream_t *stream)
// ... (unimplemented)
// return NULL;
stream->type = STREAMTYPE_DVD;
stream->demuxer = "+disc";
stream->lavf_type = "mpeg";
stream->sector_size = 2048;
+1 -2
View File
@@ -507,7 +507,6 @@ static int open_s_internal(stream_t *stream)
stream->fill_buffer = fill_buffer;
stream->control = control;
stream->close = stream_dvdnav_close;
stream->type = STREAMTYPE_DVD;
stream->demuxer = "+disc";
stream->lavf_type = "mpeg";
stream->allow_caching = false;
@@ -596,7 +595,7 @@ unsupported:
}
const stream_info_t stream_info_ifo_dvdnav = {
.name = "ifo/dvdnav",
.name = "ifo_dvdnav",
.open = ifo_dvdnav_stream_open,
.protocols = (const char*const[]){ "file", "", NULL },
};
+1 -1
View File
@@ -5,8 +5,8 @@
static int s_open (struct stream *stream)
{
stream->type = STREAMTYPE_EDL;
stream->demuxer = "edl";
stream->allow_caching = false;
return STREAM_OK;
}
+2 -2
View File
@@ -234,7 +234,7 @@ static int open_f(stream_t *stream)
.fd = -1
};
stream->priv = p;
stream->type = STREAMTYPE_FILE;
stream->is_local_file = true;
bool write = stream->mode == STREAM_WRITE;
int m = O_CLOEXEC | (write ? O_RDWR | O_CREAT | O_TRUNC : O_RDONLY);
@@ -281,7 +281,7 @@ static int open_f(stream_t *stream)
if (fstat(p->fd, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
p->use_poll = false;
stream->type = STREAMTYPE_DIR;
stream->is_directory = true;
stream->allow_caching = false;
MP_INFO(stream, "This is a directory - adding to playlist.\n");
}
+1
View File
@@ -416,6 +416,7 @@ const stream_info_t stream_info_ffmpeg = {
.protocols = (const char *const[]){
"rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh", "mmshttp", "rtp",
"httpproxy", "hls", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srtp",
"data",
NULL },
.can_write = true,
.is_safe = true,
-1
View File
@@ -30,7 +30,6 @@
static int
mf_stream_open (stream_t *stream)
{
stream->type = STREAMTYPE_MF;
stream->demuxer = "mf";
stream->allow_caching = false;
-1
View File
@@ -39,7 +39,6 @@ static int
tv_stream_open (stream_t *stream)
{
stream->type = STREAMTYPE_TV;
stream->close=tv_stream_close;
stream->demuxer = "tv";
stream->allow_caching = false;
+1 -4
View File
@@ -145,7 +145,7 @@ const struct m_sub_options tv_params_conf = {
tvi_handle_t *tv_new_handle(int size, struct mp_log *log, const tvi_functions_t *functions)
{
tvi_handle_t *h = malloc(sizeof(*h));
tvi_handle_t *h = calloc(1, sizeof(*h));
if (!h)
return NULL;
@@ -159,12 +159,9 @@ tvi_handle_t *tv_new_handle(int size, struct mp_log *log, const tvi_functions_t
h->log = log;
h->functions = functions;
h->seq = 0;
h->chanlist = -1;
h->chanlist_s = NULL;
h->norm = -1;
h->channel = -1;
h->scan = NULL;
return h;
}
+3 -2
View File
@@ -20,6 +20,7 @@
#include "config.h"
#include <stdio.h>
#include "common/common.h"
#include "video/img_fourcc.h"
#include "tv.h"
@@ -105,7 +106,7 @@ static int do_control(priv_t *priv, int cmd, void *arg)
static double grab_video_frame(priv_t *priv, char *buffer, int len)
{
memset(buffer, 0x42, len);
return 1;
return MP_NOPTS_VALUE;
}
static int get_video_framesize(priv_t *priv)
@@ -117,7 +118,7 @@ static int get_video_framesize(priv_t *priv)
static double grab_audio_frame(priv_t *priv, char *buffer, int len)
{
memset(buffer, 0x42, len);
return 1;
return MP_NOPTS_VALUE;
}
static int get_audio_framesize(priv_t *priv)
Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff. Show more