From 2cd667fb4435e5373b8ba2b6bb93144248aae231 Mon Sep 17 00:00:00 2001 From: Johns Date: Mon, 11 Feb 2013 16:53:51 +0100 Subject: [PATCH] Improved pass-through (PCM+EAC3) support. --- ChangeLog | 1 + README.txt | 19 ++- audio.c | 109 ++++++------ audio.h | 6 +- codec.c | 426 +++++++++++++++++++++++++++++------------------ codec.h | 12 +- softhddev.c | 73 ++++++-- softhddev.h | 2 + softhddevice.cpp | 72 +++++--- 9 files changed, 451 insertions(+), 269 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20f0582..891fb78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ User johns Date: + Improved pass-through (PCM+EAC3) support. Support VDR 1.7.36 new build system. Improves VDPAU display preemption handling. Add modifiers to X11 remote key names (from Sibbi). diff --git a/README.txt b/README.txt index ac9562b..7b3f5fc 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ @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): @@ -89,8 +89,8 @@ Setup: environment only if alsa is configured ALSA_DEVICE=default alsa PCM device name - ALSA_AC3_DEVICE= - alsa AC3/pass-though device name + ALSA_PASSTHROUGH_DEVICE= + alsa pass-though (AC3,EAC3,DTS,...) device name ALSA_MIXER=default alsa control device name ALSA_MIXER_CHANNEL=PCM @@ -99,8 +99,8 @@ Setup: environment only if oss is configured OSS_AUDIODEV=/dev/dsp oss dsp device name - OSS_AC3_AUDIODEV= - oss AC3/pass-though device name + OSS_PASSTHROUGHDEV= + oss pass-though (AC3,EAC3,DTS,...) device name OSS_MIXERDEV=/dev/mixer oss mixer device name OSS_MIXER_CHANNEL=pcm @@ -156,13 +156,16 @@ Setup: /etc/vdr/setup.conf delay audio or delay video 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 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 0 = off, use hardware volume control diff --git a/audio.c b/audio.c index ed6198e..369eec9 100644 --- a/audio.c +++ b/audio.c @@ -129,7 +129,7 @@ static const char *AudioModuleName; ///< which audio module to use /// Selected audio module. static const AudioModule *AudioUsedModule = &NoopModule; 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 *AudioMixerChannel; ///< mixer channel name 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_ { char FlushBuffers; ///< flag: flush buffers - char UseAc3; ///< flag: use ac3 pass-through + char Passthrough; ///< flag: use pass-through (AC3, ...) int16_t PacketSize; ///< packet size unsigned HwSampleRate; ///< hardware sample rate in Hz unsigned HwChannels; ///< hardware number of channels @@ -642,14 +642,14 @@ static unsigned AudioStartThreshold; ///< start play, if filled ** ** @param sample_rate sample-rate frequency ** @param channels number of channels -** @param use_ac3 use ac3/pass-through device +** @param passthrough use /pass-through (AC3, ...) device ** ** @retval -1 error ** @retval 0 okay ** ** @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; @@ -680,7 +680,7 @@ static int AudioRingAdd(unsigned sample_rate, int channels, int use_ac3) // FIXME: don't flush buffers here AudioRing[AudioRingWrite].FlushBuffers = 1; - AudioRing[AudioRingWrite].UseAc3 = use_ac3; + AudioRing[AudioRingWrite].Passthrough = passthrough; AudioRing[AudioRingWrite].PacketSize = 0; AudioRing[AudioRingWrite].InSampleRate = sample_rate; AudioRing[AudioRingWrite].InChannels = channels; @@ -836,8 +836,9 @@ static int AlsaPlayRingbuffer(void) if (!avail) { // full or buffer empty break; } - // muting ac3, can produce disturbance - if (AudioMute || (AudioSoftVolume && !AudioRing[AudioRingRead].UseAc3)) { + // muting pass-through ac3, can produce disturbance + if (AudioMute || (AudioSoftVolume + && !AudioRing[AudioRingRead].Passthrough)) { // FIXME: quick&dirty cast AudioSoftAmplifier((int16_t *) p, avail); // FIXME: if not all are written, we double amplify them @@ -984,23 +985,23 @@ static int AlsaThread(void) /** ** 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; snd_pcm_t *handle; int err; // &&|| hell - if (!(use_ac3 && ((device = AudioAC3Device) - || (device = getenv("ALSA_AC3_DEVICE")))) + if (!(passthrough && ((device = AudioPassthroughDevice) + || (device = getenv("ALSA_PASSTHROUGH_DEVICE")))) && !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) { device = "default"; } if (!AudioDoingInit) { // reduce blabla during init - Info(_("audio/alsa: using %sdevice '%s'\n"), use_ac3 ? "ac3 " : "", - device); + Info(_("audio/alsa: using %sdevice '%s'\n"), + passthrough ? "pass-through " : "", device); } // open none blocking; if device is already used, we don't want wait if ((err = @@ -1167,7 +1168,7 @@ static int64_t AlsaGetDelay(void) ** ** @param freq sample frequency ** @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 1 didn't support frequency/channels combination @@ -1175,7 +1176,7 @@ static int64_t AlsaGetDelay(void) ** ** @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 period_size; @@ -1183,7 +1184,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3) int delay; 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; } 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 AlsaPCMHandle = NULL; // other threads should check handle snd_pcm_close(handle); - if (!(handle = AlsaOpenPCM(use_ac3))) { + if (!(handle = AlsaOpenPCM(passthrough))) { return -1; } AlsaPCMHandle = handle; @@ -1453,7 +1454,7 @@ static int OssPlayRingbuffer(void) break; // bi.bytes could become negative! } - if (AudioSoftVolume && !AudioRing[AudioRingRead].UseAc3) { + if (AudioSoftVolume && !AudioRing[AudioRingRead].Passthrough) { // FIXME: quick&dirty cast AudioSoftAmplifier((int16_t *) p, bi.bytes); // FIXME: if not all are written, we double amplify them @@ -1560,22 +1561,22 @@ static int OssThread(void) /** ** 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; int fildes; // &&|| hell - if (!(use_ac3 && ((device = AudioAC3Device) - || (device = getenv("OSS_AC3_AUDIODEV")))) + if (!(passthrough && ((device = AudioPassthroughDevice) + || (device = getenv("OSS_PASSTHROUGHDEV")))) && !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) { device = "/dev/dsp"; } if (!AudioDoingInit) { - Info(_("audio/oss: using %sdevice '%s'\n"), use_ac3 ? "ac3 " : "", - device); + Info(_("audio/oss: using %sdevice '%s'\n"), + passthrough ? "pass-through " : "", device); } if ((fildes = open(device, O_WRONLY)) < 0) { @@ -1724,13 +1725,13 @@ static int64_t OssGetDelay(void) ** ** @param sample_rate sample rate/frequency ** @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 1 didn't support frequency/channels combination ** @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 tmp; @@ -1738,7 +1739,7 @@ static int OssSetup(int *sample_rate, int *channels, int use_ac3) audio_buf_info bi; 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; } @@ -1748,7 +1749,7 @@ static int OssSetup(int *sample_rate, int *channels, int use_ac3) fildes = OssPcmFildes; OssPcmFildes = -1; close(fildes); - if (!(fildes = OssOpenPCM(use_ac3))) { + if (!(fildes = OssOpenPCM(passthrough))) { return -1; } OssPcmFildes = fildes; @@ -1929,13 +1930,14 @@ static void NoopSetVolume( __attribute__ ((unused)) /** ** Noop setup. ** -** @param freq sample frequency -** @param channels number of channels +** @param freq sample frequency +** @param channels number of channels +** @param passthrough use pass-through (AC3, ...) device */ static int NoopSetup( __attribute__ ((unused)) int *channels, __attribute__ ((unused)) int *freq, __attribute__ ((unused)) - int use_ac3) + int passthrough) { return -1; } @@ -1973,17 +1975,17 @@ static const AudioModule NoopModule = { */ static int AudioNextRing(void) { - int use_ac3; + int passthrough; int sample_rate; int channels; size_t used; // update audio format // not always needed, but check if needed is too complex - use_ac3 = AudioRing[AudioRingRead].UseAc3; + passthrough = AudioRing[AudioRingRead].Passthrough; sample_rate = AudioRing[AudioRingRead].HwSampleRate; 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, sample_rate); // FIXME: handle error @@ -2068,10 +2070,10 @@ static void *AudioPlayHandlerThread(void *dummy) err = AudioUsedModule->Thread(); // underrun, check if new ring buffer is available if (!err) { - int use_ac3; + int passthrough; int sample_rate; int channels; - int old_use_ac3; + int old_passthrough; int old_sample_rate; int old_channels; @@ -2081,20 +2083,21 @@ static void *AudioPlayHandlerThread(void *dummy) } 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_channels = AudioRing[AudioRingRead].HwChannels; atomic_dec(&AudioRingFilled); AudioRingRead = (AudioRingRead + 1) % AUDIO_RING_MAX; - use_ac3 = AudioRing[AudioRingRead].UseAc3; + passthrough = AudioRing[AudioRingRead].Passthrough; sample_rate = AudioRing[AudioRingRead].HwSampleRate; channels = AudioRing[AudioRingRead].HwChannels; 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? - if (old_use_ac3 != use_ac3 || old_sample_rate != sample_rate + if (old_passthrough != passthrough + || old_sample_rate != sample_rate || old_channels != channels) { // FIXME: wait for buffer drain if (AudioNextRing()) { @@ -2196,7 +2199,7 @@ void AudioEnqueue(const void *samples, int count) } // audio sample modification allowed and needed? buffer = (void *)samples; - if (!AudioRing[AudioRingWrite].UseAc3 && (AudioCompression + if (!AudioRing[AudioRingWrite].Passthrough && (AudioCompression || AudioNormalize || AudioRing[AudioRingWrite].InChannels != AudioRing[AudioRingWrite].HwChannels)) { @@ -2416,7 +2419,7 @@ void AudioFlushBuffers(void) old = AudioRingWrite; AudioRingWrite = (AudioRingWrite + 1) % AUDIO_RING_MAX; AudioRing[AudioRingWrite].FlushBuffers = 1; - AudioRing[AudioRingWrite].UseAc3 = AudioRing[old].UseAc3; + AudioRing[AudioRingWrite].Passthrough = AudioRing[old].Passthrough; AudioRing[AudioRingWrite].HwSampleRate = AudioRing[old].HwSampleRate; AudioRing[AudioRingWrite].HwChannels = AudioRing[old].HwChannels; AudioRing[AudioRingWrite].InSampleRate = AudioRing[old].InSampleRate; @@ -2528,7 +2531,7 @@ int64_t AudioGetClock(void) // delay zero, if no valid time stamp 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; @@ -2548,7 +2551,7 @@ void AudioSetVolume(int volume) AudioMute = !volume; // reduce loudness for stereo output if (AudioStereoDescent && AudioRing[AudioRingRead].InChannels == 2 - && !AudioRing[AudioRingRead].UseAc3) { + && !AudioRing[AudioRingRead].Passthrough) { volume -= AudioStereoDescent; if (volume < 0) { volume = 0; @@ -2565,9 +2568,9 @@ void AudioSetVolume(int volume) /** ** Setup audio for requested format. ** -** @param freq sample frequency -** @param channels number of channels -** @param use_ac3 use ac3/pass-through device +** @param freq sample frequency +** @param channels number of channels +** @param passthrough use pass-through (AC3, ...) device ** ** @retval 0 everything ok ** @retval 1 didn't support frequency/channels combination @@ -2575,10 +2578,10 @@ void AudioSetVolume(int volume) ** ** @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, - use_ac3 ? "ac3" : "pcm"); + passthrough ? "pass-through" : ""); // invalid parameter if (!freq || !channels || !*freq || !*channels) { @@ -2586,7 +2589,7 @@ int AudioSetup(int *freq, int *channels, int use_ac3) // FIXME: set flag invalid setup 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. */ -void AudioSetDeviceAC3(const char *device) +void AudioSetPassthroughDevice(const char *device) { if (!AudioModuleName) { AudioModuleName = "alsa"; // detect alsa/OSS @@ -2732,7 +2735,7 @@ void AudioSetDeviceAC3(const char *device) AudioModuleName = "oss"; } } - AudioAC3Device = device; + AudioPassthroughDevice = device; } /** @@ -2961,7 +2964,7 @@ static void PrintVersion(void) #ifdef GIT_REV "(GIT-" GIT_REV ")" #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"); } diff --git a/audio.h b/audio.h index 808d385..4a0ac51 100644 --- a/audio.h +++ b/audio.h @@ -1,7 +1,7 @@ /// /// @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): /// @@ -48,7 +48,9 @@ extern void AudioSetCompression(int, int); ///< set compression parameters extern void AudioSetStereoDescent(int); ///< set stereo loudness descent 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 AudioInit(void); ///< setup audio module extern void AudioExit(void); ///< cleanup and exit audio module diff --git a/codec.c b/codec.c index e3860f7..13801dc 100644 --- a/codec.c +++ b/codec.c @@ -1,7 +1,7 @@ /// /// @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): /// @@ -30,13 +30,13 @@ /// 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 - /// compile audio drift correction support (experimental) + /// compile audio drift correction support (very experimental) #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 - /// use ffmpeg libswresample API + /// use ffmpeg libswresample API (autodected, Makefile) #define noUSE_SWRESAMPLE #include @@ -633,7 +633,7 @@ struct _audio_decoder_ AVCodec *AudioCodec; ///< audio codec 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 Channels; ///< current stream channels @@ -651,6 +651,10 @@ struct _audio_decoder_ #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 struct timespec LastTime; ///< last time int64_t LastPTS; ///< last PTS @@ -670,24 +674,32 @@ struct _audio_decoder_ #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 #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 #else static const int CodecAudioDrift = 0; #endif #ifdef USE_PASSTHROUGH -//static char CodecPassthroughPCM; ///< pass pcm through (unsupported) -static char CodecPassthroughAC3; ///< pass ac3 through - -//static char CodecPassthroughDTS; ///< pass dts through (unsupported) -//static char CodecPassthroughMPA; ///< pass mpa through (unsupported) + /// + /// Pass-through flags: CodecPCM, CodecAC3, CodecEAC3, ... + /// +static char CodecPassthrough; #else - -static const int CodecPassthroughAC3 = 0; +static const int CodecPassthrough = 0; #endif -static char CodecDownmix; ///< enable ac-3 downmix +static char CodecDownmix; ///< enable AC-3 decoder downmix /** ** Allocate a new audio decoder context. @@ -840,7 +852,7 @@ void CodecAudioClose(AudioDecoder * audio_decoder) void CodecSetAudioDrift(int mask) { #ifdef USE_AUDIO_DRIFT_CORRECTION - CodecAudioDrift = mask & 3; + CodecAudioDrift = mask & (CORRECT_PCM | CORRECT_AC3); #endif (void)mask; } @@ -848,12 +860,12 @@ void CodecSetAudioDrift(int mask) /** ** Set audio pass-through. ** -** @param mask enable mask (PCM, AC3) +** @param mask enable mask (PCM, AC3, EAC3) */ void CodecSetAudioPassthrough(int mask) { #ifdef USE_PASSTHROUGH - CodecPassthroughAC3 = mask & 1 ? 1 : 0; + CodecPassthrough = mask & (CodecPCM | CodecAC3 | CodecEAC3); #endif (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 /** @@ -1007,8 +1191,10 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts) audio_decoder->Drift = drift; corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000); // SPDIF/HDMI passthrough - if ((CodecAudioDrift & 2) && (!CodecPassthroughAC3 - || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)) { + if ((CodecAudioDrift & CORRECT_AC3) && (!CodecPassthroughAC3 + || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3) + && (!CodecPassthroughEAC3 + || audio_decoder->AudioCtx->codec_id != CODEC_ID_EAC3)) { audio_decoder->DriftCorr = -corr; } @@ -1045,14 +1231,15 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts) ** Handle audio format changes. ** ** @param audio_decoder audio decoder data +** +** @note this is the old not good supported version */ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) { + int passthrough; const AVCodecContext *audio_ctx; int err; - int isAC3; - // FIXME: use swr_convert from swresample (only in ffmpeg!) if (audio_decoder->ReSample) { audio_resample_close(audio_decoder->ReSample); audio_decoder->ReSample = NULL; @@ -1064,28 +1251,8 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) } audio_ctx = audio_decoder->AudioCtx; - Debug(3, "codec/audio: format change %dHz %d channels %s\n", - audio_ctx->sample_rate, audio_ctx->channels, - CodecPassthroughAC3 ? "pass-through" : ""); + if ((err = CodecAudioUpdateHelper(audio_decoder, &passthrough))) { - 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", audio_ctx->sample_rate, audio_ctx->channels, audio_decoder->HwSampleRate, audio_decoder->HwChannels); @@ -1101,19 +1268,21 @@ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) Error(_("codec/audio: resample setup error\n")); audio_decoder->HwChannels = 0; audio_decoder->HwSampleRate = 0; - return; } - } else { - Debug(3, "codec/audio: audio setup error\n"); - // FIXME: handle errors - audio_decoder->HwChannels = 0; - audio_decoder->HwSampleRate = 0; return; } + Debug(3, "codec/audio: audio setup error\n"); + // FIXME: handle errors + audio_decoder->HwChannels = 0; + audio_decoder->HwSampleRate = 0; + return; + } + if (passthrough) { // pass-through no conversion allowed + return; } // prepare audio drift resample #ifdef USE_AUDIO_DRIFT_CORRECTION - if ((CodecAudioDrift & 1) && !isAC3) { + if (CodecAudioDrift & CORRECT_PCM) { if (audio_decoder->AvResample) { 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) { #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 + FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16))); 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 *= audio_decoder->HwChannels; - CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels); + if (!(audio_decoder->Passthrough & CodecPCM)) { + CodecReorderAudioFrame(buf, n, audio_decoder->HwChannels); + } AudioEnqueue(buf, n); return; } #endif - CodecReorderAudioFrame(data, count, audio_decoder->HwChannels); + if (!(audio_decoder->Passthrough & CodecPCM)) { + CodecReorderAudioFrame(data, count, audio_decoder->HwChannels); + } AudioEnqueue(data, count); } @@ -1232,6 +1405,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) audio_ctx = audio_decoder->AudioCtx; + // FIXME: don't need to decode pass-through codecs buf_sz = sizeof(buf); l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, (AVPacket *) avpkt); if (avpkt->size != l) { @@ -1250,7 +1424,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) CodecAudioSetClock(audio_decoder, avpkt->pts); } // 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->Channels != audio_ctx->channels) { CodecAudioUpdateFormat(audio_decoder); @@ -1283,48 +1457,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) CodecAudioEnqueue(audio_decoder, outbuf, outlen); } } else { -#ifdef USE_PASSTHROUGH - // 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); + if (CodecAudioPassthroughHelper(audio_decoder, avpkt)) { return; } #if 0 @@ -1377,7 +1510,6 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) } // DTS HD? // True HD? -#endif #endif CodecAudioEnqueue(audio_decoder, buf, buf_sz); } @@ -1461,8 +1593,10 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts) audio_decoder->Drift = drift; corr = (10 * audio_decoder->HwSampleRate * drift) / (90 * 1000); // SPDIF/HDMI passthrough - if ((CodecAudioDrift & 2) && (!CodecPassthroughAC3 - || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3)) { + if ((CodecAudioDrift & CORRECT_AC3) && (!(CodecPassthrough & CodecAC3) + || audio_decoder->AudioCtx->codec_id != CODEC_ID_AC3) + && (!(CodecPassthrough & CodecEAC3) + || audio_decoder->AudioCtx->codec_id != CODEC_ID_EAC3)) { audio_decoder->DriftCorr = -corr; } @@ -1504,49 +1638,27 @@ static void CodecAudioSetClock(AudioDecoder * audio_decoder, int64_t pts) */ static void CodecAudioUpdateFormat(AudioDecoder * audio_decoder) { + int passthrough; 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; - 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; - 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; +#ifdef DEBUG + if (audio_ctx->sample_fmt == AV_SAMPLE_FMT_S16 + && audio_ctx->sample_rate == audio_decoder->HwSampleRate + && !CodecAudioDrift) { + // FIXME: use Resample only, when it is needed! + fprintf(stderr, "no resample needed\n"); } - - // 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); +#endif audio_decoder->Resample = 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; + // FIXME: don't need to decode pass-through codecs frame.data[0] = NULL; n = avcodec_decode_audio4(audio_ctx, &frame, &got_frame, (AVPacket *) avpkt); @@ -1602,42 +1715,20 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) CodecAudioSetClock(audio_decoder, avpkt->pts); } // format change - if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3 + if (audio_decoder->Passthrough != CodecPassthrough || audio_decoder->SampleRate != audio_ctx->sample_rate || audio_decoder->Channels != audio_ctx->channels) { CodecAudioUpdateFormat(audio_decoder); - } if (!audio_decoder->HwSampleRate || !audio_decoder->HwChannels) { 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 - // avpkt is the original data - spdif_sz = 6144; - if (spdif_sz < avpkt->size + 8) { - Error(_("codec/audio: decoded data smaller than encoded\n")); - 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); + if (CodecAudioPassthroughHelper(audio_decoder, avpkt)) { return; } -#endif + if (0) { char strbuf[32]; int data_sz; @@ -1665,12 +1756,19 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt) sizeof(outbuf) / (2 * audio_decoder->HwChannels), (const uint8_t **)frame.extended_data, frame.nb_samples); if (n > 0) { - CodecReorderAudioFrame((int16_t *) outbuf, - n * 2 * audio_decoder->HwChannels, audio_decoder->HwChannels); + if (!(audio_decoder->Passthrough & CodecPCM)) { + CodecReorderAudioFrame((int16_t *) outbuf, + n * 2 * audio_decoder->HwChannels, + audio_decoder->HwChannels); + } AudioEnqueue(outbuf, n * 2 * audio_decoder->HwChannels); } return; } +#ifdef DEBUG + // should be never reached + fprintf(stderr, "oops\n"); +#endif } #endif diff --git a/codec.h b/codec.h index 08cc5ac..4a11607 100644 --- a/codec.h +++ b/codec.h @@ -1,7 +1,7 @@ /// /// @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): /// @@ -23,6 +23,16 @@ /// @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 //---------------------------------------------------------------------------- diff --git a/softhddev.c b/softhddev.c index 3cded90..c638ebb 100644 --- a/softhddev.c +++ b/softhddev.c @@ -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 /// @@ -326,17 +326,11 @@ static inline int FastAc3Check(const uint8_t * p) if (p[1] != 0x77) { return 0; } - if ((p[4] & 0xC0) == 0xC0) { // invalid sample rate - return 0; - } - if ((p[4] & 0x3F) > 37) { // invalid frame size - return 0; - } return 1; } /// -/// Check for AC-3 audio. +/// Check for (E)AC-3 audio. /// /// 0x0B77xxxxxx already checked. /// @@ -347,20 +341,61 @@ static inline int FastAc3Check(const uint8_t * p) /// @retval 0 no 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) { - int fscod; - int frmsizcod; int frame_size; - // crc1 crc1 fscod|frmsizcod - fscod = data[4] >> 6; - frmsizcod = data[4] & 0x3F; // invalid is checked by fast - frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2; + 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 frmsizcod; + + // crc1 crc1 fscod|frmsizcod + fscod = data[4] >> 6; + 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; + } if (frame_size + 5 > size) { return -frame_size - 5; } + // FIXME: relaxed checks if codec is already detected // check if after this frame a new AC-3 frame starts if (FastAc3Check(data + 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 // 5 bytes 0x0B77xxxxxx AC3 audio + // 6 bytes 0x0B77xxxxxxxx EAC3 audio // 3 bytes 0x56Exxx AAC LATM audio // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio // 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)) { r = Ac3Check(q, n); codec_id = CODEC_ID_AC3; + if (r > 0 && q[5] > (10 << 3)) { + codec_id = CODEC_ID_EAC3; + } } if (!r && FastLatmCheck(q)) { r = LatmCheck(q, n); @@ -1119,6 +1158,7 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id) // 4 bytes 0xFFExxxxx Mpeg audio // 3 bytes 0x56Exxx AAC LATM audio // 5 bytes 0x0B77xxxxxx AC3 audio + // 6 bytes 0x0B77xxxxxxxx EAC3 audio // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio // PCM audio can't be found 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)) { r = Ac3Check(p, n); 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) if (AudioCodecID == codec_id && -r - 2 == n) { r = n; @@ -2787,7 +2830,7 @@ int ProcessArgs(int argc, char *const argv[]) AudioSetChannel(optarg); continue; case 'p': // pass-through audio device - AudioSetDeviceAC3(optarg); + AudioSetPassthroughDevice(optarg); continue; case 'd': // x11 display name X11DisplayName = optarg; diff --git a/softhddev.h b/softhddev.h index 69fa32e..6e446dc 100644 --- a/softhddev.h +++ b/softhddev.h @@ -106,6 +106,8 @@ extern "C" extern void PipStop(void); /// Pip play video packet extern int PipPlayVideo(const uint8_t *, int); + + extern const char *X11DisplayName; ///< x11 display name #ifdef __cplusplus } #endif diff --git a/softhddevice.cpp b/softhddevice.cpp index a9c2ab3..9f35371 100644 --- a/softhddevice.cpp +++ b/softhddevice.cpp @@ -20,6 +20,8 @@ /// $Id$ ////////////////////////////////////////////////////////////////////////////// +#define __STDC_CONSTANT_MACROS ///< needed for ffmpeg UINT64_C + #include #include #include @@ -34,15 +36,15 @@ #include "softhddev.h" #include "softhddevice.h" #include "softhddevice_service.h" + extern "C" { +#include +#include + #include "audio.h" #include "video.h" - extern const char *X11DisplayName; ///< x11 display name - - extern void CodecSetAudioDrift(int); - extern void CodecSetAudioPassthrough(int); - extern void CodecSetAudioDownmix(int); +#include "codec.h" } ////////////////////////////////////////////////////////////////////////////// @@ -126,7 +128,8 @@ static int ConfigAutoCropTolerance; ///< auto crop detection tolerance static int ConfigVideoAudioDelay; ///< config audio delay 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 ConfigAudioSoftvol; ///< config use software 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 *ConfigAudioDevice; ///< config audio stereo device -static char *ConfigAC3Device; ///< config audio passthrough device +static char *ConfigPassthroughDevice; ///< config audio pass-through device #ifdef USE_PIP static int ConfigPipX = 100 - 3 - 18; ///< config pip pip x in % @@ -615,7 +618,9 @@ class cMenuSetupSoft:public cMenuSetupPage int Audio; int AudioDelay; int AudioDrift; - int AudioPassthrough; + int AudioPassthroughPCM; + int AudioPassthroughAC3; + int AudioPassthroughEAC3; int AudioDownmix; int AudioSoftvol; int AudioNormalize; @@ -720,9 +725,6 @@ void cMenuSetupSoft::Create(void) static const char *const audiodrift[] = { "None", "PCM", "AC-3", "PCM + AC-3" }; - static const char *const passthrough[] = { - "None", "AC-3" - }; static const char *const resolution[RESOLUTIONS] = { "576i", "720p", "fake 1080i", "1080i" }; @@ -844,9 +846,13 @@ void cMenuSetupSoft::Create(void) -1000, 1000)); Add(new cMenuEditStraItem(tr("Audio drift correction"), &AudioDrift, 4, audiodrift)); - Add(new cMenuEditStraItem(tr("Audio pass-through"), &AudioPassthrough, - 2, passthrough)); - Add(new cMenuEditBoolItem(tr("Enable AC-3 (decoder) downmix"), + Add(new cMenuEditBoolItem(tr("Enable PCM pass-through"), + &AudioPassthroughPCM, trVDR("no"), trVDR("yes"))); + 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"))); Add(new cMenuEditBoolItem(tr("Volume control"), &AudioSoftvol, tr("Hardware"), tr("Software"))); @@ -1031,7 +1037,9 @@ cMenuSetupSoft::cMenuSetupSoft(void) Audio = 0; AudioDelay = ConfigVideoAudioDelay; AudioDrift = ConfigAudioDrift; - AudioPassthrough = ConfigAudioPassthrough; + AudioPassthroughPCM = ConfigAudioPassthrough & CodecPCM; + AudioPassthroughAC3 = ConfigAudioPassthrough & CodecAC3; + AudioPassthroughEAC3 = ConfigAudioPassthrough & CodecEAC3; AudioDownmix = ConfigAudioDownmix; AudioSoftvol = ConfigAudioSoftvol; AudioNormalize = ConfigAudioNormalize; @@ -1174,8 +1182,12 @@ void cMenuSetupSoft::Store(void) VideoSetAudioDelay(ConfigVideoAudioDelay); SetupStore("AudioDrift", ConfigAudioDrift = AudioDrift); CodecSetAudioDrift(ConfigAudioDrift); - SetupStore("AudioPassthrough", ConfigAudioPassthrough = AudioPassthrough); + ConfigAudioPassthrough = (AudioPassthroughPCM ? CodecPCM : 0) + | (AudioPassthroughAC3 ? CodecAC3 : 0) + | (AudioPassthroughEAC3 ? CodecEAC3 : 0); + SetupStore("AudioPassthrough", ConfigAudioPassthrough); CodecSetAudioPassthrough(ConfigAudioPassthrough); + AudioPassthroughState = 1; SetupStore("AudioDownmix", ConfigAudioDownmix = AudioDownmix); CodecSetAudioDownmix(ConfigAudioDownmix); SetupStore("AudioSoftvol", ConfigAudioSoftvol = AudioSoftvol); @@ -1786,18 +1798,23 @@ static void HandleHotkey(int code) { switch (code) { case 10: // disable pass-through - CodecSetAudioPassthrough(ConfigAudioPassthrough = 0); + AudioPassthroughState = 0; + CodecSetAudioPassthrough(0); Skins.QueueMessage(mtInfo, tr("pass-through disabled")); break; 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")); break; case 12: // toggle pass-through - CodecSetAudioPassthrough(ConfigAudioPassthrough ^= 1); - if (ConfigAudioPassthrough) { + AudioPassthroughState ^= 1; + if (AudioPassthroughState) { + CodecSetAudioPassthrough(ConfigAudioPassthrough); Skins.QueueMessage(mtInfo, tr("pass-through enabled")); } else { + CodecSetAudioPassthrough(0); Skins.QueueMessage(mtInfo, tr("pass-through disabled")); } break; @@ -2902,6 +2919,9 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) } if (!strcasecmp(name, "AudioPassthrough")) { CodecSetAudioPassthrough(ConfigAudioPassthrough = atoi(value)); + if (ConfigAudioPassthrough) { + AudioPassthroughState = 1; + } return true; } if (!strcasecmp(name, "AudioDownmix")) { @@ -3272,13 +3292,13 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command, free(tmp); return "missing option argument"; } - free(ConfigAC3Device); - ConfigAC3Device = strdup(o); - AudioSetDeviceAC3(ConfigAC3Device); + free(ConfigPassthroughDevice); + ConfigPassthroughDevice = strdup(o); + AudioSetPassthroughDevice(ConfigPassthroughDevice); } else if (!strncmp(s, "-p", 2)) { - free(ConfigAC3Device); - ConfigAC3Device = strdup(s + 2); - AudioSetDeviceAC3(ConfigAC3Device); + free(ConfigPassthroughDevice); + ConfigPassthroughDevice = strdup(s + 2); + AudioSetPassthroughDevice(ConfigPassthroughDevice); } else if (*s) { free(tmp);