Compare commits

..
44 Commits
Author SHA1 Message Date
wm4 b4f6e57d2c Release 0.2.2 2013-11-02 20:11:57 +01:00
wm4 854cdb796e 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 19:06:59 +01:00
wm4 504c452568 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-11-02 19:06:16 +01:00
wm4 ee54711ceb 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-11-02 19:06:13 +01:00
wm4 3ac568714c 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-11-02 19:06:10 +01:00
Rudolf Polzer d64b165b2a encoding-example-configs: add more comments regarding the scaling method 2013-11-02 19:06:02 +01:00
Stefano Pigozzi 78a57135f9 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-11-02 19:05:58 +01:00
wm4 09f00db142 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-11-02 19:05:43 +01:00
Rudolf Polzer 673919df7d Fix style of the HP Slate 7 vf-add line. 2013-11-02 19:05:24 +01:00
Rudolf Polzer d2fc1534c8 encoding-example-profiles: support HP Slate 7's weird aspect.
Also, replace broken noup= by lavfi expressions.
2013-11-02 19:05:21 +01:00
wm4 10c3b71720 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-11-02 19:05:10 +01:00
wm4 0ba3760745 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.

Conflicts:
	osdep/getch2.c
2013-11-02 19:05:04 +01:00
Stefano Pigozzi 1dbbd845a4 cocoa: apply the more invasive constraining only with cmd+1/2/3
Regression from bc49957
Fixes #321
2013-11-02 19:03:50 +01:00
wm4 131fad8bc9 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-11-02 19:03:40 +01:00
wm4 6af87017d1 vo_xv: check whether image allocation succeeds 2013-11-02 19:03:37 +01:00
wm4 bd70c32504 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-11-02 19:03:28 +01:00
Ben Boeckel 4c88ac3434 mpvcore: add a note about desync on track switches 2013-11-02 19:03:21 +01:00
ChrisK2 4cd36b999f osc: make sure the OSC actually fits into the video 2013-11-02 19:03:03 +01:00
wm4 1b3155cf51 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-11-02 19:02:42 +01:00
wm4 ace9a4b6c6 README: add a link to the wiki about the FFmpeg vs. Libav issue 2013-11-02 19:02:36 +01:00
wm4 84cd043f85 manpage: clarify --heartbeat-interval operation 2013-11-02 19:02:33 +01:00
wm4 6cdc5627f0 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-11-02 19:02:29 +01:00
Stefano Pigozzi cb355443dc 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-11-02 19:02:26 +01:00
wm4 cc2b5c16fa 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-11-02 19:02:21 +01:00
Alexander Preisinger bf345136b9 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-11-02 19:02:07 +01:00
Stefano Pigozzi 6b525b84bb cocoa: fix opening quarantined files on 10.9 with the bundle [2]
Fixup commit for bbc146927
2013-11-02 19:02:03 +01:00
Stefano Pigozzi ea17adcd66 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-11-02 19:01:42 +01:00
Stefano Pigozzi 31cdb96474 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-11-02 19:01:39 +01:00
wm4 b3233fa3ff gl_video: add RGB10_A2 FBO format
Maybe this can be the default in the future, but I'm not sure yet.
2013-11-02 19:01:13 +01:00
wm4 132528ff67 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-11-02 19:01:04 +01:00
wm4 efc32ac0df 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-11-02 19:00:53 +01:00
Stefano Pigozzi 042302fa65 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-11-02 19:00:36 +01:00
wm4 93ce083bd8 Release 0.2.1 2013-10-20 14:12:33 +02:00
wm4 97c93918fe input: fix --input-ar-rate=0
Crashed, instead of disabling auto-repeat.
2013-10-20 14:11:28 +02:00
Alexander Preisinger 7078df4f5e 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 14:11:25 +02:00
Alexander Preisinger 8e642809bf Revert "wayland: remove moving window by grabbing"
This reverts commit 3308bc2bc9.
2013-10-20 14:11:21 +02:00
Alexander Preisinger 8c4cf5ce84 Revert "wayland: remove outdated comment"
This reverts commit d75cfef49c.
2013-10-20 14:11:18 +02:00
wm4 16cbc4a014 vdpau_old: restore hardware decoding with old API
--hwdec=vdpau did nothing with older ffmpeg/libav versions. Oops.
2013-10-20 14:11:11 +02:00
wm4 2aff8b7523 af_lavrresample: actually free resampler
Fixes #304.
2013-10-20 14:11:04 +02:00
Nikoli 5d9353f0d5 mpv.desktop: add russian translation, sort 2013-10-20 14:11:00 +02:00
wm4 6f139ad981 sws_utils: work around libswscale crash with --contrast=-100 2013-10-20 14:10:49 +02:00
wm4 0052445e86 vf_scale: fix get/set confusion
This caused the equalizer controls to appear stuck.
2013-10-20 14:10:46 +02:00
Alessandro Ghedini 2cf8d4ffdb spelling fixes 2013-10-20 14:10:43 +02:00
wm4 e6236e22db Release 0.2.0 2013-10-16 00:11:28 +02:00
450 changed files with 23694 additions and 27745 deletions

No files matched your search

+2 -11
View File
@@ -2,7 +2,6 @@
*.a
*.d
*.exe
*.pyc
.depend
/config.h
@@ -11,7 +10,7 @@
/mpv
/mpv.app
/version.h
/input/input_conf.h
/mpvcore/input/input_conf.h
/tags
/TAGS
/video/out/gl_video_shaders.h
@@ -19,18 +18,10 @@
/demux/ebml_defs.c
/demux/ebml_types.h
/sub/osd_font.h
/player/lua/*.inc
/mpvcore/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:
- ./bootstrap.py
- ./waf configure
- ./waf build
script: ./configure && make
notifications-policy: &notifications-policy
on_success: change
on_failure: always
notifications:
email: false
email:
recipients:
- [email protected]
<<: *notifications-policy
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:
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
./configure --enable-cross-compile --target=i686-w64-mingw32
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 mingw
git clone https://github.com/mxe/mxe
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
DEST_OS=win32 TARGET=i686-w64-mingw32 ./waf configure
./waf build
./configure --enable-cross-compile --target=i686-w64-mingw32
make
# 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
@@ -1,105 +0,0 @@
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
@@ -0,0 +1,139 @@
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
+6 -5
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-profiles.conf; as for this, see below.
etc/encoding-example-profiles.conf; as for this, see below.
Encoding examples
@@ -89,12 +89,13 @@ 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-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::
etc/encoding-example-profiles.conf in the source tree. You can include it into
your configuration by doing::
mkdir -p ~/.mpv
cp /etc/mpv/encoding-profiles.conf ~/.mpv/encoding-profiles.conf
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
Refer to the top of that file for more comments - in a nutshell, the following
options are added by it::
+1
View File
@@ -10,6 +10,7 @@ latex-preamble: \usepackage[usenames,dvipsnames]{xcolor}
\makeatletter
\renewcommand{\maketitle}{
\begin{center}
\includegraphics{etc/mpv-icon-8bit-64x64.png}\\
\vspace*{-1.5em}
\begin{Huge}
\@title
+72 -97
View File
@@ -11,12 +11,6 @@ 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:
@@ -41,7 +35,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, s16, s32, float.
It supports only the following sample formats: u8, s16ne, s32ne, floatne.
``filter-size=<length>``
Length of the filter with respect to the lower sampling rate. (default:
@@ -56,9 +50,9 @@ Available filters are:
entries. (default: no)
``no-detach``
Do not detach if input and output audio format/rate/channels match.
(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:...``.)
You should add this option if you specify additional parameters, as
automatically inserted lavrresample instances will use the default
settings.
``o=<string>``
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
be documented by FFmpeg or Libav.
@@ -70,18 +64,17 @@ 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=<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.
``<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.
Valid values: 32, 40, 48, 56, 64, 80, 96, 112, 128,
160, 192, 224, 256, 320, 384, 448, 512, 576, 640.
The special value ``default`` selects a default bitrate based on the
input channel number:
Default bitrate is based on the input channel number:
:1ch: 96
:2ch: 192
@@ -90,7 +83,7 @@ Available filters are:
:5ch: 448
:6ch: 448
``minchn=<n>``
``<minchn>``
If the input channel number is less than ``<minchn>``, the filter will
detach itself (default: 5).
@@ -184,7 +177,7 @@ Available filters are:
Would amplify the sound in the upper and lower frequency region
while canceling it almost completely around 1kHz.
``channels=nch[:routes]``
``channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]``
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
@@ -195,52 +188,45 @@ Available filters are:
``<nch>``
number of output channels (1-8)
``<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.
``<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.
.. admonition:: Examples
``mpv --af=channels=4:[0-1,1-0,0-2,1-3] media.avi``
``mpv --af=channels=4:4:0:1:1:0:2:2:3: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:[0-0,0-1,0-2,0-3] media.avi``
``mpv --af=channels=6:4: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.
.. 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=in-format:in-srate:in-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 first 3 parameters restrict what the filter
accepts as input. The ``out-`` parameters change the audio format, without
All parameters are optional. The ``in-`` variants restrict what the filter
accepts as input. The ``out-`` variants change the audio format, without
actually doing a conversion. The data will be 'reinterpreted' by the
filters or audio outputs following this filter.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
a list of valid formats.
``<in-format>``
Force conversion to this format. See ``format`` filter for valid audio
format values.
``<srate>``
``<in-srate>``
Force conversion to a specific sample rate. The rate is an integer,
48000 for example.
``<channels>``
``<in-channels>``
Force mixing to a specific channel layout. See ``--channels`` option
for possible values.
@@ -250,53 +236,55 @@ Available filters are:
``<out-channels>``
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[=format]``
Convert between different sample formats. Automatically enabled when
needed by the audio output or another filter. See also ``--format``.
*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.
``<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.
``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]]]``
``volume[=v[:sc[:fast]]]``
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.
``<volumedb>``
``<v>``
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).
``<softclip>``
Turns soft clipping on. Soft-clipping can make the
``<sc>``
Turns soft clipping on (1) or off (0). 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.
``<s16>``
Force S16 sample format if set. Lower quality, but might be faster
``<fast>``
Force S16 sample format if set to 1. Lower quality, but might be faster
in some situations.
.. admonition:: Example
``mpv --af=volume=10.1 media.avi``
``mpv --af=volume=10.1:0 media.avi``
Would amplify the sound by 10.1dB and hard-clip if the sound level
is too high.
``pan=n[:<matrix>]``
``pan=n[:L00:L01:L02:...L10:L11:L12:...Ln0:Ln1:Ln2:...]``
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
@@ -308,23 +296,19 @@ Available filters are:
``<n>``
Number of output channels (1-8).
``<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.
``<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.
.. 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).
@@ -332,8 +316,8 @@ Available filters are:
.. note::
If you just want to force remixing to a certain output channel layout,
it is easier to use the ``format`` filter. For example,
``mpv '--af=format=channels=5.1' '--channels=5.1'`` would always force
it is easier to use the ``force`` filter. For example,
``mpv '--af=force=channels=5.1' '--channels=5.1'`` would always force
remixing audio to 5.1 and output it like this.
``sub[=fc:ch]``
@@ -394,12 +378,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).
@@ -418,11 +402,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::
@@ -434,7 +418,7 @@ Available filters are:
The rest is payload (non-interleaved) 16-bit data.
``<mmapped_file>``
File to map data to (required)
File to map data to (default: ``~/.mpv/mpv-af_export``).
``<nsamples>``
number of samples per channel (default: 512).
@@ -475,7 +459,7 @@ Available filters are:
This filter can cause distortion with audio signals that have a very
large dynamic range.
``ladspa=file:label:[<control0>,<control1>,...]``
``ladspa=file:label[:controls...]``
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
filter is reentrant, so multiple LADSPA plugins can be used at once.
@@ -491,21 +475,12 @@ 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.
``[<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).
``<controls>``
Controls are zero or more 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
@@ -572,7 +547,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=float,scaletempo media.ogg``
``mpv --af=format=floatne,scaletempo media.ogg``
Would make scaletempo use float code. Maybe faster on some
platforms.
+2 -22
View File
@@ -10,12 +10,6 @@ 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.
@@ -29,13 +23,10 @@ normal driver parameters.
Available audio output drivers are:
``alsa`` (Linux only)
ALSA audio output driver
ALSA 0.9/1.x 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.
@@ -159,18 +150,7 @@ Available audio output drivers are:
``null``
Produces no audio output but maintains video playback speed. Use
``--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.
``--no-audio`` for benchmarking.
``pcm``
Raw PCM/WAVE file writer audio output
+1 -14
View File
@@ -103,7 +103,6 @@ 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
@@ -136,7 +135,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``
@@ -177,24 +176,12 @@ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+11 -61
View File
@@ -31,10 +31,6 @@ 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.
@@ -48,6 +44,8 @@ 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"
@@ -88,12 +86,6 @@ 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.
@@ -120,8 +112,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``.
``multiply <property> <factor>``
Multiplies the value of a property with the numeric factor.
``speed_mult <value>``
Multiply the ``speed`` property by the given value.
``screenshot [subtitles|video|window|- [single|each-frame]]``
Take a screenshot.
@@ -193,11 +185,9 @@ List of Input Commands
``playlist_clear``
Clear the playlist, except the currently played file.
``playlist_remove current|<index>``
``playlist_remove <index>``
Remove the playlist entry at the given index. Index values start counting
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.
with 0. You cannot remove the entry for the currently played file.
``playlist_move <index1> <index2>``
Move the playlist entry at index1, so that it takes the place of the
@@ -206,28 +196,9 @@ List of Input Commands
because index2 refers to the target entry, not the index the entry
will have after moving.)
``run "command" "arg1" "arg2" ...``
(Unix only)
Run the given command. Unlike in MPlayer/mplayer2 and earlier versions of
mpv (0.2.x and older), this doesn't call the shell. Instead, the command
is run directly, with each argument passed separately. Each argument is
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``.
``run "<command>"``
Run the given command with ``/bin/sh -c``. The string is expanded like in
`Property Expansion`_.
``quit [<code>]``
Exit the player using the given exit code.
@@ -338,24 +309,6 @@ 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.
@@ -445,7 +398,8 @@ 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`` (all of these
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
``radio_set_channel``, ``radio_set_freq``, ``radio_step_freq`` (all of these
should be replaced by properties), ``stop`` (questionable use), ``get_property``
(?), ``vo_cmdline`` (experimental).
@@ -533,7 +487,6 @@ 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
@@ -580,7 +533,6 @@ 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)
@@ -592,10 +544,8 @@ 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 -29
View File
@@ -24,7 +24,6 @@ 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]
@@ -33,7 +32,6 @@ 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
@@ -92,10 +90,6 @@ 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.
@@ -285,28 +279,6 @@ 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
----------------
@@ -428,7 +400,7 @@ profile name ``default`` to continue with normal options.
[extension.flv]
profile-desc="profile for .flv files"
vf=flip
flip=yes
[ao.alsa]
device=spdif
+48 -142
View File
@@ -42,12 +42,6 @@ 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
@@ -61,8 +55,6 @@ 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,...>``
@@ -633,11 +625,15 @@ OPTIONS
``--demuxer-lavf-format=<name>``
Force a specific libavformat demuxer.
``--demuxer-lavf-genpts-mode=<no|lavf>``
``--demuxer-lavf-genpts-mode=<auto|lavf|builtin|no>``
Mode for deriving missing packet PTS values from packet DTS. ``lavf``
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.
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.
``--demuxer-lavf-o=<key>=<value>[,<key>=<value>[,...]]``
Pass AVOptions to libavformat demuxer.
@@ -800,6 +796,9 @@ 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
@@ -1118,10 +1117,11 @@ OPTIONS
:no: always use software decoding (default)
:auto: see below
:vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
:vdpau: requires ``--vo=vdpau`` (Linux only)
:vaapi: requires ``--vo=vaapi`` (Linux with Intel GPUs only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:vda: requires ``--vo=opengl`` or ``--vo=corevideo`` (OSX only)
:vda: requires ``--vo=corevideo`` (OSX only)
:crystalhd: Broadcom Crystal HD
``auto`` tries to automatically enable hardware decoding using the first
available method. This still depends what VO you are using. For example,
@@ -1132,20 +1132,12 @@ 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.
.. note::
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.
Allow hardware decoding for a given list of codecs only. The default is the
special value ``all``, which always allows all codecs.
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.
This is usually only needed with broken GPUs, where fallback on software
decoding does not work properly.
.. admonition:: Example
@@ -1222,7 +1214,7 @@ OPTIONS
like any other binding). See `INPUT.CONF`_.
``--joystick``, ``--no-joystick``
Enable/disable joystick support. Disabled by default.
Enable/disable joystick support. Enabled by default.
``--no-keepaspect``, ``--keepaspect``
``--no-keepaspect`` will always stretch the video to window size, and will
@@ -1299,21 +1291,12 @@ 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: 1)
:fps=<value>: output fps (default: 25)
:type=<value>: input file type (available: jpeg, png, tga, sgi)
``--monitoraspect=<ratio>``
@@ -1345,9 +1328,7 @@ OPTIONS
Control verbosity directly for each module. The ``all`` module changes the
verbosity of all the modules not explicitly specified on the command line.
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.
See ``--msglevel=help`` for a list of all modules.
.. note::
@@ -1358,18 +1339,17 @@ OPTIONS
Available levels:
: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.
:-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
``--msgmodule``
Prepend module name in front of each console message.
@@ -1439,20 +1419,8 @@ 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: yes).
Whether to load the on-screen-controller (default: no).
``--no-osd-bar``, ``--osd-bar``
Disable display of the OSD bar. This will make some things (like seeking)
@@ -1573,12 +1541,6 @@ 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.
@@ -1648,6 +1610,12 @@ 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
@@ -1662,19 +1630,16 @@ OPTIONS
Use the given profile(s), ``--profile=help`` displays a list of the
defined profiles.
``--pts-association-mode=<decode|sort|auto>``
``--pts-association-mode=<auto|decode|sort>``
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.
:decoder: Use decoder reordering functionality. Unlike in classic MPlayer
and mplayer2, this includes a dTS fallback. (Default.)
:auto: Try to pick a working mode from the ones below automatically
(default)
:decoder: Use decoder reordering functionality.
: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.
@@ -1855,11 +1820,6 @@ 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``
@@ -2016,30 +1976,6 @@ 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.
@@ -2153,19 +2089,6 @@ 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.
@@ -2603,13 +2526,6 @@ 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).
@@ -2632,11 +2548,6 @@ 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
@@ -2685,7 +2596,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.
@@ -2711,10 +2622,6 @@ 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.
@@ -2722,11 +2629,10 @@ OPTIONS
``--video-zoom=<value>``
Adjust the video display scale factor by the given value. The unit is in
fractions of the (scaled) window video size.
fractions of original video size.
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.
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.
This option is disabled if the ``--no-keepaspect`` option is used.
+2 -6
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 entirely using the ``--osc=no`` option.
disabled using ``--osc=no``.
Using the OSC
-------------
@@ -113,7 +113,7 @@ time passed
============= ================================================
cache status
| Shows current cache fill status (only visible when below 45%)
| Shows current cache fill status (only visible when below 48%)
time remaining
| Shows remaining playback time timestamp
@@ -176,10 +176,6 @@ 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
+89 -77
View File
@@ -7,11 +7,10 @@ syntax is:
``--vf=<filter1[=parameter1:parameter2:...],filter2,...>``
Setup a chain of video filters.
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.
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.
.. note::
@@ -75,7 +74,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.
@@ -83,7 +82,7 @@ Available filters are:
Rounds up to make both width and height divisible by <r> (default: 1).
``flip``
Flips the image upside down.
Flips the image upside down. See also ``--flip``.
``mirror``
Mirrors the image on the Y axis.
@@ -151,7 +150,7 @@ Available filters are:
:0: Disable accurate rounding (default).
:1: Enable accurate rounding.
``dsize[=w:h:aspect-method:r:aspect]``
``dsize[=aspect|w:h:aspect-method:r]``
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
@@ -200,9 +199,6 @@ 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.
@@ -236,7 +232,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
@@ -254,12 +250,7 @@ Available filters are:
.. note::
``--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=[...]'``.
``--pphelp`` shows a list of available subfilters.
Available subfilters are:
@@ -373,14 +364,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>``
@@ -414,7 +405,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.
@@ -429,30 +420,21 @@ Available filters are:
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
forces a specific threading configuration.
``noise[=<strength>[:averaged][:pattern][:temporal][:uniform][:hq]``
``noise[=luma[u][t|a][h][p]:chroma[u][t|a][h][p]]``
Adds noise.
``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.
:<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
``averaged``
averaged temporal noise (smoother, but a lot slower)
.. note::
``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
Deprecated. Use libavfilter's ``noise`` filter through ``--vf=lavfi``
instead.
``hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp]``
This filter aims to reduce image noise producing smooth images and making
@@ -468,6 +450,11 @@ 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
@@ -507,26 +494,39 @@ Available filters are:
:0: nearest-neighbor sampling, fast but incorrect
:1: linear interpolation (default)
``unsharp[=lx:ly:la:cx:cy:ca]``
``unsharp[=l|cWxH:amount[:l|cWxH:amount]]``
unsharp mask / gaussian blur
``l`` is for the luma component, ``c`` for the chroma component. ``x``/``y``
is the filter size. ``a`` is the amount.
``l``
Apply effect on luma component.
``lx``, ``ly``, ``cx``, ``cy``
``c``
Apply effect on chroma components.
``<width>x<height>``
width and height of the matrix, odd sized in both directions (min =
3:3, max = 13:11 or 11:13, usually something between 3:3 and 7:7)
3x3, max = 13x11 or 11x13, usually something between 3x3 and 7x7)
``la``, ``ca``
``amount``
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 ``u`` or ``v`` to use a chroma plane instead of the
This option may be set to 1 or 2 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,36 +670,41 @@ 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. (Ignored when libavfilter is used.)
alternatives.
``yadif=[mode[:enabled=yes|no]]``
Yet another deinterlacing filter
``<mode>``
: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.
: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.
``<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).
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
.. 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
deinterlacing support.
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``.
``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.
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.
``<lines>``
number of lines to select from the middle of the image (default: 12)
``delogo[=x:y:w:h:t:show]``
``delogo[=x:y:w:h:t]``
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).
@@ -716,9 +721,11 @@ 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).
(Not supported when using libavfilter.)
``show``
Draw a rectangle showing the area defined by x/y/w/h.
.. note::
Deprecated. Use libavfilter's ``delogo`` or ``removelogo`` filters
through ``--vf=lavfi`` instead.
``screenshot``
Optional filter for screenshot support. This is only needed if the video
@@ -811,24 +818,29 @@ Available filters are:
``mr`` or ``mono_right``
mono output (right eye only)
``gradfun[=strength[:radius|:size=<size>]]``
.. note::
Deprecated. Use libavfilter's ``stereo3d`` filter through ``--vf=lavfi``
instead.
``gradfun[=strength[:radius]]``
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.5).
threshold for detecting nearly flat regions (default: 1.2).
``<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: disabled).
detailed regions (default: 16).
``<size>``
size of the filter in percent of the image diagonal size. This is
used to calculate the final radius size (default: 1).
.. note::
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
@@ -851,10 +863,10 @@ Available filters are:
Specify the fourth parameter to pass to the library.
``vavpp``
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).
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).
``deint=<method>``
Select the deinterlacing algorithm.
+3 -21
View File
@@ -10,12 +10,6 @@ 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.
@@ -247,11 +241,6 @@ 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``
@@ -331,10 +320,8 @@ Available video output drivers are:
slightly different gamma.
``pbo``
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.
Enable use of PBOs. This is faster, but can sometimes lead to sporadic
and temporary image corruption.
``dither-depth=<N|no|auto>``
Set dither target depth to N. Default: no.
@@ -486,17 +473,12 @@ 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:fbo-format=rgb16
--vo=opengl:lscale=lanczos2:dither-depth=auto:pbo:fbo-format=rgb16
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
+25 -36
View File
@@ -2,12 +2,11 @@ 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.
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.
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.
The structure is as follows (as of commit e13c05366557cb):
* main():
@@ -43,7 +42,7 @@ player/*.c:
- the other subsystems rarely call back into the frontend, and the frontend
polls them instead (probably a good thing)
I like to call the player/*.c files the "frontend".
I like to call mplayer.c (and some other files) the "frontend".
talloc.h & talloc.c:
Hierarchical memory manager copied from Samba. It's like a malloc() with
@@ -83,47 +82,37 @@ talloc.h & talloc.c:
rid of the talloc emulation later and use TA natively.
(See ta/README for details.)
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.
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.
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/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.
options.h contains the global option struct MPOpts, and its default values
are in defaultopts.c for some reason.
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:
mpvcore/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 playloop.c. They get pushed with run_command() to command.c.
by mplayer.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.)
common/msg.h:
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:
All terminal output should go through mp_msg().
stream/*:
@@ -199,8 +188,8 @@ audio/decode/:
audio/filter/:
Audio filter chain. af_lavrresample is inserted if any form of conversion
between audio formats is needed. (af_convert24.c and af_convertsignendian.c
are also used for some formats not directly supported by FFmpeg.)
between audio formats is needed. (af_format.c is also used for some corner
cases.)
audio/out/:
Audio outputs.
@@ -240,8 +229,8 @@ sub/:
detection as well as timing postprocessing work. (Timing postprocessing
removes tiny gaps or overlaps between subtitle events.)
player/timeline/:
A timeline is the abstraction used by loadfile.c to combine several files
mpvcore/timeline/:
A timeline is the abstraction used by mplayer.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
@@ -1,156 +0,0 @@
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.
+60 -88
View File
@@ -19,7 +19,7 @@
# with MPlayer; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
include old_build/config.mak
include config.mak
###### variable declarations #######
@@ -33,8 +33,6 @@ SOURCES-$(DVBIN) += stream/dvb_tune.c \
stream/stream_dvb.c
SOURCES-$(DVDREAD) += stream/stream_dvd.c \
stream/stream_dvd_common.c
SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
stream/stream_dvd_common.c
SOURCES-$(HAVE_SYS_MMAN_H) += audio/filter/af_export.c
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
@@ -56,6 +54,7 @@ SOURCES-$(COCOA) += video/out/cocoa/view.m \
osdep/ar/HIDRemote.m \
osdep/path-macosx.m
SOURCES-$(MNG) += demux/demux_mng.c
SOURCES-$(MPG123) += audio/decode/ad_mpg123.c
SOURCES-$(NEED_GETTIMEOFDAY) += osdep/gettimeofday.c
@@ -95,7 +94,7 @@ SOURCES-$(GL) += video/out/gl_common.c video/out/gl_osd.c \
video/out/pnm_loader.c
SOURCES-$(ENCODING) += video/out/vo_lavc.c audio/out/ao_lavc.c \
common/encode_lavc.c
mpvcore/encode_lavc.c
SOURCES-$(GL_WIN32) += video/out/w32_common.c video/out/gl_w32.c
SOURCES-$(GL_X11) += video/out/x11_common.c video/out/gl_x11.c
@@ -104,10 +103,10 @@ SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
video/out/gl_wayland.c
SOURCES-$(JACK) += audio/out/ao_jack.c
SOURCES-$(JOYSTICK) += input/joystick.c
SOURCES-$(LIBQUVI) += stream/resolve/resolve_quvi.c
SOURCES-$(LIBQUVI9) += stream/resolve/resolve_quvi9.c
SOURCES-$(LIRC) += input/lirc.c
SOURCES-$(JOYSTICK) += mpvcore/input/joystick.c
SOURCES-$(LIBQUVI) += mpvcore/resolve_quvi.c
SOURCES-$(LIBQUVI9) += mpvcore/resolve_quvi9.c
SOURCES-$(LIRC) += mpvcore/input/lirc.c
SOURCES-$(OPENAL) += audio/out/ao_openal.c
SOURCES-$(OSS) += audio/out/ao_oss.c
SOURCES-$(PULSE) += audio/out/ao_pulse.c
@@ -115,16 +114,13 @@ SOURCES-$(PORTAUDIO) += audio/out/ao_portaudio.c
SOURCES-$(RSOUND) += audio/out/ao_rsound.c
SOURCES-$(SNDIO) += audio/out/ao_sndio.c
SOURCES-$(VDPAU) += video/vdpau.c video/out/vo_vdpau.c
SOURCES-$(VDPAU_GL_X11) += video/out/gl_hwdec_vdpau.c
SOURCES-$(VDA) += video/decode/vda.c
SOURCES-$(VDA_GL) += video/out/gl_hwdec_vda.c
SOURCES-$(VDPAU_DEC) += video/decode/vdpau.c
SOURCES-$(VDPAU_DEC_OLD) += video/decode/vdpau_old.c
SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
video/decode/vaapi.c \
video/vaapi.c
SOURCES-$(VAAPI_VPP) += video/filter/vf_vavpp.c
SOURCES-$(VAAPI_GLX) += video/out/gl_hwdec_vaglx.c
SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
SOURCES-$(XV) += video/out/vo_xv.c
@@ -133,7 +129,7 @@ SOURCES-$(WAYLAND) += video/out/vo_wayland.c video/out/wayland_comm
SOURCES-$(VF_LAVFI) += video/filter/vf_lavfi.c
SOURCES-$(AF_LAVFI) += audio/filter/af_lavfi.c
SOURCES-$(LUA) += player/lua.c
SOURCES-$(LUA) += mpvcore/mp_lua.c
ifeq ($(HAVE_AVUTIL_REFCOUNTING),no)
SOURCES-yes += video/decode/lavc_dr1.c
@@ -142,7 +138,6 @@ endif
SOURCES-$(DLOPEN) += video/filter/vf_dlopen.c
SOURCES = audio/audio.c \
audio/audio_buffer.c \
audio/chmap.c \
audio/chmap_sel.c \
audio/fmt-conversion.c \
@@ -155,12 +150,11 @@ SOURCES = audio/audio.c \
audio/filter/af.c \
audio/filter/af_center.c \
audio/filter/af_channels.c \
audio/filter/af_convert24.c \
audio/filter/af_convertsignendian.c \
audio/filter/af_delay.c \
audio/filter/af_dummy.c \
audio/filter/af_equalizer.c \
audio/filter/af_extrastereo.c \
audio/filter/af_force.c \
audio/filter/af_format.c \
audio/filter/af_hrtf.c \
audio/filter/af_karaoke.c \
@@ -172,26 +166,14 @@ SOURCES = audio/audio.c \
audio/filter/af_sub.c \
audio/filter/af_surround.c \
audio/filter/af_sweep.c \
audio/filter/af_tools.c \
audio/filter/af_drc.c \
audio/filter/af_volume.c \
audio/filter/filter.c \
audio/filter/tools.c \
audio/filter/window.c \
audio/out/ao.c \
audio/out/ao_null.c \
audio/out/ao_pcm.c \
bstr/bstr.c \
common/asxparser.c \
common/av_common.c \
common/av_log.c \
common/av_opts.c \
common/codecs.c \
common/cpudetect.c \
common/common.c \
common/msg.c \
common/playlist.c \
common/playlist_parser.c \
common/version.c \
demux/codec_tags.c \
demux/demux.c \
demux/demux_edl.c \
@@ -204,43 +186,41 @@ SOURCES = audio/audio.c \
demux/demux_subreader.c \
demux/ebml.c \
demux/mf.c \
input/cmd_list.c \
input/cmd_parse.c \
input/input.c \
input/keycodes.c \
misc/charset_conv.c \
misc/ring.c \
options/m_config.c \
options/m_option.c \
options/m_property.c \
options/options.c \
options/parse_commandline.c \
options/parse_configfile.c \
options/path.c \
mpvcore/asxparser.c \
mpvcore/av_common.c \
mpvcore/av_log.c \
mpvcore/av_opts.c \
mpvcore/bstr.c \
mpvcore/charset_conv.c \
mpvcore/codecs.c \
mpvcore/command.c \
mpvcore/cpudetect.c \
mpvcore/m_config.c \
mpvcore/m_option.c \
mpvcore/m_property.c \
mpvcore/mp_common.c \
mpvcore/mp_msg.c \
mpvcore/mp_ring.c \
mpvcore/mplayer.c \
mpvcore/options.c \
mpvcore/parser-cfg.c \
mpvcore/parser-mpcmd.c \
mpvcore/path.c \
mpvcore/playlist.c \
mpvcore/playlist_parser.c \
mpvcore/screenshot.c \
mpvcore/version.c \
mpvcore/input/input.c \
mpvcore/timeline/tl_edl.c \
mpvcore/timeline/tl_matroska.c \
mpvcore/timeline/tl_cue.c \
osdep/io.c \
osdep/numcores.c \
osdep/timer.c \
osdep/threads.c \
player/audio.c \
player/configfiles.c \
player/command.c \
player/dvdnav.c \
player/loadfile.c \
player/main.c \
player/misc.c \
player/osd.c \
player/playloop.c \
player/screenshot.c \
player/sub.c \
player/video.c \
player/timeline/tl_matroska.c \
player/timeline/tl_mpv_edl.c \
player/timeline/tl_cue.c \
stream/cookies.c \
stream/rar.c \
stream/stream.c \
stream/stream_avdevice.c \
stream/stream_edl.c \
stream/stream_file.c \
stream/stream_lavf.c \
stream/stream_memory.c \
@@ -251,7 +231,6 @@ SOURCES = audio/audio.c \
sub/draw_bmp.c \
sub/find_subfiles.c \
sub/img_convert.c \
sub/osd.c \
sub/sd_lavc.c \
sub/sd_lavc_conv.c \
sub/sd_lavf_srt.c \
@@ -260,6 +239,7 @@ SOURCES = audio/audio.c \
sub/sd_spu.c \
sub/sd_srt.c \
sub/spudec.c \
sub/sub.c \
ta/ta.c \
ta/ta_utils.c \
ta/ta_talloc.c \
@@ -271,12 +251,14 @@ SOURCES = audio/audio.c \
video/mp_image_pool.c \
video/sws_utils.c \
video/decode/dec_video.c \
video/decode/vd.c \
video/decode/vd_lavc.c \
video/filter/vf.c \
video/filter/pullup.c \
video/filter/vf_crop.c \
video/filter/vf_delogo.c \
video/filter/vf_divtc.c \
video/filter/vf_down3dright.c \
video/filter/vf_dsize.c \
video/filter/vf_eq.c \
video/filter/vf_expand.c \
@@ -298,6 +280,7 @@ SOURCES = audio/audio.c \
video/filter/vf_sub.c \
video/filter/vf_swapuv.c \
video/filter/vf_unsharp.c \
video/filter/vf_vo.c \
video/filter/vf_yadif.c \
video/out/bitmap_packer.c \
video/out/aspect.c \
@@ -337,20 +320,14 @@ DIRS = . \
audio/decode \
audio/filter \
audio/out \
bstr \
common \
compat \
input \
player/timeline \
mpvcore \
mpvcore/input \
mpvcore/timeline \
demux \
misc \
osdep \
osdep/ar \
player \
stream \
stream/resolve \
sub \
ta \
video \
video/decode \
video/filter \
@@ -400,8 +377,8 @@ mpv$(EXESUF): $(OBJECTS)
mpv$(EXESUF):
$(CC) -o $@ $^ $(EXTRALIBS)
input/input.c: input/input_conf.h
input/input_conf.h: TOOLS/file2string.pl etc/input.conf
mpvcore/input/input.c: mpvcore/input/input_conf.h
mpvcore/input/input_conf.h: TOOLS/file2string.pl etc/input.conf
./$^ >$@
MKVLIB_DEPS = TOOLS/lib/Parse/Matroska.pm \
@@ -430,16 +407,16 @@ sub/osd_libass.c: sub/osd_font.h
sub/osd_font.h: TOOLS/file2string.pl sub/osd_font.otf
./$^ >$@
player/lua.c: player/lua/defaults.inc
player/lua/defaults.inc: TOOLS/file2string.pl player/lua/defaults.lua
mpvcore/mp_lua.c: mpvcore/lua/defaults.inc
mpvcore/lua/defaults.inc: TOOLS/file2string.pl mpvcore/lua/defaults.lua
./$^ >$@
player/lua.c: player/lua/assdraw.inc
player/lua/assdraw.inc: TOOLS/file2string.pl player/lua/assdraw.lua
mpvcore/mp_lua.c: mpvcore/lua/assdraw.inc
mpvcore/lua/assdraw.inc: TOOLS/file2string.pl mpvcore/lua/assdraw.lua
./$^ >$@
player/lua.c: player/lua/osc.inc
player/lua/osc.inc: TOOLS/file2string.pl player/lua/osc.lua
mpvcore/mp_lua.c: mpvcore/lua/osc.inc
mpvcore/lua/osc.inc: TOOLS/file2string.pl mpvcore/lua/osc.lua
./$^ >$@
# ./configure must be rerun if it changed
@@ -460,7 +437,7 @@ version.h .version: version.sh
###### dependency declarations / specific CFLAGS ######
common/version.c osdep/mpv-rc.o: version.h
mpvcore/version.c osdep/mpv-rc.o: version.h
osdep/mpv-rc.o: osdep/mpv.exe.manifest etc/mpv-icon.ico
@@ -517,11 +494,7 @@ install-mpv-desktop: etc/mpv.desktop
$(INSTALL) -d $(prefix)/share/applications
$(INSTALL) -m 644 etc/mpv.desktop $(prefix)/share/applications/
install-mpv-config: etc/encoding-profiles.conf
$(INSTALL) -d $(CONFDIR)
$(INSTALL) -m 644 etc/encoding-profiles.conf $(CONFDIR)
install-data: install-mpv-icons install-mpv-desktop install-mpv-config
install-data: install-mpv-icons install-mpv-desktop
uninstall:
$(RM) $(BINDIR)/mpv$(EXESUF)
@@ -538,19 +511,18 @@ clean:
-$(RM) $(call ADDSUFFIXES,.pdf .tex .log .aux .out .toc,DOCS/man/*/mpv)
-$(RM) DOCS/man/*/mpv.1
-$(RM) version.h
-$(RM) input/input_conf.h
-$(RM) mpvcore/input/input_conf.h
-$(RM) video/out/vdpau_template.c
-$(RM) demux/ebml_types.h demux/ebml_defs.c
-$(RM) video/out/gl_video_shaders.h
-$(RM) video/out/x11_icon.inc
-$(RM) sub/osd_font.h
-$(RM) player/lua/defaults.inc
-$(RM) player/lua/assdraw.inc
-$(RM) player/lua/osc.inc
-$(RM) mpvcore/lua/defaults.inc
-$(RM) mpvcore/lua/assdraw.inc
-$(RM) mpvcore/lua/osc.inc
distclean: clean
-$(RM) config.log old_build/config.h old_build/config.mak Makefile config.mak config.h TAGS tags
-rmdir old_build/
-$(RM) config.log config.mak config.h TAGS tags
TAGS:
$(RM) $@; find . -name '*.[chS]' -o -name '*.asm' | xargs etags -a
+22 -23
View File
@@ -16,29 +16,12 @@ Compilation
-----------
Compiling with full features requires development files for several
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.
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.
Essential dependencies (incomplete list):
@@ -64,6 +47,22 @@ 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
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.
FFmpeg vs. Libav
----------------
-41
View File
@@ -1,41 +0,0 @@
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 (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
for my $subel (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 (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
for my $subel (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 --no-cache --no-config "$@"`
__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet "$@"`
__midentify__fileindex=0
__midentify__prefix=
while :; do
+9 -13
View File
@@ -30,28 +30,24 @@ 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_path(binary_name):
def bundle_name(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_path(binary_name), 'Contents', 'Info.plist')
return os.path.join(bundle_name(binary_name), 'Contents', 'Info.plist')
def target_directory(binary_name):
return os.path.join(bundle_path(binary_name), 'Contents', 'MacOS')
return os.path.join(bundle_name(binary_name), 'Contents', 'MacOS')
def target_binary(binary_name):
return os.path.join(target_directory(binary_name),
os.path.basename(binary_name))
return os.path.join(target_directory(binary_name), binary_name)
def copy_bundle(binary_name):
if os.path.isdir(bundle_path(binary_name)):
shutil.rmtree(bundle_path(binary_name))
if os.path.isdir(bundle_name(binary_name)):
shutil.rmtree(bundle_name(binary_name))
shutil.copytree(
os.path.join('TOOLS', 'osxbundle', bundle_name(binary_name)),
bundle_path(binary_name))
bundle_name(binary_name))
def copy_binary(binary_name):
shutil.copy(binary_name, target_binary(binary_name))
@@ -95,7 +91,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")
@@ -103,7 +99,7 @@ def bundle_dependencies(binary_name):
fix_dylibs_paths(target_binary(binary_name), lib_bundle_directory)
def main():
version = sh("./version.sh --print").strip()
version = sh("TOOLS/osxbundle/version.sh").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>
@@ -0,0 +1,120 @@
<?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
@@ -0,0 +1,15 @@
#!/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
+1 -7
View File
@@ -16,7 +16,6 @@ esac
: ${ILDETECT_DRY_RUN:=}
: ${ILDETECT_QUIET:=}
: ${ILDETECT_RUN_INTERLACED_ONLY:=}
: ${ILDETECT_FORCE_RUN:=}
: ${MAKE:=make}
# exit status:
@@ -77,12 +76,7 @@ case "$out" in
exit 2
;;
*"probably: "*)
[ -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 8
;;
*)
exit 16
+1 -1
View File
@@ -1 +1 @@
0.3.1
0.2.2
+13 -169
View File
@@ -17,60 +17,42 @@
#include <assert.h>
#include <libavutil/mem.h>
#include "talloc.h"
#include "common/common.h"
#include "mpvcore/mp_talloc.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;
update_redundant_info(mpa);
mpa->bps = af_fmt2bits(format) / 8;
}
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
{
mp_chmap_from_channels(&mpa->channels, num_channels);
update_redundant_info(mpa);
struct mp_chmap map;
mp_chmap_from_channels(&map, num_channels);
mp_audio_set_channels(mpa, &map);
}
// Use old MPlayer/ALSA channel layout.
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
{
mp_chmap_from_channels_alsa(&mpa->channels, num_channels);
update_redundant_info(mpa);
struct mp_chmap map;
mp_chmap_from_channels_alsa(&map, num_channels);
mp_audio_set_channels(mpa, &map);
}
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;
update_redundant_info(mpa);
mpa->nch = mpa->channels.num;
}
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src)
{
dst->format = src->format;
dst->channels = src->channels;
mp_audio_set_format(dst, src->format);
mp_audio_set_channels(dst, &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)
@@ -79,17 +61,11 @@ 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_fmt_to_str(format));
chmap->num, af_fmt2str_short(format));
talloc_free(chstr);
return res;
}
@@ -98,135 +74,3 @@ 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);
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;
}
+5 -30
View File
@@ -23,22 +23,14 @@
// Audio data chunk
struct mp_audio {
int samples; // number of samples in data (per channel)
void *planes[MP_NUM_CHANNELS]; // data buffer (one per plane)
int rate; // sample rate
void *audio; // data buffer
int len; // buffer length (in bytes)
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 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()
int nch; // number of channels (redundant with chmap)
int bps; // bytes per sample (redundant with format)
};
void mp_audio_set_format(struct mp_audio *mpa, int format);
@@ -47,25 +39,8 @@ 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
@@ -1,152 +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 <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
@@ -1,44 +0,0 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef 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 "common/msg.h"
#include "mpvcore/mp_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(struct mp_log *log)
void mp_chmap_print_help(int msgt, int msgl)
{
mp_info(log, "Speakers:\n");
mp_msg(msgt, msgl, "Speakers:\n");
for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
if (speaker_names[n][0])
mp_info(log, " %-16s (%s)\n",
speaker_names[n][0], speaker_names[n][1]);
mp_msg(msgt, msgl, " %-16s (%s)\n",
speaker_names[n][0], speaker_names[n][1]);
}
mp_info(log, "Standard layouts:\n");
mp_msg(msgt, msgl, "Standard layouts:\n");
for (int n = 0; std_layout_names[n][0]; n++) {
mp_info(log, " %-16s (%s)\n",
std_layout_names[n][0], std_layout_names[n][1]);
mp_msg(msgt, msgl, " %-16s (%s)\n",
std_layout_names[n][0], std_layout_names[n][1]);
}
for (int n = 0; n < MP_NUM_CHANNELS; n++)
mp_info(log, " unknown%d\n", n);
mp_msg(msgt, msgl, " unknown%d\n", n);
}
+2 -4
View File
@@ -20,7 +20,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include "bstr/bstr.h"
#include "mpvcore/bstr.h"
#define MP_NUM_CHANNELS 8
@@ -121,9 +121,7 @@ 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);
struct mp_log;
void mp_chmap_print_help(struct mp_log *log);
void mp_chmap_print_help(int msgt, int msgl);
// 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,13 +19,11 @@
#ifndef MPLAYER_AD_H
#define MPLAYER_AD_H
#include "common/codecs.h"
#include "mpvcore/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;
@@ -33,14 +31,16 @@ struct mp_decoder_list;
struct ad_functions {
const char *name;
void (*add_decoders)(struct mp_decoder_list *list);
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);
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);
};
enum ad_ctrl {
ADCTRL_RESET = 1, // flush and reset state, e.g. after seeking
ADCTRL_RESYNC_STREAM = 1, // resync, called after seeking
};
#endif /* MPLAYER_AD_H */
+201 -140
View File
@@ -29,34 +29,38 @@
#include "talloc.h"
#include "config.h"
#include "common/av_common.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "options/options.h"
#include "common/av_opts.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 "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;
struct mp_audio frame;
uint8_t *output;
uint8_t *output_packed; // used by deplanarize to store packed audio samples
int output_left;
int unitsize;
bool force_channel_map;
struct demux_packet *packet;
};
static void uninit(struct dec_audio *da);
static int decode_new_packet(struct dec_audio *da);
static void uninit(sh_audio_t *sh);
static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen);
#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}
};
@@ -64,10 +68,12 @@ const m_option_t ad_lavc_decode_opts_conf[] = {
struct pcm_map
{
int tag;
const char *codecs[6]; // {any, 1byte, 2bytes, 3bytes, 4bytes, 8bytes}
const char *codecs[5]; // {any, 1byte, 2bytes, 3bytes, 4bytes}
};
// NOTE: these are needed to make rawaudio with demux_mkv work.
// 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.
static const struct pcm_map tag_map[] = {
// Microsoft PCM
{0x0, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
@@ -75,12 +81,26 @@ 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", [5] = "pcm_f64le"}},
{0x3, {"pcm_f32le"}},
// 'raw '
{0x20776172, {"pcm_s16be", [1] = "pcm_u8"}},
// 'twos', used by demux_mkv.c internally
{MKTAG('t', 'w', 'o', 's'),
{NULL, "pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"}},
// '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"}},
{-1},
};
@@ -103,8 +123,6 @@ 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},
};
@@ -112,13 +130,11 @@ 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 <= 5)
if (bytes >= 1 && bytes <= 4)
dec = entry->codecs[bytes];
if (!dec)
dec = entry->codecs[0];
@@ -129,25 +145,27 @@ static const char *find_pcm_decoder(const struct pcm_map *map, int format,
return NULL;
}
static int setup_format(struct dec_audio *da)
static int preinit(sh_audio_t *sh)
{
struct priv *priv = da->priv;
AVCodecContext *lavc_context = priv->avctx;
struct sh_audio *sh_audio = da->header->audio;
return 1;
}
// 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");
/* 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");
}
struct mp_chmap lavc_chmap;
@@ -159,12 +177,20 @@ static int setup_format(struct dec_audio *da)
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, MP_WAVEFORMATEX *wf)
static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
{
avctx->channels = wf->nChannels;
avctx->sample_rate = wf->nSamplesPerSec;
@@ -179,30 +205,29 @@ static void set_from_wf(AVCodecContext *avctx, MP_WAVEFORMATEX *wf)
}
}
static int init(struct dec_audio *da, const char *decoder)
static int init(sh_audio_t *sh_audio, const char *decoder)
{
struct MPOpts *mpopts = da->opts;
struct MPOpts *mpopts = sh_audio->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);
da->priv = ctx;
sh_audio->context = ctx;
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
decoder = find_pcm_decoder(tag_map, sh->format,
decoder = find_pcm_decoder(tag_map, sh_audio->format,
sh_audio->wf->wBitsPerSample);
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
decoder = find_pcm_decoder(af_map, sh->format, 0);
decoder = find_pcm_decoder(af_map, sh_audio->format, 0);
ctx->force_channel_map = true;
}
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
MP_ERR(da, "Cannot find codec '%s' in libavcodec...\n", decoder);
uninit(da);
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
"Cannot find codec '%s' in libavcodec...\n", decoder);
uninit(sh_audio);
return 0;
}
@@ -213,12 +238,9 @@ static int init(struct dec_audio *da, 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
@@ -227,13 +249,14 @@ static int init(struct dec_audio *da, const char *decoder)
if (opts->avopt) {
if (parse_avopts(lavc_context, opts->avopt) < 0) {
MP_ERR(da, "setting AVOptions '%s' failed.\n", opts->avopt);
uninit(da);
mp_msg(MSGT_DECVIDEO, MSGL_ERR,
"ad_lavc: setting AVOptions '%s' failed.\n", opts->avopt);
uninit(sh_audio);
return 0;
}
}
lavc_context->codec_tag = sh->format;
lavc_context->codec_tag = sh_audio->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);
@@ -251,64 +274,72 @@ static int init(struct dec_audio *da, const char *decoder)
lavc_context->extradata_size);
}
if (sh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
mp_set_avcodec_threads(lavc_context, opts->threads);
if (sh_audio->gsh->lav_headers)
mp_copy_lav_codec_headers(lavc_context, sh_audio->gsh->lav_headers);
/* open it */
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
MP_ERR(da, "Could not open codec.\n");
uninit(da);
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Could not open codec.\n");
uninit(sh_audio);
return 0;
}
MP_VERBOSE(da, "INFO: libavcodec \"%s\" init OK!\n",
mp_msg(MSGT_DECAUDIO, MSGL_V, "INFO: libavcodec \"%s\" init OK!\n",
lavc_codec->name);
// 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);
// 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;
break;
}
if (tries >= 50) {
MP_ERR(da, "initial decode failed\n");
uninit(da);
if (++tries >= 5) {
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"ad_lavc: initial decode failed\n");
uninit(sh_audio);
return 0;
}
}
da->i_bps = lavc_context->bit_rate / 8;
sh_audio->i_bps = lavc_context->bit_rate / 8;
if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
da->i_bps = sh_audio->wf->nAvgBytesPerSec;
sh_audio->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(struct dec_audio *da)
static void uninit(sh_audio_t *sh)
{
struct priv *ctx = da->priv;
struct priv *ctx = sh->context;
if (!ctx)
return;
AVCodecContext *lavc_context = ctx->avctx;
if (lavc_context) {
if (avcodec_close(lavc_context) < 0)
MP_ERR(da, "Could not close codec.\n");
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "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(struct dec_audio *da, int cmd, void *arg)
static int control(sh_audio_t *sh, int cmd, void *arg)
{
struct priv *ctx = da->priv;
struct priv *ctx = sh->context;
switch (cmd) {
case ADCTRL_RESET:
case ADCTRL_RESYNC_STREAM:
avcodec_flush_buffers(ctx->avctx);
ctx->frame.samples = 0;
ctx->output_left = 0;
talloc_free(ctx->packet);
ctx->packet = NULL;
return CONTROL_TRUE;
@@ -316,92 +347,121 @@ static int control(struct dec_audio *da, int cmd, void *arg)
return CONTROL_UNKNOWN;
}
static int decode_new_packet(struct dec_audio *da)
static av_always_inline void deplanarize(struct sh_audio *sh)
{
struct priv *priv = da->priv;
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;
AVCodecContext *avctx = priv->avctx;
priv->frame.samples = 0;
struct demux_packet *mpkt = priv->packet;
if (!mpkt)
mpkt = demux_read_packet(da->header);
mpkt = demux_read_packet(sh->gsh);
if (!mpkt)
return -1; // error or EOF
priv->packet = talloc_steal(priv, mpkt);
int in_len = mpkt ? mpkt->len : 0;
int in_len = mpkt->len;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
mp_set_av_packet(&pkt, mpkt);
// 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;
if (mpkt->pts != MP_NOPTS_VALUE) {
sh->pts = mpkt->pts;
sh->pts_bytes = 0;
}
int got_frame = 0;
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
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;
// 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 (ret < 0) {
MP_VERBOSE(da, "lavc_audio: error\n");
mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n");
return -1;
}
if (!got_frame)
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;
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];
}
MP_DBG(da, "Decoded %d -> %d samples\n", in_len,
priv->frame.samples);
mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", in_len,
priv->output_left);
return 0;
}
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
int maxlen)
{
struct priv *priv = da->priv;
struct priv *priv = sh_audio->context;
AVCodecContext *avctx = priv->avctx;
if (!priv->frame.samples) {
if (decode_new_packet(da) < 0)
return -1;
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 (!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;
return len;
}
static void add_decoders(struct mp_decoder_list *list)
@@ -414,6 +474,7 @@ 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,
+355 -220
View File
@@ -25,40 +25,43 @@
#include "config.h"
#include "ad.h"
#include "common/msg.h"
#include "mpvcore/mp_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>
#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION < 33)
#error "This should not happen"
/* 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
#endif
/* Switch for updating bitrate info of VBR files. Not essential. */
#define AD_MPG123_MEAN_BITRATE
struct ad_mpg123_context {
mpg123_handle *handle;
bool new_format;
int sample_size;
bool need_data;
char new_format;
#ifdef AD_MPG123_MEAN_BITRATE
/* 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(struct dec_audio *da)
static int preinit(sh_audio_t *sh)
{
int err;
struct ad_mpg123_context *con;
@@ -69,8 +72,8 @@ static int preinit(struct dec_audio *da)
if (mpg123_init() != MPG123_OK)
return 0;
da->priv = talloc_zero(NULL, struct ad_mpg123_context);
con = da->priv;
sh->context = malloc(sizeof(struct ad_mpg123_context));
con = sh->context;
/* Auto-choice of optimized decoder (first argument NULL). */
con->handle = mpg123_new(NULL, &err);
if (!con->handle)
@@ -101,147 +104,33 @@ static int preinit(struct dec_audio *da)
/* 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.
* 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. */
* more than 1152 stereo samples. */
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_ERR(da, "mpg123 preinit error: %s\n",
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
mpg123_plain_strerror(err));
else
MP_ERR(da, "mpg123 preinit error: %s\n",
mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
mpg123_strerror(con->handle));
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);
if (con->handle)
mpg123_delete(con->handle);
mpg123_exit();
free(sh->context);
sh->context = NULL;
return 0;
}
@@ -258,103 +147,348 @@ 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(struct dec_audio *da)
static void update_info(sh_audio_t *sh)
{
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) {
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)) {
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;
da->i_bps = (int) (con->mean_rate * 1000 / 8);
sh->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(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
static int decode_audio(sh_audio_t *sh, unsigned char *buf, int minlen,
int maxlen)
{
struct ad_mpg123_context *con = da->priv;
void *buf = buffer->planes[0];
int ret;
int bytes;
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;
}
}
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->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;
#ifdef AD_MPG123_MEAN_BITRATE
update_info(sh);
#endif
return bytes;
}
static int control(struct dec_audio *da, int cmd, void *arg)
static int control(sh_audio_t *sh, int cmd, void *arg)
{
struct ad_mpg123_context *con = da->priv;
switch (cmd) {
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));
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");
return CONTROL_FALSE;
}
return CONTROL_TRUE;
break;
}
return CONTROL_UNKNOWN;
}
@@ -368,6 +502,7 @@ 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,
+200 -178
View File
@@ -19,214 +19,60 @@
*/
#include <string.h>
#include <assert.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include "config.h"
#include "common/msg.h"
#include "common/av_common.h"
#include "options/options.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/av_common.h"
#include "mpvcore/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;
bool need_close;
uint8_t pb_buffer[OUTBUF_SIZE];
};
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;
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;
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;
}
static void uninit(struct dec_audio *da)
static int64_t seek(void *p, int64_t offset, int whence)
{
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);
}
// spdifenc does not use seek callback.
return 0;
}
static int init(struct dec_audio *da, const char *decoder)
static int preinit(sh_audio_t *sh)
{
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;
sh->samplesize = 2;
return 1;
fail:
uninit(da);
return 0;
}
static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen)
{
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 control(struct dec_audio *da, int cmd, void *arg)
{
return CONTROL_UNKNOWN;
}
static const int codecs[] = {
static int codecs[] = {
AV_CODEC_ID_AAC,
AV_CODEC_ID_AC3,
AV_CODEC_ID_DTS,
@@ -236,6 +82,181 @@ static const 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++) {
@@ -250,6 +271,7 @@ 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,
+184 -176
View File
@@ -26,9 +26,9 @@
#include "demux/codec_tags.h"
#include "config.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "bstr/bstr.h"
#include "mpvcore/codecs.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/bstr.h"
#include "stream/stream.h"
#include "demux/demux.h"
@@ -38,8 +38,6 @@
#include "dec_audio.h"
#include "ad.h"
#include "audio/format.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/filter/af.h"
@@ -48,7 +46,7 @@ extern const struct ad_functions ad_lavc;
extern const struct ad_functions ad_spdif;
static const struct ad_functions * const ad_drivers[] = {
#if HAVE_MPG123
#ifdef CONFIG_MPG123
&ad_mpg123,
#endif
&ad_lavc,
@@ -56,57 +54,51 @@ static const struct ad_functions * const ad_drivers[] = {
NULL
};
// 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)
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
{
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);
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");
return 0;
}
d_audio->decode_buffer = mp_audio_buffer_create(NULL);
if (!reinit_audio_buffer(d_audio)) {
uninit_decoder(d_audio);
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
return 0;
}
return 1;
}
struct mp_decoder_list *audio_decoder_list(void)
struct mp_decoder_list *mp_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++)
@@ -114,10 +106,10 @@ struct mp_decoder_list *audio_decoder_list(void)
return list;
}
static struct mp_decoder_list *audio_select_decoders(const char *codec,
char *selection)
static struct mp_decoder_list *mp_select_audio_decoders(const char *codec,
char *selection)
{
struct mp_decoder_list *list = audio_decoder_list();
struct mp_decoder_list *list = mp_audio_decoder_list();
struct mp_decoder_list *new = mp_select_decoders(list, codec, selection);
talloc_free(list);
return new;
@@ -132,81 +124,86 @@ static const struct ad_functions *find_driver(const char *name)
return NULL;
}
int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders)
int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders)
{
assert(!d_audio->ad_driver);
audio_reset_decoding(d_audio);
assert(!sh_audio->initialized);
struct mp_decoder_entry *decoder = NULL;
struct mp_decoder_list *list =
audio_select_decoders(d_audio->header->codec, audio_decoders);
mp_select_audio_decoders(sh_audio->gsh->codec, audio_decoders);
mp_print_decoders(d_audio->log, MSGL_V, "Codec list:", list);
mp_print_decoders(MSGT_DECAUDIO, 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_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)) {
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)) {
decoder = sel;
break;
}
MP_WARN(d_audio, "Audio decoder init failed for "
sh_audio->ad_driver = NULL;
mp_tmsg(MSGT_DECAUDIO, MSGL_WARN, "Audio decoder init failed for "
"%s:%s\n", sel->family, sel->decoder);
}
if (d_audio->ad_driver) {
d_audio->decoder_desc =
talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family,
if (sh_audio->initialized) {
sh_audio->gsh->decoder_desc =
talloc_asprintf(NULL, "%s [%s:%s]", decoder->desc, decoder->family,
decoder->decoder);
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);
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);
} else {
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
d_audio->header->codec ? d_audio->header->codec : "<unknown>");
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>");
}
talloc_free(list);
return !!d_audio->ad_driver;
return sh_audio->initialized;
}
void audio_uninit(struct dec_audio *d_audio)
void uninit_audio(sh_audio_t *sh_audio)
{
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->afilter) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL;
}
uninit_decoder(d_audio);
talloc_free(d_audio->decode_buffer);
talloc_free(d_audio);
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);
}
int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format)
{
if (!d_audio->afilter)
d_audio->afilter = af_new(d_audio->global);
struct af_stream *afs = d_audio->afilter;
if (!sh_audio->afilter)
sh_audio->afilter = af_new(sh_audio->opts);
struct af_stream *afs = sh_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;
@@ -215,14 +212,15 @@ int audio_init_filters(struct dec_audio *d_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_VERBOSE(d_audio, "Building audio filter chain for %s -> %s...\n", s_from, s_to);
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
"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);
d_audio->afilter = NULL;
sh_audio->afilter = NULL;
return 0; // failed :(
}
@@ -233,127 +231,137 @@ int audio_init_filters(struct dec_audio *d_audio, int in_samplerate,
return 1;
}
// 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)
static void set_min_out_buffer_size(struct bstr *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;
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;
// 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;
break;
}
sh->a_buffer_len += ret;
}
// Filter
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_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);
if (af_filter(da->afilter, &filter_data, eof ? AF_FILTER_FLAG_EOF : 0) < 0)
struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
if (!filter_output)
return -1;
mp_audio_buffer_append(outbuf, &filter_data);
if (eof && filter_data.samples > 0)
error = 0; // don't end playback yet
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;
// remove processed data from decoder buffer:
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
}
sh->a_buffer_len -= len;
memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);
return error;
}
/* Try to get at least minsamples decoded+filtered samples in outbuf
/* Try to get at least minlen decoded+filtered bytes in outbuf
* (total length including possible existing data).
* 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)
* 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)
{
// 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 samples
// (Note: the reason for this is unknown, possibly a refactoring artifact)
int unitsize = 16;
// Decoded audio must be cut at boundaries of this many bytes
int unitsize = sh_audio->channels.num * sh_audio->samplesize * 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(d_audio->afilter);
double filter_multiplier = af_calc_filter_multiplier(sh_audio->afilter);
int prev_buffered = -1;
while (minsamples >= 0) {
int buffered = mp_audio_buffer_samples(outbuf);
if (minsamples < buffered || buffered == prev_buffered)
break;
prev_buffered = buffered;
/* 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 decsamples = (minsamples - buffered) / filter_multiplier;
while (minlen >= 0 && outbuf->len < minlen) {
// + some extra for possible filter buffering
decsamples += unitsize << 5;
if (huge_filter_buffer) {
int declen = (minlen - outbuf->len) / filter_multiplier + (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 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
* 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
* 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. */
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);
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);
if (res < 0)
return res;
}
return 0;
}
void audio_reset_decoding(struct dec_audio *d_audio)
void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte)
{
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);
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);
}
+9 -31
View File
@@ -20,42 +20,20 @@
#define MPLAYER_DEC_AUDIO_H
#include "audio/chmap.h"
#include "audio/audio.h"
#include "demux/stheader.h"
struct mp_audio_buffer;
struct bstr;
struct mp_decoder_list;
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;
};
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 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 init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels,
int *out_format);
+123 -172
View File
@@ -22,11 +22,8 @@
#include <string.h>
#include <assert.h>
#include "common/common.h"
#include "common/global.h"
#include "options/m_option.h"
#include "options/m_config.h"
#include "mpvcore/m_option.h"
#include "mpvcore/m_config.h"
#include "af.h"
@@ -55,20 +52,18 @@ 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_format,
&af_info_force,
&af_info_volume,
&af_info_equalizer,
&af_info_pan,
&af_info_surround,
&af_info_sub,
#if HAVE_SYS_MMAN_H
#ifdef HAVE_SYS_MMAN_H
&af_info_export,
#endif
&af_info_drc,
@@ -77,22 +72,21 @@ static struct af_info* filter_list[] = {
&af_info_lavrresample,
&af_info_sweep,
&af_info_hrtf,
#if HAVE_LADSPA
#ifdef CONFIG_LADSPA
&af_info_ladspa,
#endif
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
&af_info_scaletempo,
#if HAVE_LIBBS2B
#ifdef CONFIG_LIBBS2B
&af_info_bs2b,
#endif
#if HAVE_AF_LAVFI
#ifdef CONFIG_AF_LAVFI
&af_info_lavfi,
#endif
// Must come last, because they're fallback format conversion filter
&af_info_convert24,
&af_info_convertsignendian,
// Must come last, because it's the fallback format conversion filter
&af_info_format,
NULL
};
@@ -115,10 +109,7 @@ 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",
.aliases = {
{"force", "format"},
{0}
},
.legacy_hacks = true, // many filters have custom option parsing
};
static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
@@ -142,7 +133,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->priv)->input);
assert(arg == &((struct af_stream *)af->setup)->input);
return AF_OK;
}
return AF_UNKNOWN;
@@ -150,7 +141,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->priv;
struct af_stream *s = af->setup;
struct mp_audio *output = &s->output;
struct mp_audio *filter_output = &s->filter_output;
@@ -168,9 +159,9 @@ static int output_control(struct af_instance* af, int cmd, void* arg)
return AF_UNKNOWN;
}
static int dummy_filter(struct af_instance* af, struct mp_audio* data, int f)
static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data)
{
return 0;
return data;
}
/* Function for creating a new filter of type name.The name may
@@ -180,7 +171,8 @@ 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_ERR(s, "Couldn't find audio filter '%s'.\n", name);
mp_tmsg(MSGT_VFILTER, MSGL_ERR,
"Couldn't find audio filter '%s'.\n", name);
return NULL;
}
const struct af_info *info = desc.p;
@@ -189,37 +181,40 @@ 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_ERR(s, "There can only be one "
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one "
"instance of the filter '%s' in each stream\n", name);
return NULL;
}
}
}
MP_VERBOSE(s, "Adding filter %s \n", name);
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] 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, s->log, &desc);
if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
struct m_config *config = m_config_from_obj_desc(af, &desc);
if (m_config_initialize_obj(config, &desc, &af->priv, &args) < 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_ERR(s, "Couldn't create or open audio filter '%s'\n", name);
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[libaf] Couldn't create or open audio filter '%s'\n", name);
talloc_free(af);
return NULL;
}
@@ -280,14 +275,17 @@ static void af_remove(struct af_stream *s, struct af_instance *af)
return;
// Print friendly message
MP_VERBOSE(s, "Removing filter %s \n", af->info->name);
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);
// Detach pointers
af->prev->next = af->next;
af->next->prev = af->prev;
if (af->uninit)
af->uninit(af);
af->uninit(af);
talloc_free(af);
}
@@ -305,26 +303,26 @@ repeat:
static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
int msg_level)
{
MP_MSG(s, msg_level, "Audio filter chain:\n");
mp_msg(MSGT_AFILTER, msg_level, "Audio filter chain:\n");
struct af_instance *af = s->first;
while (af) {
MP_MSG(s, msg_level, " [%s] ", af->info->name);
mp_msg(MSGT_AFILTER, msg_level, " [%s] ", af->info->name);
if (af->data) {
char *info = mp_audio_config_to_str(af->data);
MP_MSG(s, msg_level, "%s", info);
mp_msg(MSGT_AFILTER, msg_level, "%s", info);
talloc_free(info);
}
if (af == at)
MP_MSG(s, msg_level, " <-");
MP_MSG(s, msg_level, "\n");
mp_msg(MSGT_AFILTER, msg_level, " <-");
mp_msg(MSGT_AFILTER, msg_level, "\n");
af = af->next;
}
MP_MSG(s, msg_level, " [ao] ");
mp_msg(MSGT_AFILTER, msg_level, " [ao] ");
char *info = mp_audio_config_to_str(&s->output);
MP_MSG(s, msg_level, "%s\n", info);
mp_msg(MSGT_AFILTER, msg_level, "%s\n", info);
talloc_free(info);
}
@@ -336,76 +334,14 @@ static int af_count_filters(struct af_stream *s)
return count;
}
// 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)
static char *af_find_conversion_filter(int srcfmt, int dstfmt)
{
#define NUM_FMT 64
#define NUM_FILT 32
#define NUM_NODES (NUM_FMT * NUM_FILT)
for (int n = 0; filter_list[n]; n++)
assert(n < NUM_FILT);
for (int n = 0; af_fmtstr_table[n].format; n++)
assert(n < NUM_FMT);
bool visited[NUM_NODES] = {0};
unsigned char distance[NUM_NODES];
short previous[NUM_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;
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;
}
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 NUM_FMT
#undef NUM_FILT
#undef NODE_N
return NULL;
}
static bool af_is_conversion_filter(struct af_instance *af)
@@ -416,7 +352,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 (or is closer)
// AF_OK: must call af_reinit() or equivalent, format matches
// AF_FALSE: nothing was changed, format matches
// else: error
static int af_fix_format_conversion(struct af_stream *s,
@@ -429,21 +365,18 @@ static int af_fix_format_conversion(struct af_stream *s,
struct mp_audio actual = *prev->data;
if (actual.format == in.format)
return AF_FALSE;
int dstfmt = in.format;
char *filter = af_find_conversion_filter(actual.format, &dstfmt);
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);
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_SET_FORMAT, &dstfmt)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
return rv;
*p_af = new;
return AF_OK;
@@ -459,7 +392,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_SET_CHANNELS, &in.channels) == AF_OK) {
if (prev->control(prev, AF_CONTROL_CHANNELS, &in.channels) == AF_OK) {
*p_af = prev;
return AF_OK;
}
@@ -468,7 +401,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_SET_CHANNELS, &in.channels)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.channels)))
return rv;
*p_af = new;
return AF_OK;
@@ -483,7 +416,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_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
if (prev->control(prev, AF_CONTROL_RESAMPLE_RATE, &in.rate) == AF_OK) {
*p_af = prev;
return AF_OK;
}
@@ -492,7 +425,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_SET_RESAMPLE_RATE, &in.rate)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate)))
return rv;
*p_af = new;
return AF_OK;
@@ -506,12 +439,10 @@ 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;
// Up to 7 retries per filter (channel, rate, 5x format conversions)
int max_retry = af_count_filters(s) * 7;
int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter
int retry = 0;
while (af) {
if (retry >= max_retry)
@@ -520,11 +451,10 @@ 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...
mp_audio_set_null_data(&in);
in.audio = NULL;
in.len = 0;
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;
@@ -554,8 +484,8 @@ static int af_reinit(struct af_stream *s)
break;
}
default:
MP_ERR(s, "Reinitialization did not work, "
"audio filter '%s' returned error code %i\n",
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] 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;
@@ -572,7 +502,8 @@ static int af_reinit(struct af_stream *s)
return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
negotiate_error:
MP_ERR(s, "Unable to convert audio input format to output format.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
"This error should never occur, please send a bug report.\n");
af_print_filter_chain(s, af, MSGL_ERR);
return AF_ERROR;
}
@@ -584,7 +515,7 @@ void af_uninit(struct af_stream *s)
af_remove(s, s->first->next);
}
struct af_stream *af_new(struct mpv_global *global)
struct af_stream *af_new(struct MPOpts *opts)
{
struct af_stream *s = talloc_zero(NULL, struct af_stream);
static struct af_info in = { .name = "in" };
@@ -592,8 +523,8 @@ struct af_stream *af_new(struct mpv_global *global)
*s->first = (struct af_instance) {
.info = &in,
.control = input_control,
.filter = dummy_filter,
.priv = s,
.play = dummy_play,
.setup = s,
.data = &s->input,
.mul = 1.0,
};
@@ -602,15 +533,14 @@ struct af_stream *af_new(struct mpv_global *global)
*s->last = (struct af_instance) {
.info = &out,
.control = output_control,
.filter = dummy_filter,
.priv = s,
.play = dummy_play,
.setup = s,
.data = &s->filter_output,
.mul = 1.0,
};
s->first->next = s->last;
s->last->prev = s->first;
s->opts = global->opts;
s->log = mp_log_new(s, global->log, "!af");
s->opts = opts;
return s;
}
@@ -623,7 +553,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
format of the preferred output respectively. The function is
formate 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
@@ -636,8 +566,8 @@ int af_init(struct af_stream *s)
return -1;
// Precaution in case caller is misbehaving
mp_audio_set_null_data(&s->input);
mp_audio_set_null_data(&s->output);
s->input.audio = s->output.audio = NULL;
s->input.len = s->output.len = 0;
// Check if this is the first call
if (s->first->next == s->last) {
@@ -651,9 +581,11 @@ 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_ERR(s, "Could not create audio filter chain.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "Could not create audio filter chain.\n");
af_uninit(s);
return -1;
}
@@ -687,31 +619,36 @@ struct af_instance *af_add(struct af_stream *s, char *name, char **args)
return new;
}
/* 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)
// Filter data chunk through the filters in the list
struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data)
{
struct af_instance *af = s->first;
assert(mp_audio_config_equals(af->data, data));
// Iterate through all filters
while (af) {
int r = af->filter(af, data, flags);
if (r < 0)
return r;
assert(mp_audio_config_equals(af->data, data));
do {
if (data->len <= 0)
break;
data = af->play(af, data);
af = af->next;
}
return 0;
} while (af && data);
return data;
}
// Calculate average ratio of filter output samples to input samples.
// e.g: num_output_samples = mul * num_input_samples
/* 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
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;
@@ -720,20 +657,41 @@ double af_calc_filter_multiplier(struct af_stream *s)
return mul;
}
/* Calculate the total delay [seconds of output] caused by the filters */
/* Calculate the total delay [bytes output] caused by the filters */
double af_calc_delay(struct af_stream *s)
{
struct af_instance *af = s->first;
double delay = 0.0;
register double delay = 0.0;
// Iterate through all filters
while (af) {
delay += af->delay;
delay *= af->mul;
af = af->next;
}
return delay;
}
/* Send control to all filters, starting with the last until one accepts the
* command with AF_OK. Return the accepting filter. */
/* 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
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
{
int res = AF_UNKNOWN;
@@ -746,10 +704,3 @@ 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);
}
+193 -50
View File
@@ -21,16 +21,17 @@
#include <stdio.h>
#include <stdbool.h>
#include <sys/types.h>
#include "options/options.h"
#include "config.h"
#include "mpvcore/options.h"
#include "audio/format.h"
#include "audio/chmap.h"
#include "audio/audio.h"
#include "common/msg.h"
#include "control.h"
#include "mpvcore/mp_msg.h"
struct af_instance;
struct mpv_global;
// Number of channels
#define AF_NCH MP_NUM_CHANNELS
@@ -39,14 +40,13 @@ struct mpv_global;
#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,22 +58,18 @@ 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);
/* 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);
struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data);
void *setup; // old field for priv structs
void *priv;
struct mp_audio *data; // configuration and buffer for outgoing data stream
struct mp_audio *data; // configuration for outgoing data stream
struct af_instance *next;
struct af_instance *prev;
double delay; /* Delay caused by the filter, in seconds of audio consumed
* without corresponding output */
double delay; /* Delay caused by the filter, in units of bytes read without
* corresponding output */
double mul; /* length multiplier: how much does this instance change
* the number of samples passed though. (Ratio of input
* and output, e.g. mul=4 => 1 sample becomes 4 samples) .*/
the length of the buffer. */
bool auto_inserted; // inserted by af.c, such as conversion filters
};
@@ -84,15 +80,18 @@ 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
@@ -101,46 +100,190 @@ 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;
struct af_stream *af_new(struct mpv_global *global);
/*********************************************
// Export functions
*/
/**
* \defgroup af_chain Audio filter chain functions
* \{
* \param s filter chain
*/
struct af_stream *af_new(struct MPOpts *opts);
void af_destroy(struct af_stream *s);
int af_init(struct af_stream *s);
void af_uninit(struct af_stream *s);
struct af_instance *af_add(struct af_stream *s, char *name, char **args);
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 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
*/
struct af_instance *af_control_any_rev(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);
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);
/**
* \brief soft clipping function using sin()
* \param a input value
* \return clipped value
*/
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 */
+56 -46
View File
@@ -27,7 +27,7 @@
#include <string.h>
#include "af.h"
#include "options/m_option.h"
#include "mpvcore/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 FILTER(name, type) \
static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
#define PLAY(name, type) \
static struct mp_audio *play_##name(struct af_instance *af, struct mp_audio *data) \
{ \
/* filter is called for all pairs of samples available in the buffer */ \
bs2b_cross_feed_##name(((struct af_bs2b*)(af->priv))->filter, \
(type*)(data->planes[0]), data->samples); \
(type*)(data->audio), data->len/data->bps/2); \
\
return 0; \
return data; \
}
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)
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)
/// Initialization and runtime control
@@ -74,6 +74,7 @@ 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;
@@ -86,69 +87,70 @@ static int control(struct af_instance *af, int cmd, void *arg)
and assign corresponding handlers */
switch (format) {
case AF_FORMAT_FLOAT_BE:
af->filter = filter_fbe;
af->play = play_fbe;
break;
case AF_FORMAT_FLOAT_LE:
af->filter = filter_fle;
af->play = play_fle;
break;
case AF_FORMAT_S32_BE:
af->filter = filter_s32be;
af->play = play_s32be;
break;
case AF_FORMAT_U32_BE:
af->filter = filter_u32be;
af->play = play_u32be;
break;
case AF_FORMAT_S32_LE:
af->filter = filter_s32le;
af->play = play_s32le;
break;
case AF_FORMAT_U32_LE:
af->filter = filter_u32le;
af->play = play_u32le;
break;
case AF_FORMAT_S24_BE:
af->filter = filter_s24be;
af->play = play_s24be;
break;
case AF_FORMAT_U24_BE:
af->filter = filter_u24be;
af->play = play_u24be;
break;
case AF_FORMAT_S24_LE:
af->filter = filter_s24le;
af->play = play_s24le;
break;
case AF_FORMAT_U24_LE:
af->filter = filter_u24le;
af->play = play_u24le;
break;
case AF_FORMAT_S16_BE:
af->filter = filter_s16be;
af->play = play_s16be;
break;
case AF_FORMAT_U16_BE:
af->filter = filter_u16be;
af->play = play_u16be;
break;
case AF_FORMAT_S16_LE:
af->filter = filter_s16le;
af->play = play_s16le;
break;
case AF_FORMAT_U16_LE:
af->filter = filter_u16le;
af->play = play_u16le;
break;
case AF_FORMAT_S8:
af->filter = filter_s8;
af->play = play_s8;
break;
case AF_FORMAT_U8:
af->filter = filter_u8;
af->play = play_u8;
break;
default:
af->filter = filter_f;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
af->play = play_f;
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
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_WARN(af, "[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
mp_msg(MSGT_AFILTER, MSGL_WARN,
"[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_VERBOSE(af, "[bs2b] using format %s\n",
af_fmt_to_str(af->data->format));
mp_msg(MSGT_AFILTER, MSGL_V, "[bs2b] using format %s\n",
af_fmt2str(af->data->format,buf,256));
return af_test_output(af,(struct mp_audio*)arg);
}
@@ -160,6 +162,7 @@ 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);
}
@@ -170,9 +173,13 @@ 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;
}
@@ -190,9 +197,12 @@ static int af_open(struct af_instance *af)
/// Description of this filter
struct af_info af_info_bs2b = {
.info = "Bauer stereophonic-to-binaural audio filter",
.name = "bs2b",
.open = af_open,
"Bauer stereophonic-to-binaural audio filter",
"bs2b",
"Andrew Savchenko",
"",
AF_FLAGS_REENTRANT,
af_open,
.priv_size = sizeof(struct af_bs2b),
.options = (const struct m_option[]) {
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
+49 -20
View File
@@ -29,7 +29,6 @@
#include <stdlib.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -41,7 +40,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->priv;
af_center_t* s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -49,23 +48,47 @@ 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, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
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);
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 int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
af_center_t* s = af->priv; // Setup for this instance
float* a = c->planes[0]; // Audio 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
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;
@@ -75,25 +98,31 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
a[i+ch] = (a[i]/2) + (a[i+1]/2);
}
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af_center_t* s;
af->control=control;
af->filter=filter;
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
return AF_OK;
}
#define OPT_BASE_STRUCT af_center_t
// Description of this filter
struct af_info af_info_center = {
.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}
},
"Audio filter for adding a center channel",
"center",
"Alex Beregszaszi",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+88 -68
View File
@@ -3,8 +3,6 @@
* 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
@@ -27,7 +25,6 @@
#include <string.h>
#include <inttypes.h>
#include "common/common.h"
#include "af.h"
#define FR 0
@@ -35,14 +32,12 @@
typedef struct af_channels_s{
int route[AF_NCH][2];
int nch, nr;
int nr;
int router;
char *routes;
}af_channels_t;
// Local function for copying data
static void copy(struct af_instance *af, void* in, void* out,
int ins, int inos,int outs, int outos, int len, int bps)
static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps)
{
switch(bps){
case 1:{
@@ -113,25 +108,24 @@ static void copy(struct af_instance *af, void* in, void* out,
break;
}
default:
MP_ERR(af, "Unsupported number of bytes/sample: %i"
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] 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(struct af_instance *af, int nin, int nout)
static int check_routes(af_channels_t* s, int nin, int nout)
{
af_channels_t* s = af->priv;
int i;
if((s->nr < 1) || (s->nr > AF_NCH)){
MP_ERR(af, "[channels] The number of routing pairs must be"
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);
return AF_ERROR;
}
for(i=0;i<s->nr;i++){
if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){
MP_ERR(af, "[channels] Invalid routing in pair nr. %i.\n", i);
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i);
return AF_ERROR;
}
}
@@ -141,13 +135,9 @@ static int check_routes(struct af_instance *af, int nin, int nout)
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_channels_t* s = af->priv;
af_channels_t* s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT: ;
struct mp_chmap chmap;
mp_chmap_set_unknown(&chmap, s->nch);
mp_audio_set_channels(af->data, &chmap);
case AF_CONTROL_REINIT:
// Set default channel assignment
if(!s->router){
@@ -158,14 +148,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 = MPMIN(af->data->nch,2);
s->nr = min(af->data->nch,2);
for(i=0;i<s->nr;i++){
s->route[i][FR] = 0;
s->route[i][TO] = i;
}
}
else{
s->nr = MPMIN(af->data->nch, ((struct mp_audio*)arg)->nch);
s->nr = min(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;
@@ -174,78 +164,108 @@ 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);
return check_routes(af,((struct mp_audio*)arg)->nch,af->data->nch);
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 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 int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
struct mp_audio* l = af->data; // Local data
af_channels_t* s = af->priv;
af_channels_t* s = af->setup;
int i;
mp_audio_realloc_min(af->data, data->samples);
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
// Reset unused channels
memset(l->planes[0],0,mp_audio_psize(c) / c->nch * l->nch);
memset(l->audio,0,c->len / c->nch * l->nch);
if(AF_OK == check_routes(af,c->nch,l->nch))
if(AF_OK == check_routes(s,c->nch,l->nch))
for(i=0;i<s->nr;i++)
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);
copy(c->audio,l->audio,c->nch,s->route[i][FR],
l->nch,s->route[i][TO],c->len,c->bps);
// Set output data
c->planes[0] = l->planes[0];
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
mp_audio_set_channels(c, &l->channels);
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
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;
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;
}
#define OPT_BASE_STRUCT af_channels_t
// Description of this filter
struct af_info af_info_channels = {
.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}
},
"Insert or remove channels",
"channels",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
};
-116
View File
@@ -1,116 +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 <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
@@ -1,131 +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 <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,
};
+61 -54
View File
@@ -3,8 +3,6 @@
* 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
@@ -27,7 +25,6 @@
#include <string.h>
#include <inttypes.h>
#include "common/common.h"
#include "af.h"
#define L 65536
@@ -41,48 +38,67 @@ 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->priv;
af_delay_t* s = af->setup;
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_force_interleaved_format(in);
mp_audio_copy_config(af->data, in);
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
// Allocate new delay queues
for(i=0;i<af->data->nch;i++){
s->q[i] = calloc(L,af->data->bps);
if(NULL == s->q[i])
MP_FATAL(af, "[delay] Out of memory\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
}
if(AF_OK != af_from_ms(AF_NCH, s->d, s->wi, af->data->rate, 0.0, 1000.0))
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))
return AF_ERROR;
s->ri = 0;
for(i=0;i<AF_NCH;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]);
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]);
}
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;
}
@@ -92,23 +108,25 @@ static void uninit(struct af_instance* af)
{
int i;
free(af->data);
for(i=0;i<AF_NCH;i++)
free(((af_delay_t*)(af->priv))->q[i]);
free(((af_delay_t*)(af->setup))->q[i]);
free(af->setup);
}
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
af_delay_t* s = af->priv; // Setup for this instance
af_delay_t* s = af->setup; // Setup for this instance
int nch = c->nch; // Number of channels
int len = mp_audio_psize(c)/c->bps; // Number of sample in data chunk
int len = c->len/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->planes[0];
int8_t* a = c->audio;
int8_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -122,7 +140,7 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
break;
}
case 2:{
int16_t* a = c->planes[0];
int16_t* a = c->audio;
int16_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -136,7 +154,7 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
break;
}
case 4:{
int32_t* a = c->planes[0];
int32_t* a = c->audio;
int32_t* q = s->q[ch];
int wi = s->wi[ch];
ri = s->ri;
@@ -152,39 +170,28 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
}
}
s->ri = ri;
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
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;
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;
}
#define OPT_BASE_STRUCT af_delay_t
// Description of this filter
struct af_info af_info_delay = {
.info = "Delay audio filter",
.name = "delay",
.open = af_open,
.priv_size = sizeof(af_delay_t),
.options = (const struct m_option[]) {
OPT_STRING("delays", delaystr, 0),
{0}
},
"Delay audio filter",
"delay",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
};
+65 -44
View File
@@ -26,7 +26,6 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Methods:
@@ -81,27 +80,46 @@ 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)){
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16_NE)){
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
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->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
int16_t *data = (int16_t*)c->audio; // Audio data
int len = c->len/2; // Number of samples
float curavg = 0.0, newavg, neededmul;
int tmp;
@@ -121,14 +139,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 = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
}
// Scale & clamp the samples
for (i = 0; i < len; i++)
{
tmp = s->mul * data[i];
tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
data[i] = tmp;
}
@@ -142,8 +160,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->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float *data = (float*)c->audio; // Audio data
int len = c->len/4; // Number of samples
float curavg = 0.0, newavg, neededmul, tmp;
for (i = 0; i < len; i++)
@@ -162,7 +180,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 = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
}
// Scale & clamp the samples
@@ -179,8 +197,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->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
int16_t *data = (int16_t*)c->audio; // Audio data
int len = c->len/2; // Number of samples
float curavg = 0.0, newavg, avg = 0.0;
int tmp, totallen = 0;
@@ -205,7 +223,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 = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
}
}
@@ -213,7 +231,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 = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
data[i] = tmp;
}
@@ -229,8 +247,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->planes[0]; // Audio data
int len = c->samples*c->nch; // Number of samples
float *data = (float*)c->audio; // Audio data
int len = c->len/4; // Number of samples
float curavg = 0.0, newavg, avg = 0.0, tmp;
int totallen = 0;
@@ -255,7 +273,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 = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
}
}
@@ -272,56 +290,59 @@ static void method2_float(af_drc_t *s, struct mp_audio *c)
s->idx = (s->idx + 1) % NSAMPLES;
}
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
af_drc_t *s = af->priv;
af_drc_t *s = af->setup;
if(af->data->format == (AF_FORMAT_S16))
if(af->data->format == (AF_FORMAT_S16_NE))
{
if (s->method == 2)
if (s->method)
method2_int16(s, data);
else
method1_int16(s, data);
}
else if(af->data->format == (AF_FORMAT_FLOAT))
else if(af->data->format == (AF_FORMAT_FLOAT_NE))
{
if (s->method == 2)
if (s->method)
method2_float(s, data);
else
method1_float(s, data);
}
return 0;
return data;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
int i = 0;
af->control=control;
af->filter=filter;
af_drc_t *priv = af->priv;
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;
priv->mul = MUL_INIT;
priv->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
priv->idx = 0;
((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;
for (i = 0; i < NSAMPLES; i++)
{
priv->mem[i].len = 0;
priv->mem[i].avg = 0;
((af_drc_t*)af->setup)->mem[i].len = 0;
((af_drc_t*)af->setup)->mem[i].avg = 0;
}
priv->mid_s16 = ((float)SHRT_MAX) * priv->mid_float;
return AF_OK;
}
#define OPT_BASE_STRUCT af_drc_t
// Description of this filter
struct af_info af_info_drc = {
.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}
},
"Dynamic range compression filter",
"drc",
"Alex Beregszaszi & Pierre Lombard",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+26 -11
View File
@@ -2,8 +2,6 @@
* 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
@@ -33,29 +31,46 @@ static int control(struct af_instance* af, int cmd, void* arg)
switch(cmd){
case AF_CONTROL_REINIT: ;
*af->data = *(struct mp_audio*)arg;
MP_VERBOSE(af, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
af->data->rate,af->data->nch,af_fmt_to_str(af->data->format));
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));
return AF_OK;
}
return AF_UNKNOWN;
}
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
// Deallocate memory
static void uninit(struct af_instance* af)
{
return 0;
free(af->data);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
// 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;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->filter=filter;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=malloc(sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
return AF_OK;
}
// Description of this filter
struct af_info af_info_dummy = {
.info = "dummy",
.name = "dummy",
.open = af_open,
"dummy",
"dummy",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
};
+44 -31
View File
@@ -29,7 +29,6 @@
#include <inttypes.h>
#include <math.h>
#include "common/common.h"
#include "af.h"
#define L 2 // Storage for filter taps
@@ -68,7 +67,6 @@ 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
@@ -86,7 +84,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->priv;
af_equalizer_t* s = (af_equalizer_t*)af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -99,7 +97,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);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
// Calculate number of active filters
s->K=KM;
@@ -107,7 +105,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->K--;
if(s->K != KM)
MP_INFO(af, "[equalizer] Limiting the number of filters to"
mp_msg(MSGT_AFILTER, MSGL_INFO, "[equalizer] Limiting the number of filters to"
" %i due to low sample rate.\n",s->K);
// Generate filter taps
@@ -115,7 +113,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.0 / (double)af->data->rate;
af->delay = 2 * af->data->nch * af->data->bps;
// Calculate gain factor to prevent clipping at output
for(k=0;k<AF_NCH;k++)
@@ -137,23 +135,43 @@ 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 int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
af_equalizer_t* s = (af_equalizer_t*)af->priv; // Setup
af_equalizer_t* s = (af_equalizer_t*)af->setup; // 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->planes[0])+ci;
float* out = ((float*)c->planes[0])+ci;
float* end = in + c->samples*c->nch; // Block loop end
float* in = ((float*)c->audio)+ci;
float* out = ((float*)c->audio)+ci;
float* end = in + c->len/4; // Block loop end
while(in < end){
register int k = 0; // Frequency band index
@@ -177,33 +195,28 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
out+=nch;
}
}
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
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;
}
}
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;
return AF_OK;
}
#define OPT_BASE_STRUCT af_equalizer_t
// Description of this filter
struct af_info af_info_equalizer = {
.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}
},
"Equalizer audio filter",
"equalizer",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+80 -36
View File
@@ -7,8 +7,6 @@
* 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
@@ -39,11 +37,9 @@
#include <sys/stat.h>
#include <fcntl.h>
#include "osdep/io.h"
#include "talloc.h"
#include "af.h"
#include "options/path.h"
#include "mpvcore/path.h"
#define DEF_SZ 512 // default buffer size (in samples)
#define SHARED_FILE "mpv-af_export" /* default file name
@@ -71,7 +67,7 @@ typedef struct af_export_s
*/
static int control(struct af_instance* af, int cmd, void* arg)
{
af_export_t* s = af->priv;
af_export_t* s = af->setup;
switch (cmd){
case AF_CONTROL_REINIT:{
int i=0;
@@ -90,25 +86,29 @@ 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);
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;
// 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_FATAL(af, "[export] Out of memory\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[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_FATAL(af, "[export] No filename set.\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] No filename set.\n");
return AF_ERROR;
}
// Init memory mapping
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);
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);
if(s->fd < 0) {
MP_FATAL(af, "[export] Could not open/create file: %s\n",
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[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_FATAL(af, "[export] Could not mmap file %s\n", s->filename);
MP_INFO(af, "[export] Memory mapped to file: %s (%p)\n",
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",
s->filename, s->mmap_area);
// Initialize header
@@ -137,6 +137,41 @@ 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;
}
@@ -146,7 +181,11 @@ static int control(struct af_instance* af, int cmd, void* arg)
*/
static void uninit( struct af_instance* af )
{
af_export_t* s = af->priv;
free(af->data);
af->data = NULL;
if(af->setup){
af_export_t* s = af->setup;
if (s->buf)
free(s->buf[0]);
@@ -156,19 +195,25 @@ 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 int filter( struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play( struct af_instance* af, struct mp_audio* data )
{
struct mp_audio* c = data; // Current working data
af_export_t* s = af->priv; // Setup for this instance
int16_t* a = c->planes[0]; // Incomming sound
af_export_t* s = af->setup; // Setup for this instance
int16_t* a = c->audio; // Incomming sound
int nch = c->nch; // Number of channels
int len = c->samples*c->nch; // Number of sample in data chunk
int len = c->len/c->bps; // Number of sample in data chunk
int sz = s->sz; // buffer size (in samples)
int flag = 0; // Set to 1 if buffer is filled
@@ -199,7 +244,8 @@ static int filter( struct af_instance* af, struct mp_audio* data, int flags)
&(s->count), sizeof(s->count));
}
return 0;
// We don't modify data, just export it
return data;
}
/* Allocate memory and set function pointers
@@ -210,26 +256,24 @@ static int af_open( struct af_instance* af )
{
af->control = control;
af->uninit = uninit;
af->filter = filter;
af_export_t *priv = af->priv;
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;
if (!priv->filename || !priv->filename[0]) {
MP_FATAL(af, "no export filename given");
return AF_ERROR;
}
((af_export_t *)af->setup)->filename = mp_find_user_config_file(SHARED_FILE);
return AF_OK;
}
#define OPT_BASE_STRUCT af_export_t
// Description of this filter
struct af_info af_info_export = {
.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}
},
"Sound export filter",
"export",
"Anders; Gustavo Sverzut Barbieri <[email protected]>",
"",
AF_FLAGS_REENTRANT,
af_open
};
+49 -32
View File
@@ -26,7 +26,6 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -35,42 +34,56 @@ typedef struct af_extrastereo_s
float mul;
}af_extrastereo_t;
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);
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);
// 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)
if (af->data->format == AF_FORMAT_FLOAT_NE)
{
af->filter = play_float;
af->play = play_float;
}// else
{
mp_audio_set_format(af->data, AF_FORMAT_S16);
af->filter = play_s16;
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = 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;
}
// Filter data through filter
static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
// Deallocate memory
static void uninit(struct af_instance* af)
{
af_extrastereo_t *s = af->priv;
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
{
af_extrastereo_t *s = af->setup;
register int i = 0;
int16_t *a = (int16_t*)data->planes[0]; // Audio data
int len = data->samples*data->nch; // Number of samples
int16_t *a = (int16_t*)data->audio; // Audio data
int len = data->len/2; // Number of samples
int avg, l, r;
for (i = 0; i < len; i+=2)
@@ -80,19 +93,19 @@ static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
l = avg + (int)(s->mul * (a[i] - avg));
r = avg + (int)(s->mul * (a[i + 1] - avg));
a[i] = MPCLAMP(l, SHRT_MIN, SHRT_MAX);
a[i + 1] = MPCLAMP(r, SHRT_MIN, SHRT_MAX);
a[i] = clamp(l, SHRT_MIN, SHRT_MAX);
a[i + 1] = clamp(r, SHRT_MIN, SHRT_MAX);
}
return 0;
return data;
}
static int play_float(struct af_instance* af, struct mp_audio* data, int f)
static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data)
{
af_extrastereo_t *s = af->priv;
af_extrastereo_t *s = af->setup;
register int i = 0;
float *a = (float*)data->planes[0]; // Audio data
int len = data->samples * data->nch; // Number of samples
float *a = (float*)data->audio; // Audio data
int len = data->len/4; // Number of samples
float avg, l, r;
for (i = 0; i < len; i+=2)
@@ -106,26 +119,30 @@ static int play_float(struct af_instance* af, struct mp_audio* data, int f)
a[i + 1] = af_softclip(r);
}
return 0;
return data;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->filter=play_s16;
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_extrastereo_t*)af->setup)->mul = 2.5;
return AF_OK;
}
#define OPT_BASE_STRUCT af_extrastereo_t
// Description of this filter
struct af_info af_info_extrastereo = {
.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}
},
"Increase difference between audio channels",
"extrastereo",
"Alex Beregszaszi & Pierre Lombard",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+141
View File
@@ -0,0 +1,141 @@
/*
* 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}
},
};
+452 -90
View File
@@ -1,132 +1,494 @@
/*
* This file is part of mpv.
* 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.
*
* mpv is free software; you can redistribute it and/or modify
* 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.
*
* mpv is distributed in the hope that it will be useful,
* 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 mpv. If not, see <http://www.gnu.org/licenses/>.
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <sys/types.h>
#include <libavutil/common.h>
#include "options/m_option.h"
#include "audio/format.h"
#include "config.h"
#include "af.h"
#include "compat/mpbswap.h"
struct priv {
struct m_config *config;
/* Functions used by play to convert the input audio to the correct
format */
int in_format;
int in_srate;
struct mp_chmap in_channels;
int out_format;
int out_srate;
struct mp_chmap out_channels;
// 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 fail;
};
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);
static void force_in_params(struct af_instance *af, struct mp_audio *in)
// Helper functions to check sanity for input arguments
// Sanity check for bytes per sample
static int check_bps(int bps)
{
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;
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;
}
static void force_out_params(struct af_instance *af, struct mp_audio *out)
// Check for unsupported formats
static int check_format(int format)
{
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;
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 int control(struct af_instance *af, int cmd, void *arg)
static bool test_conversion(int src_format, int dst_format)
{
struct priv *priv = af->priv;
// 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;
}
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
// 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;
force_in_params(af, in);
mp_audio_copy_config(out, in);
force_out_params(af, out);
// Make sure this filter isn't redundant
if(af->data->format == data->format)
return AF_DETACH;
if (in->nch != out->nch || in->bps != out->bps) {
MP_ERR(af, "[af_format] Forced input/output formats are incompatible.\n");
return AF_ERROR;
}
// 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);
if (priv->fail) {
MP_ERR(af, "[af_format] Failing on purpose.\n");
return AF_ERROR;
}
// 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;
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
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;
}
return AF_UNKNOWN;
}
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;
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
mp_audio_copy_config(data, af->data);
return 0;
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->filter = filter;
force_in_params(af, af->data);
force_out_params(af, af->data);
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
#define OPT_BASE_STRUCT struct priv
// 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 = {
.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}
},
"Sample format conversion",
"format",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open,
.test_conversion = test_conversion,
};
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)
{
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;
}
}
}
static void si2us(void* data, int len, int bps)
{
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);
}
static void change_bps(void* in, void* out, int len, int inbps, int outbps)
{
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;
}
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;
}
}
static void float2int(float* in, void* out, int len, int bps)
{
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;
}
}
static void int2float(void* in, float* out, int len, int bps)
{
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;
}
}
+72 -46
View File
@@ -3,8 +3,6 @@
* 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
@@ -71,7 +69,6 @@ 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
@@ -287,8 +284,9 @@ 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->priv;
af_hrtf_t *s = af->setup;
int test_output_res;
char mode;
switch(cmd) {
case AF_CONTROL_REINIT:
@@ -296,7 +294,8 @@ 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_ERR(af, "[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n",
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n",
af->data->rate);
return AF_ERROR;
}
@@ -310,12 +309,35 @@ 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);
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
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;
@@ -324,7 +346,8 @@ static int control(struct af_instance *af, int cmd, void* arg)
/* Deallocate memory */
static void uninit(struct af_instance *af)
{
af_hrtf_t *s = af->priv;
if(af->setup) {
af_hrtf_t *s = af->setup;
free(s->lf);
free(s->rf);
@@ -339,6 +362,11 @@ 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
@@ -352,44 +380,50 @@ frequencies).
2. A bass compensation is introduced to ensure that 0-200 Hz are not
damped (without any real 3D acoustical image, however).
*/
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
{
af_hrtf_t *s = af->priv;
short *in = data->planes[0]; // Input audio data
af_hrtf_t *s = af->setup;
short *in = data->audio; // Input audio data
short *out = NULL; // Output audio data
short *end = in + data->samples * data->nch; // Loop end
short *end = in + data->len / sizeof(short); // Loop end
float common, left, right, diff, left_b, right_b;
const int dblen = s->dlbuflen, hlen = s->hrflen, blen = s->basslen;
mp_audio_realloc_min(af->data, data->samples);
if(AF_OK != RESIZE_LOCAL_BUFFER(af, data))
return NULL;
if(s->print_flag) {
s->print_flag = 0;
switch (s->decode_mode) {
case HRTF_MIX_51:
MP_INFO(af, "[hrtf] Using HRTF to mix %s discrete surround into "
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[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_INFO(af, "[hrtf] Using HRTF to mix stereo into "
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using HRTF to mix stereo into "
"L, R channels\n");
break;
case HRTF_MIX_MATRIX2CH:
MP_INFO(af, "[hrtf] Using active matrix to decode 2 channel "
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[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_WARN(af, "[hrtf] bogus decode_mode: %d\n", s->decode_mode);
mp_msg(MSGT_AFILTER, MSGL_WARN,
"[hrtf] bogus decode_mode: %d\n", s->decode_mode);
break;
}
if(s->matrix_mode)
MP_INFO(af, "[hrtf] Using active matrix to decode rear center "
mp_msg(MSGT_AFILTER, MSGL_INFO,
"[hrtf] Using active matrix to decode rear center "
"channel\n");
}
out = af->data->planes[0];
out = af->data->audio;
/* MPlayer's 5 channel layout (notation for the variable):
*
@@ -529,10 +563,11 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
}
/* Set output data */
data->planes[0] = af->data->planes[0];
data->audio = af->data->audio;
data->len = data->len / data->nch * 2;
mp_audio_set_num_channels(data, 2);
return 0;
return data;
}
static int allocate(af_hrtf_t *s)
@@ -565,9 +600,14 @@ static int af_open(struct af_instance* af)
af->control = control;
af->uninit = uninit;
af->filter = filter;
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;
s = af->priv;
s = af->setup;
s->dlbuflen = DELAYBUFLEN;
s->hrflen = HRTFFILTLEN;
@@ -580,22 +620,10 @@ 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_ERR(af, "[hrtf] Memory allocation error.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n");
return AF_ERROR;
}
@@ -614,13 +642,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_ERR(af, "[hrtf] Memory allocation error.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "[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_ERR(af, "[hrtf] Unable to design low-pass "
mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Unable to design low-pass "
"filter.\n");
return AF_ERROR;
}
@@ -630,14 +658,12 @@ static int af_open(struct af_instance* af)
return AF_OK;
}
#define OPT_BASE_STRUCT af_hrtf_t
/* Description of this filter */
struct af_info af_info_hrtf = {
.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}
},
"HRTF Headphone",
"hrtf",
"ylai",
"",
AF_FLAGS_REENTRANT,
af_open
};
+25 -10
View File
@@ -35,19 +35,25 @@ 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);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
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 int play(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
float* a = c->planes[0]; // Audio data
float* a = c->audio; // Audio data
int len = c->len/4; // Number of samples in current audio block
int nch = c->nch; // Number of channels
int len = c->samples*nch; // Number of samples in current audio block
register int i;
/*
@@ -62,20 +68,29 @@ static int play(struct af_instance* af, struct mp_audio* data, int flags)
a[i+1]=a[i];
}
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control = control;
af->filter = play;
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_karaoke = {
.info = "Simple karaoke/voice-removal audio filter",
.name = "karaoke",
.flags = AF_FLAGS_NOT_REENTRANT,
.open = af_open,
"Simple karaoke/voice-removal audio filter",
"karaoke",
"Reynaldo H. Verdejo Pinochet",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+220 -147
View File
@@ -42,8 +42,6 @@
#include "af.h"
#define _(x) (x)
/* ------------------------------------------------------------------------- */
/* Filter specific data */
@@ -65,7 +63,6 @@ typedef struct af_ladspa_s
char *file;
char *label;
char *controls;
char *myname; /**< It's easy to have a concatenation of file and label */
@@ -103,18 +100,15 @@ static int af_ladspa_malloc_failed(char*);
/* ------------------------------------------------------------------------- */
#define OPT_BASE_STRUCT af_ladspa_t
/* Description */
struct af_info af_info_ladspa = {
.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}
},
"LADSPA plugin loader",
"ladspa",
"Ivo van Poorten",
"",
AF_FLAGS_REENTRANT,
af_open
};
/* ------------------------------------------------------------------------- */
@@ -137,8 +131,7 @@ struct af_info af_info_ladspa = {
* configuration. Else, it returns AF_ERROR.
*/
static int af_ladspa_parse_plugin(struct af_instance *af) {
af_ladspa_t *setup = af->priv;
static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
int p, i;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
LADSPA_PortDescriptor d;
@@ -218,30 +211,30 @@ static int af_ladspa_parse_plugin(struct af_instance *af) {
}
if (setup->ninputs == 0) {
MP_WARN(af, "%s: %s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_WARN, "%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_VERBOSE(af, "%s: this is a mono effect\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a mono effect\n", setup->myname);
} else if (setup->ninputs == 2) {
MP_VERBOSE(af, "%s: this is a stereo effect\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a stereo effect\n", setup->myname);
} else {
MP_VERBOSE(af, "%s: this is a %i-channel effect, "
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a %i-channel effect, "
"support is experimental\n", setup->myname, setup->ninputs);
}
if (setup->noutputs == 0) {
MP_ERR(af, "%s: %s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("This LADSPA plugin has no audio outputs."));
return AF_ERROR;
}
if (setup->noutputs != setup->ninputs ) {
MP_ERR(af, "%s: %s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("The number of audio inputs and audio outputs of the LADSPA plugin differ."));
return AF_ERROR;
}
MP_VERBOSE(af, "%s: this plugin has %d input control(s)\n",
mp_msg(MSGT_AFILTER, MSGL_V, "%s: this plugin has %d input control(s)\n",
setup->myname, setup->ninputcontrols);
/* Print list of controls and its range of values it accepts */
@@ -249,18 +242,18 @@ static int af_ladspa_parse_plugin(struct af_instance *af) {
for (i=0; i<setup->ninputcontrols; i++) {
p = setup->inputcontrolsmap[i];
hint = pdes->PortRangeHints[p];
MP_VERBOSE(af, " --- %d %s [", i, pdes->PortNames[p]);
mp_msg(MSGT_AFILTER, MSGL_V, " --- %d %s [", i, pdes->PortNames[p]);
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
MP_VERBOSE(af, "%0.2f , ", hint.LowerBound);
mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f , ", hint.LowerBound);
} else {
MP_VERBOSE(af, "... , ");
mp_msg(MSGT_AFILTER, MSGL_V, "... , ");
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
MP_VERBOSE(af, "%0.2f]\n", hint.UpperBound);
mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f]\n", hint.UpperBound);
} else {
MP_VERBOSE(af, "...]\n");
mp_msg(MSGT_AFILTER, MSGL_V, "...]\n");
}
}
@@ -306,9 +299,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_VERBOSE(af, "\ton windows, only absolute pathnames "
mp_msg(MSGT_AFILTER, MSGL_V, "\ton windows, only absolute pathnames "
"are supported\n");
MP_VERBOSE(af, "\ttrying %s\n", filename);
mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename);
return dlopen(filename, flag);
#endif
@@ -349,6 +342,7 @@ 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);
@@ -362,6 +356,7 @@ 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);
}
@@ -382,25 +377,24 @@ 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(struct af_instance *af) {
af_ladspa_t *setup = af->priv;
static int af_ladspa_load_plugin(af_ladspa_t *setup) {
const LADSPA_Descriptor *ladspa_descriptor;
LADSPA_Descriptor_Function descriptor_function;
int i;
/* load library */
MP_VERBOSE(af, "%s: loading ladspa plugin library %s\n",
mp_msg(MSGT_AFILTER, MSGL_V, "%s: loading ladspa plugin library %s\n",
setup->myname, setup->file);
setup->libhandle = mydlopen(setup->file, RTLD_NOW);
if (!setup->libhandle) {
MP_ERR(af, "%s: %s %s\n\t%s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s %s\n\t%s\n", setup->myname,
_("failed to load"), setup->file, dlerror() );
return AF_ERROR;
}
MP_VERBOSE(af, "%s: library found.\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%s: library found.\n", setup->myname);
/* find descriptor function */
dlerror();
@@ -408,7 +402,7 @@ static int af_ladspa_load_plugin(struct af_instance *af) {
"ladspa_descriptor");
if (!descriptor_function) {
MP_ERR(af, "%s: %s\n\t%s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n\t%s\n", setup->myname,
_("Couldn't find ladspa_descriptor() function in the specified library file."), dlerror());
return AF_ERROR;
}
@@ -416,33 +410,33 @@ static int af_ladspa_load_plugin(struct af_instance *af) {
/* if label == help, list all labels in library and exit */
if (strcmp(setup->label, "help") == 0) {
MP_INFO(af, "%s: %s %s:\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_INFO, "%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_INFO(af, " %-16s - %s (%lu)\n",
mp_msg(MSGT_AFILTER, MSGL_INFO, " %-16s - %s (%lu)\n",
ladspa_descriptor->Label,
ladspa_descriptor->Name,
ladspa_descriptor->UniqueID);
}
}
MP_VERBOSE(af, "%s: looking for label\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%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_ERR(af, "%s: %s\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_ERR, "%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_VERBOSE(af, "%s: %s found\n", setup->myname,
mp_msg(MSGT_AFILTER, MSGL_V, "%s: %s found\n", setup->myname,
setup->label);
return AF_OK;
}
@@ -463,6 +457,7 @@ static int af_ladspa_load_plugin(struct af_instance *af) {
*/
static int af_ladspa_malloc_failed(char *myname) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s", myname, "Memory allocation failed.\n");
return AF_ERROR;
}
@@ -475,6 +470,13 @@ 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
@@ -484,20 +486,157 @@ 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->priv;
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
float val;
switch(cmd) {
case AF_CONTROL_REINIT:
MP_VERBOSE(af, "%s: (re)init\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%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);
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() :-/
*/
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;
@@ -514,12 +653,13 @@ static int control(struct af_instance *af, int cmd, void *arg) {
*/
static void uninit(struct af_instance *af) {
if (af->priv) {
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
free(af->data);
if (af->setup) {
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
if (setup->myname) {
MP_VERBOSE(af, "%s: cleaning up\n", setup->myname);
mp_msg(MSGT_AFILTER, MSGL_V, "%s: cleaning up\n", setup->myname);
free(setup->myname);
}
@@ -531,6 +671,8 @@ 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);
@@ -552,6 +694,9 @@ static void uninit(struct af_instance *af) {
if (setup->libhandle)
dlclose(setup->libhandle);
free(setup);
setup = NULL;
}
}
@@ -565,22 +710,26 @@ static void uninit(struct af_instance *af) {
* \return Either AF_ERROR or AF_OK
*/
static int filter(struct af_instance *af, struct mp_audio *data, int flags) {
af_ladspa_t *setup = af->priv;
static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
af_ladspa_t *setup = af->setup;
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
float *audio = (float*)data->planes[0];
int nsamples = data->samples*data->nch;
float *audio = (float*)data->audio;
int nsamples = data->len/4; /* /4 because it's 32-bit float */
int nch = data->nch;
int rate = data->rate;
int i, p;
if (setup->status !=AF_OK)
return -1;
return data;
/* 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
@@ -588,7 +737,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags) {
*/
if (setup->nch != 0) {
MP_TRACE(af, "%s: bufsize change; free old buffer\n",
mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize change; free old buffer\n",
setup->myname);
if(setup->inbufs) {
@@ -609,7 +758,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags) {
setup->inbufs = calloc(nch, sizeof(float*));
setup->outbufs = calloc(nch, sizeof(float*));
MP_TRACE(af, "%s: bufsize = %d\n",
mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize = %d\n",
setup->myname, setup->bufsize);
for(i=0; i<nch; i++) {
@@ -721,7 +870,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags) {
/* done */
return 0;
return data;
}
/* ------------------------------------------------------------------------- */
@@ -737,105 +886,29 @@ static int af_open(struct af_instance *af) {
af->control=control;
af->uninit=uninit;
af->filter=filter;
af->play=play;
af->mul=1;
af_ladspa_t *setup = af->priv;
af->data = calloc(1, sizeof(struct mp_audio));
if (af->data == NULL)
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
setup->status = AF_ERROR; /* will be set to AF_OK if
* all went OK and filter()
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()
* should proceed.
*/
setup->myname = strdup(af_info_ladspa.name);
if (!setup->myname)
((af_ladspa_t*)af->setup)->myname = strdup(af_info_ladspa.name);
if (!((af_ladspa_t*)af->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;
}
+233 -169
View File
@@ -32,10 +32,9 @@
#include <libavutil/common.h>
#include <libavutil/mem.h>
#include "common/common.h"
#include "config.h"
#include "af.h"
#include "audio/audio_buffer.h"
#include "audio/fmt-conversion.h"
#include "audio/reorder_ch.h"
#define AC3_MAX_CHANNELS 6
@@ -51,274 +50,339 @@ typedef struct af_ac3enc_s {
struct AVCodec *lavc_acodec;
struct AVCodecContext *lavc_actx;
AVPacket pkt;
bool planarize;
int add_iec61937_header;
int bit_rate;
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 pending_data_size;
char *pending_data;
int pending_len;
int expect_len;
int min_channel_num;
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->priv;
af_ac3enc_t *s = af->setup;
struct mp_audio *data = arg;
int i, bit_rate, test_output_res;
static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \
{0, 96000, 192000, 256000, 384000, 448000, 448000};
switch (cmd){
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)
case AF_CONTROL_REINIT:
if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num)
return AF_DETACH;
mp_audio_set_format(in, s->in_sampleformat);
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);
if (in->rate != 48000 && in->rate != 44100 && in->rate != 32000)
in->rate = 48000;
af->data->rate = in->rate;
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;
mp_chmap_reorder_to_lavc(&in->channels);
if (in->nch > AC3_MAX_CHANNELS)
mp_audio_set_num_channels(in, AC3_MAX_CHANNELS);
mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc reinit: %d, %d, %f, %d.\n",
data->nch, data->rate, af->mul, s->expect_len);
mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
mp_audio_set_num_channels(af->data, 2);
bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[af->data->nch];
if (!mp_audio_config_equals(in, &orig_in))
return AF_FALSE;
if (s->lavc_actx->channels != af->data->nch ||
s->lavc_actx->sample_rate != af->data->rate ||
s->lavc_actx->bit_rate != bit_rate) {
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 = in->nch;
s->lavc_actx->channel_layout = mp_chmap_to_lavc(&in->channels);
s->lavc_actx->sample_rate = in->rate;
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->bit_rate = bit_rate;
if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) {
MP_ERR(af, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
return AF_ERROR;
}
}
if (s->lavc_actx->frame_size != AC3_FRAME_SIZE) {
MP_ERR(af, "lavcac3enc: unexpected ac3 "
mp_msg(MSGT_AFILTER, MSGL_ERR, "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->priv;
af_ac3enc_t *s = af->setup;
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 int filter(struct af_instance* af, struct mp_audio* audio, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* audio)
{
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;
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;
int max_out_samples = s->out_samples * num_frames;
mp_audio_realloc_min(out, max_out_samples);
out->samples = 0;
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;
while (audio->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) {
int ret;
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);
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;
}
mp_audio_buffer_peek(s->pending, &in_frame);
if (in_frame.samples < s->in_samples)
break;
consumed_pending = s->in_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;
}
in_frame.samples = s->in_samples;
AVFrame *frame = avcodec_alloc_frame();
if (!frame) {
MP_FATAL(af, "[libaf] Could not allocate memory \n");
return -1;
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
return NULL;
}
frame->nb_samples = s->in_samples;
frame->nb_samples = AC3_FRAME_SIZE;
frame->format = s->lavc_actx->sample_fmt;
frame->channel_layout = s->lavc_actx->channel_layout;
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;
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;
}
int ok;
ret = avcodec_encode_audio2(s->lavc_actx, &s->pkt, frame, &ok);
if (ret < 0 || !ok) {
MP_FATAL(af, "[lavac3enc] Encode failed.\n");
return -1;
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[lavac3enc] Encode failed.\n");
return NULL;
}
if (s->planarize)
free(data);
avcodec_free_frame(&frame);
mp_audio_buffer_skip(s->pending, consumed_pending);
MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n",
s->pkt.size, mp_audio_buffer_samples(s->pending));
int frame_size = s->pkt.size;
int header_len = 0;
char hdr[8];
if (s->cfg_add_iec61937_header && s->pkt.size > 5) {
int bsmod = s->pkt.data[5] & 0x7;
int len = frame_size;
frame_size = AC3_FRAME_SIZE * 2 * 2;
header_len = 8;
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
if (s->pending_len) {
s->pending_len = 0;
} else {
src += s->expect_len;
left -= s->expect_len;
}
size_t max_size = (max_out_samples - out->samples) * out->sstride;
if (frame_size > max_size)
abort();
mp_msg(MSGT_AFILTER, MSGL_DBG2, "avcodec_encode_audio got %d, pending %d.\n",
s->pkt.size, s->pending_len);
int len = s->pkt.size;
int header_len = 0;
if (s->add_iec61937_header) {
assert(s->pkt.size > 5);
int bsmod = s->pkt.data[5] & 0x7;
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);
header_len = 8;
len = AC3_FRAME_SIZE * 2 * 2;
}
assert(buf + len <= (char *)af->data->audio + af->data->len);
assert(s->pkt.size <= len - header_len);
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);
memset(buf + header_len + s->pkt.size, 0,
frame_size - (header_len + s->pkt.size));
out->samples += frame_size / out->sstride;
outsize += len;
buf += len;
}
mp_audio_buffer_append(s->pending, audio);
*audio = *out;
return 0;
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;
}
static int af_open(struct af_instance* af){
af_ac3enc_t *s = af->priv;
af_ac3enc_t *s = calloc(1,sizeof(af_ac3enc_t));
af->control=control;
af->uninit=uninit;
af->filter=filter;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=s;
s->lavc_acodec = avcodec_find_encoder_by_name("ac3");
if (!s->lavc_acodec) {
MP_ERR(af, "Audio LAVC, couldn't find encoder for codec %s.\n", "ac3");
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "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_ERR(af, "Audio LAVC, couldn't allocate context!\n");
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't allocate context!\n");
return AF_ERROR;
}
const enum AVSampleFormat *fmts = s->lavc_acodec->sample_fmts;
for (int i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
s->in_sampleformat = af_from_avformat(fmts[i]);
if (s->in_sampleformat) {
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;
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;
}
}
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));
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");
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 = {
.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}
},
"runtime encode to ac3 using libavcodec",
"lavcac3enc",
"Ulion",
"",
AF_FLAGS_REENTRANT,
af_open
};
+62 -46
View File
@@ -38,8 +38,8 @@
#include "audio/fmt-conversion.h"
#include "af.h"
#include "options/m_option.h"
#include "common/av_opts.h"
#include "mpvcore/m_option.h"
#include "mpvcore/av_opts.h"
#define IS_LIBAV_FORK (LIBAVFILTER_VERSION_MICRO < 100)
@@ -58,7 +58,14 @@ struct priv {
AVFilterContext *in;
AVFilterContext *out;
int64_t samples_in;
// 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;
AVRational timebase_out;
@@ -82,19 +89,19 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
int r;
if (bstr0(p->cfg_graph).len == 0) {
MP_FATAL(af, "lavfi: no filter graph set\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "lavfi: no filter graph set\n");
return false;
}
destroy_graph(af);
MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph);
mp_msg(MSGT_AFILTER, MSGL_V, "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_FATAL(af, "lavfi: could not set opts: '%s'\n",
mp_msg(MSGT_VFILTER, MSGL_FATAL, "lavfi: could not set opts: '%s'\n",
p->cfg_avopts);
goto error;
}
@@ -122,8 +129,6 @@ 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,
@@ -158,7 +163,7 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
return true;
error:
MP_FATAL(af, "Can't configure libavfilter graph.\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "Can't configure libavfilter graph.\n");
avfilter_graph_free(&graph);
talloc_free(tmp);
return false;
@@ -175,7 +180,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);
mp_audio_set_format(in, AF_FORMAT_FLOAT_NE);
if (!mp_chmap_is_lavc(&in->channels))
mp_chmap_reorder_to_lavc(&in->channels); // will always work
@@ -195,51 +200,55 @@ 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;
// Blatantly incorrect; we don't know what the filters do.
af->mul = out->rate / (double)in->rate;
af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
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 int filter(struct af_instance *af, struct mp_audio *data, int flags)
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
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->samples;
frame->nb_samples = data->len / in_frame_size;
frame->format = l_in->format;
// Timebase is 1/sample_rate
frame->pts = p->samples_in;
frame->pts = p->bytes_in / in_frame_size;
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, eof ? NULL : frame) < 0) {
if (av_buffersrc_add_frame(p->in, frame) < 0) {
av_frame_free(&frame);
return -1;
return NULL;
}
av_frame_free(&frame);
int64_t out_pts = AV_NOPTS_VALUE;
r->samples = 0;
size_t out_len = 0;
for (;;) {
frame = av_frame_alloc();
if (av_buffersink_get_frame(p->out, frame) < 0) {
@@ -248,55 +257,62 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
break;
}
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;
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;
if (out_pts == AV_NOPTS_VALUE)
out_pts = frame->pts;
av_frame_free(&frame);
}
p->samples_in += data->samples;
r->audio = p->out_buffer;
r->len = out_len;
p->bytes_in += data->len;
p->bytes_out += r->len;
if (out_pts != AV_NOPTS_VALUE) {
double in_time = p->samples_in / (double)data->rate;
int64_t num_in_frames = p->bytes_in / in_frame_size;
double in_time = num_in_frames / (double)data->rate;
double out_time = out_pts * av_q2d(p->timebase_out);
// Need pts past the last output sample.
out_time += r->samples / (double)r->rate;
int out_frames = r->len / out_frame_size;
out_time += out_frames / (double)r->rate;
af->delay = in_time - out_time;
af->delay = (in_time - out_time) * r->rate * out_frame_size;
}
*data = *r;
return 0;
return r;
}
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->filter = filter;
// Removing this requires fixing AVFrame.data vs. AVFrame.extended_data
assert(MP_NUM_CHANNELS <= AV_NUM_DATA_POINTERS);
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_lavfi = {
.info = "libavfilter bridge",
.name = "lavfi",
.open = af_open,
"libavfilter bridge",
"lavfi",
"",
"",
0,
af_open,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_STRING("graph", cfg_graph, 0),
+144 -178
View File
@@ -3,7 +3,6 @@
* 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
@@ -24,8 +23,6 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <libavutil/opt.h>
#include <libavutil/audioconvert.h>
#include <libavutil/common.h>
@@ -35,10 +32,10 @@
#include "talloc.h"
#include "config.h"
#if HAVE_LIBAVRESAMPLE
#if defined(CONFIG_LIBAVRESAMPLE)
#include <libavresample/avresample.h>
#define USE_SET_CHANNEL_MAPPING HAVE_AVRESAMPLE_SET_CHANNEL_MAPPING
#elif HAVE_LIBSWRESAMPLE
#elif defined(CONFIG_LIBSWRESAMPLE)
#include <libswresample/swresample.h>
#define AVAudioResampleContext SwrContext
#define avresample_alloc_context swr_alloc
@@ -51,12 +48,12 @@
#define avresample_set_channel_mapping swr_set_channel_mapping
#define USE_SET_CHANNEL_MAPPING 1
#else
#error "config.h broken or no resampler found"
#error "config.h broken"
#endif
#include "common/msg.h"
#include "options/m_option.h"
#include "common/av_opts.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/m_option.h"
#include "mpvcore/av_opts.h"
#include "audio/filter/af.h"
#include "audio/fmt-conversion.h"
#include "audio/reorder_ch.h"
@@ -88,24 +85,16 @@ struct af_resample {
uint8_t *reorder_buffer;
};
#if HAVE_LIBAVRESAMPLE
#ifdef CONFIG_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)
@@ -136,98 +125,8 @@ static bool test_conversion(int src_format, int dst_format)
af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
}
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;
}
#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 control(struct af_instance *af, int cmd, void *arg)
{
@@ -251,38 +150,120 @@ 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);
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)
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) {
mp_audio_set_format(out, in->format);
out_samplefmt = in_samplefmt;
}
af->mul = out->rate / (double)in->rate;
af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
af->delay = out->nch * s->opts.filter_size / FFMIN(af->mul, 1);
int r = ((in->format == orig_in.format) &&
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) &&
mp_chmap_equals(&in->channels, &orig_in.channels))
? AF_OK : AF_FALSE;
if (r == AF_OK && needs_lavrctx_reconfigure(s, in, out))
r = configure_lavrr(af, in, out);
return r;
? AF_OK : AF_FALSE;
}
case AF_CONTROL_SET_FORMAT: {
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: {
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_SET_CHANNELS: {
case AF_CONTROL_CHANNELS | AF_CONTROL_SET: {
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
return AF_OK;
}
case AF_CONTROL_SET_RESAMPLE_RATE:
case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
out->rate = *(int *)arg;
return AF_OK;
case AF_CONTROL_RESET:
drop_all_output(s);
return AF_OK;
}
return AF_UNKNOWN;
}
@@ -310,74 +291,52 @@ static bool needs_reorder(int *reorder, int num_ch)
return false;
}
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)
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
struct af_resample *s = af->priv;
struct mp_audio *in = data;
struct mp_audio *out = af->data;
out->samples = avresample_available(s->avrctx) +
av_rescale_rnd(get_delay(s) + in->samples,
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,
s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP);
int out_size = out->bps * out_samples * out->nch;
mp_audio_realloc_min(out, out->samples);
if (talloc_get_size(out->audio) < out_size)
out->audio = talloc_realloc_size(out, out->audio, out_size);
af->delay = get_delay(s) / (double)s->ctx.in_rate;
af->delay = out->bps * av_rescale_rnd(get_delay(s),
s->ctx.out_rate, s->ctx.in_rate,
AV_ROUND_UP);
#if !USE_SET_CHANNEL_MAPPING
do_reorder(in, s->reorder_in);
reorder_channels(data->audio, s->reorder_in, data->bps, data->nch, in_samples);
#endif
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
}
out_samples = avresample_convert(s->avrctx,
(uint8_t **) &out->audio, out_size, out_samples,
(uint8_t **) &in->audio, in_size, in_samples);
*data = *out;
#if USE_SET_CHANNEL_MAPPING
if (needs_reorder(s->reorder_out, out->nch)) {
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);
}
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);
}
#else
do_reorder(data, s->reorder_out);
reorder_channels(data->audio, s->reorder_out, out->bps, out->nch, out_samples);
#endif
return 0;
data->len = out->bps * out_samples * out->nch;
return data;
}
static int af_open(struct af_instance *af)
@@ -386,7 +345,11 @@ static int af_open(struct af_instance *af)
af->control = control;
af->uninit = uninit;
af->filter = filter;
af->play = play;
af->mul = 1;
af->data = talloc_zero(s, struct mp_audio);
af->data->rate = 0;
if (s->opts.cutoff <= 0.0)
s->opts.cutoff = af_resample_default_cutoff(s->opts.filter_size);
@@ -397,7 +360,7 @@ static int af_open(struct af_instance *af)
if (s->avrctx && s->avrctx_out) {
return AF_OK;
} else {
MP_ERR(af, "[lavrresample] Cannot initialize "
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot initialize "
"Libavresample Context. \n");
uninit(af);
return AF_ERROR;
@@ -407,9 +370,12 @@ static int af_open(struct af_instance *af)
#define OPT_BASE_STRUCT struct af_resample
struct af_info af_info_lavrresample = {
.info = "Sample frequency conversion using libavresample",
.name = "lavrresample",
.open = af_open,
"Sample frequency conversion using libavresample",
"lavrresample",
"Stefano Pigozzi (based on Michael Niedermayer's lavcresample)",
"",
AF_FLAGS_REENTRANT,
af_open,
.test_conversion = test_conversion,
.priv_size = sizeof(struct af_resample),
.priv_defaults = &(const struct af_resample) {
+83 -59
View File
@@ -25,7 +25,6 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
// Data for specific instances of this filter
@@ -33,7 +32,6 @@ 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)
@@ -48,7 +46,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->priv;
af_pan_t* s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT:
@@ -56,8 +54,9 @@ 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);
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
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)){
@@ -65,7 +64,33 @@ static int control(struct af_instance* af, int cmd, void* arg)
return AF_FALSE;
}
return AF_OK;
case AF_CONTROL_SET_PAN_LEVEL:{
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:{
int i;
int ch = ((af_control_ext_t*)arg)->ch;
float* level = ((af_control_ext_t*)arg)->arg;
@@ -75,30 +100,40 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->level[ch][i] = level[i];
return AF_OK;
}
case AF_CONTROL_SET_PAN_NOUT:
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:
// Reinit must be called after this function has been called
// Sanity check
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
MP_ERR(af, "[pan] The number of output channels must be"
mp_msg(MSGT_AFILTER, MSGL_ERR, "[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_SET_PAN_BALANCE:{
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{
float val = *(float*)arg;
if (s->nch)
return AF_ERROR;
if (af->data->nch >= 2) {
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);
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);
}
return AF_OK;
}
case AF_CONTROL_GET_PAN_BALANCE:
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET:
if (s->nch)
return AF_ERROR;
*(float*)arg = s->level[0][1] - s->level[1][0];
@@ -107,22 +142,32 @@ 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 int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
struct mp_audio* l = af->data; // Local data
af_pan_t* s = af->priv; // Setup for this instance
float* in = c->planes[0]; // Input audio data
af_pan_t* s = af->setup; // Setup for this instance
float* in = c->audio; // Input audio data
float* out = NULL; // Output audio data
float* end = in+c->samples*c->nch; // End of loop
float* end = in+c->len/4; // End of loop
int nchi = c->nch; // Number of input channels
int ncho = l->nch; // Number of output channels
register int j,k;
mp_audio_realloc_min(af->data, data->samples);
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
out = l->planes[0];
out = l->audio;
// Execute panning
// FIXME: Too slow
while(in < end){
@@ -138,53 +183,32 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
}
// Set output data
c->planes[0] = l->planes[0];
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
set_channels(c, l->nch);
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->filter=filter;
af_pan_t *s = af->priv;
int n = 0;
int j,k;
int nch = s->nch;
if(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(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;
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;
}
#define OPT_BASE_STRUCT af_pan_t
// Description of this filter
struct af_info af_info_pan = {
.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}
},
"Panning audio filter",
"pan",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
};
+404 -362
View File
@@ -36,441 +36,483 @@
#include <limits.h>
#include <assert.h>
#include "common/common.h"
#include "mpvcore/mp_common.h"
#include "af.h"
#include "options/m_option.h"
#include "mpvcore/m_option.h"
// Data for specific instances of this filter
typedef struct af_scaletempo_s
{
// 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;
// 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;
} 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 = mp_audio_psize(data) - offset;
int offset_unchanged = offset;
af_scaletempo_t* s = af->priv;
int bytes_in = data->len - 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->planes[0] + 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->audio + 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 best_corr = INT_MIN;
int best_off = 0;
float *pw, *po, *ppc, *search_start;
float best_corr = INT_MIN;
int best_off = 0;
int i, off;
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++;
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 *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;
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++;
}
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)
{
int64_t best_corr = INT64_MIN;
int best_off = 0;
int32_t *pw, *ppc;
int16_t *po, *search_start;
int64_t best_corr = INT64_MIN;
int best_off = 0;
int off;
long i;
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;
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;
}
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 = (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;
}
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);
for (int 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);
int i;
for (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);
for (int 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);
int i;
for (i=0; i<s->samples_overlap; i++) {
*pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++;
}
}
// Filter data through filter
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
af_scaletempo_t *s = af->priv;
af_scaletempo_t* s = af->priv;
int offset_in;
int max_bytes_out;
int8_t* pout;
if (s->scale == 1.0) {
af->delay = 0;
return 0;
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;
}
af->data->len = max_bytes_out;
}
mp_audio_realloc_min(af->data,
((int)(data->samples / s->frames_stride_scaled) + 1) * s->frames_stride);
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;
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);
// 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;
// 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;
// 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;
data->planes[0] = af->data->planes[0];
data->samples = (pout - (int8_t *)af->data->planes[0]) / af->data->sstride;
return 0;
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;
}
// 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 use_int = 0;
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;
MP_VERBOSE(af, "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
s->speed, s->scale_nominal, s->scale);
mp_msg(MSGT_AFILTER, MSGL_V,
"[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
s->speed, s->scale_nominal, s->scale);
mp_audio_force_interleaved_format(data);
mp_audio_copy_config(af->data, 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) {
use_int = 1;
} else {
mp_audio_set_format(af->data, AF_FORMAT_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);
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);
}
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;
}
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
}
return AF_OK;
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;
}
}
s->output_overlap = output_overlap_float;
}
}
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->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;
}
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_UNKNOWN;
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;
}
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;
}
// Deallocate memory
static void uninit(struct af_instance *af)
static void uninit(struct af_instance* af)
{
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);
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);
}
#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->filter = filter;
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;
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;
return AF_OK;
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;
}
#define OPT_BASE_STRUCT af_scaletempo_t
struct af_info af_info_scaletempo = {
.info = "Scale audio tempo while maintaining pitch",
.name = "scaletempo",
.open = af_open,
"Scale audio tempo while maintaining pitch",
"scaletempo",
"Robert Juliano",
"",
AF_FLAGS_REENTRANT,
af_open,
.priv_size = sizeof(af_scaletempo_t),
.priv_defaults = &(const af_scaletempo_t) {
.ms_stride = 60,
+45 -23
View File
@@ -41,12 +41,14 @@ typedef struct af_sinesuppress_s
double pos;
}af_sinesuppress_t;
static int play_s16(struct af_instance* af, struct mp_audio* data, int f);
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);
// 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
@@ -55,31 +57,45 @@ 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)
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
{
af->data->format = AF_FORMAT_FLOAT;
af->data->format = AF_FORMAT_FLOAT_NE;
af->data->bps = 4;
af->play = play_float;
}// else
#endif
{
mp_audio_set_format(af->data, AF_FORMAT_S16);
af->filter = play_s16;
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = 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;
}
// Filter data through filter
static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
// Deallocate memory
static void uninit(struct af_instance* af)
{
af_sinesuppress_t *s = af->priv;
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
{
af_sinesuppress_t *s = af->setup;
register int i = 0;
int16_t *a = (int16_t*)data->planes[0]; // Audio data
int len = data->samples*data->nch; // Number of samples
int16_t *a = (int16_t*)data->audio; // Audio data
int len = data->len/2; // Number of samples
for (i = 0; i < len; i++)
{
@@ -99,9 +115,9 @@ static int play_s16(struct af_instance* af, struct mp_audio* data, int f)
s->pos += 2 * M_PI * s->freq / data->rate;
}
MP_VERBOSE(af, "[sinesuppress] f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref);
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);
return 0;
return data;
}
#if 0
@@ -131,19 +147,25 @@ 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->filter=play_s16;
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;
return AF_OK;
}
#define OPT_BASE_STRUCT af_sinesuppress_t
// Description of this filter
struct af_info af_info_sinesuppress = {
.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}
},
"Sine Suppress",
"sinesuppress",
"Michael Niedermayer",
"",
0,
af_open
};
+66 -21
View File
@@ -32,7 +32,6 @@
#include <stdlib.h>
#include <string.h>
#include "common/common.h"
#include "af.h"
#include "dsp.h"
@@ -63,7 +62,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->priv;
af_sub_t* s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
@@ -71,8 +70,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, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
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);
// Design low-pass filter
s->k = 1.0;
@@ -83,10 +82,50 @@ 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]; \
@@ -99,12 +138,12 @@ static int control(struct af_instance* af, int cmd, void* arg)
#endif
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags)
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* c = data; // Current working data
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
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
int nch = c->nch; // Number of channels
int ch = s->ch; // Channel in which to insert the sub audio
register int i;
@@ -117,26 +156,32 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags)
IIR(x , s->w[1], s->q[1], a[i+ch]);
}
return 0;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af_sub_t* s;
af->control=control;
af->filter=filter;
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
return AF_OK;
}
#define OPT_BASE_STRUCT af_sub_t
// Description of this filter
struct af_info af_info_sub = {
.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}
},
"Audio filter for adding a sub-base channel",
"sub",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+59 -31
View File
@@ -88,24 +88,22 @@ 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->priv;
af_surround_t *s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT:{
struct mp_audio *in = arg;
float fc;
if (!mp_chmap_is_stereo(&in->channels)) {
MP_ERR(af, "[surround] Only stereo input is supported.\n");
return AF_DETACH;
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;
}
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_ERR(af, "[surround] Unable to design low-pass filter.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Unable to design low-pass filter.\n");
return AF_ERROR;
}
@@ -116,7 +114,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_FATAL(af, "[delay] Out of memory\n");
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[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))
@@ -124,12 +122,37 @@ 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
@@ -142,19 +165,20 @@ static float steering_matrix[][12] = {
//static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0;
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int flags){
af_surround_t* s = (af_surround_t*)af->priv;
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
af_surround_t* s = (af_surround_t*)af->setup;
float* m = steering_matrix[0];
float* in = data->planes[0]; // Input audio data
float* in = data->audio; // Input audio data
float* out = NULL; // Output audio data
float* end = in + data->samples * data->nch;
float* end = in + data->len / sizeof(float); // Loop end
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
mp_audio_realloc_min(af->data, data->samples);
if (AF_OK != RESIZE_LOCAL_BUFFER(af, data))
return NULL;
out = af->data->planes[0];
out = af->data->audio;
while(in < end){
/* Dominance:
@@ -216,28 +240,32 @@ static int filter(struct af_instance* af, struct mp_audio* data, int flags){
s->i = i; s->ri = ri; s->wi = wi;
// Set output data
data->planes[0] = af->data->planes[0];
data->audio = af->data->audio;
data->len *= 2;
mp_audio_set_channels_old(data, af->data->nch);
return 0;
return data;
}
static int af_open(struct af_instance* af){
af->control=control;
af->filter=filter;
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;
return AF_OK;
}
#define OPT_BASE_STRUCT af_surround_t
struct af_info af_info_surround =
{
.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}
},
"Surround decoder filter",
"surround",
"Steve Davies <[email protected]>",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+32 -17
View File
@@ -36,26 +36,40 @@ 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);
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
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;
}
// Filter data through filter
static int filter(struct af_instance* af, struct mp_audio* data, int f)
// Deallocate memory
static void uninit(struct af_instance* af)
{
af_sweept *s = af->priv;
free(af->data);
free(af->setup);
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
af_sweept *s = af->setup;
int i, j;
int16_t *in = (int16_t*)data->planes[0];
int16_t *in = (int16_t*)data->audio;
int chans = data->nch;
int in_len = data->samples;
int in_len = data->len/(2*chans);
for(i=0; i<in_len; i++){
for(j=0; j<chans; j++)
@@ -64,23 +78,24 @@ static int filter(struct af_instance* af, struct mp_audio* data, int f)
if(2*s->x*s->delta >= 3.141592) s->x=0;
}
return 0;
return data;
}
static int af_open(struct af_instance* af){
af->control=control;
af->filter=filter;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
af->setup=calloc(1,sizeof(af_sweept));
return AF_OK;
}
#define OPT_BASE_STRUCT af_sweept
struct af_info af_info_sweep = {
.info = "sine sweep",
.name = "sweep",
.open = af_open,
.priv_size = sizeof(af_sweept),
.options = (const struct m_option[]) {
OPT_DOUBLE("delta", delta, 0),
{0}
},
"sine sweep",
"sweep",
"Michael Niedermayer",
"",
AF_FLAGS_REENTRANT,
af_open
};
@@ -18,12 +18,10 @@
#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. input <= -200dB will become 0 gain. */
fail */
int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
{
int i = 0;
@@ -35,13 +33,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,MPCLAMP(in[i],mi,ma)/k);
out[i]=pow(10.0,clamp(in[i],mi,ma)/k);
}
return AF_OK;
}
/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
* fail. gain=0 will become -200 dB. k is just a multiplier. */
fail */
int af_to_dB(int n, float* in, float* out, float k)
{
int i = 0;
@@ -67,7 +65,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 * MPCLAMP(in[i],mi,ma)/1000.0);
out[i]=(int)((float)rate * clamp(in[i],mi,ma)/1000.0);
return AF_OK;
}
@@ -86,18 +84,7 @@ int af_to_ms(int n, int* in, float* out, int rate)
return AF_OK;
}
/*
* 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.
*/
/* Helper function for testing the output format */
int af_test_output(struct af_instance* af, struct mp_audio* out)
{
if((af->data->format != out->format) ||
+164 -82
View File
@@ -18,6 +18,22 @@
* 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>
@@ -26,101 +42,167 @@
#include <math.h>
#include <limits.h>
#include "common/common.h"
#include "af.h"
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)
// Data for specific instances of this filter
typedef struct af_volume_s
{
struct priv *s = af->priv;
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;
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
af_volume_t* s = (af_volume_t*)af->setup;
mp_audio_copy_config(af->data, in);
mp_audio_force_interleaved_format(af->data);
switch(cmd){
case AF_CONTROL_REINIT:
// Sanity check
if(!arg) return AF_ERROR;
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);
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
}
case AF_CONTROL_SET_VOLUME:
s->level = *(float *)arg;
return AF_OK;
case AF_CONTROL_GET_VOLUME:
*(float *)arg = s->level;
return AF_OK;
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_UNKNOWN;
}
static void filter_plane(struct af_instance *af, void *ptr, int num_samples)
{
struct priv *s = af->priv;
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);
}
}
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);
}
}
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);
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;
}
#define OPT_BASE_STRUCT struct priv
// 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;
}
// Description of this filter
struct af_info af_info_volume = {
.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}
},
"Volume control audio filter",
"volume",
"Anders",
"",
AF_FLAGS_NOT_REENTRANT,
af_open
};
+130
View File
@@ -0,0 +1,130 @@
/*
* 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 */
+23 -17
View File
@@ -16,6 +16,7 @@
* 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"
@@ -26,34 +27,39 @@ static const struct {
int fmt;
} audio_conversion_map[] = {
{AV_SAMPLE_FMT_U8, AF_FORMAT_U8},
{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_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_NONE, 0},
};
enum AVSampleFormat af_to_avformat(int fmt)
{
for (int i = 0; audio_conversion_map[i].fmt; i++) {
int i;
enum AVSampleFormat sample_fmt;
for (i = 0; audio_conversion_map[i].fmt; i++)
if (audio_conversion_map[i].fmt == fmt)
return audio_conversion_map[i].sample_fmt;
}
return AV_SAMPLE_FMT_NONE;
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;
}
int af_from_avformat(enum AVSampleFormat sample_fmt)
{
for (int i = 0; audio_conversion_map[i].fmt; i++) {
int i;
for (i = 0; audio_conversion_map[i].fmt; i++)
if (audio_conversion_map[i].sample_fmt == sample_fmt)
return audio_conversion_map[i].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 0;
return fmt;
}
+53 -163
View File
@@ -23,9 +23,7 @@
#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)
@@ -33,132 +31,78 @@ 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;
}
static int bits_to_mask(int bits)
/* 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)
{
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 char *name = af_fmt2str_short(format);
snprintf(str, size, "%s", name);
return str;
}
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;
}
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)
{ "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 },
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)
{ "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 },
{0}
};
bool af_fmt_is_valid(int format)
const char *af_fmt2str_short(int format)
{
for (int i = 0; af_fmtstr_table[i].name; i++) {
if (af_fmtstr_table[i].format == format)
return true;
}
return false;
}
int i;
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;
}
for (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;
@@ -169,63 +113,9 @@ 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;
}
+75 -104
View File
@@ -23,123 +23,104 @@
#ifndef MPLAYER_AF_FORMAT_H
#define MPLAYER_AF_FORMAT_H
#include <stdbool.h>
#include <sys/types.h>
#include "bstr/bstr.h"
#if BYTE_ORDER == BIG_ENDIAN
#define AF_SELECT_LE_BE(LE, BE) BE
#else
#define AF_SELECT_LE_BE(LE, BE) LE
#endif
#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)
#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)
#if BYTE_ORDER == BIG_ENDIAN
#define AF_FORMAT_NE AF_FORMAT_BE
#else
#define AF_FORMAT_NE AF_FORMAT_LE
#endif
// 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
// 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)
#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_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)
#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)
// 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)
// 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)
#define AF_FORMAT_MASK ((1<<11)-1)
#define AF_FORMAT_MASK ((1<<12)-1)
// PREDEFINED formats
#define AF_INTP (AF_FORMAT_I|AF_FORMAT_PLANAR)
#define AF_FLTP (AF_FORMAT_F|AF_FORMAT_PLANAR)
#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)
// actual sample formats
enum af_format {
AF_FORMAT_UNKNOWN = 0,
#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)
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_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_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_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_DOUBLE_LE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE),
AF_FORMAT_DOUBLE_BE = (AF_FORMAT_F|AF_FORMAT_64BIT|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_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),
#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_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_UNKNOWN 0
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_US|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)
#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)
struct af_fmt_entry {
const char *name;
@@ -149,22 +130,12 @@ 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);
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);
char* af_fmt2str(int format, char* str, int size);
const char* af_fmt2str_short(int format);
#endif /* MPLAYER_AF_FORMAT_H */
+43 -25
View File
@@ -24,13 +24,11 @@
#include "config.h"
#include "audio/out/ao.h"
#include "audio/filter/af.h"
#include "common/global.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "talloc.h"
#include "mixer.h"
struct mixer {
struct mp_log *log;
struct MPOpts *opts;
struct ao *ao;
struct af_stream *af;
@@ -49,12 +47,11 @@ struct mixer {
float balance;
};
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global)
struct mixer *mixer_init(void *talloc_ctx, struct MPOpts *opts)
{
struct mixer *mixer = talloc_ptrtype(talloc_ctx, mixer);
*mixer = (struct mixer) {
.log = mp_log_new(mixer, global->log, "mixer"),
.opts = global->opts,
.opts = opts,
.vol_l = 100,
.vol_r = 100,
.driver = "",
@@ -74,11 +71,12 @@ static void checkvolume(struct mixer *mixer)
ao_control_vol_t vol = {mixer->vol_l, mixer->vol_r};
if (mixer->softvol) {
float gain;
if (!af_control_any_rev(mixer->af, AF_CONTROL_GET_VOLUME, &gain))
gain = 1.0;
vol.left = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
vol.right = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
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;
} else {
// Rely on the values not changing if the query is not supported
ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol);
@@ -116,15 +114,26 @@ 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_ERR(mixer, "Failed to change audio output volume.\n");
mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] Failed to change audio output volume.\n");
return;
}
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");
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");
if (!(af_add(mixer->af, "volume", NULL)
&& af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mixer, "No volume control available.\n");
&& 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");
}
}
@@ -192,7 +201,9 @@ 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_GET_PAN_BALANCE, &mixer->balance);
af_control_any_rev(mixer->af,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET,
&mixer->balance);
*val = mixer->balance;
}
@@ -202,7 +213,9 @@ 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.
* 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.
*/
void mixer_setbalance(struct mixer *mixer, float val)
@@ -217,14 +230,16 @@ void mixer_setbalance(struct mixer *mixer, float val)
if (!mixer->af)
return;
if (af_control_any_rev(mixer->af, AF_CONTROL_SET_PAN_BALANCE, &val))
if (af_control_any_rev(mixer->af,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val))
return;
if (val == 0 || mixer->ao->channels.num < 2)
return;
if (!(af_pan_balance = af_add(mixer->af, "pan", NULL))) {
MP_ERR(mixer, "No balance control available.\n");
mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] No balance control available.\n");
return;
}
@@ -233,12 +248,14 @@ 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_SET_PAN_LEVEL,
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET,
&arg_ext);
level[i] = 0.f;
}
af_pan_balance->control(af_pan_balance, AF_CONTROL_SET_PAN_BALANCE, &val);
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val);
}
char *mixer_get_volume_restore_data(struct mixer *mixer)
@@ -264,7 +281,8 @@ 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_WARN(mixer, "Hardware volume control unavailable.\n");
mp_tmsg(MSGT_GLOBAL, MSGL_WARN,
"[mixer] Hardware volume control unavailable.\n");
}
}
@@ -285,7 +303,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->name;
mixer->driver = mixer->softvol ? "softvol" : ao->driver->info->short_name;
bool restore = mixer->softvol || ao->no_persistent_volume;
+2 -2
View File
@@ -28,12 +28,12 @@ enum {
SOFTVOL_AUTO = 2,
};
struct mpv_global;
struct MPOpts;
struct ao;
struct af_stream;
struct mixer;
struct mixer *mixer_init(void *talloc_ctx, struct mpv_global *global);
struct mixer *mixer_init(void *talloc_ctx, struct MPOpts *opts);
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);
+43 -64
View File
@@ -27,10 +27,10 @@
#include "ao.h"
#include "audio/format.h"
#include "options/options.h"
#include "options/m_config.h"
#include "common/msg.h"
#include "common/global.h"
#include "mpvcore/options.h"
#include "mpvcore/m_config.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mpv_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:
#if HAVE_COREAUDIO
#ifdef CONFIG_COREAUDIO
&audio_out_coreaudio,
#endif
#if HAVE_PULSE
#ifdef CONFIG_PULSE
&audio_out_pulse,
#endif
#if HAVE_SNDIO
#ifdef CONFIG_SNDIO
&audio_out_sndio,
#endif
#if HAVE_ALSA
#ifdef CONFIG_ALSA
&audio_out_alsa,
#endif
#if HAVE_WASAPI
#ifdef CONFIG_WASAPI
&audio_out_wasapi,
#endif
#if HAVE_OSS_AUDIO
#ifdef CONFIG_OSS_AUDIO
&audio_out_oss,
#endif
#if HAVE_DSOUND
#ifdef CONFIG_DSOUND
&audio_out_dsound,
#endif
#if HAVE_PORTAUDIO
#ifdef CONFIG_PORTAUDIO
&audio_out_portaudio,
#endif
// wrappers:
#if HAVE_JACK
#ifdef CONFIG_JACK
&audio_out_jack,
#endif
#if HAVE_OPENAL
#ifdef CONFIG_OPENAL
&audio_out_openal,
#endif
#if HAVE_SDL || HAVE_SDL2
#ifdef CONFIG_SDL
&audio_out_sdl,
#endif
&audio_out_null,
// should not be auto-selected:
&audio_out_pcm,
#if HAVE_ENCODING
#ifdef CONFIG_ENCODING
&audio_out_lavc,
#endif
#if HAVE_RSOUND
#ifdef CONFIG_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->name,
.description = ao->description,
.name = ao->info->short_name,
.description = ao->info->name,
.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_msg(log, MSGL_ERR, "Audio output %s not found!\n", name);
mp_tmsg_log(log, MSGL_ERR, "Audio output %s not found!\n", name);
talloc_free(log);
return NULL;
};
@@ -149,22 +149,13 @@ 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, ao->log, &desc);
if (m_config_apply_defaults(config, name, global->opts->ao_defs) < 0)
goto error;
struct m_config *config = m_config_from_obj_desc(ao, &desc);
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->sstride = af_fmt2bits(ao->format) / 8;
if (!af_fmt_is_planar(ao->format))
ao->sstride *= ao->channels.num;
ao->bps = ao->samplerate * ao->sstride;
ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8;
return ao;
error:
talloc_free(ao);
@@ -176,48 +167,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_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);
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);
if (ao)
goto done;
mp_warn(log, "Failed to initialize audio driver '%s'\n",
return ao;
mp_tmsg(MSGT_AO, MSGL_WARN, "Failed to initialize audio driver '%s'\n",
ao_list[n].name);
}
goto done;
return NULL;
}
autoprobe:
// now try the rest...
for (int i = 0; audio_out_drivers[i]; i++) {
ao = ao_create(true, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
(char *)audio_out_drivers[i]->name, NULL);
struct ao *ao = ao_create(true, global, input_ctx, encode_lavc_ctx,
samplerate, format, channels,
(char *)audio_out_drivers[i]->info->short_name, NULL);
if (ao)
goto done;
return ao;
}
done:
talloc_free(log);
return ao;
return NULL;
}
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 samples, int flags)
int ao_play(struct ao *ao, void *data, int len, int flags)
{
return ao->driver->play(ao, data, samples, flags);
return ao->driver->play(ao, data, len, flags);
}
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
@@ -243,6 +234,8 @@ 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);
}
@@ -259,20 +252,6 @@ 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)
{
+18 -15
View File
@@ -21,8 +21,8 @@
#include <stdbool.h>
#include "bstr/bstr.h"
#include "common/common.h"
#include "mpvcore/bstr.h"
#include "mpvcore/mp_common.h"
#include "audio/chmap.h"
#include "audio/chmap_sel.h"
@@ -34,8 +34,6 @@ 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
@@ -45,18 +43,28 @@ 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 <[email protected]>") */
const char *author;
/* any additional comments */
const char *comment;
} ao_info_t;
struct ao;
struct ao_driver {
bool encode;
const char *name;
const char *description;
const struct ao_info *info;
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 samples, int flags);
int (*play)(struct ao *ao, void *data, int len, int flags);
float (*get_delay)(struct ao *ao);
void (*pause)(struct ao *ao);
void (*resume)(struct ao *ao);
@@ -73,12 +81,9 @@ 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 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()
struct bstr buffer;
int buffer_playable_size;
bool probing; // if true, don't fail loudly on init
bool untimed;
bool no_persistent_volume; // the AO does the equivalent of af_volume
@@ -97,7 +102,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 samples, int flags);
int ao_play(struct ao *ao, void *data, int len, 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);
@@ -105,8 +110,6 @@ 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,
+136 -105
View File
@@ -2,7 +2,6 @@
* 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>
@@ -36,9 +35,9 @@
#include <string.h>
#include "config.h"
#include "options/options.h"
#include "options/m_option.h"
#include "common/msg.h"
#include "mpvcore/options.h"
#include "mpvcore/m_option.h"
#include "mpvcore/mp_msg.h"
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
@@ -52,18 +51,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; // in frames
int outburst; // in frames
int buffersize;
int outburst;
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,8 +77,28 @@ 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)
{
@@ -232,7 +251,6 @@ 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];
@@ -285,59 +303,51 @@ static const char *select_chmap(struct ao *ao)
return "default";
}
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)
static int try_open_device(struct ao *ao, const char *device, int open_mode,
int try_ac3)
{
struct priv *p = ao->priv;
int err, len;
char *ac3_device, *args;
if (AF_FORMAT_IS_IEC61937(ao->format)) {
void *tmp = talloc_new(NULL);
if (try_ac3) {
/* to set the non-audio bit, use AES0=6 */
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) {
len = strlen(device);
ac3_device = malloc(len + 7 + 1);
if (!ac3_device)
return -ENOMEM;
strcpy(ac3_device, device);
args = strchr(ac3_device, ':');
if (!args) {
/* no existing parameters: add it behind device name */
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);
strcat(ac3_device, ":AES0=6");
} else {
/* a simple list of parameters: add it at the end of the list */
ac3_device = talloc_asprintf(tmp, "%s,%s", device, params);
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}");
}
}
int err = snd_pcm_open
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode);
talloc_free(tmp);
err = snd_pcm_open
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode);
free(ac3_device);
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);
}
/*
@@ -353,6 +363,9 @@ 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;
@@ -369,7 +382,8 @@ static int init(struct ao *ao)
ao->channels.num);
} else {
device = select_chmap(ao);
if (strcmp(device, "default") != 0 && (ao->format & AF_FORMAT_F)) {
if (strcmp(device, "default") != 0 && ao->format == AF_FORMAT_FLOAT_NE)
{
// hack - use the converter plugin (why the heck?)
device = talloc_asprintf(ao, "plug:%s", device);
}
@@ -382,15 +396,17 @@ 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);
err = try_open_device(ao, device, open_mode, isac3);
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);
err = try_open_device(ao, device, 0, isac3);
}
CHECK_ALSA_ERROR("Playback open error");
}
@@ -412,16 +428,20 @@ 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;
ao->format = AF_FORMAT_S16_NE;
}
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_fmt_to_str(ao->format));
af_fmt2str_short(ao->format));
p->alsa_fmt = SND_PCM_FORMAT_S16_LE;
if (AF_FORMAT_IS_AC3(ao->format))
ao->format = AF_FORMAT_AC3_LE;
@@ -434,17 +454,6 @@ 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);
@@ -455,17 +464,19 @@ static int init(struct ao *ao)
mp_chmap_from_channels_alsa(&ao->channels, num_channels);
}
// 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");
}
/* 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");
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");
@@ -484,14 +495,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;
MP_VERBOSE(ao, "got buffersize=%i samples\n", p->buffersize);
p->buffersize = bufsize * p->bytes_per_sample;
MP_VERBOSE(ao, "got buffersize=%i\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->outburst = chunk_size * p->bytes_per_sample;
/* setting software parameters */
err = snd_pcm_sw_params_current(p->alsa, alsa_swparams);
@@ -522,8 +533,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 bps/%d samples buffer/%s\n",
ao->samplerate, ao->channels.num, af_fmt2bits(ao->format),
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,
p->buffersize, snd_pcm_format_description(p->alsa_fmt));
return 0;
@@ -553,6 +564,7 @@ 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)
@@ -595,8 +607,11 @@ static void audio_resume(struct ao *ao)
MP_VERBOSE(ao, "resume not supported by hardware\n");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
if (p->prepause_frames)
ao_play_silence(ao, p->prepause_frames);
if (p->prepause_frames) {
void *silence = calloc(p->prepause_frames, p->bytes_per_sample);
play(ao, silence, p->prepause_frames * p->bytes_per_sample, 0);
free(silence);
}
}
alsa_error: ;
@@ -618,22 +633,32 @@ static void reset(struct ao *ao)
alsa_error: ;
}
static int play(struct ao *ao, void **data, int samples, int flags)
/*
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)
{
struct priv *p = ao->priv;
int num_frames;
snd_pcm_sframes_t res = 0;
if (!(flags & AOPLAY_FINAL_CHUNK))
samples = samples / p->outburst * p->outburst;
len = len / p->outburst * p->outburst;
num_frames = len / p->bytes_per_sample;
if (samples == 0)
if (!p->alsa) {
MP_ERR(ao, "Device configuration error.");
return 0;
}
if (num_frames == 0)
return 0;
do {
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);
}
res = snd_pcm_writei(p->alsa, data, num_frames);
if (res == -EINTR) {
/* nothing to do */
@@ -652,10 +677,10 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
} while (res == 0);
return res < 0 ? -1 : res;
return res < 0 ? 0 : res * p->bytes_per_sample;
alsa_error:
return -1;
return 0;
}
/* how many byes are free in the buffer */
@@ -670,7 +695,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);
unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample;
if (space > p->buffersize) // Buffer underrun?
space = p->buffersize;
return space;
@@ -683,27 +708,34 @@ alsa_error:
static float get_delay(struct ao *ao)
{
struct priv *p = ao->priv;
snd_pcm_sframes_t delay;
if (p->alsa) {
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)
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
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 = {
.description = "ALSA-0.9.x-1.x audio output",
.name = "alsa",
.info = &(const struct ao_info) {
"ALSA-0.9.x-1.x audio output",
"alsa",
"Alex Beregszaszi, Zsolt Barat <[email protected]>",
"under development"
},
.init = init,
.uninit = uninit,
.control = control,
@@ -721,7 +753,6 @@ 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),
+16 -19
View File
@@ -4,9 +4,6 @@
* 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>
@@ -39,9 +36,9 @@
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "misc/ring.h"
#include "common/msg.h"
#include "mpvcore/m_option.h"
#include "mpvcore/mp_ring.h"
#include "mpvcore/mp_msg.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
@@ -154,9 +151,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 digitalvol = d->muted ? 0 : 100;
int vol = d->muted ? 0 : 100;
*control_vol = (ao_control_vol_t) {
.left = digitalvol, .right = digitalvol,
.left = vol, .right = vol,
};
return CONTROL_TRUE;
}
@@ -216,7 +213,7 @@ static void print_list(struct ao *ao)
for (int i = 0; i < n_devs; i++) {
char *name;
err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
OSStatus err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &name);
if (err == noErr)
talloc_steal(devs, name);
@@ -271,7 +268,7 @@ static int init(struct ao *ao)
selected_device = p->opt_device_id;
}
if (mp_msg_test(ao->log, MSGL_V)) {
if (mp_msg_test_log(ao->log, MSGL_V)) {
char *name;
err = CA_GET_STR(selected_device, kAudioObjectPropertyName, &name);
CHECK_CA_ERROR("could not get selected audio device name");
@@ -285,8 +282,6 @@ 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)) {
@@ -578,12 +573,10 @@ coreaudio_error:
return CONTROL_ERROR;
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *output_samples, int num_bytes, 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) {
@@ -601,7 +594,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
audio_resume(ao);
return wrote / ao->sstride;
return wrote;
}
static void reset(struct ao *ao)
@@ -614,7 +607,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) / ao->sstride;
return mp_ring_available(p->buffer);
}
static float get_delay(struct ao *ao)
@@ -704,8 +697,12 @@ static void audio_resume(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio = {
.description = "CoreAudio (OS X Audio Output)",
.name = "coreaudio",
.info = &(const struct ao_info) {
"CoreAudio (OS X Audio Output)",
"coreaudio",
"Timothy J. Wood, Dan Christiansen, Chris Roccati & Stefano Pigozzi",
"",
},
.uninit = uninit,
.init = init,
.play = play,
+5 -45
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(ao->log, level, "%s (%s)\n", message, error_string);
mp_msg_log(ao->log, level, "%s (%s)\n", message, error_string);
talloc_free(error_string);
return false;
@@ -336,47 +336,6 @@ 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)
{
@@ -396,13 +355,14 @@ 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;
const int mp_speaker_id = ca_label_to_mp_speaker_id(label);
if (mp_speaker_id < 0) {
if (label == kAudioChannelLabel_UseCoordinates ||
label == kAudioChannelLabel_Unknown ||
label > kAudioChannelLabel_TopBackRight) {
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
"bitmap, skipping layout\n", label);
all_channels_valid = false;
} else {
*bitmap |= 1ULL << mp_speaker_id;
*bitmap |= 1ULL << (label - 1);
}
}
+1 -1
View File
@@ -22,7 +22,7 @@
#include <AudioToolbox/AudioToolbox.h>
#include <inttypes.h>
#include <stdbool.h>
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "audio/out/ao.h"
#define CA_CFSTR_ENCODING kCFStringEncodingASCII
+16 -13
View File
@@ -39,9 +39,9 @@
#include "audio/format.h"
#include "ao.h"
#include "audio/reorder_ch.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "mpvcore/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 = af_fmt_from_planar(ao->format);
int format = ao->format;
int rate = ao->samplerate;
if (AF_FORMAT_IS_AC3(format))
format = AF_FORMAT_AC3;
format = AF_FORMAT_AC3_NE;
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:
case AF_FORMAT_AC3_NE:
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_fmt_to_str(format));
af_fmt2str_short(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_fmt_to_str(format));
ao->channels.num, af_fmt2str_short(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) / ao->sstride;
return space - p->min_free_space;
}
/**
@@ -606,10 +606,9 @@ 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 samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *p = ao->priv;
int len = samples * ao->sstride;
int space = check_free_buffer_size(ao);
if (space < len)
@@ -617,7 +616,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
if (!(flags & AOPLAY_FINAL_CHUNK))
len = (len / p->outburst) * p->outburst;
return write_buffer(ao, data[0], len) / ao->sstride;
return write_buffer(ao, data, len);
}
/**
@@ -635,8 +634,12 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_dsound = {
.description = "Windows DirectSound audio output",
.name = "dsound",
.info = &(const struct ao_info) {
"Windows DirectSound audio output",
"dsound",
"Gabor Szecsi <[email protected]>",
""
},
.init = init,
.uninit = uninit,
.control = control,
+154 -188
View File
@@ -4,8 +4,6 @@
* 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
@@ -29,30 +27,31 @@
#include <unistd.h>
#include "config.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "mpvcore/m_option.h"
#include "misc/ring.h"
#include "mpvcore/mp_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 (8 * 1024)
#define CHUNK_SIZE (24 * 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;
@@ -61,49 +60,76 @@ struct priv {
int autostart;
int stdlayout;
volatile int paused;
volatile int underrun;
volatile int underrun; // signals if an underrun occured
volatile float callback_interval;
volatile float callback_time;
int num_ports;
struct port_ring ports[MP_NUM_CHANNELS];
struct mp_ring *ring; // buffer for audio data
};
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 *buf, jack_nframes_t nframes)
static void silence(float **bufs, int cnt, int num_bufs)
{
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;
int i;
for (i = 0; i < num_bufs; i++)
memset(bufs[i], 0, cnt * sizeof(float));
}
/**
@@ -114,23 +140,18 @@ process_port(struct ao *ao, struct port_ring *pr, jack_nframes_t nframes)
*
* Write silence into buffers if paused or an underrun occured
*/
static int
process(jack_nframes_t nframes, void *arg)
static int outputaudio(jack_nframes_t nframes, void *arg)
{
struct ao *ao = arg;
struct priv *p = ao->priv;
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)
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)
p->underrun = 1;
if (p->estimate) {
float now = mp_time_us() / 1000000.0;
float diff = p->callback_time + p->callback_interval - now;
@@ -140,163 +161,117 @@ process(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;
ao->format = AF_FORMAT_FLOATP;
switch (p->stdlayout) {
case 0:
if (p->stdlayout == 0) {
mp_chmap_sel_add_waveext(&sel);
break;
case 1:
} else if (p->stdlayout == 1) {
mp_chmap_sel_add_alsa_def(&sel);
break;
default:
} else {
mp_chmap_sel_add_any(&sel);
}
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
goto err_chmap;
goto err_out;
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_client_open;
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;
}
if (create_ports(ao, ao->channels.num))
goto err_create_ports;
jack_set_process_callback(p->client, process, ao);
// 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 (jack_activate(p->client)) {
MP_FATAL(ao, "activate failed\n");
goto err_activate;
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;
}
}
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].port, JackPlaybackLatency,
jack_port_get_latency_range(p->ports[0], 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_chmap_sel_get_def;
goto err_out;
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_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:
err_out:
free(matching_ports);
if (p->client)
jack_client_close(p->client);
return -1;
}
static float get_delay(struct ao *ao)
{
struct priv *p = ao->priv;
int buffered = mp_ring_buffered(p->ports[0].ring); // could be less
int buffered = mp_ring_buffered(p->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;
}
@@ -306,12 +281,8 @@ static float get_delay(struct ao *ao)
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
int i;
p->paused = 1;
for (i = 0; i < p->num_ports; i++)
mp_ring_reset(p->ports[i].ring);
mp_ring_reset(p->ring);
p->paused = 0;
}
@@ -319,10 +290,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);
}
@@ -348,35 +319,30 @@ static void audio_resume(struct ao *ao)
static int get_space(struct ao *ao)
{
struct priv *p = ao->priv;
return mp_ring_available(p->ports[0].ring) / ao->sstride;
return mp_ring_available(p->ring);
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *p = ao->priv;
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);
}
if (!(flags & AOPLAY_FINAL_CHUNK))
len -= len % p->outburst;
p->underrun = 0;
return ret / ao->sstride;
return mp_ring_write(p->ring, data, len);
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_jack = {
.description = "JACK audio output",
.name = "jack",
.info = &(const struct ao_info) {
"JACK audio output",
"jack",
"Reimar Döffinger <[email protected]>",
"based on ao_sdl.c"
},
.init = init,
.uninit = uninit,
.get_space = get_space,
@@ -398,7 +364,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}
},
};
+288 -96
View File
@@ -22,31 +22,36 @@
#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 "options/options.h"
#include "common/common.h"
#include "mpvcore/options.h"
#include "mpvcore/mp_common.h"
#include "audio/format.h"
#include "audio/fmt-conversion.h"
#include "audio/reorder_ch.h"
#include "talloc.h"
#include "ao.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "common/encode_lavc.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";
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;
@@ -58,44 +63,13 @@ 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;
@@ -132,10 +106,141 @@ static int init(struct ao *ao)
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
select_format(ao, codec);
{
// 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");
}
}
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)
@@ -165,14 +270,33 @@ static int init(struct ao *ao)
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 encode(struct ao *ao, double apts, void *data);
static int play(struct ao *ao, void *data, int len, int flags);
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *ac = ao->priv;
@@ -184,10 +308,29 @@ 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) ;
}
@@ -199,11 +342,11 @@ static int get_space(struct ao *ao)
{
struct priv *ac = ao->priv;
return ac->aframesize * ac->framecount;
return ac->aframesize * ac->sample_size * ao->channels.num * 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;
@@ -226,11 +369,22 @@ static int encode(struct ao *ao, double apts, void **data)
frame = avcodec_alloc_frame();
frame->nb_samples = ac->aframesize;
assert(ao->channels.num <= AV_NUM_DATA_POINTERS);
for (int n = 0; n < ao->channels.num; n++)
frame->extended_data[n] = data[n];
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;
}
frame->linesize[0] = frame->nb_samples * ao->sstride;
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;
}
if (ectx->options->rawts || ectx->options->copyts) {
// real audio pts
@@ -260,6 +414,11 @@ static int encode(struct ao *ao, double apts, void **data)
}
avcodec_free_frame(&frame);
if (ac->planarize) {
talloc_free(data);
data = NULL;
}
}
else
{
@@ -312,47 +471,26 @@ static int encode(struct ao *ao, double apts, void **data)
return packet.size;
}
// 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)
// 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)
{
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
@@ -399,10 +537,63 @@ static int play(struct ao *ao, void **data, int samples, int flags)
// Absurd, as this range MUST contain at least one multiple of B.
}
// Fix and apply the discontinuity pts offset.
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;
}
}
}
if (!ectx->options->rawts && ectx->options->copyts) {
// fix the discontinuity pts offset
nextpts = pts;
nextpts = pts + ptsoffset / (double) ao->samplerate;
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
@@ -415,38 +606,39 @@ static int play(struct ao *ao, void **data, int samples, int flags)
outpts = pts + ectx->discontinuity_pts_offset;
}
else {
else
outpts = pts;
}
// 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);
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);
bufpos += ac->aframesize;
}
// Calculate expected pts of next audio frame (input side).
ac->expected_next_pts = pts + bufpos / (double) ao->samplerate;
talloc_free(paddingbuf);
// calculate expected pts of next audio frame
ac->expected_next_pts = pts + (bufpos + ptsoffset) / (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;
return bufpos * ac->sample_size * ao->channels.num;
}
const struct ao_driver audio_out_lavc = {
.encode = true,
.description = "audio encoding using libavcodec",
.name = "lavc",
.info = &(const struct ao_info) {
"audio encoding using libavcodec",
"lavc",
"Rudolf Polzer <[email protected]>",
""
},
.init = init,
.uninit = uninit,
.get_space = get_space,
+31 -90
View File
@@ -18,11 +18,6 @@
* 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>
@@ -30,66 +25,41 @@
#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;
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
float buffered_bytes;
int buffersize;
int outburst;
};
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();
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->buffered_bytes -= (now - priv->last_time) * ao->bps;
if (priv->buffered_bytes < 0)
priv->buffered_bytes = 0;
priv->last_time = now;
}
static int init(struct ao *ao)
{
struct priv *priv = ao->priv;
ao->untimed = priv->untimed;
struct priv *priv = talloc_zero(ao, struct priv);
ao->priv = priv;
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
// A "buffer" for this many seconds of audio
int bursts = (int)(ao->samplerate * priv->bufferlen + 1) / priv->outburst;
priv->buffersize = priv->outburst * bursts;
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;
priv->last_time = mp_time_sec();
return 0;
@@ -98,36 +68,25 @@ 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 = 0;
priv->playing_final = false;
priv->buffered_bytes = 0;
}
// stop playing, keep buffers (for pause)
static void pause(struct ao *ao)
{
struct priv *priv = ao->priv;
drain(ao);
priv->paused = true;
// for now, just call reset();
reset(ao);
}
// resume playing, after pause()
// resume playing, after audio_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)
@@ -135,28 +94,18 @@ static int get_space(struct ao *ao)
struct priv *priv = ao->priv;
drain(ao);
return priv->buffersize - priv->buffered;
return priv->buffersize - priv->buffered_bytes;
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
int accepted;
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;
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;
}
static float get_delay(struct ao *ao)
@@ -164,14 +113,16 @@ static float get_delay(struct ao *ao)
struct priv *priv = ao->priv;
drain(ao);
return priv->buffered / (double)ao->samplerate;
return priv->buffered_bytes / ao->bps;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_null = {
.description = "Null audio output",
.name = "null",
.info = &(const struct ao_info) {
"Null audio output",
"null",
"Tobias Diedrich <[email protected]>",
"",
},
.init = init,
.uninit = uninit,
.reset = reset,
@@ -180,15 +131,5 @@ 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}
},
};
+31 -21
View File
@@ -35,22 +35,22 @@
#include <AL/alext.h>
#endif
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "options/m_option.h"
#include "mpvcore/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(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
static int validate_device_opt(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_fatal(log, "Device listing not supported.\n");
mp_msg(MSGT_AO, MSGL_FATAL, "Device listing not supported.\n");
return M_OPT_EXIT;
}
const char *list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
mp_info(log, "OpenAL devices:\n");
mp_msg(MSGT_AO, MSGL_INFO, "OpenAL devices:\n");
while (list && *list) {
mp_info(log, " '%s'\n", list);
mp_msg(MSGT_AO, MSGL_INFO, " '%s'\n", list);
list = list + strlen(list) + 1;
}
return M_OPT_EXIT - 1;
@@ -169,7 +169,8 @@ 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_S16P;
ao->format = AF_FORMAT_S16_NE;
tmpbuf = malloc(CHUNK_SIZE);
return 0;
err_out:
@@ -181,6 +182,7 @@ 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);
@@ -249,30 +251,34 @@ static int get_space(struct ao *ao)
queued = NUM_BUF - queued - 3;
if (queued < 0)
return 0;
return queued * CHUNK_SAMPLES;
return queued * CHUNK_SIZE * ao->channels.num;
}
/**
* \brief write data into buffer and reset underrun flag
*/
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
ALint state;
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,
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,
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 num * CHUNK_SAMPLES;
return len * ao->channels.num * CHUNK_SIZE;
}
static float get_delay(struct ao *ao)
@@ -280,14 +286,18 @@ static float get_delay(struct ao *ao)
ALint queued;
unqueue_buffers();
alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
return queued * CHUNK_SAMPLES / (float)ao->samplerate;
return queued * CHUNK_SIZE / 2 / (float)ao->samplerate;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_openal = {
.description = "OpenAL audio output",
.name = "openal",
.info = &(const struct ao_info) {
"OpenAL audio output",
"openal",
"Reimar Döffinger <[email protected]>",
""
},
.init = init,
.uninit = uninit,
.control = control,
+44 -87
View File
@@ -3,7 +3,6 @@
*
* This file is part of MPlayer.
*
* Original author: A'rpi
* Support for >2 output channels added 2001-11-25
* - Steve Davies <steve@daviesfam.org>
*
@@ -35,13 +34,13 @@
#include <string.h>
#include "config.h"
#include "options/options.h"
#include "common/msg.h"
#include "mpvcore/options.h"
#include "mpvcore/mp_msg.h"
#if HAVE_SYS_SOUNDCARD_H
#ifdef HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>
#else
#if HAVE_SOUNDCARD_H
#ifdef HAVE_SOUNDCARD_H
#include <soundcard.h>
#endif
#endif
@@ -66,19 +65,6 @@ 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},
@@ -89,18 +75,6 @@ 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
@@ -114,14 +88,14 @@ static int format_table[][2] = {
{AFMT_S32_BE, AF_FORMAT_S32_BE},
#endif
#ifdef AFMT_FLOAT
{AFMT_FLOAT, AF_FORMAT_FLOAT},
{AFMT_FLOAT, AF_FORMAT_FLOAT_NE},
#endif
// SPECIALS
#ifdef AFMT_MPEG
{AFMT_MPEG, AF_FORMAT_MPEG2},
#endif
#ifdef AFMT_AC3
{AFMT_AC3, AF_FORMAT_AC3},
{AFMT_AC3, AF_FORMAT_AC3_NE},
#endif
{-1, -1}
};
@@ -221,14 +195,13 @@ 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;
@@ -286,49 +259,31 @@ 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;
ao->format = AF_FORMAT_AC3_NE;
oss_format = format2oss(ao->format);
if (oss_format == -1) {
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
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
af_fmt2str_short(ao->format));
#if BYTE_ORDER == BIG_ENDIAN
oss_format = AFMT_S16_BE;
#else
oss_format = AFMT_S16_LE;
#endif
ao->format = AF_FORMAT_S16;
#endif
ao->format = AF_FORMAT_S16_NE;
}
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_fmt_to_str(ao->format),
af_fmt_to_str(AF_FORMAT_S16));
ao->format = AF_FORMAT_S16;
p->dsp, af_fmt2str_short(ao->format),
af_fmt2str_short(AF_FORMAT_S16_NE));
ao->format = AF_FORMAT_S16_NE;
goto ac3_retry;
}
@@ -338,12 +293,11 @@ ac3_retry:
return -1;
}
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(ao->format));
MP_VERBOSE(ao, "sample format: %s\n", af_fmt2str_short(ao->format));
if (!AF_FORMAT_IS_AC3(ao->format)) {
struct mp_chmap_sel sel = {0};
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
mp_chmap_sel_add_alsa_def(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
int reqchannels = ao->channels.num;
@@ -394,7 +348,7 @@ ac3_retry:
// Measuring buffer size:
void *data;
p->buffersize = 0;
#if HAVE_AUDIO_SELECT
#ifdef HAVE_AUDIO_SELECT
data = malloc(p->outburst);
memset(data, 0, p->outburst);
while (p->buffersize < 0x40000) {
@@ -412,7 +366,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 #define HAVE_AUDIO_SELECT 0 in config.h!\n");
MP_ERR(ao, "Recompile mpv with #undef HAVE_AUDIO_SELECT in config.h!\n");
return -1;
}
#endif
@@ -444,23 +398,21 @@ 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) {
@@ -471,7 +423,6 @@ 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);
@@ -479,11 +430,10 @@ 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, &c);
ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &ao->channels.num);
else {
c--;
int c = ao->channels.num - 1;
ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c);
}
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
@@ -500,12 +450,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 / ao->sstride;
return playsize;
}
#endif
// check buffer
#if HAVE_AUDIO_SELECT
#ifdef HAVE_AUDIO_SELECT
{
fd_set rfds;
struct timeval tv;
@@ -518,14 +468,14 @@ static int get_space(struct ao *ao)
}
#endif
return p->outburst / ao->sstride;
return p->outburst;
}
// stop playing, keep buffers (for pause)
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
p->prepause_space = get_space(ao) * ao->sstride;
p->prepause_space = get_space(ao);
#ifdef SNDCTL_DSP_RESET
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
#else
@@ -536,30 +486,33 @@ 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 samples, int flags)
static int play(struct ao *ao, void *data, int len, 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[0], len);
return len / ao->sstride;
len = write(p->audio_fd, data, len);
return len;
}
// 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
int fillframes = get_space(ao) - p->prepause_space / ao->sstride;
if (fillframes > 0)
ao_play_silence(ao, fillframes);
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);
}
}
// return: delay in seconds between first and last sample in buffer
@@ -589,8 +542,12 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_oss = {
.description = "OSS/ioctl audio output",
.name = "oss",
.info = &(const struct ao_info) {
"OSS/ioctl audio output",
"oss",
"A'rpi",
""
},
.init = init,
.uninit = uninit,
.control = control,
+12 -14
View File
@@ -3,8 +3,6 @@
*
* 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
@@ -30,11 +28,11 @@
#include "talloc.h"
#include "options/m_option.h"
#include "mpvcore/m_option.h"
#include "audio/format.h"
#include "audio/reorder_ch.h"
#include "ao.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#ifdef __MINGW32__
// for GetFileType to detect pipes
@@ -118,9 +116,6 @@ 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
@@ -148,7 +143,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_fmt_to_str(ao->format));
ao->channels.num, af_fmt2str_short(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");
@@ -196,21 +191,24 @@ static int get_space(struct ao *ao)
return 65536;
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
int len = samples * ao->sstride;
fwrite(data[0], len, 1, priv->fp);
fwrite(data, len, 1, priv->fp);
priv->data_length += len;
return len / ao->sstride;
return len;
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_pcm = {
.description = "RAW PCM/WAVE file writer audio output",
.name = "pcm",
.info = &(const struct ao_info) {
"RAW PCM/WAVE file writer audio output",
"pcm",
"Atmosfear",
"",
},
.init = init,
.uninit = uninit,
.get_space = get_space,
+44 -41
View File
@@ -26,10 +26,10 @@
#include <portaudio.h>
#include "config.h"
#include "options/m_option.h"
#include "mpvcore/m_option.h"
#include "audio/format.h"
#include "common/msg.h"
#include "misc/ring.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mp_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, paInt16},
{AF_FORMAT_S24, paInt24},
{AF_FORMAT_S32, paInt32},
{AF_FORMAT_S8, paInt8},
{AF_FORMAT_U8, paUInt8},
{AF_FORMAT_FLOAT, paFloat32},
{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_UNKNOWN, 0}
};
static bool check_pa_ret(struct mp_log *log, int ret)
static bool check_pa_ret(int ret)
{
if (ret < 0) {
mp_err(log, "%s\n", Pa_GetErrorText(ret));
mp_msg(MSGT_AO, MSGL_ERR, "[ao/portaudio] %s\n",
Pa_GetErrorText(ret));
if (ret == paUnanticipatedHostError) {
const PaHostErrorInfo* hosterr = Pa_GetLastHostErrorInfo();
mp_err(log, "Host error: %s\n", hosterr->errorText);
mp_msg(MSGT_AO, MSGL_ERR, "[ao/portaudio] 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(struct mp_log *log, const char *name)
static int find_device(const char *name)
{
int found = paNoDevice;
if (!name)
return found;
int help = strcmp(name, "help") == 0;
int count = Pa_GetDeviceCount();
check_pa_ret(log, count);
check_pa_ret(count);
int index = to_int(name, -1);
if (help)
mp_info(log, "PortAudio devices:\n");
mp_msg(MSGT_AO, MSGL_INFO, "PortAudio devices:\n");
for (int n = 0; n < count; n++) {
const PaDeviceInfo* info = Pa_GetDeviceInfo(n);
if (help) {
if (info->maxOutputChannels < 1)
continue;
mp_info(log, " %d '%s', %d channels, latency: %.2f "
mp_msg(MSGT_AO, MSGL_INFO, " %d '%s', %d channels, latency: %.2f "
"ms, sample rate: %.0f\n", n, info->name,
info->maxOutputChannels,
info->defaultHighOutputLatency * 1000,
@@ -121,20 +121,21 @@ static int find_device(struct mp_log *log, const char *name)
}
}
if (found == paNoDevice && !help)
mp_warn(log, "Device '%s' not found!\n", name);
mp_msg(MSGT_AO, MSGL_WARN, "[ao/portaudio] Device '%s' not found!\n",
name);
return found;
}
static int validate_device_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
static int validate_device_opt(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(log, Pa_Initialize()))
if (!check_pa_ret(Pa_Initialize()))
return M_OPT_EXIT;
find_device(log, "help");
find_device("help");
Pa_Terminate();
return M_OPT_EXIT - 1;
}
@@ -204,9 +205,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);
@@ -217,14 +218,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(ao->log, priv->cfg_device);
pa_device = find_device(priv->cfg_device);
if (pa_device == paNoDevice)
goto error_exit;
@@ -241,8 +242,6 @@ 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) {
@@ -263,9 +262,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;
@@ -279,22 +278,22 @@ error_exit:
return -1;
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
pthread_mutex_lock(&priv->ring_mutex);
int write_len = mp_ring_write(priv->ring, data[0], samples * ao->sstride);
int write_len = mp_ring_write(priv->ring, data, len);
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 / ao->sstride;
return write_len;
}
static int get_space(struct ao *ao)
@@ -307,7 +306,7 @@ static int get_space(struct ao *ao)
pthread_mutex_unlock(&priv->ring_mutex);
return free / ao->sstride;
return free;
}
static float get_delay(struct ao *ao)
@@ -332,7 +331,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);
@@ -348,7 +347,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);
@@ -367,14 +366,18 @@ 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 = {
.description = "PortAudio",
.name = "portaudio",
.info = &(const struct ao_info) {
"PortAudio",
"portaudio",
"wm4",
"",
},
.init = init,
.uninit = uninit,
.reset = reset,
+14 -25
View File
@@ -28,9 +28,9 @@
#include "config.h"
#include "audio/format.h"
#include "common/msg.h"
#include "mpvcore/mp_msg.h"
#include "ao.h"
#include "input/input.h"
#include "mpvcore/input/input.h"
#define PULSE_CLIENT_NAME "mpv"
@@ -286,8 +286,6 @@ 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) {
@@ -377,14 +375,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 samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
pa_threaded_mainloop_lock(priv->mainloop);
if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0,
if (pa_stream_write(priv->stream, data, len, NULL, 0,
PA_SEEK_RELATIVE) < 0) {
GENERIC_ERR_MSG("pa_stream_write() failed");
samples = -1;
len = -1;
}
if (flags & AOPLAY_FINAL_CHUNK) {
// Force start in case the stream was too short for prebuf
@@ -392,7 +390,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(priv->mainloop);
return samples;
return len;
}
// Reset the audio stream, i.e. flush the playback buffer on the server side
@@ -427,14 +425,14 @@ static void resume(struct ao *ao)
cork(ao, false);
}
// Return number of samples that may be written to the server without blocking
// Return number of bytes 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 / ao->sstride;
return space;
}
// Return the current latency in seconds
@@ -584,19 +582,6 @@ 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;
}
@@ -605,8 +590,12 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_pulse = {
.description = "PulseAudio audio output",
.name = "pulse",
.info = &(const struct ao_info) {
"PulseAudio audio output",
"pulse",
"Lennart Poettering",
"",
},
.control = control,
.init = init,
.uninit = uninit,
+12 -10
View File
@@ -29,15 +29,15 @@
#include "talloc.h"
#include "options/m_option.h"
#include "mpvcore/m_option.h"
#include "osdep/timer.h"
#include "audio/format.h"
#include "ao.h"
struct priv {
rsound_t *rd;
char *host;
char *port;
const char *host;
const char *port;
};
static int set_format(struct ao *ao)
@@ -114,8 +114,6 @@ 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);
@@ -163,13 +161,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) / ao->sstride;
return rsd_get_avail(priv->rd);
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
return rsd_write(priv->rd, data[0], samples * ao->sstride) / ao->sstride;
return rsd_write(priv->rd, data, len);
}
static float get_delay(struct ao *ao)
@@ -181,8 +179,12 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_rsound = {
.description = "RSound output driver",
.name = "rsound",
.info = &(const struct ao_info) {
.name = "RSound output driver",
.short_name = "rsound",
.author = "Hans-Kristian Arntzen",
.comment = "",
},
.init = init,
.uninit = uninit,
.reset = reset,
+12 -11
View File
@@ -23,8 +23,8 @@
#include "audio/format.h"
#include "talloc.h"
#include "ao.h"
#include "common/msg.h"
#include "options/m_option.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/m_option.h"
#include "osdep/timer.h"
#include <libavutil/fifo.h>
@@ -144,8 +144,6 @@ static int init(struct ao *ao)
return -1;
}
ao->format = af_fmt_from_planar(ao->format);
SDL_AudioSpec desired, obtained;
switch (ao->format) {
@@ -261,7 +259,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 / ao->sstride;
return space;
}
static void pause(struct ao *ao)
@@ -292,21 +290,20 @@ static void resume(struct ao *ao)
do_resume(ao);
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, 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[0], len, NULL);
av_fifo_generic_write(priv->buffer, data, len, NULL);
SDL_CondSignal(priv->underrun_cond);
SDL_UnlockMutex(priv->buffer_mutex);
if (priv->unpause) {
priv->unpause = 0;
do_resume(ao);
}
return len / ao->sstride;
return len;
}
static float get_delay(struct ao *ao)
@@ -350,8 +347,12 @@ static float get_delay(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_sdl = {
.description = "SDL Audio",
.name = "sdl",
.info = &(const struct ao_info) {
"SDL Audio",
"sdl",
"Rudolf Polzer <[email protected]>",
""
},
.init = init,
.uninit = uninit,
.get_space = get_space,
+12 -11
View File
@@ -22,8 +22,8 @@
#include <errno.h>
#include <sndio.h>
#include "options/m_option.h"
#include "common/msg.h"
#include "mpvcore/m_option.h"
#include "mpvcore/mp_msg.h"
#include "audio/format.h"
#include "ao.h"
@@ -130,9 +130,6 @@ 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)) {
@@ -242,16 +239,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 samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *p = ao->priv;
int n;
n = sio_write(p->hdl, data[0], samples * ao->sstride);
n = sio_write(p->hdl, data, len);
p->delay += n;
if (flags & AOPLAY_FINAL_CHUNK)
reset(ao);
return n / ao->sstride;
return n;
}
/*
@@ -271,7 +268,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) / ao->sstride;
return p->par.bufsz * p->par.pchan * p->par.bps - p->delay;
}
/*
@@ -320,8 +317,12 @@ static void audio_resume(struct ao *ao)
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_sndio = {
.description = "sndio audio output",
.name = "sndio",
.info = &(const struct ao_info) {
"sndio audio output",
"sndio",
"Alexandre Ratchov <[email protected]>, Christian Neukirchen <[email protected]>",
"under development"
},
.init = init,
.uninit = uninit,
.control = control,
+121 -92
View File
@@ -1,8 +1,6 @@
/*
* 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
@@ -29,11 +27,12 @@
#include <mmdeviceapi.h>
#include <avrt.h>
#include "options/m_option.h"
#include "options/m_config.h"
#include "config.h"
#include "mpvcore/m_option.h"
#include "mpvcore/m_config.h"
#include "audio/format.h"
#include "common/msg.h"
#include "misc/ring.h"
#include "mpvcore/mp_msg.h"
#include "mpvcore/mp_ring.h"
#include "ao.h"
#ifndef BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
@@ -49,7 +48,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);
#endif
#define RING_BUFFER_COUNT 8
#define RING_BUFFER_COUNT 64
/* 20 millisecond buffer? */
#define BUFFER_TIME 20000000.0
@@ -72,7 +71,6 @@ union WAVEFMT {
};
typedef struct wasapi_state {
struct mp_log *log;
HANDLE threadLoop;
/* Init phase */
@@ -87,7 +85,6 @@ typedef struct wasapi_state {
/* Play */
HANDLE hPlay;
int is_playing;
int final_chunk;
/* Reset */
HANDLE hReset;
@@ -101,6 +98,9 @@ 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 */
@@ -294,8 +294,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_ERR(ao, "unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: unknown SubFormat %"PRIu32"\n",
(uint32_t)wformat.SubFormat.Data1);
return 0;
}
@@ -321,8 +321,10 @@ static int try_format(struct wasapi_state *state,
int af_format = format_set_bits(ao->format, bits, bits == 32);
MP_VERBOSE(ao, "trying %dch %s @ %dhz\n",
channels.num, af_fmt_to_str(af_format), samplerate);
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);
union WAVEFMT u;
u.extensible = &wformat;
@@ -345,16 +347,20 @@ static int try_format(struct wasapi_state *state,
if (hr == S_FALSE) {
if (set_ao_format(state, ao, wformat)) {
MP_VERBOSE(ao, "accepted as %dch %s @ %dhz\n",
ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
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);
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)) {
MP_VERBOSE(ao, "%dch %s @ %dhz accepted\n",
ao->channels.num, af_fmt_to_str(af_format), samplerate);
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);
return 1;
}
}
@@ -408,8 +414,10 @@ static int try_passthrough(struct wasapi_state *state,
union WAVEFMT u;
u.extensible = &wformat;
MP_VERBOSE(ao, "trying passthrough for %s...\n",
af_fmt_to_str((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE));
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);
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
state->share_mode,
@@ -430,10 +438,12 @@ static int find_formats(struct ao *const ao)
if (try_passthrough(state, ao))
return 0;
MP_ERR(ao, "couldn't use passthrough!");
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: couldn't use passthrough!");
if (!state->opt_exclusive)
MP_ERR(ao, " (try exclusive mode)");
MP_ERR(ao, "\n");
mp_msg(MSGT_AO, MSGL_ERR, " (try exclusive mode)");
mp_msg(MSGT_AO, MSGL_ERR, "\n");
LeaveCriticalSection(&state->print_lock);
return -1;
}
@@ -452,7 +462,9 @@ static int find_formats(struct ao *const ao)
return 0;
}
MP_WARN(ao, "couldn't use default mix format!\n");
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_WARN, "ao-wasapi: couldn't use default mix format!\n");
LeaveCriticalSection(&state->print_lock);
}
/* Exclusive mode, we have to guess. */
@@ -523,7 +535,9 @@ static int find_formats(struct ao *const ao)
bits = start_bits;
mp_chmap_from_channels(&ao->channels, 2);
} else {
MP_ERR(ao, "couldn't find acceptable audio format!\n");
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: couldn't find acceptable audio format!\n");
LeaveCriticalSection(&state->print_lock);
return -1;
}
}
@@ -550,8 +564,12 @@ reinit:
NULL);
/* something about buffer sizes on Win7, fixme might loop forever */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
explain_err(hr), state->defaultRequestedDuration);
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);
if (offset > 10.0)
goto exit_label; /* is 10 enough to break out of the loop?*/
IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
@@ -583,12 +601,18 @@ reinit:
state->bufferFrameCount;
state->hTask =
state->VistaBlob.pAvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
MP_VERBOSE(state, "fix_format OK, using %lld byte buffer block size!\n",
(long long) state->buffer_block_size);
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);
return 0;
exit_label:
MP_ERR(state, "fix_format fails with %s, failed to determine buffer block size!\n",
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_ERR,
"ao-wasapi: 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;
}
@@ -697,9 +721,7 @@ end:
return found;
}
static HRESULT enumerate_with_state(struct mp_log *log, char *header,
int status, int with_id)
{
static HRESULT enumerate_with_state(char *header, int status, int with_id) {
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pDevices = NULL;
@@ -726,7 +748,7 @@ static HRESULT enumerate_with_state(struct mp_log *log, char *header,
int count;
IMMDeviceCollection_GetCount(pDevices, &count);
if (count > 0) {
mp_info(log, "%s\n", header);
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: %s\n", header);
}
for (int i = 0; i < count; i++) {
@@ -741,9 +763,11 @@ static HRESULT enumerate_with_state(struct mp_log *log, char *header,
mark = " (default)";
if (with_id) {
mp_info(log, "Device #%d: %s, ID: %s%s\n", i, name, id, mark);
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: Device #%d: %s, ID: %s%s\n",
i, name, id, mark);
} else {
mp_info(log, "%s, ID: %s%s\n", name, id, mark);
mp_msg(MSGT_AO, MSGL_INFO, "ao-wasapi: %s, ID: %s%s\n",
name, id, mark);
}
free(name);
@@ -763,27 +787,24 @@ exit_label:
return hr;
}
static int enumerate_devices(struct mp_log *log)
{
static int enumerate_devices(void) {
HRESULT hr;
CoInitialize(NULL);
hr = enumerate_with_state(log, "Active devices:", DEVICE_STATE_ACTIVE, 1);
hr = enumerate_with_state("Active devices:", DEVICE_STATE_ACTIVE, 1);
EXIT_ON_ERROR(hr);
hr = enumerate_with_state(log, "Unplugged devices:", DEVICE_STATE_UNPLUGGED, 0);
hr = enumerate_with_state("Unplugged devices:", DEVICE_STATE_UNPLUGGED, 0);
EXIT_ON_ERROR(hr);
CoUninitialize();
return 0;
exit_label:
mp_err(log, "Error enumerating devices: HRESULT %08"PRIx32" \"%s\"\n",
(uint32_t)hr, explain_err(hr));
mp_msg(MSGT_AO, MSGL_ERR, "Error enumerating devices: HRESULT %08"PRIx32" \"%s\"\n",
(uint32_t)hr, explain_err(hr));
CoUninitialize();
return 1;
}
static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
char *search)
{
static HRESULT find_and_load_device(IMMDevice **ppDevice, char *search) {
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pDevices = NULL;
@@ -813,16 +834,16 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
IMMDeviceCollection_GetCount(pDevices, &count);
if (devno >= count) {
MP_ERR(ao, "no device #%d!\n", devno);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: no device #%d!\n", devno);
} else {
MP_VERBOSE(ao, "finding device #%d\n", devno);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: 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_VERBOSE(ao, "found device #%d\n", devno);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: found device #%d\n", devno);
}
} else {
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
@@ -833,7 +854,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
int count;
IMMDeviceCollection_GetCount(pDevices, &count);
MP_VERBOSE(ao, "finding device %s\n", devid);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: finding device %s\n", devid);
IMMDevice *prevDevice = NULL;
@@ -851,14 +872,14 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
if (deviceID) {
char *name;
if (!search_err) {
MP_ERR(ao, "multiple matching devices found!\n");
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: multiple matching devices found!\n");
name = get_device_name(prevDevice);
MP_ERR(ao, "%s\n", name);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: %s\n", name);
free(name);
search_err = 1;
}
name = get_device_name(pTempDevice);
MP_ERR(ao, "%s\n", name);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: %s\n", name);
free(name);
}
hr = IMMDevice_GetId(pTempDevice, &deviceID);
@@ -870,7 +891,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
}
if (deviceID == NULL) {
MP_ERR(ao, "could not find device %s!\n", devid);
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not find device %s!\n", devid);
}
}
@@ -880,12 +901,12 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice,
if (deviceID == NULL || search_err) {
hr = E_NOTFOUND;
} else {
MP_VERBOSE(ao, "loading device %S\n", deviceID);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: loading device %S\n", deviceID);
hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
if (FAILED(hr)) {
MP_ERR(ao, "could not load requested device!\n");
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: could not load requested device!\n");
}
}
@@ -896,15 +917,14 @@ exit_label:
return hr;
}
static int validate_device(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
static int validate_device(const m_option_t *opt, struct bstr name,
struct bstr param) {
if (bstr_equals0(param, "help")) {
enumerate_devices(log);
enumerate_devices();
return M_OPT_EXIT;
}
mp_dbg(log, "validating device=%s\n", param.start);
mp_msg(MSGT_AO, MSGL_DBG2, "ao-wasapi: validating device=%s\n", param.start);
char *end;
int devno = (int) strtol(param.start, &end, 10);
@@ -913,7 +933,8 @@ static int validate_device(struct mp_log *log, const m_option_t *opt,
if ((end == (void*)param.start || *end) && devno < 0)
ret = M_OPT_OUT_OF_RANGE;
mp_dbg(log, "device=%s %svalid\n", param.start, ret == 1 ? "" : "not ");
mp_msg(MSGT_AO, MSGL_DBG2, "ao-wasapi: device=%s %svalid\n",
param.start, ret == 1 ? "" : "not ");
return ret;
}
@@ -935,15 +956,15 @@ static int thread_init(struct ao *ao)
SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
char *id = get_device_id(state->pDevice);
MP_VERBOSE(ao, "default device ID: %s\n", id);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: default device ID: %s\n", id);
free(id);
} else {
hr = find_and_load_device(ao, &state->pDevice, state->opt_device);
hr = find_and_load_device(&state->pDevice, state->opt_device);
}
EXIT_ON_ERROR(hr);
char *name = get_device_name(state->pDevice);
MP_VERBOSE(ao, "device loaded: %s\n", name);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: device loaded: %s\n", name);
free(name);
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
@@ -961,7 +982,9 @@ 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 */
MP_VERBOSE(ao, "thread_init OK!\n");
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: thread_init OK!\n");
LeaveCriticalSection(&state->print_lock);
SetEvent(state->init_done);
return state->init_ret;
}
@@ -1007,7 +1030,6 @@ 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,
@@ -1021,13 +1043,15 @@ static void thread_feed(wasapi_state *state,int force_feed)
EXIT_ON_ERROR(hr);
return;
exit_label:
MP_ERR(state, "thread_feed fails with %"PRIx32"!\n", (uint32_t)hr);
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);
return;
}
static void thread_play(wasapi_state *state)
{
thread_feed(state, state->final_chunk);
thread_feed(state, 0);
state->is_playing = 1;
IAudioClient_Start(state->pAudioClient);
return;
@@ -1097,7 +1121,9 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
HANDLE playcontrol[] =
{state->hUninit, state->hPause, state->hReset, state->hGetvol,
state->hSetvol, state->hPlay, state->hFeed, NULL};
MP_VERBOSE(ao, "Entering dispatch loop!\n");
EnterCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: Entering dispatch loop!\n");
LeaveCriticalSection(&state->print_lock);
while (1) { /* watch events, poll at least every 2 seconds */
waitstatus = WaitForMultipleObjects(7, playcontrol, FALSE, 2000);
switch (waitstatus) {
@@ -1126,7 +1152,7 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
case (WAIT_OBJECT_0 + 6): /* feed */
if (state->is_playing)
feedwatch = 1;
thread_feed(state, state->final_chunk);
thread_feed(state, 0);
break;
case WAIT_TIMEOUT: /* Did our feed die? */
if (feedwatch)
@@ -1169,12 +1195,11 @@ 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) / ao->sstride;
return mp_ring_available(state->ringbuff);
}
static void reset_buffers(struct wasapi_state *state)
{
state->final_chunk = 0;
mp_ring_reset(state->ringbuff);
}
@@ -1185,11 +1210,11 @@ static int setup_buffers(struct wasapi_state *state)
return !state->ringbuff;
}
static void uninit(struct ao *ao, bool block)
static void uninit(struct ao *ao, bool immed)
{
MP_VERBOSE(ao, "uninit!\n");
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: uninit!\n");
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
state->immed = !block;
state->immed = immed;
SetEvent(state->hUninit);
/* wait up to 10 seconds */
if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT)
@@ -1197,23 +1222,22 @@ static void uninit(struct ao *ao, bool block)
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
closehandles(ao);
MP_VERBOSE(ao, "uninit END!\n");
DeleteCriticalSection(&state->print_lock);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: uninit END!\n");
}
static int init(struct ao *ao)
{
MP_VERBOSE(ao, "init!\n");
ao->format = af_fmt_from_planar(ao->format);
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: init!\n");
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(state->log);
enumerate_devices();
}
if (state->opt_exclusive) {
@@ -1232,6 +1256,7 @@ 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)
@@ -1244,18 +1269,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_ERR(ao, "fail to create thread!\n");
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: fail to create thread!\n");
return -1;
}
WaitForSingleObject(state->init_done, INFINITE); /* wait on init complete */
if (state->init_ret) {
if (!ao->probing) {
MP_ERR(ao, "thread_init failed!\n");
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: thread_init failed!\n");
}
} else {
MP_VERBOSE(ao, "Init Done!\n");
mp_msg(MSGT_AO, MSGL_V, "ao-wasapi: Init Done!\n");
if (setup_buffers(state))
MP_ERR(ao, "buffer setup failed!\n");
mp_msg(MSGT_AO, MSGL_ERR, "ao-wasapi: buffer setup failed!\n");
}
return state->init_ret;
}
@@ -1311,25 +1336,25 @@ static void reset(struct ao *ao)
reset_buffers(state);
}
static int play(struct ao *ao, void **data, int samples, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
int ret = 0;
if (!ao || !ao->priv)
return 0;
return ret;
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
if (WaitForSingleObject(state->fatal_error, 0) == WAIT_OBJECT_0) {
/* something bad happened */
return 0;
return ret;
}
int ret = mp_ring_write(state->ringbuff, data[0], samples * ao->sstride);
ret = mp_ring_write(state->ringbuff, data, len);
state->final_chunk |= flags & AOPLAY_FINAL_CHUNK;
if (!state->is_playing) {
/* start playing */
state->is_playing = 1;
SetEvent(state->hPlay);
}
return ret / ao->sstride;
return ret;
}
static float get_delay(struct ao *ao)
@@ -1337,15 +1362,19 @@ 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) * ao->sstride) /
return (float)(RING_BUFFER_COUNT * state->buffer_block_size - get_space(ao)) /
(float)state->format.Format.nAvgBytesPerSec;
}
#define OPT_BASE_STRUCT struct wasapi_state
const struct ao_driver audio_out_wasapi = {
.description = "Windows WASAPI audio output (event mode)",
.name = "wasapi",
.info = &(const struct ao_info) {
"Windows WASAPI audio output (event mode)",
"wasapi",
"Jonathan Yong <[email protected]>",
""
},
.init = init,
.uninit = uninit,
.control = control,
+68
View File
@@ -27,6 +27,74 @@
#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,6 +25,11 @@
#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);
-36
View File
@@ -1,36 +0,0 @@
#!/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)
-211
View File
@@ -1,211 +0,0 @@
/*
* av_log to mp_msg converter
* Copyright (C) 2006 Michael Niedermayer
* Copyright (C) 2009 Uoti Urpala
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include "av_log.h"
#include "config.h"
#include "common/common.h"
#include "common/global.h"
#include "common/msg.h"
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#if HAVE_LIBAVDEVICE
#include <libavdevice/avdevice.h>
#endif
#if HAVE_LIBAVFILTER
#include <libavfilter/avfilter.h>
#endif
#if HAVE_LIBAVRESAMPLE
#include <libavresample/avresample.h>
#endif
#if HAVE_LIBSWRESAMPLE
#include <libswresample/swresample.h>
#endif
#if LIBAVCODEC_VERSION_MICRO >= 100
#define LIB_PREFIX "ffmpeg"
#else
#define LIB_PREFIX "libav"
#endif
// Needed because the av_log callback does not provide a library-safe message
// callback.
static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
static struct mpv_global *log_mpv_instance;
static struct mp_log *log_root, *log_decaudio, *log_decvideo, *log_demuxer;
static bool log_print_prefix = true;
static int av_log_level_to_mp_level(int av_level)
{
if (av_level > AV_LOG_VERBOSE)
return MSGL_DEBUG;
if (av_level > AV_LOG_INFO)
return MSGL_V;
if (av_level > AV_LOG_WARNING)
return MSGL_V;
if (av_level > AV_LOG_ERROR)
return MSGL_WARN;
if (av_level > AV_LOG_FATAL)
return MSGL_ERR;
return MSGL_FATAL;
}
static struct mp_log *get_av_log(void *ptr)
{
if (!ptr)
return log_root;
AVClass *avc = *(AVClass **)ptr;
if (!avc) {
mp_warn(log_root,
"av_log callback called with bad parameters (NULL AVClass).\n"
"This is a bug in one of Libav/FFmpeg libraries used.\n");
return log_root;
}
if (!strcmp(avc->class_name, "AVCodecContext")) {
AVCodecContext *s = ptr;
if (s->codec) {
if (s->codec->type == AVMEDIA_TYPE_AUDIO) {
if (s->codec->decode)
return log_decaudio;
} else if (s->codec->type == AVMEDIA_TYPE_VIDEO) {
if (s->codec->decode)
return log_decvideo;
}
}
}
if (!strcmp(avc->class_name, "AVFormatContext")) {
AVFormatContext *s = ptr;
if (s->iformat)
return log_demuxer;
}
return log_root;
}
static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
va_list vl)
{
AVClass *avc = ptr ? *(AVClass **)ptr : NULL;
int mp_level = av_log_level_to_mp_level(level);
// Note: mp_log is thread-safe, but destruction of the log instances is not.
pthread_mutex_lock(&log_lock);
if (!log_mpv_instance) {
pthread_mutex_unlock(&log_lock);
// Fallback to stderr
vfprintf(stderr, fmt, vl);
return;
}
struct mp_log *log = get_av_log(ptr);
if (mp_msg_test(log, mp_level)) {
if (log_print_prefix)
mp_msg(log, mp_level, "%s: ", avc ? avc->item_name(ptr) : "?");
log_print_prefix = fmt[strlen(fmt) - 1] == '\n';
mp_msg_va(log, mp_level, fmt, vl);
}
pthread_mutex_unlock(&log_lock);
}
void init_libav(struct mpv_global *global)
{
pthread_mutex_lock(&log_lock);
if (!log_mpv_instance) {
log_mpv_instance = global;
log_root = mp_log_new(NULL, global->log, LIB_PREFIX);
log_decaudio = mp_log_new(log_root, log_root, "audio");
log_decvideo = mp_log_new(log_root, log_root, "video");
log_demuxer = mp_log_new(log_root, log_root, "demuxer");
av_log_set_callback(mp_msg_av_log_callback);
}
pthread_mutex_unlock(&log_lock);
avcodec_register_all();
av_register_all();
avformat_network_init();
#if HAVE_LIBAVFILTER
avfilter_register_all();
#endif
#if HAVE_LIBAVDEVICE
avdevice_register_all();
#endif
}
void uninit_libav(struct mpv_global *global)
{
pthread_mutex_lock(&log_lock);
if (log_mpv_instance == global) {
log_mpv_instance = NULL;
talloc_free(log_root);
}
pthread_mutex_unlock(&log_lock);
}
#define V(x) (x)>>16, (x)>>8 & 255, (x) & 255
static void print_version(struct mp_log *log, int v, char *name,
unsigned buildv, unsigned runv)
{
mp_msg(log, v, " %-15s %d.%d.%d", name, V(buildv));
if (buildv != runv)
mp_msg(log, v, " (runtime %d.%d.%d)", V(runv));
mp_msg(log, v, "\n");
}
#undef V
void print_libav_versions(struct mp_log *log, int v)
{
mp_msg(log, v, "%s library versions:\n", LIB_PREFIX);
print_version(log, v, "libavutil", LIBAVUTIL_VERSION_INT, avutil_version());
print_version(log, v, "libavcodec", LIBAVCODEC_VERSION_INT, avcodec_version());
print_version(log, v, "libavformat", LIBAVFORMAT_VERSION_INT, avformat_version());
print_version(log, v, "libswscale", LIBSWSCALE_VERSION_INT, swscale_version());
#if HAVE_LIBAVFILTER
print_version(log, v, "libavfilter", LIBAVFILTER_VERSION_INT, avfilter_version());
#endif
#if HAVE_LIBAVRESAMPLE
print_version(log, v, "libavresample", LIBAVRESAMPLE_VERSION_INT, avresample_version());
#endif
#if HAVE_LIBSWRESAMPLE
print_version(log, v, "libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version());
#endif
}
-8
View File
@@ -1,8 +0,0 @@
#ifndef MP_AV_LOG_H
#define MP_AV_LOG_H
struct mpv_global;
struct mp_log;
void init_libav(struct mpv_global *global);
void uninit_libav(struct mpv_global *global);
void print_libav_versions(struct mp_log *log, int v);
#endif
Loaded 100 of 450 files, more files were not shown because too many files have changed in this diff. Show more