Compare commits

...
899 Commits
Author SHA1 Message Date
wm4 76d771a6fa Release 0.3.5 2014-02-12 22:39:52 +01:00
nand 19caba0794 vo_opengl: make :srgb decompand the BT.709 values correctly
This is the same issue as addressed by 257d9f1, except this time for
the :srgb option as well. (257d9f1 only addressed :icc-profile)

The conditions of the srgb_compand mix() call are also flipped to
prevent an off-by-one error.
2014-02-12 22:34:44 +01:00
wm4 391546c52f command: fix metadata property
This crashed when retrieving the raw property value. Oops.
2014-02-12 22:34:28 +01:00
Alexander Preisinger a8af6a7852 wayland/shm: prevent the window from flying away
With the new xdg_shell the problem will be no gone by itself.
2014-02-12 22:34:09 +01:00
wm4 960aca3386 manpage: document --aspect special values
Use of these is "discouraged", but they're there to select these special
cases with the "aspect" property. They really should use some sort of
choice option type, but since it would be some work to make these work
with float values, the simple and dumb alternative was picked.
2014-02-12 22:33:54 +01:00
wm4 e72a327d74 video: fix --no-aspect
This also affects the --aspect option and the "aspect" property.
2014-02-12 22:33:52 +01:00
nand 87f955f3e9 vo_opengl: fix typo in gamma function's parameter
The correct value is 0.081, not 0.18. The scale factor also needed
slight adjustment due to the order of operations.
2014-02-12 22:33:32 +01:00
Jonas Zetterberg 5536ebe82f vo_vdpau: Discard zero timestamps
Some drivers do not supply timestamps, use old timestamp in these conditions.
2014-02-12 22:33:29 +01:00
Jonas Zetterberg ef8b37ca2a vo_vdpau: Ensure presentation time is within bounds
When a time sync happens the last sync time is the minimum time that can be
used for presentation.
2014-02-12 22:33:27 +01:00
wm4 d4a5513847 demux: fix some newlines in output messages 2014-02-12 22:33:09 +01:00
wm4 d42212cd67 player: fix an assert when reinitializing audio in some cases
This sometimes happened when changing playback speed (= reinitializing
audio) after seeking of playback start. The assertion in audio.c:441 was
triggered, because buffer_playable_samples wasn't reset correctly when
the audio buffer was cleared or shortened. The assertion is correct and
should hold up any time.
2014-02-12 22:33:03 +01:00
nand 674cc1d3a9 vo_opengl: use exactly the values defined by BT.709 for CMS
I could not see any difference whatsoever, but for usage with a 3DLUT
there's zero performance difference so we might as well follow the spec to
the letter.
2014-02-12 22:32:56 +01:00
wm4 c47bb9ca42 gl_x11: don't require a X visual for modern GL context creation
Legacy GL context creation (glXCreateContext) explicitly requires a X
visual, while the modern one (glXCreateContextAttribsARB) does not for
some reason. So fail only on the legacy code path if we don't find a
visual. Note that vo_x11_config_vo_window() will select a default visual
if a NULL visual is passed to it.
2014-02-12 22:32:43 +01:00
wm4 938dad0b88 gl_x11: always request true color visual
This fixes issue #504. For some reason, glXChooseFBConfig() will return
a fbconfig with no associated visual. (I'm not sure if this allowed.
They don't always have a visual, but since GLX_X_RENDERABLE is set
and GLX_DRAWABLE_TYPE is (implicitly) set to GLX_WINDOW_BIT, why would
there be no visual?)

Even worse, a test program seems to show that a 16 bit fbconfig is
selected (instead of 24/32 bit), which doesn't sound nice at all. Since
there _are_ better fbconfigs available, glXChooseFBConfig() should
normally sort them by quality, and return the better ones first. It's
worth noting that this function should also prefer GLX_TRUE_COLOR
over anything else, although this comes last in the sort order.

Whatever is going on, requesting GLX_X_VISUAL_TYPE with GLX_TRUE_COLOR
seems to fix it.
2014-02-12 22:32:40 +01:00
wm4 82b5361946 umpv: silence mpv terminal output
Now it does basically the same as mkv.desktop. Explanation is included
in the description comment.
2014-02-12 22:32:06 +01:00
wm4 f99f48684b umpv: improve description 2014-02-12 22:32:04 +01:00
wm4 25864a5539 umpv: use --force-window
This makes it show a window even when playing audio-only files.
2014-02-12 22:32:02 +01:00
wm4 524d9585fd audio: fix signedness of AF_FORMAT_S32P
This was marked as unsigned, but it's signed. Found by xylosper.
2014-02-12 22:32:00 +01:00
wm4 95a72baf92 mf: don't limit filename length with PATH_MAX
Use an arbitrary constant instead, which is as good as PATH_MAX.

This helps us to avoid having to think about pull request #523.

Also fix a case where a potentially signed char was passed to isspace().
2014-02-12 22:31:54 +01:00
wm4 48112a74ed player: refresh OSD on track switching
Apparently, at least sub_reload was missing a refresh at all.
2014-02-12 22:31:40 +01:00
Alexander Preisinger 8427cdc717 wayland: change minimum version
Change minimum version to 1.3 and remove the version checking in the source
code.
2014-02-12 22:31:27 +01:00
wm4 b695ca1e27 x11: fix initial VO size
This was done incorrectly in the previous commit: the fallback size used
the window size as requested with the first config call, which is the
size of the hidden window in the vo_opengl case. (That damn hidden
window again...)

Conflicts:
	video/out/x11_common.c
2014-02-12 22:31:21 +01:00
wm4 801feb6420 x11: remove apparently useless code
This code essentially does nothing. As far as I could find out, this
actually used to do something. Then it was removed with commit efe7c39f,
leaving some leftover code that didn't do anything useful. This happened
12 years ago!

Also remove a commented debug printf.
2014-02-12 22:30:34 +01:00
wm4 de6a452039 x11: fix race condition when setting aspect when leaving fullscreen
vo_opengl creates a hidden X11 window to probe the OpenGL context. It
must do that before creating a visible window, because VO creation and
VO config are separate phases.

There's a race condition involving the hidden window: when starting with
--fs, and then leaving fullscreen, the unfullscreened window is
sometimes set to the aspect ratio of the hidden window. I'm not sure why
the window size itself uses the correct size (but corrupted by the wrong
aspect), but that's perhaps because the window manager is free to ignore
the size hint while honoring the aspect, or something equally messed up.

It turns out this happens because x11_common.c thinks the size of the
hidden window is the size of the unfullscreened window. This in turn
happens because vo_x11_update_geometry() reads the size of the hidden
window when called in vo_x11_fullscreen() (called from
vo_x11_config_vo_window()) when mapping the fullscreen window. At that
point, the window could be mapped, but not necessarily. If it's not
mapped, it will get the size of the unfullscreened window... I think.

One could fix this by actively waiting until the window is mapped. Try
to pick a less hacky approach instead, and never read the window size
until MapNotify is received.

vo_x11_create_window() needs a hack, because we'd possibly set the VO's
size to 0, resulting e.g. in vdpau to fail initialization. (It'll print
error messages until a proper resize is received.)

Conflicts:
	video/out/x11_common.c
2014-02-12 22:30:23 +01:00
wm4 4b764b6cc6 old-configure: use -std=c99
See previous commit.
2014-02-12 22:29:50 +01:00
wm4 7b7c31b190 build: switch to -std=c99 for saner float semantics
This fixes a weird bug with aspect ratio handling. It has to do with
float handling: with -std=gnu99, gcc implicitly enables broken non-
standard semantics giving float variables excess precision. This can for
example make this fail in theory: "float a = 0.1; assert(a == a);"

While standard C allows excess precision _within_ expressions, it
requires truncation when storing float values in variables of types
"float" or "double". The "gnu99" mode breaks this. It can be unbroken by
using "c99", or by specifying -fexcess-precision=standard. The former
seems less likely to break compilers other than modern gcc. Note that
-ffloat-store would also fix this, but also makes float expressions less
efficient and less precise for no reason.

The code that mistakenly fails because of this is dec_video.c line 393.
It caused the container aspect to be ignored in some or all situations,
depending how the compiler optimizes. For example, on gcc-4.6 with -Os,
the aspect is always ignored.

In future, we should probably just get rid of storing aspects as floats.

Note: requires mingw-w64 trunk for math.h fixes (M_PI was hidden).
2014-02-12 22:29:21 +01:00
wm4 70a66b461d cache: refuse to seek outside of cache boundaries
Note that this still happens in the stream level, so we can't have
nice highlevel behavior restricting seeking. Instead, if a seek leads
to the demuxer requesting data outside of the cached range, the seek
will simply fail. This might confuse the demuxer, and the resulting
behavior is not necessarily useful.

Note that this also doesn't try to skip data on a forward seek. This
would just freeze the stream with slow unseekable streams.

One nice thing is that stream.h has a separate function for merely
skipping data (separate from seeking forward), which is pretty useful
in this case: we want skipping of data to work, even if we reject
seeking forward by skipping data as too expensive. This probably is
or will be useful for demux_mkv.c.
2014-02-12 22:29:11 +01:00
wm4 844e8f2b51 Release 0.3.4 2014-01-31 19:11:48 +01:00
wm4 3f4ed8920f vo_wayland: silence shadowing warning
No real problem.
2014-01-31 19:10:15 +01:00
wm4 7ad2999b5f quvi: disable subtitle fetching by default
This is slow and unreliable, basically unusable.
2014-01-31 19:08:30 +01:00
Andre D 14fdbe07ac quvi: add option to not fetch subtitles
Signed-off-by: wm4 <wm4@nowhere>
2014-01-31 19:08:26 +01:00
wm4 61ed9a8747 demux_lavf: fix crash with empty files
This used to work; I'm not sure when or why it regressed. When setting
AVProbeData.filename to NULL, libavformat will crash in rtp_probe() by
unconditionally accessing the string.

We used to set the filename to NULL to prevent probing by file extension
when we don't deem it as necessary. Using an empty string also works for
this purpose.
2014-01-31 19:08:12 +01:00
wm4 0801345d68 mp_image: reject too large image sizes
Larger sizes can introduce overflows, depending on the image format. In
the worst case, something larger than 16000x16000 with 8 bytes per pixel
will overflow 31 bits.

Maybe there should be a proper failure path instead of a hard crash, but
not yet. I imagine anything that sets a higher image size than a known
working size should be forced to call a function to check the size (much
like in ffmpeg/libavutil).
2014-01-31 19:07:47 +01:00
wm4 5bd0fe5daf sd_lavc: skip 0 sized sub-bitmaps
Not everything in the OSD path handles 0x0 sized sub-bitmaps well. At
least the code implementing --sub-gray had severe problems with it.

Fix this by skipping such bitmaps.
2014-01-31 19:07:43 +01:00
Alexander Preisinger cab3c5fbb8 wayland/shm: RGB888 as default, change options
RGB565 is one of the fastest and most supported formats on low end consumer
devices, but ffmpeg spams warning when using it. Make it opt-in instead of
opt-out.
2014-01-31 19:07:40 +01:00
Alexander Preisinger 99ceae8cc3 wayland/shm: fix memory leak 2014-01-31 19:07:37 +01:00
Alexander Preisinger ebe04ab692 wayland/shm: remove resize boilerplate
The problem seems to have solved itself. I guess the previous changes to
resizing and commit ba101ab made this possible. Consider me happy for removing
that crap.
2014-01-31 19:07:31 +01:00
James Ross-Gowan 808aa08cc0 w32: use safe DLL search paths everywhere
Windows applications that use LoadLibrary are vulnerable to DLL
preloading attacks if a malicious DLL with the same name as a system DLL
is placed in the current directory. mpv had some code to avoid this in
ao_wasapi.c. This commit just moves it to main.c, since there's no
reason it can't be used process-wide.

This change can affect how plugins are loaded in AviSynth, but it
shouldn't be a problem since MPC-HC also does this and it's a very
popular AviSynth client.
2014-01-31 19:07:09 +01:00
James Ross-Gowan 2065e0ba36 w32: enable heap corruption detection
Enable the terminate-on-corruption feature. This is recommended for new
Windows applications and shouldn't cause a performance hit. It actually
shouldn't change anything for 64-bit builds, since Win64 has this
switched on by default.

See:
http://blogs.msdn.com/b/michael_howard/archive/2008/02/18/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns.aspx
2014-01-31 19:07:07 +01:00
James Ross-Gowan 3c43a022af w32: don't disable the error reporting dialog
Windows users expect this when a program crashes. Without it, the
program just disappears. Also change the SetErrorMode call to use macros
instead of a hardcoded constant.
2014-01-31 19:07:05 +01:00
wm4 02513cd5df sub: fix crash with certain uses of --vf=sub
If, for some reason, the subtitle renderer attempts to render a
subtitle before SD_CTRL_SET_VIDEO_PARAMS was called, it passed a
value calculated from invalid values. This can happen with --vf=sub
and --start. The crash happens if 1. there was a subtitle packet that
falls into the timestamp of the rendered video frame, 2. the playloop
hasn't informed the subtitle decoder about the video resolution yet
(normally unneeded, because that is used for weird corner cases only,
so this code is a bit fuzzy), and 3. something actually requests a
frame to be drawn from the subtitle renderer, like with vf_sub.

The actual crash was due to passing NaN as pixel aspect to libass,
which then created glyphs with ridiculous sizes, involving a few
integer overflows and unchecked mallocs.

The sd_lavc.c and sd_spu.c cases probably don't crash, but I'm not
sure, and it's better fix them anyway.

Not bothering with sd_spu.c, this crap is for compatibility and will
be removed soon.

Note that this would have been no problem, had the code checked whether
SD_CTRL_SET_VIDEO_PARAMS was actually called. This commit adds such a
check (although it basically checks after using the parameters).

Regression since 49caa0a7 and 633fde4a.
2014-01-31 19:06:58 +01:00
wm4 4d986d2a98 demux_lavf: add hack to workaround too unreliable mp3 detection
This generally affects mp3 files that don't have any (or many) mp3
frames in the first 2 MB. 2 MB is the maximum probe size, and
libavformat returns a low probescore even if we give it the full 2 MB.
Trying to probe a larger buffer (or even the full file) doesn't work for
mysterious reasons.

The workaround consists in accepting a very weak probescore if the
format is detected as mp3 and we probed already 2 MB.
2014-01-31 19:06:49 +01:00
wm4 ada6f96811 demux_lavf: refactor format probing hack
Restructure it a bit, so we can use the format hack list even if no mime
type applies. Shouldn't change anything functionally yet. Preparation
for the next commit.
2014-01-31 19:06:44 +01:00
wm4 321b3952b0 vo_wayland: fix confusion of video and window sizes 2014-01-31 19:06:29 +01:00
reimar d49b49ff96 stream_pvr: Fix fd check, -1 indicates invalid, not 0.
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@36677 b3059339-0415-0410-9bf9-f77b7e298cf2
2014-01-31 19:06:20 +01:00
wm4 f088abb2bf audio: fix balance control
Balance controls as used by mixer.c was broken, because af_pan.c stopped
accepting its arguments. We have to allow 0 channels explicitly. Also,
fix null pointer access if the matrix parameter is not used.

Regression from commit 82983970.
2014-01-31 19:06:17 +01:00
wm4 4a3975bf7e waf: try to fix unicode/byte string messup
Some mpv builds identify with e.g. "mpv b'0.3.3' ". The version looks
like str() was called on a Python byte string. I couldn't reproduce it
on my machine (I tried with both Python 2 and 3), so I'm not exactly
sure what's going on here, but I'm hoping this commit does fix it.
2014-01-31 19:05:20 +01:00
wm4 a064143899 manpage: fix af_equalizer syntax
This doesn't need quoting, for some reason.
2014-01-31 19:04:54 +01:00
11rcombs f264d52099 af: fixed out-of-bounds accesses caused by NUM_FMT and co.
Signed-off-by: wm4 <wm4@nowhere>

This merges pull request #496. The problem was that at least the
initialization of the distance[] array accessed af_fmtstr_table[]
entries that were out of bounds. Small cosmetic changes applied to
the original pull request.
2014-01-31 19:04:29 +01:00
reimar 74a3c169b5 cookies.c: cols must (and does) have 7 elements.
Doesn't affect the generated code, but avoids confusion
in both humans and newer Coverity versions.

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@36623 b3059339-0415-0410-9bf9-f77b7e298cf2
2014-01-31 19:04:15 +01:00
reimar 34d9787fcf vcd_read: Fix sizeof argument.
The struct we need to copy is actually a cdrom_msf0, not cdrom_msf.
Even though the kernel for no good reason reads it in as a
cdrom_msf struct, but only uses the part shared with cdrom_msf0 -
this is probably a kernel bug.

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@36622 b3059339-0415-0410-9bf9-f77b7e298cf2
2014-01-31 19:04:09 +01:00
wm4 1c0b80d518 mkv.desktop: add two more mime types
Stolen from github issue #484. No idea whether they're needed.
2014-01-31 19:03:57 +01:00
wm4 a9bafeb589 cache: remove debug code
This was accidentally committed.

<rcombs> `mp_sleep_us(100000);` <-- are you drunk?
2014-01-31 19:03:17 +01:00
wm4 3a33db3d92 cache: reduce message spam
Output only 1 message every 5 seconds at most.
2014-01-31 19:02:45 +01:00
wm4 d276f753f7 Release 0.3.3 2014-01-15 21:03:02 +01:00
Grzegorz Blach c06000eef8 Detect Lua on FreeBSD 2014-01-15 20:57:17 +01:00
wm4 1bc1cd2305 player: avoid stalling when starting a network stream
Starting a network stream could stall by executing uncacheable stream
control requests (STREAM_CTRL_GET_LANG and STREAM_CTRL_GET_DVD_INFO).
Being uncacheable means the player has to wait until the cache is done
reading the current block of data. These requests can't be cached
because they're too complicated, so the only way to avoid them is
special casing the DVD and Bluray streams (which are the only things
which need these requests), and not doing them in other cases.

(This is kind of inelegant, but so is the rest of the DVD/BD code.)
2014-01-15 20:57:09 +01:00
wm4 a735a2abd7 demux_mkv: improve robustness by explicitly checking for level 1 elements
Matroska makes it pretty hard to resync correctly on broken files:
random data returns "valid" EBML IDs with a high probability, and when
trying to skip them it's likely that you skip a random amount of data
(instead of considering the element length invalid).

Improve upon this by skipping known level 1 elements only. Consider
everything else invalid and call the resync code. This might result in
annoying behavior when Matroska adds new level 1 elements, although it
won't be particularly harmful. Matroska doesn't really allow us to do
better (even mkvtoolnix explicitly checks for known level 1 elements).

Since we now don't always want to combine EBML element skipping and
resyncing, remove ebml_read_skip_or_resync_cluster(), and make
ebml_read_skip() more tolerant against skipping broken elements.

Also, don't resync when reading sub-elements, and instead do resyncing
when reading them results in an error.
2014-01-15 20:56:16 +01:00
wm4 112c1abe31 demux_mkv: avoid skipping too much data in corrupted files
Until now, corrupted files were detected if the size of an element (that
should be skipped) was larger than the remaining file. This still could
skip larger regions of the file itself if the broken size happened to be
within the file.

Change it so that it's never allowed to skip outside the parent's
element.
2014-01-15 20:56:13 +01:00
wm4 362b35b631 manpage: document --term-osd=force
Apparently this was forgotten when it was first added, or maybe it's an
arrifact from the rst conversion.

Conflicts:
	DOCS/man/en/options.rst
2014-01-15 20:54:32 +01:00
wm4 49cf0af294 player: don't block terminal OSD while seeking
Seeking usually show the status on OSD. In terminal OSD mode, no status
is shown, because there is already a separate status line.
Unfortunately, the mechanism for showing the status was still active,
which forced showing no message while the code for showing seek status
was active.
2014-01-15 20:53:44 +01:00
wm4 1b26874710 player: mention subtitles in av_desync_help_text
Insane .ass subtitle scripts can cause severe slowdown (depending on the
speed of the machine, or the insanity of the script), so mention how to
test without subtitles. This is mainly to make the user aware that
subtitle rendering can be a problem. For longwinded explanation, there
isn't enough space.
2014-01-15 20:53:41 +01:00
Stefano Pigozzi 8fe636da7a cocoa: fix window placement on secondary screens
For a long time the cocoa backend set the xinerama_x/y and used dx/dy from the
VO instance. This somewhat worked with some workarounds but wasn't really
what was supposed to be happening. Moreover 27e4360, which touched this
workaround introduced a regression.

New code doesn't set the xinerama_x/y values so that dx/dy are offsets in the
current screen (not a virtual screen composed of all the screens). The screen
reference detected during VOCTRL_UPDATE_SCREENINFO is also passed down to the
window initialization code.

Fixes #472
2014-01-15 20:53:19 +01:00
Stefano Pigozzi 812f149585 cocoa: refactor init window positioning code 2014-01-15 20:53:14 +01:00
wm4 635424c8f0 demux_lavf: add hack for MicroDVD for assuming frame based timing
MicroDVD files _can_ contain real timestamps instead of frame timestamps
if they declare a FPS. But this seems to be rare, so ignore that if the
FPS happens to match with the libavformat microdvd parser's default FPS.

This might actually break files that declare 23.976 FPS, but the video
file is not 23.976 FPS, but the chance that this happens is probably
very low, and the commit fixes the more common breakage with 25 FPS
video.
2014-01-15 20:51:50 +01:00
wm4 2319918721 sub: fix frame based subtitle timestamp handling
Subtitle formats with frame based timing require using the video FPS to
compute proper subtitle timestamps. But it looks like the calculation to
do that was inversed.
2014-01-15 20:51:47 +01:00
Alexander Preisinger 9fabf85a44 wayland: properly empty output list 2014-01-15 20:51:39 +01:00
Alexander Preisinger 54fd5d0979 wayland/shm: don't crash if initialization failed 2014-01-15 20:51:37 +01:00
wm4 d141d281ca wayland: fix crash when initialization fails
On X11, if no wayland compositor is running, wl_list_init() will never
be called. This will cause destroy_display() to segfault when trying to
iterate over the list.
2014-01-15 20:51:32 +01:00
Alexander Preisinger ebc3d74b04 wayland/shm: tone down warnings
Those warnings are printed far too often and actually aren't usefull at all.
2014-01-15 20:51:19 +01:00
Alexander Preisinger a63fab8921 wayland: fix memory leaks
There are still some leaks from wayland-cursor stuff, but there is no way to
free the memory as user of the cursor library.

Conflicts:
	video/out/wayland_common.c
2014-01-15 20:50:52 +01:00
wm4 4ee8d8d3f9 player: fix setting smaller timeout on Windows systems
On Windows, we don't have proper input event wakeup handling, so we
need to lower the playloop timeout in order to react fast to input.

Closes #387.
2014-01-15 20:50:02 +01:00
Alexander Preisinger 574a2484c1 wayland: cleanup registry_handle_global
The wl_registry object is already passed as a parameter. No need to create
a temporary variable.
2014-01-15 20:49:57 +01:00
Alexander Preisinger f5b92bee5e wayland: remove set_user_data from seat_listener
The user_data is passed on add_listener and can later be changed with
set_user_data. But because we don't want to change it later and because it is
the same object remove the set_user_data call.

This might be a copy&paste leftover from the initial draft for the wayland
backend.
2014-01-15 20:49:55 +01:00
Alexander Preisinger ca3d7144db wayland: use static consistently
Declare everything that is only needed inside wayland_common.c as static.
2014-01-15 20:49:52 +01:00
wm4 86e7c3defb terminal: don't initialize termcap etc. if stdout is not a terminal
Otherwise, it seems one of the term* libraries will write escape
sequences to stdout, for whatever reason.
2014-01-15 20:49:42 +01:00
Alexander Preisinger 0fe1385d80 wayland: fix fullscreen & resizing for good
I added enough logic to never set ontop or fullscreen twitce.
This commit keeps also the size of the video if multiple videos are played.
If the ratio differs the width will be kept at the same size and only the
height changes.
2014-01-15 20:49:32 +01:00
wm4 f4e9371d0f manpage: mention how to get a list of codecs for use with --hwdec-codecs 2014-01-15 20:49:26 +01:00
wm4 909185d49e wayland: silence annoying libwayland-client message
libwayland-client contains the following code [1]:

	runtime_dir = getenv("XDG_RUNTIME_DIR");
	if (!runtime_dir) {
		fprintf(stderr,
			"error: XDG_RUNTIME_DIR not set in the environment.\n");

This means this message will unconditionally and unavoidably be printed
if XDG_RUNTIME_DIR is not set. Since mpv is a terminal program, and we
want to avoid unnecessary output, work it around by not attempting to
use wayland if this environment variable is not set.

[1] http://cgit.freedesktop.org/wayland/wayland/tree/src/wayland-client.c#n636
    (cd0dccd01e16fa404e03974d30ded3aebdb1c4bc)
2014-01-15 20:49:22 +01:00
wm4 c18c324c6f wayland: fix some memory leaks on initialization failure
This commonly happens when initializing vo_opengl on a X11-only system.

Unfortunately, most wl_*_destroy() functions appear not to accept NULL
pointers, making partial deinitialization a pain: you have to add your
own NULL checks everywhere to avoid crashes.

xkb.context is uninitialized separately, because you can initialize it
just fine, even if the rest of input initialization fails.
2014-01-15 20:49:19 +01:00
wm4 6b20c24413 Release 0.3.2 2014-01-06 20:32:24 +01:00
Stefano Pigozzi 5c42d1eb92 cocoa: don't reset window size when the video size doesn't change
Fixes #459
2014-01-06 20:27:55 +01:00
Martin Herkt e34132f8d5 w32_common: don't force topmost on fullscreen
Fixes #457, #444.
2014-01-06 20:27:53 +01:00
Martin Herkt bd7ff23f96 Windows: use roaming AppData instead of local
Whatever. Fixes #458.
2014-01-06 20:27:47 +01:00
wm4 e6c184e2b1 build: don't depend on both libavresample and libswresample
When both libavresample and libswresample were detected, the script
enabled both at the same time. This is not supported; although nothing
bad happened apparently. Make the dependencies both mutually exclusive.
2014-01-06 20:23:10 +01:00
wm4 33c4920967 player: always ise [statusline] for terminal OSD
This was inconsistent: the actual statusline used [statusline] as
message prefix, while other parts of the terminal OSD used [cplayer]
(and MSGL_STATUS). This commit makes it consistent.
2014-01-06 20:23:06 +01:00
wm4 0c36f655e1 player: print an error message if run command fails
Note that we can't use mp_msg, because it's not async-signal safe (we
might be running other threads while forking, so only functions
specified to be async-signal safe can be called, and this doesn't
include stdio; mp_msg acquires a mutex too).

Also, always print a \n before running the program to flush the status
line. The effect is that a program running successfully as well as the
error message will effectively start on a new line.
2014-01-06 20:23:03 +01:00
wm4 cd31697dd8 manpage: fix typo 2014-01-06 20:22:57 +01:00
wm4 c497b82583 player: don't select subtitles added from quvi by default
Quvi subtitles are considered external subtitles (simply because they're
separate from the audio/video stream), but for the sake of subtitle
auto-selection, they should not be considered external.

Change this so that quvi subtitles are treated like muxed subtitles
(with default flag never set). This means subtitles won't be selected by
default, unless explicitly requested with --sid or --slang.
2014-01-06 20:22:53 +01:00
Alexander Preisinger 6f5dfb5102 Revert "wayland/egl: use redraw callback"
Because of this commit there were problems displaying the frmase in their right
order.

This reverts commit 96e75d234a.

Conflicts:
	video/out/gl_wayland.c
	video/out/wayland_common.h
2014-01-06 20:22:43 +01:00
Alexander Preisinger 59f970d492 wayland: remove workaround
The changes in the vo_wayland_ontop function have no effect on the workaround.

Somehow the problem just disappeared. I guess it is because of the new control
function in gl_wayland.c where the resize happens immediatly after the event
dispatch/flush.
2014-01-06 20:22:41 +01:00
wm4 29c74b42b4 demux_subreader: reject file if not opened by --sub
demux_subreader.c contains the old MPlayer subtitle parser, and I have
absolutely no confidence in this (very crappy) code. There might be
one or two security risks associated with running that code on
arbitrary input.
2014-01-06 20:22:26 +01:00
Alexander Preisinger 4b2a3e50e2 wayland/egl: misc fixes and cleanups 2014-01-06 20:22:11 +01:00
Alexander Preisinger 35b01c9604 wayland/egl: use redraw callback
This solves the issue where we would not receive any frame events. The
difference to my earlier tests is that now it looks like eglSwapBuffers uses
it's own event queue or something similiar along the lines. Becaues the
performance is the same as without any redraw callback.
2014-01-06 20:22:08 +01:00
Alexander Preisinger 09b95257fe wayland/egl: add egl_context to the wayland state
At the moment there are visual glitches when we resize the window. This happens
because in wayland there a special function for resizing EGL windows.

To prevent the glitches move the egl_context to the wayland state in
wayland_common.h and add a new control function to gl_wayland.c to wrap the
vo_wayland_control function to check for resize events.

With the new control wrapper the glitches are gone and the resizing is fluid.
2014-01-06 20:22:06 +01:00
Alexander Preisinger 0cf2df38d1 wayland: Remove nonsense comment and add warning
The reason a segmentation happend here was because we couldn't get the
requested minor version. The major version is enough for differentiating
between OpenGL 3 and OpenGL 2. If it fails there is still a fallback to any
version available.

Also add a warning if we use the fallback.
2014-01-06 20:22:03 +01:00
Alexander Preisinger 4326fdfe2a wayland: move workaround to fullscreen code
Because of this workaround there was a bug in the wayland vo.
Now it works on both vos (opengl and shm) as it should.
2014-01-06 20:21:55 +01:00
Martin Herkt 9755850b3d manpage: mention --pause 2014-01-06 20:21:45 +01:00
wm4 96b50e770b dvdnav: set correct stride for fake highlights
This didn't really matter, because the fake highlight rectangle is in a
single color, and every pixel has the same color value.
2014-01-06 20:21:43 +01:00
wm4 9f6551160a stream_pvr: fix crash on initialization
Fallout from the mp_msg conversions.
2014-01-06 20:21:39 +01:00
wm4 89b854165e audio: check for overflows 2014-01-06 20:21:37 +01:00
wm4 0beece4397 ta: check overflow in array realloc macros 2014-01-06 20:21:19 +01:00
Stefano Pigozzi 8abdf1f56e vda: fix build on OS X 10.7
Looks like on 10.8 OpenGL.h recursively includes CGLIOSurface.h. That is not
the case for 10.7 so the build was broken on that version of OS X.
2014-01-06 20:21:17 +01:00
Stefano Pigozzi 5a81f7678e build: fix cocoa configure check on OS X 10.7
It failed because the 10.7 SDK doesn't natively support array and dictionary
subscripting.
2014-01-06 20:21:14 +01:00
Alexander Preisinger e26286dfdb wayland/opengl: fix garbage borders
It seems mpv draws garbage in those regions. Now we calculate the aspect and
let weston draw the black borders.
2014-01-06 20:21:11 +01:00
Alexander Preisinger 389232e5a2 wayland/shm: only commit new frames
Only attach and commit new buffers. This also increases performance when
paused.
2014-01-06 20:21:09 +01:00
Alexander Preisinger c5de0b0a6c wayland: don't change set fullscreen twice
Newest weston chrashes if we call set_fullscreen twice. This is a major bug
I which I should probably report.
2014-01-06 20:21:07 +01:00
Martin Herkt f9bdfc0782 ao_alsa: Unbreak pause/resume
Well that was dumb.
2014-01-06 20:20:57 +01:00
Martin Herkt 5c9a7e24f6 ao_alsa: Fix PCM resume after suspend
Fixes #324
2014-01-06 20:20:55 +01:00
wm4 b56f61d2ab stream: always respect sector_size, fixes cdda://
Streams like CDDA have special requirements in what quantities data can
be read: you can only read a sector at once, not more and not less. The
stream_peek() function didn't respect that and set less (used internal
buffer size of 2048 bytes, instead of CD sector size of 2352 bytes), so
no data was read and EOF was accidentally set, making playback with
cdda:// fail.

This is a regression since commit 9a723f, but that commit merely exposed
the issue (the redundant seek would clear the EOF flag).
2014-01-06 20:20:48 +01:00
Stefano Pigozzi 82ab3a8160 build: fix typo 2014-01-01 21:50:42 +01:00
wm4 e371f2ebd3 Release 0.3.1 2014-01-01 21:03:51 +01:00
Stefano Pigozzi 29b68d69c0 build: make configure fail if both __atomic and __sync are not available 2014-01-01 20:57:27 +01:00
wm4 6c770eb6df manpage: clarifications about bitmap subtitles and --secondary-sid 2014-01-01 20:57:25 +01:00
wm4 965f336917 options: make --msglevel=help print something helpful 2014-01-01 20:57:22 +01:00
wm4 47fae324c7 manpage: fix description on the slave mode msglevel 2014-01-01 20:57:18 +01:00
wm4 6ab0dbf2ed player: load encoding-profiles.conf before the main config
Otherwise one can't add profiles based on the encoding profiles.
2014-01-01 20:57:16 +01:00
Miro Hrončok c002196ffe mpv.desktop: Add Czech translations 2014-01-01 20:57:14 +01:00
wm4 3c8fc4588d player: fix DVD playback
Crashed in case of lazily added subtitle streams, which add tracks with
track->stream set to NULL.

Fixes gituhub issue #439.
2014-01-01 20:57:12 +01:00
Alessandro Ghedini 416015f059 compat: use __atomic operations instead of __sync, when present
Fixes #434
Fixes #437
2014-01-01 20:57:09 +01:00
Alessandro Ghedini 773db053aa build: check for libatomic and __atomic operations
Add check in old-configure as well. Reformat the check to use a maximum of 80
columns in the wscript.
Signed-off-by: Stefano Pigozzi <stefano.pigozzi@gmail.com>
2014-01-01 20:57:06 +01:00
wm4 a8c09d003f manpage: fix vf_noise parameter name 2014-01-01 20:57:02 +01:00
wm4 1284a6b6b0 video: fix --brightness etc. options
They were set before the VO was intitialized, which silently failed.
2013-12-29 22:17:50 +01:00
wm4 78c35e42ea Release 0.3.0 2013-12-29 18:06:41 +01:00
Stefano Pigozzi f06a870b88 build: add flag for inline assembly
This is used to disable inline assembly (useful for old version of binutils
like the one in OpenBSD).
2013-12-29 17:39:18 +01:00
wm4 392856ed4d vd_lavc: by default, output all frames, even corrupted ones
Set the flag CODEC_FLAG_OUTPUT_CORRUPT by default. Note that there is
also CODEC_FLAG2_SHOW_ALL, which is older, but this seems to be ffmpeg
only.

Note that whether you want this enabled depends on the user. Some might
prefer that only good frames are output, while others want the decoder
to try as hard as possible to output _anything_. Since mplayer/mpv is
rather the kind of player that tries hard instead of being "clever", set
the new default to override libavcodec's default.

A nice way to test this is switching video tracks. Since mpv doesn't
wait for the next key frame, it'll start feeding the decoder with a
packet from the middle of the stream.
2013-12-29 14:19:22 +01:00
wm4 15f38b89ee player: add two more font mimetypes recognized by Haali
The Haali Matroska splitter is basically the reference implementation
for this crap, and it knows only:

    application/vnd.ms-opentype
    application/x-font-ttf
    application/x-truetype-font

Two of them were missing in our code. One of them, "application/x-font",
is probably plain incorrect, but I can't really tell.

Also see: http://www.cccp-project.net/beta/test_files/fontsample.mkv
2013-12-29 14:19:22 +01:00
wm4 6878cf2832 player: use arrays to list font mimetypes and font file extensions 2013-12-29 14:19:22 +01:00
Stefano Pigozzi fc583e74ec build: disable SDL by default
old-configure already behaves like this. Adapt wscript to the same default.
2013-12-29 14:14:09 +01:00
wm4 fd4e3af740 Install encoding-profiles.conf by default
This is probably useful.

Note that this includes a small, stupid hack to prevent loading of the
config file if vf_lavfi is not available. The profile by default uses
vf_lavfi, and the config parser will output errors if vf_lavfi is not
available.

As another caveat, we install the example profile even if encoding is
disabled (though we don't load it, since this would print errors).
2013-12-28 16:33:21 +01:00
wm4 a473739fbf player: fix buggy error condition when loading mpv.conf 2013-12-28 15:46:07 +01:00
ahodesuka 62be13b019 vo/x11_common: do not select motion events when --no-mouse-movements is set 2013-12-28 15:07:01 +01:00
wm4 4b695e7972 input: print an error if reading input.conf fails
stream_read_complete() fails if the file is larger than the requested
maximum size. But input.c didn't check for this case, and no indication
that something went wrong was printed.
2013-12-28 15:06:43 +01:00
wm4 f146e9b15a Revert "x11: don't set global error handler"
This reverts commit 877303aaa9.

The OpenGL 2.1 fallback for vo_opengl didn't work. Two things come
together: 1. trying to create an OpenGL 3.0 context will fail with
a GLXBadFBConfig error, and 2. X errors are fatal by default. Since
the reverted commit removed the X error handler, the mpv process was
killed, instead of continuing for the fallback.

(Note that this commit is not an exact inverse commit, since mp_msg
changed, but it does about the same thing.)
2013-12-27 23:22:46 +01:00
wm4 3e562583e5 demux_mkv: handle TrueHD properly
Apparently, Matroska packs TrueHD packets in a way lavc doesn't expect.
This broke decoding of some files [1] completely. A short look at the
libavcodec parser shows that parsing this ourselves would probably be
too much work, so make use of the libavcodec parser API.

[1] http://www.cccp-project.net/beta/test_files/mzero_truehd_sample.mkv
2013-12-27 20:00:24 +01:00
Stefano Pigozzi b205e63cdc Revert "cocoa: unlock on uninit"
Not sure why but this doesn't seem to be needed anymore.

This reverts commit 6ead6aa005.
2013-12-26 22:26:41 +01:00
Stefano Pigozzi 2e8b6419bd corevideo: fix video initialization when not using VDA
query_format was setting state even if wasn't the correct thing to do. Somehow
it worked by pure luck (until commit e6e6b88b6d).

Fix the initialization by setting state inside of reconfig.
2013-12-26 22:11:49 +01:00
Stefano Pigozzi c7f9c060e4 build: fix shm detection on OpenBSD
Fixes #427
2013-12-26 21:13:10 +01:00
Stefano Pigozzi dbf879be14 build: support multiple headers in check_statement
This feature will be used in the next commit.
2013-12-26 21:13:01 +01:00
Sven-Hendrik Haase 1cbb32cca2 mpv.desktop: add KDE file handlers
Currently KDE will copy a media file into a temporary folder instead of
trying to stream it if a KIO slave location file is started. This change
will tell KDE to make mpv try to directly play the file. Perhaps the
proper flags should be added according to the individual enabled
features of the build but I suggest that be for the future.

Signed-off-by: wm4 <wm4@nowhere>
2013-12-26 21:06:43 +01:00
wm4 dacb6ad98f options: simplify handling of some help options 2013-12-26 19:25:51 +01:00
wm4 4ea8612b40 input: cosmetics: move code around
Rearrange the code in an attempt to make its organization more logical.
2013-12-26 17:41:28 +01:00
wm4 34bee16faf input: split off some code from input.c to separate files
This is mostly just moving code around.
2013-12-26 17:13:25 +01:00
Stefano Pigozzi 652895abdc build: hide duplicate options from --help
Hide --enable variants from [autodetect]'ed options and --enable/--disable
variants for [enable]'d/[disable]'d options. The hidden options are still
usable, just hidden for more readability.
2013-12-26 10:09:17 +01:00
Stefano Pigozzi a410a750c7 cocoa: sanitize window title string and guard against NULL
If the utf8 string used to create the NSString for title was invalid utf8,
-stringWithUTF8String returned nil and triggered an assertion in Cocoa's
framework code.

Sanitize the utf8 string and if the sanitation wasn't enough just avoid
crashing by not setting a title.

Fixes #406
2013-12-25 18:13:09 +01:00
Stefano Pigozzi 7d553a4c26 vo_x11/vo_xv: fix build when using --disable-xext 2013-12-25 17:07:51 +01:00
wm4 f691d8f7b6 player: deselect secondary sub when switching to file with different tracks
This applies the usual logic of resetting stream selections to default
when switching to a file with a different track layout. (This is to
prevent selecting random streams.)
2013-12-25 11:32:33 +01:00
wm4 f751609450 player: fix initial selection with --secondary-sid
Also, make sure that a track can't be selected twice. While this might
work in some situations, it certainly won't work with subtitles demuxed
from a stream.

Fixes #425.
2013-12-25 11:32:29 +01:00
wm4 3720b3f17d player: add --secondary-sid for displaying a second subtitle stream
This is relatively hacky, but it's Christmas, so it's ok. This does two
things: 1. allow selecting two subtitle tracks, and 2. include a hack
that renders the second subtitle always as toptitle. See manpage
additions how to use this.
2013-12-24 17:46:14 +01:00
wm4 9292f537d6 player: add infrastructure to select multiple tracks at once
Of course this does not allow decoding multiple tracks at once; it just
adds some minor infrastructure, which could be used to achieve this.
2013-12-24 17:46:08 +01:00
wm4 2b87415f6f player: do initial seek for external tracks only once
Normally, there can be only one demuxer stream active for each demuxer
of an external file, but this assumption will be broken for multiple
subtitles support.
2013-12-24 17:45:06 +01:00
wm4 1d86134ec1 player: slightly simplify seeking in external files
For some reason, this checked whether there are external tracks at all
before doing any seeks. Possibly this was to avoid multiple
get_main_demux_pts() calls, but calling this multiple times shouldn't be
too bad.
2013-12-24 17:45:06 +01:00
wm4 b796f2bb76 player: redo demuxer stream selection
Use struct track to decide what stream to select.

Add a "selected" field and use that in some places instead of
checking mpctx->current_track.
2013-12-24 17:44:34 +01:00
wm4 96e6f3f4b6 audio: fix format ID conversion
AV_SAMPLE_FMT_NONE != 0, could apparently cause crashes in certain
situations.
2013-12-23 21:24:41 +01:00
wm4 0656134179 changes.rst: add entry for dvdnav menu 2013-12-23 20:09:50 +01:00
wm4 28c28d082a manpage: mention video-unscaled property 2013-12-23 17:57:23 +01:00
wm4 b59f2c99e7 player: fix typo in previous commit
Apparently I pushed too quickly.
2013-12-23 17:33:07 +01:00
wm4 d259d88af8 player: warn if Matroska font attachments have incorrect MIME type
Normally we shouldn't load these files. But for some reason it was added
in commit b784346e some years ago, and disabling this hack would
probably be an inconvenience. So just print a warning.
2013-12-23 17:31:42 +01:00
wm4 fd081c467d options: print any options set in verbose mode
So we will know whether someone uses broken config file options when
posting a log with -v.
2013-12-23 17:30:19 +01:00
wm4 9fb0441b16 options: disable joystick by default 2013-12-23 11:35:37 +01:00
wm4 6565ec8b41 vaapi: fix initialization error code path
"res" can be uninitialized in the error case.
2013-12-23 11:22:12 +01:00
wm4 7ef8d38769 subreader: replace some strcpy calls 2013-12-22 23:44:34 +01:00
wm4 90e4c85891 subreader: remove overlapping strcpy
Looks like this relied on undefined behavior.
2013-12-22 23:44:24 +01:00
wm4 aefb100e68 asxparser: remove commented code 2013-12-22 23:43:09 +01:00
wm4 a2bc1c28a5 stream_smb: remove dead code
Yep, smb_username/password were unused since forever, even in MPlayer.
Removal untested. (Does anyone even use smb://?)
2013-12-22 23:42:58 +01:00
wm4 88fd763e18 player: simplify mp_load_per_file_config
Get rid of the stupid and error-prone buffer size calculations, use
snprintf instead of strcpy.
2013-12-22 23:25:10 +01:00
wm4 ffcf4ece6e player: move code around
The only thing that used mp_load_per_file_config() was inside
configfiles.c too, so remove the declaration from core.h and move the
function before its use.
2013-12-22 23:25:10 +01:00
wm4 2e1c423dff player: remove code duplication for auto-loaded config profiles
Code for loading "[vo.vdpau]" profiles and similar. The messages printed
on loading change, but other than that, everything should behave about
the same.
2013-12-22 23:25:10 +01:00
wm4 2de2b60222 path: add function to split URL into prefix and path
Used in the following commit.
2013-12-22 23:25:10 +01:00
wm4 8c0675b7d0 path: don't accept empty protocol as valid
mp_is_url("://") returned true.
2013-12-22 23:25:10 +01:00
wm4 3782fa20ed path: change mp_splitext() semantics
Including the "." in the returned extension was too inconvenient. I
think originally, the semantics were supposed to work like in Python,
but screw this.

Also, return NULL instead of "" on failure (which is what its only user
actually seems to expect).
2013-12-22 23:25:08 +01:00
Miro Hrončok c31ce789c5 stream_radio: suppress error with -Werror=format-security compilation flag
Signed-off-by: wm4 <wm4@nowhere>
2013-12-22 21:00:15 +01:00
wm4 79b09592c8 vo_vdpau: fix unintended truncation of 64 bit timestamps to 32 bit
How embarrassing...

This code is inactive for all VOs other than vo_vdpau. For vo_vdpau,
this caused various issues, such as stuttering after about an hour of
running mpv; see github issue #403.
2013-12-22 18:42:19 +01:00
wm4 895932aa0a vo_vdpau: add some debugging messages for frame timing
Note that this will print a difference even with perfect sync, because
the code queues the frames _between_ vsync, probably for error margin
(though I don't understand why it uses the exact values chosen).
2013-12-22 15:49:02 +01:00
wm4 e6bea0ec5a Don't include version.h from make options.c
I find this annoying. It's the reason common/version.c exists at all.

options.c did this for the user agent, which contains the version
number. Because not including version.h means you can't build the user
agent and use it in mp_default_opts anymore, do something rather awkward
in main.c to initialize the default user agent.
2013-12-22 14:35:45 +01:00
wm4 8b7048b7d5 stream: minor cookie cleanup
Avoid global state (reload cookie file every time), actually free
loaded cookies, use mp_get_user_path() for cookie file.
2013-12-22 13:29:16 +01:00
wm4 3a637d411f options: move network related options to MPOpts 2013-12-22 13:11:22 +01:00
wm4 38be9d5fed msg: add some comments about thread-safety 2013-12-22 12:29:50 +01:00
wm4 a0e89daf49 gl_lcms: actually acquire mutex
Prevents race conditions (which can happen only in theory anyway).
2013-12-22 12:25:51 +01:00
wm4 bd5e0a2ba2 options: make --msglevel extend previous settings
Make it so --msglevel extends previous --msglevel uses, instead of
overwriting them. Do this by literally appending the --msglevel option
value to the previous one.
2013-12-22 12:18:01 +01:00
wm4 4833e92b54 vda: attempt to fix build (2)
Still no OSX here.
2013-12-22 01:44:19 +01:00
wm4 cb36f9fddb path-macosx: attempt to fix build
Untested... no OSX here.
2013-12-22 01:36:45 +01:00
wm4 0a3e9a9ac3 Merge branch 'msg_refactor'
This branch changes mp_msg() so that it doesn't require global context.
The changes are pretty violent.
2013-12-21 23:15:32 +01:00
wm4 245e5b8441 msg: remove global state 2013-12-21 23:11:12 +01:00
wm4 678ce04b3f msg: don't prefix slave-mode stuff by default 2013-12-21 22:13:05 +01:00
wm4 8e75cc3339 player: use separate msg prefix for statusline
So the user can specifically mute or unmute the status line with
--msglevel. Accidentally removed with commit 5e0c4ec3.
2013-12-21 22:13:05 +01:00
wm4 6aaea76b4d demux: don't prefix tag output with demuxer name
Add a separate mp_log instance for this purpose.
2013-12-21 22:13:05 +01:00
wm4 eef36f03ea msg: rename mp_msg_log -> mp_msg
Same for companion functions.
2013-12-21 22:13:04 +01:00
wm4 eba5d025d2 msg: convert defines to enum
Also get rid of MSGL_HINT and the many MSGL_DBG* levels.
2013-12-21 22:13:04 +01:00
wm4 3fa584e280 msg: remove legacy stuff 2013-12-21 21:43:17 +01:00
wm4 893aaf8d12 vo_opengl_old: remove leftovers 2013-12-21 21:43:17 +01:00
wm4 455214acef macosx_application: replace mp_msg with stderr for semi-fatal error
Apparently this should never be run anyway.
2013-12-21 21:43:17 +01:00
wm4 1a0e83e708 cpudetect: remove mp_msg calls
Worthless anyway.
2013-12-21 21:43:17 +01:00
wm4 ad2199128d path lookup functions: mp_msg conversions
There's a single mp_msg() in path.c, but all path lookup functions seem
to depend on it, so we get a rat-tail of stuff we have to change. This
is probably a good thing though, because we can have the path lookup
functions also access options, so we could allow overriding the default
config path, or ignore the MPV_HOME environment variable, and such
things.

Also take the chance to consistently add talloc_ctx parameters to the
path lookup functions.

Also, this change causes a big mess on configfiles.c. It's the same
issue: everything suddenly needs a (different) context argument. Make it
less wild by providing a mp_load_auto_profiles() function, which
isolates most of it to configfiles.c.
2013-12-21 21:43:17 +01:00
wm4 232b8de095 af_export: require filename argument
Since mp_find_user_config_file() is going to get a context argument,
which would be annoying to do in the audio chain (actually I'm just
lazy).
2013-12-21 21:43:17 +01:00
wm4 d9b5652cac quvi: mp_msg conversions 2013-12-21 21:43:17 +01:00
wm4 33c8fd789d charset_conv: mp_msg conversions 2013-12-21 21:43:16 +01:00
wm4 0335011f11 stream: mp_msg conversions
We also drop some slave mode stuff from stream_vcd.
2013-12-21 21:43:16 +01:00
wm4 3dbc9007b0 demux: mp_msg conversions
The TV code pretends to be part of stream/, but it's actually demuxer
code too. The audio_in code is shared between the TV code and
stream_radio.c, so stream_radio.c needs a small hack until stream.c is
converted.
2013-12-21 21:43:16 +01:00
wm4 9149e2af56 playlist_parser: mp_msg conversion 2013-12-21 21:43:16 +01:00
wm4 8cd1b33a22 x11: mp_msg conversion for fstype help output 2013-12-21 21:43:16 +01:00
wm4 9428e05b3f encode_lavc: mp_msg conversions
Miss two mp_msg calls, because these conflict with future commits.
2013-12-21 21:43:16 +01:00
wm4 09e588662e options: some more mp_msg conversions 2013-12-21 21:43:16 +01:00
wm4 d57eaa7e30 av_log: mp_msg conversion
This is pretty nasty, because FFmpeg/Libav is yet another library with a
global message callback. We do something with mutexes trying to get it
done, but of course we can't actually solve this problem. If more than
one library in a process use FFmpeg/Libav, only one of them will get log
messages.
2013-12-21 21:43:16 +01:00
wm4 7bdee8f35e m_option: add mp_log context to sub-module print_help callback 2013-12-21 21:43:16 +01:00
wm4 9242c34fa2 m_option: add mp_log callback to OPT_STRING_VALIDATE options
And also convert a bunch of other code, especially ao_wasapi and
ao_portaudio.
2013-12-21 21:43:16 +01:00
wm4 d8d42b44fc m_option, m_config: mp_msg conversions
Always pass around mp_log contexts in the option parser code. This of
course affects all users of this API as well.

In stream.c, pass a mp_null_log, because we can't do it properly yet.
This will be fixed later.
2013-12-21 21:05:02 +01:00
wm4 a2d144fc8f m_property: mp_msg conversions
Includes some semi-crappy hacks to avoid changing too much code for this
conversion (allowing NULL log argument for m_property_do()).
2013-12-21 21:04:21 +01:00
wm4 ed71606e65 input: rework how input sources are added
Until now, there were two functions to add input sources (stuff like
stdin input, slave mode, lirc, joystick). Unify them to a single
function (mp_input_add_fd()), and make sure the associated callbacks
always have a context parameter.

Change the lirc and joystick code such that they take store their state
in a context struct (probably worthless), and use the new mp_msg
replacements (the point of this refactoring).

Additionally, get rid of the ugly USE_FD0_CMD_SELECT etc. ifdeffery in
the terminal handling code.
2013-12-21 20:50:13 +01:00
wm4 dadf3a9a46 m_config: add custom context to includefunc callback 2013-12-21 20:50:13 +01:00
wm4 3846fc7587 sub/osd: mp_msg conversions 2013-12-21 20:50:13 +01:00
wm4 92f9b51426 find_subfiles: mp_msg conversions 2013-12-21 20:50:13 +01:00
wm4 15ae64d369 player/timeline: mp_msg conversions 2013-12-21 20:50:13 +01:00
wm4 9825906f73 demux: use fprintf() for printing fatal errors
We print these before calling abort(), which is deadly unclean anyway.
Avoids having to add log contexts.
2013-12-21 20:50:13 +01:00
wm4 02a9fbd0ce codecs: avoid having to print error message
The mp_select_decoders() function doesn't have a log context (and I
don't want to give it one), so get rid of the mp_msg error message by
enhancing the semantics such that the syntax error is replaced by a new
feature. Now doing "--ad=something" will enable all decoders in the
"something" module, same as "--ad=something:*". Pretty useless, but gets
rid of the annoyance.
2013-12-21 20:50:13 +01:00
wm4 5f0fbacf16 codecs: mp_msg conversion 2013-12-21 20:50:12 +01:00
wm4 71b6a52295 av_common: abuse av_log to print message instead of mp_msg
Saves a little bit of pain.
2013-12-21 20:50:12 +01:00
wm4 138d183d83 ao: some missing mp_msg conversions 2013-12-21 20:50:12 +01:00
wm4 7cc3c3aeec ao_wasapi: mp_msg conversions
Remove the nonsensical print_lock too.

Things that are called from the option validator are not converted yet,
because the option parser doesn't provide a log context yet.
2013-12-21 20:50:12 +01:00
wm4 60c06fec1e audio/fmt-conversion.c: remove unknown audio format messages
Same deal as with video/fmt-conversion.c.
2013-12-21 20:50:12 +01:00
wm4 1974c9b49d audio: mp_msg conversions 2013-12-21 20:50:12 +01:00
wm4 4abe6b862f mixer: mp_msg conversions 2013-12-21 20:50:11 +01:00
wm4 57359a1d62 lua: minor simplification
Using m_property_do() is more complicated, and will have to be changed
later for mp_msg conversions.
2013-12-21 20:50:11 +01:00
wm4 eb15265d5a osdep/priority: drop message output when setting priority 2013-12-21 20:50:11 +01:00
wm4 fb5ee5bfbe player: use MSGL_SMODE for some slave-mode stuff
Replacement for MSGT_IDENTIFY. Can't kill it off completely yet; certain
people would complain to me personally.
2013-12-21 20:50:11 +01:00
wm4 95d94238f4 img_format: drop message about unknown pixel formats
Too bad.
2013-12-21 20:50:11 +01:00
wm4 823bf0029d bitmap_packer: use printf() for fatal message
This is printed right before abort(), which is bad style anyway.
Converting this to mp_msg will help nobody, and passing through a mp_log
is effort.
2013-12-21 20:50:11 +01:00
wm4 70af7ab8e5 vaapi: mp_msg conversions
This ended up a little bit messy. In order to get a mp_log everywhere,
mostly make use of the fact that va_surface already references global
state anyway.
2013-12-21 20:50:11 +01:00
wm4 9e0b84c321 vdpau: mp_msg conversions 2013-12-21 20:50:11 +01:00
wm4 64278128d8 video/fmt-conversion.c: remove unknown pixel format messages
This removes the messages printed on unknown pixel format messages.
Passing a mp_log to them would be too messy. Actually, this is a good
change, because in the past we often had trouble with these messages
printed too often (causing terminal spam etc.), and printing warnings or
error messages on the caller sides is much cleaner.

vd_lavc.c had a change earlier to print an error message if a decoder
outputs an unsupported pixel format.
2013-12-21 20:50:11 +01:00
wm4 5beedf1967 image_writer: mp_msg conversions
Adds an awkward mp_log argument for error messages.
2013-12-21 20:50:10 +01:00
wm4 38342436cd sws_utils: mp_msg conversions
This requires the caller to provide a mp_log in order to see error
messages. Unfortunately we don't do this in most places, but I guess we
have to live with it.
2013-12-21 20:50:10 +01:00
wm4 212ce468d8 gl_lcms: use global lock to deal with crappy lcms2 message callback
lcms2 has a global message callback for error reporting. If you don't
set this, these error messages are silently thrown away. I think we
still want the error messages, so we have to do dumb stuff to avoid
clashes. This doesn't handle the case if another library in the same
process sets the message callback, but at least this should exclude
possible memory errors when running multiple instances of mpv.
2013-12-21 20:50:10 +01:00
wm4 c8268701d9 video/out: pass along global context
Will be needed for other parts (especially in gl_lcms.c).
2013-12-21 20:50:10 +01:00
wm4 2eefa31c88 video/decode: mp_msg conversions
Doesn't cover vdpau/vaapi parts yet, because these are a bit messier.
2013-12-21 20:50:10 +01:00
wm4 426ebbae5f video/filter: mp_msg conversions 2013-12-21 20:50:10 +01:00
wm4 877303aaa9 x11: don't set global error handler
This has similar problems as the ALSA message callback, though in theory
we could use the Display handle to find the right mpv instance from the
global callback. It still wouldn't work if another library happens to
set the error handler at the same time. There doesn't seem much of an
advantage overriding the error handler (though it used to be required),
so remove it.
2013-12-21 17:39:40 +01:00
wm4 fdceef6cc5 ao_alsa: don't set ALSA message callback
This could output additional, potentially useful error messages. But the
callback is global and not library-safe, and would require us to add
additional state. Remove it, because it's obviously too much of a pain.
Also, it seems ALSA prints stuff to stderr anyway.
2013-12-21 17:36:56 +01:00
wm4 ad05e76c57 msg: handle vsnprintf errors
I don't know under which circumstances this can error (other than a
broken format string). It seems it won't return an error code on I/O
errors, so maybe broken format strings are the only case. Either way,
don't continue if an error is returned.
2013-12-20 21:07:58 +01:00
wm4 e9e68fc399 msg: use a global lock to synchronize printing
We have certain race conditions coming from doing multiple fprintf()
calls (setting up colors etc.). I'm not sure whether it would be worth
changing to code such that we do only one fprintf() call (and assume
this synchronizes access), but considering it would be hard to do
(Windows compatibility, ...), and that stdio uses per FILE locks anyway,
this is simpler and probably not less efficient. Also, there's no
problem handling the weird statusline special case this way.

Note that mp_msg_* calls which are silent won't acquire the lock, and
acquiring the lock happens on actual output only (which is slow and
serialized anyway).
2013-12-20 21:07:58 +01:00
wm4 6a8fc3f5e3 msg: change --msglevel, reduce legacy glue
Basically, reimplement --msglevel. Instead of making the new msg code
use the legacy code, make the legacy code use the reimplemented
functionality.

The handling of the deprecated --identify switch changes. It temporarily
stops working; this will be fixed in later commits.

The actual sub-options syntax (like --msglevel-vo=...) goes away, but I
bet nobody knew about this or used this anyway.
2013-12-20 21:07:57 +01:00
wm4 5162c2709e msg: cosmetic changes
In particular, condense the legacy MSGT_ defines and move them to the
end of the file.
2013-12-20 21:07:57 +01:00
wm4 591a6722d2 msg: change hack to silence command line pre-parse error messages
mp_msg_levels[] will go away.
2013-12-20 21:07:57 +01:00
wm4 78292058cc terminal: remove separate formatting for --msgmodule
Instead, --msgmodule uses the same formatting as -v.
2013-12-20 21:07:57 +01:00
wm4 4d4b822171 terminal: abstract terminal color handling
Instead of making msg.c an ifdef hell for unix vs. windows code, move
the code to separate functions defined in terminal-unix.c/terminal-
win.c.

Drop the code that selects random colors for --msgmodule prefixes.
2013-12-20 21:07:57 +01:00
Martin Herkt a4fe95b0d8 w32_common: support file drag and drop 2013-12-20 20:27:56 +01:00
Martin Herkt 2d3c7f3b90 input: make parse_cmd_strv take const args 2013-12-20 19:32:45 +01:00
wm4 2f6144c74b osc: use mp.send_commandv
See previous commit. Drop no-osd prefixes, as this is the default for
send_commandv anyway. send_command should probably be renamed later.
2013-12-20 18:02:01 +01:00
wm4 18930ba7dd input, lua: add functions to take pre-split input commands
So you can pass a command as list of strings (each item is an argument),
instead of having to worry about escaping and such.

These functions also take an argument for the default command flags. In
particular, this allows setting saner defaults for commands sent by
program code.

Expose this to Lua as mp.send_commandv command (suggestions for a better
name welcome). The Lua version doesn't allow setting the default command
flags, but it can still use command prefixes. The default flags are
different from input.conf, and disable OSD and property expansion.
2013-12-20 18:01:04 +01:00
wm4 833eba5304 terminal: move SIGTTOU signal handler setup code
This comes with a real change in behavior: now the signal handler is set
only when the terminal input code is active (e.g. not with
--no-consolecontrols), but this should be ok.
2013-12-19 21:31:33 +01:00
wm4 25d4ae74f1 Rename getch2....c/h to terminal....c/h
"getch2" really tells nothing about what the heck this code does. It'd
be even worse when moving the rest of terminal handling code there.
2013-12-19 21:31:27 +01:00
wm4 8c7ea10873 crosscompile-mingw.txt: fix instructions
The instructions hardcode some paths, so it sure would be better if the
listed commands actually use this path.
2013-12-19 21:31:18 +01:00
wm4 5e0c4ec3e8 player: replace some overlooked mp_msgs
There are still some using IDENTIFY, and some without context in
configfiles.c.
2013-12-19 21:31:18 +01:00
wm4 7a8207a3fb options: use M_OPT_EXIT with -V
Otherwise, output will actually be muted during pre-pase stage, and it
will exit before running it again with output enabled.
2013-12-19 21:27:09 +01:00
wm4 b4aa39a817 ta: fix compilation with NDEBUG 2013-12-19 21:27:02 +01:00
wm4 b97b92fe40 m_option.h: actually remove config.h include
This was supposed to be done in commit 80b34ebe.
2013-12-19 21:26:09 +01:00
wm4 f5b6710bec stream: remove stale MAX_STREAM_PROTOCOLS define 2013-12-19 21:25:51 +01:00
wm4 1fe39b75db command: remove radio commands
Remove these because I'm too lazy to convert them to proper
STREAM_CTRLs. Considering that probably nobody uses radio://, caring
about this is a complete waste of time. I will add these commands back
if someone asks for them, but I don't expect this to happen.
2013-12-19 21:25:40 +01:00
Martin Herkt d2e750ccd6 w32_common: fix mouse clicks
Apparently this has been broken for a year or so. The were three
reasons for the breakage here:

    1. The window dragging hack prevented any DOWN event from
       passing through since it always returned before we even got
       the button.
    2. The window style had CS_DBLCLKS in its flags, so we did not
       get any DOWN events when the OS had detected a double click
       (instead expecting us to handle a DBL event).
    3. We never sent any mouse buttons when mouse movement handling
       was disabled.
2013-12-19 21:04:10 +01:00
wm4 03e53ab430 ao_wasapi: fix includes
Broken due to recent header renaming. Untested.
2013-12-18 17:14:31 +01:00
wm4 6ae36d3225 vf_lavfi: don't access AVFilterPad directly
Direct access is deprecated.
2013-12-18 17:13:13 +01:00
wm4 b170248389 ad_lavc: work around deprecation warning
request_channels has been deprecated for years (request_channel_layout
is the replacement), but it appears it's still needed despite the
deprecation at least on older libavcodec versions.

So still set request_channels, but to it with the avoption API, which
hides the deprecation warning. This should also prevent mpv getting
trashed when libavcodec happens to bump its major version.
2013-12-18 17:12:49 +01:00
wm4 80b34ebea4 m_option: don't include config.h in header
Requires a small workaround.
2013-12-18 17:12:27 +01:00
wm4 2c08bf1bd7 Reduce recursive config.h inclusions in headers
In my opinion, config.h inclusions should be kept to a minimum. MPlayer
code really liked including config.h everywhere, though, even in often
used header files. Try to reduce this.
2013-12-18 17:12:21 +01:00
wm4 5e0424f17f stream: move O_BINARY dummy definition 2013-12-18 17:12:16 +01:00
wm4 4ed83fe2e5 Remove the _ macro
This was a gettext-style macro to mark strings that should be
translated.
2013-12-18 17:12:07 +01:00
Rudolf Polzer 761a013736 ildetect: add ILDETECT_FORCE_RUN (if interlacing could not be decided, assume yadif). 2013-12-18 10:56:33 +01:00
wm4 94369170b8 command: better check whether file duration is available 2013-12-17 13:49:11 +01:00
wm4 978e099b65 old-makefile: fix mpvcore references and the DIRS variable 2013-12-17 08:48:34 +01:00
11rcombs ad92c893a0 Fix OSX build; remove all remaining mpvcore references 2013-12-17 08:44:21 +01:00
wm4 0112143fda Split mpvcore/ into common/, misc/, bstr/ 2013-12-17 02:39:45 +01:00
wm4 73a5417950 Merge mp_talloc.h into ta/ta_talloc.h 2013-12-17 02:18:16 +01:00
wm4 eb15151705 Move options/config related files from mpvcore/ to options/
Since m_option.h and options.h are extremely often included, a lot of
files have to be changed.

Moving path.c/h to options/ is a bit questionable, but since this is
mainly about access to config files (which are also handled in
options/), it's probably ok.
2013-12-17 02:07:57 +01:00
wm4 793f85945f Move libquvi stuff to stream/resolve/ 2013-12-17 01:40:26 +01:00
wm4 8d5214de0a Move mpvcore/input/ to input/ 2013-12-17 01:23:09 +01:00
wm4 053c743724 Rename mp_lua.c/h to lua.c/h 2013-12-17 01:15:48 +01:00
wm4 56eafe3344 Rename mp_core.h to core.h
Get rid of the mp_ prefix.
2013-12-17 01:08:53 +01:00
wm4 e449111429 Move mpvcore/player/ to player/ 2013-12-17 00:53:22 +01:00
wm4 7dc7b900c6 Replace mp_tmsg, mp_dbg -> mp_msg, remove mp_gtext(), remove set_osd_tmsg
The tmsg stuff was for the internal gettext() based translation system,
which nobody ever attempted to use and thus was removed. mp_gtext() and
set_osd_tmsg() were also for this.

mp_dbg was once enabled in debug mode only, but since we have log level
for enabling debug messages, it seems utterly useless.
2013-12-16 20:41:08 +01:00
wm4 3e6cd3ef19 input: remove LIRCCD support
This removes support for the "LIRC Client Daemon", which is separate
from LIRC, and hasn't been maintained for 10 years.

See github issue #413.
2013-12-16 20:21:25 +01:00
wm4 9662e3e509 command: mess with previous commit
Nothing actually used the returned length. Since the remaining time can
easily become 0 or negative (e.g. incorrectly estimated file duration),
the time_remaining function still needs 2 return values, though.
2013-12-16 20:14:43 +01:00
Vivek Jain 908dfa7949 command: scale osd’s time remaining by the current speed
Signed-off-by: wm4 <wm4@nowhere>
2013-12-16 20:07:50 +01:00
Alessandro Ghedini e51808ea22 manpage: fix --vf=expand aspect ratio example 2013-12-16 20:06:45 +01:00
wm4 86bf16f449 input: move multi-command parsing somewhere else
I'm planning to add a mode to pass commands as a pre-split list of
strings, and this will probably be useful to reach this goal.
2013-12-16 20:04:51 +01:00
wm4 bc723f1b68 input: adjust code to make quoted/not quoted cases more unified 2013-12-16 20:03:37 +01:00
wm4 19b506ea65 input: better error reporting for missing commands
Don't print an empty string if the command is missing.
2013-12-16 20:03:00 +01:00
wm4 5173900ed4 input: move some command flags into a bitfield 2013-12-16 20:03:00 +01:00
wm4 b65a6c3cd0 input: remove weird syntax for skipping optional arguments 2013-12-16 20:03:00 +01:00
wm4 fe367272bd manpage: undocument syntax for skipping optional arguments in input commands
"-" could skip optional arguments. I think this was a pretty bad idea,
because it introduced a weird special case.

I'll remove the special syntax, but keep compatibility for the "seek"
and "screenshot" commands.
2013-12-16 20:03:00 +01:00
wm4 e8f21b1f0a input: avoid using wakeup pipe if it's not needed
If input is not waiting for select(), writing to the wakeup pipe is
wasteful, and, if there are many wakeups, might even block the wakeup
threads if the pipe gets full.

However, if it's waiting for select(), the wakup pipe must be used to
unblock the waiting thread. Actually there's a small race condition: we
might determine that the main thread is in select(), and write to the
wakeup pipe (whether we do this while unlocked or locked doesn't really
matter). Then, the main thread might leave select() before reading from
the wakup pipe. This should be harmless, because at worst more wakeups
than needed happen, but never fewer.
2013-12-16 20:03:00 +01:00
wm4 048bb2464d mp_msg: define a bunch of convenience macros
In order to use bare mp_log contexts, I find myself creating dummy
structs (with a single "log" field) to use the MP_ERR() etc. macros,
which hardcode the idiom that a context struct has a log field. On the
other hand, just using mp_msg_log() is too much typing (and I want to
rename it to mp_msg() when the transition is done), so it seems nice to
have message printing macros that use mp_log directly.
2013-12-16 20:02:59 +01:00
wm4 cd064c679c input: don't quit with exit status 1 when receiving SIGTERM
Instead, do whatever the normal "quit" command does, which currently
is returning a status of 0.
2013-12-15 18:14:15 +01:00
wm4 ff85afa537 player: don't temporarily disable terminal handling when reloading files
There's no reason why we should do this. For some reason, the existing
code reset terminal handling to default after unloading a file, just to
initialize it again when loading a new file.

Might be related to github issue #412, although I don't think it helps,
since the default SIGTERM handler _should_ kill the mpv process.
(It's still a nice simplification, though.)
2013-12-15 18:06:21 +01:00
wm4 6dcebd9130 osd_libass: update styles when OSD changes PlayRes
The OSD style settings depend on the PlayRes, simply because all style
values are implicitly scaled by the PlayResY in libass. Also, the OSC
changes the PlayResY in certain situations, so something could go wrong.
But not sure if this actually matters in practice.
2013-12-15 17:38:48 +01:00
wm4 78b5324eee osd: use separate ASS_Renderer for each OSD object
This simplifies things, although it is slightly less efficient (probably
uses a bit more memory).

This also happens to fix that the OSC dropped the libass cache on every
frame.
2013-12-15 15:04:49 +01:00
wm4 3194058309 player: don't store subtitle renderer in osd_state
This doesn't have much value. It can't be accessed by anything else than
the actual subtitle renderer (sd_ass.c). sd_ass.c could create the
renderer itself, except that we apparently want to save memory (and some
font loading time) when using ordered chapters or multiple subtitle
tracks.
2013-12-15 13:44:39 +01:00
wm4 4ca740ccf9 dvdnav: fix incorrect clipping of highlights 2013-12-15 10:21:33 +01:00
wm4 50b3cfa221 demux_mkv: don't seek outside of the file when finding segments
The end of the current segment will be the end of the file if there is
no next segment. Normally, this didn't matter much, since UNIX files
allow seeking past the end of the file. But when opening files from
HTTP, this would print confusing error messages. So explicitly check for
EOF before trying to read a segment.
2013-12-14 21:55:03 +01:00
wm4 2f49fbff93 matroska: add --ordered-chapters-files option
This option takes a playlist. The playlist will then be used as list of
potential segment files for use with ordered chapters.
2013-12-14 21:52:37 +01:00
wm4 7f3eda41d9 manpage: mention that the "run" does not wait for the command 2013-12-14 19:56:02 +01:00
wm4 683d7e88e4 Allow some options taking filenames to refer to mpv config dir
Add the mp_get_user_path() function, and make it expand special path
prefixes. Use it for some things in mpv which take filenames
(--input-config, --screenshot-template, opengl icc-profile suboption).

This allows accessing files in the mpv config dir without hardcoding the
config path by prefixing the path with ~~/. Details see manpage
additions.
2013-12-14 19:54:49 +01:00
wm4 dcf9f77c58 dvdnav: select longest title by default
This way we probably do the right thing, and avoid all the menu shit.
2013-12-14 03:00:59 +01:00
wm4 a59ebae2ad vf_dsize: fix bug caused by typo 2013-12-14 01:50:32 +01:00
wm4 9bd7601c6d dvdnav: crappy hack to respect timed still frames
Before this, they were displayed forever. Since some dvd screens seem
not to allow escaping from the still frame using the menu, this could
get you stuck forever.
2013-12-14 01:40:52 +01:00
wm4 653980ac3a dvdnav, tv: force-disable caching
On dvdnav, caching kind of works but not really. (Not our fault, at
least not fully. It's due to libdvdnav being slightly misdesigned; see
previous commit for some explanations.)

The TV code is implemented in the demuxer, and the stream implementation
is just a wrapper, so caching makes no sense here.
2013-12-14 01:21:06 +01:00
wm4 8510ddba4c dvdnav: enable caching
No idea why this was disabled. It was in the original MPlayer code,
which doesn't make much sense to me, because using the MPlayer stream
cache seems 100% broken due to design issues.
2013-12-14 01:15:56 +01:00
wm4 6002520906 dvdnav: block when libdvdnav requests draining buffers
Not sure how this should work. Using libdvdnav with a large FIFO doesn't
make sense either: data and control commands use the same stream, so if
you want to send input to libdvdnav, you just have to read from
libdvdnav all the time to get a reaction, which is not compatible with
maintaining a buffer that could remain full for a long time.

I have no idea either whether this improves or worsens anything, though
it might be more correct.
2013-12-14 01:11:38 +01:00
wm4 df4d7fd072 stream_dvdnav: drop stream buffers on seek 2013-12-14 01:00:02 +01:00
wm4 fc75506e8b cache: add a way to explicitly resume cache 2013-12-14 00:59:26 +01:00
wm4 1948131a2a cache: try harder on EOF
EOF is a special case. Normally, the reader will block until the cache
thread has new data. Obviously we don't want to do this on EOF, because
we'd potentially block forever. On the other hand, EOF will put the
cache thread into a waiting state, so if EOF recovers, this will happen
at a "later" point. This is bad if there is some kind of external event
that ends the EOF condition. In this case, a steram_read() call would
still return EOF. Make it so that the reader waits at least for one
iteration of the cache trying to rad a new block.

Also adjust some debug messages to not print file positions in hex.
2013-12-14 00:58:06 +01:00
wm4 9a723fc03b stream: don't seek when seeking to the same position
There was already something similar in the code that did the actual
seek,  but I think seeking to the same position could still trigger an
actual seek due to weid interaction with the buffer.
2013-12-14 00:52:39 +01:00
wm4 600bccdf2a stream: add function for dropping the buffer
And use it in demux_lavf.c. It looks like otherwise, some data might be
left over, depending on how the hell av_seek_frame() behaves.
2013-12-14 00:51:00 +01:00
wm4 dd6d204e9e dvdnav: improve a comment 2013-12-13 22:41:21 +01:00
wm4 ad571d629d dvdnav: remove highlights if no PCI available
Not sure if this actually can happen. It doesn't remove the problem that
mpv sometimes shows highlights on nav screens which have no highlight.
2013-12-13 22:38:29 +01:00
wm4 37319ab644 video: change --video-zoom behavior
Use the scaled video size (i.e. as shown on the window) as reference for
zoom. This is the easiest way to fix different width/height scale
factors  as they happen when zooming video with a pixel aspect ratio
other than 1:1.

Also fix the unscaled mode, so that it 1. doesn't scale even with
--video-zoom, and 2. doesn't scale by small amounts when the video is
cropped by making the window smaller than the video.
2013-12-13 22:25:38 +01:00
wm4 ebf6d0004b dvb: prevent seek when reopening the demuxer
Since dvb input is not seekable anyway, this would probably just flush
the stream buffer, but at least it'll avoid a bogus message about
seeking errors.
2013-12-13 00:31:28 +01:00
Nikoli ce8fe6d815 build: dvdnav needs dvdread 2013-12-13 00:19:52 +01:00
wm4 b0bd93cbc1 dvdnav: support mouse interaction 2013-12-13 00:19:17 +01:00
wm4 88432b817d dec_video: fix handling of timestamp resets
This code tried to pass a still monotonic (even if not strictly
monotonic) PTS to the player, but as a result it remained stuck on
the PTS before a reset (since the PTS was lower).
2013-12-12 23:46:27 +01:00
wm4 e66e16e691 demux_lavf: fix timebase confusion
This set last_pts to bogus values, which is used for relative seeks.
The player usually uses absolute seeks, so this didn't matter much.
2013-12-12 23:43:34 +01:00
wm4 49f9291164 demux: revert accidentally included change from previous commit
Oops.
2013-12-12 01:53:37 +01:00
wm4 0530447417 Add prelimimary (basic, possibly broken) dvdnav support
This readds a more or less completely new dvdnav implementation, though
it's based on the code from before commit 41fbcee. Note that this is
rather basic, and might be broken or not quite usable in many cases.

Most importantly, navigation highlights are not correctly implemented.
This would require changes in the FFmpeg dvdsub decoder (to apply a
different internal CLUT), so supporting it is not really possible right
now. And in fact, I don't think I ever want to support it, because it's
a very small gain for a lot of work. Instead, mpv will display fake
highlights, which are an approximate bounding box around the real
highlights.

Some things like mouse input or switching audio/subtitles stream using
the dvdnav VM are not supported.

Might be quite fragile on transitions: if dvdnav initiates a transition,
and doesn't give us enough mpeg data to initialize video playback, the
player will just quit.

This is added only because some users seem to want it. I don't intend to
make mpv a good DVD player, so the very basic minimum will have to do.
How about you just convert your DVD to proper video files?
2013-12-12 01:46:45 +01:00
wm4 76ce5434b2 demux_lavf: remove unnecessary seek on initialization
This is not needed, and actually completely incorrect.
2013-12-12 01:40:44 +01:00
wm4 a937e93fd6 input: fix bogus section disabling, add debugging output
The code to remove a section from the active section array wasn't
correct (it should have tried to copy the elements in reverse), so just
replace it with a macro that does the intended thing.

Add some debug output to print the section stack.
2013-12-12 01:25:21 +01:00
wm4 fcfd044090 sd_lavc: factor out bitmap positioning code 2013-12-12 01:25:21 +01:00
wm4 7c7d214775 osd: add option for "unscaled" OSD 2013-12-10 20:07:39 +01:00
wm4 bf003033e3 osd: typo in comment 2013-12-10 20:07:39 +01:00
wm4 94f1585d5f demux: set fps for mf:// to 1 2013-12-10 20:07:39 +01:00
wm4 227d087db6 video: display last frame, drain frames on video reconfig
Until now, the player didn't care to drain frames on video reconfig.
Instead, the VO was reconfigured (i.e. resized) before the queued frames
finished displaying. This can for example be observed by passing
multiple images with different size as mf:// filename. Then the window
would resize one frame before image with the new size is displayed. With
--vo=vdpau, the effect is worse, because this VO queues more than 1
frame internally.

Fix this by explicitly draining buffered frames before video reconfig.

Raise the display time of the last frame. Otherwise, the last frame
would be shown for a very short time only. This usually doesn't matter,
but helps when playing image files. This is a byproduct of frame
draining, because normally, video timing is based on the frames queued
to the VO, and we can't do that with frames of different size or format.
So we pretend that the frame before the change is the last frame in
order to time it. This code is incorrect though: it tries to use the
framerate, which often doesn't make sense. But it's good enough to test
this code with mf://.
2013-12-10 20:07:39 +01:00
wm4 5135e93d0c vo: reset some fields properly
Otherwise, next_pts2 can be == next_pts (and not MP_NOPTS_VALUE), in
which case the player thinks the first frame has duration 0. (Weird
corner case.)
2013-12-10 20:07:39 +01:00
wm4 e6e6b88b6d video: move VO reinit from filter chain to player
This gets rid of the vf_vo pseudo-filter. It ends the idea of MPlayer's
architecture that the VO is just a (terminating) video filter. It didn't
really work for us with respect to video timing (the "end" of the video
chain isn't really made for video timing, and making it do so would be
awkward), and now we're removing it entirely. We will be able to fix
some things, such as properly draining video on reconfiguration.
2013-12-10 20:07:39 +01:00
wm4 2f46b23d51 video: move handling of brightness and deinterlacing control
Handling of brightness/gamma/saturation/etc. and deinterlacing is moved
from vf_vo.c to dec_video.c.
2013-12-10 20:07:39 +01:00
wm4 5f0585177e vf_vo: remove VO reset on filter uninit
I don't think this has any reason to exist. It's likely that this used
to be required by the old direct rendering infrastructure. (See
git blame output.)
2013-12-10 20:07:39 +01:00
wm4 9838bf5565 video: move video filter chain initialization from decoder to player
This should help fixing some issues (like not draining video frames
correctly on reinit), as well as decoupling the decoder, filter chain,
and VO code.

I also wanted to make the hardware video decoding fallback work properly
if software-only video filters are inserted. This currently has the
issue that the fallback is too violent, and throws away a bunch of
demuxer packets needed to restart software decoding properly. But
keeping "backup" packets turned out as too hacky, so I'm not doing this,
at least not yet.
2013-12-10 20:07:39 +01:00
ChrisK2 218b9d3737 osxbundle: use mpv's version.sh instead of osxbundle's 2013-12-09 21:45:09 +01:00
wm4 398bfbe4c1 compat: add compatibility kludge for Libav 9
Libav 9 still uses the unprefixed PIX_FMT_... symbols, but they will
probably be removed some time in the future.

There are some other deprecations we have yet to take care of, but
there are no clear replacements yet.
2013-12-08 23:51:39 +01:00
Diogo Franco (Kovensky) 04faf9a1cb ao_wasapi: Fix mistaken behavior on uninit
The parameter, when true, tells whether uninit should block for flushing
the buffers, not whether it should quit immediately without flushing.
2013-12-08 19:36:44 -03:00
Diogo Franco (Kovensky) c7064ce5e5 ao_wasapi: handle AOPLAY_FINAL_CHUNK
Used for writing down all samples to the audio driver, even if it's not
a full chunk; needed at EOF on weird files.
2013-12-08 19:36:43 -03:00
Diogo Franco (Kovensky) 8f4380d6d5 ao_wasapi: Reduce the buffer size to a sane value
The previous RING_BUFFER_COUNT value, 64, would have ao_wasapi buffer 64
frames of audio in the ring buffer; a delay of 1280ms, which is clearly
overkill for everything. A value of 8 buffers 8 frames for a total of
160ms.
2013-12-08 19:14:56 -03:00
Diogo Franco (Kovensky) 2329e46229 ao_wasapi: fix audio buffering delay calculation
When get_space was converted to returning samples instead of bytes, a
unit type mismatch in get_delay's calculation returned bogus values. Fix
by converting get_space's value back to bytes.

Fixes playback with ao_wasapi when reaching EOF, or seeking past it.
2013-12-08 19:03:26 -03:00
wm4 62925a5c15 options: allow hwaccel formats in -vf format/noformat
There are 3 users of the image format option type: demux_raw,
vf_format, vf_noformat. Allow the hwaccel formats (like vdpau etc.)
in general, so that the filters can use it. This won't work for
demux_raw, so explicitly reject these formats there.
2013-12-07 19:39:45 +01:00
wm4 d658b115a0 vf: redo conversion filter insertion/format negotiation
Remove the inconsistent, duplicated, and insufficient scale filter
insertion code, and do it in one place instead. This also compensates
for the earlier removal of vf_match_csp() (which was in fact duplicated
code).

The algorithm to determine where to insert a filter etc. is probably the
same, though it also comes with some changes that should make debugging
easier when trying to figure out why a chain is failing to configure.

Add an "in" pseudo filter, which makes insertion of conversion filters
easier. Also change the vf->reconfig signature. At a later point, I'll
probably change format negotiation such that the generic filter code
will choose the output format, so having separate in and out params will
be useful.
2013-12-07 19:35:55 +01:00
wm4 37fbef2ccb video/filter: make vf->control non-recursive
Reason: I never liked it being recursive. Generally, this seems to
cause more problems than trouble, and is less flexible for access
outside of the chain.
2013-12-07 19:33:38 +01:00
wm4 0af9ede546 vf: remove flags from filter format status
I don't think we need these flags anymore. Simplify the code and get rid
of the vf_format struct.

There still is the vf_format.configured field, but this can be replaced
by checking for a valid image format.
2013-12-07 19:33:11 +01:00
wm4 bb6165342d video: create a separate context for video filter chain
This adds vf_chain, which unlike vf_instance refers to the filter chain
as a whole. This makes the filter API less awkward, and will allow
handling format negotiation better.
2013-12-07 19:32:44 +01:00
wm4 75d3bf4711 video/filter: remove vf_match_csp()
This function improves automatic filter insertion, but this really
should be done by the generic filter code. Remove vf_match_csp() and all
code using it as preparation for that.

This commit temporarily makes handling of filter insertion worse for
now, but it will be fixed with the following commits.
2013-12-07 19:32:03 +01:00
wm4 6025abffda vf: remove unused function 2013-12-07 19:31:47 +01:00
wm4 a54b775f80 vf: print error when creating filter fails
Before that we relied on the filters printing their own error messages.
2013-12-07 19:31:33 +01:00
wm4 745b9d542b manpage: remove some --flip leftovers 2013-12-07 19:30:46 +01:00
wm4 070269df73 mixer: remove comment about af_pan doing downmixing
We don't do that anymore.
2013-12-07 19:30:14 +01:00
wm4 e8a5aaf883 manpage: mark "run" command as Unix-only 2013-12-07 19:29:56 +01:00
wm4 5b19a66cff command: fix compilation with MinGW
This include header is needed for the fork/exec code, which is inactive
on Windows anyway.
2013-12-07 19:29:52 +01:00
Stefano Pigozzi e4f35516fb stream: fix clang warning
Good clang catches programming errors. `open(2)` takes `int` not `mode_t`.
2013-12-07 17:14:20 +01:00
bugmen0t 20d1fc132e build: prefer 4Front OSS to native implementations
If sys/soundcard.h is actually linux/soundcard.h then it supports only OSSv3
API. This may happen when OSSLIBDIR == /usr while forgetting to replace
sys/soundcard.h from glibc.

However, after fa620ff waf prefers native implementation which is inferior
on Linux. To fix try making waf prefer oss-audio-4front. It's quite unusual
to have 4Front OSS installed where native implementation is superior, anyway.
Signed-off-by: bugmen0t <@>

Make the false positives path also undef the 4Front define.
Signed-off-by: Stefano Pigozzi <stefano.pigozzi@gmail.com>

Fixes #396
2013-12-07 17:04:34 +01:00
agiz a066dae19e cocoa: make borderless window resizable
Fixes #399
2013-12-07 15:43:49 +01:00
Stefano Pigozzi b454cc0ca3 macosx_events: fix null dereference on uninit
Bug introduced by commit 6fb020f5. It doesn't always happen, since it is
caused by the playloop and cocoa UI code running in separate threads.

Fixes #398.
2013-12-07 15:29:53 +01:00
wm4 8f3d0b5e53 player: load external subs for uncompressed rar archives
Uncompressed rar archives can be transparently opened, but the filename
the player doesn't have the direct filename (but something starting
with rar://... instead). This will lead to external subtitles not
being loaded.

This doesn't handle multi-volume rar files, but in that cases just use
the --autosub-match=fuzzy option.

Fixes #397 on github.
2013-12-06 23:00:19 +01:00
Stefano Pigozzi a53abbf4a6 build: fix regression in cross-compilation [2]
Fixup commit for 5cb8439015. getattr only works on dot notation.
2013-12-06 10:32:21 +01:00
agiz 4ed4341df8 build: fix linking to CoreFoundation
If only coreaudio was activativated and not cocoa, the build failed for
missing CoreFoundation.

Signed-off-by: Stefano Pigozzi <stefano.pigozzi@gmail.com>
Fixes #395
2013-12-06 10:25:54 +01:00
Stefano Pigozzi f89bd7c5c2 build: link ARC to get subscripting implementation
This is needed on OS X 10.7 to handle Objective-C subscripting correctly. It
was present in the old configure, but I forgot it in the wscript.
2013-12-06 00:14:01 +01:00
wm4 9839bb08df vf: declare config() as legacy 2013-12-05 22:58:54 +01:00
wm4 2d0a9c7af0 video/filter: remove unneeded config callbacks
They didn't do anything.

vf_screenshot.c actually did release the previous image, but that's not
really required. At worst you could take a screenshot and get an old
frame when there's no new frame yet.
2013-12-05 22:58:54 +01:00
wm4 66e20ef8ad video: remove --flip
The --flip option flipped the image upside-down, by trying to use VO
support, or if not available, by inserting a video filter. I'm not sure
why it existed. Maybe it was important in ancient times when VfW based
decoders output an image this way (but even then, flipping an image is a
free operation by negating the stride).

One nice thing about this is that it provided a possible path for
implementing video orientation, which is a feature we should probably
support eventually. The important part is that it would be for free for
VOs that support it, and would work even with hardware decoding.

But for now get rid of it. It's useless, trivial, stands in the way, and
supporting video orientation would require solving other problems first.
2013-12-05 22:58:54 +01:00
wm4 b2c4653b88 vf: move norm_qscale() to the only filter which uses it 2013-12-05 22:58:53 +01:00
wm4 e3809e5b52 vf_sub, vf_dlopen: default struct is not needed 2013-12-05 22:58:53 +01:00
wm4 4904df42e9 vf: cleanup removed filter entry 2013-12-05 22:58:53 +01:00
wm4 e1966e4aae video: allow hardware decoding only for certain codecs
In particular, this disables mpeg4. There are some files out there that
use GMC, a usually rarely used and ineffective feature, which is not
supported by most hardware decoders. In these cases the hw decoder
outputs garbage, while software decoding works perfectly fine. We can't
really fallback to software decoding in these cases, because we don't
know that something is wrong in the first place. I can't see any
advantages of hw decoding of mpeg4, so it's better to disable it.
2013-12-05 22:58:53 +01:00
wm4 a558d84108 docs: edl: minor corrections 2013-12-05 22:58:53 +01:00
Stefano Pigozzi 5cb8439015 build: fix regression in cross-compilation
Regression was introduced in bf90317ad in an attempt to fix the Lua check.
2013-12-05 21:38:16 +01:00
wm4 f6e68b33bc video/filter: fix some bogus free() calls
The generic filter code frees these; recent regression.
2013-12-05 01:50:05 +01:00
wm4 84cfe0d8b2 audio: flush remaining data from the filter chain on EOF
This can be reproduced with:

   mpv short.wav -af 'lavfi="aecho=0.8:0.9:5000|6800:0.3|0.25"'

An audio file that is just 1-2 seconds long should play for 8-9 seconds,
which audible echo towards the end.

The code assumes that when playing with AF_FILTER_FLAG_EOF, the filter
will either produce output, or has all remaining data flushed. I'm not
really sure whether this really works if there are multiple filters with
EOF handling in the chain. To handle it correctly, af_lavfi should retry
filtering if 1. EOF flag is set, 2. there were input samples, and 3. no
output samples were produced. But currently it seems to work well enough
anyway.
2013-12-05 00:31:55 +01:00
wm4 ed024aadb6 audio/filter: change filter callback signature
The new signature is actually closer to how it actually works, and
someone who is not familiar to the API and how it works might make fewer
fatal mistakes with the new signature than the old one. Pretty weird.

Do this to sneak in a flags parameter, which will later be used to flush
remaining data of at least vf_lavfi.
2013-12-05 00:01:46 +01:00
wm4 2bcfb49a39 ad_lavc: handle decoder EAGAIN only if there was an input packet
Otherwise, it'd probably get stuck if the decoder still returns EAGAIN
at EOF on e.g. a shortened data stream.
2013-12-04 23:30:01 +01:00
wm4 e9f49ea84d options: remove legacy hacks for sub-option handling 2013-12-04 23:13:46 +01:00
wm4 193930ac3b af: remove af->setup field
Used to be used by filters that didn't use the option parser.
2013-12-04 23:13:46 +01:00
wm4 09bd19e59e af: remove legacy option parsing hacks 2013-12-04 23:13:46 +01:00
wm4 82983970b3 af_pan: change options, use option parser
Similar to af_channels etc...
2013-12-04 23:13:46 +01:00
wm4 adc843f984 af_ladspa: change options, use option parser 2013-12-04 23:13:46 +01:00
wm4 bcd8afc2ad af_delay: change option parsing, fix bugs, use option parser
Similar situation to af_channels.
2013-12-04 23:13:46 +01:00
wm4 71b6115d66 af_channels: use "unknown" channel layouts
This will make af_channels output a channel layout that is compatible
with any destination layout. Not sure if that's a good idea though,
since the way the AO choses a layout is perhaps less predictable. On the
other hand, using the old MPlayer standard layouts doesn't make much
sense either. We'll see whether this improves or breaks someone's use
case.
2013-12-04 23:13:46 +01:00
wm4 4f581a781b af_channels: change options, fix bugs, use option parser
Apparently this stopped working after some planar changes (broken format
negotiation). Radically change option parsing in an incompatible way.
Suggest alternatives to this filter, since it barely has any importance
anymore.
2013-12-04 23:13:42 +01:00
wm4 ad8e3d8c30 af_sweep: use option parser 2013-12-04 23:12:52 +01:00
wm4 d74419e6f0 af_surround: use option parser 2013-12-04 23:12:52 +01:00
wm4 54b8a7150a af_sub: use option parser 2013-12-04 23:12:52 +01:00
wm4 ee7ff874ba af_sinesuppress: use option parser 2013-12-04 23:12:52 +01:00
wm4 98905f668f af_hrtf: use option parser 2013-12-04 23:12:52 +01:00
wm4 aaccf9d5e9 af_extrastereo: use option parser 2013-12-04 23:12:51 +01:00
wm4 2c23fae344 af_export: use option parser
Probably requires the user to quote the shared buffer filename.
2013-12-04 23:12:51 +01:00
wm4 5b7eb713a1 af_equalizer: use option parser 2013-12-04 23:12:51 +01:00
wm4 349376aa5c af_drc: use option parser 2013-12-04 23:12:51 +01:00
wm4 0205f3d214 af_center: use option parser 2013-12-04 23:12:51 +01:00
wm4 a27114bb4b af: returning NULL on filtering means error
This code used to be ok, until the assert() was added. Simplify the loop
statement, since the other NULL check for data doesn't make sense
anymore.
2013-12-04 23:12:51 +01:00
wm4 59aed93208 ad_lavc: expose an option to enable threading 2013-12-04 23:12:51 +01:00
wm4 47c4b5c000 vd_lavc: factor out libavcodec thread setup 2013-12-04 23:12:51 +01:00
wm4 0afd121ae6 vd_lavc: don't check required hwdec fields 2013-12-04 23:12:51 +01:00
wm4 9c2858f37f ad_lavc: deal with arbitrary decoder delay
Normally, audio decoder don't have a decoder delay, so the code was
fine. But FFmpeg supports multithreaded decoding for some audio codecs,
which introduces such a delay.

The delay means that we won't get decoded audio for the first few
packets, and that we need to do something to get the trailing audio
still buffered in the decoder when reaching EOF.

Two changes are needed to deal with the delay:
- If EOF is reached, pass a "flush" packet to the decoder to return the
  buffered audio. Such a flush packet is automatically setup when
  calling mp_set_av_packet() with a NULL packet.
- Use the PTS returned by the decoder, instead of the packet's. This is
  important to get correct timestamps for decoded audio. Ignoring this
  would result into offsetting the audio playback time by the decoder
  delay. Note that we can still use the timestamp of the first packet
  to get the timestamp for the start of the audio.
2013-12-04 23:12:51 +01:00
wm4 8a84da8102 av_common: add timebase parameter to mp_set_av_packet()
If the timebase is set, it's used for converting the packet timestamps.
Otherwise, the previous method of reinterpret-casting the mpv style
double timestamps to libavcodec style int64_t timestamps is used.

Also replace the kind of awkward mp_get_av_frame_pkt_ts() function by
mp_pts_from_av(), which simply converts timestamps in a way the old
function did. (Plus it takes a timebase parameter, similar to the
addition to mp_set_av_packet().)

Note that this should not change anything yet. The code in ad_lavc.c and
vd_lavc.c passes NULL for the timebase parameters. We could set
AVCodecContext.pkt_timebase and use that if we want to give libavcodec
"proper" timestamps.

This could be important for ad_lavc.c: some codecs (opus, probably mp3
and aac too) have weird requirements about doing decoding preroll on the
container level, and thus require adjusting the audio start timestamps
in some cases. libavcodec doesn't tell us how much was skipped, so we
either get shifted timestamps (by the length of the skipped data), or we
give it proper timestamps. (Note: libavcodec interprets or changes
timestamps only if pkt_timebase is set, which by default it is not.)
This would require selecting a timebase though, so I feel uncomfortable
with the idea. At least this change paves the way, and will allow some
testing.
2013-12-04 23:12:51 +01:00
wm4 e2dfdc0c7e manpage: use different quoting in example
Using "" quotes often tricks people into using this on the command line,
while shell still expands $ inside of these.
2013-12-04 23:12:50 +01:00
wm4 f221d46a44 manpage: remove unhelpful paragraph about video filter params
This makes it sound like -1 would work to set the default for any
parameter. But this is just a (crappy) convention, which doesn't work
always.
2013-12-04 23:12:50 +01:00
wm4 57b11ba706 manpage: update af_format entry
Don't bother explaining the sample format naming schema. The "ne" bit is
outdated anyway, and anyone who has to use this option will be able to
understand the naming schema just by looking at the names too.
2013-12-04 23:12:50 +01:00
Stefano Pigozzi 18f3a1a7ee build: osx: set chmod to 755 for non-bundle binary during install
When installing the bundle we also manually install the origianl binary. Waf
defaults to chmod 644, so we must explicitely set it to 755.
2013-12-04 21:39:53 +01:00
Alexander Preisinger 6e34b0ec1f wayland: print waylands display erros
This is very usefull especially if you want to use newer wayland stuff like
wl_subsurfaces and xdg_surfaces.
2013-12-04 20:29:16 +01:00
Stefano Pigozzi bf90317ad1 build: remove execute kwarg to check_cc when cross-compiling
This prevents waf from running test programs after compilation. A better
approach would be to only remove this option if the check actually errors,
but we are using this only for Lua anyway.
2013-12-04 19:31:28 +01:00
wm4 aeed84bd27 vf_noise: reduce binary size
Same issues as in previous commit.
2013-12-04 02:17:19 +01:00
wm4 0c2a0a67b6 vf_eq: revert unintended binary size increase
The vf_eq context contains a very large lookup table, and the method of
setting default values caused the vf_eq context to be included in the
compiled code.
2013-12-04 01:51:27 +01:00
wm4 05ba5a08d2 manpage: fix example 2013-12-04 01:39:57 +01:00
wm4 a91e51a8f1 manpage: remove two stray video fitler deprecation notes
vf_stereo3d now uses vf_lavfi, if mpv was compiled with libavfilter.

vf_swapuv is hereby undeprecated. It's too trivial to wrap it with
libavfilter, and it's also too useless that even typing this commit
message is not really worth the time to spend on it.
2013-12-04 00:14:24 +01:00
bugmen0t 7ee074813b ao_oss: when falling back from unknown prefer larger format 2013-12-04 00:07:40 +01:00
bugmen0t 9fcf88e42b ao_oss: add 24bit formats 2013-12-04 00:07:40 +01:00
wm4 e872a13d5f manpage: generic notice about changed sub-option parsing
Just in case someone expects these are unchanged just because they're
not mentioned in changes.rst anywhere. Documenting all of these changes
would be too much work and not helpful either.
2013-12-04 00:07:40 +01:00
wm4 437ecc5cad video/filter: remove legacy option handling hacks
All filters now either use the generic option parser, or don't have
options. This finally finishes a transition started in 2003 (see git
commit 33b62af947).

Why are MPlayer devs so monumentally lazy? Sorry, but this takes the
cake. You had 10 years.
2013-12-04 00:07:40 +01:00
wm4 079e0e7963 vf_vo: don't abuse option strings to set VO
Whoever thought this was a good idea should be punched.
2013-12-04 00:07:40 +01:00
wm4 0d5e4ba799 vf_rotate: use option parser 2013-12-04 00:07:40 +01:00
wm4 fabfb23232 vf_pp: use option parser 2013-12-04 00:07:40 +01:00
wm4 a605fae6fa vf_ilpack: use option parser 2013-12-04 00:07:40 +01:00
wm4 e92f4d4726 vf_eq: use option parser 2013-12-04 00:07:39 +01:00
wm4 3201a40234 vf_dsize: use option parser
Mostly backwards compatible, we don't change much because we just want
to get rid of the legacy option string handling.

You can't pass an aspect as first argument anymore.
2013-12-04 00:07:39 +01:00
wm4 43a6dd1ed5 vf_divtc: use option parser 2013-12-04 00:07:39 +01:00
wm4 5b7351ee3a video/filter: remove vf_down3dright
Apparently you can get this with: stereo3d=ab[2]{l,r}:sbs[2]{l,r}

So it seems the filter is redundant and can be removed.

Also see FFmpeg commit 2f11aa141a01.
2013-12-04 00:07:39 +01:00
wm4 66f74d031d vf: remove unneeded field
This was once required for padding, because many filters didn't use
designated initializers, and initialized a removed field with NULL.
2013-12-04 00:07:39 +01:00
wm4 5b48204e84 vf_pullup: change options, reroute to vf_lavfi
The options are probably mostly backwards compatible.
2013-12-04 00:07:39 +01:00
wm4 b21cb279d7 vf_unsharp: change options, reroute to vf_lavfi 2013-12-04 00:07:39 +01:00
wm4 1acb32201b vf_phase: change options, reroute to vf_lavfi
The option change is probably backwards compatible.
2013-12-04 00:07:39 +01:00
wm4 6a2eeedc4c vf_noise: reroute to vf_lavfi
Unfortunately, this forces filtering both luma and chroma, because
otherwise we'd have to deal with libavfilter's vf_noise weird handling
of YUV vs. RGB formats. Would we e.g. filter luma only, it would filter
red in RGB mode only, because it goes by component and there's no way to
distinguish YUV and RGB by just using the filter's options.
2013-12-04 00:07:39 +01:00
wm4 ad950be415 vf_stereo3d: reroute to vf_lavfi 2013-12-04 00:07:39 +01:00
wm4 54d67581d7 vf_hqdn3d: change options, reroute to vf_lavfi 2013-12-04 00:07:39 +01:00
wm4 7d04327e71 vf_gradfun: reroute to vf_lavfi
Also update the manpage. We changed defaults and added a suboption quite
some time ago, and we forgot to update the manpage at all.
2013-12-04 00:07:39 +01:00
wm4 5064ef87c3 vf_delogo: reroute to vf_lavfi
The ``file`` suboption is unsupported on lavfi.
2013-12-04 00:07:39 +01:00
wm4 86ba9a7f24 vf_yadif: change options, reroute to vf_lavfi
Also remove the ability to disable deinterlacing at runtime. You can
still disable deinterlacing at runtime by using the ``D`` key and its
automatical filter insertion/removal.
2013-12-04 00:07:38 +01:00
wm4 25635a62c3 vf_lavfi: export a wrapper function
This will allow old filter to run libavfilter instead by calling
vf_lw_set_graph(), which turns the filter into a wrapper, using a given
libavfilter graph.

Later commits use that to automatically "reroute" a bunch of filters to
libavfilter. We want to get rid of the old MPlayer filter code, because
it's bad an unmaintained, but we still don't want to force everyone to
use vf_lavfi, so this solution will do for a while.
2013-12-04 00:07:38 +01:00
wm4 733c2369d3 video: remove VFCAP_POSTPROC
This wasn't used anymore, not even until recently.
2013-12-04 00:07:38 +01:00
wm4 1176ca9465 vf_screenshot: remove unnecessary code 2013-12-04 00:07:38 +01:00
Stefano Pigozzi ca956af446 gl_video: change internal API for hwdec mp_image download
Previous API worked under the assumption that download_image is always called
after map_image. In practice this is true, but it's better to have a much
generic API that doesn't depend on the order in which the functions are called.
2013-12-02 21:26:17 +01:00
wm4 14d92a4685 manpage: mention that vda can work with vo_opengl
Also, with the addition of VDA, all hardware decoding backends work with
vo_opengl now.
2013-12-02 19:24:18 +01:00
wm4 391035d857 gl_video: use hwdec download_image call only if hwdec is active
The hwdec driver can be loaded, even if it's not used (e.g. when playing
a file with no hardware decoding after one with it enabled).

Also, check whether dlimage is NULL. Since this will do call into the
native hwdec API, there's a chance a driver could fail doing this, it's
better to check the return value, even if this case currently can't
happen.
2013-12-02 19:22:45 +01:00
Vivek Jain 6fb020f5de options: add option to disable using right Alt key as Alt Gr
mpv was hardcoded to always consider the right Alt key as Alt Gr, but there
are parituclar combinations of platforms and keyboard layouts where it's more
convenient to treat the right Alt as a keyboard modifier just like the left
one.

Fixes #388
2013-12-02 09:03:31 +01:00
Stefano Pigozzi a74d9c1803 vo_opengl: support for vda hardware decoding
The harder work was done in the previous commits. After that this feature comes
out almost for free.

The only problem is I can't get the textures created with CGLTexImageIOSurface2D
to download properly, thus the code performs download using some CoreVideo APIs.

If someone knows why download of textures created with CGLTexImageIOSurface2D
doesn't work please contact me :)
2013-12-02 09:03:31 +01:00
Stefano Pigozzi 36eebc231e gl_video: support packed YUV formats with Apple extensions
This adds support for packed YUV formats (YUVY and UYVY) using the extension
GL_APPLE_rgb_422. While supporting this formats on their own is not that
important (considering most video is planar YUV) they are used for
interoperability with IOSurfaces.

Next commit will use this formats to render VDA hardware decoded frames through
IOSurface and OpenGL interoperability.
2013-12-02 09:03:30 +01:00
wm4 cb45ea371f build: reject broken roaraudio sndio emulation
roaraudio has some sort of sndio emulation, but apparently its header
file is either blatantly broken, or an old version from the past. The
sio_onvol() function has the wrong return type (void instead of int),
and the SIO_DEVANY symbol is missing entirely. This broke the build,
because the configure check was successful anyway.
2013-12-02 00:21:59 +01:00
wm4 dc582f2505 vo_opengl: add support for rectangle textures
This allows vo_opengl to use GL_TEXTURE_RECTANGLE textures, either by
enabling it with the 'rectangle-textures' sub-option, or by having a
hwdec backend force it. By default it's off.

The _only_ reason we're adding this is because VDA can export rectangle
textures only.
2013-12-01 23:39:13 +01:00
wm4 f30c2c99d1 mp_image: deal with FFmpeg PSEUDOPAL braindeath
We got a crash in libavutil when encoding with Y8 (GRAY8). The reason
was that libavutil was copying an Y8 image allocated by us, and expected
a palette. This is because GRAY8 is a PSEUDOPAL format. It's not clear
what PSEUDOPAL means, and it makes literally no sense at all. However,
it does expect a palette allocated for some formats that are not
paletted, and libavutil crashed when trying to access the non-existent
palette.
2013-12-01 20:51:38 +01:00
wm4 1136a8b6ac input: don't emit command when using multiple mouse buttons at once
This is for key bindings that use multiple mouse buttons at once. (Yes,
this is weird, but MPlayer always had this feature, and apparently
there are people using it!)

Before this commit, clicking another mouse button while still holding
the previous mouse button forced the command bound to the previous
mouse button to be emitted. This is usually needed to make sure the
input consumer (the player and the OSC) stays in sync with the actual
mouse button state. If there's no command sent, the OSC in particular
would think the button is still held down. However, sending the command
is undesired behavior if you want to use these multiple-key binds.

Solve this by emitting commands in this situation only if a key down
command was sent earlier. Since mouse button key bindings are normally
executed on key-up only, this happens with special commands like
script_dispatch only (used by the OSD to track mouse buttons, but
also used for other OSC bindings).

See github issue #390.
2013-12-01 18:05:08 +01:00
wm4 071ae5613f input: improve some comments
I have the feeling some of these were written before everything was
changed all over again.
2013-12-01 17:34:43 +01:00
wm4 b5d7c95e36 vo_null: don't reject hwaccel formats
This is not strictly needed anymore. (On the other hand, it's not really
possible to do hw decoding with vo_null, because the VO is still
responsible for opening the hw decoder API, but that's another story.)
2013-12-01 15:18:06 +01:00
wm4 48e10da501 command: add a revert_seek command
As discussed on IRC.
2013-12-01 02:07:32 +01:00
wm4 b18f02d1ad options: add options that set defaults for af/vf/ao/vo
There are some use cases for this. For example, you can use it to set
defaults of automatically inserted filters (like af_lavrresample). It's
also useful if you have a non-trivial VO configuration, and want to use
--vo to quickly change between the drivers without repeating the whole
configuration in the --vo argument.
2013-12-01 00:12:10 +01:00
wm4 aaddcb702e old-configure: delete pre-waf config.h and config.mak 2013-11-30 23:17:44 +01:00
wm4 fafa2f4b85 osdep/io: also include unistd.h
Might be needed by fcntl() usage.
2013-11-30 23:17:20 +01:00
wm4 9d8573cfe8 Prevent creating zombies
The "run" input command does fork+exec to spawn child processes. But it
doesn't cleanup the child processes, so they are left as zombies until
mpv terminates. Leaving zombie processes around is not very nice, so
employ a simple trick to let pid 1 take care of this: we fork twice, and
when the first fork exits, the second fork becomes orphaned and becomes
pid 1's child. It becomes pid 1's responsibility to cleanup the process.
The advantage is that we don't need extra logic to cleanup the spawned
process, which could have an arbitrary lifetime.

This is e.g. described here: http://yarchive.net/comp/zombie_process.html

Also use _exit() instead of exit(). It's not really sane to run cleanup
handlers (atexit() etc.) inside a forked process.
2013-11-30 22:47:26 +01:00
wm4 95cfe58e3d Use O_CLOEXEC when creating FDs
This is needed so that new processes (created with fork+exec) don't
inherit open files, which can be important for a number of reasons.

Since O_CLOEXEC is relatively new (POSIX.1-2008, before that Linux
specific), we #define it to 0 in io.h to prevent compilation errors on
older/crappy systems. At least this is the plan.

input.c creates a pipe. For that, add a mp_set_cloexec() function (which
is based on Weston's code in vo_wayland.c, but more correct). We could
use pipe2() instead, but that is Linux specific. Technically, we have a
race condition, but it won't matter.
2013-11-30 22:40:51 +01:00
wm4 eea69682a6 build: don't check libsmbclient version number
Rationale see github issues #385. Fixes #385.
2013-11-30 19:47:09 +01:00
bugmen0t c8ab12ee4b ao_oss: add 6.1 and 7.1 speaker placement from FreeBSD 2013-11-30 19:07:17 +01:00
wm4 ac0cbd7c5e ao_oss: SNDCTL_DSP_CHANNELS takes int, not uint8_t
This caused weird issue, probably caused by setting up the wrong number
of channels, or similar. See github issue #383.

Patch by bugmen0t on github.
2013-11-30 18:58:18 +01:00
wm4 17d72de2ac ao_alsa: remove unneeded checks
If initialization succeeds, p->alsa should always be set. Additional
checks are not needed, and also this wasn't even done consistently.
2013-11-30 18:56:44 +01:00
wm4 557efff690 ao_alsa: enable "plug" for non-interleaved float formats too
I have no idea what this code does, but it seems logical it should be
active for all float formats, not just for float with interleaved
access.
2013-11-30 18:55:39 +01:00
wm4 f388f14c01 command: add a cycle_values input command 2013-11-30 01:08:23 +01:00
wm4 e61e6e6fd9 command: change the syntax and semantics of the "run" command
See the changes in input.rst for explanations.

Technically speaking, this also gets rid of some undefined behavior:
passing NULL as a vararg (execl()) is always a bug.
2013-11-30 01:08:23 +01:00
wm4 5fe5fb02b4 input: require space before '#' comments
So e.g.

    show_text abc#def

will now print "abc#def" instead of "#def". It's simpler, more
consistent with how ";" and other things are handled, and also
possibly avoids bothering the user with extra escaping.
2013-11-30 01:08:22 +01:00
wm4 924c4c1fa5 input: rearrange command parsing
This simplifies some things as preparation for the next commits.
2013-11-30 01:08:22 +01:00
Stefano Pigozzi 6565c0d979 build: make deps_neg work with multiple dependencies
Previous code only worked id len(deps_neg) was 1.
2013-11-29 23:31:17 +01:00
Stefano Pigozzi fa620ffc95 build: reimplement the OSS checks using a more declarative approach
The OSS checks were a big mess and quite buggy. This reimplementes them using
a declarative approach and clearly distinguishing between the various OSS
implementations. The code should now almost be auto-documenting.

We currently support the following implementations of OSS:
 * platform-specific (with `sys/soundcard.h`)
 * SunAudio (default on NetBSD and useable on OpenBSD even if we have sndio
   support there).
 * 4Front (default on FreeBSD)

Since now each OSS check also checks for the appropriate soundcard header,
remove the old soundcard check.

Many thanks to @bugmen0t for in depth info about all the BSDs.
Check #380 and #359 for more info on this commit.
2013-11-29 23:11:12 +01:00
Nikoli 18345400c0 build: add options for enabling and disabling any libquvi versions
Makes packaging a bit simpler.
2013-11-29 23:08:33 +01:00
wm4 597b8a3550 Take care of some libavutil deprecations, drop support for FFmpeg 1.0
PIX_FMT_* -> AV_PIX_FMT_* (except some pixdesc constants)
enum PixelFormat -> enum AVPixelFormat
Losen some version checks in certain newer pixel formats.
av_pix_fmt_descriptors -> av_pix_fmt_desc_get

This removes support for FFmpeg 1.0.x, which is even older than
Libav 9.x. Support for it probably was already broken, and its
libswresample was rejected by our build system anyway because it's
broken.

Mostly untested; it does compile with Libav 9.9.
2013-11-29 17:39:57 +01:00
wm4 f1072e7629 ao_alsa: disable ALSA resampling by default again
This partially reverts commit 7d152965. It turns out that at least some
ALSA drivers (at least snd-hda-intel) report incorrect audio delay with
non-native sample rates, even if the sample rate is only very slightly
different from the native one.

For example, 48000Hz is fine on my hda-intel system, while both 8000Hz
and 47999Hz lead to a delay off by 40ms (according to mpv's A/V
difference display), which suggests that something in ALSA is
calculating the delay using the wrong sample rate.

As an additional problem, with ALSA resampling enabled, using
48001Hz/float/2ch fails, while 49000Hz/float/2ch or 48001Hz/s16/2ch
work. With resampling disabled, all these cases work obviously, because
our own resampler doesn't just refuse any of these formats.

Since some people want to use the ALSA resampler (because it's highly
configurable, supports multiple backends, etc.), we still allow enabling
ALSA resampling with an ao_alsa suboption.
2013-11-29 15:59:53 +01:00
wm4 42714f9a99 player: always reset some variables on seek
These were confined to the video path, but resetting them even if no
video is available shouldn't really matter. Always resetting them makes
the logic easier to follow, I think.
2013-11-29 15:42:19 +01:00
wm4 2ec629bede vf_pullup: properly reset on seek
Sometimes, vf_pullup hanged on seek. This was because it never was
properly reset. Old timestamps messed up the timestamp calculations,
which made the player show frames for a ridiculously long time, which is
perceived as pausing or hanging.
2013-11-29 15:08:05 +01:00
wm4 54f15bd377 video: restore printing warning on decreasing filter PTS
Recently, the check was moved, so it was printed only for source video
PTS (since that's easier, and filters should normally behave sane). But
it turns out it's trivial to print a warning in the filter case too by
reusing the code that normally checks for PTS forward jumps without
needing any additional code, so, fine, restore warning in this case.
2013-11-29 15:06:29 +01:00
wm4 9edc2dbcf3 gl_hwdec: use a imgfmt field instead of a query_format callback
Now that vdpau always uses a single image format, this can be
simplified.
2013-11-29 14:19:51 +01:00
wm4 2a316c3506 vdpau: always let decoder output IMGFMT_VDPAU
The old ffmpeg vdpau support code uses separate vdpau pixel formats for
each decoder (pretty much because mplayer's architecture sucked), which
just gets into the way. Force the old decoder's output to IMGFMT_VDPAU,
and remove IMGFMT_IS_VDPAU() where we can remove it.

This should completely remove the differences betwene the old and new
vdpau decoder outside of the decoder.
2013-11-29 14:19:44 +01:00
wm4 60cd300558 vaapi: remove unused hw image formats, simplify
PIX_FMT_VDA_VLD and PIX_FMT_VAAPI_VLD were never used anywhere. I'm not
sure why they were even added, and they sound like they are just for
compatibility with XvMC-style decoding, which sucks anyway.

Now that there's only a single vaapi format, remove the
IMGFMT_IS_VAAPI() macro. Also get rid of IMGFMT_IS_VDA(), which was
unused.
2013-11-29 14:19:29 +01:00
wm4 0d255f07bf build: make pthreads mandatory
pthreads should be available anywhere. Even if not, for environment
without threads a pthread wrapper could be provided that can't actually
start threads, thus disabling features that require threads.

Make pthreads mandatory in order to simplify build dependencies and to
reduce ifdeffery. (Admittedly, there wasn't much complexity, but maybe
we will use pthreads more in the future, and then it'd become a real
bother.)
2013-11-28 19:28:38 +01:00
wm4 0a18f3eb9a command: allow "current" as argument to playlist_remove command
Feature request from github issue #376.
2013-11-28 19:13:48 +01:00
wm4 5318a1bea1 input: use separate type for command definitions
Introduce a mp_cmd_def struct to define commands, instead of using
mp_cmd for this. This way each command parameter can be a m_option,
instead of m_option plus some more stuff.

Define the ARG_ macros directly in terms of the OPT_ macros. Not sure if
this makes it easier to read (maybe not, even if it looks simpler), but
at least it makes it easier to add other option types.

Another idea was adding a name for each parameter (so you could have
named parameters), but not today.
2013-11-28 19:04:16 +01:00
wm4 5411fbdb23 player: simple hack to make backstep code somewhat more robust
The hr-seek code assumes that when seeking the demuxer, the first image
decoded after the seek will have a PTS exactly equal to the demuxer seek
target time, or before that target time. Incorrect timestamps,
implicitly dropped initial frames, or broken files/demuxers can all
break this assumption, and lead to hr-seek missing the seek target.

Generally, this is not much a problem (the user won't notice being off
by one frame), but it really shows when using the backstep feature. In
this case, backstepping would simply hang.

Add a simple hack that basically forces a minimal value for the --hr-
seek-demuxer-offset option (which is 0 by default) when doing a
backstep-seek. The chosen minimum value is arbitrary. There's no perfect
value, though in general it should perhaps be slightly longer than the
frametime, which the chosen value is more than enough for typical
framerates.
2013-11-28 15:20:33 +01:00
wm4 dc0b2046cd video: add insane hack to work around FFmpeg/Libav insanity
So, FFmpeg/Libav requires us to figure out video timestamps ourselves
(see last 10 commits or so), but the methods it provides for this aren't
even sufficient. In particular, everything that uses AVI-style DTS (avi,
vfw-muxed mkv, possibly mpeg4-in-ogm) with a codec that has an internal
frame delay is broken. In this case, libavcodec will shift the packet-
to-image correspondence by the codec delay, meaning that with a delay=1,
the first AVFrame.pkt_dts is not 0, but that of the second packet. All
timestamps will appear shifted. The start time (e.g. the time displayed
when doing "mpv file.avi --pause") will not be exactly 0.

(According to Libav developers, this is how it's supposed to work; just
that the first DTS values are normally negative with formats that use
DTS "properly". Who cares if it doesn't work at all with very common
video formats? There's no indication that they'll fix this soon,
either. An elegant workaround is missing too.)

Add a hack to re-enable the old PTS code for AVI and vfw-muxed MKV.
Since these timestamps are not reorderd, we wouldn't need to sort them,
but it's less code this way (and possibly more robust, should a demuxer
unexpectedly output PTS).

The original intention of all the timestamp changes recently was
actually to get rid of demuxer-specific hacks and the old timestamp
sorting code, but it looks like this didn't work out. Yet another case
where trying to replace native MPlayer functionality with FFmpeg/Libav
led to disadvantages and bugs. (Note that the old PTS sorting code
doesn't and can't handle frame dropping correctly, though.)

Bug reports:

 https://trac.ffmpeg.org/ticket/3178

 https://bugzilla.libav.org/show_bug.cgi?id=600
2013-11-28 15:20:33 +01:00
wm4 d9b5dedfe9 video: warn against non-monotonic PTS instead of decreasing PTS
And by non-monotonic, we mean "strictly non-monotonic".
2013-11-28 15:20:33 +01:00
wm4 3bed78fdfd video: add heuristic to prevent framedrop during hrseek if pts broken
Using --start with files that use DTS only, or which simply have broken
PTS timestamps, would incorrectly drop frames and possibly not execute
the seek correctly.

Add yet another heuristic to detect this. The intent is that --start and
hr-seeks in general should work correctly, but in order to keep things
fast, we still want to allow frame dropping during hr-seek if there are
no problems doing so. Do this by disabling frame dropping by default,
but re-enabling it if there are no problems found for a while. As a
consequence, --start might be somewhat slower, but normal user
interaction should remain as fast as before.

Note that there's something subtle about the added code: the
has_broken_packet_pts field is checked even before the first packet is
fed to dec_video.c, so the field must not be set to 0 right on start.
It's not initially set to 0 anyway, because the heuristic requires
decoding some images before enabling frame drop anyway.

Note 2: it's not clear whether frame dropping during hr-seek really
helps; I didn't benchmark it.
2013-11-28 15:20:33 +01:00
Stefano Pigozzi e8677aa363 build: make --disable-gl disable all the gl backends
Fixes #369
2013-11-28 09:06:43 +01:00
Stefano Pigozzi 156dcbbeb1 build: add custom -I/-L flags for the BSDs [2]
Fixup commit. .append() seems to to nothing.
2013-11-28 09:06:18 +01:00
Stefano Pigozzi f10cca0e88 ao_coreaudio: simplify ch label to speaker id conversion
Previous code was using the values of the AudioChannelLabel enum directly to
create the channel bitmap. While this was quite smart it was pretty unreadable
and fragile (what if Apple changes the values of those enums?).

Change it to use a 'dumb' conversion table.
2013-11-27 23:15:17 +01:00
Stefano Pigozzi 69220aa1cd build: add custom -I/-L flags for the BSDs
Apparently this is needed for stuff like iconv.
2013-11-27 23:15:17 +01:00
Stefano Pigozzi b5e7169a0f old-build: fix iconv check on openbsd 2013-11-27 23:15:17 +01:00
wm4 6e2ac4d40a af_lavi: actually free the filter graph on uninit
This was a memory leak.

Also remove the AF_CONTROL_COMMAND_LINE code, which was inactive. (It's
never called if the new option parser is used.)
2013-11-27 21:14:39 +01:00
wm4 aa73ac8db8 video: replace d_video->pts field, change PTS jump checks
The d_video->pts field was a bit strange. The code overwrote it multiple
times (on decoding, on filtering, then once again...), and it wasn't
really clear what purpose this field had exactly. Replace it with the
mpctx->video_next_pts field, which is relatively unambiguous.

Move the decreasing PTS check to dec_video.c. This means it acts on
decoder output, not on filter output. (Just like in the previous commit,
assume the filter chain is sane.) Drop the jitter vs. reset semantics;
the dec_video.c determined PTS never goes backwards, and demuxer
timestamps don't "jitter".
2013-11-27 21:14:39 +01:00
wm4 5d97ac229a video: if PTS is missing, make something up using the framerate
Also get rid of the PTS check _after_ filters. This means if there's a
video filter which unsets PTS, no warning will be printed. But we assume
that all filters are well-behaved enough by now.
2013-11-27 21:14:39 +01:00
wm4 f5219720f8 video: refactor PTS code, add fall back heuristic to DTS
Refactor the PTS handling code to make it cleaner, and to separate the
bits that use PTS sorting.

Add a heuristic to fall back to DTS if the PTS us non-monotonic. This
code is based on what FFmpeg/Libav use for ffplay/avplay and also
best_effort_timestamp (which is only in FFmpeg). Basically, this 1. just
uses the DTS if PTS is unset, and 2. ignores PTS entirely if PTS is non-
monotonic, but DTS is sorted.

The code is pretty much the same as in Libav [1]. I'm not sure if all of
it is really needed, or if it does more than what the paragraph above
mentions. But maybe it's fine to cargo-cult this.

This heuristic fixes playback of mpeg4 in ogm, which returns packets
with PTS==DTS, even though the PTS timestamps should follow codec
reordering. This is probably a libavformat demuxer bug, but good luck
trying to fix it.

The way vd_lavc.c returns the frame PTS and DTS to dec_video.c is a bit
inelegant, but maybe better than trying to mess the PTS back into the
decoder callback again.

[1] https://git.libav.org/?p=libav.git;a=blob;f=cmdutils.c;h=3f1c667075724c5cde69d840ed5ed7d992898334;hb=fa515c2088e1d082d45741bbd5c05e13b0500804#l1431
2013-11-27 21:14:39 +01:00
wm4 1e96f5bcd9 Move some code from player to audio/video reset functions 2013-11-27 21:14:39 +01:00
wm4 f09b2ff661 cosmetics: rename video/audio reset functions
These used the suffix _resync_stream, which is a bit misleading. Nothing
gets "resynchronized", they really just reset state.

(Some audio decoders actually used to "resync" by reading packets for
resuming playback, but that's not the case anymore.)

Also move the function in dec_video.c to the top of the file.
2013-11-27 21:14:39 +01:00
wm4 f2b5267e88 video: remove commented code 2013-11-27 21:14:39 +01:00
wm4 55070ea85f video: use dts as fallback when determining pts by sorting
This makes the new code equivalent with the old one, which often passed
dts as pts. Also rename some variables to clear up things.
2013-11-27 21:13:42 +01:00
Stefano Pigozzi fb508105d1 ao_coreaudio: map channel labels needed for 8ch layouts
The code stopped at kAudioChannelLabel_TopBackRight and missed mapping for
5 more channel labels. These are in a completely different order that the mpv
ones so they must be mapped manually.
2013-11-27 00:51:48 +01:00
wm4 addfcf9ce3 audio: better rejection of invalid formats
This includes the case when lavc decodes audio with more than 8
channels, which our audio chain currently does not support.

the changes in ad_lavc.c are just simplifications. The code tried to
avoid overriding global parameters if it found something invalid, but
that is not needed anymore.
2013-11-27 00:16:05 +01:00
Natanael Copa ce1a511e22 video/filter: fix PIC compile on x86
When using PIC on x86 (eg with hardened toolchains) the ebx register is
reserverd and cannot be used in assembly code.

For vf_eq we allow the compiler to use memory as input.

For vf_noise we temoporarily borrow the ebp register.

This fixes #361.

Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
2013-11-26 23:43:56 +01:00
wm4 7a0299478e video: unbreak --no-correct-pts with demuxers that use DTS 2013-11-26 23:43:56 +01:00
wm4 f1eb30a476 demux_mkv: fix realvideo timestamp handling
This was broken by the recent commits. Apparently realvideo timestamps
are severely mangled, and Matroska _of course_ doesn't have the sane,
umangled timestamps, but something unusable. The existing unmangling
code in demux_mkv.c didn't output proper timestamps either. Instead,
it was something weird that triggered sorting. Without sorting (it was
disabled by default recently), you'd get decreasing PTS warnings

In order to fix this, steal some code from libavcodec. Basically copy
the contents of rv34_parser.c (with some changes), which makes
everything magically work. (Maybe it would be better to use the
libavcodec parser API, but I don't want to do that just for this. An
alternative idea would be refusing to read files that have realvideo
tracks, and delegate this to demux_lavf.c, but maybe that's too redical
too.)

I wish I hadn't notice this...
2013-11-26 23:43:56 +01:00
Stefano Pigozzi 27d578a847 bootstrap.py: reindent to 4 spaces 2013-11-26 21:35:37 +01:00
Stefano Pigozzi 54948f1b62 build: remove pointless exception handling in oss check 2013-11-26 21:35:37 +01:00
Stefano Pigozzi 803ff1a283 build: unbreak __get_osslibdir__ on python3
Fixes #370
2013-11-26 21:35:37 +01:00
Stefano Pigozzi 6ead6aa005 cocoa: unlock on uninit
NSLock should be unlocked before dealloc is called on it.
2013-11-26 20:50:35 +01:00
Stefano Pigozzi 4c634762b4 build: check for pthreads with and without additional cflags 2013-11-26 14:16:29 +01:00
Stefano Pigozzi ac245f96e4 av_common: fix typo in comment 2013-11-26 09:06:08 +01:00
Martin Herkt 7d152965ce ao_alsa: do not forcibly disable ALSA resampling
Resampling with non-ancient ALSA setups works fine, so there is no
need to keep this around. Furthermore, as of writing, the default
builtin resampler used by many ALSA setups (taken from libspeex)
actually has higher quality than the default resampling modes of
avresample and swresample.
2013-11-26 02:48:00 +01:00
wm4 8846a2f95c ad_lavc: increase number of packets for initial decode
Apparently just 5 packets is not enough for the initial audio decode
(which is needed to find the format). The old code (before the recent
refactor) appeared to use 5 packets, but there were apparently other
code paths which in the end amounted to more than 5 packets being read.

The sample that failed (see github issue #368) needed 9 packets.

Fixes #368.
2013-11-26 01:49:17 +01:00
wm4 bdd00d8b65 player: don't use a loop when no loop is needed
This used a really weird idiom: a loop that iterates only once, so you
can use break; to jump out of the block.
2013-11-26 01:11:33 +01:00
wm4 fe73b14eb1 player: move code for skipping 0-sized video packets to demuxer
These packets have to be explicitly dropped, because usually libavcodec
uses 0-sized packets to flush delayed frames, meaning just passing
through these packets would have bad consequences.

Normally, libavformat doesn't output 0-sized packets anyway. But I don't
want to take any chances, so don't delete it, and just move it out of
the way to demux.c.
2013-11-26 01:07:32 +01:00
Stefano Pigozzi 49f076d86d build: make cygwin use *nix timers 2013-11-26 00:33:40 +01:00
Stefano Pigozzi 58bf7fd1f3 build: add -mwin32 on cygwin 2013-11-26 00:33:40 +01:00
Stefano Pigozzi 88099c8617 build: add a gdi check for windows
Incidentally this seems wrong in the configure as well because only gl-win32
depends on it instead of also making direct3d depend on this (as I did here).
2013-11-26 00:33:39 +01:00
wm4 ce4d1f461a sub: respect detected language for fuzzy-matched external subtitles
Solve this by passing through the language to the player, which then
uses the generic subtitle selection code to make a choice.

Fixes #367.
2013-11-25 23:41:02 +01:00
wm4 78fa766fcc player: only pause for waiting on cache if it makes sense
If the value for --cache-on-pause is larger than --cache-min, and the
cache runs below --cache-on-pause, but above --cache-min, the logic
would demand to pause the player and then unpause immediately again.
This doesn't make much sense, and alternating the pause state in each
playloop iteration has negative consequences. Add an explicit check to
avoid this situation.
2013-11-25 23:24:50 +01:00
wm4 56d3ff33f1 video: move timestamp determination code to dec_video
This means the code that tries to figure out the timestamp from
demuxer and decoder output is now all in dec_video.c. We set the
final timestamp on the returned image (mp_image.pts), as well as
the d_video->pts field.

The way the player uses d_video->pts field is still a bit messy. Maybe
this could be cleaned up later.
2013-11-25 23:16:22 +01:00
wm4 b5b1692593 video: disable PTS sorting fallback by default
It appears PTS sorting was useful only for avi files (and VfW-muxed
mkv). Maybe it was historically also important for decoders with broken
or non-existent PTS reordering (win32 codecs?). But now that we handle
demuxers which outputs DTS only correctly, it just seems dead weight.

Disable it by default. The --pts-association-mode option is now forced
to always use the decoder's PTS value. You can still enable the old
default (auto) or force sorting. But we will probably remove this option
entirely at some point.

Make demux_mkv export timestamps at DTS when it's in VfW mode. This is
needed to get correct timestamps with the new default mode. demux_lavf
already does that.
2013-11-25 23:14:54 +01:00
wm4 8743d3fbfa demux_lavf: disable genpts by default, remove the builtin genpts hack
This was needed to determine PTS from DTS, but the previous commits
make it unnecessary.

The builtin genpts hack was used for DVD, because libavformat's genpts
essentially went amok on DVD timestamp resets. See commit 65d87091 for
details.
2013-11-25 23:13:46 +01:00
wm4 9f72a9753e demux: export dts from demux_lavf, use it for avi
Having the DTS directly can be useful for restoring PTS values.

The avi file format doesn't actually store PTS values, just DTS. An
older hack explicitly exported the DTS as PTS (ignoring the [I assume]
genpts generated non-sense PTS), which is not necessary anymore due to
this change.
2013-11-25 23:13:01 +01:00
wm4 d8b59aa17f player: merge no-correct-pts with correct-pts code
Now the --no-correct-pts mode is like the normal mode, just with
different timestamp calculations. The semantics should be about the
same as before this commit.
2013-11-25 23:12:18 +01:00
wm4 88fa420b20 player: change semantics of --no-correct-pts
Before this commit, this mode estimated the frame time by subtracting
successive packet PTS values. This is complete non-sense for video
codecs which use reordering. The code compensated frame times for these
non-sense using the FPS value, but confused the rest of the player with
non-sense jumping around timestamps. So, all in all this mode is not
very useful.

Repurpose this mode for fixed frame rate playback. This gives almost the
same behavior as the old mode with forced framerate (--fps option). The
result is simpler and often more robust.
2013-11-25 23:10:18 +01:00
wm4 83dc3a81f1 dec_video: fix function signature
Just why...? And why did this take 7 years?
2013-11-25 23:09:40 +01:00
wm4 4205bbf243 video: pass PTS as part of demux_packet/AVPacket and mp_image/AVFrame
Instead of passing the PTS as separate field, pass it as part of the
usual data structures. Basically, this removes strange artifacts from
the API. (It's not finished, though: the final decoded PTS goes through
strange paths, and filter_video() finally overwrites the decoded
mp_image's pts field with it.)

We also stop using libavcodec's reordered_opaque fields, and use
AVPacket.pts and AVFrame.pkt_pts. This is slightly unorthodox, because
these pts fields are not "really" opaque anymore, yet we treat them as
such. But the end result should be the same, and reordered_opaque is
marked as partially deprecated (it's not clear whether it's really
deprecated).
2013-11-25 23:08:29 +01:00
wm4 51bce52d57 player: warn if PTS association mode switches
This normally shouldn't happen. It does happen with VfW-muxed mkv files,
and would normally happen with avi files (except that we force the
correct association mode using an explicit hack for avi).

This is usually prepended by warnings like:

 Decreasing video pts: 0.125000 < 0.167000

and after the switch, there should be no warnings anymore.

Background: avi likes to use DTS for timestamps instead of PTS.
Basically, there are no proper timestamps in the file, only frame
numbers. And Matroska is insane and stores the made-up DTS instead
of a proper PTS. This will be handled properly later.
2013-11-25 23:07:22 +01:00
Stefano Pigozzi ded99df98a build: don't abort if our OSS implementation isn't from 4Front
Original commit was implemented differently by @bugmen0t. The problem here was
that the waf API was called directly, instead of using our own check_cc (which
defaults, among other things, to non mandatory checks).
2013-11-25 22:26:39 +01:00
Stefano Pigozzi ccb50bfdae build: run oss_audio.c check with oss cflags as well 2013-11-25 22:18:45 +01:00
bugmen0t defbe48dee build: unbreak __get_osslibdir__()
- without Utils.* always returns empty string
- subprocess doesn't need extra quoting for sh -c
- "source" is a bash'ism, not in POSIX sh
- most shell commands embed newline at the end
2013-11-25 22:16:57 +01:00
bugmen0t f26fb0098f build: only check 4Front OSS after passing its CFLAGS 2013-11-25 22:16:11 +01:00
Stefano Pigozzi 2aeeb28dd1 input: discard precise scrolling commands with no value
Apparently Cocoa precise scrolling generates a lot of spurious events with
a delta that is equal to 0.0. Make sure that they are discarded and not added
to the input queue.

Even though this only known to happen with Cocoa, I implemented this at core
level since it makes sense in general.

Fixes: #310
2013-11-25 19:35:52 +01:00
Natanael Copa 98fca35318 build: fix construction of args to pkg-config
This makes it work with pkgconf (https://github.com/pkgconf/pkgconf)

Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
2013-11-25 11:00:43 +01:00
Stefano Pigozzi 4aab1d23e6 build: work around bug in waf's bundle installation
waf wrongly installs resources to BundleRoot/Resources instead of
BundleRoot/Contents/Resources. Will post a patch to them later. Either way,
this must worked around until the next waf patch release.
2013-11-25 08:58:31 +01:00
Stefano Pigozzi 8b6e05adff build: add install_files call after the mpv target was defined
This makes it work on cold compiles when there is no `build/` directory and
one does ./waf configure && ./waf install (i.e.: no ./waf build).
2013-11-24 22:23:45 +01:00
eng 9d603e9985 bootstrap.py: check version if waf already exists 2013-11-24 22:14:39 +01:00
Stefano Pigozzi 92779408d3 build: also install mpv CLI binary when building OS X bundle 2013-11-24 22:10:04 +01:00
Stefano Pigozzi cda0222064 gitignore: add generated files by old build system 2013-11-24 18:53:18 +01:00
wm4 dc87c5e452 bootstrap.py: skip download if waf already exists
It seems like a good idea not to generate any additional network traffic
and wait times if we don't have to.

Also print the URL it's downloading from.

Note that if we require a newer waf release, there will be a problem.
Running ./bootstrap.py won't get the newest waf version anymore in case
the old version is in the source dir. Not sure how to handle this.
2013-11-24 17:00:26 +01:00
Stefano Pigozzi efa7f8f376 build: make waf append pkgconfig flags as-is [2]
fixup commit... removes a redundant `return`
2013-11-24 16:01:38 +01:00
Stefano Pigozzi d8f1a57876 build: make waf append pkgconfig flags as-is
waf apparently only appends a pkgconfig flag if it doesn't already exist in
the lib storage. Since our configure often checks for multiple libraries in
one call we want to keep the flags as is. This is especially important to
always keep stuff like -lm in the right place.
2013-11-24 15:57:49 +01:00
Stefano Pigozzi 2231d5e398 travis: remove --disable-doc from libav/ffmpeg configure 2013-11-24 14:47:33 +01:00
wm4 ae709b5c03 player: close demuxer before stream
Demuxer might access stream even when closing. For now, this is not
a real problem (because it didn't actually happen), but it's cleaner.
2013-11-24 14:44:58 +01:00
wm4 4012c4a96e osd: remove mp_osd_res.video_par field
This is not needed anymore, because we decided that the PAR of the
decoded video matters, and not the PAR of the filtered video that
arrives at the VO.
2013-11-24 14:44:58 +01:00
wm4 e5311586ab Rename sub.c/.h to osd.c/.h
This was way too misleading. osd.c merely calls the subtitle renderers,
instead of actually dealing with subtitles.
2013-11-24 14:44:58 +01:00
wm4 df8d00cc1f vd_lavc: improve a comment 2013-11-24 14:44:58 +01:00
Stefano Pigozzi 38b7eede1e build: store dependencies as lists
In Python sets are unordered, so iterating them after converting to a list
always leads to different results. The code iterated on them to collect all
the flags to pass to the compiler, and since the order of the flags changed,
waf would rebuild all of the C files. Seems like in Python 2 this worked as
expected by pure chance.

This commit stores the sets as lists, and converts them to sets when the set
operations are needed.

Fixes #363
2013-11-24 14:31:14 +01:00
Stefano Pigozzi 806b410c6a build: cache compiler defines on the configure context directly 2013-11-24 14:10:35 +01:00
Stefano Pigozzi aaa1b6f683 travis: disable e-mail notifications 2013-11-23 22:31:44 +01:00
wm4 5a3e01fa80 vd_lavc: when falling back to software, revert filter error status
When mpv is started with some video filters set (--vf is used), and
hardware decoding is requested, and hardware decoding would be possible,
but is prevented due to video filters that accept software formats only,
the fallback didn't work properly sometimes.

This fallback works rather violently: it tries to initialize the filter
chain, and if it fails it throws away the frame decoded using the
hardware, and retries with software. The case that didn't work was when
decoding the current packet didn't immediately lead to a new frame. Then
the filter chain wouldn't be reinitialized, and the playloop would stop
playback as soon as it encounters the error flag.

Fix this by resetting the filter error flag (back to "uninitialized"),
which is a rather violent, but somewhat working solution.

The fallback in general should perhaps be cleaned up later.
2013-11-23 22:28:39 +01:00
wm4 f99aff1b31 Reduce stheader.h includes, move stream types to mp_common.h 2013-11-23 22:08:42 +01:00
wm4 36744a30fb Attempt to fix build on older libavcodec versions 2013-11-23 22:08:18 +01:00
wm4 e901bb7c99 audio: respect --end/--length with spdif passthrough
In theory, we can't really do this, because we don't know when a spdif
frame ends. Spdif transports compressed audio through audio setups that
were originally designed for PCM only (which includes the audio filter
chain, the AO API, most audio output APIs, etc.), and to reach this
goal, spdif pretends to be PCM. Compressed data frames are padded with
zeros, until a certain data rate is reached, which corresponds to a
pseudo-PCM format with 2 bytes per sample and 2 channels at 48000 Hz.
Of course an actual spdif frame is significantly larger than a frame
of the PCM format it pretends to be, so cutting audio data on frame
boundaries (as according to the pseudo-PCM format) merely yields an
incomplete and broken frame, not audio that plays for the desired
duration.

However, sending an incomplete frame might still be much better than the
current behavior, which simply ignores --end/--length (but still lets
the video end at the exact end time).

Should this result in trouble with real spdif receivers, this commit
probably has to be reverted.
2013-11-23 21:42:31 +01:00
wm4 f90e7ef7ea video: don't overwrite demuxer FPS value
If the --fps option was given (MPOpts->force_fps), the demuxer FPS value
was overwritten with the forced value. This was fine, since the demuxer
value wasn't needed anymore. But with the recent changes not to write to
the demuxer stream headers, we don't want to do this anymore. So
maintain the (forced/updated) FPS value in dec_video->fps.

The removed code in loadfile.c is probably redundant, and an artifact
from past refactorings.

Note that sub.c will now always use the demuxer FPS value, instead of
the user override value. I think this is fine, because it used the
demuxer's video size values too. (And it's rare that these values are
used at all.)
2013-11-23 21:41:40 +01:00
wm4 de68b8f23c video: move handling of container vs. stream AR out of vd_lavc.c
Now the actual decoder doesn't need to care about this anymore, and it's
handled in generic code instead. This simplifies vd_lavc.c, and in
particular we don't need to detect format changes in the old way
anymore.
2013-11-23 21:40:51 +01:00
wm4 215b3cedda ao_rsound: fix option types
These are option values, and the option code expects char*.

Not actually tested.
2013-11-23 21:40:33 +01:00
wm4 4c2fb8f3a2 dec_video: make vf_input and hwdec_info statically allocated
The only reason why these structs were dynamically allocated was to
avoid recursive includes in stheader.h, which is (or was) a very central
file included by almost all other files. (If a struct is referenced via
a pointer type only, it can be forward referenced, and the definition of
the struct is not needed.) Now that they're out of stheader.h, this
difference doesn't matter anymore, and the code can be simplified.

Also sneak in some sanity checks.
2013-11-23 21:39:07 +01:00
wm4 02f96efc50 dec_video: remove "initialized" field
It's redundant.
2013-11-23 21:38:39 +01:00
wm4 904c73d2d2 demux: remove gsh field from sh_audio/sh_video/sh_sub
This used to be needed to access the generic stream header from the
specific headers, which in turn was needed because the decoders had
access only to the specific headers. This is not the case anymore, so
this can finally be removed again.

Also move the "format" field from the specific headers to sh_stream.
2013-11-23 21:37:56 +01:00
wm4 639e672bd1 player: rearrange how subtitle context and stream headers are used
Use sh_stream over sh_sub. Use dec_sub (and mpctx->d_sub) instead of the
stream header. This aligns the subtitle code with the recent audio and
video refactoring.

sh_sub still has the decoder context, though. This is because we want to
avoid reinit when switching segments with ordered chapters. (Reinit is
fast, except for creating the ASS_Renderer, which in turn triggers
fontconfig.) Not sure how much this matters, though, because the initial
segment switch will lazily initialize the decoder anyway.
2013-11-23 21:37:15 +01:00
wm4 3486302514 video: move decoder context from sh_video into new struct
This is similar to the sh_audio commit.

This is mostly cosmetic in nature, except that it also adds automatical
freeing of the decoder driver's state struct (which was in
sh_video->context, now in dec_video->priv).

Also remove all the stheader.h fields that are not needed anymore.
2013-11-23 21:36:20 +01:00
wm4 057af4697c options: print lavfi filter list with --vf=lavfi=help 2013-11-23 21:35:52 +01:00
wm4 e99ae17a80 options: don't prefix sub-options with "--" in help output
Commit 0cb9227a added this to the option list help output, but it looks
strange with sub-options.
2013-11-23 21:35:03 +01:00
wm4 b5ed614839 options: implement --pphelp differently
Make it work via --vf=pp:help instead.
2013-11-23 21:34:24 +01:00
wm4 25855059af video: remove vf_pp auto-insertion
This drops the --pp option, which was probably broken for a while. The
option automatically inserted the "pp" filter. The value passed to it
was ignored (which is probably broken, it always selected maximal
quality).

Inserting this filter can be done simply with --vf=pp, so this is not
needed anymore.
2013-11-23 21:30:56 +01:00
wm4 8260590346 options: provide a way for --vf to print custom help
Useful in rather special situations. See following commits.
2013-11-23 21:29:05 +01:00
wm4 acfeb869a3 video: merge vd.c into dec_video.c
I don't feel like the separation ever made sense, and it was hard to
tell which file a function you were looking for was in.
2013-11-23 21:28:28 +01:00
wm4 4fa2babacc video: move struct mp_hwdec_info into its own header file
This means most code accessing this struct must now include hwdec.h
instead of dec_video.h. I just put it into dec_video.h at first because
I thought a separate file would be a waste, but it's more proper to do
it this way, as there are too many files which include dec_video.h only
to get the mp_hwdec_info definition.
2013-11-23 21:26:31 +01:00
wm4 9f4820f6ec audio: remove ad_driver.preinit
This never had any real use. Get rid of dec_audio.initialized too, as
it's redundant.
2013-11-23 21:26:04 +01:00
wm4 0e84dafdf0 player: remove printing of barely correct slave mode stream info
This was printed before something was decoded, and thus was not really
correct. Also, this code is hilariously broken:

        /* Assume FOURCC if all bytes >= 0x20 (' ') */
        if (sh_audio->format >= 0x20202020)
            mp_msg(MSGT_IDENTIFY, MSGL_INFO,
                   "ID_AUDIO_FORMAT=%.4s\n", (char *)&sh_audio->format);

Time to kill it.

This information can be accessed through properties instead.
2013-11-23 21:25:32 +01:00
wm4 e174d31fdd audio: don't write decoded audio format to sh_audio
sh_audio is supposed to contain file headers, not whatever was decoded.
Fix this, and write the decoded format to separate fields in the decoder
context, the dec_audio.decoded field. (Note that this field is really
only needed to communicate the audio format from decoder driver to the
generic code, so no other code accesses it.)
2013-11-23 21:25:05 +01:00
wm4 0f5ec05d8f audio: move decoder context from sh_audio into new struct
Move all state that basically changes during decoding or is needed in
order to manage decoding itself into a new struct (dec_audio).

sh_audio (defined in stheader.h) is supposed to be the audio stream
header. This should reflect the file headers for the stream. Putting the
decoder context there is strange design, to say the least.
2013-11-23 21:22:17 +01:00
Stefano Pigozzi 705a7310e6 build: remove unused mng fragment 2013-11-23 16:17:53 +01:00
Stefano Pigozzi 55ce26e454 build: also run the test binary during the lua checks 2013-11-23 16:17:53 +01:00
Stefano Pigozzi f315c93302 build: fix lua check to actually test for libquvi 2013-11-23 16:17:52 +01:00
Stefano Pigozzi 26f8887422 build: fix missing prototypes in lua check fragment 2013-11-23 16:17:52 +01:00
Stefano Pigozzi 22d8cdfe90 build: use static instead of prototype in libavfilter fragment 2013-11-23 16:17:52 +01:00
Stefano Pigozzi 3a0ce6acb9 build: fix libavfilter check to include function prototype
Apparently the check error'ed on some compilers for missing prototype and
simply adding one fixes it.
2013-11-23 14:55:55 +01:00
Stefano Pigozzi 60521783c3 build: fix install path for manual
Appending man1 to the MANDIR was forgotten, so the manual was installed in the
wrong path.
2013-11-23 11:35:16 +01:00
Stefano Pigozzi add439b06b README: mention build result being located at build/mpv 2013-11-23 11:14:22 +01:00
Stefano Pigozzi bc38a2736d build: make sure cwd is in Python's sys.path
Apparently some packaging systems (homebrew does this for example) change
`sys.path` before they run any Python script to ensure that only the correct
Python modules can be loadable.

We need to make sure cwd is in `sys.path` since we need to load
`wscript_build.py` from there.
2013-11-23 10:58:19 +01:00
Stefano Pigozzi 1748928e45 mp_ring: fix comment typo 2013-11-22 19:12:43 +01:00
Stefano Pigozzi a8976464ca build: remove abusive comment
The check seems to be working anyway, even without sys/video.h. So ./configure
was maybe wrong.
2013-11-22 19:12:43 +01:00
Stefano Pigozzi 7475f7d957 build: remove spam generated by link task
Simply override the cprogram Task's __str__ method with our own implementation.
This is way easier on the eyes when compiling mpv during development, since
warnings are not pushed outside of your average screenful of content.
2013-11-22 19:12:43 +01:00
wm4 20d354cc0c vo_opengl: fix compilation
Never do a trivial change while drunk and without actually testing it.
2013-11-22 19:08:14 +01:00
wm4 de22d2b1ba vf_vavpp: make it work with vo_opengl and software decoding
vo_opengl always loads the hwdec backend lazily, so hwdec_request_api()
has to be called to possibly load it. This makes vf_vavpp work with
software decoding. (Hardware decoding loads the backend before the
filter is initialized, so this case is different.)

Also, the VFCTRL_GET_HWDEC_INFO call doesn't need to be checked. If it
fails, the info will be left blank.
2013-11-22 18:06:34 +01:00
wm4 0619272767 vo_opengl: initialize all fields for VFCTRL_GET_HWDEC_INFO
This initialized only the load_api and load_api_ctx fields, and left the
other fields as they were. This failed with vf_vavpp, which assumed all
fields are initialized.
2013-11-22 18:06:14 +01:00
wm4 f5eed454d4 build: unbreak PVR configure test 2013-11-22 14:55:19 +01:00
enkore 87a7886005 manpage: document , mapping (frame_back_step)
See #356
2013-11-22 13:07:36 +01:00
wm4 f405a468ae input.conf: fix typo 2013-11-22 13:00:26 +01:00
Josh Driver 5e6d9998f5 Fixing list of vo's that vavpp works with, in man page. 2013-11-22 11:39:26 +01:00
Stefano Pigozzi 3f594c2e84 cocoa: use window-scale to support video scaling functionality
In the cocoa backend you can use cmd+0/1/2 to scale the window. This commit
makes it use the new window-scale functionality.
2013-11-22 08:43:02 +01:00
Stefano Pigozzi ede608ed43 cocoa: implement window-scale 2013-11-22 08:41:34 +01:00
Stefano Pigozzi 7e2edad8ef switch the build system to waf
This commit adds a new build system based on waf. configure and Makefile
are deprecated effective immediately and someday in the future they will be
removed (they are still available by running ./old-configure).

You can find how the choice for waf came to be in `DOCS/waf-buildsystem.rst`.
TL;DR: we couldn't get the same level of abstraction and customization with
other build systems we tried (CMake and autotools).

For guidance on how to build the software now, take a look at README.md
and the cross compilation guide.

CREDITS:
This is a squash of ~250 commits. Some of them are not by me, so here is the
deserved attribution:

 - @wm4 contributed some Windows fixes, renamed configure to old-configure
   and contributed to the bootstrap script. Also, GNU/Linux testing.
 - @lachs0r contributed some Windows fixes and the bootstrap script.
 - @Nikoli contributed a lot of testing and discovered many bugs.
 - @CrimsonVoid contributed changes to the bootstrap script.
2013-11-21 21:22:36 +01:00
wm4 0cb9227a73 options: prefix options with "--" in help output
This is Better(tm).
2013-11-21 16:19:23 +01:00
wm4 5dca96c1a4 stream_lavf: fix a small memory leak
Also add an explanation why this special code path for rtsp is needed.
2013-11-21 16:16:08 +01:00
wm4 d585382f0e timeline: reject mplayer2 EDL files, change EDL header
This was forgotten when the parser for mplayer2 EDL files was removed.

Change the header of the mpv EDL format to include a '#', so a naive
parser could skip the header as comment. (Maybe this is questionable;
on the other hand, if it can be simpler, why not.)

Also, strip the header in demux_edl.c before passing on the data, so the
header check doesn't need to be duplicated in tl_mpv_edl.c.
2013-11-21 15:59:00 +01:00
wm4 287d2e0603 tech-overview.txt: minor update 2013-11-20 18:16:20 +01:00
wm4 801ff8319f ta: fix typo in comment 2013-11-20 18:13:05 +01:00
wm4 31fc48f0a8 osdep/io.c: include config.h
This possibly enables code that has never been tested before
(accidentally), so let's hope this works out ok.
2013-11-20 18:12:58 +01:00
wm4 702962878b mplayer: fix passing size_t as %d to printf() 2013-11-20 18:12:14 +01:00
wm4 52467b2ac9 timeline: remove support for the mplayer2 EDL format
It was a bit too complicated and inconvenient, and I doubt anyone
actively used it. The mpv EDL format should cover all use cases.
2013-11-19 22:44:08 +01:00
wm4 f197198ca3 player: add --merge-files option 2013-11-19 22:39:14 +01:00
wm4 04bdd7af72 timeline: add edl:// URIs
Questionable change from user perspective, but internally needed to
implement the next commit. Also useful for testing timeline stuff.
2013-11-19 22:39:04 +01:00
wm4 50837129b2 timeline: add new EDL format
Edit Decision Lists (EDL) allow combining parts from multiple source
files into one virtual file. MPlayer had an EDL format (which sucked),
which mplayer2 tried to improve with its own format (which sucked). As
logic demands, mpv introduces its very own format (which sucks).

The new format should actually be much simpler and easier to use, and
its implementation is simpler and smaller too.
2013-11-19 22:38:27 +01:00
wm4 469e488308 manpage: fix typo in --video-align-y description 2013-11-19 22:20:09 +01:00
wm4 b0c75a1ff9 player: select fallback stream in timeline code for better EDL handling
The intention of the existing code was trying to match demuxer-reported
stream IDs, instead of using possibly arbitrary ordering of the frontend
track list. But EDL files can consist of quite different files, for
which trying to match the stream IDs doesn't always make sense.
2013-11-19 22:18:56 +01:00
wm4 904ae96795 player: deselect video track if initialization fails
This didn't have any consequences, other than suddenly reinitializing
video when it works again (such as with EDL timeline mixing video and
audio-only files).
2013-11-19 22:16:56 +01:00
wm4 16233bc546 vdpau_old: enable OpenGL interop
OpenGL interop was essentially disabled, because the decoder didn't
request vdpau device creation from vo_opengl.
2013-11-19 22:15:28 +01:00
wm4 b14a7da5d4 ao_null: fix simulated buffer size
The size accidentally defaulted to 200 seconds instead of 200
milliseconds, which had fatal consequences when trying to use it.
2013-11-19 22:14:23 +01:00
wm4 85f6349c78 audio/filter: rename af_tools.c to tools.c
This always bothered me.
2013-11-18 18:48:00 +01:00
wm4 82068ec56c demux: rename demux_packet.h to packet.h
This always bothered me.
2013-11-18 18:46:44 +01:00
wm4 d5bc4ee798 audio: drop buffered filter data when seeking
This could lead to (barely) audible artifacts with --af=scaletempo and
modified playback speed.
2013-11-18 14:21:01 +01:00
wm4 5594718b6b audio/filter: remove unneeded AF_CONTROLs, convert to enum
The AF control commands used an elaborate and unnecessary organization
for the command constants. Get rid of all that and convert the
definitions to a simple enum. Also remove the control commands that
were not really needed, because they were not used outside of the
filters that implemented them.
2013-11-18 14:21:01 +01:00
wm4 93852b08f3 af: cleanup documentation comments
And by "cleanup", I mean "remove". Actually, only remove the parts that
are redundant and doxygen noise. Move useful parts to the comment above
the function's implementation in the C source file.
2013-11-18 14:21:01 +01:00
wm4 75dd3ec210 player: simplify audio reset when seeking
Some decoders used to read packets and decode data when calling
resync_audio_stream(). This required a special case in mp_seek() for
audio. (A comment mentions liba52, which is long gone; but until
recently ad_mpg123.c actually exposed this behavior.)

No decoder does this anymore, and resync_audio_stream() works similar
as resync_video_stream(). Remove the special case.
2013-11-18 14:21:01 +01:00
wm4 03f93dc7a2 stream_dvb: remove bogus free calls
The priv struct is now allocated by talloc in stream.c. It doesn't need
to be manually freed, and using free() instead of talloc_free() probably
crashes.
2013-11-18 14:21:01 +01:00
wm4 b1405f3cff vo_vdpau: don't calculate destination alpha when drawing OSD
Same as MPlayer svn commit r36515 "Chose cheaper alpha blend equation."

No idea if this is actually faster, but can't hurt.
2013-11-18 14:21:01 +01:00
wm4 1151dac5f0 audio: use the decoder buffer's format, not sh_audio
When the decoder detects a format change, it overwrites the values
stored in sh_audio (this affects the members sample_format, samplerate,
channels). In the case when the old audio data still needs to be
played/filtered, the audio format as identified by sh_audio and the
format used for the decoder buffer can mismatch. In particular, they
will mismatch in the very unlikely but possible case the audio chain is
reinitialized while old data is draining during a format change.

Or in other words, sh_audio might contain the new format, while the
audio chain is still configured to use the old format.

Currently, the audio code (player/audio.c and init_audio_filters) access
sh_audio to get the current format. This is in theory incorrect for the
reasons mentioned above. Use the decoder buffer's format instead, which
should be correct at any point.
2013-11-18 14:21:00 +01:00
wm4 8f1151a00e audio: fix mid-stream audio reconfiguration
Commit 22b3f522 not only redid major aspects of audio decoding, but also
attempted to fix audio format change handling. Before that commit, data
that was already decoded but not yet filtered was thrown away on a
format change. After that commit, data was supposed to finish playing
before rebuilding filters and so on.

It was still buggy, though: the decoder buffer was initialized to the
new format too early, triggering an assertion failure. Move the reinit
call below filtering to fix this.

ad_mpg123.c needs to be adjusted so that it doesn't decode new data
before the format change is actually executed.

Add some more assertions to af_play() (audio filtering) to make sure
input data and configured format don't mismatch. This will also catch
filters which don't set the format on their output data correctly.

Regression due to planar_audio branch.
2013-11-18 14:20:59 +01:00
wm4 b78d11d328 stream: split out pthread helper function
Also split the function itself into 3.
2013-11-17 16:42:57 +01:00
wm4 2556f45f2e af_lavrresample: set cutoff as double, not int
Regression introduced with commit a89549e8.
2013-11-17 16:22:35 +01:00
wm4 8438c557cf player: write final audo chunk only if there is audio left
Don't call ao_play() if there's nothing left. Of course this still asks
the AO to play internally buffered audio by setting drain=true.
2013-11-17 16:22:32 +01:00
wm4 e403140201 ao_null: properly simulate final chunk, add buffer options
Simulate proper handling of AOPLAY_FINAL_CHUNK. Print when underruns
occur (i.e. running out of data). Add some options that control
simulated buffer and outburst sizes.

All this is useful for debugging and self-documentation. (Note that
ao_null always was supposed to simulate an ideal AO, which is the reason
why it fools people who try to use it for benchmarking video.)
2013-11-17 16:22:25 +01:00
wm4 b3eed7374e player: don't remove playback status when reinitializing DVB
Also break that line a bit.
2013-11-17 16:22:14 +01:00
wm4 fde0e9e84d demux_packet: add source stream index
Might be helpful later.
2013-11-16 21:46:17 +01:00
wm4 5957f828b0 demux: update a comment 2013-11-16 21:46:17 +01:00
wm4 2ea23fd107 demux: remove unused commands
These were replaced with DEMUXER_CTRL_SWITCHED_TRACKS a while ago.
2013-11-16 21:46:17 +01:00
wm4 a2a24b957e demux: simplify handling of filepos field
demuxer->filepos contains the byte offset of the last read packet. This
is so that the player can estimate the current playback position, if no
proper timestamps are available. Simplify it to use demux_packet->pos in
the generic demuxer code, instead of bothering every demuxer
implementation about it.

(Note that this is still a bit incorrect: it relfects the position of
the last packet read by the demuxer, not that returned to the user. But
that was already broken, and is not that trivial to fix.)
2013-11-16 21:46:17 +01:00
wm4 0cdbc6db6e demux_lavf: remove broken and commented byte based seeks
This was originally added for better seeking where libavformat's seek
function won't work well: files with timestamp resets. In these cases,
the code tried to calculate an average bitrate, and then do byte based
seeks by multiplying the seek target time with the bitrate.

Apparently this was unreliable enough that the code was just commented
(and other parts became inactive). Get rid of it.

Note that the player still does byte based seeks in these cases when
doing percent-seeks.
2013-11-16 21:46:17 +01:00
wm4 7fbf87e615 demux: reset EOF flag differently
This should be almost equivalent, but is slightly better because the
EOF flag is reset earlier.
2013-11-16 21:46:17 +01:00
wm4 8cc44a64e8 encode_lavc: add missing newline in error message 2013-11-16 21:46:17 +01:00
wm4 ca455e65a3 ao_lavc: use af_format_conversion_score()
This should allow it to select better fallback formats, instead of
picking the first encoder sample format if ao->format is not equal to
any of the encoder sample formats.

Not sure what is supposed to happen if the encoder provides no
compatible sample format (or no sample format list at all), but in this
case ao_lavc.c still fails gracefully.
2013-11-16 21:46:17 +01:00
wm4 3f7e1f0492 audio/format: add heuristic to estimate loss on format conversion
The added function af_format_conversion_score() can be used to select
the best sample format to convert to in order to reduce loss and extra
conversion work.

It calculates a "loss" score when going from one format to another, and
for each conversion that needs to be done a certain score is subtracted.
Thus, if you have to convert from one format to a set of other formats,
you can calculate the score for each conversion, and pick the one with
the highest score.

Conversion between int and float is considered the worst case. One odd
consequence is that when converting from s32 to u8 or float, u8 will be
picked.

Test program used to develop this follows:

#define MAX_FMT 200
struct entry {
    const char *name;
    int score;
};

static int compentry(const void *px1, const void *px2)
{
    const struct entry *x1 = px1;
    const struct entry *x2 = px2;
    if (x1->score > x2->score)
        return 1;
    if (x1->score < x2->score)
        return -1;
    return 0;
}

int main(int argc, char *argv[])
{
    for (int n = 0; af_fmtstr_table[n].name; n++) {
        struct entry entry[MAX_FMT];
        int entries = 0;
        for (int i = 0; af_fmtstr_table[i].name; i++) {
            assert(i < MAX_FMT);
            entry[entries].name = af_fmtstr_table[i].name;
            entry[entries].score =
                af_format_conversion_score(af_fmtstr_table[i].format,
                                           af_fmtstr_table[n].format);
            entries++;
        }
        qsort(&entry[0], entries, sizeof(entry[0]), compentry);
        for (int i = 0; i < entries; i++) {
            printf("%s -> %s: %d \n", af_fmtstr_table[n].name,
                   entry[i].name, entry[i].score);
        }
    }
}
2013-11-16 21:46:17 +01:00
wm4 0ed0f4d33a audio/format: fix doublep sample format
This was accidentally equivalent to floatp.
2013-11-16 21:46:16 +01:00
Rudolf Polzer 6391453fab ao_lavc: write the final audio chunks from uninit()
These must be written even if there was no "final frame", e.g. due to
the player being exited with "q".

Although the issue is mostly of theoretical nature, as most audio codecs
don't need the final encoding calls with NULL data. Maybe will be more
relevant in the future.
2013-11-16 18:50:07 +01:00
Rudolf Polzer 0d4628a7fd ao_lavc: fix crash with interleaved audio outputs. 2013-11-16 14:10:00 +01:00
wm4 514c454770 audio: drop "_NE"/"ne" suffix from audio formats
You get the native format by not appending any suffix to the format.

This change includes user-facing names, e.g. for the --format option.
2013-11-15 21:25:05 +01:00
wm4 2289a479b1 manpage: mark DTS-HD passthough as broken 2013-11-15 21:13:03 +01:00
wm4 3ded03b1f9 dec_audio: adjust "large" decoding amount
This used to be in bytes, now it's in samples. Divide the value by 8
(assuming a typical audio format, float samples with 2 channels).

Fix some editing mistake or non-sense about the extra buffering added
(1<<x instead of x<<5).

Also sneak in a s/MPlayer/mpv/.
2013-11-15 21:12:01 +01:00
wm4 a9d98082aa mp_ring: remove unused function
This was needed for ao_jack.c., but not anymore.
2013-11-15 21:08:48 +01:00
wm4 7f7e9a9fff af_lavcac3enc: use option parser
This changes option parsing as well as filter defaults slightly. The
default is now to encode to spdif (this is way more useful than writing
raw AC3 - what was this even useful for, other than writing broken ac3
-in-wav files?). The bitrate parameter is now always in kbps.
2013-11-15 00:24:03 +01:00
wm4 8512a08046 ad_spdif: fix regressions
Apparently this was completely broken after commit 22b3f522. Basically,
this locked up immediately completely while decoding the first packet.
The reason was that the buffer calculations confused bytes and number of
samples. Also, EOF reporting was broken (wrong return code).

The special-casing of ad_mpg123 and ad_spdif (with DECODE_MAX_UNIT) is a
bit annoying, but will eventually be solved in a better way.
2013-11-14 23:54:06 +01:00
Stefano Pigozzi 4ee51526ae osx bundle: remove embedded fonts.conf
This could cause the bundle to recache stuff because of differences with
configuration of other software using fonconfig. The defaults OS X directories
should be added to fontconfig at build time (through configure).
2013-11-14 21:23:47 +01:00
wm4 53c6d97873 ao_alsa: non-interleaved access is not always available
I thought this would always work... how disappointing.

Revert to interleaved format if requesting non-interleaved fails.
2013-11-14 21:19:04 +01:00
wm4 e91edf9aed demux: use talloc for certain stream headers
Slightly simplifies memory management. This might make adding a demuxer
cache wrapper easier at a later point, because you can just copy the
complete stream header, without worrying that the wrapper will free the
individual stream header fields.
2013-11-14 19:52:18 +01:00
wm4 d0346e087a audio: fix audio data memory leak
Practically all audio decoding and filtering code leaked sample data
memory after uninitialization due to a simple logic bug (or typo).
2013-11-14 19:51:42 +01:00
wm4 10bcab6bc1 gl_common: print SW renderer warning only if it was the only reason we rejected it 2013-11-14 19:51:42 +01:00
wm4 467ad4413e vd_lavc: select correct hw decoder profile for constrained baseline h264
The existing code tried to remove the "extra" profile flags for h264.

FF_PROFILE_H264_INTRA doesn't matter for us at all, because it's set
only for profiles the vdpau/vaapi APIs don't support.

The FF_PROFILE_H264_CONSTRAINED flag on the other hand is added to
H264_BASELINE, except that it makes the file a real subset of H264_MAIN
and H264_HIGH. Removing that flag would select the BASELINE profile,
which appears to be rarely supported by hardware decoders. This means we
accidentally rejected perfectly hardware decodable files. Use MAIN for
it instead.

(vaapi has explicit support for CONSTRAINED_BASELINE, but it seems to be
a new thing, and is not reported as supported where I tried. So don't
bother to check it, and do the same as on vdpau.)

See github issue #204.
2013-11-14 19:51:42 +01:00
wm4 597a143ec6 gl_common: remove unneeded callback
We got rid of this some time ago, but apparently not completely.
2013-11-14 19:51:40 +01:00
wm4 2b39c5d87c tvi_v4l2: remove VBI stuff
This used to be needed for teletext support. Teletext commit has been
removed (see commit ebaaa41f), and it appears this code is inactive.
It was just forgotten with the removal. Get rid of it completely.

Untested. (Like all changes to the TV code.)
2013-11-13 21:21:00 +01:00
bugmen0t 417fa2ffec configure: enable v4l2 input on freebsd 2013-11-13 21:16:14 +01:00
bugmen0t 35d7ed7bf1 tvi_v4l2: let libv4l2 convert to a known pixel format
Signed-off-by: wm4 <wm4@nowhere>

Significant modifications over the original patch by not overriding
syscalls with macros ("#define open v4l2open") for fallback, but the
other way around ("#define v4l2open open"). As consequence, the calls
have to be replaced throughout the file.

Untested, although the original patch probably was tested.
2013-11-13 21:15:59 +01:00
wm4 05e2b1f513 stream: don't include linux/types.h in some files
Apparently this is not portable to FreeBSD. It turns out that we
(probably) don't use any symbols defined by this header directly, so
the includes are not needed.
2013-11-13 20:59:50 +01:00
wm4 8444c916d4 m_option: handle audio/filter filters with old option parsing
These use the _oldargs_ hack, which failed in combination with playback
resume. Make it work.

It would be better to port all filters to new option parsing, but that's
obviously too much work, and most filters will probably be deleted and
replaced by libavfilter in the long run.
2013-11-13 20:10:17 +01:00
wm4 e5fec0ad07 ao_null: add untimed sub-option 2013-11-13 20:10:17 +01:00
wm4 621cff80df ao_null: support pausing properly
ao_null should simulate a "perfect" AO, but framestepping behaved quite
badly with it. Framstepping usually exposes problems with AOs dropping
their buffers on pause, and that's what happened here.
2013-11-13 20:10:17 +01:00
wm4 894bf603e8 mf: silence compilation warning 2013-11-13 20:10:17 +01:00
wm4 933fbf7333 ao_lavc: support non-interleaved audio 2013-11-13 20:10:17 +01:00
Alexander Preisinger 95ed81c329 wayland: create xkbcommon keymap from string
Fixes a problem where the passed size doesn't match the actuall string.
2013-11-13 00:18:20 +01:00
wm4 e4bbb1d348 Merge branch 'planar_audio'
Conflicts:
	audio/out/ao_lavc.c
2013-11-12 23:42:04 +01:00
wm4 22b3f522ca audio: add support for using non-interleaved audio from decoders directly
Most libavcodec decoders output non-interleaved audio. Add direct
support for this, and remove the hack that repacked non-interleaved
audio back to packed audio.

Remove the minlen argument from the decoder callback. Instead of
forcing every decoder to have its own decode loop to fill the buffer
until minlen is reached, leave this to the caller. So if a decoder
doesn't return enough data, it's simply called again. (In future, I
even want to change it so that decoders don't read packets directly,
but instead the caller has to pass packets to the decoders. This fits
well with this change, because now the decoder callback typically
decodes at most one packet.)

ad_mpg123.c receives some heavy refactoring. The main problem is that
it wanted to handle format changes when there was no data in the decode
output buffer yet. This sounds reasonable, but actually it would write
data into a buffer prepared for old data, since the caller doesn't know
about the format change yet. (I.e. the best place for a format change
would be _after_ writing the last sample to the output buffer.) It's
possible that this code was not perfectly sane before this commit,
and perhaps lost one frame of data after a format change, but I didn't
confirm this. Trying to fix this, I ended up rewriting the decoding
and also the probing.
2013-11-12 23:39:09 +01:00
wm4 5388a0cd40 ad_mpg123: reduce ifdeffery
Drop support for anything before 1.14.0.
2013-11-12 23:38:52 +01:00
wm4 9127aad2fd dec_audio: fix behavior on format changes
Decoder overwrites parameters in sh_audio, but we still have old audio
in the old format to filter.
2013-11-12 23:38:36 +01:00
wm4 cc5083cfe0 mp_audio: use av_malloc (cargo cult for libav*)
libav* is generally freaking horrible, and might do bad things if the
data pointer passed to it are not aligned. One way to be sure that the
alignment is correct is allocating all pointers using av_malloc().

It's possible that this is not needed at all, though. For now it might
be better to keep this, since the mp_audio code is intended to replace
another buffer in dec_audio.c, which is currently av_malloc() allocated.
The original reason why this uses av_malloc() is apparently because
libavcodec used to directly encode into mplayer buffers, which is not
the case anymore, and thus (probably) doesn't make sense anymore.

(The commit subject uses the word "cargo cult", after all.)
2013-11-12 23:35:33 +01:00
William Light e1656d369a ao_jack: switch from interleaved to planar audio 2013-11-12 23:35:12 +01:00
William Light 4bd690c998 ao_jack: refactoring, also fix "no-connect" option 2013-11-12 23:35:04 +01:00
wm4 6f557aef42 af_lavcac3enc: use planar formats
Remove the awkward planarization. It had to be done because the AC3
encoder requires planar formats, but now we support them natively.

Try to simplify buffer management with mp_audio_buffer.

Improve checking for buffer overflows and out of bound writes. In
theory, these shouldn't happen due to AC3 fixed frame sizes, but being
paranoid is better.
2013-11-12 23:34:49 +01:00
wm4 a72072c605 af_lavcac3enc: simplify format negotiation
The format negotiation is the same, except don't confusingly copy the
input format into af->data, just to overwrite it later. af->data should
alwass contain the output format, and the existing code was just a very
misguided use of the af_test_output() helper function.

Just set af->data to the output format immediately, and modify the input
format properly.

Also, if format negotiation fails (and needs another iteration), don't
initialize the libavcodec encoder.
2013-11-12 23:34:37 +01:00
wm4 824e6550f8 audio/filter: fix mul/delay scale and values
Before this commit, the af_instance->mul/delay values were in bytes.
Using bytes is confusing for non-interleaved audio, so switch mul to
samples, and delay to seconds. For delay, seconds are more intuitive
than bytes or samples, because it's used for the latency calculation.
We also might want to replace the delay mechanism with real PTS
tracking inside the filter chain some time in the future, and PTS
will also require time-adjustments to be done in seconds.

For most filters, we just remove the redundant mul=1 initialization.
(Setting this used to be required, but not anymore.)
2013-11-12 23:34:35 +01:00
wm4 7510caa0c5 ao_openal: support non-interleaved output
Since ao_openal simulates multi-channel audio by placing a bunch of
mono-sources in 3D space, non-interleaved audio is a perfect match for
it. We just have to remove the interleaving code.
2013-11-12 23:30:37 +01:00
wm4 dab6eaaa5e ao_alsa: support non-interleaved audio
ALSA supports non-interleaved audio natively using a separate API
function for writing audio. (Though you have to tell it about this on
initialization.) ALSA doesn't have separate sample formats for this,
so just pretend to negotiate the interleaved format, and assume that
all non-interleaved formats have an interleaved companion format.
2013-11-12 23:30:25 +01:00
wm4 fedb9229d5 ao_null: support non-interleaved audio
Simply change internals from using byte counts to sample counts.
2013-11-12 23:30:10 +01:00
wm4 347a86198b audio: switch output to mp_audio_buffer
Replace the code that used a single buffer with mp_audio_buffer. This
also enables non-interleaved output operation, although it's still
disabled, and no AO supports it yet.
2013-11-12 23:29:53 +01:00
wm4 d1ee9ea261 audio: add mp_audio_buffer
Implementation wise, this could be much improved, such as using a
ringbuffer that doesn't require copying data all the time. This is
why we don't use mp_audio directly instead of mp_audio_buffer.
2013-11-12 23:28:21 +01:00
wm4 380fc765e4 audio/out: prepare for non-interleaved audio
This comes with two internal AO API changes:

1. ao_driver.play now can take non-interleaved audio. For this purpose,
the data pointer is changed to void **data, where data[0] corresponds to
the pointer in the old API. Also, the len argument as well as the return
value are now in samples, not bytes. "Sample" in this context means the
unit of the smallest possible audio frame, i.e. sample_size * channels.

2. ao_driver.get_space now returns samples instead of bytes. (Similar to
the play function.)

Change all AOs to use the new API.

The AO API as exposed to the rest of the player still uses the old API.
It's emulated in ao.c. This is purely to split the commits changing all
AOs and the commits adding actual support for outputting N-I audio.
2013-11-12 23:27:51 +01:00
wm4 d115fb3b0e af: don't require filters to allocate af_instance->data, redo buffers
Allocate af_instance->data in generic code before filter initialization.
Every filter needs af->data (since it contains the output
configuration), so there's no reason why every filter should allocate
and free it.

Remove RESIZE_LOCAL_BUFFER(), and replace it with mp_audio_realloc_min().
Interestingly, most code becomes simpler, because the new function takes
the size in samples, and not in bytes. There are larger change in
af_scaletempo.c and af_lavcac3enc.c, because these had copied and
modified versions of the RESIZE_LOCAL_BUFFER macro/function.
2013-11-12 23:27:03 +01:00
wm4 e763d528e2 af_lavfi: add support for non-interleaved audio 2013-11-12 23:16:31 +01:00
wm4 4f31d56eb1 af_volume: add support for non-interleaved audio 2013-11-12 23:16:31 +01:00
wm4 45d1510e4e af_lavrresample: add support for non-interleaved audio 2013-11-12 23:16:31 +01:00
wm4 bf60281ffb audio/out: reject non-interleaved formats
No AO can handle these, so it would be a problem if they get added
later, and non-interleaved formats get accepted erroneously. Let them
gracefully fall back to other formats.

Most AOs actually would fall back, but to an unrelated formats. This is
covered by this commit too, and if possible they should pick the
interleaved variant if a non-interleaved format is requested.
2013-11-12 23:16:31 +01:00
wm4 d2e7467eb2 audio/filter: prepare filter chain for non-interleaved audio
Based on earlier work by Stefano Pigozzi.

There are 2 changes:

1. Instead of mp_audio.audio, mp_audio.planes[0] must be used.

2. mp_audio.len used to contain the size of the audio in bytes. Now
   mp_audio.samples must be used. (Where 1 sample is the smallest unit
   of audio that covers all channels.)

Also, some filters need changes to reject non-interleaved formats
properly.

Nothing uses the non-interleaved features yet, but this is needed so
that things don't just break when doing so.
2013-11-12 23:16:31 +01:00
wm4 b2d4b5ee43 audio/format: add non-interleaved audio formats 2013-11-12 23:16:27 +01:00
Alexander Preisinger e4f2fcc0ec waylad: implement functionality for window-scaling 2013-11-12 21:02:49 +01:00
wm4 84dcb76f27 demux: kill libmng support
It's a dead format that was never used anywhere.
2013-11-11 19:32:45 +01:00
wm4 2f92056e2a demux_mf: use talloc 2013-11-11 19:20:37 +01:00
wm4 59cc8fff19 demux_mf: uncrustify 2013-11-11 18:51:44 +01:00
wm4 d8882bbfb7 demux_mkv: support some raw PCM variants
This affects 64 bit floats and big endian integer PCM variants
(basically crap nobody uses). Possibly not all MS-muxed files work, but
I couldn't get or produce any samples.

Remove a bunch of format tags that are not needed anymore. Most of these
were used by demux_mov, which is long gone. Repurpose/abuse 'twos' as
mpv-internal tag for dealing with the PCM variants mentioned above.
2013-11-11 18:40:59 +01:00
Rudolf Polzer 149ab3afa2 ao_lavc: remove audio offset hack to ease supporting planar audio.
Now to shift audio pts when outputting to e.g. avi, you need an explicit
facility to insert/remove initial samples, to avoid initial regions of
the video to be sped up/slowed down.

One such facility is the delay filter in libavfilter.
2013-11-11 13:04:13 +01:00
Rudolf Polzer 8ff577a2f3 vo_lavc: fix -ovoffset.
Previously, using it led to no single frame being output, ever.
2013-11-11 13:02:34 +01:00
wm4 3cb4116243 ao: add ao_play_silence, use for ao_alsa and ao_oss
Also add a corresponding function to audio/format.c, which fills an
audio block with silence.
2013-11-10 23:05:59 +01:00
wm4 6ec1f31765 af: don't skip filtering if there's no more audio
My main problem with this is that the output format will be incorrect.
(This doesn't matter right, because there are no samples output.)

This assumes all audio filters can deal with len==0 passed in for
filtering (though I wouldn't see why not).

A filter can still signal an error by returning NULL.

af_lavrresample has to be fixed, since resampling 0 samples makes
libavresample fail and return a negative error code. (Even though it's
not documented to return an error code!)
2013-11-10 22:49:39 +01:00
wm4 775e08ba65 vo_opengl: fix alpha values written to the framebuffer
When blending OSD and subtitles onto the video, we write bogus alpha
values. This doesn't normally matter, because these values are normally
unused and discarded. But at least on Wayland, the alpha values are used
by the compositor and leads to transparent windows even with opaque
video on places where the OSD happens to use transparency.

(Also see github issue #338.)

Until now, the alpha basically contained garbage. The source factor
GL_SRC_ALPHA meant that alpha was multiplied with itself. Use GL_ONE
instead (which is why we have to use glBlendFuncSeparate()). This should
give correct results, even with video that has alpha. (Or at least it's
something close to correct, I haven't thought too hard how the
compositor will blend it, and in fact I couldn't manage to test it.)

If glBlendFuncSeparate() is not available, fall back to glBlendFunc(),
which does the same as the code did before this commit. Technically, we
support GL 1.1, but glBlendFuncSeparate is 1.4, and I guess we should
try not to crash if vo_opengl_old runs on a system with GL 1.1 drivers
only.
2013-11-10 03:14:38 +01:00
wm4 9e40d7155c ad_spdif: change API usage so that it works on Libav
Apparently we were using FFmpeg-specific APIs. I have no idea whether
this code is correct on both FFmpeg and Libav (no examples, bad
doxygen... why do they even complaint aht people are using their APIs
incorrectly?), but it appears to work on FFmpeg. That was also the case
before commit ebc4ccb though, where it used internal libavformat
symbols.

Untested on Libav, Travis will tell us.
2013-11-10 00:52:55 +01:00
wm4 1a5c863a32 player: set PulseAudio stream title to window title
Set the PulseAudio stream title, just like the VO window title is set.
Refactor update_vo_window_title() so that we can use it for AOs too.

The ao_pulse.c bit is stolen from MPlayer.
2013-11-10 00:49:13 +01:00
wm4 87ab30669c gl_hwdec_vdpau: silence warning caused by buggy GL_NV_vdpau_interop spec
VDPAU handles are integers, but for some reasons the VDPAU GL extension
expects them as void*.
2013-11-09 23:32:58 +01:00
wm4 d6abfcd578 af_volume: use only one volume setting for all channels
In theory, af_volume could use separate volume levels for each channel.
But this was never used anywhere.

MPlayer implemented something similar before (svn r36498), but kept the
old path for some reason.
2013-11-09 23:32:58 +01:00
wm4 0f82107535 ao_alsa: use correct magic spdif flags
I accidentally copied the AES4/ORIGFS constants from the ALSA headers,
instead of the AES3/FS ones. The difference is probably important.
2013-11-09 23:32:58 +01:00
wm4 53d3827843 Remove sh_audio->samplesize
This member was redundant. sh_audio->sample_format indicates the sample
size already.

The TV code is a bit strange: the redundant sample size was part of the
internal TV interface. Assume it's really redundant and not something
else. The PCM decoder ignores the sample size anyway.
2013-11-09 23:32:58 +01:00
wm4 0ff863c179 af_scaletempo: uncrustify
Also do some cosmetic changes, like merging definition and
initialization of local variables.

Remove an annoying debug mp_msg() from af_open(). It just printed the
command line parameters; if this is really needed, it could be added
to af.c instead (similar as to what vf.c does).
2013-11-09 23:32:58 +01:00
wm4 142d5c985e af_lavrresample: reconfigure libavresample only on successful init
If filter initialization fails anyway, we don't need to reconfigure
libavresample.
2013-11-09 23:32:58 +01:00
wm4 a89549e8db af_lavrresample: move libavresample setup to separate function
Helps with readability. Also remove the ctx_opt_set_* helper macros and
use av_opt_set_* directly (I think these macros were used because the
lines ended up too long, but this commit removes two indentation levels,
giving more space).
2013-11-09 23:32:57 +01:00
wm4 5735b684de af_convert24: fix complicated and incorrect format negotiation
The conversion works for native endian only. The correct check lists
supported format combination explicitly, but is also much simpler.
2013-11-09 23:32:52 +01:00
wm4 31f409a3c5 af_surround: fix format negotiation
This did strange things; perhaps caused by the channel layout changes.
2013-11-09 23:32:52 +01:00
wm4 65571dd0d5 af: allow filters to return AF_OK, even if format doesn't match
This should allow to make format negotiation much simpler, since it
takes the responsibility to compare actual input and accepted input
formats from the filters. It's also backwards compatible. Filters which
have expensive initialization still can use the old method.
2013-11-09 23:32:52 +01:00
wm4 a3e2019c2d ao: print requested audio format on init
Also remove the rather bad/incomplete log calls from ao_alsa and ao_oss.
2013-11-09 23:32:50 +01:00
wm4 3af094062e ao_alsa: add magic spdif parameters
I have no idea what these do, but apparently they are needed to inform
ALSA about spdif configuration. First, replace the literal constant "6"
for the AES0 parameter with the symbolic constants from the ALSA
headers (the final value is the same). Second, copy paste some funky
looking parameter setup from VLC's alsa output for setting the AES1,
AES2, AES3 parameters. (The code is actually not literally copy-pasted,
but does exactly the same.)

My small but non-zero hope is that this could make DTS-HD work, or at
least work into that direction. I can't test spdif stuff though, and
for DTS-HD not even opening the ALSA device succeeds on my system.
2013-11-09 01:30:02 +01:00
wm4 d240aa367e ao_alsa: redo the way parameters are added in the spdif case
Using spdif with alsa requires adding magic parameters to the device
name, and the existing code tried to deal with the situation when the
user wanted to add parameters too.

Rewrite this code, in particular remove the duplicated parameter string
as preparation for the next commit. The new code is a bit stricter, e.g.
it doesn't skip spaces before and after '{' and '}'. (Just don't add
spaces.)
2013-11-09 01:30:00 +01:00
wm4 ebc4ccbbfa ad_spdif: fix libavformat API usage
This accessed tons of private libavformat symbols all over the place.
Don't do this and convert all code to proper public APIs. As a
consequence, the code becomes shorter and cleaner (many things the code
tried are done by libavformat APIs).
2013-11-09 01:27:03 +01:00
wm4 370c5cc834 af: always remove auto-inserted filters, improve error message
It's probably better if all auto-inserted filters are removed when doing
an af_add operation. If they're really needed, they will be
automatically re-added.

Fix the error message. It used to be for an actual internal error, but
now it happens when format negotiation fails, e.g. when trying to use
spdif and real audio filters.
2013-11-09 01:27:03 +01:00
wm4 782c8e72ff vdpau: unbreak
Obviously I didn't test commit 1b8cd01, and it just crashed. Oops.
2013-11-09 01:27:03 +01:00
Stefano Pigozzi ed89332acf travis: remove e-mail notifications
Lately Travis sends out many notifications that are false positives caused by
timeout. We are annoyed.
2013-11-08 21:19:38 +01:00
wm4 6354a6b07d player: factor audio buffer clearing code
Note that the change in seek_reset is not entirely equivalent: we even
drop the remainder of buffered audio when seeking. This should be more
correct, because the whole point of the reset_ao parameter is to control
whether audio queued for output should be dropped or not.
2013-11-08 20:29:26 +01:00
wm4 8125252399 audio: don't let ao_lavc access frontend internals, change gapless audio
ao_lavc.c accesses ao->buffer, which I consider internal. The access was
done in ao_lavc.c/uninit(), which tried to get the left-over audio in
order to write the last (possibly partial) audio frame. The play()
function didn't accept partial frames, because the AOPLAY_FINAL_CHUNK
flag was not correctly set, and handling it otherwise would require an
internal FIFO.

Fix this by making sure that with gapless audio (used with encoding),
the AOPLAY_FINAL_CHUNK is set only once, instead when each file ends.
Basically, move the hack in ao_lavc's uninit to uninit_player.

One thing can not be entirely correctly handled: if gapless audio is
active, we don't know really whether the AO is closed because the file
ended playing (i.e. we want to send the buffered remainder of the audio
to the AO), or whether the user is quitting the player. (The stop_play
flag is overwritten, fixing that is perhaps not worth it.) Handle this
by adding additional code to drain the AO and the buffers when playback
is quit (see play_current_file() change).

Test case: mpv avdevice://lavfi:sine=441 avdevice://lavfi:sine=441 -length 0.2267  -gapless-audio
2013-11-08 20:00:58 +01:00
wm4 052a7d54ab audio: stop "unsupported sample format" spam
It spams these in verbose mode. It's caused by format negotiation code
in af.c. It's for the mpv format to ffmpeg-format case, and that one is
very uninteresting. (The ffmpeg supported audio formats are practically
never extended.)
2013-11-07 22:34:03 +01:00
wm4 de577d4e79 audio: fix bogus audio format comparison 2013-11-07 22:19:19 +01:00
wm4 1b8cd01b1a vdpau: drop dummy render function
This was supposed to handle preemption better. I still think the current
state isn't very nice, since the decoder can "accidentally" call the
previous render function after preemption (instead of calling the
reloaded function), so there might be issues. But all in all, this
dummy_render function is a bit confusing, and still not entirely
correct, so it's not worth it.
2013-11-07 22:17:35 +01:00
wm4 1889c62b85 af: remove a pointless macro
The code should be equivalent; a compatibility macro definition is left.
(It should be mass-replaced later.)
2013-11-07 22:15:44 +01:00
wm4 d74bac22b9 audio/format: convert format macros to enum, drop NE suffix
Turn the sample format definitions into an enum. (The format bits are
still macros.) The native endian versions of the new definitions don't
have a NE suffix anymore, although there are still compatibility defines
since too much code uses the NE variants.

Rename the format bits for special formats to help to distinguish them
from the actual definitions, e.g. AF_FORMAT_AC3 to AF_FORMAT_S_AC3.
2013-11-07 22:13:20 +01:00
wm4 91626b1c06 audio: replace af_fmt2str_short -> af_fmt_to_str
Also, remove all af_fmt2str usages.
2013-11-07 22:12:36 +01:00
wm4 aa48eeac97 audio/format: reformat 2013-11-07 22:12:26 +01:00
Rudolf Polzer 6c4b504eca encoding-example-profiles: support setsar in FFmpeg-git 2013-11-07 17:54:16 +01:00
Rudolf Polzer 633fde4ae5 sd_lavc, sd_spu: make dvdsub stretching conditional on --stretch-dvd-subs.
We found that the stretching - although it usually improves the looks of
the fonts - is incorrect.

On DVD, subtitles can cover the full area of the picture, and they have
the same pixel aspect as the movie itself.

Too bad many commercially released DVDs use bitmap fonts made with the
wrong pixel aspect (i.e. assuming 1:1) - --stretch-dvd-subs will make
these more pretty then.
2013-11-07 12:56:07 +01:00
Rudolf Polzer 49caa0a775 sd_ass, sd_lavc: use the input video's pixel aspect for scaling subtitles.
The previous code used the output video's pixel aspect for stretching
purposes, breaking rendering with e.g. -vf scale in the chain. Now
subtitles are stretched using the input video's pixel aspect only,
matching the intentions of the original subtitle author.
2013-11-07 12:56:07 +01:00
wm4 dbb7927a1e ao_oss: fix previous ao_oss commit
Basically I introduced an inverted condition, and the line removed was
inactive before commit ce72aaa.
2013-11-06 22:28:17 +01:00
Stefano Pigozzi 057c2e25b0 travis: use clang for ffmpeg/libav compilation
I overlooked the fact that the ffmpeg/libav build system only supports `--cc`
and completly ignores $CC. Hopefully this makes the build times a little
faster.

Fixes #332
2013-11-06 20:56:28 +01:00
wm4 ce72aaae7b ao_oss: hide warning 2013-11-06 20:33:48 +01:00
bugmen0t 9db560b9a9 ao_oss: don't enable -softvol by default on OSSv4 2013-11-06 20:31:38 +01:00
bugmen0t 0cffd98e8e ao_oss: make no_persistent_volume volume work when seeking 2013-11-06 20:31:36 +01:00
wm4 d445147be9 osdep: handle SIGTERM
There's no reason why we should e.g. handle SIGQUIT, but not SIGTERM.

Note that sending SIGTERM twice still kills the player.
2013-11-06 20:29:16 +01:00
wm4 ac01e6a147 input.conf: clarify the magic how default key bindings are added
This is a confusing mechanism, so the explanation should bit more clear.
2013-11-06 20:27:19 +01:00
wm4 ce8bc1320d memcpy_pic: reformat
Besides cosmetic changes, also change memcpy_pic return type and remove
config.h include.
2013-11-06 20:27:13 +01:00
wm4 6d2c5fc99a vd_lavc: remove explicit crystalhd support
This removes "--hwdec=crystalhd".

I doubt anyone even tried to use this. But even if someone wants to
use it, the decoders can still be explicitly invoked with e.g.:

    --vd=lavc:h264_crystalhd

The only advantage our special code provided was fallback to
software decoding. (But I'm not sure how the ffmpeg crystalhd
pseudo-decoder actually behaves.)

Removing this will allow some simplifications as soon as we don't need
vdpau_old.c anymore.
2013-11-06 00:47:53 +01:00
wm4 3cfbe81038 input: remove unused key_down_event command
There's no real use-case for this, and is wasn't documented (didn't even
appear on the "undocumented commands" list).
2013-11-06 00:08:38 +01:00
wm4 9dc0dfbd54 input: disallow autorepeat by default for most commands
This drops autorepeated key events for a number of commands. This should
help with slow situations accidentally triggering too many repeats. (I'm
not sure if this actually happened to users - maybe.)

It's not clear whether MP_CMD_COMMAND_LIST (multiple commands on one
binding separated by ';') should be repeated, or whether it should try
to do something clever. For now, disallow autorepeat with it.
2013-11-06 00:05:12 +01:00
wm4 01d12a51f1 gl_header_fixes: fix inverted condition
Could possibly leading to failing compilation on systems with headers
that miss the vdpau extension.
2013-11-05 22:39:19 +01:00
wm4 db6a4eec0a vo_opengl: support for vdpau hardware decoding
This uses vdpau OpenGL interop to convert a vdpau surface to a texture.

Note that this is a bit weak and primitive. Deinterlacing (or any other
form of vdpau postprocessing) is not supported. vo_opengl chroma scaling
and chroma sample position are not supported. Internally, the vdpau
video surfaces are converted to a RGBA surface first, because using the
video surfaces directly is too complicated. (These surfaces are always
split into separate fields, and the vo_opengl core expects progressive
frames or frames with weaved fields.)
2013-11-05 22:28:15 +01:00
wm4 5cca9143ab vdpau: move device and video surface management from vo_vdpau.c to vdpau.c
The goal is being able to use vdpau decoding independently from
vo_vdpau.c.
2013-11-05 22:13:05 +01:00
wm4 ff5a908328 vo_opengl: redo aspects of initialization, change hwdec API
Instead of checking for resolution and image format changes, always
fully reinit on any parameter change. Let init_video do all required
initializations, which simplifies things a little bit.

Change the gl_video/hardware decoding interop API slightly, so that
hwdec initialization gets the full image parameters.

Also make some cosmetic changes.
2013-11-05 22:05:23 +01:00
wm4 91472b84c2 vo_opengl/vaapi: use IMGFMT_RGB0
More correct, might make things slightly faster (probably
insignificant).
2013-11-05 22:05:23 +01:00
wm4 d6de87d1d3 video: make IMGFMT_RGB0 etc. exist even if libavutil doesn't support it
These formats are helpful for distinguishing surfaces with and without
alpha. Unfortunately, Libav and older version of FFmpeg don't support
them, so code will break. Fix this by treating these formats specially
on the mpv side, mapping them to RGBA on Libav, and unseting the alpha
bit in the mp_imgfmt_desc struct.
2013-11-05 22:05:23 +01:00
wm4 890d8ea194 manpage: fix incorrect default for --osc 2013-11-05 22:05:23 +01:00
wm4 7df03a1c9e vo_xv: fix compilation of SHM not available 2013-11-05 22:05:22 +01:00
Stefano Pigozzi f2d67a48c9 travis: update ffmpeg stable to 1.2.4 tarball
This is the last release in the 1.2.4 line.
2013-11-05 21:28:29 +01:00
Timothy Gu 327c88f4c9 travis: Update libav stable tarball
Signed-off-by: Timothy Gu <timothygu99@gmail.com>
2013-11-05 21:21:36 +01:00
wm4 ed02cbf92f playlist: rewrite PLS parser
Somehow the new parser ends up much smaller. Much of it is because we
don't parse some additional information. We just skip it, instead of
parsing it and then throwing it away.

More importantly, we use the physical order of entries, instead of
trying to sort them by entry number. Each "File" entry is followed by a
number that is supposed to be the entry number, and "File1" is first.
(Should it turn out that this is really needed, an additional field
should be added to playlist_entry, and then qsort().)
2013-11-04 23:55:41 +01:00
wm4 b74edd4069 demux_mkv: fix compiler warnings
Make TOOLS/matroska.pl output structs with fields sorted by name in
ebml_types.h to make the order of fields deterministic. Fix warnings in
demux_mkv.c caused by the first struct fields switching between scalar
and struct types due to non-deterministic ebml_types.h field order.
Since it's deterministic now, this shouldn't change anymore.

The warnings produced by the compilers are bogus, but we want to silence
them anyway, since this could make developers overlook legitimate
warnings.

What commits 7b52ba8, 6dd97cc, 4aae1ff were supposed to fix. An earlier
attempt sorted fields in the generated C source file, not the header
file. Hopefully this is the last commit concerning this issue...
2013-11-04 23:49:22 +01:00
Paweł Forysiuk f330884b0a Adjust wayland defines 2013-11-04 21:28:34 +01:00
wm4 2826dcff8f vdpau: drop required/deprecated access to a libavcodec struct field
Before the bitstream_buffers field was deprecated, you had to free it,
otherwise you would leak memory.

(Although vdpau.c uses a new API, they managed to introduce a new
deprecation this quickly. This is a complaint.)

This introduces a memory leak of 12 bytes per file on every file on some
_older_ libavcodec versions. This is minor enough that I don't care.
2013-11-04 19:59:03 +01:00
wm4 859964e0db stream_lavf: don't duplicate list of rtmp protocols
Assume a rtmp prefix means rtmp instead.
2013-11-04 19:57:39 +01:00
wm4 41fedab2d9 stream_lavf: support more libavformat protocols 2013-11-04 19:57:13 +01:00
wm4 bb22bee315 vo_opengl: cosmetics/fix typos 2013-11-04 19:46:28 +01:00
wm4 383d8a8b60 vo_opengl: reserve 4 texture units for video instead of 3
Video has up to 4 textures, if you include obscure formats with alpha.
This means alpha formats could always overwrite the first scaler
texture, leading to corrupted video display. This problem was recently
brought to light, when commit 571e697 started to explicitly unbind all 4
video textures, which broke rendering for non-alpha formats as well.

Fix this by reserving the correct number of texture units.
2013-11-04 19:46:15 +01:00
Stefano Pigozzi 78a9bc4a7d osx: fix -Wshadow warnings on platform specific code 2013-11-04 08:33:35 +01:00
Stefano Pigozzi 9087500179 Merge remote-tracking branch 'origin/have_configure' 2013-11-04 08:31:58 +01:00
wm4 5b6eab01af vo_opengl: fix use of uninitialized memory
Pretty bad, although it should actually not cause any misbehavior. Comes
from the hardware decoding interop commit.
2013-11-04 01:20:11 +01:00
wm4 3a831b2c01 fix HAVE_PTHREADS being undefined on windows 2013-11-04 00:48:43 +01:00
wm4 00de1320b0 fix HAVE_JACK being undefined
Holy inconsistency, let's just kill it with waf.
2013-11-04 00:48:29 +01:00
wm4 222cfe7fe6 define HAVE_COREAUDIO on platforms other than OSX 2013-11-04 00:43:47 +01:00
wm4 3813b13796 fix weird DPMS->EXT replacement 2013-11-04 00:43:27 +01:00
wm4 f7b2d644ef Merge branch 'master' into have_configure
Conflicts:
	configure
2013-11-04 00:43:06 +01:00
Paweł Forysiuk 25affdcc88 Fix -Wshadow warning about seek function in playloop.c
mpvcore/player/playloop.c: In function 'seek':
mpvcore/player/playloop.c:209:54: warning: declaration of 'seek' shadows a global declaration [-Wshadow]
mpvcore/player/playloop.c:209:12: warning: shadowed declaration is here [-Wshadow]
mpvcore/player/playloop.c: In function 'queue_seek':
mpvcore/player/playloop.c:360:25: warning: declaration of 'seek' shadows a global declaration [-Wshadow]
mpvcore/player/playloop.c:209:12: warning: shadowed declaration is here [-Wshadow]

Signed-off-by: wm4 <wm4@nowhere>
2013-11-04 00:13:24 +01:00
wm4 2d58fb3b8e vo_opengl: add support for VA-API OpenGL interop
VA-API's OpenGL/GLX interop is pretty bad and perhaps slow (renders a
X11 pixmap into a FBO, and has to go over X11, probably involves one or
more copies), and this code serves more as an example, rather than for
serious use. On the other hand, this might be work much better than
vo_vaapi, even if slightly slower.
2013-11-04 00:11:43 +01:00
wm4 571e697a7c vo_opengl: add infrastructure for hardware decoding OpenGL interop
Most hardware decoding APIs provide some OpenGL interop. This allows
using vo_opengl, without having to read the video data back from GPU.

This requires adding a backend for each hardware decoding API. (Each
backend is an entry in gl_hwdec_vaglx[].) The backends expose video data
as a set of OpenGL textures.

Add infrastructure to support this. The next commit will add support for
VA-API.
2013-11-04 00:11:07 +01:00
wm4 6f17410f88 mp_image: add helper for copying image attributes
This enw function is similar to mp_image_set_params(), but doesn't force
setting "hard" parameters like image size and format.
2013-11-03 23:55:16 +01:00
Stefano Pigozzi 37388ebb0e configure: uniform the defines to #define HAVE_xxx (0|1)
The configure followed 5 different convetions of defines because the next guy
always wanted to introduce a new better way to uniform it[1]. For an
hypothetic feature 'hurr' you could have had:

  * #define HAVE_HURR 1   / #undef HAVE_DURR
  * #define HAVE_HURR     / #undef HAVE_DURR
  * #define CONFIG_HURR 1 / #undef CONFIG_DURR
  * #define HAVE_HURR 1   / #define HAVE_DURR 0
  * #define CONFIG_HURR 1 / #define CONFIG_DURR 0

All is now uniform and uses:
  * #define HAVE_HURR 1
  * #define HAVE_DURR 0

We like definining to 0 as opposed to `undef` bcause it can help spot typos
and is very helpful when doing big reorganizations in the code.

[1]: http://xkcd.com/927/ related
2013-11-03 21:59:54 +01:00
ChrisK2 3b5f8ea571 tools: add --no-cache --no-config to mpv_identify.sh 2013-11-03 20:48:49 +01:00
wm4 388f43f6f7 player: fix quvi 0.9 playlist loading
The code made no sense at all.
2013-11-03 19:22:13 +01:00
wm4 a49ab7cc2f demux: make determining seek capability generic
Instead of having each demuxer do it (only demux_mkv actually did...),
let generic code determine whether the file is seekable. This requires
adding exceptions to demuxers where the stream is not seekable, but the
demuxer is.

Sort-of try to improve handling of unseekable files in the player. Exit
early if the file is determined to be unseekable, instead of resetting
all decoders and then performing a pointless seek.

Add an exception to allow seeking if the file is not seekable, but the
stream cache is enabled. Print a warning in this case, because seeking
outside the cache (which we can't prevent since the demuxer is not aware
of this problem) still messes everything up.
2013-11-03 19:21:47 +01:00
wm4 847cbe9d5d demux: remove movi_start/movi_end fields
Pointless, using stream->start_pos/end_pos instead.

demux_mf was the only place where this was used specially, but we can
rely on timestamps instead for this case.
2013-11-03 18:50:00 +01:00
wm4 22274f7982 stream: more consistent checks for whether stream is seekable
Never check s->seek (except in init), because it'd have to check
s->flags anyway. Also, for fast skippable streams (like pipes), don't
set the bit that indicates support for seek forward.

Make sure s->end_pos is always 0 for unseekable streams. Lots of code
outside of stream.c uses this to check seeking support.
2013-11-03 17:46:36 +01:00
wm4 84f7e213ab stream: reconnecting doesn't make sense if stream is not seekable
This stops mpv from being stuck in reconnecting at the end of the file
with some unseekable streams.

Test URL: http://playerservices.streamtheworld.com/pls/CBC_R1_VCR_H.pls
2013-11-03 17:30:34 +01:00
wm4 891a2a1f47 w32: implement functionality required for window-scale
Same semantics with respect to fullscreen as x11.
2013-11-02 17:54:46 +01:00
wm4 9423a7e23e x11: make window-scale use windowed size in fullscreen mode
This is a bit more intuitive, since before, the window size was just set
to something random when setting the window-scale property during
fullscreen.
2013-11-02 17:32:03 +01:00
wm4 81c602c7d2 ta: re-add MinGW cargo culting
Not really cargo cult, but an unexplainable, needless difference that
just exists to annoy us.

Fixes that gcc on MinGW treats format specifiers in MSVC mode. Just why?
Why?
2013-11-02 15:20:53 +01:00
wm4 4d903127ad demux: rename Windows symbols
There are some Microsoft Windows symbols which are traditionally used by
the mplayer core, because it used to be convenient (avi was the big
format, using binary windows decoders made sense...). So these symbols
have the exact same definition as the Windows one, and if mplayer is
compiled on Windows, the symbols from windows.h are used.

This broke recently just because some files were shuffled around, and
the symbols defined in ms_hdr.h collided with windows.h ones. Since we
don't have windows binary decoders anymore, there's not the slightest
reason our symbols should have the same names. Rename them to reduce the
risk for collision, and to fix the recent regression.

Drop WAVEFORMATEXTENSIBLE, because it's mostly unused. ao_dsound defines
its own version if the windows headers don't define it, and ao_wasapi is
not available on systems where this symbol is missing.

Also reindent ms_hdr.h.
2013-11-02 15:14:12 +01:00
wm4 7b52ba87a3 demux_mkv: fix warning
Now that matroska.pl generates struct fields in deterministic order,
this should be the last time I change this.

(gcc and clang shouldn't warn about this line of code, but since they
do, we want to workaround and silence the warning anyway.)
2013-11-02 02:51:23 +01:00
Diogo Franco (Kovensky) 6dd97ccf5c matroska.pl: Sort the generated struct field list
Newer versions of perl randomize the hash used for hashes every time
it's run; this makes the order of the fields be non-deterministic. Tack
a sort there to make it deterministic. Needed to fix (or allow fixing) a
buggy gcc warning.
2013-11-02 01:43:49 +00:00
wm4 e0b6fdeb8d Fix some more -Wshadow warnings
These aren't printed with newer gcc or clang versions for some reason.

All of them seem to be about local variables shadowing global functions.
2013-11-01 17:35:38 +01:00
wm4 4b6c00c50a tl_matroska: initialize segment related arrays with 0
mpv crashed when linked files were not found. The reason was that the
chapters array contained some uninitialized data.

I have no idea how this code works (after the merge). The old code
actually seems to remove missing chapters, while the new code just
leaves them unintiialized. Work around the crash by initializing the
chapters array (and a bunch of other things) with 0, which means the
missing chapter will be located at 00:00:00 and have no name.

There is a regression since commit af0306d.
2013-11-01 17:35:38 +01:00
wm4 24897eb94c video: check profiles with hardware decoding
We had some code for checking profiles earlier, which was removed in
commits 2508f38 and adfb71b. These commits mentioned that (working) hw
decoding was sometimes prevented due to profile checking, but I can't
find the samples anymore that showed this behavior. Also, I changed my
opinion, and I think checking the profiles is something that should be
done for better fallback to software decoding behavior.

The checks roughly follow VLC's vdpau profile checks, although we do
not check codec levels. (VLC's profile checks aren't necessarily
completely correct, but they're a welcome help anyway.)

Add a --vd-lavc-check-hw-profile option, which skips the profile check.
2013-11-01 17:33:33 +01:00
wm4 4aae1ff6de demux_mkv: use a more universal zero initializer
Unfortunately, we can't avoid this warning 100%, because ebml_info is
written by a Perl script. I think the script writes the struct fields in
random order (thanks Perl), so there's no way to know whether the first
struct field is a scalar or a struct.

At least {0} is always valid here, even if it shows a warning. (The
compilers are wrong, see e.g. [1].)

[1] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
2013-11-01 13:01:54 +01:00
wm4 dd344b43e8 Enable -Wshadow
This one really did bite me hard (see previous commit), so enable it by
default.

Fix some cases of shadowing throughout the codebase. None of these
change behavior, and all of these were correct code, and just tripped up
the warning.
2013-11-01 13:00:15 +01:00
wm4 2696148d64 options: fix positional suboption asrguments
E.g. "-vf scale=848:480" set the w argument twice, instead of setting w
and then h.

This was caused by accidental shadowing of a local variable.

Regression since probably 4cd143e.
2013-11-01 12:27:04 +01:00
wm4 482009d170 input: show deprecation warnings for some recently replaced commands
Let's see if I get annoyed reactions...
2013-11-01 00:25:42 +01:00
wm4 39fc0060fb command: replace speed_mult with multiply command
The compatibility layer still takes care of the old speed_mult command.
2013-10-31 23:30:14 +01:00
wm4 71ded03123 command: add generic "multiply" command
Essentially works like "add".
2013-10-31 23:30:14 +01:00
wm4 94542abf2e command: add property to scale window size 2013-10-31 23:30:14 +01:00
wm4 b55d557822 x11: factor out normal window resize code
As preparation for resizing the window with input commands in the
following commit.

Since there are already so many functions which somehow resize the
window, add the word "highlevel" to the name of this new function.
2013-10-31 19:51:46 +01:00
wm4 2537f6f467 sd_lavc: display DVD subs with unknown duration
DVD subs (rarely) have subtitle events without end timestamp. The
duration is unknown, and they should be displayed until they're
replaced by the next event.

FFmpeg fails hard to make us aware whether duration is unknown or
actually 0, so we can't distinguish between these two cases. It fails
at this twice: AVPacket.duration is set to 0 if duration is unknown,
and AVSubtitle.end_display_time has the same issue.

Add a hack that considers all bitmap subtitles with duration==0 as
events with uknown length. I'd rather accidentally display a hidden
subtitle (if they exist at all), instead of not displaying random
subtitles at all.

See github issue #325.
2013-10-31 18:17:14 +01:00
wm4 75261165af ao_pulse: fix channel layouts
The code was selecting PA_CHANNEL_POSITION_MONO for MP_SPEAKER_ID_FC,
which is correct only with the "mono" channel layout, but not anything
else. Remove the mono entry, and handle mono separately.

See github issue #326.
2013-10-31 18:17:14 +01:00
wm4 7100a10127 vd_lavc: add more ifdeffery and ffmpeg cargo culting for correctness
We mixed the "old" AVFrame management functions (avcodec_alloc_frame,
avcodec_free_frame) with reference counting. This doesn't work
correctly; you must use av_frame_alloc and av_frame_free. Of course
ffmpeg doesn't warn us about the bad usage, but will just mess up
things silently. (Thanks a lot...)

While the alloc function seems to be 100% compatible, the free function
will do bad things, such as freeing memory that might still be
referenced by another frame. I didn't experience any actual bugs, but
maybe that was pure luck.
2013-10-31 18:17:14 +01:00
wm4 ee2ba635f2 dec_video: remove unused declaration of a former global variable 2013-10-31 18:17:13 +01:00
Rudolf Polzer 9e8eba11fa encoding-example-configs: add more comments regarding the scaling method 2013-10-31 05:30:45 +01:00
Stefano Pigozzi 55dca5b6a3 gl_common: osx: fix compilation with latest XQuartz RC
Looks the gl.h header in XQuartz is incompatible with the one in OS X 10.9.
2013-10-30 23:59:43 +01:00
wm4 2c48a16e99 tech-overview.txt: reflect recent updates
mplayer.c split, and some other things.
2013-10-30 22:35:48 +01:00
wm4 03ec49be8f ta: track location debug info in 2 more cases
Was overlooked.
2013-10-30 22:19:32 +01:00
wm4 50e57c346f x11: restore support for --wid=0
This stopped working when the code was changed to create a window even
if --wid is used.

It appears we can't create our own window in this case, because in X11
there is no difference between a window with the root window as parent,
and a window that is managed by the WM. So make this (kind of worthless)
special case use the root window itself.
2013-10-30 22:19:32 +01:00
wm4 7743ed2dc6 x11: remove ancient metacity hack
Tested with recent metacity; this code is not triggered anymore. The
code was added in 2003 and probably has been unused for years.
2013-10-30 22:19:31 +01:00
wm4 4b2d93f103 x11: minor cosmetics 2013-10-30 22:19:31 +01:00
wm4 53218f33b9 main: improve a terminal message
Better prefer real English...
2013-10-30 22:19:31 +01:00
wm4 2ccc3721ef player: merge mp_osd.h into mp_core.h
Just doing this because mp_osd.h and osd.c is not consistent.

There are some other header files (command.h and screenshot.h), but
since I don't feel too good about inflating mp_core.h, I'm not merging
them, at least not yet.
2013-10-30 22:19:31 +01:00
wm4 fdd1b8cac1 mp_core: sort function prototypes by source file
I considered making a header file for each .c file, but decided against
it. Asking around, not making separate headers was deemed acceptable. In
the end, all of these depend on MPContext and store state inside of it,
so separate headers aren't all that useful anyway.
2013-10-30 22:19:31 +01:00
wm4 a17b5364ea ao_alsa: return negative value on error in play()
No functional change, because the only user of ao_play() ignores return
values below 1.
2013-10-30 22:19:15 +01:00
Rudolf Polzer 7ac88cb05b Fix style of the HP Slate 7 vf-add line. 2013-10-30 10:36:06 +01:00
Rudolf Polzer f19f4a36bc encoding-example-profiles: support HP Slate 7's weird aspect.
Also, replace broken noup= by lavfi expressions.
2013-10-30 10:30:49 +01:00
wm4 b19414f3bf Split mplayer.c
mplayer.c was a bit too big. Split it into multiple files. I hope the
way it's split makes sense. Maybe some things don't make too much sense,
or go against intuition. These will fixed as soon as I notice them.

Some files are a bit questionable (misc.c, osd.c, configfiles.c), and
suggestions how to organize this better are welcome.

Regressions are possible due to reorganized include statements.
Obviously I didn't just copy mplayer.c's orgy of include statements, but
recreated them for each file. It's easily possible that there are
oversights and mistakes, which will show up on other platforms.

There is one actual change: the public avutil.h include is removed from
encode.h, and I tried to replace most FFMIN/FFMAX/av_clip uses. I
consider using libavutil too much as dangerous, because the set of
include files they recursively pull in is rather arbitrary and is
different between FFmpeg and Libav.
2013-10-30 01:53:53 +01:00
wm4 a84258d769 Move files part of the playback core to player sub-directory
All these files access mp_core.h and MPContext, and form the actual
player application. They should be all in one place, and separate
from the other sources that are mere utility helpers.

Preparation for splitting mplayer.c into multiple smaller parts.
2013-10-30 01:51:28 +01:00
wm4 884b4600a4 gl_x11: change error message when GL3 context creation fails
On systems that provide legacy OpenGL (up to 2.1), but not GL3 and
later, creating a GL3 context will fail. We then revert to legacy GL.

Apparently the error message printed when the GL3 context creation
fails is confusing. We could just silence it, but there's still a X
error ("X11 error: GLXBadFBConfig"), which would be quite hard to
filter out. For one, it would require messing with the X11 error
handler, which doesn't even carry a context pointer (for application
private data), so we don't even want to touch it. Instead, change
the error message to inform the user what's actually happening: a
fallback to an older version of OpenGL.
2013-10-28 23:54:02 +01:00
wm4 dceccaf169 getch2: assume EOF when input file descriptor is invalid
When starting mpv with nohup, file descriptor 0 seems to be invalid for
some reason. (I'm not quite sure why it should be... /proc/pid/fd/0
seems to indicate it's just /dev/null, and using /dev/null explicitly
shows that it works just fine.)

select() will always immediately return, and this causes mpv to burn CPU
without reason. Fix this by treating it as EOF when read() returns
EBADF.

Also add EINVAL to this condition, because it seems like a good idea.
2013-10-28 23:32:57 +01:00
wm4 e10b362bdb getch2: move global state to file scope variables
Using static variables for mutable state inside functions is a bad idea,
because it's not immediately obvious that it is what it is.
2013-10-28 23:29:08 +01:00
wm4 40e68d6514 mplayer: handle subtitle renderer cleanup via uninit_player
This might actually fix an issue with DVB channel switching, because
that uses some sort of hack to reinitialize the demuxer, which causes
the subtitle renderer to initialize twice. As consequence, the assert in
add_subtitle_fonts_from_sources() would trigger.

I didn't actually test the DVB case, because I don't have the hardware.
2013-10-28 23:16:00 +01:00
wm4 96a60e15ce x11_common: refactor of fstype code
This is completely pointless, but it still somehow bugged me.
2013-10-28 22:42:47 +01:00
Stefano Pigozzi e4d23dd01d cocoa: apply the more invasive constraining only with cmd+1/2/3
Regression from bc49957
Fixes #321
2013-10-28 09:08:08 +01:00
wm4 5e9c226229 command: disable autorepeat for some commands (actually properties)
Disable autorepeat for the "add"/"cycle" commands when changing
properties that are choices (such as fullscreen, pause, and more).
In these cases, autorepeat is not very useful, and can rather harmful
effects (like triggering too many repeated commands if command
execution is slow).

(Actually, the commands are just dropped _after_ being repeated, since
input.c itself does not know enough about the commands to decide whether
or not they should be repeated.)
2013-10-28 00:33:52 +01:00
wm4 f2d438a1fd x11: fix border toggling
Trying to toggle the border during fullscreen (with "cycle border")
would leave the window stuck without border, and it couldn't be
restored. This was because vo_x11_decoration() always excepted to be
called when toggling the state, and thus confusing the contents of the
olddecor variable. Add got_motif_hints to hopefully prevent this.

Also, when changing the border, don't take fs in account. May break on
older/broken WMs, but all in all is in fact more robust and simpler,
because you do not need to update the border state manually when
returning from fullscreen.
2013-10-27 23:22:46 +01:00
wm4 6e1eb8add9 vo_xv: check whether image allocation succeeds 2013-10-27 17:58:06 +01:00
wm4 d2ee5c0330 command: don't include stream_dvd.h 2013-10-27 00:40:01 +02:00
wm4 7abc1bef40 af: replace macros with too generic names
Defining names like min, max etc. in an often used header is not really
a good idea.

Somewhat similar to MPlayer svn commit 36491, but don't use libavutil,
because that typically causes us sorrow.
2013-10-26 15:05:59 +02:00
wm4 74e7043cc7 gl_common: suggest --vo=opengl-old if OpenGL 2.1 missing
If the caller requests at least OpenGL 2.1 (which --vo=opengl does),
but we get OpenGL 1.x, suggest using opengl-old.

Based on a patch by pfor on IRC.
2013-10-26 14:17:53 +02:00
wm4 6ac5474790 af_volume: some more cosmetics 2013-10-26 14:04:38 +02:00
wm4 13fcb1925a af_volume: switch to new option parsing 2013-10-26 13:36:46 +02:00
wm4 f2660c0a29 af_volume: uncrustify
Also, use more C99 and remove "register" keywords.
2013-10-26 13:36:46 +02:00
wm4 b890093c44 af_volume: don't change volume if nothing is to be changed
On the float path. Note that this skips clipping, but we expect that
everything on the audio-path is pre-clipped anyway.
2013-10-26 13:36:34 +02:00
wm4 3b5657f0c1 af_volume: remove unused features
Roughly follows MPlayer svn commits 36492 and 36493. We also remove
the volume peak reporting. (There are much better libavfilter filters
for this, I think.)
2013-10-26 13:36:34 +02:00
Ben Boeckel 1fb1868afe mpvcore: add a note about desync on track switches 2013-10-26 12:37:42 +02:00
ChrisK2 327cdcb611 docs: osc: update cache display percentage. 2013-10-26 11:12:08 +02:00
ChrisK2 c341edda35 osc: display cache status only below 45%
Turnes out, when playing stuff over WiFi, the cache may go below 48%
quite frequently.
2013-10-26 11:09:06 +02:00
ChrisK2 e02ec80192 osc: fix bug for no-duration cases 2013-10-26 11:08:06 +02:00
ChrisK2 c54335e7df osc: add experimental seekbar tooltip
Not up to my quality standards (need ASS boundingboxes), so disabled by default and undocumented for now.

Can be enabled with seektooltip=yes in plugin_osc.conf
2013-10-26 05:01:33 +02:00
ChrisK2 3a181bac1f osc: make sure the OSC actually fits into the video 2013-10-26 03:50:11 +02:00
wm4 ecc0705f83 options: don't let watch_later etc. overwite command line options
There are certain cases where mpv will automatically set options, such
as per-file configs, per protocol/VO/AO/extension profiles, and
watch_later resume configs. All these were overwriting the user's
options, even when they were specified on command line.

Add something that explicitly preserves command line options. This
means you can now actually use the command line to override any
options that the playback resume functionality backups and restores.

It still happily overrides options set at runtime (e.g. changed via
properties while playing a file; then playing the next file might
override them again), but maybe that's not a problem with typical use.
2013-10-25 22:52:54 +02:00
wm4 d8896f0dba ao_alsa: don't include alloca.h
It's true that ALSA uses alloca() in some of its API functions, but
since this is hidden behind macros in the ALSA headers, we have no
reason to include alloca.h ourselves.

Might help with portability (FreeBSD).
2013-10-25 21:25:54 +02:00
wm4 950ad5a412 README: add a link to the wiki about the FFmpeg vs. Libav issue 2013-10-25 21:23:22 +02:00
wm4 dbb4b00a12 manpage: clarify --heartbeat-interval operation 2013-10-25 20:33:46 +02:00
wm4 de303d8ba4 x11_common: reduce screensaver heartbeat from 30 to 10 seconds
Apparently this fixes issues with Gnome, which has a smaller timeout.
See github issue #315.
2013-10-25 20:30:01 +02:00
Stefano Pigozzi bc49957458 cocoa: constraint the window position a little more
The intention of this is to not make the window go outside the screen when
changing dimensions from 2x to .5x.
2013-10-25 20:01:32 +02:00
wm4 607892b741 mplayer: make --length relative to actual start of file, instead of 0
We always use the file's timestamps, so the start time can be easily
something different from 0. Make the --length option respect this.
2013-10-25 18:39:15 +02:00
wm4 2c6f6c9f4e m_config: slightly improve name handling 2013-10-25 18:39:15 +02:00
wm4 28d3fe1d6d m_config: remove unused fields
We don't need that anymore, because the old way to get default values
by recreating the m_config is gone.
2013-10-25 18:39:15 +02:00
wm4 75e1c6f295 m_config: allow not allocating option struct, and use it
In cases we're just listing options or checking their values (as it
happens with -vo/-vf etc. suboption parsing), we don't need to allocate
abd initialize the actual option struct. All we're interested in is
the list of options.
2013-10-25 18:39:15 +02:00
Alexander Preisinger cb3327fe48 wayland: better name for mouse coordinates
I had to look up what the surf_x and surf_y members do.
That means the name is not very good. Also a comment.
2013-10-25 10:30:12 +02:00
Alexander Preisinger bef2135a44 wayland: fix fullscreen transparent border regions
The default behavior of weston changed some time ago to not fill the surface
black for fullscreen windows.

Now let mpv draw the whole screen in fullscreen mode.
2013-10-25 10:30:11 +02:00
Stefano Pigozzi 96432241bd cocoa: fix opening quarantined files on 10.9 with the bundle [2]
Fixup commit for bbc146927
2013-10-25 09:10:50 +02:00
wm4 60aea74f44 m_config: refactor option defaults handling
Keep track of the default values directly, instead of creating a new
instance of the option struct just to get the defaults.

Also get rid of the special handling of m_obj_desc.init_options.
Instead, handle it purely by the option parser. Originally, I wanted to
handle --vo=opengl-hq and --vo=direct3d_shaders with this (by making
them aliases to the real VOs with a different preset), but since --vo
=opengl-hq=help prints the wrong values (as consequence of the
simplification), I'm not doing that, and instead use something
different.
2013-10-24 22:50:13 +02:00
wm4 f6bceacaff options: fix bogus entry
This didn't make sense and caused issues with the following commit: if
it's an option with a data pointer, it should be writable.
2013-10-24 22:50:13 +02:00
wm4 7204dee3c7 m_config: don't require dragging along parent in initialization
Instead, track the opstruct explicitly. It turns out we don't need the
parent at all. Also, add something rudimentary to track the default
values.
2013-10-24 22:50:13 +02:00
wm4 b707589e7e m_config: minor simplification
I think in this case, having a separate function is confusing.
2013-10-24 22:50:13 +02:00
wm4 4cd143e4d8 m_config: store m_config_options in an array instead of a list
Might reduce memory overhead, and is also less annoying.

Since pointers to m_config_options are kept around, this assumes
they're added only during initialization. Otherwise you'd get
dangling pointers.
2013-10-24 22:50:13 +02:00
wm4 a217c08b17 m_config: refactor initialization, reduce amount of malloc'ed strings
Allocate strings only if needed (when we have to prefix sub-options).
Prepare for storing m_config_options in an array instead of a list.
2013-10-24 22:50:13 +02:00
wm4 cfc72d4fff m_config: slightly simplify dynamic option initialization
We can assume memcpy is enough, because the source should be from
static data. (It wouldn't work if the data could contain pointers
back into itself.)
2013-10-24 22:50:13 +02:00
wm4 8d5f8d5a6b m_config: don't allow aliasing with string options
Minor simplification. String options are now not allowed to use the
same variable/field anymore. (Also affects other "dynamic" options
which require memory allocation.)
2013-10-24 22:50:13 +02:00
wm4 cc235d203d m_config: cosmetics: make code less verbose 2013-10-24 22:50:13 +02:00
Stefano Pigozzi bbc1469272 cocoa: fix opening quarantined files on 10.9 with the bundle
It looks like on Mavericks the Finder is passing different arguments to mpv
depending on whether the opened file is quarantined or not.

Fixes #285
2013-10-24 21:37:55 +02:00
Stefano Pigozzi c54a1f3e41 cocoa: fix race condition with input context creation
This seems to be a problem only in OS X 10.9. I guess they improved the
general speed of the Cocoa startup and suddenly mpv core takes more time
than the Cocoa thread to initialize.

Fixes #285

(hopefully!)
2013-10-24 00:07:05 +02:00
wm4 2352a90566 manpage: use the new name for af_force in one place 2013-10-23 19:30:02 +02:00
wm4 d58d4ec93c audio/out: remove useless info struct and redundant fields 2013-10-23 19:30:02 +02:00
wm4 6d44a4dfd1 video/filter: remove useless vf_info fields
This time I didn't bother to move the contents of the author field to
the file headers. "git log" is your friend.
2013-10-23 19:30:01 +02:00
wm4 c8930e80a8 video/out: remove useless info struct and redundant fields
The author and comment fields were printed only in -v mode.
2013-10-23 19:30:01 +02:00
wm4 b08617ff71 audio/filter: remove useless af_info fields
Drop the author and comment fields. They were completely unused - not
even printed in verbose mode, just dead weight.

Also use designated initializers and drop redundant flags.
2013-10-23 19:30:01 +02:00
wm4 a46453347f af_force: set format early for better debug output
Set the input/output format in filter init. This doesn't change anything
functionally, but it makes the forced format show up in the filter chain
init verbose output (which sometimes prints the filter chain before all
filters have been configured).
2013-10-23 19:30:01 +02:00
wm4 247c89d6ba af_force: minor simplifications 2013-10-23 19:30:01 +02:00
wm4 943c785619 audio/filter: rename af_force.c to af_format.c
The filter itself was already renamed earlier - but rename the file too.
2013-10-23 19:29:30 +02:00
wm4 a37c810173 vo_opengl: don't enable PBOs with opengl-hq
The speed advantages are marginal (at least with the way it's currently
used), and it might actually be slower on some drivers, like Mesa.
2013-10-23 17:50:49 +02:00
wm4 68531e23a1 gl_video: add RGB10_A2 FBO format
Maybe this can be the default in the future, but I'm not sure yet.
2013-10-23 17:46:57 +02:00
wm4 e60b8f181d audio/filter: split af_format into separate filters, rename af_force
af_format is the old audio conversion filter. It could do all possible
conversions supported by the audio chain. However, ever since the
addition of af_lavrresample, most conversions are done by
libav/swresample, and af_format is used as fallback.

Separate out the fallback cases and remove af_format. af_convert24 does
24 bit <-> 32 bit conversions, while af_convertsignendian does sign and
endian conversions. Maybe the way the conversions are split sounds a bit
odd. But the former changes the size of the audio data, while the latter
is fully in-place, so there's at least different buffer management.

This requires a quite complicated algorithm to make sure all these
"partial" conversion filters can actually get from one format to
another. E.g. s24le->s32be always requires convertsignendian and
convert24, but af.c has no idea what the intermediate format should
be. So I added a graph search (trying every possible format and
filter) to determine required format and filter. When I wrote this,
it seemed this was still better than messing everything into
af_lavrresample, but maybe this is overkill and I'll change my
opinion. For now, it seems nice to get rid of af_format though.

The AC3->IEC61937 conversion isn't supported anymore, but I don't think
this is needed anywhere. Most AOs test all formats explicitly, or use
the AF_FORMAT_IS_IEC61937() macro (which includes AC3).

One positive consequence of this change is that conversions always
include dithering (done by libav/swresample), instead of possibly going
through af_format, which doesn't do anything fancy.

Rename af_force to af_format. It's essentially compatible with command
line uses of af_format. We retain a compatibility alias for af_force.
2013-10-23 10:04:12 +02:00
wm4 33707c6d63 audio/format: add some helper functions 2013-10-22 01:01:41 +02:00
wm4 bb5fe4d874 ao_pcm: big endian AC3 in wav doesn't work
At least not with ffmpeg.

Honestly, I have no idea how little endian AC3 works at all, since
ao_pcm doesn't do anything special about it, and treats it like s16le.
Maybe it's broken and ffmpeg has special logic to detect it.
2013-10-22 01:01:07 +02:00
wm4 da6093a5a1 mplayer: don't call libquvi for local files
This is obviously not needed, and just creates potential for bad
breakages (e.g. what happens if libquvi tries to open a normal filename
as http URL?).

Note that for simplicity, we still pass file:// URIs to quvi, and we
don't exclude other protocol prefixes either. In general, we don't know
what protocols quvi might support, so we don't try to second-guess it.
(Even though in practice, it's probably only "http" and "https".)
2013-10-20 21:33:27 +02:00
Stefano Pigozzi 3b156caf78 cocoa: set and clear gl context inside of sync sections
The code did not set and unset the current context inside sync sections. I am
not sure if this was an actual problem but this is better since the context is
linked to a single thread. In my brief tests this seems to avoid garbage to
show up in fullscreen.
2013-10-20 21:25:55 +02:00
wm4 afa9e50587 input: fix --input-ar-rate=0
Crashed, instead of disabling auto-repeat.
2013-10-20 13:55:10 +02:00
Alexander Preisinger 6a4e59677e wayland: use mp_input_test_dragging
Instead of removing dragging we now test if it we should drag the window or
not. Because if the OSC shows up we can not drag the window because that would
cause mouse events that makes the OSC disappear.
2013-10-20 09:25:28 +02:00
Alexander Preisinger bbb1057d06 Revert "wayland: remove moving window by grabbing"
This reverts commit 3308bc2bc9.
2013-10-20 09:24:37 +02:00
Alexander Preisinger 38ef925ab7 Revert "wayland: remove outdated comment"
This reverts commit d75cfef49c.
2013-10-20 09:24:37 +02:00
wm4 280c8351ac demux_mkv: use standard C default initialization syntax
gcc and clang happen to allow {} to default-initialize a struct, but
strictly speaking, C99 requires at least {0}. In one case, we use {{0}},
but that's only because gcc as well as clang are too damn stupid not
to warn about {0}, which is a perfectly valid construct in this case.

(Sure is funny, don't warn about the non-standard case, but warn about
another standard conform case.)
2013-10-19 23:15:06 +02:00
wm4 47bd0a6614 demux_mkv: cosmetics: add redundant braces for consistent style
Leaving these braces away just because the syntax allows them is really
obnoxious. It removes the visual cues which help understanding the code
at the first look.

For the record,

  if (cond)
      something();

is ok, as long as there's no else branch, and the if body is one
physical line. But everything else should have braces.
2013-10-19 23:10:03 +02:00
wm4 6d5e887a20 demux_mkv: fill ordered chapters info only if it's present
This was probably not a real problem. But it's not entirely clear
whether this could actually happen or not, so it's better to be
defensive. The code is now also somewhat easier to understand.
2013-10-19 23:02:53 +02:00
wm4 f50b105d06 tl_matroska: fix use-after-free with --cache
If cache was enabled, using ordered chapters could easily crash. The
reason is that enable_cache() reopens the demuxer and closes the old
one. The code after that (reading m->ordered_chapters etc.) then
accessed freed data.

This commit also avoids enabling cache for files which are not used
(which would make opening much slower).
2013-10-19 22:58:02 +02:00
wm4 d2d0bc31bf mp_talloc: simplify a bit
Implement MP_GROW_ARRAY using MP_TARRAY_GROW. MP_GROW_ARRAY is basically
the earlier version of MP_TARRAY_GROW, and had different semantics. When
I added MP_TARRAY_GROW, I didn't dare to change it, but I think all code
that relied on the exact semantics of MP_GROW_ARRAY is gone now, or the
difference doesn't matter anyway. The semantic change is that now
(n+1)*2 elements are preallocated instead of n*2.

Also, implement MP_TARRAY_GROW using MP_RESIZE_ARRAY, which saves 1 line
of code.

In future, these macros should be part of TA.
2013-10-19 21:43:08 +02:00
wm4 d7287d60dd vdpau_old: restore hardware decoding with old API
--hwdec=vdpau did nothing with older ffmpeg/libav versions. Oops.
2013-10-19 20:57:43 +02:00
wm4 bea36dc33f configure: use pkg-config for libsmbclient
Apparently, Samba has .pc files now.
2013-10-19 13:32:30 +02:00
wm4 c01feaaa79 af_lavrresample: actually free resampler
Fixes #304.
2013-10-19 13:19:35 +02:00
wm4 e046fa584a mp_msg: remove gettext() support
Was disabled by default, was never used, internal support was
inconsistent and poor, and there has been virtually no interest in
creating translations.

And I don't even think that a terminal program should be translated.
This is something for (hypothetical) GUIs.
2013-10-18 22:38:10 +02:00
wm4 fd0ffa7b40 mplayer: mp_msg replacements
Not 100% complete; not touching the more complicated cases.
2013-10-18 21:59:52 +02:00
Nikoli 30384a7e67 mpv.desktop: add russian translation, sort 2013-10-18 20:26:39 +04:00
ChrisK2 6e34322dec osc: make transparency of background-box configurable
see boxalpha option
2013-10-18 00:33:01 +02:00
wm4 4a4d2155d8 getch2: remove pointless ifdeffery
Apparently this was for MorphOS. If you really want to use that, ask
the devs to provide dummy headers and declarations for ioctl() instead.
2013-10-17 23:16:18 +02:00
wm4 fbd932410a tv: simplify ifdeffery
Too ugly to leave it be.
2013-10-17 23:13:55 +02:00
wm4 af0306d48d Merge Matroska ordered chapter changes
This adds support for ChapterSegmentEditionUID (pull request #258),
and also fixes issue #278 (pull request #292).

In fact, this is a straight merge of pr/292, which also contains pr/258.
2013-10-17 00:32:14 +02:00
wm4 1eda9554d4 sws_utils: work around libswscale crash with --contrast=-100 2013-10-16 21:39:51 +02:00
wm4 20e1c5f7d9 vf_scale: fix get/set confusion
This caused the equalizer controls to appear stuck.
2013-10-16 21:35:27 +02:00
Alessandro Ghedini de3a979f69 spelling fixes 2013-10-16 12:36:34 +02:00
Ben Boeckel 1b30a0bbf9 matroska: don't add time for chapters without a source 2013-10-15 21:16:05 -04:00
Ben Boeckel 648e8fbeed matroska: log about where missing time comes from 2013-10-15 21:04:16 -04:00
Ben Boeckel 78545fee42 matroska: only error if at least a millisecond is missing 2013-10-15 20:53:10 -04:00
Ben Boeckel abee328f20 matroska: account for missing information from short sources
If a source is shorter than the requested chapter length, account for
the time missing.
2013-10-15 20:53:10 -04:00
Ben Boeckel e80fd3f9e1 matroska: modify chapter limits by join_diff
When adding or removing frames to avoid 1-frame seeks on chapter
boundaries, the end of the chapter needs to be pushed or pulled by the
same amount to keep the intended end frame the same.
2013-10-15 20:46:38 -04:00
wm4 d3c140bbf2 demux_mkv: add support for HEVC
Note that you still need --vd-lavc-o='strict=-2' to enable the decoder.

Also, there's no guarantee that all required features for HEVC demuxing
are actually implemented, nor that the current muxing schema is the
final one.
2013-10-16 00:47:52 +02:00
Ben Boeckel 67eb187826 matroska: use memmove when collapsing the source list down 2013-10-08 22:00:23 -04:00
Ben Boeckel 47345219a3 matroska: avoid requesting the same source multiple times 2013-10-08 22:00:23 -04:00
Ben Boeckel f72a900892 matroska: support chapter references to ordered editions
Since ordered editions can reference external files, when an ordered
chapter references another ordered edition, we have to go and figure out
what is is referencing as well.
2013-10-08 00:51:50 -04:00
Ben Boeckel 5cd33853f2 matroska: recursively search for referenced segments
When playing a Matroska file with ordered chapters, it may reference
another file by edition uid. When this edition is also ordered, it may
reference other files. When this occurs, the new segment/edition pair is
added to the list of sources to search for.
2013-10-08 00:51:50 -04:00
Ben Boeckel d8e5ac00bb matroska: refactor ordered chapter timeline building
This will need to be recursive to support chapters referencing ordered
editions in other files.
2013-10-07 22:43:42 -04:00
Ben Boeckel 2fe2be4df3 matroska: select the edition using the requested edition uid 2013-10-07 22:42:40 -04:00
Ben Boeckel 069a2d047d matroska: set the edition uid when reading a chapter reference 2013-10-07 22:40:40 -04:00
Ben Boeckel 4f287f1792 matroska: parse the requested edition for the segment reference 2013-10-07 22:40:40 -04:00
Ben Boeckel 07fbba3935 matroska: store segment/edition uids in a single structure
To support edition references in matroska chapters, editions need to be
remembered for each chapter and source. To facilitate easier management
of these now-paired uids, a single structure is used.
2013-10-07 22:40:39 -04:00
454 changed files with 28476 additions and 24046 deletions

No files matched your search

+11 -2
View File
@@ -2,6 +2,7 @@
*.a
*.d
*.exe
*.pyc
.depend
/config.h
@@ -10,7 +11,7 @@
/mpv
/mpv.app
/version.h
/mpvcore/input/input_conf.h
/input/input_conf.h
/tags
/TAGS
/video/out/gl_video_shaders.h
@@ -18,10 +19,18 @@
/demux/ebml_defs.c
/demux/ebml_types.h
/sub/osd_font.h
/mpvcore/lua/*.inc
/player/lua/*.inc
/DOCS/man/*/mpv.1
/DOCS/man/*/mpv.aux
/DOCS/man/*/mpv.log
/DOCS/man/*/mpv.out
/DOCS/man/*/mpv.pdf
/DOCS/man/*/mpv.toc
/waf
/build
/.waf*
/.lock-waf_*
/old_build
/Makefile
+5 -5
View File
@@ -13,17 +13,17 @@ branches:
- ci
before_install: ./travis-deps libass-stable $LIBAV
script: ./configure && make
script:
- ./bootstrap.py
- ./waf configure
- ./waf build
notifications-policy: &notifications-policy
on_success: change
on_failure: always
notifications:
email:
recipients:
- mpv-team@googlegroups.com
<<: *notifications-policy
email: false
irc:
channels:
- "irc.freenode.org#mpv-player-dev"
+4 -4
View File
@@ -3,7 +3,7 @@ produce both 32 bit and 64 bit executables. MinGW-w64 is available from
http://mingw-w64.sourceforge.net.
You have to run mpv's configure with these arguments:
./configure --enable-cross-compile --target=i686-w64-mingw32
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
Using mingw-w64-cmake to setup a MinGW-w64 environment is recommended (this will
also build mpv and its dependencies): https://github.com/lachs0r/mingw-w64-cmake
@@ -22,7 +22,7 @@ Example with MXE
# or more!
cd /opt
git clone https://github.com/mxe/mxe
git clone https://github.com/mxe/mxe mingw
cd mingw
# Edit the MXE target, so that MinGW-w64 for 32 bit targets is built.
@@ -44,8 +44,8 @@ make pthreads
git clone https://github.com/mpv-player/mpv.git
cd mpv
export PATH=/opt/mingw/usr/bin/:$PATH
./configure --enable-cross-compile --target=i686-w64-mingw32
make
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
./waf build
# This should work. Note however that MXE’s ffmpeg package might be very old
# in order to avoid breaking e.g. xine-lib, so you might want to update that
+105
View File
@@ -0,0 +1,105 @@
EDL files
=========
EDL files basically concatenate ranges of video/audio from multiple source
files into a single continuous virtual file. Each such range is called a
segment, and consists of source file, source offset, and segment length.
For example::
# mpv EDL v0
f1.mkv,10,20
f2.mkv
f1.mkv,40,10
This would skip the first 10 seconds of the file f1.mkv, then play the next
20 seconds, then switch to the file f2.mkv and play all of it, then switch
back to f1.mkv, skip to the 40 second mark, and play 10 seconds, and then
stop playback. The difference to specifying the files directly on command
line (and using ``--{ --start=10 --length=20 f1.mkv --}`` etc.) is that the
virtual EDL file appears as a virtual timeline (like a single file), instead
as a playlist.
The general simplified syntax is::
# mpv EDL v0
<filename>
<filename>,<start in seconds>,<length in seconds>
If the start time is omitted, 0 is used. If the length is omitted, the
estimated duration of the source file is used.
Note::
mpv can't use ordered chapter files or libquvi-resolved URLs in EDL
entries. Usage of relative or absolute paths as well as any protocol
prefixes is prevented for security reasons.
Syntax of mpv EDL files
=======================
Generally, the format is relatively strict. No superfluous whitespace (except
empty lines and commented lines) are allowed. You must use UNIX line breaks.
The first line in the file must be ``# mpv EDL v0``. This designates that the
file uses format version 0, which is not frozen yet and may change any time.
(If you need a stable EDL file format, make a feature request. Likewise, if
you have suggestions for improvements, it's not too late yet.)
The rest of the lines belong to one of these classes:
1) An empty or commented line. A comment starts with ``#``, which must be the
first character in the line. The rest of the line (up until the next line
break) is ignored. An empty line has 0 bytes between two line feed bytes.
2) A segment entry in all other cases.
Each segment entry consists of a list of named or unnamed parameters.
Parameters are separated with ``,``. Named parameters consist of a name,
followed by ``=``, followed by the value. Unnamed parameters have only a
value, and the name is implicit from the parameter position.
Syntax::
segment_entry ::= <param> ( <param> ',' )*
param ::= [ <name> '=' ] ( <value> | '%' <number> '%' <valuebytes> )
The ``name`` string can consist of any characters, except ``=%,;\n``. The
``value`` string can consist of any characters except of ``,;\n``.
The construct starting with ``%`` allows defining any value with arbitrary
contents inline, where ``number`` is an integer giving the number of bytes in
``valuebytes``. If a parameter value contains disallowed characters, it has to
be guarded by a length specifier using this syntax.
The parameter name defines the meaning of the parameter:
1) ``file``, the source file to use for this segment.
2) ``start``, a time value that specifies the start offset into the source file.
3) ``length``, a time value that specifies the length of the segment.
(Currently, time values are floating point values in seconds.)
Unnamed parameters carry implicit names. The parameter position determines
which of the parameters listed above is set. For example, the second parameter
implicitly uses the name ``start``.
Example::
# mpv EDL v0
%18%filename,with,.mkv,10,length=20,param3=%13%value,escaped,param4=value2
this sets ``file`` to ``filename,with,.mkv``, ``start`` to ``10``, ``length``
to ``20``, ``param3`` to ``value,escaped``, ``param4`` to ``value2``.
Instead of line breaks, the character ``;`` can be used. Line feed bytes and
``;`` are treated equally.
Syntax of EDL URIs
==================
mpv accepts inline EDL data in form of ``edl://`` URIs. Other than the
header, the syntax is exactly the same. It's far more convenient to use ``;``
instead of line breaks, but that is orthogonal.
Example: ``edl://f1.mkv,length=5,start=10;f2.mkv,30,20;f3.mkv``
-139
View File
@@ -1,139 +0,0 @@
Format of mplayer2 EDL files
============================
The first line in the file must be ``mplayer EDL file, version 2``.
The rest of the lines belong to one of these classes:
1) lines specifying source files
2) empty lines
3) lines specifying timeline segments.
Lines beginning with ``<`` specify source files. These lines first
contain an identifier used to refer to the source file later, then the
filename separated by whitespace. The identifier must start with a
letter. Filenames that start or end with whitespace or contain
newlines are not supported.
On other lines ``#`` characters delimit comments. Lines that contain
only whitespace after comments have been removed are ignored.
Timeline segments must appear in the file in chronological order. Each
segment has the following information associated with it:
- duration
- output start time
- output end time (= output start time + duration)
- source id (specifies the file the content of the segment comes from)
- source start time (timestamp in the source file)
- source end time (= source start time + duration)
The output timestamps must form a continuous timeline from 0 to the
end of the last segment, such that each new segment starts from the
time the previous one ends at. Source files and times may change
arbitrarily between segments.
The general format for lines specifying timeline segments is
[output time info] source_id [source time info]
source_id must be an identifier defined on a ``<`` line. Both the time
info parts consists of zero or more of the following elements:
1) ``timestamp``
2) ``-timestamp``
3) ``+duration``
4) ``*``
5) ``-*``
, where ``timestamp`` and ``duration`` are decimal numbers (computations
are done with nanosecond precision). Whitespace around ``+`` and ``-`` is
optional. 1) and 2) specify start and end time of the segment on
output or source side. 3) specifies duration; the semantics are the
same whether this appears on output or source side. 4) and 5) are
ignored on the output side (they're always implicitly assumed). On the
source side 4) specifies that the segment starts where the previous
segment *using this source* ended; if there was no previous segment
time 0 is used. 5) specifies that the segment ends where the next
segment using this source starts.
Redundant information may be omitted. It will be filled in using the
following rules:
- output start for first segment is 0
- two of [output start, output end, duration] imply third
- two of [source start, source end, duration] imply third
- output start = output end of previous segment
- output end = output start of next segment
- if ``*``, source start = source end of earlier segment
- if ``-*``, source end = source start of a later segment
As a special rule, a last zero-duration segment without a source
specification may appear. This will produce no corresponding segment
in the resulting timeline, but can be used as syntax to specify the
end time of the timeline (with effect equal to adding -time on the
previous line).
Examples::
mplayer EDL file, version 2
< id1 filename
0 id1 123
100 id1 456
200 id1 789
300
All segments come from the source file ``filename``. First segment
(output time 0-100) comes from time 123-223, second 456-556, third
789-889.
::
mplayer EDL file, version 2
< f filename
f 60-120
f 600-660
f 30- 90
Play first seconds 60-120 from the file, then 600-660, then 30-90.
::
mplayer EDL file, version 2
< id1 filename1
< id2 filename2
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
+10 id1 *
+10 id2 *
This plays time 0-10 from filename1, then 0-10 from filename1, then
10-20 from filename1, then 10-20 from filename2, then 20-30 from
filename1, then 20-30 from filename2.
::
mplayer EDL file, version 2
< t1 filename1
< t2 filename2
t1 * +2 # segment 1
+2 t2 100 # segment 2
t1 * # segment 3
t2 *-* # segment 4
t1 3 -* # segment 5
+0.111111 t2 102.5 # segment 6
7.37 t1 5 +1 # segment 7
This rather pathological example illustrates the rules for filling in
implied data. All the values can be determined by recursively applying
the rules given above, and the full end result is this::
+2 0-2 t1 0-2 # segment 1
+2 2-4 t2 100-102 # segment 2
+0.758889 4-4.758889 t1 2-2.758889 # segment 3
+0.5 4.4758889-5.258889 t2 102-102.5 # segment 4
+2 5.258889-7.258889 t1 3-5 # segment 5
+0.111111 7.258889-7.37 t2 102.5-102.611111 # segment 6
+1 7.37-8.37 t1 5-6 # segment 7
+5 -6
View File
@@ -43,7 +43,7 @@ One can then encode using this profile using the command::
mpv infile -o outfile.mp4 -profile myencprofile
Some example profiles are provided in a file
etc/encoding-example-profiles.conf; as for this, see below.
etc/encoding-profiles.conf; as for this, see below.
Encoding examples
@@ -89,13 +89,12 @@ Device targets
As the options for various devices can get complex, profiles can be used.
An example profile file for encoding is provided in
etc/encoding-example-profiles.conf in the source tree. You can include it into
your configuration by doing::
etc/encoding-profiles.conf in the source tree. This file is installed and loaded
by default (if libavfilter is enabled at compilation). If you want to modify
it, you can replace and it with your own copy by doing::
mkdir -p ~/.mpv
curl https://raw.github.com/mpv-player/mpv/master/etc/encoding-example-profiles.conf \
> ~/.mpv/encoding-profiles.conf
echo "include = $HOME/.mpv/encoding-profiles.conf" >> ~/.mpv/config
cp /etc/mpv/encoding-profiles.conf ~/.mpv/encoding-profiles.conf
Refer to the top of that file for more comments - in a nutshell, the following
options are added by it::
-1
View File
@@ -10,7 +10,6 @@ latex-preamble: \usepackage[usenames,dvipsnames]{xcolor}
\makeatletter
\renewcommand{\maketitle}{
\begin{center}
\includegraphics{etc/mpv-icon-8bit-64x64.png}\\
\vspace*{-1.5em}
\begin{Huge}
\@title
+98 -73
View File
@@ -11,6 +11,12 @@ syntax is:
To get a full list of available audio filters, see ``--af=help``.
You can also set defaults for each filter. The defaults are applied before the
normal filter parameters.
``--af-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
Set defaults for each filter.
Audio filters are managed in lists. There are a few commands to manage the
filter list:
@@ -35,7 +41,7 @@ Available filters are:
This filter is automatically enabled if the audio output does not support
the audio configuration of the file being played.
It supports only the following sample formats: u8, s16ne, s32ne, floatne.
It supports only the following sample formats: u8, s16, s32, float.
``filter-size=<length>``
Length of the filter with respect to the lower sampling rate. (default:
@@ -50,9 +56,9 @@ Available filters are:
entries. (default: no)
``no-detach``
Do not detach if input and output audio format/rate/channels match.
You should add this option if you specify additional parameters, as
automatically inserted lavrresample instances will use the default
settings.
(If you just want to set defaults for this filter that will be used
even by automatically inserted lavrresample instances, you should
prefer setting them with ``--af-defaults=lavrresample:...``.)
``o=<string>``
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
be documented by FFmpeg or Libav.
@@ -64,17 +70,18 @@ Available filters are:
outputting to S/PDIF. If the input sample rate is not 48 kHz, 44.1 kHz or
32 kHz, it will be resampled to 48 kHz.
``<tospdif>``
Output raw AC-3 stream if zero or not set, output to S/PDIF for
passthrough when ``<tospdif>`` is set non-zero.
``<bitrate>``
The bitrate use for the AC-3 stream. Set it to either 384 or 384000
to get 384 kbps.
``tospdif=<yes|no>``
Output raw AC-3 stream if ``no``, output to S/PDIF for
passthrough if ``yes`` (default).
``bitrate=<rate>``
The bitrate use for the AC-3 stream. Set it to 384 to get 384 kbps.
Valid values: 32, 40, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, 384, 448, 512, 576, 640.
Default bitrate is based on the input channel number:
The special value ``default`` selects a default bitrate based on the
input channel number:
:1ch: 96
:2ch: 192
@@ -83,7 +90,7 @@ Available filters are:
:5ch: 448
:6ch: 448
``<minchn>``
``minchn=<n>``
If the input channel number is less than ``<minchn>``, the filter will
detach itself (default: 5).
@@ -140,7 +147,7 @@ Available filters are:
0 no matrix decoding (default)
==== ===================================
``equalizer=[g1:g2:g3:...:g10]``
``equalizer=g1:g2:g3:...:g10``
10 octave band graphic equalizer, implemented using 10 IIR band-pass
filters. This means that it works regardless of what type of audio is
being played back. The center frequencies for the 10 bands are:
@@ -177,7 +184,7 @@ Available filters are:
Would amplify the sound in the upper and lower frequency region
while canceling it almost completely around 1kHz.
``channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]``
``channels=nch[:routes]``
Can be used for adding, removing, routing and copying audio channels. If
only ``<nch>`` is given, the default routing is used. It works as follows:
If the number of output channels is greater than the number of input
@@ -188,45 +195,52 @@ Available filters are:
``<nch>``
number of output channels (1-8)
``<nr>``
number of routes (1-8)
``<from1:to1:from2:to2:from3:to3:...>``
Pairs of numbers between 0 and 7 that define where to route each
channel.
``<routes>``
List of ``,`` separated routes, in the form ``from1-to1,from2-to2,...``.
Each pair defines where to route each channel. There can be at most
8 routes. Without this argument, the default routing is used. Since
``,`` is also used to separate filters, you must quote this argument
with ``[...]`` or similar.
.. admonition:: Examples
``mpv --af=channels=4:4:0:1:1:0:2:2:3:3 media.avi``
``mpv --af=channels=4:[0-1,1-0,0-2,1-3] media.avi``
Would change the number of channels to 4 and set up 4 routes that
swap channel 0 and channel 1 and leave channel 2 and 3 intact.
Observe that if media containing two channels were played back,
channels 2 and 3 would contain silence but 0 and 1 would still be
swapped.
``mpv --af=channels=6:4:0:0:0:1:0:2:0:3 media.avi``
``mpv --af=channels=6:[0-0,0-1,0-2,0-3] media.avi``
Would change the number of channels to 6 and set up 4 routes that
copy channel 0 to channels 0 to 3. Channel 4 and 5 will contain
silence.
``force=in-format:in-srate:in-channels:out-format:out-srate:out-channels``
.. note::
You should probably not use this filter. If you want to change the
output channel layout, try the ``format`` filter, which can make mpv
automatically up- and downmix standard channel layouts.
``format=format:srate:channels:out-format:out-srate:out-channels``
Force a specific audio format/configuration without actually changing the
audio data. Keep in mind that the filter system might auto-insert actual
conversion filters before or after this filter if needed.
All parameters are optional. The ``in-`` variants restrict what the filter
accepts as input. The ``out-`` variants change the audio format, without
All parameters are optional. The first 3 parameters restrict what the filter
accepts as input. The ``out-`` parameters change the audio format, without
actually doing a conversion. The data will be 'reinterpreted' by the
filters or audio outputs following this filter.
``<in-format>``
Force conversion to this format. See ``format`` filter for valid audio
format values.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
a list of valid formats.
``<in-srate>``
``<srate>``
Force conversion to a specific sample rate. The rate is an integer,
48000 for example.
``<in-channels>``
``<channels>``
Force mixing to a specific channel layout. See ``--channels`` option
for possible values.
@@ -236,55 +250,53 @@ Available filters are:
``<out-channels>``
``format[=format]``
Convert between different sample formats. Automatically enabled when
needed by the audio output or another filter. See also ``--format``.
See also ``--format``, ``--srate``, and ``--channels`` for related options.
Keep in mind that ``--channels`` does not actually force the number of
channels in most cases, while this filter can do this.
``<format>``
Sets the desired format. The general form is 'sbe', where 's' denotes
the sign (either 's' for signed or 'u' for unsigned), 'b' denotes the
number of bits per sample (16, 24 or 32) and 'e' denotes the
endianness ('le' means little-endian, 'be' big-endian and 'ne' the
endianness of the computer mpv is running on). Valid values (amongst
others) are: 's16le', 'u32be' and 'u24ne'. Exceptions to this rule that
are also valid format specifiers: u8, s8, floatle, floatbe, floatne,
mpeg2, and ac3.
*NOTE*: this filter used to be named ``force``. Also, unlike the old
``format`` filter, this does not do any actual conversion anymore.
Conversion is done by other, automatically inserted filters.
``volume[=v[:sc[:fast]]]``
``convert24``
Filter for internal use only. Converts between 24-bit and 32-bit sample
formats.
``convertsignendian``
Filter for internal use only. Converts between signed/unsigned formats
and formats with different endian.
``volume[=volumedb[:softclip[:s16]]]``
Implements software volume control. Use this filter with caution since it
can reduce the signal to noise ratio of the sound. In most cases it is
best to use the *Master* volume control of your sound card or the volume
knob on your amplifier.
This filter has a second feature: It measures the overall maximum sound
level and prints out that level when mpv exits. This feature currently
only works with floating-point data.
*NOTE*: This filter is not reentrant and can therefore only be enabled
once for every audio stream.
``<v>``
``<volumedb>``
Sets the desired gain in dB for all channels in the stream from -200dB
to +60dB, where -200dB mutes the sound completely and +60dB equals a
gain of 1000 (default: 0).
``<sc>``
Turns soft clipping on (1) or off (0). Soft-clipping can make the
``<softclip>``
Turns soft clipping on. Soft-clipping can make the
sound more smooth if very high volume levels are used. Enable this
option if the dynamic range of the loudspeakers is very low.
*WARNING*: This feature creates distortion and should be considered a
last resort.
``<fast>``
Force S16 sample format if set to 1. Lower quality, but might be faster
``<s16>``
Force S16 sample format if set. Lower quality, but might be faster
in some situations.
.. admonition:: Example
``mpv --af=volume=10.1:0 media.avi``
``mpv --af=volume=10.1 media.avi``
Would amplify the sound by 10.1dB and hard-clip if the sound level
is too high.
``pan=n[:L00:L01:L02:...L10:L11:L12:...Ln0:Ln1:Ln2:...]``
``pan=n[:<matrix>]``
Mixes channels arbitrarily. Basically a combination of the volume and the
channels filter that can be used to down-mix many channels to only a few,
e.g. stereo to mono, or vary the "width" of the center speaker in a
@@ -296,19 +308,23 @@ Available filters are:
``<n>``
Number of output channels (1-8).
``<Lij>``
How much of input channel i is mixed into output channel j (0-1). So
in principle you first have n numbers saying what to do with the first
input channel, then n numbers that act on the second input channel
etc. If you do not specify any numbers for some input channels, 0 is
assumed.
``<matrix>``
A list of values ``[L00,L01,L02,...,L10,L11,L12,...,Ln0,Ln1,Ln2,...]``,
where each element ``Lij`` means how much of input channel i is mixed
into output channel j (range 0-1). So in principle you first have n
numbers saying what to do with the first input channel, then n numbers
that act on the second input channel etc. If you do not specify any
numbers for some input channels, 0 is assumed.
Note that the values are separated by ``,``, which is already used
by the option parser to separate filters. This is why you must quote
the value list with ``[...]`` or similar.
.. admonition:: Examples
``mpv --af=pan=1:0.5:0.5 media.avi``
``mpv --af=pan=1:[0.5,0.5] media.avi``
Would downmix from stereo to mono.
``mpv --af=pan=3:1:0:0.5:0:1:0.5 media.avi``
``mpv --af=pan=3:[1,0,0.5,0,1,0.5] media.avi``
Would give 3 channel output leaving channels 0 and 1 intact, and mix
channels 0 and 1 into output channel 2 (which could be sent to a
subwoofer for example).
@@ -316,8 +332,8 @@ Available filters are:
.. note::
If you just want to force remixing to a certain output channel layout,
it is easier to use the ``force`` filter. For example,
``mpv '--af=force=channels=5.1' '--channels=5.1'`` would always force
it is easier to use the ``format`` filter. For example,
``mpv '--af=format=channels=5.1' '--channels=5.1'`` would always force
remixing audio to 5.1 and output it like this.
``sub[=fc:ch]``
@@ -378,12 +394,12 @@ Available filters are:
Would add surround sound decoding with 15ms delay for the sound to
the rear speakers.
``delay[=ch1:ch2:...]``
``delay[=[ch1,ch2,...]]``
Delays the sound to the loudspeakers such that the sound from the
different channels arrives at the listening position simultaneously. It is
only useful if you have more than 2 loudspeakers.
``ch1,ch2,...``
``[ch1,ch2,...]``
The delay in ms that should be imposed on each channel (floating point
number between 0 and 1000).
@@ -402,11 +418,11 @@ Available filters are:
.. admonition:: Example
``mpv --af=delay=10.5:10.5:0:0:7:0 media.avi``
``mpv --af=delay=[10.5,10.5,0,0,7,0] media.avi``
Would delay front left and right by 10.5ms, the two rear channels
and the subwoofer by 0ms and the center channel by 7ms.
``export[=mmapped_file[:nsamples]]``
``export=mmapped_file:nsamples]``
Exports the incoming signal to other processes using memory mapping
(``mmap()``). Memory mapped areas contain a header::
@@ -418,7 +434,7 @@ Available filters are:
The rest is payload (non-interleaved) 16-bit data.
``<mmapped_file>``
File to map data to (default: ``~/.mpv/mpv-af_export``).
File to map data to (required)
``<nsamples>``
number of samples per channel (default: 512).
@@ -459,7 +475,7 @@ Available filters are:
This filter can cause distortion with audio signals that have a very
large dynamic range.
``ladspa=file:label[:controls...]``
``ladspa=file:label:[<control0>,<control1>,...]``
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
filter is reentrant, so multiple LADSPA plugins can be used at once.
@@ -475,12 +491,21 @@ Available filters are:
one filter, but others contain many of them. Entering 'help' here
will list all available filters within the specified library, which
eliminates the use of 'listplugins' from the LADSPA SDK.
``<controls>``
Controls are zero or more floating point values that determine the
behavior of the loaded plugin (for example delay, threshold or gain).
``[<control0>,<control1>,...]``
Controls are zero or more ``,`` separated floating point values that
determine the behavior of the loaded plugin (for example delay,
threshold or gain).
In verbose mode (add ``-v`` to the mpv command line), all
available controls and their valid ranges are printed. This eliminates
the use of 'analyseplugin' from the LADSPA SDK.
Note that ``,`` is already used by the option parser to separate
filters, so you must quote the list of values with ``[...]`` or
similar.
.. admonition:: Example
``mpv --af=ladspa='/usr/lib/ladspa/delay.so':delay_5s:[0.5,0.2] media.avi``
Does something.
``karaoke``
Simple voice removal filter exploiting the fact that voice is usually
@@ -547,7 +572,7 @@ Available filters are:
``mpv --af=scaletempo=stride=30:overlap=.50:search=10 media.ogg``
Would tweak the quality and performace parameters.
``mpv --af=format=floatne,scaletempo media.ogg``
``mpv --af=format=float,scaletempo media.ogg``
Would make scaletempo use float code. Maybe faster on some
platforms.
+22 -2
View File
@@ -10,6 +10,12 @@ syntax is:
If the list has a trailing ',', mpv will fall back on drivers not contained
in the list. Suboptions are optional and can mostly be omitted.
You can also set defaults for each driver. The defaults are applied before the
normal driver parameters.
``--ao-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
Set defaults for each driver.
.. note::
See ``--ao=help`` for a list of compiled-in audio output drivers.
@@ -23,10 +29,13 @@ in the list. Suboptions are optional and can mostly be omitted.
Available audio output drivers are:
``alsa`` (Linux only)
ALSA 0.9/1.x audio output driver
ALSA audio output driver
``no-block``
Sets noblock-mode.
``resample=yes``
Enable ALSA resampling plugin. (This is disabled by default, because
some drivers report incorrect audio delay in some cases.)
``device=<device>``
Sets the device name. For ac3 output via S/PDIF, use an "iec958" or
"spdif" device, unless you really know how to set it correctly.
@@ -150,7 +159,18 @@ Available audio output drivers are:
``null``
Produces no audio output but maintains video playback speed. Use
``--no-audio`` for benchmarking.
``--ao=null:untimed`` for benchmarking.
``untimed``
Do not simulate timing of a perfect audio device. This means audio
decoding will go as fast as possible, instead of timing it to the
system clock.
``buffer``
Simulated buffer length in seconds.
``outburst``
Simulated chunk size in samples.
``pcm``
Raw PCM/WAVE file writer audio output
+14 -1
View File
@@ -103,6 +103,7 @@ General Changes for mplayer2 to mpv
* Native VAAPI support
* OSX: VDA support using libavcodec hwaccel API insted of FFmpeg's decoder. Up
to 2-2.5x reduction in CPU usage.
* Make hardware decoding in general work with the ``opengl`` video output.
* General bug fixes and removal of long-standing issues
* General code cleanups (including refactoring or rewrites of many parts)
* Many more changes
@@ -135,7 +136,7 @@ Command Line Switches
=========================== ========================================
``-no<opt>`` ``--no-<opt>`` (add a dash)
``-nosound`` ``--no-audio``
``-use-filename-title`` ``--title="${filename}"``
``-use-filename-title`` ``--title='${filename}'``
``-loop 0`` ``--loop=inf``
``-hardframedrop`` ``--framedrop=hard``
``-osdlevel`` ``--osd-level``
@@ -176,12 +177,24 @@ Command Line Switches
``-af volnorm`` ``--af=drc`` (renamed)
``-zoom`` Inverse available as ``--video-unscaled``
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
``-pp ...`` ``'--vf=pp=[...]'``
``-pphelp`` ``--vf=pp:help``
``dvdnav://`` ``dvdnav://menu``
=========================== ========================================
.. note::
``-opt val`` becomes ``--opt=val``.
.. note::
Quite some video filters, video outputs, audio filters, audio outputs, had
changes in their option parsing. These aren't mentioned in the table above.
Also, some video and audio filters have been removed, and you have to use
libavfilter (using ``--vf=lavfi=[...]`` or ``--af=lavfi=[...]``) to get
them back.
input.conf and Slave Commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+61 -11
View File
@@ -31,6 +31,10 @@ General Input Command Syntax
``[Shift+][Ctrl+][Alt+][Meta+]<key> [{<section>}] [<prefixes>] <command> (<argument>)* [; <command>]``
Note that by default, the right Alt key can be used to create special
characters, and thus does not register as a modifier. The option
``--no-right-alt-gr`` changes this behavior.
Newlines always start a new binding. ``#`` starts a comment (outside of quoted
string arguments). To bind commands to the ``#`` key, ``SHARP`` can be used.
@@ -44,8 +48,6 @@ Arguments are separated by whitespace. This applies even to string arguments.
For this reason, string arguments should be quoted with ``"``. Inside quotes,
C-style escaping can be used.
Optional arguments can be skipped with ``-``.
You can bind multiple commands to one key. For example:
| a show_text "command 1" ; show_text "command 2"
@@ -86,6 +88,12 @@ List of Input Commands
keyframes
Always restart playback at keyframe boundaries (fast).
``revert_seek``
Undoes the ``seek`` command, and some other commands that seek (but not
necessarily all of them). Calling this command once will jump to the
playback position before the seek. Calling it a second time undoes the
``revert_seek`` command itself.
``frame_step``
Play one frame, then pause.
@@ -112,8 +120,8 @@ List of Input Commands
overflow, set the property back to the minimum, on underflow set it to the
maximum. If ``up`` or ``down`` is omitted, assume ``up``.
``speed_mult <value>``
Multiply the ``speed`` property by the given value.
``multiply <property> <factor>``
Multiplies the value of a property with the numeric factor.
``screenshot [subtitles|video|window|- [single|each-frame]]``
Take a screenshot.
@@ -185,9 +193,11 @@ List of Input Commands
``playlist_clear``
Clear the playlist, except the currently played file.
``playlist_remove <index>``
``playlist_remove current|<index>``
Remove the playlist entry at the given index. Index values start counting
with 0. You cannot remove the entry for the currently played file.
with 0. The special value ``current`` removes the current entry. Note that
removing the current entry also stops playback and starts playing the next
entry.
``playlist_move <index1> <index2>``
Move the playlist entry at index1, so that it takes the place of the
@@ -196,9 +206,28 @@ List of Input Commands
because index2 refers to the target entry, not the index the entry
will have after moving.)
``run "<command>"``
Run the given command with ``/bin/sh -c``. The string is expanded like in
`Property Expansion`_.
``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
expanded like in `Property Expansion`_. Note that there is a static limit
of (as of this writing) 10 arguments (this limit could be raised on demand).
The program is run in a detached way. mpv doesn't wait until the command
is completed, but continues playback right after spawning it.
To get the old behavior, use ``/bin/sh`` and ``-c`` as the first two
arguments.
.. admonition:: Example
``run "/bin/sh" "-c" "echo ${title} > /tmp/playing"``
This is not a particularly good example, because it doesn't handle
escaping, and a specially prepared file might allow an attacker to
execute arbitrary shell commands. It is recommended to write a small
shell script, and call that with ``run``.
``quit [<code>]``
Exit the player using the given exit code.
@@ -309,6 +338,24 @@ Input Commands that are Possibly Subject to Change
- ``b vf set ""`` remove all video filters on ``b``
- ``c vf toggle lavfi=gradfun`` toggle debanding on ``c``
``cycle_values ["!reverse"] <property> "<value1>" "<value2>" ...``
Cycle through a list of values. Each invocation of the command will set the
given property to the next value in the list. The command maintains an
internal counter which value to pick next, and which is initially 0. It is
reset to 0 once the last value is reached.
The internal counter is associated using the property name and the value
list. If multiple commands (bound to different keys) use the same name
and value list, they will share the internal counter.
The special argument ``!reverse`` can be used to cycle the value list in
reverse. Compared with a command that just lists the value in reverse, this
command will actually share the internal counter with the forward-cycling
key binding.
Note that there is a static limit of (as of this writing) 10 arguments
(this limit could be raised on demand).
``enable_section "<section>" [default|exclusive]``
Enable all key bindings in the named input section.
@@ -398,8 +445,7 @@ Input Commands that are Possibly Subject to Change
Undocumented commands: ``tv_start_scan``, ``tv_step_channel``, ``tv_step_norm``,
``tv_step_chanlist``, ``tv_set_channel``, ``tv_last_channel``, ``tv_set_freq``,
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
``radio_set_channel``, ``radio_set_freq``, ``radio_step_freq`` (all of these
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel`` (all of these
should be replaced by properties), ``stop`` (questionable use), ``get_property``
(?), ``vo_cmdline`` (experimental).
@@ -487,6 +533,7 @@ Name W Comment
``ratio-pos`` x position in current file (0.0-1.0)
``time-pos`` x position in current file in seconds
``time-remaining`` estimated remaining length of the file in seconds
``playtime-remaining`` ``time-remaining`` scaled by the the current ``speed``
``chapter`` x current chapter number
``edition`` x current MKV edition number
``titles`` number of DVD titles
@@ -533,6 +580,7 @@ Name W Comment
``fps`` container FPS (may contain bogus values)
``dwidth`` video width (after filters and aspect scaling)
``dheight`` video height
``window-scale`` x window size multiplier (1 means video size)
``aspect`` x video aspect
``osd-width`` last known OSD width (can be 0)
``osd-height`` last known OSD height (can be 0)
@@ -544,8 +592,10 @@ Name W Comment
``video-pan-x`` x see ``--video-pan-x``
``video-pan-y`` x see ``--video-pan-y``
``video-zoom`` x see ``--video-zoom``
``video-unscaled`` x see ``--video-unscaled``
``program`` x switch TS program (write-only)
``sid`` x current subtitle track (similar to ``--sid``)
``secondary-sid`` x secondary subtitle track (``--secondary-sid``)
``sub`` x alias for ``sid``
``sub-delay`` x see ``--sub-delay``
``sub-pos`` x see ``--sub-pos``
+1 -1
View File
@@ -3,7 +3,7 @@ LUA SCRIPTING
mpv can load Lua scripts. These scripts can be used to control mpv in a similar
way to slave mode. mpv provides the builtin module ``mp`` (can be loaded
with ``require 'mpv'``), which provides functions to send commands to the
with ``require 'mp'``), which provides functions to send commands to the
mpv core and to retrieve information about playback state, user settings,
file information, and so on.
+29 -1
View File
@@ -24,6 +24,7 @@ SYNOPSIS
| **mpv** [options] {group of files and options}
| **mpv** [bd]://[title][/device] [options]
| **mpv** dvd://[title|[start\_title]-end\_title][/device] [options]
| **mpv** dvdnav://[longest|menu|title][/device] [options]
| **mpv** \vcd://[/device]
| **mpv** \tv://[channel][/input_id] [options]
| **mpv** radio://[channel|frequency][/capture] [options]
@@ -32,6 +33,7 @@ SYNOPSIS
| **mpv** \mf://[filemask|\@listfile] [-mf options] [options]
| **mpv** cdda://track[-endtrack][:speed][/device] [options]
| **mpv** [file|mms[t]|http|httpproxy|rt[s]p|ftp|udp|smb]://[user:pass\@]URL[:port] [options]
| **mpv** edl://[edl specification as in edl-mpv.rst]
DESCRIPTION
@@ -90,6 +92,10 @@ p / SPACE
Step forward. Pressing once will pause movie, every consecutive press will
play one frame and then go into pause mode again.
,
Step backward. Pressing once will pause movie, every consecutive press will
play one frame in reverse and then go into pause mode again.
q / ESC
Stop playing and quit.
@@ -279,6 +285,28 @@ It has the following format::
``mpv --ao=pcm:file=%`expr length "$NAME"`%"$NAME" test.avi``
Paths
-----
Some care must be taken when passing arbitrary paths and filenames to mpv. For
example, paths starting with ``-`` will be interpreted as options. Likewise,
if a path contains the sequence ``://``, the string before that might be
interpreted as protocol prefix, even though ``://`` can be part of a legal
UNIX path. To avoid problems with arbitrary paths, you should be sure that
absolute paths passed to mpv start with ``/``, and relative paths with ``./``.
The name ``-`` itself is interpreted as stdin, and will cause mpv to disable
console controls. (Which makes it suitable for playing data piped to stdin.)
For paths passed to suboptions, the situation is further complicated by the
need to escape special characters. To work this around, the path can be
additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
Some mpv options interpret paths starting with ``~``. Currently, the prefix
``~~/`` expands to the mpv configuration directory (usually ``~/.mpv/``).
``~/`` expands to the user's home directory. (The trailing ``/`` is always
required.)
Per-File Options
----------------
@@ -400,7 +428,7 @@ profile name ``default`` to continue with normal options.
[extension.flv]
profile-desc="profile for .flv files"
flip=yes
vf=flip
[ao.alsa]
device=spdif
+174 -51
View File
@@ -42,6 +42,12 @@ OPTIONS
requested number of output channels is set with the ``--channels`` option.
Useful for playing surround audio on a stereo system.
``--ad-lavc-threads=<0-16>``
Number of threads to use for decoding. Whether threading is actually
supported depends on codec. As of this writing, it's supported for some
lossless codecs only. 0 means autodetect number of cores on the
machine and use that, up to the maximum of 16 (default: 1).
``--ad-lavc-o=<key>=<value>[,<key>=<value>[,...]]``
Pass AVOptions to libavcodec decoder. Note, a patch to make the o=
unneeded and pass all unknown options through the AVOption system is
@@ -55,6 +61,8 @@ OPTIONS
DTS-HD tracks can be sent over HDMI but not over the original
coax/toslink S/PDIF system.
Note: this feature is broken.
``--dtshd`` and ``--no-dtshd`` are deprecated aliases.
``--af=<filter1[=parameter1:parameter2:...],filter2,...>``
@@ -95,6 +103,13 @@ OPTIONS
Override movie aspect ratio, in case aspect information is incorrect or
missing in the file being played. See also ``--no-aspect``.
Two values have special meaning:
:0: disable aspect ratio handling, pretend the video has square pixels
:-1: use the video stream or container aspect (default)
But note that handling of these special values might change in the future.
.. admonition:: Examples
- ``--aspect=4:3`` or ``--aspect=1.3333``
@@ -625,15 +640,11 @@ OPTIONS
``--demuxer-lavf-format=<name>``
Force a specific libavformat demuxer.
``--demuxer-lavf-genpts-mode=<auto|lavf|builtin|no>``
``--demuxer-lavf-genpts-mode=<no|lavf>``
Mode for deriving missing packet PTS values from packet DTS. ``lavf``
enables libavformat's ``genpts`` option. ``builtin`` enables equivalent
code in mpv. ``auto`` will enable either lavf (normal playback) or builtin
(DVD playback) in correct-pts mode. The difference between them is that
the builtin code will not potentially read until EOF trying to derive the
PTS (which is very bad for DVD playback). On the other hand, builtin might
give up too early, which is why lavf is preferred normally. ``no`` disables
both.
enables libavformat's ``genpts`` option. ``no`` disables it. This used
to be enabled by default, but then it was deemed as not needed anymore.
Enabling this might help with timestamp problems, or make them worse.
``--demuxer-lavf-o=<key>=<value>[,<key>=<value>[,...]]``
Pass AVOptions to libavformat demuxer.
@@ -796,9 +807,6 @@ OPTIONS
``--no-fixed-vo`` enforces closing and reopening the video window for
multiple files (one (un)initialization for each file).
``--flip``
Flip image upside-down.
``--force-rgba-osd-rendering``
Change how some video outputs render the OSD and text subtitles. This
does not change appearance of the subtitles and only has performance
@@ -1045,9 +1053,14 @@ OPTIONS
``mpv --heartbeat-cmd="gnome-screensaver-command -p" file``
``--heartbeat-interval=<sec>``
Time between ``--heartbeat-cmd`` invocations in seconds (default: 30).
.. note::
This does not affect the normal screensaver operation in any way.
``--help``
Show short summary of options.
@@ -1112,11 +1125,10 @@ OPTIONS
:no: always use software decoding (default)
:auto: see below
:vdpau: requires ``--vo=vdpau`` (Linux only)
:vaapi: requires ``--vo=vaapi`` (Linux with Intel GPUs only)
:vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:vda: requires ``--vo=corevideo`` (OSX only)
:crystalhd: Broadcom Crystal HD
:vda: requires ``--vo=opengl`` or ``--vo=corevideo`` (OSX only)
``auto`` tries to automatically enable hardware decoding using the first
available method. This still depends what VO you are using. For example,
@@ -1127,12 +1139,23 @@ OPTIONS
The ``vaapi-copy`` function allows you to use vaapi with any VO. Because
this copies the decoded video back to system RAM, it's quite inefficient.
``--hwdec-codecs=<codec1,codec2,...|all>``
Allow hardware decoding for a given list of codecs only. The default is the
special value ``all``, which always allows all codecs.
.. note::
This is usually only needed with broken GPUs, where fallback on software
decoding does not work properly.
When using this switch, hardware decoding is still only done for some
codecs. See ``--hwdec-codecs`` to enable hardware decoding for more
codecs.
``--hwdec-codecs=<codec1,codec2,...|all>``
Allow hardware decoding for a given list of codecs only. The special value
``all`` always allows all codecs.
You can get the list of allowed codecs with ``mpv --vd=help``. Remove the
prefix, e.g. instead of ``lavc:h264`` use ``h264``.
By default this is set to ``h264,vc1,wmv3``.
This is usually only needed with broken GPUs, where a codec is reported
as supported, but decoding causes more problems than it solves.
.. admonition:: Example
@@ -1209,7 +1232,7 @@ OPTIONS
like any other binding). See `INPUT.CONF`_.
``--joystick``, ``--no-joystick``
Enable/disable joystick support. Enabled by default.
Enable/disable joystick support. Disabled by default.
``--no-keepaspect``, ``--keepaspect``
``--no-keepaspect`` will always stretch the video to window size, and will
@@ -1286,12 +1309,21 @@ OPTIONS
``--media-keys``, ``--no-media-keys``
OSX only: Enabled by default. Enables/disable media keys support.
``--merge-files``
Pretend that all files passed to mpv are concatenated into a single, big
file. This uses timeline/EDL support internally. Note that this won't work
for ordered chapter files or quvi-resolved URLs (such as youtube links).
This option is interpreted at program start, and doesn't affect for
example files or playlists loaded with the ``loadfile`` or ``loadlist``
commands.
``--mf=<option1:option2:...>``
Used when decoding from multiple PNG or JPEG files with ``mf://``.
Available options are:
:fps=<value>: output fps (default: 25)
:fps=<value>: output fps (default: 1)
:type=<value>: input file type (available: jpeg, png, tga, sgi)
``--monitoraspect=<ratio>``
@@ -1323,7 +1355,9 @@ OPTIONS
Control verbosity directly for each module. The ``all`` module changes the
verbosity of all the modules not explicitly specified on the command line.
See ``--msglevel=help`` for a list of all modules.
Run mpv with ``--msglevel=all=trace`` to see all messages mpv outputs. You
can use the module names printed in the output (prefixed to each line in
``[...]``) to limit the output to interesting modules.
.. note::
@@ -1334,17 +1368,18 @@ OPTIONS
Available levels:
:-1: complete silence
:0: fatal messages only
:1: error messages
:2: warning messages
:3: short hints
:4: informational messages
:5: status messages (default)
:6: verbose messages
:7: debug level 2
:8: debug level 3
:9: debug level 4
:no: complete silence
:fatal: fatal messages only
:error: error messages
:warn: warning messages
:info: informational messages
:status: status messages (default)
:v: verbose messages
:debug: debug messages
:trace: very noisy debug messages
One special case is the ``identify`` module name. This is silenced by
default, and can be enabled with the ``-identify`` option.
``--msgmodule``
Prepend module name in front of each console message.
@@ -1414,8 +1449,20 @@ OPTIONS
search for video segments from other files, and will also ignore any
chapter order specified for the main file.
``--ordered-chapters-files=<playlist-file>``
Loads the given file as playlist, and tries to use the files contained in
it as reference files when opening a Matroska file that uses ordered
chapters. This overrides the normal mechanism for loading referenced
files by scanning the same directory the main file is located in.
Useful for loading ordered chapter files that are not located on the local
filesystem, or if the referenced files are in different directories.
Note: a playlist can be as simple as a text file containing filenames
separated by newlines.
``--osc``, ``--no-osc``
Whether to load the on-screen-controller (default: no).
Whether to load the on-screen-controller (default: yes).
``--no-osd-bar``, ``--osd-bar``
Disable display of the OSD bar. This will make some things (like seeking)
@@ -1536,6 +1583,12 @@ OPTIONS
``--osd-scale=<factor>``
OSD font size multiplicator, multiplied with ``--osd-font-size`` value.
``--osd-scale-by-window=yes|no``
Whether to scale the OSD with the window size (default: yes). If this is
disabled, ``--osd-font-size`` and other OSD options that use scaled pixels
are always in actual pixels. The effect is that changing the window size
won't change the OSD font size.
``--osd-shadow-color=<#RRGGBB>, --sub-text-shadow-color=<#RRGGBB>``
See ``--osd-color``. Color used for OSD/sub text shadow.
@@ -1565,6 +1618,9 @@ OPTIONS
controls how much of the image is cropped. May not work with all video
output drivers.
``--pause``
Start the player in paused state.
``--playing-msg=<string>``
Print out a string after starting playback. The string is expanded for
properties, e.g. ``--playing-msg='file: ${filename}'`` will print the string
@@ -1605,12 +1661,6 @@ OPTIONS
FIXME: This needs to be clarified and documented thoroughly.
``--pp=<quality>``
See also ``--vf=pp``.
``--pphelp``
See also ``--vf=pp``.
``--priority=<prio>``
(Windows only.)
Set process priority for mpv according to the predefined priorities
@@ -1625,16 +1675,19 @@ OPTIONS
Use the given profile(s), ``--profile=help`` displays a list of the
defined profiles.
``--pts-association-mode=<auto|decode|sort>``
``--pts-association-mode=<decode|sort|auto>``
Select the method used to determine which container packet timestamp
corresponds to a particular output frame from the video decoder. Normally
you should not need to change this option.
:auto: Try to pick a working mode from the ones below automatically
(default)
:decoder: Use decoder reordering functionality.
:decoder: Use decoder reordering functionality. Unlike in classic MPlayer
and mplayer2, this includes a dTS fallback. (Default.)
:sort: Maintain a buffer of unused pts values and use the lowest value
for the frame.
:auto: Try to pick a working mode from the ones above automatically.
You can also try to use ``--no-correct-pts`` for files with completely
broken timestamps.
``--pvr=<option1:option2:...>``
This option tunes various encoding properties of the PVR capture module.
@@ -1700,6 +1753,15 @@ OPTIONS
Also see ``--really-quiet`` and ``--msglevel``.
``--quvi-fetch-subtitles=<yes|no>``
Toggles fetching of subtitles from streaming sites with libquvi. Disabled
by default, because it's unreliable and slow. Note that when enabled,
subtitles will always be fetched, even if subtitles are explicitly
disabled with ``--no-sub`` (because you might want to enable subtitles
at runtime).
Supported when using libquvi 0.9.x.
``--quvi-format=<best|default|...>``
Video format/quality that is directly passed to libquvi (default: ``best``).
This is used when opening links to streaming sites like YouTube. The
@@ -1815,6 +1877,11 @@ OPTIONS
- ``--reset-on-next-file=""``
Do not reset pause mode.
``--right-alt-gr``, ``--no-right-alt-gr``
(Cocoa and Windows only)
Use the right Alt key as Alt Gr to produce special characters. If disabled,
count the right Alt as an Alt modifier key. Enabled by default.
``--rtsp-transport=<lavf|udp|tcp|http>``
Select RTSP transport method (default: tcp). This selects the underlying
network transport when playing ``rtsp://...`` URLs. The value ``lavf``
@@ -1971,6 +2038,30 @@ OPTIONS
Specify the screen width for video output drivers which do not know the
screen resolution, like ``x11`` and TV-out.
``--secondary-sid=<ID|auto|no>``
Select a secondary subtitle stream. This is similar to ``--sid``. If a
secondary subtitle is selected, it will be rendered as toptitle (i.e. on
the top of the screen) alongside the normal subtitle, and provides a way
to render two subtitles at once.
there are some caveats associated with this feature. For example, bitmap
subtitles will always be rendered in their usual position, so selecting a
bitmap subtitle as secondary subtitle will result in overlapping subtitles.
Secondary subtitles are never shown on the terminal if video is disabled.
.. note::
Styling and interpretation of any formatting tags is disabled for the
secondary subtitle. Internally, the same mechanism as ``--no-ass`` is
used to strip the styling.
.. note::
If the main subtitle stream contains formatting tags which display the
subtitle at the top of the screen, it will overlap with the secondary
subtitle. To prevent this, you could use ``--no-ass`` to disable
styling in the main subtitle stream.
``--show-profile=<profile>``
Show the description and content of a profile.
@@ -2084,6 +2175,19 @@ OPTIONS
``--start='#2' --end='#4'``
Plays chapters 2 and 3, and exits.
``--stretch-dvd-subs=<yes|no>``
Stretch DVD subtitles when playing anamorphic videos for better looking
fonts on badly mastered DVDs. This switch has no effect when the
video is stored with square pixels - which for DVD input cannot be the case
though.
Many studios tend to use bitmap fonts designed for square pixels when
authoring DVDs, causing the fonts to look stretched on playback on DVD
players. This option fixes them, however at the price of possibly
misaligning sume subtitles (e.g. sign translations).
Disabled by default.
``--ssf=<mode>``
Specifies software scaler parameters.
@@ -2108,7 +2212,7 @@ OPTIONS
``--stop-screensaver``, ``--no-stop-screensaver``
Turns off the screensaver (or screen blanker and similar mechanisms) at
startup and turns it on again on exit (default: yes). The screensaver is
always reenabled when the player is paused.
always re-enabled when the player is paused.
This is not supported on all video outputs or platforms. Sometimes it is
implemented, but does not work (happens often on GNOME). You might be able
@@ -2281,7 +2385,7 @@ OPTIONS
Some ``--sws`` options are tunable. The description of the ``scale``
video filter has further information.
``--term-osd, --no-term-osd``
``--term-osd, --no-term-osd``, ``--term-osd=force``
Display OSD messages on the console when no video output is available.
Enabled by default.
@@ -2290,6 +2394,8 @@ OPTIONS
console. The escape sequence should move the pointer to the beginning of
the line used for the OSD and clear it (default: ``^[[A\r^[[K``).
``force`` enables terminal OSD even if a video window is created.
``--title=<string>``
Set the window title. Properties are expanded on playback start.
(See `Property Expansion`_.)
@@ -2521,6 +2627,13 @@ OPTIONS
See ``--vd=help`` for a full list of available decoders.
``--vd-lavc-check-hw-profile=<yes|no>``
Check hardware decoder profile (default: yes). If ``no`` is set, the
highest profile of the hardware decoder is unconditionally selected, and
decoding is forced even if the profile of the video is higher than that.
The result is most likely broken decoding, but may also help if the
detected or reported profiles are somehow incorrect.
``--vd-lavc-bitexact``
Only use bit-exact algorithms in all decoding steps (for codec testing).
@@ -2543,6 +2656,11 @@ OPTIONS
``--vd--lavc-o=debug=pict``
``--vd-lavc-show-all=<yes|no>``
Show even broken/corrupt frames (default: yes). If this option is set to
no, libavcodec won't output frames that were either decoded before an
initial keyframe was decoded, or frames that are recognized as corrupted.
``--vd-lavc-skiploopfilter=<skipvalue> (H.264 only)``
Skips the loop filter (AKA deblocking) during H.264 decoding. Since
the filtered frame is supposed to be used as reference for decoding
@@ -2591,7 +2709,7 @@ OPTIONS
to pad the video to screen if video and screen aspect ratios are different.
``--video-align-y=-1`` would move the video to the top of the screen
(leaving a border only on the bottom), a value of ``0`` centers it
(default), and a value of ``-1`` would put the video at the bottom of the
(default), and a value of ``1`` would put the video at the bottom of the
screen.
If video and screen aspect match perfectly, these options do nothing.
@@ -2617,6 +2735,10 @@ OPTIONS
``--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.)
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
disables scaling, disabling all aspect correction.
Note that the scaler algorithm may still be used, even if the video isn't
scaled. For example, this can influence chroma conversion.
@@ -2624,10 +2746,11 @@ OPTIONS
``--video-zoom=<value>``
Adjust the video display scale factor by the given value. The unit is in
fractions of original video size.
fractions of the (scaled) window video size.
For example, given a 1280x720 video, ``--video-zoom=-0.1`` would make the
video by 128 pixels smaller in X direction, and 72 pixels in Y direction.
For example, given a 1280x720 video shown in a 1280x720 window,
``--video-zoom=-0.1`` would make the video by 128 pixels smaller in
X direction, and 72 pixels in Y direction.
This option is disabled if the ``--no-keepaspect`` option is used.
+6 -2
View File
@@ -6,7 +6,7 @@ offer basic mouse-controllability. It is intended to make interaction easier
for new users and to enable precise and direct seeking.
The OSC is enabled by default if mpv was compiled with lua support. It can be
disabled using ``--osc=no``.
disabled entirely using the ``--osc=no`` option.
Using the OSC
-------------
@@ -113,7 +113,7 @@ time passed
============= ================================================
cache status
| Shows current cache fill status (only visible when below 48%)
| Shows current cache fill status (only visible when below 45%)
time remaining
| Shows remaining playback time timestamp
@@ -176,6 +176,10 @@ Configurable parameters
| Default: 0
| Horizontal alignment, -1 (left) to 1 (right)
``boxalpha``
| Default: 80
| Alpha of the background box, 0 (opaque) to 255 (fully transparent)
``hidetimeout``
| Default: 500
| Duration in ms until the OSC hides if no mouse movement, negative value
+77 -89
View File
@@ -7,10 +7,11 @@ syntax is:
``--vf=<filter1[=parameter1:parameter2:...],filter2,...>``
Setup a chain of video filters.
Many parameters are optional and set to default values if omitted. To
explicitly use a default value, set a parameter to '-1'. Parameters ``w:h``
means width x height in pixels, ``x:y`` means x;y position counted from the
upper left corner of the bigger image.
You can also set defaults for each filter. The defaults are applied before the
normal filter parameters.
``--vf-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
Set defaults for each filter.
.. note::
@@ -74,7 +75,7 @@ Available filters are:
.. admonition:: Example
``expand=800:::::4/3``
``expand=800::::4/3``
Expands to 800x600, unless the source is higher resolution, in
which case it expands to fill a 4/3 aspect.
@@ -82,7 +83,7 @@ Available filters are:
Rounds up to make both width and height divisible by <r> (default: 1).
``flip``
Flips the image upside down. See also ``--flip``.
Flips the image upside down.
``mirror``
Mirrors the image on the Y axis.
@@ -150,7 +151,7 @@ Available filters are:
:0: Disable accurate rounding (default).
:1: Enable accurate rounding.
``dsize[=aspect|w:h:aspect-method:r]``
``dsize[=w:h:aspect-method:r:aspect]``
Changes the intended display size/aspect at an arbitrary point in the
filter chain. Aspect can be given as a fraction (4/3) or floating point
number (1.33). Alternatively, you may specify the exact display width and
@@ -199,6 +200,9 @@ Available filters are:
Rounds up to make both width and height divisible by ``<r>``
(default: 1).
``<aspect>``
Force an aspect ratio.
``format[=fmt[:outfmt]]``
Restricts the color space for the next filter without doing any conversion.
Use together with the scale filter for a real conversion.
@@ -232,7 +236,7 @@ Available filters are:
``<fmt>``
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
``pp[=filter1[:option1[:option2...]]/[-]filter2...]``
``pp[=[filter1[:option1[:option2...]]/[-]filter2...]]``
Enables the specified chain of postprocessing subfilters. Subfilters must
be separated by '/' and can be disabled by prepending a '-'. Each
subfilter and some options have a short and a long name that can be used
@@ -250,7 +254,12 @@ Available filters are:
.. note::
``--pphelp`` shows a list of available subfilters.
``--vf=pp:help`` shows a list of available subfilters.
.. note::
Unlike in MPlayer or in earlier versions, you must quote the pp string
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
Available subfilters are:
@@ -364,14 +373,14 @@ Available filters are:
``--vf=pp=de/-al``
default filters without brightness/contrast correction
``--vf=pp=default/tmpnoise:1:2:3``
``--vf=pp=[default/tmpnoise:1:2:3]``
Enable default filters & temporal denoiser.
``--vf=pp=hb:y/vb:a``
``--vf=pp=[hb:y/vb:a]``
Horizontal deblocking on luminance only, and switch vertical
deblocking on or off automatically depending on available CPU time.
``lavfi=graph[:sws_flags[:o=opts]]``
``lavfi=graph[:sws-flags[:o=opts]]``
Filter video using FFmpeg's libavfilter.
``<graph>``
@@ -405,7 +414,7 @@ Available filters are:
``'--vf=lavfi=graph="gradfun=radius=30:strength=20,vflip"'``
Same as before, but uses named parameters for everything.
``<sws_flags>``
``<sws-flags>``
If libavfilter inserts filters for pixel format conversion, this
option gives the flags which should be passed to libswscale. This
option is numeric and takes a bit-wise combination of ``SWS_`` flags.
@@ -420,21 +429,30 @@ Available filters are:
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
forces a specific threading configuration.
``noise[=luma[u][t|a][h][p]:chroma[u][t|a][h][p]]``
``noise[=<strength>[:averaged][:pattern][:temporal][:uniform][:hq]``
Adds noise.
:<0-100>: luma noise
:<0-100>: chroma noise
:u: uniform noise (gaussian otherwise)
:t: temporal noise (noise pattern changes between frames)
:a: averaged temporal noise (smoother, but a lot slower)
:h: high quality (slightly better looking, slightly slower)
:p: mix random noise with a (semi)regular pattern
``strength``
Set the noise for all components. If you want different strength
values for luma and chroma, use libavfilter's noise filter directly
(using ``--vf=lavfi=[noise=...]``), or tell the libavfilter developers
to stop being stupid.
.. note::
``averaged``
averaged temporal noise (smoother, but a lot slower)
Deprecated. Use libavfilter's ``noise`` filter through ``--vf=lavfi``
instead.
``pattern``
mix random noise with a (semi)regular pattern
``temporal``
temporal noise (noise pattern changes between frames)
``uniform``
uniform noise (gaussian otherwise)
``hq``
high quality (slightly better looking, slightly slower) - not available
when using libavfilter
``hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp]``
This filter aims to reduce image noise producing smooth images and making
@@ -450,11 +468,6 @@ Available filters are:
chroma temporal strength (default:
``luma_tmp*chroma_spatial/luma_spatial``)
.. note::
Deprecated. Use libavfilter's ``hqdn3d`` filter through ``--vf=lavfi``
instead.
``eq[=gamma:contrast:brightness:saturation:rg:gg:bg:weight]``
Software equalizer that uses lookup tables (slow), allowing gamma correction
in addition to simple brightness and contrast adjustment. The parameters are
@@ -494,39 +507,26 @@ Available filters are:
:0: nearest-neighbor sampling, fast but incorrect
:1: linear interpolation (default)
``unsharp[=l|cWxH:amount[:l|cWxH:amount]]``
``unsharp[=lx:ly:la:cx:cy:ca]``
unsharp mask / gaussian blur
``l``
Apply effect on luma component.
``l`` is for the luma component, ``c`` for the chroma component. ``x``/``y``
is the filter size. ``a`` is the amount.
``c``
Apply effect on chroma components.
``<width>x<height>``
``lx``, ``ly``, ``cx``, ``cy``
width and height of the matrix, odd sized in both directions (min =
3x3, max = 13x11 or 11x13, usually something between 3x3 and 7x7)
3:3, max = 13:11 or 11:13, usually something between 3:3 and 7:7)
``amount``
``la``, ``ca``
Relative amount of sharpness/blur to add to the image (a sane range
should be -1.5-1.5).
:<0: blur
:>0: sharpen
.. note::
Deprecated. Use libavfilter's ``unsharp`` filter through ``--vf=lavfi``
instead.
``swapuv``
Swap U & V plane.
.. note::
Deprecated. Use libavfilter's ``swapuv`` filter through ``--vf=lavfi``
instead.
``pullup[=jl:jr:jt:jb:sb:mp]``
Pulldown reversal (inverse telecine) filter, capable of handling mixed
hard-telecine, 24000/1001 fps progressive, and 30000/1001 fps progressive
@@ -551,7 +551,7 @@ Available filters are:
frames in the output.
``mp`` (metric plane)
This option may be set to 1 or 2 to use a chroma plane instead of the
This option may be set to ``u`` or ``v`` to use a chroma plane instead of the
luma plane for doing ``pullup``'s computations. This may improve accuracy
on very clean source material, but more likely will decrease accuracy,
especially if there is chroma noise (rainbow effect) or any grayscale
@@ -670,41 +670,36 @@ Available filters are:
``v``
Verbose operation. Prints the selected mode for each frame and the
average squared difference between fields for ``t``, ``b``, and ``p``
alternatives.
alternatives. (Ignored when libavfilter is used.)
``yadif=[mode[:enabled=yes|no]]``
Yet another deinterlacing filter
``<mode>``
:0: Output 1 frame for each frame.
:1: Output 1 frame for each field.
:2: Like 0 but skips spatial interlacing check.
:3: Like 1 but skips spatial interlacing check.
:frame: Output 1 frame for each frame.
:field: Output 1 frame for each field.
:frame-nospatial: Like ``frame`` but skips spatial interlacing check.
:field-nospatial: Like ``field`` but skips spatial interlacing check.
``<enabled>``
:yes: Filter is active (default).
:no: Filter is not active, but can be activated with the ``D`` key
(or any other key that toggles the ``deinterlace`` property).
.. note::
Deprecated. Use libavfilter's ``yadif`` filter through ``--vf=lavfi``
instead.
This filter, or libavfilter's implementation if available, is automatically
inserted when using the ``D`` key (or any other key that toggles the
``deinterlace`` property), assuming the video output does not have native
This filter, is automatically inserted when using the ``D`` key (or any
other key that toggles the ``deinterlace`` property or when using the
``--deinterlace`` switch), assuming the video output does not have native
deinterlacing support.
``down3dright[=lines]``
Reposition and resize stereoscopic images. Extracts both stereo fields and
places them side by side, resizing them to maintain the original movie
aspect.
If you just want to set the default mode, put this filter and its options
into ``--vf-defaults`` instead, and enable deinterlacing with ``D`` or
``--deinterlace``.
``<lines>``
number of lines to select from the middle of the image (default: 12)
Also note that the ``D`` key is stupid enough to insert an interlacer twice
when inserting yadif with ``--vf``, so using the above methods is
recommended.
``delogo[=x:y:w:h:t]``
``delogo[=x:y:w:h:t:show]``
Suppresses a TV station logo by a simple interpolation of the surrounding
pixels. Just set a rectangle covering the logo and watch it disappear (and
sometimes something even uglier appear - your mileage may vary).
@@ -721,11 +716,9 @@ Available filters are:
You can specify a text file to load the coordinates from. Each line
must have a timestamp (in seconds, and in ascending order) and the
``x:y:w:h:t`` coordinates (``t`` can be omitted).
.. note::
Deprecated. Use libavfilter's ``delogo`` or ``removelogo`` filters
through ``--vf=lavfi`` instead.
(Not supported when using libavfilter.)
``show``
Draw a rectangle showing the area defined by x/y/w/h.
``screenshot``
Optional filter for screenshot support. This is only needed if the video
@@ -818,29 +811,24 @@ Available filters are:
``mr`` or ``mono_right``
mono output (right eye only)
.. note::
Deprecated. Use libavfilter's ``stereo3d`` filter through ``--vf=lavfi``
instead.
``gradfun[=strength[:radius]]``
``gradfun[=strength[:radius|:size=<size>]]``
Fix the banding artifacts that are sometimes introduced into nearly flat
regions by truncation to 8bit color depth. Interpolates the gradients that
should go where the bands are, and dithers them.
``<strength>``
Maximum amount by which the filter will change any one pixel. Also the
threshold for detecting nearly flat regions (default: 1.2).
threshold for detecting nearly flat regions (default: 1.5).
``<radius>``
Neighborhood to fit the gradient to. Larger radius makes for smoother
gradients, but also prevents the filter from modifying pixels near
detailed regions (default: 16).
detailed regions (default: disabled).
.. note::
``<size>``
size of the filter in percent of the image diagonal size. This is
used to calculate the final radius size (default: 1).
Deprecated. Use libavfilter's ``gradfun`` filter through ``--vf=lavfi``
instead.
``dlopen=dll[:a0[:a1[:a2[:a3]]]]``
Loads an external library to filter the image. The library interface
@@ -863,10 +851,10 @@ Available filters are:
Specify the fourth parameter to pass to the library.
``vavpp``
VA-API video post processing. Works with ``--vo=vaapi`` only. Currently
deinterlaces. This filter is automatically inserted if deinterlacing is
requested (either using the ``D`` key, by default mapped to the command
``cycle deinterlace``, or the ``--deinterlace`` option).
VA-AP-API video post processing. Works with ``--vo=vaapi`` and ``--vo=opengl``
only. Currently deinterlaces. This filter is automatically inserted if
deinterlacing is requested (either using the ``D`` key, by default mapped to
the command ``cycle deinterlace``, or the ``--deinterlace`` option).
``deint=<method>``
Select the deinterlacing algorithm.
+24 -6
View File
@@ -10,6 +10,12 @@ syntax is:
If the list has a trailing ',', mpv will fall back on drivers not contained
in the list. Suboptions are optional and can mostly be omitted.
You can also set defaults for each driver. The defaults are applied before the
normal driver parameters.
``--vo-defaults=<driver1[:parameter1:parameter2:...],driver2,...>``
Set defaults for each driver.
.. note::
See ``--vo=help`` for a list of compiled-in video output drivers.
@@ -241,6 +247,11 @@ Available video output drivers are:
Some features are available with OpenGL 3 capable graphics drivers only
(or if the necessary extensions are available).
Hardware decoding over OpenGL-interop is supported to some degree. Note
that in this mode, some corner case might not be gracefully handled, and
colorspace conversion and chroma upsampling is generally in the hand of
the hardware decoder APIs.
``lscale=<filter>``
``bilinear``
@@ -320,8 +331,10 @@ Available video output drivers are:
slightly different gamma.
``pbo``
Enable use of PBOs. This is faster, but can sometimes lead to sporadic
and temporary image corruption.
Enable use of PBOs. This is slightly faster, but can sometimes lead to
sporadic and temporary image corruption (in theory, because reupload
is not retried when it fails), and perhaps actually triggers slower
paths with drivers that don't support PBOs properly.
``dither-depth=<N|no|auto>``
Set dither target depth to N. Default: no.
@@ -422,8 +435,8 @@ Available video output drivers are:
Selects the internal format of textures used for FBOs. The format can
influence performance and quality of the video output. (FBOs are not
always used, and typically only when using extended scalers.)
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb16, rgb16f, rgb32f,
rgba12, rgba16, rgba16f, rgba32f.
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb10_a2, rgb16, rgb16f,
rgb32f, rgba12, rgba16, rgba16f, rgba32f.
Default: rgb.
``gamma``
@@ -473,12 +486,17 @@ Available video output drivers are:
Set the YUV chroma sample location. auto means use the bitstream
flags (default: auto).
``rectangle-textures``
Force use of rectangle textures (default: no). Normally this shouldn't
have any advantages over normal textures. Note that hardware decoding
overrides this flag.
``opengl-hq``
Same as ``opengl``, but with default settings for high quality rendering.
This is equivalent to::
--vo=opengl:lscale=lanczos2:dither-depth=auto:pbo:fbo-format=rgb16
--vo=opengl:lscale=lanczos2:dither-depth=auto:fbo-format=rgb16
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
@@ -776,4 +794,4 @@ Available video output drivers are:
``alpha``
Use a buffer format that supports videos and images with alpha
informations
information
+36 -25
View File
@@ -2,11 +2,12 @@ NOTE: DOCS/OUTDATED-tech/* may contain more detailed information, but most of it
is possibly or definitely outdated. This file intends to give a big
picture of how mpv is structured.
mpvcore/mplayer.c:
This contains the main play loop, anything related to mpv and playback
related initializations. It also contains the main function. Generally, it
accesses all other subsystems, initializes them, and pushes data between
them during playback.
player/*.c:
Essentially makes up the player applications, including the main() function
and the playback loop.
Generally, it accesses all other subsystems, initializes them, and pushes
data between them during playback.
The structure is as follows (as of commit e13c05366557cb):
* main():
@@ -42,7 +43,7 @@ mpvcore/mplayer.c:
- the other subsystems rarely call back into the frontend, and the frontend
polls them instead (probably a good thing)
I like to call mplayer.c (and some other files) the "frontend".
I like to call the player/*.c files the "frontend".
talloc.h & talloc.c:
Hierarchical memory manager copied from Samba. It's like a malloc() with
@@ -82,37 +83,47 @@ talloc.h & talloc.c:
rid of the talloc emulation later and use TA natively.
(See ta/README for details.)
mpvcore/mp_core.h:
Data structures for mplayer.c and command.c. They are usually not accessed
by other parts of mpv for the sake of modularization.
player/command.c:
This contains the implementation for slave commands and properties.
Properties are essentially dynamic variables changed by certain commands.
This is basically responsible for all user commands, like initiating
seeking, switching tracks, etc. It calls into other player/*.c files,
where most of the work is done, but also calls other parts of mpv.
player/core.h:
Data structures and function prototypes for most of player/*.c. They are
usually not accessed by other parts of mpv for the sake of modularization.
Note that there are lots of global variables floating around everywhere
else. This is an ongoing transition, and eventually there should be no
global variables anymore.
options.h contains the global option struct MPOpts, and its default values
are in defaultopts.c for some reason.
options/options.h, options/options.c
options.h contains the global option struct MPOpts. The option declarations
(option names, types, and MPOpts offsets for the option parser) are in
options.c. Most default values for options and MPOpts are in
mp_default_opts at the end of options.c.
mpvcore/input/input.c:
MPOpts is unfortunarely quite monolithic, and virtually accessed by
everything.But some components (like video outputs and video filters) have
their own sub-option tables separate from MPOpts.
The actual option parser is spread over m_option.c, m_config.c, and
parser-mpcmd.c, and uses the option table in options.c.
input/input.c:
This translates keyboard input comming from libvo and other sources (such
as remote control devices like Apple IR or slave mode commands) to the
key bindings listed in the user's (or the builtin) input.conf and turns
them into items of type struct mp_cmd. These commands are queued, and read
by mplayer.c. They get pushed with run_command() to command.c.
by playloop.c. They get pushed with run_command() to command.c.
Note that keyboard input and slave mode input are essentially the same
things. Just looking at input.conf should make this clear. (The other
direction of slave mode communication, mpv to application, consists of
random mp_msg() calls all over the code in all parts of the player.)
mpvcore/command.c:
This contains the implementation for slave commands and properties.
Properties are essentially dynamic variables changed by certain commands.
This is basically responsible for all user commands, like initiating
seeking, switching tracks, etc. It calls into mplayer.c, where most of the
work is done, but also into other parts of mpv.
mpvcore/mp_msg.h:
common/msg.h:
All terminal output should go through mp_msg().
stream/*:
@@ -188,8 +199,8 @@ audio/decode/:
audio/filter/:
Audio filter chain. af_lavrresample is inserted if any form of conversion
between audio formats is needed. (af_format.c is also used for some corner
cases.)
between audio formats is needed. (af_convert24.c and af_convertsignendian.c
are also used for some formats not directly supported by FFmpeg.)
audio/out/:
Audio outputs.
@@ -229,8 +240,8 @@ sub/:
detection as well as timing postprocessing work. (Timing postprocessing
removes tiny gaps or overlaps between subtitle events.)
mpvcore/timeline/:
A timeline is the abstraction used by mplayer.c to combine several files
player/timeline/:
A timeline is the abstraction used by loadfile.c to combine several files
into one seemingly linear video. It's mainly used for ordered chapters
playback. The high level code to find and load other files containing the
segments for playing an ordered chapters file is in tl_matroska.c.
+156
View File
@@ -0,0 +1,156 @@
waf build system overview
=========================
mpv's new build system is based on waf and it should completly replace the
custom ./configure + Makefile based system inherited from MPlayer.
Goals and the choice of waf
===========================
The new system comes with some goals, which can be summed up as: be as good as
the old one at what it did well (customizability) and fix some of it's major
shortcomings:
1) The build system must be uniform in how it handles any single feature check.
Repetition and boilerplate have to be avoided.
When adding a new feature using the old configure, one had to add a fair
amount of code to the shell script to do option parsing, detection of the
feature and declaration of variables for the Makefile to pickup. The worst
part is this pieces are spread apart in the configure and copy pasted for
any single case. That brings us to..
2) --enable-feature has to override the user and help him understand that he
has libraries missing and should install them for the feature to be enabled.
3) Must be customizable, hackable, pleasant to the developer eyes and to work
with in general.
4) Must have separate configuration and build steps.
Goal 2 comes as a given on pretty much any build system, since autotools made
this behaviour very popular among users (and rightly so).
Goal 1+3 were somewhat harder to accomplish as it looks like all of the build
systems we evaluated (waf included!) had problems with them. For reference we
had proof of concept build systems with waf, CMake and autotools.
What puts waf apart from CMake and autotools, is that projects using it use
Python to program their build system. Also while the Waf Book shows really
simple API usages, you can write your own build system on top of waf that is
tailored to the project's specific needs.
mpv's custom configure step on top of waf
=========================================
To some extents mpv has a custom build system written on top of waf. This
document will not go over the standard waf behaviour as that is documented in
the ``Waf book``.
All of the configuration process is handled with a declarative approach. Lists
of dictionaries define the checks, and some custom Python code traverses these
lists and depending on the check definition it calls into the actual waf API.
A simple example using pkg-config would be::
{
'name': '--vdpau',
'desc': 'VDPAU acceleration',
'deps': [ 'x11' ],
'func': check_pkg_config('vdpau', '>= 0.2'),
}
This defines a feature called ``vdpau`` which can be enabled or disabled by
the users with configure flags (that's the meaning of ``--``). This feature
depends on another feature whose name is ``x11``, and the autodetection check
consists of running ``pkg-config`` and looking for ``vdpau`` with version
``>= 0.2``. If the check succeds a ``#define HAVE_VDPAU 1`` will be added to
``config.h``, if not ``#define HAVE_VDPAU 0`` will be added.
The defines names are automatically prepended with ``HAVE_``, capitalized and
special characters are replaced with underscores. This happens in
``waftools/inflectors.py``.
Mandatory fields:
-----------------
``name``: indicates the unique identifier used by the custom dependency code
to refer to a feature. If the unique identifier is prepended with ``--``
the build system will also generate options for ``./waf configure`` so that
the feature can be enabled and disabled.
``desc``: this is the textual representation of the feature used in the
interactions with the users.
``func``: function that will perform the check. These functions are defined in
``waftools/checks``. The reusable checks are all functions that return
functions. The return functions will then be applied using waf's configuration
context.
The source code for the reusable checks is a bit convoluted, but it should be
easy to pick up their usage from the ``wscript``. Their signature mirrors
the semantics of some of the shell functions used in mplayer.
If someone expresses some interest, I will extend this document with official
documentation for each check function.
Optional fields
---------------
``deps``: list of dependencies of this feature. It is a list of names of
other features as defined in the ``name`` field (minus the eventual leading
``--``). All of the dependencies must be satisfied. If they are not the check
will be skipped without even running ``func``.
``deps_any``: like deps but it is satisfied even if only one of the dependencies
is satisfied. You can think of ``deps`` as a 'and' condition and ``deps_any``
as a 'or' condition.
``deps_neg``: like deps but it is satisfied when none of the dependencies is
satisfied.
``req``: defaults to False. If set to True makes this feature a hard
dependency of mpv (configuration will fail if autodetection fails). If set to
True you must also provide ``fmsg``.
``fmsg``: string with the failure message in case a required dependency is not
satisfied.
``os_specific_checks``: this takes a dictionary that has ``os-`` dependencies
as keys (such as ``os-win32``), and by values has another dictionary that is
merged on top of the current feature definition only for that specific OS.
For example::
{
'name': '--pthreads',
'desc': 'POSIX threads',
'func': check_pthreads,
'os_specific_checks': {
'os-win32': {
'func': check_pthreads_w32_static.
}
}
}
will override the value of ``func`` with ``check_pthreads_w32_static`` only
if the target OS of the build is Windows.
``groups``: groups a dependency with another one. This can be used to disabled
all the grouped dependencies with one ``--disable-``. At the moment this is
only used for OpenGL backends, where you want to disable them when
``--disable-gl`` is passed to the configure.
mpv's custom build step on top of waf
=====================================
Build step is pretty much vanilla waf. The only difference being that the list
of source files can contain both strings or tuples. If a tuple is found,
the second element in the tuple will the used to match the features detected
in the configure step (the ``name`` field described above). If this feature
was not enabled during configure, the source file will not be compiled in.
All of the custom Python for this is inside the function ``filtered_sources``
contained in the file ``waftools/dependencies.py``.
Also ``dependencies_use`` and ``dependencies_includes`` collect cflags and
ldflags that were generated from the features checks in the configure step.
+29 -20
View File
@@ -16,12 +16,29 @@ Compilation
-----------
Compiling with full features requires development files for several
external libraries. Below is a list of some important requirements. For
more information see the output of `./configure --help` for a list of options,
or look at the list of enabled and disabled features printed after running
`./configure`. If you think you have support for some feature installed
but configure fails to detect it, the file `config.log` may contain
information about the reasons for the failure.
external libraries. Below is a list of some important requirements.
The mpv build system uses *waf* but we don't store it in your source tree. The
script './bootstrap.py' will download the latest version of waf that was tested
with the build system.
For a list of the available build options use `./waf configure --help`. If
you think you have support for some feature installed but configure fails to
detect it, the file `build/config.log` may contain information about the
reasons for the failure.
NOTE: To avoid cluttering the output with unreadable spam, `--help` only shows
one of the two switches for each option. If the option is autodetected by
default, the `--disable-***` switch is printed; if the option is disabled by
default, the `--enable-***` switch is printed. Either way, you can use
`--enable-***` or `--disable-**` regardless of what is printed by `--help`.
To build the software you can use `./waf build`: the result of the compilation
will be located in `build/mpv`. You can use `./waf install` to install mpv
to the *prefix* after it is compiled.
NOTE: Using the old build system (with `./old-configure`) should still work,
but will be removed in a future version of mpv.
Essential dependencies (incomplete list):
@@ -47,21 +64,12 @@ 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.
### configure `--enable-*` parameters
FFmpeg vs. Libav
----------------
The `--enable-*` parameters unconditionally force options on, completely
skipping autodetection. This behavior is unlike what you may be used to from
autoconf-based configure scripts that can decide to override you. This greater
level of control comes at a price. You may have to provide the correct compiler
and linker flags yourself.
If you used one of these options and experience a compilation or
linking failure, make sure you have passed the necessary compiler/linker flags
to configure.
mpv's configure script is greedy and automatically enables features as a result
of autodetection. The cases where you may want to use `--enable-*` are very
limited.
Generally, mpv should work with the latest release as well as the git version
of both FFmpeg and Libav. But FFmpeg is preferred, and some mpv features work
with FFmpeg only. See the [wiki article][ffmpeg_vs_libav] about the issue.
Bug reports
-----------
@@ -97,3 +105,4 @@ To contact the `mpv` team in private write to `mpv-team@googlegroups.com`.
[issue-tracker]: https://github.com/mpv-player/mpv/issues
[mpv-users]: https://groups.google.com/forum/?hl=en#!forum/mpv-users
[mpv-devel]: https://groups.google.com/forum/?hl=en#!forum/mpv-devel
[ffmpeg_vs_libav]: https://github.com/mpv-player/mpv/wiki/FFmpeg-versus-Libav
+41
View File
@@ -0,0 +1,41 @@
Changes
-------
- Build system switches to waf. The old build system is still available by
running ``./old-configure``, but it will be removed some time in the future.
- The "run" input command changes in a not backwards compatible way.
- Some input.conf commands were replaced (such as speed_mult).
- Many audio/video filter have changed sub-options in a not backwards
compatible way. (This is a consequence of unifying audio/video filter
option parsing.)
- Most video filters now use libavfilter internally if available.
- Disable key autorepeat for some key binds, which prevents accidentally
triggering a binding multiple times.
- Encoding: install and include encoding profiles by default.
Bug fixes
---------
- Fix a bug that degraded vdpau playback after ~1 hour of runtime.
- Restrict hardware decoding to codecs where it generally makes sense. This
excludes mpeg4 for example, which often causes hardware decoders to decode to
garbage. Use ``--hwdec-codecs`` to enable hardware decoding for more codecs.
New features
------------
- Add window-scale property, which allows changing the window size via mpv key
bindings.
- Add revert_seek command, which is meant to undo accidental seeks.
- Hardware decoding (vdpau, vda, vaapi) can now be used with vo_opengl.
- Pulseaudio: set pulse stream name to mpv window title (like MPlayer).
- Support showing 2 subtitles at a time (``--secondary-sid``).
- Re-add some basic ``dvdnav://`` support. This doesn't handle navigation
highlights correctly, but navigation should mostly work. (Note that
``dvdnav://`` by default starts the longest title, instead of the DVD menu.
The menu is available with ``dvdnav://menu``.)
- Win32: you can drop files on the mpv window.
This listing is not complete. There are many more bug fixes and changes. The
complete change log can be viewed by running ``git log f572d2..f06a87`` in
the git repository.
+3 -3
View File
@@ -56,7 +56,7 @@ sub generate_c_header {
my $l = max(map { length $_->{valname} } values %{$el->{subelements}});
# Output each variable, with pointers for array (multiple) elements
for my $subel (values %{$el->{subelements}}) {
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
printf " %-${l}s %s%s;\n",
$subel->{valname}, $subel->{multiple}?'*':' ', $subel->{fieldname};
}
@@ -101,7 +101,7 @@ sub generate_c_definitions {
# define a field for each subelement
# also does lots of macro magic, but doesn't open a scope
for my $subel (values %{$el->{subelements}}) {
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
print "F($subel->{definename}, $subel->{fieldname}, ".
($subel->{multiple}?'1':'0').")\n";
}
@@ -166,4 +166,4 @@ sub process_elem {
} else {
print "$elem->{type} ". $elem->get_value ."\n";
}
}
}
+1 -1
View File
@@ -104,7 +104,7 @@ for __midentify__key in $__midentify__allprops; do
eval unset $__midentify__nextprefix$__midentify__key
done
__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet "$@"`
__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet --no-cache --no-config "$@"`
__midentify__fileindex=0
__midentify__prefix=
while :; do
+13 -9
View File
@@ -30,24 +30,28 @@ def user_dylib_lst(input_file):
return [lib for lib in dylib_lst(input_file).split("\n") if
is_user_lib(lib, input_file)]
def bundle_name(binary_name):
def bundle_path(binary_name):
return "%s.app" % binary_name
def bundle_name(binary_name):
return os.path.basename(bundle_path(binary_name))
def target_plist(binary_name):
return os.path.join(bundle_name(binary_name), 'Contents', 'Info.plist')
return os.path.join(bundle_path(binary_name), 'Contents', 'Info.plist')
def target_directory(binary_name):
return os.path.join(bundle_name(binary_name), 'Contents', 'MacOS')
return os.path.join(bundle_path(binary_name), 'Contents', 'MacOS')
def target_binary(binary_name):
return os.path.join(target_directory(binary_name), binary_name)
return os.path.join(target_directory(binary_name),
os.path.basename(binary_name))
def copy_bundle(binary_name):
if os.path.isdir(bundle_name(binary_name)):
shutil.rmtree(bundle_name(binary_name))
if os.path.isdir(bundle_path(binary_name)):
shutil.rmtree(bundle_path(binary_name))
shutil.copytree(
os.path.join('TOOLS', 'osxbundle', bundle_name(binary_name)),
bundle_name(binary_name))
bundle_path(binary_name))
def copy_binary(binary_name):
shutil.copy(binary_name, target_binary(binary_name))
@@ -91,7 +95,7 @@ def fix_dylibs_paths(target_file, dest_dir, root=True):
fix_dylibs_paths(dylib_dest_path, dest_dir, False)
def apply_plist_template(plist_file, version):
sh("sed -i -e 's/{{VERSION}}/%s/g' %s" % (version, plist_file))
sh("sed -i -e 's/${VERSION}/%s/g' %s" % (version, plist_file))
def bundle_dependencies(binary_name):
lib_bundle_directory = os.path.join(target_directory(binary_name), "lib")
@@ -99,7 +103,7 @@ def bundle_dependencies(binary_name):
fix_dylibs_paths(target_binary(binary_name), lib_bundle_directory)
def main():
version = sh("TOOLS/osxbundle/version.sh").strip()
version = sh("./version.sh --print").strip()
usage = "usage: %prog [options] arg"
parser = OptionParser(usage)
+1 -1
View File
@@ -183,7 +183,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{{VERSION}}</string>
<string>${VERSION}</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleURLTypes</key>
@@ -1,120 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/System/Library/Fonts</dir>
<dir>/Library/Fonts</dir>
<dir>~/Library/Fonts</dir>
<dir>/Network/Library/Fonts</dir>
<cachedir>~/.fontconfig</cachedir>
<!--
Accept deprecated 'mono' alias, replacing it with 'monospace'
-->
<match target="pattern">
<test qual="any" name="family">
<string>mono</string>
</test>
<edit name="family" mode="assign" binding="same">
<string>monospace</string>
</edit>
</match>
<!--
Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
-->
<match target="pattern">
<test qual="any" name="family">
<string>sans serif</string>
</test>
<edit name="family" mode="assign" binding="same">
<string>sans-serif</string>
</edit>
</match>
<!--
Accept deprecated 'sans' alias, replacing it with 'sans-serif'
-->
<match target="pattern">
<test qual="any" name="family">
<string>sans</string>
</test>
<edit name="family" mode="assign" binding="same">
<string>sans-serif</string>
</edit>
</match>
<config>
<!--
These are the default Unicode chars that are expected to be blank
in fonts. All other blank chars are assumed to be broken and
won't appear in the resulting charsets
-->
<blank>
<int>0x0020</int> <!-- SPACE -->
<int>0x00A0</int> <!-- NO-BREAK SPACE -->
<int>0x00AD</int> <!-- SOFT HYPHEN -->
<int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
<int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
<int>0x0601</int> <!-- ARABIC SIGN SANAH -->
<int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
<int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
<int>0x06DD</int> <!-- ARABIC END OF AYAH -->
<int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
<int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
<int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
<int>0x1680</int> <!-- OGHAM SPACE MARK -->
<int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
<int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
<int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
<int>0x2000</int> <!-- EN QUAD -->
<int>0x2001</int> <!-- EM QUAD -->
<int>0x2002</int> <!-- EN SPACE -->
<int>0x2003</int> <!-- EM SPACE -->
<int>0x2004</int> <!-- THREE-PER-EM SPACE -->
<int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
<int>0x2006</int> <!-- SIX-PER-EM SPACE -->
<int>0x2007</int> <!-- FIGURE SPACE -->
<int>0x2008</int> <!-- PUNCTUATION SPACE -->
<int>0x2009</int> <!-- THIN SPACE -->
<int>0x200A</int> <!-- HAIR SPACE -->
<int>0x200B</int> <!-- ZERO WIDTH SPACE -->
<int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
<int>0x200D</int> <!-- ZERO WIDTH JOINER -->
<int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
<int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
<int>0x2028</int> <!-- LINE SEPARATOR -->
<int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
<int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
<int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
<int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
<int>0x202D</int> <!-- LEFT-TO-RIGHT OVERRIDE -->
<int>0x202E</int> <!-- RIGHT-TO-LEFT OVERRIDE -->
<int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
<int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
<int>0x2060</int> <!-- WORD JOINER -->
<int>0x2061</int> <!-- FUNCTION APPLICATION -->
<int>0x2062</int> <!-- INVISIBLE TIMES -->
<int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
<int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
<int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
<int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
<int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
<int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
<int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
<int>0x2800</int> <!-- BRAILLE PATTERN BLANK -->
<int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
<int>0x3164</int> <!-- HANGUL FILLER -->
<int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
<int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
<int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
<int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
<int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
</blank>
<!--
Rescan configuration every 30 seconds when FcFontSetList is called
-->
<rescan>
<int>30</int>
</rescan>
</config>
</fontconfig>
-15
View File
@@ -1,15 +0,0 @@
#!/bin/sh
# Extract revision number from file used by daily tarball snapshots
# or from "git describe" output
git_revision=$(cat snapshot_version 2> /dev/null)
test $git_revision || test ! -d .git || \
git_revision=`git describe --match "v[0-9]*" --always`
git_revision=$(expr "$git_revision" : v*'\(.*\)')
test $git_revision || git_revision=UNKNOWN
# releases extract the version number from the VERSION file
version=$(cat VERSION 2> /dev/null)
test $version || version=$git_revision
echo $version
+15 -13
View File
@@ -3,20 +3,18 @@
"""
This script emulates "unique application" functionality on Linux. When starting
playback with this script, it will try to reuse an already running instance of
mpv (but only if that was started with umpv).
mpv (but only if that was started with umpv). Other mpv instances (not started
by umpv) are ignored, and the script doesn't know about them.
This script only uses mpv instances started with umpv. Other instances are
ignored, and don't exist for the script.
This only takes filenames as arguments. If mpv is already running, they are
appended to mpv's internal playlist. If the file does not exist or is otherwise
not playable, mpv will skip the playlist entry when attempting to play it (from
the GUI perspective, it's silently ignored).
This only takes filenames as arguments. Custom options can't be used; the script
interprets them as filenames. If mpv is already running, the files passed to
umpv are appended to mpv's internal playlist. If a file does not exist or is
otherwise not playable, mpv will skip the playlist entry when attempting to
play it (from the GUI perspective, it's silently ignored).
If mpv isn't running yet, this script will start mpv and let it control the
current terminal. If you play an audio-only file, mpv will not open a window
(that is its normal behavior; but it might not be what a user expects if this
script is used in a GUI setting).
current terminal. It will not write output to stdout/stderr, because this
will typically just fill ~/.xsession-errors with garbage.
mpv will terminate if there are no more files to play, and running the umpv
script after that will start a new mpv instance.
@@ -28,7 +26,10 @@ Note that you can control the mpv instance by writing to the command fifo:
Warning:
The script attempts to make sure the FIFO is safely created (i.e. not world-
readable, not trying to open a bogus
writable), and checks that it's really a FIFO. This is important for security,
because the FIFO allows anyone with write access to run arbitrary commands
in mpv's context using the "run" input command. If you are worried about
security, you should verify that the code handles these concerns correctly.
"""
import sys
@@ -88,4 +89,5 @@ else:
raise e
os.mkfifo(FIFO, 0600)
subprocess.check_call(["mpv", "--input-file=" + FIFO] + files)
subprocess.check_call(["mpv", "--really-quiet", "--force-window",
"--input-file=" + FIFO, "--"] + files)
+7 -1
View File
@@ -16,6 +16,7 @@ esac
: ${ILDETECT_DRY_RUN:=}
: ${ILDETECT_QUIET:=}
: ${ILDETECT_RUN_INTERLACED_ONLY:=}
: ${ILDETECT_FORCE_RUN:=}
: ${MAKE:=make}
# exit status:
@@ -76,7 +77,12 @@ case "$out" in
exit 2
;;
*"probably: "*)
exit 8
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
[ -n "$ILDETECT_DRY_RUN" ] || \
$ILDETECT_MPV "$@" -vf-pre yadif
r=$?
[ $r -eq 0 ] || exit $(($r | 16))
exit 0
;;
*)
exit 16
+1
View File
@@ -0,0 +1 @@
0.3.5
+174 -13
View File
@@ -15,44 +15,65 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <limits.h>
#include <stdlib.h>
#include <assert.h>
#include "mpvcore/mp_talloc.h"
#include <libavutil/mem.h>
#include "talloc.h"
#include "common/common.h"
#include "audio.h"
static void update_redundant_info(struct mp_audio *mpa)
{
assert(mp_chmap_is_empty(&mpa->channels) ||
mp_chmap_is_valid(&mpa->channels));
mpa->nch = mpa->channels.num;
mpa->bps = af_fmt2bits(mpa->format) / 8;
if (af_fmt_is_planar(mpa->format)) {
mpa->spf = 1;
mpa->num_planes = mpa->nch;
mpa->sstride = mpa->bps;
} else {
mpa->spf = mpa->nch;
mpa->num_planes = 1;
mpa->sstride = mpa->bps * mpa->nch;
}
}
void mp_audio_set_format(struct mp_audio *mpa, int format)
{
mpa->format = format;
mpa->bps = af_fmt2bits(format) / 8;
update_redundant_info(mpa);
}
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
{
struct mp_chmap map;
mp_chmap_from_channels(&map, num_channels);
mp_audio_set_channels(mpa, &map);
mp_chmap_from_channels(&mpa->channels, num_channels);
update_redundant_info(mpa);
}
// Use old MPlayer/ALSA channel layout.
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
{
struct mp_chmap map;
mp_chmap_from_channels_alsa(&map, num_channels);
mp_audio_set_channels(mpa, &map);
mp_chmap_from_channels_alsa(&mpa->channels, num_channels);
update_redundant_info(mpa);
}
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
{
assert(mp_chmap_is_empty(chmap) || mp_chmap_is_valid(chmap));
mpa->channels = *chmap;
mpa->nch = mpa->channels.num;
update_redundant_info(mpa);
}
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src)
{
mp_audio_set_format(dst, src->format);
mp_audio_set_channels(dst, &src->channels);
dst->format = src->format;
dst->channels = src->channels;
dst->rate = src->rate;
update_redundant_info(dst);
}
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
@@ -61,11 +82,17 @@ bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
mp_chmap_equals(&a->channels, &b->channels);
}
bool mp_audio_config_valid(const struct mp_audio *mpa)
{
return mp_chmap_is_valid(&mpa->channels) && af_fmt_is_valid(mpa->format)
&& mpa->rate >= 1 && mpa->rate < 10000000;
}
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format)
{
char *chstr = mp_chmap_to_str(chmap);
char *res = talloc_asprintf(NULL, "%dHz %s %dch %s", srate, chstr,
chmap->num, af_fmt2str_short(format));
chmap->num, af_fmt_to_str(format));
talloc_free(chstr);
return res;
}
@@ -74,3 +101,137 @@ char *mp_audio_config_to_str(struct mp_audio *mpa)
{
return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format);
}
void mp_audio_force_interleaved_format(struct mp_audio *mpa)
{
if (af_fmt_is_planar(mpa->format))
mp_audio_set_format(mpa, af_fmt_from_planar(mpa->format));
}
// Return used size of a plane. (The size is the same for all planes.)
int mp_audio_psize(struct mp_audio *mpa)
{
return mpa->samples * mpa->sstride;
}
void mp_audio_set_null_data(struct mp_audio *mpa)
{
for (int n = 0; n < MP_NUM_CHANNELS; n++)
mpa->planes[n] = NULL;
mpa->samples = 0;
}
static void mp_audio_destructor(void *ptr)
{
struct mp_audio *mpa = ptr;
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
// Note: don't free if not allocated by mp_audio_realloc
if (mpa->allocated[n])
av_free(mpa->planes[n]);
}
}
/* Reallocate the data stored in mpa->planes[n] so that enough samples are
* available on every plane. The previous data is kept (for the smallest
* common number of samples before/after resize).
*
* mpa->samples is not set or used.
*
* This function is flexible enough to handle format and channel layout
* changes. In these cases, all planes are reallocated as needed. Unused
* planes are freed.
*
* mp_audio_realloc(mpa, 0) will still yield non-NULL for mpa->data[n].
*
* Allocated data is implicitly freed on talloc_free(mpa).
*/
void mp_audio_realloc(struct mp_audio *mpa, int samples)
{
assert(samples >= 0);
if (samples >= INT_MAX / mpa->sstride)
abort(); // oom
int size = MPMAX(samples * mpa->sstride, 1);
for (int n = 0; n < mpa->num_planes; n++) {
if (size != mpa->allocated[n]) {
// Note: av_realloc() can't be used (see libavutil doxygen)
void *new = av_malloc(size);
if (!new)
abort();
if (mpa->allocated[n])
memcpy(new, mpa->planes[n], MPMIN(mpa->allocated[n], size));
av_free(mpa->planes[n]);
mpa->planes[n] = new;
mpa->allocated[n] = size;
}
}
for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) {
av_free(mpa->planes[n]);
mpa->planes[n] = NULL;
mpa->allocated[n] = 0;
}
talloc_set_destructor(mpa, mp_audio_destructor);
}
// Like mp_audio_realloc(), but only reallocate if the audio grows in size.
void mp_audio_realloc_min(struct mp_audio *mpa, int samples)
{
if (samples > mp_audio_get_allocated_size(mpa))
mp_audio_realloc(mpa, samples);
}
/* Get the size allocated for the data, in number of samples. If the allocated
* size isn't on sample boundaries (e.g. after format changes), the returned
* sample number is a rounded down value.
*
* Note that this only works in situations where mp_audio_realloc() also works!
*/
int mp_audio_get_allocated_size(struct mp_audio *mpa)
{
int size = 0;
for (int n = 0; n < mpa->num_planes; n++) {
int s = mpa->allocated[n] / mpa->sstride;
size = n == 0 ? s : MPMIN(size, s);
}
return size;
}
// Clear the samples [start, start + length) with silence.
void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length)
{
assert(start >= 0 && length >= 0 && start + length <= mpa->samples);
int offset = start * mpa->sstride;
int size = length * mpa->sstride;
for (int n = 0; n < mpa->num_planes; n++) {
if (n > 0 && mpa->planes[n] == mpa->planes[0])
continue; // silly optimization for special cases
af_fill_silence((char *)mpa->planes[n] + offset, size, mpa->format);
}
}
// All integer parameters are in samples.
// dst and src can overlap.
void mp_audio_copy(struct mp_audio *dst, int dst_offset,
struct mp_audio *src, int src_offset, int length)
{
assert(mp_audio_config_equals(dst, src));
assert(length >= 0);
assert(dst_offset >= 0 && dst_offset + length <= dst->samples);
assert(src_offset >= 0 && src_offset + length <= src->samples);
for (int n = 0; n < dst->num_planes; n++) {
memmove((char *)dst->planes[n] + dst_offset * dst->sstride,
(char *)src->planes[n] + src_offset * src->sstride,
length * dst->sstride);
}
}
// Set data to the audio after the given number of samples (i.e. slice it).
void mp_audio_skip_samples(struct mp_audio *data, int samples)
{
assert(samples >= 0 && samples <= data->samples);
for (int n = 0; n < data->num_planes; n++)
data->planes[n] = (uint8_t *)data->planes[n] + samples * data->sstride;
data->samples -= samples;
}
+30 -5
View File
@@ -23,14 +23,22 @@
// Audio data chunk
struct mp_audio {
void *audio; // data buffer
int len; // buffer length (in bytes)
int rate; // sample rate
int samples; // number of samples in data (per channel)
void *planes[MP_NUM_CHANNELS]; // data buffer (one per plane)
int rate; // sample rate
struct mp_chmap channels; // channel layout, use mp_audio_set_*() to set
int format; // format (AF_FORMAT_...), use mp_audio_set_format() to set
// Redundant fields, for convenience
int nch; // number of channels (redundant with chmap)
int bps; // bytes per sample (redundant with format)
int sstride; // distance between 2 samples in bytes on a plane
// interleaved: bps * nch
// planar: bps
int nch; // number of channels (redundant with chmap)
int spf; // sub-samples per sample on each plane
int num_planes; // number of planes
int bps; // size of sub-samples (af_fmt2bits(format) / 8)
// private
int allocated[MP_NUM_CHANNELS]; // use mp_audio_get_allocated_size()
};
void mp_audio_set_format(struct mp_audio *mpa, int format);
@@ -39,8 +47,25 @@ void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels);
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap);
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src);
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b);
bool mp_audio_config_valid(const struct mp_audio *mpa);
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format);
char *mp_audio_config_to_str(struct mp_audio *mpa);
void mp_audio_force_interleaved_format(struct mp_audio *mpa);
int mp_audio_psize(struct mp_audio *mpa);
void mp_audio_set_null_data(struct mp_audio *mpa);
void mp_audio_realloc(struct mp_audio *mpa, int samples);
void mp_audio_realloc_min(struct mp_audio *mpa, int samples);
int mp_audio_get_allocated_size(struct mp_audio *mpa);
void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length);
void mp_audio_copy(struct mp_audio *dst, int dst_offset,
struct mp_audio *src, int src_offset, int length);
void mp_audio_skip_samples(struct mp_audio *data, int samples);
#endif
+152
View File
@@ -0,0 +1,152 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <limits.h>
#include <assert.h>
#include "common/common.h"
#include "audio_buffer.h"
#include "audio.h"
#include "format.h"
struct mp_audio_buffer {
struct mp_audio *buffer;
};
struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx)
{
struct mp_audio_buffer *ab = talloc(talloc_ctx, struct mp_audio_buffer);
*ab = (struct mp_audio_buffer) {
.buffer = talloc_zero(ab, struct mp_audio),
};
return ab;
}
// Reinitialize the buffer, set a new format, drop old data.
// The audio data in fmt is not used, only the format.
void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt)
{
mp_audio_copy_config(ab->buffer, fmt);
mp_audio_realloc(ab->buffer, 1);
ab->buffer->samples = 0;
}
void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format,
const struct mp_chmap *channels, int srate)
{
struct mp_audio mpa = {0};
mp_audio_set_format(&mpa, format);
mp_audio_set_channels(&mpa, channels);
mpa.rate = srate;
mp_audio_buffer_reinit(ab, &mpa);
}
void mp_audio_buffer_get_format(struct mp_audio_buffer *ab,
struct mp_audio *out_fmt)
{
*out_fmt = (struct mp_audio){0};
mp_audio_copy_config(out_fmt, ab->buffer);
}
// Make the total size of the internal buffer at least this number of samples.
void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples)
{
mp_audio_realloc_min(ab->buffer, samples);
}
// Get number of samples that can be written without forcing a resize of the
// internal buffer.
int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab)
{
return mp_audio_get_allocated_size(ab->buffer) - ab->buffer->samples;
}
// Get a pointer to the end of the buffer (where writing would append). If the
// internal buffer is too small for the given number of samples, it's resized.
// After writing to the buffer, mp_audio_buffer_finish_write() has to be used
// to make the written data part of the readable buffer.
void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int samples,
struct mp_audio *out_buffer)
{
assert(samples >= 0);
mp_audio_realloc_min(ab->buffer, ab->buffer->samples + samples);
*out_buffer = *ab->buffer;
out_buffer->samples = ab->buffer->samples + samples;
mp_audio_skip_samples(out_buffer, ab->buffer->samples);
}
void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples)
{
assert(samples >= 0 && samples <= mp_audio_buffer_get_write_available(ab));
ab->buffer->samples += samples;
}
// Append data to the end of the buffer.
// If the buffer is not large enough, it is transparently resized.
// For now always copies the data.
void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa)
{
int offset = ab->buffer->samples;
ab->buffer->samples += mpa->samples;
mp_audio_realloc_min(ab->buffer, ab->buffer->samples);
mp_audio_copy(ab->buffer, offset, mpa, 0, mpa->samples);
}
// Prepend silence to the start of the buffer.
void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples)
{
assert(samples >= 0);
int oldlen = ab->buffer->samples;
ab->buffer->samples += samples;
mp_audio_realloc_min(ab->buffer, ab->buffer->samples);
mp_audio_copy(ab->buffer, samples, ab->buffer, 0, oldlen);
mp_audio_fill_silence(ab->buffer, 0, samples);
}
// Get the start of the current readable buffer.
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa)
{
*out_mpa = *ab->buffer;
}
// Skip leading samples. (Used with mp_audio_buffer_peek() to read data.)
void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples)
{
assert(samples >= 0 && samples <= ab->buffer->samples);
mp_audio_copy(ab->buffer, 0, ab->buffer, samples,
ab->buffer->samples - samples);
ab->buffer->samples -= samples;
}
void mp_audio_buffer_clear(struct mp_audio_buffer *ab)
{
ab->buffer->samples = 0;
}
// Return number of buffered audio samples
int mp_audio_buffer_samples(struct mp_audio_buffer *ab)
{
return ab->buffer->samples;
}
// Return amount of buffered audio in seconds.
double mp_audio_buffer_seconds(struct mp_audio_buffer *ab)
{
return ab->buffer->samples / (double)ab->buffer->rate;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MP_AUDIO_BUFFER_H
#define MP_AUDIO_BUFFER_H
struct mp_audio_buffer;
struct mp_audio;
struct mp_chmap;
struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx);
void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt);
void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format,
const struct mp_chmap *channels, int srate);
void mp_audio_buffer_get_format(struct mp_audio_buffer *ab,
struct mp_audio *out_fmt);
void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples);
int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab);
void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int minsamples,
struct mp_audio *out_buffer);
void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples);
void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa);
void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples);
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa);
void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples);
void mp_audio_buffer_clear(struct mp_audio_buffer *ab);
int mp_audio_buffer_samples(struct mp_audio_buffer *ab);
double mp_audio_buffer_seconds(struct mp_audio_buffer *ab);
#endif
+9 -9
View File
@@ -18,7 +18,7 @@
#include <stdlib.h>
#include <assert.h>
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "chmap.h"
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
@@ -471,19 +471,19 @@ bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
return true;
}
void mp_chmap_print_help(int msgt, int msgl)
void mp_chmap_print_help(struct mp_log *log)
{
mp_msg(msgt, msgl, "Speakers:\n");
mp_info(log, "Speakers:\n");
for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
if (speaker_names[n][0])
mp_msg(msgt, msgl, " %-16s (%s)\n",
speaker_names[n][0], speaker_names[n][1]);
mp_info(log, " %-16s (%s)\n",
speaker_names[n][0], speaker_names[n][1]);
}
mp_msg(msgt, msgl, "Standard layouts:\n");
mp_info(log, "Standard layouts:\n");
for (int n = 0; std_layout_names[n][0]; n++) {
mp_msg(msgt, msgl, " %-16s (%s)\n",
std_layout_names[n][0], std_layout_names[n][1]);
mp_info(log, " %-16s (%s)\n",
std_layout_names[n][0], std_layout_names[n][1]);
}
for (int n = 0; n < MP_NUM_CHANNELS; n++)
mp_msg(msgt, msgl, " unknown%d\n", n);
mp_info(log, " unknown%d\n", n);
}
+4 -2
View File
@@ -20,7 +20,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include "mpvcore/bstr.h"
#include "bstr/bstr.h"
#define MP_NUM_CHANNELS 8
@@ -121,7 +121,9 @@ void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
char *mp_chmap_to_str(const struct mp_chmap *src);
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
void mp_chmap_print_help(int msgt, int msgl);
struct mp_log;
void mp_chmap_print_help(struct mp_log *log);
// Use these to avoid chaos in case lavc's definition should diverge from MS.
#define mp_chmap_to_waveext mp_chmap_to_lavc
+8 -8
View File
@@ -19,11 +19,13 @@
#ifndef MPLAYER_AD_H
#define MPLAYER_AD_H
#include "mpvcore/codecs.h"
#include "common/codecs.h"
#include "demux/stheader.h"
#include "demux/demux.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "dec_audio.h"
struct mp_decoder_list;
@@ -31,16 +33,14 @@ struct mp_decoder_list;
struct ad_functions {
const char *name;
void (*add_decoders)(struct mp_decoder_list *list);
int (*preinit)(sh_audio_t *sh);
int (*init)(sh_audio_t *sh, const char *decoder);
void (*uninit)(sh_audio_t *sh);
int (*control)(sh_audio_t *sh, int cmd, void *arg);
int (*decode_audio)(sh_audio_t *sh, unsigned char *buffer, int minlen,
int maxlen);
int (*init)(struct dec_audio *da, const char *decoder);
void (*uninit)(struct dec_audio *da);
int (*control)(struct dec_audio *da, int cmd, void *arg);
int (*decode_audio)(struct dec_audio *da, struct mp_audio *buffer, int maxlen);
};
enum ad_ctrl {
ADCTRL_RESYNC_STREAM = 1, // resync, called after seeking
ADCTRL_RESET = 1, // flush and reset state, e.g. after seeking
};
#endif /* MPLAYER_AD_H */
+140 -201
View File
@@ -29,38 +29,34 @@
#include "talloc.h"
#include "config.h"
#include "mpvcore/av_common.h"
#include "mpvcore/codecs.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/options.h"
#include "mpvcore/av_opts.h"
#include "common/av_common.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "options/options.h"
#include "common/av_opts.h"
#include "ad.h"
#include "audio/reorder_ch.h"
#include "audio/fmt-conversion.h"
#include "compat/mpbswap.h"
#include "compat/libav.h"
struct priv {
AVCodecContext *avctx;
AVFrame *avframe;
uint8_t *output;
uint8_t *output_packed; // used by deplanarize to store packed audio samples
int output_left;
int unitsize;
struct mp_audio frame;
bool force_channel_map;
struct demux_packet *packet;
};
static void uninit(sh_audio_t *sh);
static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen);
static void uninit(struct dec_audio *da);
static int decode_new_packet(struct dec_audio *da);
#define OPT_BASE_STRUCT struct MPOpts
const m_option_t ad_lavc_decode_opts_conf[] = {
OPT_FLOATRANGE("ac3drc", ad_lavc_param.ac3drc, 0, 0, 2),
OPT_FLAG("downmix", ad_lavc_param.downmix, 0),
OPT_INTRANGE("threads", ad_lavc_param.threads, 0, 1, 16),
OPT_STRING("o", ad_lavc_param.avopt, 0),
{0}
};
@@ -68,12 +64,10 @@ const m_option_t ad_lavc_decode_opts_conf[] = {
struct pcm_map
{
int tag;
const char *codecs[5]; // {any, 1byte, 2bytes, 3bytes, 4bytes}
const char *codecs[6]; // {any, 1byte, 2bytes, 3bytes, 4bytes, 8bytes}
};
// NOTE: some of these are needed to make rawaudio with demux_mkv and others
// work. ffmpeg does similar mapping internally, not part of the public
// API. Some of these might be dead leftovers for demux_mov support.
// NOTE: these are needed to make rawaudio with demux_mkv work.
static const struct pcm_map tag_map[] = {
// Microsoft PCM
{0x0, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
@@ -81,26 +75,12 @@ static const struct pcm_map tag_map[] = {
// MS PCM, Extended
{0xfffe, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
// IEEE float
{0x3, {"pcm_f32le"}},
{0x3, {"pcm_f32le", [5] = "pcm_f64le"}},
// 'raw '
{0x20776172, {"pcm_s16be", [1] = "pcm_u8"}},
// 'twos'/'sowt'
{0x736F7774, {"pcm_s16be", [1] = "pcm_s8"}},
{0x74776F73, {"pcm_s16be", [1] = "pcm_s8"}},
// 'fl32'/'FL32'
{0x32336c66, {"pcm_f32be"}},
{0x32334C46, {"pcm_f32be"}},
// '23lf'/'lpcm'
{0x666c3332, {"pcm_f32le"}},
{0x6D63706C, {"pcm_f32le"}},
// 'in24', bigendian int24
{0x34326e69, {"pcm_s24be"}},
// '42ni', little endian int24, MPlayer internal fourCC
{0x696e3234, {"pcm_s24le"}},
// 'in32', bigendian int32
{0x32336e69, {"pcm_s32be"}},
// '23ni', little endian int32, MPlayer internal fourCC
{0x696e3332, {"pcm_s32le"}},
// 'twos', used by demux_mkv.c internally
{MKTAG('t', 'w', 'o', 's'),
{NULL, "pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"}},
{-1},
};
@@ -123,6 +103,8 @@ static const struct pcm_map af_map[] = {
{AF_FORMAT_S32_BE, {"pcm_s32be"}},
{AF_FORMAT_FLOAT_LE, {"pcm_f32le"}},
{AF_FORMAT_FLOAT_BE, {"pcm_f32be"}},
{AF_FORMAT_DOUBLE_LE, {"pcm_f64le"}},
{AF_FORMAT_DOUBLE_BE, {"pcm_f64be"}},
{-1},
};
@@ -130,11 +112,13 @@ static const char *find_pcm_decoder(const struct pcm_map *map, int format,
int bits_per_sample)
{
int bytes = (bits_per_sample + 7) / 8;
if (bytes == 8)
bytes = 5; // 64 bit entry
for (int n = 0; map[n].tag != -1; n++) {
const struct pcm_map *entry = &map[n];
if (entry->tag == format) {
const char *dec = NULL;
if (bytes >= 1 && bytes <= 4)
if (bytes >= 1 && bytes <= 5)
dec = entry->codecs[bytes];
if (!dec)
dec = entry->codecs[0];
@@ -145,27 +129,25 @@ static const char *find_pcm_decoder(const struct pcm_map *map, int format,
return NULL;
}
static int preinit(sh_audio_t *sh)
static int setup_format(struct dec_audio *da)
{
return 1;
}
struct priv *priv = da->priv;
AVCodecContext *lavc_context = priv->avctx;
struct sh_audio *sh_audio = da->header->audio;
/* Prefer playing audio with the samplerate given in container data
* if available, but take number the number of channels and sample format
* from the codec, since if the codec isn't using the correct values for
* those everything breaks anyway.
*/
static int setup_format(sh_audio_t *sh_audio,
const AVCodecContext *lavc_context)
{
struct priv *priv = sh_audio->context;
int sample_format =
af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
int samplerate = lavc_context->sample_rate;
// If not set, try container samplerate
if (!samplerate && sh_audio->wf) {
samplerate = sh_audio->wf->nSamplesPerSec;
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "ad_lavc: using container rate.\n");
// Note: invalid parameters are rejected by dec_audio.c
int fmt = lavc_context->sample_fmt;
mp_audio_set_format(&da->decoded, af_from_avformat(fmt));
if (!da->decoded.format)
MP_FATAL(da, "unsupported lavc format %s", av_get_sample_fmt_name(fmt));
da->decoded.rate = lavc_context->sample_rate;
if (!da->decoded.rate && sh_audio->wf) {
// If not set, try container samplerate.
// (Maybe this can't happen, and it's an artifact from the past.)
da->decoded.rate = sh_audio->wf->nSamplesPerSec;
MP_WARN(da, "using container rate.\n");
}
struct mp_chmap lavc_chmap;
@@ -177,20 +159,12 @@ static int setup_format(sh_audio_t *sh_audio,
if (lavc_chmap.num == sh_audio->channels.num)
lavc_chmap = sh_audio->channels;
}
mp_audio_set_channels(&da->decoded, &lavc_chmap);
if (!mp_chmap_equals(&lavc_chmap, &sh_audio->channels) ||
samplerate != sh_audio->samplerate ||
sample_format != sh_audio->sample_format) {
sh_audio->channels = lavc_chmap;
sh_audio->samplerate = samplerate;
sh_audio->sample_format = sample_format;
sh_audio->samplesize = af_fmt2bits(sh_audio->sample_format) / 8;
return 1;
}
return 0;
}
static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
static void set_from_wf(AVCodecContext *avctx, MP_WAVEFORMATEX *wf)
{
avctx->channels = wf->nChannels;
avctx->sample_rate = wf->nSamplesPerSec;
@@ -205,29 +179,30 @@ static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
}
}
static int init(sh_audio_t *sh_audio, const char *decoder)
static int init(struct dec_audio *da, const char *decoder)
{
struct MPOpts *mpopts = sh_audio->opts;
struct MPOpts *mpopts = da->opts;
struct ad_lavc_param *opts = &mpopts->ad_lavc_param;
AVCodecContext *lavc_context;
AVCodec *lavc_codec;
struct sh_stream *sh = da->header;
struct sh_audio *sh_audio = sh->audio;
struct priv *ctx = talloc_zero(NULL, struct priv);
sh_audio->context = ctx;
da->priv = ctx;
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
decoder = find_pcm_decoder(tag_map, sh_audio->format,
decoder = find_pcm_decoder(tag_map, sh->format,
sh_audio->wf->wBitsPerSample);
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
decoder = find_pcm_decoder(af_map, sh_audio->format, 0);
decoder = find_pcm_decoder(af_map, sh->format, 0);
ctx->force_channel_map = true;
}
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
"Cannot find codec '%s' in libavcodec...\n", decoder);
uninit(sh_audio);
MP_ERR(da, "Cannot find codec '%s' in libavcodec...\n", decoder);
uninit(da);
return 0;
}
@@ -238,9 +213,12 @@ static int init(sh_audio_t *sh_audio, const char *decoder)
lavc_context->codec_id = lavc_codec->id;
if (opts->downmix) {
lavc_context->request_channels = mpopts->audio_output_channels.num;
lavc_context->request_channel_layout =
mp_chmap_to_lavc(&mpopts->audio_output_channels);
// Compatibility for Libav 9
av_opt_set_int(lavc_context, "request_channels",
mpopts->audio_output_channels.num,
AV_OPT_SEARCH_CHILDREN);
}
// Always try to set - option only exists for AC3 at the moment
@@ -249,14 +227,13 @@ static int init(sh_audio_t *sh_audio, const char *decoder)
if (opts->avopt) {
if (parse_avopts(lavc_context, opts->avopt) < 0) {
mp_msg(MSGT_DECVIDEO, MSGL_ERR,
"ad_lavc: setting AVOptions '%s' failed.\n", opts->avopt);
uninit(sh_audio);
MP_ERR(da, "setting AVOptions '%s' failed.\n", opts->avopt);
uninit(da);
return 0;
}
}
lavc_context->codec_tag = sh_audio->format;
lavc_context->codec_tag = sh->format;
lavc_context->sample_rate = sh_audio->samplerate;
lavc_context->bit_rate = sh_audio->i_bps * 8;
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
@@ -274,72 +251,64 @@ static int init(sh_audio_t *sh_audio, const char *decoder)
lavc_context->extradata_size);
}
if (sh_audio->gsh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh_audio->gsh->lav_headers);
if (sh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
mp_set_avcodec_threads(lavc_context, opts->threads);
/* open it */
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Could not open codec.\n");
uninit(sh_audio);
MP_ERR(da, "Could not open codec.\n");
uninit(da);
return 0;
}
mp_msg(MSGT_DECAUDIO, MSGL_V, "INFO: libavcodec \"%s\" init OK!\n",
MP_VERBOSE(da, "INFO: libavcodec \"%s\" init OK!\n",
lavc_codec->name);
// Decode at least 1 byte: (to get header filled)
for (int tries = 0;;) {
int x = decode_audio(sh_audio, sh_audio->a_buffer, 1,
sh_audio->a_buffer_size);
if (x > 0) {
sh_audio->a_buffer_len = x;
// Decode at least 1 sample: (to get header filled)
for (int tries = 1; ; tries++) {
int x = decode_new_packet(da);
if (x >= 0 && ctx->frame.samples > 0) {
MP_VERBOSE(da, "Initial decode succeeded after %d packets.\n", tries);
break;
}
if (++tries >= 5) {
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"ad_lavc: initial decode failed\n");
uninit(sh_audio);
if (tries >= 50) {
MP_ERR(da, "initial decode failed\n");
uninit(da);
return 0;
}
}
sh_audio->i_bps = lavc_context->bit_rate / 8;
da->i_bps = lavc_context->bit_rate / 8;
if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec;
da->i_bps = sh_audio->wf->nAvgBytesPerSec;
int af_sample_fmt =
af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
if (af_sample_fmt == AF_FORMAT_UNKNOWN) {
uninit(sh_audio);
return 0;
}
return 1;
}
static void uninit(sh_audio_t *sh)
static void uninit(struct dec_audio *da)
{
struct priv *ctx = sh->context;
struct priv *ctx = da->priv;
if (!ctx)
return;
AVCodecContext *lavc_context = ctx->avctx;
if (lavc_context) {
if (avcodec_close(lavc_context) < 0)
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not close codec.\n");
MP_ERR(da, "Could not close codec.\n");
av_freep(&lavc_context->extradata);
av_freep(&lavc_context);
}
avcodec_free_frame(&ctx->avframe);
talloc_free(ctx);
sh->context = NULL;
}
static int control(sh_audio_t *sh, int cmd, void *arg)
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct priv *ctx = sh->context;
struct priv *ctx = da->priv;
switch (cmd) {
case ADCTRL_RESYNC_STREAM:
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
ctx->output_left = 0;
ctx->frame.samples = 0;
talloc_free(ctx->packet);
ctx->packet = NULL;
return CONTROL_TRUE;
@@ -347,121 +316,92 @@ static int control(sh_audio_t *sh, int cmd, void *arg)
return CONTROL_UNKNOWN;
}
static av_always_inline void deplanarize(struct sh_audio *sh)
static int decode_new_packet(struct dec_audio *da)
{
struct priv *priv = sh->context;
uint8_t **planes = priv->avframe->extended_data;
size_t bps = av_get_bytes_per_sample(priv->avctx->sample_fmt);
size_t nb_samples = priv->avframe->nb_samples;
size_t channels = priv->avctx->channels;
size_t size = bps * nb_samples * channels;
if (talloc_get_size(priv->output_packed) != size)
priv->output_packed =
talloc_realloc_size(priv, priv->output_packed, size);
reorder_to_packed(priv->output_packed, planes, bps, channels, nb_samples);
priv->output = priv->output_packed;
}
static int decode_new_packet(struct sh_audio *sh)
{
struct priv *priv = sh->context;
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
priv->frame.samples = 0;
struct demux_packet *mpkt = priv->packet;
if (!mpkt)
mpkt = demux_read_packet(sh->gsh);
if (!mpkt)
return -1; // error or EOF
mpkt = demux_read_packet(da->header);
priv->packet = talloc_steal(priv, mpkt);
int in_len = mpkt->len;
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt);
mp_set_av_packet(&pkt, mpkt, NULL);
if (mpkt->pts != MP_NOPTS_VALUE) {
sh->pts = mpkt->pts;
sh->pts_bytes = 0;
// If we don't have a PTS yet, use the first packet PTS we can get.
if (da->pts == MP_NOPTS_VALUE && mpkt && mpkt->pts != MP_NOPTS_VALUE) {
da->pts = mpkt->pts;
da->pts_offset = 0;
}
int got_frame = 0;
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
// At least "shorten" decodes sub-frames, instead of the whole packet.
// At least "mpc8" can return 0 and wants the packet again next time.
if (ret >= 0) {
ret = FFMIN(ret, mpkt->len); // sanity check against decoder overreads
mpkt->buffer += ret;
mpkt->len -= ret;
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
if (mpkt) {
// At least "shorten" decodes sub-frames, instead of the whole packet.
// At least "mpc8" can return 0 and wants the packet again next time.
if (ret >= 0) {
ret = FFMIN(ret, mpkt->len); // sanity check against decoder overreads
mpkt->buffer += ret;
mpkt->len -= ret;
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
}
if (mpkt->len == 0 || ret < 0) {
talloc_free(mpkt);
priv->packet = NULL;
}
// LATM may need many packets to find mux info
if (ret == AVERROR(EAGAIN))
return 0;
}
if (mpkt->len == 0 || ret < 0) {
talloc_free(mpkt);
priv->packet = NULL;
}
// LATM may need many packets to find mux info
if (ret == AVERROR(EAGAIN))
return 0;
if (ret < 0) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n");
MP_VERBOSE(da, "lavc_audio: error\n");
return -1;
}
if (!got_frame)
return 0;
uint64_t unitsize = (uint64_t)av_get_bytes_per_sample(avctx->sample_fmt) *
avctx->channels;
if (unitsize > 100000)
abort();
priv->unitsize = unitsize;
uint64_t output_left = unitsize * priv->avframe->nb_samples;
if (output_left > 500000000)
abort();
priv->output_left = output_left;
if (av_sample_fmt_is_planar(avctx->sample_fmt) && avctx->channels > 1) {
deplanarize(sh);
} else {
priv->output = priv->avframe->data[0];
return mpkt ? 0 : -1; // -1: eof
if (setup_format(da) < 0)
return -1;
priv->frame.samples = priv->avframe->nb_samples;
mp_audio_copy_config(&priv->frame, &da->decoded);
for (int n = 0; n < priv->frame.num_planes; n++)
priv->frame.planes[n] = priv->avframe->data[n];
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
if (out_pts != MP_NOPTS_VALUE) {
da->pts = out_pts;
da->pts_offset = 0;
}
mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", in_len,
priv->output_left);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len,
priv->frame.samples);
return 0;
}
static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
int maxlen)
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
{
struct priv *priv = sh_audio->context;
AVCodecContext *avctx = priv->avctx;
struct priv *priv = da->priv;
int len = -1;
while (len < minlen) {
if (!priv->output_left) {
if (decode_new_packet(sh_audio) < 0)
break;
continue;
}
if (setup_format(sh_audio, avctx))
return len;
int size = (minlen - len + priv->unitsize - 1);
size -= size % priv->unitsize;
size = FFMIN(size, priv->output_left);
if (size > maxlen)
abort();
memcpy(buf, priv->output, size);
priv->output += size;
priv->output_left -= size;
if (len < 0)
len = size;
else
len += size;
buf += size;
maxlen -= size;
sh_audio->pts_bytes += size;
if (!priv->frame.samples) {
if (decode_new_packet(da) < 0)
return -1;
}
return len;
if (!mp_audio_config_equals(buffer, &priv->frame))
return 0;
buffer->samples = MPMIN(priv->frame.samples, maxlen);
mp_audio_copy(buffer, 0, &priv->frame, 0, buffer->samples);
mp_audio_skip_samples(&priv->frame, buffer->samples);
da->pts_offset += buffer->samples;
return 0;
}
static void add_decoders(struct mp_decoder_list *list)
@@ -474,7 +414,6 @@ static void add_decoders(struct mp_decoder_list *list)
const struct ad_functions ad_lavc = {
.name = "lavc",
.add_decoders = add_decoders,
.preinit = preinit,
.init = init,
.uninit = uninit,
.control = control,
+220 -355
View File
@@ -25,43 +25,40 @@
#include "config.h"
#include "ad.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
/* Reducing the ifdeffery to two main variants:
* 1. most compatible to any libmpg123 version
* 2. fastest variant with recent libmpg123 (>=1.14)
* Running variant 2 on older libmpg123 versions may work in
* principle, but is not supported.
* So, please leave the check for MPG123_API_VERSION there, m-kay?
*/
#include <mpg123.h>
/* Enable faster mode of operation with newer libmpg123, avoiding
* unnecessary memcpy() calls. */
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION >= 33)
#define AD_MPG123_FRAMEWISE
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION < 33)
#error "This should not happen"
#endif
/* Switch for updating bitrate info of VBR files. Not essential. */
#define AD_MPG123_MEAN_BITRATE
struct ad_mpg123_context {
mpg123_handle *handle;
char new_format;
#ifdef AD_MPG123_MEAN_BITRATE
bool new_format;
int sample_size;
bool need_data;
/* Running mean for bit rate, stream length estimation. */
float mean_rate;
unsigned int mean_count;
/* Time delay for updates. */
short delay;
#endif
/* If the stream is actually VBR. */
char vbr;
};
static void uninit(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
mpg123_close(con->handle);
mpg123_delete(con->handle);
mpg123_exit();
}
/* This initializes libmpg123 and prepares the handle, including funky
* parameters. */
static int preinit(sh_audio_t *sh)
static int preinit(struct dec_audio *da)
{
int err;
struct ad_mpg123_context *con;
@@ -72,8 +69,8 @@ static int preinit(sh_audio_t *sh)
if (mpg123_init() != MPG123_OK)
return 0;
sh->context = malloc(sizeof(struct ad_mpg123_context));
con = sh->context;
da->priv = talloc_zero(NULL, struct ad_mpg123_context);
con = da->priv;
/* Auto-choice of optimized decoder (first argument NULL). */
con->handle = mpg123_new(NULL, &err);
if (!con->handle)
@@ -104,33 +101,147 @@ static int preinit(sh_audio_t *sh)
/* Example for RVA choice (available since libmpg123 1.0.0):
mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
#ifdef AD_MPG123_FRAMEWISE
/* Prevent funky automatic resampling.
* This way, we can be sure that one frame will never produce
* more than 1152 stereo samples. */
* more than 1152 stereo samples.
* Background:
* Going to decode directly to the output buffer. It is important to have
* MPG123_AUTO_RESAMPLE disabled for the buffer size being an all-time
* limit.
* We need at least 1152 samples. dec_audio.c normally guarantees this. */
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
#else
/* Older mpg123 is vulnerable to concatenated streams when gapless cutting
* is enabled (will only play the jingle of a badly constructed radio
* stream). The versions using framewise decoding are fine with that. */
mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.);
#endif
return 1;
bad_end:
if (!con->handle)
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
MP_ERR(da, "mpg123 preinit error: %s\n",
mpg123_plain_strerror(err));
else
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
MP_ERR(da, "mpg123 preinit error: %s\n",
mpg123_strerror(con->handle));
if (con->handle)
mpg123_delete(con->handle);
mpg123_exit();
free(sh->context);
sh->context = NULL;
uninit(da);
return 0;
}
static int mpg123_format_to_af(int mpg123_encoding)
{
/* Without external force, mpg123 will always choose signed encoding,
* and non-16-bit only on builds that don't support it.
* Be reminded that it doesn't matter to the MPEG file what encoding
* is produced from it. */
switch (mpg123_encoding) {
case MPG123_ENC_SIGNED_8: return AF_FORMAT_S8;
case MPG123_ENC_SIGNED_16: return AF_FORMAT_S16;
case MPG123_ENC_SIGNED_32: return AF_FORMAT_S32;
case MPG123_ENC_FLOAT_32: return AF_FORMAT_FLOAT;
}
return 0;
}
/* libmpg123 has a new format ready; query and store, return return value
of mpg123_getformat() */
static int set_format(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
int ret;
long rate;
int channels;
int encoding;
ret = mpg123_getformat(con->handle, &rate, &channels, &encoding);
if (ret == MPG123_OK) {
mp_audio_set_num_channels(&da->decoded, channels);
da->decoded.rate = rate;
int af = mpg123_format_to_af(encoding);
if (!af) {
/* This means we got a funny custom build of libmpg123 that only supports an unknown format. */
MP_ERR(da, "Bad encoding from mpg123: %i.\n", encoding);
return MPG123_ERR;
}
mp_audio_set_format(&da->decoded, af);
con->sample_size = channels * (af_fmt2bits(af) / 8);
con->new_format = 0;
}
return ret;
}
static int feed_new_packet(struct dec_audio *da)
{
struct ad_mpg123_context *con = da->priv;
int ret;
struct demux_packet *pkt = demux_read_packet(da->header);
if (!pkt)
return -1; /* EOF. */
/* Next bytes from that presentation time. */
if (pkt->pts != MP_NOPTS_VALUE) {
da->pts = pkt->pts;
da->pts_offset = 0;
}
/* Have to use mpg123_feed() to avoid decoding here. */
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
talloc_free(pkt);
if (ret == MPG123_ERR)
return -1;
if (ret == MPG123_NEW_FORMAT)
con->new_format = 1;
return 0;
}
/* Now we really start accessing some data and determining file format.
* Format now is allowed to change on-the-fly. Here is the only point
* that has MPlayer react to errors. We have to pray that exceptional
* erros in other places simply cannot occur. */
static int init(struct dec_audio *da, const char *decoder)
{
if (!preinit(da))
return 0;
struct ad_mpg123_context *con = da->priv;
int ret;
ret = mpg123_open_feed(con->handle);
if (ret != MPG123_OK)
goto fail;
for (int n = 0; ; n++) {
if (feed_new_packet(da) < 0) {
ret = MPG123_NEED_MORE;
goto fail;
}
size_t got_now = 0;
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
if (ret == MPG123_OK || ret == MPG123_NEW_FORMAT) {
ret = set_format(da);
if (ret == MPG123_OK)
break;
}
if (ret != MPG123_NEED_MORE)
goto fail;
// max. 16 retries (randomly chosen number)
if (n > 16) {
ret = MPG123_NEED_MORE;
goto fail;
}
}
return 1;
fail:
if (ret == MPG123_NEED_MORE) {
MP_ERR(da, "Could not find mp3 stream.\n");
} else {
MP_ERR(da, "mpg123 init error: %s\n",
mpg123_strerror(con->handle));
}
uninit(da);
return 0;
}
@@ -147,348 +258,103 @@ static int compute_bitrate(struct mpg123_frameinfo *i)
samples_per_frame[i->version][i->layer] + 0.5);
}
/* Opted against the header printout from old mp3lib, too much
* irrelevant info. This is modelled after the mpg123 app's
* standard output line.
* If more verbosity is demanded, one can add more detail and
* also throw in ID3v2 info which libmpg123 collects anyway. */
static void print_header_compact(struct mpg123_frameinfo *i)
{
static const char *smodes[5] = {
"stereo", "joint-stereo", "dual-channel", "mono", "invalid"
};
static const char *layers[4] = {
"Unknown", "I", "II", "III"
};
static const char *versions[4] = {
"1.0", "2.0", "2.5", "x.x"
};
mp_msg(MSGT_DECAUDIO, MSGL_V, "MPEG %s layer %s, ",
versions[i->version], layers[i->layer]);
switch (i->vbr) {
case MPG123_CBR:
if (i->bitrate)
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s", i->bitrate);
else
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s (free format)",
compute_bitrate(i));
break;
case MPG123_VBR:
mp_msg(MSGT_DECAUDIO, MSGL_V, "VBR");
break;
case MPG123_ABR:
mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s ABR", i->abr_rate);
break;
default:
mp_msg(MSGT_DECAUDIO, MSGL_V, "???");
}
mp_msg(MSGT_DECAUDIO, MSGL_V, ", %ld Hz %s\n", i->rate,
smodes[i->mode]);
}
/* libmpg123 has a new format ready; query and store, return return value
of mpg123_getformat() */
static int set_format(sh_audio_t *sh, struct ad_mpg123_context *con)
{
int ret;
long rate;
int channels;
int encoding;
ret = mpg123_getformat(con->handle, &rate, &channels, &encoding);
if(ret == MPG123_OK) {
mp_chmap_from_channels(&sh->channels, channels);
sh->samplerate = rate;
/* Without external force, mpg123 will always choose signed encoding,
* and non-16-bit only on builds that don't support it.
* Be reminded that it doesn't matter to the MPEG file what encoding
* is produced from it. */
switch (encoding) {
case MPG123_ENC_SIGNED_8:
sh->sample_format = AF_FORMAT_S8;
sh->samplesize = 1;
break;
case MPG123_ENC_SIGNED_16:
sh->sample_format = AF_FORMAT_S16_NE;
sh->samplesize = 2;
break;
/* To stay compatible with the oldest libmpg123 headers, do not rely
* on float and 32 bit encoding symbols being defined.
* Those formats came later */
case 0x1180: /* MPG123_ENC_SIGNED_32 */
sh->sample_format = AF_FORMAT_S32_NE;
sh->samplesize = 4;
break;
case 0x200: /* MPG123_ENC_FLOAT_32 */
sh->sample_format = AF_FORMAT_FLOAT_NE;
sh->samplesize = 4;
break;
default:
/* This means we got a funny custom build of libmpg123 that only supports an unknown format. */
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"Bad encoding from mpg123: %i.\n", encoding);
return MPG123_ERR;
}
#ifdef AD_MPG123_FRAMEWISE
/* Going to decode directly to MPlayer's memory. It is important
* to have MPG123_AUTO_RESAMPLE disabled for the buffer size
* being an all-time limit. */
sh->audio_out_minsize = 1152 * 2 * sh->samplesize;
#endif
con->new_format = 0;
}
return ret;
}
/* This tries to extract a requested amount of decoded data.
* Even when you request 0 bytes, it will feed enough input so that
* the decoder _could_ have delivered something.
* Returns byte count >= 0, -1 on error.
*
* Thoughts on exact pts keeping:
* We have to assume that MPEG frames are cut in pieces by packet boundaries.
* Also, it might be possible that the first packet does not contain enough
* data to ensure initial stream sync... or re-sync on erroneous streams.
* So we need something robust to relate the decoded byte count to the correct
* time stamp. This is tricky, though. From the outside, you cannot tell if,
* after having fed two packets until the first output arrives, one should
* start counting from the first packet's pts or the second packet's.
* So, let's just count from the last fed package's pts. If the packets are
* exactly cut to MPEG frames, this will cause one frame mismatch in the
* beginning (when mpg123 peeks ahead for the following header), but will
* be corrected with the third frame already. One might add special code to
* not increment the base pts past the first packet's after a resync before
* the first decoded bytes arrived. */
static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
{
int ret = MPG123_OK;
int got = 0;
struct ad_mpg123_context *con = sh->context;
/* There will be one MPG123_NEW_FORMAT message on first open.
* This will be handled in init(). */
do {
size_t got_now = 0;
/* Fetch new format now, after old data has been used. */
if(con->new_format)
ret = set_format(sh, con);
/* Feed the decoder. This will only fire from the second round on. */
if (ret == MPG123_NEED_MORE) {
/* Feed more input data. */
struct demux_packet *pkt = demux_read_packet(sh->gsh);
if (!pkt)
break; /* Apparently that's it. EOF. */
/* Next bytes from that presentation time. */
if (pkt->pts != MP_NOPTS_VALUE) {
sh->pts = pkt->pts;
sh->pts_bytes = 0;
}
#ifdef AD_MPG123_FRAMEWISE
/* Have to use mpg123_feed() to avoid decoding here. */
ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
#else
/* Do not use mpg123_feed(), added in later libmpg123 versions. */
ret = mpg123_decode(con->handle, pkt->buffer, pkt->len, NULL, 0, NULL);
#endif
talloc_free(pkt);
if (ret == MPG123_ERR)
break;
/* Indication of format change is possible here (from mpg123_decode()). */
if(ret == MPG123_NEW_FORMAT) {
con->new_format = 1;
if(got)
break; /* Do not switch format during a chunk. */
ret = set_format(sh, con);
}
}
/* Theoretically, mpg123 could return MPG123_DONE, so be prepared.
* Should not happen in our usage, but it is a valid return code. */
else if (ret == MPG123_ERR || ret == MPG123_DONE)
break;
/* Try to decode a bit. This is the return value that counts
* for the loop condition. */
#ifdef AD_MPG123_FRAMEWISE
if (!buf) { /* fake call just for feeding to get format */
ret = set_format(sh, con);
} else { /* This is the decoding. One frame at a time. */
ret = mpg123_replace_buffer(con->handle, buf, count);
if (ret == MPG123_OK)
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
}
#else
ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got,
&got_now);
#endif
got += got_now;
sh->pts_bytes += got_now;
/* Indication of format change should happen here. */
if(ret == MPG123_NEW_FORMAT) {
con->new_format = 1;
if(got)
break; /* Do not switch format during a chunk. */
ret = set_format(sh, con);
}
#ifdef AD_MPG123_FRAMEWISE
} while (ret == MPG123_NEED_MORE || (got == 0 && count != 0));
#else
} while (ret == MPG123_NEED_MORE || got < count);
#endif
if (ret == MPG123_ERR) {
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 decoding failed: %s\n",
mpg123_strerror(con->handle));
}
return got;
}
/* Close, reopen stream. Feed data until we know the format of the stream.
* 1 on success, 0 on error */
static int reopen_stream(sh_audio_t *sh)
{
struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
mpg123_close(con->handle);
/* No resetting of the context:
* We do not want to loose the mean bitrate data. */
/* Open and make sure we have fed enough data to get stream properties. */
if (MPG123_OK == mpg123_open_feed(con->handle) &&
/* Feed data until mpg123 is ready (has found stream beginning). */
!decode_a_bit(sh, NULL, 0) &&
set_format(sh, con) == MPG123_OK) { /* format setting again just for return value */
return 1;
} else {
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"mpg123 failed to reopen stream: %s\n",
mpg123_strerror(con->handle));
return 0;
}
}
/* Now we really start accessing some data and determining file format.
* Format now is allowed to change on-the-fly. Here is the only point
* that has MPlayer react to errors. We have to pray that exceptional
* erros in other places simply cannot occur. */
static int init(sh_audio_t *sh, const char *decoder)
{
mpg123_id3v2 *v2;
struct mpg123_frameinfo finfo;
struct ad_mpg123_context *con = sh->context;
con->new_format = 0;
if (reopen_stream(sh) &&
/* Get MPEG header info. */
MPG123_OK == mpg123_info(con->handle, &finfo) &&
/* Since we queried format, mpg123 should have read past ID3v2 tags.
* We need to decide if printing of UTF-8 encoded text info is wanted. */
MPG123_OK == mpg123_id3(con->handle, NULL, &v2)) {
/* If we are here, we passed all hurdles. Yay! Extract the info. */
print_header_compact(&finfo);
/* Do we want to print out the UTF-8 Id3v2 info?
if (v2)
print_id3v2(v2); */
/* Have kb/s, want B/s
* For VBR, the first frame will be a bad estimate. */
sh->i_bps = (finfo.bitrate ? finfo.bitrate : compute_bitrate(&finfo))
* 1000 / 8;
#ifdef AD_MPG123_MEAN_BITRATE
con->delay = 1;
con->mean_rate = 0.;
con->mean_count = 0;
#endif
con->vbr = (finfo.vbr != MPG123_CBR);
return 1;
} else {
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 init error: %s\n",
mpg123_strerror(con->handle));
return 0;
}
}
static void uninit(sh_audio_t *sh)
{
struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
mpg123_close(con->handle);
mpg123_delete(con->handle);
free(sh->context);
sh->context = NULL;
mpg123_exit();
}
#ifdef AD_MPG123_MEAN_BITRATE
/* Update mean bitrate. This could be dropped if accurate time display
* on audio file playback is not desired. */
static void update_info(sh_audio_t *sh)
static void update_info(struct dec_audio *da)
{
struct ad_mpg123_context *con = sh->context;
if (con->vbr && --con->delay < 1) {
struct mpg123_frameinfo finfo;
if (MPG123_OK == mpg123_info(con->handle, &finfo)) {
struct ad_mpg123_context *con = da->priv;
struct mpg123_frameinfo finfo;
if (mpg123_info(con->handle, &finfo) != MPG123_OK)
return;
if (finfo.vbr != MPG123_CBR) {
if (--con->delay < 1) {
if (++con->mean_count > ((unsigned int) -1) / 2)
con->mean_count = ((unsigned int) -1) / 4;
/* Might not be numerically optimal, but works fine enough. */
con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
finfo.bitrate) / con->mean_count;
sh->i_bps = (int) (con->mean_rate * 1000 / 8);
da->i_bps = (int) (con->mean_rate * 1000 / 8);
con->delay = 10;
}
} else {
da->i_bps = (finfo.bitrate ? finfo.bitrate : compute_bitrate(&finfo))
* 1000 / 8;
con->delay = 1;
con->mean_rate = 0.;
con->mean_count = 0;
}
}
#endif
static int decode_audio(sh_audio_t *sh, unsigned char *buf, int minlen,
int maxlen)
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
{
int bytes;
struct ad_mpg123_context *con = da->priv;
void *buf = buffer->planes[0];
int ret;
bytes = decode_a_bit(sh, buf, maxlen);
/* This EOF is ignored, apparently, until input data is exhausted. */
if (bytes == 0)
return -1; /* EOF */
if (con->new_format) {
ret = set_format(da);
if (ret == MPG123_OK) {
return 0; // let caller handle format change
} else if (ret == MPG123_NEED_MORE) {
con->need_data = true;
} else {
goto mpg123_fail;
}
}
#ifdef AD_MPG123_MEAN_BITRATE
update_info(sh);
#endif
return bytes;
if (con->need_data) {
if (feed_new_packet(da) < 0)
return -1;
}
if (!mp_audio_config_equals(&da->decoded, buffer))
return 0;
size_t got_now = 0;
ret = mpg123_replace_buffer(con->handle, buf, maxlen * con->sample_size);
if (ret != MPG123_OK)
goto mpg123_fail;
ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
int got_samples = got_now / con->sample_size;
buffer->samples += got_samples;
da->pts_offset += got_samples;
if (ret == MPG123_NEW_FORMAT) {
con->new_format = true;
} else if (ret == MPG123_NEED_MORE) {
con->need_data = true;
} else if (ret != MPG123_OK && ret != MPG123_DONE) {
goto mpg123_fail;
}
update_info(da);
return 0;
mpg123_fail:
MP_ERR(da, "mpg123 decoding error: %s\n",
mpg123_strerror(con->handle));
return -1;
}
static int control(sh_audio_t *sh, int cmd, void *arg)
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct ad_mpg123_context *con = da->priv;
switch (cmd) {
case ADCTRL_RESYNC_STREAM:
/* Close/reopen the stream for mpg123 to make sure it doesn't
* think that it still knows the exact stream position.
* Otherwise, we would have funny effects from the gapless code.
* Oh, and it helps to minimize artifacts from jumping in the stream. */
if (reopen_stream(sh)) {
#ifdef AD_MPG123_MEAN_BITRATE
update_info(sh);
#endif
return CONTROL_TRUE;
} else {
/* MPlayer ignores this case! It just keeps on decoding.
* So we have to make sure resync never fails ... */
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"mpg123 cannot reopen stream for resync.\n");
case ADCTRL_RESET:
mpg123_close(con->handle);
if (mpg123_open_feed(con->handle) != MPG123_OK) {
MP_ERR(da, "mpg123 failed to reopen stream: %s\n",
mpg123_strerror(con->handle));
return CONTROL_FALSE;
}
break;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
@@ -502,7 +368,6 @@ static void add_decoders(struct mp_decoder_list *list)
const struct ad_functions ad_mpg123 = {
.name = "mpg123",
.add_decoders = add_decoders,
.preinit = preinit,
.init = init,
.uninit = uninit,
.control = control,
+178 -200
View File
@@ -19,60 +19,214 @@
*/
#include <string.h>
#include <assert.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include "config.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/av_common.h"
#include "mpvcore/options.h"
#include "common/msg.h"
#include "common/av_common.h"
#include "options/options.h"
#include "ad.h"
#define FILENAME_SPDIFENC "spdif"
#define OUTBUF_SIZE 65536
struct spdifContext {
struct mp_log *log;
AVFormatContext *lavf_ctx;
int iec61937_packet_size;
int out_buffer_len;
int out_buffer_size;
uint8_t *out_buffer;
uint8_t pb_buffer[OUTBUF_SIZE];
bool need_close;
};
static void uninit(sh_audio_t *sh);
static int read_packet(void *p, uint8_t *buf, int buf_size)
{
// spdifenc does not use read callback.
return 0;
}
static int write_packet(void *p, uint8_t *buf, int buf_size)
{
int len;
struct spdifContext *ctx = p;
len = FFMIN(buf_size, ctx->out_buffer_size -ctx->out_buffer_len);
memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, len);
ctx->out_buffer_len += len;
return len;
int buffer_left = ctx->out_buffer_size - ctx->out_buffer_len;
if (buf_size > buffer_left) {
MP_ERR(ctx, "spdif packet too large.\n");
buf_size = buffer_left;
}
memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, buf_size);
ctx->out_buffer_len += buf_size;
return buf_size;
}
static int64_t seek(void *p, int64_t offset, int whence)
static void uninit(struct dec_audio *da)
{
// spdifenc does not use seek callback.
struct spdifContext *spdif_ctx = da->priv;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
if (lavf_ctx) {
if (spdif_ctx->need_close)
av_write_trailer(lavf_ctx);
if (lavf_ctx->pb)
av_freep(&lavf_ctx->pb->buffer);
av_freep(&lavf_ctx->pb);
avformat_free_context(lavf_ctx);
}
}
static int init(struct dec_audio *da, const char *decoder)
{
struct spdifContext *spdif_ctx = talloc_zero(NULL, struct spdifContext);
da->priv = spdif_ctx;
spdif_ctx->log = da->log;
AVFormatContext *lavf_ctx = avformat_alloc_context();
if (!lavf_ctx)
goto fail;
lavf_ctx->oformat = av_guess_format("spdif", NULL, NULL);
if (!lavf_ctx->oformat)
goto fail;
spdif_ctx->lavf_ctx = lavf_ctx;
void *buffer = av_mallocz(OUTBUF_SIZE);
if (!buffer)
abort();
lavf_ctx->pb = avio_alloc_context(buffer, OUTBUF_SIZE, 1, spdif_ctx, NULL,
write_packet, NULL);
if (!lavf_ctx->pb) {
av_free(buffer);
goto fail;
}
// Request minimal buffering (not available on Libav)
#if LIBAVFORMAT_VERSION_MICRO >= 100
lavf_ctx->pb->direct = 1;
#endif
AVStream *stream = avformat_new_stream(lavf_ctx, 0);
if (!stream)
goto fail;
stream->codec->codec_id = mp_codec_to_av_codec_id(decoder);
AVDictionary *format_opts = NULL;
int num_channels = 0;
int sample_format = 0;
int samplerate = 0;
switch (stream->codec->codec_id) {
case AV_CODEC_ID_AAC:
spdif_ctx->iec61937_packet_size = 16384;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_AC3:
spdif_ctx->iec61937_packet_size = 6144;
sample_format = AF_FORMAT_AC3_LE;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_DTS:
if (da->opts->dtshd) {
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 2*4;
} else {
spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_AC3_LE;
samplerate = 48000;
num_channels = 2;
}
break;
case AV_CODEC_ID_EAC3:
spdif_ctx->iec61937_packet_size = 24576;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 2;
break;
case AV_CODEC_ID_MP3:
spdif_ctx->iec61937_packet_size = 4608;
sample_format = AF_FORMAT_MPEG2;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_TRUEHD:
spdif_ctx->iec61937_packet_size = 61440;
sample_format = AF_FORMAT_IEC61937_LE;
samplerate = 192000;
num_channels = 8;
break;
default:
abort();
}
mp_audio_set_num_channels(&da->decoded, num_channels);
mp_audio_set_format(&da->decoded, sample_format);
da->decoded.rate = samplerate;
if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
MP_FATAL(da, "libavformat spdif initialization failed.\n");
av_dict_free(&format_opts);
goto fail;
}
av_dict_free(&format_opts);
spdif_ctx->need_close = true;
return 1;
fail:
uninit(da);
return 0;
}
static int preinit(sh_audio_t *sh)
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
{
sh->samplesize = 2;
return 1;
struct spdifContext *spdif_ctx = da->priv;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
int sstride = 2 * da->decoded.channels.num;
assert(sstride == buffer->sstride);
if (maxlen * sstride < spdif_ctx->iec61937_packet_size)
return 0;
spdif_ctx->out_buffer_len = 0;
spdif_ctx->out_buffer_size = maxlen * sstride;
spdif_ctx->out_buffer = buffer->planes[0];
struct demux_packet *mpkt = demux_read_packet(da->header);
if (!mpkt)
return -1;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
pkt.pts = pkt.dts = 0;
MP_VERBOSE(da, "spdif packet, size=%d\n", pkt.size);
if (mpkt->pts != MP_NOPTS_VALUE) {
da->pts = mpkt->pts;
da->pts_offset = 0;
}
int ret = av_write_frame(lavf_ctx, &pkt);
avio_flush(lavf_ctx->pb);
buffer->samples = spdif_ctx->out_buffer_len / sstride;
da->pts_offset += buffer->samples;
talloc_free(mpkt);
if (ret < 0)
return -1;
return 0;
}
static int codecs[] = {
static int control(struct dec_audio *da, int cmd, void *arg)
{
return CONTROL_UNKNOWN;
}
static const int codecs[] = {
AV_CODEC_ID_AAC,
AV_CODEC_ID_AC3,
AV_CODEC_ID_DTS,
@@ -82,181 +236,6 @@ static int codecs[] = {
AV_CODEC_ID_NONE
};
static int init(sh_audio_t *sh, const char *decoder)
{
int srate, bps, *dtshd_rate;
AVFormatContext *lavf_ctx = NULL;
AVStream *stream = NULL;
const AVOption *opt = NULL;
struct spdifContext *spdif_ctx = NULL;
spdif_ctx = av_mallocz(sizeof(*spdif_ctx));
if (!spdif_ctx)
goto fail;
spdif_ctx->lavf_ctx = avformat_alloc_context();
if (!spdif_ctx->lavf_ctx)
goto fail;
sh->context = spdif_ctx;
lavf_ctx = spdif_ctx->lavf_ctx;
lavf_ctx->oformat = av_guess_format(FILENAME_SPDIFENC, NULL, NULL);
if (!lavf_ctx->oformat)
goto fail;
lavf_ctx->priv_data = av_mallocz(lavf_ctx->oformat->priv_data_size);
if (!lavf_ctx->priv_data)
goto fail;
lavf_ctx->pb = avio_alloc_context(spdif_ctx->pb_buffer, OUTBUF_SIZE, 1, spdif_ctx,
read_packet, write_packet, seek);
if (!lavf_ctx->pb)
goto fail;
stream = avformat_new_stream(lavf_ctx, 0);
if (!stream)
goto fail;
lavf_ctx->duration = AV_NOPTS_VALUE;
lavf_ctx->start_time = AV_NOPTS_VALUE;
lavf_ctx->streams[0]->codec->codec_id = mp_codec_to_av_codec_id(decoder);
lavf_ctx->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
if (AVERROR_PATCHWELCOME == lavf_ctx->oformat->write_header(lavf_ctx)) {
mp_msg(MSGT_DECAUDIO,MSGL_INFO,
"This codec is not supported by spdifenc.\n");
goto fail;
}
srate = 48000; //fake value
bps = 768000/8; //fake value
int num_channels = 0;
switch (lavf_ctx->streams[0]->codec->codec_id) {
case AV_CODEC_ID_AAC:
spdif_ctx->iec61937_packet_size = 16384;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = srate;
num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_AC3:
spdif_ctx->iec61937_packet_size = 6144;
sh->sample_format = AF_FORMAT_AC3_LE;
sh->samplerate = srate;
num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_DTS:
if(sh->opts->dtshd) {
opt = av_opt_find(&lavf_ctx->oformat->priv_class,
"dtshd_rate", NULL, 0, 0);
if (!opt)
goto fail;
dtshd_rate = (int*)(((uint8_t*)lavf_ctx->priv_data) +
opt->offset);
*dtshd_rate = 192000*4;
spdif_ctx->iec61937_packet_size = 32768;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000; // DTS core require 48000
num_channels = 2*4;
sh->i_bps = bps;
} else {
spdif_ctx->iec61937_packet_size = 32768;
sh->sample_format = AF_FORMAT_AC3_LE;
sh->samplerate = srate;
num_channels = 2;
sh->i_bps = bps;
}
break;
case AV_CODEC_ID_EAC3:
spdif_ctx->iec61937_packet_size = 24576;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000;
num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_MP3:
spdif_ctx->iec61937_packet_size = 4608;
sh->sample_format = AF_FORMAT_MPEG2;
sh->samplerate = srate;
num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_TRUEHD:
spdif_ctx->iec61937_packet_size = 61440;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000;
num_channels = 8;
sh->i_bps = bps;
break;
default:
break;
}
if (num_channels)
mp_chmap_from_channels(&sh->channels, num_channels);
return 1;
fail:
uninit(sh);
return 0;
}
static int decode_audio(sh_audio_t *sh, unsigned char *buf,
int minlen, int maxlen)
{
struct spdifContext *spdif_ctx = sh->context;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
AVPacket pkt;
spdif_ctx->out_buffer_len = 0;
spdif_ctx->out_buffer_size = maxlen;
spdif_ctx->out_buffer = buf;
while (spdif_ctx->out_buffer_len + spdif_ctx->iec61937_packet_size < maxlen
&& spdif_ctx->out_buffer_len < minlen) {
struct demux_packet *mpkt = demux_read_packet(sh->gsh);
if (!mpkt)
break;
mp_set_av_packet(&pkt, mpkt);
mp_msg(MSGT_DECAUDIO,MSGL_V, "pkt.data[%p] pkt.size[%d]\n",
pkt.data, pkt.size);
if (mpkt->pts != MP_NOPTS_VALUE) {
sh->pts = mpkt->pts;
sh->pts_bytes = 0;
}
int out_len = spdif_ctx->out_buffer_len;
int ret = lavf_ctx->oformat->write_packet(lavf_ctx, &pkt);
avio_flush(lavf_ctx->pb);
sh->pts_bytes += spdif_ctx->out_buffer_len - out_len;
talloc_free(mpkt);
if (ret < 0)
break;
}
return spdif_ctx->out_buffer_len;
}
static int control(sh_audio_t *sh, int cmd, void *arg)
{
return CONTROL_UNKNOWN;
}
static void uninit(sh_audio_t *sh)
{
struct spdifContext *spdif_ctx = sh->context;
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
if (lavf_ctx) {
if (lavf_ctx->oformat)
lavf_ctx->oformat->write_trailer(lavf_ctx);
av_freep(&lavf_ctx->pb);
if (lavf_ctx->streams) {
av_freep(&lavf_ctx->streams[0]->codec);
av_freep(&lavf_ctx->streams[0]->info);
av_freep(&lavf_ctx->streams[0]);
}
av_freep(&lavf_ctx->streams);
av_freep(&lavf_ctx->priv_data);
}
av_freep(&lavf_ctx);
av_freep(&spdif_ctx);
}
static void add_decoders(struct mp_decoder_list *list)
{
for (int n = 0; codecs[n] != AV_CODEC_ID_NONE; n++) {
@@ -271,7 +250,6 @@ static void add_decoders(struct mp_decoder_list *list)
const struct ad_functions ad_spdif = {
.name = "spdif",
.add_decoders = add_decoders,
.preinit = preinit,
.init = init,
.uninit = uninit,
.control = control,
+176 -184
View File
@@ -26,9 +26,9 @@
#include "demux/codec_tags.h"
#include "config.h"
#include "mpvcore/codecs.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/bstr.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "bstr/bstr.h"
#include "stream/stream.h"
#include "demux/demux.h"
@@ -38,6 +38,8 @@
#include "dec_audio.h"
#include "ad.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/filter/af.h"
@@ -46,7 +48,7 @@ extern const struct ad_functions ad_lavc;
extern const struct ad_functions ad_spdif;
static const struct ad_functions * const ad_drivers[] = {
#ifdef CONFIG_MPG123
#if HAVE_MPG123
&ad_mpg123,
#endif
&ad_lavc,
@@ -54,51 +56,57 @@ static const struct ad_functions * const ad_drivers[] = {
NULL
};
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
// ad_mpg123 needs to be able to decode 1152 samples at once
// ad_spdif needs up to 8192
#define DECODE_MAX_UNIT MPMAX(8192, 1152)
// At least 8192 samples, plus hack for ad_mpg123 and ad_spdif
#define DECODE_BUFFER_SAMPLES (8192 + DECODE_MAX_UNIT)
// Drop audio buffer and reinit it (after format change)
// Returns whether the format was valid at all.
static bool reinit_audio_buffer(struct dec_audio *da)
{
assert(!sh_audio->initialized);
resync_audio_stream(sh_audio);
sh_audio->samplesize = 4;
sh_audio->sample_format = AF_FORMAT_FLOAT_NE;
sh_audio->audio_out_minsize = 8192; // default, preinit() may change it
if (!sh_audio->ad_driver->preinit(sh_audio)) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder preinit failed.\n");
if (!mp_audio_config_valid(&da->decoded)) {
MP_ERR(da, "Audio decoder did not specify audio "
"format, or requested an unsupported configuration!\n");
return false;
}
mp_audio_buffer_reinit(da->decode_buffer, &da->decoded);
mp_audio_buffer_preallocate_min(da->decode_buffer, DECODE_BUFFER_SAMPLES);
return true;
}
static void uninit_decoder(struct dec_audio *d_audio)
{
if (d_audio->ad_driver) {
MP_VERBOSE(d_audio, "Uninit audio decoder.\n");
d_audio->ad_driver->uninit(d_audio);
}
d_audio->ad_driver = NULL;
talloc_free(d_audio->priv);
d_audio->priv = NULL;
}
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
{
if (!d_audio->ad_driver->init(d_audio, decoder)) {
MP_VERBOSE(d_audio, "Audio decoder init failed.\n");
d_audio->ad_driver = NULL;
uninit_decoder(d_audio);
return 0;
}
const int base_size = 65536;
// At least 64 KiB plus rounding up to next decodable unit size
sh_audio->a_buffer_size = base_size + sh_audio->audio_out_minsize;
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
"dec_audio: Allocating %d + %d = %d bytes for output buffer.\n",
sh_audio->audio_out_minsize, base_size,
sh_audio->a_buffer_size);
sh_audio->a_buffer = av_mallocz(sh_audio->a_buffer_size);
if (!sh_audio->a_buffer)
abort();
sh_audio->a_buffer_len = 0;
if (!sh_audio->ad_driver->init(sh_audio, decoder)) {
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Audio decoder init failed.\n");
uninit_audio(sh_audio); // free buffers
return 0;
}
sh_audio->initialized = 1;
if (mp_chmap_is_empty(&sh_audio->channels) || !sh_audio->samplerate) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder did not specify "
"audio format!\n");
uninit_audio(sh_audio); // free buffers
d_audio->decode_buffer = mp_audio_buffer_create(NULL);
if (!reinit_audio_buffer(d_audio)) {
uninit_decoder(d_audio);
return 0;
}
return 1;
}
struct mp_decoder_list *mp_audio_decoder_list(void)
struct mp_decoder_list *audio_decoder_list(void)
{
struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
for (int i = 0; ad_drivers[i] != NULL; i++)
@@ -106,10 +114,10 @@ struct mp_decoder_list *mp_audio_decoder_list(void)
return list;
}
static struct mp_decoder_list *mp_select_audio_decoders(const char *codec,
char *selection)
static struct mp_decoder_list *audio_select_decoders(const char *codec,
char *selection)
{
struct mp_decoder_list *list = mp_audio_decoder_list();
struct mp_decoder_list *list = audio_decoder_list();
struct mp_decoder_list *new = mp_select_decoders(list, codec, selection);
talloc_free(list);
return new;
@@ -124,86 +132,81 @@ static const struct ad_functions *find_driver(const char *name)
return NULL;
}
int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders)
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders)
{
assert(!sh_audio->initialized);
assert(!d_audio->ad_driver);
audio_reset_decoding(d_audio);
struct mp_decoder_entry *decoder = NULL;
struct mp_decoder_list *list =
mp_select_audio_decoders(sh_audio->gsh->codec, audio_decoders);
audio_select_decoders(d_audio->header->codec, audio_decoders);
mp_print_decoders(MSGT_DECAUDIO, MSGL_V, "Codec list:", list);
mp_print_decoders(d_audio->log, MSGL_V, "Codec list:", list);
for (int n = 0; n < list->num_entries; n++) {
struct mp_decoder_entry *sel = &list->entries[n];
const struct ad_functions *driver = find_driver(sel->family);
if (!driver)
continue;
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Opening audio decoder %s:%s\n",
sel->family, sel->decoder);
sh_audio->ad_driver = driver;
if (init_audio_codec(sh_audio, sel->decoder)) {
MP_VERBOSE(d_audio, "Opening audio decoder %s:%s\n",
sel->family, sel->decoder);
d_audio->ad_driver = driver;
if (init_audio_codec(d_audio, sel->decoder)) {
decoder = sel;
break;
}
sh_audio->ad_driver = NULL;
mp_tmsg(MSGT_DECAUDIO, MSGL_WARN, "Audio decoder init failed for "
MP_WARN(d_audio, "Audio decoder init failed for "
"%s:%s\n", sel->family, sel->decoder);
}
if (sh_audio->initialized) {
sh_audio->gsh->decoder_desc =
talloc_asprintf(NULL, "%s [%s:%s]", decoder->desc, decoder->family,
if (d_audio->ad_driver) {
d_audio->decoder_desc =
talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family,
decoder->decoder);
mp_msg(MSGT_DECAUDIO, MSGL_INFO, "Selected audio codec: %s\n",
sh_audio->gsh->decoder_desc);
mp_msg(MSGT_DECAUDIO, MSGL_V,
"AUDIO: %d Hz, %d ch, %s\n",
sh_audio->samplerate, sh_audio->channels.num,
af_fmt2str_short(sh_audio->sample_format));
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
"ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n",
sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels.num);
MP_INFO(d_audio, "Selected audio codec: %s\n",
d_audio->decoder_desc);
MP_VERBOSE(d_audio, "AUDIO: %d Hz, %d ch, %s\n",
d_audio->decoded.rate, d_audio->decoded.channels.num,
af_fmt_to_str(d_audio->decoded.format));
MP_SMODE(d_audio, "ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n",
d_audio->i_bps * 8, d_audio->decoded.rate,
d_audio->decoded.channels.num);
} else {
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"Failed to initialize an audio decoder for codec '%s'.\n",
sh_audio->gsh->codec ? sh_audio->gsh->codec : "<unknown>");
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
d_audio->header->codec ? d_audio->header->codec : "<unknown>");
}
talloc_free(list);
return sh_audio->initialized;
return !!d_audio->ad_driver;
}
void uninit_audio(sh_audio_t *sh_audio)
void audio_uninit(struct dec_audio *d_audio)
{
if (sh_audio->afilter) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL;
if (!d_audio)
return;
if (d_audio->afilter) {
MP_VERBOSE(d_audio, "Uninit audio filters...\n");
af_destroy(d_audio->afilter);
d_audio->afilter = NULL;
}
if (sh_audio->initialized) {
mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Uninit audio.\n");
sh_audio->ad_driver->uninit(sh_audio);
sh_audio->initialized = 0;
}
talloc_free(sh_audio->gsh->decoder_desc);
sh_audio->gsh->decoder_desc = NULL;
av_freep(&sh_audio->a_buffer);
uninit_decoder(d_audio);
talloc_free(d_audio->decode_buffer);
talloc_free(d_audio);
}
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format)
{
if (!sh_audio->afilter)
sh_audio->afilter = af_new(sh_audio->opts);
struct af_stream *afs = sh_audio->afilter;
if (!d_audio->afilter)
d_audio->afilter = af_new(d_audio->global);
struct af_stream *afs = d_audio->afilter;
// input format: same as codec's output format:
mp_audio_buffer_get_format(d_audio->decode_buffer, &afs->input);
// Sample rate can be different when adjusting playback speed
afs->input.rate = in_samplerate;
mp_audio_set_channels(&afs->input, &sh_audio->channels);
mp_audio_set_format(&afs->input, sh_audio->sample_format);
// output format: same as ao driver's input format (if missing, fallback to input)
afs->output.rate = *out_samplerate;
@@ -212,15 +215,14 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
char *s_from = mp_audio_config_to_str(&afs->input);
char *s_to = mp_audio_config_to_str(&afs->output);
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
"Building audio filter chain for %s -> %s...\n", s_from, s_to);
MP_VERBOSE(d_audio, "Building audio filter chain for %s -> %s...\n", s_from, s_to);
talloc_free(s_from);
talloc_free(s_to);
// let's autoprobe it!
if (af_init(afs) != 0) {
af_destroy(afs);
sh_audio->afilter = NULL;
d_audio->afilter = NULL;
return 0; // failed :(
}
@@ -231,137 +233,127 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
return 1;
}
static void set_min_out_buffer_size(struct bstr *outbuf, int len)
// Filter len bytes of input, put result into outbuf.
static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf,
int len)
{
size_t oldlen = talloc_get_size(outbuf->start);
if (oldlen < len) {
assert(outbuf->start); // talloc context should be already set
mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size "
"from %zd to %d\n", oldlen, len);
outbuf->start = talloc_realloc_size(NULL, outbuf->start, len);
}
}
static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
{
assert(len - 1 + sh->audio_out_minsize <= sh->a_buffer_size);
int error = 0;
// Decode more bytes if needed
int old_samplerate = sh->samplerate;
struct mp_chmap old_channels = sh->channels;
int old_sample_format = sh->sample_format;
while (sh->a_buffer_len < len) {
unsigned char *buf = sh->a_buffer + sh->a_buffer_len;
int minlen = len - sh->a_buffer_len;
int maxlen = sh->a_buffer_size - sh->a_buffer_len;
int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen);
int format_change = sh->samplerate != old_samplerate
|| !mp_chmap_equals(&sh->channels, &old_channels)
|| sh->sample_format != old_sample_format;
if (ret <= 0 || format_change) {
error = format_change ? -2 : -1;
// samples from format-changing call get discarded too
len = sh->a_buffer_len;
struct mp_audio config;
mp_audio_buffer_get_format(da->decode_buffer, &config);
while (mp_audio_buffer_samples(da->decode_buffer) < len) {
int maxlen = mp_audio_buffer_get_write_available(da->decode_buffer);
if (maxlen < DECODE_MAX_UNIT)
break;
struct mp_audio buffer;
mp_audio_buffer_get_write_buffer(da->decode_buffer, maxlen, &buffer);
buffer.samples = 0;
error = da->ad_driver->decode_audio(da, &buffer, maxlen);
if (error < 0)
break;
// Commit the data just read as valid data
mp_audio_buffer_finish_write(da->decode_buffer, buffer.samples);
// Format change
if (!mp_audio_config_equals(&da->decoded, &config)) {
// If there are still samples left in the buffer, let them drain
// first, and don't signal a format change to the caller yet.
if (mp_audio_buffer_samples(da->decode_buffer) > 0)
break;
error = -2;
break;
}
sh->a_buffer_len += ret;
}
// Filter
struct mp_audio filter_input = {
.audio = sh->a_buffer,
.len = len,
.rate = sh->samplerate,
};
mp_audio_set_format(&filter_input, sh->sample_format);
mp_audio_set_channels(&filter_input, &sh->channels);
struct mp_audio filter_data;
mp_audio_buffer_peek(da->decode_buffer, &filter_data);
filter_data.rate = da->afilter->input.rate; // due to playback speed change
len = MPMIN(filter_data.samples, len);
filter_data.samples = len;
bool eof = filter_data.samples == 0 && error < 0;
struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
if (!filter_output)
if (af_filter(da->afilter, &filter_data, eof ? AF_FILTER_FLAG_EOF : 0) < 0)
return -1;
set_min_out_buffer_size(outbuf, outbuf->len + filter_output->len);
memcpy(outbuf->start + outbuf->len, filter_output->audio,
filter_output->len);
outbuf->len += filter_output->len;
mp_audio_buffer_append(outbuf, &filter_data);
if (eof && filter_data.samples > 0)
error = 0; // don't end playback yet
// remove processed data from decoder buffer:
sh->a_buffer_len -= len;
memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);
mp_audio_buffer_skip(da->decode_buffer, len);
// Assume the filter chain is drained from old data at this point.
// (If not, the remaining old data is discarded.)
if (error == -2) {
if (!reinit_audio_buffer(da))
error = -1; // switch to invalid format
}
return error;
}
/* Try to get at least minlen decoded+filtered bytes in outbuf
/* Try to get at least minsamples decoded+filtered samples in outbuf
* (total length including possible existing data).
* Return 0 on success, -1 on error/EOF (not distinguished).
* In the former case outbuf->len is always >= minlen on return.
* In case of EOF/error it might or might not be.
* Outbuf.start must be talloc-allocated, and will be reallocated
* if needed to fit all filter output. */
int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen)
* Return 0 on success, -1 on error/EOF (not distinguidaed).
* In the former case outbuf has at least minsamples buffered on return.
* In case of EOF/error it might or might not be. */
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
int minsamples)
{
// Indicates that a filter seems to be buffering large amounts of data
int huge_filter_buffer = 0;
// Decoded audio must be cut at boundaries of this many bytes
int unitsize = sh_audio->channels.num * sh_audio->samplesize * 16;
// Decoded audio must be cut at boundaries of this many samples
// (Note: the reason for this is unknown, possibly a refactoring artifact)
int unitsize = 16;
/* Filter output size will be about filter_multiplier times input size.
* If some filter buffers audio in big blocks this might only hold
* as average over time. */
double filter_multiplier = af_calc_filter_multiplier(sh_audio->afilter);
double filter_multiplier = af_calc_filter_multiplier(d_audio->afilter);
/* If the decoder set audio_out_minsize then it can do the equivalent of
* "while (output_len < target_len) output_len += audio_out_minsize;",
* so we must guarantee there is at least audio_out_minsize-1 bytes
* more space in the output buffer than the minimum length we try to
* decode. */
int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize;
if (!unitsize)
return -1;
max_decode_len -= max_decode_len % unitsize;
int prev_buffered = -1;
while (minsamples >= 0) {
int buffered = mp_audio_buffer_samples(outbuf);
if (minsamples < buffered || buffered == prev_buffered)
break;
prev_buffered = buffered;
while (minlen >= 0 && outbuf->len < minlen) {
int decsamples = (minsamples - buffered) / filter_multiplier;
// + some extra for possible filter buffering
int declen = (minlen - outbuf->len) / filter_multiplier + (unitsize << 5);
if (huge_filter_buffer)
decsamples += unitsize << 5;
if (huge_filter_buffer) {
/* Some filter must be doing significant buffering if the estimated
* input length didn't produce enough output from filters.
* Feed the filters 2k bytes at a time until we have enough output.
* Very small amounts could make filtering inefficient while large
* amounts can make MPlayer demux the file unnecessarily far ahead
* Feed the filters 250 samples at a time until we have enough
* output. Very small amounts could make filtering inefficient while
* large amounts can make mpv demux the file unnecessarily far ahead
* to get audio data and buffer video frames in memory while doing
* so. However the performance impact of either is probably not too
* significant as long as the value is not completely insane. */
declen = 2000;
declen -= declen % unitsize;
if (declen > max_decode_len)
declen = max_decode_len;
else
/* if this iteration does not fill buffer, we must have lots
* of buffering in filters */
huge_filter_buffer = 1;
int res = filter_n_bytes(sh_audio, outbuf, declen);
decsamples = 250;
}
/* if this iteration does not fill buffer, we must have lots
* of buffering in filters */
huge_filter_buffer = 1;
int res = filter_n_bytes(d_audio, outbuf, decsamples);
if (res < 0)
return res;
}
return 0;
}
void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte)
void audio_reset_decoding(struct dec_audio *d_audio)
{
set_min_out_buffer_size(outbuf, outbuf->len + count);
memmove(outbuf->start + count, outbuf->start, outbuf->len);
memset(outbuf->start, byte, count);
outbuf->len += count;
}
void resync_audio_stream(sh_audio_t *sh_audio)
{
sh_audio->pts = MP_NOPTS_VALUE;
if (!sh_audio->initialized)
return;
sh_audio->ad_driver->control(sh_audio, ADCTRL_RESYNC_STREAM, NULL);
if (d_audio->ad_driver)
d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
if (d_audio->afilter)
af_control_all(d_audio->afilter, AF_CONTROL_RESET, NULL);
d_audio->pts = MP_NOPTS_VALUE;
d_audio->pts_offset = 0;
if (d_audio->decode_buffer)
mp_audio_buffer_clear(d_audio->decode_buffer);
}
+31 -9
View File
@@ -20,20 +20,42 @@
#define MPLAYER_DEC_AUDIO_H
#include "audio/chmap.h"
#include "audio/audio.h"
#include "demux/stheader.h"
struct bstr;
struct mp_audio_buffer;
struct mp_decoder_list;
struct mp_decoder_list *mp_audio_decoder_list(void);
int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders);
int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen);
void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte);
void resync_audio_stream(sh_audio_t *sh_audio);
void skip_audio_frame(sh_audio_t *sh_audio);
void uninit_audio(sh_audio_t *sh_audio);
struct dec_audio {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
const struct ad_functions *ad_driver;
struct sh_stream *header;
struct mp_audio_buffer *decode_buffer;
struct af_stream *afilter;
char *decoder_desc;
// set by decoder
struct mp_audio decoded; // format of decoded audio (no data, temporarily
// different from decode_buffer during format
// changes)
int i_bps; // input bitrate, can change with VBR sources
// last known pts value in output from decoder
double pts;
// number of samples output by decoder after last known pts
int pts_offset;
// For free use by the ad_driver
void *priv;
};
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
struct mp_decoder_list *audio_decoder_list(void);
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders);
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
int minsamples);
void audio_reset_decoding(struct dec_audio *d_audio);
void audio_uninit(struct dec_audio *d_audio);
int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format);
+172 -123
View File
@@ -22,8 +22,11 @@
#include <string.h>
#include <assert.h>
#include "mpvcore/m_option.h"
#include "mpvcore/m_config.h"
#include "common/common.h"
#include "common/global.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "af.h"
@@ -52,18 +55,20 @@ extern struct af_info af_info_karaoke;
extern struct af_info af_info_scaletempo;
extern struct af_info af_info_bs2b;
extern struct af_info af_info_lavfi;
extern struct af_info af_info_convert24;
extern struct af_info af_info_convertsignendian;
static struct af_info* filter_list[] = {
&af_info_dummy,
&af_info_delay,
&af_info_channels,
&af_info_force,
&af_info_format,
&af_info_volume,
&af_info_equalizer,
&af_info_pan,
&af_info_surround,
&af_info_sub,
#ifdef HAVE_SYS_MMAN_H
#if HAVE_SYS_MMAN_H
&af_info_export,
#endif
&af_info_drc,
@@ -72,21 +77,22 @@ static struct af_info* filter_list[] = {
&af_info_lavrresample,
&af_info_sweep,
&af_info_hrtf,
#ifdef CONFIG_LADSPA
#if HAVE_LADSPA
&af_info_ladspa,
#endif
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
&af_info_scaletempo,
#ifdef CONFIG_LIBBS2B
#if HAVE_LIBBS2B
&af_info_bs2b,
#endif
#ifdef CONFIG_AF_LAVFI
#if HAVE_AF_LAVFI
&af_info_lavfi,
#endif
// Must come last, because it's the fallback format conversion filter
&af_info_format,
// Must come last, because they're fallback format conversion filter
&af_info_convert24,
&af_info_convertsignendian,
NULL
};
@@ -109,7 +115,10 @@ static bool get_desc(struct m_obj_desc *dst, int index)
const struct m_obj_list af_obj_list = {
.get_desc = get_desc,
.description = "audio filters",
.legacy_hacks = true, // many filters have custom option parsing
.aliases = {
{"force", "format"},
{0}
},
};
static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
@@ -133,7 +142,7 @@ static int input_control(struct af_instance* af, int cmd, void* arg)
{
switch (cmd) {
case AF_CONTROL_REINIT:
assert(arg == &((struct af_stream *)af->setup)->input);
assert(arg == &((struct af_stream *)af->priv)->input);
return AF_OK;
}
return AF_UNKNOWN;
@@ -141,7 +150,7 @@ static int input_control(struct af_instance* af, int cmd, void* arg)
static int output_control(struct af_instance* af, int cmd, void* arg)
{
struct af_stream *s = af->setup;
struct af_stream *s = af->priv;
struct mp_audio *output = &s->output;
struct mp_audio *filter_output = &s->filter_output;
@@ -159,9 +168,9 @@ static int output_control(struct af_instance* af, int cmd, void* arg)
return AF_UNKNOWN;
}
static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data)
static int dummy_filter(struct af_instance* af, struct mp_audio* data, int f)
{
return data;
return 0;
}
/* Function for creating a new filter of type name.The name may
@@ -171,8 +180,7 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
{
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) {
mp_tmsg(MSGT_VFILTER, MSGL_ERR,
"Couldn't find audio filter '%s'.\n", name);
MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
return NULL;
}
const struct af_info *info = desc.p;
@@ -181,40 +189,37 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
if (info->flags & AF_FLAGS_NOT_REENTRANT) {
for (struct af_instance *cur = s->first; cur; cur = cur->next) {
if (cur->info == info) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one "
MP_ERR(s, "There can only be one "
"instance of the filter '%s' in each stream\n", name);
return NULL;
}
}
}
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name);
MP_VERBOSE(s, "Adding filter %s \n", name);
struct af_instance *af = talloc_zero(NULL, struct af_instance);
*af = (struct af_instance) {
.info = info,
.mul = 1,
.data = talloc_zero(af, struct mp_audio),
.log = mp_log_new(af, s->log, name),
};
struct m_config *config = m_config_from_obj_desc(af, &desc);
if (m_config_initialize_obj(config, &desc, &af->priv, &args) < 0)
struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
goto error;
if (m_config_set_obj_params(config, args) < 0)
goto error;
af->priv = config->optstruct;
// Initialize the new filter
if (af->info->open(af) != AF_OK)
goto error;
if (args && af->control) {
// Single option string for old filters
char *opts = (char *)args; // m_config_initialize_obj did this
assert(!af->priv);
if (af->control(af, AF_CONTROL_COMMAND_LINE, opts) <= AF_ERROR)
goto error;
}
return af;
error:
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[libaf] Couldn't create or open audio filter '%s'\n", name);
MP_ERR(s, "Couldn't create or open audio filter '%s'\n", name);
talloc_free(af);
return NULL;
}
@@ -275,17 +280,14 @@ static void af_remove(struct af_stream *s, struct af_instance *af)
return;
// Print friendly message
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",
af->info->name);
// Notify filter before changing anything
af->control(af, AF_CONTROL_PRE_DESTROY, 0);
MP_VERBOSE(s, "Removing filter %s \n", af->info->name);
// Detach pointers
af->prev->next = af->next;
af->next->prev = af->prev;
af->uninit(af);
if (af->uninit)
af->uninit(af);
talloc_free(af);
}
@@ -303,26 +305,26 @@ repeat:
static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
int msg_level)
{
mp_msg(MSGT_AFILTER, msg_level, "Audio filter chain:\n");
MP_MSG(s, msg_level, "Audio filter chain:\n");
struct af_instance *af = s->first;
while (af) {
mp_msg(MSGT_AFILTER, msg_level, " [%s] ", af->info->name);
MP_MSG(s, msg_level, " [%s] ", af->info->name);
if (af->data) {
char *info = mp_audio_config_to_str(af->data);
mp_msg(MSGT_AFILTER, msg_level, "%s", info);
MP_MSG(s, msg_level, "%s", info);
talloc_free(info);
}
if (af == at)
mp_msg(MSGT_AFILTER, msg_level, " <-");
mp_msg(MSGT_AFILTER, msg_level, "\n");
MP_MSG(s, msg_level, " <-");
MP_MSG(s, msg_level, "\n");
af = af->next;
}
mp_msg(MSGT_AFILTER, msg_level, " [ao] ");
MP_MSG(s, msg_level, " [ao] ");
char *info = mp_audio_config_to_str(&s->output);
mp_msg(MSGT_AFILTER, msg_level, "%s\n", info);
MP_MSG(s, msg_level, "%s\n", info);
talloc_free(info);
}
@@ -334,14 +336,76 @@ static int af_count_filters(struct af_stream *s)
return count;
}
static char *af_find_conversion_filter(int srcfmt, int dstfmt)
// Finds the first conversion filter on the way from srcfmt to dstfmt.
// Conversions form a DAG: each node is a format/filter pair, and possible
// conversions are edges. We search the DAG for the shortest path.
// Some cases visit the same filter multiple times, but with different formats
// (like u24le->s8), so one node per format or filter separate is not enough.
// Returns the filter and dest. format for the first conversion step.
// (So we know what conversion filter with what format to insert next.)
static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
{
for (int n = 0; filter_list[n]; n++) {
struct af_info *af = filter_list[n];
if (af->test_conversion && af->test_conversion(srcfmt, dstfmt))
return (char *)af->name;
#define MAX_NODES (64 * 32)
int num_fmt = 0, num_filt = 0;
for (int n = 0; filter_list[n]; n++)
num_filt = n + 1;
for (int n = 0; af_fmtstr_table[n].format; n++)
num_fmt = n + 1;
int num_nodes = num_fmt * num_filt;
assert(num_nodes < MAX_NODES);
bool visited[MAX_NODES] = {0};
unsigned char distance[MAX_NODES];
short previous[MAX_NODES] = {0};
for (int n = 0; n < num_nodes; n++) {
distance[n] = 255;
if (af_fmtstr_table[n % num_fmt].format == srcfmt)
distance[n] = 0;
}
return NULL;
while (1) {
int next = -1;
for (int n = 0; n < num_nodes; n++) {
if (!visited[n] && (next < 0 || (distance[n] < distance[next])))
next = n;
}
if (next < 0 || distance[next] == 255)
return NULL;
visited[next] = true;
int fmt = next % num_fmt;
if (af_fmtstr_table[fmt].format == *dstfmt) {
// Best match found
for (int cur = next; cur >= 0; cur = previous[cur] - 1) {
if (distance[cur] == 1) {
*dstfmt = af_fmtstr_table[cur % num_fmt].format;
return (char *)filter_list[cur / num_fmt]->name;
}
}
return NULL;
}
for (int n = 0; filter_list[n]; n++) {
struct af_info *af = filter_list[n];
if (!af->test_conversion)
continue;
for (int i = 0; af_fmtstr_table[i].format; i++) {
if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format,
af_fmtstr_table[i].format))
{
int other = n * num_fmt + i;
int ndist = distance[next] + 1;
if (ndist < distance[other]) {
distance[other] = ndist;
previous[other] = next + 1;
}
}
}
}
}
assert(0);
#undef MAX_NODES
}
static bool af_is_conversion_filter(struct af_instance *af)
@@ -352,7 +416,7 @@ static bool af_is_conversion_filter(struct af_instance *af)
// in is what af can take as input - insert a conversion filter if the actual
// input format doesn't match what af expects.
// Returns:
// AF_OK: must call af_reinit() or equivalent, format matches
// AF_OK: must call af_reinit() or equivalent, format matches (or is closer)
// AF_FALSE: nothing was changed, format matches
// else: error
static int af_fix_format_conversion(struct af_stream *s,
@@ -365,18 +429,21 @@ static int af_fix_format_conversion(struct af_stream *s,
struct mp_audio actual = *prev->data;
if (actual.format == in.format)
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) {
*p_af = prev;
return AF_OK;
}
char *filter = af_find_conversion_filter(actual.format, in.format);
int dstfmt = in.format;
char *filter = af_find_conversion_filter(actual.format, &dstfmt);
if (!filter)
return AF_ERROR;
if (strcmp(filter, prev->info->name) == 0) {
if (prev->control(prev, AF_CONTROL_SET_FORMAT, &dstfmt) == AF_OK) {
*p_af = prev;
return AF_OK;
}
}
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt)))
return rv;
*p_af = new;
return AF_OK;
@@ -392,7 +459,7 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
struct mp_audio actual = *prev->data;
if (mp_chmap_equals(&actual.channels, &in.channels))
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_CHANNELS, &in.channels) == AF_OK) {
if (prev->control(prev, AF_CONTROL_SET_CHANNELS, &in.channels) == AF_OK) {
*p_af = prev;
return AF_OK;
}
@@ -401,7 +468,7 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.channels)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_CHANNELS, &in.channels)))
return rv;
*p_af = new;
return AF_OK;
@@ -416,7 +483,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
struct mp_audio actual = *prev->data;
if (actual.rate == in.rate)
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_RESAMPLE_RATE, &in.rate) == AF_OK) {
if (prev->control(prev, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
*p_af = prev;
return AF_OK;
}
@@ -425,7 +492,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate)))
return rv;
*p_af = new;
return AF_OK;
@@ -439,10 +506,12 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
// In that case, you should always rebuild the filter chain, or abort.
static int af_reinit(struct af_stream *s)
{
remove_auto_inserted_filters(s);
// Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
struct af_instance *af = s->first->next;
int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter
// Up to 7 retries per filter (channel, rate, 5x format conversions)
int max_retry = af_count_filters(s) * 7;
int retry = 0;
while (af) {
if (retry >= max_retry)
@@ -451,10 +520,11 @@ static int af_reinit(struct af_stream *s)
// Check if this is the first filter
struct mp_audio in = *af->prev->data;
// Reset just in case...
in.audio = NULL;
in.len = 0;
mp_audio_set_null_data(&in);
int rv = af->control(af, AF_CONTROL_REINIT, &in);
if (rv == AF_OK && !mp_audio_config_equals(&in, af->prev->data))
rv = AF_FALSE; // conversion filter needed
switch (rv) {
case AF_OK:
af = af->next;
@@ -484,8 +554,8 @@ static int af_reinit(struct af_stream *s)
break;
}
default:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not "
"work, audio filter '%s' returned error code %i\n",
MP_ERR(s, "Reinitialization did not work, "
"audio filter '%s' returned error code %i\n",
af->info->name, rv);
af_print_filter_chain(s, af, MSGL_ERR);
return AF_ERROR;
@@ -502,8 +572,7 @@ static int af_reinit(struct af_stream *s)
return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
negotiate_error:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
"This error should never occur, please send a bug report.\n");
MP_ERR(s, "Unable to convert audio input format to output format.\n");
af_print_filter_chain(s, af, MSGL_ERR);
return AF_ERROR;
}
@@ -515,7 +584,7 @@ void af_uninit(struct af_stream *s)
af_remove(s, s->first->next);
}
struct af_stream *af_new(struct MPOpts *opts)
struct af_stream *af_new(struct mpv_global *global)
{
struct af_stream *s = talloc_zero(NULL, struct af_stream);
static struct af_info in = { .name = "in" };
@@ -523,8 +592,8 @@ struct af_stream *af_new(struct MPOpts *opts)
*s->first = (struct af_instance) {
.info = &in,
.control = input_control,
.play = dummy_play,
.setup = s,
.filter = dummy_filter,
.priv = s,
.data = &s->input,
.mul = 1.0,
};
@@ -533,14 +602,15 @@ struct af_stream *af_new(struct MPOpts *opts)
*s->last = (struct af_instance) {
.info = &out,
.control = output_control,
.play = dummy_play,
.setup = s,
.filter = dummy_filter,
.priv = s,
.data = &s->filter_output,
.mul = 1.0,
};
s->first->next = s->last;
s->last->prev = s->first;
s->opts = opts;
s->opts = global->opts;
s->log = mp_log_new(s, global->log, "!af");
return s;
}
@@ -553,7 +623,7 @@ void af_destroy(struct af_stream *s)
/* Initialize the stream "s". This function creates a new filter list
if necessary according to the values set in input and output. Input
and output should contain the format of the current movie and the
formate of the preferred output respectively. The function is
format of the preferred output respectively. The function is
reentrant i.e. if called with an already initialized stream the
stream will be reinitialized.
If one of the prefered output parameters is 0 the one that needs
@@ -566,8 +636,8 @@ int af_init(struct af_stream *s)
return -1;
// Precaution in case caller is misbehaving
s->input.audio = s->output.audio = NULL;
s->input.len = s->output.len = 0;
mp_audio_set_null_data(&s->input);
mp_audio_set_null_data(&s->output);
// Check if this is the first call
if (s->first->next == s->last) {
@@ -581,11 +651,9 @@ int af_init(struct af_stream *s)
}
}
remove_auto_inserted_filters(s);
if (af_reinit(s) != AF_OK) {
// Something is stuffed audio out will not work
mp_msg(MSGT_AFILTER, MSGL_ERR, "Could not create audio filter chain.\n");
MP_ERR(s, "Could not create audio filter chain.\n");
af_uninit(s);
return -1;
}
@@ -619,36 +687,31 @@ struct af_instance *af_add(struct af_stream *s, char *name, char **args)
return new;
}
// Filter data chunk through the filters in the list
struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data)
/* Filter data chunk through the filters in the list.
* On success, *data is set to the filtered data/format.
* Warning: input audio data will be overwritten.
*/
int af_filter(struct af_stream *s, struct mp_audio *data, int flags)
{
struct af_instance *af = s->first;
assert(mp_audio_config_equals(af->data, data));
// Iterate through all filters
do {
if (data->len <= 0)
break;
data = af->play(af, data);
while (af) {
int r = af->filter(af, data, flags);
if (r < 0)
return r;
assert(mp_audio_config_equals(af->data, data));
af = af->next;
} while (af && data);
return data;
}
return 0;
}
/* Calculate the minimum output buffer size for given input data d
* when using the RESIZE_LOCAL_BUFFER macro. The +t+1 part ensures the
* value is >= len*mul rounded upwards to whole samples even if the
* double 'mul' is inexact. */
int af_lencalc(double mul, struct mp_audio *d)
{
int t = d->bps * d->nch;
return d->len * mul + t + 1;
}
// Calculate average ratio of filter output size to input size
// Calculate average ratio of filter output samples to input samples.
// e.g: num_output_samples = mul * num_input_samples
double af_calc_filter_multiplier(struct af_stream *s)
{
struct af_instance *af = s->first;
double mul = 1;
// Iterate through all filters and calculate total multiplication factor
do {
mul *= af->mul;
af = af->next;
@@ -657,41 +720,20 @@ double af_calc_filter_multiplier(struct af_stream *s)
return mul;
}
/* Calculate the total delay [bytes output] caused by the filters */
/* Calculate the total delay [seconds of output] caused by the filters */
double af_calc_delay(struct af_stream *s)
{
struct af_instance *af = s->first;
register double delay = 0.0;
// Iterate through all filters
double delay = 0.0;
while (af) {
delay += af->delay;
delay *= af->mul;
af = af->next;
}
return delay;
}
/* Helper function called by the macro with the same name this
function should not be called directly */
int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data)
{
// Calculate new length
register int len = af_lencalc(af->mul, data);
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
"old len = %i, new len = %i\n", af->info->name, af->data->len, len);
// If there is a buffer free it
free(af->data->audio);
// Create new buffer and check that it is OK
af->data->audio = malloc(len);
if (!af->data->audio) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
return AF_ERROR;
}
af->data->len = len;
return AF_OK;
}
// documentation in af.h
/* Send control to all filters, starting with the last until one accepts the
* command with AF_OK. Return the accepting filter. */
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
{
int res = AF_UNKNOWN;
@@ -704,3 +746,10 @@ struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
}
return NULL;
}
/* Send control to all filters. Never stop, even if a filter returns AF_OK. */
void af_control_all(struct af_stream *s, int cmd, void *arg)
{
for (struct af_instance *af = s->first; af; af = af->next)
af->control(af, cmd, arg);
}
+46 -189
View File
@@ -21,17 +21,16 @@
#include <stdio.h>
#include <stdbool.h>
#include <sys/types.h>
#include "config.h"
#include "mpvcore/options.h"
#include "options/options.h"
#include "audio/format.h"
#include "audio/chmap.h"
#include "audio/audio.h"
#include "control.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
struct af_instance;
struct mpv_global;
// Number of channels
#define AF_NCH MP_NUM_CHANNELS
@@ -40,13 +39,14 @@ struct af_instance;
#define AF_FLAGS_REENTRANT 0x00000000
#define AF_FLAGS_NOT_REENTRANT 0x00000001
// Flags for af->filter()
#define AF_FILTER_FLAG_EOF 1
/* Audio filter information not specific for current instance, but for
a specific filter */
struct af_info {
const char *info;
const char *name;
const char *author;
const char *comment;
const int flags;
int (*open)(struct af_instance *vf);
bool (*test_conversion)(int src_format, int dst_format);
@@ -58,18 +58,22 @@ struct af_info {
// Linked list of audio filters
struct af_instance {
const struct af_info *info;
struct mp_log *log;
int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af);
struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data);
void *setup; // old field for priv structs
/* flags is a bit mask of AF_FILTER_FLAG_* values
* returns 0 on success, negative value on error
*/
int (*filter)(struct af_instance *af, struct mp_audio *data, int flags);
void *priv;
struct mp_audio *data; // configuration for outgoing data stream
struct mp_audio *data; // configuration and buffer for outgoing data stream
struct af_instance *next;
struct af_instance *prev;
double delay; /* Delay caused by the filter, in units of bytes read without
* corresponding output */
double delay; /* Delay caused by the filter, in seconds of audio consumed
* without corresponding output */
double mul; /* length multiplier: how much does this instance change
the length of the buffer. */
* the number of samples passed though. (Ratio of input
* and output, e.g. mul=4 => 1 sample becomes 4 samples) .*/
bool auto_inserted; // inserted by af.c, such as conversion filters
};
@@ -80,18 +84,15 @@ struct af_stream {
struct af_instance *last;
// The user sets the input format (what the decoder outputs), and sets some
// or all fields in output to the output format the AO accepts.
// See fixup_output_format().
struct mp_audio input;
struct mp_audio output;
struct mp_audio filter_output;
struct mp_log *log;
struct MPOpts *opts;
};
/*********************************************
// Return values
*/
// Return values
#define AF_DETACH 2
#define AF_OK 1
#define AF_TRUE 1
@@ -100,190 +101,46 @@ struct af_stream {
#define AF_ERROR -2
#define AF_FATAL -3
// Parameters for af_control_*
enum af_control {
AF_CONTROL_REINIT = 1,
AF_CONTROL_RESET,
AF_CONTROL_SET_RESAMPLE_RATE,
AF_CONTROL_SET_FORMAT,
AF_CONTROL_SET_CHANNELS,
AF_CONTROL_SET_VOLUME,
AF_CONTROL_GET_VOLUME,
AF_CONTROL_SET_PAN_LEVEL,
AF_CONTROL_SET_PAN_NOUT,
AF_CONTROL_SET_PAN_BALANCE,
AF_CONTROL_GET_PAN_BALANCE,
AF_CONTROL_SET_PLAYBACK_SPEED,
};
// Argument for AF_CONTROL_SET_PAN_LEVEL
typedef struct af_control_ext_s {
void* arg; // Argument
int ch; // Chanel number
} af_control_ext_t;
/*********************************************
// Export functions
*/
/**
* \defgroup af_chain Audio filter chain functions
* \{
* \param s filter chain
*/
struct af_stream *af_new(struct MPOpts *opts);
struct af_stream *af_new(struct mpv_global *global);
void af_destroy(struct af_stream *s);
/**
* \brief Initialize the stream "s".
* \return 0 on success, -1 on failure
*
* This function creates a new filter list if necessary, according
* to the values set in input and output. Input and output should contain
* the format of the current movie and the format of the preferred output
* respectively.
* Filters to convert to the preferred output format are inserted
* automatically, except when they are set to 0.
* The function is reentrant i.e. if called with an already initialized
* stream the stream will be reinitialized.
*/
int af_init(struct af_stream *s);
/**
* \brief Uninit and remove all filters from audio filter chain
*/
void af_uninit(struct af_stream *s);
/**
* \brief This function adds the filter "name" to the stream s.
* \param name name of filter to add
* \return pointer to the new filter, NULL if insert failed
*
* The filter will be inserted somewhere nice in the
* list of filters (i.e. at the beginning unless the
* first filter is the format filter (why??).
*/
struct af_instance *af_add(struct af_stream *s, char *name, char **args);
/**
* \brief filter data chunk through the filters in the list
* \param data data to play
* \return resulting data
* \ingroup af_chain
*/
struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data);
/**
* \brief send control to all filters, starting with the last until
* one accepts the command with AF_OK.
* \param cmd filter control command
* \param arg argument for filter command
* \return the accepting filter or NULL if none was found
*/
int af_filter(struct af_stream *s, struct mp_audio *data, int flags);
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
void af_control_all(struct af_stream *s, int cmd, void *arg);
/**
* \brief calculate average ratio of filter output lenth to input length
* \return the ratio
*/
double af_calc_filter_multiplier(struct af_stream *s);
/**
* \brief Calculate the total delay caused by the filters
* \return delay in bytes of "missing" output
*/
double af_calc_delay(struct af_stream *s);
/** \} */ // end of af_chain group
// Helper functions and macros used inside the audio filters
/**
* \defgroup af_filter Audio filter helper functions
* \{
*/
/* Helper function called by the macro with the same name only to be
called from inside filters */
int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data);
/* Helper function used to calculate the exact buffer length needed
when buffers are resized. The returned length is >= than what is
needed */
int af_lencalc(double mul, struct mp_audio *data);
/**
* \brief convert dB to gain value
* \param n number of values to convert
* \param in [in] values in dB, <= -200 will become 0 gain
* \param out [out] gain values
* \param k input values are divided by this
* \param mi minimum dB value, input will be clamped to this
* \param ma maximum dB value, input will be clamped to this
* \return AF_ERROR on error, AF_OK otherwise
*/
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
/**
* \brief convert gain value to dB
* \param n number of values to convert
* \param in [in] gain values, 0 wil become -200 dB
* \param out [out] values in dB
* \param k output values will be multiplied by this
* \return AF_ERROR on error, AF_OK otherwise
*/
int af_to_dB(int n, float *in, float *out, float k);
/**
* \brief convert milliseconds to sample time
* \param n number of values to convert
* \param in [in] values in milliseconds
* \param out [out] sample time values
* \param rate sample rate
* \param mi minimum ms value, input will be clamped to this
* \param ma maximum ms value, input will be clamped to this
* \return AF_ERROR on error, AF_OK otherwise
*/
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
/**
* \brief convert sample time to milliseconds
* \param n number of values to convert
* \param in [in] sample time values
* \param out [out] values in milliseconds
* \param rate sample rate
* \return AF_ERROR on error, AF_OK otherwise
*/
int af_to_ms(int n, int *in, float *out, int rate);
/**
* \brief test if output format matches
* \param af audio filter
* \param out needed format, will be overwritten by available
* format if they do not match
* \return AF_FALSE if formats do not match, AF_OK if they match
*
* compares the format, bps, rate and nch values of af->data with out
*/
int af_test_output(struct af_instance *af, struct mp_audio *out);
/**
* \brief soft clipping function using sin()
* \param a input value
* \return clipped value
*/
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
int af_to_dB(int n, float *in, float *out, float k);
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
int af_to_ms(int n, int *in, float *out, int rate);
float af_softclip(float a);
/** \} */ // end of af_filter group, but more functions of this group below
/** Memory reallocation macro: if a local buffer is used (i.e. if the
filter doesn't operate on the incoming buffer this macro must be
called to ensure the buffer is big enough.
* \ingroup af_filter
*/
#define RESIZE_LOCAL_BUFFER(a, d) \
((a->data->len < af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
/* Some other useful macro definitions*/
#ifndef min
#define min(a, b)(((a) > (b)) ? (b) : (a))
#endif
#ifndef max
#define max(a, b)(((a) > (b)) ? (a) : (b))
#endif
#ifndef clamp
#define clamp(a, min, max) (((a) > (max)) ? (max) : (((a) < (min)) ? (min) : (a)))
#endif
#ifndef sign
#define sign(a) (((a) > 0) ? (1) : (-1))
#endif
#ifndef lrnd
#define lrnd(a, b) ((b)((a) >= 0.0 ? (a) + 0.5 : (a) - 0.5))
#endif
#endif /* MPLAYER_AF_H */
+46 -56
View File
@@ -27,7 +27,7 @@
#include <string.h>
#include "af.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
/// Internal specific data of the filter
struct af_bs2b {
@@ -37,33 +37,33 @@ struct af_bs2b {
t_bs2bdp filter; ///< instance of a library filter
};
#define PLAY(name, type) \
static struct mp_audio *play_##name(struct af_instance *af, struct mp_audio *data) \
#define FILTER(name, type) \
static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
{ \
/* filter is called for all pairs of samples available in the buffer */ \
bs2b_cross_feed_##name(((struct af_bs2b*)(af->priv))->filter, \
(type*)(data->audio), data->len/data->bps/2); \
(type*)(data->planes[0]), data->samples); \
\
return data; \
return 0; \
}
PLAY(f, float)
PLAY(fbe, float)
PLAY(fle, float)
PLAY(s32be, int32_t)
PLAY(u32be, uint32_t)
PLAY(s32le, int32_t)
PLAY(u32le, uint32_t)
PLAY(s24be, bs2b_int24_t)
PLAY(u24be, bs2b_uint24_t)
PLAY(s24le, bs2b_int24_t)
PLAY(u24le, bs2b_uint24_t)
PLAY(s16be, int16_t)
PLAY(u16be, uint16_t)
PLAY(s16le, int16_t)
PLAY(u16le, uint16_t)
PLAY(s8, int8_t)
PLAY(u8, uint8_t)
FILTER(f, float)
FILTER(fbe, float)
FILTER(fle, float)
FILTER(s32be, int32_t)
FILTER(u32be, uint32_t)
FILTER(s32le, int32_t)
FILTER(u32le, uint32_t)
FILTER(s24be, bs2b_int24_t)
FILTER(u24be, bs2b_uint24_t)
FILTER(s24le, bs2b_int24_t)
FILTER(u24le, bs2b_uint24_t)
FILTER(s16be, int16_t)
FILTER(u16be, uint16_t)
FILTER(s16le, int16_t)
FILTER(u16le, uint16_t)
FILTER(s8, int8_t)
FILTER(u8, uint8_t)
/// Initialization and runtime control
@@ -74,7 +74,6 @@ static int control(struct af_instance *af, int cmd, void *arg)
switch (cmd) {
case AF_CONTROL_REINIT: {
int format;
char buf[256];
// Sanity check
if (!arg) return AF_ERROR;
@@ -87,70 +86,69 @@ static int control(struct af_instance *af, int cmd, void *arg)
and assign corresponding handlers */
switch (format) {
case AF_FORMAT_FLOAT_BE:
af->play = play_fbe;
af->filter = filter_fbe;
break;
case AF_FORMAT_FLOAT_LE:
af->play = play_fle;
af->filter = filter_fle;
break;
case AF_FORMAT_S32_BE:
af->play = play_s32be;
af->filter = filter_s32be;
break;
case AF_FORMAT_U32_BE:
af->play = play_u32be;
af->filter = filter_u32be;
break;
case AF_FORMAT_S32_LE:
af->play = play_s32le;
af->filter = filter_s32le;
break;
case AF_FORMAT_U32_LE:
af->play = play_u32le;
af->filter = filter_u32le;
break;
case AF_FORMAT_S24_BE:
af->play = play_s24be;
af->filter = filter_s24be;
break;
case AF_FORMAT_U24_BE:
af->play = play_u24be;
af->filter = filter_u24be;
break;
case AF_FORMAT_S24_LE:
af->play = play_s24le;
af->filter = filter_s24le;
break;
case AF_FORMAT_U24_LE:
af->play = play_u24le;
af->filter = filter_u24le;
break;
case AF_FORMAT_S16_BE:
af->play = play_s16be;
af->filter = filter_s16be;
break;
case AF_FORMAT_U16_BE:
af->play = play_u16be;
af->filter = filter_u16be;
break;
case AF_FORMAT_S16_LE:
af->play = play_s16le;
af->filter = filter_s16le;
break;
case AF_FORMAT_U16_LE:
af->play = play_u16le;
af->filter = filter_u16le;
break;
case AF_FORMAT_S8:
af->play = play_s8;
af->filter = filter_s8;
break;
case AF_FORMAT_U8:
af->play = play_u8;
af->filter = filter_u8;
break;
default:
af->play = play_f;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
af->filter = filter_f;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
break;
}
// bs2b have srate limits, try to resample if needed
if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) {
af->data->rate = BS2B_DEFAULT_SRATE;
mp_msg(MSGT_AFILTER, MSGL_WARN,
"[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
MP_WARN(af, "[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
"[bs2b] Trying to resample to %d Hz.\n",
af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE);
}
bs2b_set_srate(s->filter, (long)af->data->rate);
mp_msg(MSGT_AFILTER, MSGL_V, "[bs2b] using format %s\n",
af_fmt2str(af->data->format,buf,256));
MP_VERBOSE(af, "[bs2b] using format %s\n",
af_fmt_to_str(af->data->format));
return af_test_output(af,(struct mp_audio*)arg);
}
@@ -162,7 +160,6 @@ static int control(struct af_instance *af, int cmd, void *arg)
static void uninit(struct af_instance *af)
{
struct af_bs2b *s = af->priv;
free(af->data);
if (s->filter)
bs2b_close(s->filter);
}
@@ -173,13 +170,9 @@ static int af_open(struct af_instance *af)
struct af_bs2b *s = af->priv;
af->control = control;
af->uninit = uninit;
af->mul = 1;
if (!(af->data = calloc(1, sizeof(struct mp_audio))))
return AF_ERROR;
// NULL means failed initialization
if (!(s->filter = bs2b_open())) {
free(af->data);
return AF_ERROR;
}
@@ -197,12 +190,9 @@ static int af_open(struct af_instance *af)
/// Description of this filter
struct af_info af_info_bs2b = {
"Bauer stereophonic-to-binaural audio filter",
"bs2b",
"Andrew Savchenko",
"",
AF_FLAGS_REENTRANT,
af_open,
.info = "Bauer stereophonic-to-binaural audio filter",
.name = "bs2b",
.open = af_open,
.priv_size = sizeof(struct af_bs2b),
.options = (const struct m_option[]) {
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
+20 -49
View File
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -40,7 +41,7 @@ typedef struct af_center_s
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_center_t* s = af->setup;
af_center_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -48,47 +49,23 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_set_channels_old(af->data, max(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
return af_test_output(af,(struct mp_audio*)arg);
}
case AF_CONTROL_COMMAND_LINE:{
int ch=1;
sscanf(arg,"%i", &ch);
return control(af,AF_CONTROL_CENTER_CH | AF_CONTROL_SET, &ch);
}
case AF_CONTROL_CENTER_CH | AF_CONTROL_SET: // Requires reinit
// Sanity check
if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Center channel number must be between "
" 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg);
return AF_ERROR;
}
s->ch = *(int*)arg;
return AF_OK;
case AF_CONTROL_CENTER_CH | AF_CONTROL_GET:
*(int*)arg = s->ch;
return AF_OK;
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_center_t* s = af->setup; // Setup for this instance
float* a = c->audio; // Audio data
int len = c->len/4; // Number of samples in current audio block
af_center_t* s = af->priv; // Setup for this instance
float* a = c->planes[0]; // Audio data
int nch = c->nch; // Number of channels
int len = c->samples*c->nch; // Number of samples in current audio block
int ch = s->ch; // Channel in which to insert the center audio
register int i;
@@ -98,31 +75,25 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
a[i+ch] = (a[i]/2) + (a[i+1]/2);
}
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af_center_t* s;
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=s=calloc(1,sizeof(af_center_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
// Set default values
s->ch = 1; // Channel nr 2
af->filter=filter;
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_center_t
struct af_info af_info_center = {
"Audio filter for adding a center channel",
"center",
"Alex Beregszaszi",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Audio filter for adding a center channel",
.name = "center",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_center_t),
.options = (const struct m_option[]) {
OPT_INTRANGE("channel", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(1)),
{0}
},
};
+68 -88
View File
@@ -3,6 +3,8 @@
* command line parameter channels. It is stupid and can only add
* silence or copy channels, not mix or filter.
*
* Original author: Anders
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -25,6 +27,7 @@
#include <string.h>
#include <inttypes.h>
#include "common/common.h"
#include "af.h"
#define FR 0
@@ -32,12 +35,14 @@
typedef struct af_channels_s{
int route[AF_NCH][2];
int nr;
int nch, nr;
int router;
char *routes;
}af_channels_t;
// Local function for copying data
static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps)
static void copy(struct af_instance *af, void* in, void* out,
int ins, int inos,int outs, int outos, int len, int bps)
{
switch(bps){
case 1:{
@@ -108,24 +113,25 @@ static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int
break;
}
default:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Unsupported number of bytes/sample: %i"
MP_ERR(af, "Unsupported number of bytes/sample: %i"
" please report this error on the MPlayer mailing list. \n",bps);
}
}
// Make sure the routes are sane
static int check_routes(af_channels_t* s, int nin, int nout)
static int check_routes(struct af_instance *af, int nin, int nout)
{
af_channels_t* s = af->priv;
int i;
if((s->nr < 1) || (s->nr > AF_NCH)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
MP_ERR(af, "[channels] The number of routing pairs must be"
" between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
return AF_ERROR;
}
for(i=0;i<s->nr;i++){
if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i);
MP_ERR(af, "[channels] Invalid routing in pair nr. %i.\n", i);
return AF_ERROR;
}
}
@@ -135,9 +141,13 @@ static int check_routes(af_channels_t* s, int nin, int nout)
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_channels_t* s = af->setup;
af_channels_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:
case AF_CONTROL_REINIT: ;
struct mp_chmap chmap;
mp_chmap_set_unknown(&chmap, s->nch);
mp_audio_set_channels(af->data, &chmap);
// Set default channel assignment
if(!s->router){
@@ -148,14 +158,14 @@ static int control(struct af_instance* af, int cmd, void* arg)
// If mono: fake stereo
if(((struct mp_audio*)arg)->nch == 1){
s->nr = min(af->data->nch,2);
s->nr = MPMIN(af->data->nch,2);
for(i=0;i<s->nr;i++){
s->route[i][FR] = 0;
s->route[i][TO] = i;
}
}
else{
s->nr = min(af->data->nch, ((struct mp_audio*)arg)->nch);
s->nr = MPMIN(af->data->nch, ((struct mp_audio*)arg)->nch);
for(i=0;i<s->nr;i++){
s->route[i][FR] = i;
s->route[i][TO] = i;
@@ -164,108 +174,78 @@ static int control(struct af_instance* af, int cmd, void* arg)
}
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_force_interleaved_format((struct mp_audio*)arg);
mp_audio_set_format(af->data, ((struct mp_audio*)arg)->format);
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch);
case AF_CONTROL_COMMAND_LINE:{
int nch = 0;
int n = 0;
// Check number of channels and number of routing pairs
sscanf(arg, "%i:%i%n", &nch, &s->nr, &n);
// If router scan commandline for routing pairs
if(s->nr){
char* cp = &((char*)arg)[n];
int ch = 0;
// Sanity check
if((s->nr < 1) || (s->nr > AF_NCH)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
" between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
}
s->router = 1;
// Scan for pairs on commandline
while((*cp == ':') && (ch < s->nr)){
sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Routing from channel %i to"
" channel %i\n",s->route[ch][FR],s->route[ch][TO]);
cp = &cp[n];
ch++;
}
}
struct mp_chmap chmap;
mp_chmap_from_channels(&chmap, nch);
if (AF_OK != af->control(af, AF_CONTROL_CHANNELS | AF_CONTROL_SET, &chmap))
return AF_ERROR;
return AF_OK;
}
case AF_CONTROL_CHANNELS | AF_CONTROL_SET:
// Reinit must be called after this function has been called
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
if(!s->router)
mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels"
" to %i\n",af->data->nch);
return AF_OK;
return check_routes(af,((struct mp_audio*)arg)->nch,af->data->nch);
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->setup);
if (af->data)
free(af->data->audio);
free(af->data);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
struct mp_audio* l = af->data; // Local data
af_channels_t* s = af->setup;
af_channels_t* s = af->priv;
int i;
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
mp_audio_realloc_min(af->data, data->samples);
// Reset unused channels
memset(l->audio,0,c->len / c->nch * l->nch);
memset(l->planes[0],0,mp_audio_psize(c) / c->nch * l->nch);
if(AF_OK == check_routes(s,c->nch,l->nch))
if(AF_OK == check_routes(af,c->nch,l->nch))
for(i=0;i<s->nr;i++)
copy(c->audio,l->audio,c->nch,s->route[i][FR],
l->nch,s->route[i][TO],c->len,c->bps);
copy(af, c->planes[0],l->planes[0],c->nch,s->route[i][FR],
l->nch,s->route[i][TO],mp_audio_psize(c),c->bps);
// Set output data
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
c->planes[0] = l->planes[0];
mp_audio_set_channels(c, &l->channels);
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_channels_t));
if((af->data == NULL) || (af->setup == NULL))
return AF_ERROR;
return AF_OK;
af->control=control;
af->filter=filter;
af_channels_t *s = af->priv;
// If router scan commandline for routing pairs
if(s->routes && s->routes[0]){
char* cp = s->routes;
int ch = 0;
// Scan for pairs on commandline
do {
int n = 0;
if (ch >= AF_NCH) {
MP_FATAL(af, "[channels] Can't have more than %d routes.\n", AF_NCH);
return AF_ERROR;
}
sscanf(cp, "%i-%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
MP_VERBOSE(af, "[channels] Routing from channel %i to"
" channel %i\n",s->route[ch][FR],s->route[ch][TO]);
cp = &cp[n];
ch++;
} while(*cp == ',' && *(cp++));
s->nr = ch;
if (s->nr > 0)
s->router = 1;
}
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_channels_t
struct af_info af_info_channels = {
"Insert or remove channels",
"channels",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "Insert or remove channels",
.name = "channels",
.open = af_open,
.priv_size = sizeof(af_channels_t),
.options = (const struct m_option[]) {
OPT_INTRANGE("nch", nch, 0, 1, AF_NCH, OPTDEF_INT(2)),
OPT_STRING("routes", routes, 0),
{0}
},
};
+116
View File
@@ -0,0 +1,116 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "audio/format.h"
#include "af.h"
static bool test_conversion(int src_format, int dst_format)
{
return (src_format == AF_FORMAT_U24 && dst_format == AF_FORMAT_U32) ||
(src_format == AF_FORMAT_S24 && dst_format == AF_FORMAT_S32) ||
(src_format == AF_FORMAT_U32 && dst_format == AF_FORMAT_U24) ||
(src_format == AF_FORMAT_S32 && dst_format == AF_FORMAT_S24);
}
static int control(struct af_instance *af, int cmd, void *arg)
{
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (!test_conversion(in->format, out->format))
return AF_DETACH;
if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_24BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 32));
} else if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_32BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 24));
} else {
abort();
}
out->rate = in->rate;
mp_audio_set_channels(out, &in->channels);
assert(test_conversion(in->format, out->format));
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_SET_FORMAT: {
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// The LSB is always ignored.
#if BYTE_ORDER == BIG_ENDIAN
#define SHIFT(x) ((3-(x))*8)
#else
#define SHIFT(x) (((x)+1)*8)
#endif
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
mp_audio_realloc_min(af->data, data->samples);
struct mp_audio *out = af->data;
size_t len = mp_audio_psize(data) / data->bps;
if (data->bps == 4) {
for (int s = 0; s < len; s++) {
uint32_t val = *((uint32_t *)data->planes[0] + s);
uint8_t *ptr = (uint8_t *)out->planes[0] + s * 3;
ptr[0] = val >> SHIFT(0);
ptr[1] = val >> SHIFT(1);
ptr[2] = val >> SHIFT(2);
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 24));
} else {
for (int s = 0; s < len; s++) {
uint8_t *ptr = (uint8_t *)data->planes[0] + s * 3;
uint32_t val = ptr[0] << SHIFT(0)
| ptr[1] << SHIFT(1)
| ptr[2] << SHIFT(2);
*((uint32_t *)out->planes[0] + s) = val;
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 32));
}
data->planes[0] = out->planes[0];
return 0;
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->filter = filter;
return AF_OK;
}
struct af_info af_info_convert24 = {
.info = "Convert between 24 and 32 bit sample format",
.name = "convert24",
.open = af_open,
.test_conversion = test_conversion,
};
+131
View File
@@ -0,0 +1,131 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "af.h"
#include "audio/format.h"
#include "compat/mpbswap.h"
static bool test_conversion(int src_format, int dst_format)
{
if ((src_format & AF_FORMAT_PLANAR) ||
(dst_format & AF_FORMAT_PLANAR))
return false;
int src_noend = src_format & ~AF_FORMAT_END_MASK;
int dst_noend = dst_format & ~AF_FORMAT_END_MASK;
// We can swap endian for all formats, but sign only for integer formats.
if (src_noend == dst_noend)
return true;
if (((src_noend & ~AF_FORMAT_SIGN_MASK) ==
(dst_noend & ~AF_FORMAT_SIGN_MASK)) &&
((src_noend & AF_FORMAT_POINT_MASK) == AF_FORMAT_I))
return true;
return false;
}
static int control(struct af_instance *af, int cmd, void *arg)
{
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (!test_conversion(in->format, out->format))
return AF_DETACH;
out->rate = in->rate;
mp_audio_set_channels(out, &in->channels);
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_SET_FORMAT: {
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
static void endian(void *data, int len, int bps)
{
switch (bps) {
case 2:
for (int i = 0; i < len; i++) {
((uint16_t*)data)[i] = bswap_16(((uint16_t *)data)[i]);
}
break;
case 3:
for(int i = 0; i < len; i++) {
uint8_t s = ((uint8_t *)data)[3 * i];
((uint8_t *)data)[3 * i] = ((uint8_t *)data)[3 * i + 2];
((uint8_t *)data)[3 * i + 2] = s;
}
break;
case 4:
for(int i = 0; i < len; i++) {
((uint32_t*)data)[i] = bswap_32(((uint32_t *)data)[i]);
}
break;
}
}
static void si2us(void *data, int len, int bps, bool le)
{
ptrdiff_t i = -(len * bps);
uint8_t *p = &((uint8_t *)data)[len * bps];
if (le && bps > 1)
p += bps - 1;
if (len <= 0)
return;
do {
p[i] ^= 0x80;
} while (i += bps);
}
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
int infmt = data->format;
int outfmt = af->data->format;
size_t len = data->samples * data->nch;
if ((infmt & AF_FORMAT_END_MASK) != (outfmt & AF_FORMAT_END_MASK))
endian(data->planes[0], len, data->bps);
if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK))
si2us(data->planes[0], len, data->bps,
(outfmt & AF_FORMAT_END_MASK) == AF_FORMAT_LE);
mp_audio_set_format(data, outfmt);
return 0;
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->filter = filter;
return AF_OK;
}
struct af_info af_info_convertsignendian = {
.info = "Convert between sample format sign/endian",
.name = "convertsignendian",
.open = af_open,
.test_conversion = test_conversion,
};
+54 -61
View File
@@ -3,6 +3,8 @@
* channels and can be used for simple position panning.
* An extension for this filter would be a reverb.
*
* Original author: Anders
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -25,6 +27,7 @@
#include <string.h>
#include <inttypes.h>
#include "common/common.h"
#include "af.h"
#define L 65536
@@ -38,67 +41,48 @@ typedef struct af_delay_s
int wi[AF_NCH]; // Write index
int ri; // Read index
float d[AF_NCH]; // Delay [ms]
char *delaystr;
}af_delay_t;
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_delay_t* s = af->setup;
af_delay_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
int i;
struct mp_audio *in = arg;
if (in->bps != 1 && in->bps != 2 && in->bps != 4) {
MP_FATAL(af, "[delay] Sample format not supported\n");
return AF_ERROR;
}
// Free prevous delay queues
for(i=0;i<af->data->nch;i++)
free(s->q[i]);
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_force_interleaved_format(in);
mp_audio_copy_config(af->data, in);
// Allocate new delay queues
for(i=0;i<af->data->nch;i++){
s->q[i] = calloc(L,af->data->bps);
if(NULL == s->q[i])
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
MP_FATAL(af, "[delay] Out of memory\n");
}
return control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,s->d);
}
case AF_CONTROL_COMMAND_LINE:{
int n = 1;
int i = 0;
char* cl = arg;
while(n && i < AF_NCH ){
sscanf(cl,"%f:%n",&s->d[i],&n);
if(n==0 || cl[n-1] == '\0')
break;
cl=&cl[n];
i++;
}
return AF_OK;
}
case AF_CONTROL_DELAY_LEN | AF_CONTROL_SET:{
int i;
if(AF_OK != af_from_ms(AF_NCH, arg, s->wi, af->data->rate, 0.0, 1000.0))
if(AF_OK != af_from_ms(AF_NCH, s->d, s->wi, af->data->rate, 0.0, 1000.0))
return AF_ERROR;
s->ri = 0;
for(i=0;i<AF_NCH;i++){
mp_msg(MSGT_AFILTER, MSGL_DBG2, "[delay] Channel %i delayed by %0.3fms\n",
i,clamp(s->d[i],0.0,1000.0));
mp_msg(MSGT_AFILTER, MSGL_DBG3, "[delay] Channel %i delayed by %i samples\n",
i,s->wi[i]);
MP_DBG(af, "[delay] Channel %i delayed by %0.3fms\n",
i,MPCLAMP(s->d[i],0.0,1000.0));
MP_TRACE(af, "[delay] Channel %i delayed by %i samples\n",
i,s->wi[i]);
}
return AF_OK;
}
case AF_CONTROL_DELAY_LEN | AF_CONTROL_GET:{
int i;
for(i=0;i<AF_NCH;i++){
if(s->ri > s->wi[i])
s->wi[i] = L - (s->ri - s->wi[i]);
else
s->wi[i] = s->wi[i] - s->ri;
}
return af_to_ms(AF_NCH, s->wi, arg, af->data->rate);
}
}
return AF_UNKNOWN;
}
@@ -108,25 +92,23 @@ static void uninit(struct af_instance* af)
{
int i;
free(af->data);
for(i=0;i<AF_NCH;i++)
free(((af_delay_t*)(af->setup))->q[i]);
free(af->setup);
free(((af_delay_t*)(af->priv))->q[i]);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_delay_t* s = af->setup; // Setup for this instance
af_delay_t* s = af->priv; // Setup for this instance
int nch = c->nch; // Number of channels
int len = c->len/c->bps; // Number of sample in data chunk
int len = mp_audio_psize(c)/c->bps; // Number of sample in data chunk
int ri = 0;
int ch,i;
for(ch=0;ch<nch;ch++){
switch(c->bps){
case 1:{
int8_t* a = c->audio;
int8_t* a = c->planes[0];
int8_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -140,7 +122,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
break;
}
case 2:{
int16_t* a = c->audio;
int16_t* a = c->planes[0];
int16_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -154,7 +136,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
break;
}
case 4:{
int32_t* a = c->audio;
int32_t* a = c->planes[0];
int32_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -170,28 +152,39 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
}
}
s->ri = ri;
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_delay_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
return AF_OK;
af->control=control;
af->uninit=uninit;
af->filter=filter;
af_delay_t *s = af->priv;
int n = 1;
int i = 0;
char* cl = s->delaystr;
while(cl && n && i < AF_NCH ){
sscanf(cl,"%f%n",&s->d[i],&n);
if(n==0 || cl[n-1] == '\0')
break;
cl=&cl[n];
if (*cl != ',')
break;
cl++;
i++;
}
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_delay_t
struct af_info af_info_delay = {
"Delay audio filter",
"delay",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "Delay audio filter",
.name = "delay",
.open = af_open,
.priv_size = sizeof(af_delay_t),
.options = (const struct m_option[]) {
OPT_STRING("delays", delaystr, 0),
{0}
},
};
+44 -65
View File
@@ -26,6 +26,7 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Methods:
@@ -80,46 +81,27 @@ typedef struct af_volume_s
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_drc_t* s = (af_drc_t*)af->setup;
switch(cmd){
case AF_CONTROL_REINIT:
// Sanity check
if(!arg) return AF_ERROR;
mp_audio_force_interleaved_format((struct mp_audio*)arg);
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16_NE)){
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16)){
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
}
return af_test_output(af,(struct mp_audio*)arg);
case AF_CONTROL_COMMAND_LINE:{
int i = 0;
float target = DEFAULT_TARGET;
sscanf((char*)arg,"%d:%f", &i, &target);
if (i != 1 && i != 2)
return AF_ERROR;
s->method = i-1;
s->mid_s16 = ((float)SHRT_MAX) * target;
s->mid_float = target;
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
static void method1_int16(af_drc_t *s, struct mp_audio *c)
{
register int i = 0;
int16_t *data = (int16_t*)c->audio; // Audio data
int len = c->len/2; // Number of samples
int16_t *data = (int16_t*)c->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float curavg = 0.0, newavg, neededmul;
int tmp;
@@ -139,14 +121,14 @@ static void method1_int16(af_drc_t *s, struct mp_audio *c)
s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
// clamp the mul coefficient
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
}
// Scale & clamp the samples
for (i = 0; i < len; i++)
{
tmp = s->mul * data[i];
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
data[i] = tmp;
}
@@ -160,8 +142,8 @@ static void method1_int16(af_drc_t *s, struct mp_audio *c)
static void method1_float(af_drc_t *s, struct mp_audio *c)
{
register int i = 0;
float *data = (float*)c->audio; // Audio data
int len = c->len/4; // Number of samples
float *data = (float*)c->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float curavg = 0.0, newavg, neededmul, tmp;
for (i = 0; i < len; i++)
@@ -180,7 +162,7 @@ static void method1_float(af_drc_t *s, struct mp_audio *c)
s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
// clamp the mul coefficient
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
}
// Scale & clamp the samples
@@ -197,8 +179,8 @@ static void method1_float(af_drc_t *s, struct mp_audio *c)
static void method2_int16(af_drc_t *s, struct mp_audio *c)
{
register int i = 0;
int16_t *data = (int16_t*)c->audio; // Audio data
int len = c->len/2; // Number of samples
int16_t *data = (int16_t*)c->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float curavg = 0.0, newavg, avg = 0.0;
int tmp, totallen = 0;
@@ -223,7 +205,7 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
if (avg >= SIL_S16)
{
s->mul = s->mid_s16 / avg;
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
}
}
@@ -231,7 +213,7 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
for (i = 0; i < len; i++)
{
tmp = s->mul * data[i];
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
data[i] = tmp;
}
@@ -247,8 +229,8 @@ static void method2_int16(af_drc_t *s, struct mp_audio *c)
static void method2_float(af_drc_t *s, struct mp_audio *c)
{
register int i = 0;
float *data = (float*)c->audio; // Audio data
int len = c->len/4; // Number of samples
float *data = (float*)c->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float curavg = 0.0, newavg, avg = 0.0, tmp;
int totallen = 0;
@@ -273,7 +255,7 @@ static void method2_float(af_drc_t *s, struct mp_audio *c)
if (avg >= SIL_FLOAT)
{
s->mul = s->mid_float / avg;
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
}
}
@@ -290,59 +272,56 @@ static void method2_float(af_drc_t *s, struct mp_audio *c)
s->idx = (s->idx + 1) % NSAMPLES;
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
af_drc_t *s = af->setup;
af_drc_t *s = af->priv;
if(af->data->format == (AF_FORMAT_S16_NE))
if(af->data->format == (AF_FORMAT_S16))
{
if (s->method)
if (s->method == 2)
method2_int16(s, data);
else
method1_int16(s, data);
}
else if(af->data->format == (AF_FORMAT_FLOAT_NE))
else if(af->data->format == (AF_FORMAT_FLOAT))
{
if (s->method)
if (s->method == 2)
method2_float(s, data);
else
method1_float(s, data);
}
return data;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
int i = 0;
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_drc_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
af->filter=filter;
af_drc_t *priv = af->priv;
((af_drc_t*)af->setup)->mul = MUL_INIT;
((af_drc_t*)af->setup)->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
((af_drc_t*)af->setup)->idx = 0;
((af_drc_t*)af->setup)->mid_s16 = ((float)SHRT_MAX) * DEFAULT_TARGET;
((af_drc_t*)af->setup)->mid_float = DEFAULT_TARGET;
priv->mul = MUL_INIT;
priv->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
priv->idx = 0;
for (i = 0; i < NSAMPLES; i++)
{
((af_drc_t*)af->setup)->mem[i].len = 0;
((af_drc_t*)af->setup)->mem[i].avg = 0;
priv->mem[i].len = 0;
priv->mem[i].avg = 0;
}
priv->mid_s16 = ((float)SHRT_MAX) * priv->mid_float;
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_drc_t
struct af_info af_info_drc = {
"Dynamic range compression filter",
"drc",
"Alex Beregszaszi & Pierre Lombard",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Dynamic range compression filter",
.name = "drc",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_drc_t),
.options = (const struct m_option[]) {
OPT_INTRANGE("method", method, 0, 1, 2, OPTDEF_INT(1)),
OPT_FLOAT("target", mid_float, 0, OPTDEF_FLOAT(DEFAULT_TARGET)),
{0}
},
};
+10 -25
View File
@@ -2,6 +2,8 @@
* The name speaks for itself. This filter is a dummy and will
* not blow up regardless of what you do with it.
*
* Original author: Anders
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -31,46 +33,29 @@ static int control(struct af_instance* af, int cmd, void* arg)
switch(cmd){
case AF_CONTROL_REINIT: ;
*af->data = *(struct mp_audio*)arg;
mp_msg(MSGT_AFILTER, MSGL_V, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
af->data->rate,af->data->nch,af_fmt2str_short(af->data->format));
MP_VERBOSE(af, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
af->data->rate,af->data->nch,af_fmt_to_str(af->data->format));
return AF_OK;
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
// Do something necessary to get rid of annoying warning during compile
if(!af)
mp_msg(MSGT_AFILTER, MSGL_ERR, "EEEK: Argument af == NULL in af_dummy.c play().");
return data;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=malloc(sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
af->filter=filter;
return AF_OK;
}
// Description of this filter
struct af_info af_info_dummy = {
"dummy",
"dummy",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "dummy",
.name = "dummy",
.open = af_open,
};
+31 -44
View File
@@ -29,6 +29,7 @@
#include <inttypes.h>
#include <math.h>
#include "common/common.h"
#include "af.h"
#define L 2 // Storage for filter taps
@@ -67,6 +68,7 @@ typedef struct af_equalizer_s
int K; // Number of used eq bands
int channels; // Number of channels
float gain_factor; // applied at output to avoid clipping
double p[KM];
} af_equalizer_t;
// 2nd order Band-pass Filter design
@@ -84,7 +86,7 @@ static void bp2(float* a, float* b, float fc, float q){
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_equalizer_t* s = (af_equalizer_t*)af->setup;
af_equalizer_t* s = (af_equalizer_t*)af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -97,7 +99,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
// Calculate number of active filters
s->K=KM;
@@ -105,7 +107,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->K--;
if(s->K != KM)
mp_msg(MSGT_AFILTER, MSGL_INFO, "[equalizer] Limiting the number of filters to"
MP_INFO(af, "[equalizer] Limiting the number of filters to"
" %i due to low sample rate.\n",s->K);
// Generate filter taps
@@ -113,7 +115,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
bp2(s->a[k],s->b[k],F[k]/((float)af->data->rate),Q);
// Calculate how much this plugin adds to the overall time delay
af->delay = 2 * af->data->nch * af->data->bps;
af->delay = 2.0 / (double)af->data->rate;
// Calculate gain factor to prevent clipping at output
for(k=0;k<AF_NCH;k++)
@@ -135,43 +137,23 @@ static int control(struct af_instance* af, int cmd, void* arg)
return af_test_output(af,arg);
}
case AF_CONTROL_COMMAND_LINE:{
float g[10]={0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
int i,j;
sscanf((char*)arg,"%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", &g[0], &g[1],
&g[2], &g[3], &g[4], &g[5], &g[6], &g[7], &g[8] ,&g[9]);
for(i=0;i<AF_NCH;i++){
for(j=0;j<KM;j++){
((af_equalizer_t*)af->setup)->g[i][j] =
pow(10.0,clamp(g[j],G_MIN,G_MAX)/20.0)-1.0;
}
}
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_equalizer_t* s = (af_equalizer_t*)af->setup; // Setup
af_equalizer_t* s = (af_equalizer_t*)af->priv; // Setup
uint32_t ci = af->data->nch; // Index for channels
uint32_t nch = af->data->nch; // Number of channels
while(ci--){
float* g = s->g[ci]; // Gain factor
float* in = ((float*)c->audio)+ci;
float* out = ((float*)c->audio)+ci;
float* end = in + c->len/4; // Block loop end
float* in = ((float*)c->planes[0])+ci;
float* out = ((float*)c->planes[0])+ci;
float* end = in + c->samples*c->nch; // Block loop end
while(in < end){
register int k = 0; // Frequency band index
@@ -195,28 +177,33 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
out+=nch;
}
}
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_equalizer_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
af->filter=filter;
af_equalizer_t *priv = af->priv;
for(int i=0;i<AF_NCH;i++){
for(int j=0;j<KM;j++){
priv->g[i][j] = pow(10.0,MPCLAMP(priv->p[j],G_MIN,G_MAX)/20.0)-1.0;
}
}
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_equalizer_t
struct af_info af_info_equalizer = {
"Equalizer audio filter",
"equalizer",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Equalizer audio filter",
.name = "equalizer",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_equalizer_t),
.options = (const struct m_option[]) {
#define BAND(n) OPT_DOUBLE("e" #n, p[n], 0)
BAND(0), BAND(1), BAND(2), BAND(3), BAND(4),
BAND(5), BAND(6), BAND(7), BAND(8), BAND(9),
{0}
},
};
+36 -80
View File
@@ -7,6 +7,8 @@
* the area changes),
* the rest is payload (non-interleaved).
*
* Authors: Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -37,9 +39,11 @@
#include <sys/stat.h>
#include <fcntl.h>
#include "osdep/io.h"
#include "talloc.h"
#include "af.h"
#include "mpvcore/path.h"
#include "options/path.h"
#define DEF_SZ 512 // default buffer size (in samples)
#define SHARED_FILE "mpv-af_export" /* default file name
@@ -67,7 +71,7 @@ typedef struct af_export_s
*/
static int control(struct af_instance* af, int cmd, void* arg)
{
af_export_t* s = af->setup;
af_export_t* s = af->priv;
switch (cmd){
case AF_CONTROL_REINIT:{
int i=0;
@@ -86,29 +90,25 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Accept only int16_t as input format (which sucks)
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
// If buffer length isn't set, set it to the default value
if(s->sz == 0)
s->sz = DEF_SZ;
mp_audio_set_format(af->data, AF_FORMAT_S16);
// Allocate new buffers (as one continuous block)
s->buf[0] = calloc(s->sz*af->data->nch, af->data->bps);
if(NULL == s->buf[0])
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Out of memory\n");
MP_FATAL(af, "[export] Out of memory\n");
for(i = 1; i < af->data->nch; i++)
s->buf[i] = (uint8_t *)s->buf[0] + i*s->sz*af->data->bps;
if (!s->filename) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] No filename set.\n");
MP_FATAL(af, "[export] No filename set.\n");
return AF_ERROR;
}
// Init memory mapping
s->fd = open(s->filename, O_RDWR | O_CREAT | O_TRUNC, 0640);
mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Exporting to file: %s\n", s->filename);
s->fd = open(s->filename, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
MP_INFO(af, "[export] Exporting to file: %s\n", s->filename);
if(s->fd < 0) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not open/create file: %s\n",
MP_FATAL(af, "[export] Could not open/create file: %s\n",
s->filename);
return AF_ERROR;
}
@@ -125,8 +125,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
// mmap size
s->mmap_area = mmap(0, mapsize, PROT_READ|PROT_WRITE,MAP_SHARED, s->fd, 0);
if(s->mmap_area == NULL)
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not mmap file %s\n", s->filename);
mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Memory mapped to file: %s (%p)\n",
MP_FATAL(af, "[export] Could not mmap file %s\n", s->filename);
MP_INFO(af, "[export] Memory mapped to file: %s (%p)\n",
s->filename, s->mmap_area);
// Initialize header
@@ -137,41 +137,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Use test_output to return FALSE if necessary
return af_test_output(af, (struct mp_audio*)arg);
}
case AF_CONTROL_COMMAND_LINE:{
int i=0;
char *str = arg;
if (!str){
talloc_free(s->filename);
s->filename = mp_find_user_config_file(SHARED_FILE);
return AF_OK;
}
while((str[i]) && (str[i] != ':'))
i++;
talloc_free(s->filename);
s->filename = talloc_array_size(NULL, 1, i + 1);
memcpy(s->filename, str, i);
s->filename[i] = 0;
sscanf(str + i + 1, "%d", &(s->sz));
return af->control(af, AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET, &s->sz);
}
case AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET:
s->sz = * (int *) arg;
if((s->sz <= 0) || (s->sz > 2048))
mp_msg(MSGT_AFILTER, MSGL_ERR, "[export] Buffer size must be between"
" 1 and 2048\n" );
return AF_OK;
case AF_CONTROL_EXPORT_SZ | AF_CONTROL_GET:
*(int*) arg = s->sz;
return AF_OK;
}
return AF_UNKNOWN;
}
@@ -181,11 +146,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
*/
static void uninit( struct af_instance* af )
{
free(af->data);
af->data = NULL;
if(af->setup){
af_export_t* s = af->setup;
af_export_t* s = af->priv;
if (s->buf)
free(s->buf[0]);
@@ -195,25 +156,19 @@ static void uninit( struct af_instance* af )
if(s->fd > -1)
close(s->fd);
talloc_free(s->filename);
free(af->setup);
af->setup = NULL;
}
}
/* Filter data through filter
af audio filter instance
data audio data
*/
static struct mp_audio* play( struct af_instance* af, struct mp_audio* data )
static int filter( struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_export_t* s = af->setup; // Setup for this instance
int16_t* a = c->audio; // Incomming sound
af_export_t* s = af->priv; // Setup for this instance
int16_t* a = c->planes[0]; // Incomming sound
int nch = c->nch; // Number of channels
int len = c->len/c->bps; // Number of sample in data chunk
int len = c->samples*c->nch; // Number of sample in data chunk
int sz = s->sz; // buffer size (in samples)
int flag = 0; // Set to 1 if buffer is filled
@@ -244,8 +199,7 @@ static struct mp_audio* play( struct af_instance* af, struct mp_audio* data )
&(s->count), sizeof(s->count));
}
// We don't modify data, just export it
return data;
return 0;
}
/* Allocate memory and set function pointers
@@ -256,24 +210,26 @@ static int af_open( struct af_instance* af )
{
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul=1;
af->data = calloc(1, sizeof(struct mp_audio));
af->setup = calloc(1, sizeof(af_export_t));
if((af->data == NULL) || (af->setup == NULL))
return AF_ERROR;
af->filter = filter;
af_export_t *priv = af->priv;
((af_export_t *)af->setup)->filename = mp_find_user_config_file(SHARED_FILE);
if (!priv->filename || !priv->filename[0]) {
MP_FATAL(af, "no export filename given");
return AF_ERROR;
}
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_export_t
struct af_info af_info_export = {
"Sound export filter",
"export",
"Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "Sound export filter",
.name = "export",
.open = af_open,
.priv_size = sizeof(af_export_t),
.options = (const struct m_option[]) {
OPT_STRING("filename", filename, 0),
OPT_INTRANGE("buffersamples", sz, 0, 1, 2048, OPTDEF_INT(DEF_SZ)),
{0}
},
};
+31 -48
View File
@@ -26,6 +26,7 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -34,56 +35,42 @@ typedef struct af_extrastereo_s
float mul;
}af_extrastereo_t;
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data);
static int play_s16(struct af_instance* af, struct mp_audio* data, int f);
static int play_float(struct af_instance* af, struct mp_audio* data, int f);
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_extrastereo_t* s = (af_extrastereo_t*)af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
// Sanity check
if(!arg) return AF_ERROR;
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_force_interleaved_format(af->data);
mp_audio_set_num_channels(af->data, 2);
if (af->data->format == AF_FORMAT_FLOAT_NE)
if (af->data->format == AF_FORMAT_FLOAT)
{
af->play = play_float;
af->filter = play_float;
}// else
{
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = play_s16;
mp_audio_set_format(af->data, AF_FORMAT_S16);
af->filter = play_s16;
}
return af_test_output(af,(struct mp_audio*)arg);
}
case AF_CONTROL_COMMAND_LINE:{
float f;
sscanf((char*)arg,"%f", &f);
s->mul = f;
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
{
af_extrastereo_t *s = af->setup;
af_extrastereo_t *s = af->priv;
register int i = 0;
int16_t *a = (int16_t*)data->audio; // Audio data
int len = data->len/2; // Number of samples
int16_t *a = (int16_t*)data->planes[0]; // Audio data
int len = data->samples*data->nch; // Number of samples
int avg, l, r;
for (i = 0; i < len; i+=2)
@@ -93,19 +80,19 @@ static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
l = avg + (int)(s->mul * (a[i] - avg));
r = avg + (int)(s->mul * (a[i + 1] - avg));
a[i] = clamp(l, SHRT_MIN, SHRT_MAX);
a[i + 1] = clamp(r, SHRT_MIN, SHRT_MAX);
a[i] = MPCLAMP(l, SHRT_MIN, SHRT_MAX);
a[i + 1] = MPCLAMP(r, SHRT_MIN, SHRT_MAX);
}
return data;
return 0;
}
static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data)
static int play_float(struct af_instance* af, struct mp_audio* data, int f)
{
af_extrastereo_t *s = af->setup;
af_extrastereo_t *s = af->priv;
register int i = 0;
float *a = (float*)data->audio; // Audio data
int len = data->len/4; // Number of samples
float *a = (float*)data->planes[0]; // Audio data
int len = data->samples * data->nch; // Number of samples
float avg, l, r;
for (i = 0; i < len; i+=2)
@@ -119,30 +106,26 @@ static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data
a[i + 1] = af_softclip(r);
}
return data;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play_s16;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_extrastereo_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
af->filter=play_s16;
((af_extrastereo_t*)af->setup)->mul = 2.5;
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_extrastereo_t
struct af_info af_info_extrastereo = {
"Increase difference between audio channels",
"extrastereo",
"Alex Beregszaszi & Pierre Lombard",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Increase difference between audio channels",
.name = "extrastereo",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_extrastereo_t),
.options = (const struct m_option[]) {
OPT_FLOAT("mul", mul, 0, OPTDEF_FLOAT(2.5)),
{0}
},
};
-141
View File
@@ -1,141 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <libavutil/common.h>
#include "mpvcore/m_option.h"
#include "audio/format.h"
#include "af.h"
struct priv {
struct m_config *config;
int in_format;
int in_srate;
struct mp_chmap in_channels;
int out_format;
int out_srate;
struct mp_chmap out_channels;
int fail;
struct mp_audio data;
struct mp_audio temp;
};
static int control(struct af_instance *af, int cmd, void *arg)
{
struct priv *priv = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (priv->in_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(in, priv->in_format);
if (priv->in_channels.num)
mp_audio_set_channels(in, &priv->in_channels);
if (priv->in_srate)
in->rate = priv->in_srate;
mp_audio_copy_config(out, in);
if (priv->out_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(out, priv->out_format);
if (priv->out_channels.num)
mp_audio_set_channels(out, &priv->out_channels);
if (priv->out_srate)
out->rate = priv->out_srate;
if (in->nch != out->nch || in->bps != out->bps) {
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[af_force] Forced input/output formats are incompatible.\n");
return AF_ERROR;
}
if (priv->fail) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[af_force] Failing on purpose.\n");
return AF_ERROR;
}
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
}
return AF_UNKNOWN;
}
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
struct priv *priv = af->priv;
struct mp_audio *r = &priv->temp;
*r = *af->data;
r->audio = data->audio;
r->len = data->len;
return r;
}
static void uninit(struct af_instance *af)
{
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
struct priv *priv = af->priv;
af->data = &priv->data;
return AF_OK;
}
#define OPT_BASE_STRUCT struct priv
struct af_info af_info_force = {
"Force audio format",
"force",
"",
"",
0,
af_open,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.in_format = AF_FORMAT_UNKNOWN,
.out_format = AF_FORMAT_UNKNOWN,
},
.options = (const struct m_option[]) {
OPT_AUDIOFORMAT("format", in_format, 0),
OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000),
OPT_CHMAP("channels", in_channels, CONF_MIN, .min = 0),
OPT_AUDIOFORMAT("out-format", out_format, 0),
OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000),
OPT_CHMAP("out-channels", out_channels, CONF_MIN, .min = 0),
OPT_FLAG("fail", fail, 0),
{0}
},
};
+95 -457
View File
@@ -1,494 +1,132 @@
/*
* This audio filter changes the format of a data block. Valid
* formats are: AFMT_U8, AFMT_S8, AFMT_S16_LE, AFMT_S16_BE
* AFMT_U16_LE, AFMT_U16_BE, AFMT_S32_LE and AFMT_S32_BE.
* This file is part of mpv.
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <sys/types.h>
#include "config.h"
#include <libavutil/common.h>
#include "options/m_option.h"
#include "audio/format.h"
#include "af.h"
#include "compat/mpbswap.h"
/* Functions used by play to convert the input audio to the correct
format */
struct priv {
struct m_config *config;
// Switch endianness
static void endian(void* in, void* out, int len, int bps);
// From signed to unsigned and the other way
static void si2us(void* data, int len, int bps);
// Change the number of bits per sample
static void change_bps(void* in, void* out, int len, int inbps, int outbps);
// From float to int signed
static void float2int(float* in, void* out, int len, int bps);
// From signed int to float
static void int2float(void* in, float* out, int len, int bps);
int in_format;
int in_srate;
struct mp_chmap in_channels;
int out_format;
int out_srate;
struct mp_chmap out_channels;
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data);
// Helper functions to check sanity for input arguments
// Sanity check for bytes per sample
static int check_bps(int bps)
{
if(bps != 4 && bps != 3 && bps != 2 && bps != 1){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample"
" must be 1, 2, 3 or 4. Current value is %i \n",bps);
return AF_ERROR;
}
return AF_OK;
}
// Check for unsupported formats
static int check_format(int format)
{
char buf[256];
if ((format & AF_FORMAT_SPECIAL_MASK) == 0)
return AF_OK;
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] Sample format %s not yet supported \n",
af_fmt2str(format,buf,256));
return AF_ERROR;
}
static bool test_conversion(int src_format, int dst_format)
{
// This is the fallback conversion filter, so this filter is always
// inserted on format mismatches if no other filter can handle it.
// Initializing the filter might still fail.
return true;
}
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
switch(cmd){
case AF_CONTROL_REINIT:{
char buf1[256];
char buf2[256];
struct mp_audio *data = arg;
int supported_ac3 = 0;
// Make sure this filter isn't redundant
if(af->data->format == data->format)
return AF_DETACH;
// A bit complex because we can convert AC3
// to generic iec61937 but not the other way
// round.
if (AF_FORMAT_IS_AC3(af->data->format))
supported_ac3 = AF_FORMAT_IS_AC3(data->format);
else if (AF_FORMAT_IS_IEC61937(af->data->format))
supported_ac3 = AF_FORMAT_IS_IEC61937(data->format);
// Allow trivial AC3-endianness conversion
if (!supported_ac3)
// Check for errors in configuration
if((AF_OK != check_bps(data->bps)) ||
(AF_OK != check_format(data->format)) ||
(AF_OK != check_bps(af->data->bps)) ||
(AF_OK != check_format(af->data->format)))
return AF_ERROR;
af_fmt2str(data->format,buf1,256);
af_fmt2str(af->data->format,buf2,256);
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Changing sample format from %s to %s\n",
buf1, buf2);
af->data->rate = data->rate;
mp_audio_set_channels(af->data, &data->channels);
af->mul = (double)af->data->bps / data->bps;
af->play = play; // set default
// look whether only endianness differences are there
if ((af->data->format & ~AF_FORMAT_END_MASK) ==
(data->format & ~AF_FORMAT_END_MASK))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated endianness conversion only\n");
af->play = play_swapendian;
}
if ((data->format == AF_FORMAT_FLOAT_NE) &&
(af->data->format == AF_FORMAT_S16_NE))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
buf1, buf2);
af->play = play_float_s16;
}
if ((data->format == AF_FORMAT_S16_NE) &&
(af->data->format == AF_FORMAT_FLOAT_NE))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
buf1, buf2);
af->play = play_s16_float;
}
return AF_OK;
}
case AF_CONTROL_COMMAND_LINE:{
int format = af_str2fmt_short(bstr0(arg));
if (!format) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg);
return AF_ERROR;
}
if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
return AF_ERROR;
return AF_OK;
}
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:{
// Check for errors in configuration
if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg))
return AF_ERROR;
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
if (af->data)
free(af->data->audio);
free(af->data);
af->setup = 0;
}
static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/c->bps; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
endian(c->audio,l->audio,len,c->bps);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
return c;
}
static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/4; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
float2int(c->audio, l->audio, len, 2);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*2;
return c;
}
static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/2; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
int2float(c->audio, l->audio, len, 2);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*4;
return c;
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/c->bps; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
// Change to cpu native endian format
if((c->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
endian(c->audio,c->audio,len,c->bps);
// Conversion table
if((c->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
float2int(c->audio, l->audio, len, l->bps);
if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
si2us(l->audio,len,l->bps);
} else {
// Input must be int
// Change signed/unsigned
if((c->format&AF_FORMAT_SIGN_MASK) != (l->format&AF_FORMAT_SIGN_MASK)){
si2us(c->audio,len,c->bps);
}
// Convert to special formats
switch(l->format&AF_FORMAT_POINT_MASK){
case(AF_FORMAT_F):
int2float(c->audio, l->audio, len, c->bps);
break;
default:
// Change the number of bits
if(c->bps != l->bps)
change_bps(c->audio,l->audio,len,c->bps,l->bps);
else
memcpy(l->audio,c->audio,len*c->bps);
break;
}
}
// Switch from cpu native endian to the correct endianness
if((l->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
endian(l->audio,l->audio,len,l->bps);
// Set output data
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*l->bps;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
return AF_OK;
}
// Description of this filter
struct af_info af_info_format = {
"Sample format conversion",
"format",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open,
.test_conversion = test_conversion,
int fail;
};
static inline uint32_t load24bit(void* data, int pos) {
#if BYTE_ORDER == BIG_ENDIAN
return (((uint32_t)((uint8_t*)data)[3*pos])<<24) |
(((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
(((uint32_t)((uint8_t*)data)[3*pos+2])<<8);
#else
return (((uint32_t)((uint8_t*)data)[3*pos])<<8) |
(((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
(((uint32_t)((uint8_t*)data)[3*pos+2])<<24);
#endif
}
static inline void store24bit(void* data, int pos, uint32_t expanded_value) {
#if BYTE_ORDER == BIG_ENDIAN
((uint8_t*)data)[3*pos]=expanded_value>>24;
((uint8_t*)data)[3*pos+1]=expanded_value>>16;
((uint8_t*)data)[3*pos+2]=expanded_value>>8;
#else
((uint8_t*)data)[3*pos]=expanded_value>>8;
((uint8_t*)data)[3*pos+1]=expanded_value>>16;
((uint8_t*)data)[3*pos+2]=expanded_value>>24;
#endif
}
// Function implementations used by play
static void endian(void* in, void* out, int len, int bps)
static void force_in_params(struct af_instance *af, struct mp_audio *in)
{
register int i;
switch(bps){
case(2):{
for(i=0;i<len;i++){
((uint16_t*)out)[i]=bswap_16(((uint16_t*)in)[i]);
}
break;
}
case(3):{
register uint8_t s;
for(i=0;i<len;i++){
s=((uint8_t*)in)[3*i];
((uint8_t*)out)[3*i]=((uint8_t*)in)[3*i+2];
if (in != out)
((uint8_t*)out)[3*i+1]=((uint8_t*)in)[3*i+1];
((uint8_t*)out)[3*i+2]=s;
}
break;
}
case(4):{
for(i=0;i<len;i++){
((uint32_t*)out)[i]=bswap_32(((uint32_t*)in)[i]);
}
break;
}
}
struct priv *priv = af->priv;
if (priv->in_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(in, priv->in_format);
if (priv->in_channels.num)
mp_audio_set_channels(in, &priv->in_channels);
if (priv->in_srate)
in->rate = priv->in_srate;
}
static void si2us(void* data, int len, int bps)
static void force_out_params(struct af_instance *af, struct mp_audio *out)
{
register long i = -(len * bps);
register uint8_t *p = &((uint8_t *)data)[len * bps];
#if AF_FORMAT_NE == AF_FORMAT_LE
p += bps - 1;
#endif
if (len <= 0) return;
do {
p[i] ^= 0x80;
} while (i += bps);
struct priv *priv = af->priv;
if (priv->out_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(out, priv->out_format);
if (priv->out_channels.num)
mp_audio_set_channels(out, &priv->out_channels);
if (priv->out_srate)
out->rate = priv->out_srate;
}
static void change_bps(void* in, void* out, int len, int inbps, int outbps)
static int control(struct af_instance *af, int cmd, void *arg)
{
register int i;
switch(inbps){
case(1):
switch(outbps){
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=((uint16_t)((uint8_t*)in)[i])<<8;
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t)((uint8_t*)in)[i])<<24);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=((uint32_t)((uint8_t*)in)[i])<<24;
break;
struct priv *priv = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
force_in_params(af, in);
mp_audio_copy_config(out, in);
force_out_params(af, out);
if (in->nch != out->nch || in->bps != out->bps) {
MP_ERR(af, "[af_format] Forced input/output formats are incompatible.\n");
return AF_ERROR;
}
if (priv->fail) {
MP_ERR(af, "[af_format] Failing on purpose.\n");
return AF_ERROR;
}
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
break;
case(2):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)((((uint16_t*)in)[i])>>8);
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t)((uint16_t*)in)[i])<<16);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=((uint32_t)((uint16_t*)in)[i])<<16;
break;
}
break;
case(3):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)(load24bit(in, i)>>24);
break;
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=(uint16_t)(load24bit(in, i)>>16);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=(uint32_t)load24bit(in, i);
break;
}
break;
case(4):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)((((uint32_t*)in)[i])>>24);
break;
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=(uint16_t)((((uint32_t*)in)[i])>>16);
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t*)in)[i]);
break;
}
break;
}
return AF_UNKNOWN;
}
static void float2int(float* in, void* out, int len, int bps)
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
register int i;
switch(bps){
case(1):
for(i=0;i<len;i++)
((int8_t*)out)[i] = lrintf(127.0 * clamp(in[i], -1.0f, +1.0f));
break;
case(2):
for(i=0;i<len;i++)
((int16_t*)out)[i] = lrintf(32767.0 * clamp(in[i], -1.0f, +1.0f));
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f)));
break;
case(4):
for(i=0;i<len;i++)
((int32_t*)out)[i] = lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f));
break;
}
mp_audio_copy_config(data, af->data);
return 0;
}
static void int2float(void* in, float* out, int len, int bps)
static int af_open(struct af_instance *af)
{
register int i;
switch(bps){
case(1):
for(i=0;i<len;i++)
out[i]=(1.0/128.0)*((int8_t*)in)[i];
break;
case(2):
for(i=0;i<len;i++)
out[i]=(1.0/32768.0)*((int16_t*)in)[i];
break;
case(3):
for(i=0;i<len;i++)
out[i]=(1.0/2147483648.0)*((int32_t)load24bit(in, i));
break;
case(4):
for(i=0;i<len;i++)
out[i]=(1.0/2147483648.0)*((int32_t*)in)[i];
break;
}
af->control = control;
af->filter = filter;
force_in_params(af, af->data);
force_out_params(af, af->data);
return AF_OK;
}
#define OPT_BASE_STRUCT struct priv
struct af_info af_info_format = {
.info = "Force audio format",
.name = "format",
.open = af_open,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_AUDIOFORMAT("format", in_format, 0),
OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000),
OPT_CHMAP("channels", in_channels, CONF_MIN, .min = 0),
OPT_AUDIOFORMAT("out-format", out_format, 0),
OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000),
OPT_CHMAP("out-channels", out_channels, CONF_MIN, .min = 0),
OPT_FLAG("fail", fail, 0),
{0}
},
};
+46 -72
View File
@@ -3,6 +3,8 @@
* encoded rear channels into headphone signal using FIR filtering
* with HRTF.
*
* Author: ylai
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -69,6 +71,7 @@ typedef struct af_hrtf_s {
/* Cyclic position on the ring buffer */
int cyc_pos;
int print_flag;
int mode;
} af_hrtf_t;
/* Convolution on a ring buffer
@@ -284,9 +287,8 @@ static inline void update_ch(af_hrtf_t *s, short *in, const int k)
/* Initialization and runtime control */
static int control(struct af_instance *af, int cmd, void* arg)
{
af_hrtf_t *s = af->setup;
af_hrtf_t *s = af->priv;
int test_output_res;
char mode;
switch(cmd) {
case AF_CONTROL_REINIT:
@@ -294,8 +296,7 @@ static int control(struct af_instance *af, int cmd, void* arg)
if(af->data->rate != 48000) {
// automatic samplerate adjustment in the filter chain
// is not yet supported.
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n",
MP_ERR(af, "[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n",
af->data->rate);
return AF_ERROR;
}
@@ -309,35 +310,12 @@ static int control(struct af_instance *af, int cmd, void* arg)
}
else if (af->data->nch < 5)
mp_audio_set_channels_old(af->data, 5);
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
mp_audio_set_format(af->data, AF_FORMAT_S16);
test_output_res = af_test_output(af, (struct mp_audio*)arg);
af->mul = 2.0 / af->data->nch;
// after testing input set the real output format
mp_audio_set_num_channels(af->data, 2);
s->print_flag = 1;
return test_output_res;
case AF_CONTROL_COMMAND_LINE:
sscanf((char*)arg, "%c", &mode);
switch(mode) {
case 'm':
/* Use matrix rear decoding. */
s->matrix_mode = 1;
break;
case 's':
/* Input needs matrix decoding. */
s->decode_mode = HRTF_MIX_MATRIX2CH;
break;
case '0':
s->matrix_mode = 0;
break;
default:
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[hrtf] Mode is neither 'm', 's', nor '0' (%c).\n",
mode);
return AF_ERROR;
}
s->print_flag = 1;
return AF_OK;
}
return AF_UNKNOWN;
@@ -346,8 +324,7 @@ static int control(struct af_instance *af, int cmd, void* arg)
/* Deallocate memory */
static void uninit(struct af_instance *af)
{
if(af->setup) {
af_hrtf_t *s = af->setup;
af_hrtf_t *s = af->priv;
free(s->lf);
free(s->rf);
@@ -362,11 +339,6 @@ static void uninit(struct af_instance *af)
free(s->fwrbuf_r);
free(s->fwrbuf_lr);
free(s->fwrbuf_rr);
free(af->setup);
}
if(af->data)
free(af->data->audio);
free(af->data);
}
/* Filter data through filter
@@ -380,50 +352,44 @@ frequencies).
2. A bass compensation is introduced to ensure that 0-200 Hz are not
damped (without any real 3D acoustical image, however).
*/
static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
af_hrtf_t *s = af->setup;
short *in = data->audio; // Input audio data
af_hrtf_t *s = af->priv;
short *in = data->planes[0]; // Input audio data
short *out = NULL; // Output audio data
short *end = in + data->len / sizeof(short); // Loop end
short *end = in + data->samples * data->nch; // Loop end
float common, left, right, diff, left_b, right_b;
const int dblen = s->dlbuflen, hlen = s->hrflen, blen = s->basslen;
if(AF_OK != RESIZE_LOCAL_BUFFER(af, data))
return NULL;
mp_audio_realloc_min(af->data, data->samples);
if(s->print_flag) {
s->print_flag = 0;
switch (s->decode_mode) {
case HRTF_MIX_51:
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using HRTF to mix %s discrete surround into "
MP_INFO(af, "[hrtf] Using HRTF to mix %s discrete surround into "
"L, R channels\n", s->matrix_mode ? "5+1" : "5");
break;
case HRTF_MIX_STEREO:
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using HRTF to mix stereo into "
MP_INFO(af, "[hrtf] Using HRTF to mix stereo into "
"L, R channels\n");
break;
case HRTF_MIX_MATRIX2CH:
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using active matrix to decode 2 channel "
MP_INFO(af, "[hrtf] Using active matrix to decode 2 channel "
"input, HRTF to mix %s matrix surround into "
"L, R channels\n", "3/2");
break;
default:
mp_msg(MSGT_AFILTER, MSGL_WARN,
"[hrtf] bogus decode_mode: %d\n", s->decode_mode);
MP_WARN(af, "[hrtf] bogus decode_mode: %d\n", s->decode_mode);
break;
}
if(s->matrix_mode)
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using active matrix to decode rear center "
MP_INFO(af, "[hrtf] Using active matrix to decode rear center "
"channel\n");
}
out = af->data->audio;
out = af->data->planes[0];
/* MPlayer's 5 channel layout (notation for the variable):
*
@@ -563,11 +529,10 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
}
/* Set output data */
data->audio = af->data->audio;
data->len = data->len / data->nch * 2;
data->planes[0] = af->data->planes[0];
mp_audio_set_num_channels(data, 2);
return data;
return 0;
}
static int allocate(af_hrtf_t *s)
@@ -600,14 +565,9 @@ static int af_open(struct af_instance* af)
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
af->data = calloc(1, sizeof(struct mp_audio));
af->setup = calloc(1, sizeof(af_hrtf_t));
if((af->data == NULL) || (af->setup == NULL))
return AF_ERROR;
af->filter = filter;
s = af->setup;
s = af->priv;
s->dlbuflen = DELAYBUFLEN;
s->hrflen = HRTFFILTLEN;
@@ -620,10 +580,22 @@ static int af_open(struct af_instance* af)
s->matrix_mode = 0;
s->decode_mode = HRTF_MIX_51;
switch (s->mode) {
case 0: /* Use matrix rear decoding. */
s->matrix_mode = 1;
break;
case 1: /* Input needs matrix decoding. */
s->decode_mode = HRTF_MIX_MATRIX2CH;
break;
case 2:
s->matrix_mode = 0;
break;
}
s->print_flag = 1;
if (allocate(s) != 0) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n");
MP_ERR(af, "[hrtf] Memory allocation error.\n");
return AF_ERROR;
}
@@ -642,13 +614,13 @@ static int af_open(struct af_instance* af)
s->cr_ir = cr_filt + (s->cr_o = pulse_detect(cr_filt));
if((s->ba_ir = malloc(s->basslen * sizeof(float))) == NULL) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n");
MP_ERR(af, "[hrtf] Memory allocation error.\n");
return AF_ERROR;
}
fc = 2.0 * BASSFILTFREQ / (float)af->data->rate;
if(af_filter_design_fir(s->basslen, s->ba_ir, &fc, LP | KAISER, 4 * M_PI) ==
-1) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Unable to design low-pass "
MP_ERR(af, "[hrtf] Unable to design low-pass "
"filter.\n");
return AF_ERROR;
}
@@ -658,12 +630,14 @@ static int af_open(struct af_instance* af)
return AF_OK;
}
/* Description of this filter */
#define OPT_BASE_STRUCT af_hrtf_t
struct af_info af_info_hrtf = {
"HRTF Headphone",
"hrtf",
"ylai",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "HRTF Headphone",
.name = "hrtf",
.open = af_open,
.priv_size = sizeof(af_hrtf_t),
.options = (const struct m_option[]) {
OPT_CHOICE("mode", mode, 0, ({"m", 0}, {"s", 1}, {"0", 2})),
{0}
},
};
+10 -25
View File
@@ -35,25 +35,19 @@ static int control(struct af_instance* af, int cmd, void* arg)
switch(cmd){
case AF_CONTROL_REINIT:
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
return af_test_output(af,(struct mp_audio*)arg);
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int play(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
float* a = c->audio; // Audio data
int len = c->len/4; // Number of samples in current audio block
float* a = c->planes[0]; // Audio data
int nch = c->nch; // Number of channels
int len = c->samples*nch; // Number of samples in current audio block
register int i;
/*
@@ -68,29 +62,20 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
a[i+1]=a[i];
}
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
af->data = calloc(1,sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
af->filter = play;
return AF_OK;
}
// Description of this filter
struct af_info af_info_karaoke = {
"Simple karaoke/voice-removal audio filter",
"karaoke",
"Reynaldo H. Verdejo Pinochet",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Simple karaoke/voice-removal audio filter",
.name = "karaoke",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
};
+147 -220
View File
@@ -42,6 +42,8 @@
#include "af.h"
#define _(x) (x)
/* ------------------------------------------------------------------------- */
/* Filter specific data */
@@ -63,6 +65,7 @@ typedef struct af_ladspa_s
char *file;
char *label;
char *controls;
char *myname; /**< It's easy to have a concatenation of file and label */
@@ -100,15 +103,18 @@ static int af_ladspa_malloc_failed(char*);
/* ------------------------------------------------------------------------- */
/* Description */
#define OPT_BASE_STRUCT af_ladspa_t
struct af_info af_info_ladspa = {
"LADSPA plugin loader",
"ladspa",
"Ivo van Poorten",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "LADSPA plugin loader",
.name = "ladspa",
.open = af_open,
.priv_size = sizeof(af_ladspa_t),
.options = (const struct m_option[]) {
OPT_STRING("file", file, 0),
OPT_STRING("label", label, 0),
OPT_STRING("controls", controls, 0),
{0}
},
};
/* ------------------------------------------------------------------------- */
@@ -131,7 +137,8 @@ struct af_info af_info_ladspa = {
* configuration. Else, it returns AF_ERROR.
*/
static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
static int af_ladspa_parse_plugin(struct af_instance *af) {
af_ladspa_t *setup = af->priv;
int p, i;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
LADSPA_PortDescriptor d;
@@ -211,30 +218,30 @@ static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
}
if (setup->ninputs == 0) {
mp_msg(MSGT_AFILTER, MSGL_WARN, "%s: %s\n", setup->myname,
MP_WARN(af, "%s: %s\n", setup->myname,
_("WARNING! This LADSPA plugin has no audio inputs.\n The incoming audio signal will be lost."));
} else if (setup->ninputs == 1) {
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a mono effect\n", setup->myname);
MP_VERBOSE(af, "%s: this is a mono effect\n", setup->myname);
} else if (setup->ninputs == 2) {
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a stereo effect\n", setup->myname);
MP_VERBOSE(af, "%s: this is a stereo effect\n", setup->myname);
} else {
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a %i-channel effect, "
MP_VERBOSE(af, "%s: this is a %i-channel effect, "
"support is experimental\n", setup->myname, setup->ninputs);
}
if (setup->noutputs == 0) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
MP_ERR(af, "%s: %s\n", setup->myname,
_("This LADSPA plugin has no audio outputs."));
return AF_ERROR;
}
if (setup->noutputs != setup->ninputs ) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
MP_ERR(af, "%s: %s\n", setup->myname,
_("The number of audio inputs and audio outputs of the LADSPA plugin differ."));
return AF_ERROR;
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this plugin has %d input control(s)\n",
MP_VERBOSE(af, "%s: this plugin has %d input control(s)\n",
setup->myname, setup->ninputcontrols);
/* Print list of controls and its range of values it accepts */
@@ -242,18 +249,18 @@ static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
for (i=0; i<setup->ninputcontrols; i++) {
p = setup->inputcontrolsmap[i];
hint = pdes->PortRangeHints[p];
mp_msg(MSGT_AFILTER, MSGL_V, " --- %d %s [", i, pdes->PortNames[p]);
MP_VERBOSE(af, " --- %d %s [", i, pdes->PortNames[p]);
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f , ", hint.LowerBound);
MP_VERBOSE(af, "%0.2f , ", hint.LowerBound);
} else {
mp_msg(MSGT_AFILTER, MSGL_V, "... , ");
MP_VERBOSE(af, "... , ");
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f]\n", hint.UpperBound);
MP_VERBOSE(af, "%0.2f]\n", hint.UpperBound);
} else {
mp_msg(MSGT_AFILTER, MSGL_V, "...]\n");
MP_VERBOSE(af, "...]\n");
}
}
@@ -299,9 +306,9 @@ static void* mydlopen(const char *filename, int flag) {
/* For Windows there's only absolute path support.
* If you have a Windows machine, feel free to fix this.
* (path separator, shared objects extension, et cetera). */
mp_msg(MSGT_AFILTER, MSGL_V, "\ton windows, only absolute pathnames "
MP_VERBOSE(af, "\ton windows, only absolute pathnames "
"are supported\n");
mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename);
MP_VERBOSE(af, "\ttrying %s\n", filename);
return dlopen(filename, flag);
#endif
@@ -342,7 +349,6 @@ static void* mydlopen(const char *filename, int flag) {
}
strcpy(buf+needslash+(end-start), filename);
mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", buf);
result=dlopen(buf, flag);
free(buf);
@@ -356,7 +362,6 @@ static void* mydlopen(const char *filename, int flag) {
} /* end if there's a ladspapath */
/* last resort, just open it again, so the dlerror() message is correct */
mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename);
return dlopen(filename,flag);
}
@@ -377,24 +382,25 @@ static void* mydlopen(const char *filename, int flag) {
* \return Either AF_ERROR or AF_OK, depending on the success of the operation.
*/
static int af_ladspa_load_plugin(af_ladspa_t *setup) {
static int af_ladspa_load_plugin(struct af_instance *af) {
af_ladspa_t *setup = af->priv;
const LADSPA_Descriptor *ladspa_descriptor;
LADSPA_Descriptor_Function descriptor_function;
int i;
/* load library */
mp_msg(MSGT_AFILTER, MSGL_V, "%s: loading ladspa plugin library %s\n",
MP_VERBOSE(af, "%s: loading ladspa plugin library %s\n",
setup->myname, setup->file);
setup->libhandle = mydlopen(setup->file, RTLD_NOW);
if (!setup->libhandle) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s %s\n\t%s\n", setup->myname,
MP_ERR(af, "%s: %s %s\n\t%s\n", setup->myname,
_("failed to load"), setup->file, dlerror() );
return AF_ERROR;
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: library found.\n", setup->myname);
MP_VERBOSE(af, "%s: library found.\n", setup->myname);
/* find descriptor function */
dlerror();
@@ -402,7 +408,7 @@ static int af_ladspa_load_plugin(af_ladspa_t *setup) {
"ladspa_descriptor");
if (!descriptor_function) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n\t%s\n", setup->myname,
MP_ERR(af, "%s: %s\n\t%s\n", setup->myname,
_("Couldn't find ladspa_descriptor() function in the specified library file."), dlerror());
return AF_ERROR;
}
@@ -410,33 +416,33 @@ static int af_ladspa_load_plugin(af_ladspa_t *setup) {
/* if label == help, list all labels in library and exit */
if (strcmp(setup->label, "help") == 0) {
mp_msg(MSGT_AFILTER, MSGL_INFO, "%s: %s %s:\n", setup->myname,
MP_INFO(af, "%s: %s %s:\n", setup->myname,
_("available labels in"), setup->file);
for (i=0; ; i++) {
ladspa_descriptor = descriptor_function(i);
if (ladspa_descriptor == NULL) {
return AF_ERROR;
}
mp_msg(MSGT_AFILTER, MSGL_INFO, " %-16s - %s (%lu)\n",
MP_INFO(af, " %-16s - %s (%lu)\n",
ladspa_descriptor->Label,
ladspa_descriptor->Name,
ladspa_descriptor->UniqueID);
}
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: looking for label\n", setup->myname);
MP_VERBOSE(af, "%s: looking for label\n", setup->myname);
/* find label in library */
for (i=0; ; i++) {
ladspa_descriptor = descriptor_function(i);
if (ladspa_descriptor == NULL) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
MP_ERR(af, "%s: %s\n", setup->myname,
_("Couldn't find label in plugin library."));
return AF_ERROR;
}
if (strcmp(ladspa_descriptor->Label, setup->label) == 0) {
setup->plugin_descriptor = ladspa_descriptor;
mp_msg(MSGT_AFILTER, MSGL_V, "%s: %s found\n", setup->myname,
MP_VERBOSE(af, "%s: %s found\n", setup->myname,
setup->label);
return AF_OK;
}
@@ -457,7 +463,6 @@ static int af_ladspa_load_plugin(af_ladspa_t *setup) {
*/
static int af_ladspa_malloc_failed(char *myname) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s", myname, "Memory allocation failed.\n");
return AF_ERROR;
}
@@ -470,13 +475,6 @@ static int af_ladspa_malloc_failed(char *myname) {
* Commands:
* CONTROL_REINIT Sets the af structure with proper values for number
* of channels, rate, format, et cetera.
* CONTROL_COMMAND_LINE Parses the suboptions given to this filter
* through arg. It first parses the filename and
* the label. After that, it loads the filter
* and finds out its proprties. Then in continues
* parsing the controls given on the commandline,
* if any are needed.
*
* \param af Audio filter instance
* \param cmd The command to execute
* \param arg Arguments to the command
@@ -486,157 +484,20 @@ static int af_ladspa_malloc_failed(char *myname) {
*/
static int control(struct af_instance *af, int cmd, void *arg) {
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
float val;
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
switch(cmd) {
case AF_CONTROL_REINIT:
mp_msg(MSGT_AFILTER, MSGL_V, "%s: (re)init\n", setup->myname);
MP_VERBOSE(af, "%s: (re)init\n", setup->myname);
if (!arg) return AF_ERROR;
/* accept FLOAT, let af_format do conversion */
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
/* arg->len is not set here yet, so init of buffers and connecting the
* filter, has to be done in play() :-/
*/
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
return af_test_output(af, (struct mp_audio*)arg);
case AF_CONTROL_COMMAND_LINE: {
char *buf;
char *line = arg;
mp_msg(MSGT_AFILTER, MSGL_V, "%s: parse suboptions\n", setup->myname);
/* suboption parser here!
* format is (ladspa=)file:label:controls....
*/
if (!line) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("No suboptions specified."));
return AF_ERROR;
}
buf = malloc(strlen(line)+1);
if (!buf) return af_ladspa_malloc_failed(setup->myname);
/* file... */
buf[0] = '\0';
sscanf(line, "%[^:]", buf);
if (buf[0] == '\0') {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("No library file specified."));
free(buf);
return AF_ERROR;
}
line += strlen(buf);
setup->file = strdup(buf);
if (!setup->file) {
free(buf);
return af_ladspa_malloc_failed(setup->myname);
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: file --> %s\n", setup->myname,
setup->file);
if (*line != '\0') line++; /* read ':' */
/* label... */
buf[0] = '\0';
sscanf(line, "%[^:]", buf);
if (buf[0] == '\0') {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("No filter label specified."));
free(buf);
return AF_ERROR;
}
line += strlen(buf);
setup->label = strdup(buf);
if (!setup->label) {
free(buf);
return af_ladspa_malloc_failed(setup->myname);
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: label --> %s\n", setup->myname,
setup->label);
/* if (*line != '0') line++; */ /* read ':' */
free(buf); /* no longer needed */
/* set new setup->myname */
free(setup->myname);
setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
strlen(setup->label)+6, 1);
snprintf(setup->myname, strlen(af_info_ladspa.name)+
strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
af_info_ladspa.name, setup->file, setup->label);
/* load plugin :) */
if ( af_ladspa_load_plugin(setup) != AF_OK )
return AF_ERROR;
/* see what inputs, outputs and controls this plugin has */
if ( af_ladspa_parse_plugin(setup) != AF_OK )
return AF_ERROR;
/* ninputcontrols is set by now, read control values from arg */
for (int i = 0; i < setup->ninputcontrols; i++) {
if (!line || *line != ':') {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
}
line++;
if (sscanf(line, "%f", &val) != 1) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
}
setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
line = strchr(line, ':');
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
for (int i = 0; i < setup->ninputcontrols; i++) {
mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
setup->inputcontrols[setup->inputcontrolsmap[i]]);
}
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
/* check boundaries of inputcontrols */
mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
setup->myname);
for (int i = 0; i < setup->ninputcontrols; i++) {
int p = setup->inputcontrolsmap[i];
LADSPA_PortRangeHint hint =
setup->plugin_descriptor->PortRangeHints[p];
val = setup->inputcontrols[p];
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
val < hint.LowerBound) {
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is below lower boundary of %0.4f.\n",
setup->myname, i, hint.LowerBound);
return AF_ERROR;
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
val > hint.UpperBound) {
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is above upper boundary of %0.4f.\n",
setup->myname, i, hint.UpperBound);
return AF_ERROR;
}
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: all controls have sane values\n",
setup->myname);
/* All is well! */
setup->status = AF_OK;
return AF_OK; }
}
return AF_UNKNOWN;
@@ -653,13 +514,12 @@ static int control(struct af_instance *af, int cmd, void *arg) {
*/
static void uninit(struct af_instance *af) {
free(af->data);
if (af->setup) {
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
if (af->priv) {
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
if (setup->myname) {
mp_msg(MSGT_AFILTER, MSGL_V, "%s: cleaning up\n", setup->myname);
MP_VERBOSE(af, "%s: cleaning up\n", setup->myname);
free(setup->myname);
}
@@ -671,8 +531,6 @@ static void uninit(struct af_instance *af) {
free(setup->chhandles);
}
free(setup->file);
free(setup->label);
free(setup->inputcontrolsmap);
free(setup->inputcontrols);
free(setup->outputcontrolsmap);
@@ -694,9 +552,6 @@ static void uninit(struct af_instance *af) {
if (setup->libhandle)
dlclose(setup->libhandle);
free(setup);
setup = NULL;
}
}
@@ -710,26 +565,22 @@ static void uninit(struct af_instance *af) {
* \return Either AF_ERROR or AF_OK
*/
static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
af_ladspa_t *setup = af->setup;
static int filter(struct af_instance *af, struct mp_audio *data, int flags) {
af_ladspa_t *setup = af->priv;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
float *audio = (float*)data->audio;
int nsamples = data->len/4; /* /4 because it's 32-bit float */
float *audio = (float*)data->planes[0];
int nsamples = data->samples*data->nch;
int nch = data->nch;
int rate = data->rate;
int i, p;
if (setup->status !=AF_OK)
return data;
return -1;
/* See if it's the first call. If so, setup inbufs/outbufs, instantiate
* plugin, connect ports and activate plugin
*/
/* 2004-12-07: Also check if the buffersize has to be changed!
* data->len is not constant per se! re-init buffers.
*/
if ( (setup->bufsize != nsamples/nch) || (setup->nch != nch) ) {
/* if setup->nch==0, it's the first call, if not, something has
@@ -737,7 +588,7 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
*/
if (setup->nch != 0) {
mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize change; free old buffer\n",
MP_TRACE(af, "%s: bufsize change; free old buffer\n",
setup->myname);
if(setup->inbufs) {
@@ -758,7 +609,7 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
setup->inbufs = calloc(nch, sizeof(float*));
setup->outbufs = calloc(nch, sizeof(float*));
mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize = %d\n",
MP_TRACE(af, "%s: bufsize = %d\n",
setup->myname, setup->bufsize);
for(i=0; i<nch; i++) {
@@ -870,7 +721,7 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
/* done */
return data;
return 0;
}
/* ------------------------------------------------------------------------- */
@@ -886,29 +737,105 @@ static int af_open(struct af_instance *af) {
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->filter=filter;
af->data = calloc(1, sizeof(struct mp_audio));
if (af->data == NULL)
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
af_ladspa_t *setup = af->priv;
af->setup = calloc(1, sizeof(af_ladspa_t));
if (af->setup == NULL) {
free(af->data);
af->data=NULL;
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
}
((af_ladspa_t*)af->setup)->status = AF_ERROR; /* will be set to AF_OK if
* all went OK and play()
setup->status = AF_ERROR; /* will be set to AF_OK if
* all went OK and filter()
* should proceed.
*/
((af_ladspa_t*)af->setup)->myname = strdup(af_info_ladspa.name);
if (!((af_ladspa_t*)af->setup)->myname)
setup->myname = strdup(af_info_ladspa.name);
if (!setup->myname)
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
if (!setup->file || !setup->file[0]) {
MP_ERR(af, "%s: %s\n", setup->myname,
_("No library file specified."));
uninit(af);
return AF_ERROR;
}
if (!setup->label || !setup->label[0]) {
MP_ERR(af, "%s: %s\n", setup->myname,
_("No filter label specified."));
uninit(af);
return AF_ERROR;
}
free(setup->myname);
setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
strlen(setup->label)+6, 1);
snprintf(setup->myname, strlen(af_info_ladspa.name)+
strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
af_info_ladspa.name, setup->file, setup->label);
/* load plugin :) */
if ( af_ladspa_load_plugin(af) != AF_OK )
return AF_ERROR;
/* see what inputs, outputs and controls this plugin has */
if ( af_ladspa_parse_plugin(af) != AF_OK )
return AF_ERROR;
/* ninputcontrols is set by now, read control values from arg */
float val;
char *line = setup->controls;
for (int i = 0; i < setup->ninputcontrols; i++) {
if (!line || (i != 0 && *line != ',')) {
MP_ERR(af, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
}
if (i != 0)
line++;
if (sscanf(line, "%f", &val) != 1) {
MP_ERR(af, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
}
setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
line = strchr(line, ',');
}
MP_VERBOSE(af, "%s: input controls: ", setup->myname);
for (int i = 0; i < setup->ninputcontrols; i++) {
MP_VERBOSE(af, "%0.4f ",
setup->inputcontrols[setup->inputcontrolsmap[i]]);
}
MP_VERBOSE(af, "\n");
/* check boundaries of inputcontrols */
MP_VERBOSE(af, "%s: checking boundaries of input controls\n",
setup->myname);
for (int i = 0; i < setup->ninputcontrols; i++) {
int p = setup->inputcontrolsmap[i];
LADSPA_PortRangeHint hint =
setup->plugin_descriptor->PortRangeHints[p];
val = setup->inputcontrols[p];
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
val < hint.LowerBound) {
MP_ERR(af, "%s: Input control #%d is below lower boundary of %0.4f.\n",
setup->myname, i, hint.LowerBound);
return AF_ERROR;
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
val > hint.UpperBound) {
MP_ERR(af, "%s: Input control #%d is above upper boundary of %0.4f.\n",
setup->myname, i, hint.UpperBound);
return AF_ERROR;
}
}
MP_VERBOSE(af, "%s: all controls have sane values\n",
setup->myname);
/* All is well! */
setup->status = AF_OK;
return AF_OK;
}
+163 -227
View File
@@ -32,9 +32,10 @@
#include <libavutil/common.h>
#include <libavutil/mem.h>
#include "config.h"
#include "common/common.h"
#include "af.h"
#include "audio/reorder_ch.h"
#include "audio/audio_buffer.h"
#include "audio/fmt-conversion.h"
#define AC3_MAX_CHANNELS 6
@@ -50,339 +51,274 @@ typedef struct af_ac3enc_s {
struct AVCodec *lavc_acodec;
struct AVCodecContext *lavc_actx;
AVPacket pkt;
bool planarize;
int add_iec61937_header;
int bit_rate;
int pending_data_size;
char *pending_data;
int pending_len;
int expect_len;
int min_channel_num;
struct mp_audio_buffer *pending;
int in_samples; // samples of input per AC3 frame
int out_samples; // upper bound on encoded output per AC3 frame
int in_sampleformat;
int cfg_add_iec61937_header;
int cfg_bit_rate;
int cfg_min_channel_num;
} af_ac3enc_t;
// Initialization and runtime control
static int control(struct af_instance *af, int cmd, void *arg)
{
af_ac3enc_t *s = af->setup;
struct mp_audio *data = arg;
int i, bit_rate, test_output_res;
af_ac3enc_t *s = af->priv;
static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \
{0, 96000, 192000, 256000, 384000, 448000, 448000};
switch (cmd){
case AF_CONTROL_REINIT:
if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num)
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
if (AF_FORMAT_IS_AC3(in->format) || in->nch < s->cfg_min_channel_num)
return AF_DETACH;
mp_audio_set_format(af->data, s->in_sampleformat);
if (data->rate == 48000 || data->rate == 44100 || data->rate == 32000)
af->data->rate = data->rate;
else
af->data->rate = 48000;
if (data->nch > AC3_MAX_CHANNELS)
mp_audio_set_num_channels(af->data, AC3_MAX_CHANNELS);
else
mp_audio_set_channels(af->data, &data->channels);
mp_chmap_reorder_to_lavc(&af->data->channels);
test_output_res = af_test_output(af, data);
mp_audio_set_format(in, s->in_sampleformat);
s->pending_len = 0;
s->expect_len = AC3_FRAME_SIZE * data->nch * af->data->bps;
assert(s->expect_len <= s->pending_data_size);
if (s->add_iec61937_header)
af->mul = (double)AC3_FRAME_SIZE * 2 * 2 / s->expect_len;
else
af->mul = (double)AC3_MAX_CODED_FRAME_SIZE / s->expect_len;
if (in->rate != 48000 && in->rate != 44100 && in->rate != 32000)
in->rate = 48000;
af->data->rate = in->rate;
mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc reinit: %d, %d, %f, %d.\n",
data->nch, data->rate, af->mul, s->expect_len);
mp_chmap_reorder_to_lavc(&in->channels);
if (in->nch > AC3_MAX_CHANNELS)
mp_audio_set_num_channels(in, AC3_MAX_CHANNELS);
bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[af->data->nch];
mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
mp_audio_set_num_channels(af->data, 2);
if (s->lavc_actx->channels != af->data->nch ||
s->lavc_actx->sample_rate != af->data->rate ||
s->lavc_actx->bit_rate != bit_rate) {
if (!mp_audio_config_equals(in, &orig_in))
return AF_FALSE;
s->in_samples = AC3_FRAME_SIZE;
if (s->cfg_add_iec61937_header) {
s->out_samples = AC3_FRAME_SIZE;
} else {
s->out_samples = AC3_MAX_CODED_FRAME_SIZE / af->data->sstride;
}
af->mul = s->out_samples / (double)s->in_samples;
mp_audio_buffer_reinit(s->pending, in);
MP_DBG(af, "af_lavcac3enc reinit: %d, %d, %f, %d.\n",
in->nch, in->rate, af->mul, s->in_samples);
int bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[in->nch];
if (s->lavc_actx->channels != in->nch ||
s->lavc_actx->sample_rate != in->rate ||
s->lavc_actx->bit_rate != bit_rate)
{
avcodec_close(s->lavc_actx);
// Put sample parameters
s->lavc_actx->channels = af->data->nch;
s->lavc_actx->channel_layout = mp_chmap_to_lavc(&af->data->channels);
s->lavc_actx->sample_rate = af->data->rate;
s->lavc_actx->channels = in->nch;
s->lavc_actx->channel_layout = mp_chmap_to_lavc(&in->channels);
s->lavc_actx->sample_rate = in->rate;
s->lavc_actx->bit_rate = bit_rate;
if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) {
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
MP_ERR(af, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
return AF_ERROR;
}
}
if (s->lavc_actx->frame_size != AC3_FRAME_SIZE) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "lavcac3enc: unexpected ac3 "
MP_ERR(af, "lavcac3enc: unexpected ac3 "
"encoder frame size %d\n", s->lavc_actx->frame_size);
return AF_ERROR;
}
mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
mp_audio_set_num_channels(af->data, 2);
return test_output_res;
case AF_CONTROL_COMMAND_LINE:
mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc cmdline: %s.\n", (char*)arg);
s->bit_rate = 0;
s->min_channel_num = 0;
s->add_iec61937_header = 0;
sscanf(arg,"%d:%d:%d", &s->add_iec61937_header, &s->bit_rate,
&s->min_channel_num);
if (s->bit_rate < 1000)
s->bit_rate *= 1000;
if (s->bit_rate) {
for (i = 0; i < 19; ++i)
if (ac3_bitrate_tab[i] * 1000 == s->bit_rate)
break;
if (i >= 19) {
mp_msg(MSGT_AFILTER, MSGL_WARN, "af_lavcac3enc unable set unsupported "
"bitrate %d, use default bitrate (check manpage to see "
"supported bitrates).\n", s->bit_rate);
s->bit_rate = 0;
}
}
if (s->min_channel_num == 0)
s->min_channel_num = 5;
mp_msg(MSGT_AFILTER, MSGL_V, "af_lavcac3enc config spdif:%d, bitrate:%d, "
"minchnum:%d.\n", s->add_iec61937_header, s->bit_rate,
s->min_channel_num);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
af_ac3enc_t *s = af->setup;
af_ac3enc_t *s = af->priv;
if (af->data)
free(af->data->audio);
free(af->data);
if (s) {
av_free_packet(&s->pkt);
if(s->lavc_actx) {
avcodec_close(s->lavc_actx);
av_free(s->lavc_actx);
}
free(s->pending_data);
free(s);
}
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* audio)
static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
{
af_ac3enc_t *s = af->setup;
struct mp_audio *c = audio; // Current working data
struct mp_audio *l;
int left, outsize = 0;
char *buf, *src;
int max_output_len;
int frame_num = (audio->len + s->pending_len) / s->expect_len;
int samplesize = af_fmt2bits(s->in_sampleformat) / 8;
struct mp_audio *out = af->data;
af_ac3enc_t *s = af->priv;
int num_frames = (audio->samples + mp_audio_buffer_samples(s->pending))
/ s->in_samples;
if (s->add_iec61937_header)
max_output_len = AC3_FRAME_SIZE * 2 * 2 * frame_num;
else
max_output_len = AC3_MAX_CODED_FRAME_SIZE * frame_num;
int max_out_samples = s->out_samples * num_frames;
mp_audio_realloc_min(out, max_out_samples);
out->samples = 0;
if (af->data->len < max_output_len) {
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
"old len = %i, new len = %i\n", af->info->name, af->data->len,
max_output_len);
free(af->data->audio);
af->data->audio = malloc(max_output_len);
if (!af->data->audio) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
return NULL;
}
af->data->len = max_output_len;
}
l = af->data; // Local data
buf = l->audio;
src = c->audio;
left = c->len;
while (left > 0) {
while (audio->samples > 0) {
int ret;
if (left + s->pending_len < s->expect_len) {
memcpy(s->pending_data + s->pending_len, src, left);
src += left;
s->pending_len += left;
left = 0;
break;
}
char *src2 = src;
if (s->pending_len) {
int needs = s->expect_len - s->pending_len;
if (needs > 0) {
memcpy(s->pending_data + s->pending_len, src, needs);
src += needs;
left -= needs;
int consumed_pending = 0;
struct mp_audio in_frame;
int pending = mp_audio_buffer_samples(s->pending);
if (pending == 0 && audio->samples >= s->in_samples) {
in_frame = *audio;
mp_audio_skip_samples(audio, s->in_samples);
} else {
if (pending > 0 && pending < s->in_samples) {
struct mp_audio tmp = *audio;
tmp.samples = MPMIN(tmp.samples, s->in_samples);
mp_audio_buffer_append(s->pending, &tmp);
mp_audio_skip_samples(audio, tmp.samples);
}
src2= s->pending_data;
}
void *data = (void *) src2;
if (s->planarize) {
void *data2 = malloc(s->expect_len);
reorder_to_planar(data2, data, samplesize,
c->nch, s->expect_len / samplesize / c->nch);
data = data2;
mp_audio_buffer_peek(s->pending, &in_frame);
if (in_frame.samples < s->in_samples)
break;
consumed_pending = s->in_samples;
}
in_frame.samples = s->in_samples;
AVFrame *frame = avcodec_alloc_frame();
if (!frame) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
return NULL;
MP_FATAL(af, "[libaf] Could not allocate memory \n");
return -1;
}
frame->nb_samples = AC3_FRAME_SIZE;
frame->nb_samples = s->in_samples;
frame->format = s->lavc_actx->sample_fmt;
frame->channel_layout = s->lavc_actx->channel_layout;
ret = avcodec_fill_audio_frame(frame, c->nch, s->lavc_actx->sample_fmt,
(const uint8_t*)data, s->expect_len, 0);
if (ret < 0) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[lavac3enc] Frame setup failed.\n");
return NULL;
}
assert(in_frame.num_planes <= AV_NUM_DATA_POINTERS);
for (int n = 0; n < in_frame.num_planes; n++)
frame->data[n] = in_frame.planes[n];
frame->linesize[0] = s->in_samples * audio->sstride;
int ok;
ret = avcodec_encode_audio2(s->lavc_actx, &s->pkt, frame, &ok);
if (ret < 0 || !ok) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[lavac3enc] Encode failed.\n");
return NULL;
MP_FATAL(af, "[lavac3enc] Encode failed.\n");
return -1;
}
if (s->planarize)
free(data);
avcodec_free_frame(&frame);
if (s->pending_len) {
s->pending_len = 0;
} else {
src += s->expect_len;
left -= s->expect_len;
}
mp_audio_buffer_skip(s->pending, consumed_pending);
mp_msg(MSGT_AFILTER, MSGL_DBG2, "avcodec_encode_audio got %d, pending %d.\n",
s->pkt.size, s->pending_len);
MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n",
s->pkt.size, mp_audio_buffer_samples(s->pending));
int len = s->pkt.size;
int frame_size = s->pkt.size;
int header_len = 0;
if (s->add_iec61937_header) {
assert(s->pkt.size > 5);
char hdr[8];
if (s->cfg_add_iec61937_header && s->pkt.size > 5) {
int bsmod = s->pkt.data[5] & 0x7;
int len = frame_size;
AV_WB16(buf, 0xF872); // iec 61937 syncword 1
AV_WB16(buf + 2, 0x4E1F); // iec 61937 syncword 2
buf[4] = bsmod; // bsmod
buf[5] = 0x01; // data-type ac3
AV_WB16(buf + 6, len << 3); // number of bits in payload
memset(buf + 8 + len, 0, AC3_FRAME_SIZE * 2 * 2 - 8 - len);
frame_size = AC3_FRAME_SIZE * 2 * 2;
header_len = 8;
len = AC3_FRAME_SIZE * 2 * 2;
AV_WB16(hdr, 0xF872); // iec 61937 syncword 1
AV_WB16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
hdr[4] = bsmod; // bsmod
hdr[5] = 0x01; // data-type ac3
AV_WB16(hdr + 6, len << 3); // number of bits in payload
}
assert(buf + len <= (char *)af->data->audio + af->data->len);
assert(s->pkt.size <= len - header_len);
size_t max_size = (max_out_samples - out->samples) * out->sstride;
if (frame_size > max_size)
abort();
char *buf = (char *)out->planes[0] + out->samples * out->sstride;
memcpy(buf, hdr, header_len);
memcpy(buf + header_len, s->pkt.data, s->pkt.size);
outsize += len;
buf += len;
memset(buf + header_len + s->pkt.size, 0,
frame_size - (header_len + s->pkt.size));
out->samples += frame_size / out->sstride;
}
c->audio = l->audio;
mp_audio_set_num_channels(c, 2);
mp_audio_set_format(c, af->data->format);
c->len = outsize;
mp_msg(MSGT_AFILTER, MSGL_DBG2, "play return size %d, pending %d\n",
outsize, s->pending_len);
return c;
mp_audio_buffer_append(s->pending, audio);
*audio = *out;
return 0;
}
static int af_open(struct af_instance* af){
af_ac3enc_t *s = calloc(1,sizeof(af_ac3enc_t));
af_ac3enc_t *s = af->priv;
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=s;
af->filter=filter;
s->lavc_acodec = avcodec_find_encoder_by_name("ac3");
if (!s->lavc_acodec) {
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't find encoder for codec %s.\n", "ac3");
MP_ERR(af, "Audio LAVC, couldn't find encoder for codec %s.\n", "ac3");
return AF_ERROR;
}
s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec);
if (!s->lavc_actx) {
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't allocate context!\n");
MP_ERR(af, "Audio LAVC, couldn't allocate context!\n");
return AF_ERROR;
}
const enum AVSampleFormat *fmts = s->lavc_acodec->sample_fmts;
for (int i = 0; ; i++) {
if (fmts[i] == AV_SAMPLE_FMT_NONE) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, encoder doesn't "
"support expected sample formats!\n");
return AF_ERROR;
} else if (fmts[i] == AV_SAMPLE_FMT_S16) {
s->in_sampleformat = AF_FORMAT_S16_NE;
for (int i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
s->in_sampleformat = af_from_avformat(fmts[i]);
if (s->in_sampleformat) {
s->lavc_actx->sample_fmt = fmts[i];
s->planarize = 0;
break;
} else if (fmts[i] == AV_SAMPLE_FMT_FLT) {
s->in_sampleformat = AF_FORMAT_FLOAT_NE;
s->lavc_actx->sample_fmt = fmts[i];
s->planarize = 0;
break;
} else if (fmts[i] == AV_SAMPLE_FMT_S16P) {
s->in_sampleformat = AF_FORMAT_S16_NE;
s->lavc_actx->sample_fmt = fmts[i];
s->planarize = 1;
break;
} else if (fmts[i] == AV_SAMPLE_FMT_FLTP) {
s->in_sampleformat = AF_FORMAT_FLOAT_NE;
s->lavc_actx->sample_fmt = fmts[i];
s->planarize = 1;
break;
}
}
char buf[100];
mp_msg(MSGT_AFILTER, MSGL_V, "[af_lavcac3enc]: in sample format: %s\n",
af_fmt2str(s->in_sampleformat, buf, 100));
s->pending_data_size = AF_NCH * AC3_FRAME_SIZE *
af_fmt2bits(s->in_sampleformat) / 8;
s->pending_data = malloc(s->pending_data_size);
if (s->planarize)
mp_msg(MSGT_AFILTER, MSGL_WARN,
"[af_lavcac3enc]: need to planarize audio data\n");
if (!s->in_sampleformat) {
MP_ERR(af, "Audio LAVC, encoder doesn't "
"support expected sample formats!\n");
return AF_ERROR;
}
MP_VERBOSE(af, "[af_lavcac3enc]: in sample format: %s\n",
af_fmt_to_str(s->in_sampleformat));
av_init_packet(&s->pkt);
s->pending = mp_audio_buffer_create(af);
if (s->cfg_bit_rate) {
int i;
for (i = 0; i < 19; i++) {
if (ac3_bitrate_tab[i] == s->cfg_bit_rate)
break;
}
if (i >= 19) {
MP_WARN(af, "af_lavcac3enc unable set unsupported "
"bitrate %d, use default bitrate (check manpage to see "
"supported bitrates).\n", s->cfg_bit_rate);
s->cfg_bit_rate = 0;
}
}
return AF_OK;
}
#define OPT_BASE_STRUCT struct af_ac3enc_s
struct af_info af_info_lavcac3enc = {
"runtime encode to ac3 using libavcodec",
"lavcac3enc",
"Ulion",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "runtime encode to ac3 using libavcodec",
.name = "lavcac3enc",
.open = af_open,
.priv_size = sizeof(struct af_ac3enc_s),
.priv_defaults = &(const struct af_ac3enc_s){
.cfg_add_iec61937_header = 1,
.cfg_min_channel_num = 5,
},
.options = (const struct m_option[]) {
OPT_FLAG("tospdif", cfg_add_iec61937_header, 0),
OPT_CHOICE_OR_INT("bitrate", cfg_bit_rate, 0, 32, 640,
({"default", 0})),
OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6),
{0}
},
};
+46 -62
View File
@@ -38,8 +38,8 @@
#include "audio/fmt-conversion.h"
#include "af.h"
#include "mpvcore/m_option.h"
#include "mpvcore/av_opts.h"
#include "options/m_option.h"
#include "common/av_opts.h"
#define IS_LIBAV_FORK (LIBAVFILTER_VERSION_MICRO < 100)
@@ -58,14 +58,7 @@ struct priv {
AVFilterContext *in;
AVFilterContext *out;
// Guarantee that the data stays valid until next filter call
char *out_buffer;
struct mp_audio data;
struct mp_audio temp;
int64_t bytes_in;
int64_t bytes_out;
int64_t samples_in;
AVRational timebase_out;
@@ -89,19 +82,19 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
int r;
if (bstr0(p->cfg_graph).len == 0) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "lavfi: no filter graph set\n");
MP_FATAL(af, "lavfi: no filter graph set\n");
return false;
}
destroy_graph(af);
mp_msg(MSGT_AFILTER, MSGL_V, "lavfi: create graph: '%s'\n", p->cfg_graph);
MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph);
AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph)
goto error;
if (parse_avopts(graph, p->cfg_avopts) < 0) {
mp_msg(MSGT_VFILTER, MSGL_FATAL, "lavfi: could not set opts: '%s'\n",
MP_FATAL(af, "lavfi: could not set opts: '%s'\n",
p->cfg_avopts);
goto error;
}
@@ -129,6 +122,8 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL,
AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
r = av_opt_set_int_list(out, "sample_fmts", sample_fmts,
@@ -163,7 +158,7 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
return true;
error:
mp_msg(MSGT_AFILTER, MSGL_FATAL, "Can't configure libavfilter graph.\n");
MP_FATAL(af, "Can't configure libavfilter graph.\n");
avfilter_graph_free(&graph);
talloc_free(tmp);
return false;
@@ -180,7 +175,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
struct mp_audio *out = af->data;
if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE)
mp_audio_set_format(in, AF_FORMAT_FLOAT_NE);
mp_audio_set_format(in, AF_FORMAT_FLOAT);
if (!mp_chmap_is_lavc(&in->channels))
mp_chmap_reorder_to_lavc(&in->channels); // will always work
@@ -200,55 +195,51 @@ static int control(struct af_instance *af, int cmd, void *arg)
mp_chmap_from_channels(&out_cm, l_out->channels);
mp_audio_set_channels(out, &out_cm);
if (!mp_audio_config_valid(out))
return AF_ERROR;
p->timebase_out = l_out->time_base;
af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
// Blatantly incorrect; we don't know what the filters do.
af->mul = out->rate / (double)in->rate;
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_COMMAND_LINE: {
talloc_free(p->cfg_graph);
p->cfg_graph = talloc_strdup(p, (char *)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
struct priv *p = af->priv;
struct mp_audio *r = af->data;
bool eof = data->samples == 0 && (flags & AF_FILTER_FLAG_EOF);
AVFilterLink *l_in = p->in->outputs[0];
struct mp_audio *r = &p->temp;
*r = *af->data;
int in_frame_size = data->bps * data->channels.num;
int out_frame_size = r->bps * r->channels.num;
AVFrame *frame = av_frame_alloc();
frame->nb_samples = data->len / in_frame_size;
frame->nb_samples = data->samples;
frame->format = l_in->format;
// Timebase is 1/sample_rate
frame->pts = p->bytes_in / in_frame_size;
frame->pts = p->samples_in;
av_frame_set_channels(frame, l_in->channels);
av_frame_set_channel_layout(frame, l_in->channel_layout);
av_frame_set_sample_rate(frame, l_in->sample_rate);
frame->data[0] = data->audio;
frame->extended_data = frame->data;
for (int n = 0; n < data->num_planes; n++)
frame->data[n] = data->planes[n];
frame->linesize[0] = frame->nb_samples * data->sstride;
if (av_buffersrc_add_frame(p->in, frame) < 0) {
if (av_buffersrc_add_frame(p->in, eof ? NULL : frame) < 0) {
av_frame_free(&frame);
return NULL;
return -1;
}
av_frame_free(&frame);
int64_t out_pts = AV_NOPTS_VALUE;
size_t out_len = 0;
r->samples = 0;
for (;;) {
frame = av_frame_alloc();
if (av_buffersink_get_frame(p->out, frame) < 0) {
@@ -257,62 +248,55 @@ static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
break;
}
size_t new_len = out_len + frame->nb_samples * out_frame_size;
if (new_len > talloc_get_size(p->out_buffer))
p->out_buffer = talloc_realloc(p, p->out_buffer, char, new_len);
memcpy(p->out_buffer + out_len, frame->data[0], new_len - out_len);
out_len = new_len;
mp_audio_realloc_min(r, r->samples + frame->nb_samples);
for (int n = 0; n < r->num_planes; n++) {
memcpy((char *)r->planes[n] + r->samples * r->sstride,
frame->extended_data[n], frame->nb_samples * r->sstride);
}
r->samples += frame->nb_samples;
if (out_pts == AV_NOPTS_VALUE)
out_pts = frame->pts;
av_frame_free(&frame);
}
r->audio = p->out_buffer;
r->len = out_len;
p->bytes_in += data->len;
p->bytes_out += r->len;
p->samples_in += data->samples;
if (out_pts != AV_NOPTS_VALUE) {
int64_t num_in_frames = p->bytes_in / in_frame_size;
double in_time = num_in_frames / (double)data->rate;
double in_time = p->samples_in / (double)data->rate;
double out_time = out_pts * av_q2d(p->timebase_out);
// Need pts past the last output sample.
int out_frames = r->len / out_frame_size;
out_time += out_frames / (double)r->rate;
out_time += r->samples / (double)r->rate;
af->delay = (in_time - out_time) * r->rate * out_frame_size;
af->delay = in_time - out_time;
}
return r;
*data = *r;
return 0;
}
static void uninit(struct af_instance *af)
{
destroy_graph(af);
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
struct priv *priv = af->priv;
af->data = &priv->data;
af->filter = filter;
// Removing this requires fixing AVFrame.data vs. AVFrame.extended_data
assert(MP_NUM_CHANNELS <= AV_NUM_DATA_POINTERS);
return AF_OK;
}
#define OPT_BASE_STRUCT struct priv
struct af_info af_info_lavfi = {
"libavfilter bridge",
"lavfi",
"",
"",
0,
af_open,
.info = "libavfilter bridge",
.name = "lavfi",
.open = af_open,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_STRING("graph", cfg_graph, 0),
+181 -144
View File
@@ -3,6 +3,7 @@
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
* This file is part of mpv.
* Based on Michael Niedermayer's lavcresample.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,6 +24,8 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <libavutil/opt.h>
#include <libavutil/audioconvert.h>
#include <libavutil/common.h>
@@ -32,27 +35,28 @@
#include "talloc.h"
#include "config.h"
#if defined(CONFIG_LIBAVRESAMPLE)
#if HAVE_LIBAVRESAMPLE
#include <libavresample/avresample.h>
#define USE_SET_CHANNEL_MAPPING HAVE_AVRESAMPLE_SET_CHANNEL_MAPPING
#elif defined(CONFIG_LIBSWRESAMPLE)
#elif HAVE_LIBSWRESAMPLE
#include <libswresample/swresample.h>
#define AVAudioResampleContext SwrContext
#define avresample_alloc_context swr_alloc
#define avresample_open swr_init
#define avresample_close(x) do { } while(0)
#define avresample_free swr_free
#define avresample_available(x) 0
#define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \
swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples)
#define avresample_set_channel_mapping swr_set_channel_mapping
#define USE_SET_CHANNEL_MAPPING 1
#else
#error "config.h broken"
#error "config.h broken or no resampler found"
#endif
#include "mpvcore/mp_msg.h"
#include "mpvcore/m_option.h"
#include "mpvcore/av_opts.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "common/av_opts.h"
#include "audio/filter/af.h"
#include "audio/fmt-conversion.h"
#include "audio/reorder_ch.h"
@@ -84,16 +88,24 @@ struct af_resample {
uint8_t *reorder_buffer;
};
#ifdef CONFIG_LIBAVRESAMPLE
#if HAVE_LIBAVRESAMPLE
static int get_delay(struct af_resample *s)
{
return avresample_get_delay(s->avrctx);
}
static void drop_all_output(struct af_resample *s)
{
while (avresample_read(s->avrctx, NULL, 1000) > 0) {}
}
#else
static int get_delay(struct af_resample *s)
{
return swr_get_delay(s->avrctx, s->ctx.in_rate);
}
static void drop_all_output(struct af_resample *s)
{
while (swr_drop_output(s->avrctx, 1000) > 0) {}
}
#endif
static double af_resample_default_cutoff(int filter_size)
@@ -124,8 +136,98 @@ static bool test_conversion(int src_format, int dst_format)
af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
}
#define ctx_opt_set_int(a,b) av_opt_set_int(s->avrctx, (a), (b), 0)
#define ctx_opt_set_dbl(a,b) av_opt_set_double(s->avrctx, (a), (b), 0)
static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
struct mp_audio *out)
{
struct af_resample *s = af->priv;
enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
if (in_samplefmt == AV_SAMPLE_FMT_NONE || out_samplefmt == AV_SAMPLE_FMT_NONE)
return AF_ERROR;
avresample_close(s->avrctx);
avresample_close(s->avrctx_out);
s->ctx.out_rate = out->rate;
s->ctx.in_rate = in->rate;
s->ctx.out_format = out->format;
s->ctx.in_format = in->format;
s->ctx.out_channels= out->channels;
s->ctx.in_channels = in->channels;
s->ctx.filter_size = s->opts.filter_size;
s->ctx.phase_shift = s->opts.phase_shift;
s->ctx.linear = s->opts.linear;
s->ctx.cutoff = s->opts.cutoff;
av_opt_set_int(s->avrctx, "filter_size", s->ctx.filter_size, 0);
av_opt_set_int(s->avrctx, "phase_shift", s->ctx.phase_shift, 0);
av_opt_set_int(s->avrctx, "linear_interp", s->ctx.linear, 0);
av_opt_set_double(s->avrctx, "cutoff", s->ctx.cutoff, 0);
if (parse_avopts(s->avrctx, s->avopts) < 0) {
MP_FATAL(af, "af_lavrresample: could not set opts: '%s'\n", s->avopts);
return AF_ERROR;
}
struct mp_chmap map_in = in->channels;
struct mp_chmap map_out = out->channels;
// Try not to do any remixing if at least one is "unknown".
if (mp_chmap_is_unknown(&map_in) || mp_chmap_is_unknown(&map_out)) {
mp_chmap_set_unknown(&map_in, map_in.num);
mp_chmap_set_unknown(&map_out, map_out.num);
}
// unchecked: don't take any channel reordering into account
uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in);
uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out);
av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0);
av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0);
av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0);
av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0);
av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0);
av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmt, 0);
struct mp_chmap in_lavc;
mp_chmap_from_lavc(&in_lavc, in_ch_layout);
mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc);
struct mp_chmap out_lavc;
mp_chmap_from_lavc(&out_lavc, out_ch_layout);
mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out);
// Same configuration; we just reorder.
av_opt_set_int(s->avrctx_out, "in_channel_layout", out_ch_layout, 0);
av_opt_set_int(s->avrctx_out, "out_channel_layout", out_ch_layout, 0);
av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmt, 0);
av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0);
av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0);
av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0);
#if USE_SET_CHANNEL_MAPPING
// API has weird requirements, quoting avresample.h:
// * This function can only be called when the allocated context is not open.
// * Also, the input channel layout must have already been set.
avresample_set_channel_mapping(s->avrctx, s->reorder_in);
avresample_set_channel_mapping(s->avrctx_out, s->reorder_out);
#endif
if (avresample_open(s->avrctx) < 0 ||
avresample_open(s->avrctx_out) < 0)
{
MP_ERR(af, "[lavrresample] Cannot open "
"Libavresample Context. \n");
return AF_ERROR;
}
return AF_OK;
}
static int control(struct af_instance *af, int cmd, void *arg)
{
@@ -149,120 +251,38 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (mp_chmap_is_empty(&out->channels))
mp_audio_set_channels(out, &in->channels);
enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
if (in_samplefmt == AV_SAMPLE_FMT_NONE) {
mp_audio_set_format(in, AF_FORMAT_FLOAT_NE);
in_samplefmt = af_to_avformat(in->format);
}
enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
if (out_samplefmt == AV_SAMPLE_FMT_NONE) {
if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE)
mp_audio_set_format(in, AF_FORMAT_FLOAT);
if (af_to_avformat(out->format) == AV_SAMPLE_FMT_NONE)
mp_audio_set_format(out, in->format);
out_samplefmt = in_samplefmt;
}
af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
af->delay = out->nch * s->opts.filter_size / FFMIN(af->mul, 1);
af->mul = out->rate / (double)in->rate;
if (needs_lavrctx_reconfigure(s, in, out)) {
avresample_close(s->avrctx);
avresample_close(s->avrctx_out);
s->ctx.out_rate = out->rate;
s->ctx.in_rate = in->rate;
s->ctx.out_format = out->format;
s->ctx.in_format = in->format;
s->ctx.out_channels= out->channels;
s->ctx.in_channels = in->channels;
s->ctx.filter_size = s->opts.filter_size;
s->ctx.phase_shift = s->opts.phase_shift;
s->ctx.linear = s->opts.linear;
s->ctx.cutoff = s->opts.cutoff;
ctx_opt_set_int("filter_size", s->ctx.filter_size);
ctx_opt_set_int("phase_shift", s->ctx.phase_shift);
ctx_opt_set_int("linear_interp", s->ctx.linear);
ctx_opt_set_dbl("cutoff", s->ctx.cutoff);
if (parse_avopts(s->avrctx, s->avopts) < 0) {
mp_msg(MSGT_VFILTER, MSGL_FATAL,
"af_lavrresample: could not set opts: '%s'\n", s->avopts);
return AF_ERROR;
}
struct mp_chmap map_in = in->channels;
struct mp_chmap map_out = out->channels;
// Try not to do any remixing if at least one is "unknown".
if (mp_chmap_is_unknown(&map_in) || mp_chmap_is_unknown(&map_out)) {
mp_chmap_set_unknown(&map_in, map_in.num);
mp_chmap_set_unknown(&map_out, map_out.num);
}
// unchecked: don't take any channel reordering into account
uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in);
uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out);
ctx_opt_set_int("in_channel_layout", in_ch_layout);
ctx_opt_set_int("out_channel_layout", out_ch_layout);
ctx_opt_set_int("in_sample_rate", s->ctx.in_rate);
ctx_opt_set_int("out_sample_rate", s->ctx.out_rate);
ctx_opt_set_int("in_sample_fmt", in_samplefmt);
ctx_opt_set_int("out_sample_fmt", out_samplefmt);
struct mp_chmap in_lavc;
mp_chmap_from_lavc(&in_lavc, in_ch_layout);
mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc);
struct mp_chmap out_lavc;
mp_chmap_from_lavc(&out_lavc, out_ch_layout);
mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out);
// Same configuration; we just reorder.
av_opt_set_int(s->avrctx_out, "in_channel_layout", out_ch_layout, 0);
av_opt_set_int(s->avrctx_out, "out_channel_layout", out_ch_layout, 0);
av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmt, 0);
av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0);
av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0);
av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0);
#if USE_SET_CHANNEL_MAPPING
// API has weird requirements, quoting avresample.h:
// * This function can only be called when the allocated context is not open.
// * Also, the input channel layout must have already been set.
avresample_set_channel_mapping(s->avrctx, s->reorder_in);
avresample_set_channel_mapping(s->avrctx_out, s->reorder_out);
#endif
if (avresample_open(s->avrctx) < 0 ||
avresample_open(s->avrctx_out) < 0)
{
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot open "
"Libavresample Context. \n");
return AF_ERROR;
}
}
return ((in->format == orig_in.format) &&
int r = ((in->format == orig_in.format) &&
mp_chmap_equals(&in->channels, &orig_in.channels))
? AF_OK : AF_FALSE;
? AF_OK : AF_FALSE;
if (r == AF_OK && needs_lavrctx_reconfigure(s, in, out))
r = configure_lavrr(af, in, out);
return r;
}
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: {
case AF_CONTROL_SET_FORMAT: {
if (af_to_avformat(*(int*)arg) == AV_SAMPLE_FMT_NONE)
return AF_FALSE;
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
case AF_CONTROL_CHANNELS | AF_CONTROL_SET: {
case AF_CONTROL_SET_CHANNELS: {
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
return AF_OK;
}
case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
case AF_CONTROL_SET_RESAMPLE_RATE:
out->rate = *(int *)arg;
return AF_OK;
case AF_CONTROL_RESET:
drop_all_output(s);
return AF_OK;
}
return AF_UNKNOWN;
}
@@ -275,8 +295,10 @@ static void uninit(struct af_instance *af)
struct af_resample *s = af->priv;
if (s->avrctx)
avresample_close(s->avrctx);
avresample_free(&s->avrctx);
if (s->avrctx_out)
avresample_close(s->avrctx_out);
avresample_free(&s->avrctx_out);
}
static bool needs_reorder(int *reorder, int num_ch)
@@ -288,52 +310,74 @@ static bool needs_reorder(int *reorder, int num_ch)
return false;
}
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
static void reorder_planes(struct mp_audio *mpa, int *reorder)
{
struct mp_audio prev = *mpa;
for (int n = 0; n < mpa->num_planes; n++) {
assert(reorder[n] >= 0 && reorder[n] < mpa->num_planes);
mpa->planes[n] = prev.planes[reorder[n]];
}
}
#if !USE_SET_CHANNEL_MAPPING
static void do_reorder(struct mp_audio *mpa, int *reorder)
{
if (af_fmt_is_planar(mpa->format)) {
reorder_planes(mpa, reorder);
} else {
reorder_channels(mpa->planes[0], reorder, mpa->bps, mpa->nch, mpa->samples);
}
}
#endif
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
struct af_resample *s = af->priv;
struct mp_audio *in = data;
struct mp_audio *out = af->data;
int in_size = data->len;
int in_samples = in_size / (data->bps * data->nch);
int out_samples = avresample_available(s->avrctx) +
av_rescale_rnd(get_delay(s) + in_samples,
out->samples = avresample_available(s->avrctx) +
av_rescale_rnd(get_delay(s) + in->samples,
s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP);
int out_size = out->bps * out_samples * out->nch;
if (talloc_get_size(out->audio) < out_size)
out->audio = talloc_realloc_size(out, out->audio, out_size);
mp_audio_realloc_min(out, out->samples);
af->delay = out->bps * av_rescale_rnd(get_delay(s),
s->ctx.out_rate, s->ctx.in_rate,
AV_ROUND_UP);
af->delay = get_delay(s) / (double)s->ctx.in_rate;
#if !USE_SET_CHANNEL_MAPPING
reorder_channels(data->audio, s->reorder_in, data->bps, data->nch, in_samples);
do_reorder(in, s->reorder_in);
#endif
out_samples = avresample_convert(s->avrctx,
(uint8_t **) &out->audio, out_size, out_samples,
(uint8_t **) &in->audio, in_size, in_samples);
if (out->samples) {
out->samples = avresample_convert(s->avrctx,
(uint8_t **) out->planes, out->samples * out->sstride, out->samples,
(uint8_t **) in->planes, in->samples * in->sstride, in->samples);
if (out->samples < 0)
return -1; // error
}
*data = *out;
#if USE_SET_CHANNEL_MAPPING
if (needs_reorder(s->reorder_out, out->nch)) {
if (talloc_get_size(s->reorder_buffer) < out_size)
s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
data->audio = s->reorder_buffer;
out_samples = avresample_convert(s->avrctx_out,
(uint8_t **) &data->audio, out_size, out_samples,
(uint8_t **) &out->audio, out_size, out_samples);
if (af_fmt_is_planar(out->format)) {
reorder_planes(data, s->reorder_out);
} else {
int out_size = out->samples * out->sstride;
if (talloc_get_size(s->reorder_buffer) < out_size)
s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
data->planes[0] = s->reorder_buffer;
int out_samples = avresample_convert(s->avrctx_out,
(uint8_t **) data->planes, out_size, out->samples,
(uint8_t **) out->planes, out_size, out->samples);
assert(out_samples == data->samples);
}
}
#else
reorder_channels(data->audio, s->reorder_out, out->bps, out->nch, out_samples);
do_reorder(data, s->reorder_out);
#endif
data->len = out->bps * out_samples * out->nch;
return data;
return 0;
}
static int af_open(struct af_instance *af)
@@ -342,11 +386,7 @@ static int af_open(struct af_instance *af)
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
af->data = talloc_zero(s, struct mp_audio);
af->data->rate = 0;
af->filter = filter;
if (s->opts.cutoff <= 0.0)
s->opts.cutoff = af_resample_default_cutoff(s->opts.filter_size);
@@ -357,7 +397,7 @@ static int af_open(struct af_instance *af)
if (s->avrctx && s->avrctx_out) {
return AF_OK;
} else {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot initialize "
MP_ERR(af, "[lavrresample] Cannot initialize "
"Libavresample Context. \n");
uninit(af);
return AF_ERROR;
@@ -367,12 +407,9 @@ static int af_open(struct af_instance *af)
#define OPT_BASE_STRUCT struct af_resample
struct af_info af_info_lavrresample = {
"Sample frequency conversion using libavresample",
"lavrresample",
"Stefano Pigozzi (based on Michael Niedermayer's lavcresample)",
"",
AF_FLAGS_REENTRANT,
af_open,
.info = "Sample frequency conversion using libavresample",
.name = "lavrresample",
.open = af_open,
.test_conversion = test_conversion,
.priv_size = sizeof(struct af_resample),
.priv_defaults = &(const struct af_resample) {
+59 -83
View File
@@ -25,6 +25,7 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -32,6 +33,7 @@ typedef struct af_pan_s
{
int nch; // Number of output channels; zero means same as input
float level[AF_NCH][AF_NCH]; // Gain level for each channel
char *matrixstr;
}af_pan_t;
static void set_channels(struct mp_audio *mpa, int num)
@@ -46,7 +48,7 @@ static void set_channels(struct mp_audio *mpa, int num)
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_pan_t* s = af->setup;
af_pan_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:
@@ -54,9 +56,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
set_channels(af->data, s->nch ? s->nch: ((struct mp_audio*)arg)->nch);
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
if((af->data->format != ((struct mp_audio*)arg)->format) ||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
@@ -64,33 +65,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
return AF_FALSE;
}
return AF_OK;
case AF_CONTROL_COMMAND_LINE:{
int nch = 0;
int n = 0;
char* cp = NULL;
int j,k;
// Read number of outputs
sscanf((char*)arg,"%i%n", &nch,&n);
if(AF_OK != control(af,AF_CONTROL_PAN_NOUT | AF_CONTROL_SET, &nch))
return AF_ERROR;
// Read pan values
cp = &((char*)arg)[n];
j = 0; k = 0;
while((*cp == ':') && (k < AF_NCH)){
sscanf(cp, ":%f%n" , &s->level[j][k], &n);
mp_msg(MSGT_AFILTER, MSGL_V, "[pan] Pan level from channel %i to"
" channel %i = %f\n",k,j,s->level[j][k]);
cp =&cp[n];
j++;
if(j>=nch){
j = 0;
k++;
}
}
return AF_OK;
}
case AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET:{
case AF_CONTROL_SET_PAN_LEVEL:{
int i;
int ch = ((af_control_ext_t*)arg)->ch;
float* level = ((af_control_ext_t*)arg)->arg;
@@ -100,40 +75,30 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->level[ch][i] = level[i];
return AF_OK;
}
case AF_CONTROL_PAN_LEVEL | AF_CONTROL_GET:{
int i;
int ch = ((af_control_ext_t*)arg)->ch;
float* level = ((af_control_ext_t*)arg)->arg;
if (ch >= AF_NCH)
return AF_FALSE;
for(i=0;i<AF_NCH;i++)
level[i] = s->level[ch][i];
return AF_OK;
}
case AF_CONTROL_PAN_NOUT | AF_CONTROL_SET:
case AF_CONTROL_SET_PAN_NOUT:
// Reinit must be called after this function has been called
// Sanity check
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[pan] The number of output channels must be"
MP_ERR(af, "[pan] The number of output channels must be"
" between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
return AF_ERROR;
}
s->nch=((int*)arg)[0];
return AF_OK;
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{
case AF_CONTROL_SET_PAN_BALANCE:{
float val = *(float*)arg;
if (s->nch)
return AF_ERROR;
if (af->data->nch >= 2) {
s->level[0][0] = min(1.f, 1.f - val);
s->level[0][1] = max(0.f, val);
s->level[1][0] = max(0.f, -val);
s->level[1][1] = min(1.f, 1.f + val);
s->level[0][0] = MPMIN(1.f, 1.f - val);
s->level[0][1] = MPMAX(0.f, val);
s->level[1][0] = MPMAX(0.f, -val);
s->level[1][1] = MPMIN(1.f, 1.f + val);
}
return AF_OK;
}
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET:
case AF_CONTROL_GET_PAN_BALANCE:
if (s->nch)
return AF_ERROR;
*(float*)arg = s->level[0][1] - s->level[1][0];
@@ -142,32 +107,22 @@ static int control(struct af_instance* af, int cmd, void* arg)
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
if(af->data)
free(af->data->audio);
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
struct mp_audio* l = af->data; // Local data
af_pan_t* s = af->setup; // Setup for this instance
float* in = c->audio; // Input audio data
af_pan_t* s = af->priv; // Setup for this instance
float* in = c->planes[0]; // Input audio data
float* out = NULL; // Output audio data
float* end = in+c->len/4; // End of loop
float* end = in+c->samples*c->nch; // End of loop
int nchi = c->nch; // Number of input channels
int ncho = l->nch; // Number of output channels
register int j,k;
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
mp_audio_realloc_min(af->data, data->samples);
out = l->audio;
out = l->planes[0];
// Execute panning
// FIXME: Too slow
while(in < end){
@@ -183,32 +138,53 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
}
// Set output data
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
c->planes[0] = l->planes[0];
set_channels(c, l->nch);
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_pan_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
return AF_OK;
af->control=control;
af->filter=filter;
af_pan_t *s = af->priv;
int n = 0;
int j,k;
int nch = s->nch;
if(nch && AF_OK != control(af,AF_CONTROL_SET_PAN_NOUT, &nch))
return AF_ERROR;
// Read pan values
char *cp = s->matrixstr;
j = 0; k = 0;
while(cp && k < AF_NCH){
sscanf(cp, "%f%n" , &s->level[j][k], &n);
MP_VERBOSE(af, "[pan] Pan level from channel %i to"
" channel %i = %f\n",k,j,s->level[j][k]);
cp =&cp[n];
j++;
if(j>=nch){
j = 0;
k++;
}
if (*cp != ',')
break;
cp++;
}
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_pan_t
struct af_info af_info_pan = {
"Panning audio filter",
"pan",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "Panning audio filter",
.name = "pan",
.open = af_open,
.priv_size = sizeof(af_pan_t),
.options = (const struct m_option[]) {
OPT_INTRANGE("channels", nch, 0, 0, AF_NCH),
OPT_STRING("matrix", matrixstr, 0),
{0}
},
};
+360 -402
View File
@@ -36,483 +36,441 @@
#include <limits.h>
#include <assert.h>
#include "mpvcore/mp_common.h"
#include "common/common.h"
#include "af.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
// Data for specific instances of this filter
typedef struct af_scaletempo_s
{
// stride
float scale;
float speed;
float frames_stride_scaled;
float frames_stride_error;
int bytes_per_frame;
int bytes_stride;
float bytes_stride_scaled;
int bytes_queue;
int bytes_queued;
int bytes_to_slide;
int8_t* buf_queue;
// overlap
int samples_overlap;
int samples_standing;
int bytes_overlap;
int bytes_standing;
void* buf_overlap;
void* table_blend;
void (*output_overlap)(struct af_scaletempo_s* s, void* out_buf, int bytes_off);
// best overlap
int frames_search;
int num_channels;
void* buf_pre_corr;
void* table_window;
int (*best_overlap_offset)(struct af_scaletempo_s* s);
// command line
float scale_nominal;
float ms_stride;
float percent_overlap;
float ms_search;
int speed_opt;
short speed_tempo;
short speed_pitch;
// stride
float scale;
float speed;
int frames_stride;
float frames_stride_scaled;
float frames_stride_error;
int bytes_per_frame;
int bytes_stride;
int bytes_queue;
int bytes_queued;
int bytes_to_slide;
int8_t *buf_queue;
// overlap
int samples_overlap;
int samples_standing;
int bytes_overlap;
int bytes_standing;
void *buf_overlap;
void *table_blend;
void (*output_overlap)(struct af_scaletempo_s *s, void *out_buf,
int bytes_off);
// best overlap
int frames_search;
int num_channels;
void *buf_pre_corr;
void *table_window;
int (*best_overlap_offset)(struct af_scaletempo_s *s);
// command line
float scale_nominal;
float ms_stride;
float percent_overlap;
float ms_search;
int speed_opt;
short speed_tempo;
short speed_pitch;
} af_scaletempo_t;
static int fill_queue(struct af_instance* af, struct mp_audio* data, int offset)
static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset)
{
af_scaletempo_t* s = af->priv;
int bytes_in = data->len - offset;
int offset_unchanged = offset;
af_scaletempo_t *s = af->priv;
int bytes_in = mp_audio_psize(data) - offset;
int offset_unchanged = offset;
if (s->bytes_to_slide > 0) {
if (s->bytes_to_slide < s->bytes_queued) {
int bytes_move = s->bytes_queued - s->bytes_to_slide;
memmove(s->buf_queue,
s->buf_queue + s->bytes_to_slide,
bytes_move);
s->bytes_to_slide = 0;
s->bytes_queued = bytes_move;
} else {
int bytes_skip;
s->bytes_to_slide -= s->bytes_queued;
bytes_skip = MPMIN(s->bytes_to_slide, bytes_in);
s->bytes_queued = 0;
s->bytes_to_slide -= bytes_skip;
offset += bytes_skip;
bytes_in -= bytes_skip;
if (s->bytes_to_slide > 0) {
if (s->bytes_to_slide < s->bytes_queued) {
int bytes_move = s->bytes_queued - s->bytes_to_slide;
memmove(s->buf_queue, s->buf_queue + s->bytes_to_slide, bytes_move);
s->bytes_to_slide = 0;
s->bytes_queued = bytes_move;
} else {
int bytes_skip;
s->bytes_to_slide -= s->bytes_queued;
bytes_skip = MPMIN(s->bytes_to_slide, bytes_in);
s->bytes_queued = 0;
s->bytes_to_slide -= bytes_skip;
offset += bytes_skip;
bytes_in -= bytes_skip;
}
}
}
if (bytes_in > 0) {
int bytes_copy = MPMIN(s->bytes_queue - s->bytes_queued, bytes_in);
assert(bytes_copy >= 0);
memcpy(s->buf_queue + s->bytes_queued,
(int8_t*)data->audio + offset,
bytes_copy);
s->bytes_queued += bytes_copy;
offset += bytes_copy;
}
if (bytes_in > 0) {
int bytes_copy = MPMIN(s->bytes_queue - s->bytes_queued, bytes_in);
assert(bytes_copy >= 0);
memcpy(s->buf_queue + s->bytes_queued,
(int8_t *)data->planes[0] + offset, bytes_copy);
s->bytes_queued += bytes_copy;
offset += bytes_copy;
}
return offset - offset_unchanged;
return offset - offset_unchanged;
}
#define UNROLL_PADDING (4*4)
#define UNROLL_PADDING (4 * 4)
static int best_overlap_offset_float(af_scaletempo_t* s)
static int best_overlap_offset_float(af_scaletempo_t *s)
{
float *pw, *po, *ppc, *search_start;
float best_corr = INT_MIN;
int best_off = 0;
int i, off;
float best_corr = INT_MIN;
int best_off = 0;
pw = s->table_window;
po = s->buf_overlap;
po += s->num_channels;
ppc = s->buf_pre_corr;
for (i=s->num_channels; i<s->samples_overlap; i++) {
*ppc++ = *pw++ * *po++;
}
float *pw = s->table_window;
float *po = s->buf_overlap;
po += s->num_channels;
float *ppc = s->buf_pre_corr;
for (int i = s->num_channels; i < s->samples_overlap; i++)
*ppc++ = *pw++ **po++;
search_start = (float*)s->buf_queue + s->num_channels;
for (off=0; off<s->frames_search; off++) {
float corr = 0;
float* ps = search_start;
ppc = s->buf_pre_corr;
for (i=s->num_channels; i<s->samples_overlap; i++) {
corr += *ppc++ * *ps++;
float *search_start = (float *)s->buf_queue + s->num_channels;
for (int off = 0; off < s->frames_search; off++) {
float corr = 0;
float *ps = search_start;
ppc = s->buf_pre_corr;
for (int i = s->num_channels; i < s->samples_overlap; i++)
corr += *ppc++ **ps++;
if (corr > best_corr) {
best_corr = corr;
best_off = off;
}
search_start += s->num_channels;
}
if (corr > best_corr) {
best_corr = corr;
best_off = off;
}
search_start += s->num_channels;
}
return best_off * 4 * s->num_channels;
return best_off * 4 * s->num_channels;
}
static int best_overlap_offset_s16(af_scaletempo_t* s)
static int best_overlap_offset_s16(af_scaletempo_t *s)
{
int32_t *pw, *ppc;
int16_t *po, *search_start;
int64_t best_corr = INT64_MIN;
int best_off = 0;
int off;
long i;
int64_t best_corr = INT64_MIN;
int best_off = 0;
pw = s->table_window;
po = s->buf_overlap;
po += s->num_channels;
ppc = s->buf_pre_corr;
for (i=s->num_channels; i<s->samples_overlap; i++) {
*ppc++ = ( *pw++ * *po++ ) >> 15;
}
int32_t *pw = s->table_window;
int16_t *po = s->buf_overlap;
po += s->num_channels;
int32_t *ppc = s->buf_pre_corr;
for (long i = s->num_channels; i < s->samples_overlap; i++)
*ppc++ = (*pw++ **po++) >> 15;
search_start = (int16_t*)s->buf_queue + s->num_channels;
for (off=0; off<s->frames_search; off++) {
int64_t corr = 0;
int16_t* ps = search_start;
ppc = s->buf_pre_corr;
ppc += s->samples_overlap - s->num_channels;
ps += s->samples_overlap - s->num_channels;
i = -(s->samples_overlap - s->num_channels);
do {
corr += ppc[i+0] * ps[i+0];
corr += ppc[i+1] * ps[i+1];
corr += ppc[i+2] * ps[i+2];
corr += ppc[i+3] * ps[i+3];
i += 4;
} while (i < 0);
if (corr > best_corr) {
best_corr = corr;
best_off = off;
int16_t *search_start = (int16_t *)s->buf_queue + s->num_channels;
for (int off = 0; off < s->frames_search; off++) {
int64_t corr = 0;
int16_t *ps = search_start;
ppc = s->buf_pre_corr;
ppc += s->samples_overlap - s->num_channels;
ps += s->samples_overlap - s->num_channels;
long i = -(s->samples_overlap - s->num_channels);
do {
corr += ppc[i + 0] * ps[i + 0];
corr += ppc[i + 1] * ps[i + 1];
corr += ppc[i + 2] * ps[i + 2];
corr += ppc[i + 3] * ps[i + 3];
i += 4;
} while (i < 0);
if (corr > best_corr) {
best_corr = corr;
best_off = off;
}
search_start += s->num_channels;
}
search_start += s->num_channels;
}
return best_off * 2 * s->num_channels;
return best_off * 2 * s->num_channels;
}
static void output_overlap_float(af_scaletempo_t* s, void* buf_out,
int bytes_off)
static void output_overlap_float(af_scaletempo_t *s, void *buf_out,
int bytes_off)
{
float* pout = buf_out;
float* pb = s->table_blend;
float* po = s->buf_overlap;
float* pin = (float*)(s->buf_queue + bytes_off);
int i;
for (i=0; i<s->samples_overlap; i++) {
*pout++ = *po - *pb++ * ( *po - *pin++ ); po++;
}
float *pout = buf_out;
float *pb = s->table_blend;
float *po = s->buf_overlap;
float *pin = (float *)(s->buf_queue + bytes_off);
for (int i = 0; i < s->samples_overlap; i++) {
*pout++ = *po - *pb++ *(*po - *pin++);
po++;
}
}
static void output_overlap_s16(af_scaletempo_t* s, void* buf_out,
int bytes_off)
static void output_overlap_s16(af_scaletempo_t *s, void *buf_out,
int bytes_off)
{
int16_t* pout = buf_out;
int32_t* pb = s->table_blend;
int16_t* po = s->buf_overlap;
int16_t* pin = (int16_t*)(s->buf_queue + bytes_off);
int i;
for (i=0; i<s->samples_overlap; i++) {
*pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++;
}
int16_t *pout = buf_out;
int32_t *pb = s->table_blend;
int16_t *po = s->buf_overlap;
int16_t *pin = (int16_t *)(s->buf_queue + bytes_off);
for (int i = 0; i < s->samples_overlap; i++) {
*pout++ = *po - ((*pb++ *(*po - *pin++)) >> 16);
po++;
}
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
af_scaletempo_t* s = af->priv;
int offset_in;
int max_bytes_out;
int8_t* pout;
af_scaletempo_t *s = af->priv;
if (s->scale == 1.0) {
af->delay = 0;
return data;
}
// RESIZE_LOCAL_BUFFER - can't use macro
max_bytes_out = ((int)(data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride;
if (max_bytes_out > af->data->len) {
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
"old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out);
af->data->audio = realloc(af->data->audio, max_bytes_out);
if (!af->data->audio) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory\n");
return NULL;
if (s->scale == 1.0) {
af->delay = 0;
return 0;
}
af->data->len = max_bytes_out;
}
offset_in = fill_queue(af, data, 0);
pout = af->data->audio;
while (s->bytes_queued >= s->bytes_queue) {
int ti;
float tf;
int bytes_off = 0;
mp_audio_realloc_min(af->data,
((int)(data->samples / s->frames_stride_scaled) + 1) * s->frames_stride);
// output stride
if (s->output_overlap) {
if (s->best_overlap_offset)
bytes_off = s->best_overlap_offset(s);
s->output_overlap(s, pout, bytes_off);
int offset_in = fill_queue(af, data, 0);
int8_t *pout = af->data->planes[0];
while (s->bytes_queued >= s->bytes_queue) {
int ti;
float tf;
int bytes_off = 0;
// output stride
if (s->output_overlap) {
if (s->best_overlap_offset)
bytes_off = s->best_overlap_offset(s);
s->output_overlap(s, pout, bytes_off);
}
memcpy(pout + s->bytes_overlap,
s->buf_queue + bytes_off + s->bytes_overlap,
s->bytes_standing);
pout += s->bytes_stride;
// input stride
memcpy(s->buf_overlap,
s->buf_queue + bytes_off + s->bytes_stride,
s->bytes_overlap);
tf = s->frames_stride_scaled + s->frames_stride_error;
ti = (int)tf;
s->frames_stride_error = tf - ti;
s->bytes_to_slide = ti * s->bytes_per_frame;
offset_in += fill_queue(af, data, offset_in);
}
memcpy(pout + s->bytes_overlap,
s->buf_queue + bytes_off + s->bytes_overlap,
s->bytes_standing);
pout += s->bytes_stride;
// input stride
memcpy(s->buf_overlap,
s->buf_queue + bytes_off + s->bytes_stride,
s->bytes_overlap);
tf = s->frames_stride_scaled + s->frames_stride_error;
ti = (int)tf;
s->frames_stride_error = tf - ti;
s->bytes_to_slide = ti * s->bytes_per_frame;
// This filter can have a negative delay when scale > 1:
// output corresponding to some length of input can be decided and written
// after receiving only a part of that input.
af->delay = (s->bytes_queued - s->bytes_to_slide) / s->scale
/ af->data->sstride / af->data->rate;
offset_in += fill_queue(af, data, offset_in);
}
// This filter can have a negative delay when scale > 1:
// output corresponding to some length of input can be decided and written
// after receiving only a part of that input.
af->delay = s->bytes_queued - s->bytes_to_slide;
data->audio = af->data->audio;
data->len = pout - (int8_t *)af->data->audio;
return data;
data->planes[0] = af->data->planes[0];
data->samples = (pout - (int8_t *)af->data->planes[0]) / af->data->sstride;
return 0;
}
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
static int control(struct af_instance *af, int cmd, void *arg)
{
af_scaletempo_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
struct mp_audio* data = (struct mp_audio*)arg;
float srate = data->rate / 1000;
int nch = data->nch;
int bps;
int use_int = 0;
int frames_stride, frames_overlap;
int i, j;
af_scaletempo_t *s = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *data = (struct mp_audio *)arg;
float srate = data->rate / 1000;
int nch = data->nch;
int use_int = 0;
mp_msg(MSGT_AFILTER, MSGL_V,
"[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
s->speed, s->scale_nominal, s->scale);
MP_VERBOSE(af, "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
s->speed, s->scale_nominal, s->scale);
mp_audio_copy_config(af->data, data);
mp_audio_force_interleaved_format(data);
mp_audio_copy_config(af->data, data);
if (s->scale == 1.0) {
if (s->speed_tempo && s->speed_pitch)
return AF_DETACH;
af->delay = 0;
af->mul = 1;
return af_test_output(af, data);
}
if (data->format == AF_FORMAT_S16_NE) {
use_int = 1;
} else {
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
bps = af->data->bps;
frames_stride = srate * s->ms_stride;
s->bytes_stride = frames_stride * bps * nch;
s->bytes_stride_scaled = s->scale * s->bytes_stride;
s->frames_stride_scaled = s->scale * frames_stride;
s->frames_stride_error = 0;
af->mul = (double)s->bytes_stride / s->bytes_stride_scaled;
af->delay = 0;
frames_overlap = frames_stride * s->percent_overlap;
if (frames_overlap <= 0) {
s->bytes_standing = s->bytes_stride;
s->samples_standing = s->bytes_standing / bps;
s->output_overlap = NULL;
s->bytes_overlap = 0;
} else {
s->samples_overlap = frames_overlap * nch;
s->bytes_overlap = frames_overlap * nch * bps;
s->bytes_standing = s->bytes_stride - s->bytes_overlap;
s->samples_standing = s->bytes_standing / bps;
s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
if(!s->buf_overlap || !s->table_blend) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
memset(s->buf_overlap, 0, s->bytes_overlap);
if (use_int) {
int32_t* pb = s->table_blend;
int64_t blend = 0;
for (i=0; i<frames_overlap; i++) {
int32_t v = blend / frames_overlap;
for (j=0; j<nch; j++) {
*pb++ = v;
}
blend += 65536; // 2^16
if (s->scale == 1.0) {
if (s->speed_tempo && s->speed_pitch)
return AF_DETACH;
af->delay = 0;
af->mul = 1;
return af_test_output(af, data);
}
s->output_overlap = output_overlap_s16;
} else {
float* pb = s->table_blend;
for (i=0; i<frames_overlap; i++) {
float v = i / (float)frames_overlap;
for (j=0; j<nch; j++) {
*pb++ = v;
}
if (data->format == AF_FORMAT_S16) {
use_int = 1;
} else {
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
}
s->output_overlap = output_overlap_float;
}
int bps = af->data->bps;
s->frames_stride = srate * s->ms_stride;
s->bytes_stride = s->frames_stride * bps * nch;
s->frames_stride_scaled = s->scale * s->frames_stride;
s->frames_stride_error = 0;
af->mul = 1.0 / s->scale;
af->delay = 0;
int frames_overlap = s->frames_stride * s->percent_overlap;
if (frames_overlap <= 0) {
s->bytes_standing = s->bytes_stride;
s->samples_standing = s->bytes_standing / bps;
s->output_overlap = NULL;
s->bytes_overlap = 0;
} else {
s->samples_overlap = frames_overlap * nch;
s->bytes_overlap = frames_overlap * nch * bps;
s->bytes_standing = s->bytes_stride - s->bytes_overlap;
s->samples_standing = s->bytes_standing / bps;
s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
if (!s->buf_overlap || !s->table_blend) {
MP_FATAL(af, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
memset(s->buf_overlap, 0, s->bytes_overlap);
if (use_int) {
int32_t *pb = s->table_blend;
int64_t blend = 0;
for (int i = 0; i < frames_overlap; i++) {
int32_t v = blend / frames_overlap;
for (int j = 0; j < nch; j++)
*pb++ = v;
blend += 65536; // 2^16
}
s->output_overlap = output_overlap_s16;
} else {
float *pb = s->table_blend;
for (int i = 0; i < frames_overlap; i++) {
float v = i / (float)frames_overlap;
for (int j = 0; j < nch; j++)
*pb++ = v;
}
s->output_overlap = output_overlap_float;
}
}
s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
if (s->frames_search <= 0)
s->best_overlap_offset = NULL;
else {
if (use_int) {
int64_t t = frames_overlap;
int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
s->buf_pre_corr = realloc(s->buf_pre_corr,
s->bytes_overlap * 2 + UNROLL_PADDING);
s->table_window = realloc(s->table_window,
s->bytes_overlap * 2 - nch * bps * 2);
if (!s->buf_pre_corr || !s->table_window) {
MP_FATAL(af, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0,
UNROLL_PADDING);
int32_t *pw = s->table_window;
for (int i = 1; i < frames_overlap; i++) {
int32_t v = (i * (t - i) * n) >> 15;
for (int j = 0; j < nch; j++)
*pw++ = v;
}
s->best_overlap_offset = best_overlap_offset_s16;
} else {
s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
s->table_window = realloc(s->table_window,
s->bytes_overlap - nch * bps);
if (!s->buf_pre_corr || !s->table_window) {
MP_FATAL(af, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
float *pw = s->table_window;
for (int i = 1; i < frames_overlap; i++) {
float v = i * (frames_overlap - i);
for (int j = 0; j < nch; j++)
*pw++ = v;
}
s->best_overlap_offset = best_overlap_offset_float;
}
}
s->bytes_per_frame = bps * nch;
s->num_channels = nch;
s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap)
* bps * nch;
s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING);
if (!s->buf_queue) {
MP_FATAL(af, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
s->bytes_queued = 0;
s->bytes_to_slide = 0;
MP_DBG(af, "[scaletempo] "
"%.2f stride_in, %i stride_out, %i standing, "
"%i overlap, %i search, %i queue, %s mode\n",
s->frames_stride_scaled,
(int)(s->bytes_stride / nch / bps),
(int)(s->bytes_standing / nch / bps),
(int)(s->bytes_overlap / nch / bps),
s->frames_search,
(int)(s->bytes_queue / nch / bps),
(use_int ? "s16" : "float"));
return af_test_output(af, (struct mp_audio *)arg);
}
s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
if (s->frames_search <= 0) {
s->best_overlap_offset = NULL;
} else {
if (use_int) {
int64_t t = frames_overlap;
int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
int32_t* pw;
s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap * 2 + UNROLL_PADDING);
s->table_window = realloc(s->table_window, s->bytes_overlap * 2 - nch * bps * 2);
if(!s->buf_pre_corr || !s->table_window) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
return AF_ERROR;
case AF_CONTROL_SET_PLAYBACK_SPEED: {
if (s->speed_tempo) {
if (s->speed_pitch)
break;
s->speed = *(double *)arg;
s->scale = s->speed * s->scale_nominal;
} else {
if (s->speed_pitch) {
s->speed = 1 / *(double *)arg;
s->scale = s->speed * s->scale_nominal;
break;
}
}
memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, UNROLL_PADDING);
pw = s->table_window;
for (i=1; i<frames_overlap; i++) {
int32_t v = ( i * (t - i) * n ) >> 15;
for (j=0; j<nch; j++) {
*pw++ = v;
}
}
s->best_overlap_offset = best_overlap_offset_s16;
} else {
float* pw;
s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
s->table_window = realloc(s->table_window, s->bytes_overlap - nch * bps);
if(!s->buf_pre_corr || !s->table_window) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
return AF_ERROR;
}
pw = s->table_window;
for (i=1; i<frames_overlap; i++) {
float v = i * (frames_overlap - i);
for (j=0; j<nch; j++) {
*pw++ = v;
}
}
s->best_overlap_offset = best_overlap_offset_float;
}
return AF_OK;
}
s->bytes_per_frame = bps * nch;
s->num_channels = nch;
s->bytes_queue
= (s->frames_search + frames_stride + frames_overlap) * bps * nch;
s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING);
if(!s->buf_queue) {
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
return AF_ERROR;
case AF_CONTROL_RESET:
s->bytes_queued = 0;
s->bytes_to_slide = 0;
s->frames_stride_error = 0;
memset(s->buf_overlap, 0, s->bytes_overlap);
}
s->bytes_queued = 0;
s->bytes_to_slide = 0;
mp_msg (MSGT_AFILTER, MSGL_DBG2, "[scaletempo] "
"%.2f stride_in, %i stride_out, %i standing, "
"%i overlap, %i search, %i queue, %s mode\n",
s->frames_stride_scaled,
(int)(s->bytes_stride / nch / bps),
(int)(s->bytes_standing / nch / bps),
(int)(s->bytes_overlap / nch / bps),
s->frames_search,
(int)(s->bytes_queue / nch / bps),
(use_int?"s16":"float"));
return af_test_output(af, (struct mp_audio*)arg);
}
case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:{
if (s->speed_tempo) {
if (s->speed_pitch) {
break;
}
s->speed = *(double *)arg;
s->scale = s->speed * s->scale_nominal;
} else {
if (s->speed_pitch) {
s->speed = 1 / *(double *)arg;
s->scale = s->speed * s->scale_nominal;
break;
}
}
return AF_OK;
}
case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:{
s->scale = *(float*)arg;
s->scale = s->speed * s->scale_nominal;
return AF_OK;
}
case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
*(float*)arg = s->scale;
return AF_OK;
}
return AF_UNKNOWN;
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
static void uninit(struct af_instance *af)
{
af_scaletempo_t* s = af->priv;
free(af->data->audio);
free(af->data);
free(s->buf_queue);
free(s->buf_overlap);
free(s->buf_pre_corr);
free(s->table_blend);
free(s->table_window);
af_scaletempo_t *s = af->priv;
free(s->buf_queue);
free(s->buf_overlap);
free(s->buf_pre_corr);
free(s->table_blend);
free(s->table_window);
}
#define SCALE_TEMPO 1
#define SCALE_PITCH 2
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af_scaletempo_t *s = af->priv;
static int af_open(struct af_instance *af)
{
af_scaletempo_t *s = af->priv;
af->control = control;
af->uninit = uninit;
af->play = play;
af->mul = 1;
af->data = calloc(1,sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
af->control = control;
af->uninit = uninit;
af->filter = filter;
s->speed_tempo = !!(s->speed_opt & SCALE_TEMPO);
s->speed_pitch = !!(s->speed_opt & SCALE_PITCH);
s->speed_tempo = !!(s->speed_opt & SCALE_TEMPO);
s->speed_pitch = !!(s->speed_opt & SCALE_PITCH);
s->scale = s->speed * s->scale_nominal;
mp_msg(MSGT_AFILTER, MSGL_DBG2, "[scaletempo] %6.3f scale, %6.2f stride, %6.2f overlap, %6.2f search, speed = %s\n", s->scale_nominal, s->ms_stride, s->percent_overlap, s->ms_search, (s->speed_tempo?(s->speed_pitch?"tempo and speed":"tempo"):(s->speed_pitch?"pitch":"none")));
return AF_OK;
s->scale = s->speed * s->scale_nominal;
return AF_OK;
}
#define OPT_BASE_STRUCT af_scaletempo_t
struct af_info af_info_scaletempo = {
"Scale audio tempo while maintaining pitch",
"scaletempo",
"Robert Juliano",
"",
AF_FLAGS_REENTRANT,
af_open,
.info = "Scale audio tempo while maintaining pitch",
.name = "scaletempo",
.open = af_open,
.priv_size = sizeof(af_scaletempo_t),
.priv_defaults = &(const af_scaletempo_t) {
.ms_stride = 60,
+22 -44
View File
@@ -41,14 +41,12 @@ typedef struct af_sinesuppress_s
double pos;
}af_sinesuppress_t;
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data);
static int play_s16(struct af_instance* af, struct mp_audio* data, int f);
//static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data);
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_sinesuppress_t* s = (af_sinesuppress_t*)af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
// Sanity check
@@ -57,45 +55,31 @@ static int control(struct af_instance* af, int cmd, void* arg)
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_num_channels(af->data, 1);
#if 0
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT)
{
af->data->format = AF_FORMAT_FLOAT_NE;
af->data->format = AF_FORMAT_FLOAT;
af->data->bps = 4;
af->play = play_float;
}// else
#endif
{
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = play_s16;
mp_audio_set_format(af->data, AF_FORMAT_S16);
af->filter = play_s16;
}
return af_test_output(af,(struct mp_audio*)arg);
}
case AF_CONTROL_COMMAND_LINE:{
float f1,f2;
sscanf((char*)arg,"%f:%f", &f1,&f2);
s->freq = f1;
s->decay = f2;
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
{
af_sinesuppress_t *s = af->setup;
af_sinesuppress_t *s = af->priv;
register int i = 0;
int16_t *a = (int16_t*)data->audio; // Audio data
int len = data->len/2; // Number of samples
int16_t *a = (int16_t*)data->planes[0]; // Audio data
int len = data->samples*data->nch; // Number of samples
for (i = 0; i < len; i++)
{
@@ -115,9 +99,9 @@ static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
s->pos += 2 * M_PI * s->freq / data->rate;
}
mp_msg(MSGT_AFILTER, MSGL_V, "[sinesuppress] f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref);
MP_VERBOSE(af, "[sinesuppress] f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref);
return data;
return 0;
}
#if 0
@@ -147,25 +131,19 @@ static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play_s16;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_sinesuppress_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
((af_sinesuppress_t*)af->setup)->freq = 50.0;
((af_sinesuppress_t*)af->setup)->decay = 0.0001;
af->filter=play_s16;
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_sinesuppress_t
struct af_info af_info_sinesuppress = {
"Sine Suppress",
"sinesuppress",
"Michael Niedermayer",
"",
0,
af_open
.info = "Sine Suppress",
.name = "sinesuppress",
.open = af_open,
.priv_size = sizeof(af_sinesuppress_t),
.options = (const struct m_option[]) {
OPT_DOUBLE("freq", freq, 0, OPTDEF_DOUBLE(50.0)),
OPT_DOUBLE("decay", decay, 0, OPTDEF_DOUBLE(0.0001)),
{0}
},
};
+21 -66
View File
@@ -32,6 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
#include "dsp.h"
@@ -62,7 +63,7 @@ typedef struct af_sub_s
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_sub_t* s = af->setup;
af_sub_t* s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -70,8 +71,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
mp_audio_set_channels_old(af->data, max(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
// Design low-pass filter
s->k = 1.0;
@@ -82,50 +83,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
return AF_ERROR;
return af_test_output(af,(struct mp_audio*)arg);
}
case AF_CONTROL_COMMAND_LINE:{
int ch=5;
float fc=60.0;
sscanf(arg,"%f:%i", &fc , &ch);
if(AF_OK != control(af,AF_CONTROL_SUB_CH | AF_CONTROL_SET, &ch))
return AF_ERROR;
return control(af,AF_CONTROL_SUB_FC | AF_CONTROL_SET, &fc);
}
case AF_CONTROL_SUB_CH | AF_CONTROL_SET: // Requires reinit
// Sanity check
if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Subwoofer channel number must be between "
" 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg);
return AF_ERROR;
}
s->ch = *(int*)arg;
return AF_OK;
case AF_CONTROL_SUB_CH | AF_CONTROL_GET:
*(int*)arg = s->ch;
return AF_OK;
case AF_CONTROL_SUB_FC | AF_CONTROL_SET: // Requires reinit
// Sanity check
if((*(float*)arg > 300) || (*(float*)arg < 20)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Cutoff frequency must be between 20Hz and"
" 300Hz current value is %0.2f",*(float*)arg);
return AF_ERROR;
}
// Set cutoff frequency
s->fc = *(float*)arg;
return AF_OK;
case AF_CONTROL_SUB_FC | AF_CONTROL_GET:
*(float*)arg = s->fc;
return AF_OK;
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
#ifndef IIR
#define IIR(in,w,q,out) { \
float h0 = (q)[0]; \
@@ -138,12 +99,12 @@ static void uninit(struct af_instance* af)
#endif
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
{
struct mp_audio* c = data; // Current working data
af_sub_t* s = af->setup; // Setup for this instance
float* a = c->audio; // Audio data
int len = c->len/4; // Number of samples in current audio block
af_sub_t* s = af->priv; // Setup for this instance
float* a = c->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples in current audio block
int nch = c->nch; // Number of channels
int ch = s->ch; // Channel in which to insert the sub audio
register int i;
@@ -156,32 +117,26 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
IIR(x , s->w[1], s->q[1], a[i+ch]);
}
return c;
return 0;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af_sub_t* s;
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=s=calloc(1,sizeof(af_sub_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
// Set default values
s->ch = 5; // Channel nr 6
s->fc = 60; // Cutoff frequency 60Hz
af->filter=filter;
return AF_OK;
}
// Description of this filter
#define OPT_BASE_STRUCT af_sub_t
struct af_info af_info_sub = {
"Audio filter for adding a sub-base channel",
"sub",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Audio filter for adding a sub-base channel",
.name = "sub",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_sub_t),
.options = (const struct m_option[]) {
OPT_FLOATRANGE("fc", fc, 0, 20, 300, OPTDEF_FLOAT(60.0)),
OPT_INTRANGE("ch", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(5)),
{0}
},
};
+31 -59
View File
@@ -88,22 +88,24 @@ typedef struct af_surround_s
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_surround_t *s = af->setup;
af_surround_t *s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:{
struct mp_audio *in = arg;
float fc;
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch*2);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
if (af->data->nch != 4){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Only stereo input is supported.\n");
return AF_DETACH;
if (!mp_chmap_is_stereo(&in->channels)) {
MP_ERR(af, "[surround] Only stereo input is supported.\n");
return AF_DETACH;
}
mp_audio_set_format(in, AF_FORMAT_FLOAT);
mp_audio_copy_config(af->data, in);
mp_audio_set_channels_old(af->data, in->nch * 2);
// Surround filer coefficients
fc = 2.0 * 7000.0/(float)af->data->rate;
if (-1 == af_filter_design_fir(L, s->w, &fc, LP|HAMMING, 0)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Unable to design low-pass filter.\n");
MP_ERR(af, "[surround] Unable to design low-pass filter.\n");
return AF_ERROR;
}
@@ -114,7 +116,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->dl = calloc(LD,af->data->bps);
s->dr = calloc(LD,af->data->bps);
if((NULL == s->dl) || (NULL == s->dr))
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
MP_FATAL(af, "[delay] Out of memory\n");
// Initialize delay queue index
if(AF_OK != af_from_ms(1, &s->d, &s->wi, af->data->rate, 0.0, 1000.0))
@@ -122,37 +124,12 @@ static int control(struct af_instance* af, int cmd, void* arg)
// printf("%i\n",s->wi);
s->ri = 0;
if((af->data->format != ((struct mp_audio*)arg)->format) ||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
mp_audio_set_format((struct mp_audio*)arg, af->data->format);
return AF_FALSE;
}
return AF_OK;
}
case AF_CONTROL_COMMAND_LINE:{
float d = 0;
sscanf((char*)arg,"%f",&d);
if ((d < 0) || (d > 1000)){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Invalid delay time, valid time values"
" are 0ms to 1000ms current value is %0.3f ms\n",d);
return AF_ERROR;
}
s->d = d;
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
if(af->data)
free(af->data->audio);
free(af->data);
free(af->setup);
}
// The beginnings of an active matrix...
static float steering_matrix[][12] = {
// LL RL LR RR LS RS
@@ -165,20 +142,19 @@ static float steering_matrix[][12] = {
//static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0;
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
af_surround_t* s = (af_surround_t*)af->setup;
static int filter(struct af_instance* af, struct mp_audio* data, int flags){
af_surround_t* s = (af_surround_t*)af->priv;
float* m = steering_matrix[0];
float* in = data->audio; // Input audio data
float* in = data->planes[0]; // Input audio data
float* out = NULL; // Output audio data
float* end = in + data->len / sizeof(float); // Loop end
float* end = in + data->samples * data->nch;
int i = s->i; // Filter queue index
int ri = s->ri; // Read index for delay queue
int wi = s->wi; // Write index for delay queue
if (AF_OK != RESIZE_LOCAL_BUFFER(af, data))
return NULL;
mp_audio_realloc_min(af->data, data->samples);
out = af->data->audio;
out = af->data->planes[0];
while(in < end){
/* Dominance:
@@ -240,32 +216,28 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
s->i = i; s->ri = ri; s->wi = wi;
// Set output data
data->audio = af->data->audio;
data->len *= 2;
data->planes[0] = af->data->planes[0];
mp_audio_set_channels_old(data, af->data->nch);
return data;
return 0;
}
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=2;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_surround_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
((af_surround_t*)af->setup)->d = 20;
af->filter=filter;
return AF_OK;
}
#define OPT_BASE_STRUCT af_surround_t
struct af_info af_info_surround =
{
"Surround decoder filter",
"surround",
"Steve Davies <steve@daviesfam.org>",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Surround decoder filter",
.name = "surround",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(af_surround_t),
.options = (const struct m_option[]) {
OPT_FLOATRANGE("d", d, 0, 0, 1000, OPTDEF_FLOAT(20.0)),
{0}
},
};
+16 -31
View File
@@ -36,40 +36,26 @@ typedef struct af_sweep_s{
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_sweept* s = (af_sweept*)af->setup;
struct mp_audio *data= (struct mp_audio*)arg;
switch(cmd){
case AF_CONTROL_REINIT:
mp_audio_copy_config(af->data, data);
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
mp_audio_set_format(af->data, AF_FORMAT_S16);
return af_test_output(af, data);
case AF_CONTROL_COMMAND_LINE:
sscanf((char*)arg,"%lf", &s->delta);
return AF_OK;
/* case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
af->data->rate = *(int*)arg;
return AF_OK;*/
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
static int filter(struct af_instance* af, struct mp_audio* data, int f)
{
af_sweept *s = af->setup;
af_sweept *s = af->priv;
int i, j;
int16_t *in = (int16_t*)data->audio;
int16_t *in = (int16_t*)data->planes[0];
int chans = data->nch;
int in_len = data->len/(2*chans);
int in_len = data->samples;
for(i=0; i<in_len; i++){
for(j=0; j<chans; j++)
@@ -78,24 +64,23 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
if(2*s->x*s->delta >= 3.141592) s->x=0;
}
return data;
return 0;
}
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_sweept));
af->filter=filter;
return AF_OK;
}
#define OPT_BASE_STRUCT af_sweept
struct af_info af_info_sweep = {
"sine sweep",
"sweep",
"Michael Niedermayer",
"",
AF_FLAGS_REENTRANT,
af_open
.info = "sine sweep",
.name = "sweep",
.open = af_open,
.priv_size = sizeof(af_sweept),
.options = (const struct m_option[]) {
OPT_DOUBLE("delta", delta, 0),
{0}
},
};
+83 -165
View File
@@ -18,22 +18,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* This audio filter changes the volume of the sound, and can be used
when the mixer doesn't support the PCM channel. It can handle
between 1 and AF_NCH channels. The volume can be adjusted between -60dB
to +20dB and is set on a per channels basis. The is accessed through
AF_CONTROL_VOLUME_LEVEL.
The filter has support for soft-clipping, it is enabled by
AF_CONTROL_VOLUME_SOFTCLIPP. It has also a probing feature which
can be used to measure the power in the audio stream, both an
instantaneous value and the maximum value can be probed. The
probing is enable by AF_CONTROL_VOLUME_PROBE_ON_OFF and is done on a
per channel basis. The result from the probing is obtained using
AF_CONTROL_VOLUME_PROBE_GET and AF_CONTROL_VOLUME_PROBE_GET_MAX. The
probed values are calculated in dB.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -42,167 +26,101 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
typedef struct af_volume_s
struct priv {
float level; // Gain level for each channel
int soft; // Enable/disable soft clipping
int fast; // Use fix-point volume control
float cfg_volume;
};
static int control(struct af_instance *af, int cmd, void *arg)
{
int enable[AF_NCH]; // Enable/disable / channel
float pow[AF_NCH]; // Estimated power level [dB]
float max[AF_NCH]; // Max Power level [dB]
float level[AF_NCH]; // Gain level for each channel
float time; // Forgetting factor for power estimate
int soft; // Enable/disable soft clipping
int fast; // Use fix-point volume control
}af_volume_t;
struct priv *s = af->priv;
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
mp_audio_copy_config(af->data, in);
mp_audio_force_interleaved_format(af->data);
if (s->fast && af_fmt_from_planar(in->format) != AF_FORMAT_FLOAT) {
mp_audio_set_format(af->data, AF_FORMAT_S16);
} else {
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
}
if (af_fmt_is_planar(in->format))
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
return af_test_output(af, in);
}
case AF_CONTROL_SET_VOLUME:
s->level = *(float *)arg;
return AF_OK;
case AF_CONTROL_GET_VOLUME:
*(float *)arg = s->level;
return AF_OK;
}
return AF_UNKNOWN;
}
static void filter_plane(struct af_instance *af, void *ptr, int num_samples)
{
af_volume_t* s = (af_volume_t*)af->setup;
struct priv *s = af->priv;
switch(cmd){
case AF_CONTROL_REINIT:
// Sanity check
if(!arg) return AF_ERROR;
if (af_fmt_from_planar(af->data->format) == AF_FORMAT_S16) {
int16_t *a = ptr;
int vol = 256.0 * s->level;
if (vol != 256) {
for (int i = 0; i < num_samples; i++) {
int x = (a[i] * vol) >> 8;
a[i] = MPCLAMP(x, SHRT_MIN, SHRT_MAX);
}
}
} else if (af_fmt_from_planar(af->data->format) == AF_FORMAT_FLOAT) {
float *a = ptr;
float vol = s->level;
if (vol != 1.0) {
for (int i = 0; i < num_samples; i++) {
float x = a[i] * vol;
a[i] = s->soft ? af_softclip(x) : MPCLAMP(x, -1.0, 1.0);
}
}
}
}
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
static int filter(struct af_instance *af, struct mp_audio *data, int f)
{
for (int n = 0; n < data->num_planes; n++)
filter_plane(af, data->planes[n], data->samples * data->spf);
if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
}
else{
// Cutoff set to 10Hz for forgetting factor
float x = 2.0*M_PI*15.0/(float)af->data->rate;
float t = 2.0-cos(x);
s->time = 1.0 - (t - sqrt(t*t - 1));
mp_msg(MSGT_AFILTER, MSGL_DBG2, "[volume] Forgetting factor = %0.5f\n",s->time);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
return af_test_output(af,(struct mp_audio*)arg);
case AF_CONTROL_COMMAND_LINE:{
float v=1000.0;
float vol[AF_NCH];
int i;
sscanf((char*)arg,"%f:%i:%i", &v, &s->soft, &s->fast);
float dest = 0.0;
if (v < 1000)
af_from_dB(1,&v,&dest,20.0,-200.0,60.0);
for(i=0;i<AF_NCH;i++) vol[i]=dest;
return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol);
}
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET:
memcpy(s->level, arg, sizeof(float) * AF_NCH);
return AF_OK;
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET:
memcpy(arg, s->level, sizeof(float) * AF_NCH);
return AF_OK;
case AF_CONTROL_PRE_DESTROY:{
float m = 0.0;
int i;
if(!s->fast){
for(i=0;i<AF_NCH;i++)
m=max(m,s->max[i]);
af_to_dB(1, &m, &m, 10.0);
mp_msg(MSGT_AFILTER, MSGL_V, "[volume] The maximum volume was %0.2fdB \n", m);
}
return 0;
}
static int af_open(struct af_instance *af)
{
struct priv *s = af->priv;
af->control = control;
af->filter = filter;
af_from_dB(1, &s->cfg_volume, &s->level, 20.0, -200.0, 60.0);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
af_volume_t* s = (af_volume_t*)af->setup; // Setup for this instance
register int nch = c->nch; // Number of channels
register int i = 0;
// Basic operation volume control only (used on slow machines)
if(af->data->format == (AF_FORMAT_S16_NE)){
int16_t* a = (int16_t*)c->audio; // Audio data
int len = c->len/2; // Number of samples
for (int ch = 0; ch < nch; ch++) {
int vol = 256.0 * s->level[ch];
if (s->enable[ch] && vol != 256) {
for(i=ch;i<len;i+=nch){
register int x = (a[i] * vol) >> 8;
a[i]=clamp(x,SHRT_MIN,SHRT_MAX);
}
}
}
}
// Machine is fast and data is floating point
else if(af->data->format == (AF_FORMAT_FLOAT_NE)){
float* a = (float*)c->audio; // Audio data
int len = c->len/4; // Number of samples
for (int ch = 0; ch < nch; ch++) {
// Volume control (fader)
if(s->enable[ch]){
float t = 1.0 - s->time;
for(i=ch;i<len;i+=nch){
register float x = a[i];
register float pow = x*x;
// Check maximum power value
if(pow > s->max[ch])
s->max[ch] = pow;
// Set volume
x *= s->level[ch];
// Peak meter
pow = x*x;
if(pow > s->pow[ch])
s->pow[ch] = pow;
else
s->pow[ch] = t*s->pow[ch] + pow*s->time; // LP filter
/* Soft clipping, the sound of a dream, thanks to Jon Wattes
post to Musicdsp.org */
if(s->soft)
x=af_softclip(x);
// Hard clipping
else
x=clamp(x,-1.0,1.0);
a[i] = x;
}
}
}
}
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
int i = 0;
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_volume_t));
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
// Enable volume control and set initial volume to 0dB.
for(i=0;i<AF_NCH;i++){
((af_volume_t*)af->setup)->enable[i] = 1;
((af_volume_t*)af->setup)->level[i] = 1.0;
}
return AF_OK;
}
#define OPT_BASE_STRUCT struct priv
// Description of this filter
struct af_info af_info_volume = {
"Volume control audio filter",
"volume",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
.info = "Volume control audio filter",
.name = "volume",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_FLOATRANGE("volumedb", cfg_volume, 0, -200, 60),
OPT_FLAG("softclip", soft, 0),
OPT_FLAG("s16", fast, 0),
{0}
},
};
-130
View File
@@ -1,130 +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.
*/
#ifndef MPLAYER_CONTROL_H
#define MPLAYER_CONTROL_H
#include <sys/types.h>
/*********************************************
// Extended control used with arguments that operates on only one
// channel at the time
*/
typedef struct af_control_ext_s{
void* arg; // Argument
int ch; // Chanel number
}af_control_ext_t;
/*********************************************
// Control parameters
*/
/* The control system is divided into 3 levels
mandatory calls - all filters must answer to all of these
optional calls - are optional
filter specific calls - applies only to some filters
*/
#define AF_CONTROL_MANDATORY 0x10000000
#define AF_CONTROL_OPTIONAL 0x20000000
#define AF_CONTROL_FILTER_SPECIFIC 0x40000000
// MANDATORY CALLS
/* Reinitialize filter. The optional argument contains the new
configuration in form of a struct mp_audio struct. If the filter does not
support the new format the struct should be changed and AF_FALSE
should be returned. If the incoming and outgoing data streams are
identical the filter can return AF_DETACH. This will remove the
filter. */
#define AF_CONTROL_REINIT 0x00000100 | AF_CONTROL_MANDATORY
// OPTIONAL CALLS
// Called just before destruction of a filter
#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL
/* Commandline parameters. If there were any commandline parameters
for this specific filter, they will be given as a char* in the
argument */
#define AF_CONTROL_COMMAND_LINE 0x00000300 | AF_CONTROL_OPTIONAL
// FILTER SPECIFIC CALLS
// Basic operations: These can be ored with any of the below calls
// Set argument
#define AF_CONTROL_SET 0x00000000
// Get argument
#define AF_CONTROL_GET 0x00000001
// Resample
// Set output rate in resample
#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC
// Format
#define AF_CONTROL_FORMAT_FMT 0x00000400 | AF_CONTROL_FILTER_SPECIFIC
// Channels
// Set number of output channels in channels
#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC
// Volume
// Set volume level, arg is a float* with the volume for all the channels
#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC
// Pan
// Pan levels, arg is a control_ext with a float*
#define AF_CONTROL_PAN_LEVEL 0x00001A00 | AF_CONTROL_FILTER_SPECIFIC
// Number of outputs from pan, arg is int*
#define AF_CONTROL_PAN_NOUT 0x00001B00 | AF_CONTROL_FILTER_SPECIFIC
// Balance, arg is float*; range -1 (left) to 1 (right), 0 center
#define AF_CONTROL_PAN_BALANCE 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC
// Delay length in ms, arg is a control_ext with a float*
#define AF_CONTROL_DELAY_LEN 0x00001E00 | AF_CONTROL_FILTER_SPECIFIC
// Subwoofer
// Channel number which to insert the filtered data, arg in int*
#define AF_CONTROL_SUB_CH 0x00001F00 | AF_CONTROL_FILTER_SPECIFIC
// Cutoff frequency [Hz] for lowpass filter, arg is float*
#define AF_CONTROL_SUB_FC 0x00002000 | AF_CONTROL_FILTER_SPECIFIC
// Export
#define AF_CONTROL_EXPORT_SZ 0x00003000 | AF_CONTROL_FILTER_SPECIFIC
// Channel number which to inster the filtered data, arg in int*
#define AF_CONTROL_CENTER_CH 0x00003200 | AF_CONTROL_FILTER_SPECIFIC
#define AF_CONTROL_PLAYBACK_SPEED 0x00003500 | AF_CONTROL_FILTER_SPECIFIC
#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00003600 | AF_CONTROL_FILTER_SPECIFIC
#endif /* MPLAYER_CONTROL_H */
@@ -18,10 +18,12 @@
#include <math.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
/* Convert to gain value from dB. Returns AF_OK if of and AF_ERROR if
fail */
* fail. input <= -200dB will become 0 gain. */
int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
{
int i = 0;
@@ -33,13 +35,13 @@ int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
if(in[i]<=-200)
out[i]=0.0;
else
out[i]=pow(10.0,clamp(in[i],mi,ma)/k);
out[i]=pow(10.0,MPCLAMP(in[i],mi,ma)/k);
}
return AF_OK;
}
/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
fail */
* fail. gain=0 will become -200 dB. k is just a multiplier. */
int af_to_dB(int n, float* in, float* out, float k)
{
int i = 0;
@@ -65,7 +67,7 @@ int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma)
return AF_ERROR;
for(i=0;i<n;i++)
out[i]=(int)((float)rate * clamp(in[i],mi,ma)/1000.0);
out[i]=(int)((float)rate * MPCLAMP(in[i],mi,ma)/1000.0);
return AF_OK;
}
@@ -84,7 +86,18 @@ int af_to_ms(int n, int* in, float* out, int rate)
return AF_OK;
}
/* Helper function for testing the output format */
/*
* test if output format matches
* af: audio filter
* out: needed format, will be overwritten by available
* format if they do not match
* returns: AF_FALSE if formats do not match, AF_OK if they match
*
* compares the format, rate and nch values of af->data with out
* Note: logically, *out=*af->data always happens, because out contains the
* format only, no actual audio data or memory allocations. *out always
* contains the parameters from af->data after the function returns.
*/
int af_test_output(struct af_instance* af, struct mp_audio* out)
{
if((af->data->format != out->format) ||
+17 -23
View File
@@ -16,7 +16,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mpvcore/mp_msg.h"
#include <libavutil/avutil.h>
#include <libavutil/samplefmt.h>
#include "format.h"
@@ -27,39 +26,34 @@ static const struct {
int fmt;
} audio_conversion_map[] = {
{AV_SAMPLE_FMT_U8, AF_FORMAT_U8},
{AV_SAMPLE_FMT_S16, AF_FORMAT_S16_NE},
{AV_SAMPLE_FMT_S32, AF_FORMAT_S32_NE},
{AV_SAMPLE_FMT_FLT, AF_FORMAT_FLOAT_NE},
{AV_SAMPLE_FMT_DBL, AF_FORMAT_DOUBLE_NE},
{AV_SAMPLE_FMT_S16, AF_FORMAT_S16},
{AV_SAMPLE_FMT_S32, AF_FORMAT_S32},
{AV_SAMPLE_FMT_FLT, AF_FORMAT_FLOAT},
{AV_SAMPLE_FMT_DBL, AF_FORMAT_DOUBLE},
{AV_SAMPLE_FMT_U8P, AF_FORMAT_U8P},
{AV_SAMPLE_FMT_S16P, AF_FORMAT_S16P},
{AV_SAMPLE_FMT_S32P, AF_FORMAT_S32P},
{AV_SAMPLE_FMT_FLTP, AF_FORMAT_FLOATP},
{AV_SAMPLE_FMT_DBLP, AF_FORMAT_DOUBLEP},
{AV_SAMPLE_FMT_NONE, 0},
};
enum AVSampleFormat af_to_avformat(int fmt)
{
int i;
enum AVSampleFormat sample_fmt;
for (i = 0; audio_conversion_map[i].fmt; i++)
for (int i = 0; audio_conversion_map[i].fmt; i++) {
if (audio_conversion_map[i].fmt == fmt)
break;
sample_fmt = audio_conversion_map[i].sample_fmt;
if (sample_fmt == AF_FORMAT_UNKNOWN)
mp_msg(MSGT_GLOBAL, MSGL_V, "Unsupported sample format: %s\n",
af_fmt2str_short(fmt));
return sample_fmt;
return audio_conversion_map[i].sample_fmt;
}
return AV_SAMPLE_FMT_NONE;
}
int af_from_avformat(enum AVSampleFormat sample_fmt)
{
int i;
for (i = 0; audio_conversion_map[i].fmt; i++)
for (int i = 0; audio_conversion_map[i].fmt; i++) {
if (audio_conversion_map[i].sample_fmt == sample_fmt)
break;
int fmt = audio_conversion_map[i].fmt;
if (!fmt) {
const char *fmtname = av_get_sample_fmt_name(sample_fmt);
mp_msg(MSGT_GLOBAL, MSGL_ERR, "Unsupported AVSampleFormat %s (%d)\n",
fmtname ? fmtname : "INVALID", sample_fmt);
return audio_conversion_map[i].fmt;
}
return fmt;
return 0;
}
+164 -54
View File
@@ -23,7 +23,9 @@
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <assert.h>
#include "common/common.h"
#include "audio/filter/af.h"
int af_fmt2bits(int format)
@@ -31,78 +33,132 @@ int af_fmt2bits(int format)
if (AF_FORMAT_IS_AC3(format)) return 16;
if (format == AF_FORMAT_UNKNOWN)
return 0;
switch(format & AF_FORMAT_BITS_MASK)
{
case AF_FORMAT_8BIT: return 8;
case AF_FORMAT_16BIT: return 16;
case AF_FORMAT_24BIT: return 24;
case AF_FORMAT_32BIT: return 32;
case AF_FORMAT_64BIT: return 64;
switch (format & AF_FORMAT_BITS_MASK) {
case AF_FORMAT_8BIT: return 8;
case AF_FORMAT_16BIT: return 16;
case AF_FORMAT_24BIT: return 24;
case AF_FORMAT_32BIT: return 32;
case AF_FORMAT_64BIT: return 64;
}
return 0;
}
/* Convert format to str input str is a buffer for the
converted string, size is the size of the buffer */
char* af_fmt2str(int format, char* str, int size)
static int bits_to_mask(int bits)
{
const char *name = af_fmt2str_short(format);
snprintf(str, size, "%s", name);
return str;
switch (bits) {
case 8: return AF_FORMAT_8BIT;
case 16: return AF_FORMAT_16BIT;
case 24: return AF_FORMAT_24BIT;
case 32: return AF_FORMAT_32BIT;
case 64: return AF_FORMAT_64BIT;
}
return 0;
}
const struct af_fmt_entry af_fmtstr_table[] = {
{ "mpeg2", AF_FORMAT_MPEG2 },
{ "ac3le", AF_FORMAT_AC3_LE },
{ "ac3be", AF_FORMAT_AC3_BE },
{ "ac3ne", AF_FORMAT_AC3_NE },
{ "iec61937le", AF_FORMAT_IEC61937_LE },
{ "iec61937be", AF_FORMAT_IEC61937_BE },
{ "iec61937ne", AF_FORMAT_IEC61937_NE },
int af_fmt_change_bits(int format, int bits)
{
if (!af_fmt_is_valid(format) || (format & AF_FORMAT_SPECIAL_MASK))
return 0;
int mask = bits_to_mask(bits);
format = (format & ~AF_FORMAT_BITS_MASK) | mask;
return af_fmt_is_valid(format) ? format : 0;
}
{ "u8", AF_FORMAT_U8 },
{ "s8", AF_FORMAT_S8 },
{ "u16le", AF_FORMAT_U16_LE },
{ "u16be", AF_FORMAT_U16_BE },
{ "u16ne", AF_FORMAT_U16_NE },
{ "s16le", AF_FORMAT_S16_LE },
{ "s16be", AF_FORMAT_S16_BE },
{ "s16ne", AF_FORMAT_S16_NE },
{ "u24le", AF_FORMAT_U24_LE },
{ "u24be", AF_FORMAT_U24_BE },
{ "u24ne", AF_FORMAT_U24_NE },
{ "s24le", AF_FORMAT_S24_LE },
{ "s24be", AF_FORMAT_S24_BE },
{ "s24ne", AF_FORMAT_S24_NE },
{ "u32le", AF_FORMAT_U32_LE },
{ "u32be", AF_FORMAT_U32_BE },
{ "u32ne", AF_FORMAT_U32_NE },
{ "s32le", AF_FORMAT_S32_LE },
{ "s32be", AF_FORMAT_S32_BE },
{ "s32ne", AF_FORMAT_S32_NE },
{ "floatle", AF_FORMAT_FLOAT_LE },
{ "floatbe", AF_FORMAT_FLOAT_BE },
{ "floatne", AF_FORMAT_FLOAT_NE },
{ "doublele", AF_FORMAT_DOUBLE_LE },
{ "doublebe", AF_FORMAT_DOUBLE_BE },
{ "doublene", AF_FORMAT_DOUBLE_NE },
static const int planar_formats[][2] = {
{AF_FORMAT_U8P, AF_FORMAT_U8},
{AF_FORMAT_S16P, AF_FORMAT_S16},
{AF_FORMAT_S32P, AF_FORMAT_S32},
{AF_FORMAT_FLOATP, AF_FORMAT_FLOAT},
{AF_FORMAT_DOUBLEP, AF_FORMAT_DOUBLE},
};
// Return the planar format corresponding to the given format.
// If the format is already planar, return it.
// Return 0 if there's no equivalent.
int af_fmt_to_planar(int format)
{
for (int n = 0; n < MP_ARRAY_SIZE(planar_formats); n++) {
if (planar_formats[n][1] == format)
return planar_formats[n][0];
if (planar_formats[n][0] == format)
return format;
}
return 0;
}
// Return the interleaved format corresponding to the given format.
// If the format is already interleaved, return it.
// Always succeeds if format is actually planar; otherwise return 0.
int af_fmt_from_planar(int format)
{
for (int n = 0; n < MP_ARRAY_SIZE(planar_formats); n++) {
if (planar_formats[n][0] == format)
return planar_formats[n][1];
}
return format;
}
// false for interleaved and AF_FORMAT_UNKNOWN
bool af_fmt_is_planar(int format)
{
return !!(format & AF_FORMAT_PLANAR);
}
#define FMT(string, id) \
{string, id},
#define FMT_ENDIAN(string, id) \
{string, id}, \
{string "le", MP_CONCAT(id, _LE)}, \
{string "be", MP_CONCAT(id, _BE)}, \
const struct af_fmt_entry af_fmtstr_table[] = {
FMT("mpeg2", AF_FORMAT_MPEG2)
FMT_ENDIAN("ac3", AF_FORMAT_AC3)
FMT_ENDIAN("iec61937", AF_FORMAT_IEC61937)
FMT("u8", AF_FORMAT_U8)
FMT("s8", AF_FORMAT_S8)
FMT_ENDIAN("u16", AF_FORMAT_U16)
FMT_ENDIAN("s16", AF_FORMAT_S16)
FMT_ENDIAN("u24", AF_FORMAT_U24)
FMT_ENDIAN("s24", AF_FORMAT_S24)
FMT_ENDIAN("u32", AF_FORMAT_U32)
FMT_ENDIAN("s32", AF_FORMAT_S32)
FMT_ENDIAN("float", AF_FORMAT_FLOAT)
FMT_ENDIAN("double", AF_FORMAT_DOUBLE)
FMT("u8p", AF_FORMAT_U8P)
FMT("s16p", AF_FORMAT_S16P)
FMT("s32p", AF_FORMAT_S32P)
FMT("floatp", AF_FORMAT_FLOATP)
FMT("doublep", AF_FORMAT_DOUBLEP)
{0}
};
const char *af_fmt2str_short(int format)
bool af_fmt_is_valid(int format)
{
int i;
for (int i = 0; af_fmtstr_table[i].name; i++) {
if (af_fmtstr_table[i].format == format)
return true;
}
return false;
}
for (i = 0; af_fmtstr_table[i].name; i++)
if (af_fmtstr_table[i].format == format)
return af_fmtstr_table[i].name;
const char *af_fmt_to_str(int format)
{
for (int i = 0; af_fmtstr_table[i].name; i++) {
if (af_fmtstr_table[i].format == format)
return af_fmtstr_table[i].name;
}
return "??";
}
int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int samplerate)
{
assert(!af_fmt_is_planar(format));
int bps = (af_fmt2bits(format) / 8);
int framelen = channels * bps;
int bytes = seconds * bps * samplerate;
@@ -113,9 +169,63 @@ int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int sampler
int af_str2fmt_short(bstr str)
{
for (int i = 0; af_fmtstr_table[i].name; i++)
for (int i = 0; af_fmtstr_table[i].name; i++) {
if (!bstrcasecmp0(str, af_fmtstr_table[i].name))
return af_fmtstr_table[i].format;
}
return 0;
}
void af_fill_silence(void *dst, size_t bytes, int format)
{
bool us = (format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_US;
memset(dst, us ? 0x80 : 0, bytes);
}
#define FMT_DIFF(type, a, b) (((a) & type) - ((b) & type))
// Returns a "score" that serves as heuristic how lossy or hard a conversion is.
// If the formats are equal, 1024 is returned. If they are gravely incompatible
// (like s16<->ac3), INT_MIN is returned. If there is implied loss of precision
// (like s16->s8), a value <0 is returned.
int af_format_conversion_score(int dst_format, int src_format)
{
if (dst_format == AF_FORMAT_UNKNOWN || src_format == AF_FORMAT_UNKNOWN)
return INT_MIN;
if (dst_format == src_format)
return 1024;
// Just endian swapping (separate because it works for special formats)
if ((dst_format & ~AF_FORMAT_END_MASK) == (src_format & ~AF_FORMAT_END_MASK))
return 1024 - 2;
// Can't be normally converted
if (AF_FORMAT_IS_SPECIAL(dst_format) || AF_FORMAT_IS_SPECIAL(src_format))
return INT_MIN;
int score = 1024;
if (FMT_DIFF(AF_FORMAT_INTERLEAVING_MASK, dst_format, src_format))
score -= 1; // has to (de-)planarize
if (FMT_DIFF(AF_FORMAT_END_MASK, dst_format, src_format))
score -= 2; // has to swap endian
if (FMT_DIFF(AF_FORMAT_SIGN_MASK, dst_format, src_format))
score -= 4; // has to swap sign
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format)) {
int dst_bits = dst_format & AF_FORMAT_BITS_MASK;
if ((dst_format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
// For int->float, always prefer 32 bit float.
score -= dst_bits == AF_FORMAT_32BIT ? 8 : 0;
} else {
// For float->int, always prefer highest bit depth int
score -= 8 * (AF_FORMAT_64BIT - dst_bits);
}
} else {
int bits = FMT_DIFF(AF_FORMAT_BITS_MASK, dst_format, src_format);
if (bits > 0) {
score -= 8 * bits; // has to add padding
} else if (bits < 0) {
score -= 1024 - 8 * bits; // has to reduce bit depth
}
}
// Consider this the worst case.
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format))
score -= 2048; // has to convert float<->int
return score;
}
+102 -73
View File
@@ -23,104 +23,123 @@
#ifndef MPLAYER_AF_FORMAT_H
#define MPLAYER_AF_FORMAT_H
#include <stdbool.h>
#include <sys/types.h>
#include "config.h"
#include "mpvcore/bstr.h"
// Endianness
#define AF_FORMAT_BE (0<<0) // Big Endian
#define AF_FORMAT_LE (1<<0) // Little Endian
#define AF_FORMAT_END_MASK (1<<0)
#include "bstr/bstr.h"
#if BYTE_ORDER == BIG_ENDIAN
#define AF_FORMAT_NE AF_FORMAT_BE
#define AF_SELECT_LE_BE(LE, BE) BE
#else
#define AF_FORMAT_NE AF_FORMAT_LE
#define AF_SELECT_LE_BE(LE, BE) LE
#endif
// Endianness
#define AF_FORMAT_BE (0<<0) // Big Endian
#define AF_FORMAT_LE (1<<0) // Little Endian
#define AF_FORMAT_END_MASK (1<<0)
#define AF_FORMAT_NE AF_SELECT_LE_BE(AF_FORMAT_LE, AF_FORMAT_BE)
// Signed/unsigned
#define AF_FORMAT_SI (0<<1) // Signed
#define AF_FORMAT_US (1<<1) // Unsigned
#define AF_FORMAT_SIGN_MASK (1<<1)
#define AF_FORMAT_SI (0<<1) // Signed
#define AF_FORMAT_US (1<<1) // Unsigned
#define AF_FORMAT_SIGN_MASK (1<<1)
// Bits used
#define AF_FORMAT_8BIT (0<<3)
#define AF_FORMAT_16BIT (1<<3)
#define AF_FORMAT_24BIT (2<<3)
#define AF_FORMAT_32BIT (3<<3)
#define AF_FORMAT_64BIT (4<<3)
#define AF_FORMAT_BITS_MASK (7<<3)
// Some code assumes they're sorted by size.
#define AF_FORMAT_8BIT (0<<3)
#define AF_FORMAT_16BIT (1<<3)
#define AF_FORMAT_24BIT (2<<3)
#define AF_FORMAT_32BIT (3<<3)
#define AF_FORMAT_64BIT (4<<3)
#define AF_FORMAT_BITS_MASK (7<<3)
// Special flags refering to non pcm data (note: 1<<6, 2<<6, 5<<6 unused)
#define AF_FORMAT_MPEG2 (3<<6) // MPEG(2) audio
#define AF_FORMAT_AC3 (4<<6) // Dolby Digital AC3
#define AF_FORMAT_IEC61937 (6<<6)
#define AF_FORMAT_SPECIAL_MASK (7<<6)
#define AF_FORMAT_S_MPEG2 (3<<6) // MPEG(2) audio
#define AF_FORMAT_S_AC3 (4<<6) // Dolby Digital AC3
#define AF_FORMAT_S_IEC61937 (6<<6)
#define AF_FORMAT_SPECIAL_MASK (7<<6)
// Fixed or floating point
#define AF_FORMAT_I (1<<9) // Int
#define AF_FORMAT_F (2<<9) // Foating point
#define AF_FORMAT_POINT_MASK (3<<9)
#define AF_FORMAT_MASK ((1<<11)-1)
// Interleaving (planar formats have data for each channel in separate planes)
#define AF_FORMAT_INTERLEAVED (0<<11) // must be 0
#define AF_FORMAT_PLANAR (1<<11)
#define AF_FORMAT_INTERLEAVING_MASK (1<<11)
// PREDEFINED formats
#define AF_FORMAT_MASK ((1<<12)-1)
#define AF_FORMAT_U8 (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE)
#define AF_FORMAT_S8 (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT|AF_FORMAT_NE)
#define AF_FORMAT_U16_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_LE)
#define AF_FORMAT_U16_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_BE)
#define AF_FORMAT_S16_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_LE)
#define AF_FORMAT_S16_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_BE)
#define AF_FORMAT_U24_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_LE)
#define AF_FORMAT_U24_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_BE)
#define AF_FORMAT_S24_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_LE)
#define AF_FORMAT_S24_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_BE)
#define AF_FORMAT_U32_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_LE)
#define AF_FORMAT_U32_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_BE)
#define AF_FORMAT_S32_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_LE)
#define AF_FORMAT_S32_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_BE)
#define AF_INTP (AF_FORMAT_I|AF_FORMAT_PLANAR)
#define AF_FLTP (AF_FORMAT_F|AF_FORMAT_PLANAR)
#define AF_FORMAT_FLOAT_LE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE)
#define AF_FORMAT_FLOAT_BE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE)
// actual sample formats
enum af_format {
AF_FORMAT_UNKNOWN = 0,
#define AF_FORMAT_DOUBLE_LE (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE)
#define AF_FORMAT_DOUBLE_BE (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE)
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT|AF_FORMAT_NE),
AF_FORMAT_U16_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_LE),
AF_FORMAT_U16_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_BE),
AF_FORMAT_S16_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_LE),
AF_FORMAT_S16_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_BE),
AF_FORMAT_U24_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_LE),
AF_FORMAT_U24_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_BE),
AF_FORMAT_S24_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_LE),
AF_FORMAT_S24_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_BE),
AF_FORMAT_U32_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_LE),
AF_FORMAT_U32_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_BE),
AF_FORMAT_S32_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_LE),
AF_FORMAT_S32_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_BE),
#define AF_FORMAT_AC3_LE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE)
#define AF_FORMAT_AC3_BE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE)
AF_FORMAT_FLOAT_LE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE),
AF_FORMAT_FLOAT_BE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE),
#define AF_FORMAT_IEC61937_LE (AF_FORMAT_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_LE)
#define AF_FORMAT_IEC61937_BE (AF_FORMAT_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_BE)
AF_FORMAT_DOUBLE_LE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE),
AF_FORMAT_DOUBLE_BE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE),
#if BYTE_ORDER == BIG_ENDIAN
#define AF_FORMAT_U16_NE AF_FORMAT_U16_BE
#define AF_FORMAT_S16_NE AF_FORMAT_S16_BE
#define AF_FORMAT_U24_NE AF_FORMAT_U24_BE
#define AF_FORMAT_S24_NE AF_FORMAT_S24_BE
#define AF_FORMAT_U32_NE AF_FORMAT_U32_BE
#define AF_FORMAT_S32_NE AF_FORMAT_S32_BE
#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_BE
#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_BE
#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_BE
#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_BE
#else
#define AF_FORMAT_U16_NE AF_FORMAT_U16_LE
#define AF_FORMAT_S16_NE AF_FORMAT_S16_LE
#define AF_FORMAT_U24_NE AF_FORMAT_U24_LE
#define AF_FORMAT_S24_NE AF_FORMAT_S24_LE
#define AF_FORMAT_U32_NE AF_FORMAT_U32_LE
#define AF_FORMAT_S32_NE AF_FORMAT_S32_LE
#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_LE
#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_LE
#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_LE
#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_LE
#endif
AF_FORMAT_AC3_LE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE),
AF_FORMAT_AC3_BE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE),
#define AF_FORMAT_UNKNOWN 0
AF_FORMAT_IEC61937_LE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_LE),
AF_FORMAT_IEC61937_BE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_BE),
#define AF_FORMAT_IS_AC3(fmt) (((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_AC3)
#define AF_FORMAT_IS_IEC61937(fmt) (AF_FORMAT_IS_AC3(fmt) || ((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_IEC61937)
AF_FORMAT_MPEG2 = (AF_FORMAT_S_MPEG2),
// Planar variants
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_NE),
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_NE),
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT|AF_FORMAT_NE),
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT|AF_FORMAT_NE),
// Native endian variants
AF_FORMAT_U16 = AF_SELECT_LE_BE(AF_FORMAT_U16_LE, AF_FORMAT_U16_BE),
AF_FORMAT_S16 = AF_SELECT_LE_BE(AF_FORMAT_S16_LE, AF_FORMAT_S16_BE),
AF_FORMAT_U24 = AF_SELECT_LE_BE(AF_FORMAT_U24_LE, AF_FORMAT_U24_BE),
AF_FORMAT_S24 = AF_SELECT_LE_BE(AF_FORMAT_S24_LE, AF_FORMAT_S24_BE),
AF_FORMAT_U32 = AF_SELECT_LE_BE(AF_FORMAT_U32_LE, AF_FORMAT_U32_BE),
AF_FORMAT_S32 = AF_SELECT_LE_BE(AF_FORMAT_S32_LE, AF_FORMAT_S32_BE),
AF_FORMAT_FLOAT = AF_SELECT_LE_BE(AF_FORMAT_FLOAT_LE, AF_FORMAT_FLOAT_BE),
AF_FORMAT_DOUBLE = AF_SELECT_LE_BE(AF_FORMAT_DOUBLE_LE, AF_FORMAT_DOUBLE_BE),
AF_FORMAT_AC3 = AF_SELECT_LE_BE(AF_FORMAT_AC3_LE, AF_FORMAT_AC3_BE),
AF_FORMAT_IEC61937 = AF_SELECT_LE_BE(AF_FORMAT_IEC61937_LE, AF_FORMAT_IEC61937_BE),
};
#define AF_FORMAT_IS_AC3(fmt) \
(((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_AC3)
#define AF_FORMAT_IS_IEC61937(fmt) \
(AF_FORMAT_IS_AC3(fmt) || \
((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_IEC61937)
#define AF_FORMAT_IS_SPECIAL(fmt) \
((fmt & AF_FORMAT_SPECIAL_MASK) != 0)
struct af_fmt_entry {
const char *name;
@@ -130,12 +149,22 @@ struct af_fmt_entry {
extern const struct af_fmt_entry af_fmtstr_table[];
int af_str2fmt_short(bstr str);
const char *af_fmt_to_str(int format);
int af_fmt2bits(int format);
int af_fmt_change_bits(int format, int bits);
int af_fmt_to_planar(int format);
int af_fmt_from_planar(int format);
bool af_fmt_is_planar(int format);
// Amount of bytes that contain audio of the given duration, aligned to frames.
int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int samplerate);
char* af_fmt2str(int format, char* str, int size);
const char* af_fmt2str_short(int format);
bool af_fmt_is_valid(int format);
void af_fill_silence(void *dst, size_t bytes, int format);
int af_format_conversion_score(int dst_format, int src_format);
#endif /* MPLAYER_AF_FORMAT_H */
+25 -43
View File
@@ -24,11 +24,13 @@
#include "config.h"
#include "audio/out/ao.h"
#include "audio/filter/af.h"
#include "mpvcore/mp_msg.h"
#include "common/global.h"
#include "common/msg.h"
#include "talloc.h"
#include "mixer.h"
struct mixer {
struct mp_log *log;
struct MPOpts *opts;
struct ao *ao;
struct af_stream *af;
@@ -47,11 +49,12 @@ struct mixer {
float balance;
};
struct mixer *mixer_init(void *talloc_ctx, struct MPOpts *opts)
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global)
{
struct mixer *mixer = talloc_ptrtype(talloc_ctx, mixer);
*mixer = (struct mixer) {
.opts = opts,
.log = mp_log_new(mixer, global->log, "mixer"),
.opts = global->opts,
.vol_l = 100,
.vol_r = 100,
.driver = "",
@@ -71,12 +74,11 @@ static void checkvolume(struct mixer *mixer)
ao_control_vol_t vol = {mixer->vol_l, mixer->vol_r};
if (mixer->softvol) {
float vals[AF_NCH];
if (!af_control_any_rev(mixer->af,
AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, vals))
vals[0] = vals[1] = 1.0;
vol.left = (vals[0] / (mixer->opts->softvol_max / 100.0)) * 100.0;
vol.right = (vals[1] / (mixer->opts->softvol_max / 100.0)) * 100.0;
float gain;
if (!af_control_any_rev(mixer->af, AF_CONTROL_GET_VOLUME, &gain))
gain = 1.0;
vol.left = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
vol.right = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
} else {
// Rely on the values not changing if the query is not supported
ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol);
@@ -114,26 +116,15 @@ static void setvolume_internal(struct mixer *mixer, float l, float r)
struct ao_control_vol vol = {.left = l, .right = r};
if (!mixer->softvol) {
if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] Failed to change audio output volume.\n");
MP_ERR(mixer, "Failed to change audio output volume.\n");
return;
}
float vals[AF_NCH];
vals[0] = l / 100.0 * mixer->opts->softvol_max / 100.0;
vals[1] = r / 100.0 * mixer->opts->softvol_max / 100.0;
for (int i = 2; i < AF_NCH; i++)
vals[i] = (vals[0] + vals[1]) / 2.0;
if (!af_control_any_rev(mixer->af,
AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET,
vals))
{
mp_tmsg(MSGT_GLOBAL, MSGL_V, "[Mixer] Inserting volume filter.\n");
float gain = (l + r) / 2.0 / 100.0 * mixer->opts->softvol_max / 100.0;
if (!af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)) {
MP_VERBOSE(mixer, "Inserting volume filter.\n");
if (!(af_add(mixer->af, "volume", NULL)
&& af_control_any_rev(mixer->af,
AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET,
vals)))
mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] No volume control available.\n");
&& af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mixer, "No volume control available.\n");
}
}
@@ -201,9 +192,7 @@ void mixer_decvolume(struct mixer *mixer)
void mixer_getbalance(struct mixer *mixer, float *val)
{
if (mixer->af)
af_control_any_rev(mixer->af,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET,
&mixer->balance);
af_control_any_rev(mixer->af, AF_CONTROL_GET_PAN_BALANCE, &mixer->balance);
*val = mixer->balance;
}
@@ -213,9 +202,7 @@ void mixer_getbalance(struct mixer *mixer, float *val)
* af_pan instance that was automatically inserted for balance control
* only and is otherwise an identity transform, but if the filter was
* there for another reason, then ignoring and overriding the original
* values is completely wrong. In particular, this will break
* automatically inserted downmix filters; the original coefficients that
* are significantly below 1 will be overwritten with much higher values.
* values is completely wrong.
*/
void mixer_setbalance(struct mixer *mixer, float val)
@@ -230,16 +217,14 @@ void mixer_setbalance(struct mixer *mixer, float val)
if (!mixer->af)
return;
if (af_control_any_rev(mixer->af,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val))
if (af_control_any_rev(mixer->af, AF_CONTROL_SET_PAN_BALANCE, &val))
return;
if (val == 0 || mixer->ao->channels.num < 2)
return;
if (!(af_pan_balance = af_add(mixer->af, "pan", NULL))) {
mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] No balance control available.\n");
MP_ERR(mixer, "No balance control available.\n");
return;
}
@@ -248,14 +233,12 @@ void mixer_setbalance(struct mixer *mixer, float val)
for (i = 2; i < AF_NCH; i++) {
arg_ext.ch = i;
level[i] = 1.f;
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET,
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_LEVEL,
&arg_ext);
level[i] = 0.f;
}
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val);
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_BALANCE, &val);
}
char *mixer_get_volume_restore_data(struct mixer *mixer)
@@ -281,8 +264,7 @@ static void probe_softvol(struct mixer *mixer)
ao_control_vol_t vol;
if (ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK) {
mixer->softvol = true;
mp_tmsg(MSGT_GLOBAL, MSGL_WARN,
"[mixer] Hardware volume control unavailable.\n");
MP_WARN(mixer, "Hardware volume control unavailable.\n");
}
}
@@ -303,7 +285,7 @@ static void restore_volume(struct mixer *mixer)
int force_mute = -1;
const char *prev_driver = mixer->driver;
mixer->driver = mixer->softvol ? "softvol" : ao->driver->info->short_name;
mixer->driver = mixer->softvol ? "softvol" : ao->driver->name;
bool restore = mixer->softvol || ao->no_persistent_volume;
+2 -2
View File
@@ -28,12 +28,12 @@ enum {
SOFTVOL_AUTO = 2,
};
struct MPOpts;
struct mpv_global;
struct ao;
struct af_stream;
struct mixer;
struct mixer *mixer_init(void *talloc_ctx, struct MPOpts *opts);
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global);
void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af);
void mixer_uninit_audio(struct mixer *mixer);
bool mixer_audio_initialized(struct mixer *mixer);
+64 -43
View File
@@ -27,10 +27,10 @@
#include "ao.h"
#include "audio/format.h"
#include "mpvcore/options.h"
#include "mpvcore/m_config.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mpv_global.h"
#include "options/options.h"
#include "options/m_config.h"
#include "common/msg.h"
#include "common/global.h"
extern const struct ao_driver audio_out_oss;
extern const struct ao_driver audio_out_coreaudio;
@@ -50,47 +50,47 @@ extern const struct ao_driver audio_out_sdl;
static const struct ao_driver * const audio_out_drivers[] = {
// native:
#ifdef CONFIG_COREAUDIO
#if HAVE_COREAUDIO
&audio_out_coreaudio,
#endif
#ifdef CONFIG_PULSE
#if HAVE_PULSE
&audio_out_pulse,
#endif
#ifdef CONFIG_SNDIO
#if HAVE_SNDIO
&audio_out_sndio,
#endif
#ifdef CONFIG_ALSA
#if HAVE_ALSA
&audio_out_alsa,
#endif
#ifdef CONFIG_WASAPI
#if HAVE_WASAPI
&audio_out_wasapi,
#endif
#ifdef CONFIG_OSS_AUDIO
#if HAVE_OSS_AUDIO
&audio_out_oss,
#endif
#ifdef CONFIG_DSOUND
#if HAVE_DSOUND
&audio_out_dsound,
#endif
#ifdef CONFIG_PORTAUDIO
#if HAVE_PORTAUDIO
&audio_out_portaudio,
#endif
// wrappers:
#ifdef CONFIG_JACK
#if HAVE_JACK
&audio_out_jack,
#endif
#ifdef CONFIG_OPENAL
#if HAVE_OPENAL
&audio_out_openal,
#endif
#ifdef CONFIG_SDL
#if HAVE_SDL || HAVE_SDL2
&audio_out_sdl,
#endif
&audio_out_null,
// should not be auto-selected:
&audio_out_pcm,
#ifdef CONFIG_ENCODING
#if HAVE_ENCODING
&audio_out_lavc,
#endif
#ifdef CONFIG_RSOUND
#if HAVE_RSOUND
&audio_out_rsound,
#endif
NULL
@@ -102,8 +102,8 @@ static bool get_desc(struct m_obj_desc *dst, int index)
return false;
const struct ao_driver *ao = audio_out_drivers[index];
*dst = (struct m_obj_desc) {
.name = ao->info->short_name,
.description = ao->info->name,
.name = ao->name,
.description = ao->description,
.priv_size = ao->priv_size,
.priv_defaults = ao->priv_defaults,
.options = ao->options,
@@ -130,7 +130,7 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
struct mp_log *log = mp_log_new(NULL, global->log, "ao");
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &ao_obj_list, bstr0(name))) {
mp_tmsg_log(log, MSGL_ERR, "Audio output %s not found!\n", name);
mp_msg(log, MSGL_ERR, "Audio output %s not found!\n", name);
talloc_free(log);
return NULL;
};
@@ -149,13 +149,22 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
};
if (ao->driver->encode != !!ao->encode_lavc_ctx)
goto error;
struct m_config *config = m_config_from_obj_desc(ao, &desc);
struct m_config *config = m_config_from_obj_desc(ao, ao->log, &desc);
if (m_config_apply_defaults(config, name, global->opts->ao_defs) < 0)
goto error;
if (m_config_set_obj_params(config, args) < 0)
goto error;
ao->priv = config->optstruct;
char *chmap = mp_chmap_to_str(&ao->channels);
MP_VERBOSE(ao, "requested format: %d Hz, %s channels, %s\n",
ao->samplerate, chmap, af_fmt_to_str(ao->format));
talloc_free(chmap);
if (ao->driver->init(ao) < 0)
goto error;
ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8;
ao->sstride = af_fmt2bits(ao->format) / 8;
if (!af_fmt_is_planar(ao->format))
ao->sstride *= ao->channels.num;
ao->bps = ao->samplerate * ao->sstride;
return ao;
error:
talloc_free(ao);
@@ -167,48 +176,48 @@ struct ao *ao_init_best(struct mpv_global *global,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels)
{
struct mp_log *log = mp_log_new(NULL, global->log, "ao");
struct ao *ao = NULL;
struct m_obj_settings *ao_list = global->opts->audio_driver_list;
if (ao_list && ao_list[0].name) {
for (int n = 0; ao_list[n].name; n++) {
if (strlen(ao_list[n].name) == 0)
goto autoprobe;
mp_tmsg(MSGT_AO, MSGL_V, "Trying preferred audio driver '%s'\n",
ao_list[n].name);
struct ao *ao = ao_create(false, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
ao_list[n].name, ao_list[n].attribs);
mp_verbose(log, "Trying preferred audio driver '%s'\n",
ao_list[n].name);
ao = ao_create(false, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
ao_list[n].name, ao_list[n].attribs);
if (ao)
return ao;
mp_tmsg(MSGT_AO, MSGL_WARN, "Failed to initialize audio driver '%s'\n",
goto done;
mp_warn(log, "Failed to initialize audio driver '%s'\n",
ao_list[n].name);
}
return NULL;
goto done;
}
autoprobe:
// now try the rest...
for (int i = 0; audio_out_drivers[i]; i++) {
struct ao *ao = ao_create(true, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
(char *)audio_out_drivers[i]->info->short_name, NULL);
ao = ao_create(true, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
(char *)audio_out_drivers[i]->name, NULL);
if (ao)
return ao;
goto done;
}
return NULL;
done:
talloc_free(log);
return ao;
}
void ao_uninit(struct ao *ao, bool cut_audio)
{
assert(ao->buffer.len >= ao->buffer_playable_size);
ao->buffer.len = ao->buffer_playable_size;
ao->driver->uninit(ao, cut_audio);
if (!cut_audio && ao->buffer.len)
mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n");
talloc_free(ao);
}
int ao_play(struct ao *ao, void *data, int len, int flags)
int ao_play(struct ao *ao, void **data, int samples, int flags)
{
return ao->driver->play(ao, data, len, flags);
return ao->driver->play(ao, data, samples, flags);
}
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
@@ -234,8 +243,6 @@ int ao_get_space(struct ao *ao)
void ao_reset(struct ao *ao)
{
ao->buffer.len = 0;
ao->buffer_playable_size = 0;
if (ao->driver->reset)
ao->driver->reset(ao);
}
@@ -252,6 +259,20 @@ void ao_resume(struct ao *ao)
ao->driver->resume(ao);
}
int ao_play_silence(struct ao *ao, int samples)
{
if (samples <= 0 || AF_FORMAT_IS_SPECIAL(ao->format))
return 0;
char *p = talloc_size(NULL, samples * ao->sstride);
af_fill_silence(p, samples * ao->sstride, ao->format);
void *tmp[MP_NUM_CHANNELS];
for (int n = 0; n < MP_NUM_CHANNELS; n++)
tmp[n] = p;
int r = ao_play(ao, tmp, samples, 0);
talloc_free(p);
return r;
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map)
{
+15 -18
View File
@@ -21,8 +21,8 @@
#include <stdbool.h>
#include "mpvcore/bstr.h"
#include "mpvcore/mp_common.h"
#include "bstr/bstr.h"
#include "common/common.h"
#include "audio/chmap.h"
#include "audio/chmap_sel.h"
@@ -34,6 +34,8 @@ enum aocontrol {
// _MUTE commands take a pointer to bool
AOCONTROL_GET_MUTE,
AOCONTROL_SET_MUTE,
// Has char* as argument, which contains the desired stream title.
AOCONTROL_UPDATE_STREAM_TITLE,
};
#define AOPLAY_FINAL_CHUNK 1
@@ -43,28 +45,18 @@ typedef struct ao_control_vol {
float right;
} ao_control_vol_t;
typedef struct ao_info {
/* driver name ("Matrox Millennium G200/G400" */
const char *name;
/* short name (for config strings) ("mga") */
const char *short_name;
/* author ("Aaron Holtzman <aholtzma@ess.engr.uvic.ca>") */
const char *author;
/* any additional comments */
const char *comment;
} ao_info_t;
struct ao;
struct ao_driver {
bool encode;
const struct ao_info *info;
const char *name;
const char *description;
int (*control)(struct ao *ao, enum aocontrol cmd, void *arg);
int (*init)(struct ao *ao);
void (*uninit)(struct ao *ao, bool cut_audio);
void (*reset)(struct ao*ao);
int (*get_space)(struct ao *ao);
int (*play)(struct ao *ao, void *data, int len, int flags);
int (*play)(struct ao *ao, void **data, int samples, int flags);
float (*get_delay)(struct ao *ao);
void (*pause)(struct ao *ao);
void (*resume)(struct ao *ao);
@@ -81,9 +73,12 @@ struct ao {
struct mp_chmap channels;
int format;
int bps; // bytes per second
int sstride; // size of a sample on each plane
// (format_size*num_channels/num_planes)
double pts; // some mplayer.c state (why is this here?)
struct bstr buffer;
int buffer_playable_size;
struct mp_audio_buffer *buffer; // queued audio; passed to play() later
int buffer_playable_samples;// part of the part of the buffer the AO hasn't
// accepted yet with play()
bool probing; // if true, don't fail loudly on init
bool untimed;
bool no_persistent_volume; // the AO does the equivalent of af_volume
@@ -102,7 +97,7 @@ struct ao *ao_init_best(struct mpv_global *global,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
void ao_uninit(struct ao *ao, bool cut_audio);
int ao_play(struct ao *ao, void *data, int len, int flags);
int ao_play(struct ao *ao, void **data, int samples, int flags);
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
double ao_get_delay(struct ao *ao);
int ao_get_space(struct ao *ao);
@@ -110,6 +105,8 @@ void ao_reset(struct ao *ao);
void ao_pause(struct ao *ao);
void ao_resume(struct ao *ao);
int ao_play_silence(struct ao *ao, int samples);
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map);
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
+109 -142
View File
@@ -2,6 +2,7 @@
* ALSA 0.9.x-1.x audio output driver
*
* Copyright (C) 2004 Alex Beregszaszi
* Zsolt Barat <joy@streamminister.de>
*
* modified for real ALSA 0.9.0 support by Zsolt Barat <joy@streamminister.de>
* additional AC-3 passthrough support by Andy Lo A Foe <andy@alsaplayer.org>
@@ -33,12 +34,11 @@
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <alloca.h>
#include "config.h"
#include "mpvcore/options.h"
#include "mpvcore/m_option.h"
#include "mpvcore/mp_msg.h"
#include "options/options.h"
#include "options/m_option.h"
#include "common/msg.h"
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
@@ -52,18 +52,18 @@
struct priv {
snd_pcm_t *alsa;
snd_pcm_format_t alsa_fmt;
size_t bytes_per_sample;
int can_pause;
snd_pcm_sframes_t prepause_frames;
float delay_before_pause;
int buffersize;
int outburst;
int buffersize; // in frames
int outburst; // in frames
int cfg_block;
char *cfg_device;
char *cfg_mixer_device;
char *cfg_mixer_name;
int cfg_mixer_index;
int cfg_resample;
};
#define BUFFER_TIME 500000 // 0.5 s
@@ -78,28 +78,8 @@ struct priv {
} while (0)
static float get_delay(struct ao *ao);
static int play(struct ao *ao, void *data, int len, int flags);
static void uninit(struct ao *ao, bool immed);
static void alsa_error_handler(const char *file, int line, const char *function,
int err, const char *format, ...)
{
char tmp[0xc00];
va_list va;
va_start(va, format);
vsnprintf(tmp, sizeof tmp, format, va);
va_end(va);
if (err) {
mp_msg(MSGT_AO, MSGL_ERR, "alsa-lib: %s:%i:(%s) %s: %s\n",
file, line, function, tmp, snd_strerror(err));
} else {
mp_msg(MSGT_AO, MSGL_ERR, "alsa-lib: %s:%i:(%s) %s\n",
file, line, function, tmp);
}
}
/* to set/get/query special features/parameters */
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
@@ -252,6 +232,7 @@ static const int mp_to_alsa_format[][2] = {
static int find_alsa_format(int af_format)
{
af_format = af_fmt_from_planar(af_format);
for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) {
if (mp_to_alsa_format[n][0] == af_format)
return mp_to_alsa_format[n][1];
@@ -304,51 +285,59 @@ static const char *select_chmap(struct ao *ao)
return "default";
}
static int try_open_device(struct ao *ao, const char *device, int open_mode,
int try_ac3)
static int map_iec958_srate(int srate)
{
switch (srate) {
case 44100: return IEC958_AES3_CON_FS_44100;
case 48000: return IEC958_AES3_CON_FS_48000;
case 32000: return IEC958_AES3_CON_FS_32000;
case 22050: return IEC958_AES3_CON_FS_22050;
case 24000: return IEC958_AES3_CON_FS_24000;
case 88200: return IEC958_AES3_CON_FS_88200;
case 768000: return IEC958_AES3_CON_FS_768000;
case 96000: return IEC958_AES3_CON_FS_96000;
case 176400: return IEC958_AES3_CON_FS_176400;
case 192000: return IEC958_AES3_CON_FS_192000;
default: return IEC958_AES3_CON_FS_NOTID;
}
}
static int try_open_device(struct ao *ao, const char *device, int open_mode)
{
struct priv *p = ao->priv;
int err, len;
char *ac3_device, *args;
if (try_ac3) {
if (AF_FORMAT_IS_IEC61937(ao->format)) {
void *tmp = talloc_new(NULL);
/* to set the non-audio bit, use AES0=6 */
len = strlen(device);
ac3_device = malloc(len + 7 + 1);
if (!ac3_device)
return -ENOMEM;
strcpy(ac3_device, device);
args = strchr(ac3_device, ':');
if (!args) {
char *params = talloc_asprintf(tmp,
"AES0=%d,AES1=%d,AES2=0,AES3=%d",
IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
map_iec958_srate(ao->samplerate));
const char *ac3_device = device;
int len = strlen(device);
char *end = strchr(device, ':');
if (!end) {
/* no existing parameters: add it behind device name */
strcat(ac3_device, ":AES0=6");
ac3_device = talloc_asprintf(tmp, "%s:%s", device, params);
} else if (end[1] == '\0') {
/* ":" but no parameters */
ac3_device = talloc_asprintf(tmp, "%s%s", device, params);
} else if (end[1] == '{' && device[len - 1] == '}') {
/* parameters in config syntax: add it inside the { } block */
ac3_device = talloc_asprintf(tmp, "%.*s %s}", len - 1, device, params);
} else {
do {
++args;
} while (isspace(*args));
if (*args == '\0') {
/* ":" but no parameters */
strcat(ac3_device, "AES0=6");
} else if (*args != '{') {
/* a simple list of parameters: add it at the end of the list */
strcat(ac3_device, ",AES0=6");
} else {
/* parameters in config syntax: add it inside the { } block */
do {
--len;
} while (len > 0 && isspace(ac3_device[len]));
if (ac3_device[len] == '}')
strcpy(ac3_device + len, " AES0=6}");
}
/* a simple list of parameters: add it at the end of the list */
ac3_device = talloc_asprintf(tmp, "%s,%s", device, params);
}
err = snd_pcm_open
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode);
free(ac3_device);
int err = snd_pcm_open
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode);
talloc_free(tmp);
if (!err)
return 0;
}
return snd_pcm_open
(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, open_mode);
return snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, open_mode);
}
/*
@@ -364,9 +353,6 @@ static int init(struct ao *ao)
struct priv *p = ao->priv;
MP_VERBOSE(ao, "requested format: %d Hz, %d channels, %x\n",
ao->samplerate, ao->channels.num, ao->format);
p->prepause_frames = 0;
p->delay_before_pause = 0;
@@ -383,8 +369,7 @@ static int init(struct ao *ao)
ao->channels.num);
} else {
device = select_chmap(ao);
if (strcmp(device, "default") != 0 && ao->format == AF_FORMAT_FLOAT_NE)
{
if (strcmp(device, "default") != 0 && (ao->format & AF_FORMAT_F)) {
// hack - use the converter plugin (why the heck?)
device = talloc_asprintf(ao, "plug:%s", device);
}
@@ -397,17 +382,15 @@ static int init(struct ao *ao)
p->can_pause = 1;
MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
snd_lib_error_set_handler(alsa_error_handler);
int open_mode = p->cfg_block ? 0 : SND_PCM_NONBLOCK;
int isac3 = AF_FORMAT_IS_IEC61937(ao->format);
//modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
err = try_open_device(ao, device, open_mode, isac3);
err = try_open_device(ao, device, open_mode);
if (err < 0) {
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, isac3);
err = try_open_device(ao, device, 0);
}
CHECK_ALSA_ERROR("Playback open error");
}
@@ -429,20 +412,16 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
CHECK_ALSA_ERROR("Unable to get initial parameters");
err = snd_pcm_hw_params_set_access
(p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
CHECK_ALSA_ERROR("Unable to set access type");
p->alsa_fmt = find_alsa_format(ao->format);
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
p->alsa_fmt = SND_PCM_FORMAT_S16;
ao->format = AF_FORMAT_S16_NE;
ao->format = AF_FORMAT_S16;
}
err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt);
if (err < 0) {
MP_INFO(ao, "Format %s is not supported by hardware, trying default.\n",
af_fmt2str_short(ao->format));
af_fmt_to_str(ao->format));
p->alsa_fmt = SND_PCM_FORMAT_S16_LE;
if (AF_FORMAT_IS_AC3(ao->format))
ao->format = AF_FORMAT_AC3_LE;
@@ -455,6 +434,17 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
CHECK_ALSA_ERROR("Unable to set format");
snd_pcm_access_t access = af_fmt_is_planar(ao->format)
? SND_PCM_ACCESS_RW_NONINTERLEAVED
: SND_PCM_ACCESS_RW_INTERLEAVED;
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
if (err < 0 && af_fmt_is_planar(ao->format)) {
ao->format = af_fmt_from_planar(ao->format);
access = SND_PCM_ACCESS_RW_INTERLEAVED;
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
}
CHECK_ALSA_ERROR("Unable to set access type");
int num_channels = ao->channels.num;
err = snd_pcm_hw_params_set_channels_near
(p->alsa, alsa_hwparams, &num_channels);
@@ -465,19 +455,17 @@ static int init(struct ao *ao)
mp_chmap_from_channels_alsa(&ao->channels, num_channels);
}
/* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
prefer our own resampler, since that allows users to choose the resampler,
even per file if desired */
err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
CHECK_ALSA_ERROR("Unable to disable resampling");
// Some ALSA drivers have broken delay reporting, so disable the ALSA
// resampling plugin by default.
if (!p->cfg_resample) {
err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
CHECK_ALSA_ERROR("Unable to disable resampling");
}
err = snd_pcm_hw_params_set_rate_near
(p->alsa, alsa_hwparams, &ao->samplerate, NULL);
CHECK_ALSA_ERROR("Unable to set samplerate-2");
p->bytes_per_sample = af_fmt2bits(ao->format) / 8;
p->bytes_per_sample *= ao->channels.num;
err = snd_pcm_hw_params_set_buffer_time_near
(p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
CHECK_ALSA_ERROR("Unable to set buffer time near");
@@ -496,14 +484,14 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize);
CHECK_ALSA_ERROR("Unable to get buffersize");
p->buffersize = bufsize * p->bytes_per_sample;
MP_VERBOSE(ao, "got buffersize=%i\n", p->buffersize);
p->buffersize = bufsize;
MP_VERBOSE(ao, "got buffersize=%i samples\n", p->buffersize);
err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL);
CHECK_ALSA_ERROR("Unable to get period size");
MP_VERBOSE(ao, "got period size %li\n", chunk_size);
p->outburst = chunk_size * p->bytes_per_sample;
p->outburst = chunk_size;
/* setting software parameters */
err = snd_pcm_sw_params_current(p->alsa, alsa_swparams);
@@ -534,8 +522,8 @@ static int init(struct ao *ao)
p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
ao->samplerate, ao->channels.num, (int)p->bytes_per_sample,
MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bps/%d samples buffer/%s\n",
ao->samplerate, ao->channels.num, af_fmt2bits(ao->format),
p->buffersize, snd_pcm_format_description(p->alsa_fmt));
return 0;
@@ -565,7 +553,6 @@ static void uninit(struct ao *ao, bool immed)
alsa_error:
p->alsa = NULL;
snd_lib_error_set_handler(NULL);
}
static void audio_pause(struct ao *ao)
@@ -598,9 +585,11 @@ static void audio_resume(struct ao *ao)
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_SUSPENDED) {
MP_INFO(ao, "PCM in suspend mode, trying to resume.\n");
while ((err = snd_pcm_resume(p->alsa)) == -EAGAIN)
sleep(1);
}
if (p->can_pause) {
err = snd_pcm_pause(p->alsa, 0);
CHECK_ALSA_ERROR("pcm resume error");
@@ -608,11 +597,8 @@ static void audio_resume(struct ao *ao)
MP_VERBOSE(ao, "resume not supported by hardware\n");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
if (p->prepause_frames) {
void *silence = calloc(p->prepause_frames, p->bytes_per_sample);
play(ao, silence, p->prepause_frames * p->bytes_per_sample, 0);
free(silence);
}
if (p->prepause_frames)
ao_play_silence(ao, p->prepause_frames);
}
alsa_error: ;
@@ -634,42 +620,29 @@ static void reset(struct ao *ao)
alsa_error: ;
}
/*
plays 'len' bytes of 'data'
returns: number of bytes played
modified last at 29.06.02 by jp
thanxs for marius <marius@rospot.com> for giving us the light ;)
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int num_frames;
snd_pcm_sframes_t res = 0;
if (!(flags & AOPLAY_FINAL_CHUNK))
len = len / p->outburst * p->outburst;
num_frames = len / p->bytes_per_sample;
samples = samples / p->outburst * p->outburst;
if (!p->alsa) {
MP_ERR(ao, "Device configuration error.");
return 0;
}
if (num_frames == 0)
if (samples == 0)
return 0;
do {
res = snd_pcm_writei(p->alsa, data, num_frames);
if (af_fmt_is_planar(ao->format)) {
res = snd_pcm_writen(p->alsa, data, samples);
} else {
res = snd_pcm_writei(p->alsa, data[0], samples);
}
if (res == -EINTR) {
/* nothing to do */
res = 0;
} else if (res == -ESTRPIPE) { /* suspend */
MP_INFO(ao, "PCM in suspend mode, trying to resume.\n");
while ((res = snd_pcm_resume(p->alsa)) == -EAGAIN)
sleep(1);
}
if (res < 0) {
audio_resume(ao);
} else if (res < 0) {
MP_ERR(ao, "Write error: %s\n", snd_strerror(res));
res = snd_pcm_prepare(p->alsa);
int err = res;
@@ -678,10 +651,10 @@ static int play(struct ao *ao, void *data, int len, int flags)
}
} while (res == 0);
return res < 0 ? 0 : res * p->bytes_per_sample;
return res < 0 ? -1 : res;
alsa_error:
return 0;
return -1;
}
/* how many byes are free in the buffer */
@@ -696,7 +669,7 @@ static int get_space(struct ao *ao)
err = snd_pcm_status(p->alsa, status);
CHECK_ALSA_ERROR("cannot get pcm status");
unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample;
unsigned space = snd_pcm_status_get_avail(status);
if (space > p->buffersize) // Buffer underrun?
space = p->buffersize;
return space;
@@ -709,34 +682,27 @@ alsa_error:
static float get_delay(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->alsa) {
snd_pcm_sframes_t delay;
snd_pcm_sframes_t delay;
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
return p->delay_before_pause;
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
return p->delay_before_pause;
if (snd_pcm_delay(p->alsa, &delay) < 0)
return 0;
if (delay < 0) {
/* underrun - move the application pointer forward to catch up */
snd_pcm_forward(p->alsa, -delay);
delay = 0;
}
return (float)delay / (float)ao->samplerate;
} else
if (snd_pcm_delay(p->alsa, &delay) < 0)
return 0;
if (delay < 0) {
/* underrun - move the application pointer forward to catch up */
snd_pcm_forward(p->alsa, -delay);
delay = 0;
}
return (float)delay / (float)ao->samplerate;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_alsa = {
.info = &(const struct ao_info) {
"ALSA-0.9.x-1.x audio output",
"alsa",
"Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>",
"under development"
},
.description = "ALSA-0.9.x-1.x audio output",
.name = "alsa",
.init = init,
.uninit = uninit,
.control = control,
@@ -754,6 +720,7 @@ const struct ao_driver audio_out_alsa = {
.cfg_mixer_index = 0,
},
.options = (const struct m_option[]) {
OPT_FLAG("resample", cfg_resample, 0),
OPT_STRING("device", cfg_device, 0),
OPT_FLAG("block", cfg_block, 0),
OPT_STRING("mixer-device", cfg_mixer_device, 0),
+19 -16
View File
@@ -4,6 +4,9 @@
* original copyright (C) Timothy J. Wood - Aug 2000
* ported to MPlayer libao2 by Dan Christiansen
*
* Chris Roccati
* Stefano Pigozzi
*
* The S/PDIF part of the code is based on the auhal audio output
* module from VideoLAN:
* Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org>
@@ -36,9 +39,9 @@
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "mpvcore/m_option.h"
#include "mpvcore/mp_ring.h"
#include "mpvcore/mp_msg.h"
#include "options/m_option.h"
#include "misc/ring.h"
#include "common/msg.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
@@ -151,9 +154,9 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
if (p->is_digital) {
struct priv_d *d = p->digital;
// Digital output has no volume adjust.
int vol = d->muted ? 0 : 100;
int digitalvol = d->muted ? 0 : 100;
*control_vol = (ao_control_vol_t) {
.left = vol, .right = vol,
.left = digitalvol, .right = digitalvol,
};
return CONTROL_TRUE;
}
@@ -213,7 +216,7 @@ static void print_list(struct ao *ao)
for (int i = 0; i < n_devs; i++) {
char *name;
OSStatus err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
if (err == noErr)
talloc_steal(devs, name);
@@ -268,7 +271,7 @@ static int init(struct ao *ao)
selected_device = p->opt_device_id;
}
if (mp_msg_test_log(ao->log, MSGL_V)) {
if (mp_msg_test(ao->log, MSGL_V)) {
char *name;
err = CA_GET_STR(selected_device, kAudioObjectPropertyName, &name);
CHECK_CA_ERROR("could not get selected audio device name");
@@ -282,6 +285,8 @@ static int init(struct ao *ao)
// Save selected device id
p->device = selected_device;
ao->format = af_fmt_from_planar(ao->format);
bool supports_digital = false;
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
if (AF_FORMAT_IS_AC3(ao->format)) {
@@ -573,10 +578,12 @@ coreaudio_error:
return CONTROL_ERROR;
}
static int play(struct ao *ao, void *output_samples, int num_bytes, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
struct priv_d *d = p->digital;
void *output_samples = data[0];
int num_bytes = samples * ao->sstride;
// Check whether we need to reset the digital output stream.
if (p->is_digital && d->stream_asbd_changed) {
@@ -594,7 +601,7 @@ static int play(struct ao *ao, void *output_samples, int num_bytes, int flags)
int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
audio_resume(ao);
return wrote;
return wrote / ao->sstride;
}
static void reset(struct ao *ao)
@@ -607,7 +614,7 @@ static void reset(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->buffer);
return mp_ring_available(p->buffer) / ao->sstride;
}
static float get_delay(struct ao *ao)
@@ -697,12 +704,8 @@ static void audio_resume(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio = {
.info = &(const struct ao_info) {
"CoreAudio (OS X Audio Output)",
"coreaudio",
"Timothy J. Wood, Dan Christiansen, Chris Roccati & Stefano Pigozzi",
"",
},
.description = "CoreAudio (OS X Audio Output)",
.name = "coreaudio",
.uninit = uninit,
.init = init,
.play = play,
+45 -5
View File
@@ -57,7 +57,7 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
if (code == noErr) return true;
char *error_string = fourcc_repr(NULL, code);
mp_msg_log(ao->log, level, "%s (%s)\n", message, error_string);
mp_msg(ao->log, level, "%s (%s)\n", message, error_string);
talloc_free(error_string);
return false;
@@ -336,6 +336,47 @@ bool ca_change_format(struct ao *ao, AudioStreamID stream,
return format_set;
}
static const int speaker_map[][2] = {
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
{ kAudioChannelLabel_Right, MP_SPEAKER_ID_FR },
{ kAudioChannelLabel_Center, MP_SPEAKER_ID_FC },
{ kAudioChannelLabel_LFEScreen, MP_SPEAKER_ID_LFE },
{ kAudioChannelLabel_LeftSurround, MP_SPEAKER_ID_BL },
{ kAudioChannelLabel_RightSurround, MP_SPEAKER_ID_BR },
{ kAudioChannelLabel_LeftCenter, MP_SPEAKER_ID_FLC },
{ kAudioChannelLabel_RightCenter, MP_SPEAKER_ID_FRC },
{ kAudioChannelLabel_CenterSurround, MP_SPEAKER_ID_BC },
{ kAudioChannelLabel_LeftSurroundDirect, MP_SPEAKER_ID_SL },
{ kAudioChannelLabel_RightSurroundDirect, MP_SPEAKER_ID_SR },
{ kAudioChannelLabel_TopCenterSurround, MP_SPEAKER_ID_TC },
{ kAudioChannelLabel_VerticalHeightLeft, MP_SPEAKER_ID_TFL },
{ kAudioChannelLabel_VerticalHeightCenter, MP_SPEAKER_ID_TFC },
{ kAudioChannelLabel_VerticalHeightRight, MP_SPEAKER_ID_TFR },
{ kAudioChannelLabel_TopBackLeft, MP_SPEAKER_ID_TBL },
{ kAudioChannelLabel_TopBackCenter, MP_SPEAKER_ID_TBC },
{ kAudioChannelLabel_TopBackRight, MP_SPEAKER_ID_TBR },
// unofficial extensions
{ kAudioChannelLabel_RearSurroundLeft, MP_SPEAKER_ID_SDL },
{ kAudioChannelLabel_RearSurroundRight, MP_SPEAKER_ID_SDR },
{ kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
{ kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
{ kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
{ kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
{ kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
{ kAudioChannelLabel_Unknown, -1 },
};
static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
{
for (int i = 0; speaker_map[i][0] != kAudioChannelLabel_Unknown; i++)
if (speaker_map[i][0] == label)
return speaker_map[i][1];
return -1;
}
static bool ca_bitmap_from_ch_desc(struct ao *ao, AudioChannelLayout *layout,
uint32_t *bitmap)
{
@@ -355,14 +396,13 @@ static bool ca_bitmap_from_ch_desc(struct ao *ao, AudioChannelLayout *layout,
for (int j=0; j < ch_num && all_channels_valid; j++) {
AudioChannelLabel label = layout->mChannelDescriptions[j].mChannelLabel;
if (label == kAudioChannelLabel_UseCoordinates ||
label == kAudioChannelLabel_Unknown ||
label > kAudioChannelLabel_TopBackRight) {
const int mp_speaker_id = ca_label_to_mp_speaker_id(label);
if (mp_speaker_id < 0) {
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
"bitmap, skipping layout\n", label);
all_channels_valid = false;
} else {
*bitmap |= 1ULL << (label - 1);
*bitmap |= 1ULL << mp_speaker_id;
}
}
+1 -1
View File
@@ -22,7 +22,7 @@
#include <AudioToolbox/AudioToolbox.h>
#include <inttypes.h>
#include <stdbool.h>
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "audio/out/ao.h"
#define CA_CFSTR_ENCODING kCFStringEncodingASCII
+13 -16
View File
@@ -39,9 +39,9 @@
#include "audio/format.h"
#include "ao.h"
#include "audio/reorder_ch.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "osdep/timer.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
/**
\todo use the definitions from the win32 api headers when they define these
@@ -388,11 +388,11 @@ static int init(struct ao *ao)
WAVEFORMATEXTENSIBLE wformat;
DSBUFFERDESC dsbpridesc;
DSBUFFERDESC dsbdesc;
int format = ao->format;
int format = af_fmt_from_planar(ao->format);
int rate = ao->samplerate;
if (AF_FORMAT_IS_AC3(format))
format = AF_FORMAT_AC3_NE;
format = AF_FORMAT_AC3;
else {
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_waveext(&sel);
@@ -400,14 +400,14 @@ static int init(struct ao *ao)
return -1;
}
switch (format) {
case AF_FORMAT_AC3_NE:
case AF_FORMAT_AC3:
case AF_FORMAT_S24_LE:
case AF_FORMAT_S16_LE:
case AF_FORMAT_U8:
break;
default:
MP_VERBOSE(ao, "format %s not supported defaulting to Signed 16-bit Little-Endian\n",
af_fmt2str_short(format));
af_fmt_to_str(format));
format = AF_FORMAT_S16_LE;
}
//set our audio parameters
@@ -416,7 +416,7 @@ static int init(struct ao *ao)
ao->bps = ao->channels.num * rate * (af_fmt2bits(format) >> 3);
int buffersize = ao->bps; // space for 1 sec
MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate,
ao->channels.num, af_fmt2str_short(format));
ao->channels.num, af_fmt_to_str(format));
MP_VERBOSE(ao, "Buffersize:%d bytes (%d msec)\n",
buffersize, buffersize / ao->bps * 1000);
@@ -596,7 +596,7 @@ static int get_space(struct ao *ao)
int space = check_free_buffer_size(ao);
if (space < p->min_free_space)
return 0;
return space - p->min_free_space;
return (space - p->min_free_space) / ao->sstride;
}
/**
@@ -606,9 +606,10 @@ static int get_space(struct ao *ao)
\param flags currently unused
\return number of played bytes
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
int space = check_free_buffer_size(ao);
if (space < len)
@@ -616,7 +617,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
if (!(flags & AOPLAY_FINAL_CHUNK))
len = (len / p->outburst) * p->outburst;
return write_buffer(ao, data, len);
return write_buffer(ao, data[0], len) / ao->sstride;
}
/**
@@ -634,12 +635,8 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_dsound = {
.info = &(const struct ao_info) {
"Windows DirectSound audio output",
"dsound",
"Gabor Szecsi <deje@miki.hu>",
""
},
.description = "Windows DirectSound audio output",
.name = "dsound",
.init = init,
.uninit = uninit,
.control = control,
+188 -154
View File
@@ -4,6 +4,8 @@
* Copyleft 2001 by Felix Bünemann (atmosfear@users.sf.net)
* and Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de)
*
* Copyleft 2013 by William Light <wrl@illest.net> for the mpv project
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@@ -27,31 +29,30 @@
#include <unistd.h>
#include "config.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
#include "mpvcore/mp_ring.h"
#include "misc/ring.h"
#include <jack/jack.h>
//! maximum number of channels supported, avoids lots of mallocs
#define MAX_CHANS MP_NUM_CHANNELS
//! size of one chunk, if this is too small MPlayer will start to "stutter"
//! after a short time of playback
#define CHUNK_SIZE (24 * 1024)
#define CHUNK_SIZE (8 * 1024)
//! number of "virtual" chunks the buffer consists of
#define NUM_CHUNKS 8
struct port_ring {
jack_port_t *port;
struct mp_ring *ring;
};
struct priv {
jack_port_t * ports[MAX_CHANS];
int num_ports; // Number of used ports == number of channels
jack_client_t *client;
int outburst;
float jack_latency;
char *cfg_port;
char *cfg_client_name;
@@ -60,76 +61,49 @@ struct priv {
int autostart;
int stdlayout;
volatile int paused;
volatile int underrun; // signals if an underrun occured
volatile int underrun;
volatile float callback_interval;
volatile float callback_time;
struct mp_ring *ring; // buffer for audio data
int num_ports;
struct port_ring ports[MP_NUM_CHANNELS];
};
static void silence(float **bufs, int cnt, int num_bufs);
struct deinterleave {
float **bufs;
int num_bufs;
int cur_buf;
int pos;
};
static void deinterleave(void *info, void *src, int len)
{
struct deinterleave *di = info;
float *s = src;
int i;
len /= sizeof(float);
for (i = 0; i < len; i++) {
di->bufs[di->cur_buf++][di->pos] = s[i];
if (di->cur_buf >= di->num_bufs) {
di->cur_buf = 0;
di->pos++;
}
}
}
/**
* \brief read data from buffer and splitting it into channels
* \param bufs num_bufs float buffers, each will contain the data of one channel
* \param cnt number of samples to read per channel
* \param num_bufs number of channels to split the data into
* \return number of samples read per channel, equals cnt unless there was too
* little data in the buffer
*
* Assumes the data in the buffer is of type float, the number of bytes
* read is res * num_bufs * sizeof(float), where res is the return value.
* If there is not enough data in the buffer remaining parts will be filled
* with silence.
*/
static int read_buffer(struct mp_ring *ring, float **bufs, int cnt, int num_bufs)
{
struct deinterleave di = {
bufs, num_bufs, 0, 0
};
int buffered = mp_ring_buffered(ring);
if (cnt * sizeof(float) * num_bufs > buffered) {
silence(bufs, cnt, num_bufs);
cnt = buffered / sizeof(float) / num_bufs;
}
mp_ring_read_cb(ring, &di, cnt * num_bufs * sizeof(float), deinterleave);
return cnt;
}
// end ring buffer stuff
/**
* \brief fill the buffers with silence
* \param bufs num_bufs float buffers, each will contain the data of one channel
* \param cnt number of samples in each buffer
* \param num_bufs number of buffers
*/
static void silence(float **bufs, int cnt, int num_bufs)
static void
silence(float *buf, jack_nframes_t nframes)
{
int i;
for (i = 0; i < num_bufs; i++)
memset(bufs[i], 0, cnt * sizeof(float));
memset(buf, 0, nframes * sizeof(*buf));
}
static int
process_port(struct ao *ao, struct port_ring *pr, jack_nframes_t nframes)
{
struct priv *p = ao->priv;
int buffered;
float *buf;
buf = jack_port_get_buffer(pr->port, nframes);
if (p->paused || p->underrun) {
silence(buf, nframes);
return 0;
}
buffered = mp_ring_buffered(pr->ring) / sizeof(float);
if (buffered < nframes) {
mp_ring_read(pr->ring, (void *) buf, buffered * sizeof(float));
silence(&buf[buffered], nframes - buffered);
return 1;
}
mp_ring_read(pr->ring, (void *) buf, nframes * sizeof(float));
return 0;
}
/**
@@ -140,18 +114,23 @@ static void silence(float **bufs, int cnt, int num_bufs)
*
* Write silence into buffers if paused or an underrun occured
*/
static int outputaudio(jack_nframes_t nframes, void *arg)
static int
process(jack_nframes_t nframes, void *arg)
{
struct ao *ao = arg;
struct priv *p = ao->priv;
float *bufs[MAX_CHANS];
int i;
for (i = 0; i < p->num_ports; i++)
bufs[i] = jack_port_get_buffer(p->ports[i], nframes);
if (p->paused || p->underrun || !p->ring)
silence(bufs, nframes, p->num_ports);
else if (read_buffer(p->ring, bufs, nframes, p->num_ports) < nframes)
int i, underrun;
underrun = 0;
for (i = 0; i < p->num_ports; i++) {
if (process_port(ao, &p->ports[i], nframes))
underrun = 1;
}
if (underrun)
p->underrun = 1;
if (p->estimate) {
float now = mp_time_us() / 1000000.0;
float diff = p->callback_time + p->callback_interval - now;
@@ -161,117 +140,163 @@ static int outputaudio(jack_nframes_t nframes, void *arg)
p->callback_time = now;
p->callback_interval = (float)nframes / (float)ao->samplerate;
}
return 0;
}
static int
connect_to_outports(struct ao *ao)
{
struct priv *p = ao->priv;
char *port_name = (p->cfg_port && p->cfg_port[0]) ? p->cfg_port : NULL;
const char **matching_ports = NULL;
int port_flags = JackPortIsInput;
int i;
if (!port_name)
port_flags |= JackPortIsPhysical;
matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags);
if (!matching_ports || !matching_ports[0]) {
MP_FATAL(ao, "no ports to connect to\n");
goto err_get_ports;
}
for (i = 0; i < p->num_ports && matching_ports[i]; i++) {
if (jack_connect(p->client, jack_port_name(p->ports[i].port),
matching_ports[i])) {
MP_FATAL(ao, "connecting failed\n");
goto err_connect;
}
}
free(matching_ports);
return 0;
err_connect:
free(matching_ports);
err_get_ports:
return -1;
}
static int
create_ports(struct ao *ao, int nports)
{
struct priv *p = ao->priv;
struct port_ring *pr;
char pname[30];
int i;
for (i = 0; i < nports; i++) {
pr = &p->ports[i];
snprintf(pname, sizeof(pname), "out_%d", i);
pr->port = jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (!pr->port) {
MP_FATAL(ao, "not enough ports available\n");
goto err_port_register;
}
pr->ring = mp_ring_new(p, NUM_CHUNKS * CHUNK_SIZE);
}
p->num_ports = nports;
return 0;
err_port_register:
return -1;
}
static int init(struct ao *ao)
{
struct priv *p = ao->priv;
const char **matching_ports = NULL;
char *port_name = p->cfg_port && p->cfg_port[0] ? p->cfg_port : NULL;
jack_options_t open_options = JackNullOption;
int port_flags = JackPortIsInput;
int i;
struct mp_chmap_sel sel = {0};
jack_options_t open_options;
if (p->stdlayout == 0) {
ao->format = AF_FORMAT_FLOATP;
switch (p->stdlayout) {
case 0:
mp_chmap_sel_add_waveext(&sel);
} else if (p->stdlayout == 1) {
break;
case 1:
mp_chmap_sel_add_alsa_def(&sel);
} else {
break;
default:
mp_chmap_sel_add_any(&sel);
}
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
goto err_out;
goto err_chmap;
open_options = JackNullOption;
if (!p->autostart)
open_options |= JackNoStartServer;
p->client = jack_client_open(p->cfg_client_name, open_options, NULL);
if (!p->client) {
MP_FATAL(ao, "cannot open server\n");
goto err_out;
}
jack_set_process_callback(p->client, outputaudio, ao);
// list matching ports if connections should be made
if (p->connect) {
if (!port_name)
port_flags |= JackPortIsPhysical;
matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags);
if (!matching_ports || !matching_ports[0]) {
MP_FATAL(ao, "no physical ports available\n");
goto err_out;
}
i = 1;
p->num_ports = ao->channels.num;
while (matching_ports[i])
i++;
if (p->num_ports > i)
p->num_ports = i;
goto err_client_open;
}
// create out output ports
for (i = 0; i < p->num_ports; i++) {
char pname[30];
snprintf(pname, 30, "out_%d", i);
p->ports[i] =
jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (!p->ports[i]) {
MP_FATAL(ao, "not enough ports available\n");
goto err_out;
}
}
if (create_ports(ao, ao->channels.num))
goto err_create_ports;
jack_set_process_callback(p->client, process, ao);
if (jack_activate(p->client)) {
MP_FATAL(ao, "activate failed\n");
goto err_out;
}
for (i = 0; i < p->num_ports; i++) {
if (jack_connect(p->client, jack_port_name(p->ports[i]),
matching_ports[i]))
{
MP_FATAL(ao, "connecting failed\n");
goto err_out;
}
goto err_activate;
}
ao->samplerate = jack_get_sample_rate(p->client);
if (p->connect)
if (connect_to_outports(ao))
goto err_connect;
jack_latency_range_t jack_latency_range;
jack_port_get_latency_range(p->ports[0], JackPlaybackLatency,
jack_port_get_latency_range(p->ports[0].port, JackPlaybackLatency,
&jack_latency_range);
p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client))
/ (float)ao->samplerate;
p->callback_interval = 0;
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports))
goto err_out;
goto err_chmap_sel_get_def;
ao->format = AF_FORMAT_FLOAT_NE;
int unitsize = ao->channels.num * sizeof(float);
p->outburst = (CHUNK_SIZE + unitsize - 1) / unitsize * unitsize;
p->ring = mp_ring_new(p, NUM_CHUNKS * p->outburst);
free(matching_ports);
return 0;
err_out:
free(matching_ports);
if (p->client)
jack_client_close(p->client);
err_chmap_sel_get_def:
err_connect:
jack_deactivate(p->client);
err_activate:
err_create_ports:
jack_client_close(p->client);
err_client_open:
err_chmap:
return -1;
}
static float get_delay(struct ao *ao)
{
struct priv *p = ao->priv;
int buffered = mp_ring_buffered(p->ring); // could be less
int buffered = mp_ring_buffered(p->ports[0].ring); // could be less
float in_jack = p->jack_latency;
if (p->estimate && p->callback_interval > 0) {
float elapsed = mp_time_us() / 1000000.0 - p->callback_time;
in_jack += p->callback_interval - elapsed;
if (in_jack < 0)
in_jack = 0;
}
return (float)buffered / (float)ao->bps + in_jack;
}
@@ -281,8 +306,12 @@ static float get_delay(struct ao *ao)
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
int i;
p->paused = 1;
mp_ring_reset(p->ring);
for (i = 0; i < p->num_ports; i++)
mp_ring_reset(p->ports[i].ring);
p->paused = 0;
}
@@ -290,10 +319,10 @@ static void reset(struct ao *ao)
static void uninit(struct ao *ao, bool immed)
{
struct priv *p = ao->priv;
if (!immed)
mp_sleep_us(get_delay(ao) * 1000 * 1000);
// HACK, make sure jack doesn't loop-output dirty buffers
reset(ao);
mp_sleep_us(100 * 1000);
jack_client_close(p->client);
}
@@ -319,30 +348,35 @@ static void audio_resume(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->ring);
return mp_ring_available(p->ports[0].ring) / ao->sstride;
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
if (!(flags & AOPLAY_FINAL_CHUNK))
len -= len % p->outburst;
struct port_ring *pr;
int i, len, ret;
len = samples * ao->sstride;
ret = 0;
for (i = 0; i < p->num_ports; i++) {
pr = &p->ports[i];
ret = mp_ring_write(pr->ring, data[i], len);
}
p->underrun = 0;
return mp_ring_write(p->ring, data, len);
return ret / ao->sstride;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_jack = {
.info = &(const struct ao_info) {
"JACK audio output",
"jack",
"Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
"based on ao_sdl.c"
},
.description = "JACK audio output",
.name = "jack",
.init = init,
.uninit = uninit,
.get_space = get_space,
@@ -364,7 +398,7 @@ const struct ao_driver audio_out_jack = {
OPT_FLAG("autostart", autostart, 0),
OPT_FLAG("connect", connect, 0),
OPT_CHOICE("std-channel-layout", stdlayout, 0,
({"waveext", 0}, {"alsa", 1}, {"any", 2})),
({"waveext", 0}, {"alsa", 1}, {"any", 2})),
{0}
},
};
+96 -288
View File
@@ -22,36 +22,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <libavutil/common.h>
#include <libavutil/audioconvert.h>
#include "compat/libav.h"
#include "config.h"
#include "mpvcore/options.h"
#include "mpvcore/mp_common.h"
#include "options/options.h"
#include "common/common.h"
#include "audio/format.h"
#include "audio/reorder_ch.h"
#include "audio/fmt-conversion.h"
#include "talloc.h"
#include "ao.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "mpvcore/encode_lavc.h"
static const char *sample_padding_signed = "\x00\x00\x00\x00";
static const char *sample_padding_u8 = "\x80";
static const char *sample_padding_float = "\x00\x00\x00\x00";
#include "common/encode_lavc.h"
struct priv {
uint8_t *buffer;
size_t buffer_size;
AVStream *stream;
bool planarize;
int pcmhack;
int aframesize;
int aframecount;
int offset;
int offset_left;
int64_t savepts;
int framecount;
int64_t lastpts;
@@ -63,13 +58,44 @@ struct priv {
int worst_time_base_is_stream;
};
static void select_format(struct ao *ao, AVCodec *codec)
{
int best_score = INT_MIN;
int best_format = 0;
// Check the encoder's list of supported formats.
for (const enum AVSampleFormat *sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat)
{
int fmt = af_from_avformat(*sampleformat);
if (!fmt) {
MP_WARN(ao, "unsupported lavc format %s",
av_get_sample_fmt_name(*sampleformat));
continue;
}
int score = af_format_conversion_score(fmt, ao->format);
if (score > best_score) {
best_score = score;
best_format = fmt;
}
}
if (best_format) {
ao->format = best_format;
} else {
MP_ERR(ao, "sample format not found\n"); // shouldn't happen
}
}
// open & setup audio device
static int init(struct ao *ao)
{
struct priv *ac = talloc_zero(ao, struct priv);
const enum AVSampleFormat *sampleformat;
AVCodec *codec;
ao->priv = ac;
if (!encode_lavc_available(ao->encode_lavc_ctx)) {
MP_ERR(ao, "the option --o (output file) must be specified\n");
return -1;
@@ -106,141 +132,10 @@ static int init(struct ao *ao)
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
{
// first check if the selected format is somewhere in the list of
// supported formats by the codec
for (sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat) {
switch (*sampleformat) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
if (ao->format == AF_FORMAT_U8)
goto out_search;
break;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
if (ao->format == AF_FORMAT_S16_BE)
goto out_search;
if (ao->format == AF_FORMAT_S16_LE)
goto out_search;
break;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
if (ao->format == AF_FORMAT_S32_BE)
goto out_search;
if (ao->format == AF_FORMAT_S32_LE)
goto out_search;
break;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
if (ao->format == AF_FORMAT_FLOAT_BE)
goto out_search;
if (ao->format == AF_FORMAT_FLOAT_LE)
goto out_search;
break;
// FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP?
default:
break;
}
}
out_search:
;
}
if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) {
// if the selected format is not supported, we have to pick the first
// one we CAN support
// note: not needing to select endianness here, as the switch() below
// does that anyway for us
for (sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat) {
switch (*sampleformat) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
ao->format = AF_FORMAT_U8;
goto out_takefirst;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
ao->format = AF_FORMAT_S16_NE;
goto out_takefirst;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
ao->format = AF_FORMAT_S32_NE;
goto out_takefirst;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
ao->format = AF_FORMAT_FLOAT_NE;
goto out_takefirst;
// FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP?
default:
break;
}
}
out_takefirst:
;
}
switch (ao->format) {
// now that we have chosen a format, set up the fields for it, boldly
// switching endianness if needed (mplayer code will convert for us
// anyway, but ffmpeg always expects native endianness)
case AF_FORMAT_U8:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8;
ac->sample_size = 1;
ac->sample_padding = sample_padding_u8;
ao->format = AF_FORMAT_U8;
break;
default:
case AF_FORMAT_S16_BE:
case AF_FORMAT_S16_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
ac->sample_size = 2;
ac->sample_padding = sample_padding_signed;
ao->format = AF_FORMAT_S16_NE;
break;
case AF_FORMAT_S32_BE:
case AF_FORMAT_S32_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
ac->sample_size = 4;
ac->sample_padding = sample_padding_signed;
ao->format = AF_FORMAT_S32_NE;
break;
case AF_FORMAT_FLOAT_BE:
case AF_FORMAT_FLOAT_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT;
ac->sample_size = 4;
ac->sample_padding = sample_padding_float;
ao->format = AF_FORMAT_FLOAT_NE;
break;
}
// detect if we have to planarize
ac->planarize = false;
{
bool found_format = false;
bool found_planar_format = false;
for (sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat) {
if (*sampleformat == ac->stream->codec->sample_fmt)
found_format = true;
if (*sampleformat ==
av_get_planar_sample_fmt(ac->stream->codec->sample_fmt))
found_planar_format = true;
}
if (!found_format && found_planar_format) {
ac->stream->codec->sample_fmt =
av_get_planar_sample_fmt(ac->stream->codec->sample_fmt);
ac->planarize = true;
}
if (!found_format && !found_planar_format) {
// shouldn't happen
MP_ERR(ao, "sample format not found\n");
}
}
select_format(ao, codec);
ac->sample_size = af_fmt2bits(ao->format) / 8;
ac->stream->codec->sample_fmt = af_to_avformat(ao->format);
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
@@ -270,33 +165,14 @@ out_takefirst:
ac->savepts = MP_NOPTS_VALUE;
ac->lastpts = MP_NOPTS_VALUE;
ac->offset = ac->stream->codec->sample_rate *
encode_lavc_getoffset(ao->encode_lavc_ctx, ac->stream);
ac->offset_left = ac->offset;
ao->untimed = true;
ao->priv = ac;
if (ac->planarize)
MP_WARN(ao, "need to planarize audio data\n");
return 0;
}
static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
{
int i;
if (sz == 1) {
memset(buf, cnt, *(char *)padding);
return;
}
for (i = 0; i < cnt; ++i)
memcpy((char *) buf + i * sz, padding, sz);
}
// close audio device
static int encode(struct ao *ao, double apts, void *data);
static int play(struct ao *ao, void *data, int len, int flags);
static int encode(struct ao *ao, double apts, void **data);
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *ac = ao->priv;
@@ -308,29 +184,10 @@ static void uninit(struct ao *ao, bool cut_audio)
}
if (ac->buffer) {
if (ao->buffer.len > 0) {
// TRICK: append aframesize-1 samples to the end, then play() will
// encode all it can
size_t extralen =
(ac->aframesize - 1) * ao->channels.num * ac->sample_size;
void *paddingbuf = talloc_size(ao, ao->buffer.len + extralen);
memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
fill_with_padding((char *) paddingbuf + ao->buffer.len,
extralen / ac->sample_size,
ac->sample_size, ac->sample_padding);
int written = play(ao, paddingbuf, ao->buffer.len + extralen, 0);
if (written < ao->buffer.len) {
MP_ERR(ao, "did not write enough data at the end\n");
}
talloc_free(paddingbuf);
ao->buffer.len = 0;
}
double outpts = ac->expected_next_pts;
if (!ectx->options->rawts && ectx->options->copyts)
outpts += ectx->discontinuity_pts_offset;
outpts += encode_lavc_getoffset(ectx, ac->stream);
while (encode(ao, outpts, NULL) > 0) ;
}
@@ -342,11 +199,11 @@ static int get_space(struct ao *ao)
{
struct priv *ac = ao->priv;
return ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount;
return ac->aframesize * ac->framecount;
}
// must get exactly ac->aframesize amount of data
static int encode(struct ao *ao, double apts, void *data)
static int encode(struct ao *ao, double apts, void **data)
{
AVFrame *frame;
AVPacket packet;
@@ -369,22 +226,11 @@ static int encode(struct ao *ao, double apts, void *data)
frame = avcodec_alloc_frame();
frame->nb_samples = ac->aframesize;
if (ac->planarize) {
void *data2 = talloc_size(ao, ac->aframesize * ao->channels.num *
ac->sample_size);
reorder_to_planar(data2, data, ac->sample_size, ao->channels.num,
ac->aframesize);
data = data2;
}
assert(ao->channels.num <= AV_NUM_DATA_POINTERS);
for (int n = 0; n < ao->channels.num; n++)
frame->extended_data[n] = data[n];
size_t audiolen = ac->aframesize * ao->channels.num * ac->sample_size;
if (avcodec_fill_audio_frame(frame, ao->channels.num,
ac->stream->codec->sample_fmt, data,
audiolen, 1))
{
MP_ERR(ao, "error filling\n");
return -1;
}
frame->linesize[0] = frame->nb_samples * ao->sstride;
if (ectx->options->rawts || ectx->options->copyts) {
// real audio pts
@@ -414,11 +260,6 @@ static int encode(struct ao *ao, double apts, void *data)
}
avcodec_free_frame(&frame);
if (ac->planarize) {
talloc_free(data);
data = NULL;
}
}
else
{
@@ -471,26 +312,47 @@ static int encode(struct ao *ao, double apts, void *data)
return packet.size;
}
// plays 'len' bytes of 'data'
// it should round it down to frame sizes
// return: number of bytes played
static int play(struct ao *ao, void *data, int len, int flags)
// this should round samples down to frame sizes
// return: number of samples played
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
int bufpos = 0;
int64_t ptsoffset;
void *paddingbuf = NULL;
double nextpts;
double pts = ao->pts;
double outpts;
len /= ac->sample_size * ao->channels.num;
if (!encode_lavc_start(ectx)) {
MP_WARN(ao, "not ready yet for encoding audio\n");
return 0;
}
size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1;
if (flags & AOPLAY_FINAL_CHUNK) {
int written = 0;
if (samples > 0) {
void *tmp = talloc_new(NULL);
size_t bytelen = samples * ao->sstride;
size_t extralen = (ac->aframesize - 1) * ao->sstride;
void *padded[MP_NUM_CHANNELS];
for (int n = 0; n < num_planes; n++) {
padded[n] = talloc_size(tmp, bytelen + extralen);
memcpy(padded[n], data[n], bytelen);
af_fill_silence((char *)padded[n] + bytelen, extralen, ao->format);
}
// No danger of recursion, because AOPLAY_FINAL_CHUNK not set
written = play(ao, padded, (bytelen + extralen) / ao->sstride, 0);
if (written < samples) {
MP_ERR(ao, "did not write enough data at the end\n");
}
talloc_free(tmp);
}
return FFMIN(written, samples);
}
if (pts == MP_NOPTS_VALUE) {
MP_WARN(ao, "frame without pts, please report; synthesizing pts instead\n");
// synthesize pts from previous expected next pts
@@ -537,63 +399,10 @@ static int play(struct ao *ao, void *data, int len, int flags)
// Absurd, as this range MUST contain at least one multiple of B.
}
ptsoffset = ac->offset;
// this basically just edits ao->apts for syncing purposes
if (ectx->options->copyts || ectx->options->rawts) {
// we do not send time sync data to the video side,
// but we always need the exact pts, even if zero
} else {
// here we must "simulate" the pts editing
// 1. if we have to skip stuff, we skip it
// 2. if we have to add samples, we add them
// 3. we must still adjust ptsoffset appropriately for AV sync!
// invariant:
// if no partial skipping is done, the first frame gets ao->apts passed as pts!
if (ac->offset_left < 0) {
if (ac->offset_left <= -len) {
// skip whole frame
ac->offset_left += len;
return len * ac->sample_size * ao->channels.num;
} else {
// skip part of this frame, buffer/encode the rest
bufpos -= ac->offset_left;
ptsoffset += ac->offset_left;
ac->offset_left = 0;
}
} else if (ac->offset_left > 0) {
// make a temporary buffer, filled with zeroes at the start
// (don't worry, only happens once)
paddingbuf = talloc_size(ac, ac->sample_size * ao->channels.num *
(ac->offset_left + len));
fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size,
ac->sample_padding);
data = (char *) paddingbuf + ac->sample_size * ao->channels.num *
ac->offset_left;
bufpos -= ac->offset_left; // yes, negative!
ptsoffset += ac->offset_left;
ac->offset_left = 0;
// now adjust the bufpos so the final value of bufpos is positive!
/*
int cnt = (len - bufpos) / ac->aframesize;
int finalbufpos = bufpos + cnt * ac->aframesize;
*/
int finalbufpos = len - (len - bufpos) % ac->aframesize;
if (finalbufpos < 0) {
MP_WARN(ao, "cannot attain the "
"exact requested audio sync; shifting by %d frames\n",
-finalbufpos);
bufpos -= finalbufpos;
}
}
}
// Fix and apply the discontinuity pts offset.
if (!ectx->options->rawts && ectx->options->copyts) {
// fix the discontinuity pts offset
nextpts = pts + ptsoffset / (double) ao->samplerate;
nextpts = pts;
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
@@ -606,39 +415,38 @@ static int play(struct ao *ao, void *data, int len, int flags)
outpts = pts + ectx->discontinuity_pts_offset;
}
else
else {
outpts = pts;
}
while (len - bufpos >= ac->aframesize) {
encode(ao,
outpts + (bufpos + ptsoffset) / (double) ao->samplerate + encode_lavc_getoffset(ectx, ac->stream),
(char *) data + ac->sample_size * bufpos * ao->channels.num);
// Shift pts by the pts offset first.
outpts += encode_lavc_getoffset(ectx, ac->stream);
while (samples - bufpos >= ac->aframesize) {
void *start[MP_NUM_CHANNELS];
for (int n = 0; n < num_planes; n++)
start[n] = (char *)data[n] + bufpos * ao->sstride;
encode(ao, outpts + bufpos / (double) ao->samplerate, start);
bufpos += ac->aframesize;
}
talloc_free(paddingbuf);
// calculate expected pts of next audio frame
ac->expected_next_pts = pts + (bufpos + ptsoffset) / (double) ao->samplerate;
// Calculate expected pts of next audio frame (input side).
ac->expected_next_pts = pts + bufpos / (double) ao->samplerate;
// Set next allowed input pts value (input side).
if (!ectx->options->rawts && ectx->options->copyts) {
// set next allowed output pts value
nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
}
return bufpos * ac->sample_size * ao->channels.num;
return bufpos;
}
const struct ao_driver audio_out_lavc = {
.encode = true,
.info = &(const struct ao_info) {
"audio encoding using libavcodec",
"lavc",
"Rudolf Polzer <divVerent@xonotic.org>",
""
},
.description = "audio encoding using libavcodec",
.name = "lavc",
.init = init,
.uninit = uninit,
.get_space = get_space,
+90 -31
View File
@@ -18,6 +18,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Note: this does much more than just ignoring audio output. It simulates
* (to some degree) an ideal AO.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -25,41 +30,66 @@
#include "config.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "common/msg.h"
#include "audio/format.h"
#include "ao.h"
struct priv {
bool paused;
double last_time;
float buffered_bytes;
int buffersize;
int outburst;
bool playing_final;
float buffered; // samples
int buffersize; // samples
int untimed;
float bufferlen; // seconds
// Minimal unit of audio samples that can be written at once. If play() is
// called with sizes not aligned to this, a rounded size will be returned.
// (This is not needed by the AO API, but many AOs behave this way.)
int outburst; // samples
};
static void drain(struct ao *ao)
{
struct priv *priv = ao->priv;
if (ao->untimed) {
priv->buffered = 0;
return;
}
if (priv->paused)
return;
double now = mp_time_sec();
priv->buffered_bytes -= (now - priv->last_time) * ao->bps;
if (priv->buffered_bytes < 0)
priv->buffered_bytes = 0;
if (priv->buffered > 0) {
priv->buffered -= (now - priv->last_time) * ao->samplerate;
if (priv->buffered < 0) {
if (!priv->playing_final)
MP_ERR(ao, "buffer underrun\n");
priv->buffered = 0;
}
}
priv->last_time = now;
}
static int init(struct ao *ao)
{
struct priv *priv = talloc_zero(ao, struct priv);
ao->priv = priv;
struct priv *priv = ao->priv;
ao->untimed = priv->untimed;
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
int samplesize = af_fmt2bits(ao->format) / 8;
priv->outburst = 256 * ao->channels.num * samplesize;
// A "buffer" for about 0.2 seconds of audio
priv->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * priv->outburst;
// A "buffer" for this many seconds of audio
int bursts = (int)(ao->samplerate * priv->bufferlen + 1) / priv->outburst;
priv->buffersize = priv->outburst * bursts;
priv->last_time = mp_time_sec();
return 0;
@@ -68,25 +98,36 @@ static int init(struct ao *ao)
// close audio device
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *priv = ao->priv;
if (!cut_audio && !priv->paused)
mp_sleep_us(1000.0 * 1000.0 * priv->buffered / ao->samplerate);
}
// stop playing and empty buffers (for seeking/pause)
static void reset(struct ao *ao)
{
struct priv *priv = ao->priv;
priv->buffered_bytes = 0;
priv->buffered = 0;
priv->playing_final = false;
}
// stop playing, keep buffers (for pause)
static void pause(struct ao *ao)
{
// for now, just call reset();
reset(ao);
struct priv *priv = ao->priv;
drain(ao);
priv->paused = true;
}
// resume playing, after audio_pause()
// resume playing, after pause()
static void resume(struct ao *ao)
{
struct priv *priv = ao->priv;
drain(ao);
priv->paused = false;
priv->last_time = mp_time_sec();
}
static int get_space(struct ao *ao)
@@ -94,18 +135,28 @@ static int get_space(struct ao *ao)
struct priv *priv = ao->priv;
drain(ao);
return priv->buffersize - priv->buffered_bytes;
return priv->buffersize - priv->buffered;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int accepted;
int maxbursts = (priv->buffersize - priv->buffered_bytes) / priv->outburst;
int playbursts = len / priv->outburst;
int bursts = playbursts > maxbursts ? maxbursts : playbursts;
priv->buffered_bytes += bursts * priv->outburst;
return bursts * priv->outburst;
resume(ao);
priv->playing_final = flags & AOPLAY_FINAL_CHUNK;
if (priv->playing_final) {
// Last audio chunk - don't round to outburst.
accepted = MPMIN(priv->buffersize - priv->buffered, samples);
} else {
int maxbursts = (priv->buffersize - priv->buffered) / priv->outburst;
int playbursts = samples / priv->outburst;
int bursts = playbursts > maxbursts ? maxbursts : playbursts;
accepted = bursts * priv->outburst;
}
priv->buffered += accepted;
return accepted;
}
static float get_delay(struct ao *ao)
@@ -113,16 +164,14 @@ static float get_delay(struct ao *ao)
struct priv *priv = ao->priv;
drain(ao);
return priv->buffered_bytes / ao->bps;
return priv->buffered / (double)ao->samplerate;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_null = {
.info = &(const struct ao_info) {
"Null audio output",
"null",
"Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
"",
},
.description = "Null audio output",
.name = "null",
.init = init,
.uninit = uninit,
.reset = reset,
@@ -131,5 +180,15 @@ const struct ao_driver audio_out_null = {
.get_delay = get_delay,
.pause = pause,
.resume = resume,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.bufferlen = 0.2,
.outburst = 256,
},
.options = (const struct m_option[]) {
OPT_FLAG("untimed", untimed, 0),
OPT_FLOATRANGE("buffer", bufferlen, 0, 0, 100),
OPT_INTRANGE("outburst", outburst, 0, 1, 100000),
{0}
},
};
+21 -31
View File
@@ -35,22 +35,22 @@
#include <AL/alext.h>
#endif
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
#define MAX_CHANS MP_NUM_CHANNELS
#define NUM_BUF 128
#define CHUNK_SIZE 512
#define CHUNK_SAMPLES (CHUNK_SIZE / 2)
static ALuint buffers[MAX_CHANS][NUM_BUF];
static ALuint sources[MAX_CHANS];
static int cur_buf[MAX_CHANS];
static int unqueue_buf[MAX_CHANS];
static int16_t *tmpbuf;
static struct ao *ao_data;
@@ -79,18 +79,18 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
return CONTROL_UNKNOWN;
}
static int validate_device_opt(const m_option_t *opt, struct bstr name,
struct bstr param)
static int validate_device_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
if (bstr_equals0(param, "help")) {
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_TRUE) {
mp_msg(MSGT_AO, MSGL_FATAL, "Device listing not supported.\n");
mp_fatal(log, "Device listing not supported.\n");
return M_OPT_EXIT;
}
const char *list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
mp_msg(MSGT_AO, MSGL_INFO, "OpenAL devices:\n");
mp_info(log, "OpenAL devices:\n");
while (list && *list) {
mp_msg(MSGT_AO, MSGL_INFO, " '%s'\n", list);
mp_info(log, " '%s'\n", list);
list = list + strlen(list) + 1;
}
return M_OPT_EXIT - 1;
@@ -169,8 +169,7 @@ static int init(struct ao *ao)
alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
if (alcGetError(dev) == ALC_NO_ERROR && freq)
ao->samplerate = freq;
ao->format = AF_FORMAT_S16_NE;
tmpbuf = malloc(CHUNK_SIZE);
ao->format = AF_FORMAT_S16P;
return 0;
err_out:
@@ -182,7 +181,6 @@ static void uninit(struct ao *ao, bool immed)
{
ALCcontext *ctx = alcGetCurrentContext();
ALCdevice *dev = alcGetContextsDevice(ctx);
free(tmpbuf);
if (!immed) {
ALint state;
alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
@@ -251,34 +249,30 @@ static int get_space(struct ao *ao)
queued = NUM_BUF - queued - 3;
if (queued < 0)
return 0;
return queued * CHUNK_SIZE * ao->channels.num;
return queued * CHUNK_SAMPLES;
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
ALint state;
int i, j, k;
int ch;
int16_t *d = data;
len /= ao->channels.num * CHUNK_SIZE;
for (i = 0; i < len; i++) {
for (ch = 0; ch < ao->channels.num; ch++) {
for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao->channels.num)
tmpbuf[j] = d[k];
alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf,
int num = samples / CHUNK_SAMPLES;
for (int i = 0; i < num; i++) {
for (int ch = 0; ch < ao->channels.num; ch++) {
int16_t *d = data[ch];
d += i * CHUNK_SAMPLES;
alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, d,
CHUNK_SIZE, ao->samplerate);
alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]);
cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF;
}
d += ao->channels.num * CHUNK_SIZE / 2;
}
alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) // checked here in case of an underrun
alSourcePlayv(ao->channels.num, sources);
return len * ao->channels.num * CHUNK_SIZE;
return num * CHUNK_SAMPLES;
}
static float get_delay(struct ao *ao)
@@ -286,18 +280,14 @@ static float get_delay(struct ao *ao)
ALint queued;
unqueue_buffers();
alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
return queued * CHUNK_SIZE / 2 / (float)ao->samplerate;
return queued * CHUNK_SAMPLES / (float)ao->samplerate;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_openal = {
.info = &(const struct ao_info) {
"OpenAL audio output",
"openal",
"Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
""
},
.description = "OpenAL audio output",
.name = "openal",
.init = init,
.uninit = uninit,
.control = control,
+87 -44
View File
@@ -3,6 +3,7 @@
*
* This file is part of MPlayer.
*
* Original author: A'rpi
* Support for >2 output channels added 2001-11-25
* - Steve Davies <steve@daviesfam.org>
*
@@ -34,13 +35,13 @@
#include <string.h>
#include "config.h"
#include "mpvcore/options.h"
#include "mpvcore/mp_msg.h"
#include "options/options.h"
#include "common/msg.h"
#ifdef HAVE_SYS_SOUNDCARD_H
#if HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>
#else
#ifdef HAVE_SOUNDCARD_H
#if HAVE_SOUNDCARD_H
#include <soundcard.h>
#endif
#endif
@@ -65,6 +66,19 @@ struct priv {
static const char *mixer_channels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
/* like alsa except for 6.1 and 7.1, from pcm/matrix_map.h */
static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
{0}, // empty
MP_CHMAP_INIT_MONO, // mono
MP_CHMAP2(FL, FR), // stereo
MP_CHMAP3(FL, FR, LFE), // 2.1
MP_CHMAP4(FL, FR, BL, BR), // 4.0
MP_CHMAP5(FL, FR, BL, BR, FC), // 5.0
MP_CHMAP6(FL, FR, BL, BR, FC, LFE), // 5.1
MP_CHMAP7(FL, FR, BL, BR, FC, LFE, BC), // 6.1
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), // 7.1
};
static int format_table[][2] = {
{AFMT_U8, AF_FORMAT_U8},
{AFMT_S8, AF_FORMAT_S8},
@@ -75,6 +89,18 @@ static int format_table[][2] = {
#ifdef AFMT_S24_PACKED
{AFMT_S24_PACKED, AF_FORMAT_S24_LE},
#endif
#ifdef AFMT_U24_LE
{AFMT_U24_LE, AF_FORMAT_U24_LE},
#endif
#ifdef AFMT_U24_BE
{AFMT_U24_BE, AF_FORMAT_U24_BE},
#endif
#ifdef AFMT_S24_LE
{AFMT_S24_LE, AF_FORMAT_S24_LE},
#endif
#ifdef AFMT_S24_BE
{AFMT_S24_BE, AF_FORMAT_S24_BE},
#endif
#ifdef AFMT_U32_LE
{AFMT_U32_LE, AF_FORMAT_U32_LE},
#endif
@@ -88,14 +114,14 @@ static int format_table[][2] = {
{AFMT_S32_BE, AF_FORMAT_S32_BE},
#endif
#ifdef AFMT_FLOAT
{AFMT_FLOAT, AF_FORMAT_FLOAT_NE},
{AFMT_FLOAT, AF_FORMAT_FLOAT},
#endif
// SPECIALS
#ifdef AFMT_MPEG
{AFMT_MPEG, AF_FORMAT_MPEG2},
#endif
#ifdef AFMT_AC3
{AFMT_AC3, AF_FORMAT_AC3_NE},
{AFMT_AC3, AF_FORMAT_AC3},
#endif
{-1, -1}
};
@@ -195,13 +221,14 @@ static int init(struct ao *ao)
struct priv *p = ao->priv;
int oss_format;
#ifdef SNDCTL_DSP_GETPLAYVOL
ao->no_persistent_volume = true;
#endif
const char *mchan = NULL;
if (p->cfg_oss_mixer_channel && p->cfg_oss_mixer_channel[0])
mchan = p->cfg_oss_mixer_channel;
MP_VERBOSE(ao, "%d Hz %d chans %s\n", ao->samplerate,
ao->channels.num, af_fmt2str_short(ao->format));
if (mchan) {
int fd, devs, i;
@@ -259,31 +286,49 @@ static int init(struct ao *ao)
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
#endif
ao->format = af_fmt_from_planar(ao->format);
if (AF_FORMAT_IS_AC3(ao->format)) {
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
}
ac3_retry:
if (AF_FORMAT_IS_AC3(ao->format))
ao->format = AF_FORMAT_AC3_NE;
ao->format = AF_FORMAT_AC3;
oss_format = format2oss(ao->format);
if (oss_format == -1) {
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
af_fmt2str_short(ao->format));
af_fmt_to_str(ao->format));
#if defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
#if BYTE_ORDER == BIG_ENDIAN
oss_format = AFMT_S32_BE;
#else
oss_format = AFMT_S32_LE;
#endif
ao->format = AF_FORMAT_S32;
#elif defined(AFMT_S24_LE) && defined(AFMT_S24_BE)
#if BYTE_ORDER == BIG_ENDIAN
oss_format = AFMT_S24_BE;
#else
oss_format = AFMT_S24_LE;
#endif
ao->format = AF_FORMAT_S24;
#else
#if BYTE_ORDER == BIG_ENDIAN
oss_format = AFMT_S16_BE;
#else
oss_format = AFMT_S16_LE;
#endif
ao->format = AF_FORMAT_S16_NE;
ao->format = AF_FORMAT_S16;
#endif
}
if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format) < 0 ||
oss_format != format2oss(ao->format))
{
MP_WARN(ao, "Can't set audio device %s to %s output, trying %s...\n",
p->dsp, af_fmt2str_short(ao->format),
af_fmt2str_short(AF_FORMAT_S16_NE));
ao->format = AF_FORMAT_S16_NE;
p->dsp, af_fmt_to_str(ao->format),
af_fmt_to_str(AF_FORMAT_S16));
ao->format = AF_FORMAT_S16;
goto ac3_retry;
}
@@ -293,11 +338,12 @@ ac3_retry:
return -1;
}
MP_VERBOSE(ao, "sample format: %s\n", af_fmt2str_short(ao->format));
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(ao->format));
if (!AF_FORMAT_IS_AC3(ao->format)) {
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_alsa_def(&sel);
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
int reqchannels = ao->channels.num;
@@ -348,7 +394,7 @@ ac3_retry:
// Measuring buffer size:
void *data;
p->buffersize = 0;
#ifdef HAVE_AUDIO_SELECT
#if HAVE_AUDIO_SELECT
data = malloc(p->outburst);
memset(data, 0, p->outburst);
while (p->buffersize < 0x40000) {
@@ -366,7 +412,7 @@ ac3_retry:
free(data);
if (p->buffersize == 0) {
MP_ERR(ao, "*** Your audio driver DOES NOT support select() ***\n");
MP_ERR(ao, "Recompile mpv with #undef HAVE_AUDIO_SELECT in config.h!\n");
MP_ERR(ao, "Recompile mpv with #define HAVE_AUDIO_SELECT 0 in config.h!\n");
return -1;
}
#endif
@@ -398,21 +444,23 @@ static void uninit(struct ao *ao, bool immed)
p->audio_fd = -1;
}
#ifndef SNDCTL_DSP_RESET
static void close_device(struct ao *ao)
{
struct priv *p = ao->priv;
#ifdef SNDCTL_DSP_RESET
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#endif
close(p->audio_fd);
p->audio_fd = -1;
}
#endif
// stop playing and empty buffers (for seeking/pause)
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
int oss_format;
#ifdef SNDCTL_DSP_RESET
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#else
close_device(ao);
p->audio_fd = open(p->dsp, O_WRONLY);
if (p->audio_fd < 0) {
@@ -423,6 +471,7 @@ static void reset(struct ao *ao)
#if defined(FD_CLOEXEC) && defined(F_SETFD)
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
#endif
#endif
oss_format = format2oss(ao->format);
@@ -430,10 +479,11 @@ static void reset(struct ao *ao)
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
if (!AF_FORMAT_IS_AC3(ao->format)) {
int c = ao->channels.num;
if (ao->channels.num > 2)
ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &ao->channels.num);
ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &c);
else {
int c = ao->channels.num - 1;
c--;
ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c);
}
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
@@ -450,12 +500,12 @@ static int get_space(struct ao *ao)
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
// calculate exact buffer space:
playsize = p->zz.fragments * p->zz.fragsize;
return playsize;
return playsize / ao->sstride;
}
#endif
// check buffer
#ifdef HAVE_AUDIO_SELECT
#if HAVE_AUDIO_SELECT
{
fd_set rfds;
struct timeval tv;
@@ -468,14 +518,14 @@ static int get_space(struct ao *ao)
}
#endif
return p->outburst;
return p->outburst / ao->sstride;
}
// stop playing, keep buffers (for pause)
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
p->prepause_space = get_space(ao);
p->prepause_space = get_space(ao) * ao->sstride;
#ifdef SNDCTL_DSP_RESET
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#else
@@ -486,33 +536,30 @@ static void audio_pause(struct ao *ao)
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
if (len == 0)
return len;
if (len > p->outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
len /= p->outburst;
len *= p->outburst;
}
len = write(p->audio_fd, data, len);
return len;
len = write(p->audio_fd, data[0], len);
return len / ao->sstride;
}
// resume playing, after audio_pause()
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
int fillcnt;
#ifndef SNDCTL_DSP_RESET
reset(ao);
#endif
fillcnt = get_space(ao) - p->prepause_space;
if (fillcnt > 0 && !(ao->format & AF_FORMAT_SPECIAL_MASK)) {
void *silence = calloc(fillcnt, 1);
play(ao, silence, fillcnt, 0);
free(silence);
}
int fillframes = get_space(ao) - p->prepause_space / ao->sstride;
if (fillframes > 0)
ao_play_silence(ao, fillframes);
}
// return: delay in seconds between first and last sample in buffer
@@ -542,12 +589,8 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_oss = {
.info = &(const struct ao_info) {
"OSS/ioctl audio output",
"oss",
"A'rpi",
""
},
.description = "OSS/ioctl audio output",
.name = "oss",
.init = init,
.uninit = uninit,
.control = control,
+14 -13
View File
@@ -3,6 +3,8 @@
*
* This file is part of MPlayer.
*
* Original author: Atmosfear
*
* 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
@@ -28,11 +30,11 @@
#include "talloc.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
#include "audio/format.h"
#include "audio/reorder_ch.h"
#include "ao.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#ifdef __MINGW32__
// for GetFileType to detect pipes
@@ -116,6 +118,9 @@ static int init(struct ao *ao)
if (!priv->outputfilename)
priv->outputfilename =
talloc_strdup(priv, priv->waveheader ? "audiodump.wav" : "audiodump.pcm");
ao->format = af_fmt_from_planar(ao->format);
if (priv->waveheader) {
// WAV files must have one of the following formats
@@ -125,7 +130,6 @@ static int init(struct ao *ao)
case AF_FORMAT_S24_LE:
case AF_FORMAT_S32_LE:
case AF_FORMAT_FLOAT_LE:
case AF_FORMAT_AC3_BE:
case AF_FORMAT_AC3_LE:
break;
default:
@@ -144,7 +148,7 @@ static int init(struct ao *ao)
MP_INFO(ao, "File: %s (%s)\nPCM: Samplerate: %d Hz Channels: %d Format: %s\n",
priv->outputfilename,
priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
ao->channels.num, af_fmt2str_short(ao->format));
ao->channels.num, af_fmt_to_str(ao->format));
MP_INFO(ao, "Info: Faster dumping is achieved with -no-video\n");
MP_INFO(ao, "Info: To write WAVE files use -ao pcm:waveheader (default).\n");
@@ -192,24 +196,21 @@ static int get_space(struct ao *ao)
return 65536;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
fwrite(data, len, 1, priv->fp);
fwrite(data[0], len, 1, priv->fp);
priv->data_length += len;
return len;
return len / ao->sstride;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_pcm = {
.info = &(const struct ao_info) {
"RAW PCM/WAVE file writer audio output",
"pcm",
"Atmosfear",
"",
},
.description = "RAW PCM/WAVE file writer audio output",
.name = "pcm",
.init = init,
.uninit = uninit,
.get_space = get_space,
+41 -44
View File
@@ -26,10 +26,10 @@
#include <portaudio.h>
#include "config.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
#include "audio/format.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mp_ring.h"
#include "common/msg.h"
#include "misc/ring.h"
#include "ao.h"
struct priv {
@@ -56,30 +56,30 @@ struct format_map {
static const struct format_map format_maps[] = {
// first entry is the default format
{AF_FORMAT_S16_NE, paInt16},
{AF_FORMAT_S24_NE, paInt24},
{AF_FORMAT_S32_NE, paInt32},
{AF_FORMAT_S8, paInt8},
{AF_FORMAT_U8, paUInt8},
{AF_FORMAT_FLOAT_NE, paFloat32},
{AF_FORMAT_S16, paInt16},
{AF_FORMAT_S24, paInt24},
{AF_FORMAT_S32, paInt32},
{AF_FORMAT_S8, paInt8},
{AF_FORMAT_U8, paUInt8},
{AF_FORMAT_FLOAT, paFloat32},
{AF_FORMAT_UNKNOWN, 0}
};
static bool check_pa_ret(int ret)
static bool check_pa_ret(struct mp_log *log, int ret)
{
if (ret < 0) {
mp_msg(MSGT_AO, MSGL_ERR, "[ao/portaudio] %s\n",
Pa_GetErrorText(ret));
mp_err(log, "%s\n", Pa_GetErrorText(ret));
if (ret == paUnanticipatedHostError) {
const PaHostErrorInfo* hosterr = Pa_GetLastHostErrorInfo();
mp_msg(MSGT_AO, MSGL_ERR, "[ao/portaudio] Host error: %s\n",
hosterr->errorText);
mp_err(log, "Host error: %s\n", hosterr->errorText);
}
return false;
}
return true;
}
#define CHECK_PA_RET(ret) check_pa_ret(ao->log, (ret))
static int seconds_to_bytes(struct ao *ao, double seconds)
{
return af_fmt_seconds_to_bytes(ao->format, seconds, ao->channels.num,
@@ -93,23 +93,23 @@ static int to_int(const char *s, int return_on_error)
return (s[0] && !endptr[0]) ? res : return_on_error;
}
static int find_device(const char *name)
static int find_device(struct mp_log *log, const char *name)
{
int found = paNoDevice;
if (!name)
return found;
int help = strcmp(name, "help") == 0;
int count = Pa_GetDeviceCount();
check_pa_ret(count);
check_pa_ret(log, count);
int index = to_int(name, -1);
if (help)
mp_msg(MSGT_AO, MSGL_INFO, "PortAudio devices:\n");
mp_info(log, "PortAudio devices:\n");
for (int n = 0; n < count; n++) {
const PaDeviceInfo* info = Pa_GetDeviceInfo(n);
if (help) {
if (info->maxOutputChannels < 1)
continue;
mp_msg(MSGT_AO, MSGL_INFO, " %d '%s', %d channels, latency: %.2f "
mp_info(log, " %d '%s', %d channels, latency: %.2f "
"ms, sample rate: %.0f\n", n, info->name,
info->maxOutputChannels,
info->defaultHighOutputLatency * 1000,
@@ -121,21 +121,20 @@ static int find_device(const char *name)
}
}
if (found == paNoDevice && !help)
mp_msg(MSGT_AO, MSGL_WARN, "[ao/portaudio] Device '%s' not found!\n",
name);
mp_warn(log, "Device '%s' not found!\n", name);
return found;
}
static int validate_device_opt(const m_option_t *opt, struct bstr name,
struct bstr param)
static int validate_device_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
// Note: we do not check whether the device actually exist, because this
// might break elaborate configs with several AOs trying several
// devices. We do it merely for making "help" special.
if (bstr_equals0(param, "help")) {
if (!check_pa_ret(Pa_Initialize()))
if (!check_pa_ret(log, Pa_Initialize()))
return M_OPT_EXIT;
find_device("help");
find_device(log, "help");
Pa_Terminate();
return M_OPT_EXIT - 1;
}
@@ -205,9 +204,9 @@ static void uninit(struct ao *ao, bool cut_audio)
pthread_mutex_unlock(&priv->ring_mutex);
check_pa_ret(Pa_StopStream(priv->stream));
CHECK_PA_RET(Pa_StopStream(priv->stream));
}
check_pa_ret(Pa_CloseStream(priv->stream));
CHECK_PA_RET(Pa_CloseStream(priv->stream));
}
pthread_mutex_destroy(&priv->ring_mutex);
@@ -218,14 +217,14 @@ static int init(struct ao *ao)
{
struct priv *priv = ao->priv;
if (!check_pa_ret(Pa_Initialize()))
if (!CHECK_PA_RET(Pa_Initialize()))
return -1;
pthread_mutex_init(&priv->ring_mutex, NULL);
int pa_device = Pa_GetDefaultOutputDevice();
if (priv->cfg_device && priv->cfg_device[0])
pa_device = find_device(priv->cfg_device);
pa_device = find_device(ao->log, priv->cfg_device);
if (pa_device == paNoDevice)
goto error_exit;
@@ -242,6 +241,8 @@ static int init(struct ao *ao)
= Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
};
ao->format = af_fmt_from_planar(ao->format);
const struct format_map *fmt = format_maps;
while (fmt->pa_format) {
if (fmt->mp_format == ao->format) {
@@ -262,9 +263,9 @@ static int init(struct ao *ao)
priv->framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8);
ao->bps = ao->samplerate * priv->framelen;
if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
if (!CHECK_PA_RET(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
goto error_exit;
if (!check_pa_ret(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate,
if (!CHECK_PA_RET(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate,
paFramesPerBufferUnspecified, paNoFlag,
stream_callback, ao)))
goto error_exit;
@@ -278,22 +279,22 @@ error_exit:
return -1;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
pthread_mutex_lock(&priv->ring_mutex);
int write_len = mp_ring_write(priv->ring, data, len);
int write_len = mp_ring_write(priv->ring, data[0], samples * ao->sstride);
if (flags & AOPLAY_FINAL_CHUNK)
priv->play_remaining = true;
pthread_mutex_unlock(&priv->ring_mutex);
if (Pa_IsStreamStopped(priv->stream) == 1)
check_pa_ret(Pa_StartStream(priv->stream));
CHECK_PA_RET(Pa_StartStream(priv->stream));
return write_len;
return write_len / ao->sstride;
}
static int get_space(struct ao *ao)
@@ -306,7 +307,7 @@ static int get_space(struct ao *ao)
pthread_mutex_unlock(&priv->ring_mutex);
return free;
return free / ao->sstride;
}
static float get_delay(struct ao *ao)
@@ -331,7 +332,7 @@ static void reset(struct ao *ao)
struct priv *priv = ao->priv;
if (Pa_IsStreamStopped(priv->stream) != 1)
check_pa_ret(Pa_AbortStream(priv->stream));
CHECK_PA_RET(Pa_AbortStream(priv->stream));
pthread_mutex_lock(&priv->ring_mutex);
@@ -347,7 +348,7 @@ static void pause(struct ao *ao)
{
struct priv *priv = ao->priv;
check_pa_ret(Pa_AbortStream(priv->stream));
CHECK_PA_RET(Pa_AbortStream(priv->stream));
double stream_time = Pa_GetStreamTime(priv->stream);
@@ -366,18 +367,14 @@ static void resume(struct ao *ao)
{
struct priv *priv = ao->priv;
check_pa_ret(Pa_StartStream(priv->stream));
CHECK_PA_RET(Pa_StartStream(priv->stream));
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_portaudio = {
.info = &(const struct ao_info) {
"PortAudio",
"portaudio",
"wm4",
"",
},
.description = "PortAudio",
.name = "portaudio",
.init = init,
.uninit = uninit,
.reset = reset,
+29 -15
View File
@@ -28,9 +28,9 @@
#include "config.h"
#include "audio/format.h"
#include "mpvcore/mp_msg.h"
#include "common/msg.h"
#include "ao.h"
#include "mpvcore/input/input.h"
#include "input/input.h"
#define PULSE_CLIENT_NAME "mpv"
@@ -147,7 +147,6 @@ static const struct format_map {
};
static const int speaker_map[][2] = {
{PA_CHANNEL_POSITION_MONO, MP_SPEAKER_ID_FC},
{PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL},
{PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR},
{PA_CHANNEL_POSITION_FRONT_CENTER, MP_SPEAKER_ID_FC},
@@ -174,6 +173,10 @@ static bool chmap_pa_from_mp(pa_channel_map *dst, struct mp_chmap *src)
if (src->num > PA_CHANNELS_MAX)
return false;
dst->channels = src->num;
if (mp_chmap_equals(src, &(const struct mp_chmap)MP_CHMAP_INIT_MONO)) {
dst->map[0] = PA_CHANNEL_POSITION_MONO;
return true;
}
for (int n = 0; n < src->num; n++) {
int mp_speaker = src->speaker[n];
int pa_speaker = PA_CHANNEL_POSITION_INVALID;
@@ -283,6 +286,8 @@ static int init(struct ao *ao)
ss.channels = ao->channels.num;
ss.rate = ao->samplerate;
ao->format = af_fmt_from_planar(ao->format);
const struct format_map *fmt_map = format_maps;
while (fmt_map->mp_format != ao->format) {
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
@@ -372,14 +377,14 @@ static void cork(struct ao *ao, bool pause)
}
// Play the specified data to the pulseaudio server
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
if (pa_stream_write(priv->stream, data, len, NULL, 0,
if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0,
PA_SEEK_RELATIVE) < 0) {
GENERIC_ERR_MSG("pa_stream_write() failed");
len = -1;
samples = -1;
}
if (flags & AOPLAY_FINAL_CHUNK) {
// Force start in case the stream was too short for prebuf
@@ -387,7 +392,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(priv->mainloop);
return len;
return samples;
}
// Reset the audio stream, i.e. flush the playback buffer on the server side
@@ -422,14 +427,14 @@ static void resume(struct ao *ao)
cork(ao, false);
}
// Return number of bytes that may be written to the server without blocking
// Return number of samples that may be written to the server without blocking
static int get_space(struct ao *ao)
{
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
size_t space = pa_stream_writable_size(priv->stream);
pa_threaded_mainloop_unlock(priv->mainloop);
return space;
return space / ao->sstride;
}
// Return the current latency in seconds
@@ -579,6 +584,19 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
pa_threaded_mainloop_unlock(priv->mainloop);
return CONTROL_OK;
}
case AOCONTROL_UPDATE_STREAM_TITLE: {
char *title = (char *)arg;
pa_threaded_mainloop_lock(priv->mainloop);
if (!waitop(priv, pa_stream_set_name(priv->stream, title,
success_cb, ao)))
{
GENERIC_ERR_MSG("pa_stream_set_name() failed");
return CONTROL_ERROR;
}
return CONTROL_OK;
}
default:
return CONTROL_UNKNOWN;
}
@@ -587,12 +605,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_pulse = {
.info = &(const struct ao_info) {
"PulseAudio audio output",
"pulse",
"Lennart Poettering",
"",
},
.description = "PulseAudio audio output",
.name = "pulse",
.control = control,
.init = init,
.uninit = uninit,
+10 -12
View File
@@ -29,15 +29,15 @@
#include "talloc.h"
#include "mpvcore/m_option.h"
#include "options/m_option.h"
#include "osdep/timer.h"
#include "audio/format.h"
#include "ao.h"
struct priv {
rsound_t *rd;
const char *host;
const char *port;
char *host;
char *port;
};
static int set_format(struct ao *ao)
@@ -114,6 +114,8 @@ static int init(struct ao *ao)
rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate);
rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num);
ao->format = af_fmt_from_planar(ao->format);
int rsd_format = set_format(ao);
rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format);
@@ -161,13 +163,13 @@ static void audio_resume(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *priv = ao->priv;
return rsd_get_avail(priv->rd);
return rsd_get_avail(priv->rd) / ao->sstride;
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
return rsd_write(priv->rd, data, len);
return rsd_write(priv->rd, data[0], samples * ao->sstride) / ao->sstride;
}
static float get_delay(struct ao *ao)
@@ -179,12 +181,8 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_rsound = {
.info = &(const struct ao_info) {
.name = "RSound output driver",
.short_name = "rsound",
.author = "Hans-Kristian Arntzen",
.comment = "",
},
.description = "RSound output driver",
.name = "rsound",
.init = init,
.uninit = uninit,
.reset = reset,
+11 -12
View File
@@ -23,8 +23,8 @@
#include "audio/format.h"
#include "talloc.h"
#include "ao.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/m_option.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "osdep/timer.h"
#include <libavutil/fifo.h>
@@ -144,6 +144,8 @@ static int init(struct ao *ao)
return -1;
}
ao->format = af_fmt_from_planar(ao->format);
SDL_AudioSpec desired, obtained;
switch (ao->format) {
@@ -259,7 +261,7 @@ static int get_space(struct ao *ao)
SDL_LockMutex(priv->buffer_mutex);
int space = av_fifo_space(priv->buffer);
SDL_UnlockMutex(priv->buffer_mutex);
return space;
return space / ao->sstride;
}
static void pause(struct ao *ao)
@@ -290,20 +292,21 @@ static void resume(struct ao *ao)
do_resume(ao);
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
SDL_LockMutex(priv->buffer_mutex);
int free = av_fifo_space(priv->buffer);
if (len > free) len = free;
av_fifo_generic_write(priv->buffer, data, len, NULL);
av_fifo_generic_write(priv->buffer, data[0], len, NULL);
SDL_CondSignal(priv->underrun_cond);
SDL_UnlockMutex(priv->buffer_mutex);
if (priv->unpause) {
priv->unpause = 0;
do_resume(ao);
}
return len;
return len / ao->sstride;
}
static float get_delay(struct ao *ao)
@@ -347,12 +350,8 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_sdl = {
.info = &(const struct ao_info) {
"SDL Audio",
"sdl",
"Rudolf Polzer <divVerent@xonotic.org>",
""
},
.description = "SDL Audio",
.name = "sdl",
.init = init,
.uninit = uninit,
.get_space = get_space,
+11 -12
View File
@@ -22,8 +22,8 @@
#include <errno.h>
#include <sndio.h>
#include "mpvcore/m_option.h"
#include "mpvcore/mp_msg.h"
#include "options/m_option.h"
#include "common/msg.h"
#include "audio/format.h"
#include "ao.h"
@@ -130,6 +130,9 @@ static int init(struct ao *ao)
MP_ERR(ao, "can't open sndio %s\n", p->dev);
goto error;
}
ao->format = af_fmt_from_planar(ao->format);
sio_initpar(&p->par);
for (i = 0, ap = af_to_par;; i++, ap++) {
if (i == sizeof(af_to_par) / sizeof(struct af_to_par)) {
@@ -239,16 +242,16 @@ static void reset(struct ao *ao)
/*
* play given number of bytes until sio_write() blocks
*/
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
int n;
n = sio_write(p->hdl, data, len);
n = sio_write(p->hdl, data[0], samples * ao->sstride);
p->delay += n;
if (flags & AOPLAY_FINAL_CHUNK)
reset(ao);
return n;
return n / ao->sstride;
}
/*
@@ -268,7 +271,7 @@ static int get_space(struct ao *ao)
; /* nothing */
sio_revents(p->hdl, p->pfd);
return p->par.bufsz * p->par.pchan * p->par.bps - p->delay;
return (p->par.bufsz * p->par.pchan * p->par.bps - p->delay) / ao->sstride;
}
/*
@@ -317,12 +320,8 @@ static void audio_resume(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_sndio = {
.info = &(const struct ao_info) {
"sndio audio output",
"sndio",
"Alexandre Ratchov <alex@caoua.org>, Christian Neukirchen <chneukirchen@gmail.com>",
"under development"
},
.description = "sndio audio output",
.name = "sndio",
.init = init,
.uninit = uninit,
.control = control,
+92 -138
View File
@@ -1,6 +1,8 @@
/*
* This file is part of mpv.
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -27,18 +29,13 @@
#include <mmdeviceapi.h>
#include <avrt.h>
#include "config.h"
#include "mpvcore/m_option.h"
#include "mpvcore/m_config.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "audio/format.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mp_ring.h"
#include "common/msg.h"
#include "misc/ring.h"
#include "ao.h"
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
#define BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE 0x00000001
#endif
#ifndef PKEY_Device_FriendlyName
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
@@ -48,7 +45,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);
#endif
#define RING_BUFFER_COUNT 64
#define RING_BUFFER_COUNT 8
/* 20 millisecond buffer? */
#define BUFFER_TIME 20000000.0
@@ -71,6 +68,7 @@ union WAVEFMT {
};
typedef struct wasapi_state {
struct mp_log *log;
HANDLE threadLoop;
/* Init phase */
@@ -85,6 +83,7 @@ typedef struct wasapi_state {
/* Play */
HANDLE hPlay;
int is_playing;
int final_chunk;
/* Reset */
HANDLE hReset;
@@ -98,9 +97,6 @@ typedef struct wasapi_state {
DWORD vol_hw_support, status;
float audio_volume;
/* Prints, for in case line buffers are disabled */
CRITICAL_SECTION print_lock;
/* Buffers */
struct mp_ring *ringbuff;
size_t buffer_block_size; /* Size of each block in bytes */
@@ -136,17 +132,6 @@ static int fill_VistaBlob(wasapi_state *state)
{
if (!state)
return 1;
HMODULE hkernel32 = GetModuleHandleW(L"kernel32.dll");
if (!hkernel32)
return 1;
WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
(WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(hkernel32, "SetDllDirectoryW");
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(hkernel32, "SetSearchPathMode");
if (pSetSearchPathMode)
pSetDllDirectory(L""); /* Attempt to use safe search paths */
if (pSetSearchPathMode)
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll");
if (!state->VistaBlob.hAvrt)
goto exit_label;
@@ -160,8 +145,6 @@ static int fill_VistaBlob(wasapi_state *state)
exit_label:
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
if (pSetSearchPathMode)
pSetDllDirectory(NULL);
return 1;
}
@@ -294,8 +277,8 @@ static int set_ao_format(struct wasapi_state *state,
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
MP_ERR(ao, "unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
return 0;
}
@@ -321,10 +304,8 @@ static int try_format(struct wasapi_state *state,
int af_format = format_set_bits(ao->format, bits, bits == 32);
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: trying %dch %s @ %dhz\n",
channels.num, af_fmt2str_short(af_format), samplerate);
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "trying %dch %s @ %dhz\n",
channels.num, af_fmt_to_str(af_format), samplerate);
union WAVEFMT u;
u.extensible = &wformat;
@@ -347,20 +328,16 @@ static int try_format(struct wasapi_state *state,
if (hr == S_FALSE) {
if (set_ao_format(state, ao, wformat)) {
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt2str_short(ao->format), ao->samplerate);
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
return 1;
}
} 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)) {
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: %dch %s @ %dhz accepted\n",
ao->channels.num, af_fmt2str_short(af_format), samplerate);
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "%dch %s @ %dhz accepted\n",
ao->channels.num, af_fmt_to_str(af_format), samplerate);
return 1;
}
}
@@ -414,10 +391,8 @@ static int try_passthrough(struct wasapi_state *state,
union WAVEFMT u;
u.extensible = &wformat;
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: trying passthrough for %s...\n",
af_fmt2str_short((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE));
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "trying passthrough for %s...\n",
af_fmt_to_str((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode,
@@ -438,12 +413,10 @@ static int find_formats(struct ao *const ao)
if (try_passthrough(state, ao))
return 0;
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: couldn't use passthrough!");
MP_ERR(ao, "couldn't use passthrough!");
if (!state->opt_exclusive)
mp_msg(MSGT_AO, MSGL_ERR, " (try exclusive mode)");
mp_msg(MSGT_AO, MSGL_ERR, "\n");
LeaveCriticalSection(&state->print_lock);
MP_ERR(ao, " (try exclusive mode)");
MP_ERR(ao, "\n");
return -1;
}
@@ -462,9 +435,7 @@ static int find_formats(struct ao *const ao)
return 0;
}
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_WARN, "ao-wasapi: couldn't use default mix format!\n");
LeaveCriticalSection(&state->print_lock);
MP_WARN(ao, "couldn't use default mix format!\n");
}
/* Exclusive mode, we have to guess. */
@@ -535,9 +506,7 @@ static int find_formats(struct ao *const ao)
bits = start_bits;
mp_chmap_from_channels(&ao->channels, 2);
} else {
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: couldn't find acceptable audio format!\n");
LeaveCriticalSection(&state->print_lock);
MP_ERR(ao, "couldn't find acceptable audio format!\n");
return -1;
}
}
@@ -564,12 +533,8 @@ reinit:
NULL);
/* something about buffer sizes on Win7, fixme might loop forever */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
EnterCriticalSection(&state->print_lock);
mp_msg(
MSGT_AO, MSGL_V,
"ao-wasapi: IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
explain_err(hr), state->defaultRequestedDuration);
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
explain_err(hr), state->defaultRequestedDuration);
if (offset > 10.0)
goto exit_label; /* is 10 enough to break out of the loop?*/
IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
@@ -601,18 +566,12 @@ reinit:
state->bufferFrameCount;
state->hTask =
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V,
"ao-wasapi: fix_format OK, using %lld byte buffer block size!\n",
(long long) state->buffer_block_size);
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(state, "fix_format OK, using %lld byte buffer block size!\n",
(long long) state->buffer_block_size);
return 0;
exit_label:
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR,
"ao-wasapi: fix_format fails with %s, failed to determine buffer block size!\n",
MP_ERR(state, "fix_format fails with %s, failed to determine buffer block size!\n",
explain_err(hr));
LeaveCriticalSection(&state->print_lock);
SetEvent(state->fatal_error);
return 1;
}
@@ -721,7 +680,9 @@ end:
return found;
}
static HRESULT enumerate_with_state(char *header, int status, int with_id) {
static HRESULT enumerate_with_state(struct mp_log *log, char *header,
int status, int with_id)
{
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pDevices = NULL;
@@ -748,7 +709,7 @@ static HRESULT enumerate_with_state(char *header, int status, int with_id) {
int count;
IMMDeviceCollection_GetCount(pDevices, &count);
if (count > 0) {
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: %s\n", header);
mp_info(log, "%s\n", header);
}
for (int i = 0; i < count; i++) {
@@ -763,11 +724,9 @@ static HRESULT enumerate_with_state(char *header, int status, int with_id) {
mark = " (default)";
if (with_id) {
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: Device #%d: %s, ID: %s%s\n",
i, name, id, mark);
mp_info(log, "Device #%d: %s, ID: %s%s\n", i, name, id, mark);
} else {
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: %s, ID: %s%s\n",
name, id, mark);
mp_info(log, "%s, ID: %s%s\n", name, id, mark);
}
free(name);
@@ -787,24 +746,27 @@ exit_label:
return hr;
}
static int enumerate_devices(void) {
static int enumerate_devices(struct mp_log *log)
{
HRESULT hr;
CoInitialize(NULL);
hr = enumerate_with_state("Active devices:", DEVICE_STATE_ACTIVE, 1);
hr = enumerate_with_state(log, "Active devices:", DEVICE_STATE_ACTIVE, 1);
EXIT_ON_ERROR(hr);
hr = enumerate_with_state("Unplugged devices:", DEVICE_STATE_UNPLUGGED, 0);
hr = enumerate_with_state(log, "Unplugged devices:", DEVICE_STATE_UNPLUGGED, 0);
EXIT_ON_ERROR(hr);
CoUninitialize();
return 0;
exit_label:
mp_msg(MSGT_AO, MSGL_ERR, "Error enumerating devices: HRESULT %08"PRIx32" \"%s\"\n",
(uint32_t)hr, explain_err(hr));
mp_err(log, "Error enumerating devices: HRESULT %08"PRIx32" \"%s\"\n",
(uint32_t)hr, explain_err(hr));
CoUninitialize();
return 1;
}
static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
char *search)
{
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pDevices = NULL;
@@ -834,16 +796,16 @@ static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
IMMDeviceCollection_GetCount(pDevices, &count);
if (devno >= count) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: no device #%d!\n", devno);
MP_ERR(ao, "no device #%d!\n", devno);
} else {
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: finding device #%d\n", devno);
MP_VERBOSE(ao, "finding device #%d\n", devno);
hr = IMMDeviceCollection_Item(pDevices, devno, &pTempDevice);
EXIT_ON_ERROR(hr);
hr = IMMDevice_GetId(pTempDevice, &deviceID);
EXIT_ON_ERROR(hr);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: found device #%d\n", devno);
MP_VERBOSE(ao, "found device #%d\n", devno);
}
} else {
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
@@ -854,7 +816,7 @@ static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
int count;
IMMDeviceCollection_GetCount(pDevices, &count);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: finding device %s\n", devid);
MP_VERBOSE(ao, "finding device %s\n", devid);
IMMDevice *prevDevice = NULL;
@@ -872,14 +834,14 @@ static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
if (deviceID) {
char *name;
if (!search_err) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: multiple matching devices found!\n");
MP_ERR(ao, "multiple matching devices found!\n");
name = get_device_name(prevDevice);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: %s\n", name);
MP_ERR(ao, "%s\n", name);
free(name);
search_err = 1;
}
name = get_device_name(pTempDevice);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: %s\n", name);
MP_ERR(ao, "%s\n", name);
free(name);
}
hr = IMMDevice_GetId(pTempDevice, &deviceID);
@@ -891,7 +853,7 @@ static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
}
if (deviceID == NULL) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not find device %s!\n", devid);
MP_ERR(ao, "could not find device %s!\n", devid);
}
}
@@ -901,12 +863,12 @@ static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
if (deviceID == NULL || search_err) {
hr = E_NOTFOUND;
} else {
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: loading device %S\n", deviceID);
MP_VERBOSE(ao, "loading device %S\n", deviceID);
hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
if (FAILED(hr)) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not load requested device!\n");
MP_ERR(ao, "could not load requested device!\n");
}
}
@@ -917,14 +879,15 @@ exit_label:
return hr;
}
static int validate_device(const m_option_t *opt, struct bstr name,
struct bstr param) {
static int validate_device(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
if (bstr_equals0(param, "help")) {
enumerate_devices();
enumerate_devices(log);
return M_OPT_EXIT;
}
mp_msg(MSGT_AO, MSGL_DBG2, "ao-wasapi: validating device=%s\n", param.start);
mp_dbg(log, "validating device=%s\n", param.start);
char *end;
int devno = (int) strtol(param.start, &end, 10);
@@ -933,8 +896,7 @@ static int validate_device(const m_option_t *opt, struct bstr name,
if ((end == (void*)param.start || *end) && devno < 0)
ret = M_OPT_OUT_OF_RANGE;
mp_msg(MSGT_AO, MSGL_DBG2, "ao-wasapi: device=%s %svalid\n",
param.start, ret == 1 ? "" : "not ");
mp_dbg(log, "device=%s %svalid\n", param.start, ret == 1 ? "" : "not ");
return ret;
}
@@ -956,15 +918,15 @@ static int thread_init(struct ao *ao)
SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
char *id = get_device_id(state->pDevice);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: default device ID: %s\n", id);
MP_VERBOSE(ao, "default device ID: %s\n", id);
free(id);
} else {
hr = find_and_load_device(&state->pDevice, state->opt_device);
hr = find_and_load_device(ao, &state->pDevice, state->opt_device);
}
EXIT_ON_ERROR(hr);
char *name = get_device_name(state->pDevice);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: device loaded: %s\n", name);
MP_VERBOSE(ao, "device loaded: %s\n", name);
free(name);
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
@@ -982,9 +944,7 @@ static int thread_init(struct ao *ao)
if (state->init_ret)
goto exit_label;
if (!fix_format(state)) { /* now that we're sure what format to use */
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: thread_init OK!\n");
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "thread_init OK!\n");
SetEvent(state->init_done);
return state->init_ret;
}
@@ -1030,6 +990,7 @@ static void thread_feed(wasapi_state *state,int force_feed)
/* should be smaller than buffer block size by now */
memset(pData,0,client_buffer);
mp_ring_read(state->ringbuff, (unsigned char *)pData, client_buffer);
state->final_chunk = 0;
} else {
/* buffer underrun?! abort */
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
@@ -1043,15 +1004,13 @@ static void thread_feed(wasapi_state *state,int force_feed)
EXIT_ON_ERROR(hr);
return;
exit_label:
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: thread_feed fails with %"PRIx32"!\n", (uint32_t)hr);
LeaveCriticalSection(&state->print_lock);
MP_ERR(state, "thread_feed fails with %"PRIx32"!\n", (uint32_t)hr);
return;
}
static void thread_play(wasapi_state *state)
{
thread_feed(state, 0);
thread_feed(state, state->final_chunk);
state->is_playing = 1;
IAudioClient_Start(state->pAudioClient);
return;
@@ -1121,9 +1080,7 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
HANDLE playcontrol[] =
{state->hUninit, state->hPause, state->hReset, state->hGetvol,
state->hSetvol, state->hPlay, state->hFeed, NULL};
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: Entering dispatch loop!\n");
LeaveCriticalSection(&state->print_lock);
MP_VERBOSE(ao, "Entering dispatch loop!\n");
while (1) { /* watch events, poll at least every 2 seconds */
waitstatus = WaitForMultipleObjects(7, playcontrol, FALSE, 2000);
switch (waitstatus) {
@@ -1152,7 +1109,7 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
case (WAIT_OBJECT_0 + 6): /* feed */
if (state->is_playing)
feedwatch = 1;
thread_feed(state, 0);
thread_feed(state, state->final_chunk);
break;
case WAIT_TIMEOUT: /* Did our feed die? */
if (feedwatch)
@@ -1195,11 +1152,12 @@ static int get_space(struct ao *ao)
if (!ao || !ao->priv)
return -1;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
return mp_ring_available(state->ringbuff);
return mp_ring_available(state->ringbuff) / ao->sstride;
}
static void reset_buffers(struct wasapi_state *state)
{
state->final_chunk = 0;
mp_ring_reset(state->ringbuff);
}
@@ -1210,11 +1168,11 @@ static int setup_buffers(struct wasapi_state *state)
return !state->ringbuff;
}
static void uninit(struct ao *ao, bool immed)
static void uninit(struct ao *ao, bool block)
{
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: uninit!\n");
MP_VERBOSE(ao, "uninit!\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
state->immed = immed;
state->immed = !block;
SetEvent(state->hUninit);
/* wait up to 10 seconds */
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT)
@@ -1222,22 +1180,23 @@ static void uninit(struct ao *ao, bool immed)
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
closehandles(ao);
DeleteCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: uninit END!\n");
MP_VERBOSE(ao, "uninit END!\n");
}
static int init(struct ao *ao)
{
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: init!\n");
MP_VERBOSE(ao, "init!\n");
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))
return -1;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
state->log = ao->log;
fill_VistaBlob(state);
if (state->opt_list) {
enumerate_devices();
enumerate_devices(state->log);
}
if (state->opt_exclusive) {
@@ -1256,7 +1215,6 @@ static int init(struct ao *ao)
state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
state->fatal_error = CreateEventW(NULL, TRUE, FALSE, NULL);
state->hFeed = CreateEvent(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
InitializeCriticalSection(&state->print_lock);
if (!state->init_done || !state->fatal_error || !state->hPlay ||
!state->hPause || !state->hFeed || !state->hReset || !state->hGetvol ||
!state->hSetvol || !state->hDoneVol)
@@ -1269,18 +1227,18 @@ static int init(struct ao *ao)
state->threadLoop = (HANDLE)CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL);
if (!state->threadLoop) {
/* failed to init thread */
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: fail to create thread!\n");
MP_ERR(ao, "fail to create thread!\n");
return -1;
}
WaitForSingleObject(state->init_done, INFINITE); /* wait on init complete */
if (state->init_ret) {
if (!ao->probing) {
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: thread_init failed!\n");
MP_ERR(ao, "thread_init failed!\n");
}
} else {
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: Init Done!\n");
MP_VERBOSE(ao, "Init Done!\n");
if (setup_buffers(state))
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: buffer setup failed!\n");
MP_ERR(ao, "buffer setup failed!\n");
}
return state->init_ret;
}
@@ -1336,25 +1294,25 @@ static void reset(struct ao *ao)
reset_buffers(state);
}
static int play(struct ao *ao, void *data, int len, int flags)
static int play(struct ao *ao, void **data, int samples, int flags)
{
int ret = 0;
if (!ao || !ao->priv)
return ret;
return 0;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (WaitForSingleObject(state->fatal_error, 0) == WAIT_OBJECT_0) {
/* something bad happened */
return ret;
return 0;
}
ret = mp_ring_write(state->ringbuff, data, len);
int ret = mp_ring_write(state->ringbuff, data[0], samples * ao->sstride);
state->final_chunk |= flags & AOPLAY_FINAL_CHUNK;
if (!state->is_playing) {
/* start playing */
state->is_playing = 1;
SetEvent(state->hPlay);
}
return ret;
return ret / ao->sstride;
}
static float get_delay(struct ao *ao)
@@ -1362,19 +1320,15 @@ static float get_delay(struct ao *ao)
if (!ao || !ao->priv)
return -1.0f;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
return (float)(RING_BUFFER_COUNT * state->buffer_block_size - get_space(ao)) /
return (float)(RING_BUFFER_COUNT * state->buffer_block_size - get_space(ao) * ao->sstride) /
(float)state->format.Format.nAvgBytesPerSec;
}
#define OPT_BASE_STRUCT struct wasapi_state
const struct ao_driver audio_out_wasapi = {
.info = &(const struct ao_info) {
"Windows WASAPI audio output (event mode)",
"wasapi",
"Jonathan Yong <10walls@gmail.com>",
""
},
.description = "Windows WASAPI audio output (event mode)",
.name = "wasapi",
.init = init,
.uninit = uninit,
.control = control,
-68
View File
@@ -27,74 +27,6 @@
#include "chmap.h"
#include "reorder_ch.h"
static inline void reorder_to_planar_(void *restrict out, const void *restrict in,
size_t size, size_t nchan, size_t nmemb)
{
size_t i, c;
char *outptr = (char *) out;
size_t instep = nchan * size;
for (c = 0; c < nchan; ++c) {
const char *inptr = ((const char *) in) + c * size;
for (i = 0; i < nmemb; ++i, inptr += instep, outptr += size) {
memcpy(outptr, inptr, size);
}
}
}
void reorder_to_planar(void *restrict out, const void *restrict in,
size_t size, size_t nchan, size_t nmemb)
{
// special case for mono (nothing to do...)
if (nchan == 1)
memcpy(out, in, size * nchan * nmemb);
// these calls exist to insert an inline copy of to_planar_ here with known
// value of size to help the compiler replace the memcpy calls by mov
// instructions
else if (size == 1)
reorder_to_planar_(out, in, 1, nchan, nmemb);
else if (size == 2)
reorder_to_planar_(out, in, 2, nchan, nmemb);
else if (size == 4)
reorder_to_planar_(out, in, 4, nchan, nmemb);
// general case (calls memcpy a lot, should actually never happen, but
// stays here for correctness purposes)
else
reorder_to_planar_(out, in, size, nchan, nmemb);
}
static inline void reorder_to_packed_(uint8_t *out, uint8_t **in,
size_t size, size_t nchan, size_t nmemb)
{
size_t outstep = nchan * size;
for (size_t c = 0; c < nchan; ++c) {
char *outptr = out + c * size;
char *inptr = in[c];
for (size_t i = 0; i < nmemb; ++i, outptr += outstep, inptr += size) {
memcpy(outptr, inptr, size);
}
}
}
// out = destination array of packed samples of given size, nmemb frames
// in[channel] = source array of samples for the given channel
void reorder_to_packed(uint8_t *out, uint8_t **in,
size_t size, size_t nchan, size_t nmemb)
{
if (nchan == 1)
memcpy(out, in, size * nchan * nmemb);
// See reorder_to_planar() why this is done this way
else if (size == 1)
reorder_to_packed_(out, in, 1, nchan, nmemb);
else if (size == 2)
reorder_to_packed_(out, in, 2, nchan, nmemb);
else if (size == 4)
reorder_to_packed_(out, in, 4, nchan, nmemb);
else
reorder_to_packed_(out, in, size, nchan, nmemb);
}
#define MAX_SAMPLESIZE 8
static void reorder_channels_(uint8_t *restrict data, int *restrict ch_order,
-5
View File
@@ -25,11 +25,6 @@
#include <inttypes.h>
void reorder_to_planar(void *restrict out, const void *restrict in,
size_t size, size_t nchan, size_t nmemb);
void reorder_to_packed(uint8_t *out, uint8_t **in,
size_t size, size_t nchan, size_t nmemb);
void reorder_channels(void *restrict data, int *restrict ch_order,
size_t sample_size, size_t num_ch, size_t num_frames);
Executable
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python
# This script simply downloads waf to the current directory
from __future__ import print_function
import os, sys, stat, hashlib, subprocess
WAFRELEASE = "waf-1.7.13"
WAFURL = "https://waf.googlecode.com/files/" + WAFRELEASE
SHA256HASH = "03cc750049350ee01cdbc584b70924e333fcc17ba4a2d04648dab1535538a873"
if os.path.exists("waf"):
wafver = subprocess.check_output(['./waf', '--version']).decode()
if WAFRELEASE.split('-')[1] == wafver.split(' ')[1]:
print("Found 'waf', skipping download.")
sys.exit(0)
try:
from urllib.request import urlopen
except:
from urllib2 import urlopen
print("Downloading %s..." % WAFURL)
waf = urlopen(WAFURL).read()
if SHA256HASH == hashlib.sha256(waf).hexdigest():
with open("waf", "wb") as wf:
wf.write(waf)
os.chmod("waf", os.stat("waf").st_mode | stat.S_IXUSR)
print("Checksum verified.")
else:
print("The checksum of the downloaded file does not match!")
print("Please download and verify the file manually.")
sys.exit(1)
+1 -1
View File
@@ -25,7 +25,7 @@
#include "talloc.h"
#include "mpvcore/bstr.h"
#include "bstr/bstr.h"
int bstrcmp(struct bstr str1, struct bstr str2)
{
Loaded 100 of 454 files, more files were not shown because too many files have changed in this diff. Show more