Index: libs/libmythtv/decoderbase.h
===================================================================
--- libs/libmythtv/decoderbase.h	(revision 11213)
+++ libs/libmythtv/decoderbase.h	(working copy)
@@ -21,13 +21,18 @@
         stream_id(-1), easy_reader(false), wide_aspect_ratio(false) {}
     StreamInfo(int a, int b, uint c, int d, bool e = false, bool f = false) :
         av_stream_index(a), language(b), language_index(c), stream_id(d),
-        easy_reader(e), wide_aspect_ratio(f) {}
+        av_substream_index(-1), easy_reader(e), wide_aspect_ratio(f) {}
+    // Audio stream/substream constructor
+    StreamInfo(int a, int b, uint c, int d, int e) :
+        av_stream_index(a), language(b), language_index(c), stream_id(d),
+        av_substream_index(e), easy_reader(false), wide_aspect_ratio(false) {}
 
   public:
     int  av_stream_index;
     int  language; ///< ISO639 canonical language key
     uint language_index;
     int  stream_id;
+    int  av_substream_index; // -1 = no substream, 0 for first dual audio stream
     bool easy_reader;
     bool wide_aspect_ratio;
 
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 11213)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -1468,8 +1468,18 @@
                 lang_indx = lang_aud_cnt[lang];
                 lang_aud_cnt[lang]++;
             }
-            tracks[kTrackTypeAudio].push_back(
-                StreamInfo(i, lang, lang_indx, ic->streams[i]->id));
+            if (ic->streams[i]->codec->avcodec_dual_language)
+            {
+                tracks[kTrackTypeAudio].push_back(
+                    StreamInfo(i, lang, lang_indx, ic->streams[i]->id, 0));
+                tracks[kTrackTypeAudio].push_back(
+                    StreamInfo(i, lang, lang_indx, ic->streams[i]->id, 1));
+            }
+            else
+            {
+                tracks[kTrackTypeAudio].push_back(
+                    StreamInfo(i, lang, lang_indx, ic->streams[i]->id));
+            }
 
             VERBOSE(VB_AUDIO, LOC + QString(
                         "Audio Track #%1 is A/V stream #%2 "
@@ -1533,6 +1543,64 @@
     return scanerror;
 }
 
+/** \fn AvFormatDecoder::FixAudioStreamSubIndexes(bool, bool)
+ *  \brief Reacts to DUAL/STEREO changes on the fly and fix streams.
+ *
+ *  This function kicks in when a switch between dual and stereo
+ *  mpeg audio is detected. Such changes can and will happen at
+ *  any time.
+ *
+ *  It can only be used in the case where the mpeg file contains
+ *  exactly one audio stream that switches automatically between
+ *  stereo (1 language) and dual (2 unspecified languages) audio.
+ *
+ *  If this method returns true, the stream list has changed and
+ *  a new audio stream needs to be selected, usually using
+ *  AvFormatDecoder::autoSelectSubtitleTrack()
+ *
+ *  \param wasDual true if the stream used to contain dual audio
+ *  \param isDual true if the stream currently contains dual audio
+ *  \return true if a change was made
+ */
+bool AvFormatDecoder::FixAudioStreamSubIndexes(bool wasDual, bool isDual)
+{
+    if (wasDual == isDual)
+    {
+        // No changes
+        return false;
+    }
+
+    QMutexLocker locker(&avcodeclock);
+    int numStreams = tracks[kTrackTypeAudio].size();
+    if (isDual)
+    {
+        assert(numStreams == 1);
+
+        // When assertions are off: ignore change
+        if (numStreams != 1)
+            return false;
+
+        // Split stream in two (Language I + Language II)
+        tracks[kTrackTypeAudio].push_back(tracks[kTrackTypeAudio][0]);
+        tracks[kTrackTypeAudio][0].av_substream_index = 0;
+        tracks[kTrackTypeAudio][1].av_substream_index = 1;
+    }
+    else
+    {
+        assert(numStreams == 2);
+
+        if (numStreams != 2)
+            return false;
+
+        // Remove extra stream
+        tracks[kTrackTypeAudio][0].av_substream_index = -1;
+        tracks[kTrackTypeAudio].pop_back();
+    }
+
+    // Changed
+    return true;
+}
+
 int get_avf_buffer(struct AVCodecContext *c, AVFrame *pic)
 {
     AvFormatDecoder *nd = (AvFormatDecoder *)(c->opaque);
@@ -2346,7 +2414,9 @@
  *   preference:
  *
  *   1) The stream last selected by the user, which is
- *      recalled as the Nth stream in the preferred language.
+ *      recalled as the Nth stream in the preferred language
+ *      or the Nth substream when audio is in dual language
+ *      format (each channel contains a different language track)
  *      If it can not be located we attempt to find a stream
  *      in the same language.
  *
@@ -2413,6 +2483,24 @@
     int selTrack = (1 == numStreams) ? 0 : -1;
     int wlang    = wtrack.language;
 
+
+    if (selTrack < 0 && wtrack.av_substream_index >=0)
+    {
+        VERBOSE(VB_AUDIO, LOC + "Trying to reselect audio sub-stream");
+        // Dual stream without language information: choose
+        // the previous (sub)stream that was kept in wtrack.
+        int substream_index = wtrack.av_substream_index;
+
+        for (uint i = 0; i < numStreams; i++)
+        {
+            if (atracks[i].av_substream_index == substream_index)
+            {
+                selTrack = i;
+                break;
+            }
+        }
+    }
+
     if ((selTrack < 0) && wlang >= -1 && numStreams)
     {
         VERBOSE(VB_AUDIO, LOC + "Trying to reselect audio track");
@@ -2493,6 +2581,40 @@
     return selTrack;
 }
 
+static void filter_audio_data(int channel, AudioInfo* audioInfo,
+                              char *buffer, int bufsize)
+{
+    // Only stereo -> mono (left or right) is supported
+    assert(audioInfo->channels == 2);
+    assert(channel == 0 || channel == 1);
+
+    const int samplesize = audioInfo->sample_size;
+    const int samples = bufsize / samplesize;
+    const int halfsample = samplesize/2;
+
+    const char *from;
+    char *to;
+
+    if (channel==0)
+    {
+        from=buffer;
+        to=buffer+halfsample;
+    }
+    else
+    {
+        from=buffer+halfsample;
+        to=buffer;
+    }
+
+    for (int sample = 0; sample < samples;
+         sample++, from += samplesize, to += samplesize)
+    {
+        for(int bit=0; bit < halfsample; bit++)
+            to[bit]=from[bit];
+    }
+}
+
+
 bool AvFormatDecoder::GetFrame(int onlyvideo)
 {
     AVPacket *pkt = NULL;
@@ -2761,6 +2883,7 @@
         avcodeclock.lock();
         int ctype  = curstream->codec->codec_type;
         int audIdx = selectedTrack[kTrackTypeAudio].av_stream_index;
+        int audSubIdx = selectedTrack[kTrackTypeAudio].av_substream_index;
         int subIdx = selectedTrack[kTrackTypeSubtitle].av_stream_index;
         avcodeclock.unlock();
 
@@ -2770,6 +2893,15 @@
             {
                 case CODEC_TYPE_AUDIO:
                 {
+                    bool reselectAudioTrack = false;
+
+                    // detect switches between stereo and dual languages
+                    if (FixAudioStreamSubIndexes(audSubIdx != -1,
+                                                 curstream->codec->avcodec_dual_language))
+                    {
+                        reselectAudioTrack = true;
+                    }
+
                     // detect channels on streams that need
                     // to be decoded before we can know this
                     if (!curstream->codec->channels)
@@ -2781,16 +2913,26 @@
                             &data_size, ptr, len);
                         if (curstream->codec->channels)
                         {
-                            currentTrack[kTrackTypeAudio] = -1;
-                            selectedTrack[kTrackTypeAudio]
-                                .av_stream_index = -1;
-                            audIdx = -1;
-                            AutoSelectAudioTrack();
-                            audIdx = selectedTrack[kTrackTypeAudio]
-                                .av_stream_index;
+                            reselectAudioTrack = true;
                         }
                     }
 
+                    if (reselectAudioTrack)
+                    {
+                        QMutexLocker locker(&avcodeclock);
+                        currentTrack[kTrackTypeAudio] = -1;
+                        selectedTrack[kTrackTypeAudio]
+                            .av_stream_index = -1;
+                        audIdx = -1;
+                        audSubIdx = -1;
+                        AutoSelectAudioTrack();
+                        audIdx = selectedTrack[kTrackTypeAudio]
+                            .av_stream_index;
+                        audSubIdx = selectedTrack[kTrackTypeAudio]
+                            .av_substream_index;
+                    }
+
+
                     if (firstloop && pkt->pts != (int64_t)AV_NOPTS_VALUE)
                         lastapts = (long long)(av_q2d(curstream->time_base) *
                                                pkt->pts * 1000);
@@ -2873,6 +3015,11 @@
                             .arg(pkt->pts).arg(pkt->dts).arg(temppts)
 							.arg(lastapts)); 
 
+                    if (audSubIdx != -1)
+                    {
+                        filter_audio_data(audSubIdx, &audioOut,
+                                          (char *)audioSamples, data_size);
+                    }
                     GetNVP()->AddAudioData((char *)audioSamples, data_size,
                                            temppts);
 
Index: libs/libmythtv/mpegrecorder.h
===================================================================
--- libs/libmythtv/mpegrecorder.h	(revision 11213)
+++ libs/libmythtv/mpegrecorder.h	(working copy)
@@ -77,6 +77,7 @@
     int bitrate, maxbitrate, streamtype, aspectratio;
     int audtype, audsamplerate, audbitratel1, audbitratel2;
     int audvolume;
+    int language;
 
     // Input file descriptors
     int chanfd;
@@ -101,6 +102,7 @@
 
     static const int   audRateL1[];
     static const int   audRateL2[];
+    static const char* languages[];
     static const char *streamType[];
     static const char *aspectRatio[];
     static const unsigned int kBuildBufferMaxSize;
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 11213)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -184,6 +184,7 @@
     void SeekReset(long long, uint skipFrames, bool doFlush, bool discardFrames);
 
     bool SetupAudioStream(void);
+    bool FixAudioStreamSubIndexes(bool wasDual, bool isDual);
 
     /// Update our position map, keyframe distance, and the like.
     /// Called for key frame packets.
Index: libs/libmythtv/mpegrecorder.cpp
===================================================================
--- libs/libmythtv/mpegrecorder.cpp	(revision 11213)
+++ libs/libmythtv/mpegrecorder.cpp	(working copy)
@@ -65,6 +65,11 @@
     "SVCD",      "DVD-Special 1", "DVD-Special 2", 0
 };
 
+const char* MpegRecorder::languages[] =
+{
+    "Language I", "Language II", "Dual", 0
+};
+
 const char* MpegRecorder::aspectRatio[] =
 {
     "Square", "4:3", "16:9", "2.21:1", 0
@@ -90,7 +95,7 @@
     streamtype(0),            aspectratio(2),
     audtype(2),               audsamplerate(48000),
     audbitratel1(14),         audbitratel2(14),
-    audvolume(80),
+    audvolume(80),            language(0),
     // Input file descriptors
     chanfd(-1),               readfd(-1),
     // Keyframe tracking inforamtion
@@ -201,6 +206,22 @@
                     QString("%1 is invalid").arg(value));
         }
     }
+    else if (opt == "mpeg2language")
+    {
+        bool found = false;
+        for (unsigned int i = 0; i < sizeof(languages) / sizeof(char*); i++)
+        {
+            if (QString(languages[i]) == value)
+            {
+                language = i;
+                found = true;
+                break;
+            }
+        }
+
+        if (!found)
+            cerr << "MPEG2 language (stereo) flag : " << value << " is invalid\n";
+    }
     else if (opt == "mpeg2aspectratio")
     {
         bool found = false;
@@ -268,6 +289,8 @@
               profile->byName("mpeg2streamtype")->getValue());
     SetOption("mpeg2aspectratio",
               profile->byName("mpeg2aspectratio")->getValue());
+    SetOption("mpeg2language",
+              profile->byName("mpeg2language")->getValue());
 
     SetIntOption(profile, "samplerate");
     SetOption("mpeg2audtype", profile->byName("mpeg2audtype")->getValue());
@@ -322,6 +345,45 @@
         return false;
     }
 
+    struct v4l2_tuner vt;
+    memset(&vt, 0, sizeof(struct v4l2_tuner));
+    if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) < 0)
+    {
+        cerr << "Error getting tuner settings\n";
+        perror("VIDIOC_G_TUNER:");
+        return false;
+    }
+
+    switch(language)
+    {
+        case 0:
+        default: /* just in case */
+            vt.audmode=V4L2_TUNER_MODE_LANG1;
+            break;
+
+        case 1:
+            vt.audmode=V4L2_TUNER_MODE_LANG2;
+            break;
+
+        case 2:
+            if(audtype != 1)
+                vt.audmode=V4L2_TUNER_MODE_STEREO;
+            else
+            {
+                vt.audmode=V4L2_TUNER_MODE_LANG1;
+                cerr << "Dual audio mode incompatible with Layer I audio, falling back to Language I (mpeg2language)\n";
+            }
+            break;
+    }
+
+    if (ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0)
+    {
+        cerr << "Error setting tuner audio mode\n";
+        perror("VIDIOC_S_TUNER:");
+        return false;
+    }
+
+
     struct v4l2_control ctrl;
     ctrl.id = V4L2_CID_AUDIO_VOLUME;
     ctrl.value = 65536 / 100 *audvolume;
Index: libs/libmythtv/recordingprofile.cpp
===================================================================
--- libs/libmythtv/recordingprofile.cpp	(revision 11213)
+++ libs/libmythtv/recordingprofile.cpp	(working copy)
@@ -214,6 +214,26 @@
     };
 };
 
+class MPEG2language: public CodecParam, public ComboBoxSetting {
+    public:
+        MPEG2language(const RecordingProfile& parent):
+            CodecParam(parent, "mpeg2language") {
+            setLabel(QObject::tr("SAP/Bilingual"));
+
+            addSelection("Language I");
+            addSelection("Language II");
+            addSelection("Dual");
+            setValue(0);
+            setHelpText(QObject::tr("Chooses the language(s) to record when "
+                                    "two languages are broadcast. Only Layer II "
+                                    "supports the recording of two languages (Dual). "
+                                    "Version 0.1.10+ of ivtv driver is required for this "
+                                    "setting to have any effect."));
+        };
+};
+
+
+
 class AudioCompressionSettings: public VerticalConfigurationGroup,
                                 public TriggeredConfigurationGroup {
 public:
@@ -243,6 +263,7 @@
         params->setLabel("MPEG-2 Hardware Encoder");
         params->addChild(new SampleRate(parent, false));
         params->addChild(new MPEG2AudioBitrateSettings(parent));
+        params->addChild(new MPEG2language(parent));
         params->addChild(new MPEG2audVolume(parent));
         addTarget("MPEG-2 Hardware Encoder", params);
 
Index: libs/libavcodec/mpegaudiodec.c
===================================================================
--- libs/libavcodec/mpegaudiodec.c	(revision 11213)
+++ libs/libavcodec/mpegaudiodec.c	(working copy)
@@ -1269,6 +1269,7 @@
 
     avctx->sample_rate = s->sample_rate;
     avctx->channels = s->nb_channels;
+    avctx->avcodec_dual_language = s->mode == MPA_DUAL ? 1 : 0;
     avctx->bit_rate = s->bit_rate;
     avctx->sub_id = s->layer;
     return s->frame_size;
Index: libs/libavcodec/avcodec.h
===================================================================
--- libs/libavcodec/avcodec.h	(revision 11213)
+++ libs/libavcodec/avcodec.h	(working copy)
@@ -823,6 +823,20 @@
      */
     int delay;
 
+
+   /**
+    * set when bilingual audio data has been detected.
+    * 0 normally, 1 if dual language flag is set
+    *
+    * this is a hack to keep binary compatibility with
+    * old versions of the library.
+    *
+    * - encoding: unused (called delay in this case...)
+    * - decoding: set by lavc
+    */
+#define avcodec_dual_language delay
+
+
     /* - encoding parameters */
     float qcompress;  ///< amount of qscale change between easy & hard scenes (0.0-1.0)
     float qblur;      ///< amount of qscale smoothing over time (0.0-1.0)

    Source: geocities.com/szermatt