Compare commits

...
302 Commits
Author SHA1 Message Date
Diogo Franco (Kovensky) c4e4d9cbba Release v0.7.3 2015-01-27 10:23:08 +09:00
Diogo Franco (Kovensky) fa53c3fc7b Update RELEASE_NOTES 2015-01-27 10:21:22 +09:00
wm4 c61f76829f player: fix framestep over timeline segment boundaries
This was subtly broken by commit a937ba20. Instead of framestepping over
the timeline segment boundary, it would just unpause playback, because
seeking now resets mpctx->step_frames. This was especially apparent when
doing something like "mpv *.jpg --merge-files".

Fix by restoring the step_frames field specifically if the seek is done
for switching segment boundaries. Hopefully the number fields which need
such an exception on seeking won't grow and turn this code into a mess.
2015-01-27 10:20:19 +09:00
wm4 30e77e0fc5 options: list correct default value for --screenshot-template
Make the default value part of the option metadata, instead of doing
this in the screenshot code. Makes more sense with --list-options and
the command.c option metadata properties.
2015-01-27 10:20:19 +09:00
Diogo Franco (Kovensky) 328a0f9c80 manpage: fix typo 2015-01-26 16:10:24 +09:00
wm4 4f2104961e manpage: describe some more config directory artifacts
Conflicts:
	DOCS/man/mpv.rst
2015-01-26 16:10:24 +09:00
Diogo Franco (Kovensky) c2ef2bcb90 Update RELEASE_NOTES 2015-01-26 16:10:24 +09:00
Ben Boeckel 86229e8714 ao_pulse: plug a memory leak 2015-01-26 16:10:24 +09:00
wm4 295fdeb086 lua: do not use math.pow()
It's the first thing that breaks with Lua 5.3. I don't know if there
are other failures because I don't care enough.
2015-01-26 16:10:24 +09:00
wm4 909c4d7711 input: fix dangling pointer
Removes undefined behavior that showed up as crap when running with -v.
2015-01-26 16:10:24 +09:00
wm4 0f83f1e4b3 ytdl: implement user-agent and cookie overrides
For some sites, youtube-dl sends a special user-agent. If we don't send
the same user-agent, the server will reject mpv's connection attempt.
This was observed with trailers.apple.com. Fix it by forcing the
user-agent youtube-dl uses.

Some sites set cookies when doing a website access, and require the
client to provide these cookies when downloading the actual media. This
is needed at least by nicovideo.jp. Fix by adding youtube-dl's cookies
to our request headers.

Both of these require a very recent youtube-dl version (youtube-dl added
the necessary headers a few hours ago). The script still works with
older youtube-dl versions, though.
2015-01-26 16:10:24 +09:00
Vítor Galvão 9c83e8483e youtube-dl_mpv.sh: unquote ${video_url} 2015-01-25 17:00:21 +09:00
wm4 e0e58e18c0 input: handle mixing key press and up/down events better 2015-01-25 17:00:21 +09:00
James Ross-Gowan 6f9eb960c7 ao_wasapi: fix try_format logic in shared mode
The MSDN documentation for IsFormatSupported says a return code of
AUDCLNT_E_UNSUPPORTED_FORMAT means the function "succeeded but the
specified format is not supported in exclusive mode." This seems to
imply that the format is supported in shared mode, and that's what the
old code assumed, however try_format would incorrectly return success
with some drivers.

The remarks section of the documentation contradicts that assumption. It
says that in shared mode, if the audio engine does not support the
caller-specified format or any similar format, ppClosestMatch is set to
NULL and the function returns AUDCLNT_E_UNSUPPORTED_FORMAT. This is the
same as in exclusive mode, so treat AUDCLNT_E_UNSUPPORTED_FORMAT the
same regardless of opt_exclusive. In shared mode, the format selection
code will fall back to the mix format, which should always be supported.
2015-01-25 17:00:21 +09:00
xylosper a4b21b5a13 player: some fixes for property notification
`core-idle` depends on seeking state `mpctx->restart_complete`,
so make `core-idle` notified whenever `seeking` is notified, too.

`paused-for-cache` can be changed on MPV_EVENT_CACHE_UPDATE obviously.

Finally, `MPV_EVENT_PLAYBACK_RESTART` should be notified after
`mpctx->restart_complete` changed.

Conflicts:
	player/command.c
2015-01-25 17:00:21 +09:00
Vítor Galvão 799458a5b1 youtube-dl_mpv.sh: use curly braces and double quotes consistently 2015-01-25 17:00:21 +09:00
wm4 c774f2c3bb vo_opengl: improve terminal messages with lscale suboption errors
Make it more apparent what the hell the user did wrong.
2015-01-25 17:00:21 +09:00
wm4 9e6091a5b1 vo_opengl: guarantee correct reinitialization on setting options
At least the scale_sep_fbo could have been uninitialized or initialized
incorrectly when switching between scalers (e.g. from bilinear to
lanczos). Calling check_resize() should take care of this.
2015-01-25 17:00:21 +09:00
wm4 74c2146092 vo_vdpau: don't render to an output surface if it could be busy
There was a case when we could have rendered to an output surface while
it's still used for display. Not sure why the API doesn't do this
automatically.
2015-01-25 17:00:21 +09:00
wm4 7af2cb08e2 vo_direct3d: disable shaders if unavailable
Apparently, extremely crappy graphics drivers don't allow you to use
shaders. Simply disable use of shaders if this happens, and use the
"old" method instead.

One unexpectedly tricky thing is that you need a d3d_device to create
a shader, which in turn requires a window, so the initialization order
changes.

Conflicts:
	video/out/vo_direct3d.c
2015-01-25 17:00:21 +09:00
wm4 155ecb2c14 vo: never autoselect vo_null
Same deal as with commit d44b4ccb.

Conflicts:
	video/out/vo.c
2015-01-25 17:00:21 +09:00
wm4 2a2dc4d73c ao_alsa: reinitialize if device got broken
Apparently, physically disconnecting the audio device (consider USB
audio) breaks the ALSA device handle forever. It will signal ENODEV.
Fortunately, it's easy for us to handle this, and we can just use
existing mechanisms that will make the playback core close and reopen
the AO. Whether the immediate reopening will actually succeeds really is
ALSA's problem, though.
2015-01-25 17:00:21 +09:00
wm4 d855be5ec1 vf_scale: replace ancient fallback image format selection
If video output and VO don't support the same format, a conversion
filter needs to be insert. Since a VO can support multiple formats, and
the filter chain also can deal with multiple formats, you basically have
to pick from a huge matrix of possible conversions.

The old MPlayer code had a quite naive algorithm: it first checked
whether any conversion from the list of preferred conversions matched,
and if not, it was falling back on checking a hardcoded list of output
formats (more or less sorted by quality). This had some unintended side-
effects, like not using obvious "replacement" formats, selecting the
wrong colorspace, selecting a bit depth that is too high or too low, and
more.

Use avcodec_find_best_pix_fmt_of_list() provided by FFmpeg instead. This
function was made for this purpose, and should select the "best" format.

Libav provides a similar function, but with a different name - there is
a function with the same name in FFmpeg, but it has different semantics
(I'm not sure if Libav or FFmpeg fucked up here).

This also removes handling of VFCAP_CSP_SUPPORTED vs.
VFCAP_CSP_SUPPORTED_BY_HW, which has no meaning anymore, except possibly
for filter chains with multiple scale filters.

Fixes #1494.
2015-01-25 17:00:21 +09:00
wm4 141728832b stream: reject overly long URLs 2015-01-25 17:00:20 +09:00
wm4 a8b364cad3 stream_lavf: escape disallowed characters in http URLs
In my opinion, libavformat should be doing this. But a patch handling a
very safe case rejected, so I suppose we have to do it manually. (This
patch was only escaping spaces, which can never work because they break
the basic syntax of the HTTP protocol.)

This commit attempts to do 2 things:
- Try to guess whether libavformat will use the URL for http. This is
  not always trivial, because some protocols will recursively pass part
  of the user URL to http in some way.
- Try to fix invalid URLs. We fix only the simplest case: only
  characters that are never valid are escaped. This excludes invalid
  escape codes, which happen with freestanding '%' characters.

Fixes #1495.
2015-01-25 17:00:20 +09:00
wm4 c282cd679c vo_opengl: fix typo 2015-01-25 17:00:20 +09:00
wm4 1f815558b8 build: reduce worst case with mismatching FFmpeg pkg-config files
Handles mismatching libavfilter/libavdevice and libavcodec slightly
better.

libavfilter and libavdevice are optional, and thus are checked
separately and at a later point of the build. But if a user system has
at least 2 FFmpeg installations, and one of them lacks libavfilter or
libavdevice, the build script will pick up the libavfilter/libavdevice
package of the "other" FFmpeg installation. The moment waf picks these
up, all include paths will start pointing at the "wrong" FFmpeg, and the
FFmpeg API checks done earlier might be wrong too, leading to obscure
and hard to explain compilation failures.

Just moving the libavfilter/libavdevice checks before the FFmpeg API
checks somewhat deals with this issue. Certainly not a proper solution,
but since the change is harmless, and there is no proper solution, and
the change doesn't actually add anything new, why not.

Conflicts:
	wscript
2015-01-25 17:00:20 +09:00
wm4 a0de98871b README: extend dependecy list, link Windows compilation docs
Also, fix vo.rst, don't want to waste a separate commit on this.

Conflicts:
	DOCS/man/vo.rst
2015-01-25 17:00:20 +09:00
wm4 3711327430 audio: don't force any parameters if spdif is used
The existing code only ignored --audio-channels, but not --audio-rate or
--audio-format if spdif passthrough is used. Setting these makes no
sense.
2015-01-25 17:00:20 +09:00
wm4 7d523cd47a ao_coreaudio: reset possibly random errno value
In general, you need to check errno when using strtol(), but as far as I
know, strtol() won't reset errno on success. This has to be done
manually. The code could have failed sporadically if strtol() succeeded,
and errno was already set to one of the checked values.

(This strtol() still isn't fully error checked, but I don't know if it's
intentional, e.g. for parsing a numeric prefix only.)
2015-01-25 17:00:20 +09:00
wm4 224abe9e23 vo: restore framedrop
Fix inverted condition in commit 234d6329.
2015-01-25 17:00:20 +09:00
wm4 c6d54cccb5 player: mention mpv encoding support for transcoding in desync. warning 2015-01-25 17:00:20 +09:00
wm4 081368dabf video: fix waiting for last frame/format reconfig
We still need to send the VO a duration in these cases. Disabling
framedrop has logically absolutely nothing to do with these cases; it
was overlooked in commit 918b06c4.

So we always send the frame duration (or a guess for it), and check
whether framedropping is actually enabled in the VO code. (It would
be cleaner to send framedrop as a flag, but I don't care about that
right now.)
2015-01-25 17:00:20 +09:00
wm4 3b5b2144c3 demux: return EOF when reading from unselected stream
Normally the player doesn't read from unselected streams, so this should
be a no-op. But unfortunately, some broken files can severely confuse
the player, and assign the same demuxer stream to multiple front-end
tracks. Then selecting one of the tracks would deselect the other track,
with the end result that the demuxer stream for the selected track is
deselected. This could happen with mkv files that use the same track
number (which is of course broken). timeline_set_part() sets the tracks
using demuxer_stream_by_demuxer_id(), using the broken non-unique IDs.

The observable effect was that the player never quit, because
demux_read_packet_async() told the caller to wait some longer for new
packets. Fix by returning EOF instead.

Fixes #1481.
2015-01-25 17:00:20 +09:00
Stefano Pigozzi a80166f22a cocoa: don't set application icon in libmpv 2015-01-25 17:00:20 +09:00
wm4 126b7c4739 player: respect --untimed on last frame
The last video frame is another case that has a separate code path,
although it's pretty similar to the one in commit 73e5aa87. Fix this
in a different way, which also takes care of the last frame case,
although without context the code becomes slightly more tricky.

As further cleanup, move the decision about framedropping itself to
the same place, so the check in vo.c becomes much simpler. The check
for the vo->driver->encode flag, which is remvoed completely, was
redundant too.

Fixes #1480.
2015-01-25 17:00:20 +09:00
wm4 f8b842cdf3 manpage: fix osd-ass-cc examples
The "\\" escape was rendered as "\" on the website. I'm hoping quoting
this in ``...`` will render it correctly.

Also add an example for show_text, which awkwardly does not require
escaping the "\".
2015-01-25 17:00:19 +09:00
wm4 ad9ea546d3 player: respect --untimed on video format changes
If the video format changes (e.g. different frame size), a special code
path is entered to wait until the currently displayed frame is done.
Otherwise, the frame before the change would be destroyed by the
vo_reconfig() call.

This code path didn't respect --untimed; correct this.

Fixes #1475.
2015-01-25 17:00:19 +09:00
ChrisK2 c63d929261 OSC: Reset all styles for idle message 2015-01-25 17:00:19 +09:00
wm4 595c4cf872 osc: fix disabling OSC
Upon the "DEL" key binding or the "disable-osc" message, the OSC should
stay permanently invisible. This was recently broken (not sure by what),
because other code accidentally reenables it anyway, which resulted in
the OSC appearing again when moving the mouse.
2015-01-25 17:00:19 +09:00
wm4 4ebed102f0 ao_alsa: fix a small memory leak 2015-01-25 17:00:19 +09:00
wm4 371b52e377 player: fallback to seek time for percent-pos property
The percent-pos property normally goes by time, except for file formats
like .ts or .ogg, where you can't trust the timestamps and duration info
to compute the position in the overall files. These use the byte
position and size instead.

When the file position was unavailable (e.g. due to an ongoing seek),
the percent-pos was unknown. Change it to use the time position instead.
In most cases, it's actually accurate enough, and the temporary
unavailability of the property can be annoying, e.g. on the terminal
status line.
2015-01-25 17:00:19 +09:00
wm4 d8152b7003 wayland: implement key modifiers
Includes shift, ctrl, alt, meta.
2015-01-25 17:00:19 +09:00
wm4 6a9cdfa771 wayland: don't compute absurd window size
For some reason, schedule_resize() can be called with everything set to
0. The code couldn't handle wl->window.aspect set to 0, converting NaNs
to integers. Just work this around.

(I have no idea what I'm doing. This is probably a corner case caused
by my broken-ish wayland setup.)
2015-01-25 17:00:19 +09:00
wm4 399e746275 manpage: improve --title description
It might be rather surprising that --title also sets the audio stream
title (for PulseAudio and wasapi), so it certainly should be mentioned.
2015-01-25 17:00:19 +09:00
wm4 929c4a5529 client API: reasonable behavior if window is closed
Closing the video window sends CLOSE_WIN, which is normally mapped to
the "quit" command. The client API normally disables all key bindings,
and closing the window does nothing. It's simply left to the application
to handle this. This is fine - an embedded window can not be destroyed
by user interaction.

But sometimes, the window might be destroyed anyway, for example because
the containing window is destroyed. If this happens, CLOSE_WIN should
better not be ignored. We can't expect client API users to handle this
specially (by providing their own input.conf), so provide some fallback
for this pseudo key binding. The "quit" command might be too intrusive
(not every client necessarily handles "unexpected" MPV_EVENT_SHUTDOWN),
but I think it's still reasonable.
2015-01-25 17:00:19 +09:00
wm4 38903486fb player: enable demuxer thread for external audio files
Enable asynchronous reading for external files. This excludes subtitle
files (so it's effectively enabled for audio files only), because most
subtitle files are fully read on loading, and running a thread for them
would just cause slowdowns and increase resource usage, without having
any advantages.

In theory, an external file could provide multiple tracks from the same
demuxer, but demux_start_thread() is idempotent, so the code can be
kept simple.

Should help with playing DASH with ytdl_hook.
2015-01-25 17:00:19 +09:00
wm4 c56b237b3c ao_alsa: fix dtshd passthrough
We must not try to remap channels with this. Whethever ALSA gives us,
and whatever we do with it, the result will probably be nonsense.

Untested, as I don't have the required hardware.
2015-01-25 17:00:19 +09:00
wm4 63e401ccc9 tl_matroska: check chapter bounds
I have no idea what this does, but it's without doubt a sufficient fix
for the issue at hand.

Fixes #1445.

Conflicts:
	player/timeline/tl_matroska.c
2015-01-25 17:00:18 +09:00
wm4 35cc1830a3 ao_pulse: disable latency calculation hacks by default
This used to be required to workaround PulseAudio bugs. Even later, when
the bugs were (partially?) fixed in PulseAudio, I had the feeling the
hacks gave better behavior. On the other hand, I couldn't actually
reproduce any bad behavior without the hacks lately. On top of this, it
seems our hacks sometimes perform much worse than PulseAudio's native
implementation (see #1430).

So disable the hacks by default, but still leave the code and the option
in case it still helps somewhere. Also, being able to blame PulseAudio's
code by using its native API is much easier than trying to debug our own
(mplayer2-derived) hacks.
2015-01-25 17:00:18 +09:00
wm4 f4a9c9424f win32: request UTF-16 API variants, Vista+ APIs, and COM C macros
Put the Vista+ (_WIN32_WINNT) and the COM C (COBJMACROS) defines into
the build system, instead of defining them over and over in the code.

Conflicts:
	video/out/w32_common.c
	waftools/checks/custom.py
2015-01-25 17:00:18 +09:00
wm4 d9da0c9ce9 terminal: always print to stderr with --no-input-terminal
The function terminal_in_background() reports whether the player was
backgrounded. In this case, we don't want to annoy the user by still
printing the status to stderr. If no terminal interaction is assumed,
this mechanism is disabled, and stderr is always used. The read_terminal
variable signals this case.

Oddly, just redirecting stderr will disable output to stderr, because
the background check with tcgetpgrp() is done on stderr, but
read_terminal is still true (because that one depends on stdin and
stdout).

Explicitly disable this mechanism if --no-input-terminal is used by
setting read_terminal to true only if terminal input is actually
initialized.
2015-01-25 17:00:18 +09:00
Philip Sequeira 5f3f36a800 TOOLS/zsh.pl: complete options based on types
No more equals signs for options that don't take values.

Complete values for options with preset choices.

Complete --no-whatever where applicable.

Fixes #997.
2015-01-25 17:00:18 +09:00
wm4 12c316d95e player: use libavutil API to get number of CPUs
Our own code was introduced when FFmpeg didn't provide this API (or
maybe didn't even have a way to determine the CPU count). But now,
av_cpu_count() is available for all FFmpeg/Libav versions we support,
and there's no reason to have our own code.

libavutil's code seems to be slightly more sophisticated than our's, and
it's possible that the detected CPU count is different on some platforms
after this change.
2015-01-25 17:00:18 +09:00
wm4 cf997ff691 player: print used number of threads in verbose mode
Also, don't use av_log() for mpv output.
2015-01-25 17:00:18 +09:00
wm4 1560f74dde demux_mkv: avoid PTS warning with image attachments
Removes an annoying "No video PTS! Making something up." warning.

Mark it as keyframe, which is needed to prevent strange behavior with
PNG. Also, don't leak the picture data.
2015-01-25 17:00:18 +09:00
wm4 9b9d453748 manpage: clarify syntax for color options
Options which take colors accept two variants. The first is "r/g/b/a",
the second is "#AARRGGBB". Since they put alpha at different places,
it's probably better to document the second variant explicitly. (It's a
bit strange that they put alpha in different places, but on the other
hand, it's kind of natural. The second variant should probably be
considered deprecated.)
2015-01-25 17:00:18 +09:00
wm4 cb9b59858d TOOLS/lua/observe-all: add a warning
This is just natural, but it's also not that obvious.
2015-01-25 17:00:18 +09:00
wm4 3f85092ffe TOOLS/lua/autoload: fix behavior with network URLs
readdir() fails if the directory is an URL, so just exit instead of
letting the Lua script fail.
2015-01-25 17:00:18 +09:00
wm4 f4634e404f vf_vapoursynth: fix Lua backend
It couldn't handle the newly added float variable.
2015-01-25 17:00:18 +09:00
wm4 dbc00297cc vf_vapoursynth: pass through container FPS value
This is basically a hack; but apparently a needed one, since many
vapoursynth filters insist on having a FPS set.

We need to apply the FPS override before creating the filters. Also
change some terminal output related to the FPS value.
2015-01-25 17:00:18 +09:00
wm4 1e14d65db8 manpage: vf_vapoursynth: document mpv-specific predefined variables 2015-01-25 17:00:17 +09:00
wm4 57829ed95a x11: make display-names property observable
Reuse MP_EVENT_WIN_STATE for this.
2015-01-25 17:00:17 +09:00
wm4 2f69e8f93c video: better pipelining with vf_vapoursynth
Most of this is explained in the code comments. This change should
improve performance with vapoursynth, especially if concurrent requests
are used.

This should change nothing if vf_vapoursynth is not in the filter chain,
since non-threaded filters obviously can not asynchronously finish
filtering of frames.
2015-01-25 17:00:17 +09:00
wm4 6b063b3767 TOOLS/stats-conv: more improvements
Add an explicit "signal" event type, because the implicit one was
confusing.

Don't rescale the Y axis of the second graph, it was nonsense.

Make the legend for the second graph separate (and cleanup the code
creating the graphs).
2015-01-25 17:00:17 +09:00
Kevin Mitchell a5191c1479 ao/wasapi: style/code formatting tweaks 2015-01-25 17:00:17 +09:00
Kevin Mitchell cfc5a65494 ao/wasapi: use IsEqualGUID and IsEqualPropertyKey
before we were reinventing this wheel
2015-01-25 17:00:17 +09:00
Kevin Mitchell 307b8925ee ao/wasapi: set the ao with the waveformat channelmap
hopefully this fixes #1350
2015-01-25 17:00:17 +09:00
Kevin Mitchell 2090a35c57 ao/wasapi: improve exclusive mode format search
fixes #1376
2015-01-25 17:00:17 +09:00
Kevin Mitchell 2746910515 ao/wasapi: revamp set_waveformatex
* bits instead of bytes
* add valid_bits argument
* just pass in the mp_chmap and get the number and wavext channel map from that
* indicate valid bits in waveformat_to_str
* make appropriate accomodations in try_format
2015-01-25 17:00:17 +09:00
Kevin Mitchell 40dce4048f ao/wasapi: add CO_E_NOTINITIALIZED to explain_err
someone on irc reported seeing this error
2015-01-25 17:00:17 +09:00
ChrisK2 465bd38dc6 OSC: idlemessage: fix alignment 2015-01-25 17:00:17 +09:00
wm4 5992ff785c TOOLS/stas-conv: add timed value event type 2015-01-25 17:00:17 +09:00
wm4 b6d8694062 TOOLS/stats-conv: better output
Sort the legend by the used y value of binary events/signals, add a way
to filter branches (although that requires editing the script), and use
the full screen if the second subplot is not used.
2015-01-25 17:00:17 +09:00
ChrisK2 7b8ab2005e OSC: display "drop files here" message when idle + forced-window
This currently doesn't work properly on OSX due to some bugs.
2015-01-25 17:00:16 +09:00
wm4 4912fef753 build: rename MPLAYER_CONFDIR define
We certainly don't use the mplayer configuration dir. The name didn't
matter, but now that it's in user-visible output (as part of config.h
being dumped in verbose mode), it's a bit too strange.
2015-01-25 17:00:16 +09:00
wm4 c9c9919b2d player: print config.h contents in verbose mode
It was requested that mpv should print what features etc. have been
enabled at compile time. It can print the configure command line, but it
obviously doesn't include autodetected features.

I tried to think of a nicer way than dumping the config.h as text, but
this was still the simplest way.

Conflicts:
	player/main.c
2015-01-25 17:00:16 +09:00
wm4 b1c76e6a59 build: prefer libswresample over libavresample on FFmpeg
I hoped we could always use libavresample, but the FFmpeg project is
being too dickish to enable libavresample by default - which means we
need our libswresample-to-libavresample hack anyway.

Give up, and use the "supported" one of the duplicated libraries when
compiling against FFmpeg (relying on the fact that libswresample won't
be present if compiling against Libav).
2015-01-25 17:00:16 +09:00
wm4 58c91c02e2 DOCS/client_api_examples: don't throw char* in C++ code
C++ is the worst language ever, and allows throwing any type, even if it
doesn't make sense. In this case, we were throwing char*, which the
runtime typically treats as opaque, instead of printing it as message if
such an exception was not caught.

Conflicts:
	DOCS/client_api_examples/qml/mpvrenderer.cpp
2015-01-25 17:00:16 +09:00
wm4 5c1c8b5fab command: make the "run" command work on Windows too
Do so by using mp_subprocess(). Although this uses completely different
code on Unix too, you shouldn't notice a difference. A less ncie thing
is that this reserves an entire thread while the command is running
(which wastes some memory for stack, at least). But this is probably
still the simplest way, and the fork() trick is apparently not
implementable with posix_subprocess().

Conflicts:
	player/command.c
2015-01-25 17:00:16 +09:00
wm4 85f54ce5d0 subprocess: allow disabling redirection of stdout/stderr
If the stdout or stderr write callback is NULL, then don't redirect this
stream. Preparation for the next commit.

Not sure what to do on Windows; it seems STARTUPINFO doesn't allow
redirection only one of them. So just let them write nothing. For our
intended use-case (next commit), this is probably sensible.
2015-01-25 17:00:16 +09:00
wm4 c473688c5f gl_hwdec_vda: refuse to work with old OpenGL versions
The way we use rectangle textures (required by VDA for no reason) works
onl in OpenGL 3.0 or higher. Below that, the shader will fail to
compile. We could add support for older OpenGL versions, but that would
be a major pain.

This normally doesn't matter; mpv itself always creates OpenGL 3.2
contexts on OSX. But it could matter if a client API user uses
vo_opengl_cb, and gives it a 2.1 context (which OSX also allows you to
create).

Conflicts:
	video/out/gl_hwdec_vda.c
2015-01-25 17:00:16 +09:00
wm4 413bd22527 DOCS/client_api_examples: add an explicit license
IANAL, but we don't give a shit what you do with this code.
2015-01-25 17:00:16 +09:00
wm4 ccf8c42a42 ao_alsa: print channel map if setting it fails
This message is printed when the audio device advertised a channel map,
but couldn't set it - which is probably a dmix bug (we'll never know,
ALSA doesn't take bug reports).

Print the requested map, so that the user (maybe) can make a connection
when seeing the message and the actually used channel map, which might
be less confusing. Or at least less useless.
2015-01-25 17:00:16 +09:00
Stefano Pigozzi 4759f61e73 build: fix linking with --enable-static-build 2015-01-25 17:00:16 +09:00
Stefano Pigozzi 1a346ce11b ao: add debug log with the detected channel maps
This could be helpful with bug reports.
2015-01-25 17:00:16 +09:00
Stefano Pigozzi 891a9727c6 chmap_sel: add multichannel fallback heuristic
Instead of just failing during channel map selection, try to select a close
layout that makes most sense and upmix/downmix to that instead of failing AO
initialization. The heuristic is rather simple, and uses the following steps:

1) If mono is required always prefer stereo to a multichannel upmix.
2) Search for an upmix that is an exact superset of the required channel map.
3) Search for a downmix that is the exact subset of the required channel map.
4) Search for either an upmix or downmix that is the closest (minimum difference
   of channels) to the required channel map.
2015-01-25 17:00:15 +09:00
Stefano Pigozzi 2e133e9dde chmap: add a 7.1(rear) layout name
This is common on Apple systems so it's handy to have a label for it.
2015-01-25 17:00:15 +09:00
James Ross-Gowan f5c19f29af w32_common: allow window resizing with --no-border
Conflicts:
	video/out/w32_common.c
2015-01-25 17:00:15 +09:00
Niklas Haas ca048e8d6d vo_opengl: Ignore approx-gamma for BT.2020-CL
The details of the non-linear transformation from/to BT.2020's constant
luminance system don't really make sense with any other gamma curve,
since changing the gamma curve completely breaks the chroma channels.
2015-01-25 17:00:15 +09:00
wm4 a2f4391ca1 stream_pvr: sort channel list by --tv-channels order
Apparently this is what users would expect.

Going the way of least resistance (in terms of messing with this old,
rarely used code), sorting them by some kind of addition timestamp
(called priority in the patch) is the easiest.

Fixes #1390.

Conflicts:
	stream/stream_pvr.c
2015-01-25 17:00:15 +09:00
Stefano Pigozzi 4971ad9e17 cocoa: move the gl function loader to gl_cocoa.c 2015-01-25 17:00:15 +09:00
wm4 3d94938246 command: stip path from playlist OSD display
But only if it's not e.g. a http URL.

Fixes #1388.
2015-01-25 17:00:15 +09:00
Stefano Pigozzi d627c579ea ao_coreaudio: remove useless guard
useless after 069016fd6c
2015-01-25 17:00:15 +09:00
Stefano Pigozzi 752dd2ef76 ao_coreaudio: fix some naming conventions 2015-01-25 17:00:15 +09:00
Stefano Pigozzi 562e42f826 ao_coreaudio: fix channel mapping
There where 3 major errors in the previous code:

1) The kAudioDevicePropertyPreferredChannelLayout selector returns a single
   layout not an array.
2) The check for AudioChannelLayout allocation size was wrong (didn't account
   for variable sized struct).
3) Didn't query the kAudioDevicePropertyPreferredChannelsForStereo selector
   since I didn't know about it's existence.

All of these are fixed.

Might help with #1367
2015-01-25 17:00:15 +09:00
Stefano Pigozzi ad3d8ce5c6 ao_coreaudio: fix typo 2015-01-25 17:00:15 +09:00
Stefano Pigozzi 777bffd307 ao_coreaudio: move some code to make output readable 2015-01-25 17:00:15 +09:00
Stefano Pigozzi 7f839323cb ao_coreaudio: add more layout debug outputs
Should help remote debugging #1367 with --msg-level=ao=debug
2015-01-25 17:00:15 +09:00
Stefano Pigozzi cb4b28e336 ao_coreaudio: fix AudioChannelLayout allocations
AudioChannelLayout uses a trailing variable sized array so we need to
query CoreAudio for the size of the struct it is going to need (or the
conversion of that particular layout would fail).

Fixes #1366
2015-01-25 17:00:14 +09:00
Stefano Pigozzi 35be53108e ao_coreaudio: fix mono/stereo channel mapping
Needed after af3bbb800d since now we use channel mapping all the time.

Fixes #1357
2015-01-25 17:00:14 +09:00
wm4 9f4d44f8c7 stream_pvr: increase timeout, slightly better error reporting
An attempt to find out what's wrong with issue #1382.

I don't even know why a timeout would be needed; for robustness with
broken devices maybe?
2015-01-25 17:00:14 +09:00
wm4 d60940a080 stream: always disable cache for pseudo-streams
Streams which don't have a full_buffer function never return any actual
data. Slight improvement over commit 5640c195.
2015-01-25 17:00:14 +09:00
wm4 dab7602a89 client API: document requirement to block SIGPIPE
I noticed that the IPC code does not use MSG_NOSIGNAL or SO_NOSIGPIPE.
The former is "only" POSIX 2008 and also requires switching to sendto(),
while the latter is even less portable.

Not going to bother with this obsolete 80ies crap, just block SIGPIPE,
and instruct client API users to do the same.
2015-01-25 17:00:14 +09:00
wm4 37210f0cbc ipc: report some user errors better
Using the IPC with a program, it's not often obvious that a newline must
be sent to terminate a command. Print a warning if the connection is
closed while there is still uninterpreted data in the buffer.

Print the OS reported error if reading/writing the socket fails. Print
an erro if JSON parsing fails.

I considered silencing write errors if the write end is closed (EPIPE),
because a client might send a bunch of commands, and then close the
socket without wanting to read the reply. But then, mpv disconnects
without reading further commands that might still be buffered, so it's
probably a good idea to always print the error.
2015-01-25 17:00:14 +09:00
wm4 f24b8cbd47 Do not call strerror()
...because everything is terrible.

strerror() is not documented as having to be thread-safe by POSIX and
C11. (Which is pretty much bullshit, because both mandate threads and
some form of thread-local storage - so there's no excuse why
implementation couldn't implement this in a thread-safe way. Especially
with C11 this is ridiculous, because there is no way to use threads and
convert error numbers to strings at the same time!)

Since we heavily use threads now, we should avoid unsafe functions like
strerror().

strerror_r() is in POSIX, but GNU/glibc deliberately fucks it up and
gives the function different semantics than the POSIX one. It's a bit of
work to convince this piece of shit to expose the POSIX standard
function, and not the messed up GNU one.

strerror_l() is also in POSIX, but only since the 2008 standard, and
thus is not widespread.

The solution is using avlibc (libavutil, by its official name), which
handles the unportable details for us, mostly. We avoid some pain.
2015-01-25 17:00:14 +09:00
wm4 b706198207 stream_edl: disable caching
stream_edl merely makes demux_edl act "special", which checks for the
stream type explicitly and then does something with its URL. If a cache
is added before the stream, it'll try to use the cache's URL (i.e. an
empty string), and will then obviously fail to parse the URL. While this
is slightly stupid, just disabling the entirely useless cache is the
most effective solution.

Fixes #1378.
2015-01-25 17:00:14 +09:00
wm4 e213e20c2d demux_mkv: use attachment filename as coverart title 2015-01-25 17:00:14 +09:00
wm4 2ade5c8477 demux_mf: add probing by mime type
Reuses the mime type table introduced in the previous commit.
2015-01-25 17:00:14 +09:00
wm4 15a191f920 demux_mkv: support embedded coverart
The code could as well be in demux.c, but it's better to avoid
accidental clashes with demux_lavf.c.

FFmpeg provides no way yet to map a mime type to a codec, so do it
manually. (It _can_ map a mime type to an "input format", but not a
codec.)

Fixes #1374.
2015-01-25 17:00:14 +09:00
wm4 899822c001 old-configure: fix disabling VAAPI GLX with --disable-gl
waf handles this correctly.
2015-01-25 17:00:14 +09:00
wm4 a9db58cc3b vo_opengl_old: fix OSD regression
Commit 0e8fbdbd removed the rg_texture requirement from vo_opengl;
commit 541f6731 changed to a more convenient method. Both commits broke
vo_opengl_old in some ways. vo_opengl_old always requires GL_ALPHA for
single-channel texture, because it draws the OSD without shaders and by
using certain blend modes.

So we need to explicitly distinguish between vo_opengl and vo_opengl_old
in the OSD renderer, and force fixed texture formats for vo_opengl_old.
The other logic is specific to the internals of vo_opengl. (Although it
might be possible to get the same result by playing with the old GL
fixed-function functions in vo_opengl_old. But seems like a waste of
time.)

Fixes #1370.

Conflicts:
	video/out/gl_osd.c
2015-01-25 17:00:14 +09:00
wm4 21c4c50ac6 vo_opengl: remove pointless glFinish calls 2015-01-25 17:00:13 +09:00
wm4 86493730b2 command: ignore chapter cycling if there's only 1 chapter
If there's only 1 chapter, the seeking by chapter (using the chapter
property) will either jump to the chapter point, or quit playback. This
is as designed, but seems like a useless and annoying behavior.

Conflicts:
	player/command.c
2015-01-25 17:00:13 +09:00
wm4 87e635e843 x11: fix mysterious issue with window embedding on OpenBox
For some reason, when using window embedding, and the window manager is
OpenBox, calling XSetWMNormalHints() before the window is mapped, the
initial window position will be off. It leaves some vertical space,
instead of placing it on the top/left corner. Suspiciously, the vertical
space is as much as a the height of normal window decoration.

I don't know what kind of issue this is. Possibly an OpenBox bug, but
then this happens even if the override-redirect flag is set. (This flag
basically tells the X server to ignore the window manager. Normally we
don't set it.) On other window managers, it works fine. So I don't know
why this is happening.

But this is easy to workaround. XSetWMNormalHints() isn't needed at all
if embedding.

Should fix #1235.
2015-01-25 17:00:13 +09:00
wm4 b0759abe9c x11: copy WinID option
For the purpose of making "--wid" setable at any time.
2015-01-25 17:00:13 +09:00
wm4 ebd4bf9792 player: fix random crashes on uninitialization
On uninitialization, the player will unselect all subtitles, and then
destroy the subtitle decoder. But it didn't correctly remove the
subtitle decoder from the OSD state, so it could happen that it would
access it after the decoder was destroyed.

Could lead to random crashes when switching files often.

Fixes #1389.
2015-01-25 17:00:13 +09:00
wm4 ab4c857ff6 player: cosmetics: rename a function
Something which has this many important sideffects shouldn't start have
a "get" prefix.

Conflicts:
	player/sub.c
2015-01-25 17:00:13 +09:00
wm4 e3f4491a24 demux_mkv: enable codec parsing for vfw-muxed files too
For some codecs, we need to invoke a codec parser (because libavcodec
will run into trouble otherwise). This was done based on the Matroska
codec field.

But this ignores handling of vfw-muxed files, which use a pseudo-codec
to signal presence of vfw structures, which we must unmangle to get the
real codec. Handle this by rearranging the code.

This fixes at least mp3-in-mkv for vfw-muxed files; typically old files.

Conflicts:
	demux/demux_mkv.c
2015-01-25 17:00:13 +09:00
wm4 12ee2e64be client API: fix mpv_wakeup()
Of course this was going to get stuck in the retry loop.

Fixes #1372.
2015-01-25 17:00:13 +09:00
wm4 ead565afb3 client API: be more lenient about mpv_suspend/resume mismatches
Before this commit, this was defined to trigger undefined behavior. This
was nice because it required less code; but on the other hand, Lua as
well as IPC support had to check these things manually. Do it directly
in the API to avoid code duplication, and to make the API more robust.
(The total code size still grows, though...)

Since all of the failure cases were originally meant to ruin things
forever, there is no way to return error codes. So just print the
errors.
2015-01-25 17:00:13 +09:00
wm4 9f80936ddc player: update chapter marks when switching files
This wasn't done, so the chapter marks of the previous file were
visible. It's harmless, but looks unpolished, so fix it.

Conflicts:
	player/command.c
2015-01-25 17:00:13 +09:00
wm4 6a8dad4c3a manpage: update the ao_alsa example (no-block was removed)
Also, don't use the "hw" device, but "plughw". ALSA docs say never to
use "hw".
2015-01-25 17:00:13 +09:00
wm4 3d710630a4 vo_opengl: don't use 10 bit video on Intel GPUs
vo_opengl was originally written against OpenGL 3 core, and it seems
GPUs/drivers supporting this are mostly sane. Later, it was made to work
with OpenGL 2.1 too. Lately we removed the requirement for RG textures,
and look, someone reported a problem with "lesser" Intel GPUs.

This commit does the same in vo_opengl what was added to vo_opengl_old a
long time ago.

Fixes #1383.

Conflicts:
	video/out/gl_common.c
	video/out/gl_video.c
2015-01-25 17:00:13 +09:00
wm4 f83e28d893 vo_opengl: remove requirement for RG textures
Features not supported are disabled (although with a misleading error
message).

Conflicts:
	video/out/gl_video.c
	video/out/vo_opengl.c
	video/out/vo_opengl_cb.c
2015-01-25 17:00:12 +09:00
wm4 3f66dd82b1 vo_opengl: never use 1D textures for lookup textures
This was a microoptimization for small filters which need 4 or less
weights per sample point. When I originally wrote this code, using a 1D
texture seemed to give a slight speed gain, but now I couldn't measure
any difference.

Remove this to simplify the code.
2015-01-25 17:00:12 +09:00
wm4 c4c95f0dde vo_opengl: refactor: merge convolution function and sampler entrypoint
There's not much of a reason to have the actual convolution code in a
separate function. Merging them actually simplifies the code a bit, and
gets rid of the repetitious macro invocations to define the functions
for each filter size.

There should be no changes in behavior or output.
2015-01-25 17:00:12 +09:00
wm4 60c6307a97 vo_opengl: extend filter size to 64
For better downscaling.

Maybe the list of filter sizes shouldn't be static...
2015-01-25 17:00:12 +09:00
wm4 99f237beb9 vo_opengl: extend filter size to 32
Also replace the weights calculations for 8/12/16 with the generic
weight function definition macro. (The weights 2/4/6 follow slightly
different rules.)
2015-01-25 17:00:12 +09:00
wm4 6cf54d6bc0 gl_video: minor simplification
Not needed anymore.
2015-01-25 17:00:12 +09:00
Kevin Mitchell c6ce2c6b43 ao/wasapi: make set_ao_format EX/EXTENSIBLE agnostic
There is no guarantee that closestMatch returned by IsFormatSupported
is actually a WAVEFORMATEXTENSIBLE.

http://msdn.microsoft.com/en-us/library/windows/desktop/dd370876%28v=vs.85%29.aspx

We should therefore not blindly treat it as such.
2015-01-25 17:00:12 +09:00
Kevin Mitchell 7f92344138 ao/wasapi: fix set_ao_format
Before it used whatever was in ao->format and changed the bits even
though this might have nothing to do with the actual WAVEFORMAT
negotiated with WASAPI.

For example, if the initial ao->format was a float and we had set the
WAVEFORMAT to s24, this would create a non-existent float24 format.
Worse, it might put an u16 into ao->format when WAVEFORMAT described s16.
WASAPI doesn't support unsigned at all as far as I can tell.
2015-01-25 17:00:12 +09:00
Kevin Mitchell d56a1112d2 ao/wasapi: show actual waveformat tried
also remove bogus ao_format
2015-01-25 17:00:12 +09:00
Kevin Mitchell b1292c044a ao/wasapi: don't assume 32-bits == float
This was based on old WAVEFORMATEX restrictions
http://msdn.microsoft.com/en-us/library/windows/hardware/ff538799%28v=vs.85%29.aspx

With the new WAVEFORMATEXTENSIBLE, this is no longer a problem. and we
can have s32 or float32 so we need to actually check / set these correctly.

fixes #1287
2015-01-25 17:00:12 +09:00
Kevin Mitchell 18fb4bff6f ao/format: add af_fmt_is_float 2015-01-25 17:00:12 +09:00
Kevin Mitchell 7e60fb2c97 ao/wasapi: make sure that < 16-bit pcm never happens
it just sucks. noone should have to listen to that.
2015-01-25 17:00:12 +09:00
Kevin Mitchell 8c8af64820 ao/wasapi: get rid of WAVEFMT union
It only confused the issue. Replace it's functionality with
waveformat_copy function where needed.
2015-01-25 17:00:12 +09:00
wm4 3ee6992656 manpage: rename input.conf supersection
By now, input.conf is actually just a small part of input handling.
Rename the section to something else ("command interface" was the
first reasonable thing that came to mind).

Also fix a minor typo further down.
2015-01-25 17:00:12 +09:00
wm4 9a7f332571 manpage: lua: fix example
Oops.
2015-01-25 17:00:11 +09:00
wm4 fadf87f2a7 manpage: minor fixes
Also update the Lua example. The "pause" event was declared deprecated,
so the example should use the newer API.
2015-01-25 17:00:11 +09:00
wm4 62c7a372ec player: don't ignore relative seeks by 0 seconds
I don't know why this done; most likely it had no real reason.

Remove it because it breaks "refresh seeks" to the same position.
(Although the refresh seeks mpv sometimes does were fine.)
2015-01-25 17:00:11 +09:00
wm4 0692da3007 command: avoid returning invalid FPS values
It's possible that fps is sometimes 0 in case it's unset.
2015-01-25 17:00:11 +09:00
wm4 0fcb96139d player: reset frame step counter on seeks
I suppose this wasn't done in order to keep the frame step counter
active even in the next file, but actually it was reset anyway.
2015-01-25 17:00:11 +09:00
wm4 5f6b5feac6 lua: add a function that formats Lua values as strings
Yep, Lua is so crappy that the stdlib doesn't provide anything like
this.

Repurposes the undocumented mp.format_table() function and moves it to
mp.utils.
2015-01-25 17:00:11 +09:00
wm4 e2a4fde6de build: don't install some files if only libmpv is built
encoding-profiles.conf can be used by libmpv, but it won't load it
from this path by default, so exclude it as well.

Fixes #1293.
2015-01-25 17:00:11 +09:00
Kevin Mitchell 01401b5f5b ao/wasapi: handle VistaBlob failure more gracefully 2015-01-25 17:00:11 +09:00
Kevin Mitchell f794eca255 ao/wasapi: remove unnecessary check of audio thread input
it would have caused a deadlock if it fired anyway.
2015-01-25 17:00:11 +09:00
Kevin Mitchell ad2a461a09 ao/wasapi: more consistent/reliable method of computing extra WAVEFORMATEXTENSIBLE size 2015-01-25 17:00:11 +09:00
Kevin Mitchell 2e931b34f4 ao/wasapi: more missed cleanup on failure 2015-01-25 17:00:11 +09:00
Kevin Mitchell ade8b6214d ao/wasapi: check return values
Only issue a warning for failure of wasapi_enumerate_devices and
wasapi_fill_VistaBlob.
2015-01-25 17:00:11 +09:00
Kevin Mitchell 78e957a2bd ao/wasapi: make functions return bool that were acting like it
this involved inverting the logic of find_formats, enumerate_devies
and wasapi_fill_VistaBlob. The latter two were trivial as their return
values were not actually checked (to be fixed in a later
commit).
2015-01-25 17:00:11 +09:00
Kevin Mitchell 9eb75631c7 ao/wasapi: check full GUID of KSDATAFORMAT to determine float 2015-01-25 17:00:11 +09:00
Kevin Mitchell 0c8a419775 ao/wasapi: expose GUID and PKEY convenience functions
Give them the prefix mp_ and make them nonstatic.
2015-01-25 17:00:10 +09:00
Kevin Mitchell 885c6e5b07 ao/wasapi: remove unused variable 2015-01-25 17:00:10 +09:00
Kevin Mitchell 44a7649e79 ao/wasapi: safely define PKEY constants
Before these definitions were incorrectly guarded by and #ifdef
but since they aren't macros, this would never be true so that
if they were ever added to mingw headers we would have problems.

rename KSDATAFORMAT constants with the same mp prefix for consistency.
also use DEFINE_GUID rather than defining the bare structure
2015-01-25 17:00:10 +09:00
Kevin Mitchell fded4de8a7 ao/wasapi: avoid redundant passing of ao and wasapi_state as arguments
also drop some useless const declaraitons
2015-01-25 17:00:10 +09:00
Kevin Mitchell f20575f3dd ao/wasapi: just return 0 unconditionally from the thread
We weren't actually checking this value anyway. We only really
cared about init failure, which was checked another way.
2015-01-25 17:00:10 +09:00
Kevin Mitchell 1936cfdb09 ao/wasapi: fix unmatched CoUninitialize() on init failure 2015-01-25 17:00:10 +09:00
Kevin Mitchell 84c2dc882d ao/wasapi: more debugging messages 2015-01-25 17:00:10 +09:00
Kevin Mitchell d72718ca65 ao/wasapi: simplify the init retry 2015-01-25 17:00:10 +09:00
Kevin Mitchell 8a73b02952 ao/wasapi: make get_device_delay return an error code 2015-01-25 17:00:10 +09:00
wm4 b4174ab4e9 ao_wasapi: don't treat SetDisplayName() failure as fatal
Same for SetIconPath().
2015-01-25 17:00:10 +09:00
wm4 ce5f0f1a7a demux_playlist: detect headerless m3u files by extension
m3u files are normally just text files with a list of filenames. Nothing
actually mandates that there is a header. Until now, we've rejected such
files, because there's absolutely no way to detect them.

If nothing else claims the file, the extension is ".m3u", and if the
contents of the file look like text, then load it as m3u playlist. The
text heuristic is pretty cheap, but at least it should prevent trying
to load binary data as playlist. (Which would "work", but result in a
catastrophic user experience.)

Due to the text heuristic, UTF-16/32 files will be rejected (unless they
have a header), but I don't care.
2015-01-25 17:00:10 +09:00
ChrisK2 e02146b0c0 ytdl_hook: Check for empty playlists
Sometimes we get empty playlists back, print a warning message
instead of crash
2015-01-25 17:00:10 +09:00
James Ross-Gowan 804468b1ac subprocess-win: Always quote argv[0]
If the program name isn't quoted and the .exe it refers to isn't found,
CreateProcess will add the program arguments to the program name and
continue searching, so for "program arg1 arg2", CreateProcess would try
"program.exe", "program arg1.exe", then "program arg1 arg2.exe". This
behaviour is weird and not really desirable, so prevent it by always
quoting the program name.

When quoting argv[0], escape sequences shouldn't be used. msvcrt, .NET
and CommandLineToArgvW all treat argv[0] literally and end it on the
trailing quote, without processing escape sequences.
2015-01-25 17:00:10 +09:00
wm4 d8455f34c1 player: respect --untimed on last frame
The last video frame is another case that has a separate code path,
although it's pretty similar to the one in commit 73e5aa87. Fix this
in a different way, which also takes care of the last frame case,
although without context the code becomes slightly more tricky.

As further cleanup, move the decision about framedropping itself to
the same place, so the check in vo.c becomes much simpler. The check
for the vo->driver->encode flag, which is remvoed completely, was
redundant too.

Fixes #1480.
2015-01-25 17:00:10 +09:00
wm4 b0877e5454 player: respect --untimed on video format changes
If the video format changes (e.g. different frame size), a special code
path is entered to wait until the currently displayed frame is done.
Otherwise, the frame before the change would be destroyed by the
vo_reconfig() call.

This code path didn't respect --untimed; correct this.

Fixes #1475.
2015-01-25 17:00:09 +09:00
wm4 c926927c59 player: don't fall asleep on audio decoding errors
This makes it retry later.

Fixes #1474.
2015-01-25 17:00:09 +09:00
wm4 9af1ff9817 player: fix --stop-playback-on-init-failure on audio init failure
This was forgotten when the option was implemented, and makes this
option work as advertised.

Fixes #1473 (though the default behavior is probably still stupid).
2015-01-25 17:00:09 +09:00
wm4 ca34fd569d image_writer: check for conversion errors
This can happen when e.g. a VO returns a screenshot in an unsupported
format.
2015-01-25 17:00:09 +09:00
Oliver Freyermuth 4bb2da8204 cache: cache-position needs to be int64_t
Both max_filepos and offset are int64_t,
so pos can overflow, e.g. causing endless loops
in stream implementation.
2015-01-25 17:00:09 +09:00
wm4 6d7eb8346a x11: always use glXGetProcAddressARB()
glXGetProcAddress() is outdated, and as far as I know doesn't give all
the guarantees the "new" ARB function gives.

Probably doesn't matter too much, because until now it always appeared
to work. On the other hand, since this function is (bogusly) used only
on the gl3 code path, it could have happened that users hit this, and
just reverted to vo_opengl_old instead.
2015-01-25 17:00:09 +09:00
Soeren D. Schulze cd99121185 video/out/win_state.c: fix --monitoraspect 2015-01-25 17:00:09 +09:00
wm4 392159337f manpage: document details of option quoting and escaping
Makes me realize what a mess this is. I hope it can be simplified in the
far future, preferably by killing the suboption parser completely.
2015-01-25 17:00:09 +09:00
wm4 fffd7e7a3a x11: make display-names property stricter
Returning the property before the window is mapped could lead to
confusing behavior, and in particular strange differences between
vo_vdpau and vo_opengl. (vo_opengl creates the window right at the
start, while vdpau waits until the first reconfigure event.) It might
even be possible that for vo_opengl random results were returned,
because the hidden window can have different placement than the actual,
final one on initial video reconfig.

Fix this by returning the property only if the window is considered
mapped. command.c handles this case specifically, and makes the property
unavailable, instead of returning an empty list.
2015-01-25 17:00:09 +09:00
wm4 59781d3cb3 af_volume: dump applied replaygain in verbose mode 2015-01-25 17:00:09 +09:00
Ryan Jacobs 4a6cb85993 manpage: talk about one script, not multiple scripts
There aren't multiple scripts. We're just talking about one: youtube-dl.

Signed-off-by: wm4 <wm4@nowhere>
2015-01-25 17:00:09 +09:00
wm4 74a3d29f21 command: make empty vf-metadata not an error
If a filter exists, but has no metadata, just return success. This
allows the user to distinguish between no metadata available, and filter
not inserted.

See #1408.
2015-01-25 17:00:09 +09:00
wm4 07acf5cb9a vf: fix crash if filter doesn't provide control entrypoint
This input command crashed:

    vf add @mf:format=yuv420p ; show_text "${vf-metadata/mf}"

Fixes #1408.
2015-01-25 17:00:09 +09:00
Stefano Pigozzi 8e1e814166 options: expand ~ for the include option
Fixes #1406
2015-01-25 17:00:09 +09:00
wm4 da21670880 player: make --shuffle/--merge-files affect runtime loaded playlists
Until now, these options took effect only at program start. This could
be confusing when e.g. doing "mpv list.m3u --shuffle". Make them always
take effect when a playlist is loaded either via a playlist file, or
with the "loadlist" command.
2015-01-25 17:00:08 +09:00
wm4 2cac69f221 sub: reset sub decoder correctly when cycling subtitles
reset_subtitles() works in mpctx->d_sub[], which is set to NULL before
calling it from uninit_sub(). This fixes resetting the subtitle when
cycling subtitle tracks.

Actually, this was probably a feature, because it's annoying if
subtitles don't show up when cycling them. But it also can have
unintended consequences, so get rid of it.
2015-01-25 17:00:08 +09:00
wm4 99cca56a99 vo_opengl, x11: make legacy context warning not an error
Really, this doesn't actually matter. It's printed as error only because
it was once thought to be an mostly unneeded fallback, but it turned out
this is still frequently needed, and users are getting confused.
2015-01-25 17:00:08 +09:00
wm4 676457256a vo_opengl: fix broken rejection of extended scaling
Possibly explains why some users got mysterious FBO errors on crappy
hardware.
2015-01-25 17:00:08 +09:00
wm4 7368d8f353 lua: don't ignore key press events for script key bindings
Meh.
2015-01-25 17:00:08 +09:00
wm4 2c9e0c0a29 m_option: never return NOPTS value as number from properties
MP_NOPTS_VALUE (basically INT64_MIN) is basically an special timestamp
value that means "unset" or "unknown". Its exact value is internal, and
should never be returned or interpreted by any API.

So return "no" instead (what is also what the parser accepts).
2015-01-25 17:00:08 +09:00
wm4 8d3259a391 client API: fix log buffer overflow case
It just crashed. The prefix and text fields point to static strings in
this case. Oops.

Fixes the issue mentioned in #838.
2015-01-25 17:00:08 +09:00
wm4 faf561c4ec mp_image: reject invalid display aspect ratio
Having any of these set to 0 makes no sense.

I think some code might still be using 0/0 aspect ratio to signal unset
aspect ratio, but I didn't find it. If there is still code like this, it
should be fixed instead.

Fixes #1467.
2015-01-25 17:00:08 +09:00
wm4 8c54b56f43 player: fix crash wtih --secondary-sid
Fises #1463.
2015-01-25 17:00:08 +09:00
wm4 a44ffb729b bstr: fix possible undefined behavior with length 0 strings
BSTR_P() passes the string length and start pointer to printf-like
functions. If the lenfth is 0, the pointer can be NULL, but we're
actually still not allowed to pass a NULL pointer in any case.

This is mostly a technically, because nobody in their right mind would
attempt to specifically break such cases. But it's still undefined
behavior, and some libcs might be strict about this.
2015-01-25 17:00:08 +09:00
wm4 947a791992 player: don't set tag strings to NULL
bstr is a bounded string type, consisting of a pointer and a length
value. If the length is 0, the pointer can be NULL. This is somewhat
logical due to how this abstraction works, but it can leak when
converting to C strings.

talloc_strndup() returns NULL instead of "" in this case, which broke
some other code. Use bstrto0() instead, which is the "proper" function
to convert bstr to char*.

Fixes #1462.
2015-01-25 17:00:08 +09:00
wm4 42f82c5ccf demux_mkv: fix EBML parsing checks
Reading IDs must be checked too. This was basically forgotten in commit
f3a978cd. Also set the *length parameter for ebml_parse_length() in some
error cases, which _really_ should happen.

Fixes #1461.
2015-01-25 17:00:08 +09:00
wm4 1a3a52ea25 tl_matroska: fix minor memory leak in error case
Going the way of least resistance. Fixes #1460.
2015-01-25 17:00:08 +09:00
wm4 77d5aa56b3 player: check sufficient track selection before destroying VO
mpv needs at least an audio or video track to play something. If the
track selection is basically insufficient, the player will immediately
skip to the next file (or quit).

One slightly annoying thing might be that trying to play a subtitle file
will close the VO window, and then go to the next file immediately (so
"mpv 1.mkv 2.srt 3.mkv" would flash the video window when 2.srt is
skipped). Move the check to before the video window is possibly closed.

This is a minor cosmetic issue; one can use --force-window to avoid
closing the video window at all.

Fixes #1459.
2015-01-25 17:00:08 +09:00
wm4 1f795b6172 demux_mkv: better check for some EBML parsing
Apparently, originally this code was meant to be able to read past the
buffer somewhat, which is why the buffer allocation was padded by 8
byte. This is unclean and confuses valgrind. This probably could have
crashed with certain invalid files too.

Also revert the change added with 10a2f69; it should be not needed
anymore.
2015-01-25 17:00:08 +09:00
wm4 22a06b0e81 demux_mkv: avoid endless loop with broken files
Fixes #1457.
2015-01-25 17:00:07 +09:00
wm4 f551da2cfb ao_pulse: exit AO if stream fails
This can for example reproduced by killing the pulseaudio server. If
this happens, just try to reload the AO, instead of breaking everything
forever.
2015-01-25 17:00:07 +09:00
wm4 ac06ba0aec demux_mkv: improve robustness against broken libavcodec parsers
The VP9 codec parser has a bug: it doesn't set the data/size pointers
passed to it. As I understand, it must always do this, and in fact, if
it doesn't some libavcodec generic code would be in trouble too.

This helps with #1448, but is not the full fix for it. The codec parser
must be fixed in libavcodec itself.
2015-01-25 17:00:07 +09:00
wm4 99dc774dbb ao: remove coreaudio_exclusive from autoprobing list
Apparently this was a mistake.
2015-01-25 17:00:07 +09:00
xylosper 83d44a4f6a gl_video.c: invalidate image_params in uninit_video()
When the given mp_image_params does not match with that of gl_video,
gl_video_config() always calls uninit_video() but calls init_video()
only if valid format is given.

Since uninit_video() does not change image_params of gl_video,
when the same params as the previous one is given to gl_video_config()
after gl_video is unitialized with invalid format, gl_video_config()
never calls init_video().

To prevent this, invalidate image_params of gl_video in uninit_video().
2015-01-25 17:00:07 +09:00
James Ross-Gowan 6609f68691 dxva2: copy mp_image attributes to sw_img 2015-01-25 17:00:07 +09:00
James Ross-Gowan e2beeffb92 mp_image: copy missing attributes
This adds a couple of missing fields to mp_image_copy_attributes so
things like rotation metadata work.
2015-01-25 17:00:07 +09:00
wm4 704c24776a video: fix timeline with some container formats
Using edl or --merge-files with .avi files didn't work, because the DTS
was not offset. Only the PTS was adjusted, which led to nonsense
timestamps.
2015-01-25 17:00:07 +09:00
wm4 cc32aa436e sd_lavc: apply fallback to video resolution only for vobsubs
Commit 87c13de6 added a fallback to video resolution if the subtitle
resolution is unknown. Apparently this fixed some broken files with
vobsubs.

This broke some DVB subtitles. Apparently .ts captures with 1920x1080
video resolution and 720x576 subtitles do exist. The sample at hand had
some streams with 720x576 resolution and no sub resolution set, and some
streams with 1920x1080 resolution and sub resolution set (both against
the same 1920x1080 video). My conclusion is that 720x576 is the only
reasonable fallback for DVB (but I can't be sure).

The fallback is removed for PGS too. I don't know about the PGS case; it
seems the sub resolution must always be set, so it shouldn't matter.

Fixes #1425.
2015-01-25 17:00:07 +09:00
wm4 57f149e26a options: don't cut off long options in --list-options
This was by design, so that overly long options don't mess up the column
layout, but I guess it doesn't have much worth.

Fixes #1426.
2015-01-25 17:00:07 +09:00
wm4 a77fec7547 manpage: fix --video-unscaled description
This was changed a long time ago in commit 37319ab6.
2015-01-25 17:00:07 +09:00
wm4 18c8d60b54 ytdl: silence "succeeded" message
Pretty useless by now.
2015-01-25 17:00:07 +09:00
Stefano Pigozzi 1eb4e90161 cocoa: fix build on 10.9
Use -isInFullScreenMode instead of the property introduced with the 10.10 SDK.
2015-01-25 17:00:07 +09:00
Stefano Pigozzi f3e515c790 cocoa: fix NSMapGet error
regression from 64b6b2ea45
2015-01-25 17:00:07 +09:00
wm4 0547b2a637 manpage: fix typo 2015-01-25 17:00:06 +09:00
Stefano Pigozzi 906448243c cocoa: fix uninitialization while in fullscreen
This is only needed for switching video track with `_`, since Cocoa
automatically handles cleaning up the application's presentation options when
quitting the process.

Fixes #1399
2015-01-25 17:00:06 +09:00
wm4 1ea7b386a9 Update copyright year 2015-01-25 17:00:06 +09:00
wm4 a82f2e2792 demux_playlist: unquote entries in pls playlists
I guess these parsers still have a way to go...
2015-01-25 17:00:06 +09:00
wm4 e0231926df screenshots: set AVFrame parameters
Currently, libavcodec is rather lenient, but it might get stricter in
the future.

Fixes #1398.
2015-01-25 17:00:06 +09:00
wm4 2c20c6f892 player: ordered chapters: filter missing chapters
Ever since someone (not me) added some Matroska features which nobody
ever uses (ordered edition or some bullshit), I haven't had a fucking
clue what the fuck is going on in this fucking file. (Still agreed to
it, so it's my fault.)

mplayer2 handled missing chapters correctly (and I suppose in a somewhat
clean/simple manner), but the changed code doesn't. Since I can't even
follow this code because it's so arcanely complicated, just add a hack
that has the same effect.
2015-01-25 17:00:06 +09:00
Stefano Pigozzi bde22281f7 osxbundle: fix cascading config loading
mpv does 'cascading' configs by overriding options as the config become more
important (bundle -> system level -> user level).

Unfortunately mpv also loads two sets of configs files one after the other [1].
First it looks for 'config', then for 'mpv.conf'. For this reason a mpv.conf in
ANY location will override ANY config files named config (even if the mpv.conf
is in a system path and config in a user path).

[1]: Relevant code in player/configfiles.c

    load_all_cfgfiles(mpctx, section, "config");
    load_all_cfgfiles(mpctx, section, "mpv.conf");

Fixes: #1361
2015-01-14 17:03:29 +01:00
wm4 697a7b7d19 manpage: fix typo
Fixes #1384.
2015-01-14 17:03:29 +01:00
wm4 422502f524 player: fix a typo in message output
This typo has been around for over a year. Oops.
2015-01-14 17:03:28 +01:00
wm4 34cbc9057c stream: always disable cache for pseudo-streams
Streams which don't have a full_buffer function never return any actual
data. Slight improvement over commit 5640c195.
2015-01-14 17:03:28 +01:00
wm4 36a2dd5ace stream_edl: disable caching
stream_edl merely makes demux_edl act "special", which checks for the
stream type explicitly and then does something with its URL. If a cache
is added before the stream, it'll try to use the cache's URL (i.e. an
empty string), and will then obviously fail to parse the URL. While this
is slightly stupid, just disabling the entirely useless cache is the
most effective solution.

Fixes #1378.
2015-01-14 16:22:26 +01:00
wm4 6cec24e980 ao_alsa: fix unpause path atfer previous commit
The resume code was accidentally fully removed from this code path.
2015-01-14 16:22:01 +01:00
wm4 9b2b95ee52 ao_alsa: fix resuming from suspend mode
snd_pcm_prepare() was not always called, which could result in an
infinite loop.

Whether snd_pcm_prepare() was actually called depended on whether the
device was a hw device (or other characteristics; depending on
snd_pcm_hw_params_can_pause()), and required real suspend (annoying for
testing), so it was somewhat tricky to reproduce without knowing these
things.
2015-01-14 16:21:45 +01:00
wm4 47d972102e sd_lavc: ignore image subtitles with unknown duration after 1 minute
Most image subtitle formats implicitly terminate the current subtitle
event with the next event (e.g. a new packet read from the demuxer will
instruct the subtitle render to stop display). If the subtitle event is
just trailing, it will be displayed forever. So there's no proper way
of doing this and we just apply an heuristic to avoid annoyances.
2015-01-14 16:19:41 +01:00
wm4 0cf7316c47 sd_lavc: compensate for a stupid libavcodec API issue
The libavcodec PGS decoder sets end_display_time to UINT32_MAX, in an
attempt to signal unknown end time (the API does not allow to signal
this properly, and this was a backwards compatible hack).

While we have no issues with the large value, our code wants to
distinguish between known and unknown end time explicitly.
2015-01-14 16:19:22 +01:00
wm4 3a6572d206 player: don't show "0%" percentage in infinite streams 2015-01-14 16:18:17 +01:00
wm4 8ab8fa8960 ao_alsa: fix setting mono channel map
When setting the ALSA channel map, we never actually set the map we got
from ALSA directly, but convert it to mpv's, and then back to ALSA's.
mpv and ALSA use different conventions for mono, and there is already an
exception for ALSA->mpv, but not mpv->ALSA.
2015-01-14 16:17:59 +01:00
wm4 295622bab1 audio: fix previous commit
This would have always forced mono first (if supported by the AO),
instead of stereo.
2015-01-14 16:17:33 +01:00
wm4 8def7ccb4f audio: fix fallback if audio API does not support mono
This makes it fallback to stereo properly.
2015-01-14 16:17:19 +01:00
Stefano Pigozzi f88a86a936 cocoa: mark window for redraw after title change
This used to work correctly without the call to displayIfNeeded. I think this
may only be needed for Yosemite.

Fixes #1330
2015-01-14 16:17:05 +01:00
Alessandro Ghedini 1783e8cade Release 0.7.2 2014-12-20 12:00:13 +01:00
wm4 c7d6b21789 options: enable --ytdl by default
Let's see who complains...
2014-12-19 19:23:28 +01:00
ChrisK2 8d4e6498c3 ytdl: bump requrired youtube-dl version to 2014.11.26 2014-12-19 19:23:22 +01:00
ChrisK2 9984f4fffd ytdl: Try to handle multi-arc videos
this currently uses a sketchy but apparently working workaround,
which will be removed once the neccessary changes in youtube-dl
are implemented

Fixes #1277
2014-12-19 19:23:15 +01:00
ChrisK2 75a62c7a64 ytdl: When using DASH, print actual duration
prints the actual duration as reported by youtube-dl to the
terminal when available
2014-12-19 19:23:11 +01:00
wm4 73eaadf6c3 vo_opengl: clamp filters to their size
This gives better results with fancy-downscaling. The issue here is that
fancy-downscalign "extends" the filter radius by some amount, which
requires using a larger filter size and shader. Then most of the filter
is "unused", but some filters still return non-0 coefficients, which
create heavy artifacts. Just clamp them off.

I'm not sure if this is the right solution, but at least it's better
than before.
2014-12-19 19:23:07 +01:00
Stefano Pigozzi 292f6f1904 cocoa: don't change app activation policy in libmpv
/cc @mpv-player/stable
2014-12-17 20:15:38 +01:00
Stefano Pigozzi 5a2947c9d0 cocoa: remove urlencoding for url open events
This fixes using the mpv:// custom protocol on Yosemite were apparently
we receive an url which is automatically urlencoded by the system.

/cc mpv-player/stable
2014-12-17 20:15:07 +01:00
Stefano Pigozzi fdea35a71b ao_coreaudio: add missing goto for error path 2014-12-17 20:13:23 +01:00
wm4 8f6eb196cd build: require alsa libs not older than 6 years
Some IEC958 flags we use have been introduced in 2008, which makes
compilation fail on older systems.
2014-12-17 20:12:44 +01:00
wm4 3b6e063599 vo_x11: don't attempt to resize when unconfigured
Fixes #1347.

The previous commit actually fixes the crash.
2014-12-17 20:12:06 +01:00
wm4 015218f8c8 vo_x11: check allocation errors
Avoids a crash if allocation fails.
2014-12-17 20:11:41 +01:00
wm4 00d1d6d807 build: add checks for some compiler warning flags 2014-12-17 20:11:30 +01:00
wm4 49c3cbfb4e vd_lavc: fix error handling path
The ctx->pic check must uninitialize the decoder.
2014-12-17 20:11:12 +01:00
wm4 925becfb15 mp_image: check for malloc failure
Or rather, the only reason av_buffer_create() can fail is a malloc
failure.
2014-12-17 20:11:03 +01:00
wm4 2cebc52065 stream_cdda: don't return number of tracks as number of titles
Apparently, we treat different tracks as titles, so returning the number
of titles (which is user-visible as "disc-titles" property) is
completely misguided.
2014-12-17 20:10:54 +01:00
wm4 34e504bf93 Check some mallocs
Fixes #1337.
2014-12-17 20:09:24 +01:00
James Ross-Gowan f0649ce841 pipe-win32: possible fix for Windows XP deadlock
This fixes a hang with the VirtualBox OpenGL drivers. It might help
with #1325 as well.
2014-12-17 20:09:05 +01:00
wm4 65af6a55b7 manpage: fix mistake in --video-zoom mention
Fixes #1331.
2014-12-17 20:08:48 +01:00
wm4 1326d3f0b8 command: fix crash with some forms of script_binding
"script_binding name ..." just crashed it. Oops.
2014-12-17 20:08:15 +01:00
wm4 b062b54626 vo_opengl: fix linkage type
Can happen on Windows, I suppose.
2014-12-17 20:06:05 +01:00
wm4 9bee56cc67 vo_opengl: don't crash if framebuffers are not available
In theory, vo_opengl supports operation without framebuffers. But this
has been broken for a while now (commit cc00b3ff is a contender). It
crashed because it unconditionally called gl->BindFramebuffer() (which
is NULL if framebuffers are missing).

Since this function is actually only called to set the default
framebuffer, the simplest way to deal with this is to provide a dummy
function, insteas of uglifying the code with additional if branches.
2014-12-17 20:05:46 +01:00
James Ross-Gowan 9fb0180eb2 w32_common: fix GUID linking in Cygwin 2014-12-17 20:05:07 +01:00
wm4 4f8af1a518 player: restore ab-loop settings with playback resume
Fixes #1324.
2014-12-17 20:05:01 +01:00
wm4 62b6048468 build: fix --disable-libass
Still supported, but obviously untested.

You shouldn't use this option anyway.
2014-12-17 20:04:02 +01:00
ChrisK2 b6363c8100 DOCS: Improve documentation of --ytdl-format option
As suggested in #1321
2014-12-08 00:06:41 +01:00
wm4 db4cec8a6c sub: remove assertion
This should clearly be impossible, but it seems to happen with ordered
chapters for a user.

Since I can't tell what the actual bug is and it seems impossible to
know the details without downloading possibly huge files, this is
probably the best we can do.

Should at least partially fix #1319.
2014-12-08 00:06:17 +01:00
reimar 4de6f4f616 af_hrtf: Fix out-of-range read.
Based on patch by Yuriy Kaminskiy [yumkam gmail].

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@37330 b3059339-0415-0410-9bf9-f77b7e298cf2
Signed-off-by: wm4 <wm4@nowhere>
2014-12-08 00:04:42 +01:00
wm4 7c53bb4bb1 demux_mkv: reject 0 TimecodeScale
Also reject anything over INT_MAX; no particular reason for this upper
bound.

Fixes #1317.
2014-12-08 00:04:27 +01:00
Stefano Pigozzi 115709e3c1 cocoa: save screenshots to desktop when using app bundle
Fixes #947
2014-12-08 00:03:39 +01:00
Stefano Pigozzi 1e8dbf8212 cocoa: don't create Dock icon for audio only files
fixes #635
2014-12-08 00:03:34 +01:00
wm4 1bfaab13ca ao_alsa: hackfix mono playback
ALSA returns "FL" as channel layout when trying to play mono. mpv and
libavresample don't like this; in particular, using libavresample to
convert stereo to "FL" fails.
2014-12-08 00:02:45 +01:00
wm4 083235d91f osc, dvd, bd: fix mouse state when changing menu modes
The flags weren't correctly set, and the mouse cursor remained visible
after leaving menu mode.

This was apparently broken in 0.7.0 too.

Fixes #1316.
2014-12-08 00:02:24 +01:00
wm4 bc4e115b30 discnav: remove redundant call
This was made the default a while ago.
2014-12-08 00:02:16 +01:00
Stefano Pigozzi 807763c85e coreaudio: don't output too many channel descriptions
for #1279 and #1249
2014-12-07 23:59:33 +01:00
Stefano Pigozzi 69fa6c4b73 coreaudio: add missing \n in log line 2014-12-07 23:59:23 +01:00
Stefano Pigozzi 3141381c2e coreaudio: don't print layout a second time
For #1279
2014-12-07 23:59:16 +01:00
Stefano Pigozzi 8f847437a9 cocoa: don't async redraw when waiting for VO redraw
This fixes the very annoying glitch where the black bars disappear for
a single frame when going fullscreen.
2014-12-07 23:59:02 +01:00
wm4 487fcea6f4 ao_alsa: try to fallback to "default" device if device is busy
ALSA is crap. It's impossible to make multichannel playback just do the
right thing. dmix (the default on most distros) can do stereo only, and
will refuse to play multichannel. On the other hand, if you try like mpv
(and mplayer) to open a multichannel device (like "surround51" etc.),
this will actually open a hardware device, which will either fail if
dmix is active, or block out dmix if opening succeeds.

This commit falls back to "default" (i.e. dmix) if opening a
multichannel device fails, which is a tiny step towards the right
behavior. (Although fixing it fully is impossible.)
2014-12-07 23:57:34 +01:00
wm4 63a31db0a3 input: add a hack to fix keyboard navigation with dvd/bd menu
If the user has LEFT/RIGHT/etc. bound in his input.conf, then these were
overriding the menu keys in dvdnav mode.

This hack works because the dvdnav crap happens to be the only user of
MP_INPUT_ON_TOP. If it finds a default key binding in the dvdnav menu
section, it will use that, instead of continuing search and possibly
finding the user key bindings meant for normal playback.
2014-12-07 23:56:25 +01:00
Stefano Pigozzi 575edecccb coreaudio: reject descriptions with too many channels
This is a fix attempt for #1279 and #1249.
2014-12-07 23:55:28 +01:00
Stefano Pigozzi 14adda8f6b coreaudio: fix more layout prints 2014-12-07 23:55:08 +01:00
Stefano Pigozzi 7cacff6d61 coreaudio: fix prints of uint32_t in log_layout 2014-12-07 23:54:22 +01:00
wm4 4ced125239 dvd, bd, osc: disable OSC while a menu is active
They interfere.

It turns out that commit b6ca4a48 actually broke this in weird ways, but
this solution is better anyway.
2014-12-07 23:54:12 +01:00
wm4 ac94c81134 input, lua: make removing key bindings work
This just kept adding bindings to the input section, rather than
defining it. One bad effect was that mp.remove_key_binding() in Lua
didn't work.
2014-12-07 23:52:33 +01:00
wm4 9501050ddc osc: always force mouse_move binding
So the OSC will still appear when using --no-input-default-bindings. It
also means it may override a user's mouse_move binding, but I guess
users who don't want the OSC should just use the --no-osc option.
2014-12-07 23:50:41 +01:00
wm4 c688880f9e lua: fix broken error message
luaL_error() doesn't support %.*s, because it uses Lua's own format
string mechanism that just looks like the C one. Just drop this part.
2014-12-07 23:50:13 +01:00
wm4 47ec404a52 player: don't signal an error if --stream-dump is used
The player thinks an error happened because no audio or video was played
after finishing the file, but this obviously makes no sense with stream
dumping. (error_playing follows the client API convention that negative
values are errors.)
2014-12-07 23:49:55 +01:00
Alessandro Ghedini 18b15a436f Release 0.7.1 2014-12-02 18:24:30 +01:00
Stefano Pigozzi 6583ad6970 cocoa: fix opening reference URLs (.file/id=)
Fixes #1202

/cc @mpv-player/stable
2014-12-01 21:38:51 +01:00
Niklas Haas 863701391c vo_opengl: Linearize non-RGB sRGB files correctly (eg. JPEG)
Signed-off-by: wm4 <wm4@nowhere>
2014-12-01 21:07:26 +01:00
Niklas Haas 3803c14279 vo_opengl: Reword comment in shader
I didn't quite understand this comment after looking at the code again
months later, so I reworded it for better clarity.
2014-12-01 21:07:12 +01:00
wm4 39126c3828 vo_opengl: make operator precedence explicit
More readable.
2014-12-01 21:07:06 +01:00
Stefano Pigozzi b9cfc3622c ao_coreaudio: initialize fetched properties to zeros
Should hopefully fix #1249 and #1279
2014-12-01 21:06:07 +01:00
wm4 1b0bf6507e options: fix channel map options sometimes failing
Channel amp otpions were not copied correctly: it copied the size of a
pointer to struct chmap, not the struct itself. Since mp_chmap is
currently 9 bytes, this meant the last channel entry was not copied
correctly on 64 bit systems, leading to very strange failures. It could
be triggered especially when using the client API, because the client
API always copies options on access (mpv command line options tend to
work directly on options).
2014-12-01 21:06:00 +01:00
wm4 7ca6aa4ad0 options: fix swapped deprecated/replacement in warning
This was relevant for --af and --vo (both use this option type, and have
deprecated aliases).
2014-12-01 21:05:56 +01:00
wm4 b82a98722a player: simplify and fix ordered chapter EOF handling
Ordered chapter EOF was handled as special-case of ending the last
segment. This broke --kee-open, because it set AT_END_OF_FILE in an
"inconvenient" place (after checking for --keep-open, and before the
code that exits playback if EOF is reached).

We don't actually need to handle the last segment specially. Instead, we
remain in the same segment if it ends. The normal playback logic will
recognize EOF, because the end of the segment "cuts off" the file.

Now timeline_set_from_time() never "fails", and we can remove the old
segment EOF handling code in mp_seek().
2014-12-01 21:05:53 +01:00
wm4 3d6d7012ff command: sub_add with "auto" should not always select a subtitle track
Running "sub_add file.srt auto" during hook execution automatically
selected the first added track. This happened because all tracks added
with sub_add are marked as "external", and external subtitles are always
selected by default.

Fix this by negating the "external" flag when autoselecting subtitles
during loading. The no_default flag exists for this purpose; it was
probably added for libquvi originally, where we had the same issue.
2014-12-01 21:05:49 +01:00
wm4 3051ff9f25 mixer: don't show softvol neutral marker on OSD if not using softvol
Also fix the comment on the softvol field.
2014-12-01 21:05:45 +01:00
wm4 9bf66294f8 vo_opengl: fix srgb with certain inputs
Sampling from the source texture and scaling must always be done
separately in this mode.

Fix suggested by haasn.

Still looks a bit wrong, though.
2014-12-01 21:05:20 +01:00
wm4 577378b827 vo_opengl: something
Insert explanation here.

Fixes #1023.
2014-12-01 21:05:15 +01:00
wm4 1679222e18 audio: fix one of the previous commits
Fixes commit 52c51149. It broke multichannel (or possibly everything)
for ao_alsa, ao_oss, ao_sndio.
2014-12-01 18:31:49 +01:00
wm4 74270cd1db audio: allow more than 20 channel map entries
This could trigger an assertion when using ao_alsa or ao_coreaudio. The
code was simply assuming the number of channel maps was bounded
statically (which was true at first in both AOs).

Fix by using dynamic memory allocation. It needs to be explicitly
enabled by the AOs by setting a temp context, because otherwise the
memory couldn't be freed. (Or at least this seems to be the most elegant
solution.)

Fixes #1306.
2014-12-01 15:41:26 +01:00
wm4 718bf3b39e vd_lavc: don't crash if codec could not be opened
Fixes #1307.
2014-12-01 15:41:17 +01:00
wm4 5ecb381d78 demux_playlist: don't ignore last line in m3u
If EOF is reached after reading a line, the EOF flag is set. This was a
problem for the m3u code, which checked for EOF _after_ reading a line,
which will discard the last line read.

Also fix a typo in an unrelated part of the file.
2014-12-01 15:40:57 +01:00
wm4 17f1332955 win32: add screen offset when handling overlarge windows
MS Windows doesn't allow windows larger than the screen, so we include
a hack to make the window smaller. This hack recenters the window (what
else would it do?).

It didn't account for the virtual offset of the current screen, and it
was reported that it forces the window to the first screen.

Should fix #1292.
2014-12-01 15:38:28 +01:00
Stefano Pigozzi f4a1dfdffb build: fix typos
thanks to @Nikoli
2014-11-28 15:51:43 +01:00
James Ross-Gowan d3972125a1 subprocess-win: remove a Vista-only flag
PIPE_REJECT_REMOTE_CLIENTS isn't supported on XP. It's not really
needed, so remove it. Also fix error checking for CreateNamedPipe.
2014-11-28 15:51:39 +01:00
wm4 71f504a43d demux_mkv: fix a possible out of bounds access
The if branch has a weak check to test whether the codec_id is the short
ID, and handles the long IDs in the else branch. The long IDs are all
longer than 12 bytes long, so hardcoding the string offset to get the
trailing part of the name makes sense. But the if condition checks for
another thing, which could get the else branch run even if the codec_id
is short.

Fix the bogus control flow and check if the codec_id is long enough. One
of these checks could be considered redundant, but include them both for
defensive coding.
2014-11-28 15:51:32 +01:00
wm4 bc1370041d audio: alternative fix for previous commit
This is a somewhat obscure situation, and happens only if audio starts
again after it has ended (in particular can happens with files where
audio starts later). It doesn't matter much whether audio starts
immediately or some milliseconds later, so simplify it.
2014-11-28 15:51:03 +01:00
wm4 4f74869cd8 audio: fix busy loop when seeking while paused
When playing paused, the amount of decoded audio is limited to a small
amount (1 sample), because we don't write any audio to the AO when
paused. The small amount could trigger the case of the wanted audio
being too far in the future in the PTS sync code, which set the audio
status to STATUS_DRAINING, which in turn triggered the EOF code in the
next iteration. This was ok, but unfortunately, this triggered another
retry in order to check resuming from EOF by setting the status to
STATUS_SYNCING, which in turn lead to the busy loop by alternating
between the 2 states. So don't try resyncing while paused.

Since the PTS syncing code also calls ao_reset(), this could cause the
pulseaudio daemon to consume some CPU time as well.

This was caused by commit 33b57f55. Before that, the playloop was merely
run more often, but didn't cause any problems.

Fixes #1288.
2014-11-28 15:50:58 +01:00
Alessandro Ghedini 8d8b36df1b manpage: lua: fix typo paramater -> parameter 2014-11-28 15:49:52 +01:00
Alessandro Ghedini 2da6c95208 Release 0.7.0 2014-11-25 20:18:53 +01:00
160 changed files with 3309 additions and 1710 deletions

No files matched your search

+1
View File
@@ -25,6 +25,7 @@ API changes
::
--- mpv 0.7.0 is released ---
1.10 - deprecate/disable everything directly related to script_dispatch
(most likely affects nobody)
1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason
+14
View File
@@ -0,0 +1,14 @@
All examples in this directory and its sub-directories are licensed
under one of the following licenses:
WTFPL, ISC, Ms-PL, AGPLv3, BSD (any)
Pick any license of your liking, and disregard the others.
(The full text of each license is available on this website:
http://opensource.org/licenses/alphabetical )
Additionally, you may consider the example code to be public domain.
You are free to use any of the example code without further
requirements or need for attribution.
+3 -4
View File
@@ -1,9 +1,8 @@
// License: pick one of: public domain, WTFPL, ISC, Ms-PL, AGPLv3
// This example can be built with: qmake && make
#include <clocale>
#include <sstream>
#include <stdexcept>
#include <QtGlobal>
#include <QFileDialog>
@@ -57,7 +56,7 @@ MainWindow::MainWindow(QWidget *parent) :
mpv = mpv_create();
if (!mpv)
throw "can't create mpv instance";
throw std::runtime_error("can't create mpv instance");
// Create a video child window. Force Qt to create a native window, and
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa
@@ -94,7 +93,7 @@ MainWindow::MainWindow(QWidget *parent) :
mpv_set_wakeup_callback(mpv, wakeup, this);
if (mpv_initialize(mpv) < 0)
throw "mpv failed to initialize";
throw std::runtime_error("mpv failed to initialize");
}
void MainWindow::handle_mpv_event(mpv_event *event)
+9 -4
View File
@@ -28,8 +28,8 @@ normal driver parameters.
.. admonition:: Examples
- ``--ao=alsa,oss,`` Try the ALSA driver, then the OSS driver, then others.
- ``--ao=alsa:no-block:device=[hw:0,3]`` Sets noblock-mode and the
device-name as first card, fourth device.
- ``--ao=alsa:resample=yes:device=[plughw:0,3]`` Lets ALSA resample and
sets the device-name as first card, fourth device.
Available audio output drivers are:
@@ -147,10 +147,15 @@ Available audio output drivers are:
changes. Default: 250.
``latency-hacks=<yes|no>``
Enable hacks to workaround PulseAudio timing bugs (default: yes). If
Enable hacks to workaround PulseAudio timing bugs (default: no). If
enabled, mpv will do elaborate latency calculations on its own. If
disabled, it will use PulseAudio automatically updated timing
information. Disabling this might help with e.g. networked audio.
information. Disabling this might help with e.g. networked audio or
some plugins, while enabling it might help in some unknown situations
(it used to be required to get good behavior on old PulseAudio versions).
If you have stuttering video when using pulse, try to enable this
option. (Or alternatively, try to update PulseAudio.)
``portaudio``
PortAudio audio output driver. This works on all platforms, and has
+2 -2
View File
@@ -113,8 +113,8 @@ Miscellaneous
* Matroska edition switching at runtime.
* Support for playing URLs of popular streaming sites directly.
(e.g. ``mpv https://www.youtube.com/watch?v=...``).
Requires a recent version of ``youtube-dl`` to be installed, and
``ytdl=yes`` in the mpv config file.
Requires a recent version of ``youtube-dl`` to be installed. Can be
disabled with ``ytdl=no`` in the mpv config file.
* Support for precise scrolling which scales the parameter of commands. If the
input doesn't support precise scrolling the scale factor stays 1.
* Allow changing/adjusting video filters at runtime. (This is also used to make
+15 -5
View File
@@ -1,5 +1,13 @@
INPUT.CONF
==========
COMMAND INTERFACE
=================
The mpv core can be controlled with commands and properties. A number of ways
to interact with the player use them: key bindings (``input.conf``), OSD
(showing information with properties), JSON IPC, the client API (``libmpv``),
and the classic slave mode.
input.conf
----------
The input.conf file consists of a list of key bindings, for example::
@@ -232,7 +240,6 @@ List of Input Commands
will have after moving.)
``run "command" "arg1" "arg2" ...``
(Unix only)
Run the given command. Unlike in MPlayer/mplayer2 and earlier versions of
mpv (0.2.x and older), this doesn't call the shell. Instead, the command
is run directly, with each argument passed separately. Each argument is
@@ -814,7 +821,7 @@ Property list
"title" MPV_FORMAT_STRING
"default" MPV_FORMAT_FLAG
``ab-loop-a``, ``ab-loop-b`` (TW)
``ab-loop-a``, ``ab-loop-b`` (RW)
Set/get A-B loop points. See corresponding options and ``ab_loop`` command.
The special value ``no`` on either of these properties disables looping.
@@ -1421,13 +1428,16 @@ Property list
.. admonition:: Example
--osd-status-msg='This is ${osd-ass-cc/0}{\\b1}bold text'
- ``--osd-status-msg='This is ${osd-ass-cc/0}{\\b1}bold text'``
- ``show_text "This is ${osd-ass-cc/0}{\b1}bold text"``
Any ASS override tags as understood by libass can be used.
Note that you need to escape the ``\`` character, because the string is
processed for C escape sequences before passing it to the OSD code.
A list of tags can be found here: http://docs.aegisub.org/latest/ASS_Tags/
``vo-configured``
Return whether the VO is configured right now. Usually this corresponds to
whether the video window is visible. If the ``--force-window`` option is
+10 -7
View File
@@ -22,10 +22,12 @@ A script which leaves fullscreen mode when the player is paused:
::
function on_pause()
mp.set_property("fullscreen", "no")
function on_pause_change(name, value)
if value == true then
mp.set_property("fullscreen", "no")
end
end
mp.register_event("pause", on_pause)
mp.observe_property("pause", "bool", on_pause_change)
This script provides a pretty weird feature, but Lua scripting was made to
allow users implement features which are not going to be added to the mpv core.
@@ -573,7 +575,7 @@ strictly part of the guaranteed API.
Runs an external process and waits until it exits. Returns process status
and the captured output.
The paramater ``t`` is a table. The function reads the following entries:
The parameter ``t`` is a table. The function reads the following entries:
``args``
Array of strings. The first array entry is the executable. This
@@ -623,6 +625,10 @@ strictly part of the guaranteed API.
trailing text is returned as 3rd return value. (The 3rd return value is
always there, but with ``trail`` set, no error is raised.)
``utils.to_string(v)``
Turn the given value into a string. Formats tables and their contents. This
doesn't do anything special; it is only needed because Lua is terrible.
Events
------
@@ -716,9 +722,6 @@ List of events
``command-reply``
Undocumented (not useful for Lua scripts).
``script-input-dispatch``
Undocumented (used internally).
``client-message``
Undocumented (used internally).
+85 -15
View File
@@ -235,11 +235,43 @@ is the same as ``--no-fs``.
If an option is marked as *(XXX only)*, it will only work in combination with
the *XXX* option or if *XXX* is compiled in.
.. note::
Escaping spaces and other special characters
--------------------------------------------
The suboption parser (used for example for ``--ao=pcm`` suboptions)
supports a special kind of string-escaping intended for use with external
GUIs.
Keep in mind that the shell will partially parse and mangle the arguments you
pass to mpv. For example, you might need to quote or escape options and
filenames:
``mpv "filename with spaces.mkv" --title="window title"``
It gets more complicated if the suboption parser is involved. The suboption
parser puts several options into a single string, and passes them to a
component at once, instead of using multiple options on the level of the
command line.
The suboption parser can quote strings with ``"``, ``'``, and ``[...]``.
Additionally, there is a special form of quoting with ``%n%`` described below.
For example, the ``opengl`` VO can take multiple options:
``mpv test.mkv --vo=opengl:lscale=lanczos2:icc-profile=file.icc,xv``
This passed ``lscale=lanczos2`` and ``icc-profile=file.icc`` to ``opengl``,
and also specifies ``xv`` as fallback VO. If the icc-profile path contains
spaces or characters like ``,`` or ``:``, you need to quote them:
``mpv '--vo=opengl:icc-profile="file with spaces.icc",xv'``
Shells may actually strip some quotes from the string passed to the commandline,
so the example quotes the string twice, ensuring that mpv recieves the ``"``
quotes.
The ``[...]`` from of quotes wraps everything between ``[`` and ``]``. It's
useful with shells that don't interpret these characters in the middle of
an argument (like bash).
A special kind of string-escaping intended for use with external scripts and
programs is started with ``%``.
It has the following format::
@@ -253,6 +285,15 @@ It has the following format::
``mpv --ao=pcm:file=%`expr length "$NAME"`%"$NAME" test.avi``
Suboptions passed to the client API are also subject to escaping. Using
``mpv_set_option_string()`` is exactly like passing ``--name=data`` to the
command line (but without shell processing of the string). Some options
support passing values in a more structured way instead of flat strings, and
can avoid the suboption parsing mess. For example, ``--vf`` supports
``MPV_FORMAT_NODE``, which let's you pass suboptions as a nested data structure
of maps and arrays. (``--vo`` supports this in the same way, although this
fact is undocumented.)
Paths
-----
@@ -340,6 +381,17 @@ setting them to *no*. Even suboptions can be specified in this way.
# Use quotes for text that can contain spaces:
status-msg="Time: ${time-pos}"
Escaping spaces and special characters
--------------------------------------
This is done like with command line options. The shell is not involved here,
but option values still need to be quoted as a whole if it contains certain
characters like spaces. A config entry can be quoted with ``"`` and ``'``,
as well as with the ``%n%`` syntax mentioned before. This is like passing
the exact contents of the quoted string as command line option. C-style escapes
are currently _not_ interpreted on this level, although some options to this
manually. (This is a mess and should probably be changed at some point.)
Putting Command Line Options into the Configuration File
--------------------------------------------------------
@@ -436,7 +488,7 @@ listed.
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
- The total file duration (or ``00:00:00`` if unknown) (``length`` property)
- The total file duration (absent if unknown) (``length`` property)
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
is the user-requested speed, and not the actual speed (usually they should
be the same, unless playback is too slow). (``speed`` property.)
@@ -487,7 +539,7 @@ PROTOCOLS
``bd://[title][/device]`` ``--bluray-device=PATH``
Play a Blu-Ray disc. Currently, this does not accept ISO files. Instead,
you must mount the ISO file as filesystem, and point ``--bluray-device``
to the mounted directly.
to the mounted directory directly.
``bdnav://[title][/device]``
Play a Blu-Ray disc, with navigation features enabled. This feature is
@@ -718,6 +770,26 @@ FILES
and files with no ``.lua`` extension are ignored. The ``--load-scripts=no``
option disables loading these files.
``~/.config/mpv/watch_later/``
Contains temporary config files needed for resuming playback of files with
the watch later feature. See for example the ``Q`` key binding, or the
``quit_watch_later`` input command.
Each file is a small config file which is loaded if the corresponding media
file is loaded. It contains the playback position and some (not necessarily
all) settings that were changed during playback. The filenames are hashed
from the full paths of the media files. It's in general not possible to
extract the media filename from this hash. However, you can set the
``--write-filename-in-watch-later-config`` option, and the player will
add the media filename to the contents of the resume config file.
``~/.config/mpv/lua-settings/osc.conf``
This is loaded by the OSC script. See the `ON SCREEN CONTROLLER`_ docs
for details.
Other files in this directory are specific to the corresponding scripts
as well, and the mpv core doesn't touch them.
Note that the environment variables ``$XDG_CONFIG_HOME`` and ``$MPV_HOME`` can
override the standard directory ``~/.config/mpv/``.
@@ -728,18 +800,16 @@ FILES ON WINDOWS
================
On win32 (if compiled with MinGW, but not Cygwin), the default config file
locations are different:
locations are different. They are generally located under ``%APPDATA%/mpv/``.
For example, the path to mpv.conf is ``%APPDATA%/mpv/mpv.conf``, which maps to
a system and user-specific path, for example
``%APPDATA%/mpv/mpv.conf``
Preferred mpv config file. This maps to a system and user-specific path,
for example ``C:\users\USERNAME\Application Data\mpv\mpv.conf``. You can
find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.exe.
``C:\users\USERNAME\Application Data\mpv\mpv.conf``
``%APPDATA%/mpv/input.conf``
key bindings (see `INPUT.CONF`_ section)
You can find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.exe.
``%APPDATA%/mpv/lua/``
equivalent of ``~/.config/mpv/lua/`` on Unix.
Other config files (such as ``input.conf``) are in the same directory. See the
`FILES`_ section above.
The environment variable ``$MPV_HOME`` completely overrides these, like on
UNIX.
+20 -24
View File
@@ -51,8 +51,8 @@ Track Selection
other software using FFmpeg (consider ``ffprobe``).
Note that with external tracks (added with ``--sub-file`` and similar
options) will have duplicate IDs. In that case, the first stream in order
is selected.
options), there will be streams with duplicate IDs. In this case, the
first stream in order is selected.
``--edition=<ID|auto>``
(Matroska files only)
@@ -124,8 +124,8 @@ Playback Control
chapter to end playing at. Also see ``--start``.
``--playlist=<filename>``
Play files according to a playlist file (Supports some common formats.If
no format is detected, t will be treated as list of files, separated by
Play files according to a playlist file (Supports some common formats. If
no format is detected, it will be treated as list of files, separated by
newline characters. Note that XML playlist formats are not supported.)
You can play playlists directly and without this option, however, this
@@ -360,10 +360,6 @@ Program Behavior
file. This uses timeline/EDL support internally. Note that this won't work
for ordered chapter files.
This option is interpreted at program start, and doesn't affect for
example files or playlists loaded with the ``loadfile`` or ``loadlist``
commands.
``--no-resume-playback``
Do not restore playback position from the ``watch_later`` configuration
subdirectory (usually ``~/.config/mpv/watch_later/``).
@@ -425,21 +421,21 @@ Program Behavior
``--ytdl``, ``--no-ytdl``
Enable the youtube-dl hook-script. It will look at the input URL, and will
play the video located on the website. This works with many streaming sites,
not just the one the scripts are named after. This requires a recent version
of youtube-dl to be installed on the system.
not just the one that the script is named after. This requires a recent
version of youtube-dl to be installed on the system. (Enabled by default.)
If the script can't do anything with an URL, it will do nothing.
Currently disabled by default, because youtube-dl's generic extractor can
get stuck on some URL, preventing playback.
(Note: this is the replacement for the now removed libquvi support.)
``--ytdl-format=<best|worst|mp4|webm|...>``
Video format/quality that is directly passed to youtube-dl. The possible
values are specific to the website and the video, for a given url the
available formats can be found with the command
``youtube-dl --list-formats URL``.
``youtube-dl --list-formats URL``. See youtube-dl's documentation for
available aliases. To use experimental DASH support for youtube, use
``bestvideo+bestaudio``.
(Default: ``best``)
Video
-----
@@ -498,7 +494,7 @@ Video
in mpv 0.5.x and before.) This tells the decoder to skip frames (unless
they are needed to decode future frames). May help with slow systems,
but can produce unwatchably choppy output, or even freeze the display
complete. Not recommended.
completely. Not recommended.
The ``--vd-lavc-framedrop`` option controls what frames to drop.
<decoder+vo>
Enable both modes. Not recommended.
@@ -576,9 +572,8 @@ Video
``--video-unscaled``
Disable scaling of the video. If the window is larger than the video,
black bars are added. Otherwise, the video is cropped. The video still
can be influenced by the other ``--video-...`` options. (If the
``--video-zoom`` option is set to a value other than ``1``, scaling is
enabled, but the video isn't automatically scaled to the window size.)
can be influenced by the other ``--video-...`` options. (But not all; for
example ``--video-zoom`` does nothing if this option is enabled.)
The video and monitor aspects aspect will be ignored. Aspect correction
would require to scale the video in the X or Y direction, but this option
@@ -1434,15 +1429,16 @@ Window
------
``--title=<string>``
Set the window title. Properties are expanded on playback start.
(See `Property Expansion`_.)
Set the window title. This is used for the video window, and if possible,
also sets the audio stream title.
Properties are expanded. (See `Property Expansion`_.)
.. warning::
There is a danger of this causing significant CPU usage, depending on
the properties used and the window manager. Changing the window title
is often a slow operation, and if the title changes every frame,
playback can be ruined.
the properties used. Changing the window title is often a slow
operation, and if the title changes every frame, playback can be ruined.
``--screen=<default|0-32>``
In multi-monitor configurations (i.e. a single desktop that spans across
@@ -2434,7 +2430,7 @@ OSD
Alternatively, the color can be specified as a RGB hex triplet in the form
``#RRGGBB``, where each 2-digit group expresses a color value in the
range 0 (``00``) to 255 (``FF``). For example, ``#FF0000`` is red.
This is similar to web colors.
This is similar to web colors. Alpha is given with ``#AARRGGBB``.
.. admonition:: Examples
+20
View File
@@ -910,6 +910,26 @@ Available filters are:
making it higher than the number of cores can actually make it
slower.
The following variables are defined by mpv:
``video_in``
The mpv video source as vapoursynth clip. Note that this has no length
set, which confuses many filters. Using ``Trim`` on the clip with a
high dummy length can turn it into a finite clip.
``video_in_dw``, ``video_in_dh``
Display size of the video. Can be different from video size if the
video does not use square pixels (e.g. DVD).
``container_fps``
FPS value as reported by file headers. This value can be wrong or
completely broken (e.g. 0 or NaN). Even if the value is correct,
if another filter changes the real FPS (by dropping or inserting
frames), the value of this variable might not be useful. Note that
the ``--fps`` command line option overrides this value.
Useful for some filters which insist on having a FPS.
``vapoursynth-lazy``
The same as ``vapoursynth``, but doesn't load Python scripts. Instead, a
custom backend using Lua and the raw VapourSynth API is used. The syntax
+1 -1
View File
@@ -456,7 +456,7 @@ Available video output drivers are:
luma scaler is used instead. Setting this option is often useless.
``lscale-down=<filter>``, ``cscale-down=<filter>``
Like ``lscale`` and ``cscale``, but apply these filters on downscaling
Like ``lscale``, but apply these filters on downscaling
instead. If these options are unset, the filter implied by ``lscale``
(and ``cscale``, respectively) will be applied.
+12 -4
View File
@@ -46,16 +46,23 @@ Essential dependencies (incomplete list):
- gcc or clang
- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
- Audio output development headers (libasound, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavresample)
- Audio output development headers (libasound/ALSA, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
and either libswresample or libavresample)
At least FFmpeg 2.1.4 or Libav 10 is required.
- libjpeg (for screenshots)
- Lua (strictly speaking optional, but required for the OSC pseudo-GUI)
- libass
- Lua (optional, required for the OSC pseudo-GUI)
- libjpeg (optional, used for screenshots only)
- Enca (optional, for subtitle charset detection)
- vdpau and vaapi libraries for hardware decoding on Linux (optional)
Libass dependencies:
- gcc or clang, yasm on x86 and x86_64
- fribidi, freetype, fontconfig development headers (for libass)
- harfbuzz (optional, required for correct rendering of combining characters,
particularly for correct rendering of non-English text on OSX, and
Arabic/Indic scripts on any platform)
FFmpeg dependencies:
@@ -76,6 +83,7 @@ If you are running Mac OSX and using homebrew we provide [homebrew-mpv][homebrew
to date formula that compiles mpv with sensible dependencies and defaults for
OSX.
FFmpeg vs. Libav
----------------
+214
View File
@@ -0,0 +1,214 @@
Release 0.7.3
=============
Changes
-------
- Several documentation updates and typo fixes.
- Don't show "0%" position when the stream is infinite / has unknown length.
- Fixes the config file loading order so that lower priority `mpv.conf` files
don't override higher priority `config` files.
- (OSX) Remove coreaudio_exclusive from the "auto" codec list.
- Failing to create a GL3 context is now a warning instead of error, to reduce
user confusion.
- The subtitle decoder now gets reset when cycling subtitles. This makes the
currently shown subtitle event disappear even if cycling back to the current
subtitle track.
- `--shuffle` and `--merge-files` now affects the contents of playlist files,
instead of just the list of files given as arguments to mpv.
- `./waf install` avoids installing a few additional data/config files if only
libmpv was built.
- Improved channel mapping when the file's channel map doesn't match the AO's
available channel maps.
- (OSX) VDA now gracefully refuses to run on non-OpenGL3-capable systems instead
of trying and failing to build its shaders.
- Add workaround for vf_vapoursynth filters that expect an FPS.
- The default value for `--screenshot-template` now shows up in
`--list-options`.
- `ao_pulse`'s `latency-hacks` suboption is now off by default, as it causes
issues with newer pulseaudio releases. If A/V Sync issues happen, either
add the `latency-hacks=on` pulse suboption in mpv.conf, or update the
pulseaudio daemon (bug #1430).
- (Linux) `ao_alsa` now deals better with audio device disconnection.
- Client API: timestamp properties that have no value return "no" instead of
magic number.
Bug fixes
---------
- (Windows, OSX) Many `ao_wasapi` and `ao_coreaudio` fixes.
- (OSX) Make the window title update correctly on OS X Yosemite.
- Fixes for handling mono audio on various AOs.
- (Linux) Fixes resuming from suspend on ao_alsa.
- Fixes for playlist file parsing.
- Overly long options in the --list-options output now break the column layout
instead of getting truncated; fixes shell completion scripts.
- Fix rendering resolution of certain DVB subtitles (bug #1425).
- Fix EDL or --merge-files breaking timestamps with .avi files.
- Workaround for libavcodec bug with the VP9 codec parser (bug #1448),
fixes possible crash.
- Improve robustness of the matroska parser with broken files (bugs #1457,
#1461).
- Improve 10bit video compatibility with older GPUs (specially Intel) (bug
#1383).
- Fixes flashing the VO window when playing a list of files that includes
non-media files (bug #1459).
- Workaround for window embedding in OpenBox (bug #1235)
- Fixes for several crashes and lockups (bugs #838, #1389, #1408, #1463, #1473,
#1474).
- Fixes for the zsh completion script (bugs #997)
New features
------------
- mpv now prints the contents of its config.h when running in verbose mode, to
aid with debugging.
- The `include=` option now accepts `~` to refer to the HOME dir (bug #1406).
- `af_volume` now prints ReplayGain values in verbose mode.
- m3u playlists that don't have the `#EXTM3U` header are now accepted if they
"look like" ASCII or UTF-8 text and have the .m3u extension.
- Chapter marks in the seek bar now update when switching files.
- Supports embedded cover art in MKV files (bug #1374).
- (Windows) Video window can now be resized even with --no-border.
- (Windows) Client API: the "run" command now works on Windows too.
- (Linux) vo_wayland now supports key modifiers (Meta, Alt, Control, Shift)
- Client API: "display-names" property is now observable.
This listing is not complete. A complete changelog can be seen by running
`git log v0.7.2..v0.7.3` in the git repository.
Release 0.7.2
=============
Changes
-------
- Give precedence to the DVD menu navigation keyboard bindings so that user
defined LEFT/RIGHT/... bindings don't break DVD menu navigation.
- Try to fallback to the "default" device if the selected device is busy in the
alsa AO.
- Don't create Dock icon for audio only files on OS X.
- Save screenshots to desktop when using the app bundle on OS X.
- Restore ab-loop settings with playback resume.
- Bump required youtube-dl version to 2014.11.26 and enable the ytdl_hook Lua
script by default (now playing videos from YouTube and the like will work
out of the box without any configuration change needed).
Bug fixes
---------
- Don't signal an error if --stream-dump is used.
- Fix removing key bindings from Lua scripts.
- Reject channel descriptions with too many channels in the coreaudio AO.
- Don't async redraw when waiting for VO redraw on OS X (this fixes the very
annoying glitch where the black bars disappear for a single frame when going
fullscreen).
- Fix mono playback with the also AO.
- Don't crash if framebuffers are not available in the opengl VO.
New features
------------
- Try to handle multi-arc videos in the ytdl_hook Lua script.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 47ec404..c7d6b21`` in
the git repository.
Release 0.7.1
=============
Changes
-------
- Don't show the volume neutral marker on the OSD if softvol is disabled.
- Don't select a subtitle track when executing the sub_add input command in
"auto" mode.
Bug fixes
---------
- Fix busy loop when seeking while paused (this fixes a problem with pulseaudio
that caused mpv and the pulseaudio daemon to use 100% CPU).
- Fix Lua function utils.subprocess() in Windows versions older than Vista.
- Avoid creating a window bigger than the screen on Windows.
- Don't ignore the last line in m3u playlists.
- Don't crash if a codec could not be opened.
- Dynamically allocate audio channel map entries (this should fix a crash in
the alsa and coreaudio AOs with audio devices that support more than 20
channel maps).
- Ignore the "srgb" option in the opengl VO if hardware decoding is enabled.
- Linearize non-RGB sRGB files correctly (eg. JPEG).
- Fix opening reference URLs (.file/id=) on OS X.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 8d8b36d..6583ad6`` in
the git repository.
Release 0.7.0
=============
Changes
-------
- Buffer partial log messages in the client API (the client API will now only
pass full log messages to clients).
- Remove ncurses/terminfo/termcap support (it was disabled by default and
replaced by new code since v0.6.0).
- Enable cdda:// support by default again (it was disabled since v0.6.0).
- Cascade-load input.conf (if there are several input.confs in the set of valid
config paths, load them all).
- Draw the OSD twice in 3D mode (this fixes subtitles display in 3D mode).
- Make wasapi the default AO on Windows again since many of its problems have
been solved.
- Use "site-functions" subdir to install the zsh completion script instead of
the Debian-specific "vendor-completions" (also provide the --zshdir waf
configure option for changing this value).
- Improve synchronization between the Cocoa GUI and the player (this fixes some
long standing deadlock issues on Mac OS X).
- Remove --fs-missioncontrol option (only relevant to Mac OS X).
New features
------------
- Enable pitch correction by default when playing at higher speeds (this can be
controlled with the --audio-pitch-correction option).
- Open stream and demuxer asynchronously (this should avoid having the player
get blocked on network streams).
- Add cache-buffering-state property for querying the cache fill status until
the player unpauses.
- Add support for listing and selecting the audio device (note that it is not
implemented for all AOs, see the --audio-device option for more information).
- Add support for a JSON-based IPC mechanism (note that this is not currently
supported on Windows, see the JSON IPC section in the manpage for more
information).
- Add Lua utility function for starting processes (see utils.subprocess() in the
manpage).
- Add Lua utility function for parsing JSON (see utils.parse_json() in the
manpage).
- Add field-dominance property (see --field-dominance option).
- Add video-rotate property (see --video-rotate option).
- Add playback-abort property for querying whether playback is stopped or is to
be stopped.
- Add cursor-autohide property (see --cursor-autohide option).
- Add vo-configured property for querying whether a window is created.
- Add support for dxva2 hardware acceleration on Windows.
- Drop libquvi support (this has been replaced by a built-in Lua script that
invokes the youtube-dl tool, which needs to be installed, see the --ytdl
option).
- Add support for loading chapters from an external file (see the
--chapters-file option).
- Add window-minimized property for querying whether the window is minimized
(works for X11 only).
- Make it possible to configure the OSC seekbar style (see the "seekbarstyle"
OSC option).
- Add support for libmpv on Mac OS X (it used to be broken, now it's fully
functional and also provides support for embedding the mpv window inside a
Cocoa/Qt application).
- Try to use the audio channel map reported by ALSA in the alsa AO.
- Add option to disable text OSD rendering completely (useful for working around
certain fontconfig issues, see the --use-text-osd option).
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log 7759c18..9479daa`` in
the git repository.
+3
View File
@@ -25,6 +25,9 @@ function find_and_add_entries()
return
end
local files = mputils.readdir(dir, "files")
if files == nil then
return
end
table.sort(files)
if dir == "." then
dir = ""
+6 -8
View File
@@ -1,16 +1,14 @@
-- Test script for property change notification mechanism.
-- Note that watching/reading some properties can be very expensive, or
-- require the player to synchronously wait on network (when playing
-- remote files), so you should in general only watch properties you
-- are interested in.
function format_property_val(v)
if type(v) == "table" then
return mp.format_table(v) -- undocumented function; might be removed
else
return tostring(v)
end
end
local utils = require("mp.utils")
for i,name in ipairs(mp.get_property_native("property-list")) do
mp.observe_property(name, "native", function(name, val)
print("property '" .. name .. "' changed to '" ..
format_property_val(val) .. "'")
utils.to_string(val) .. "'")
end)
end
@@ -0,0 +1 @@
screenshot-template=~/Desktop/shot%n
+43 -19
View File
@@ -1,9 +1,12 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plot
import sys
import re
filename = sys.argv[1]
event_regex = re.compile(".*")
"""
This script is meant to display stats written by mpv --dump-stats=filename.
In general, each line in that file is an event of the form:
@@ -18,17 +21,19 @@ e.g.:
Currently, the following event types are supported:
'signal' <name> singular event
'start' <name> start of the named event
'end' <name> end of the named event
'value' <float> <name> a normal value (as opposed to event)
'event-timed' <ts> <name> singular event at the given timestamp
<name> singular event
'value-timed' <ts> <float> <name>
a value for an event at the given timestamp
<name> singular event (same as 'signal')
"""
class G:
events = {}
sevents = [] # events, deterministically sorted
start = None
# http://matplotlib.org/api/markers_api.html#module-matplotlib.markers
markers = ["o", "8", "s", "p", "*", "h", "+", "x", "D"]
@@ -46,16 +51,15 @@ class Event:
def get_event(event, evtype):
if event not in G.events:
e = Event()
G.events[event] = e
e.name = event
e.vals = []
e.type = evtype
e.marker = "o"
e.numid = len(G.events)
G.sevents = list(G.events.values())
G.sevents.sort(key=lambda x: x.name)
if e.type == "event-signal":
e.marker = find_marker()
if not event_regex.match(e.name):
return e
G.events[event] = e
return G.events[event]
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
@@ -85,25 +89,45 @@ for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
val = int(val) / 1000 - G.start
e = get_event(name, "event-signal")
e.vals.append((val, 1))
elif event.startswith("value-timed "):
_, tsval, val, name = event.split(" ", 3)
tsval = int(tsval) / 1000 - G.start
val = float(val)
e = get_event(name, "value")
e.vals.append((tsval, val))
elif event.startswith("signal "):
name = event.split(" ", 2)[1]
e = get_event(name, "event-signal")
e.vals.append((ts, 1))
else:
e = get_event(event, "event-signal")
e.vals.append((ts, 1))
for e in G.sevents:
e.vals = [(x, y * e.numid / len(G.events)) for (x, y) in e.vals]
plot.hold(True)
mainpl = plot.subplot(2, 1, 1)
legend = []
for e in G.sevents:
# deterministically sort them; make sure the legend is sorted too
G.sevents = list(G.events.values())
G.sevents.sort(key=lambda x: x.name)
hasval = False
for e, index in zip(G.sevents, range(len(G.sevents))):
m = len(G.sevents)
if e.type == "value":
plot.subplot(2, 1, 2, sharex=mainpl)
hasval = True
else:
plot.subplot(2, 1, 1)
pl, = plot.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals]
fig = plot.figure()
fig.hold(True)
ax = [None, None]
plots = 2 if hasval else 1
ax[0] = fig.add_subplot(plots, 1, 1)
if hasval:
ax[1] = fig.add_subplot(plots, 1, 2, sharex=ax[0])
legends = [[], []]
for e in G.sevents:
cur = ax[1 if e.type == "value" else 0]
pl, = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
if e.type == "event-signal":
plot.setp(pl, marker = e.marker, linestyle = "None")
legend.append(pl)
plot.subplot(2, 1, 1)
plot.legend(legend, [pl.get_label() for pl in legend])
for cur in ax:
if cur is not None:
cur.legend()
plot.show()
+8 -8
View File
@@ -6,17 +6,17 @@ cookies_dir="$(mktemp -d /tmp/youtube-dl_mpv.XXXX)"
cookies_file="${cookies_dir}/cookies"
user_agent="$(youtube-dl --dump-user-agent)" # or set whatever you want
video_url="$(youtube-dl \
--user-agent="$user_agent" \
--cookies="$cookies_file" \
video_url=$(youtube-dl \
--user-agent="${user_agent}" \
--cookies="${cookies_file}" \
--get-url \
"$@")"
"$@")
set -f
${MPV:-mpv} \
--cookies \
--cookies-file="$cookies_file" \
--user-agent="$user_agent" \
-- $video_url
--cookies-file="${cookies_file}" \
--user-agent="${user_agent}" \
-- ${video_url}
rm -rf "$cookies_dir"
rm -rf "${cookies_dir}"
+70 -31
View File
@@ -7,7 +7,7 @@ use warnings;
my $mpv = $ARGV[0] || 'mpv';
my @opts = parse_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)', 1);
my @opts = parse_main_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)');
my @ao = parse_opts("$mpv --ao=help", '^ ([^\s\:]*)\s*: (.*)');
my @vo = parse_opts("$mpv --vo=help", '^ ([^\s\:]*)\s*: (.*)');
@@ -54,7 +54,7 @@ my $profile_comp = <<'EOS';
profiles=($profiles $current)
fi
done
if [[ $state == profile ]]; then
if [[ $state == show-profile ]]; then
# For --show-profile, only one allowed
if (( ${#profiles} > 0 )); then
_values 'profile' $profiles && rc=0
@@ -118,7 +118,7 @@ $vf_str
_describe -t values 'video filters' values && rc=0
;;
profile|profiles)
profile|show-profile)
$profile_comp
;;
@@ -133,7 +133,7 @@ $profile_comp
_tags files urls
while _tags; do
_requested files expl 'media file' _files -g \\
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
if _requested urls; then
while _next_label urls expl URL; do
_urls "\$expl[@]" && rc=0
@@ -150,8 +150,73 @@ EOS
print $tmpl;
sub parse_main_opts {
my ($cmd, $regex) = @_;
my @list;
my @lines = split /\n/, `$cmd`;
foreach my $line (@lines) {
my ($name, $desc) = ($line =~ /^$regex/) or next;
next if ($desc eq 'removed' || $desc eq 'alias');
if ($desc =~ /^Flag/) {
push @list, $name;
$name =~ /^--(.*)/;
if ($1 !~ /^(\{|\}|v|list-options|really-quiet|no-.*)$/) {
push @list, "--no-$1";
}
} elsif ($desc =~ /^Print/) {
push @list, $name;
} else {
# Option takes argument
my $entry = $name;
$desc =~ s/\:/\\:/g;
$entry .= "=-:$desc:";
if ($desc =~ /^Choices\\: ([^(]*)/) {
my $choices = $1;
$choices =~ s/ +$//; # strip trailing space
$entry .= "($choices)";
# If "no" is one of the choices, it can also be
# negated like a flag (--no-whatever).
if ($choices =~ /\bno\b/) {
$name =~ s/^--/--no-/;
push @list, $name;
}
} elsif ($line =~ /\[file\]/) {
$entry .= '->files';
} elsif ($name =~ /^--(ao|vo|af|vf|profile|show-profile)$/) {
$entry .= "->$1";
}
push @list, $entry;
}
}
# Sort longest first, because zsh won't complete an option listed
# after one that's a prefix of it.
@list = sort {
$a =~ /([^=]*)/; my $ma = $1;
$b =~ /([^=]*)/; my $mb = $1;
length($mb) <=> length($ma)
} @list;
return @list;
}
sub parse_opts {
my ($cmd, $regex, $parsing_main_options) = @_;
my ($cmd, $regex) = @_;
my @list;
my @lines = split /\n/, `$cmd`;
@@ -163,40 +228,14 @@ sub parse_opts {
my $entry = $1;
if ($parsing_main_options) {
$entry .= '=-';
}
if (defined $2) {
my $desc = $2;
$desc =~ s/\:/\\:/g;
$entry .= ':' . $desc;
}
if ($parsing_main_options) {
$entry .= ':';
$entry .= '->ao' if ($1 eq '--ao');
$entry .= '->vo' if ($1 eq '--vo');
$entry .= '->af' if ($1 eq '--af');
$entry .= '->vf' if ($1 eq '--vf');
$entry .= '->profiles' if ($1 eq '--profile');
$entry .= '->profile' if ($1 eq '--show-profile');
$entry .= '->files' if ($line =~ /\[file\]/);
}
push @list, $entry
}
if ($parsing_main_options) {
@list = sort {
$a =~ /(.*?)\:/; my $ma = $1;
$b =~ /(.*?)\:/; my $mb = $1;
length($mb) <=> length($ma)
} @list;
}
return @list;
}
+1
View File
@@ -0,0 +1 @@
0.7.3
+24
View File
@@ -85,6 +85,7 @@ static const char *const std_layout_names[][2] = {
{"7.1(alsa)", "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
{"7.1(wide)", "fl-fr-fc-lfe-bl-br-flc-frc"},
{"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
{"7.1(rear)", "fl-fr-fc-lfe-bl-br-sdl-sdr"},
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
{"downmix", "dl-dr"},
{"auto", ""}, // not in lavc
@@ -374,6 +375,29 @@ void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
assert(to->speaker[dst[n]] == from->speaker[n]);
}
// Performs the difference between a and b, and store it in diff. If b has
// channels that do not appear in a, those will not appear in the difference.
// To get to those the argument ordering in the function call has to be
// inverted. For the same reason, the diff with a superset will return no
// speakers.
void mp_chmap_diff(const struct mp_chmap *a, const struct mp_chmap *b,
struct mp_chmap *diff)
{
uint64_t a_mask = mp_chmap_to_lavc_unchecked(a);
uint64_t b_mask = mp_chmap_to_lavc_unchecked(b);
mp_chmap_from_lavc(diff, (a_mask ^ b_mask) & a_mask);
}
// Checks whether a contains all the speakers in b
bool mp_chmap_contains(const struct mp_chmap *a, const struct mp_chmap *b)
{
struct mp_chmap d1;
struct mp_chmap d2;
mp_chmap_diff(a, b, &d1);
mp_chmap_diff(b, a, &d2);
return a->num >= b->num && d1.num >= 0 && d2.num == 0;
}
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
// order, return that, e.g. "3.0" instead of "fl-fr-fc".
// Unassigned but valid speakers get names like "sp28".
+5
View File
@@ -120,6 +120,11 @@ void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
const struct mp_chmap *to);
void mp_chmap_diff(const struct mp_chmap *a, const struct mp_chmap *b,
struct mp_chmap *diff);
bool mp_chmap_contains(const struct mp_chmap *a, const struct mp_chmap *b);
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src);
#define mp_chmap_to_str(m) mp_chmap_to_str_buf((char[64]){0}, 64, (m))
+110 -10
View File
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include "common/common.h"
#include "chmap_sel.h"
@@ -81,14 +82,21 @@ void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
}
}
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
// Add a channel map that should be allowed.
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
{
assert(s->num_chmaps < ARRAY_LEN(s->chmaps));
if (mp_chmap_is_valid(map))
s->chmaps[s->num_chmaps++] = *map;
if (!mp_chmap_is_valid(map))
return;
if (!s->chmaps)
s->chmaps = s->chmaps_storage;
if (s->num_chmaps == MP_ARRAY_SIZE(s->chmaps_storage)) {
if (!s->tmp)
return;
s->chmaps = talloc_memdup(s->tmp, s->chmaps, sizeof(s->chmaps_storage));
}
if (s->chmaps != s->chmaps_storage)
MP_TARRAY_GROW(s->tmp, s->chmaps, s->num_chmaps);
s->chmaps[s->num_chmaps++] = *map;
}
// Allow all waveext formats in default order.
@@ -176,18 +184,110 @@ bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
return true;
}
}
if (mp_chmap_sel_fallback(s, map))
return true;
// Fallback to mono/stereo as last resort
if (map->num == 1) {
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
} else if (map->num >= 2) {
*map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
}
*map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
if (test_layout(s, map))
return true;
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
if (test_layout(s, map))
return true;
*map = (struct mp_chmap) {0};
return false;
}
#define UPMIX_IDX 0
#define DOWNMIX_IDX 1
#define FALLBACK_IDX 2
static bool test_fallbacks(struct mp_chmap *a, struct mp_chmap *b,
int best_diffs[2], struct mp_chmap best[2])
{
struct mp_chmap diff1, diff2;
mp_chmap_diff(a, b, &diff1);
if (mp_chmap_contains(a, b) && best_diffs[UPMIX_IDX] > diff1.num) {
best[UPMIX_IDX] = *a;
best_diffs[UPMIX_IDX] = diff1.num;
return true;
}
mp_chmap_diff(b, a, &diff2);
if (mp_chmap_contains(b, a) && best_diffs[DOWNMIX_IDX] > diff2.num) {
best[DOWNMIX_IDX] = *a;
best_diffs[DOWNMIX_IDX] = diff2.num;
return true;
}
if (diff1.num > 0 && best_diffs[FALLBACK_IDX] > diff1.num) {
best[FALLBACK_IDX] = *a;
best_diffs[FALLBACK_IDX] = diff1.num;
return true;
}
if (diff2.num > 0 && best_diffs[FALLBACK_IDX] > diff2.num) {
best[FALLBACK_IDX] = *a;
best_diffs[FALLBACK_IDX] = diff2.num;
return true;
}
return false;
}
// Determine which channel map to fallback to given a source channel map. It
// uses the following heuristic:
// 1) If mono is required always prefer stereo to a multichannel upmix.
// 2) Search for an upmix that is an exact superset of the required chmap.
// 3) Search for a downmix that is the exact subset of the required chmap.
// 4) Search for either an upmix or downmix that is the closest (minimum
// difference of speakers) to the required chmap.
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map)
{
// special case: if possible always fallback mono to stereo (instead of
// looking for a multichannel upmix)
struct mp_chmap mono = MP_CHMAP_INIT_MONO;
struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
if (mp_chmap_equals(&mono, map) && test_layout(s, &stereo)) {
*map = stereo;
return true;
}
int best_diffs[] = { INT_MAX, INT_MAX, INT_MAX };
struct mp_chmap best[] = { {0}, {0}, {0} };
for (int n = 0; n < s->num_chmaps; n++) {
struct mp_chmap e = s->chmaps[n];
if (mp_chmap_is_unknown(&e))
continue;
if (test_fallbacks(&e, map, best_diffs, best))
continue;
// in case we didn't match any fallback retry after replacing speakers
for (int i = 0; i < MP_ARRAY_SIZE(speaker_replacements); i++) {
struct mp_chmap t = e;
struct mp_chmap *r = (struct mp_chmap *)speaker_replacements[i];
if (replace_speakers(&t, r)) {
if (test_fallbacks(&t, map, best_diffs, best))
continue;
}
}
}
for (int i = UPMIX_IDX; i < MP_ARRAY_SIZE(best); i++) {
if (best_diffs[i] < INT_MAX) {
*map = best[i];
return true;
}
}
return false;
}
// Set map to a default layout with num channels. Used for audio APIs that
// return a channel count as part of format negotiation, but give no
// information about the channel layout.
+6 -1
View File
@@ -26,8 +26,12 @@ struct mp_chmap_sel {
// should be considered opaque
bool allow_any, allow_waveext;
bool speakers[MP_SPEAKER_ID_COUNT];
struct mp_chmap chmaps[20];
struct mp_chmap *chmaps;
int num_chmaps;
struct mp_chmap chmaps_storage[20];
void *tmp; // set to any talloc context to allow more chmaps entries
};
void mp_chmap_sel_add_any(struct mp_chmap_sel *s);
@@ -37,6 +41,7 @@ void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s);
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map);
void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id);
bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map);
bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map);
bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
int num);
+1 -1
View File
@@ -136,7 +136,7 @@ static int init(struct dec_audio *da, const char *decoder)
if (sh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
mp_set_avcodec_threads(lavc_context, opts->threads);
mp_set_avcodec_threads(da->log, lavc_context, opts->threads);
/* open it */
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
+7 -2
View File
@@ -280,8 +280,13 @@ static inline void update_ch(af_hrtf_t *s, short *in, const int k)
}
/* We need to update the bass compensation delay line, too. */
s->ba_l[k] = in[0] + in[4] + in[2];
s->ba_r[k] = in[4] + in[1] + in[3];
// TODO: should this use lf/cf/rf etc. instead?
s->ba_l[k] = in[0];
s->ba_r[k] = in[1];
if (s->decode_mode == HRTF_MIX_51) {
s->ba_l[k] += in[4] + in[2];
s->ba_r[k] += in[4] + in[3];
}
}
static void clear_coeff(af_hrtf_t *s, float *c)
+5 -1
View File
@@ -76,8 +76,12 @@ static int control(struct af_instance *af, int cmd, void *arg)
gain += s->rgain_preamp;
af_from_dB(1, &gain, &s->rgain, 20.0, -200.0, 60.0);
if (!s->rgain_clip) // clipping prevention
MP_VERBOSE(af, "Applying replay-gain: %f\n", s->rgain);
if (!s->rgain_clip) { // clipping prevention
s->rgain = MPMIN(s->rgain, 1.0 / peak);
MP_VERBOSE(af, "...with clipping prevention: %f\n", s->rgain);
}
}
if (s->detach && fabs(s->level * s->rgain - 1.0) < 0.00001)
return AF_DETACH;
+5
View File
@@ -45,6 +45,11 @@ int af_fmt2bits(int format)
return af_fmt2bps(format) * 8;
}
bool af_fmt_is_float(int format)
{
return !!(format & AF_FORMAT_F);
}
static int bits_to_mask(int bits)
{
switch (bits) {
+1
View File
@@ -109,6 +109,7 @@ const char *af_fmt_to_str(int format);
int af_fmt2bps(int format);
int af_fmt2bits(int format);
bool af_fmt_is_float(int format);
int af_fmt_change_bits(int format, int bits);
int af_fmt_to_planar(int format);
+2 -2
View File
@@ -36,7 +36,7 @@ struct mixer {
struct ao *ao;
struct af_stream *af;
// Static, dependent on ao/softvol settings
bool softvol; // use AO (true) or af_volume (false)
bool softvol; // use AO (false) or af_volume (true)
bool ao_softvol; // AO has private or per-app volume
bool emulate_mute; // if true, emulate mute with volume=0
// Last known values (possibly out of sync with reality)
@@ -72,7 +72,7 @@ bool mixer_audio_initialized(struct mixer *mixer)
float mixer_getneutralvolume(struct mixer *mixer)
{
// gain == 1
return 1.0 / mixer->opts->softvol_max * 100.0 * 100.0;
return mixer->softvol ? 1.0 / mixer->opts->softvol_max * 100.0 * 100.0 : 100;
}
static void checkvolume(struct mixer *mixer)
+8 -2
View File
@@ -89,11 +89,11 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_SNDIO
&audio_out_sndio,
#endif
&audio_out_null,
// should not be auto-selected:
#if HAVE_COREAUDIO
&audio_out_coreaudio_exclusive,
#endif
&audio_out_null,
// should not be auto-selected:
&audio_out_pcm,
#if HAVE_ENCODING
&audio_out_lavc,
@@ -415,6 +415,12 @@ void ao_request_reload(struct ao *ao)
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map)
{
if (mp_msg_test(ao->log, MSGL_DEBUG)) {
for (int i = 0; i < s->num_chmaps; i++) {
struct mp_chmap c = s->chmaps[i];
MP_DBG(ao, "chmap_sel #%d: %s\n", i, mp_chmap_to_str(&c));
}
}
return mp_chmap_sel_adjust(s, map);
}
+42 -9
View File
@@ -279,7 +279,7 @@ static int find_alsa_channel(int mp_channel)
static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
{
struct priv *p = ao->priv;
struct mp_chmap_sel chmap_sel = {0};
struct mp_chmap_sel chmap_sel = {.tmp = p};
snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(p->alsa);
if (!maps)
@@ -461,17 +461,22 @@ static int init(struct ao *ao)
if (strcmp(device, "default") != 0)
device = talloc_asprintf(ao, "plug:%s", device);
}
const char *old_dev = device;
if (ao->device)
device = ao->device;
if (p->cfg_device && p->cfg_device[0])
device = p->cfg_device;
bool user_set_device = device != old_dev; // not strcmp()
MP_VERBOSE(ao, "using device: %s\n", device);
MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
err = try_open_device(ao, device, p->cfg_block ? 0 : SND_PCM_NONBLOCK);
if (err < 0) {
if (err != -EBUSY && !p->cfg_block) {
if (err == -EBUSY && !user_set_device && strcmp(device, "default") != 0) {
MP_WARN(ao, "Device '%s' busy, retrying default.\n", device);
err = try_open_device(ao, "default", 0);
} else if (err != -EBUSY && !p->cfg_block) {
MP_WARN(ao, "Open in nonblock-mode "
"failed, trying to open in block-mode.\n");
err = try_open_device(ao, device, 0);
@@ -534,7 +539,9 @@ static int init(struct ao *ao)
CHECK_ALSA_ERROR("Unable to set access type");
struct mp_chmap dev_chmap = ao->channels;
if (query_chmaps(ao, &dev_chmap)) {
if (AF_FORMAT_IS_IEC61937(ao->format)) {
dev_chmap.num = 0; // disable chmap API
} else if (query_chmaps(ao, &dev_chmap)) {
ao->channels = dev_chmap;
} else {
dev_chmap.num = 0;
@@ -593,16 +600,23 @@ static int init(struct ao *ao)
for (int c = 0; c < dev_chmap.num; c++)
alsa_chmap->pos[c] = find_alsa_channel(dev_chmap.speaker[c]);
// mpv and ALSA use different conventions for mono
if (dev_chmap.num == 1 && dev_chmap.speaker[0] == MP_SP(FC))
alsa_chmap->pos[0] = SND_CHMAP_MONO;
char tmp[128];
if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
MP_VERBOSE(ao, "trying to set ALSA channel map: %s\n", tmp);
err = snd_pcm_set_chmap(p->alsa, alsa_chmap);
if (err == -ENXIO) {
MP_WARN(ao, "Device does not support requested channel map\n");
MP_WARN(ao, "Device does not support requested channel map (%s)\n",
mp_chmap_to_str(&dev_chmap));
} else {
CHECK_ALSA_WARN("Channel map setup failed");
}
free(alsa_chmap);
}
snd_pcm_chmap_t *alsa_chmap = snd_pcm_get_chmap(p->alsa);
@@ -617,7 +631,9 @@ static int init(struct ao *ao)
MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
if (mp_chmap_is_valid(&chmap)) {
if (AF_FORMAT_IS_IEC61937(ao->format)) {
MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
} else if (mp_chmap_is_valid(&chmap)) {
if (mp_chmap_equals(&chmap, &ao->channels)) {
MP_VERBOSE(ao, "which is what we requested.\n");
} else if (chmap.num == ao->channels.num) {
@@ -630,6 +646,10 @@ static int init(struct ao *ao)
MP_WARN(ao, "Got unknown channel map from ALSA.\n");
}
// mpv and ALSA use different conventions for mono
if (ao->channels.num == 1)
ao->channels.speaker[0] = MP_SP(FC);
free(alsa_chmap);
}
#endif
@@ -756,7 +776,7 @@ static void audio_pause(struct ao *ao)
alsa_error: ;
}
static void audio_resume(struct ao *ao)
static void resume_device(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
@@ -767,6 +787,14 @@ static void audio_resume(struct ao *ao)
while ((err = snd_pcm_resume(p->alsa)) == -EAGAIN)
sleep(1);
}
}
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
resume_device(ao);
if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
@@ -818,10 +846,15 @@ static int play(struct ao *ao, void **data, int samples, int flags)
if (res == -EINTR || res == -EAGAIN) { /* retry */
res = 0;
} else if (res == -ESTRPIPE) { /* suspend */
audio_resume(ao);
} else if (res == -ENODEV) {
MP_WARN(ao, "Device lost, trying to recover...\n");
ao_request_reload(ao);
} else if (res < 0) {
MP_ERR(ao, "Write error: %s\n", snd_strerror(res));
if (res == -ESTRPIPE) { /* suspend */
resume_device(ao);
} else {
MP_ERR(ao, "Write error: %s\n", snd_strerror(res));
}
res = snd_pcm_prepare(p->alsa);
int err = res;
CHECK_ALSA_ERROR("pcm prepare error");
+146 -71
View File
@@ -173,32 +173,83 @@ coreaudio_error:
return CONTROL_ERROR;
}
static bool init_chmap(struct ao *ao)
static AudioChannelLayout* ca_query_layout(struct ao *ao, void *talloc_ctx)
{
struct priv *p = ao->priv;
OSStatus err;
AudioChannelLayout *layouts;
size_t n_layouts;
uint32_t psize;
AudioChannelLayout *r = NULL;
err = CA_GET_ARY_O(p->device,
kAudioDevicePropertyPreferredChannelLayout,
&layouts, &n_layouts);
CHECK_CA_ERROR("could not get audio device prefered layouts");
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertyPreferredChannelLayout,
.mScope = kAudioDevicePropertyScopeOutput,
.mElement = kAudioObjectPropertyElementWildcard,
};
struct mp_chmap_sel chmap_sel = {0};
for (int i = 0; i < n_layouts; i++) {
struct mp_chmap chmap = {0};
if (ca_layout_to_mp_chmap(ao, &layouts[i], &chmap))
mp_chmap_sel_add_map(&chmap_sel, &chmap);
err = AudioObjectGetPropertyDataSize(p->device, &p_addr, 0, NULL, &psize);
CHECK_CA_ERROR("could not get device preferred layout (size)");
r = talloc_size(talloc_ctx, psize);
err = AudioObjectGetPropertyData(p->device, &p_addr, 0, NULL, &psize, r);
CHECK_CA_ERROR("could not get device preferred layout (get)");
coreaudio_error:
return r;
}
static AudioChannelLayout* ca_query_stereo_layout(struct ao *ao, void *talloc_ctx)
{
struct priv *p = ao->priv;
OSStatus err;
const int nch = 2;
uint32_t channels[nch];
AudioChannelLayout *r = NULL;
AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
.mSelector = kAudioDevicePropertyPreferredChannelsForStereo,
.mScope = kAudioDevicePropertyScopeOutput,
.mElement = kAudioObjectPropertyElementWildcard,
};
uint32_t psize = sizeof(channels);
err = AudioObjectGetPropertyData(p->device, &p_addr, 0, NULL, &psize, channels);
CHECK_CA_ERROR("could not get device preferred stereo layout");
psize = sizeof(AudioChannelLayout) + nch * sizeof(AudioChannelDescription);
r = talloc_zero_size(talloc_ctx, psize);
r->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
r->mNumberChannelDescriptions = nch;
AudioChannelDescription desc = {0};
desc.mChannelFlags = kAudioChannelFlags_AllOff;
for(int i = 0; i < nch; i++) {
desc.mChannelLabel = channels[i];
r->mChannelDescriptions[i] = desc;
}
talloc_free(layouts);
coreaudio_error:
return r;
}
if (ao->channels.num < 3) {
struct mp_chmap chmap;
mp_chmap_from_channels(&chmap, ao->channels.num);
static bool init_chmap(struct ao *ao)
{
struct priv *p = ao->priv;
void *ta_ctx = talloc_new(NULL);
struct mp_chmap_sel chmap_sel = {.tmp = p};
struct mp_chmap chmap = {0};
AudioChannelLayout *ml = ca_query_layout(ao, ta_ctx);
if (ml && ca_layout_to_mp_chmap(ao, ml, &chmap))
mp_chmap_sel_add_map(&chmap_sel, &chmap);
}
AudioChannelLayout *sl = ca_query_stereo_layout(ao, ta_ctx);
if (sl && ca_layout_to_mp_chmap(ao, sl, &chmap))
mp_chmap_sel_add_map(&chmap_sel, &chmap);
talloc_free(ta_ctx);
if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
MP_ERR(ao, "could not select a suitable channel map among the "
@@ -336,7 +387,7 @@ static const int speaker_map[][2] = {
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
{ kAudioChannelLabel_Unknown, -1 },
{ kAudioChannelLabel_Unknown, MP_SPEAKER_ID_UNKNOWN0 },
};
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
@@ -347,86 +398,110 @@ static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
return -1;
}
static void ca_log_layout(struct ao *ao, AudioChannelLayout *layout)
static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
{
if (!mp_msg_test(ao->log, MSGL_V))
if (!mp_msg_test(ao->log, l))
return;
AudioChannelDescription *descs = layout->mChannelDescriptions;
MP_VERBOSE(ao, "layout: tag: <%d>, bitmap: <%d>, "
"descriptions <%d>\n",
layout->mChannelLayoutTag,
layout->mChannelBitmap,
layout->mNumberChannelDescriptions);
mp_msg(ao->log, l, "layout: tag: <%u>, bitmap: <%u>, "
"descriptions <%u>\n",
(unsigned) layout->mChannelLayoutTag,
(unsigned) layout->mChannelBitmap,
(unsigned) layout->mNumberChannelDescriptions);
for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
AudioChannelDescription d = descs[i];
MP_VERBOSE(ao, " - description %d: label <%d, %d>, flags: <%u>, "
"coords: <%f, %f, %f>\n", i,
d.mChannelLabel,
ca_label_to_mp_speaker_id(d.mChannelLabel),
d.mChannelFlags,
d.mCoordinates[0],
d.mCoordinates[1],
d.mCoordinates[2]);
mp_msg(ao->log, l, " - description %d: label <%u, %u>, "
" flags: <%u>, coords: <%f, %f, %f>\n", i,
(unsigned) d.mChannelLabel,
(unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
(unsigned) d.mChannelFlags,
d.mCoordinates[0],
d.mCoordinates[1],
d.mCoordinates[2]);
}
}
static AudioChannelLayout *ca_layout_to_custom_layout(
struct ao *ao, void *talloc_ctx, AudioChannelLayout *l)
{
AudioChannelLayoutTag tag = l->mChannelLayoutTag;
AudioChannelLayout *r;
OSStatus err;
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
uint32_t psize;
err = AudioFormatGetPropertyInfo(
kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t), &l->mChannelBitmap, &psize);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
r = talloc_size(NULL, psize);
err = AudioFormatGetProperty(
kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (get)");
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
uint32_t psize;
err = AudioFormatGetPropertyInfo(
kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize);
r = talloc_size(NULL, psize);
CHECK_CA_ERROR("failed to convert channel tag to descriptions (info)");
err = AudioFormatGetProperty(
kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize, r);
CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
} else {
r = l;
}
return r;
coreaudio_error:
return NULL;
}
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap)
{
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
uint32_t layout_size = sizeof(layout);
OSStatus err;
void *talloc_ctx = talloc_new(NULL);
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t),
&layout->mChannelBitmap,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions");
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag),
&layout->mChannelLayoutTag,
&layout_size,
layout);
CHECK_CA_ERROR("failed to convert channel tag to descriptions");
MP_DBG(ao, "input channel layout:\n");
ca_log_layout(ao, MSGL_DEBUG, layout);
AudioChannelLayout *l = ca_layout_to_custom_layout(ao, talloc_ctx, layout);
if (!l)
goto coreaudio_error;
MP_VERBOSE(ao, "converted input channel layout:\n");
ca_log_layout(ao, MSGL_V, l);
if (l->mNumberChannelDescriptions > MP_NUM_CHANNELS) {
MP_VERBOSE(ao, "layout has too many descriptions (%u, max: %d)\n",
(unsigned) l->mNumberChannelDescriptions, MP_NUM_CHANNELS);
return false;
}
ca_log_layout(ao, layout);
// If the channel layout uses channel descriptions, from my
// experiments there are there three possibile cases:
// * The description has a label kAudioChannelLabel_Unknown:
// Can't do anything about this (looks like non surround
// layouts are like this).
// * The description uses positional information: this in
// theory could be used but one would have to map spatial
// positions to labels which is not really feasible.
// * The description has a well known label which can be mapped
// to the waveextensible definition: this is the kind of
// descriptions we process here.
for (int n = 0; n < layout->mNumberChannelDescriptions; n++) {
AudioChannelLabel label = layout->mChannelDescriptions[n].mChannelLabel;
for (int n = 0; n < l->mNumberChannelDescriptions; n++) {
AudioChannelLabel label = l->mChannelDescriptions[n].mChannelLabel;
uint8_t speaker = ca_label_to_mp_speaker_id(label);
if (label == kAudioChannelLabel_Unknown)
continue;
if (speaker < 0) {
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
"bitmap, skipping layout\n", label);
MP_VERBOSE(ao, "channel label=%u unusable to build channel "
"bitmap, skipping layout\n", (unsigned) label);
goto coreaudio_error;
} else {
chmap->speaker[n] = speaker;
chmap->num = n + 1;
}
}
talloc_free(talloc_ctx);
return chmap->num > 0;
coreaudio_error:
ca_log_layout(ao, layout);
MP_VERBOSE(ao, "converted input channel layout (failed):\n");
ca_log_layout(ao, MSGL_V, layout);
talloc_free(talloc_ctx);
return false;
}
+2 -2
View File
@@ -62,7 +62,7 @@ OSStatus ca_get_ary(AudioObjectID id, ca_scope scope, ca_sel selector,
err = AudioObjectGetPropertyDataSize(id, &p_addr, 0, NULL, &p_size);
CHECK_CA_ERROR_SILENT_L(coreaudio_error);
*data = talloc_size(NULL, p_size);
*data = talloc_zero_size(NULL, p_size);
*elements = p_size / element_size;
err = ca_get(id, scope, selector, p_size, *data);
@@ -87,7 +87,7 @@ OSStatus ca_get_str(AudioObjectID id, ca_scope scope, ca_sel selector,
CFStringGetMaximumSizeForEncoding(
CFStringGetLength(string), CA_CFSTR_ENCODING) + 1;
*data = talloc_size(NULL, size);
*data = talloc_zero_size(NULL, size);
CFStringGetCString(string, *data, size, CA_CFSTR_ENCODING);
CFRelease(string);
coreaudio_error:
+2 -1
View File
@@ -65,7 +65,8 @@ coreaudio_error:
OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
{
OSStatus err = noErr;
int selection = name ? strtol(name, (char **)NULL, 10) : -1;
errno = 0;
int selection = name && name[0] ? strtol(name, (char **)NULL, 10) : -1;
if (errno == EINVAL || errno == ERANGE) {
selection = -1;
MP_WARN(ao, "device identifier '%s' is invalid\n", name);
+1 -1
View File
@@ -228,7 +228,7 @@ static int LoadDirectSound(struct ao *ao)
struct priv *p = ao->priv;
// initialize directsound
p->hdsound_dll = LoadLibrary("DSOUND.DLL");
p->hdsound_dll = LoadLibrary(L"DSOUND.DLL");
if (p->hdsound_dll == NULL) {
MP_ERR(ao, "cannot load DSOUND.DLL\n");
return 0;
+8 -5
View File
@@ -38,6 +38,7 @@
#include "config.h"
#include "options/options.h"
#include "common/common.h"
#include "common/msg.h"
#include "osdep/timer.h"
#include "osdep/endian.h"
@@ -107,11 +108,11 @@ static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
#endif
#if !defined(AFMT_U32_NE) && defined(AFMT_U32_LE) && defined(AFMT_U32_BE)
#define AFMT_U32MP_SELECT_LE_BE(AFMT_U32_LE, AFMT_U32_BE)
#define AFMT_U32_NE AFMT_U32MP_SELECT_LE_BE(AFMT_U32_LE, AFMT_U32_BE)
#endif
#if !defined(AFMT_S32_NE) && defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
#define AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
#define AFMT_S32_NE AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
#endif
static const int format_table[][2] = {
@@ -305,14 +306,16 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
p->audio_fd = open(p->dsp, O_WRONLY);
#endif
if (p->audio_fd < 0) {
MP_ERR(ao, "Can't open audio device %s: %s\n", p->dsp, strerror(errno));
MP_ERR(ao, "Can't open audio device %s: %s\n",
p->dsp, mp_strerror(errno));
goto fail;
}
#ifdef __linux__
/* Remove the non-blocking flag */
if (fcntl(p->audio_fd, F_SETFL, 0) < 0) {
MP_ERR(ao, "Can't make file descriptor blocking: %s\n", strerror(errno));
MP_ERR(ao, "Can't make file descriptor blocking: %s\n",
mp_strerror(errno));
goto fail;
}
#endif
@@ -434,7 +437,7 @@ static int init(struct ao *ao)
if ((fd = open(p->oss_mixer_device, O_RDONLY)) == -1) {
MP_ERR(ao, "Can't open mixer device %s: %s\n",
p->oss_mixer_device, strerror(errno));
p->oss_mixer_device, mp_strerror(errno));
} else {
ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
close(fd);
+8 -2
View File
@@ -86,8 +86,12 @@ static void stream_state_cb(pa_stream *s, void *userdata)
struct ao *ao = userdata;
struct priv *priv = ao->priv;
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
MP_VERBOSE(ao, "Stream failed.\n");
ao_request_reload(ao);
pa_threaded_mainloop_signal(priv->mainloop, 0);
break;
case PA_STREAM_READY:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal(priv->mainloop, 0);
break;
@@ -409,6 +413,9 @@ static int init(struct ao *ao)
&format, 1, proplist)))
goto unlock_and_fail;
pa_format_info_free(format);
format = NULL;
pa_proplist_free(proplist);
proplist = NULL;
@@ -782,7 +789,6 @@ const struct ao_driver audio_out_pulse = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.cfg_buffer = 250,
.cfg_latency_hacks = 1,
},
.options = (const struct m_option[]) {
OPT_STRING("host", cfg_host, 0),
+51 -38
View File
@@ -17,9 +17,6 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#define COBJMACROS 1
#define _WIN32_WINNT 0x600
#include <stdlib.h>
#include <inttypes.h>
#include <process.h>
@@ -36,34 +33,39 @@
#include "osdep/timer.h"
#include "osdep/io.h"
static double get_device_delay(struct wasapi_state *state) {
static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
UINT64 sample_count = atomic_load(&state->sample_count);
UINT64 position, qpc_position;
HRESULT hr;
switch (hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position)) {
case S_OK: case S_FALSE:
break;
default:
MP_ERR(state, "IAudioClock::GetPosition returned %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position);
/* GetPosition succeeded, but the result may be inaccurate due to the length of the call */
/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx */
if (hr == S_FALSE) {
MP_DBG(state, "Possibly inaccurate device position.\n");
hr = S_OK;
}
EXIT_ON_ERROR(hr);
LARGE_INTEGER qpc_count;
QueryPerformanceCounter(&qpc_count);
double qpc_diff = (qpc_count.QuadPart * 1e7 / state->qpc_frequency.QuadPart) - qpc_position;
position += state->clock_frequency * (uint64_t)(qpc_diff / 1e7);
position += state->clock_frequency * (uint64_t) (qpc_diff / 1e7);
/* convert position to the same base as sample_count */
position = position * state->format.Format.nSamplesPerSec / state->clock_frequency;
double diff = sample_count - position;
double delay = diff / state->format.Format.nSamplesPerSec;
*delay = diff / state->format.Format.nSamplesPerSec;
MP_TRACE(state, "Device delay: %g samples (%g ms)\n", diff, delay * 1000);
MP_TRACE(state, "Device delay: %g samples (%g ms)\n", diff, *delay * 1000);
return delay;
return S_OK;
exit_label:
MP_ERR(state, "Error getting device delay: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
static void thread_feed(struct ao *ao)
@@ -81,6 +83,9 @@ static void thread_feed(struct ao *ao)
frame_count -= padding;
MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding);
}
double delay;
hr = get_device_delay(state, &delay);
EXIT_ON_ERROR(hr);
BYTE *pData;
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
@@ -88,9 +93,10 @@ static void thread_feed(struct ao *ao)
EXIT_ON_ERROR(hr);
BYTE *data[1] = {pData};
ao_read_data(ao, (void**)data, frame_count, (int64_t) (
mp_time_us() + get_device_delay(state) * 1e6 +
frame_count * 1e6 / state->format.Format.nSamplesPerSec));
mp_time_us() + delay * 1e6 +
frame_count * 1e6 / state->format.Format.nSamplesPerSec));
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
frame_count, 0);
@@ -101,7 +107,7 @@ static void thread_feed(struct ao *ao)
return;
exit_label:
MP_ERR(state, "Error feeding audio: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
wasapi_explain_err(hr), (uint32_t) hr);
MP_VERBOSE(ao, "Requesting ao reload\n");
ao_request_reload(ao);
return;
@@ -110,30 +116,24 @@ exit_label:
static DWORD __stdcall ThreadLoop(void *lpParameter)
{
struct ao *ao = lpParameter;
if (!ao || !ao->priv)
return -1;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
int thread_ret;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
state->init_ret = wasapi_thread_init(ao);
SetEvent(state->init_done);
if (state->init_ret != S_OK) {
thread_ret = -1;
if (state->init_ret != S_OK)
goto exit_label;
}
DWORD waitstatus;
HANDLE playcontrol[] =
{state->hUninit, state->hFeed, state->hForceFeed, NULL};
MP_DBG(ao, "Entering dispatch loop\n");
while (1) { /* watch events */
while (true) { /* watch events */
waitstatus = MsgWaitForMultipleObjects(3, playcontrol, FALSE, INFINITE,
QS_POSTMESSAGE | QS_SENDMESSAGE);
switch (waitstatus) {
case WAIT_OBJECT_0: /*shutdown*/
thread_ret = 0;
MP_DBG(ao, "Thread shutdown\n");
goto exit_label;
case (WAIT_OBJECT_0 + 1): /* feed */
thread_feed(ao);
@@ -143,11 +143,11 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
SetEvent(state->hFeedDone);
break;
case (WAIT_OBJECT_0 + 3): /* messages to dispatch (COM marshalling) */
MP_DBG(ao, "Dispatch\n");
wasapi_dispatch();
break;
default:
MP_ERR(ao, "Unhandled case in thread loop");
thread_ret = -1;
goto exit_label;
}
}
@@ -155,7 +155,8 @@ exit_label:
wasapi_thread_uninit(ao);
CoUninitialize();
return thread_ret;
MP_DBG(ao, "Thread return\n");
return 0;
}
static void closehandles(struct ao *ao)
@@ -174,32 +175,40 @@ static void uninit(struct ao *ao)
MP_DBG(ao, "Uninit wasapi\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
wasapi_release_proxies(state);
SetEvent(state->hUninit);
if (state->hUninit)
SetEvent(state->hUninit);
/* wait up to 10 seconds */
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT){
MP_ERR(ao, "Audio loop thread refuses to abort");
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT) {
MP_ERR(ao, "Audio loop thread refuses to abort\n");
return;
}
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
closehandles(ao);
CoUninitialize();
MP_DBG(ao, "Uninit wasapi done\n");
}
static int init(struct ao *ao)
{
MP_DBG(ao, "Init wasapi\n");
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
ao->format = af_fmt_from_planar(ao->format);
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
MP_ERR(ao, "Error adjusting channel map to waveext channel order\n");
return -1;
}
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
state->log = ao->log;
wasapi_fill_VistaBlob(state);
if(!wasapi_fill_VistaBlob(state))
MP_WARN(ao, "Error loading thread priority functions\n");
if (state->opt_list) {
wasapi_enumerate_devices(state->log, NULL, NULL);
if(!wasapi_enumerate_devices(state->log, NULL, NULL))
MP_WARN(ao, "Error enumerating devices\n");
}
if (state->opt_exclusive) {
@@ -216,16 +225,18 @@ static int init(struct ao *ao)
if (!state->init_done || !state->hFeed || !state->hUninit ||
!state->hForceFeed || !state->hFeedDone)
{
closehandles(ao);
MP_ERR(ao, "Error initing events\n");
uninit(ao);
/* failed to init events */
return -1;
}
state->init_ret = E_FAIL;
state->threadLoop = (HANDLE)CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL);
state->threadLoop = (HANDLE) CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL);
if (!state->threadLoop) {
/* failed to init thread */
MP_ERR(ao, "Failed to create thread\n");
uninit(ao);
return -1;
}
@@ -321,7 +332,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
}
}
static void audio_reset(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
@@ -342,7 +352,10 @@ static void audio_resume(struct ao *ao)
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
wasapi_enumerate_devices(mp_null_log, ao, list);
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if(!wasapi_enumerate_devices(mp_null_log, ao, list))
MP_WARN(ao, "Error enumerating devices\n");
CoUninitialize();
}
#define OPT_BASE_STRUCT struct wasapi_state
+4 -4
View File
@@ -20,9 +20,6 @@
#ifndef MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
#define COBJMACROS 1
#define _WIN32_WINNT 0x600
#include <audioclient.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>
@@ -106,7 +103,10 @@ typedef struct wasapi_state {
int opt_list;
char *opt_device;
/* We still need to support XP, don't use these functions directly, blob owned by main thread */
/* Don't use these functions directly in case
they are unimplemented for some reason.
(XP shouldn't be an issue since it doesn't support wasapi, maybe wine?)
Blob is owned by the main thread */
struct {
HMODULE hAvrt;
HANDLE (WINAPI *pAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD);
+10 -55
View File
@@ -17,9 +17,6 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#define COBJMACROS 1
#define _WIN32_WINNT 0x600
#include <initguid.h>
#include <audioclient.h>
#include <endpointvolume.h>
@@ -30,50 +27,9 @@
#include "ao_wasapi.h"
#include "ao_wasapi_utils.h"
static int GUID_compare(const GUID *l, const GUID *r)
{
unsigned int i;
if (l->Data1 != r->Data1) return 1;
if (l->Data2 != r->Data2) return 1;
if (l->Data3 != r->Data3) return 1;
for (i = 0; i < 8; i++) {
if (l->Data4[i] != r->Data4[i]) return 1;
}
return 0;
}
static int PKEY_compare(const PROPERTYKEY *l, const PROPERTYKEY *r)
{
if (GUID_compare(&l->fmtid, &r->fmtid)) return 1;
if (l->pid != r->pid) return 1;
return 0;
}
static char *GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
{
snprintf(buf, buf_size,
"{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}",
(unsigned) guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
return buf;
}
static char *PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
{
buf = GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
size_t guid_len = strnlen(buf, buf_size);
snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32, (uint32_t)pkey->pid );
return buf;
}
#define PKEY_to_str(pkey) PKEY_to_str_buf((char[42]){0}, 42, (pkey))
static char* ERole_to_str(ERole role)
{
switch(role){
switch (role) {
case eConsole: return "console";
case eMultimedia: return "multimedia";
case eCommunications: return "communications";
@@ -83,7 +39,7 @@ static char* ERole_to_str(ERole role)
static char* EDataFlow_to_str(EDataFlow flow)
{
switch(flow){
switch (flow) {
case eRender: return "render";
case eCapture: return "capture";
case eAll: return "all";
@@ -95,8 +51,8 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
IMMNotificationClient* This, REFIID riid, void **ppvObject)
{
/* Compatible with IMMNotificationClient and IUnknown */
if (!GUID_compare(&IID_IMMNotificationClient, riid) ||
!GUID_compare(&IID_IUnknown, riid)) {
if (IsEqualGUID(&IID_IMMNotificationClient, riid) ||
IsEqualGUID(&IID_IUnknown, riid)) {
*ppvObject = (void *)This;
return S_OK;
} else {
@@ -127,7 +83,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)){
if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
switch (dwNewState) {
case DEVICE_STATE_DISABLED:
case DEVICE_STATE_NOTPRESENT:
@@ -187,8 +143,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
MP_VERBOSE(ao, "New default device %S\n", pwstrDeviceId);
/* don't care about "eCapture" or non-"eMultimedia" roles */
if ( flow == eCapture ||
role != eMultimedia ) return S_OK;
if (flow == eCapture || role != eMultimedia) return S_OK;
/* stay on the device the user specified */
if (state->opt_device) {
@@ -197,7 +152,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
}
/* don't reload if already on the new default */
if ( pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId) ){
if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
MP_VERBOSE(ao, "Already using default device, no reload required\n");
return S_OK;
}
@@ -219,12 +174,12 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged(
if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
MP_VERBOSE(ao, "OnPropertyValueChanged triggered\n");
MP_VERBOSE(ao, "Changed property: ");
if (!PKEY_compare(&PKEY_AudioEngine_DeviceFormat, &key)) {
if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) {
MP_VERBOSE(change->ao,
"PKEY_AudioEngine_DeviceFormat - requesting ao reload\n");
ao_request_reload(change->ao);
} else {
MP_VERBOSE(ao, "%s\n", PKEY_to_str(&key));
MP_VERBOSE(ao, "%s\n", mp_PKEY_to_str(&key));
}
}
return S_OK;
@@ -276,7 +231,7 @@ void wasapi_change_uninit(struct ao *ao)
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
struct change_notify *change = &state->change;
if( state->pEnumerator && change->client.lpVtbl )
if(state->pEnumerator && change->client.lpVtbl)
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(
state->pEnumerator, (IMMNotificationClient *)change);
+312 -261
View File
@@ -17,10 +17,8 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#define COBJMACROS 1
#define _WIN32_WINNT 0x600
#include <math.h>
#include <libavutil/common.h>
#include <initguid.h>
#include <audioclient.h>
#include <endpointvolume.h>
@@ -35,46 +33,67 @@
#define MIXER_DEFAULT_LABEL L"mpv - video player"
#ifndef PKEY_Device_FriendlyName
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,
DEFINE_PROPERTYKEY(mp_PKEY_Device_FriendlyName,
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc,
DEFINE_PROPERTYKEY(mp_PKEY_Device_DeviceDesc,
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);
#endif
/* Supposed to use __uuidof, but it is C++ only, declare our own */
static const GUID local_KSDATAFORMAT_SUBTYPE_PCM = {
0x1, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
};
static const GUID local_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
0x3, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
};
DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_PCM,
0x00000001, 0x0000, 0x0010, 0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
0x00000003, 0x0000, 0x0010, 0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
union WAVEFMT {
WAVEFORMATEX *ex;
WAVEFORMATEXTENSIBLE *extensible;
};
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
{
snprintf(buf, buf_size,
"{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}",
(unsigned) guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
return buf;
}
int wasapi_fill_VistaBlob(wasapi_state *state)
char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
{
buf = mp_GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
size_t guid_len = strnlen(buf, buf_size);
snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32, (uint32_t) pkey->pid);
return buf;
}
bool wasapi_fill_VistaBlob(wasapi_state *state)
{
if (!state)
return 1;
goto exit_label;
state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll");
if (!state->VistaBlob.hAvrt)
goto exit_label;
state->VistaBlob.pAvSetMmThreadCharacteristicsW =
(HANDLE (WINAPI *)(LPCWSTR, LPDWORD))
GetProcAddress(state->VistaBlob.hAvrt, "AvSetMmThreadCharacteristicsW");
GetProcAddress(state->VistaBlob.hAvrt, "AvSetMmThreadCharacteristicsW");
if (!state->VistaBlob.pAvSetMmThreadCharacteristicsW)
goto exit_label;
state->VistaBlob.pAvRevertMmThreadCharacteristics =
(WINBOOL (WINAPI *)(HANDLE))
GetProcAddress(state->VistaBlob.hAvrt, "AvRevertMmThreadCharacteristics");
return 0;
GetProcAddress(state->VistaBlob.hAvrt, "AvRevertMmThreadCharacteristics");
if (!state->VistaBlob.pAvRevertMmThreadCharacteristics)
goto exit_label;
return true;
exit_label:
if (state->VistaBlob.hAvrt)
if (state->VistaBlob.hAvrt) {
FreeLibrary(state->VistaBlob.hAvrt);
return 1;
state->VistaBlob.hAvrt = NULL;
}
return false;
}
const char *wasapi_explain_err(const HRESULT hr)
@@ -85,7 +104,11 @@ const char *wasapi_explain_err(const HRESULT hr)
E(E_FAIL)
E(E_OUTOFMEMORY)
E(E_POINTER)
E(E_HANDLE)
E(E_NOTIMPL)
E(E_INVALIDARG)
E(REGDB_E_IIDNOTREG)
E(CO_E_NOTINITIALIZED)
E(AUDCLNT_E_NOT_INITIALIZED)
E(AUDCLNT_E_ALREADY_INITIALIZED)
E(AUDCLNT_E_WRONG_ENDPOINT_TYPE)
@@ -123,148 +146,216 @@ const char *wasapi_explain_err(const HRESULT hr)
#undef E
}
static void set_format(WAVEFORMATEXTENSIBLE *wformat, WORD bytepersample,
DWORD samplerate, WORD channels, DWORD chanmask)
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
WORD bits, WORD valid_bits, bool is_float,
DWORD samplerate, struct mp_chmap *channels)
{
int block_align = channels * bytepersample;
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; /* Only PCM is supported */
wformat->Format.nChannels = channels;
int block_align = channels->num * bits / 8;
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
wformat->Format.nAvgBytesPerSec = samplerate * block_align;
wformat->Format.nBlockAlign = block_align;
wformat->Format.wBitsPerSample = bytepersample * 8;
wformat->Format.cbSize =
22; /* must be at least 22 for WAVE_FORMAT_EXTENSIBLE */
if (bytepersample == 4)
wformat->SubFormat = local_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
wformat->Format.wBitsPerSample = bits;
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
if (is_float)
wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
wformat->SubFormat = local_KSDATAFORMAT_SUBTYPE_PCM;
wformat->Samples.wValidBitsPerSample = wformat->Format.wBitsPerSample;
wformat->dwChannelMask = chanmask;
wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM;
wformat->Samples.wValidBitsPerSample = valid_bits;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
}
static int format_set_bits(int old_format, int bits, int fp)
static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
{
if (fp) {
switch (bits) {
case 64: return AF_FORMAT_DOUBLE;
case 32: return AF_FORMAT_FLOAT;
default: return 0;
}
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
return wformat->Samples.wValidBitsPerSample;
} else {
return wf->wBitsPerSample;
}
return af_fmt_change_bits(old_format, bits);
}
static int set_ao_format(struct wasapi_state *state,
struct ao *const ao,
WAVEFORMATEXTENSIBLE wformat)
static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
{
if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) {
MP_ERR(ao, "Unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
return 0;
char* type = "?";
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
&wformat->SubFormat))
type = "float";
else if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_PCM,
&wformat->SubFormat))
type = "s";
break;
}
case WAVE_FORMAT_IEEE_FLOAT:
type = "float";
break;
case WAVE_FORMAT_PCM:
type = "s";
break;
}
int valid_bits = waveformat_valid_bits(wf);
if (valid_bits == wf->wBitsPerSample) {
snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" @ %"PRIu32"hz",
wf->nChannels, type, valid_bits, (unsigned) wf->nSamplesPerSec);
} else {
snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" (in %s%"PRIu16") @ %"PRIu32"hz",
wf->nChannels, type, valid_bits, type, wf->wBitsPerSample,
(unsigned) wf->nSamplesPerSec);
}
return buf;
}
#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
// .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT
int format = format_set_bits(ao->format,
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
static bool waveformat_is_float(WAVEFORMATEX *wf)
{
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
return IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, &wformat->SubFormat);
}
case WAVE_FORMAT_IEEE_FLOAT:
return true;
default:
return false;
}
}
static bool waveformat_is_pcm(WAVEFORMATEX *wf)
{
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
return IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_PCM, &wformat->SubFormat);
}
case WAVE_FORMAT_PCM:
return true;
default:
return false;
}
}
static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
{
if (src->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
*dst = *(WAVEFORMATEXTENSIBLE *)src;
else
dst->Format = *src;
}
static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
// explicitly disallow 8 bits - this will catch if the
// closestMatch returned by IsFormatSupported in try_format below returns
// a bit depth of less than 16
if (wf->wBitsPerSample < 16)
return false;
int format;
if (waveformat_is_float(wf)) {
format = AF_FORMAT_FLOAT;
} else if (waveformat_is_pcm(wf)) {
format = AF_FORMAT_S32;
} else {
MP_ERR(ao, "Unknown WAVEFORMAT\n");
return false;
}
// set the correct number of bits (the 32-bits assumed above may be wrong)
format = af_fmt_change_bits(format, wf->wBitsPerSample);
if (!format)
return 0;
return false;
ao->samplerate = wformat.Format.nSamplesPerSec;
ao->bps = wformat.Format.nAvgBytesPerSec;
ao->samplerate = wf->nSamplesPerSec;
ao->bps = wf->nAvgBytesPerSec;
ao->format = format;
if (ao->channels.num != wformat.Format.nChannels) {
mp_chmap_from_channels(&ao->channels, wformat.Format.nChannels);
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
mp_chmap_from_waveext(&ao->channels, wformat->dwChannelMask);
} else {
mp_chmap_from_channels(&ao->channels, wf->nChannels);
}
state->format = wformat;
return 1;
if (ao->channels.num != wf->nChannels) {
MP_ERR(ao, "Channel map doesn't match number of channels\n");
return false;
}
waveformat_copy(&state->format, wf);
return true;
}
static int try_format(struct wasapi_state *state,
struct ao *const ao,
int bits, int samplerate,
const struct mp_chmap channels)
static bool try_format(struct ao *ao,
int bits, int valid_bits, bool is_float,
int samplerate, struct mp_chmap *channels)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
/* If for some reason we get 8-bits, try 16-bit equivalent instead.
This can happen if 8-bits is initially requested in ao->format
or in the unlikely event that GetMixFormat return 8-bits.
If someone with a "vintage" sound card or something complains
this can be reconsidered */
bits = FFMAX(16, bits);
WAVEFORMATEXTENSIBLE wformat;
set_format(&wformat, bits / 8, samplerate, channels.num, mp_chmap_to_waveext(&channels));
int af_format = format_set_bits(ao->format, bits, bits == 32);
if (!af_format)
return 0;
MP_VERBOSE(ao, "Trying %dch %s @ %dhz\n",
channels.num, af_fmt_to_str(af_format), samplerate);
union WAVEFMT u;
u.extensible = &wformat;
set_waveformat(&wformat, bits, valid_bits, is_float, samplerate, channels);
MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat.Format));
WAVEFORMATEX *closestMatch;
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode,
u.ex, &closestMatch);
&wformat.Format, &closestMatch);
if (closestMatch) {
if (closestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
u.ex = closestMatch;
wformat = *u.extensible;
} else {
wformat.Format = *closestMatch;
}
waveformat_copy(&wformat, closestMatch);
CoTaskMemFree(closestMatch);
}
if (hr == S_FALSE) {
if (set_ao_format(state, ao, wformat)) {
if (set_ao_format(ao, &wformat.Format)) {
MP_VERBOSE(ao, "Accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
return 1;
return true;
}
} if (hr == S_OK || (!state->opt_exclusive && hr == AUDCLNT_E_UNSUPPORTED_FORMAT)) {
// AUDCLNT_E_UNSUPPORTED_FORMAT here means "works in shared, doesn't in exclusive"
if (set_ao_format(state, ao, wformat)) {
} else if (hr == S_OK) {
if (set_ao_format(ao, &wformat.Format)) {
MP_VERBOSE(ao, "%dch %s @ %dhz accepted\n",
ao->channels.num, af_fmt_to_str(af_format), samplerate);
return 1;
ao->channels.num, af_fmt_to_str(ao->format), samplerate);
return true;
}
}
return 0;
return false;
}
static int try_mix_format(struct wasapi_state *state,
struct ao *const ao)
static bool try_mix_format(struct ao *ao)
{
WAVEFORMATEX *deviceFormat = NULL;
WAVEFORMATEX *closestMatch = NULL;
int ret = 0;
HRESULT hr = IAudioClient_GetMixFormat(state->pAudioClient, &deviceFormat);
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
WAVEFORMATEX *wf = NULL;
HRESULT hr = IAudioClient_GetMixFormat(state->pAudioClient, &wf);
EXIT_ON_ERROR(hr);
union WAVEFMT u;
u.ex = deviceFormat;
WAVEFORMATEXTENSIBLE wformat = *u.extensible;
bool ret = try_format(ao, wf->wBitsPerSample, waveformat_valid_bits(wf),
waveformat_is_float(wf), wf->nSamplesPerSec, &ao->channels);
ret = try_format(state, ao, wformat.Format.wBitsPerSample,
wformat.Format.nSamplesPerSec, ao->channels);
if (ret)
state->format = wformat;
exit_label:
SAFE_RELEASE(deviceFormat, CoTaskMemFree(deviceFormat));
SAFE_RELEASE(closestMatch, CoTaskMemFree(closestMatch));
SAFE_RELEASE(wf, CoTaskMemFree(wf));
return ret;
exit_label:
MP_ERR(state, "Error getting mix format: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
SAFE_RELEASE(wf, CoTaskMemFree(wf));
return false;
}
static int try_passthrough(struct wasapi_state *state,
struct ao *const ao)
static bool try_passthrough(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
WAVEFORMATEXTENSIBLE wformat = {
.Format = {
.wFormatTag = WAVE_FORMAT_EXTENSIBLE,
@@ -277,129 +368,94 @@ static int try_passthrough(struct wasapi_state *state,
},
.Samples.wValidBitsPerSample = 16,
.dwChannelMask = mp_chmap_to_waveext(&ao->channels),
.SubFormat = local_KSDATAFORMAT_SUBTYPE_PCM,
.SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM,
};
wformat.SubFormat.Data1 = WAVE_FORMAT_DOLBY_AC3_SPDIF; // see INIT_WAVEFORMATEX_GUID macro
union WAVEFMT u;
u.extensible = &wformat;
MP_VERBOSE(ao, "Trying passthrough for %s...\n", af_fmt_to_str(ao->format));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode,
u.ex, NULL);
&wformat.Format, NULL);
if (!FAILED(hr)) {
ao->format = ao->format;
state->format = wformat;
return 1;
return true;
}
return 0;
return false;
}
static int find_formats(struct ao *const ao)
static bool search_sample_formats(struct ao *ao, int samplerate, struct mp_chmap *channels)
{
// try float
for (int bits = 64; bits; bits -= 32) {
if (try_format(ao, bits, bits, true, samplerate, channels))
return true;
}
// try int
// some common bit depths / container sizes (requests welcome)
int bits[] = {32, 24, 32, 24, 32, 16};
int valid_bits[] = {32, 24, 24, 20, 20, 16};
for (int i = 0; i < MP_ARRAY_SIZE(bits); i++) {
if (try_format(ao, bits[i], valid_bits[i], false, samplerate, channels))
return true;
}
return false;
}
static bool find_formats(struct ao *ao)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (AF_FORMAT_IS_IEC61937(ao->format)) {
if (try_passthrough(state, ao))
return 0;
if (try_passthrough(ao))
return true;
MP_ERR(ao, "Couldn't use passthrough");
if (!state->opt_exclusive)
MP_ERR(ao, " (try exclusive mode)");
MP_ERR(ao, "\n");
return -1;
return false;
}
/* See if the format works as-is */
int bits = af_fmt2bits(ao->format);
/* don't try 8bits -- there are various 8bit modes other than PCM (*-law et al);
let's just stick to PCM or float here. */
if (bits == 8) {
bits = 16;
} else if (try_format(state, ao, bits, ao->samplerate, ao->channels)) {
return 0;
}
if (try_format(ao, bits, bits, af_fmt_is_float(ao->format),
ao->samplerate, &ao->channels))
return true;
if (!state->opt_exclusive) {
/* shared mode, we can use the system default mix format. */
if (try_mix_format(state, ao)) {
return 0;
}
if (try_mix_format(ao))
return true;
MP_WARN(ao, "Couldn't use default mix format\n");
}
/* Exclusive mode, we have to guess. */
/* as far as testing shows, only PCM 16/24LE (44100Hz - 192kHz) is supported
* Tested on Realtek High Definition Audio, (Realtek Semiconductor Corp. 6.0.1.6312)
* Drivers dated 2/18/2011
*/
/* try float first for non-16bit audio */
if (bits != 16) {
bits = 32;
}
int start_bits = bits;
while (1) { // not infinite -- returns at bottom
for (; bits > 8; bits -= 8) {
int samplerate = ao->samplerate;
if (try_format(state, ao, bits, samplerate, ao->channels)) {
return 0;
}
// make samplerate fit in [44100 192000]
// we check for samplerate > 96k so that we can upsample instead of downsampling later
if (samplerate < 44100 || samplerate > 96000) {
if (samplerate < 44100)
samplerate = 44100;
if (samplerate > 96000)
samplerate = 192000;
if (try_format(state, ao, bits, samplerate, ao->channels)) {
return 0;
}
}
// try bounding to 96kHz
if (samplerate > 48000) {
samplerate = 96000;
if (try_format(state, ao, bits, samplerate, ao->channels)) {
return 0;
}
}
// try bounding to 48kHz
if (samplerate > 44100) {
samplerate = 48000;
if (try_format(state, ao, bits, samplerate, ao->channels)) {
return 0;
}
}
/* How bad is this? try 44100hz, but only on 16bit */
if (bits == 16 && samplerate != 44100) {
samplerate = 44100;
if (try_format(state, ao, bits, samplerate, ao->channels)) {
return 0;
}
}
while (true) {
int samplerate = ao->samplerate;
// try integer multiples of requested sample rate first
while (samplerate <= 384000) {
if (search_sample_formats(ao, samplerate, &ao->channels))
return true;
samplerate += ao->samplerate;
}
if (ao->channels.num > 6) {
/* Maybe this is 5.1 hardware with no support for more. */
bits = start_bits;
mp_chmap_from_channels(&ao->channels, 6);
} else if (ao->channels.num != 2) {
/* Poor quality hardware? Try stereo mode, go through the list again. */
bits = start_bits;
mp_chmap_from_channels(&ao->channels, 2);
// now try list of typical sample rates (requests welcome)
int samplerates[] = {384000, 352800, 192000, 176400, 96000, 88200,
48000, 44100, 32000, 22050, 16000, 11025,
8000};
for (int i = 0; i < MP_ARRAY_SIZE(samplerates); i++) {
if (search_sample_formats(ao, samplerates[i], &ao->channels))
return true;
}
if (ao->channels.num > 1) {
mp_chmap_from_channels(&ao->channels, ao->channels.num - 1);
} else {
MP_ERR(ao, "Couldn't find acceptable audio format\n");
return -1;
return false;
}
}
}
@@ -424,7 +480,7 @@ static HRESULT init_clock(struct wasapi_state *state) {
return S_OK;
exit_label:
MP_ERR(state, "Error obtaining the audio device's timing: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
@@ -434,7 +490,7 @@ static HRESULT init_session_display(struct wasapi_state *state) {
hr = IAudioClient_GetService(state->pAudioClient,
&IID_IAudioSessionControl,
(void **) &state->pSessionControl);
(void **)&state->pSessionControl);
EXIT_ON_ERROR(hr);
GetModuleFileNameW(NULL, path, MAX_PATH);
@@ -447,9 +503,9 @@ static HRESULT init_session_display(struct wasapi_state *state) {
return S_OK;
exit_label:
MP_ERR(state, "Error setting audio session display name: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
return hr;
MP_WARN(state, "Error setting audio session display name: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
return S_OK; // No reason to abort initialization.
}
static HRESULT fix_format(struct ao *ao)
@@ -463,15 +519,16 @@ static HRESULT fix_format(struct ao *ao)
MP_VERBOSE(state, "Device period: %.2g ms\n", (double) devicePeriod / 10000.0 );
/* integer multiple of device period close to 50ms */
bufferPeriod = bufferDuration = ceil( 50.0 * 10000.0 / devicePeriod ) * devicePeriod;
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
bufferPeriod = 0;
bufferPeriod = bufferDuration = ceil(50.0 * 10000.0 / devicePeriod) * devicePeriod;
/* handle unsupported buffer size */
/* hopefully this shouldn't happen because of the above integer device period */
/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875%28v=vs.85%29.aspx */
int retries=0;
reinit:
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
bufferPeriod = 0;
MP_DBG(state, "IAudioClient::Initialize\n");
hr = IAudioClient_Initialize(state->pAudioClient,
state->share_mode,
@@ -482,21 +539,18 @@ reinit:
NULL);
/* something about buffer sizes on Win7 */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s (0x%"PRIx32"), used %lld * 100ns\n",
wasapi_explain_err(hr), (uint32_t)hr, bufferDuration);
if (retries > 0) {
hr = E_FAIL;
if (retries > 0)
EXIT_ON_ERROR(hr);
} else {
else
retries ++;
}
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s (0x%"PRIx32"), used %lld * 100ns\n",
wasapi_explain_err(hr), (uint32_t) hr, bufferDuration);
IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
bufferPeriod = bufferDuration =
(REFERENCE_TIME)((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
state->bufferFrameCount) + 0.5);
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
bufferPeriod = 0;
(REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
state->bufferFrameCount) + 0.5);
IAudioClient_Release(state->pAudioClient);
state->pAudioClient = NULL;
@@ -516,7 +570,7 @@ reinit:
MP_DBG(state, "IAudioClient::Initialize pAudioVolume\n");
hr = IAudioClient_GetService(state->pAudioClient,
&IID_ISimpleAudioVolume,
(void **) &state->pAudioVolume);
(void **)&state->pAudioVolume);
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize IAudioClient_SetEventHandle\n");
@@ -533,8 +587,8 @@ reinit:
state->format.Format.wBitsPerSample / 8 *
state->bufferFrameCount;
bufferDuration =
(REFERENCE_TIME)((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
state->bufferFrameCount) + 0.5);
(REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
state->bufferFrameCount) + 0.5);
MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%.2g ms)\n",
state->bufferFrameCount, (double) bufferDuration / 10000.0 );
@@ -544,15 +598,18 @@ reinit:
hr = init_session_display(state);
EXIT_ON_ERROR(hr);
state->hTask =
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
if (state->VistaBlob.hAvrt) {
state->hTask =
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
}
MP_VERBOSE(state, "Format fixed. Using %lld byte buffer block size\n",
(long long) state->buffer_block_size);
return S_OK;
exit_label:
MP_ERR(state, "Error initializing device: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
@@ -594,7 +651,7 @@ static char* get_device_name(IMMDevice *pDevice) {
PROPVARIANT devname;
PropVariantInit(&devname);
hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName, &devname);
hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName, &devname);
EXIT_ON_ERROR(hr);
namestr = mp_to_utf8(NULL, devname.pwszVal);
@@ -619,7 +676,7 @@ static char* get_device_desc(IMMDevice *pDevice) {
PROPVARIANT devdesc;
PropVariantInit(&devdesc);
hr = IPropertyStore_GetValue(pProps, &PKEY_Device_DeviceDesc, &devdesc);
hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_DeviceDesc, &devdesc);
EXIT_ON_ERROR(hr);
desc = mp_to_utf8(NULL, devdesc.pwszVal);
@@ -724,25 +781,21 @@ exit_label:
return hr;
}
int wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
bool wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
struct ao_device_list *list)
{
HRESULT hr;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
hr = enumerate_with_state(log, ao, list, "Active devices:",
DEVICE_STATE_ACTIVE, 1);
EXIT_ON_ERROR(hr);
hr = enumerate_with_state(log, ao, list, "Unplugged devices:",
DEVICE_STATE_UNPLUGGED, 0);
EXIT_ON_ERROR(hr);
CoUninitialize();
return 0;
return true;
exit_label:
mp_err(log, "Error enumerating devices: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
CoUninitialize();
return 1;
wasapi_explain_err(hr), (uint32_t) hr);
return false;
}
static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
@@ -760,7 +813,7 @@ static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerat
return S_OK;
exit_label:
MP_ERR(ao , "Error loading default device: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
@@ -869,7 +922,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
exit_label:
SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
CoTaskMemFree(deviceID);
return hr;
@@ -889,7 +942,7 @@ int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
int devno = (int) strtol(param.start, &end, 10);
int ret = 1;
if ((end == (void*)param.start || *end) && devno < 0)
if ((end == (void *)param.start || *end) && devno < 0)
ret = M_OPT_OUT_OF_RANGE;
mp_dbg(log, "device=%s %svalid\n", param.start, ret == 1 ? "" : "not ");
@@ -899,10 +952,8 @@ int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
HRESULT hr;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
#define UNMARSHAL(type, to, from) do { \
hr = CoGetInterfaceAndReleaseStream((from), &(type), (void**) &(to)); \
hr = CoGetInterfaceAndReleaseStream((from), &(type), (void **)&(to)); \
(from) = NULL; \
EXIT_ON_ERROR(hr); \
} while (0)
@@ -914,11 +965,10 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
#undef UNMARSHAL
return S_OK;
exit_label:
if (hr != S_OK) {
MP_ERR(state, "Error reading COM proxy: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
}
MP_ERR(state, "Error reading COM proxy: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
@@ -927,8 +977,6 @@ void wasapi_release_proxies(wasapi_state *state) {
SAFE_RELEASE(state->pAudioVolumeProxy, IUnknown_Release(state->pAudioVolumeProxy));
SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy));
SAFE_RELEASE(state->pSessionControlProxy, IUnknown_Release(state->pSessionControlProxy));
CoUninitialize();
}
static HRESULT create_proxies(struct wasapi_state *state) {
@@ -938,7 +986,7 @@ static HRESULT create_proxies(struct wasapi_state *state) {
hr = CreateStreamOnHGlobal(NULL, TRUE, &(to)); \
EXIT_ON_ERROR(hr); \
hr = CoMarshalInterThreadInterfaceInStream(&(type), \
(IUnknown*) (from), \
(IUnknown *)(from), \
&(to)); \
EXIT_ON_ERROR(hr); \
} while (0)
@@ -948,13 +996,16 @@ static HRESULT create_proxies(struct wasapi_state *state) {
MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume);
MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl);
return S_OK;
exit_label:
MP_ERR(state, "Error creating COM proxy: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
static void destroy_proxies(struct wasapi_state *state) {
SAFE_RELEASE(state->sAudioClient, IUnknown_Release(state->sAudioClient));
SAFE_RELEASE(state->sAudioVolume, IUnknown_Release(state->sAudioVolume));
SAFE_RELEASE(state->sAudioClient, IUnknown_Release(state->sAudioClient));
SAFE_RELEASE(state->sAudioVolume, IUnknown_Release(state->sAudioVolume));
SAFE_RELEASE(state->sEndpointVolume, IUnknown_Release(state->sEndpointVolume));
SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));
}
@@ -977,7 +1028,7 @@ retry:
state->initial_volume = -1.0;
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
&IID_IMMDeviceEnumerator, (void**)&state->pEnumerator);
&IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
EXIT_ON_ERROR(hr);
char *device = state->opt_device;
@@ -1009,21 +1060,21 @@ retry:
MP_DBG(ao, "Query hardware volume support\n");
hr = IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume,
&state->vol_hw_support);
if ( hr != S_OK )
MP_WARN(ao, "Query hardware volume control: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
if (hr != S_OK)
MP_WARN(ao, "Error querying hardware volume control: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t) hr);
MP_DBG(ao, "Probing formats\n");
if (find_formats(ao)){
if (!find_formats(ao)) {
hr = E_FAIL;
EXIT_ON_ERROR(hr);
}
MP_DBG(ao, "Fixing format\n");
hr = fix_format(ao);
if ( (hr == AUDCLNT_E_DEVICE_IN_USE ||
hr == AUDCLNT_E_DEVICE_INVALIDATED) &&
retry_wait <= 8 ) {
if ((hr == AUDCLNT_E_DEVICE_IN_USE ||
hr == AUDCLNT_E_DEVICE_INVALIDATED) &&
retry_wait <= 8) {
wasapi_thread_uninit(ao);
MP_WARN(ao, "Retrying in %"PRId64" us\n", retry_wait);
mp_sleep_us(retry_wait);
@@ -1052,7 +1103,7 @@ retry:
return S_OK;
exit_label:
MP_ERR(state, "Error setting up audio thread: %s (0x%"PRIx32")\n",
wasapi_explain_err(hr), (uint32_t)hr);
wasapi_explain_err(hr), (uint32_t) hr);
return hr;
}
+8 -3
View File
@@ -27,12 +27,17 @@
#include "ao.h"
#include "internal.h"
int wasapi_fill_VistaBlob(wasapi_state *state);
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid);
char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey);
#define mp_GUID_to_str(guid) mp_GUID_to_str_buf((char[40]){0}, 40, (guid))
#define mp_PKEY_to_str(pkey) mp_PKEY_to_str_buf((char[42]){0}, 42, (pkey))
bool wasapi_fill_VistaBlob(wasapi_state *state);
const char *wasapi_explain_err(const HRESULT hr);
int wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
struct ao_device_list *list);
bool wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
struct ao_device_list *list);
int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
+7 -6
View File
@@ -22,6 +22,7 @@
#include <libavutil/dict.h>
#include <libavutil/opt.h>
#include <libavutil/error.h>
#include <libavutil/cpu.h>
#include <libavcodec/avcodec.h>
#include "common/common.h"
@@ -30,8 +31,6 @@
#include "av_common.h"
#include "codecs.h"
#include "osdep/numcores.h"
int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size)
{
if (size) {
@@ -126,19 +125,21 @@ void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
dst->dts = mp_pts_to_av(mpkt ? mpkt->dts : MP_NOPTS_VALUE, tb);
}
void mp_set_avcodec_threads(AVCodecContext *avctx, int threads)
void mp_set_avcodec_threads(struct mp_log *l, AVCodecContext *avctx, int threads)
{
if (threads == 0) {
threads = default_thread_count();
threads = av_cpu_count();
if (threads < 1) {
av_log(avctx, AV_LOG_WARNING, "Could not determine "
"thread count to use, defaulting to 1.\n");
mp_warn(l, "Could not determine thread count to use, defaulting to 1.\n");
threads = 1;
} else {
mp_verbose(l, "Detected %d logical cores.\n", threads);
}
// Apparently some libavcodec versions have or had trouble with more
// than 16 threads, and/or print a warning when using > 16.
threads = MPMIN(threads, 16);
}
mp_verbose(l, "Requesting %d threads for decoding.\n", threads);
avctx->thread_count = threads;
}
+1 -1
View File
@@ -34,7 +34,7 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb);
int64_t mp_pts_to_av(double mp_pts, AVRational *tb);
double mp_pts_from_av(int64_t av_pts, AVRational *tb);
void mp_set_avcodec_threads(AVCodecContext *avctx, int threads);
void mp_set_avcodec_threads(struct mp_log *l, AVCodecContext *avctx, int threads);
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type);
int mp_codec_to_av_codec_id(const char *codec);
const char *mp_codec_from_av_codec_id(int codec_id);
+9
View File
@@ -20,6 +20,7 @@
#include <assert.h>
#include <libavutil/common.h>
#include <libavutil/error.h>
#include "talloc.h"
#include "misc/bstr.h"
@@ -243,3 +244,11 @@ bool mp_append_escaped_string(void *talloc_ctx, bstr *dst, bstr *src)
}
return false;
}
// Behaves like strerror()/strerror_r(), but is thread- and GNU-safe.
char *mp_strerror_buf(char *buf, size_t buf_size, int errnum)
{
// This handles the nasty details of calling the right function for us.
av_strerror(AVERROR(errnum), buf, buf_size);
return buf;
}
+3
View File
@@ -89,4 +89,7 @@ bool mp_append_escaped_string_noalloc(void *talloc_ctx, struct bstr *dst,
bool mp_append_escaped_string(void *talloc_ctx, struct bstr *dst,
struct bstr *src);
char *mp_strerror_buf(char *buf, size_t buf_size, int errnum);
#define mp_strerror(e) mp_strerror_buf((char[80]){0}, 80, e)
#endif /* MPLAYER_MPCOMMON_H */
+3 -2
View File
@@ -185,10 +185,11 @@ static void flush_status_line(struct mp_log_root *root)
root->blank_lines = 0;
}
void mp_msg_flush_status_line(struct mpv_global *global)
void mp_msg_flush_status_line(struct mp_log *log)
{
pthread_mutex_lock(&mp_msg_lock);
flush_status_line(global->log->root);
if (log->root)
flush_status_line(log->root);
pthread_mutex_unlock(&mp_msg_lock);
}
+2 -1
View File
@@ -9,9 +9,10 @@ void mp_msg_uninit(struct mpv_global *global);
void mp_msg_update_msglevels(struct mpv_global *global);
void mp_msg_mute(struct mpv_global *global, bool mute);
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr);
void mp_msg_flush_status_line(struct mpv_global *global);
bool mp_msg_has_status_line(struct mpv_global *global);
void mp_msg_flush_status_line(struct mp_log *log);
struct mp_log_buffer_entry {
char *prefix;
int level;
+3 -3
View File
@@ -29,15 +29,15 @@ void mp_tags_set_bstr(struct mp_tags *tags, bstr key, bstr value)
for (int n = 0; n < tags->num_keys; n++) {
if (bstrcasecmp0(key, tags->keys[n]) == 0) {
talloc_free(tags->values[n]);
tags->values[n] = talloc_strndup(tags, value.start, value.len);
tags->values[n] = bstrto0(tags, value);
return;
}
}
MP_RESIZE_ARRAY(tags, tags->keys, tags->num_keys + 1);
MP_RESIZE_ARRAY(tags, tags->values, tags->num_keys + 1);
tags->keys[tags->num_keys] = talloc_strndup(tags, key.start, key.len);
tags->values[tags->num_keys] = talloc_strndup(tags, value.start, value.len);
tags->keys[tags->num_keys] = bstrto0(tags, key);
tags->values[tags->num_keys] = bstrto0(tags, value);
tags->num_keys++;
}
+17
View File
@@ -421,3 +421,20 @@ void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le");
sh->codec = talloc_strdup(sh->audio, codec);
}
static const char *const mimetype_to_codec[][2] = {
{"image/jpeg", "mjpeg"},
{"image/png", "png"},
{0}
};
const char *mp_map_mimetype_to_video_codec(const char *mimetype)
{
if (mimetype) {
for (int n = 0; mimetype_to_codec[n][0]; n++) {
if (strcmp(mimetype_to_codec[n][0], mimetype) == 0)
return mimetype_to_codec[n][1];
}
}
return NULL;
}
+2
View File
@@ -30,4 +30,6 @@ void mp_set_codec_from_tag(struct sh_stream *sh);
void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
bool is_be);
const char *mp_map_mimetype_to_video_codec(const char *mimetype);
#endif
+1 -1
View File
@@ -554,7 +554,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);
r = *out_pkt ? 1 : (ds->eof ? -1 : 0);
r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0);
ds->active = ds->selected; // enable readahead
ds->in->eof = false; // force retry
pthread_cond_signal(&ds->in->wakeup); // possibly read more
+4
View File
@@ -286,6 +286,8 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
.buf_size = 0,
.buf = av_mallocz(PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE),
};
if (!avpd.buf)
return -1;
bool final_probe = false;
do {
@@ -679,6 +681,8 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
return -1;
avfc = avformat_alloc_context();
if (!avfc)
return -1;
if (lavfdopts->cryptokey)
parse_cryptokey(avfc, lavfdopts->cryptokey);
+5 -1
View File
@@ -33,6 +33,7 @@
#include "stream/stream.h"
#include "demux.h"
#include "stheader.h"
#include "codec_tags.h"
#include "mf.h"
#define MF_MAX_FILE_SIZE (1024 * 1024 * 256)
@@ -186,7 +187,10 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
if (!mf || mf->nr_of_files < 1)
goto error;
const char *codec = probe_format(mf, demuxer->opts->mf_type, check);
char *force_type = demuxer->opts->mf_type;
const char *codec = mp_map_mimetype_to_video_codec(demuxer->stream->mime_type);
if (!codec || (force_type && force_type[0]))
codec = probe_format(mf, force_type, check);
if (!codec)
goto error;
+46 -19
View File
@@ -307,7 +307,7 @@ static bstr demux_mkv_decode(struct mp_log *log, mkv_track_t *track,
dest = NULL;
goto error;
}
dstlen *= 2;
dstlen = MPMAX(1, 2 * dstlen);
}
size = dstlen - out_avail;
} else if (enc->comp_algo == 3) {
@@ -345,6 +345,10 @@ static int demux_mkv_read_info(demuxer_t *demuxer)
if (info.n_timecode_scale) {
mkv_d->tc_scale = info.timecode_scale;
MP_VERBOSE(demuxer, "| + timecode scale: %" PRIu64 "\n", mkv_d->tc_scale);
if (mkv_d->tc_scale < 1 || mkv_d->tc_scale > INT_MAX) {
res = -1;
goto out;
}
}
if (info.n_duration) {
mkv_d->duration = info.duration * mkv_d->tc_scale / 1e9;
@@ -1119,7 +1123,26 @@ skip:
return 0;
}
static void add_coverart(struct demuxer *demuxer)
{
for (int n = 0; n < demuxer->num_attachments; n++) {
struct demux_attachment *att = &demuxer->attachments[n];
const char *codec = mp_map_mimetype_to_video_codec(att->type);
if (!codec)
continue;
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
if (!sh)
break;
sh->codec = codec;
sh->attached_picture = new_demux_packet_from(att->data, att->data_size);
if (sh->attached_picture) {
sh->attached_picture->pts = 0;
talloc_steal(sh, sh->attached_picture);
sh->attached_picture->keyframe = true;
}
sh->title = att->name;
}
}
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track);
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track);
@@ -1148,7 +1171,6 @@ typedef struct {
char *id;
int fourcc;
int extradata;
bool parse;
} videocodec_info_t;
static const videocodec_info_t vinfo[] = {
@@ -1161,7 +1183,7 @@ static const videocodec_info_t vinfo[] = {
{MKV_V_MPEG4_AVC, MP_FOURCC('a', 'v', 'c', '1'), 1},
{MKV_V_THEORA, MP_FOURCC('t', 'h', 'e', 'o'), 1},
{MKV_V_VP8, MP_FOURCC('V', 'P', '8', '0'), 0},
{MKV_V_VP9, MP_FOURCC('V', 'P', '9', '0'), 0, true},
{MKV_V_VP9, MP_FOURCC('V', 'P', '9', '0'), 0},
{MKV_V_DIRAC, MP_FOURCC('d', 'r', 'a', 'c'), 0},
{MKV_V_PRORES, MP_FOURCC('p', 'r', '0', '0'), 0},
{MKV_V_HEVC, MP_FOURCC('H', 'E', 'V', 'C'), 1},
@@ -1234,7 +1256,6 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
extradata = track->private_data;
extradata_size = track->private_size;
}
track->parse = vi->parse;
if (!vi->id) {
MP_WARN(demuxer, "Unknown/unsupported "
"CodecID (%s) or missing/bad CodecPrivate\n"
@@ -1245,6 +1266,9 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
}
}
if (sh->format == MP_FOURCC('V', 'P', '9', '0'))
track->parse = true;
if (extradata_size > 0x1000000) {
MP_WARN(demuxer, "Invalid CodecPrivate\n");
return 1;
@@ -1289,10 +1313,9 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
static const struct mkv_audio_tag {
char *id; bool prefix; uint32_t formattag;
bool parse;
} mkv_audio_tags[] = {
{ MKV_A_MP2, 0, 0x0055 },
{ MKV_A_MP3, 0, 0x0055, true },
{ MKV_A_MP3, 0, 0x0055 },
{ MKV_A_AC3, 1, 0x2000 },
{ MKV_A_EAC3, 1, MP_FOURCC('E', 'A', 'C', '3') },
{ MKV_A_DTS, 0, 0x2001 },
@@ -1313,7 +1336,7 @@ static const struct mkv_audio_tag {
{ MKV_A_QDMC, 0, MP_FOURCC('Q', 'D', 'M', 'C') },
{ MKV_A_QDMC2, 0, MP_FOURCC('Q', 'D', 'M', '2') },
{ MKV_A_WAVPACK, 0, MP_FOURCC('W', 'V', 'P', 'K') },
{ MKV_A_TRUEHD, 0, MP_FOURCC('T', 'R', 'H', 'D'), true },
{ MKV_A_TRUEHD, 0, MP_FOURCC('T', 'R', 'H', 'D') },
{ MKV_A_FLAC, 0, MP_FOURCC('f', 'L', 'a', 'C') },
{ MKV_A_ALAC, 0, MP_FOURCC('a', 'L', 'a', 'C') },
{ MKV_A_REAL28, 0, MP_FOURCC('2', '8', '_', '8') },
@@ -1374,7 +1397,6 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
continue;
}
track->a_formattag = t->formattag;
track->parse = t->parse;
break;
}
}
@@ -1389,6 +1411,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
if (track->a_formattag == 0x0055) { /* MP3 || MP2 */
sh_a->bitrate = 16000 * 8;
sh_a->block_align = 1152;
track->parse = true;
} else if ((track->a_formattag == 0x2000) /* AC3 */
|| track->a_formattag == MP_FOURCC('E', 'A', 'C', '3')
|| (track->a_formattag == 0x2001)) { /* DTS */
@@ -1414,20 +1437,22 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
sh_a->bitrate = 16000 * 8;
sh_a->block_align = 1024;
if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) {
if (!extradata_len) {
if (!strcmp(track->codec_id, MKV_A_AAC)) {
if (track->private_data && !extradata_len) {
extradata = track->private_data;
extradata_len = track->private_size;
}
} else {
/* Recreate the 'private data' */
/* which faad2 uses in its initialization */
srate_idx = aac_get_sample_rate_index(track->a_sfreq);
if (!strncmp(&track->codec_id[12], "MAIN", 4))
const char *tail = "";
if (strlen(track->codec_id) >= 12)
tail = &track->codec_id[12];
if (!strncmp(tail, "MAIN", 4))
profile = 0;
else if (!strncmp(&track->codec_id[12], "LC", 2))
else if (!strncmp(tail, "LC", 2))
profile = 1;
else if (!strncmp(&track->codec_id[12], "SSR", 3))
else if (!strncmp(tail, "SSR", 3))
profile = 2;
else
profile = 3;
@@ -1572,12 +1597,13 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
AV_WB32(data + 8, 0);
memcpy(data + 12, track->private_data, track->private_size);
}
} else if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K') ||
track->a_formattag == MP_FOURCC('T', 'R', 'H', 'D')) {
} else if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K')) {
if (!track->ms_compat) {
extradata = track->private_data;
extradata_len = track->private_size;
}
} else if (track->a_formattag == MP_FOURCC('T', 'R', 'H', 'D')) {
track->parse = true;
} else if (track->a_formattag == MP_FOURCC('T', 'T', 'A', '1')) {
sh_a->codecdata_len = 30;
sh_a->codecdata = talloc_zero_size(sh_a, sh_a->codecdata_len);
@@ -1852,6 +1878,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
process_tags(demuxer);
display_create_tracks(demuxer);
add_coverart(demuxer);
if (demuxer->opts->mkv_probe_duration)
probe_last_timestamp(demuxer);
@@ -2272,8 +2299,8 @@ static bool mkv_parse_packet(mkv_track_t *track, bstr *raw, bstr *out)
}
if (track->av_parser && track->av_parser_codec) {
while (raw->len) {
uint8_t *data;
int size;
uint8_t *data = NULL;
int size = 0;
int len = av_parser_parse2(track->av_parser, track->av_parser_codec,
&data, &size, raw->start, raw->len,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
+29 -3
View File
@@ -47,6 +47,8 @@ struct pl_parser {
bool error;
bool probing;
bool force;
enum demux_check check_level;
struct stream *real_stream;
};
static char *pl_get_line0(struct pl_parser *p)
@@ -79,14 +81,33 @@ static bool pl_eof(struct pl_parser *p)
return p->error || p->s->eof;
}
static bool maybe_text(bstr d)
{
for (int n = 0; n < d.len; n++) {
unsigned char c = d.start[n];
if (c < 32 && c != '\n' && c != '\r' && c != '\t')
return false;
}
return true;
}
static int parse_m3u(struct pl_parser *p)
{
bstr line = bstr_strip(pl_get_line(p));
if (p->probing && !bstr_equals0(line, "#EXTM3U"))
if (p->probing && !bstr_equals0(line, "#EXTM3U")) {
// Last resort: if the file extension is m3u, it might be headerless.
if (p->check_level == DEMUX_CHECK_UNSAFE) {
char *ext = mp_splitext(p->real_stream->url, NULL);
bstr data = stream_peek(p->real_stream, PROBE_SIZE);
if (ext && strcmp(ext, "m3u") && data.len > 10 && maybe_text(data))
goto ok;
}
return -1;
}
ok:
if (p->probing)
return 0;
while (!pl_eof(p)) {
while (line.len || !pl_eof(p)) {
if (line.len > 0 && !bstr_startswith0(line, "#"))
pl_add(p, line);
line = bstr_strip(pl_get_line(p));
@@ -110,7 +131,7 @@ static int parse_ref_init(struct pl_parser *p)
"application/vnd.ms.wms-hdr.asfv1", NULL};
bstr burl = bstr0(p->s->url);
if (bstr_eatstart0(&burl, "http://") && check_mimetype(p->s, mmsh_types)) {
MP_INFO(p, "Redirectiong to mmsh://\n");
MP_INFO(p, "Redirecting to mmsh://\n");
playlist_add_file(p->pl, talloc_asprintf(p, "mmsh://%.*s", BSTR_P(burl)));
return 0;
}
@@ -158,6 +179,9 @@ static int parse_pls(struct pl_parser *p)
if (bstr_split_tok(line, "=", &key, &value) &&
bstr_case_startswith(key, bstr0("File")))
{
value = bstr_strip(value);
if (bstr_startswith0(value, "\"") && bstr_endswith0(value, "\""))
value = bstr_splice(value, 1, -1);
pl_add(p, value);
}
}
@@ -223,12 +247,14 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
struct pl_parser *p = talloc_zero(NULL, struct pl_parser);
p->log = demuxer->log;
p->pl = talloc_zero(p, struct playlist);
p->real_stream = demuxer->stream;
bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE);
p->s = open_memory_stream(probe_buf.start, probe_buf.len);
p->s->mime_type = demuxer->stream->mime_type;
p->utf16 = stream_skip_bom(p->s);
p->force = force;
p->check_level = check;
p->probing = true;
const struct pl_format *fmt = probe_pl(p);
free_stream(p->s);
+32 -27
View File
@@ -262,34 +262,38 @@ int ebml_resync_cluster(struct mp_log *log, stream_t *s)
struct generic;
#define generic_struct struct generic
static uint32_t ebml_parse_id(uint8_t *data, int *length)
static uint32_t ebml_parse_id(uint8_t *data, size_t data_len, int *length)
{
*length = -1;
uint8_t *end = data + data_len;
if (data == end)
return EBML_ID_INVALID;
int len = 1;
uint32_t id = *data++;
for (int len_mask = 0x80; !(id & len_mask); len_mask >>= 1) {
len++;
if (len > 4) {
*length = -1;
if (len > 4)
return EBML_ID_INVALID;
}
}
*length = len;
while (--len)
while (--len && data < end)
id = (id << 8) | *data++;
return id;
}
static uint64_t parse_vlen(uint8_t *data, int *length, bool is_length)
static uint64_t ebml_parse_length(uint8_t *data, size_t data_len, int *length)
{
*length = -1;
uint8_t *end = data + data_len;
if (data == end)
return -1;
uint64_t r = *data++;
int len = 1;
int len_mask;
for (len_mask = 0x80; !(r & len_mask); len_mask >>= 1) {
len++;
if (len > 8) {
*length = -1;
if (len > 8)
return -1;
}
}
r &= len_mask - 1;
@@ -297,25 +301,20 @@ static uint64_t parse_vlen(uint8_t *data, int *length, bool is_length)
if (r == len_mask - 1)
num_allones++;
for (int i = 1; i < len; i++) {
if (data == end)
return -1;
if (*data == 255)
num_allones++;
r = (r << 8) | *data++;
}
if (is_length && num_allones == len) {
// According to Matroska specs this means "unknown length"
// Could be supported if there are any actual files using it
*length = -1;
// According to Matroska specs this means "unknown length"
// Could be supported if there are any actual files using it
if (num_allones == len)
return -1;
}
*length = len;
return r;
}
static uint64_t ebml_parse_length(uint8_t *data, int *length)
{
return parse_vlen(data, length, true);
}
static uint64_t ebml_parse_uint(uint8_t *data, int length)
{
assert(length >= 1 && length <= 8);
@@ -363,7 +362,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
while (p < end) {
uint8_t *startp = p;
int len;
uint32_t id = ebml_parse_id(p, &len);
uint32_t id = ebml_parse_id(p, end - p, &len);
if (len > end - p)
goto past_end_error;
if (len < 0) {
@@ -371,7 +370,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
goto other_error;
}
p += len;
uint64_t length = ebml_parse_length(p, &len);
uint64_t length = ebml_parse_length(p, end - p, &len);
if (len > end - p)
goto past_end_error;
if (len < 0) {
@@ -458,11 +457,17 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
while (data < end) {
int len;
uint32_t id = ebml_parse_id(data, &len);
assert(len >= 0 && len <= end - data);
uint32_t id = ebml_parse_id(data, end - data, &len);
if (len < 0 || len > end - data) {
MP_DBG(ctx, "Error parsing subelement\n");
break;
}
data += len;
uint64_t length = ebml_parse_length(data, &len);
assert(len >= 0 && len <= end - data);
uint64_t length = ebml_parse_length(data, end - data, &len);
if (len < 0 || len > end - data) {
MP_DBG(ctx, "Error parsing subelement length\n");
break;
}
data += len;
if (length > end - data) {
// Try to parse what is possible from inside this partial element
@@ -582,7 +587,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
case EBML_TYPE_EBML_ID:;
uint32_t *idptr;
GETPTR(idptr, uint32_t);
*idptr = ebml_parse_id(data, &len);
*idptr = ebml_parse_id(data, end - data, &len);
if (len != length) {
MP_DBG(ctx, "ebml_id broken value\n");
goto error;
@@ -614,7 +619,7 @@ int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx,
MP_MSG(ctx, msglevel, "Refusing to read element over 100 MB in size\n");
return -1;
}
ctx->talloc_ctx = talloc_size(NULL, length + 8);
ctx->talloc_ctx = talloc_size(NULL, length);
int read_len = stream_read(s, ctx->talloc_ctx, length);
if (read_len < length)
MP_MSG(ctx, msglevel, "Unexpected end of file - partial or corrupt file?\n");
+1
View File
@@ -409,6 +409,7 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd)
memset(&ret->args[i].v, 0, ret->args[i].type->type->size);
m_option_copy(ret->args[i].type, &ret->args[i].v, &cmd->args[i].v);
}
ret->original = bstrdup(ret, cmd->original);
if (cmd->id == MP_CMD_COMMAND_LIST) {
struct mp_cmd *prev = NULL;
+13 -7
View File
@@ -445,6 +445,8 @@ static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx,
}
if (s->flags & MP_INPUT_EXCLUSIVE)
break;
if (best_bind && (s->flags & MP_INPUT_ON_TOP))
break;
}
return best_bind;
@@ -458,6 +460,8 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, char *force_section,
struct cmd_bind *cmd = find_any_bind_for_key(ictx, force_section, code);
if (cmd == NULL) {
if (code == MP_KEY_CLOSE_WIN)
return mp_input_parse_cmd_strv(ictx->log, (const char*[]){"quit", 0});
int msgl = MSGL_WARN;
if (code == MP_KEY_MOUSE_MOVE || code == MP_KEY_MOUSE_LEAVE)
msgl = MSGL_DEBUG;
@@ -589,10 +593,11 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale)
release_down_cmd(ictx, false);
} else {
// Press of key with no separate down/up events
if (ictx->last_key_down == code) {
// Mixing press events and up/down with the same key is not allowed
MP_WARN(ictx, "Mixing key presses and up/down.\n");
}
// Mixing press events and up/down with the same key is not supported,
// and input sources shouldn't do this, but can happen anyway if
// multiple input sources interfere with each others.
if (ictx->last_key_down == code)
release_down_cmd(ictx, false);
cmd = resolve_key(ictx, code);
}
@@ -1019,14 +1024,15 @@ void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
if (!name || !name[0])
return; // parse_config() changes semantics with restrict_section==empty
input_lock(ictx);
// Delete:
struct cmd_bind_section *bs = get_bind_section(ictx, bstr0(name));
remove_binds(bs, builtin);
if (contents) {
// Redefine:
parse_config(ictx, builtin, bstr0(contents), location, name);
} else {
// Disable:
mp_input_disable_section(ictx, name);
// Delete:
struct cmd_bind_section *bs = get_bind_section(ictx, bstr0(name));
remove_binds(bs, builtin);
}
input_unlock(ictx);
}
+9 -18
View File
@@ -61,8 +61,6 @@ struct client_arg {
bool close_client_fd;
bool writable;
int suspend_counter;
};
static mpv_node *mpv_node_map_get(mpv_node *src, const char *key)
@@ -245,6 +243,7 @@ static char *json_execute_command(struct client_arg *arg, void *ta_parent,
rc = json_parse(ta_parent, &msg_node, &src, 3);
if (rc < 0) {
MP_ERR(arg, "malformed JSON received\n");
rc = MPV_ERROR_INVALID_PARAMETER;
goto error;
}
@@ -420,21 +419,11 @@ static char *json_execute_command(struct client_arg *arg, void *ta_parent,
rc = mpv_request_log_messages(arg->client,
cmd_node->u.list->values[1].u.string);
} else if (!strcmp("suspend", cmd)) {
if (arg->suspend_counter < INT_MAX) {
mpv_suspend(arg->client);
arg->suspend_counter++;
rc = MPV_ERROR_SUCCESS;
} else {
rc = MPV_ERROR_INVALID_PARAMETER;
}
mpv_suspend(arg->client);
rc = MPV_ERROR_SUCCESS;
} else if (!strcmp("resume", cmd)) {
if (arg->suspend_counter > 0) {
mpv_resume(arg->client);
arg->suspend_counter--;
rc = MPV_ERROR_SUCCESS;
} else {
rc = MPV_ERROR_INVALID_PARAMETER;
}
mpv_resume(arg->client);
rc = MPV_ERROR_SUCCESS;
} else {
mpv_node result_node;
@@ -558,7 +547,7 @@ static void *client_thread(void *p)
if (errno == EAGAIN)
break;
MP_ERR(arg, "Read error\n");
MP_ERR(arg, "Read error (%s)\n", mp_strerror(errno));
goto done;
}
@@ -594,7 +583,7 @@ static void *client_thread(void *p)
rc = ipc_write(arg->client_fd, reply_msg,
strlen(reply_msg));
if (rc < 0) {
MP_ERR(arg, "Write error\n");
MP_ERR(arg, "Write error (%s)\n", mp_strerror(errno));
talloc_free(tmp);
goto done;
}
@@ -607,6 +596,8 @@ static void *client_thread(void *p)
}
done:
if (client_msg.len > 0)
MP_WARN(arg, "Ignoring unterminated command on disconnect.\n");
talloc_free(client_msg.start);
if (arg->close_client_fd)
close(arg->client_fd);
+10 -5
View File
@@ -28,6 +28,7 @@
#include <errno.h>
#include <poll.h>
#include "common/common.h"
#include "common/msg.h"
#include "keycodes.h"
@@ -65,7 +66,8 @@ static struct ctx *joystick_init(struct input_ctx *ictx, struct mp_log *log, cha
fd = open( dev ? dev : JS_DEV , O_RDONLY | O_NONBLOCK );
if(fd < 0) {
mp_err(log, "Can't open joystick device %s: %s\n",dev ? dev : JS_DEV,strerror(errno));
mp_err(log, "Can't open joystick device %s: %s\n",dev ? dev : JS_DEV,
mp_strerror(errno));
return NULL;
}
@@ -83,7 +85,8 @@ static struct ctx *joystick_init(struct input_ctx *ictx, struct mp_log *log, cha
initialized = 1;
break;
}
MP_ERR(ctx, "Error while reading joystick device: %s\n",strerror(errno));
MP_ERR(ctx, "Error while reading joystick device: %s\n",
mp_strerror(errno));
close(fd);
talloc_free(ctx);
return NULL;
@@ -117,10 +120,12 @@ static int mp_input_joystick_read(void *pctx, int fd) {
continue;
else if(errno == EAGAIN)
return 0;
if( r < 0)
MP_ERR(ctx, "Error while reading joystick device: %s\n",strerror(errno));
else
if( r < 0) {
MP_ERR(ctx, "Error while reading joystick device: %s\n",
mp_strerror(errno));
} else {
MP_ERR(ctx, "Error while reading joystick device: %s\n","EOF");
}
return -1;
}
l += r;
+2 -1
View File
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <poll.h>
#include "common/common.h"
#include "common/msg.h"
#include "input.h"
@@ -49,7 +50,7 @@ static struct ctx *mp_input_lirc_init(struct input_ctx *ictx, struct mp_log *log
mode = fcntl(lirc_sock, F_GETFL);
if (mode < 0 || fcntl(lirc_sock, F_SETFL, mode | O_NONBLOCK) < 0) {
mp_err(log, "setting non-blocking mode failed: %s\n", strerror(errno));
mp_err(log, "setting non-blocking mode failed: %s\n", mp_strerror(errno));
lirc_deinit();
return NULL;
}
+10
View File
@@ -45,6 +45,16 @@ static void read_pipe_thread(struct mp_input_src *src, void *param)
goto done;
}
// If we're reading from stdin, unset it. All I/O on synchronous handles is
// serialized, so stupid DLLs that call GetFileType on stdin can hang the
// process if they do it while we're reading from it. At least, the
// VirtualBox OpenGL ICD is affected by this, but only on Windows XP.
// GetFileType works differently in later versions of Windows. See:
// https://support.microsoft.com/kb/2009703
// http://blogs.msdn.com/b/oldnewthing/archive/2011/12/02/10243553.aspx
if ((void*)_get_osfhandle(fd) == GetStdHandle(STD_INPUT_HANDLE))
SetStdHandle(STD_INPUT_HANDLE, NULL);
waio = waio_alloc((void *)_get_osfhandle(fd), 0, NULL, NULL);
if (!waio) {
MP_ERR(src, "Can't initialize win32 file reader.\n");
+2
View File
@@ -117,6 +117,8 @@ extern "C" {
* (used through libass), ALSA, FFmpeg, and possibly more.
* - The FPU precision must be set at least to double precision.
* - On Windows, mpv will call timeBeginPeriod(1).
* - SIGPIPE should be blocked. Some parts rely on this signal not crashing the
* process (such as ffmpeg OpenSSL support, or the mpv IPC code).
* - On memory exhaustion, mpv will kill the process.
*
* Embedding the video window
+1 -1
View File
@@ -207,7 +207,7 @@ static inline int bstr_eatstart0(struct bstr *s, const char *prefix)
}
// create a pair (not single value!) for "%.*s" printf syntax
#define BSTR_P(bstr) (int)((bstr).len), (bstr).start
#define BSTR_P(bstr) (int)((bstr).len), ((bstr).start ? (char*)(bstr).start : "")
#define WHITESPACE " \f\n\r\t\v"
+10 -11
View File
@@ -607,13 +607,8 @@ define_yes_no $_vaapi HAVE_VAAPI_HWACCEL
if test "$_vaapi" = yes ; then
check_pkg_config "VAAPI VPP" auto VAAPI_VPP 'libva >= 0.34.0'
check_pkg_config "VAAPI GLX" auto VAAPI_GLX 'libva-glx >= 0.32.0'
_vaapi_glx=$(defretval)
else
check_yes_no no VAAPI_VPP
check_yes_no no VAAPI_GLX
_vaapi_glx=no
fi
check_pkg_config "Xinerama" $_xinerama XINERAMA 'xinerama'
@@ -696,6 +691,10 @@ _vdpau_gl_x11=no
check_yes_no $_vdpau_gl_x11 VDPAU_GL_X11
echores "$_vdpau_gl_x11"
_vaapi_glx=no
(test "$_gl_x11" = yes && test "$_vaapi" = yes) && _vaapi_glx=auto
check_pkg_config "VAAPI with OpenGL/X11" $_vaapi_glx VAAPI_GLX 'libva-glx >= 0.32.0'
check_pkg_config "SDL 2.0" $_sdl2 SDL2 'sdl2'
check_statement_libs "OSS Audio" $_ossaudio OSS_AUDIO $_soundcard_header "int x = SNDCTL_DSP_SETFRAGMENT;"
@@ -752,14 +751,14 @@ check_pkg_config "FFmpeg/Libav" $_ffmpeg FFMPEG \
"libavutil >= 52.48.101 libavcodec >= 55.34.1 libavformat >= 55.12.0 libswscale >= 2.1.2"
test $(defretval) = no && die "Unable to find development files for some of the required Libav libraries above. Aborting."
check_pkg_config "Libavresample" $_libavresample LIBAVRESAMPLE 'libavresample >= 1.1.0'
_libavresample=$(defretval)
_libswresample=auto
test $_libavresample = yes && _libswresample=no
check_pkg_config "Libswresample" $_libswresample LIBSWRESAMPLE 'libswresample >= 0.17.104'
_libswresample=$(defretval)
_libavresample=auto
test $_libswresample = yes && _libavresample=no
check_pkg_config "Libavresample" $_libavresample LIBAVRESAMPLE 'libavresample >= 1.1.0'
_libavresample=$(defretval)
if test "$_libswresample" = no && test "$_libavresample" = no ; then
die "No resampler found. Install libavresample or libswresample (FFmpeg)."
fi
@@ -951,7 +950,7 @@ cat > $TMPC << EOF
#define CONFIGURATION "$configuration"
#define MPLAYER_CONFDIR "$_confdir"
#define MPV_CONFDIR "$_confdir"
/* we didn't bother to add actual config checks for this, or they are
for platforms not supported by this configure script */
-1
View File
@@ -191,7 +191,6 @@ SOURCES = audio/audio.c \
options/parse_configfile.c \
options/path.c \
osdep/io.c \
osdep/numcores.c \
osdep/semaphore_osx.c \
osdep/subprocess-posix.c \
osdep/terminal-unix.c \
+1 -1
View File
@@ -734,7 +734,7 @@ void m_config_print_option_list(const struct m_config *config)
continue;
if (co->is_generated)
continue;
MP_INFO(config, " %s%-30.30s", prefix, co->name);
MP_INFO(config, " %s%-30s", prefix, co->name);
if (opt->type == &m_option_type_choice) {
MP_INFO(config, " Choices:");
struct m_opt_choice_alternatives *alt = opt->priv;
+38 -7
View File
@@ -2146,7 +2146,7 @@ static int parse_chmap(struct mp_log *log, const m_option_t *opt,
const m_option_type_t m_option_type_chmap = {
.name = "Audio channels or channel map",
.size = sizeof(struct mp_chmap *),
.size = sizeof(struct mp_chmap),
.parse = parse_chmap,
.copy = copy_opt,
};
@@ -2171,6 +2171,7 @@ static int parse_timestring(struct bstr str, double *time, char endchar)
return len;
}
#define HAS_NOPTS(opt) ((opt)->min == MP_NOPTS_VALUE)
static int parse_time(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
@@ -2180,7 +2181,7 @@ static int parse_time(struct mp_log *log, const m_option_t *opt,
if (param.len == 0)
return M_OPT_MISSING_PARAM;
if (opt->min == MP_NOPTS_VALUE && bstr_equals0(param, "no")) {
if (HAS_NOPTS(opt) == MP_NOPTS_VALUE && bstr_equals0(param, "no")) {
time = MP_NOPTS_VALUE;
} else if (!parse_timestring(param, &time, 0)) {
mp_err(log, "Option %.*s: invalid time: '%.*s'\n",
@@ -2193,25 +2194,55 @@ static int parse_time(struct mp_log *log, const m_option_t *opt,
return 1;
}
static char *print_time(const m_option_t *opt, const void *val)
{
double pts = *(double *)val;
if (pts == MP_NOPTS_VALUE && HAS_NOPTS(opt))
return talloc_strdup(NULL, "no"); // symmetry with parsing
return talloc_asprintf(NULL, "%f", pts);
}
static char *pretty_print_time(const m_option_t *opt, const void *val)
{
double pts = *(double *)val;
if (pts == MP_NOPTS_VALUE && opt->min == MP_NOPTS_VALUE)
if (pts == MP_NOPTS_VALUE && HAS_NOPTS(opt))
return talloc_strdup(NULL, "no"); // symmetry with parsing
return mp_format_time(pts, false);
}
static int time_set(const m_option_t *opt, void *dst, struct mpv_node *src)
{
if (HAS_NOPTS(opt) && src->format == MPV_FORMAT_STRING) {
if (strcmp(src->u.string, "no") == 0) {
*(double *)dst = MP_NOPTS_VALUE;
return 1;
}
}
return double_set(opt, dst, src);
}
static int time_get(const m_option_t *opt, void *ta_parent,
struct mpv_node *dst, void *src)
{
if (HAS_NOPTS(opt) && *(double *)src == MP_NOPTS_VALUE) {
dst->format = MPV_FORMAT_STRING;
dst->u.string = talloc_strdup(ta_parent, "no");
return 1;
}
return double_get(opt, ta_parent, dst, src);
}
const m_option_type_t m_option_type_time = {
.name = "Time",
.size = sizeof(double),
.parse = parse_time,
.print = print_double,
.print = print_time,
.pretty_print = pretty_print_time,
.copy = copy_opt,
.add = add_double,
.clamp = clamp_double,
.set = double_set,
.get = double_get,
.set = time_set,
.get = time_get,
};
@@ -2620,7 +2651,7 @@ static int parse_obj_settings(struct mp_log *log, struct bstr opt,
if (m_obj_list_find(&desc, list, str)) {
if (desc.replaced_name)
mp_warn(log, "Driver '%s' has been replaced with '%s'!\n",
desc.name, desc.replaced_name);
desc.replaced_name, desc.name);
} else {
if (!list->allow_unknown_entries) {
mp_err(log, "Option %.*s: %.*s doesn't exist.\n",
+2 -1
View File
@@ -614,7 +614,7 @@ const struct MPOpts mp_default_opts = {
.use_text_osd = 1,
#if HAVE_LUA
.lua_load_osc = 1,
.lua_load_ytdl = 0,
.lua_load_ytdl = 1,
.lua_ytdl_format = NULL,
#endif
.auto_load_scripts = 1,
@@ -684,6 +684,7 @@ const struct MPOpts mp_default_opts = {
.sub_fix_timing = 1,
.sub_cp = "auto",
.mkv_subtitle_preroll_secs = 1.0,
.screenshot_template = "shot%n",
.hwdec_codecs = "h264,vc1,wmv3",
+2 -1
View File
@@ -27,6 +27,7 @@
#include "osdep/io.h"
#include "parse_configfile.h"
#include "common/common.h"
#include "common/msg.h"
#include "misc/ctype.h"
#include "m_option.h"
@@ -79,7 +80,7 @@ int m_config_parse_config_file(m_config_t *config, const char *conffile,
MP_VERBOSE(config, "\n");
if ((fp = fopen(conffile, "r")) == NULL) {
MP_VERBOSE(config, "Can't open config file: %s\n", strerror(errno));
MP_VERBOSE(config, "Can't open config file: %s\n", mp_strerror(errno));
ret = 0;
goto out;
}
+1 -1
View File
@@ -74,7 +74,7 @@ static int mp_add_xdg_config_dirs(struct mpv_global *global, char **dirs, int i)
i = mp_add_macosx_bundle_dir(global, dirs, i);
#endif
dirs[i++] = MPLAYER_CONFDIR;
dirs[i++] = MPV_CONFDIR;
return i;
}
+6 -17
View File
@@ -233,6 +233,8 @@ Application *mpv_shared_app(void)
options:NSAnchoredSearch
range:NSMakeRange(0, [MPV_PROTOCOL length])];
url = [url stringByRemovingPercentEncoding];
self.files = @[url];
if (self.willStopOnOpenEvent) {
@@ -329,7 +331,10 @@ void init_cocoa_application(void)
NSApp = mpv_shared_app();
[NSApp setDelegate:NSApp];
[NSApp initialize_menu];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// Will be set to Regular from cocoa_common during UI creation so that we
// don't create an icon when playing audio only files.
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
atexit_b(^{
// Because activation policy has just been set to behave like a real
@@ -439,21 +444,6 @@ static bool bundle_started_from_finder(int argc, char **argv)
}
}
static const char macosx_icon[] =
#include "osdep/macosx_icon.inc"
;
static void set_application_icon(NSApplication *app)
{
NSData *icon_data = [NSData dataWithBytesNoCopy:(void *)macosx_icon
length:sizeof(macosx_icon)
freeWhenDone:NO];
NSImage *icon = [[NSImage alloc] initWithData:icon_data];
[app setApplicationIconImage:icon];
[icon release];
[icon_data release];
}
void macosx_finder_args_preinit(int *argc, char ***argv)
{
Application *app = mpv_shared_app();
@@ -475,7 +465,6 @@ void macosx_finder_args_preinit(int *argc, char ***argv)
*argc = cocoa_argc;
*argv = cocoa_argv;
} else {
set_application_icon(app);
for (int i = 0; i < *argc; i++ ) {
NSString *arg = [NSString stringWithUTF8String:(*argv)[i]];
[app.argumentsList addObject:arg];
+4 -2
View File
@@ -440,8 +440,10 @@ void cocoa_set_input_context(struct input_ctx *input_context)
size_t num_files = [files count];
char **files_utf8 = talloc_array(NULL, char*, num_files);
[files enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *_){
char *filename = (char *)[obj UTF8String];
size_t bytes = [obj lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:obj];
NSString *path = [url path];
char *filename = (char *)[path UTF8String];
size_t bytes = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
files_utf8[i] = talloc_memdup(files_utf8, filename, bytes + 1);
}];
mp_event_drop_files(_inputContext, num_files, files_utf8);
+1 -1
View File
@@ -35,7 +35,7 @@ FILETYPE VFT_APP
VALUE "CompanyName", "mpv\000"
VALUE "FileDescription", "mpv - video player\000"
VALUE "FileVersion",VERSION
VALUE "LegalCopyright", " (C) 2000-2014 mpv/mplayer2/MPlayer\000"
VALUE "LegalCopyright", " (C) 2000-2015 mpv/mplayer2/MPlayer\000"
//VALUE "LegalTrademarks"," \000";
VALUE "OriginalFilename", "mpv.exe\000"
VALUE "ProductName", "mpv - video player\000"
-66
View File
@@ -1,66 +0,0 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "numcores.h"
#ifdef _SC_NPROCESSORS_ONLN
#define SCNAME _SC_NPROCESSORS_ONLN
#elif defined _SC_NPROC_ONLN
#define SCNAME _SC_NPROC_ONLN
#elif defined _SC_CRAY_NCPU
#define SCNAME _SC_CRAY_NCPU
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
int default_thread_count(void)
{
SYSTEM_INFO info;
GetSystemInfo(&info);
int count = info.dwNumberOfProcessors;
if (count > 0)
return count;
return -1;
}
#elif defined SCNAME
int default_thread_count(void)
{
long nprocs = sysconf(SCNAME);
if (nprocs < 1)
return -1;
return nprocs;
}
#else
int default_thread_count(void)
{
return -2;
}
#endif
-1
View File
@@ -1 +0,0 @@
int default_thread_count(void);
+4 -4
View File
@@ -66,18 +66,18 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
int p_stderr[2] = {-1, -1};
pid_t pid = -1;
if (mp_make_cloexec_pipe(p_stdout) < 0)
if (on_stdout && mp_make_cloexec_pipe(p_stdout) < 0)
goto done;
if (mp_make_cloexec_pipe(p_stderr) < 0)
if (on_stderr && mp_make_cloexec_pipe(p_stderr) < 0)
goto done;
if (posix_spawn_file_actions_init(&fa))
goto done;
fa_destroy = true;
// redirect stdout and stderr
if (posix_spawn_file_actions_adddup2(&fa, p_stdout[1], 1))
if (p_stdout[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stdout[1], 1))
goto done;
if (posix_spawn_file_actions_adddup2(&fa, p_stderr[1], 2))
if (p_stderr[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stderr[1], 2))
goto done;
if (posix_spawnp(&pid, args[0], &fa, NULL, args, environ)) {
+13 -9
View File
@@ -15,7 +15,6 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#define _WIN32_WINNT 0x0600
#include <windows.h>
#include <string.h>
@@ -84,12 +83,14 @@ static void write_arg(bstr *cmdline, char *arg)
// Convert an array of arguments to a properly escaped command-line string
static wchar_t *write_cmdline(void *ctx, char **argv)
{
// argv[0] should always be quoted. Otherwise, arguments may be interpreted
// as part of the program name. Also, it can't contain escape sequences.
bstr cmdline = {0};
bstr_xappend_asprintf(NULL, &cmdline, "\"%s\"", argv[0]);
for (int i = 0; argv[i]; i++) {
for (int i = 1; argv[i]; i++) {
bstr_xappend(NULL, &cmdline, bstr0(" "));
write_arg(&cmdline, argv[i]);
if (argv[i + 1])
bstr_xappend(NULL, &cmdline, bstr0(" "));
}
wchar_t *wcmdline = mp_from_utf8(ctx, cmdline.start);
@@ -111,9 +112,8 @@ static int create_overlapped_pipe(HANDLE *read, HANDLE *write)
// overlapped pipes, so instead, use a named pipe with a unique name
*read = CreateNamedPipeW(buf, PIPE_ACCESS_INBOUND |
FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
1, 0, 4096, 0, NULL);
if (!*read)
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 4096, 0, NULL);
if (*read == INVALID_HANDLE_VALUE)
goto error;
// Open the write end of the pipe as a synchronous handle
@@ -221,6 +221,10 @@ static int async_read(HANDLE file, void *buf, unsigned size, OVERLAPPED* ol)
return 0;
}
static void write_none(void *ctx, char *data, size_t size)
{
}
int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
char **error)
@@ -234,8 +238,8 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
char buf[4096];
subprocess_read_cb read_cb;
} pipes[2] = {
{ .read_cb = on_stdout },
{ .read_cb = on_stderr },
{ .read_cb = on_stdout ? on_stdout : write_none },
{ .read_cb = on_stderr ? on_stderr : write_none },
};
// If the function exits before CreateProcess, there was an init error
+7 -6
View File
@@ -416,6 +416,11 @@ void terminal_setup_getch(struct input_ctx *ictx)
if (mp_make_wakeup_pipe(death_pipe) < 0)
return;
// Disable reading from the terminal even if stdout is not a tty, to make
// mpv ... | less
// do the right thing.
read_terminal = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
input_ctx = ictx;
if (pthread_create(&input_thread, NULL, terminal_thread, NULL)) {
@@ -455,6 +460,7 @@ void terminal_uninit(void)
}
getch2_enabled = 0;
read_terminal = false;
}
bool terminal_in_background(void)
@@ -477,18 +483,13 @@ int terminal_init(void)
assert(!getch2_enabled);
getch2_enabled = 1;
// Disable reading from the terminal even if stdout is not a tty, to make
// mpv ... | less
// do the right thing.
read_terminal = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
// handlers to fix terminal settings
setsigaction(SIGCONT, continue_sighandler, 0, true);
setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false);
setsigaction(SIGTTIN, SIG_IGN, 0, true);
setsigaction(SIGTTOU, SIG_IGN, 0, true);
// don't crash on SIGPIPE caused by semi-broken libraries
// get sane behavior, instead of hysteric UNIX-nonsense
setsigaction(SIGPIPE, SIG_IGN, 0, true);
getch2_poll();
+6 -8
View File
@@ -227,15 +227,13 @@ void reinit_audio_chain(struct MPContext *mpctx)
struct af_stream *afs = mpctx->d_audio->afilter;
afs->output = (struct mp_audio){0};
if (mpctx->ao) {
ao_get_format(mpctx->ao, &afs->output);
} else {
afs->output = (struct mp_audio){0};
} else if (!AF_FORMAT_IS_SPECIAL(in_format.format)) {
afs->output.rate = opts->force_srate;
mp_audio_set_format(&afs->output, opts->audio_output_format);
// automatic downmix
if (!AF_FORMAT_IS_SPECIAL(in_format.format))
mp_audio_set_channels(&afs->output, &opts->audio_output_channels);
mp_audio_set_channels(&afs->output, &opts->audio_output_channels);
}
// filter input format: same as codec's output format:
@@ -460,8 +458,7 @@ static void do_fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
mpctx->d_audio->init_retries += 1;
if (mpctx->d_audio->init_retries >= 50) {
MP_ERR(mpctx, "Error initializing audio.\n");
struct track *track = mpctx->current_track[0][STREAM_AUDIO];
mp_deselect_track(mpctx, track);
error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
return;
}
}
@@ -508,6 +505,8 @@ static void do_fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
mpctx->sleeptime = 0;
return; // retry on next iteration
}
if (status == AD_ERR)
mpctx->sleeptime = 0;
}
// If EOF was reached before, but now something can be decoded, try to
@@ -515,7 +514,6 @@ static void do_fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
// later. Retrying is needed to get the correct sync PTS.
if (mpctx->audio_status >= STATUS_DRAINING && status == AD_OK) {
mpctx->audio_status = STATUS_SYNCING;
mpctx->sleeptime = 0;
return; // retry on next iteration
}
+50 -7
View File
@@ -108,6 +108,7 @@ struct mpv_handle {
uint64_t event_mask;
bool queued_wakeup;
bool choke_warning;
int suspend_count;
mpv_event *events; // ringbuffer of max_events entries
int max_events; // allocated number of entries in events
@@ -300,12 +301,47 @@ void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d)
void mpv_suspend(mpv_handle *ctx)
{
mp_dispatch_suspend(ctx->mpctx->dispatch);
bool do_suspend = false;
pthread_mutex_lock(&ctx->lock);
if (ctx->suspend_count == INT_MAX) {
MP_ERR(ctx, "suspend counter overflow");
} else {
do_suspend = ctx->suspend_count == 0;
ctx->suspend_count++;
}
pthread_mutex_unlock(&ctx->lock);
if (do_suspend)
mp_dispatch_suspend(ctx->mpctx->dispatch);
}
void mpv_resume(mpv_handle *ctx)
{
mp_dispatch_resume(ctx->mpctx->dispatch);
bool do_resume = false;
pthread_mutex_lock(&ctx->lock);
if (ctx->suspend_count == 0) {
MP_ERR(ctx, "suspend counter underflow");
} else {
do_resume = ctx->suspend_count == 1;
ctx->suspend_count--;
}
pthread_mutex_unlock(&ctx->lock);
if (do_resume)
mp_dispatch_resume(ctx->mpctx->dispatch);
}
void mp_resume_all(mpv_handle *ctx)
{
pthread_mutex_lock(&ctx->lock);
bool do_resume = ctx->suspend_count > 0;
ctx->suspend_count = 0;
pthread_mutex_unlock(&ctx->lock);
if (do_resume)
mp_dispatch_resume(ctx->mpctx->dispatch);
}
static void lock_core(mpv_handle *ctx)
@@ -325,6 +361,8 @@ void mpv_detach_destroy(mpv_handle *ctx)
if (!ctx)
return;
mp_resume_all(ctx);
pthread_mutex_lock(&ctx->lock);
// 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
@@ -665,8 +703,6 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
if (timeout < 0)
timeout = 1e20;
if (ctx->queued_wakeup)
timeout = 0;
int64_t deadline = mp_add_timeout(mp_time_us(), timeout);
@@ -674,6 +710,13 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
talloc_free_children(event);
while (1) {
if (ctx->queued_wakeup)
deadline = 0;
// This will almost surely lead to a deadlock. (Polling is still ok.)
if (ctx->suspend_count && timeout > 0) {
MP_ERR(ctx, "attempting to wait while core is suspended");
break;
}
if (ctx->num_events) {
*event = ctx->events[ctx->first_event];
ctx->first_event = (ctx->first_event + 1) % ctx->max_events;
@@ -691,13 +734,13 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
event->event_id = MPV_EVENT_LOG_MESSAGE;
struct mpv_event_log_message *cmsg = talloc_ptrtype(event, cmsg);
*cmsg = (struct mpv_event_log_message){
.prefix = talloc_steal(event, msg->prefix),
.prefix = msg->prefix,
.level = mp_log_levels[msg->level],
.log_level = mp_mpv_log_levels[msg->level],
.text = talloc_steal(event, msg->text),
.text = msg->text,
};
talloc_steal(event, msg);
event->data = cmsg;
talloc_free(msg);
break;
}
}
+2
View File
@@ -32,6 +32,8 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
struct mp_log *mp_client_get_log(struct mpv_handle *ctx);
struct MPContext *mp_client_get_core(struct mpv_handle *ctx);
void mp_resume_all(struct mpv_handle *ctx);
// m_option.c
void *node_get_alloc(struct mpv_node *node);
+83 -55
View File
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <assert.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <sys/types.h>
@@ -62,11 +63,9 @@
#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifndef __MINGW32__
#include <sys/wait.h>
#endif
#include "osdep/io.h"
#include "osdep/subprocess.h"
#include "core.h"
@@ -568,10 +567,14 @@ static int mp_property_percent_pos(void *ctx, struct m_property *prop,
.max = 100,
};
return M_PROPERTY_OK;
case M_PROPERTY_PRINT:
*(char **)arg = talloc_asprintf(NULL, "%d", get_percent_pos(mpctx));
case M_PROPERTY_PRINT: {
int pos = get_percent_pos(mpctx);
if (pos < 0)
return M_PROPERTY_UNAVAILABLE;
*(char **)arg = talloc_asprintf(NULL, "%d", pos);
return M_PROPERTY_OK;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
@@ -690,6 +693,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
int chapter = get_current_chapter(mpctx);
int num = get_chapter_count(mpctx);
if (chapter < -1)
return M_PROPERTY_UNAVAILABLE;
@@ -702,7 +706,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
.type = CONF_TYPE_INT,
.flags = M_OPT_MIN | M_OPT_MAX,
.min = -1,
.max = get_chapter_count(mpctx) - 1,
.max = num - 1,
};
return M_PROPERTY_OK;
case M_PROPERTY_PRINT: {
@@ -716,6 +720,8 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
if (action == M_PROPERTY_SWITCH) {
struct m_property_switch_arg *sarg = arg;
step_all = ROUND(sarg->inc);
if (num < 2) // semi-broken file; ignore for user convenience
return M_PROPERTY_UNAVAILABLE;
// Check threshold for relative backward seeks
if (mpctx->opts->chapter_seek_threshold >= 0 && step_all < 0) {
double current_chapter_start =
@@ -732,7 +738,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
chapter += step_all;
if (chapter < -1)
chapter = -1;
if (chapter >= get_chapter_count(mpctx) && step_all > 0) {
if (chapter >= num && step_all > 0) {
mpctx->stop_play = PT_NEXT_ENTRY;
} else {
mp_seek_chapter(mpctx, chapter);
@@ -1030,8 +1036,10 @@ static int tag_property(int action, void *arg, struct mp_tags *tags)
res = talloc_asprintf_append_buffer(res, "%s: %s\n",
tags->keys[n], tags->values[n]);
}
if (!res)
res = talloc_strdup(NULL, "(empty)");
*(char **)arg = res;
return res ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE;
return M_PROPERTY_OK;
}
case M_PROPERTY_KEY_ACTION: {
struct m_property_action_arg *ka = arg;
@@ -1098,22 +1106,16 @@ static int mp_property_vf_metadata(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
struct vf_chain *vf = mpctx->d_video->vfilter;
switch(action) {
case M_PROPERTY_GET_TYPE:
case M_PROPERTY_GET:
case M_PROPERTY_GET_NODE:
return M_PROPERTY_NOT_IMPLEMENTED;
case M_PROPERTY_KEY_ACTION: {
if (action == M_PROPERTY_KEY_ACTION) {
struct m_property_action_arg *ka = arg;
bstr key;
char *rem;
m_property_split_path(ka->key, &key, &rem);
struct mp_tags vf_metadata;
struct mp_tags vf_metadata = {0};
switch (vf_control_by_label(vf, VFCTRL_GET_METADATA, &vf_metadata, key)) {
case CONTROL_NA:
return M_PROPERTY_UNAVAILABLE;
case CONTROL_UNKNOWN:
return M_PROPERTY_UNKNOWN;
case CONTROL_NA: // empty
case CONTROL_OK:
if (strlen(rem)) {
struct m_property_action_arg next_ka = *ka;
@@ -1127,7 +1129,6 @@ static int mp_property_vf_metadata(void *ctx, struct m_property *prop,
return M_PROPERTY_ERROR;
}
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
@@ -2535,9 +2536,10 @@ static int mp_property_fps(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (!mpctx->d_video)
return M_PROPERTY_UNAVAILABLE;
return m_property_float_ro(action, arg, mpctx->d_video->fps);
float fps = mpctx->d_video ? mpctx->d_video->fps : 0;
if (fps < 0.1 || !isfinite(fps))
return M_PROPERTY_UNAVAILABLE;;
return m_property_float_ro(action, arg, fps);
}
static int mp_property_vf_fps(void *ctx, struct m_property *prop,
@@ -2868,10 +2870,16 @@ static int mp_property_playlist(void *ctx, struct m_property *prop,
for (struct playlist_entry *e = mpctx->playlist->first; e; e = e->next)
{
char *p = e->filename;
if (!mp_is_url(bstr0(p))) {
char *s = mp_basename(e->filename);
if (s[0])
p = s;
}
if (mpctx->playlist->current == e) {
res = talloc_asprintf_append(res, "> %s <\n", e->filename);
res = talloc_asprintf_append(res, "> %s <\n", p);
} else {
res = talloc_asprintf_append(res, "%s\n", e->filename);
res = talloc_asprintf_append(res, "%s\n", p);
}
}
@@ -3342,14 +3350,14 @@ static const char *const *const mp_event_property_change[] = {
"width", "height", "fps", "aspect", "vo-configured"),
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio"),
E(MPV_EVENT_SEEK, "seeking"),
E(MPV_EVENT_PLAYBACK_RESTART, "seeking"),
E(MPV_EVENT_SEEK, "seeking", "core-idle"),
E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle"),
E(MPV_EVENT_METADATA_UPDATE, "metadata"),
E(MPV_EVENT_CHAPTER_CHANGE, "chapter", "chapter-metadata"),
E(MP_EVENT_CACHE_UPDATE, "cache", "cache-free", "cache-used", "cache-idle",
"demuxer-cache-duration", "demuxer-cache-idle"),
"demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache"),
E(MP_EVENT_WIN_RESIZE, "window-scale"),
E(MP_EVENT_WIN_STATE, "window-minimized"),
E(MP_EVENT_WIN_STATE, "window-minimized", "display-names"),
};
#undef E
@@ -3831,6 +3839,39 @@ static void overlay_uninit(struct MPContext *mpctx)
osd_set_external2(mpctx->osd, NULL);
}
struct subprocess_args {
struct mp_log *log;
char **args;
};
static void *run_subprocess(void *ptr)
{
struct subprocess_args *p = ptr;
pthread_detach(pthread_self());
mp_msg_flush_status_line(p->log);
char *err = NULL;
if (mp_subprocess(p->args, NULL, NULL, NULL, NULL, &err) < 0)
mp_err(p->log, "Running subprocess failed: %s\n", err);
talloc_free(p);
return NULL;
}
static void subprocess_detached(struct mp_log *log, char **args)
{
struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
p->log = mp_log_new(p, log, NULL);
int num_args = 0;
for (int n = 0; args[n]; n++)
MP_TARRAY_APPEND(p, p->args, num_args, talloc_strdup(p, args[n]));
MP_TARRAY_APPEND(p, p->args, num_args, NULL);
pthread_t thread;
if (pthread_create(&thread, NULL, run_subprocess, p))
talloc_free(p);
}
struct cycle_counter {
char **args;
int counter;
@@ -4166,7 +4207,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
if (!mpctx->num_sources)
return -1;
struct osd_sub_state state;
get_osd_sub_state(mpctx, 0, &state);
update_osd_sub_state(mpctx, 0, &state);
double refpts = get_current_time(mpctx);
if (state.dec_sub && refpts != MP_NOPTS_VALUE) {
double a[2];
@@ -4255,16 +4296,16 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
bool append = cmd->args[1].v.i;
struct playlist *pl = playlist_parse_file(filename, mpctx->global);
if (pl) {
prepare_playlist(mpctx, pl);
struct playlist_entry *new = pl->current;
if (!append)
playlist_clear(mpctx->playlist);
playlist_append_entries(mpctx->playlist, pl);
talloc_free(pl);
if (!append && mpctx->playlist->first) {
struct playlist_entry *e =
mp_check_playlist_resume(mpctx, mpctx->playlist);
mp_set_playlist_entry(mpctx, e ? e : mpctx->playlist->first);
}
if (!append && mpctx->playlist->first)
mp_set_playlist_entry(mpctx, new ? new : mpctx->playlist->first);
mp_notify_property(mpctx, "playlist");
} else {
MP_ERR(mpctx, "Unable to load playlist %s.\n", filename);
@@ -4350,7 +4391,9 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
struct track *sub = mp_add_subtitles(mpctx, cmd->args[0].v.s);
if (!sub)
return -1;
if (cmd->args[1].v.i != 1) {
if (cmd->args[1].v.i == 1) {
sub->no_default = true;
} else {
mp_switch_track(mpctx, sub->type, sub);
mp_mark_user_track_selection(mpctx, 0, sub->type);
}
@@ -4393,28 +4436,10 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
break;
case MP_CMD_RUN: {
#ifndef __MINGW32__
mp_msg_flush_status_line(mpctx->global);
char *args[MP_CMD_MAX_ARGS + 1] = {0};
for (int n = 0; n < cmd->nargs; n++)
args[n] = cmd->args[n].v.s;
pid_t child = fork();
if (child == 0) {
// Fork twice; the second process will be made child of pid 1 as
// soon as the first process exists, and we don't have to care
// about having to wait for the second process to terminate.
if (fork() == 0) {
execvp(args[0], args);
// mp_msg() is not safe to be called from a forked process.
char s[] = "Executing program failed.\n";
write(2, s, sizeof(s) - 1);
_exit(1);
}
_exit(0);
}
int st;
while (child != -1 && waitpid(child, &st, 0) < 0 && errno == EINTR) {}
#endif
subprocess_detached(mpctx->log, args);
break;
}
@@ -4492,7 +4517,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
char *sep = strchr(name, '/');
char *target = NULL;
char space[MAX_CLIENT_NAME];
if (name) {
if (sep) {
snprintf(space, sizeof(space), "%.*s", (int)(sep - name), name);
target = space;
name = sep + 1;
@@ -4624,8 +4649,11 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
}
ctx->prev_pts = now;
}
if (event == MPV_EVENT_SEEK) {
if (event == MPV_EVENT_SEEK)
ctx->prev_pts = MP_NOPTS_VALUE;
if (event == MPV_EVENT_END_FILE || event == MPV_EVENT_FILE_LOADED) {
// Update chapters - does nothing if something else is visible.
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
}
}
+2
View File
@@ -243,6 +243,8 @@ static const char *const backup_properties[] = {
"options/ass-use-margins",
"options/ass-vsfilter-aspect-compat",
"options/ass-style-override",
"options/ab-loop-a",
"options/ab-loop-b",
0
};
+4 -2
View File
@@ -390,6 +390,7 @@ 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);
void reselect_demux_streams(struct MPContext *mpctx);
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
// main.c
int mpv_main(int argc, char *argv[]);
@@ -397,6 +398,7 @@ int mp_initialize(struct MPContext *mpctx);
struct MPContext *mp_create(void);
void mp_destroy(struct MPContext *mpctx);
void mp_print_version(struct mp_log *log, int always);
void wakeup_playloop(void *ctx);
// misc.c
double get_start_time(struct MPContext *mpctx);
@@ -470,8 +472,8 @@ void uninit_sub_all(struct MPContext *mpctx);
void update_osd_msg(struct MPContext *mpctx);
void update_subtitles(struct MPContext *mpctx);
void uninit_sub_renderer(struct MPContext *mpctx);
void get_osd_sub_state(struct MPContext *mpctx, int order,
struct osd_sub_state *out_state);
void update_osd_sub_state(struct MPContext *mpctx, int order,
struct osd_sub_state *out_state);
// timeline/tl_matroska.c
void build_ordered_chapter_timeline(struct MPContext *mpctx);
+2 -3
View File
@@ -125,9 +125,8 @@ void mp_nav_init(struct MPContext *mpctx)
MP_VERBOSE(mpctx->nav_state, "enabling\n");
mp_input_enable_section(mpctx->input, "discnav", 0);
mp_input_set_section_mouse_area(mpctx->input, "discnav-menu",
INT_MIN, INT_MIN, INT_MAX, INT_MAX);
mp_input_enable_section(mpctx->input, "discnav",
MP_INPUT_ALLOW_VO_DRAGGING | MP_INPUT_ALLOW_HIDE_CURSOR);
update_state(mpctx);
}
+46 -16
View File
@@ -241,6 +241,15 @@ static void enable_demux_thread(struct MPContext *mpctx)
if (mpctx->demuxer && mpctx->opts->demuxer_thread) {
demux_set_wakeup_cb(mpctx->demuxer, wakeup_demux, mpctx);
demux_start_thread(mpctx->demuxer);
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->is_external && track->stream &&
track->stream->type != STREAM_SUB)
{
demux_set_wakeup_cb(track->demuxer, wakeup_demux, mpctx);
demux_start_thread(track->demuxer);
}
}
}
}
@@ -300,14 +309,17 @@ double timeline_set_from_time(struct MPContext *mpctx, double pts, bool *need_re
{
if (pts < 0)
pts = 0;
int new = mpctx->num_timeline_parts - 1;
for (int i = 0; i < mpctx->num_timeline_parts; i++) {
struct timeline_part *p = mpctx->timeline + i;
if (pts < (p + 1)->start) {
*need_reset = timeline_set_part(mpctx, i, false);
return pts - p->start + p->source_start;
if (pts < mpctx->timeline[i + 1].start) {
new = i;
break;
}
}
return MP_NOPTS_VALUE;
*need_reset = timeline_set_part(mpctx, new, false);
return pts - mpctx->timeline[new].start + mpctx->timeline[new].source_start;
}
static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
@@ -718,12 +730,30 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename)
STREAM_SUB);
}
// Do stuff to a newly loaded playlist. This includes any processing that may
// be required after loading a playlist.
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl)
{
struct MPOpts *opts = mpctx->opts;
if (opts->shuffle)
playlist_shuffle(pl);
if (opts->merge_files)
merge_playlist_files(pl);
pl->current = mp_check_playlist_resume(mpctx, pl);
if (!pl->current)
pl->current = pl->first;
}
// Replace the current playlist entry with playlist contents. Moves the entries
// from the given playlist pl, so the entries don't actually need to be copied.
static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
{
if (pl->first) {
struct playlist_entry *new = mp_check_playlist_resume(mpctx, pl);
prepare_playlist(mpctx, pl);
struct playlist_entry *new = pl->current;
playlist_transfer_entries(mpctx->playlist, pl);
// current entry is replaced
if (mpctx->playlist->current)
@@ -900,7 +930,6 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->paused = false;
mpctx->paused_for_cache = false;
mpctx->playing_msg_shown = false;
mpctx->step_frames = 0;
mpctx->backstep_active = false;
mpctx->audio_delay = 0;
mpctx->max_frames = -1;
@@ -964,6 +993,7 @@ static void play_current_file(struct MPContext *mpctx)
if (opts->stream_dump && opts->stream_dump[0]) {
stream_dump(mpctx);
mpctx->error_playing = 1;
goto terminate_playback;
}
@@ -1090,16 +1120,11 @@ goto_reopen_demuxer: ;
}
#endif
reinit_video_chain(mpctx);
reinit_audio_chain(mpctx);
reinit_subs(mpctx, 0);
reinit_subs(mpctx, 1);
//==================== START PLAYING =======================
if (!mpctx->d_video && !mpctx->d_audio) {
struct demuxer *d = mpctx->demuxer;
if (!mpctx->current_track[0][STREAM_VIDEO] &&
!mpctx->current_track[0][STREAM_AUDIO])
{
MP_FATAL(mpctx, "No video or audio streams selected.\n");
struct demuxer *d = mpctx->demuxer;
if (d->stream->uncached_type == STREAMTYPE_DVB) {
int dir = mpctx->last_dvb_step;
if (demux_stream_control(d, STREAM_CTRL_DVB_STEP_CHANNEL, &dir) > 0)
@@ -1109,6 +1134,11 @@ goto_reopen_demuxer: ;
goto terminate_playback;
}
reinit_video_chain(mpctx);
reinit_audio_chain(mpctx);
reinit_subs(mpctx, 0);
reinit_subs(mpctx, 1);
MP_VERBOSE(mpctx, "Starting playback...\n");
if (mpctx->max_frames == 0) {
+7 -28
View File
@@ -81,7 +81,6 @@ struct script_ctx {
struct mp_log *log;
struct mpv_handle *client;
struct MPContext *mpctx;
int suspended;
};
#if LUA_VERSION_NUM <= 501
@@ -388,8 +387,7 @@ static int load_lua(struct mpv_handle *client, const char *fname)
r = 0;
error_out:
if (ctx->suspended)
mpv_resume(ctx->client);
mp_resume_all(client);
if (ctx->state)
lua_close(ctx->state);
talloc_free(ctx);
@@ -447,33 +445,20 @@ static int script_find_config_file(lua_State *L)
static int script_suspend(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
if (!ctx->suspended)
mpv_suspend(ctx->client);
ctx->suspended++;
mpv_suspend(ctx->client);
return 0;
}
static int script_resume(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
if (ctx->suspended < 1)
luaL_error(L, "trying to resume, but core is not suspended");
ctx->suspended--;
if (!ctx->suspended)
mpv_resume(ctx->client);
mpv_resume(ctx->client);
return 0;
}
static void resume_all(struct script_ctx *ctx)
{
if (ctx->suspended)
mpv_resume(ctx->client);
ctx->suspended = 0;
}
static int script_resume_all(lua_State *L)
{
resume_all(get_ctx(L));
mp_resume_all(get_ctx(L)->client);
return 0;
}
@@ -483,13 +468,7 @@ static int script_wait_event(lua_State *L)
{
struct script_ctx *ctx = get_ctx(L);
double timeout = luaL_optnumber(L, 1, 1e20);
// This will almost surely lead to a deadlock. (Polling is still ok.)
if (ctx->suspended && timeout > 0)
luaL_error(L, "attempting to wait while core is suspended");
mpv_event *event = mpv_wait_event(ctx->client, timeout);
mpv_event *event = mpv_wait_event(ctx->client, luaL_optnumber(L, 1, 1e20));
lua_newtable(L); // event
lua_pushstring(L, mpv_event_name(event->event_id)); // event name
@@ -1043,7 +1022,7 @@ static int script_input_enable_section(lua_State *L)
} else if (bstr_equals0(val, "exclusive")) {
flags |= MP_INPUT_EXCLUSIVE;
} else {
luaL_error(L, "invalid flag: '%.*s'", BSTR_P(val));
luaL_error(L, "invalid flag");
}
}
mp_input_enable_section(mpctx->input, section, flags);
@@ -1191,7 +1170,7 @@ static int script_subprocess(lua_State *L)
luaL_checktype(L, 1, LUA_TTABLE);
void *tmp = mp_lua_PITA(L);
resume_all(ctx);
mp_resume_all(ctx->client);
lua_getfield(L, 1, "args"); // args
int num_args = mp_lua_len(L, -1);
+1 -1
View File
@@ -21,7 +21,7 @@ function ass_mt.draw_stop(ass)
end
function ass_mt.coord(ass, x, y)
local scale = math.pow(2, ass.scale - 1)
local scale = 2 ^ (ass.scale - 1)
local ix = math.ceil(x * scale)
local iy = math.ceil(y * scale)
ass.text = string.format("%s %d %d", ass.text, ix, iy)
+23 -18
View File
@@ -177,9 +177,9 @@ local function add_binding(attrs, key, name, fn, rp)
if event == "r" and not repeatable then
return
end
if is_mouse and event == "u" then
if is_mouse and (event == "u" or event == "p") then
fn()
elseif (not is_mouse) and (event == "d" or event == "r") then
elseif (not is_mouse) and (event == "d" or event == "r" or event == "p") then
fn()
end
end
@@ -485,7 +485,9 @@ function mp.add_hook(name, pri, cb)
mp.commandv("hook_add", name, id, pri)
end
function mp.format_table(t, set)
local mp_utils = package.loaded["mp.utils"]
function mp_utils.format_table(t, set)
if not set then
set = { [t] = true }
end
@@ -508,30 +510,33 @@ function mp.format_table(t, set)
vals[#keys] = v
end
end
local function fmtval(v)
if type(v) == "string" then
return "\"" .. v .. "\""
elseif type(v) == "table" then
if set[v] then
return "[cycle]"
end
set[v] = true
return mp.format_table(v, set)
else
return tostring(v)
end
end
for i = 1, #keys do
if #res > 1 then
res = res .. ", "
end
if i > arr then
res = res .. fmtval(keys[i]) .. " = "
res = res .. mp_utils.to_string(keys[i], set) .. " = "
end
res = res .. fmtval(vals[i])
res = res .. mp_utils.to_string(vals[i], set)
end
res = res .. "}"
return res
end
function mp_utils.to_string(v, set)
if type(v) == "string" then
return "\"" .. v .. "\""
elseif type(v) == "table" then
if set then
if set[v] then
return "[cycle]"
end
set[v] = true
end
return mp_utils.format_table(v, set)
else
return tostring(v)
end
end
return {}
+72 -5
View File
@@ -71,6 +71,8 @@ local state = {
fullscreen = false,
timer = nil,
cache_idle = false,
idle = false,
enabled = true,
}
@@ -1723,6 +1725,7 @@ function render()
for k,cords in pairs(osc_param.areas["showhide"]) do
mp.set_mouse_area(cords.x1, cords.y1, cords.x2, cords.y2, "showhide")
end
do_enable_keybindings()
--mouse input area
local mouse_over_osc = false
@@ -1846,16 +1849,61 @@ end
-- called by mpv on every frame
function tick()
if (state.fullscreen and user_opts.showfullscreen) or (not state.fullscreen and user_opts.showwindowed) then
if (state.idle) then
-- render idle message
msg.debug("idle message")
local icon_x, icon_y = 320 - 26, 140
local ass = assdraw.ass_new()
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&H430142&\\1a&H00&\\bord0\\shad0\\p6}m 1605 828 b 1605 1175 1324 1456 977 1456 631 1456 349 1175 349 828 349 482 631 200 977 200 1324 200 1605 482 1605 828{\\p0}")
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&HDDDBDD&\\1a&H00&\\bord0\\shad0\\p6}m 1296 910 b 1296 1131 1117 1310 897 1310 676 1310 497 1131 497 910 497 689 676 511 897 511 1117 511 1296 689 1296 910{\\p0}")
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&H691F69&\\1a&H00&\\bord0\\shad0\\p6}m 762 1113 l 762 708 b 881 776 1000 843 1119 911 1000 978 881 1046 762 1113{\\p0}")
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&H682167&\\1a&H00&\\bord0\\shad0\\p6}m 925 42 b 463 42 87 418 87 880 87 1343 463 1718 925 1718 1388 1718 1763 1343 1763 880 1763 418 1388 42 925 42 m 925 42 m 977 200 b 1324 200 1605 482 1605 828 1605 1175 1324 1456 977 1456 631 1456 349 1175 349 828 349 482 631 200 977 200{\\p0}")
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&H753074&\\1a&H00&\\bord0\\shad0\\p6}m 977 198 b 630 198 348 480 348 828 348 1176 630 1458 977 1458 1325 1458 1607 1176 1607 828 1607 480 1325 198 977 198 m 977 198 m 977 202 b 1323 202 1604 483 1604 828 1604 1174 1323 1454 977 1454 632 1454 351 1174 351 828 351 483 632 202 977 202{\\p0}")
ass:new_event()
ass:pos(icon_x, icon_y)
ass:append("{\\rDefault\\an7\\c&HE5E5E5&\\1a&H00&\\bord0\\shad0\\p6}m 895 10 b 401 10 0 410 0 905 0 1399 401 1800 895 1800 1390 1800 1790 1399 1790 905 1790 410 1390 10 895 10 m 895 10 m 925 42 b 1388 42 1763 418 1763 880 1763 1343 1388 1718 925 1718 463 1718 87 1343 87 880 87 418 463 42 925 42{\\p0}")
ass:new_event()
ass:pos(320, icon_y+65)
ass:an(8)
ass:append("Drop files to play here.")
mp.set_osd_ass(640, 360, ass.text)
mp.disable_key_bindings("showhide")
elseif (state.fullscreen and user_opts.showfullscreen)
or (not state.fullscreen and user_opts.showwindowed) then
-- render the OSC
render()
else
-- Flush OSD
mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "")
end
end
function do_enable_keybindings()
if state.enabled then
mp.enable_key_bindings("showhide", "allow-vo-dragging|allow-hide-cursor")
end
end
function enable_osc(enable)
state.enabled = enable
if enable then
mp.enable_key_bindings("showhide")
do_enable_keybindings()
show_osc()
else
hide_osc()
@@ -1872,16 +1920,35 @@ mp.register_event("tracks-changed", request_init)
mp.register_script_message("enable-osc", function() enable_osc(true) end)
mp.register_script_message("disable-osc", function() enable_osc(false) end)
mp.observe_property("fullscreen", "bool", function(name, val) state.fullscreen = val end)
mp.observe_property("fullscreen", "bool",
function(name, val)
state.fullscreen = val
end
)
mp.observe_property("idle", "bool",
function(name, val)
state.idle = val
tick()
end
)
mp.observe_property("pause", "bool", pause_state)
mp.observe_property("cache-idle", "bool", cache_state)
mp.observe_property("disc-menu-active", "bool", function(name, val)
if val == true then
hide_osc()
mp.disable_key_bindings("showhide")
else
do_enable_keybindings()
end
end)
-- mouse show/hide bindings
mp.set_key_bindings({
{"mouse_move", function(e) process_event("mouse_move", nil) end},
{"mouse_leave", mouse_leave},
}, "showhide")
mp.enable_key_bindings("showhide", "allow-vo-dragging|allow-hide-cursor")
}, "showhide", "force")
do_enable_keybindings()
--mouse input bindings
mp.set_key_bindings({
+84 -22
View File
@@ -3,17 +3,41 @@ local msg = require 'mp.msg'
local ytdl = {
path = "youtube-dl",
minver = "2014.11.09",
minver = "2014.11.26",
vercheck = nil,
}
mp.add_hook("on_load", 10, function ()
local function exec(args)
local ret = utils.subprocess({args = args})
return ret.status, ret.stdout
end
local function exec(args)
local ret = utils.subprocess({args = args})
return ret.status, ret.stdout
-- return if it was explicitly set on the command line
local function option_was_set(name)
return mp.get_property_bool("option-info/" .. name .. "/set-from-commandline",
false)
end
-- youtube-dl may set special http headers for some sites (user-agent, cookies)
local function set_http_headers(http_headers)
if not http_headers then
return
end
local headers = {}
local useragent = http_headers["User-Agent"]
if useragent and not option_was_set("user-agent") then
mp.set_property("file-local-options/user-agent", useragent)
end
local cookies = http_headers["Cookie"]
if cookies then
headers[#headers + 1] = "Cookie: " .. cookies
end
if #headers > 0 and not option_was_set("http-header-fields") then
mp.set_property_native("file-local-options/http-header-fields", headers)
end
end
mp.add_hook("on_load", 10, function ()
local url = mp.get_property("stream-open-filename")
if (url:find("http://") == 1) or (url:find("https://") == 1)
@@ -87,7 +111,7 @@ mp.add_hook("on_load", 10, function ()
return
end
msg.info("youtube-dl succeeded!")
msg.verbose("youtube-dl succeeded!")
-- what did we get?
if not (json["direct"] == nil) and (json["direct"] == true) then
@@ -97,30 +121,67 @@ mp.add_hook("on_load", 10, function ()
elseif not (json["_type"] == nil) and (json["_type"] == "playlist") then
-- a playlist
local playlist = "#EXTM3U\n"
for i, entry in pairs(json.entries) do
local site = entry.url
-- some extractors will still return the full info for
-- all clips in the playlist and the URL will point
-- directly to the file in that case, which we don't
-- want so get the webpage URL instead, which is what
-- we want
if not (entry["webpage_url"] == nil) then
site = entry["webpage_url"]
end
playlist = playlist .. "ytdl://" .. site .. "\n"
if (#json.entries == 0) then
msg.warn("Got empty playlist, nothing to play.")
return
end
mp.set_property("stream-open-filename", "memory://" .. playlist)
-- some funky guessing to detect multi-arc videos
if not (json.entries[1]["webpage_url"] == nil)
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 .. entry.url .. ";"
end
msg.debug("EDL: " .. playlist)
-- can't change the http headers for each entry, so use the 1st
if json.entries[1] then
set_http_headers(json.entries[1].http_headers)
end
mp.set_property("stream-open-filename", playlist)
if not (json.title == nil) then
mp.set_property("file-local-options/media-title", json.title)
end
else
local playlist = "#EXTM3U\n"
for i, entry in pairs(json.entries) do
local site = entry.url
-- some extractors will still return the full info for
-- all clips in the playlist and the URL will point
-- directly to the file in that case, which we don't
-- want so get the webpage URL instead, which is what
-- we want
if not (entry["webpage_url"] == nil) then
site = entry["webpage_url"]
end
playlist = playlist .. "ytdl://" .. site .. "\n"
end
mp.set_property("stream-open-filename", "memory://" .. playlist)
end
else -- probably a video
local streamurl = ""
-- DASH?
if not (json["requested_formats"] == nil) then
msg.info("NOTE: Using DASH, expect inaccurate duration.")
msg.info("Using DASH, expect inaccurate duration.")
if not (json.duration == nil) then
msg.info("Actual duration: " .. mp.format_time(json.duration))
end
-- video url
streamurl = json["requested_formats"][1].url
@@ -135,6 +196,7 @@ mp.add_hook("on_load", 10, function ()
elseif not (json.url == nil) then
-- normal video
streamurl = json.url
set_http_headers(json.http_headers)
else
msg.error("No URL found in JSON data.")
return
+22 -18
View File
@@ -46,6 +46,7 @@
#include "options/parse_commandline.h"
#include "common/playlist.h"
#include "options/options.h"
#include "options/path.h"
#include "input/input.h"
#include "audio/decode/dec_audio.h"
@@ -83,6 +84,10 @@
static bool terminal_initialized;
#ifndef FULLCONFIG
#define FULLCONFIG "(missing)\n"
#endif
enum exit_reason {
EXIT_NONE,
EXIT_NORMAL,
@@ -107,10 +112,15 @@ void mp_print_version(struct mp_log *log, int always)
{
int v = always ? MSGL_INFO : MSGL_V;
mp_msg(log, v,
"%s (C) 2000-2014 mpv/MPlayer/mplayer2 projects\n built on %s\n",
"%s (C) 2000-2015 mpv/MPlayer/mplayer2 projects\n built on %s\n",
mpv_version, mpv_builddate);
print_libav_versions(log, v);
mp_msg(log, v, "\n");
// Only in verbose mode.
if (!always) {
mp_msg(log, MSGL_V, "Configuration: " CONFIGURATION "\n");
mp_msg(log, MSGL_V, "config.h:\n%s\n", FULLCONFIG);
}
}
static void shutdown_clients(struct MPContext *mpctx)
@@ -306,7 +316,10 @@ static void osdep_preinit(int *p_argc, char ***p_argv)
static int cfg_include(void *ctx, char *filename, int flags)
{
struct MPContext *mpctx = ctx;
return m_config_parse_config_file(mpctx->mconfig, filename, NULL, flags);
char *fname = mp_get_user_path(NULL, mpctx->global, filename);
int r = m_config_parse_config_file(mpctx->mconfig, fname, NULL, flags);
talloc_free(fname);
return r;
}
struct MPContext *mp_create(void)
@@ -355,7 +368,7 @@ struct MPContext *mp_create(void)
return mpctx;
}
static void wakeup_playloop(void *ctx)
void wakeup_playloop(void *ctx)
{
struct MPContext *mpctx = ctx;
mp_input_wakeup(mpctx->input);
@@ -457,15 +470,7 @@ int mp_initialize(struct MPContext *mpctx)
mpctx->ipc_ctx = mp_init_ipc(mpctx->clients, mpctx->global);
#endif
if (opts->shuffle)
playlist_shuffle(mpctx->playlist);
if (opts->merge_files)
merge_playlist_files(mpctx->playlist);
mpctx->playlist->current = mp_check_playlist_resume(mpctx, mpctx->playlist);
if (!mpctx->playlist->current)
mpctx->playlist->current = mpctx->playlist->first;
prepare_playlist(mpctx, mpctx->playlist);
MP_STATS(mpctx, "end init");
@@ -493,6 +498,11 @@ int mpv_main(int argc, char *argv[])
mp_msg_update_msglevels(mpctx->global);
MP_VERBOSE(mpctx, "Command line:");
for (int i = 0; i < argc; i++)
MP_VERBOSE(mpctx, " '%s'", argv[i]);
MP_VERBOSE(mpctx, "\n");
mp_print_version(mpctx->log, false);
mp_parse_cfgfiles(mpctx);
@@ -512,12 +522,6 @@ int mpv_main(int argc, char *argv[])
if (handle_help_options(mpctx))
exit_player(mpctx, EXIT_NONE);
MP_VERBOSE(mpctx, "Configuration: " CONFIGURATION "\n");
MP_VERBOSE(mpctx, "Command line:");
for (int i = 0; i < argc; i++)
MP_VERBOSE(mpctx, " '%s'", argv[i]);
MP_VERBOSE(mpctx, "\n");
if (!mpctx->playlist->first && !opts->player_idle_mode) {
mp_print_version(mpctx->log, true);
MP_INFO(mpctx, "%s", mp_help_text);
+12 -21
View File
@@ -154,6 +154,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->playback_pts = MP_NOPTS_VALUE;
mpctx->last_seek_pts = MP_NOPTS_VALUE;
mpctx->cache_wait_time = 0;
mpctx->step_frames = 0;
mpctx->restart_complete = false;
#if HAVE_ENCODING
@@ -167,6 +168,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
{
struct MPOpts *opts = mpctx->opts;
uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts;
int prev_step = mpctx->step_frames;
if (!mpctx->demuxer)
return -1;
@@ -214,16 +216,6 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
bool need_reset = false;
demuxer_amount = timeline_set_from_time(mpctx, seek.amount,
&need_reset);
if (demuxer_amount == MP_NOPTS_VALUE) {
assert(!need_reset);
mpctx->stop_play = AT_END_OF_FILE;
// When seeking outside of the file, but not when ending last segment.
if (!timeline_fallthrough) {
clear_audio_output_buffers(mpctx);
reset_playback_state(mpctx);
}
return -1;
}
if (need_reset) {
reinit_video_chain(mpctx);
reinit_audio_chain(mpctx);
@@ -272,6 +264,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
if (timeline_fallthrough) {
// Important if video reinit happens.
mpctx->vo_pts_history_seek_ts = prev_seek_ts;
mpctx->step_frames = prev_step;
} else {
mpctx->vo_pts_history_seek_ts++;
mpctx->backstep_active = false;
@@ -320,10 +313,6 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
seek->exact = exact;
if (seek->type == MPSEEK_ABSOLUTE)
return;
if (seek->amount == 0) {
*seek = (struct seek_params){ 0 };
return;
}
seek->type = MPSEEK_RELATIVE;
return;
case MPSEEK_ABSOLUTE:
@@ -420,9 +409,9 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
len = endpos - startpos;
}
double pos = get_current_time(mpctx);
if (len > 0 && !demuxer->ts_resets_possible) {
if (len > 0)
ans = MPCLAMP((pos - start) / len, 0, 1);
} else {
if (ans < 0 || demuxer->ts_resets_possible) {
int64_t size;
if (demux_stream_control(demuxer, STREAM_CTRL_GET_SIZE, &size) > 0) {
if (size > 0 && demuxer->filepos >= 0)
@@ -437,10 +426,11 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
return ans;
}
// 0-100, -1 if unknown
int get_percent_pos(struct MPContext *mpctx)
{
int pos = get_current_pos_ratio(mpctx, false) * 100;
return MPCLAMP(pos, 0, 100);
double pos = get_current_pos_ratio(mpctx, false);
return pos < 0 ? -1 : pos * 100;
}
// -2 is no chapters, -1 is before first chapter
@@ -914,8 +904,8 @@ void run_playloop(struct MPContext *mpctx)
fill_audio_out_buffers(mpctx, endpts); // actually play prepared buffer
if (!mpctx->restart_complete) {
mpctx->hrseek_active = false;
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
mpctx->restart_complete = true;
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
if (!mpctx->playing_msg_shown) {
if (opts->playing_msg) {
char *msg =
@@ -961,10 +951,11 @@ void run_playloop(struct MPContext *mpctx)
mpctx->audio_status == STATUS_EOF &&
mpctx->video_status == STATUS_EOF)
{
if (end_is_new_segment) {
int new_part = mpctx->timeline_part + 1;
if (end_is_new_segment && new_part < mpctx->num_timeline_parts) {
mp_seek(mpctx, (struct seek_params){
.type = MPSEEK_ABSOLUTE,
.amount = mpctx->timeline[mpctx->timeline_part+1].start
.amount = mpctx->timeline[new_part].start
}, true);
} else
mpctx->stop_play = AT_END_OF_FILE;
+1 -1
View File
@@ -129,7 +129,7 @@ static char *create_fname(struct MPContext *mpctx, char *template,
struct tm *local_time = localtime(&raw_time);
if (!template || *template == '\0')
template = "shot%n";
return NULL;
for (;;) {
char *next = strchr(template, '%');
+12 -13
View File
@@ -127,8 +127,8 @@ void uninit_sub_renderer(struct MPContext *mpctx)
static void init_sub_renderer(struct MPContext *mpctx) {}
void uninit_sub_renderer(struct MPContext *mpctx) {}
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
struct mp_osd_res *dim) {}
void mp_ass_configure_fonts(struct ass_renderer *a, struct MPOpts *b,
struct mpv_global *c, struct mp_log *d) {}
#endif
@@ -146,10 +146,9 @@ void uninit_stream_sub_decoders(struct demuxer *demuxer)
void uninit_sub(struct MPContext *mpctx, int order)
{
if (mpctx->d_sub[order]) {
mpctx->d_sub[order] = NULL; // Note: not free'd.
int obj = order ? OSDTYPE_SUB2 : OSDTYPE_SUB;
osd_set_sub(mpctx->osd, obj, NULL);
reset_subtitles(mpctx, order);
mpctx->d_sub[order] = NULL; // Note: not free'd.
update_osd_sub_state(mpctx, order, NULL); // unset
reselect_demux_streams(mpctx);
}
}
@@ -193,24 +192,25 @@ void reset_subtitle_state(struct MPContext *mpctx)
reset_subtitles(mpctx, 1);
}
void get_osd_sub_state(struct MPContext *mpctx, int order,
struct osd_sub_state *out_state)
void update_osd_sub_state(struct MPContext *mpctx, int order,
struct osd_sub_state *out_state)
{
struct MPOpts *opts = mpctx->opts;
struct track *track = mpctx->current_track[order][STREAM_SUB];
struct dec_sub *dec_sub = mpctx->d_sub[order];
int obj = order ? OSDTYPE_SUB2 : OSDTYPE_SUB;
bool textsub = dec_sub && sub_has_get_text(dec_sub);
struct osd_sub_state state = {
.dec_sub = dec_sub,
// Decides whether to use OSD path or normal subtitle rendering path.
.render_bitmap_subs = opts->ass_enabled || !sub_has_get_text(dec_sub),
.render_bitmap_subs = opts->ass_enabled || !textsub,
.video_offset = get_track_video_offset(mpctx, track),
};
// Secondary subs are rendered with the "text" renderer to transform them
// to toptitles.
if (order == 1 && sub_has_get_text(dec_sub))
if (order == 1 && textsub)
state.render_bitmap_subs = false;
if (!mpctx->current_track[0][STREAM_VIDEO])
@@ -227,10 +227,9 @@ static void update_subtitle(struct MPContext *mpctx, int order)
struct track *track = mpctx->current_track[order][STREAM_SUB];
struct dec_sub *dec_sub = mpctx->d_sub[order];
if (!track)
if (!track || !dec_sub)
return;
assert(dec_sub);
int obj = order ? OSDTYPE_SUB2 : OSDTYPE_SUB;
if (mpctx->d_video) {
@@ -240,7 +239,7 @@ static void update_subtitle(struct MPContext *mpctx, int order)
}
struct osd_sub_state state;
get_osd_sub_state(mpctx, order, &state);
update_osd_sub_state(mpctx, order, &state);
double refpts_s = mpctx->playback_pts - state.video_offset;
double curpts_s = refpts_s - opts->sub_delay;
@@ -344,5 +343,5 @@ void reinit_subs(struct MPContext *mpctx, int order)
struct dec_sub *dec_sub = mpctx->d_sub[order];
reinit_subdec(mpctx, track, dec_sub);
get_osd_sub_state(mpctx, order, NULL);
update_osd_sub_state(mpctx, order, NULL);
}
+25 -11
View File
@@ -381,6 +381,7 @@ static void build_timeline_loop(struct MPContext *mpctx,
uint64_t *last_end_time,
struct timeline_part **timeline,
struct demux_chapter *chapters,
int num_chapters,
int *part_count,
uint64_t skip,
uint64_t limit)
@@ -418,6 +419,10 @@ static void build_timeline_loop(struct MPContext *mpctx,
* recursive ordered chapter editions? If so, more code will be
* needed to add chapters for external non-ordered segment loading
* as well since that part is not recursive. */
if (i >= num_chapters)
break; // probably needed only for broken sources
if (!limit) {
chapters[i].pts = *starttime / 1e9;
chapters[i].name = talloc_strdup(chapters, c->name);
@@ -477,11 +482,9 @@ static void build_timeline_loop(struct MPContext *mpctx,
} else {
build_timeline_loop(mpctx, sources, num_sources, j, starttime,
missing_time, last_end_time, timeline,
chapters, part_count, c->start, c->end);
/* The loop call has added time as needed (we can't add it here
* due to 'join_diff' in the add_timeline_part function. Since
* the time has already been added as needed, the chapter has
* an effective 0 length at this point. */
chapters, num_chapters, part_count,
c->start, c->end);
// Already handled by the loop call.
chapter_length = 0;
}
*last_end_time = c->end;
@@ -562,20 +565,31 @@ void build_ordered_chapter_timeline(struct MPContext *mpctx)
struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline, 0);
struct demux_chapter *chapters =
talloc_zero_array(NULL, struct demux_chapter, m->num_ordered_chapters);
// Stupid hack, because fuck everything.
for (int n = 0; n < m->num_ordered_chapters; n++)
chapters[n].pts = -1;
uint64_t starttime = 0;
uint64_t missing_time = 0;
uint64_t last_end_time = 0;
int part_count = 0;
build_timeline_loop(mpctx, sources, num_sources, 0, &starttime,
&missing_time, &last_end_time, &timeline,
chapters, &part_count, 0, 0);
chapters, m->num_ordered_chapters, &part_count, 0, 0);
// Fuck everything (2): filter out all "unset" chapters.
for (int n = m->num_ordered_chapters - 1; n >= 0; n--) {
if (chapters[n].pts == -1)
MP_TARRAY_REMOVE_AT(chapters, m->num_ordered_chapters, n);
}
if (!part_count) {
// None of the parts come from the file itself???
talloc_free(sources);
talloc_free(timeline);
talloc_free(chapters);
return;
// None of the parts come from the file itself???
// Broken file, but we need at least 1 valid timeline part - add a dummy.
MP_WARN(mpctx, "Ordered chapters file with no parts?\n");
struct timeline_part new = {
.source = demuxer,
};
MP_TARRAY_APPEND(NULL, timeline, part_count, new);
}
struct timeline_part new = {
+38 -15
View File
@@ -66,7 +66,7 @@ static const char av_desync_help_text[] =
"This means either the audio or the video is played too slowly.\n"
"Possible reasons, problems, workarounds:\n"
"- Your system is simply too slow for this file.\n"
" Transcode it to a lower bitrate file with tools like HandBrake.\n"
" Transcode it to a lower bitrate file with e.g. mpv encoding support.\n"
"- Slow video output.\n"
" Try a different --vo driver (--vo=help for a list). Make sure framedrop\n"
" is not disabled, or experiment with different values for --framedrop.\n"
@@ -74,7 +74,7 @@ static const char av_desync_help_text[] =
" Download the file instead.\n"
"- Try to find out whether audio/video/subs are causing this by experimenting\n"
" with --no-video, --no-audio, or --no-sub.\n"
"- If you swiched audio or video tracks, try seeking to force synchronization.\n"
"- If you switched audio or video tracks, try seeking to force synchronization.\n"
"If none of this helps you, file a bug report.\n\n";
void update_fps(struct MPContext *mpctx)
@@ -178,6 +178,9 @@ static void recreate_video_filters(struct MPContext *mpctx)
vf_destroy(d_video->vfilter);
d_video->vfilter = vf_new(mpctx->global);
d_video->vfilter->hwdec = d_video->hwdec_info;
d_video->vfilter->wakeup_callback = wakeup_playloop;
d_video->vfilter->wakeup_callback_ctx = mpctx;
d_video->vfilter->container_fps = d_video->fps;
vf_append_filter_list(d_video->vfilter, opts->vf_settings);
@@ -287,6 +290,13 @@ int reinit_video_chain(struct MPContext *mpctx)
d_video->fps = sh->video->fps;
d_video->vo = mpctx->video_out;
if (opts->force_fps) {
d_video->fps = opts->force_fps;
MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
}
update_fps(mpctx);
vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);
recreate_video_filters(mpctx);
@@ -310,16 +320,6 @@ int reinit_video_chain(struct MPContext *mpctx)
reset_video_state(mpctx);
reset_subtitle_state(mpctx);
if (opts->force_fps) {
d_video->fps = opts->force_fps;
MP_INFO(mpctx, "FPS forced to be %5.3f.\n", d_video->fps);
}
if (!sh->video->fps && !opts->force_fps && !opts->correct_pts) {
MP_ERR(mpctx, "FPS not specified in the "
"header or invalid, use the -fps option.\n");
}
update_fps(mpctx);
return 1;
err_out:
@@ -380,6 +380,8 @@ static int decode_image(struct MPContext *mpctx)
return VD_WAIT;
if (pkt && pkt->pts != MP_NOPTS_VALUE)
pkt->pts += mpctx->video_offset;
if (pkt && pkt->dts != MP_NOPTS_VALUE)
pkt->dts += mpctx->video_offset;
if ((pkt && pkt->pts >= mpctx->hrseek_pts - .005) ||
d_video->has_broken_packet_pts ||
!mpctx->opts->hr_seek_framedrop)
@@ -430,7 +432,8 @@ static int video_filter(struct MPContext *mpctx, bool eof)
return VD_ERROR;
// There is already a filtered frame available.
if (vf_output_frame(vf, eof) > 0)
// If vf_needs_input() returns > 0, the filter wants input anyway.
if (vf_output_frame(vf, eof) > 0 && vf_needs_input(vf) < 1)
return VD_PROGRESS;
// Decoder output is different from filter input?
@@ -491,6 +494,20 @@ static int video_decode_and_filter(struct MPContext *mpctx)
return r;
}
static int video_feed_async_filter(struct MPContext *mpctx)
{
struct dec_video *d_video = mpctx->d_video;
struct vf_chain *vf = d_video->vfilter;
if (vf->initialized < 0)
return VD_ERROR;
if (vf_needs_input(vf) < 1)
return 0;
mpctx->sleeptime = 0; // retry until done
return video_decode_and_filter(mpctx);
}
/* Modify video timing to match the audio timeline. There are two main
* reasons this is needed. First, video and audio can start from different
* positions at beginning of file or after a seek (MPlayer starts both
@@ -777,8 +794,12 @@ void write_video(struct MPContext *mpctx, double endpts)
double time_frame = MPMAX(mpctx->time_frame, -1);
int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);
if (!vo_is_ready_for_frame(vo, pts))
return; // wait until VO wakes us up to get more frames
// wait until VO wakes us up to get more frames
if (!vo_is_ready_for_frame(vo, pts)) {
if (video_feed_async_filter(mpctx) < 0)
goto error;
return;
}
int64_t duration = -1;
double diff = -1;
@@ -788,6 +809,8 @@ void write_video(struct MPContext *mpctx, double endpts)
diff = vpts1 - vpts0;
if (diff < 0 && mpctx->d_video->fps > 0)
diff = 1.0 / mpctx->d_video->fps; // fallback to demuxer-reported fps
if (opts->untimed || vo->driver->untimed)
diff = -1; // disable frame dropping and aspects of frame timing
if (diff >= 0) {
// expected A/V sync correction is ignored
diff /= opts->playback_speed;
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff. Show more