Improved pass-through (PCM+EAC3) support.

This commit is contained in:
Johns 2013-02-11 16:53:51 +01:00
parent 145d65ff01
commit 2cd667fb44
9 changed files with 451 additions and 269 deletions

View File

@ -1,6 +1,7 @@
User johns User johns
Date: Date:
Improved pass-through (PCM+EAC3) support.
Support VDR 1.7.36 new build system. Support VDR 1.7.36 new build system.
Improves VDPAU display preemption handling. Improves VDPAU display preemption handling.
Add modifiers to X11 remote key names (from Sibbi). Add modifiers to X11 remote key names (from Sibbi).

View File

@ -1,6 +1,6 @@
@file README.txt @brief A software HD output device for VDR @file README.txt @brief A software HD output device for VDR
Copyright (c) 2011, 2012 by Johns. All Rights Reserved. Copyright (c) 2011 - 2013 by Johns. All Rights Reserved.
Contributor(s): Contributor(s):
@ -89,8 +89,8 @@ Setup: environment
only if alsa is configured only if alsa is configured
ALSA_DEVICE=default ALSA_DEVICE=default
alsa PCM device name alsa PCM device name
ALSA_AC3_DEVICE= ALSA_PASSTHROUGH_DEVICE=
alsa AC3/pass-though device name alsa pass-though (AC3,EAC3,DTS,...) device name
ALSA_MIXER=default ALSA_MIXER=default
alsa control device name alsa control device name
ALSA_MIXER_CHANNEL=PCM ALSA_MIXER_CHANNEL=PCM
@ -99,8 +99,8 @@ Setup: environment
only if oss is configured only if oss is configured
OSS_AUDIODEV=/dev/dsp OSS_AUDIODEV=/dev/dsp
oss dsp device name oss dsp device name
OSS_AC3_AUDIODEV= OSS_PASSTHROUGHDEV=
oss AC3/pass-though device name oss pass-though (AC3,EAC3,DTS,...) device name
OSS_MIXERDEV=/dev/mixer OSS_MIXERDEV=/dev/mixer
oss mixer device name oss mixer device name
OSS_MIXER_CHANNEL=pcm OSS_MIXER_CHANNEL=pcm
@ -156,13 +156,16 @@ Setup: /etc/vdr/setup.conf
delay audio or delay video delay audio or delay video
softhddevice.AudioPassthrough = 0 softhddevice.AudioPassthrough = 0
0 = none, 1 = AC-3 0 = none, 1 = PCM, 2 = MPA, 4 = AC-3, 8 = EAC-3
for AC-3 the pass-through device is used. for PCM/AC-3/EAC-3 the pass-through device is used and the audio
stream is passed undecoded to the output device.
z.b. 12 = AC-3+EAC-3, 13 = PCM+AC-3+EAC-3
note: MPA/DTS/TrueHD/... aren't supported yet
softhddevice.AudioDownmix = 0 softhddevice.AudioDownmix = 0
0 = none, 1 = downmix 0 = none, 1 = downmix
Use ffmpeg/libav downmix AC-3 to stereo. Use ffmpeg/libav downmix of AC-3/EAC-3 audio to stereo.
softhddevice.AudioSoftvol = 0 softhddevice.AudioSoftvol = 0
0 = off, use hardware volume control 0 = off, use hardware volume control

101
audio.c
View File

@ -129,7 +129,7 @@ static const char *AudioModuleName; ///< which audio module to use
/// Selected audio module. /// Selected audio module.
static const AudioModule *AudioUsedModule = &NoopModule; static const AudioModule *AudioUsedModule = &NoopModule;
static const char *AudioPCMDevice; ///< PCM device name static const char *AudioPCMDevice; ///< PCM device name
static const char *AudioAC3Device; ///< AC3 device name static const char *AudioPassthroughDevice; ///< Passthrough device name
static const char *AudioMixerDevice; ///< mixer device name static const char *AudioMixerDevice; ///< mixer device name
static const char *AudioMixerChannel; ///< mixer channel name static const char *AudioMixerChannel; ///< mixer channel name
static char AudioDoingInit; ///> flag in init, reduce error static char AudioDoingInit; ///> flag in init, reduce error
@ -620,7 +620,7 @@ static void AudioResample(const int16_t * in, int in_chan, int frames,
typedef struct _audio_ring_ring_ typedef struct _audio_ring_ring_
{ {
char FlushBuffers; ///< flag: flush buffers char FlushBuffers; ///< flag: flush buffers
char UseAc3; ///< flag: use ac3 pass-through char Passthrough; ///< flag: use pass-through (AC3, ...)
int16_t PacketSize; ///< packet size int16_t PacketSize; ///< packet size
unsigned HwSampleRate; ///< hardware sample rate in Hz unsigned HwSampleRate; ///< hardware sample rate in Hz
unsigned HwChannels; ///< hardware number of channels unsigned HwChannels; ///< hardware number of channels
@ -642,14 +642,14 @@ static unsigned AudioStartThreshold; ///< start play, if filled
** **
** @param sample_rate sample-rate frequency ** @param sample_rate sample-rate frequency
** @param channels number of channels ** @param channels number of channels
** @param use_ac3 use ac3/pass-through device ** @param passthrough use /pass-through (AC3, ...) device
** **
** @retval -1 error ** @retval -1 error
** @retval 0 okay ** @retval 0 okay
** **
** @note this function shouldn't fail. Checks are done during AudoInit. ** @note this function shouldn't fail. Checks are done during AudoInit.
*/ */
static int AudioRingAdd(unsigned sample_rate, int channels, int use_ac3) static int AudioRingAdd(unsigned sample_rate, int channels, int passthrough)
{ {
unsigned u; unsigned u;
@ -680,7 +680,7 @@ static int AudioRingAdd(unsigned sample_rate, int channels, int use_ac3)
// FIXME: don't flush buffers here // FIXME: don't flush buffers here
AudioRing[AudioRingWrite].FlushBuffers = 1; AudioRing[AudioRingWrite].FlushBuffers = 1;
AudioRing[AudioRingWrite].UseAc3 = use_ac3; AudioRing[AudioRingWrite].Passthrough = passthrough;
AudioRing[AudioRingWrite].PacketSize = 0; AudioRing[AudioRingWrite].PacketSize = 0;
AudioRing[AudioRingWrite].InSampleRate = sample_rate; AudioRing[AudioRingWrite].InSampleRate = sample_rate;
AudioRing[AudioRingWrite].InChannels = channels; AudioRing[AudioRingWrite].InChannels = channels;
@ -836,8 +836,9 @@ static int AlsaPlayRingbuffer(void)
if (!avail) { // full or buffer empty if (!avail) { // full or buffer empty
break; break;
} }
// muting ac3, can produce disturbance // muting pass-through ac3, can produce disturbance
if (AudioMute || (AudioSoftVolume && !AudioRing[AudioRingRead].UseAc3)) { if (AudioMute || (AudioSoftVolume
&& !AudioRing[AudioRingRead].Passthrough)) {
// FIXME: quick&dirty cast // FIXME: quick&dirty cast
AudioSoftAmplifier((int16_t *) p, avail); AudioSoftAmplifier((int16_t *) p, avail);
// FIXME: if not all are written, we double amplify them // FIXME: if not all are written, we double amplify them
@ -984,23 +985,23 @@ static int AlsaThread(void)
/** /**
** Open alsa pcm device. ** Open alsa pcm device.
** **
** @param use_ac3 use ac3/pass-through device ** @param passthrough use pass-through (AC3, ...) device
*/ */
static snd_pcm_t *AlsaOpenPCM(int use_ac3) static snd_pcm_t *AlsaOpenPCM(int passthrough)
{ {
const char *device; const char *device;
snd_pcm_t *handle; snd_pcm_t *handle;
int err; int err;
// &&|| hell // &&|| hell
if (!(use_ac3 && ((device = AudioAC3Device) if (!(passthrough && ((device = AudioPassthroughDevice)
|| (device = getenv("ALSA_AC3_DEVICE")))) || (device = getenv("ALSA_PASSTHROUGH_DEVICE"))))
&& !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) { && !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) {
device = "default"; device = "default";
} }
if (!AudioDoingInit) { // reduce blabla during init if (!AudioDoingInit) { // reduce blabla during init
Info(_("audio/alsa: using %sdevice '%s'\n"), use_ac3 ? "ac3 " : "", Info(_("audio/alsa: using %sdevice '%s'\n"),
device); passthrough ? "pass-through " : "", device);
} }
// open none blocking; if device is already used, we don't want wait // open none blocking; if device is already used, we don't want wait
if ((err = if ((err =
@ -1167,7 +1168,7 @@ static int64_t AlsaGetDelay(void)
** **
** @param freq sample frequency ** @param freq sample frequency
** @param channels number of channels ** @param channels number of channels
** @param use_ac3 use ac3/pass-through device ** @param passthrough use pass-through (AC3, ...) device
** **
** @retval 0 everything ok ** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination ** @retval 1 didn't support frequency/channels combination
@ -1175,7 +1176,7 @@ static int64_t AlsaGetDelay(void)
** **
** @todo FIXME: remove pointer for freq + channels ** @todo FIXME: remove pointer for freq + channels
*/ */
static int AlsaSetup(int *freq, int *channels, int use_ac3) static int AlsaSetup(int *freq, int *channels, int passthrough)
{ {
snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
@ -1183,7 +1184,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
int delay; int delay;
if (!AlsaPCMHandle) { // alsa not running yet if (!AlsaPCMHandle) { // alsa not running yet
// FIXME: if open fails for ac3, we never recover // FIXME: if open fails for fe. pass-through, we never recover
return -1; return -1;
} }
if (1) { // close+open to fix HDMI no sound bug if (1) { // close+open to fix HDMI no sound bug
@ -1193,7 +1194,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
// FIXME: need lock // FIXME: need lock
AlsaPCMHandle = NULL; // other threads should check handle AlsaPCMHandle = NULL; // other threads should check handle
snd_pcm_close(handle); snd_pcm_close(handle);
if (!(handle = AlsaOpenPCM(use_ac3))) { if (!(handle = AlsaOpenPCM(passthrough))) {
return -1; return -1;
} }
AlsaPCMHandle = handle; AlsaPCMHandle = handle;
@ -1453,7 +1454,7 @@ static int OssPlayRingbuffer(void)
break; // bi.bytes could become negative! break; // bi.bytes could become negative!
} }
if (AudioSoftVolume && !AudioRing[AudioRingRead].UseAc3) { if (AudioSoftVolume && !AudioRing[AudioRingRead].Passthrough) {
// FIXME: quick&dirty cast // FIXME: quick&dirty cast
AudioSoftAmplifier((int16_t *) p, bi.bytes); AudioSoftAmplifier((int16_t *) p, bi.bytes);
// FIXME: if not all are written, we double amplify them // FIXME: if not all are written, we double amplify them
@ -1560,22 +1561,22 @@ static int OssThread(void)
/** /**
** Open OSS pcm device. ** Open OSS pcm device.
** **
** @param use_ac3 use ac3/pass-through device ** @param passthrough use pass-through (AC3, ...) device
*/ */
static int OssOpenPCM(int use_ac3) static int OssOpenPCM(int passthrough)
{ {
const char *device; const char *device;
int fildes; int fildes;
// &&|| hell // &&|| hell
if (!(use_ac3 && ((device = AudioAC3Device) if (!(passthrough && ((device = AudioPassthroughDevice)
|| (device = getenv("OSS_AC3_AUDIODEV")))) || (device = getenv("OSS_PASSTHROUGHDEV"))))
&& !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) { && !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) {
device = "/dev/dsp"; device = "/dev/dsp";
} }
if (!AudioDoingInit) { if (!AudioDoingInit) {
Info(_("audio/oss: using %sdevice '%s'\n"), use_ac3 ? "ac3 " : "", Info(_("audio/oss: using %sdevice '%s'\n"),
device); passthrough ? "pass-through " : "", device);
} }
if ((fildes = open(device, O_WRONLY)) < 0) { if ((fildes = open(device, O_WRONLY)) < 0) {
@ -1724,13 +1725,13 @@ static int64_t OssGetDelay(void)
** **
** @param sample_rate sample rate/frequency ** @param sample_rate sample rate/frequency
** @param channels number of channels ** @param channels number of channels
** @param use_ac3 use ac3/pass-through device ** @param passthrough use pass-through (AC3, ...) device
** **
** @retval 0 everything ok ** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination ** @retval 1 didn't support frequency/channels combination
** @retval -1 something gone wrong ** @retval -1 something gone wrong
*/ */
static int OssSetup(int *sample_rate, int *channels, int use_ac3) static int OssSetup(int *sample_rate, int *channels, int passthrough)
{ {
int ret; int ret;
int tmp; int tmp;
@ -1738,7 +1739,7 @@ static int OssSetup(int *sample_rate, int *channels, int use_ac3)
audio_buf_info bi; audio_buf_info bi;
if (OssPcmFildes == -1) { // OSS not ready if (OssPcmFildes == -1) { // OSS not ready
// FIXME: if open fails for ac3, we never recover // FIXME: if open fails for fe. pass-through, we never recover
return -1; return -1;
} }
@ -1748,7 +1749,7 @@ static int OssSetup(int *sample_rate, int *channels, int use_ac3)
fildes = OssPcmFildes; fildes = OssPcmFildes;
OssPcmFildes = -1; OssPcmFildes = -1;
close(fildes); close(fildes);
if (!(fildes = OssOpenPCM(use_ac3))) { if (!(fildes = OssOpenPCM(passthrough))) {
return -1; return -1;
} }
OssPcmFildes = fildes; OssPcmFildes = fildes;
@ -1931,11 +1932,12 @@ static void NoopSetVolume( __attribute__ ((unused))
** **
** @param freq sample frequency ** @param freq sample frequency
** @param channels number of channels ** @param channels number of channels
** @param passthrough use pass-through (AC3, ...) device
*/ */
static int NoopSetup( __attribute__ ((unused)) static int NoopSetup( __attribute__ ((unused))
int *channels, __attribute__ ((unused)) int *channels, __attribute__ ((unused))
int *freq, __attribute__ ((unused)) int *freq, __attribute__ ((unused))
int use_ac3) int passthrough)
{ {
return -1; return -1;
} }
@ -1973,17 +1975,17 @@ static const AudioModule NoopModule = {
*/ */
static int AudioNextRing(void) static int AudioNextRing(void)
{ {
int use_ac3; int passthrough;
int sample_rate; int sample_rate;
int channels; int channels;
size_t used; size_t used;
// update audio format // update audio format
// not always needed, but check if needed is too complex // not always needed, but check if needed is too complex
use_ac3 = AudioRing[AudioRingRead].UseAc3; passthrough = AudioRing[AudioRingRead].Passthrough;
sample_rate = AudioRing[AudioRingRead].HwSampleRate; sample_rate = AudioRing[AudioRingRead].HwSampleRate;
channels = AudioRing[AudioRingRead].HwChannels; channels = AudioRing[AudioRingRead].HwChannels;
if (AudioUsedModule->Setup(&sample_rate, &channels, use_ac3)) { if (AudioUsedModule->Setup(&sample_rate, &channels, passthrough)) {
Error(_("audio: can't set channels %d sample-rate %dHz\n"), channels, Error(_("audio: can't set channels %d sample-rate %dHz\n"), channels,
sample_rate); sample_rate);
// FIXME: handle error // FIXME: handle error
@ -2068,10 +2070,10 @@ static void *AudioPlayHandlerThread(void *dummy)
err = AudioUsedModule->Thread(); err = AudioUsedModule->Thread();
// underrun, check if new ring buffer is available // underrun, check if new ring buffer is available
if (!err) { if (!err) {
int use_ac3; int passthrough;
int sample_rate; int sample_rate;
int channels; int channels;
int old_use_ac3; int old_passthrough;
int old_sample_rate; int old_sample_rate;
int old_channels; int old_channels;
@ -2081,20 +2083,21 @@ static void *AudioPlayHandlerThread(void *dummy)
} }
Debug(3, "audio: next ring buffer\n"); Debug(3, "audio: next ring buffer\n");
old_use_ac3 = AudioRing[AudioRingRead].UseAc3; old_passthrough = AudioRing[AudioRingRead].Passthrough;
old_sample_rate = AudioRing[AudioRingRead].HwSampleRate; old_sample_rate = AudioRing[AudioRingRead].HwSampleRate;
old_channels = AudioRing[AudioRingRead].HwChannels; old_channels = AudioRing[AudioRingRead].HwChannels;
atomic_dec(&AudioRingFilled); atomic_dec(&AudioRingFilled);
AudioRingRead = (AudioRingRead + 1) % AUDIO_RING_MAX; AudioRingRead = (AudioRingRead + 1) % AUDIO_RING_MAX;
use_ac3 = AudioRing[AudioRingRead].UseAc3; passthrough = AudioRing[AudioRingRead].Passthrough;
sample_rate = AudioRing[AudioRingRead].HwSampleRate; sample_rate = AudioRing[AudioRingRead].HwSampleRate;
channels = AudioRing[AudioRingRead].HwChannels; channels = AudioRing[AudioRingRead].HwChannels;
Debug(3, "audio: thread channels %d frequency %dHz %s\n", Debug(3, "audio: thread channels %d frequency %dHz %s\n",
channels, sample_rate, use_ac3 ? "ac3" : "pcm"); channels, sample_rate, passthrough ? "pass-through" : "");
// audio config changed? // audio config changed?
if (old_use_ac3 != use_ac3 || old_sample_rate != sample_rate if (old_passthrough != passthrough
|| old_sample_rate != sample_rate
|| old_channels != channels) { || old_channels != channels) {
// FIXME: wait for buffer drain // FIXME: wait for buffer drain
if (AudioNextRing()) { if (AudioNextRing()) {
@ -2196,7 +2199,7 @@ void AudioEnqueue(const void *samples, int count)
} }
// audio sample modification allowed and needed? // audio sample modification allowed and needed?
buffer = (void *)samples; buffer = (void *)samples;
if (!AudioRing[AudioRingWrite].UseAc3 && (AudioCompression if (!AudioRing[AudioRingWrite].Passthrough && (AudioCompression
|| AudioNormalize || AudioNormalize
|| AudioRing[AudioRingWrite].InChannels != || AudioRing[AudioRingWrite].InChannels !=
AudioRing[AudioRingWrite].HwChannels)) { AudioRing[AudioRingWrite].HwChannels)) {
@ -2416,7 +2419,7 @@ void AudioFlushBuffers(void)
old = AudioRingWrite; old = AudioRingWrite;
AudioRingWrite = (AudioRingWrite + 1) % AUDIO_RING_MAX; AudioRingWrite = (AudioRingWrite + 1) % AUDIO_RING_MAX;
AudioRing[AudioRingWrite].FlushBuffers = 1; AudioRing[AudioRingWrite].FlushBuffers = 1;
AudioRing[AudioRingWrite].UseAc3 = AudioRing[old].UseAc3; AudioRing[AudioRingWrite].Passthrough = AudioRing[old].Passthrough;
AudioRing[AudioRingWrite].HwSampleRate = AudioRing[old].HwSampleRate; AudioRing[AudioRingWrite].HwSampleRate = AudioRing[old].HwSampleRate;
AudioRing[AudioRingWrite].HwChannels = AudioRing[old].HwChannels; AudioRing[AudioRingWrite].HwChannels = AudioRing[old].HwChannels;
AudioRing[AudioRingWrite].InSampleRate = AudioRing[old].InSampleRate; AudioRing[AudioRingWrite].InSampleRate = AudioRing[old].InSampleRate;
@ -2528,7 +2531,7 @@ int64_t AudioGetClock(void)
// delay zero, if no valid time stamp // delay zero, if no valid time stamp
if ((delay = AudioGetDelay())) { if ((delay = AudioGetDelay())) {
if (AudioRing[AudioRingRead].UseAc3) { if (AudioRing[AudioRingRead].Passthrough) {
return AudioRing[AudioRingRead].PTS + 0 * 90 - delay; return AudioRing[AudioRingRead].PTS + 0 * 90 - delay;
} }
return AudioRing[AudioRingRead].PTS + 0 * 90 - delay; return AudioRing[AudioRingRead].PTS + 0 * 90 - delay;
@ -2548,7 +2551,7 @@ void AudioSetVolume(int volume)
AudioMute = !volume; AudioMute = !volume;
// reduce loudness for stereo output // reduce loudness for stereo output
if (AudioStereoDescent && AudioRing[AudioRingRead].InChannels == 2 if (AudioStereoDescent && AudioRing[AudioRingRead].InChannels == 2
&& !AudioRing[AudioRingRead].UseAc3) { && !AudioRing[AudioRingRead].Passthrough) {
volume -= AudioStereoDescent; volume -= AudioStereoDescent;
if (volume < 0) { if (volume < 0) {
volume = 0; volume = 0;
@ -2567,7 +2570,7 @@ void AudioSetVolume(int volume)
** **
** @param freq sample frequency ** @param freq sample frequency
** @param channels number of channels ** @param channels number of channels
** @param use_ac3 use ac3/pass-through device ** @param passthrough use pass-through (AC3, ...) device
** **
** @retval 0 everything ok ** @retval 0 everything ok
** @retval 1 didn't support frequency/channels combination ** @retval 1 didn't support frequency/channels combination
@ -2575,10 +2578,10 @@ void AudioSetVolume(int volume)
** **
** @todo add support to report best fitting format. ** @todo add support to report best fitting format.
*/ */
int AudioSetup(int *freq, int *channels, int use_ac3) int AudioSetup(int *freq, int *channels, int passthrough)
{ {
Debug(3, "audio: setup channels %d frequency %dHz %s\n", *channels, *freq, Debug(3, "audio: setup channels %d frequency %dHz %s\n", *channels, *freq,
use_ac3 ? "ac3" : "pcm"); passthrough ? "pass-through" : "");
// invalid parameter // invalid parameter
if (!freq || !channels || !*freq || !*channels) { if (!freq || !channels || !*freq || !*channels) {
@ -2586,7 +2589,7 @@ int AudioSetup(int *freq, int *channels, int use_ac3)
// FIXME: set flag invalid setup // FIXME: set flag invalid setup
return -1; return -1;
} }
return AudioRingAdd(*freq, *channels, use_ac3); return AudioRingAdd(*freq, *channels, passthrough);
} }
/** /**
@ -2722,7 +2725,7 @@ void AudioSetDevice(const char *device)
** **
** @note this is currently usable with alsa only. ** @note this is currently usable with alsa only.
*/ */
void AudioSetDeviceAC3(const char *device) void AudioSetPassthroughDevice(const char *device)
{ {
if (!AudioModuleName) { if (!AudioModuleName) {
AudioModuleName = "alsa"; // detect alsa/OSS AudioModuleName = "alsa"; // detect alsa/OSS
@ -2732,7 +2735,7 @@ void AudioSetDeviceAC3(const char *device)
AudioModuleName = "oss"; AudioModuleName = "oss";
} }
} }
AudioAC3Device = device; AudioPassthroughDevice = device;
} }
/** /**
@ -2961,7 +2964,7 @@ static void PrintVersion(void)
#ifdef GIT_REV #ifdef GIT_REV
"(GIT-" GIT_REV ")" "(GIT-" GIT_REV ")"
#endif #endif
",\n\t(c) 2009 - 2012 by Johns\n" ",\n\t(c) 2009 - 2013 by Johns\n"
"\tLicense AGPLv3: GNU Affero General Public License version 3\n"); "\tLicense AGPLv3: GNU Affero General Public License version 3\n");
} }

View File

@ -1,7 +1,7 @@
/// ///
/// @file audio.h @brief Audio module headerfile /// @file audio.h @brief Audio module headerfile
/// ///
/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. /// Copyright (c) 2009 - 2013 by Johns. All Rights Reserved.
/// ///
/// Contributor(s): /// Contributor(s):
/// ///
@ -48,7 +48,9 @@ extern void AudioSetCompression(int, int); ///< set compression parameters
extern void AudioSetStereoDescent(int); ///< set stereo loudness descent extern void AudioSetStereoDescent(int); ///< set stereo loudness descent
extern void AudioSetDevice(const char *); ///< set PCM audio device extern void AudioSetDevice(const char *); ///< set PCM audio device
extern void AudioSetDeviceAC3(const char *); ///< set pass-through device
/// set pass-through device
extern void AudioSetPassthroughDevice(const char *);
extern void AudioSetChannel(const char *); ///< set mixer channel extern void AudioSetChannel(const char *); ///< set mixer channel
extern void AudioInit(void); ///< setup audio module extern void AudioInit(void); ///< setup audio module
extern void AudioExit(void); ///< cleanup and exit audio module extern void AudioExit(void); ///< cleanup and exit audio module

410
codec.c
View File

@ -1,7 +1,7 @@
/// ///
/// @file codec.c @brief Codec functions /// @file codec.c @brief Codec functions
/// ///
/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. /// Copyright (c) 2009 - 2013 by Johns. All Rights Reserved.
/// ///
/// Contributor(s): /// Contributor(s):
/// ///
@ -30,13 +30,13 @@
/// many bugs and incompatiblity in it. Don't use this shit. /// many bugs and incompatiblity in it. Don't use this shit.
/// ///
/// compile with passthrough support (stable, ac3 only) /// compile with pass-through support (stable, AC-3, E-AC-3 only)
#define USE_PASSTHROUGH #define USE_PASSTHROUGH
/// compile audio drift correction support (experimental) /// compile audio drift correction support (very experimental)
#define USE_AUDIO_DRIFT_CORRECTION #define USE_AUDIO_DRIFT_CORRECTION
/// compile AC3 audio drift correction support (experimental) /// compile AC-3 audio drift correction support (very experimental)
#define USE_AC3_DRIFT_CORRECTION #define USE_AC3_DRIFT_CORRECTION
/// use ffmpeg libswresample API /// use ffmpeg libswresample API (autodected, Makefile)
#define noUSE_SWRESAMPLE #define noUSE_SWRESAMPLE
#include <stdio.h> #include <stdio.h>
@ -633,7 +633,7 @@ struct _audio_decoder_
AVCodec *AudioCodec; ///< audio codec AVCodec *AudioCodec; ///< audio codec
AVCodecContext *AudioCtx; ///< audio codec context AVCodecContext *AudioCtx; ///< audio codec context
int PassthroughAC3; ///< current ac-3 pass-through char Passthrough; ///< current pass-through flags
int SampleRate; ///< current stream sample rate int SampleRate; ///< current stream sample rate
int Channels; ///< current stream channels int Channels; ///< current stream channels
@ -651,6 +651,10 @@ struct _audio_decoder_
#endif #endif
#endif #endif
uint16_t Spdif[24576 / 2]; ///< SPDIF output buffer
int SpdifIndex; ///< index into SPDIF output buffer
int SpdifCount; ///< SPDIF repeat counter
int64_t LastDelay; ///< last delay int64_t LastDelay; ///< last delay
struct timespec LastTime; ///< last time struct timespec LastTime; ///< last time
int64_t LastPTS; ///< last PTS int64_t LastPTS; ///< last PTS
@ -670,24 +674,32 @@ struct _audio_decoder_
#endif #endif
}; };
///
/// IEC Data type enumeration.
///
enum IEC61937
{
IEC61937_AC3 = 0x01, ///< AC-3 data
// FIXME: more data types
IEC61937_EAC3 = 0x15, ///< E-AC-3 data
};
#ifdef USE_AUDIO_DRIFT_CORRECTION #ifdef USE_AUDIO_DRIFT_CORRECTION
#define CORRECT_PCM 1 ///< do PCM audio-drift correction #define CORRECT_PCM 1 ///< do PCM audio-drift correction
#define CORRECT_AC3 2 ///< do AC3§ audio-drift correction #define CORRECT_AC3 2 ///< do AC3 audio-drift correction
static char CodecAudioDrift; ///< flag: enable audio-drift correction static char CodecAudioDrift; ///< flag: enable audio-drift correction
#else #else
static const int CodecAudioDrift = 0; static const int CodecAudioDrift = 0;
#endif #endif
#ifdef USE_PASSTHROUGH #ifdef USE_PASSTHROUGH
//static char CodecPassthroughPCM; ///< pass pcm through (unsupported) ///
static char CodecPassthroughAC3; ///< pass ac3 through /// Pass-through flags: CodecPCM, CodecAC3, CodecEAC3, ...
///
//static char CodecPassthroughDTS; ///< pass dts through (unsupported) static char CodecPassthrough;
//static char CodecPassthroughMPA; ///< pass mpa through (unsupported)
#else #else
static const int CodecPassthrough = 0;
static const int CodecPassthroughAC3 = 0;
#endif #endif
static char CodecDownmix; ///< enable ac-3 downmix static char CodecDownmix; ///< enable AC-3 decoder downmix
/** /**
** Allocate a new audio decoder context. ** Allocate a new audio decoder context.
@ -840,7 +852,7 @@ void CodecAudioClose(AudioDecoder * audio_decoder)
void CodecSetAudioDrift(int mask) void CodecSetAudioDrift(int mask)
{ {
#ifdef USE_AUDIO_DRIFT_CORRECTION #ifdef USE_AUDIO_DRIFT_CORRECTION
CodecAudioDrift = mask & 3; CodecAudioDrift = mask & (CORRECT_PCM | CORRECT_AC3);
#endif #endif
(void)mask; (void)mask;
} }
@ -848,12 +860,12 @@ void CodecSetAudioDrift(int mask)
/** /**
** Set audio pass-through. ** Set audio pass-through.
** **
** @param mask enable mask (PCM, AC3) ** @param mask enable mask (PCM, AC3, EAC3)
*/ */
void CodecSetAudioPassthrough(int mask) void CodecSetAudioPassthrough(int mask)
{ {
#ifdef USE_PASSTHROUGH #ifdef USE_PASSTHROUGH
CodecPassthroughAC3 = mask & 1 ? 1 : 0; CodecPassthrough = mask & (CodecPCM | CodecAC3 | CodecEAC3);
#endif #endif
(void)mask; (void)mask;
} }
@ -932,6 +944,178 @@ static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
} }
} }
/**
** Handle audio format changes helper.
**
** @param audio_decoder audio decoder data
** @param[out] passthrough pass-through output
*/
static int CodecAudioUpdateHelper(AudioDecoder * audio_decoder,
int *passthrough)
{
const AVCodecContext *audio_ctx;
int err;
audio_ctx = audio_decoder->AudioCtx;
Debug(3, "codec/audio: format change %s %dHz *%d channels%s%s%s%s%s\n",
av_get_sample_fmt_name(audio_ctx->sample_fmt), audio_ctx->sample_rate,
audio_ctx->channels, CodecPassthrough & CodecPCM ? " PCM" : "",
CodecPassthrough & CodecMPA ? " MPA" : "",
CodecPassthrough & CodecAC3 ? " AC3" : "",
CodecPassthrough & CodecEAC3 ? " EAC3" : "",
CodecPassthrough ? " pass-through" : "");
*passthrough = 0;
audio_decoder->SampleRate = audio_ctx->sample_rate;
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
audio_decoder->Channels = audio_ctx->channels;
audio_decoder->HwChannels = audio_ctx->channels;
audio_decoder->Passthrough = CodecPassthrough;
// SPDIF/HDMI pass-through
if ((CodecPassthrough & CodecAC3 && audio_ctx->codec_id == CODEC_ID_AC3)
|| (CodecPassthrough & CodecEAC3
&& audio_ctx->codec_id == CODEC_ID_EAC3)) {
audio_decoder->HwChannels = 2;
audio_decoder->SpdifIndex = 0; // reset buffer
audio_decoder->SpdifCount = 0;
*passthrough = 1;
}
// channels not support?
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels, *passthrough))) {
Debug(3, "codec/audio: audio setup error\n");
// FIXME: handle errors
audio_decoder->HwChannels = 0;
audio_decoder->HwSampleRate = 0;
return err;
}
Debug(3, "codec/audio: resample %s %dHz *%d -> %s %dHz *%d\n",
av_get_sample_fmt_name(audio_ctx->sample_fmt), audio_ctx->sample_rate,
audio_ctx->channels, av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
audio_decoder->HwSampleRate, audio_decoder->HwChannels);
return 0;
}
/**
** Audio pass-through decoder helper.
**
** @param audio_decoder audio decoder data
** @param avpkt undecoded audio packet
*/
static int CodecAudioPassthroughHelper(AudioDecoder * audio_decoder,
const AVPacket * avpkt)
{
#ifdef USE_PASSTHROUGH
const AVCodecContext *audio_ctx;
audio_ctx = audio_decoder->AudioCtx;
// SPDIF/HDMI passthrough
if (CodecPassthrough & CodecAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
uint16_t *spdif;
int spdif_sz;
spdif = audio_decoder->Spdif;
spdif_sz = 6144;
#ifdef USE_AC3_DRIFT_CORRECTION
// FIXME: this works with some TVs/AVReceivers
// FIXME: write burst size drift correction, which should work with all
if (CodecAudioDrift & CORRECT_AC3) {
int x;
x = (audio_decoder->DriftFrac +
(audio_decoder->DriftCorr * spdif_sz)) / (10 *
audio_decoder->HwSampleRate * 100);
audio_decoder->DriftFrac =
(audio_decoder->DriftFrac +
(audio_decoder->DriftCorr * spdif_sz)) % (10 *
audio_decoder->HwSampleRate * 100);
// round to word border
x *= audio_decoder->HwChannels * 4;
if (x < -64) { // limit correction
x = -64;
} else if (x > 64) {
x = 64;
}
spdif_sz += x;
}
#endif
// build SPDIF header and append A52 audio to it
// avpkt is the original data
if (spdif_sz < avpkt->size + 8) {
Error(_("codec/audio: decoded data smaller than encoded\n"));
return -1;
}
spdif[0] = htole16(0xF872); // iec 61937 sync word
spdif[1] = htole16(0x4E1F);
spdif[2] = htole16(IEC61937_AC3 | (avpkt->data[5] & 0x07) << 8);
spdif[3] = htole16(avpkt->size * 8);
// copy original data for output
// FIXME: not 100% sure, if endian is correct on not intel hardware
swab(avpkt->data, spdif + 4, avpkt->size);
// FIXME: don't need to clear always
memset(spdif + 4 + avpkt->size / 2, 0, spdif_sz - 8 - avpkt->size);
// don't play with the ac-3 samples
AudioEnqueue(spdif, spdif_sz);
return 1;
}
if (CodecPassthrough & CodecEAC3 && audio_ctx->codec_id == CODEC_ID_EAC3) {
uint16_t *spdif;
int spdif_sz;
int repeat;
// build SPDIF header and append A52 audio to it
// avpkt is the original data
spdif = audio_decoder->Spdif;
spdif_sz = 6144;
// 24576 = 4 * 6144
if (spdif_sz < audio_decoder->SpdifIndex + avpkt->size + 8) {
Error(_("codec/audio: decoded data smaller than encoded\n"));
return -1;
}
// check if we must pack multiple packets
repeat = 1;
if ((avpkt->data[4] & 0xc0) != 0xc0) { // fscod
static const uint8_t eac3_repeat[4] = { 6, 3, 2, 1 };
// fscod2
repeat = eac3_repeat[(avpkt->data[4] & 0x30) >> 4];
}
//fprintf(stderr, "repeat %d\n", repeat);
// copy original data for output
// pack upto repeat EAC-3 pakets into one IEC 61937 burst
// FIXME: not 100% sure, if endian is correct on not intel hardware
swab(avpkt->data, spdif + 4 + audio_decoder->SpdifIndex, avpkt->size);
audio_decoder->SpdifIndex += avpkt->size;
if (++audio_decoder->SpdifCount < repeat) {
return 1;
}
spdif[0] = htole16(0xF872); // iec 61937 sync word
spdif[1] = htole16(0x4E1F);
spdif[2] = htole16(IEC61937_EAC3);
spdif[3] = htole16(audio_decoder->SpdifIndex * 8);
memset(spdif + 4 + audio_decoder->SpdifIndex / 2, 0,
spdif_sz - 8 - audio_decoder->SpdifIndex);
// don't play with the eac-3 samples
AudioEnqueue(spdif, spdif_sz);
audio_decoder->SpdifIndex = 0;
audio_decoder->SpdifCount = 0;
return 1;
}
#endif
return 0;
}
#ifndef USE_SWRESAMPLE #ifndef USE_SWRESAMPLE
/** /**
@ -1007,8 +1191,10 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
audio_decoder->Drift = drift; audio_decoder->Drift = drift;
corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000); corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000);
// SPDIF/HDMI passthrough // SPDIF/HDMI passthrough
if ((CodecAudioDrift & 2) && (!CodecPassthroughAC3 if ((CodecAudioDrift & CORRECT_AC3) && (!CodecPassthroughAC3
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)) { || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)
&& (!CodecPassthroughEAC3
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_EAC3)) {
audio_decoder->DriftCorr = -corr; audio_decoder->DriftCorr = -corr;
} }
@ -1045,14 +1231,15 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
** Handle audio format changes. ** Handle audio format changes.
** **
** @param audio_decoder audio decoder data ** @param audio_decoder audio decoder data
**
** @note this is the old not good supported version
*/ */
static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
{ {
int passthrough;
const AVCodecContext *audio_ctx; const AVCodecContext *audio_ctx;
int err; int err;
int isAC3;
// FIXME: use swr_convert from swresample (only in ffmpeg!)
if (audio_decoder->ReSample) { if (audio_decoder->ReSample) {
audio_resample_close(audio_decoder->ReSample); audio_resample_close(audio_decoder->ReSample);
audio_decoder->ReSample = NULL; audio_decoder->ReSample = NULL;
@ -1064,28 +1251,8 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
} }
audio_ctx = audio_decoder->AudioCtx; audio_ctx = audio_decoder->AudioCtx;
Debug(3, "codec/audio: format change %dHz %d channels %s\n", if ((err = CodecAudioUpdateHelper(audio_decoder, &passthrough))) {
audio_ctx->sample_rate, audio_ctx->channels,
CodecPassthroughAC3 ? "pass-through" : "");
audio_decoder->SampleRate = audio_ctx->sample_rate;
audio_decoder->HwSampleRate = audio_ctx->sample_rate;
audio_decoder->Channels = audio_ctx->channels;
audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
audio_decoder->HwChannels = 2;
isAC3 = 1;
} else {
audio_decoder->HwChannels = audio_ctx->channels;
isAC3 = 0;
}
// channels not support?
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels, isAC3))) {
Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n", Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n",
audio_ctx->sample_rate, audio_ctx->channels, audio_ctx->sample_rate, audio_ctx->channels,
audio_decoder->HwSampleRate, audio_decoder->HwChannels); audio_decoder->HwSampleRate, audio_decoder->HwChannels);
@ -1101,19 +1268,21 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
Error(_("codec/audio: resample setup error\n")); Error(_("codec/audio: resample setup error\n"));
audio_decoder->HwChannels = 0; audio_decoder->HwChannels = 0;
audio_decoder->HwSampleRate = 0; audio_decoder->HwSampleRate = 0;
}
return; return;
} }
} else {
Debug(3, "codec/audio: audio setup error\n"); Debug(3, "codec/audio: audio setup error\n");
// FIXME: handle errors // FIXME: handle errors
audio_decoder->HwChannels = 0; audio_decoder->HwChannels = 0;
audio_decoder->HwSampleRate = 0; audio_decoder->HwSampleRate = 0;
return; return;
} }
if (passthrough) { // pass-through no conversion allowed
return;
} }
// prepare audio drift resample // prepare audio drift resample
#ifdef USE_AUDIO_DRIFT_CORRECTION #ifdef USE_AUDIO_DRIFT_CORRECTION
if ((CodecAudioDrift & 1) && !isAC3) { if (CodecAudioDrift & CORRECT_PCM) {
if (audio_decoder->AvResample) { if (audio_decoder->AvResample) {
Error(_("codec/audio: overwrite resample\n")); Error(_("codec/audio: overwrite resample\n"));
} }
@ -1144,7 +1313,7 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count) void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count)
{ {
#ifdef USE_AUDIO_DRIFT_CORRECTION #ifdef USE_AUDIO_DRIFT_CORRECTION
if ((CodecAudioDrift & 1) && audio_decoder->AvResample) { if ((CodecAudioDrift & CORRECT_PCM) && audio_decoder->AvResample) {
int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 + int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16))); FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16)));
int16_t buftmp[MAX_CHANNELS][(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4]; int16_t buftmp[MAX_CHANNELS][(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4];
@ -1205,12 +1374,16 @@ void CodecAudioEnqueue(AudioDecoder * audio_decoder, int16_t * data, int count)
n *= 2; n *= 2;
n *= audio_decoder->HwChannels; n *= audio_decoder->HwChannels;
if (!(audio_decoder->Passthrough & CodecPCM)) {
CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels); CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels);
}
AudioEnqueue(buf, n); AudioEnqueue(buf, n);
return; return;
} }
#endif #endif
if (!(audio_decoder->Passthrough & CodecPCM)) {
CodecReorderAudioFrame(data, count, audio_decoder->HwChannels); CodecReorderAudioFrame(data, count, audio_decoder->HwChannels);
}
AudioEnqueue(data, count); AudioEnqueue(data, count);
} }
@ -1232,6 +1405,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
audio_ctx = audio_decoder->AudioCtx; audio_ctx = audio_decoder->AudioCtx;
// FIXME: don't need to decode pass-through codecs
buf_sz = sizeof(buf); buf_sz = sizeof(buf);
l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, (AVPacket *) avpkt); l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, (AVPacket *) avpkt);
if (avpkt->size != l) { if (avpkt->size != l) {
@ -1250,7 +1424,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
CodecAudioSetClock(audio_decoder, avpkt->pts); CodecAudioSetClock(audio_decoder, avpkt->pts);
} }
// FIXME: must first play remainings bytes, than change and play new. // FIXME: must first play remainings bytes, than change and play new.
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3 if (audio_decoder->Passthrough != CodecPassthrough
|| audio_decoder->SampleRate != audio_ctx->sample_rate || audio_decoder->SampleRate != audio_ctx->sample_rate
|| audio_decoder->Channels != audio_ctx->channels) { || audio_decoder->Channels != audio_ctx->channels) {
CodecAudioUpdateFormat(audio_decoder); CodecAudioUpdateFormat(audio_decoder);
@ -1283,48 +1457,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
CodecAudioEnqueue(audio_decoder, outbuf, outlen); CodecAudioEnqueue(audio_decoder, outbuf, outlen);
} }
} else { } else {
#ifdef USE_PASSTHROUGH if (CodecAudioPassthroughHelper(audio_decoder, avpkt)) {
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
// build SPDIF header and append A52 audio to it
// avpkt is the original data
buf_sz = 6144;
#ifdef USE_AC3_DRIFT_CORRECTION
if (CodecAudioDrift & 2) {
int x;
x = (audio_decoder->DriftFrac +
(audio_decoder->DriftCorr * buf_sz)) / (10 *
audio_decoder->HwSampleRate * 100);
audio_decoder->DriftFrac =
(audio_decoder->DriftFrac +
(audio_decoder->DriftCorr * buf_sz)) % (10 *
audio_decoder->HwSampleRate * 100);
x *= audio_decoder->HwChannels * 4;
if (x < -64) { // limit correction
x = -64;
} else if (x > 64) {
x = 64;
}
buf_sz += x;
}
#endif
if (buf_sz < avpkt->size + 8) {
Error(_
("codec/audio: decoded data smaller than encoded\n"));
return;
}
// copy original data for output
// FIXME: not 100% sure, if endian is correct
buf[0] = htole16(0xF872); // iec 61937 sync word
buf[1] = htole16(0x4E1F);
buf[2] = htole16(0x01 | (avpkt->data[5] & 0x07) << 8);
buf[3] = htole16(avpkt->size * 8);
swab(avpkt->data, buf + 4, avpkt->size);
memset(buf + 4 + avpkt->size / 2, 0, buf_sz - 8 - avpkt->size);
// don't play with the ac-3 samples
AudioEnqueue(buf, buf_sz);
return; return;
} }
#if 0 #if 0
@ -1377,7 +1510,6 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
} }
// DTS HD? // DTS HD?
// True HD? // True HD?
#endif
#endif #endif
CodecAudioEnqueue(audio_decoder, buf, buf_sz); CodecAudioEnqueue(audio_decoder, buf, buf_sz);
} }
@ -1461,8 +1593,10 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
audio_decoder->Drift = drift; audio_decoder->Drift = drift;
corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000); corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000);
// SPDIF/HDMI passthrough // SPDIF/HDMI passthrough
if ((CodecAudioDrift & 2) && (!CodecPassthroughAC3 if ((CodecAudioDrift & CORRECT_AC3) && (!(CodecPassthrough & CodecAC3)
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)) { || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)
&& (!(CodecPassthrough & CodecEAC3)
|| audio_decoder->AudioCtx->codec_id != CODEC_ID_EAC3)) {
audio_decoder->DriftCorr = -corr; audio_decoder->DriftCorr = -corr;
} }
@ -1504,49 +1638,27 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts)
*/ */
static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder)
{ {
int passthrough;
const AVCodecContext *audio_ctx; const AVCodecContext *audio_ctx;
int err;
int isAC3; if (CodecAudioUpdateHelper(audio_decoder, &passthrough)) {
// FIXME: handle swresample format conversions.
return;
}
if (passthrough) { // pass-through no conversion allowed
return;
}
audio_ctx = audio_decoder->AudioCtx; audio_ctx = audio_decoder->AudioCtx;
Debug(3, "codec/audio: format change %s %dHz *%d channels %s\n",
av_get_sample_fmt_name(audio_ctx->sample_fmt), audio_ctx->sample_rate,
audio_ctx->channels, CodecPassthroughAC3 ? "pass-through" : "");
audio_decoder->SampleRate = audio_ctx->sample_rate; #ifdef DEBUG
audio_decoder->HwSampleRate = audio_ctx->sample_rate; if (audio_ctx->sample_fmt == AV_SAMPLE_FMT_S16
audio_decoder->Channels = audio_ctx->channels; && audio_ctx->sample_rate == audio_decoder->HwSampleRate
audio_decoder->PassthroughAC3 = CodecPassthroughAC3; && !CodecAudioDrift) {
// FIXME: use Resample only, when it is needed!
// SPDIF/HDMI passthrough fprintf(stderr, "no resample needed\n");
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
audio_decoder->HwChannels = 2;
isAC3 = 1;
} else {
audio_decoder->HwChannels = audio_ctx->channels;
isAC3 = 0;
} }
#endif
// channels not support?
if ((err =
AudioSetup(&audio_decoder->HwSampleRate,
&audio_decoder->HwChannels, isAC3))) {
Debug(3, "codec/audio: audio setup error\n");
// FIXME: handle errors
audio_decoder->HwChannels = 0;
audio_decoder->HwSampleRate = 0;
return;
}
if (isAC3) { // no AC3 conversion allowed
return;
}
Debug(3, "codec/audio: resample %s %dHz *%d -> %s %dHz *%d\n",
av_get_sample_fmt_name(audio_ctx->sample_fmt), audio_ctx->sample_rate,
audio_ctx->channels, av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
audio_decoder->HwSampleRate, audio_decoder->HwChannels);
audio_decoder->Resample = audio_decoder->Resample =
swr_alloc_set_opts(audio_decoder->Resample, audio_ctx->channel_layout, swr_alloc_set_opts(audio_decoder->Resample, audio_ctx->channel_layout,
@ -1579,6 +1691,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
audio_ctx = audio_decoder->AudioCtx; audio_ctx = audio_decoder->AudioCtx;
// FIXME: don't need to decode pass-through codecs
frame.data[0] = NULL; frame.data[0] = NULL;
n = avcodec_decode_audio4(audio_ctx, &frame, &got_frame, n = avcodec_decode_audio4(audio_ctx, &frame, &got_frame,
(AVPacket *) avpkt); (AVPacket *) avpkt);
@ -1602,42 +1715,20 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
CodecAudioSetClock(audio_decoder, avpkt->pts); CodecAudioSetClock(audio_decoder, avpkt->pts);
} }
// format change // format change
if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3 if (audio_decoder->Passthrough != CodecPassthrough
|| audio_decoder->SampleRate != audio_ctx->sample_rate || audio_decoder->SampleRate != audio_ctx->sample_rate
|| audio_decoder->Channels != audio_ctx->channels) { || audio_decoder->Channels != audio_ctx->channels) {
CodecAudioUpdateFormat(audio_decoder); CodecAudioUpdateFormat(audio_decoder);
} }
if (!audio_decoder->HwSampleRate || !audio_decoder->HwChannels) { if (!audio_decoder->HwSampleRate || !audio_decoder->HwChannels) {
return; // unsupported sample format return; // unsupported sample format
} }
#ifdef USE_PASSTHROUGH
// SPDIF/HDMI passthrough
if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
int16_t spdif[6144 / 2];
int spdif_sz;
// build SPDIF header and append A52 audio to it if (CodecAudioPassthroughHelper(audio_decoder, avpkt)) {
// avpkt is the original data
spdif_sz = 6144;
if (spdif_sz < avpkt->size + 8) {
Error(_("codec/audio: decoded data smaller than encoded\n"));
return; return;
} }
// copy original data for output
spdif[0] = htole16(0xF872); // iec 61937 sync word
spdif[1] = htole16(0x4E1F);
spdif[2] = htole16(0x01 | (avpkt->data[5] & 0x07) << 8);
spdif[3] = htole16(avpkt->size * 8);
// FIXME: not 100% sure, if endian is correct on not intel hardware
swab(avpkt->data, spdif + 4, avpkt->size);
memset(spdif + 4 + avpkt->size / 2, 0, spdif_sz - 8 - avpkt->size);
// don't play with the ac-3 samples
AudioEnqueue(spdif, spdif_sz);
return;
}
#endif
if (0) { if (0) {
char strbuf[32]; char strbuf[32];
int data_sz; int data_sz;
@ -1665,12 +1756,19 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
sizeof(outbuf) / (2 * audio_decoder->HwChannels), sizeof(outbuf) / (2 * audio_decoder->HwChannels),
(const uint8_t **)frame.extended_data, frame.nb_samples); (const uint8_t **)frame.extended_data, frame.nb_samples);
if (n > 0) { if (n > 0) {
if (!(audio_decoder->Passthrough & CodecPCM)) {
CodecReorderAudioFrame((int16_t *) outbuf, CodecReorderAudioFrame((int16_t *) outbuf,
n * 2 * audio_decoder->HwChannels, audio_decoder->HwChannels); n * 2 * audio_decoder->HwChannels,
audio_decoder->HwChannels);
}
AudioEnqueue(outbuf, n * 2 * audio_decoder->HwChannels); AudioEnqueue(outbuf, n * 2 * audio_decoder->HwChannels);
} }
return; return;
} }
#ifdef DEBUG
// should be never reached
fprintf(stderr, "oops\n");
#endif
} }
#endif #endif

12
codec.h
View File

@ -1,7 +1,7 @@
/// ///
/// @file codec.h @brief Codec module headerfile /// @file codec.h @brief Codec module headerfile
/// ///
/// Copyright (c) 2009 - 2012 by Johns. All Rights Reserved. /// Copyright (c) 2009 - 2013 by Johns. All Rights Reserved.
/// ///
/// Contributor(s): /// Contributor(s):
/// ///
@ -23,6 +23,16 @@
/// @addtogroup Codec /// @addtogroup Codec
/// @{ /// @{
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
#define CodecPCM 0x01 ///< PCM bit mask
#define CodecMPA 0x02 ///< MPA bit mask (planned)
#define CodecAC3 0x04 ///< AC-3 bit mask
#define CodecEAC3 0x08 ///< EAC-3 bit mask
#define CodecDTS 0x10 ///< DTS bit mask (planned)
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Typedefs // Typedefs
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -314,7 +314,7 @@ const uint16_t Ac3FrameSizeTable[38][3] = {
}; };
/// ///
/// Fast check for AC3 audio. /// Fast check for (E)AC3 audio.
/// ///
/// 5 bytes 0x0B77xxxxxx AC3 audio /// 5 bytes 0x0B77xxxxxx AC3 audio
/// ///
@ -326,17 +326,11 @@ static inline int FastAc3Check(const uint8_t * p)
if (p[1] != 0x77) { if (p[1] != 0x77) {
return 0; return 0;
} }
if ((p[4] & 0xC0) == 0xC0) { // invalid sample rate
return 0;
}
if ((p[4] & 0x3F) > 37) { // invalid frame size
return 0;
}
return 1; return 1;
} }
/// ///
/// Check for AC-3 audio. /// Check for (E)AC-3 audio.
/// ///
/// 0x0B77xxxxxx already checked. /// 0x0B77xxxxxx already checked.
/// ///
@ -347,20 +341,61 @@ static inline int FastAc3Check(const uint8_t * p)
/// @retval 0 no valid AC-3 audio /// @retval 0 no valid AC-3 audio
/// @retval >0 valid AC-3 audio /// @retval >0 valid AC-3 audio
/// ///
/// o AC3 Header
/// AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB CCDDDDDD EEEEEFFF
///
/// o a 16x Frame sync, always 0x0B77
/// o b 16x CRC 16
/// o c 2x Samplerate
/// o d 6x Framesize code
/// o e 5x Bitstream ID
/// o f 3x Bitstream mode
///
/// o EAC3 Header
/// AAAAAAAA AAAAAAAA BBCCCDDD DDDDDDDD EEFFGGGH IIIII...
///
/// o a 16x Frame sync, always 0x0B77
/// o b 2x Frame type
/// o c 3x Sub stream ID
/// o d 10x Framesize - 1 in words
/// o e 2x Framesize code
/// o f 2x Framesize code 2
///
static int Ac3Check(const uint8_t * data, int size) static int Ac3Check(const uint8_t * data, int size)
{ {
int frame_size;
if (size < 5) { // need 5 bytes to see if AC3/EAC3
return -5;
}
if (data[5] > (10 << 3)) { // EAC3
if ((data[4] & 0xF0) == 0xF0) { // invalid fscod fscod2
return 0;
}
frame_size = ((data[2] & 0x03) << 8) + data[3] + 1;
frame_size *= 2;
} else { // AC3
int fscod; int fscod;
int frmsizcod; int frmsizcod;
int frame_size;
// crc1 crc1 fscod|frmsizcod // crc1 crc1 fscod|frmsizcod
fscod = data[4] >> 6; fscod = data[4] >> 6;
frmsizcod = data[4] & 0x3F; // invalid is checked by fast if (fscod == 0x03) { // invalid sample rate
return 0;
}
frmsizcod = data[4] & 0x3F;
if (frmsizcod > 37) { // invalid frame size
return 0;
}
// invalid is checked above
frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2; frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2;
}
if (frame_size + 5 > size) { if (frame_size + 5 > size) {
return -frame_size - 5; return -frame_size - 5;
} }
// FIXME: relaxed checks if codec is already detected
// check if after this frame a new AC-3 frame starts // check if after this frame a new AC-3 frame starts
if (FastAc3Check(data + frame_size)) { if (FastAc3Check(data + frame_size)) {
return frame_size; return frame_size;
@ -617,6 +652,7 @@ static void PesParse(PesDemux * pesdx, const uint8_t * data, int size,
// 4 bytes 0xFFExxxxx Mpeg audio // 4 bytes 0xFFExxxxx Mpeg audio
// 5 bytes 0x0B77xxxxxx AC3 audio // 5 bytes 0x0B77xxxxxx AC3 audio
// 6 bytes 0x0B77xxxxxxxx EAC3 audio
// 3 bytes 0x56Exxx AAC LATM audio // 3 bytes 0x56Exxx AAC LATM audio
// 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio
// PCM audio can't be found // PCM audio can't be found
@ -629,6 +665,9 @@ static void PesParse(PesDemux * pesdx, const uint8_t * data, int size,
if (!r && FastAc3Check(q)) { if (!r && FastAc3Check(q)) {
r = Ac3Check(q, n); r = Ac3Check(q, n);
codec_id = CODEC_ID_AC3; codec_id = CODEC_ID_AC3;
if (r > 0 && q[5] > (10 << 3)) {
codec_id = CODEC_ID_EAC3;
}
} }
if (!r && FastLatmCheck(q)) { if (!r && FastLatmCheck(q)) {
r = LatmCheck(q, n); r = LatmCheck(q, n);
@ -1119,6 +1158,7 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
// 4 bytes 0xFFExxxxx Mpeg audio // 4 bytes 0xFFExxxxx Mpeg audio
// 3 bytes 0x56Exxx AAC LATM audio // 3 bytes 0x56Exxx AAC LATM audio
// 5 bytes 0x0B77xxxxxx AC3 audio // 5 bytes 0x0B77xxxxxx AC3 audio
// 6 bytes 0x0B77xxxxxxxx EAC3 audio
// 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio
// PCM audio can't be found // PCM audio can't be found
r = 0; r = 0;
@ -1134,6 +1174,9 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
if ((id == 0xbd || (id & 0xF0) == 0x80) && !r && FastAc3Check(p)) { if ((id == 0xbd || (id & 0xF0) == 0x80) && !r && FastAc3Check(p)) {
r = Ac3Check(p, n); r = Ac3Check(p, n);
codec_id = CODEC_ID_AC3; codec_id = CODEC_ID_AC3;
if (r > 0 && p[5] > (10 << 3)) {
codec_id = CODEC_ID_EAC3;
}
/* faster ac3 detection at end of pes packet (no improvemnts) /* faster ac3 detection at end of pes packet (no improvemnts)
if (AudioCodecID == codec_id && -r - 2 == n) { if (AudioCodecID == codec_id && -r - 2 == n) {
r = n; r = n;
@ -2787,7 +2830,7 @@ int ProcessArgs(int argc, char *const argv[])
AudioSetChannel(optarg); AudioSetChannel(optarg);
continue; continue;
case 'p': // pass-through audio device case 'p': // pass-through audio device
AudioSetDeviceAC3(optarg); AudioSetPassthroughDevice(optarg);
continue; continue;
case 'd': // x11 display name case 'd': // x11 display name
X11DisplayName = optarg; X11DisplayName = optarg;

View File

@ -106,6 +106,8 @@ extern "C"
extern void PipStop(void); extern void PipStop(void);
/// Pip play video packet /// Pip play video packet
extern int PipPlayVideo(const uint8_t *, int); extern int PipPlayVideo(const uint8_t *, int);
extern const char *X11DisplayName; ///< x11 display name
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -20,6 +20,8 @@
/// $Id$ /// $Id$
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#define __STDC_CONSTANT_MACROS ///< needed for ffmpeg UINT64_C
#include <vdr/interface.h> #include <vdr/interface.h>
#include <vdr/plugin.h> #include <vdr/plugin.h>
#include <vdr/player.h> #include <vdr/player.h>
@ -34,15 +36,15 @@
#include "softhddev.h" #include "softhddev.h"
#include "softhddevice.h" #include "softhddevice.h"
#include "softhddevice_service.h" #include "softhddevice_service.h"
extern "C" extern "C"
{ {
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include "audio.h" #include "audio.h"
#include "video.h" #include "video.h"
extern const char *X11DisplayName; ///< x11 display name #include "codec.h"
extern void CodecSetAudioDrift(int);
extern void CodecSetAudioPassthrough(int);
extern void CodecSetAudioDownmix(int);
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -126,7 +128,8 @@ static int ConfigAutoCropTolerance; ///< auto crop detection tolerance
static int ConfigVideoAudioDelay; ///< config audio delay static int ConfigVideoAudioDelay; ///< config audio delay
static char ConfigAudioDrift; ///< config audio drift static char ConfigAudioDrift; ///< config audio drift
static char ConfigAudioPassthrough; ///< config audio pass-through static char ConfigAudioPassthrough; ///< config audio pass-through mask
static char AudioPassthroughState; ///< flag audio pass-through on/off
static char ConfigAudioDownmix; ///< config ffmpeg audio downmix static char ConfigAudioDownmix; ///< config ffmpeg audio downmix
static char ConfigAudioSoftvol; ///< config use software volume static char ConfigAudioSoftvol; ///< config use software volume
static char ConfigAudioNormalize; ///< config use normalize volume static char ConfigAudioNormalize; ///< config use normalize volume
@ -138,7 +141,7 @@ int ConfigAudioBufferTime; ///< config size ms of audio buffer
static char *ConfigX11Display; ///< config x11 display static char *ConfigX11Display; ///< config x11 display
static char *ConfigAudioDevice; ///< config audio stereo device static char *ConfigAudioDevice; ///< config audio stereo device
static char *ConfigAC3Device; ///< config audio passthrough device static char *ConfigPassthroughDevice; ///< config audio pass-through device
#ifdef USE_PIP #ifdef USE_PIP
static int ConfigPipX = 100 - 3 - 18; ///< config pip pip x in % static int ConfigPipX = 100 - 3 - 18; ///< config pip pip x in %
@ -615,7 +618,9 @@ class cMenuSetupSoft:public cMenuSetupPage
int Audio; int Audio;
int AudioDelay; int AudioDelay;
int AudioDrift; int AudioDrift;
int AudioPassthrough; int AudioPassthroughPCM;
int AudioPassthroughAC3;
int AudioPassthroughEAC3;
int AudioDownmix; int AudioDownmix;
int AudioSoftvol; int AudioSoftvol;
int AudioNormalize; int AudioNormalize;
@ -720,9 +725,6 @@ void cMenuSetupSoft::Create(void)
static const char *const audiodrift[] = { static const char *const audiodrift[] = {
"None", "PCM", "AC-3", "PCM + AC-3" "None", "PCM", "AC-3", "PCM + AC-3"
}; };
static const char *const passthrough[] = {
"None", "AC-3"
};
static const char *const resolution[RESOLUTIONS] = { static const char *const resolution[RESOLUTIONS] = {
"576i", "720p", "fake 1080i", "1080i" "576i", "720p", "fake 1080i", "1080i"
}; };
@ -844,9 +846,13 @@ void cMenuSetupSoft::Create(void)
-1000, 1000)); -1000, 1000));
Add(new cMenuEditStraItem(tr("Audio drift correction"), &AudioDrift, 4, Add(new cMenuEditStraItem(tr("Audio drift correction"), &AudioDrift, 4,
audiodrift)); audiodrift));
Add(new cMenuEditStraItem(tr("Audio pass-through"), &AudioPassthrough, Add(new cMenuEditBoolItem(tr("Enable PCM pass-through"),
2, passthrough)); &AudioPassthroughPCM, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Enable AC-3 (decoder) downmix"), Add(new cMenuEditBoolItem(tr("Enable AC-3 pass-through"),
&AudioPassthroughAC3, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Enable EAC-3 pass-through"),
&AudioPassthroughEAC3, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Enable (E)AC-3 (decoder) downmix"),
&AudioDownmix, trVDR("no"), trVDR("yes"))); &AudioDownmix, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Volume control"), &AudioSoftvol, Add(new cMenuEditBoolItem(tr("Volume control"), &AudioSoftvol,
tr("Hardware"), tr("Software"))); tr("Hardware"), tr("Software")));
@ -1031,7 +1037,9 @@ cMenuSetupSoft::cMenuSetupSoft(void)
Audio = 0; Audio = 0;
AudioDelay = ConfigVideoAudioDelay; AudioDelay = ConfigVideoAudioDelay;
AudioDrift = ConfigAudioDrift; AudioDrift = ConfigAudioDrift;
AudioPassthrough = ConfigAudioPassthrough; AudioPassthroughPCM = ConfigAudioPassthrough & CodecPCM;
AudioPassthroughAC3 = ConfigAudioPassthrough & CodecAC3;
AudioPassthroughEAC3 = ConfigAudioPassthrough & CodecEAC3;
AudioDownmix = ConfigAudioDownmix; AudioDownmix = ConfigAudioDownmix;
AudioSoftvol = ConfigAudioSoftvol; AudioSoftvol = ConfigAudioSoftvol;
AudioNormalize = ConfigAudioNormalize; AudioNormalize = ConfigAudioNormalize;
@ -1174,8 +1182,12 @@ void cMenuSetupSoft::Store(void)
VideoSetAudioDelay(ConfigVideoAudioDelay); VideoSetAudioDelay(ConfigVideoAudioDelay);
SetupStore("AudioDrift", ConfigAudioDrift = AudioDrift); SetupStore("AudioDrift", ConfigAudioDrift = AudioDrift);
CodecSetAudioDrift(ConfigAudioDrift); CodecSetAudioDrift(ConfigAudioDrift);
SetupStore("AudioPassthrough", ConfigAudioPassthrough = AudioPassthrough); ConfigAudioPassthrough = (AudioPassthroughPCM ? CodecPCM : 0)
| (AudioPassthroughAC3 ? CodecAC3 : 0)
| (AudioPassthroughEAC3 ? CodecEAC3 : 0);
SetupStore("AudioPassthrough", ConfigAudioPassthrough);
CodecSetAudioPassthrough(ConfigAudioPassthrough); CodecSetAudioPassthrough(ConfigAudioPassthrough);
AudioPassthroughState = 1;
SetupStore("AudioDownmix", ConfigAudioDownmix = AudioDownmix); SetupStore("AudioDownmix", ConfigAudioDownmix = AudioDownmix);
CodecSetAudioDownmix(ConfigAudioDownmix); CodecSetAudioDownmix(ConfigAudioDownmix);
SetupStore("AudioSoftvol", ConfigAudioSoftvol = AudioSoftvol); SetupStore("AudioSoftvol", ConfigAudioSoftvol = AudioSoftvol);
@ -1786,18 +1798,23 @@ static void HandleHotkey(int code)
{ {
switch (code) { switch (code) {
case 10: // disable pass-through case 10: // disable pass-through
CodecSetAudioPassthrough(ConfigAudioPassthrough = 0); AudioPassthroughState = 0;
CodecSetAudioPassthrough(0);
Skins.QueueMessage(mtInfo, tr("pass-through disabled")); Skins.QueueMessage(mtInfo, tr("pass-through disabled"));
break; break;
case 11: // enable pass-through case 11: // enable pass-through
CodecSetAudioPassthrough(ConfigAudioPassthrough = 1); // note: you can't enable, without configured pass-through
AudioPassthroughState = 1;
CodecSetAudioPassthrough(ConfigAudioPassthrough);
Skins.QueueMessage(mtInfo, tr("pass-through enabled")); Skins.QueueMessage(mtInfo, tr("pass-through enabled"));
break; break;
case 12: // toggle pass-through case 12: // toggle pass-through
CodecSetAudioPassthrough(ConfigAudioPassthrough ^= 1); AudioPassthroughState ^= 1;
if (ConfigAudioPassthrough) { if (AudioPassthroughState) {
CodecSetAudioPassthrough(ConfigAudioPassthrough);
Skins.QueueMessage(mtInfo, tr("pass-through enabled")); Skins.QueueMessage(mtInfo, tr("pass-through enabled"));
} else { } else {
CodecSetAudioPassthrough(0);
Skins.QueueMessage(mtInfo, tr("pass-through disabled")); Skins.QueueMessage(mtInfo, tr("pass-through disabled"));
} }
break; break;
@ -2902,6 +2919,9 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
} }
if (!strcasecmp(name, "AudioPassthrough")) { if (!strcasecmp(name, "AudioPassthrough")) {
CodecSetAudioPassthrough(ConfigAudioPassthrough = atoi(value)); CodecSetAudioPassthrough(ConfigAudioPassthrough = atoi(value));
if (ConfigAudioPassthrough) {
AudioPassthroughState = 1;
}
return true; return true;
} }
if (!strcasecmp(name, "AudioDownmix")) { if (!strcasecmp(name, "AudioDownmix")) {
@ -3272,13 +3292,13 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command,
free(tmp); free(tmp);
return "missing option argument"; return "missing option argument";
} }
free(ConfigAC3Device); free(ConfigPassthroughDevice);
ConfigAC3Device = strdup(o); ConfigPassthroughDevice = strdup(o);
AudioSetDeviceAC3(ConfigAC3Device); AudioSetPassthroughDevice(ConfigPassthroughDevice);
} else if (!strncmp(s, "-p", 2)) { } else if (!strncmp(s, "-p", 2)) {
free(ConfigAC3Device); free(ConfigPassthroughDevice);
ConfigAC3Device = strdup(s + 2); ConfigPassthroughDevice = strdup(s + 2);
AudioSetDeviceAC3(ConfigAC3Device); AudioSetPassthroughDevice(ConfigPassthroughDevice);
} else if (*s) { } else if (*s) {
free(tmp); free(tmp);