mirror of
https://github.com/mpv-player/mpv
synced 2025-04-01 00:07:33 +00:00
Merge branch 'audio_changes'
Conflicts: audio/out/ao_lavc.c
This commit is contained in:
commit
e6e5a7b221
@ -30,15 +30,15 @@ filter list.
|
||||
Available filters are:
|
||||
|
||||
lavrresample[=option1:option2:...]
|
||||
Changes the sample rate of the audio stream to an integer <srate> in Hz.
|
||||
Can be used if you have a fixed frequency sound card or if you are stuck
|
||||
with an old sound card that is only capable of max 44.1kHz.
|
||||
This filter uses libavresample (or libswresample, depending on the build)
|
||||
to change sample rate, sample format, or channel layout of the audio stream.
|
||||
This filter is automatically enabled if the audio output doesn't support
|
||||
the audio configuration of the file being played.
|
||||
|
||||
This filter is automatically enabled if necessary. It only supports the
|
||||
16-bit integer native-endian format.
|
||||
It supports only the following sample formats: u8, s16ne, s32ne, floatne.
|
||||
|
||||
srate=<srate>
|
||||
the output sample rate (defaut: 44100)
|
||||
the output sample rate
|
||||
length=<length>
|
||||
length of the filter with respect to the lower sampling rate (default:
|
||||
16)
|
||||
@ -50,6 +50,11 @@ lavrresample[=option1:option2:...]
|
||||
linear
|
||||
if set then filters will be linearly interpolated between polyphase
|
||||
entries (default: no)
|
||||
no-detach
|
||||
don't detach if input and output audio format/rate/channels are the
|
||||
same. You should add this option if you specify additional parameters,
|
||||
as automatically inserted lavrresample instances will use the
|
||||
default settings.
|
||||
|
||||
lavcac3enc[=tospdif[:bitrate[:minchn]]]
|
||||
Encode multi-channel audio to AC-3 at runtime using libavcodec. Supports
|
||||
@ -205,6 +210,34 @@ channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]
|
||||
Would change the number of channels to 6 and set up 4 routes that copy
|
||||
channel 0 to channels 0 to 3. Channel 4 and 5 will contain silence.
|
||||
|
||||
force=in-format:in-srate:in-channels:out-format:out-srate:out-channels
|
||||
Force a specific audio format/configuration without actually changing the
|
||||
audio data. Keep in mind that the filter system might auto-insert actual
|
||||
conversion filters before or after this filter if needed.
|
||||
|
||||
All parameters are optional. The ``in-`` variants restrict what the filter
|
||||
accepts as input. The ``out-`` variants change the audio format, without
|
||||
actually doing a conversion. The data will be 'reinterpreted' by the
|
||||
filters or audio outputs following this filter.
|
||||
|
||||
<in-format>
|
||||
Force conversion to this format. See ``format`` filter for valid audio
|
||||
format values.
|
||||
|
||||
<in-srate>
|
||||
Force conversion to a specific sample rate. The rate is an integer,
|
||||
48000 for example.
|
||||
|
||||
<in-channels>
|
||||
Force mixing to a specific channel layout. See ``--channels`` option
|
||||
for possible values.
|
||||
|
||||
<out-format>
|
||||
|
||||
<out-srate>
|
||||
|
||||
<out-channels>
|
||||
|
||||
format[=format]
|
||||
Convert between different sample formats. Automatically enabled when
|
||||
needed by the sound card or another filter. See also ``--format``.
|
||||
@ -219,7 +252,7 @@ format[=format]
|
||||
rule that are also valid format specifiers: u8, s8, floatle, floatbe,
|
||||
floatne, mpeg2, and ac3.
|
||||
|
||||
volume[=v[:sc]]
|
||||
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 set the level for the PCM sound to max, leave this filter out and
|
||||
@ -233,8 +266,7 @@ volume[=v[:sc]]
|
||||
|
||||
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, use e.g. ``--af-adv=force=5``, or use
|
||||
``--af=stats``.
|
||||
only works with floating-point data.
|
||||
|
||||
*NOTE*: This filter is not reentrant and can therefore only be enabled
|
||||
once for every audio stream.
|
||||
@ -250,6 +282,9 @@ volume[=v[:sc]]
|
||||
|
||||
*WARNING*: This feature creates distortion and should be considered a
|
||||
last resort.
|
||||
<fast>
|
||||
Force S16 sample format if set to 1. Lower quality, but might be faster
|
||||
in some situations.
|
||||
|
||||
*EXAMPLE*:
|
||||
|
||||
@ -286,6 +321,11 @@ pan=n[:L00:L01:L02:...L10:L11:L12:...Ln0:Ln1:Ln2:...]
|
||||
channels 0 and 1 into output channel 2 (which could be sent to a
|
||||
subwoofer for example).
|
||||
|
||||
*NOTE*: if you just want to force remixing to a certain output channel
|
||||
layout, it's 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]
|
||||
Adds a subwoofer channel to the audio stream. The audio data used for
|
||||
creating the subwoofer channel is an average of the sound in channel 0 and
|
||||
|
@ -124,6 +124,7 @@ Command line switches
|
||||
-afm hwac3 --ad=spdif:ac3,spdif:dts
|
||||
-x W, -y H --geometry=WxH + --no-keepaspect
|
||||
-xy W --autofit=W
|
||||
-a52drc level --ad-lavc-ac3drc=level
|
||||
=================================== ===================================
|
||||
|
||||
*NOTE*: ``-opt val`` becomes ``--opt=val``.
|
||||
|
@ -1,11 +1,3 @@
|
||||
--a52drc=<level>
|
||||
Select the Dynamic Range Compression level for AC-3 audio streams. <level>
|
||||
is a float value ranging from 0 to 1, where 0 means no compression and 1
|
||||
(which is the default) means full compression (make loud passages more
|
||||
silent and vice versa). Values up to 2 are also accepted, but are purely
|
||||
experimental. This option only shows an effect if the AC-3 stream contains
|
||||
the required range compression information.
|
||||
|
||||
--abs=<value>
|
||||
(``--ao=oss`` only) (OBSOLETE)
|
||||
Override audio driver/card buffer size detection.
|
||||
@ -37,6 +29,25 @@
|
||||
``--ad=help``
|
||||
List all available decoders.
|
||||
|
||||
--ad-lavc-ac3drc=<level>
|
||||
Select the Dynamic Range Compression level for AC-3 audio streams. <level>
|
||||
is a float value ranging from 0 to 1, where 0 means no compression and 1
|
||||
(which is the default) means full compression (make loud passages more
|
||||
silent and vice versa). Values up to 2 are also accepted, but are purely
|
||||
experimental. This option only shows an effect if the AC-3 stream contains
|
||||
the required range compression information.
|
||||
|
||||
--ad-lavc-downmix=<yes|no>
|
||||
Whether to request audio channel downmixing from the decoder (default: yes).
|
||||
Some decoders, like AC-3, AAC and DTS, can remix audio on decoding. The
|
||||
requested number of output channels is set with the ``--channels`` option.
|
||||
Useful for playing surround audio on a stereo system.
|
||||
|
||||
--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
|
||||
welcome. A full list of AVOptions can be found in the FFmpeg manual.
|
||||
|
||||
--af=<filter1[=parameter1:parameter2:...],filter2,...>
|
||||
Specify a list of audio filters to apply to the audio stream. See
|
||||
`audio_filters` for details and descriptions of the available filters.
|
||||
@ -44,40 +55,6 @@
|
||||
``--af-clr`` exist to modify a previously specified list, but you
|
||||
shouldn't need these for typical use.
|
||||
|
||||
--af-adv=<force=(0-7):list=(filters)>
|
||||
See also ``--af``.
|
||||
Specify advanced audio filter options:
|
||||
|
||||
force=<0-7>
|
||||
Forces the insertion of audio filters to one of the following:
|
||||
|
||||
0
|
||||
Use completely automatic filter insertion (currently identical to
|
||||
1).
|
||||
1
|
||||
Optimize for accuracy (default).
|
||||
2
|
||||
Optimize for speed. *Warning*: Some features in the audio filters
|
||||
may silently fail, and the sound quality may drop.
|
||||
3
|
||||
Use no automatic insertion of filters and no optimization.
|
||||
*Warning*: It may be possible to crash mpv using this setting.
|
||||
4
|
||||
Use automatic insertion of filters according to 0 above, but use
|
||||
floating point processing when possible.
|
||||
5
|
||||
Use automatic insertion of filters according to 1 above, but use
|
||||
floating point processing when possible.
|
||||
6
|
||||
Use automatic insertion of filters according to 2 above, but use
|
||||
floating point processing when possible.
|
||||
7
|
||||
Use no automatic insertion of filters according to 3 above, and
|
||||
use floating point processing when possible.
|
||||
|
||||
list=<filters>
|
||||
Same as ``--af``.
|
||||
|
||||
--aid=<ID|auto|no>
|
||||
Select audio channel. ``auto`` selects the default, ``no`` disables audio.
|
||||
See also ``--alang``.
|
||||
@ -381,25 +358,24 @@
|
||||
--cdrom-device=<path>
|
||||
Specify the CD-ROM device (default: ``/dev/cdrom``).
|
||||
|
||||
--channels=<number>
|
||||
--channels=<number|layout>
|
||||
Request the number of playback channels (default: 2). mpv asks the
|
||||
decoder to decode the audio into as many channels as specified. Then it is
|
||||
up to the decoder to fulfill the requirement. This is usually only
|
||||
important when playing videos with AC-3 audio (like DVDs). In that case
|
||||
liba52 does the decoding by default and correctly downmixes the audio into
|
||||
the requested number of channels. To directly control the number of output
|
||||
channels independently of how many channels are decoded, use the channels
|
||||
filter (``--af=channels``).
|
||||
important when playing videos with AC-3, AAC or DTS audio. In that case
|
||||
libavcodec downmixes the audio into the requested number of channels if
|
||||
possible.
|
||||
|
||||
*NOTE*: This option is honored by codecs (AC-3 only), filters (surround)
|
||||
and audio output drivers (OSS at least).
|
||||
|
||||
Available options are:
|
||||
The ``--channels`` option either takes a channel number or an explicit
|
||||
channel layout. Channel numbers refer to default layouts, e.g. 2 channels
|
||||
refer to stereo, 6 refers to 5.1.
|
||||
|
||||
:2: stereo
|
||||
:4: surround
|
||||
:6: full 5.1
|
||||
:8: full 7.1
|
||||
See ``--channels=help`` output for defined default layouts. This also
|
||||
lists speaker names, which can be used to express arbitrary channel
|
||||
layouts (e.g. ``fl-fr-lfe`` is 2.1).
|
||||
|
||||
--chapter=<start[-end]>
|
||||
Specify which chapter to start playing at. Optionally specify which
|
||||
@ -1985,9 +1961,8 @@
|
||||
--srate=<Hz>
|
||||
Select the output sample rate to be used (of course sound cards have
|
||||
limits on this). If the sample frequency selected is different from that
|
||||
of the current media, the resample or lavcresample audio filter will be
|
||||
inserted into the audio filter layer to compensate for the difference. The
|
||||
type of resampling can be controlled by the ``--af-adv`` option.
|
||||
of the current media, the lavrresample audio filter will be
|
||||
inserted into the audio filter layer to compensate for the difference.
|
||||
|
||||
--start=<relative time>
|
||||
Seek to given time position.
|
||||
|
5
Makefile
5
Makefile
@ -124,6 +124,10 @@ ifeq ($(HAVE_AVUTIL_REFCOUNTING),no)
|
||||
endif
|
||||
|
||||
SOURCES = talloc.c \
|
||||
audio/audio.c \
|
||||
audio/chmap.c \
|
||||
audio/chmap_sel.c \
|
||||
audio/fmt-conversion.c \
|
||||
audio/format.c \
|
||||
audio/mixer.c \
|
||||
audio/reorder_ch.c \
|
||||
@ -138,6 +142,7 @@ SOURCES = talloc.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 \
|
||||
|
@ -1,3 +1,10 @@
|
||||
# Usage:
|
||||
# uncrustify -l C -c TOOLS/uncrustify.cfg --no-backup --replace file.c
|
||||
#
|
||||
# Keep in mind that this uncrustify configuration still produces some
|
||||
# bad/broken formatting.
|
||||
#
|
||||
|
||||
code_width=80
|
||||
indent_align_string=false
|
||||
indent_braces=false
|
||||
|
76
audio/audio.c
Normal file
76
audio/audio.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include "core/mp_talloc.h"
|
||||
#include "audio.h"
|
||||
|
||||
void mp_audio_set_format(struct mp_audio *mpa, int format)
|
||||
{
|
||||
mpa->format = format;
|
||||
mpa->bps = af_fmt2bits(format) / 8;
|
||||
}
|
||||
|
||||
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
|
||||
{
|
||||
struct mp_chmap map;
|
||||
mp_chmap_from_channels(&map, num_channels);
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
// Use old MPlayer/ALSA channel layout.
|
||||
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
|
||||
{
|
||||
struct mp_chmap map;
|
||||
mp_chmap_from_channels_alsa(&map, num_channels);
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
|
||||
{
|
||||
assert(mp_chmap_is_empty(chmap) || mp_chmap_is_valid(chmap));
|
||||
mpa->channels = *chmap;
|
||||
mpa->nch = mpa->channels.num;
|
||||
}
|
||||
|
||||
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src)
|
||||
{
|
||||
mp_audio_set_format(dst, src->format);
|
||||
mp_audio_set_channels(dst, &src->channels);
|
||||
dst->rate = src->rate;
|
||||
}
|
||||
|
||||
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
|
||||
{
|
||||
return a->format == b->format && a->rate == b->rate &&
|
||||
mp_chmap_equals(&a->channels, &b->channels);
|
||||
}
|
||||
|
||||
char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format)
|
||||
{
|
||||
char *chstr = mp_chmap_to_str(chmap);
|
||||
char *res = talloc_asprintf(NULL, "%dHz %s %dch %s", srate, chstr,
|
||||
chmap->num, af_fmt2str_short(format));
|
||||
talloc_free(chstr);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *mp_audio_config_to_str(struct mp_audio *mpa)
|
||||
{
|
||||
return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format);
|
||||
}
|
46
audio/audio.h
Normal file
46
audio/audio.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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_H
|
||||
#define MP_AUDIO_H
|
||||
|
||||
#include "format.h"
|
||||
#include "chmap.h"
|
||||
|
||||
// Audio data chunk
|
||||
struct mp_audio {
|
||||
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 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);
|
||||
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
486
audio/chmap.c
Normal file
486
audio/chmap.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* 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 "core/mp_msg.h"
|
||||
#include "chmap.h"
|
||||
|
||||
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
|
||||
// Use of these names is hard-coded in some places (e.g. ao_alsa.c)
|
||||
static const char *speaker_names[MP_SPEAKER_ID_COUNT][2] = {
|
||||
[MP_SPEAKER_ID_FL] = {"fl", "front left"},
|
||||
[MP_SPEAKER_ID_FR] = {"fr", "front right"},
|
||||
[MP_SPEAKER_ID_FC] = {"fc", "front center"},
|
||||
[MP_SPEAKER_ID_LFE] = {"lfe", "low frequency"},
|
||||
[MP_SPEAKER_ID_BL] = {"bl", "back left"},
|
||||
[MP_SPEAKER_ID_BR] = {"br", "back right"},
|
||||
[MP_SPEAKER_ID_FLC] = {"flc", "front left-of-center"},
|
||||
[MP_SPEAKER_ID_FRC] = {"frc", "front right-of-center"},
|
||||
[MP_SPEAKER_ID_BC] = {"bc", "back center"},
|
||||
[MP_SPEAKER_ID_SL] = {"sl", "side left"},
|
||||
[MP_SPEAKER_ID_SR] = {"sr", "side right"},
|
||||
[MP_SPEAKER_ID_TC] = {"tc", "top center"},
|
||||
[MP_SPEAKER_ID_TFL] = {"tfl", "top front left"},
|
||||
[MP_SPEAKER_ID_TFC] = {"tfc", "top front center"},
|
||||
[MP_SPEAKER_ID_TFR] = {"tfr", "top front right"},
|
||||
[MP_SPEAKER_ID_TBL] = {"tbl", "top back left"},
|
||||
[MP_SPEAKER_ID_TBC] = {"tbc", "top back center"},
|
||||
[MP_SPEAKER_ID_TBR] = {"tbr", "top back right"},
|
||||
[MP_SPEAKER_ID_DL] = {"dl", "downmix left"},
|
||||
[MP_SPEAKER_ID_DR] = {"dr", "downmix right"},
|
||||
[MP_SPEAKER_ID_WL] = {"wl", "wide left"},
|
||||
[MP_SPEAKER_ID_WR] = {"wr", "wide right"},
|
||||
[MP_SPEAKER_ID_SDL] = {"sdl", "surround direct left"},
|
||||
[MP_SPEAKER_ID_SDR] = {"sdr", "surround direct right"},
|
||||
[MP_SPEAKER_ID_LFE2] = {"lfe2", "low frequency 2"},
|
||||
};
|
||||
|
||||
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
|
||||
// Channel order corresponds to lavc/waveex, except for the alsa entries.
|
||||
static const char *std_layout_names[][2] = {
|
||||
{"empty", ""}, // not in lavc
|
||||
{"mono", "fc"},
|
||||
{"stereo", "fl-fr"},
|
||||
{"2.1", "fl-fr-lfe"},
|
||||
{"3.0", "fl-fr-fc"},
|
||||
{"3.0(back)", "fl-fr-bc"},
|
||||
{"4.0", "fl-fr-fc-bc"},
|
||||
{"quad", "fl-fr-bl-br"},
|
||||
{"quad(side)", "fl-fr-sl-sr"},
|
||||
{"3.1", "fl-fr-fc-lfe"},
|
||||
{"5.0", "fl-fr-fc-bl-br"},
|
||||
{"5.0(alsa)", "fl-fr-bl-br-fc"}, // not in lavc
|
||||
{"5.0(side)", "fl-fr-fc-sl-sr"},
|
||||
{"4.1", "fl-fr-fc-lfe-bc"},
|
||||
{"4.1(alsa)", "fl-fr-bl-br-lfe"}, // not in lavc
|
||||
{"5.1", "fl-fr-fc-lfe-bl-br"},
|
||||
{"5.1(alsa)", "fl-fr-bl-br-fc-lfe"}, // not in lavc
|
||||
{"5.1(side)", "fl-fr-fc-lfe-sl-sr"},
|
||||
{"6.0", "fl-fr-fc-bc-sl-sr"},
|
||||
{"6.0(front)", "fl-fr-flc-frc-sl-sr"},
|
||||
{"hexagonal", "fl-fr-fc-bl-br-bc"},
|
||||
{"6.1", "fl-fr-fc-lfe-bl-br-bc"},
|
||||
{"6.1(front)", "fl-fr-lfe-flc-frc-sl-sr"},
|
||||
{"7.0", "fl-fr-fc-bl-br-sl-sr"},
|
||||
{"7.0(front)", "fl-fr-fc-flc-frc-sl-sr"},
|
||||
{"7.1", "fl-fr-fc-lfe-bl-br-sl-sr"},
|
||||
{"7.1(alsa)", "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
|
||||
{"7.1(wide)", "fl-fr-fc-lfe-bl-br-flc-frc"},
|
||||
{"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
|
||||
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
|
||||
{"downmix", "dl-dr"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct mp_chmap default_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, FC, BC), // 4.0
|
||||
MP_CHMAP5(FL, FR, FC, BL, BR), // 5.0
|
||||
MP_CHMAP6(FL, FR, FC, LFE, BL, BR), // 5.1
|
||||
MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), // 6.1
|
||||
MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1
|
||||
};
|
||||
|
||||
// The channel order was lavc/waveex, but differs from lavc for 5, 6 and 8
|
||||
// channels. 3 and 7 channels were likely undefined (no ALSA support).
|
||||
static const char *mplayer_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
[1] = "mono",
|
||||
[2] = "stereo",
|
||||
[4] = "4.0",
|
||||
[5] = "5.0(alsa)",
|
||||
[6] = "5.1(alsa)",
|
||||
[8] = "7.1(alsa)",
|
||||
};
|
||||
|
||||
// Returns true if speakers are mapped uniquely, and there's at least 1 channel.
|
||||
bool mp_chmap_is_valid(const struct mp_chmap *src)
|
||||
{
|
||||
bool mapped[MP_SPEAKER_ID_COUNT] = {0};
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int sp = src->speaker[n];
|
||||
if (sp >= MP_SPEAKER_ID_COUNT || mapped[sp])
|
||||
return false;
|
||||
mapped[sp] = true;
|
||||
}
|
||||
return src->num > 0;
|
||||
}
|
||||
|
||||
bool mp_chmap_is_empty(const struct mp_chmap *src)
|
||||
{
|
||||
return src->num == 0;
|
||||
}
|
||||
|
||||
// Return true if the channel map defines the number of the channels only, and
|
||||
// the channels have to meaning associated with them.
|
||||
bool mp_chmap_is_unknown(const struct mp_chmap *src)
|
||||
{
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int speaker = src->speaker[n];
|
||||
if (speaker >= MP_SPEAKER_ID_UNKNOWN0 &&
|
||||
speaker <= MP_SPEAKER_ID_UNKNOWN_LAST)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: empty channel maps compare as equal. Invalid ones can equal too.
|
||||
bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
if (a->num != b->num)
|
||||
return false;
|
||||
for (int n = 0; n < a->num; n++) {
|
||||
if (a->speaker[n] != b->speaker[n])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whether they use the same speakers (even if in different order).
|
||||
bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
struct mp_chmap t1 = *a, t2 = *b;
|
||||
mp_chmap_reorder_norm(&t1);
|
||||
mp_chmap_reorder_norm(&t2);
|
||||
return mp_chmap_equals(&t1, &t2);
|
||||
}
|
||||
|
||||
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b)
|
||||
{
|
||||
if (mp_chmap_equals(a, b))
|
||||
return true;
|
||||
if (a->num == b->num && (mp_chmap_is_unknown(a) || mp_chmap_is_unknown(b)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mp_chmap_is_stereo(const struct mp_chmap *src)
|
||||
{
|
||||
static const struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
|
||||
return mp_chmap_equals(src, &stereo);
|
||||
}
|
||||
|
||||
static int comp_uint8(const void *a, const void *b)
|
||||
{
|
||||
return *(const uint8_t *)a - *(const uint8_t *)b;
|
||||
}
|
||||
|
||||
// Reorder channels to normal order, with monotonically increasing speaker IDs.
|
||||
// We define this order as the same order used with waveex.
|
||||
void mp_chmap_reorder_norm(struct mp_chmap *map)
|
||||
{
|
||||
uint8_t *arr = &map->speaker[0];
|
||||
qsort(arr, map->num, 1, comp_uint8);
|
||||
}
|
||||
|
||||
// Set *dst to a standard layout with the given number of channels.
|
||||
// If the number of channels is invalid, an invalid map is set, and
|
||||
// mp_chmap_is_valid(dst) will return false.
|
||||
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels)
|
||||
{
|
||||
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
|
||||
*dst = (struct mp_chmap) {0};
|
||||
} else {
|
||||
*dst = default_layouts[num_channels];
|
||||
}
|
||||
}
|
||||
|
||||
// Try to do what mplayer/mplayer2/mpv did before channel layouts were
|
||||
// introduced, i.e. get the old default channel order.
|
||||
void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels)
|
||||
{
|
||||
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
|
||||
*dst = (struct mp_chmap) {0};
|
||||
} else {
|
||||
mp_chmap_from_str(dst, bstr0(mplayer_layouts[num_channels]));
|
||||
if (!dst->num)
|
||||
mp_chmap_from_channels(dst, num_channels);
|
||||
}
|
||||
}
|
||||
|
||||
// Set *dst to an unknown layout for the given numbers of channels.
|
||||
// If the number of channels is invalid, an invalid map is set, and
|
||||
// mp_chmap_is_valid(dst) will return false.
|
||||
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
|
||||
{
|
||||
if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
|
||||
*dst = (struct mp_chmap) {0};
|
||||
} else {
|
||||
dst->num = num_channels;
|
||||
for (int n = 0; n < dst->num; n++)
|
||||
dst->speaker[n] = MP_SPEAKER_ID_UNKNOWN0 + n;
|
||||
}
|
||||
}
|
||||
|
||||
// Return channel index of the given speaker, or -1.
|
||||
static int mp_chmap_find_speaker(const struct mp_chmap *map, int speaker)
|
||||
{
|
||||
for (int n = 0; n < map->num; n++) {
|
||||
if (map->speaker[n] == speaker)
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void mp_chmap_remove_speaker(struct mp_chmap *map, int speaker)
|
||||
{
|
||||
int index = mp_chmap_find_speaker(map, speaker);
|
||||
if (index >= 0) {
|
||||
for (int n = index; n < map->num - 1; n++)
|
||||
map->speaker[n] = map->speaker[n + 1];
|
||||
map->num--;
|
||||
}
|
||||
}
|
||||
|
||||
// Some decoders output additional, redundant channels, which are usually
|
||||
// useless and will mess up proper audio output channel handling.
|
||||
// map: channel map from which the channels should be removed
|
||||
// requested: if not NULL, and if it contains any of the "useless" channels,
|
||||
// don't remove them (this is for convenience)
|
||||
void mp_chmap_remove_useless_channels(struct mp_chmap *map,
|
||||
const struct mp_chmap *requested)
|
||||
{
|
||||
if (requested &&
|
||||
mp_chmap_find_speaker(requested, MP_SPEAKER_ID_DL) >= 0)
|
||||
return;
|
||||
|
||||
if (map->num > 2) {
|
||||
mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DL);
|
||||
mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DR);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
|
||||
// Warning: this ignores the order of the channels, and will return a channel
|
||||
// mask even if the order is different from libavcodec's.
|
||||
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
|
||||
{
|
||||
// lavc has no concept for unknown layouts yet, so pick a default
|
||||
struct mp_chmap t = *src;
|
||||
if (mp_chmap_is_unknown(&t))
|
||||
mp_chmap_from_channels(&t, t.num);
|
||||
uint64_t mask = 0;
|
||||
for (int n = 0; n < t.num; n++)
|
||||
mask |= 1ULL << t.speaker[n];
|
||||
return mask;
|
||||
}
|
||||
|
||||
// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
|
||||
// Returns 0 if the channel order doesn't match lavc's or if it's invalid.
|
||||
uint64_t mp_chmap_to_lavc(const struct mp_chmap *src)
|
||||
{
|
||||
if (!mp_chmap_is_lavc(src))
|
||||
return 0;
|
||||
return mp_chmap_to_lavc_unchecked(src);
|
||||
}
|
||||
|
||||
// Set channel map from the ffmpeg/libav channel layout as in
|
||||
// <libavutil/channel_layout.h>.
|
||||
// If the number of channels exceed MP_NUM_CHANNELS, set dst to empty.
|
||||
void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src)
|
||||
{
|
||||
dst->num = 0;
|
||||
for (int n = 0; n < 64; n++) {
|
||||
if (src & (1ULL << n)) {
|
||||
if (dst->num >= MP_NUM_CHANNELS) {
|
||||
dst->num = 0;
|
||||
return;
|
||||
}
|
||||
dst->speaker[dst->num] = n;
|
||||
dst->num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_chmap_is_lavc(const struct mp_chmap *src)
|
||||
{
|
||||
if (!mp_chmap_is_valid(src))
|
||||
return false;
|
||||
if (mp_chmap_is_unknown(src))
|
||||
return true;
|
||||
// lavc's channel layout is a bit mask, and channels are always ordered
|
||||
// from LSB to MSB speaker bits, so speaker IDs have to increase.
|
||||
assert(src->num > 0);
|
||||
for (int n = 1; n < src->num; n++) {
|
||||
if (src->speaker[n - 1] >= src->speaker[n])
|
||||
return false;
|
||||
}
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
if (src->speaker[n] >= 64)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
|
||||
{
|
||||
if (!mp_chmap_is_valid(map))
|
||||
return;
|
||||
uint64_t mask = mp_chmap_to_lavc_unchecked(map);
|
||||
mp_chmap_from_lavc(map, mask);
|
||||
}
|
||||
|
||||
// Get reordering array for from->to reordering. from->to must have the same set
|
||||
// of speakers (i.e. same number and speaker IDs, just different order). Then,
|
||||
// for each speaker n, dst[n] will be set such that:
|
||||
// to->speaker[dst[n]] = from->speaker[n]
|
||||
// (dst[n] gives the source channel for destination channel n)
|
||||
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
const struct mp_chmap *to)
|
||||
{
|
||||
assert(from->num == to->num);
|
||||
if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) {
|
||||
for (int n = 0; n < from->num; n++)
|
||||
dst[n] = n;
|
||||
return;
|
||||
}
|
||||
// Same set of speakers required
|
||||
assert(mp_chmap_equals_reordered(from, to));
|
||||
for (int n = 0; n < from->num; n++) {
|
||||
int src = from->speaker[n];
|
||||
dst[n] = -1;
|
||||
for (int i = 0; i < to->num; i++) {
|
||||
if (src == to->speaker[i]) {
|
||||
dst[n] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(dst[n] != -1);
|
||||
}
|
||||
for (int n = 0; n < from->num; n++)
|
||||
assert(to->speaker[dst[n]] == from->speaker[n]);
|
||||
}
|
||||
|
||||
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
|
||||
// order, return that, e.g. "3.0" instead of "fl-fr-fc".
|
||||
// Unassigned but valid speakers get names like "sp28".
|
||||
char *mp_chmap_to_str(const struct mp_chmap *src)
|
||||
{
|
||||
char *res = talloc_strdup(NULL, "");
|
||||
|
||||
if (mp_chmap_is_unknown(src))
|
||||
return talloc_asprintf_append_buffer(res, "unknown%d", src->num);
|
||||
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int sp = src->speaker[n];
|
||||
const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL;
|
||||
char buf[10];
|
||||
if (!s) {
|
||||
snprintf(buf, sizeof(buf), "sp%d", sp);
|
||||
s = buf;
|
||||
}
|
||||
res = talloc_asprintf_append_buffer(res, "%s%s", n > 0 ? "-" : "", s);
|
||||
}
|
||||
|
||||
// To standard layout name
|
||||
for (int n = 0; std_layout_names[n][0]; n++) {
|
||||
if (res && strcmp(res, std_layout_names[n][1]) == 0) {
|
||||
talloc_free(res);
|
||||
res = talloc_strdup(NULL, std_layout_names[n][0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// If src can be parsed as channel map (as produced by mp_chmap_to_str()),
|
||||
// return true and set *dst. Otherwise, return false and don't change *dst.
|
||||
// Note: call mp_chmap_is_valid() to test whether the returned map is valid
|
||||
// the map could be empty, or contain multiply mapped channels
|
||||
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
|
||||
{
|
||||
// Single number corresponds to mp_chmap_from_channels()
|
||||
if (src.len > 0) {
|
||||
bstr t = src;
|
||||
bool unknown = bstr_eatstart0(&t, "unknown");
|
||||
bstr rest;
|
||||
long long count = bstrtoll(t, &rest, 10);
|
||||
if (rest.len == 0) {
|
||||
struct mp_chmap res;
|
||||
if (unknown) {
|
||||
mp_chmap_set_unknown(&res, count);
|
||||
} else {
|
||||
mp_chmap_from_channels(&res, count);
|
||||
}
|
||||
if (mp_chmap_is_valid(&res)) {
|
||||
*dst = res;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From standard layout name
|
||||
for (int n = 0; std_layout_names[n][0]; n++) {
|
||||
if (bstr_equals0(src, std_layout_names[n][0])) {
|
||||
src = bstr0(std_layout_names[n][1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit speaker list (separated by "-")
|
||||
struct mp_chmap res = {0};
|
||||
while (src.len) {
|
||||
bstr s;
|
||||
bstr_split_tok(src, "-", &s, &src);
|
||||
int speaker = -1;
|
||||
for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
|
||||
const char *name = speaker_names[n][0];
|
||||
if (name && bstr_equals0(s, name)) {
|
||||
speaker = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (speaker < 0) {
|
||||
if (bstr_eatstart0(&s, "sp")) {
|
||||
long long sp = bstrtoll(s, &s, 0);
|
||||
if (s.len == 0 && sp >= 0 && sp < MP_SPEAKER_ID_COUNT)
|
||||
speaker = sp;
|
||||
}
|
||||
if (speaker < 0)
|
||||
return false;
|
||||
}
|
||||
if (res.num >= MP_NUM_CHANNELS)
|
||||
return false;
|
||||
res.speaker[res.num] = speaker;
|
||||
res.num++;
|
||||
}
|
||||
|
||||
*dst = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_chmap_print_help(int msgt, int msgl)
|
||||
{
|
||||
mp_msg(msgt, msgl, "Speakers:\n");
|
||||
for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
|
||||
if (speaker_names[n][0])
|
||||
mp_msg(msgt, msgl, " %-16s (%s)\n",
|
||||
speaker_names[n][0], speaker_names[n][1]);
|
||||
}
|
||||
mp_msg(msgt, msgl, "Standard layouts:\n");
|
||||
for (int n = 0; std_layout_names[n][0]; n++) {
|
||||
mp_msg(msgt, msgl, " %-16s (%s)\n",
|
||||
std_layout_names[n][0], std_layout_names[n][1]);
|
||||
}
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++)
|
||||
mp_msg(msgt, msgl, " unknown%d\n", n);
|
||||
}
|
132
audio/chmap.h
Normal file
132
audio/chmap.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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_CHMAP_H
|
||||
#define MP_CHMAP_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "core/bstr.h"
|
||||
|
||||
#define MP_NUM_CHANNELS 8
|
||||
|
||||
// Speaker a channel can be assigned to.
|
||||
// This corresponds to WAVEFORMATEXTENSIBLE channel mask bit indexes.
|
||||
// E.g. channel_mask = (1 << MP_SPEAKER_ID_FL) | ...
|
||||
enum {
|
||||
// Official WAVEFORMATEXTENSIBLE (shortened names)
|
||||
MP_SPEAKER_ID_FL = 0, // FRONT_LEFT
|
||||
MP_SPEAKER_ID_FR, // FRONT_RIGHT
|
||||
MP_SPEAKER_ID_FC, // FRONT_CENTER
|
||||
MP_SPEAKER_ID_LFE, // LOW_FREQUENCY
|
||||
MP_SPEAKER_ID_BL, // BACK_LEFT
|
||||
MP_SPEAKER_ID_BR, // BACK_RIGHT
|
||||
MP_SPEAKER_ID_FLC, // FRONT_LEFT_OF_CENTER
|
||||
MP_SPEAKER_ID_FRC, // FRONT_RIGHT_OF_CENTER
|
||||
MP_SPEAKER_ID_BC, // BACK_CENTER
|
||||
MP_SPEAKER_ID_SL, // SIDE_LEFT
|
||||
MP_SPEAKER_ID_SR, // SIDE_RIGHT
|
||||
MP_SPEAKER_ID_TC, // TOP_CENTER
|
||||
MP_SPEAKER_ID_TFL, // TOP_FRONT_LEFT
|
||||
MP_SPEAKER_ID_TFC, // TOP_FRONT_CENTER
|
||||
MP_SPEAKER_ID_TFR, // TOP_FRONT_RIGHT
|
||||
MP_SPEAKER_ID_TBL, // TOP_BACK_LEFT
|
||||
MP_SPEAKER_ID_TBC, // TOP_BACK_CENTER
|
||||
MP_SPEAKER_ID_TBR, // TOP_BACK_RIGHT
|
||||
// Inofficial/libav* extensions
|
||||
MP_SPEAKER_ID_DL = 29, // STEREO_LEFT (stereo downmix special speakers)
|
||||
MP_SPEAKER_ID_DR, // STEREO_RIGHT
|
||||
MP_SPEAKER_ID_WL, // WIDE_LEFT
|
||||
MP_SPEAKER_ID_WR, // WIDE_RIGHT
|
||||
MP_SPEAKER_ID_SDL, // SURROUND_DIRECT_LEFT
|
||||
MP_SPEAKER_ID_SDR, // SURROUND_DIRECT_RIGHT
|
||||
MP_SPEAKER_ID_LFE2, // LOW_FREQUENCY_2
|
||||
|
||||
// Special mpv-specific speaker entries reserved for channels which have no
|
||||
// known meaning.
|
||||
MP_SPEAKER_ID_UNKNOWN0 = 64,
|
||||
MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
|
||||
|
||||
// Including the unassigned IDs in between. This is not a valid ID anymore.
|
||||
MP_SPEAKER_ID_COUNT,
|
||||
};
|
||||
|
||||
struct mp_chmap {
|
||||
uint8_t num; // number of channels
|
||||
// Given a channel n, speaker[n] is the speaker ID driven by that channel.
|
||||
// Entries after speaker[num - 1] are undefined.
|
||||
uint8_t speaker[MP_NUM_CHANNELS];
|
||||
};
|
||||
|
||||
#define MP_SP(speaker) MP_SPEAKER_ID_ ## speaker
|
||||
|
||||
#define MP_CHMAP2(a, b) \
|
||||
{2, {MP_SP(a), MP_SP(b)}}
|
||||
#define MP_CHMAP3(a, b, c) \
|
||||
{3, {MP_SP(a), MP_SP(b), MP_SP(c)}}
|
||||
#define MP_CHMAP4(a, b, c, d) \
|
||||
{4, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d)}}
|
||||
#define MP_CHMAP5(a, b, c, d, e) \
|
||||
{5, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e)}}
|
||||
#define MP_CHMAP6(a, b, c, d, e, f) \
|
||||
{6, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f)}}
|
||||
#define MP_CHMAP7(a, b, c, d, e, f, g) \
|
||||
{7, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g)}}
|
||||
#define MP_CHMAP8(a, b, c, d, e, f, g, h) \
|
||||
{8, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h)}}
|
||||
|
||||
#define MP_CHMAP_INIT_MONO {1, {MP_SPEAKER_ID_FC}}
|
||||
#define MP_CHMAP_INIT_STEREO MP_CHMAP2(FL, FR)
|
||||
|
||||
bool mp_chmap_is_valid(const struct mp_chmap *src);
|
||||
bool mp_chmap_is_empty(const struct mp_chmap *src);
|
||||
bool mp_chmap_is_unknown(const struct mp_chmap *src);
|
||||
bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b);
|
||||
bool mp_chmap_is_stereo(const struct mp_chmap *src);
|
||||
|
||||
void mp_chmap_reorder_norm(struct mp_chmap *map);
|
||||
|
||||
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels);
|
||||
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels);
|
||||
void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels);
|
||||
|
||||
void mp_chmap_remove_useless_channels(struct mp_chmap *map,
|
||||
const struct mp_chmap *requested);
|
||||
|
||||
uint64_t mp_chmap_to_lavc(const struct mp_chmap *src);
|
||||
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src);
|
||||
void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src);
|
||||
|
||||
bool mp_chmap_is_lavc(const struct mp_chmap *src);
|
||||
void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
|
||||
|
||||
void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
|
||||
const struct mp_chmap *to);
|
||||
|
||||
char *mp_chmap_to_str(const struct mp_chmap *src);
|
||||
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
|
||||
void mp_chmap_print_help(int msgt, int msgl);
|
||||
|
||||
// Use these to avoid chaos in case lavc's definition should diverge from MS.
|
||||
#define mp_chmap_to_waveext mp_chmap_to_lavc
|
||||
#define mp_chmap_from_waveext mp_chmap_from_lavc
|
||||
#define mp_chmap_is_waveext mp_chmap_is_lavc
|
||||
#define mp_chmap_reorder_to_waveext mp_chmap_reorder_to_lavc
|
||||
|
||||
#endif
|
210
audio/chmap_sel.c
Normal file
210
audio/chmap_sel.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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 "chmap_sel.h"
|
||||
|
||||
// 5.1 and 5.1(side) are practically the same. It doesn't make much sense to
|
||||
// reject either of them.
|
||||
static const int replaceable_speakers[][2] = {
|
||||
{MP_SPEAKER_ID_SL, MP_SPEAKER_ID_BL},
|
||||
{MP_SPEAKER_ID_SR, MP_SPEAKER_ID_BR},
|
||||
{-1},
|
||||
};
|
||||
|
||||
// list[] contains a list of speaker pairs, with each pair indicating how
|
||||
// a speaker can be swapped for another speaker. Try to replace speakers from
|
||||
// the left of the list with the ones on the right, or the other way around.
|
||||
static bool replace_speakers(struct mp_chmap *map, const int list[][2])
|
||||
{
|
||||
if (!mp_chmap_is_valid(map))
|
||||
return false;
|
||||
for (int dir = 0; dir < 2; dir++) {
|
||||
int from = dir ? 0 : 1;
|
||||
int to = dir ? 1 : 0;
|
||||
bool replaced = false;
|
||||
struct mp_chmap t = *map;
|
||||
for (int n = 0; n < t.num; n++) {
|
||||
for (int i = 0; list[i][0] != -1; i++) {
|
||||
if (t.speaker[n] == list[i][from]) {
|
||||
t.speaker[n] = list[i][to];
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replaced && mp_chmap_is_valid(&t)) {
|
||||
*map = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow all channel layouts that can be expressed with mp_chmap.
|
||||
// (By default, all layouts are rejected.)
|
||||
void mp_chmap_sel_add_any(struct mp_chmap_sel *s)
|
||||
{
|
||||
s->allow_any = true;
|
||||
}
|
||||
|
||||
// Allow all waveext formats, and force waveext channel order.
|
||||
void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s)
|
||||
{
|
||||
s->allow_waveext = true;
|
||||
}
|
||||
|
||||
void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
|
||||
{
|
||||
for (int n = 0; n < MP_NUM_CHANNELS; n++) {
|
||||
struct mp_chmap t;
|
||||
mp_chmap_from_channels_alsa(&t, n);
|
||||
if (t.num)
|
||||
mp_chmap_sel_add_map(s, &t);
|
||||
}
|
||||
}
|
||||
|
||||
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
// Add a channel map that should be allowed.
|
||||
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
|
||||
{
|
||||
assert(s->num_chmaps < ARRAY_LEN(s->chmaps));
|
||||
if (mp_chmap_is_valid(map))
|
||||
s->chmaps[s->num_chmaps++] = *map;
|
||||
}
|
||||
|
||||
// Allow all waveext formats in default order.
|
||||
void mp_chmap_sel_add_waveext_def(struct mp_chmap_sel *s)
|
||||
{
|
||||
for (int n = 1; n < MP_NUM_CHANNELS; n++) {
|
||||
struct mp_chmap map;
|
||||
mp_chmap_from_channels(&map, n);
|
||||
mp_chmap_sel_add_map(s, &map);
|
||||
}
|
||||
}
|
||||
|
||||
// Whitelist a speaker (MP_SPEAKER_ID_...). All layouts that contain whitelisted
|
||||
// speakers are allowed.
|
||||
void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id)
|
||||
{
|
||||
assert(id >= 0 && id < MP_SPEAKER_ID_COUNT);
|
||||
s->speakers[id] = true;
|
||||
}
|
||||
|
||||
static bool test_speakers(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
for (int n = 0; n < map->num; n++) {
|
||||
if (!s->speakers[map->speaker[n]])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_maps(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
for (int n = 0; n < s->num_chmaps; n++) {
|
||||
if (mp_chmap_equals_reordered(&s->chmaps[n], map)) {
|
||||
*map = s->chmaps[n];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool test_waveext(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
if (s->allow_waveext) {
|
||||
struct mp_chmap t = *map;
|
||||
mp_chmap_reorder_to_waveext(&t);
|
||||
if (mp_chmap_is_waveext(&t)) {
|
||||
*map = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool test_layout(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
if (!mp_chmap_is_valid(map))
|
||||
return false;
|
||||
|
||||
return s->allow_any || test_waveext(s, map) || test_speakers(s, map) ||
|
||||
test_maps(s, map);
|
||||
}
|
||||
|
||||
// Determine which channel map to use given a source channel map, and various
|
||||
// parameters restricting possible choices. If the map doesn't match, select
|
||||
// a fallback and set it.
|
||||
// If no matching layout is found, a reordered layout may be returned.
|
||||
// If that is not possible, a fallback for up/downmixing may be returned.
|
||||
// If no choice is possible, set *map to empty.
|
||||
bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
|
||||
{
|
||||
if (test_layout(s, map))
|
||||
return true;
|
||||
if (mp_chmap_is_unknown(map)) {
|
||||
struct mp_chmap t = {0};
|
||||
if (mp_chmap_sel_get_def(s, &t, map->num) && test_layout(s, &t)) {
|
||||
*map = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 5.1 <-> 5.1(side)
|
||||
if (replace_speakers(map, replaceable_speakers) && test_layout(s, map))
|
||||
return true;
|
||||
// Fallback to mono/stereo as last resort
|
||||
if (map->num == 1) {
|
||||
*map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
|
||||
} else if (map->num >= 2) {
|
||||
*map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
|
||||
}
|
||||
if (test_layout(s, map))
|
||||
return true;
|
||||
*map = (struct mp_chmap) {0};
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set map to a default layout with num channels. Used for audio APIs that
|
||||
// return a channel count as part of format negotiation, but give no
|
||||
// information about the channel layout.
|
||||
// If the channel count is correct, do nothing and leave *map untouched.
|
||||
bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
|
||||
int num)
|
||||
{
|
||||
if (map->num != num) {
|
||||
*map = (struct mp_chmap) {0};
|
||||
// Set of speakers or waveext might allow it.
|
||||
struct mp_chmap t;
|
||||
mp_chmap_from_channels(&t, num);
|
||||
mp_chmap_reorder_to_waveext(&t);
|
||||
if (test_layout(s, &t)) {
|
||||
*map = t;
|
||||
} else {
|
||||
for (int n = 0; n < s->num_chmaps; n++) {
|
||||
if (s->chmaps[n].num == num) {
|
||||
*map = s->chmaps[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map->num > 0;
|
||||
}
|
43
audio/chmap_sel.h
Normal file
43
audio/chmap_sel.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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_CHMAP_SEL_H
|
||||
#define MP_CHMAP_SEL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "chmap.h"
|
||||
|
||||
struct mp_chmap_sel {
|
||||
// should be considered opaque
|
||||
bool allow_any, allow_waveext;
|
||||
bool speakers[MP_SPEAKER_ID_COUNT];
|
||||
struct mp_chmap chmaps[20];
|
||||
int num_chmaps;
|
||||
};
|
||||
|
||||
void mp_chmap_sel_add_any(struct mp_chmap_sel *s);
|
||||
void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s);
|
||||
void mp_chmap_sel_add_waveext_def(struct mp_chmap_sel *s);
|
||||
void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s);
|
||||
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map);
|
||||
void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id);
|
||||
bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map);
|
||||
bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
|
||||
int num);
|
||||
|
||||
#endif
|
@ -48,10 +48,4 @@ extern const ad_functions_t * const mpcodecs_ad_drivers[];
|
||||
// fallback if ADCTRL_SKIP not implemented: ds_fill_buffer(sh_audio->ds);
|
||||
#define ADCTRL_SKIP_FRAME 2 // skip block/frame, called while seeking
|
||||
|
||||
// fallback if ADCTRL_QUERY_FORMAT not implemented: sh_audio->sample_format
|
||||
#define ADCTRL_QUERY_FORMAT 3 // test for availabilty of a format
|
||||
|
||||
// fallback: use hw mixer in libao
|
||||
#define ADCTRL_SET_VOLUME 4 // not used at the moment
|
||||
|
||||
#endif /* MPLAYER_AD_H */
|
||||
|
@ -32,9 +32,11 @@
|
||||
#include "core/codecs.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/options.h"
|
||||
#include "core/av_opts.h"
|
||||
|
||||
#include "ad_internal.h"
|
||||
#include "audio/reorder_ch.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
#include "compat/mpbswap.h"
|
||||
#include "compat/libav.h"
|
||||
@ -49,6 +51,16 @@ struct priv {
|
||||
int output_left;
|
||||
int unitsize;
|
||||
int previous_data_left; // input demuxer packet data
|
||||
bool force_channel_map;
|
||||
};
|
||||
|
||||
#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_STRING("o", ad_lavc_param.avopt, 0),
|
||||
{0}
|
||||
};
|
||||
|
||||
struct pcm_map
|
||||
@ -144,17 +156,9 @@ static int preinit(sh_audio_t *sh)
|
||||
static int setup_format(sh_audio_t *sh_audio,
|
||||
const AVCodecContext *lavc_context)
|
||||
{
|
||||
int sample_format = sh_audio->sample_format;
|
||||
switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
|
||||
case AV_SAMPLE_FMT_U8: sample_format = AF_FORMAT_U8; break;
|
||||
case AV_SAMPLE_FMT_S16: sample_format = AF_FORMAT_S16_NE; break;
|
||||
case AV_SAMPLE_FMT_S32: sample_format = AF_FORMAT_S32_NE; break;
|
||||
case AV_SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break;
|
||||
default:
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n");
|
||||
sample_format = AF_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
struct priv *priv = sh_audio->context;
|
||||
int sample_format =
|
||||
af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
|
||||
bool broken_srate = false;
|
||||
int samplerate = lavc_context->sample_rate;
|
||||
int container_samplerate = sh_audio->container_out_samplerate;
|
||||
@ -166,10 +170,20 @@ static int setup_format(sh_audio_t *sh_audio,
|
||||
else if (container_samplerate)
|
||||
samplerate = container_samplerate;
|
||||
|
||||
if (lavc_context->channels != sh_audio->channels ||
|
||||
struct mp_chmap lavc_chmap;
|
||||
mp_chmap_from_lavc(&lavc_chmap, lavc_context->channel_layout);
|
||||
// No channel layout or layout disagrees with channel count
|
||||
if (lavc_chmap.num != lavc_context->channels)
|
||||
mp_chmap_from_channels(&lavc_chmap, lavc_context->channels);
|
||||
if (priv->force_channel_map) {
|
||||
if (lavc_chmap.num == sh_audio->channels.num)
|
||||
lavc_chmap = sh_audio->channels;
|
||||
}
|
||||
|
||||
if (!mp_chmap_equals(&lavc_chmap, &sh_audio->channels) ||
|
||||
samplerate != sh_audio->samplerate ||
|
||||
sample_format != sh_audio->sample_format) {
|
||||
sh_audio->channels = lavc_context->channels;
|
||||
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;
|
||||
@ -198,41 +212,59 @@ static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
|
||||
|
||||
static int init(sh_audio_t *sh_audio, const char *decoder)
|
||||
{
|
||||
struct MPOpts *opts = sh_audio->opts;
|
||||
struct MPOpts *mpopts = sh_audio->opts;
|
||||
struct ad_lavc_param *opts = &mpopts->ad_lavc_param;
|
||||
AVCodecContext *lavc_context;
|
||||
AVCodec *lavc_codec;
|
||||
|
||||
struct priv *ctx = talloc_zero(NULL, struct priv);
|
||||
sh_audio->context = ctx;
|
||||
|
||||
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
|
||||
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_audio->format, 0);
|
||||
ctx->force_channel_map = true;
|
||||
}
|
||||
|
||||
lavc_codec = avcodec_find_decoder_by_name(decoder);
|
||||
if (!lavc_codec) {
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"Cannot find codec '%s' in libavcodec...\n", decoder);
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct priv *ctx = talloc_zero(NULL, struct priv);
|
||||
sh_audio->context = ctx;
|
||||
lavc_context = avcodec_alloc_context3(lavc_codec);
|
||||
ctx->avctx = lavc_context;
|
||||
ctx->avframe = avcodec_alloc_frame();
|
||||
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
lavc_context->codec_id = lavc_codec->id;
|
||||
|
||||
lavc_context->request_channels = opts->audio_output_channels;
|
||||
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);
|
||||
}
|
||||
|
||||
// Always try to set - option only exists for AC3 at the moment
|
||||
av_opt_set_double(lavc_context, "drc_scale", opts->drc_level,
|
||||
av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
|
||||
AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
if (opts->avopt) {
|
||||
if (parse_avopts(lavc_context, opts->avopt) < 0) {
|
||||
mp_msg(MSGT_DECVIDEO, MSGL_ERR,
|
||||
"ad_lavc: setting AVOptions '%s' failed.\n", opts->avopt);
|
||||
uninit(sh_audio);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (sh_audio->wf)
|
||||
set_from_wf(lavc_context, sh_audio->wf);
|
||||
@ -279,13 +311,9 @@ static int init(sh_audio_t *sh_audio, const char *decoder)
|
||||
if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
|
||||
sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec;
|
||||
|
||||
switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
@ -442,13 +470,6 @@ static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
|
||||
memcpy(buf, priv->output, size);
|
||||
priv->output += size;
|
||||
priv->output_left -= size;
|
||||
if (avctx->channels >= 5) {
|
||||
int samplesize = av_get_bytes_per_sample(avctx->sample_fmt);
|
||||
reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
avctx->channels,
|
||||
size / samplesize, samplesize);
|
||||
}
|
||||
if (len < 0)
|
||||
len = size;
|
||||
else
|
||||
|
@ -358,7 +358,7 @@ static int init(sh_audio_t *sh, const char *decoder)
|
||||
con->mean_count = 0;
|
||||
#endif
|
||||
con->vbr = (finfo.vbr != MPG123_CBR);
|
||||
sh->channels = channels;
|
||||
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.
|
||||
|
@ -148,19 +148,20 @@ static int init(sh_audio_t *sh, const char *decoder)
|
||||
}
|
||||
sh->ds->buffer_pos -= in_size;
|
||||
|
||||
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;
|
||||
sh->channels = 2;
|
||||
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;
|
||||
sh->channels = 2;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
case AV_CODEC_ID_DTS:
|
||||
@ -175,13 +176,13 @@ static int init(sh_audio_t *sh, const char *decoder)
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = 192000; // DTS core require 48000
|
||||
sh->channels = 2*4;
|
||||
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;
|
||||
sh->channels = 2;
|
||||
num_channels = 2;
|
||||
sh->i_bps = bps;
|
||||
}
|
||||
break;
|
||||
@ -189,26 +190,28 @@ static int init(sh_audio_t *sh, const char *decoder)
|
||||
spdif_ctx->iec61937_packet_size = 24576;
|
||||
sh->sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sh->samplerate = 192000;
|
||||
sh->channels = 2;
|
||||
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;
|
||||
sh->channels = 2;
|
||||
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;
|
||||
sh->channels = 8;
|
||||
num_channels = 8;
|
||||
sh->i_bps = bps;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (num_channels)
|
||||
mp_chmap_from_channels(&sh->channels, num_channels);
|
||||
|
||||
return 1;
|
||||
|
||||
|
@ -41,22 +41,14 @@
|
||||
|
||||
int fakemono = 0;
|
||||
|
||||
struct af_cfg af_cfg = {1, NULL}; // Configuration for audio filters
|
||||
struct af_cfg af_cfg = {0}; // Configuration for audio filters
|
||||
|
||||
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
|
||||
{
|
||||
assert(!sh_audio->initialized);
|
||||
resync_audio_stream(sh_audio);
|
||||
sh_audio->samplesize = 2;
|
||||
sh_audio->sample_format = AF_FORMAT_S16_NE;
|
||||
if ((af_cfg.force & AF_INIT_FORMAT_MASK) == AF_INIT_FLOAT) {
|
||||
int fmt = AF_FORMAT_FLOAT_NE;
|
||||
if (sh_audio->ad_driver->control(sh_audio, ADCTRL_QUERY_FORMAT,
|
||||
&fmt) == CONTROL_TRUE) {
|
||||
sh_audio->sample_format = fmt;
|
||||
sh_audio->samplesize = 4;
|
||||
}
|
||||
}
|
||||
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");
|
||||
@ -94,7 +86,7 @@ static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
|
||||
|
||||
sh_audio->initialized = 1;
|
||||
|
||||
if (!sh_audio->channels || !sh_audio->samplerate) {
|
||||
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
|
||||
@ -102,7 +94,7 @@ static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
|
||||
}
|
||||
|
||||
if (!sh_audio->o_bps)
|
||||
sh_audio->o_bps = sh_audio->channels * sh_audio->samplerate
|
||||
sh_audio->o_bps = sh_audio->channels.num * sh_audio->samplerate
|
||||
* sh_audio->samplesize;
|
||||
return 1;
|
||||
}
|
||||
@ -168,14 +160,14 @@ int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders)
|
||||
sh_audio->gsh->decoder_desc);
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V,
|
||||
"AUDIO: %d Hz, %d ch, %s, %3.1f kbit/%3.2f%% (ratio: %d->%d)\n",
|
||||
sh_audio->samplerate, sh_audio->channels,
|
||||
sh_audio->samplerate, sh_audio->channels.num,
|
||||
af_fmt2str_short(sh_audio->sample_format),
|
||||
sh_audio->i_bps * 8 * 0.001,
|
||||
((float) sh_audio->i_bps / sh_audio->o_bps) * 100.0,
|
||||
sh_audio->i_bps, sh_audio->o_bps);
|
||||
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);
|
||||
sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels.num);
|
||||
} else {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
|
||||
"Failed to initialize an audio decoder for codec '%s'.\n",
|
||||
@ -191,7 +183,7 @@ void uninit_audio(sh_audio_t *sh_audio)
|
||||
if (sh_audio->afilter) {
|
||||
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
|
||||
af_uninit(sh_audio->afilter);
|
||||
free(sh_audio->afilter);
|
||||
af_destroy(sh_audio->afilter);
|
||||
sh_audio->afilter = NULL;
|
||||
}
|
||||
if (sh_audio->initialized) {
|
||||
@ -207,43 +199,41 @@ void uninit_audio(sh_audio_t *sh_audio)
|
||||
|
||||
|
||||
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
|
||||
int *out_samplerate, int *out_channels, int *out_format)
|
||||
int *out_samplerate, struct mp_chmap *out_channels,
|
||||
int *out_format)
|
||||
{
|
||||
struct af_stream *afs = sh_audio->afilter;
|
||||
if (!afs) {
|
||||
afs = calloc(1, sizeof(struct af_stream));
|
||||
afs->opts = sh_audio->opts;
|
||||
}
|
||||
if (!afs)
|
||||
afs = af_new(sh_audio->opts);
|
||||
// input format: same as codec's output format:
|
||||
afs->input.rate = in_samplerate;
|
||||
afs->input.nch = sh_audio->channels;
|
||||
afs->input.format = sh_audio->sample_format;
|
||||
af_fix_parameters(&(afs->input));
|
||||
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;
|
||||
afs->output.nch = *out_channels;
|
||||
afs->output.format = *out_format;
|
||||
af_fix_parameters(&(afs->output));
|
||||
afs->output.rate = *out_samplerate;
|
||||
mp_audio_set_channels(&afs->output, out_channels);
|
||||
mp_audio_set_format(&afs->output, *out_format);
|
||||
|
||||
// filter config:
|
||||
memcpy(&afs->cfg, &af_cfg, sizeof(struct af_cfg));
|
||||
|
||||
char *s_from = mp_audio_config_to_str(&afs->input);
|
||||
char *s_to = mp_audio_config_to_str(&afs->output);
|
||||
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
|
||||
"Building audio filter chain for %dHz/%dch/%s -> %dHz/%dch/%s...\n",
|
||||
afs->input.rate, afs->input.nch,
|
||||
af_fmt2str_short(afs->input.format), afs->output.rate,
|
||||
afs->output.nch, af_fmt2str_short(afs->output.format));
|
||||
"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 (0 != af_init(afs)) {
|
||||
sh_audio->afilter = NULL;
|
||||
free(afs);
|
||||
af_destroy(afs);
|
||||
return 0; // failed :(
|
||||
}
|
||||
|
||||
*out_samplerate = afs->output.rate;
|
||||
*out_channels = afs->output.nch;
|
||||
*out_channels = afs->output.channels;
|
||||
*out_format = afs->output.format;
|
||||
|
||||
// ok!
|
||||
@ -270,7 +260,7 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
|
||||
|
||||
// Decode more bytes if needed
|
||||
int old_samplerate = sh->samplerate;
|
||||
int old_channels = sh->channels;
|
||||
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;
|
||||
@ -278,7 +268,7 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int 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
|
||||
|| sh->channels != old_channels
|
||||
|| !mp_chmap_equals(&sh->channels, &old_channels)
|
||||
|| sh->sample_format != old_sample_format;
|
||||
if (ret <= 0 || format_change) {
|
||||
error = format_change ? -2 : -1;
|
||||
@ -294,10 +284,10 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
|
||||
.audio = sh->a_buffer,
|
||||
.len = len,
|
||||
.rate = sh->samplerate,
|
||||
.nch = sh->channels,
|
||||
.format = sh->sample_format
|
||||
};
|
||||
af_fix_parameters(&filter_input);
|
||||
mp_audio_set_format(&filter_input, sh->sample_format);
|
||||
mp_audio_set_channels(&filter_input, &sh->channels);
|
||||
|
||||
struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
|
||||
if (!filter_output)
|
||||
return -1;
|
||||
@ -325,7 +315,7 @@ 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 bytes
|
||||
int unitsize = sh_audio->channels * sh_audio->samplesize * 16;
|
||||
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
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef MPLAYER_DEC_AUDIO_H
|
||||
#define MPLAYER_DEC_AUDIO_H
|
||||
|
||||
#include "audio/chmap.h"
|
||||
#include "demux/stheader.h"
|
||||
|
||||
struct bstr;
|
||||
@ -33,6 +34,7 @@ void skip_audio_frame(sh_audio_t *sh_audio);
|
||||
void uninit_audio(sh_audio_t *sh_audio);
|
||||
|
||||
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
|
||||
int *out_samplerate, int *out_channels, int *out_format);
|
||||
int *out_samplerate, struct mp_chmap *out_channels,
|
||||
int *out_format);
|
||||
|
||||
#endif /* MPLAYER_DEC_AUDIO_H */
|
||||
|
1058
audio/filter/af.c
1058
audio/filter/af.c
File diff suppressed because it is too large
Load Diff
@ -20,31 +20,21 @@
|
||||
#define MPLAYER_AF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "core/options.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/audio.h"
|
||||
#include "control.h"
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
struct af_instance;
|
||||
|
||||
// Number of channels
|
||||
#ifndef AF_NCH
|
||||
#define AF_NCH 8
|
||||
#endif
|
||||
|
||||
// Audio data chunk
|
||||
struct mp_audio {
|
||||
void *audio; // data buffer
|
||||
int len; // buffer length
|
||||
int rate; // sample rate
|
||||
int nch; // number of channels
|
||||
int format; // format
|
||||
int bps; // bytes per sample
|
||||
};
|
||||
|
||||
#define AF_NCH MP_NUM_CHANNELS
|
||||
|
||||
// Flags used for defining the behavior of an audio filter
|
||||
#define AF_FLAGS_REENTRANT 0x00000000
|
||||
@ -59,6 +49,7 @@ struct af_info {
|
||||
const char *comment;
|
||||
const int flags;
|
||||
int (*open)(struct af_instance *vf);
|
||||
bool (*test_conversion)(int src_format, int dst_format);
|
||||
};
|
||||
|
||||
// Linked list of audio filters
|
||||
@ -75,29 +66,11 @@ struct af_instance {
|
||||
* corresponding output */
|
||||
double mul; /* length multiplier: how much does this instance change
|
||||
the length of the buffer. */
|
||||
bool auto_inserted; // inserted by af.c, such as conversion filters
|
||||
};
|
||||
|
||||
// Initialization flags
|
||||
extern int *af_cpu_speed;
|
||||
|
||||
#define AF_INIT_AUTO 0x00000000
|
||||
#define AF_INIT_SLOW 0x00000001
|
||||
#define AF_INIT_FAST 0x00000002
|
||||
#define AF_INIT_FORCE 0x00000003
|
||||
#define AF_INIT_TYPE_MASK 0x00000003
|
||||
|
||||
#define AF_INIT_INT 0x00000000
|
||||
#define AF_INIT_FLOAT 0x00000004
|
||||
#define AF_INIT_FORMAT_MASK 0x00000004
|
||||
|
||||
// Default init type
|
||||
#ifndef AF_INIT_TYPE
|
||||
#define AF_INIT_TYPE (af_cpu_speed ? *af_cpu_speed : AF_INIT_SLOW)
|
||||
#endif
|
||||
|
||||
// Configuration switches
|
||||
struct af_cfg {
|
||||
int force; // Initialization type
|
||||
char **list; /* list of names of filters that are added to filter
|
||||
list during first initialization of stream */
|
||||
};
|
||||
@ -107,9 +80,12 @@ struct af_stream {
|
||||
// The first and last filter in the list
|
||||
struct af_instance *first;
|
||||
struct af_instance *last;
|
||||
// Storage for input and output data formats
|
||||
// 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;
|
||||
// Configuration for this stream
|
||||
struct af_cfg cfg;
|
||||
struct MPOpts *opts;
|
||||
@ -139,6 +115,9 @@ struct af_stream {
|
||||
* \param s filter chain
|
||||
*/
|
||||
|
||||
struct af_stream *af_new(struct MPOpts *opts);
|
||||
void af_destroy(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief Initialize the stream "s".
|
||||
* \return 0 on success, -1 on failure
|
||||
@ -161,10 +140,10 @@ void af_uninit(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief Reinit the filter list from the given filter on downwards
|
||||
* \param Filter instance to begin the reinit from
|
||||
* See af.c.
|
||||
* \return AF_OK on success or AF_ERROR on failure
|
||||
*/
|
||||
int af_reinit(struct af_stream *s, struct af_instance *af);
|
||||
int af_reinit(struct af_stream *s);
|
||||
|
||||
/**
|
||||
* \brief This function adds the filter "name" to the stream s.
|
||||
@ -306,23 +285,13 @@ float af_softclip(float a);
|
||||
/** Print a list of all available audio filters */
|
||||
void af_help(void);
|
||||
|
||||
/**
|
||||
* \brief fill the missing parameters in the struct mp_audio structure
|
||||
* \param data structure to fill
|
||||
* \ingroup af_filter
|
||||
*
|
||||
* Currently only sets bps based on format
|
||||
*/
|
||||
void af_fix_parameters(struct mp_audio *data);
|
||||
|
||||
/** 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)
|
||||
((a->data->len < af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
|
||||
|
||||
/* Some other useful macro definitions*/
|
||||
#ifndef min
|
||||
|
@ -105,9 +105,8 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
|
||||
format = ((struct mp_audio*)arg)->format;
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = 2; // bs2b is useful only for 2ch audio
|
||||
af->data->bps = ((struct mp_audio*)arg)->bps;
|
||||
af->data->format = format;
|
||||
mp_audio_set_num_channels(af->data, 2); // bs2b is useful only for 2ch audio
|
||||
mp_audio_set_format(af->data, format);
|
||||
|
||||
/* check for formats supported by libbs2b
|
||||
and assign corresponding handlers */
|
||||
@ -162,8 +161,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
break;
|
||||
default:
|
||||
af->play = play_f;
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -48,9 +48,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;
|
||||
af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
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);
|
||||
}
|
||||
|
@ -164,8 +164,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
}
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->format = ((struct mp_audio*)arg)->format;
|
||||
af->data->bps = ((struct mp_audio*)arg)->bps;
|
||||
mp_audio_set_format(af->data, ((struct mp_audio*)arg)->format);
|
||||
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
|
||||
return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch);
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
@ -194,54 +193,20 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
}
|
||||
}
|
||||
|
||||
if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch))
|
||||
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
|
||||
|
||||
// Sanity check
|
||||
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of output channels must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
af->data->nch=((int*)arg)[0];
|
||||
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;
|
||||
case AF_CONTROL_CHANNELS | AF_CONTROL_GET:
|
||||
*(int*)arg = af->data->nch;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
int* route = ((af_control_ext_t*)arg)->arg;
|
||||
s->route[ch][FR] = route[FR];
|
||||
s->route[ch][TO] = route[TO];
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
int* route = ((af_control_ext_t*)arg)->arg;
|
||||
route[FR] = s->route[ch][FR];
|
||||
route[TO] = s->route[ch][TO];
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET:
|
||||
s->nr = *(int*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET:
|
||||
*(int*)arg = s->nr;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET:
|
||||
s->router = *(int*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET:
|
||||
*(int*)arg = s->router;
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
@ -277,7 +242,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
// Set output data
|
||||
c->audio = l->audio;
|
||||
c->len = c->len / c->nch * l->nch;
|
||||
c->nch = l->nch;
|
||||
mp_audio_set_channels(c, &l->channels);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
@ -52,10 +52,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
for(i=0;i<af->data->nch;i++)
|
||||
free(s->q[i]);
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
af->data->format = ((struct mp_audio*)arg)->format;
|
||||
af->data->bps = ((struct mp_audio*)arg)->bps;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
|
||||
// Allocate new delay queues
|
||||
for(i=0;i<af->data->nch;i++){
|
||||
|
@ -87,15 +87,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
|
||||
if(((struct mp_audio*)arg)->format == (AF_FORMAT_S16_NE)){
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
}else{
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
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:{
|
||||
|
@ -29,8 +29,8 @@
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
memcpy(af->data,(struct mp_audio*)arg,sizeof(struct mp_audio));
|
||||
case AF_CONTROL_REINIT: ;
|
||||
*af->data = *(struct mp_audio*)arg;
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
|
||||
af->data->rate,af->data->nch,af_fmt2str_short(af->data->format));
|
||||
return AF_OK;
|
||||
|
@ -96,10 +96,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
|
||||
// Calculate number of active filters
|
||||
s->K=KM;
|
||||
@ -150,30 +148,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET:{
|
||||
float* gain = ((af_control_ext_t*)arg)->arg;
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
int k;
|
||||
if(ch >= AF_NCH || ch < 0)
|
||||
return AF_ERROR;
|
||||
|
||||
for(k = 0 ; k<KM ; k++)
|
||||
s->g[ch][k] = pow(10.0,clamp(gain[k],G_MIN,G_MAX)/20.0)-1.0;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_GET:{
|
||||
float* gain = ((af_control_ext_t*)arg)->arg;
|
||||
int ch = ((af_control_ext_t*)arg)->ch;
|
||||
int k;
|
||||
if(ch >= AF_NCH || ch < 0)
|
||||
return AF_ERROR;
|
||||
|
||||
for(k = 0 ; k<KM ; k++)
|
||||
gain[k] = log10(s->g[ch][k]+1.0) * 20.0;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
@ -85,10 +85,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
close(s->fd);
|
||||
|
||||
// Accept only int16_t as input format (which sucks)
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
|
||||
|
||||
// If buffer length isn't set, set it to the default value
|
||||
if(s->sz == 0)
|
||||
|
@ -47,17 +47,14 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = 2;
|
||||
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
if (af->data->format == AF_FORMAT_FLOAT_NE)
|
||||
{
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
af->play = play_float;
|
||||
}// else
|
||||
{
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
|
||||
af->play = play_s16;
|
||||
}
|
||||
|
||||
@ -69,12 +66,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
s->mul = f;
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_ES_MUL | AF_CONTROL_SET:
|
||||
s->mul = *(float*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_ES_MUL | AF_CONTROL_GET:
|
||||
*(float*)arg = s->mul;
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
146
audio/filter/af_force.c
Normal file
146
audio/filter/af_force.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 "core/m_config.h"
|
||||
#include "core/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;
|
||||
|
||||
struct mp_audio data;
|
||||
struct mp_audio temp;
|
||||
};
|
||||
|
||||
static const struct priv defaults = {
|
||||
.in_format = AF_FORMAT_UNKNOWN,
|
||||
.out_format = AF_FORMAT_UNKNOWN,
|
||||
};
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
static const struct m_option options[] = {
|
||||
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),
|
||||
{0}
|
||||
};
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
struct priv *priv = af->setup;
|
||||
|
||||
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 format are incompatible.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
}
|
||||
case AF_CONTROL_COMMAND_LINE: {
|
||||
if (m_config_parse_suboptions(priv->config, "af_force", (char *)arg) < 0)
|
||||
return AF_ERROR;
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
struct priv *priv = af->setup;
|
||||
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)
|
||||
{
|
||||
talloc_free(af->setup);
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
af->control = control;
|
||||
af->uninit = uninit;
|
||||
af->play = play;
|
||||
af->mul = 1;
|
||||
struct priv *priv = talloc(NULL, struct priv);
|
||||
af->setup = priv;
|
||||
*priv = defaults;
|
||||
priv->config = m_config_simple(priv);
|
||||
talloc_steal(priv, priv->config);
|
||||
m_config_register_options(priv->config, options);
|
||||
af->data = &priv->data;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
struct af_info af_info_force = {
|
||||
"Force audio format",
|
||||
"force",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
af_open
|
||||
};
|
@ -75,6 +75,14 @@ static int check_format(int format)
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
// This is the fallback conversion filter, so this filter is always
|
||||
// inserted on format mismatches if no other filter can handle it.
|
||||
// Initializing the filter might still fail.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
@ -86,8 +94,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
int supported_ac3 = 0;
|
||||
|
||||
// Make sure this filter isn't redundant
|
||||
if(af->data->format == data->format &&
|
||||
af->data->bps == data->bps)
|
||||
if(af->data->format == data->format)
|
||||
return AF_DETACH;
|
||||
|
||||
// A bit complex because we can convert AC3
|
||||
@ -113,7 +120,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
buf1, buf2);
|
||||
|
||||
af->data->rate = data->rate;
|
||||
af->data->nch = data->nch;
|
||||
mp_audio_set_channels(af->data, &data->channels);
|
||||
af->mul = (double)af->data->bps / data->bps;
|
||||
|
||||
af->play = play; // set default
|
||||
@ -147,7 +154,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
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))
|
||||
if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
|
||||
return AF_ERROR;
|
||||
return AF_OK;
|
||||
}
|
||||
@ -156,8 +163,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg))
|
||||
return AF_ERROR;
|
||||
|
||||
af->data->format = *(int*)arg;
|
||||
af->data->bps = af_fmt2bits(af->data->format)/8;
|
||||
mp_audio_set_format(af->data, *(int*)arg);
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
@ -186,7 +192,7 @@ static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio*
|
||||
endian(c->audio,l->audio,len,c->bps);
|
||||
|
||||
c->audio = l->audio;
|
||||
c->format = l->format;
|
||||
mp_audio_set_format(c, l->format);
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -203,9 +209,8 @@ static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio*
|
||||
float2int(c->audio, l->audio, len, 2);
|
||||
|
||||
c->audio = l->audio;
|
||||
mp_audio_set_format(c, l->format);
|
||||
c->len = len*2;
|
||||
c->bps = 2;
|
||||
c->format = l->format;
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -222,9 +227,8 @@ static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio*
|
||||
int2float(c->audio, l->audio, len, 2);
|
||||
|
||||
c->audio = l->audio;
|
||||
mp_audio_set_format(c, l->format);
|
||||
c->len = len*4;
|
||||
c->bps = 4;
|
||||
c->format = l->format;
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -276,9 +280,8 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
|
||||
// Set output data
|
||||
c->audio = l->audio;
|
||||
mp_audio_set_format(c, l->format);
|
||||
c->len = len*l->bps;
|
||||
c->bps = l->bps;
|
||||
c->format = l->format;
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -301,7 +304,8 @@ struct af_info af_info_format = {
|
||||
"Anders",
|
||||
"",
|
||||
AF_FLAGS_REENTRANT,
|
||||
af_open
|
||||
af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
|
||||
static inline uint32_t load24bit(void* data, int pos) {
|
||||
|
@ -299,7 +299,7 @@ static int control(struct af_instance *af, int cmd, void* arg)
|
||||
af->data->rate);
|
||||
return AF_ERROR;
|
||||
}
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch);
|
||||
if(af->data->nch == 2) {
|
||||
/* 2 channel input */
|
||||
if(s->decode_mode != HRTF_MIX_MATRIX2CH) {
|
||||
@ -308,13 +308,12 @@ static int control(struct af_instance *af, int cmd, void* arg)
|
||||
}
|
||||
}
|
||||
else if (af->data->nch < 5)
|
||||
af->data->nch = 5;
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
mp_audio_set_channels_old(af->data, 5);
|
||||
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
|
||||
af->data->nch = 2;
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
s->print_flag = 1;
|
||||
return test_output_res;
|
||||
case AF_CONTROL_COMMAND_LINE:
|
||||
@ -566,7 +565,7 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
|
||||
/* Set output data */
|
||||
data->audio = af->data->audio;
|
||||
data->len = data->len / data->nch * 2;
|
||||
data->nch = 2;
|
||||
mp_audio_set_num_channels(data, 2);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -34,10 +34,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
af->data->format= AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
|
@ -291,7 +291,7 @@ static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
|
||||
static void* mydlopen(const char *filename, int flag) {
|
||||
char *buf;
|
||||
const char *end, *start, *ladspapath;
|
||||
int endsinso, needslash;
|
||||
int endsinso;
|
||||
size_t filenamelen;
|
||||
void *result = NULL;
|
||||
|
||||
@ -324,9 +324,9 @@ static void* mydlopen(const char *filename, int flag) {
|
||||
ladspapath=getenv("LADSPA_PATH");
|
||||
|
||||
if (ladspapath) {
|
||||
|
||||
start=ladspapath;
|
||||
while (*start != '\0') {
|
||||
int needslash;
|
||||
end=start;
|
||||
while ( (*end != ':') && (*end != '\0') )
|
||||
end++;
|
||||
@ -487,7 +487,6 @@ static int af_ladspa_malloc_failed(char *myname) {
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
|
||||
int i, r;
|
||||
float val;
|
||||
|
||||
switch(cmd) {
|
||||
@ -498,10 +497,8 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
|
||||
/* accept FLOAT, let af_format do conversion */
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
|
||||
/* arg->len is not set here yet, so init of buffers and connecting the
|
||||
* filter, has to be done in play() :-/
|
||||
@ -538,7 +535,10 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
}
|
||||
line += strlen(buf);
|
||||
setup->file = strdup(buf);
|
||||
if (!setup->file) return af_ladspa_malloc_failed(setup->myname);
|
||||
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 ':' */
|
||||
@ -554,7 +554,10 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
}
|
||||
line += strlen(buf);
|
||||
setup->label = strdup(buf);
|
||||
if (!setup->label) return af_ladspa_malloc_failed(setup->myname);
|
||||
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 ':' */
|
||||
@ -581,15 +584,14 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
|
||||
/* ninputcontrols is set by now, read control values from arg */
|
||||
|
||||
for(i=0; i<setup->ninputcontrols; i++) {
|
||||
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++;
|
||||
r = sscanf(line, "%f", &val);
|
||||
if (r!=1) {
|
||||
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;
|
||||
@ -599,7 +601,7 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
}
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
|
||||
for(i=0; i<setup->ninputcontrols; i++) {
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]]);
|
||||
}
|
||||
@ -609,7 +611,7 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
|
||||
setup->myname);
|
||||
for(i=0; i<setup->ninputcontrols; i++) {
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
int p = setup->inputcontrolsmap[i];
|
||||
LADSPA_PortRangeHint hint =
|
||||
setup->plugin_descriptor->PortRangeHints[p];
|
||||
@ -651,8 +653,6 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
*/
|
||||
|
||||
static void uninit(struct af_instance *af) {
|
||||
int i;
|
||||
|
||||
free(af->data);
|
||||
if (af->setup) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
|
||||
@ -664,7 +664,7 @@ static void uninit(struct af_instance *af) {
|
||||
}
|
||||
|
||||
if (setup->chhandles) {
|
||||
for(i=0; i<setup->nch; i+=setup->ninputs) {
|
||||
for (int i = 0; i < setup->nch; i+=setup->ninputs) {
|
||||
if (pdes->deactivate) pdes->deactivate(setup->chhandles[i]);
|
||||
if (pdes->cleanup) pdes->cleanup(setup->chhandles[i]);
|
||||
}
|
||||
@ -681,13 +681,13 @@ static void uninit(struct af_instance *af) {
|
||||
free(setup->outputs);
|
||||
|
||||
if (setup->inbufs) {
|
||||
for(i=0; i<setup->nch; i++)
|
||||
for(int i = 0; i < setup->nch; i++)
|
||||
free(setup->inbufs[i]);
|
||||
free(setup->inbufs);
|
||||
}
|
||||
|
||||
if (setup->outbufs) {
|
||||
for(i=0; i<setup->nch; i++)
|
||||
for (int i = 0; i < setup->nch; i++)
|
||||
free(setup->outbufs[i]);
|
||||
free(setup->outbufs);
|
||||
}
|
||||
|
@ -75,16 +75,16 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num)
|
||||
return AF_DETACH;
|
||||
|
||||
af->data->format = s->in_sampleformat;
|
||||
af->data->bps = af_fmt2bits(s->in_sampleformat) / 8;
|
||||
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)
|
||||
af->data->nch = AC3_MAX_CHANNELS;
|
||||
mp_audio_set_num_channels(af->data, AC3_MAX_CHANNELS);
|
||||
else
|
||||
af->data->nch = data->nch;
|
||||
mp_audio_set_channels(af->data, &data->channels);
|
||||
mp_chmap_reorder_to_lavc(&af->data->channels);
|
||||
test_output_res = af_test_output(af, data);
|
||||
|
||||
s->pending_len = 0;
|
||||
@ -108,8 +108,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
|
||||
// Put sample parameters
|
||||
s->lavc_actx->channels = af->data->nch;
|
||||
s->lavc_actx->channel_layout =
|
||||
av_get_default_channel_layout(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;
|
||||
|
||||
@ -123,9 +122,8 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
"encoder frame size %d\n", s->lavc_actx->frame_size);
|
||||
return AF_ERROR;
|
||||
}
|
||||
af->data->format = AF_FORMAT_AC3_BE;
|
||||
af->data->bps = 2;
|
||||
af->data->nch = 2;
|
||||
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);
|
||||
@ -235,15 +233,6 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
src2= s->pending_data;
|
||||
}
|
||||
|
||||
if (c->nch >= 5) {
|
||||
reorder_channel_nch(src2,
|
||||
AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
|
||||
c->nch,
|
||||
s->expect_len / samplesize,
|
||||
samplesize);
|
||||
}
|
||||
|
||||
void *data = (void *) src2;
|
||||
if (s->planarize) {
|
||||
void *data2 = malloc(s->expect_len);
|
||||
@ -316,8 +305,8 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
buf += len;
|
||||
}
|
||||
c->audio = l->audio;
|
||||
c->nch = 2;
|
||||
c->bps = 2;
|
||||
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);
|
||||
|
@ -43,6 +43,7 @@
|
||||
#define avresample_available(x) 0
|
||||
#define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \
|
||||
swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples)
|
||||
#define avresample_set_channel_mapping swr_set_channel_mapping
|
||||
#else
|
||||
#error "config.h broken"
|
||||
#endif
|
||||
@ -50,6 +51,7 @@
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/subopt-helper.h"
|
||||
#include "audio/filter/af.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
struct af_resample_opts {
|
||||
int filter_size;
|
||||
@ -57,14 +59,24 @@ struct af_resample_opts {
|
||||
int linear;
|
||||
double cutoff;
|
||||
|
||||
int out_rate;
|
||||
int in_rate;
|
||||
int in_format;
|
||||
struct mp_chmap in_channels;
|
||||
int out_rate;
|
||||
int out_format;
|
||||
struct mp_chmap out_channels;
|
||||
};
|
||||
|
||||
struct af_resample {
|
||||
int allow_detach;
|
||||
struct AVAudioResampleContext *avrctx;
|
||||
struct AVAudioResampleContext *avrctx_out; // for output channel reordering
|
||||
struct af_resample_opts ctx; // opts in the context
|
||||
struct af_resample_opts opts; // opts requested by the user
|
||||
// At least libswresample keeps a pointer around for this:
|
||||
int reorder_in[MP_NUM_CHANNELS];
|
||||
int reorder_out[MP_NUM_CHANNELS];
|
||||
uint8_t *reorder_buffer;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LIBAVRESAMPLE
|
||||
@ -88,8 +100,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s,
|
||||
struct mp_audio *in,
|
||||
struct mp_audio *out)
|
||||
{
|
||||
return s->ctx.out_rate != out->rate ||
|
||||
s->ctx.in_rate != in->rate ||
|
||||
return s->ctx.in_rate != in->rate ||
|
||||
s->ctx.in_format != in->format ||
|
||||
!mp_chmap_equals(&s->ctx.in_channels, &in->channels) ||
|
||||
s->ctx.out_rate != out->rate ||
|
||||
s->ctx.out_format != out->format ||
|
||||
!mp_chmap_equals(&s->ctx.out_channels, &out->channels) ||
|
||||
s->ctx.filter_size != s->opts.filter_size ||
|
||||
s->ctx.phase_shift != s->opts.phase_shift ||
|
||||
s->ctx.linear != s->opts.linear ||
|
||||
@ -97,6 +113,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s,
|
||||
|
||||
}
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE &&
|
||||
af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
#define ctx_opt_set_int(a,b) av_opt_set_int(s->avrctx, (a), (b), 0)
|
||||
#define ctx_opt_set_dbl(a,b) av_opt_set_double(s->avrctx, (a), (b), 0)
|
||||
|
||||
@ -108,36 +130,70 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT: {
|
||||
if ((out->rate == in->rate) || (out->rate == 0))
|
||||
struct mp_audio orig_in = *in;
|
||||
|
||||
if (((out->rate == in->rate) || (out->rate == 0)) &&
|
||||
(out->format == in->format) &&
|
||||
(mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) &&
|
||||
s->allow_detach)
|
||||
return AF_DETACH;
|
||||
|
||||
out->nch = FFMIN(in->nch, AF_NCH);
|
||||
out->format = AF_FORMAT_S16_NE;
|
||||
out->bps = 2;
|
||||
af->mul = (double) out->rate / in->rate;
|
||||
if (out->rate == 0)
|
||||
out->rate = in->rate;
|
||||
|
||||
if (mp_chmap_is_empty(&out->channels))
|
||||
mp_audio_set_channels(out, &in->channels);
|
||||
|
||||
enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
|
||||
if (in_samplefmt == AV_SAMPLE_FMT_NONE) {
|
||||
mp_audio_set_format(in, AF_FORMAT_FLOAT_NE);
|
||||
in_samplefmt = af_to_avformat(in->format);
|
||||
}
|
||||
enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
|
||||
if (out_samplefmt == AV_SAMPLE_FMT_NONE) {
|
||||
mp_audio_set_format(out, in->format);
|
||||
out_samplefmt = in_samplefmt;
|
||||
}
|
||||
|
||||
af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
|
||||
af->delay = out->nch * s->opts.filter_size / FFMIN(af->mul, 1);
|
||||
|
||||
if (needs_lavrctx_reconfigure(s, in, out)) {
|
||||
if (s->avrctx)
|
||||
avresample_close(s->avrctx);
|
||||
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;
|
||||
|
||||
int ch_layout = av_get_default_channel_layout(out->nch);
|
||||
struct mp_chmap map_in = in->channels;
|
||||
struct mp_chmap map_out = out->channels;
|
||||
|
||||
ctx_opt_set_int("in_channel_layout", ch_layout);
|
||||
ctx_opt_set_int("out_channel_layout", ch_layout);
|
||||
// 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", AV_SAMPLE_FMT_S16);
|
||||
ctx_opt_set_int("out_sample_fmt", AV_SAMPLE_FMT_S16);
|
||||
ctx_opt_set_int("in_sample_fmt", in_samplefmt);
|
||||
ctx_opt_set_int("out_sample_fmt", out_samplefmt);
|
||||
|
||||
ctx_opt_set_int("filter_size", s->ctx.filter_size);
|
||||
ctx_opt_set_int("phase_shift", s->ctx.phase_shift);
|
||||
@ -145,20 +201,51 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
|
||||
ctx_opt_set_dbl("cutoff", s->ctx.cutoff);
|
||||
|
||||
if (avresample_open(s->avrctx) < 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);
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int out_rate, test_output_res;
|
||||
// hack to make af_test_output ignore the samplerate change
|
||||
out_rate = out->rate;
|
||||
out->rate = in->rate;
|
||||
test_output_res = af_test_output(af, in);
|
||||
out->rate = out_rate;
|
||||
return test_output_res;
|
||||
return ((in->format == orig_in.format) &&
|
||||
mp_chmap_equals(&in->channels, &orig_in.channels))
|
||||
? AF_OK : AF_FALSE;
|
||||
}
|
||||
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_CHANNELS | AF_CONTROL_SET: {
|
||||
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_COMMAND_LINE: {
|
||||
s->opts.cutoff = 0.0;
|
||||
@ -169,6 +256,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{"phase_shift", OPT_ARG_INT, &s->opts.phase_shift, NULL},
|
||||
{"linear", OPT_ARG_BOOL, &s->opts.linear, NULL},
|
||||
{"cutoff", OPT_ARG_FLOAT, &s->opts.cutoff, NULL},
|
||||
{"detach", OPT_ARG_BOOL, &s->allow_detach, NULL},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -198,10 +286,21 @@ static void uninit(struct af_instance *af)
|
||||
struct af_resample *s = af->setup;
|
||||
if (s->avrctx)
|
||||
avresample_close(s->avrctx);
|
||||
if (s->avrctx_out)
|
||||
avresample_close(s->avrctx_out);
|
||||
talloc_free(af->setup);
|
||||
}
|
||||
}
|
||||
|
||||
static bool needs_reorder(int *reorder, int num_ch)
|
||||
{
|
||||
for (int n = 0; n < num_ch; n++) {
|
||||
if (reorder[n] != n)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
struct af_resample *s = af->setup;
|
||||
@ -227,10 +326,18 @@ static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
|
||||
(uint8_t **) &out->audio, out_size, out_samples,
|
||||
(uint8_t **) &in->audio, in_size, in_samples);
|
||||
|
||||
out_size = out->bps * out_samples * out->nch;
|
||||
in->audio = out->audio;
|
||||
in->len = out_size;
|
||||
in->rate = s->ctx.out_rate;
|
||||
*data = *out;
|
||||
|
||||
if (needs_reorder(s->reorder_out, out->nch)) {
|
||||
if (talloc_get_size(s->reorder_buffer) < out_size)
|
||||
s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
|
||||
data->audio = s->reorder_buffer;
|
||||
out_samples = avresample_convert(s->avrctx_out,
|
||||
(uint8_t **) &data->audio, out_size, out_samples,
|
||||
(uint8_t **) &out->audio, out_size, out_samples);
|
||||
}
|
||||
|
||||
data->len = out->bps * out_samples * out->nch;
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -244,7 +351,7 @@ static int af_open(struct af_instance *af)
|
||||
af->mul = 1;
|
||||
af->data = talloc_zero(s, struct mp_audio);
|
||||
|
||||
af->data->rate = 44100;
|
||||
af->data->rate = 0;
|
||||
|
||||
int default_filter_size = 16;
|
||||
s->opts = (struct af_resample_opts) {
|
||||
@ -254,10 +361,13 @@ static int af_open(struct af_instance *af)
|
||||
.phase_shift = 10,
|
||||
};
|
||||
|
||||
s->allow_detach = 1;
|
||||
|
||||
s->avrctx = avresample_alloc_context();
|
||||
s->avrctx_out = avresample_alloc_context();
|
||||
af->setup = s;
|
||||
|
||||
if (s->avrctx) {
|
||||
if (s->avrctx && s->avrctx_out) {
|
||||
return AF_OK;
|
||||
} else {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot initialize "
|
||||
@ -273,5 +383,6 @@ struct af_info af_info_lavrresample = {
|
||||
"Stefano Pigozzi (based on Michael Niedermayer's lavcresample)",
|
||||
"",
|
||||
AF_FLAGS_REENTRANT,
|
||||
af_open
|
||||
af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
|
@ -34,6 +34,15 @@ typedef struct af_pan_s
|
||||
float level[AF_NCH][AF_NCH]; // Gain level for each channel
|
||||
}af_pan_t;
|
||||
|
||||
static void set_channels(struct mp_audio *mpa, int num)
|
||||
{
|
||||
struct mp_chmap map;
|
||||
// "unknown" channel layouts make it easier to pass through audio data,
|
||||
// without triggering remixing.
|
||||
mp_chmap_set_unknown(&map, num);
|
||||
mp_audio_set_channels(mpa, &map);
|
||||
}
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
@ -45,15 +54,13 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
af->data->nch = s->nch ? s->nch: ((struct mp_audio*)arg)->nch;
|
||||
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)){
|
||||
((struct mp_audio*)arg)->format = af->data->format;
|
||||
((struct mp_audio*)arg)->bps = af->data->bps;
|
||||
mp_audio_set_format((struct mp_audio*)arg, af->data->format);
|
||||
return AF_FALSE;
|
||||
}
|
||||
return AF_OK;
|
||||
@ -109,14 +116,11 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "[pan] The number of output channels must be"
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
|
||||
" between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
|
||||
return AF_ERROR;
|
||||
}
|
||||
s->nch=((int*)arg)[0];
|
||||
return AF_OK;
|
||||
case AF_CONTROL_PAN_NOUT | AF_CONTROL_GET:
|
||||
*(int*)arg = af->data->nch;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{
|
||||
float val = *(float*)arg;
|
||||
if (s->nch)
|
||||
@ -181,7 +185,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
|
||||
// Set output data
|
||||
c->audio = l->audio;
|
||||
c->len = c->len / c->nch * l->nch;
|
||||
c->nch = l->nch;
|
||||
set_channels(c, l->nch);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
@ -302,26 +302,22 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
"[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
|
||||
s->speed, s->scale_nominal, s->scale);
|
||||
|
||||
mp_audio_copy_config(af->data, data);
|
||||
|
||||
if (s->scale == 1.0) {
|
||||
if (s->speed_tempo && s->speed_pitch)
|
||||
return AF_DETACH;
|
||||
memcpy(af->data, data, sizeof(struct mp_audio));
|
||||
af->delay = 0;
|
||||
af->mul = 1;
|
||||
return af_test_output(af, data);
|
||||
}
|
||||
|
||||
af->data->rate = data->rate;
|
||||
af->data->nch = data->nch;
|
||||
if ( data->format == AF_FORMAT_S16_LE
|
||||
|| data->format == AF_FORMAT_S16_BE ) {
|
||||
if (data->format == AF_FORMAT_S16_NE) {
|
||||
use_int = 1;
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = bps = 2;
|
||||
} else {
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = bps = 4;
|
||||
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;
|
||||
|
@ -54,8 +54,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = 1;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_num_channels(af->data, 1);
|
||||
#if 0
|
||||
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
|
||||
{
|
||||
@ -65,8 +65,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
}// else
|
||||
#endif
|
||||
{
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
|
||||
af->play = play_s16;
|
||||
}
|
||||
|
||||
@ -79,18 +78,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
s->decay = f2;
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_SS_FREQ | AF_CONTROL_SET:
|
||||
s->freq = *(float*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SS_FREQ | AF_CONTROL_GET:
|
||||
*(float*)arg = s->freq;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SS_DECAY | AF_CONTROL_SET:
|
||||
s->decay = *(float*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SS_DECAY | AF_CONTROL_GET:
|
||||
*(float*)arg = s->decay;
|
||||
return AF_OK;
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
@ -70,9 +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;
|
||||
af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
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;
|
||||
|
@ -92,10 +92,9 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
float fc;
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch*2;
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
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");
|
||||
@ -125,8 +124,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
|
||||
if((af->data->format != ((struct mp_audio*)arg)->format) ||
|
||||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
|
||||
((struct mp_audio*)arg)->format = af->data->format;
|
||||
((struct mp_audio*)arg)->bps = af->data->bps;
|
||||
mp_audio_set_format((struct mp_audio*)arg, af->data->format);
|
||||
return AF_FALSE;
|
||||
}
|
||||
return AF_OK;
|
||||
@ -244,7 +242,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
|
||||
// Set output data
|
||||
data->audio = af->data->audio;
|
||||
data->len *= 2;
|
||||
data->nch = af->data->nch;
|
||||
mp_audio_set_channels_old(data, af->data->nch);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -41,12 +41,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
af->data->nch = data->nch;
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
af->data->rate = data->rate;
|
||||
mp_audio_copy_config(af->data, data);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
|
||||
|
||||
return AF_OK;
|
||||
return af_test_output(af, data);
|
||||
case AF_CONTROL_COMMAND_LINE:
|
||||
sscanf((char*)arg,"%lf", &s->delta);
|
||||
return AF_OK;
|
||||
|
@ -90,8 +90,8 @@ int af_test_output(struct af_instance* af, struct mp_audio* out)
|
||||
if((af->data->format != out->format) ||
|
||||
(af->data->bps != out->bps) ||
|
||||
(af->data->rate != out->rate) ||
|
||||
(af->data->nch != out->nch)){
|
||||
memcpy(out,af->data,sizeof(struct mp_audio));
|
||||
!mp_chmap_equals(&af->data->channels, &out->channels)){
|
||||
*out = *af->data;
|
||||
return AF_FALSE;
|
||||
}
|
||||
return AF_OK;
|
||||
|
@ -66,12 +66,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
af->data->nch = ((struct mp_audio*)arg)->nch;
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
|
||||
if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){
|
||||
af->data->format = AF_FORMAT_S16_NE;
|
||||
af->data->bps = 2;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
|
||||
}
|
||||
else{
|
||||
// Cutoff set to 10Hz for forgetting factor
|
||||
@ -79,42 +77,21 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
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);
|
||||
af->data->format = AF_FORMAT_FLOAT_NE;
|
||||
af->data->bps = 4;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
|
||||
}
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
case AF_CONTROL_COMMAND_LINE:{
|
||||
float v=0.0;
|
||||
float vol[AF_NCH];
|
||||
int i;
|
||||
sscanf((char*)arg,"%f:%i", &v, &s->soft);
|
||||
sscanf((char*)arg,"%f:%i:%i", &v, &s->soft, &s->fast);
|
||||
for(i=0;i<AF_NCH;i++) vol[i]=v;
|
||||
return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol);
|
||||
}
|
||||
case AF_CONTROL_POST_CREATE:
|
||||
s->fast = ((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) ==
|
||||
AF_INIT_FLOAT) ? 0 : 1;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_SET:
|
||||
memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
|
||||
return AF_OK;
|
||||
case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_GET:
|
||||
memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
|
||||
return AF_OK;
|
||||
case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET:
|
||||
s->soft = *(int*)arg;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_GET:
|
||||
*(int*)arg = s->soft;
|
||||
return AF_OK;
|
||||
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET:
|
||||
return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0);
|
||||
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET:
|
||||
return af_to_dB(AF_NCH,s->level,(float*)arg,20.0);
|
||||
case AF_CONTROL_VOLUME_PROBE | AF_CONTROL_GET:
|
||||
return af_to_dB(AF_NCH,s->pow,(float*)arg,10.0);
|
||||
case AF_CONTROL_VOLUME_PROBE_MAX | AF_CONTROL_GET:
|
||||
return af_to_dB(AF_NCH,s->max,(float*)arg,10.0);
|
||||
case AF_CONTROL_PRE_DESTROY:{
|
||||
float m = 0.0;
|
||||
int i;
|
||||
@ -122,7 +99,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
|
||||
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_INFO, "[volume] The maximum volume was %0.2fdB \n", m);
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[volume] The maximum volume was %0.2fdB \n", m);
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
|
@ -21,48 +21,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*********************************************
|
||||
// Control info struct.
|
||||
//
|
||||
// This struct is the argument in a info call to a filter.
|
||||
*/
|
||||
|
||||
// Argument types
|
||||
#define AF_CONTROL_TYPE_BOOL (0x0<<0)
|
||||
#define AF_CONTROL_TYPE_CHAR (0x1<<0)
|
||||
#define AF_CONTROL_TYPE_INT (0x2<<0)
|
||||
#define AF_CONTROL_TYPE_FLOAT (0x3<<0)
|
||||
#define AF_CONTROL_TYPE_STRUCT (0x4<<0)
|
||||
#define AF_CONTROL_TYPE_SPECIAL (0x5<<0) // a pointer to a function for example
|
||||
#define AF_CONTROL_TYPE_MASK (0x7<<0)
|
||||
// Argument geometry
|
||||
#define AF_CONTROL_GEOM_SCALAR (0x0<<3)
|
||||
#define AF_CONTROL_GEOM_ARRAY (0x1<<3)
|
||||
#define AF_CONTROL_GEOM_MATRIX (0x2<<3)
|
||||
#define AF_CONTROL_GEOM_MASK (0x3<<3)
|
||||
// Argument properties
|
||||
#define AF_CONTROL_PROP_READ (0x0<<5) // The argument can be read
|
||||
#define AF_CONTROL_PROP_WRITE (0x1<<5) // The argument can be written
|
||||
#define AF_CONTROL_PROP_SAVE (0x2<<5) // Can be saved
|
||||
#define AF_CONTROL_PROP_RUNTIME (0x4<<5) // Acessable during execution
|
||||
#define AF_CONTROL_PROP_CHANNEL (0x8<<5) // Argument is set per channel
|
||||
#define AF_CONTROL_PROP_MASK (0xF<<5)
|
||||
|
||||
typedef struct af_control_info_s{
|
||||
int def; // Control enumrification
|
||||
char* name; // Name of argument
|
||||
char* info; // Description of what it does
|
||||
int flags; // Flags as defined above
|
||||
float max; // Max and min value
|
||||
float min; // (only aplicable on float and int)
|
||||
int xdim; // 1st dimension
|
||||
int ydim; // 2nd dimension (=0 for everything except matrix)
|
||||
size_t sz; // Size of argument in bytes
|
||||
int ch; // Channel number (for future use)
|
||||
void* arg; // Data (for future use)
|
||||
}af_control_info_t;
|
||||
|
||||
|
||||
/*********************************************
|
||||
// Extended control used with arguments that operates on only one
|
||||
// channel at the time
|
||||
@ -98,11 +56,6 @@ typedef struct af_control_ext_s{
|
||||
|
||||
// OPTIONAL CALLS
|
||||
|
||||
/* Called just after creation with the af_cfg for the stream in which
|
||||
the filter resides as input parameter this call can be used by the
|
||||
filter to initialize itself */
|
||||
#define AF_CONTROL_POST_CREATE 0x00000100 | AF_CONTROL_OPTIONAL
|
||||
|
||||
// Called just before destruction of a filter
|
||||
#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL
|
||||
|
||||
@ -119,20 +72,12 @@ typedef struct af_control_ext_s{
|
||||
#define AF_CONTROL_SET 0x00000000
|
||||
// Get argument
|
||||
#define AF_CONTROL_GET 0x00000001
|
||||
// Get info about the control, i.e fill in everything except argument
|
||||
#define AF_CONTROL_INFO 0x00000002
|
||||
|
||||
// Resample
|
||||
|
||||
// Set output rate in resample
|
||||
#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Enable sloppy resampling
|
||||
#define AF_CONTROL_RESAMPLE_SLOPPY 0x00000200 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set resampling accuracy
|
||||
#define AF_CONTROL_RESAMPLE_ACCURACY 0x00000300 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Format
|
||||
|
||||
#define AF_CONTROL_FORMAT_FMT 0x00000400 | AF_CONTROL_FILTER_SPECIFIC
|
||||
@ -142,69 +87,11 @@ typedef struct af_control_ext_s{
|
||||
// Set number of output channels in channels
|
||||
#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set number of channel routes
|
||||
#define AF_CONTROL_CHANNELS_ROUTES 0x00000700 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set channel routing pair, arg is int[2] and ch is used
|
||||
#define AF_CONTROL_CHANNELS_ROUTING 0x00000800 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set nuber of channel routing pairs, arg is int*
|
||||
#define AF_CONTROL_CHANNELS_NR 0x00000900 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set make af_channels into a router
|
||||
#define AF_CONTROL_CHANNELS_ROUTER 0x00000A00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Volume
|
||||
|
||||
// Turn volume control on and off, arg is int*
|
||||
#define AF_CONTROL_VOLUME_ON_OFF 0x00000B00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Turn soft clipping of the volume on and off, arg is binary
|
||||
#define AF_CONTROL_VOLUME_SOFTCLIP 0x00000C00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set volume level, arg is a float* with the volume for all the channels
|
||||
#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Probed power level for all channels, arg is a float*
|
||||
#define AF_CONTROL_VOLUME_PROBE 0x00000E00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Maximum probed power level for all channels, arg is a float*
|
||||
#define AF_CONTROL_VOLUME_PROBE_MAX 0x00000F00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Compressor/expander
|
||||
|
||||
// Turn compressor/expander on and off
|
||||
#define AF_CONTROL_COMP_ON_OFF 0x00001000 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Compression/expansion threshold [dB]
|
||||
#define AF_CONTROL_COMP_THRESH 0x00001100 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Compression/expansion attack time [ms]
|
||||
#define AF_CONTROL_COMP_ATTACK 0x00001200 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Compression/expansion release time [ms]
|
||||
#define AF_CONTROL_COMP_RELEASE 0x00001300 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Compression/expansion gain level [dB]
|
||||
#define AF_CONTROL_COMP_RATIO 0x00001400 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Noise gate
|
||||
|
||||
// Turn noise gate on an off
|
||||
#define AF_CONTROL_GATE_ON_OFF 0x00001500 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Noise gate threshold [dB]
|
||||
#define AF_CONTROL_GATE_THRESH 0x00001600 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Noise gate attack time [ms]
|
||||
#define AF_CONTROL_GATE_ATTACK 0x00001700 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Noise gate release time [ms]
|
||||
#define AF_CONTROL_GATE_RELEASE 0x00001800 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Noise gate release range level [dB]
|
||||
#define AF_CONTROL_GATE_RANGE 0x00001900 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Pan
|
||||
|
||||
// Pan levels, arg is a control_ext with a float*
|
||||
@ -216,9 +103,6 @@ typedef struct af_control_ext_s{
|
||||
// Balance, arg is float*; range -1 (left) to 1 (right), 0 center
|
||||
#define AF_CONTROL_PAN_BALANCE 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
// Set equalizer gain, arg is a control_ext with a float*
|
||||
#define AF_CONTROL_EQUALIZER_GAIN 0x00001D00 | 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
|
||||
@ -236,21 +120,10 @@ typedef struct af_control_ext_s{
|
||||
// Export
|
||||
#define AF_CONTROL_EXPORT_SZ 0x00003000 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
|
||||
// ExtraStereo Multiplier
|
||||
#define AF_CONTROL_ES_MUL 0x00003100 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
|
||||
// Center
|
||||
|
||||
// Channel number which to inster the filtered data, arg in int*
|
||||
#define AF_CONTROL_CENTER_CH 0x00003200 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
|
||||
// SineSuppress
|
||||
#define AF_CONTROL_SS_FREQ 0x00003300 | AF_CONTROL_FILTER_SPECIFIC
|
||||
#define AF_CONTROL_SS_DECAY 0x00003400 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
#define AF_CONTROL_PLAYBACK_SPEED 0x00003500 | AF_CONTROL_FILTER_SPECIFIC
|
||||
#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00003600 | AF_CONTROL_FILTER_SPECIFIC
|
||||
|
||||
|
65
audio/fmt-conversion.c
Normal file
65
audio/fmt-conversion.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 "core/mp_msg.h"
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
#include "format.h"
|
||||
#include "fmt-conversion.h"
|
||||
|
||||
static const struct {
|
||||
enum AVSampleFormat sample_fmt;
|
||||
int fmt;
|
||||
} audio_conversion_map[] = {
|
||||
{AV_SAMPLE_FMT_U8, AF_FORMAT_U8},
|
||||
{AV_SAMPLE_FMT_S16, AF_FORMAT_S16_NE},
|
||||
{AV_SAMPLE_FMT_S32, AF_FORMAT_S32_NE},
|
||||
{AV_SAMPLE_FMT_FLT, AF_FORMAT_FLOAT_NE},
|
||||
{AV_SAMPLE_FMT_DBL, AF_FORMAT_DOUBLE_NE},
|
||||
|
||||
{AV_SAMPLE_FMT_NONE, 0},
|
||||
};
|
||||
|
||||
enum AVSampleFormat af_to_avformat(int fmt)
|
||||
{
|
||||
int i;
|
||||
enum AVSampleFormat sample_fmt;
|
||||
for (i = 0; audio_conversion_map[i].fmt; i++)
|
||||
if (audio_conversion_map[i].fmt == fmt)
|
||||
break;
|
||||
sample_fmt = audio_conversion_map[i].sample_fmt;
|
||||
if (sample_fmt == AF_FORMAT_UNKNOWN)
|
||||
mp_msg(MSGT_GLOBAL, MSGL_V, "Unsupported sample format: %s\n",
|
||||
af_fmt2str_short(fmt));
|
||||
return sample_fmt;
|
||||
}
|
||||
|
||||
int af_from_avformat(enum AVSampleFormat sample_fmt)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; audio_conversion_map[i].fmt; i++)
|
||||
if (audio_conversion_map[i].sample_fmt == sample_fmt)
|
||||
break;
|
||||
int fmt = audio_conversion_map[i].fmt;
|
||||
if (!fmt) {
|
||||
const char *fmtname = av_get_sample_fmt_name(sample_fmt);
|
||||
mp_msg(MSGT_GLOBAL, MSGL_ERR, "Unsupported AVSampleFormat %s (%d)\n",
|
||||
fmtname ? fmtname : "INVALID", sample_fmt);
|
||||
}
|
||||
return fmt;
|
||||
}
|
25
audio/fmt-conversion.h
Normal file
25
audio/fmt-conversion.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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_SAMPLE_FMT_CONVERSION_H
|
||||
#define MPLAYER_SAMPLE_FMT_CONVERSION_H
|
||||
|
||||
enum AVSampleFormat af_to_avformat(int fmt);
|
||||
int af_from_avformat(enum AVSampleFormat sample_fmt);
|
||||
|
||||
#endif /* MPLAYER_SAMPLE_FMT_CONVERSION_H */
|
@ -29,24 +29,17 @@
|
||||
int af_fmt2bits(int format)
|
||||
{
|
||||
if (AF_FORMAT_IS_AC3(format)) return 16;
|
||||
return (format & AF_FORMAT_BITS_MASK)+8;
|
||||
// return (((format & AF_FORMAT_BITS_MASK)>>3)+1) * 8;
|
||||
#if 0
|
||||
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_48BIT: return 48;
|
||||
case AF_FORMAT_64BIT: return 64;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int af_bits2fmt(int bits)
|
||||
{
|
||||
return (bits/8 - 1) << 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert format to str input str is a buffer for the
|
||||
@ -94,6 +87,9 @@ const struct af_fmt_entry af_fmtstr_table[] = {
|
||||
{ "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}
|
||||
};
|
||||
|
@ -53,8 +53,7 @@
|
||||
#define AF_FORMAT_16BIT (1<<3)
|
||||
#define AF_FORMAT_24BIT (2<<3)
|
||||
#define AF_FORMAT_32BIT (3<<3)
|
||||
#define AF_FORMAT_40BIT (4<<3)
|
||||
#define AF_FORMAT_48BIT (5<<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)
|
||||
@ -85,6 +84,9 @@
|
||||
#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)
|
||||
|
||||
#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)
|
||||
|
||||
#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)
|
||||
|
||||
@ -99,6 +101,7 @@
|
||||
#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
|
||||
@ -109,6 +112,7 @@
|
||||
#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
|
||||
@ -127,7 +131,6 @@ extern const struct af_fmt_entry af_fmtstr_table[];
|
||||
|
||||
int af_str2fmt_short(bstr str);
|
||||
int af_fmt2bits(int format);
|
||||
int af_bits2fmt(int bits);
|
||||
char* af_fmt2str(int format, char* str, int size);
|
||||
const char* af_fmt2str_short(int format);
|
||||
|
||||
|
@ -217,7 +217,7 @@ void mixer_setbalance(mixer_t *mixer, float val)
|
||||
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val))
|
||||
return;
|
||||
|
||||
if (val == 0 || mixer->ao->channels < 2)
|
||||
if (val == 0 || mixer->ao->channels.num < 2)
|
||||
return;
|
||||
|
||||
if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) {
|
||||
|
@ -250,14 +250,24 @@ void ao_resume(struct ao *ao)
|
||||
ao->driver->resume(ao);
|
||||
}
|
||||
|
||||
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
struct mp_chmap *map)
|
||||
{
|
||||
return mp_chmap_sel_adjust(s, map);
|
||||
}
|
||||
|
||||
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
struct mp_chmap *map, int num)
|
||||
{
|
||||
return mp_chmap_sel_get_def(s, map, num);
|
||||
}
|
||||
|
||||
int old_ao_init(struct ao *ao, char *params)
|
||||
{
|
||||
assert(!global_ao);
|
||||
global_ao = ao;
|
||||
ao_subdevice = params ? talloc_strdup(ao, params) : NULL;
|
||||
if (ao->driver->old_functions->init(ao->samplerate, ao->channels,
|
||||
if (ao->driver->old_functions->init(ao->samplerate, &ao->channels,
|
||||
ao->format, 0) == 0) {
|
||||
global_ao = NULL;
|
||||
return -1;
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "core/bstr.h"
|
||||
#include "core/mp_common.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
|
||||
enum aocontrol {
|
||||
// _VOLUME commands take struct ao_control_vol pointer for input/output.
|
||||
@ -55,7 +57,7 @@ typedef struct ao_info {
|
||||
/* interface towards mplayer and */
|
||||
typedef struct ao_old_functions {
|
||||
int (*control)(int cmd, void *arg);
|
||||
int (*init)(int rate, int channels, int format, int flags);
|
||||
int (*init)(int rate, const struct mp_chmap *channels, int format, int flags);
|
||||
void (*uninit)(int immed);
|
||||
void (*reset)(void);
|
||||
int (*get_space)(void);
|
||||
@ -68,7 +70,6 @@ typedef struct ao_old_functions {
|
||||
struct ao;
|
||||
|
||||
struct ao_driver {
|
||||
bool is_new;
|
||||
bool encode;
|
||||
const struct ao_info *info;
|
||||
const struct ao_old_functions *old_functions;
|
||||
@ -86,9 +87,9 @@ struct ao_driver {
|
||||
/* global data used by mplayer and plugins */
|
||||
struct ao {
|
||||
int samplerate;
|
||||
int channels;
|
||||
struct mp_chmap channels;
|
||||
int format;
|
||||
int bps;
|
||||
int bps; // bytes per second
|
||||
int outburst;
|
||||
int buffersize;
|
||||
double pts;
|
||||
@ -121,6 +122,11 @@ void ao_reset(struct ao *ao);
|
||||
void ao_pause(struct ao *ao);
|
||||
void ao_resume(struct ao *ao);
|
||||
|
||||
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,
|
||||
struct mp_chmap *map, int num);
|
||||
|
||||
int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
|
||||
int old_ao_init(struct ao *ao, char *params);
|
||||
void old_ao_uninit(struct ao *ao, bool cut_audio);
|
||||
|
1307
audio/out/ao_alsa.c
1307
audio/out/ao_alsa.c
File diff suppressed because it is too large
Load Diff
@ -412,7 +412,7 @@ static void print_help(void)
|
||||
free(devids);
|
||||
}
|
||||
|
||||
static int init(int rate,int channels,int format,int flags)
|
||||
static int init(int rate,const struct mp_chmap *channels,int format,int flags)
|
||||
{
|
||||
AudioStreamBasicDescription inDesc;
|
||||
AudioComponentDescription desc;
|
||||
@ -439,7 +439,7 @@ int device_id, display_help = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, channels, af_fmt2str_short(format), flags);
|
||||
ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, ao_data.channels.num, af_fmt2str_short(format), flags);
|
||||
|
||||
ao = calloc(1, sizeof(ao_coreaudio_t));
|
||||
|
||||
@ -499,10 +499,15 @@ int device_id, display_help = 0;
|
||||
// Save selected device id
|
||||
ao->i_selected_dev = devid_def;
|
||||
|
||||
struct mp_chmap_sel chmap_sel = {0};
|
||||
mp_chmap_sel_add_waveext(&chmap_sel);
|
||||
if (!ao_chmap_sel_adjust(&ao_data, &ao_data.channels, &chmap_sel))
|
||||
goto err_out;
|
||||
|
||||
// Build Description for the input format
|
||||
inDesc.mSampleRate=rate;
|
||||
inDesc.mFormatID=ao->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM;
|
||||
inDesc.mChannelsPerFrame=channels;
|
||||
inDesc.mChannelsPerFrame=ao_data.channels.num;
|
||||
inDesc.mBitsPerChannel=af_fmt2bits(format);
|
||||
|
||||
if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {
|
||||
@ -521,7 +526,7 @@ int device_id, display_help = 0;
|
||||
inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
|
||||
inDesc.mFramesPerPacket = 1;
|
||||
ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8);
|
||||
ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*ao_data.channels.num*(inDesc.mBitsPerChannel/8);
|
||||
print_format(MSGL_V, "source:",&inDesc);
|
||||
|
||||
if (ao->b_supports_digital)
|
||||
@ -605,7 +610,8 @@ int device_id, display_help = 0;
|
||||
ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame;
|
||||
|
||||
ao_data.samplerate = inDesc.mSampleRate;
|
||||
ao_data.channels = inDesc.mChannelsPerFrame;
|
||||
if (!ao_chmap_sel_get_def(&ao_data, &chmap_sel, &ao_data.channels, inDesc.mChannelsPerFrame))
|
||||
goto err_out2;
|
||||
ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame;
|
||||
ao_data.outburst = ao->chunk_size;
|
||||
ao_data.buffersize = ao_data.bps;
|
||||
@ -837,7 +843,8 @@ static int OpenSPDIF(void)
|
||||
ao->chunk_size = ao->stream_format.mBytesPerPacket;
|
||||
|
||||
ao_data.samplerate = ao->stream_format.mSampleRate;
|
||||
ao_data.channels = ao->stream_format.mChannelsPerFrame;
|
||||
// Applies default ordering; ok because AC3 data is always in mpv internal channel order
|
||||
mp_chmap_from_channels(&ao_data.channels, ao->stream_format.mChannelsPerFrame);
|
||||
ao_data.bps = ao_data.samplerate * (ao->stream_format.mBytesPerPacket/ao->stream_format.mFramesPerPacket);
|
||||
ao_data.outburst = ao->chunk_size;
|
||||
ao_data.buffersize = ao_data.bps;
|
||||
|
@ -64,26 +64,6 @@ LIBAO_EXTERN(dsound)
|
||||
|
||||
static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
|
||||
|
||||
#define SPEAKER_FRONT_LEFT 0x1
|
||||
#define SPEAKER_FRONT_RIGHT 0x2
|
||||
#define SPEAKER_FRONT_CENTER 0x4
|
||||
#define SPEAKER_LOW_FREQUENCY 0x8
|
||||
#define SPEAKER_BACK_LEFT 0x10
|
||||
#define SPEAKER_BACK_RIGHT 0x20
|
||||
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
|
||||
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
|
||||
#define SPEAKER_BACK_CENTER 0x100
|
||||
#define SPEAKER_SIDE_LEFT 0x200
|
||||
#define SPEAKER_SIDE_RIGHT 0x400
|
||||
#define SPEAKER_TOP_CENTER 0x800
|
||||
#define SPEAKER_TOP_FRONT_LEFT 0x1000
|
||||
#define SPEAKER_TOP_FRONT_CENTER 0x2000
|
||||
#define SPEAKER_TOP_FRONT_RIGHT 0x4000
|
||||
#define SPEAKER_TOP_BACK_LEFT 0x8000
|
||||
#define SPEAKER_TOP_BACK_CENTER 0x10000
|
||||
#define SPEAKER_TOP_BACK_RIGHT 0x20000
|
||||
#define SPEAKER_RESERVED 0x80000000
|
||||
|
||||
#if 0
|
||||
#define DSSPEAKER_HEADPHONE 0x00000001
|
||||
#define DSSPEAKER_MONO 0x00000002
|
||||
@ -107,28 +87,6 @@ typedef struct {
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
|
||||
#endif
|
||||
|
||||
static const int channel_mask[] = {
|
||||
/* 1 */ SPEAKER_FRONT_CENTER,
|
||||
/* 2 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
|
||||
/* 3 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_LOW_FREQUENCY,
|
||||
/* 4 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
|
||||
/* 5 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
| SPEAKER_LOW_FREQUENCY,
|
||||
/* 6 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
| SPEAKER_LOW_FREQUENCY,
|
||||
/* 7 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_BACK_LEFT | SPEAKER_BACK_CENTER | SPEAKER_BACK_RIGHT
|
||||
| SPEAKER_LOW_FREQUENCY,
|
||||
/* 8 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
|
||||
| SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
|
||||
| SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
|
||||
| SPEAKER_LOW_FREQUENCY,
|
||||
};
|
||||
|
||||
static HINSTANCE hdsound_dll = NULL; ///handle to the dll
|
||||
static LPDIRECTSOUND hds = NULL; ///direct sound object
|
||||
static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer
|
||||
@ -349,22 +307,9 @@ static int write_buffer(unsigned char *data, int len)
|
||||
if (SUCCEEDED(res))
|
||||
{
|
||||
if (!AF_FORMAT_IS_AC3(ao_data.format)) {
|
||||
int sampsize = af_fmt2bits(ao_data.format) / 8;
|
||||
reorder_channel_copy_nch(data,
|
||||
AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
lpvPtr1,
|
||||
AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
|
||||
ao_data.channels,
|
||||
dwBytes1 / sampsize,
|
||||
sampsize);
|
||||
memcpy(lpvPtr1, data, dwBytes1);
|
||||
if (lpvPtr2 != NULL)
|
||||
reorder_channel_copy_nch(data + dwBytes1,
|
||||
AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
lpvPtr2,
|
||||
AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
|
||||
ao_data.channels,
|
||||
dwBytes2 / sampsize,
|
||||
sampsize);
|
||||
memcpy(lpvPtr2, (char *)data + dwBytes1, dwBytes2);
|
||||
|
||||
write_offset+=dwBytes1+dwBytes2;
|
||||
if(write_offset>=buffer_size)
|
||||
@ -432,7 +377,7 @@ static int control(int cmd, void *arg)
|
||||
\param flags unused
|
||||
\return 1=success 0=fail
|
||||
*/
|
||||
static int init(int rate, int channels, int format, int flags)
|
||||
static int init(int rate, const struct mp_chmap *channels, int format, int flags)
|
||||
{
|
||||
int res;
|
||||
if (!InitDirectSound()) return 0;
|
||||
@ -445,15 +390,14 @@ static int init(int rate, int channels, int format, int flags)
|
||||
DSBUFFERDESC dsbpridesc;
|
||||
DSBUFFERDESC dsbdesc;
|
||||
|
||||
//check if the channel count and format is supported in general
|
||||
if (channels > FF_ARRAY_ELEMS(channel_mask)) {
|
||||
UninitDirectSound();
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (AF_FORMAT_IS_AC3(format))
|
||||
if (AF_FORMAT_IS_AC3(format)) {
|
||||
format = AF_FORMAT_AC3_NE;
|
||||
} else {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
|
||||
return 0;
|
||||
}
|
||||
switch(format){
|
||||
case AF_FORMAT_AC3_NE:
|
||||
case AF_FORMAT_S24_LE:
|
||||
@ -465,25 +409,24 @@ static int init(int rate, int channels, int format, int flags)
|
||||
format=AF_FORMAT_S16_LE;
|
||||
}
|
||||
//fill global ao_data
|
||||
ao_data.channels = channels;
|
||||
ao_data.samplerate = rate;
|
||||
ao_data.format = format;
|
||||
ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
|
||||
ao_data.bps = ao_data.channels.num * rate * (af_fmt2bits(format)>>3);
|
||||
if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
|
||||
mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
|
||||
mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, ao_data.channels.num, af_fmt2str_short(format));
|
||||
mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
|
||||
|
||||
//fill waveformatex
|
||||
ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
|
||||
wformat.Format.nChannels = channels;
|
||||
wformat.Format.cbSize = (ao_data.channels.num > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
|
||||
wformat.Format.nChannels = ao_data.channels.num;
|
||||
wformat.Format.nSamplesPerSec = rate;
|
||||
if (AF_FORMAT_IS_AC3(format)) {
|
||||
wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
|
||||
wformat.Format.wBitsPerSample = 16;
|
||||
wformat.Format.nBlockAlign = 4;
|
||||
} else {
|
||||
wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
|
||||
wformat.Format.wFormatTag = (ao_data.channels.num > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
|
||||
wformat.Format.wBitsPerSample = af_fmt2bits(format);
|
||||
wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
|
||||
}
|
||||
@ -503,8 +446,8 @@ static int init(int rate, int channels, int format, int flags)
|
||||
| DSBCAPS_GLOBALFOCUS /** Allows background playing */
|
||||
| DSBCAPS_CTRLVOLUME; /** volume control enabled */
|
||||
|
||||
if (channels > 2) {
|
||||
wformat.dwChannelMask = channel_mask[channels - 1];
|
||||
if (ao_data.channels.num > 2) {
|
||||
wformat.dwChannelMask = mp_chmap_to_waveext(&ao_data.channels);
|
||||
wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
|
||||
// Needed for 5.1 on emu101k - shit soundblaster
|
||||
|
@ -50,7 +50,7 @@ static const ao_info_t info =
|
||||
LIBAO_EXTERN(jack)
|
||||
|
||||
//! maximum number of channels supported, avoids lots of mallocs
|
||||
#define MAX_CHANS 8
|
||||
#define MAX_CHANS MP_NUM_CHANNELS
|
||||
static jack_port_t *ports[MAX_CHANS];
|
||||
static int num_ports; ///< Number of used ports == number of channels
|
||||
static jack_client_t *client;
|
||||
@ -202,7 +202,8 @@ static void print_help (void)
|
||||
);
|
||||
}
|
||||
|
||||
static int init(int rate, int channels, int format, int flags) {
|
||||
static int init(int rate, const struct mp_chmap *channels, int format, int flags)
|
||||
{
|
||||
const char **matching_ports = NULL;
|
||||
char *port_name = NULL;
|
||||
char *client_name = NULL;
|
||||
@ -222,10 +223,12 @@ static int init(int rate, int channels, int format, int flags) {
|
||||
print_help();
|
||||
return 0;
|
||||
}
|
||||
if (channels > MAX_CHANS) {
|
||||
mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels);
|
||||
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!client_name) {
|
||||
client_name = malloc(40);
|
||||
sprintf(client_name, "mpv [%d]", getpid());
|
||||
@ -249,9 +252,9 @@ static int init(int rate, int channels, int format, int flags) {
|
||||
goto err_out;
|
||||
}
|
||||
i = 1;
|
||||
num_ports = ao_data.channels.num;
|
||||
while (matching_ports[i]) i++;
|
||||
if (channels > i) channels = i;
|
||||
num_ports = channels;
|
||||
if (num_ports > i) num_ports = i;
|
||||
|
||||
// create out output ports
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
@ -281,10 +284,12 @@ static int init(int rate, int channels, int format, int flags) {
|
||||
/ (float)rate;
|
||||
callback_interval = 0;
|
||||
|
||||
ao_data.channels = channels;
|
||||
if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, num_ports))
|
||||
goto err_out;
|
||||
|
||||
ao_data.samplerate = rate;
|
||||
ao_data.format = AF_FORMAT_FLOAT_NE;
|
||||
ao_data.bps = channels * rate * sizeof(float);
|
||||
ao_data.bps = ao_data.channels.num * rate * sizeof(float);
|
||||
ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS;
|
||||
ao_data.outburst = CHUNK_SIZE;
|
||||
free(matching_ports);
|
||||
|
@ -101,7 +101,14 @@ static int init(struct ao *ao, char *params)
|
||||
ac->stream->codec->time_base.den = ao->samplerate;
|
||||
|
||||
ac->stream->codec->sample_rate = ao->samplerate;
|
||||
ac->stream->codec->channels = ao->channels;
|
||||
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_any(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
mp_chmap_reorder_to_lavc(&ao->channels);
|
||||
ac->stream->codec->channels = ao->channels.num;
|
||||
ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels);
|
||||
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
|
||||
|
||||
@ -243,36 +250,6 @@ out_takefirst:
|
||||
|
||||
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
|
||||
|
||||
switch (ao->channels) {
|
||||
case 1:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO;
|
||||
break;
|
||||
case 2:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
break;
|
||||
/* someone please check if these are what mplayer normally assumes
|
||||
case 3:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND;
|
||||
break;
|
||||
case 4:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2;
|
||||
break;
|
||||
*/
|
||||
case 5:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0;
|
||||
break;
|
||||
case 6:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1;
|
||||
break;
|
||||
case 8:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1;
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||||
"ao-lavc: unknown channel layout; hoping for the best\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
|
||||
return -1;
|
||||
|
||||
@ -282,11 +259,12 @@ out_takefirst:
|
||||
|
||||
if (ac->pcmhack) {
|
||||
ac->aframesize = 16384; // "enough"
|
||||
ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200;
|
||||
ac->buffer_size =
|
||||
ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200;
|
||||
} else {
|
||||
ac->aframesize = ac->stream->codec->frame_size;
|
||||
ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 +
|
||||
200;
|
||||
ac->buffer_size =
|
||||
ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200;
|
||||
}
|
||||
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
|
||||
ac->buffer_size = FF_MIN_BUFFER_SIZE;
|
||||
@ -304,10 +282,10 @@ out_takefirst:
|
||||
ac->offset_left = ac->offset;
|
||||
|
||||
//fill_ao_data:
|
||||
ao->outburst = ac->aframesize * ac->sample_size * ao->channels *
|
||||
ac->framecount;
|
||||
ao->outburst =
|
||||
ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount;
|
||||
ao->buffersize = ao->outburst * 2;
|
||||
ao->bps = ao->channels * ao->samplerate * ac->sample_size;
|
||||
ao->bps = ao->channels.num * ao->samplerate * ac->sample_size;
|
||||
ao->untimed = true;
|
||||
ao->priv = ac;
|
||||
|
||||
@ -348,7 +326,7 @@ static void uninit(struct ao *ao, bool cut_audio)
|
||||
// TRICK: append aframesize-1 samples to the end, then play() will
|
||||
// encode all it can
|
||||
size_t extralen =
|
||||
(ac->aframesize - 1) * ao->channels * ac->sample_size;
|
||||
(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,
|
||||
@ -392,12 +370,6 @@ static int encode(struct ao *ao, double apts, void *data)
|
||||
int status, gotpacket;
|
||||
|
||||
ac->aframecount++;
|
||||
if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) {
|
||||
reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
|
||||
ao->channels,
|
||||
ac->aframesize * ao->channels, ac->sample_size);
|
||||
}
|
||||
|
||||
if (data)
|
||||
ectx->audio_pts_offset = realapts - apts;
|
||||
@ -411,12 +383,18 @@ static int encode(struct ao *ao, double apts, void *data)
|
||||
frame->nb_samples = ac->aframesize;
|
||||
|
||||
if (ac->planarize) {
|
||||
void *data2 = talloc_size(ao, ac->aframesize * ao->channels * ac->sample_size);
|
||||
reorder_to_planar(data2, data, ac->sample_size, ao->channels, ac->aframesize);
|
||||
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;
|
||||
}
|
||||
|
||||
if (avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1)) {
|
||||
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_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n");
|
||||
return -1;
|
||||
}
|
||||
@ -523,7 +501,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
double pts = ao->pts;
|
||||
double outpts;
|
||||
|
||||
len /= ac->sample_size * ao->channels;
|
||||
len /= ac->sample_size * ao->channels.num;
|
||||
|
||||
if (!encode_lavc_start(ectx)) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN,
|
||||
@ -593,7 +571,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
if (ac->offset_left <= -len) {
|
||||
// skip whole frame
|
||||
ac->offset_left += len;
|
||||
return len * ac->sample_size * ao->channels;
|
||||
return len * ac->sample_size * ao->channels.num;
|
||||
} else {
|
||||
// skip part of this frame, buffer/encode the rest
|
||||
bufpos -= ac->offset_left;
|
||||
@ -604,11 +582,11 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
// 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 *
|
||||
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 *
|
||||
data = (char *) paddingbuf + ac->sample_size * ao->channels.num *
|
||||
ac->offset_left;
|
||||
bufpos -= ac->offset_left; // yes, negative!
|
||||
ptsoffset += ac->offset_left;
|
||||
@ -651,7 +629,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
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);
|
||||
(char *) data + ac->sample_size * bufpos * ao->channels.num);
|
||||
bufpos += ac->aframesize;
|
||||
}
|
||||
|
||||
@ -667,11 +645,10 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
ectx->next_in_pts = nextpts;
|
||||
}
|
||||
|
||||
return bufpos * ac->sample_size * ao->channels;
|
||||
return bufpos * ac->sample_size * ao->channels.num;
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_lavc = {
|
||||
.is_new = true,
|
||||
.encode = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"audio encoding using libavcodec",
|
||||
|
@ -48,11 +48,17 @@ static int init(struct ao *ao, char *params)
|
||||
{
|
||||
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;
|
||||
|
||||
int samplesize = af_fmt2bits(ao->format) / 8;
|
||||
ao->outburst = 256 * ao->channels * samplesize;
|
||||
ao->outburst = 256 * ao->channels.num * samplesize;
|
||||
// A "buffer" for about 0.2 seconds of audio
|
||||
ao->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * ao->outburst;
|
||||
ao->bps = ao->channels * ao->samplerate * samplesize;
|
||||
ao->bps = ao->channels.num * ao->samplerate * samplesize;
|
||||
priv->last_time = GetTimer();
|
||||
|
||||
return 0;
|
||||
@ -110,7 +116,6 @@ static float get_delay(struct ao *ao)
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_null = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"Null audio output",
|
||||
"null",
|
||||
|
@ -53,7 +53,7 @@ static const ao_info_t info =
|
||||
|
||||
LIBAO_EXTERN(openal)
|
||||
|
||||
#define MAX_CHANS 8
|
||||
#define MAX_CHANS MP_NUM_CHANNELS
|
||||
#define NUM_BUF 128
|
||||
#define CHUNK_SIZE 512
|
||||
static ALuint buffers[MAX_CHANS][NUM_BUF];
|
||||
@ -109,15 +109,29 @@ static void list_devices(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int init(int rate, int channels, int format, int flags) {
|
||||
struct speaker {
|
||||
int id;
|
||||
float pos[3];
|
||||
};
|
||||
|
||||
static const struct speaker speaker_pos[] = {
|
||||
{MP_SPEAKER_ID_FL, {-1, 0, 0.5}},
|
||||
{MP_SPEAKER_ID_FR, { 1, 0, 0.5}},
|
||||
{MP_SPEAKER_ID_FC, { 0, 0, 1}},
|
||||
{MP_SPEAKER_ID_LFE, { 0, 0, 0.1}},
|
||||
{MP_SPEAKER_ID_BL, {-1, 0, -1}},
|
||||
{MP_SPEAKER_ID_BR, { 1, 0, -1}},
|
||||
{MP_SPEAKER_ID_BC, { 0, 0, -1}},
|
||||
{MP_SPEAKER_ID_SL, {-1, 0, 0}},
|
||||
{MP_SPEAKER_ID_SR, { 1, 0, 0}},
|
||||
{-1},
|
||||
};
|
||||
|
||||
static int init(int rate, const struct mp_chmap *channels, int format,
|
||||
int flags)
|
||||
{
|
||||
float position[3] = {0, 0, 0};
|
||||
float direction[6] = {0, 0, 1, 0, -1, 0};
|
||||
float sppos[MAX_CHANS][3] = {
|
||||
{-1, 0, 0.5}, {1, 0, 0.5},
|
||||
{-1, 0, -1}, {1, 0, -1},
|
||||
{0, 0, 1}, {0, 0, 0.1},
|
||||
{-1, 0, 0}, {1, 0, 0},
|
||||
};
|
||||
ALCdevice *dev = NULL;
|
||||
ALCcontext *ctx = NULL;
|
||||
ALCint freq = 0;
|
||||
@ -137,9 +151,22 @@ static int init(int rate, int channels, int format, int flags) {
|
||||
list_devices();
|
||||
goto err_out;
|
||||
}
|
||||
if (channels > MAX_CHANS) {
|
||||
mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels);
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (i = 0; speaker_pos[i].id != -1; i++)
|
||||
mp_chmap_sel_add_speaker(&sel, speaker_pos[i].id);
|
||||
if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
|
||||
goto err_out;
|
||||
struct speaker speakers[MAX_CHANS];
|
||||
for (i = 0; i < ao_data.channels.num; i++) {
|
||||
speakers[i].id = -1;
|
||||
for (int n = 0; speaker_pos[n].id >= 0; n++) {
|
||||
if (speaker_pos[n].id == ao_data.channels.speaker[i])
|
||||
speakers[i] = speaker_pos[n];
|
||||
}
|
||||
if (speakers[i].id < 0) {
|
||||
mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Unknown channel layout\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
dev = alcOpenDevice(device);
|
||||
if (!dev) {
|
||||
@ -150,25 +177,22 @@ static int init(int rate, int channels, int format, int flags) {
|
||||
alcMakeContextCurrent(ctx);
|
||||
alListenerfv(AL_POSITION, position);
|
||||
alListenerfv(AL_ORIENTATION, direction);
|
||||
alGenSources(channels, sources);
|
||||
for (i = 0; i < channels; i++) {
|
||||
alGenSources(ao_data.channels.num, sources);
|
||||
for (i = 0; i < ao_data.channels.num; i++) {
|
||||
cur_buf[i] = 0;
|
||||
unqueue_buf[i] = 0;
|
||||
alGenBuffers(NUM_BUF, buffers[i]);
|
||||
alSourcefv(sources[i], AL_POSITION, sppos[i]);
|
||||
alSourcefv(sources[i], AL_POSITION, speakers[i].pos);
|
||||
alSource3f(sources[i], AL_VELOCITY, 0, 0, 0);
|
||||
}
|
||||
if (channels == 1)
|
||||
alSource3f(sources[0], AL_POSITION, 0, 0, 1);
|
||||
ao_data.channels = channels;
|
||||
alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
|
||||
if (alcGetError(dev) == ALC_NO_ERROR && freq)
|
||||
rate = freq;
|
||||
ao_data.samplerate = rate;
|
||||
ao_data.format = AF_FORMAT_S16_NE;
|
||||
ao_data.bps = channels * rate * 2;
|
||||
ao_data.bps = ao_data.channels.num * rate * 2;
|
||||
ao_data.buffersize = CHUNK_SIZE * NUM_BUF;
|
||||
ao_data.outburst = channels * CHUNK_SIZE;
|
||||
ao_data.outburst = ao_data.channels.num * CHUNK_SIZE;
|
||||
tmpbuf = malloc(CHUNK_SIZE);
|
||||
free(device);
|
||||
return 1;
|
||||
@ -200,7 +224,7 @@ static void uninit(int immed) {
|
||||
static void unqueue_buffers(void) {
|
||||
ALint p;
|
||||
int s;
|
||||
for (s = 0; s < ao_data.channels; s++) {
|
||||
for (s = 0; s < ao_data.channels.num; s++) {
|
||||
int till_wrap = NUM_BUF - unqueue_buf[s];
|
||||
alGetSourcei(sources[s], AL_BUFFERS_PROCESSED, &p);
|
||||
if (p >= till_wrap) {
|
||||
@ -219,7 +243,7 @@ static void unqueue_buffers(void) {
|
||||
* \brief stop playing and empty buffers (for seeking/pause)
|
||||
*/
|
||||
static void reset(void) {
|
||||
alSourceStopv(ao_data.channels, sources);
|
||||
alSourceStopv(ao_data.channels.num, sources);
|
||||
unqueue_buffers();
|
||||
}
|
||||
|
||||
@ -227,14 +251,14 @@ static void reset(void) {
|
||||
* \brief stop playing, keep buffers (for pause)
|
||||
*/
|
||||
static void audio_pause(void) {
|
||||
alSourcePausev(ao_data.channels, sources);
|
||||
alSourcePausev(ao_data.channels.num, sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief resume playing, after audio_pause()
|
||||
*/
|
||||
static void audio_resume(void) {
|
||||
alSourcePlayv(ao_data.channels, sources);
|
||||
alSourcePlayv(ao_data.channels.num, sources);
|
||||
}
|
||||
|
||||
static int get_space(void) {
|
||||
@ -243,7 +267,7 @@ static int get_space(void) {
|
||||
alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
|
||||
queued = NUM_BUF - queued - 3;
|
||||
if (queued < 0) return 0;
|
||||
return queued * CHUNK_SIZE * ao_data.channels;
|
||||
return queued * CHUNK_SIZE * ao_data.channels.num;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,22 +278,22 @@ static int play(void *data, int len, int flags) {
|
||||
int i, j, k;
|
||||
int ch;
|
||||
int16_t *d = data;
|
||||
len /= ao_data.channels * CHUNK_SIZE;
|
||||
len /= ao_data.channels.num * CHUNK_SIZE;
|
||||
for (i = 0; i < len; i++) {
|
||||
for (ch = 0; ch < ao_data.channels; ch++) {
|
||||
for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels)
|
||||
for (ch = 0; ch < ao_data.channels.num; ch++) {
|
||||
for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels.num)
|
||||
tmpbuf[j] = d[k];
|
||||
alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf,
|
||||
CHUNK_SIZE, ao_data.samplerate);
|
||||
alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]);
|
||||
cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF;
|
||||
}
|
||||
d += ao_data.channels * CHUNK_SIZE / 2;
|
||||
d += ao_data.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_data.channels, sources);
|
||||
return len * ao_data.channels * CHUNK_SIZE;
|
||||
alSourcePlayv(ao_data.channels.num, sources);
|
||||
return len * ao_data.channels.num * CHUNK_SIZE;
|
||||
}
|
||||
|
||||
static float get_delay(void) {
|
||||
|
@ -221,12 +221,12 @@ static int control(int cmd,void *arg){
|
||||
|
||||
// open & setup audio device
|
||||
// return: 1=success 0=fail
|
||||
static int init(int rate,int channels,int format,int flags){
|
||||
static int init(int rate,const struct mp_chmap *channels,int format,int flags){
|
||||
char *mixer_channels [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
|
||||
int oss_format;
|
||||
char *mdev = mixer_device, *mchan = mixer_channel;
|
||||
|
||||
mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,channels,
|
||||
mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,ao_data.channels.num,
|
||||
af_fmt2str_short(format));
|
||||
|
||||
if (ao_subdevice) {
|
||||
@ -339,25 +339,31 @@ ac3_retry:
|
||||
mp_msg(MSGT_AO,MSGL_V,"audio_setup: sample format: %s (requested: %s)\n",
|
||||
af_fmt2str_short(ao_data.format), af_fmt2str_short(format));
|
||||
|
||||
ao_data.channels = channels;
|
||||
if(!AF_FORMAT_IS_AC3(format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_alsa_def(&sel);
|
||||
if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
|
||||
return 0;
|
||||
int reqchannels = ao_data.channels.num;
|
||||
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
|
||||
if (ao_data.channels > 2) {
|
||||
if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1 ||
|
||||
ao_data.channels != channels ) {
|
||||
mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", channels);
|
||||
if (reqchannels > 2) {
|
||||
int nchannels = reqchannels;
|
||||
if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
|
||||
nchannels != reqchannels ) {
|
||||
mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int c = ao_data.channels-1;
|
||||
int c = reqchannels-1;
|
||||
if (ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
|
||||
mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", ao_data.channels);
|
||||
mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels);
|
||||
return 0;
|
||||
}
|
||||
ao_data.channels=c+1;
|
||||
if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, c + 1))
|
||||
return 0;
|
||||
}
|
||||
mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels, channels);
|
||||
mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels.num, reqchannels);
|
||||
// set rate
|
||||
ao_data.samplerate=rate;
|
||||
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
|
||||
@ -403,7 +409,7 @@ ac3_retry:
|
||||
#endif
|
||||
}
|
||||
|
||||
ao_data.bps=ao_data.channels;
|
||||
ao_data.bps=ao_data.channels.num;
|
||||
switch (ao_data.format & AF_FORMAT_BITS_MASK) {
|
||||
case AF_FORMAT_8BIT:
|
||||
break;
|
||||
@ -459,10 +465,10 @@ static void reset(void){
|
||||
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
|
||||
ioctl (audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
|
||||
if(!AF_FORMAT_IS_AC3(ao_data.format)) {
|
||||
if (ao_data.channels > 2)
|
||||
ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels);
|
||||
if (ao_data.channels.num > 2)
|
||||
ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels.num);
|
||||
else {
|
||||
int c = ao_data.channels-1;
|
||||
int c = ao_data.channels.num-1;
|
||||
ioctl (audio_fd, SNDCTL_DSP_STEREO, &c);
|
||||
}
|
||||
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
|
||||
|
@ -69,7 +69,7 @@ static void fput32le(uint32_t val, FILE *fp)
|
||||
|
||||
static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
|
||||
{
|
||||
bool use_waveex = ao->channels >= 5 && ao->channels <= 8;
|
||||
bool use_waveex = true;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
|
||||
WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
|
||||
@ -86,30 +86,17 @@ static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
|
||||
fput32le(WAV_ID_FMT, fp);
|
||||
fput32le(fmt_chunk_size, fp);
|
||||
fput16le(use_waveex ? WAV_ID_FORMAT_EXTENSIBLE : fmt, fp);
|
||||
fput16le(ao->channels, fp);
|
||||
fput16le(ao->channels.num, fp);
|
||||
fput32le(ao->samplerate, fp);
|
||||
fput32le(ao->bps, fp);
|
||||
fput16le(ao->channels * (bits / 8), fp);
|
||||
fput16le(ao->channels.num * (bits / 8), fp);
|
||||
fput16le(bits, fp);
|
||||
|
||||
if (use_waveex) {
|
||||
// Extension chunk
|
||||
fput16le(22, fp);
|
||||
fput16le(bits, fp);
|
||||
switch (ao->channels) {
|
||||
case 5:
|
||||
fput32le(0x0607, fp); // L R C Lb Rb
|
||||
break;
|
||||
case 6:
|
||||
fput32le(0x060f, fp); // L R C Lb Rb LFE
|
||||
break;
|
||||
case 7:
|
||||
fput32le(0x0727, fp); // L R C Cb Ls Rs LFE
|
||||
break;
|
||||
case 8:
|
||||
fput32le(0x063f, fp); // L R C Lb Rb Ls Rs LFE
|
||||
break;
|
||||
}
|
||||
fput32le(mp_chmap_to_waveext(&ao->channels), fp);
|
||||
// 2 bytes format + 14 bytes guid
|
||||
fput32le(fmt, fp);
|
||||
fput32le(0x00100000, fp);
|
||||
@ -159,14 +146,19 @@ static int init(struct ao *ao, char *params)
|
||||
}
|
||||
}
|
||||
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
|
||||
ao->outburst = 65536;
|
||||
ao->bps = ao->channels * ao->samplerate * (af_fmt2bits(ao->format) / 8);
|
||||
ao->bps = ao->channels.num * ao->samplerate * (af_fmt2bits(ao->format) / 8);
|
||||
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\n"
|
||||
"PCM: Samplerate: %d Hz Channels: %d Format: %s\n",
|
||||
priv->outputfilename,
|
||||
priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
|
||||
ao->channels, af_fmt2str_short(ao->format));
|
||||
ao->channels.num, af_fmt2str_short(ao->format));
|
||||
mp_tmsg(MSGT_AO, MSGL_INFO,
|
||||
"[AO PCM] Info: Faster dumping is achieved with -no-video\n"
|
||||
"[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
|
||||
@ -222,20 +214,12 @@ static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
if (ao->channels == 5 || ao->channels == 6 || ao->channels == 8) {
|
||||
int frame_size = af_fmt2bits(ao->format) / 8;
|
||||
len -= len % (frame_size * ao->channels);
|
||||
reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
|
||||
ao->channels, len / frame_size, frame_size);
|
||||
}
|
||||
fwrite(data, len, 1, priv->fp);
|
||||
priv->data_length += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_pcm = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"RAW PCM/WAVE file writer audio output",
|
||||
"pcm",
|
||||
|
@ -273,9 +273,15 @@ static int init(struct ao *ao, char *params)
|
||||
if (pa_device == paNoDevice)
|
||||
goto error_exit;
|
||||
|
||||
// The actual channel order probably depends on the platform.
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext_def(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
goto error_exit;
|
||||
|
||||
PaStreamParameters sp = {
|
||||
.device = pa_device,
|
||||
.channelCount = ao->channels,
|
||||
.channelCount = ao->channels.num,
|
||||
.suggestedLatency
|
||||
= Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
|
||||
};
|
||||
@ -298,7 +304,7 @@ static int init(struct ao *ao, char *params)
|
||||
|
||||
ao->format = fmt->mp_format;
|
||||
sp.sampleFormat = fmt->pa_format;
|
||||
priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8);
|
||||
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)))
|
||||
@ -413,7 +419,6 @@ static void resume(struct ao *ao)
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_portaudio = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"PortAudio",
|
||||
"portaudio",
|
||||
|
@ -143,6 +143,59 @@ static const struct format_map {
|
||||
{AF_FORMAT_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
static const int speaker_map[][2] = {
|
||||
{PA_CHANNEL_POSITION_MONO, MP_SPEAKER_ID_FC},
|
||||
{PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL},
|
||||
{PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR},
|
||||
{PA_CHANNEL_POSITION_FRONT_CENTER, MP_SPEAKER_ID_FC},
|
||||
{PA_CHANNEL_POSITION_REAR_CENTER, MP_SPEAKER_ID_BC},
|
||||
{PA_CHANNEL_POSITION_REAR_LEFT, MP_SPEAKER_ID_BL},
|
||||
{PA_CHANNEL_POSITION_REAR_RIGHT, MP_SPEAKER_ID_BR},
|
||||
{PA_CHANNEL_POSITION_LFE, MP_SPEAKER_ID_LFE},
|
||||
{PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, MP_SPEAKER_ID_FLC},
|
||||
{PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, MP_SPEAKER_ID_FRC},
|
||||
{PA_CHANNEL_POSITION_SIDE_LEFT, MP_SPEAKER_ID_SL},
|
||||
{PA_CHANNEL_POSITION_SIDE_RIGHT, MP_SPEAKER_ID_SR},
|
||||
{PA_CHANNEL_POSITION_TOP_CENTER, MP_SPEAKER_ID_TC},
|
||||
{PA_CHANNEL_POSITION_TOP_FRONT_LEFT, MP_SPEAKER_ID_TFL},
|
||||
{PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, MP_SPEAKER_ID_TFR},
|
||||
{PA_CHANNEL_POSITION_TOP_FRONT_CENTER, MP_SPEAKER_ID_TFC},
|
||||
{PA_CHANNEL_POSITION_TOP_REAR_LEFT, MP_SPEAKER_ID_TBL},
|
||||
{PA_CHANNEL_POSITION_TOP_REAR_RIGHT, MP_SPEAKER_ID_TBR},
|
||||
{PA_CHANNEL_POSITION_TOP_REAR_CENTER, MP_SPEAKER_ID_TBC},
|
||||
{PA_CHANNEL_POSITION_INVALID, -1}
|
||||
};
|
||||
|
||||
static bool chmap_pa_from_mp(pa_channel_map *dst, struct mp_chmap *src)
|
||||
{
|
||||
if (src->num > PA_CHANNELS_MAX)
|
||||
return false;
|
||||
dst->channels = src->num;
|
||||
for (int n = 0; n < src->num; n++) {
|
||||
int mp_speaker = src->speaker[n];
|
||||
int pa_speaker = PA_CHANNEL_POSITION_INVALID;
|
||||
for (int i = 0; speaker_map[i][1] != -1; i++) {
|
||||
if (speaker_map[i][1] == mp_speaker) {
|
||||
pa_speaker = speaker_map[i][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pa_speaker == PA_CHANNEL_POSITION_INVALID)
|
||||
return false;
|
||||
dst->map[n] = pa_speaker;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool select_chmap(struct ao *ao, pa_channel_map *dst)
|
||||
{
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (int n = 0; speaker_map[n][1] != -1; n++)
|
||||
mp_chmap_sel_add_speaker(&sel, speaker_map[n][1]);
|
||||
return ao_chmap_sel_adjust(ao, &sel, &ao->channels) &&
|
||||
chmap_pa_from_mp(dst, &ao->channels);
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
struct priv *priv = ao->priv;
|
||||
@ -209,30 +262,6 @@ static int init(struct ao *ao, char *params)
|
||||
priv->broken_pause = true;
|
||||
}
|
||||
|
||||
ss.channels = ao->channels;
|
||||
ss.rate = ao->samplerate;
|
||||
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
mp_msg(MSGT_AO, MSGL_V,
|
||||
"AO: [pulse] Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
ss.format = fmt_map->pa_format;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss)) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
|
||||
ao->bps = pa_bytes_per_second(&ss);
|
||||
|
||||
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
|
||||
goto fail;
|
||||
@ -260,6 +289,32 @@ static int init(struct ao *ao, char *params)
|
||||
if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
ss.channels = ao->channels.num;
|
||||
ss.rate = ao->samplerate;
|
||||
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
mp_msg(MSGT_AO, MSGL_V,
|
||||
"AO: [pulse] Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
ss.format = fmt_map->pa_format;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss)) {
|
||||
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!select_chmap(ao, &map))
|
||||
goto fail;
|
||||
|
||||
ao->bps = pa_bytes_per_second(&ss);
|
||||
|
||||
if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss,
|
||||
&map)))
|
||||
goto unlock_and_fail;
|
||||
@ -495,7 +550,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
const ao_control_vol_t *vol = arg;
|
||||
struct pa_cvolume volume;
|
||||
|
||||
pa_cvolume_reset(&volume, ao->channels);
|
||||
pa_cvolume_reset(&volume, ao->channels.num);
|
||||
if (volume.channels != 2)
|
||||
pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(vol->left));
|
||||
else {
|
||||
@ -533,7 +588,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_pulse = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"PulseAudio audio output",
|
||||
"pulse",
|
||||
|
@ -121,8 +121,16 @@ static int init(struct ao *ao, char *params)
|
||||
free(port);
|
||||
}
|
||||
|
||||
// Actual channel layout unknown.
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext_def(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
|
||||
rsd_free(priv->rd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate);
|
||||
rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels);
|
||||
rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num);
|
||||
|
||||
int rsd_format = set_format(ao);
|
||||
rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format);
|
||||
@ -132,7 +140,7 @@ static int init(struct ao *ao, char *params)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao->bps = ao->channels * ao->samplerate * af_fmt2bits(ao->format) / 8;
|
||||
ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -189,7 +197,6 @@ static float get_delay(struct ao *ao)
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_rsound = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
.name = "RSound output driver",
|
||||
.short_name = "rsound",
|
||||
|
@ -160,6 +160,13 @@ static int init(struct ao *ao, char *params)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext_def(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
|
||||
uninit(ao, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_AudioSpec desired, obtained;
|
||||
|
||||
int bytes = 0;
|
||||
@ -185,7 +192,7 @@ static int init(struct ao *ao, char *params)
|
||||
#endif
|
||||
}
|
||||
desired.freq = ao->samplerate;
|
||||
desired.channels = ao->channels;
|
||||
desired.channels = ao->channels.num;
|
||||
desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate * buflen));
|
||||
desired.callback = audio_callback;
|
||||
desired.userdata = ao;
|
||||
@ -236,9 +243,13 @@ static int init(struct ao *ao, char *params)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
|
||||
uninit(ao, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao->samplerate = obtained.freq;
|
||||
ao->channels = obtained.channels;
|
||||
ao->bps = ao->channels * ao->samplerate * bytes;
|
||||
ao->bps = ao->channels.num * ao->samplerate * bytes;
|
||||
ao->buffersize = obtained.size * bufcnt;
|
||||
ao->outburst = obtained.size;
|
||||
priv->buffer = av_fifo_alloc(ao->buffersize);
|
||||
@ -362,7 +373,6 @@ static float get_delay(struct ao *ao)
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_sdl = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"SDL Audio",
|
||||
"sdl",
|
||||
|
@ -20,11 +20,12 @@
|
||||
#define MPLAYER_AUDIO_OUT_INTERNAL_H
|
||||
|
||||
#include "core/options.h"
|
||||
#include "ao.h"
|
||||
|
||||
// prototypes:
|
||||
//static ao_info_t info;
|
||||
static int control(int cmd, void *arg);
|
||||
static int init(int rate,int channels,int format,int flags);
|
||||
static int init(int rate,const struct mp_chmap *channels,int format,int flags);
|
||||
static void uninit(int immed);
|
||||
static void reset(void);
|
||||
static int get_space(void);
|
||||
|
1373
audio/reorder_ch.c
1373
audio/reorder_ch.c
File diff suppressed because it is too large
Load Diff
@ -25,114 +25,6 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
// L - Left
|
||||
// R - Right
|
||||
// C - Center
|
||||
// Ls - Left Surround
|
||||
// Rs - Right Surround
|
||||
// Cs - Center Surround
|
||||
// Rls - Rear Left Surround
|
||||
// Rrs - Rear Right Surround
|
||||
|
||||
#define AF_LFE (1<<7)
|
||||
|
||||
#define AF_CHANNEL_LAYOUT_MONO ((100<<8)|1)
|
||||
#define AF_CHANNEL_LAYOUT_STEREO ((101<<8)|2)
|
||||
#define AF_CHANNEL_LAYOUT_1_0 AF_CHANNEL_LAYOUT_MONO // C
|
||||
#define AF_CHANNEL_LAYOUT_2_0 AF_CHANNEL_LAYOUT_STEREO // L R
|
||||
#define AF_CHANNEL_LAYOUT_2_1 ((102<<8)|3) // L R LFE
|
||||
#define AF_CHANNEL_LAYOUT_3_0_A ((103<<8)|3) // L R C
|
||||
#define AF_CHANNEL_LAYOUT_3_0_B ((104<<8)|3) // C L R
|
||||
#define AF_CHANNEL_LAYOUT_4_0_A ((105<<8)|4) // L R C Cs
|
||||
#define AF_CHANNEL_LAYOUT_4_0_B ((106<<8)|4) // C L R Cs
|
||||
#define AF_CHANNEL_LAYOUT_4_0_C ((107<<8)|4) // L R Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_0_A ((108<<8)|5) // L R C Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_0_B ((109<<8)|5) // L R Ls Rs C
|
||||
#define AF_CHANNEL_LAYOUT_5_0_C ((110<<8)|5) // L C R Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_0_D ((111<<8)|5) // C L R Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_1_A ((112<<8)|6|AF_LFE) // L R C LFE Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_1_B ((113<<8)|6|AF_LFE) // L R Ls Rs C LFE
|
||||
#define AF_CHANNEL_LAYOUT_5_1_C ((114<<8)|6|AF_LFE) // L C R Ls Rs LFE
|
||||
#define AF_CHANNEL_LAYOUT_5_1_D ((115<<8)|6|AF_LFE) // C L R Ls Rs LFE
|
||||
#define AF_CHANNEL_LAYOUT_5_1_E ((116<<8)|6|AF_LFE) // LFE L C R Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_5_1_F ((117<<8)|6|AF_LFE) // C L R LFE Ls Rs
|
||||
#define AF_CHANNEL_LAYOUT_6_1_A ((118<<8)|7|AF_LFE) // L R C LFE Ls Rs Cs
|
||||
#define AF_CHANNEL_LAYOUT_7_1_A ((119<<8)|8|AF_LFE) // L R C LFE Ls Rs Rls Rrs
|
||||
#define AF_CHANNEL_LAYOUT_7_1_B ((120<<8)|8|AF_LFE) // L R Ls Rs C LFE Rls Rrs
|
||||
#define AF_CHANNEL_LAYOUT_7_1_C ((121<<8)|8|AF_LFE) // L C R Ls Rs LFE Rls Rrs
|
||||
#define AF_CHANNEL_LAYOUT_7_1_D ((122<<8)|8|AF_LFE) // C L R Ls Rs Rls Rrs LFE
|
||||
#define AF_CHANNEL_LAYOUT_7_1_F ((123<<8)|8|AF_LFE) // C L R LFE Ls Rs Rls Rrs
|
||||
|
||||
|
||||
#define AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_B
|
||||
#define AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_B
|
||||
#define AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_B
|
||||
#define AF_CHANNEL_LAYOUT_MPLAYER_5CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT
|
||||
#define AF_CHANNEL_LAYOUT_MPLAYER_6CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT
|
||||
#define AF_CHANNEL_LAYOUT_MPLAYER_8CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT
|
||||
#define AF_CHANNEL_LAYOUT_AAC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_D
|
||||
#define AF_CHANNEL_LAYOUT_AAC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_D
|
||||
#define AF_CHANNEL_LAYOUT_AAC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_D
|
||||
#define AF_CHANNEL_LAYOUT_WAVEEX_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
|
||||
#define AF_CHANNEL_LAYOUT_WAVEEX_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
|
||||
#define AF_CHANNEL_LAYOUT_WAVEEX_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
|
||||
#define AF_CHANNEL_LAYOUT_LAVC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
|
||||
#define AF_CHANNEL_LAYOUT_LAVC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
|
||||
#define AF_CHANNEL_LAYOUT_LAVC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
|
||||
#define AF_CHANNEL_LAYOUT_VORBIS_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_C
|
||||
#define AF_CHANNEL_LAYOUT_VORBIS_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_C
|
||||
#define AF_CHANNEL_LAYOUT_VORBIS_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_C
|
||||
|
||||
#define AF_CHANNEL_MASK 0xFF
|
||||
#define AF_GET_CH_NUM(A) ((A)&0x7F)
|
||||
#define AF_GET_CH_NUM_WITH_LFE(A) ((A)&0xFF)
|
||||
#define AF_IS_SAME_CH_NUM(A,B) (((A)&0xFF)==((B)&0xFF))
|
||||
#define AF_IS_LAYOUT_SPECIFIED(A) ((A)&0xFFFFF800)
|
||||
#define AF_IS_LAYOUT_UNSPECIFIED(A) (!AF_IS_LAYOUT_SPECIFIED(A))
|
||||
|
||||
/// Optimized channel reorder between channel layouts with same channel number.
|
||||
void reorder_channel_copy(void *src,
|
||||
int src_layout,
|
||||
void *dest,
|
||||
int dest_layout,
|
||||
int samples,
|
||||
int samplesize);
|
||||
|
||||
/// Same with reorder_channel_copy, but works on single buffer.
|
||||
void reorder_channel(void *buf,
|
||||
int src_layout,
|
||||
int dest_layout,
|
||||
int samples,
|
||||
int samplesize);
|
||||
|
||||
// Channel layout definitions for different audio sources or targets
|
||||
// When specified channel number, they will be map to the specific layouts.
|
||||
#define AF_CHANNEL_LAYOUT_ALSA_DEFAULT 0
|
||||
#define AF_CHANNEL_LAYOUT_AAC_DEFAULT 1
|
||||
#define AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT 2
|
||||
#define AF_CHANNEL_LAYOUT_LAVC_DEFAULT 3
|
||||
#define AF_CHANNEL_LAYOUT_VORBIS_DEFAULT 4
|
||||
#define AF_CHANNEL_LAYOUT_SOURCE_NUM 5
|
||||
#define AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT AF_CHANNEL_LAYOUT_ALSA_DEFAULT
|
||||
|
||||
/// Optimized channel reorder between different audio sources and targets.
|
||||
void reorder_channel_copy_nch(void *src,
|
||||
int src_layout,
|
||||
void *dest,
|
||||
int dest_layout,
|
||||
int chnum,
|
||||
int samples,
|
||||
int samplesize);
|
||||
|
||||
/// Same with reorder_channel_copy_nch, but works on single buffer.
|
||||
void reorder_channel_nch(void *buf,
|
||||
int src_layout,
|
||||
int dest_layout,
|
||||
int chnum,
|
||||
int samples,
|
||||
int samplesize);
|
||||
|
||||
/// Utility function for planar audio conversions
|
||||
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,
|
||||
|
@ -175,11 +175,6 @@ const m_option_t mfopts_conf[]={
|
||||
|
||||
#include "audio/filter/af.h"
|
||||
extern struct af_cfg af_cfg; // Audio filter configuration, defined in libmpcodecs/dec_audio.c
|
||||
const m_option_t audio_filter_conf[]={
|
||||
{"list", &af_cfg.list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
|
||||
{"force", &af_cfg.force, CONF_TYPE_INT, CONF_RANGE, 0, 7, NULL},
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
|
||||
extern int mp_msg_levels[MSGT_MAX];
|
||||
extern int mp_msg_level_all;
|
||||
@ -281,6 +276,7 @@ const m_option_t msgl_config[]={
|
||||
};
|
||||
|
||||
extern const m_option_t lavc_decode_opts_conf[];
|
||||
extern const m_option_t ad_lavc_decode_opts_conf[];
|
||||
|
||||
#define OPT_BASE_STRUCT struct MPOpts
|
||||
|
||||
@ -418,7 +414,7 @@ const m_option_t common_opts[] = {
|
||||
// force video/audio rate:
|
||||
OPT_DOUBLE("fps", force_fps, CONF_MIN, 0),
|
||||
OPT_INTRANGE("srate", force_srate, 0, 1000, 8*48000),
|
||||
OPT_INTRANGE("channels", audio_output_channels, 0, 1, 8),
|
||||
OPT_CHMAP("channels", audio_output_channels, CONF_MIN, .min = 1),
|
||||
OPT_AUDIOFORMAT("format", audio_output_format, 0),
|
||||
OPT_FLOATRANGE("speed", playback_speed, 0, 0.01, 100.0),
|
||||
|
||||
@ -428,15 +424,12 @@ const m_option_t common_opts[] = {
|
||||
// ignore header-specified delay (dwStart)
|
||||
OPT_FLAG("ignore-start", ignore_start, 0),
|
||||
|
||||
OPT_FLOATRANGE("a52drc", drc_level, 0, 0, 2),
|
||||
|
||||
// ------------------------- codec/vfilter options --------------------
|
||||
|
||||
// MP3-only: select stereo/left/right
|
||||
{"stereo", &fakemono, CONF_TYPE_INT, CONF_RANGE, 0, 2, NULL},
|
||||
|
||||
{"af*", &af_cfg.list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
|
||||
{"af-adv", (void *) audio_filter_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
|
||||
|
||||
OPT_SETTINGSLIST("vf*", vf_settings, 0, &vf_obj_list),
|
||||
|
||||
@ -472,6 +465,8 @@ const m_option_t common_opts[] = {
|
||||
|
||||
{"lavdopts", (void *) lavc_decode_opts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
|
||||
{"lavfdopts", (void *) lavfdopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
|
||||
|
||||
{"ad-lavc", (void *) ad_lavc_decode_opts_conf, CONF_TYPE_SUBCONFIG},
|
||||
// ------------------------- subtitles options --------------------
|
||||
|
||||
OPT_STRINGLIST("sub", sub_name, 0),
|
||||
|
@ -684,20 +684,10 @@ static int mp_property_channels(m_option_t *prop, int action, void *arg,
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
switch (mpctx->sh_audio->channels) {
|
||||
case 1:
|
||||
*(char **) arg = talloc_strdup(NULL, "mono");
|
||||
break;
|
||||
case 2:
|
||||
*(char **) arg = talloc_strdup(NULL, "stereo");
|
||||
break;
|
||||
default:
|
||||
*(char **) arg = talloc_asprintf(NULL, "%d channels",
|
||||
mpctx->sh_audio->channels);
|
||||
}
|
||||
*(char **) arg = mp_chmap_to_str(&mpctx->sh_audio->channels);
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET:
|
||||
*(int *)arg = mpctx->sh_audio->channels;
|
||||
*(int *)arg = mpctx->sh_audio->channels.num;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
@ -2319,7 +2309,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
break;
|
||||
}
|
||||
af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
|
||||
af_reinit(sh_audio->afilter, af);
|
||||
af_reinit(sh_audio->afilter);
|
||||
}
|
||||
break;
|
||||
case MP_CMD_SHOW_CHAPTERS:
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "defaultopts.h"
|
||||
#include "core/options.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/chmap.h"
|
||||
|
||||
void set_default_mplayer_options(struct MPOpts *opts)
|
||||
{
|
||||
@ -72,10 +73,9 @@ void set_default_mplayer_options(struct MPOpts *opts)
|
||||
.audio_display = 1,
|
||||
.sub_visibility = 1,
|
||||
.extension_parsing = 1,
|
||||
.audio_output_channels = 2,
|
||||
.audio_output_channels = MP_CHMAP_INIT_STEREO,
|
||||
.audio_output_format = -1, // AF_FORMAT_UNKNOWN
|
||||
.playback_speed = 1.,
|
||||
.drc_level = 1.,
|
||||
.movie_aspect = -1.,
|
||||
.sub_auto = 1,
|
||||
.osd_bar_visible = 1,
|
||||
@ -93,6 +93,10 @@ void set_default_mplayer_options(struct MPOpts *opts)
|
||||
.workaround_bugs = 1, // autodetect
|
||||
.error_concealment = 3,
|
||||
},
|
||||
.ad_lavc_param = {
|
||||
.ac3drc = 1.,
|
||||
.downmix = 1,
|
||||
},
|
||||
.input = {
|
||||
.key_fifo_size = 7,
|
||||
.ar_delay = 200,
|
||||
|
@ -1604,6 +1604,48 @@ const m_option_type_t m_option_type_afmt = {
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
#include "audio/chmap.h"
|
||||
|
||||
static int parse_chmap(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
// min>0: at least min channels, min=0: empty ok, min=-1: invalid ok
|
||||
int min_ch = (opt->flags & M_OPT_MIN) ? opt->min : 1;
|
||||
|
||||
if (bstr_equals0(param, "help")) {
|
||||
mp_chmap_print_help(MSGT_CFGPARSER, MSGL_INFO);
|
||||
return M_OPT_EXIT - 1;
|
||||
}
|
||||
|
||||
if (param.len == 0 && min_ch >= 1)
|
||||
return M_OPT_MISSING_PARAM;
|
||||
|
||||
struct mp_chmap res = {0};
|
||||
if (!mp_chmap_from_str(&res, param)) {
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
|
||||
"Error parsing channel layout: %.*s\n", BSTR_P(param));
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
|
||||
if ((min_ch > 0 && !mp_chmap_is_valid(&res)) ||
|
||||
(min_ch >= 0 && mp_chmap_is_empty(&res)))
|
||||
{
|
||||
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
|
||||
"Invalid channel layout: %.*s\n", BSTR_P(param));
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
|
||||
*(struct mp_chmap *)dst = res;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_chmap = {
|
||||
.name = "Audio channels or channel map",
|
||||
.size = sizeof(struct mp_chmap *),
|
||||
.parse = parse_chmap,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
static int parse_timestring(struct bstr str, double *time, char endchar)
|
||||
{
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "core/bstr.h"
|
||||
#include "audio/chmap.h"
|
||||
|
||||
// m_option allows to parse, print and copy data of various types.
|
||||
|
||||
@ -60,6 +61,7 @@ extern const m_option_type_t m_option_type_afmt;
|
||||
extern const m_option_type_t m_option_type_color;
|
||||
extern const m_option_type_t m_option_type_geometry;
|
||||
extern const m_option_type_t m_option_type_size_box;
|
||||
extern const m_option_type_t m_option_type_chmap;
|
||||
|
||||
// Callback used by m_option_type_print_func options.
|
||||
typedef int (*m_opt_func_full_t)(const m_option_t *, const char *, const char *);
|
||||
@ -194,6 +196,7 @@ union m_option_value {
|
||||
struct m_color color;
|
||||
struct m_geometry geometry;
|
||||
struct m_geometry size_box;
|
||||
struct mp_chmap chmap;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@ -568,6 +571,9 @@ int m_option_required_params(const m_option_t *opt);
|
||||
#define OPT_AUDIOFORMAT(...) \
|
||||
OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_type_afmt)
|
||||
|
||||
#define OPT_CHMAP(...) \
|
||||
OPT_GENERAL(struct mp_chmap, __VA_ARGS__, .type = &m_option_type_chmap)
|
||||
|
||||
|
||||
#define M_CHOICES(choices) \
|
||||
.priv = (void *)&(const struct m_opt_choice_alternatives[]){ \
|
||||
|
@ -330,7 +330,7 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
|
||||
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
||||
"ID_AUDIO_RATE=%d\n", mpctx->sh_audio->samplerate);
|
||||
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
||||
"ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels);
|
||||
"ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels.num);
|
||||
start_pts = ds_get_next_pts(mpctx->sh_audio->ds);
|
||||
}
|
||||
if (video_start_pts != MP_NOPTS_VALUE) {
|
||||
@ -1700,6 +1700,12 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
mpctx->ao = ao_create(opts, mpctx->input);
|
||||
mpctx->ao->samplerate = opts->force_srate;
|
||||
mpctx->ao->format = opts->audio_output_format;
|
||||
// Automatic downmix
|
||||
if (mp_chmap_is_stereo(&opts->audio_output_channels) &&
|
||||
!mp_chmap_is_stereo(&mpctx->sh_audio->channels))
|
||||
{
|
||||
mp_chmap_from_channels(&mpctx->ao->channels, 2);
|
||||
}
|
||||
}
|
||||
ao = mpctx->ao;
|
||||
|
||||
@ -1716,6 +1722,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
if (!ao->initialized) {
|
||||
ao->buffersize = opts->ao_buffersize;
|
||||
ao->encode_lavc_ctx = mpctx->encode_lavc_ctx;
|
||||
mp_chmap_remove_useless_channels(&ao->channels,
|
||||
&opts->audio_output_channels);
|
||||
ao_init(ao, opts->audio_driver_list);
|
||||
if (!ao->initialized) {
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
||||
@ -1723,12 +1731,10 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
goto init_error;
|
||||
}
|
||||
ao->buffer.start = talloc_new(ao);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_INFO,
|
||||
"AO: [%s] %dHz %dch %s (%d bytes per sample)\n",
|
||||
ao->driver->info->short_name,
|
||||
ao->samplerate, ao->channels,
|
||||
af_fmt2str_short(ao->format),
|
||||
af_fmt2bits(ao->format) / 8);
|
||||
char *s = mp_audio_fmt_to_str(ao->samplerate, &ao->channels, ao->format);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_INFO, "AO: [%s] %s\n",
|
||||
ao->driver->info->short_name, s);
|
||||
talloc_free(s);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Description: %s\nAO: Author: %s\n",
|
||||
ao->driver->info->name, ao->driver->info->author);
|
||||
if (strlen(ao->driver->info->comment) > 0)
|
||||
@ -2295,7 +2301,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize)
|
||||
ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay
|
||||
- mpctx->audio_delay;
|
||||
bytes = ptsdiff * bps;
|
||||
bytes -= bytes % (ao->channels * af_fmt2bits(ao->format) / 8);
|
||||
bytes -= bytes % (ao->channels.num * af_fmt2bits(ao->format) / 8);
|
||||
|
||||
// ogg demuxers give packets without timing
|
||||
if (written_pts <= 1 && sh_audio->pts == MP_NOPTS_VALUE) {
|
||||
@ -2364,7 +2370,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
bool partial_fill = false;
|
||||
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
||||
bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK);
|
||||
int unitsize = ao->channels * af_fmt2bits(ao->format) / 8;
|
||||
int unitsize = ao->channels.num * af_fmt2bits(ao->format) / 8;
|
||||
|
||||
if (mpctx->paused)
|
||||
playsize = 1; // just initialize things (audio pts at least)
|
||||
|
@ -155,12 +155,11 @@ typedef struct MPOpts {
|
||||
|
||||
double force_fps;
|
||||
|
||||
int audio_output_channels;
|
||||
struct mp_chmap audio_output_channels;
|
||||
int audio_output_format;
|
||||
int force_srate;
|
||||
int dtshd;
|
||||
float playback_speed;
|
||||
float drc_level;
|
||||
struct m_obj_settings *vf_settings;
|
||||
float movie_aspect;
|
||||
int flip;
|
||||
@ -210,6 +209,12 @@ typedef struct MPOpts {
|
||||
char *avopt;
|
||||
} lavc_param;
|
||||
|
||||
struct ad_lavc_param {
|
||||
float ac3drc;
|
||||
int downmix;
|
||||
char *avopt;
|
||||
} ad_lavc_param;
|
||||
|
||||
struct lavfdopts {
|
||||
int probesize;
|
||||
int probescore;
|
||||
|
@ -340,7 +340,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
sh_audio->format = codec->codec_tag;
|
||||
|
||||
// probably unneeded
|
||||
sh_audio->channels = codec->channels;
|
||||
mp_chmap_from_channels(&sh_audio->channels, codec->channels);
|
||||
if (codec->channel_layout)
|
||||
mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
|
||||
sh_audio->samplerate = codec->sample_rate;
|
||||
sh_audio->i_bps = codec->bit_rate / 8;
|
||||
|
||||
|
@ -1349,7 +1349,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
|
||||
sh_a->format = track->a_formattag;
|
||||
sh_a->wf->wFormatTag = track->a_formattag;
|
||||
sh_a->channels = track->a_channels;
|
||||
mp_chmap_from_channels(&sh_a->channels, track->a_channels);
|
||||
sh_a->wf->nChannels = track->a_channels;
|
||||
sh_a->samplerate = (uint32_t) track->a_sfreq;
|
||||
sh_a->container_out_samplerate = track->a_osfreq;
|
||||
@ -1367,7 +1367,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
free(sh_a->wf);
|
||||
sh_a->wf = NULL;
|
||||
} else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */
|
||||
sh_a->wf->nAvgBytesPerSec = sh_a->channels * sh_a->samplerate * 2;
|
||||
sh_a->wf->nAvgBytesPerSec = sh_a->channels.num * sh_a->samplerate * 2;
|
||||
sh_a->wf->nBlockAlign = sh_a->wf->nAvgBytesPerSec;
|
||||
if (!strcmp(track->codec_id, MKV_A_PCM_BE))
|
||||
sh_a->format = mmioFOURCC('t', 'w', 'o', 's');
|
||||
@ -1539,7 +1539,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
char *data = sh_a->codecdata;
|
||||
memcpy(data + 0, "TTA1", 4);
|
||||
AV_WL16(data + 4, 1);
|
||||
AV_WL16(data + 6, sh_a->channels);
|
||||
AV_WL16(data + 6, sh_a->channels.num);
|
||||
AV_WL16(data + 8, sh_a->wf->wBitsPerSample);
|
||||
AV_WL32(data + 10, sh_a->samplerate);
|
||||
// Bogus: last frame won't be played.
|
||||
|
@ -31,12 +31,12 @@
|
||||
#include "audio/format.h"
|
||||
|
||||
|
||||
static int channels = 2;
|
||||
static struct mp_chmap channels = MP_CHMAP_INIT_STEREO;
|
||||
static int samplerate = 44100;
|
||||
static int format = AF_FORMAT_S16_NE;
|
||||
|
||||
const m_option_t demux_rawaudio_opts[] = {
|
||||
{ "channels", &channels, CONF_TYPE_INT,CONF_RANGE,1,8, NULL },
|
||||
{ "channels", &channels, &m_option_type_chmap, CONF_MIN, 1 },
|
||||
{ "rate", &samplerate, CONF_TYPE_INT,CONF_RANGE,1000,8*48000, NULL },
|
||||
{ "format", &format, CONF_TYPE_AFMT, 0, 0, 0, NULL },
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
@ -55,11 +55,12 @@ static demuxer_t* demux_rawaudio_open(demuxer_t* demuxer) {
|
||||
sh_audio->format = format;
|
||||
sh_audio->wf = w = malloc(sizeof(*w));
|
||||
w->wFormatTag = 0;
|
||||
w->nChannels = sh_audio->channels = channels;
|
||||
sh_audio->channels = channels;
|
||||
w->nChannels = sh_audio->channels.num;
|
||||
w->nSamplesPerSec = sh_audio->samplerate = samplerate;
|
||||
int samplesize = (af_fmt2bits(format) + 7) / 8;
|
||||
w->nAvgBytesPerSec = samplerate * samplesize * channels;
|
||||
w->nBlockAlign = channels * samplesize;
|
||||
w->nAvgBytesPerSec = samplerate * samplesize * w->nChannels;
|
||||
w->nBlockAlign = w->nChannels * samplesize;
|
||||
w->wBitsPerSample = 8 * samplesize;
|
||||
w->cbSize = 0;
|
||||
|
||||
@ -105,7 +106,7 @@ static void demux_rawaudio_seek(demuxer_t *demuxer,float rel_seek_secs,float aud
|
||||
else
|
||||
pos = base + (rel_seek_secs*sh_audio->i_bps);
|
||||
|
||||
pos -= (pos % (sh_audio->channels * sh_audio->samplesize) );
|
||||
pos -= (pos % (sh_audio->channels.num * sh_audio->samplesize) );
|
||||
stream_seek(s,pos);
|
||||
// printf("demux_rawaudio: streamtell=%d\n",(int)stream_tell(demuxer->stream));
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "codec_tags.h"
|
||||
|
||||
#include "audio/chmap.h"
|
||||
#include "aviheader.h"
|
||||
#include "ms_hdr.h"
|
||||
struct MPOpts;
|
||||
@ -96,8 +97,8 @@ typedef struct sh_audio {
|
||||
int samplerate;
|
||||
int container_out_samplerate;
|
||||
int samplesize;
|
||||
int channels;
|
||||
int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec)
|
||||
struct mp_chmap channels;
|
||||
int o_bps; // == samplerate*samplesize*channels.num (uncompr. bytes/sec)
|
||||
int i_bps; // == bitrate (compressed bytes/sec)
|
||||
// in buffers:
|
||||
int audio_in_minsize; // initial size to allocate for a_in_buffer if any
|
||||
|
10
stream/tv.c
10
stream/tv.c
@ -792,23 +792,25 @@ static demuxer_t* demux_open_tv(demuxer_t *demuxer)
|
||||
&sh_audio->samplerate);
|
||||
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLESIZE,
|
||||
&sh_audio->samplesize);
|
||||
int nchannels = sh_audio->channels.num;
|
||||
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS,
|
||||
&sh_audio->channels);
|
||||
&nchannels);
|
||||
mp_chmap_from_channels(&sh_audio->channels, nchannels);
|
||||
|
||||
sh_audio->gsh->codec = "mp-pcm";
|
||||
sh_audio->format = audio_format;
|
||||
|
||||
sh_audio->i_bps = sh_audio->o_bps =
|
||||
sh_audio->samplerate * sh_audio->samplesize *
|
||||
sh_audio->channels;
|
||||
sh_audio->channels.num;
|
||||
|
||||
// emulate WF for win32 codecs:
|
||||
sh_audio->wf = malloc(sizeof(*sh_audio->wf));
|
||||
sh_audio->wf->wFormatTag = sh_audio->format;
|
||||
sh_audio->wf->nChannels = sh_audio->channels;
|
||||
sh_audio->wf->nChannels = sh_audio->channels.num;
|
||||
sh_audio->wf->wBitsPerSample = sh_audio->samplesize * 8;
|
||||
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
|
||||
sh_audio->wf->nBlockAlign = sh_audio->samplesize * sh_audio->channels;
|
||||
sh_audio->wf->nBlockAlign = sh_audio->samplesize * sh_audio->channels.num;
|
||||
sh_audio->wf->nAvgBytesPerSec = sh_audio->i_bps;
|
||||
|
||||
mp_tmsg(MSGT_DECVIDEO, MSGL_V, " TV audio: %d channels, %d bits, %d Hz\n",
|
||||
|
Loading…
Reference in New Issue
Block a user