[mythtv] [PATCH] AC3->SPDIF Passthrough support

Jason Hoos jhoos at thwack.net
Wed Nov 26 13:27:50 EST 2003


Attached is a patch to add support for AC3 to SPDIF passthrough.  It is
meant to work with digital tuner cards that can receive high-def
programs that have AC3 soundtracks.

To try it out, you'll need to set your audio output to an appropriate 
device.  For ALSA, usually one of 'spdif', 'plug:spdif', 'iec958', or
'hw0,1' should work, depending on how your system is configured; for OSS, 
/dev/adsp should work, although I didn't try it yet.  You might also
need to try something funky like 'iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2'
if nothing else works, although the AES settings didn't seem to be 
necessary on my machine for whatever reason.  

Also note that if you have an analog tuner card as well as a digital
one, you might need to lock the sample rate of the analog card to 
48kHz (the setting is someplace in the TV card setup) to get normal
stereo sound to work over the SPDIF as well.  If you are using ALSA,
setting the audio device to 'plug:spdif' instead of just 'spdif' should 
take also care of it since that tells ALSA to do the necessary rate conversion.

Finally, check 'Enable AC3 to SPDIF passthrough' on the General setup screen 
(it should be right under the audio output device setting), and enjoy.
:)

Also note that there's a hunk of this patch that might fail at the end
of avformatdecoder.cpp if you haven't also applied Doug Larrick's big
position-map patch, if so just copy and paste the new function to the
end of avformatdecoder.cpp instead.

Jason

-------------- next part --------------
--- libs/libmythtv/avformatdecoder.cpp.orig	2003-11-26 02:03:15.000000000 -0600
+++ libs/libmythtv/avformatdecoder.cpp	2003-11-26 01:58:38.000000000 -0600
@@ -1,5 +1,6 @@
 #include <iostream>
 #include <assert.h>
+#include <unistd.h>
 
 using namespace std;
 
@@ -14,8 +15,11 @@
 #ifdef USING_XVMC
 #include "../libavcodec/xvmc_render.h"
 #endif
+#include "../libavcodec/liba52/a52.h"
 }
 
+#define MAX_AC3_FRAME_SIZE 6144
+
 extern pthread_mutex_t avcodeclock;
 
 AvFormatDecoder::AvFormatDecoder(NuppelVideoPlayer *parent, QSqlDatabase *db,
@@ -55,6 +59,8 @@
 
     memset(&params, 0, sizeof(AVFormatParameters));
     memset(prvpkt, 0, 3);
+
+    do_ac3_passthru = gContext->GetNumSetting("AC3PassThru", false);
 }
 
 AvFormatDecoder::~AvFormatDecoder()
@@ -375,10 +381,22 @@
             {
                 bitrate += enc->bit_rate;
                 m_parent->SetEffDsp(enc->sample_rate * 100);
-                m_parent->SetAudioParams(16, enc->channels, enc->sample_rate);
-                audio_sample_size = enc->channels * 2;
-                audio_sampling_rate = enc->sample_rate;
-                audio_channels = enc->channels;
+                if (do_ac3_passthru && enc->codec_id == CODEC_ID_AC3) 
+                {
+                    // An AC3 stream looks like a 48KHz 2ch audio stream to 
+                    // the sound card 
+                    audio_sample_size = 4;
+                    audio_sampling_rate = 48000;
+                    audio_channels = 2;
+                }
+                else
+                {
+                    audio_sample_size = enc->channels * 2;
+                    audio_sampling_rate = enc->sample_rate;
+                    audio_channels = enc->channels;
+                }
+                m_parent->SetAudioParams(16, audio_channels, 
+                                         audio_sampling_rate);
                 break;
             }
             case CODEC_TYPE_DATA:
@@ -1086,8 +1104,16 @@
                         continue;
                     }
 
-                    ret = avcodec_decode_audio(&curstream->codec, samples,
-                                               &data_size, ptr, len);
+                    if (do_ac3_passthru)
+                    {
+                        data_size = pkt->size;
+                        ret = EncodeAC3Frame(ptr, len, samples, data_size);
+                    }
+                    else
+                    {
+                        ret = avcodec_decode_audio(&curstream->codec, samples,
+                                                   &data_size, ptr, len);
+                    }
 
                     ptr += ret;
                     len -= ret;
@@ -1095,17 +1121,20 @@
                     if (data_size <= 0)
                         continue;
 
-                    if (CheckAudioParams(curstream->codec.sample_rate,
-                                         curstream->codec.channels))
+                    if (!do_ac3_passthru)
                     {
-                        audio_sampling_rate = curstream->codec.sample_rate;
-                        audio_channels = curstream->codec.channels;
-                        audio_sample_size = audio_channels * 2;
-
-                        m_parent->SetEffDsp(audio_sampling_rate * 100);
-                        m_parent->SetAudioParams(16, audio_channels,
-                                                 audio_sampling_rate);
-                        m_parent->ReinitAudio();
+                        if (CheckAudioParams(curstream->codec.sample_rate,
+                                             curstream->codec.channels))
+                        {
+                            audio_sampling_rate = curstream->codec.sample_rate;
+                            audio_channels = curstream->codec.channels;
+                            audio_sample_size = audio_channels * 2;
+
+                            m_parent->SetEffDsp(audio_sampling_rate * 100);
+                            m_parent->SetAudioParams(16, audio_channels,
+                                                     audio_sampling_rate);
+                            m_parent->ReinitAudio();
+                        }
                     }
 
                     long long temppts = lastapts;
@@ -1403,3 +1432,48 @@
    
     return true;
 }
+
+int AvFormatDecoder::EncodeAC3Frame(unsigned char *data, int len, 
+                                    short *samples, int &samples_size)
+{
+    int enc_len;
+    int flags, sample_rate, bit_rate;
+    unsigned char* ucsamples = (unsigned char*) samples;
+
+    // we don't do any length/crc validation of the AC3 frame here; presumably
+    // the receiver will have enough sense to do that.  if someone has a
+    // receiver that doesn't, here would be a good place to put in a call
+    // to a52_crc16_block(samples+2, data_size-2) - but what do we do if the
+    // packet is bad?  we'd need to send something that the receiver would
+    // ignore, and if so, may as well just assume that it will ignore 
+    // anything with a bad CRC...
+   
+    enc_len = a52_syncinfo(data, &flags, &sample_rate, &bit_rate);
+
+    if (enc_len == 0 || enc_len > len)
+    {
+        samples_size = 0;
+        return len;
+    }
+
+    if (enc_len > MAX_AC3_FRAME_SIZE - 8)
+        enc_len = MAX_AC3_FRAME_SIZE - 8;
+ 
+    swab(data, ucsamples + 8, enc_len);
+
+    // the following values come from ao_hwac3.c in mplayer.
+    // they form a valid IEC958 AC3 header.
+    ucsamples[0] = 0x72;
+    ucsamples[1] = 0xF8;
+    ucsamples[2] = 0x1F;
+    ucsamples[3] = 0x4E;
+    ucsamples[4] = 0x01; 
+    ucsamples[5] = 0x00;
+    ucsamples[6] = (enc_len << 3) & 0xFF;
+    ucsamples[7] = (enc_len >> 5) & 0xFF;
+    memset(ucsamples + 8 + enc_len, 0, MAX_AC3_FRAME_SIZE - 8 - enc_len);
+    samples_size = MAX_AC3_FRAME_SIZE;
+
+    return len;  // consume whole frame even if len > enc_len ?
+}
+
--- libs/libmythtv/avformatdecoder.h.orig	2003-11-26 02:03:15.000000000 -0600
+++ libs/libmythtv/avformatdecoder.h	2003-11-25 23:05:27.000000000 -0600
@@ -93,6 +93,9 @@
                       int *lower_bound, int *upper_bound=NULL);
     int AdjustKeyframedist(long long current_framenum, long long current_pos);
 
+    int EncodeAC3Frame(unsigned char* data, int len, short *samples,
+		       int &samples_size);
+
     RingBuffer *ringBuffer;
 
     AVFormatContext *ic;
@@ -157,6 +160,8 @@
     long long video_last_P_pts;
     long long lastvpts;
     long long lastapts; 
+
+    bool do_ac3_passthru;
 };
 
 #endif
Index: programs/mythfrontend/globalsettings.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythfrontend/globalsettings.cpp,v
retrieving revision 1.113
diff -u -r1.113 globalsettings.cpp
--- programs/mythfrontend/globalsettings.cpp	21 Nov 2003 17:41:27 -0000	1.113
+++ programs/mythfrontend/globalsettings.cpp	26 Nov 2003 07:57:25 -0000
@@ -115,6 +129,19 @@
         };
 };
 
+class AC3PassThrough: public CheckBoxSetting, public GlobalSetting {
+public:
+    AC3PassThrough():
+        GlobalSetting("AC3PassThru") {
+        setLabel(QObject::tr("Enable AC3 to SPDIF passthrough"));
+        setValue(false);
+        setHelpText(QObject::tr("Enable sending AC3 sound directly to your "
+                    "sound card's SPDIF output, on sources which contain "
+                    "AC3 soundtracks (usually digital TV).  Requires that "
+                    "the audio output device be set to something suitable."));
+    };
+};
+
 class Deinterlace: public CheckBoxSetting, public GlobalSetting {
 public:
     Deinterlace():
@@ -1207,6 +1234,7 @@
          setUseLabel(false);
 
          addChild(new AudioOutputDevice());
+         addChild(new AC3PassThrough());
          addChild(new AggressiveBuffer());
 
          Setting* volumeControl = new MythControlsVolume();


More information about the mythtv-dev mailing list