Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 11213)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -73,7 +73,7 @@
     AvFormatDecoderPrivate(bool allow_libmpeg2)
         : mpeg2dec(NULL), allow_mpeg2dec(allow_libmpeg2) { ; }
    ~AvFormatDecoderPrivate() { DestroyMPEG2(); }
-    
+
     bool InitMPEG2();
     bool HasMPEG2Dec() const { return (bool)(mpeg2dec); }
 
@@ -213,7 +213,7 @@
                             !(info->display_picture->flags &
                               PIC_FLAG_PROGRESSIVE_FRAME);
                         partialFrames.enqueue(frm);
-                        
+
                     }
                 }
                 if (info->discard_fbuf)
@@ -261,7 +261,7 @@
       lastccptsu(0),
       using_null_videoout(use_null_videoout),
       video_codec_id(kCodec_NONE),
-      maxkeyframedist(-1), 
+      maxkeyframedist(-1),
       ccd(new CCDecoder(this)),
       // Audio
       audioSamples(new short int[AVCODEC_MAX_AUDIO_FRAME_SIZE]),
@@ -382,7 +382,7 @@
     long long ts = (long long)( diff / fps );
     if (av_seek_frame(ic, -1, ts, AVSEEK_FLAG_BACKWARD) < 0)
     {
-        VERBOSE(VB_IMPORTANT, LOC_ERR 
+        VERBOSE(VB_IMPORTANT, LOC_ERR
                 <<"av_seek_frame(ic, -1, "< 0 && !ateof; skipFrames--)
     {
-	GetFrame(0);
+        GetFrame(0);
         if (decoded_video_frame)
             GetNVP()->DiscardVideoFrame(decoded_video_frame);
     }
@@ -725,7 +725,7 @@
         return -1;
     }
 
-    /* av_find_stream_info() eventually makes calls to avcodec_open() and avcodec_close() 
+    /* av_find_stream_info() eventually makes calls to avcodec_open() and avcodec_close()
        so we have to use the avcodeclock */
     avcodeclock.lock();
     int ret = av_find_stream_info(ic);
@@ -873,7 +873,7 @@
     enc->rate_emu = 0;
     enc->error_rate = 0;
 
-    AVCodec *codec = avcodec_find_decoder(enc->codec_id);    
+    AVCodec *codec = avcodec_find_decoder(enc->codec_id);
 
     if (!gContext->GetNumSetting("DecodeExtraAudio", 0) &&
         codec->id != CODEC_ID_MPEG2VIDEO_XVMC           &&
@@ -1093,7 +1093,7 @@
             }
         }
 
-        if (enc->codec_type != CODEC_TYPE_AUDIO && 
+        if (enc->codec_type != CODEC_TYPE_AUDIO &&
             enc->codec_type != CODEC_TYPE_VIDEO &&
             enc->codec_type != CODEC_TYPE_SUBTITLE)
             continue;
@@ -1103,7 +1103,7 @@
         AVCodec *codec = avcodec_find_decoder(enc->codec_id);
         if (!codec)
         {
-            VERBOSE(VB_IMPORTANT, LOC_ERR + 
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
                     QString("Could not find decoder for "
                             "codec (%1), ignoring.")
                     .arg(codec_id_string(enc->codec_id)));
@@ -1164,7 +1164,15 @@
                 lang_indx = lang_aud_cnt[lang];
                 lang_aud_cnt[lang]++;
             }
-            audioStreams.push_back(StreamInfo(i, lang, lang_indx));
+            if (enc->avcodec_dual_language)
+            {
+                audioStreams.push_back(StreamInfo(i, lang, lang_indx, 0));
+                audioStreams.push_back(StreamInfo(i, lang, lang_indx, 1));
+            }
+            else
+            {
+                audioStreams.push_back(StreamInfo(i, lang, lang_indx));
+            }
 
             VERBOSE(VB_AUDIO, LOC + QString(
                         "Audio Track #%1 is A/V stream #%2 "
@@ -1200,6 +1208,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 = audioStreams.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)
+        audioStreams.push_back(audioStreams[0]);
+        audioStreams[0].av_substream_index = 0;
+        audioStreams[1].av_substream_index = 1;
+    }
+    else
+    {
+        assert(numStreams == 2);
+
+        if (numStreams != 2)
+            return false;
+
+        // Remove extra stream
+        audioStreams[0].av_substream_index = -1;
+        audioStreams.pop_back();
+    }
+
+    // Changed
+    return true;
+}
+
 int get_avf_buffer(struct AVCodecContext *c, AVFrame *pic)
 {
     AvFormatDecoder *nd = (AvFormatDecoder *)(c->opaque);
@@ -1339,13 +1405,13 @@
 
         cc_code = *current++;
         curbytes++;
-    
+
         if (buf_size - curbytes < 2)
             break;
-    
+
         data1 = *current;
         data2 = *(current + 1);
-    
+
         switch (cc_code)
         {
             case 0xfe:
@@ -1422,10 +1488,10 @@
     {
         cc_code = *current++;
         curbytes++;
-    
+
         if (buf_size - curbytes < 2)
             break;
-    
+
         data1 = *current++;
         data2 = *current++;
         curbytes += 2;
@@ -1566,7 +1632,7 @@
         {
             long long startpos = pkt->pos;
 
-            VERBOSE(VB_PLAYBACK, LOC + 
+            VERBOSE(VB_PLAYBACK, LOC +
                     QString("positionMap[ %1 ] == %2.")
                     .arg(prevgoppos / keyframedist)
                     .arg((int)startpos));
@@ -1640,7 +1706,7 @@
                 align_dimensions(context, awidth, aheight);
 
                 GetNVP()->SetVideoParams(awidth, aheight, seqFPS,
-                                         keyframedist, aspect, 
+                                         keyframedist, aspect,
                                          kScan_Detect);
 
                 current_width  = width;
@@ -1724,12 +1790,12 @@
             continue;
 
         const uint line  = ((i < 18) ? i : i-18) + min_blank;
-        const uint field = (i<18) ? 0 : 1; 
+        const uint field = (i<18) ? 0 : 1;
         const uint id2 = *buf & 0xf;
         switch (id2)
         {
             case VBI_TYPE_TELETEXT:
-                // SECAM lines  6-23 
+                // SECAM lines  6-23
                 // PAL   lines  6-22
                 // NTSC  lines 10-21 (rare)
                 //ttd->Decode(buf+1, VBI_IVTV);
@@ -1903,6 +1969,39 @@
     return selectedTrack;
 }
 
+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];
+    }
+}
+
 /** \fn AvFormatDecoder::autoSelectAudioTrack(void)
  *  \brief Selects the best audio track.
  *
@@ -1915,12 +2014,12 @@
  *      If it can not be located we attempt to find a stream
  *      in the same language.
  *
- *   2) If we can not reselect the last user selected stream,
+ *   3) If we can not reselect the last user selected stream,
  *      then for each preferred language from most preferred
  *      to least preferred, we try to find a new stream based
  *      on the algorithm below.
  *
- *   3) If we can not select a stream in a preferred language
+ *   4) If we can not select a stream in a preferred language
  *      we try to select a stream irrespective of language
  *      based on the algorithm below.
  *
@@ -1972,6 +2071,21 @@
 
     int selectedTrack = (1 == numStreams) ? 0 : -1;
 
+    // Dual stream without explicit language information: choose
+    // the previous (sub)stream that was kept in wantedAudioStream.
+    if (selectedTrack < 0 && wantedAudioStream.av_substream_index != -1)
+    {
+        int substream_index = wantedAudioStream.av_substream_index;
+        for (uint i = 0; i < numStreams; i++)
+        {
+            if (audioStreams[i].av_substream_index == substream_index)
+            {
+                selectedTrack = i;
+                break;
+            }
+        }
+    }
+
     if ((selectedTrack < 0) && wantedAudioStream.language>=-1 && numStreams)
     {
         VERBOSE(VB_AUDIO, LOC + "Trying to reselect audio track");
@@ -2115,7 +2229,7 @@
 {
     uint numStreams = subtitleStreams.size();
 
-    if ((currentSubtitleTrack >= 0) && 
+    if ((currentSubtitleTrack >= 0) &&
         (currentSubtitleTrack < (int)numStreams))
     {
         return true; // subtitle already selected
@@ -2176,9 +2290,9 @@
     selectedSubtitleStream = subtitleStreams[currentSubtitleTrack];
     if (wantedSubtitleStream.av_stream_index < 0)
         wantedSubtitleStream = selectedSubtitleStream;
-     
+
     int lang = subtitleStreams[currentSubtitleTrack].language;
-    VERBOSE(VB_PLAYBACK, LOC + 
+    VERBOSE(VB_PLAYBACK, LOC +
             QString("Selected subtitle track #%1 in the %2 language(%3)")
             .arg(currentSubtitleTrack+1)
             .arg(iso639_key_toName(lang)).arg(lang));
@@ -2380,6 +2494,7 @@
         avcodeclock.lock();
         int ctype  = curstream->codec->codec_type;
         int audIdx = selectedAudioStream.av_stream_index;
+        int audSubIdx = selectedAudioStream.av_substream_index;
         int subIdx = selectedSubtitleStream.av_stream_index;
         avcodeclock.unlock();
 
@@ -2389,6 +2504,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)
@@ -2398,16 +2522,24 @@
                         ret = avcodec_decode_audio(
                             curstream->codec, audioSamples,
                             &data_size, ptr, len);
-                        if (curstream->codec->channels)
+                        if (curstream->codec->channels != 0)
                         {
-                            currentAudioTrack = -1;
-                            selectedAudioStream.av_stream_index = -1;
-                            audIdx = -1;
-                            autoSelectAudioTrack();
-                            audIdx = selectedAudioStream.av_stream_index;
+                            reselectAudioTrack = true;
                         }
                     }
 
+                    if (reselectAudioTrack)
+                    {
+                        QMutexLocker locker(&avcodeclock);
+                        currentAudioTrack = -1;
+                        selectedAudioStream.av_stream_index = -1;
+                        audIdx = -1;
+                        audSubIdx = -1;
+                        autoSelectAudioTrack();
+                        audIdx = selectedAudioStream.av_stream_index;
+                        audSubIdx = selectedAudioStream.av_substream_index;
+                    }
+
                     if (firstloop && pkt->pts != (int64_t)AV_NOPTS_VALUE)
                         lastapts = (long long)(av_q2d(curstream->time_base) *
                                                pkt->pts * 1000);
@@ -2480,9 +2612,14 @@
 
                     // calc for next frame
                     lastapts += (long long)((double)(data_size * 1000) /
-                                (curstream->codec->channels * 2) / 
+                                (curstream->codec->channels * 2) /
                                 curstream->codec->sample_rate);
 
+                    if (audSubIdx != -1)
+                    {
+                        filter_audio_data(audSubIdx, &audioOut,
+                                          (char *)audioSamples, data_size);
+                    }
                     GetNVP()->AddAudioData((char *)audioSamples, data_size,
                                            temppts);
 
@@ -2539,21 +2676,21 @@
                     if (!directrendering)
                     {
                         AVPicture tmppicture;
- 
+
                         VideoFrame *xf = picframe;
                         picframe = GetNVP()->GetNextVideoFrame(false);
 
                         tmppicture.data[0] = picframe->buf;
-                        tmppicture.data[1] = tmppicture.data[0] + 
+                        tmppicture.data[1] = tmppicture.data[0] +
                                         picframe->width * picframe->height;
-                        tmppicture.data[2] = tmppicture.data[1] + 
+                        tmppicture.data[2] = tmppicture.data[1] +
                                         picframe->width * picframe->height / 4;
 
                         tmppicture.linesize[0] = picframe->width;
                         tmppicture.linesize[1] = picframe->width / 2;
                         tmppicture.linesize[2] = picframe->width / 2;
 
-                        img_convert(&tmppicture, PIX_FMT_YUV420P, 
+                        img_convert(&tmppicture, PIX_FMT_YUV420P,
                                     (AVPicture *)&mpa_pic,
                                     context->pix_fmt,
                                     context->width,
@@ -2627,7 +2764,7 @@
                     ptr += len;
                     len = 0;
 
-                    if (gotSubtitles) 
+                    if (gotSubtitles)
                     {
                         subtitle.start_display_time += pts;
                         subtitle.end_display_time += pts;
@@ -2707,7 +2844,7 @@
     {
         assert(curstream);
         assert(curstream->codec);
-        codec_ctx = curstream->codec;        
+        codec_ctx = curstream->codec;
         bool do_ac3_passthru = (allow_ac3_passthru && !transcoding &&
                                 !disable_passthru &&
                                 (codec_ctx->codec_id == CODEC_ID_AC3));
@@ -2843,7 +2980,7 @@
 {
     m_parent->AddTextData((char*)buf, len, timecode, type);
 }
-  
+
 static int DTS_SAMPLEFREQS[16] =
 {
     0,      8000,   16000,  32000,  64000,  128000, 11025,  22050,
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 11213)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -23,11 +23,14 @@
 class StreamInfo
 {
   public:
-    StreamInfo() : av_stream_index(-1), language(-2), language_index(0) {}
+    StreamInfo() : av_stream_index(-1), av_substream_index(-1), language(-2), language_index(0) {}
     StreamInfo(int a, int b, uint c)
-        : av_stream_index(a), language(b), language_index(c) {}
+        : av_stream_index(a), av_substream_index(-1), language(b), language_index(c) {}
+    StreamInfo(int a, int b, uint c, int d)
+        : av_stream_index(a), av_substream_index(d), language(b), language_index(c) {}
   public:
     int  av_stream_index;
+    int  av_substream_index; // -1 = all, otherwise a channel number
     int  language; ///< ISO639 canonical language key
     uint language_index;
 };
@@ -127,17 +130,18 @@
     virtual void SetDisablePassThrough(bool disable);
     virtual void incCurrentAudioTrack();
     virtual void decCurrentAudioTrack();
-    virtual bool setCurrentAudioTrack(int trackNo);    
+    virtual bool setCurrentAudioTrack(int trackNo);
     virtual QStringList listAudioTracks() const;
 
     void AddTextData(unsigned char *buf, int len, long long timecode, char type);
 
     virtual void incCurrentSubtitleTrack();
     virtual void decCurrentSubtitleTrack();
-    virtual bool setCurrentSubtitleTrack(int trackNo);    
+    virtual bool setCurrentSubtitleTrack(int trackNo);
     virtual QStringList listSubtitleTracks() const;
 
     int ScanStreams(bool novideo);
+    bool FixAudioStreamSubIndexes(bool wasDual, bool isDual);
 
     virtual bool DoRewind(long long desiredFrame, bool doflush = true);
     virtual bool DoFastForward(long long desiredFrame, bool doflush = true);
Index: libs/libmythtv/mpegrecorder.cpp
===================================================================
--- libs/libmythtv/mpegrecorder.cpp	(revision 11213)
+++ libs/libmythtv/mpegrecorder.cpp	(working copy)
@@ -46,6 +46,7 @@
                                            "DVD-Special 2", 0 };
 const char* MpegRecorder::aspectRatio[] = { "Square", "4:3", "16:9", 
                                             "2.21:1", 0 };
+const char* MpegRecorder::languages[] = { "Language I", "Language II", "Dual" };
 
 #define BUILDBUFFERMAX (1024 * 1024)
 
@@ -75,6 +76,7 @@
     audbitratel1 = 14;
     audbitratel2 = 14;
     audvolume = 80;
+    language = 0;
 
     deviceIsMpegFile = false;
     bufferSize = 4096;
@@ -175,6 +177,22 @@
         if (!found)
             cerr << "MPEG2 stream type: " << value << " is invalid\n";
     }
+    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;
@@ -235,6 +253,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());
@@ -357,6 +377,45 @@
     if (ivtvcodec.framerate)
         keyframedist = 12;
 
+    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)
@@ -193,6 +193,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:
@@ -222,6 +242,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/libmythtv/mpegrecorder.h
===================================================================
--- libs/libmythtv/mpegrecorder.h	(revision 11213)
+++ libs/libmythtv/mpegrecorder.h	(working copy)
@@ -74,6 +74,7 @@
     int bitrate, maxbitrate, streamtype, aspectratio;
     int audtype, audsamplerate, audbitratel1, audbitratel2;
     int audvolume;
+    int language;
 
     int chanfd;
     int readfd;
@@ -89,6 +90,7 @@
     static const int audRateL2[];
     static const char* streamType[];
     static const char* aspectRatio[];
+    static const char* languages[];
 
     unsigned int leftovers;
     long long lastpackheaderpos;
Index: libs/libavcodec/mpegaudiodec.c
===================================================================
--- libs/libavcodec/mpegaudiodec.c	(revision 11213)
+++ libs/libavcodec/mpegaudiodec.c	(working copy)
@@ -1229,6 +1229,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)
@@ -855,6 +855,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