diff -uN vdr-1.2.5-orig/DD-receiver-status.txt vdr-1.2.5-ac3/DD-receiver-status.txt
--- vdr-1.2.5-orig/DD-receiver-status.txt	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/DD-receiver-status.txt	2003-10-05 21:43:05.000000000 +0200
@@ -0,0 +1,32 @@
+                                      |     Working status
+ Receiver                             | AC3overDVB | Bitstreamout
+--------------------------------------+------------+-------------
+Creative 5700 Inspire                 |     ok     |
+Cambridge SoundWorks DTT-3500         |    fail    |
+DENON AVR-1802                        |     ok     |
+DENON AVR-3801                        |     ok     |
+DENON AVR-3802                        |     ok     |
+Harman/Kardon AVR 2500                |     ok     |
+Harman/Kardon AVR 3000                |     ok     |
+Harman/Kardon AVR 45RDS               |     ok     |     ok
+Hercules Thrustmaster Sound System 5.1|    fail    |     ok
+Kenwood KRF-V8060D                    |     ok     |
+Logitech Z-680                        |     ok     |    fail
+Marantz SR7200                        |     ok     |
+Onkyo DS-575                          |     ok     |
+Onkyo TXDS-676                        |     ok     |
+Panasonic SA HE 70                    |     ok     |
+Pioneer VSA-E07                       |     ok     |     ok
+Pioneer VSX-709RDS                    |     ok     |
+Sherwood R-956R                       |     ok     |
+Sony STR-DB870 QS                     |     ok     |     ok
+Sony STR-DE445                        |     ok     |
+Technics SA-DX 750                    |     ok     |     ok    (Sound mode needs to be set to "Pro Logic")
+Yamaha DSP-A1                         |    fail    |
+Yamaha DSP-A5                         |     ok     |
+Yamaha DSP-A595                       |     ok     |
+Yamaha RX-V420RDS                     |     ok     |     ok
+Yamaha RX-V520RDS                     |     ok     |
+Yamaha RX-V1300RDS                    |     ok     |
+
+Collected from user information. No warranty.
diff -uN vdr-1.2.5-orig/Makefile vdr-1.2.5-ac3/Makefile
--- vdr-1.2.5-orig/Makefile	2003-08-09 13:09:45.000000000 +0200
+++ vdr-1.2.5-ac3/Makefile	2003-10-13 21:36:58.000000000 +0200
@@ -39,6 +39,9 @@
        receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sources.o\
        spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o
 
+# AC3overDVB
+OBJS += multichannelaudio.o pes.o
+
 OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
 FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
 
@@ -98,6 +101,14 @@
 genfontfile: genfontfile.c
 	$(CC) $(CFLAGS) -o $@ -L/usr/X11R6/lib $< -lX11
 
+# test tools
+
+test_cPES: test_cPES.o pes.o thread.o tools.o ringbuffer.o
+	$(CXX) $(CXXFLAGS) $^ -lpthread -o $@
+
+test_cMuCha: test_cMuCha.o pes.o multichannelaudio.o thread.o tools.o ringbuffer.o
+	$(CXX) $(CXXFLAGS) $^ -lpthread -o $@
+
 # The libdtv library:
 
 $(DTVLIB) $(DTVDIR)/libdtv.h:
@@ -144,6 +155,7 @@
 	-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
 	-rm -rf include
 	-rm -rf srcdoc
+	-rm -f test_cPES test_cPES.o test_cMuCha test_cMuCha.o
 fontclean:
 	-rm -f fontfix.c fontosd.c
 CLEAN: clean fontclean
diff -uN vdr-1.2.5-orig/README.AC3overDVB vdr-1.2.5-ac3/README.AC3overDVB
--- vdr-1.2.5-orig/README.AC3overDVB	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/README.AC3overDVB	2003-10-17 18:35:49.000000000 +0200
@@ -0,0 +1,118 @@
+***
+***
+*** AC3overDVB patch for VDR 1.2.x
+***
+***
+
+This patch is based on the AC3overDVB implementation in vdr-1.0.4-AIO.
+ * Support for encapsulating DD  by Wolfgang Fiesel
+ * Support for encapsulating DTS by <unknown>
+ * Initially ported to VDR 1.1.x by Jan Rieger
+ * Currently maintained by Stefan Huelswitt
+
+Takes AC3 or DTS data from a recorded or transfered stream, encapsulates the
+data into special headers and sends them out via SP/DIF connector of the card.
+
+No decoding is done. This has to be done by your external DD/DTS receiver. Most
+receivers can detect the encapsulated data, but not all.
+
+If your receiver doesn't detect the encapsulated stream (at the beginning of
+replay in general) you will hear the digital data played as PCM data. It's a
+spiky noise. Don't turn up the volume too much, or the speakers may be damaged.
+You have been warned!
+
+You can find a status list of working/non-working receivers in the file
+DD-receiver-status.txt. The lastest version of this file is available at
+<http://www.muempf.de/down/DD-receiver-status.txt>.
+This list has been build from user comments. The is no waranty that the given
+information are correct. If you find incorrect information or want to contribute
+to the list, please mail to huels@iname.com.
+
+To enable the digital audio, you must select "multichannel audio" from
+setup/dvb. For LiveAC3 you must enable "force transfermode" too. Transfer mode
+is enforced only for channels with DD pid.
+
+If you want to hear digital audio with the DVD plugin, you should either use
+dvd-0.3.1 and the patch dvd-0.3.1.AC3overDVB.diff.gz or use dvd-0.3.4 series
+from http://www.jausoft.de/Files/vdr/vdr-dvd.
+
+History:
+--------
+
+17.10.2003 Version 0.2.1
+-Fixed lost audio problem when returning from a replay/channel with digital
+ audio (thanks to Friedhelm Büscher for pointing me into the right direction).
+-Now muting the analog out while playing digital audio.
+-Rewrote LPCM detection. The damned AC3 continuation packets still got
+ misdetected sometimes.
+-Added some magic to minimize the effects of a corrupted AC3 stream.
+-Small addition to allow derived classes to keep track of skipped data.
+-Fixed a bug which could lead to incomplete encapsulated frames.
+-Fixed data access behond end of buffer.
+-Fixed still picture race with new code in VDR 1.2.5.
+
+01.06.2003 Version 0.2.0
+-Versions bump for vdr 1.2.0
+
+30.05.2003 Version 0.1.8
+-Adapted to the changes in vdr up to 1.1.33.
+-Fixed a race condition in the stream scanner.
+-Fixed MPEG2 pack header detection.
+
+27.04.2003 Version 0.1.7
+-Adapted to the changes in vdr up to 1.1.29.
+
+18.04.2003 Version 0.1.6
+-Improved LPCM frame detection (with some streams valid packets inside the AC3
+ stream were detected as LPCM).
+-Fixed PTS extraction (has broken DD on Pro7 in some cases).
+
+13.04.2003 Version 0.1.5
+-Made the stream scanner take care of packet differences between MPEG1 and
+ MPEG2.
+-Adapted to the changes in vdr 1.1.27.
+
+27.03.2003 Version 0.1.4
+-Fixed stream scanner (cPES) for some unusual packet types
+
+13.02.2003 Version 0.1.3
+-Fixed audio out via cAudios when multichannelaudio is disabled in the setup.
+-Fixed DVD audio out via cAudios (skip extra header).
+-Fixed audio distortion for StillPicture (e.g. while moving marks).
+-Added the define HAVE_AC3_OVER_DVB to make the patch detectable.
+
+03.02.2003 Version 0.1.2
+-Added code to make commandline option "-a" and other audio plugins work
+ together with AC3overDVB.
+-Adapted to the changes in vdr 1.1.23.
+
+29.01.2003 Version 0.1.1
+-Fixed substream detection.
+-Fixed many issues with encapsulation and switching between digital and normal
+ audio. Should avoid the "stacato" noise in most cases. Please report.
+-Finished new PES scanner and rewrote most parts of the stream handling.
+-Improved audio sync with DVD plugin. Please test & report. I don't have too
+ much DVDs for testing.
+-Moved setup options to DVB menupage.
+-Added option to force transfermode on channels with DD-Pid (LiveAC3).
+-NOTE: some of the cDevice API was changed to give the device a better control
+ over the replayed stream. This may break DXR3 stuff for now, but on long terms
+ this is IMO the best solution. Any questions? Any opinions?
+
+22.01.2003 Version 0.1.0
+-Improved data flow (less copy operations).
+-Encapsulated packets are re-inserted into the stream at the original position.
+ Does this give a better sync?
+-Changed lPCM description part. May be some more receivers can detect the stream
+ this way (thanks to Dr. Werner Fink).
+-Fixed digital audio when starting another replay while watching a replay.
+
+19.01.2003
+-Fixed substream detection for Premiere recordings. Unfortunaly this broke MP3
+ playback.
+
+18.01.2003
+-Fixed PTS output
+
+17.01.2003
+-Initial release
diff -uN vdr-1.2.5-orig/audio.c vdr-1.2.5-ac3/audio.c
--- vdr-1.2.5-orig/audio.c	2002-11-03 12:53:34.000000000 +0100
+++ vdr-1.2.5-ac3/audio.c	2003-10-13 21:36:58.000000000 +0200
@@ -14,7 +14,7 @@
 
 cAudio::cAudio(void)
 {
-  Audios.Add(this);
+  Audios.Register(this);
 }
 
 cAudio::~cAudio()
@@ -25,22 +25,91 @@
 
 cAudios Audios;
 
+cAudios::cAudios(void)
+{
+  numAudios=0;
+  buffer=0;
+}
+
+cAudios::~cAudios()
+{
+  free(buffer);
+}
+
+void cAudios::Register(cAudio *au)
+{
+  Add(au);
+  numAudios++;
+}
+
+#define BUFF_SIZE 16384
+#define HDR_SIZE  6
+
+void cAudios::BClear(void)
+{
+  have=0; need=HDR_SIZE;
+}
+
 void cAudios::PlayAudio(const uchar *Data, int Length)
 {
-  for (cAudio *audio = First(); audio; audio = Next(audio))
-      audio->Play(Data, Length);
+  if(numAudios) { // action only if we actual have slaves
+    if(!buffer) {
+      buffer=MALLOC(uchar,BUFF_SIZE);
+      BClear();
+      }
+    if(buffer) {
+      // we have to buffer up a complete packet here, as the new stream
+      // processing in PlayVideo() cannot quaranty to pass complete packets.
+      while(Length>0) {
+        if(have<need) { // buffer more data
+          int r=min(need-have,Length);
+          memcpy(buffer+have,Data,r);
+          have+=r; Data+=r; Length-=r;
+          }
+        if(have>=need) {
+          if(need==HDR_SIZE) { // only header buffered
+            if(buffer[0]!=0x00 || buffer[1]!=0x00 || buffer[2]!=0x01) {
+              printf("sanity check failed in cAudios\n");
+              BClear();
+              break;
+              }
+            need+=(buffer[4]*256+buffer[5]);
+            if(need>BUFF_SIZE) {
+              printf("buffer overflow in cAudios\n");
+              BClear();
+              break;
+              }
+            }
+          else { // packet complete
+            for (cAudio *audio = First(); audio; audio = Next(audio))
+                audio->Play(buffer, need);
+
+            have-=need;
+            if(have>0) { // this should not happen
+              memmove(buffer,buffer+need,have);
+              printf("memmove in cAudios (should not happen)\n");
+              }
+            need=HDR_SIZE;
+            }
+          }
+        }
+      }
+    }
 }
 
 void cAudios::MuteAudio(bool On)
 {
-  for (cAudio *audio = First(); audio; audio = Next(audio))
-      audio->Mute(On);
+  if(numAudios)
+    for (cAudio *audio = First(); audio; audio = Next(audio))
+        audio->Mute(On);
 }
 
 void cAudios::ClearAudio(void)
 {
-  for (cAudio *audio = First(); audio; audio = Next(audio))
-      audio->Clear();
+  BClear();
+  if(numAudios)
+    for (cAudio *audio = First(); audio; audio = Next(audio))
+        audio->Clear();
 }
 
 // --- cExternalAudio --------------------------------------------------------
diff -uN vdr-1.2.5-orig/audio.h vdr-1.2.5-ac3/audio.h
--- vdr-1.2.5-orig/audio.h	2002-11-03 12:50:02.000000000 +0100
+++ vdr-1.2.5-ac3/audio.h	2003-10-13 21:36:58.000000000 +0200
@@ -31,7 +31,16 @@
   };
 
 class cAudios : public cList<cAudio> {
+private:
+  int numAudios;
+  uchar *buffer;
+  int have, need;
+  //
+  void BClear(void);
 public:
+  cAudios(void);
+  ~cAudios();
+  void Register(cAudio *au);
   void PlayAudio(const uchar *Data, int Length);
   void MuteAudio(bool On);
   void ClearAudio(void);
diff -uN vdr-1.2.5-orig/config.c vdr-1.2.5-ac3/config.c
--- vdr-1.2.5-orig/config.c	2003-08-24 13:00:24.000000000 +0200
+++ vdr-1.2.5-ac3/config.c	2003-10-13 21:36:58.000000000 +0200
@@ -272,6 +272,8 @@
   UseSubtitle = 1;
   RecordingDirs = 1;
   VideoFormat = 0;
+  PlayMultichannelAudio = 0;
+  LiveMultichannelAudio = 0;
   RecordDolbyDigital = 1;
   ChannelInfoPos = 0;
   OSDwidth = 52;
@@ -424,6 +426,8 @@
   else if (!strcasecmp(Name, "UseSubtitle"))         UseSubtitle        = atoi(Value);
   else if (!strcasecmp(Name, "RecordingDirs"))       RecordingDirs      = atoi(Value);
   else if (!strcasecmp(Name, "VideoFormat"))         VideoFormat        = atoi(Value);
+  else if (!strcasecmp(Name, "PlayMultichannelAudio"))PlayMultichannelAudio = atoi(Value);
+  else if (!strcasecmp(Name, "LiveMultichannelAudio"))LiveMultichannelAudio = atoi(Value);
   else if (!strcasecmp(Name, "RecordDolbyDigital"))  RecordDolbyDigital = atoi(Value);
   else if (!strcasecmp(Name, "ChannelInfoPos"))      ChannelInfoPos     = atoi(Value);
   else if (!strcasecmp(Name, "OSDwidth"))            OSDwidth           = atoi(Value);
@@ -474,6 +478,8 @@
   Store("UseSubtitle",        UseSubtitle);
   Store("RecordingDirs",      RecordingDirs);
   Store("VideoFormat",        VideoFormat);
+  Store("PlayMultichannelAudio", PlayMultichannelAudio);
+  Store("LiveMultichannelAudio", LiveMultichannelAudio);
   Store("RecordDolbyDigital", RecordDolbyDigital);
   Store("ChannelInfoPos",     ChannelInfoPos);
   Store("OSDwidth",           OSDwidth);
diff -uN vdr-1.2.5-orig/config.h vdr-1.2.5-ac3/config.h
--- vdr-1.2.5-orig/config.h	2003-09-17 18:08:56.000000000 +0200
+++ vdr-1.2.5-ac3/config.h	2003-10-13 21:36:58.000000000 +0200
@@ -227,6 +227,8 @@
   int UseSubtitle;
   int RecordingDirs;
   int VideoFormat;
+  int PlayMultichannelAudio;
+  int LiveMultichannelAudio;
   int RecordDolbyDigital;
   int ChannelInfoPos;
   int OSDwidth, OSDheight;
diff -uN vdr-1.2.5-orig/device.c vdr-1.2.5-ac3/device.c
--- vdr-1.2.5-orig/device.c	2003-08-15 14:34:36.000000000 +0200
+++ vdr-1.2.5-ac3/device.c	2003-10-13 21:36:58.000000000 +0200
@@ -442,6 +442,21 @@
 {
 }
 
+int cDevice::ReplayNumAudioTracks(void) const
+{
+  return 0;
+}
+
+const char **cDevice::ReplayGetAudioTracks(int *CurrentTrack) const
+{
+  return NULL;
+}
+
+bool cDevice::ReplaySetAudioTrack(int Index)
+{
+  return false;
+}
+
 bool cDevice::ToggleMute(void)
 {
   int OldVolume = volume;
diff -uN vdr-1.2.5-orig/device.h vdr-1.2.5-ac3/device.h
--- vdr-1.2.5-orig/device.h	2003-08-15 15:05:50.000000000 +0200
+++ vdr-1.2.5-ac3/device.h	2003-10-13 21:36:58.000000000 +0200
@@ -308,6 +308,8 @@
        ///< Sets the current audio track to the given value, which should be within the
        ///< range of the list returned by a previous call to GetAudioTracks() (otherwise
        ///< nothing will happen).
+  virtual bool HasDigitalAudioOut(void) const { return false; }
+       ///< Returns true if this device can deliver AC3 or DTS over a SP/DIF output.
 
 // Player facilities
 
@@ -349,10 +351,13 @@
        ///< Actually plays the given data block as video. The data must be
        ///< part of a PES (Packetized Elementary Stream) which can contain
        ///< one video and one audio strem.
-  virtual void PlayAudio(const uchar *Data, int Length);
+  void PlayAudio(const uchar *Data, int Length);
        ///< Plays additional audio streams, like Dolby Digital.
        ///< A derived class must call the base class function to make sure data
        ///< is distributed to all registered cAudio objects.
+  virtual int ReplayNumAudioTracks(void) const;
+  virtual const char **ReplayGetAudioTracks(int *CurrentTrack) const;
+  virtual bool ReplaySetAudioTrack(int Index);
   bool Replaying(void) const;
        ///< Returns true if we are currently replaying.
   void StopReplay(void);
diff -uN vdr-1.2.5-orig/dvbdevice.c vdr-1.2.5-ac3/dvbdevice.c
--- vdr-1.2.5-orig/dvbdevice.c	2003-09-06 15:19:33.000000000 +0200
+++ vdr-1.2.5-ac3/dvbdevice.c	2003-10-16 20:21:08.000000000 +0200
@@ -308,6 +308,8 @@
 // --- cDvbDevice ------------------------------------------------------------
 
 cDvbDevice::cDvbDevice(int n)
+:cPES(prSkip)
+,cMultichannelAudio(rb)
 {
   dvbTuner = NULL;
   frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
@@ -342,6 +344,8 @@
         frontendType = feinfo.type;
         ciHandler = cCiHandler::CreateCiHandler(DvbName(DEV_DVB_CA, n));
         dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
+        // setup the PES analyser
+        SetupPES();
         }
      else
         LOG_ERROR;
@@ -653,6 +657,10 @@
 
   bool DoTune = !dvbTuner->IsTunedTo(Channel);
 
+  bool LiveDD = Setup.PlayMultichannelAudio && Setup.LiveMultichannelAudio
+                && LiveView
+                && (Channel->Dpid1() || Channel->Dpid2());
+
   bool TurnOffLivePIDs = HasDecoder()
                          && (DoTune
                             || IsEncrypted && pidHandles[ptVideo].pid != Channel->Vpid() // CA channels can only be decrypted in "live" mode
@@ -661,10 +669,14 @@
                             || pidHandles[ptVideo].pid == Channel->Vpid() // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
                             );
 
-  bool StartTransferMode = IsPrimaryDevice() && !IsEncrypted && !DoTune
-                           && (LiveView && HasPid(Channel->Vpid()) && pidHandles[ptVideo].pid != Channel->Vpid() // the PID is already set as DMX_PES_OTHER
-                              || !LiveView && pidHandles[ptVideo].pid == Channel->Vpid() // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
-                              );
+  bool StartTransferMode = IsPrimaryDevice() && !IsEncrypted
+                           && (LiveDD
+                               || (!DoTune
+                                   && (LiveView && HasPid(Channel->Vpid()) && pidHandles[ptVideo].pid != Channel->Vpid() // the PID is already set as DMX_PES_OTHER
+                                       || !LiveView && pidHandles[ptVideo].pid == Channel->Vpid() // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
+                                      )
+                                   )
+                                );
 
   bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode
                         && (IsEncrypted // CA channels can only be decrypted in "live" mode
@@ -725,13 +737,72 @@
 
 void cDvbDevice::SetVolumeDevice(int Volume)
 {
-  if (HasDecoder()) {
+  if (HasDecoder() && (audioTrack!=0xBD || !Volume)) {
      audio_mixer_t am;
      am.volume_left = am.volume_right = Volume;
      CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
      }
 }
 
+static uchar trackId[]      = { 0xC0, 0xC1, 0xBD };
+static bool  trackDigital[] = { false,false,true };
+static const char *trackNames[] = { "Audio 1","Audio 2","Digital Audio" };
+static const char *tracks[4];
+
+int cDvbDevice::ReplayNumAudioTracks(void) const
+{
+  int n=0;
+  for(unsigned int i=0 ; i<sizeof(trackId) ; i++)
+    if((!trackDigital[i] || Setup.PlayMultichannelAudio) && Seen(trackId[i]))
+      n++;
+  return n;
+}
+
+const char **cDvbDevice::ReplayGetAudioTracks(int *CurrentTrack) const
+{
+  int i=0;
+  int num=ReplayNumAudioTracks();
+  if (num) {
+     for(unsigned int j=0 ; j<sizeof(trackId) ; j++)
+       if((!trackDigital[j] || Setup.PlayMultichannelAudio) && Seen(trackId[j])) {
+         tracks[i]=trackNames[j];
+         if(trackId[j]==audioTrack && CurrentTrack) *CurrentTrack=i;
+         i++;
+         }
+     tracks[i]=0;
+     return tracks;
+     }
+  return NULL;
+}
+
+bool cDvbDevice::ReplaySetAudioTrack(int Index)
+{
+  if (0 <= Index && Index < ReplayNumAudioTracks()) {
+     int i=0;
+     uchar newAudioTrack=0;
+     for(unsigned int j=0 ; j<sizeof(trackId) ; j++)
+       if((!trackDigital[j] || Setup.PlayMultichannelAudio) && Seen(trackId[j])) {
+         if(Index==i) {
+           newAudioTrack=trackId[j];
+           break;
+           }
+         i++;
+         }
+     if(newAudioTrack && newAudioTrack!=audioTrack) {
+       int oldAudioTrack=audioTrack;
+       audioTrack=newAudioTrack;
+       trackSelected=true;
+       if((audioTrack&0xF0)==0xC0)
+         GoAnalog(oldAudioTrack==0xBD);
+       else
+         GoDigital();
+       cMultichannelAudio::Reset();
+       return true;
+       }
+     }
+  return false;
+}
+
 int cDvbDevice::NumAudioTracksDevice(void) const
 {
   int n = 0;
@@ -744,12 +815,15 @@
 
 const char **cDvbDevice::GetAudioTracksDevice(int *CurrentTrack) const
 {
-  if (NumAudioTracksDevice()) {
+  int i=0;
+  int num=NumAudioTracksDevice();
+  if (num) {
      if (CurrentTrack)
         *CurrentTrack = (pidHandles[ptAudio].pid == aPid1) ? 0 : 1;
-     static const char *audioTracks1[] = { "Audio 1", NULL };
-     static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
-     return NumAudioTracksDevice() > 1 ? audioTracks2 : audioTracks1;
+     tracks[i]=trackNames[i]; i++;
+     if(num > 1) { tracks[i]=trackNames[i]; i++; }
+     tracks[i]=0;
+     return tracks;
      }
   return NULL;
 }
@@ -772,6 +846,137 @@
   return cDevice::CanReplay() && (Ca() <= MAXDEVICES || ciHandler); // with non-LL-firmware we can only replay if there is no CA recording going on
 }
 
+static const unsigned char testAudio[] = {
+  0x00,0x00,0x01,0xc0,0x07,0xfa,0x87,0x80,0x05,0x2f,0x46,0x05,0x80,0xcf,0xff,0xfc,
+  0xa4,0x00,0xd2,0x6c,0x36,0x22,0x12,0x11,0x11,0x21,0x11,0x11,0x11,0x43,0x33,0xd5,
+  0xa4,0x6b,0xac,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0x12,0x32,0x23,0x23,0x22,0x22,
+  0x22,0x22,0x32,0x23,0x35,0x51,0x54,0xe3,0x1c,0x71,0xa6,0x1c,0x75,0xc7,0x99,0x75,
+  0xf7,0xda,0x71,0xd7,0x5c,0x65,0xd6,0xd9,0x71,0x97,0x1c,0x75,0xc7,0x5b,0x65,0xc6,
+  0xdc,0x6d,0xb5,0xd9,0x5d,0x95,0xd7,0x61,0x84,0x13,0x51,0x36,0x1b,0x69,0xb6,0x5b,
+  0x6d,0xd4,0xd3,0x5d,0x75,0x55,0x4d,0x76,0x9c,0x75,0xc7,0xdf,0x71,0xe6,0xdb,0x75,
+  0xd7,0x5c,0x79,0xc6,0xdd,0x75,0xd7,0xdd,0x82,0xf2,0xf4,0x60,0xcc,0xdb,0x95,0x96,
+  0x16,0xd5,0x4d,0xb5,0xac,0xd3,0x49,0x8e,0xbd,0xb1,0x6e,0x65,0x34,0xaa,0x69,0x40,
+  0x42,0x9a,0x5c,0x54,0x69,0xde,0x36,0x52,0xd1,0x88,0x89,0xdb,0x08,0x31,0xb5,0xac,
+  0x5e,0x6e,0xe7,0xb5,0x05,0x53,0x82,0x45,0xcb,0x54,0xeb,0xbd,0xb5,0x0b,0x98,0xc9,
+  0xaa,0xdb,0x12,0x65,0x41,0x1a,0x87,0xda,0xd4,0xa4,0xac,0x4d,0x45,0x51,0xe9,0x38,
+  0x88,0xe6,0x41,0xaa,0x6b,0x46,0xfc,0xed,0xcb,0x7a,0xda,0x0c,0xb4,0x94,0x42,0x44,
+  0x45,0x2f,0x69,0x1a,0xf6,0x49,0x86,0xb4,0x49,0x88,0x93,0x68,0xf8,0x85,0xd0,0x21,
+  0x8d,0x14,0xb2,0xea,0xa5,0xc3,0x71,0x34,0xc4,0x6e,0x2d,0x08,0x24,0xd0,0xcd,0x21,
+  0xe3,0x92,0x19,0x6d,0x54,0xb2,0x12,0xcd,0x64,0xc4,0xb2,0x6d,0x20,0x06,0x62,0x12,
+  0x11,0x16,0x96,0xd5,0x22,0x6e,0x8b,0xdc,0xba,0x95,0xe3,0x70,0xf1,0x23,0xcd,0x67,
+  0x06,0x85,0xb2,0x71,0xd9,0x55,0x8a,0x54,0xbb,0x37,0x98,0x63,0x60,0x6a,0x9a,0xc6,
+  0x00,0x5d,0x5f,0x0a,0x8a,0xc8,0x90,0xd2,0x4c,0x4c,0x81,0x44,0x00,0x90,0x01,0x82,
+  0x45,0x25,0x9d,0xe5,0xb6,0xa5,0x66,0x0d,0x65,0x6b,0x02,0x98,0x82,0x95,0x09,0x2e,
+  0x59,0xc8,0x46,0xa6,0xd6,0x32,0x29,0xdb,0x0a,0x3a,0xca,0x17,0x09,0x4a,0x34,0xd1,
+  0xa3,0x04,0x78,0xb2,0x57,0xa3,0x84,0x85,0x31,0x05,0x51,0x92,0x0d,0x83,0x5c,0x18,
+  0xb9,0xce,0x02,0x53,0x08,0xd7,0xb6,0x5c,0x4d,0x71,0x3d,0x8d,0x6d,0xc6,0xb5,0xa5,
+  0x99,0xda,0x8c,0xa6,0xe3,0xb5,0xb2,0x37,0xac,0xf5,0x65,0xae,0xe1,0x52,0x3b,0x05,
+  0x63,0xc4,0xa9,0x86,0xd6,0xb6,0x09,0x52,0x9a,0xac,0xa9,0x0f,0x1d,0x2d,0x43,0x20,
+  0xa5,0x6b,0x54,0xbb,0x96,0xbc,0x58,0xf9,0x4d,0x88,0x71,0xbd,0x1b,0xb0,0x93,0x1d,
+  0x9c,0xd0,0xc1,0x70,0xd2,0xc5,0x42,0x89,0x88,0xdb,0x66,0x0d,0x93,0x42,0xaa,0xd8,
+  0x85,0x13,0xa0,0x34,0x96,0x4d,0x6a,0xd4,0xe7,0x1b,0x22,0x16,0x45,0x34,0xe4,0x6b,
+  0x8f,0x1c,0xdb,0x4c,0xcf,0xaa,0xb5,0x36,0x0e,0xeb,0xc1,0xc8,0xeb,0x51,0x1e,0x0c,
+  0x99,0x51,0x2c,0xd6,0xa9,0x56,0xba,0xba,0x94,0x20,0x2c,0x4a,0xdd,0x53,0x12,0x2c,
+  0x6b,0x55,0x41,0xa3,0x6a,0xb2,0xd8,0x86,0xcb,0x2c,0x6e,0x26,0x8e,0x7a,0x6a,0x1b,
+  0x35,0x35,0xa0,0xa8,0xa3,0xa6,0x35,0x16,0x67,0x35,0xb0,0x5d,0x33,0x15,0x11,0x1a,
+  0x38,0xa3,0x95,0xa7,0x06,0x51,0x61,0x6a,0x88,0x20,0x54,0xa5,0xce,0xd3,0xed,0xb2,
+  0xa7,0x1b,0x63,0x4c,0xee,0xa4,0x66,0xa8,0x5a,0x4e,0x49,0x91,0xd2,0x38,0x69,0xef,
+  0x68,0x48,0xea,0x34,0x40,0x28,0x70,0xf1,0xf6,0x9c,0x80,0x05,0x28,0x5a,0xad,0x0c,
+  0x4b,0x3a,0x0f,0x4c,0xe3,0x49,0x6a,0x69,0x47,0x7e,0x12,0xa1,0x73,0x91,0x5e,0xb7,
+  0x4e,0xeb,0x93,0x29,0x59,0x08,0xac,0xd8,0x35,0xaa,0x6b,0x80,0x00,0x00,0xff,0xfc,
+  0xa4,0x00,0x17,0xc7,0x46,0x22,0x12,0x11,0x11,0x11,0x11,0x11,0x21,0x43,0x42,0xad,
+  0xa4,0x63,0xac,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0xbb,0xbb,0x88,0x88,0x88,0x00,
+  0x00,0x00,0x00,0x00,0x04,0x50,0x29,0xb6,0x5a,0x75,0xd6,0x9e,0x79,0xb7,0x5b,0x7d,
+  0xc6,0xdc,0x71,0xd7,0x5e,0x7a,0x07,0x5c,0x71,0xd6,0x9d,0x71,0xc5,0xd9,0x65,0xa5,
+  0x9b,0x61,0xa7,0x1a,0x55,0x45,0x97,0x61,0x86,0x5b,0x5d,0xd6,0xdb,0x6d,0xc7,0x1e,
+  0x79,0xd5,0x54,0x61,0x86,0x5c,0x55,0x55,0x58,0x69,0xa7,0x9d,0x7e,0x08,0x1f,0x8e,
+  0x08,0xa0,0x86,0x07,0xe1,0x89,0xf8,0xe3,0x79,0xf8,0x1f,0x86,0x18,0xde,0x86,0x18,
+  0x60,0x74,0x22,0x14,0x61,0x16,0xcb,0x4d,0xb9,0x4a,0xd6,0xd6,0x6b,0x8a,0xe2,0x35,
+  0xc4,0x9d,0x0b,0x18,0x28,0x33,0x60,0x71,0x4d,0xa7,0x18,0x0c,0xa9,0xc2,0x9e,0xc6,
+  0x42,0xf8,0x11,0xa5,0x70,0x1b,0xa8,0x5b,0x7a,0x5d,0x3e,0xf3,0x26,0xa7,0x67,0x33,
+  0x40,0x74,0x11,0xae,0xce,0x6c,0xfa,0x6b,0x63,0x6d,0x58,0x3c,0xab,0xd5,0x32,0x93,
+  0x2b,0xb8,0x45,0x2a,0x9c,0x59,0x8d,0x0a,0xa6,0xdc,0x32,0x11,0x81,0x37,0xbb,0x68,
+  0xb7,0x0f,0x37,0xa2,0xb3,0x26,0xb6,0x33,0x08,0x62,0x29,0xad,0xaf,0x34,0x6d,0xb4,
+  0x5e,0x9e,0x44,0xa5,0x25,0x52,0x19,0xf9,0xa8,0x13,0x21,0xb6,0x31,0xe6,0xa0,0xc3,
+  0x54,0xfc,0xb2,0xd4,0x9c,0xa4,0x54,0x9b,0x9b,0x6e,0xb6,0x32,0xa2,0x3e,0x0c,0x7b,
+  0x5a,0x52,0x8e,0xb0,0xb4,0x01,0x44,0x5c,0xd0,0xb2,0x4f,0x37,0x65,0xb2,0xd6,0x96,
+  0xeb,0x3c,0xb6,0xdb,0x78,0x65,0x8e,0x64,0xc4,0xe8,0xd2,0x70,0x23,0x4d,0x4a,0x24,
+  0x94,0xe2,0xc3,0x5a,0x66,0xca,0xe4,0x1a,0x26,0xc8,0xac,0x41,0x2b,0x3b,0x6e,0x2d,
+  0xdd,0x03,0x73,0x9e,0x6b,0xaa,0xac,0x78,0xce,0x36,0x74,0xd7,0x68,0x34,0xad,0x54,
+  0x21,0x0a,0x76,0xb1,0x96,0x9d,0x4d,0x05,0x11,0x4f,0x1d,0xe1,0x55,0x40,0x2f,0xd8,
+  0x5f,0xa4,0xaa,0x03,0xd1,0x00,0x91,0x6d,0xbe,0x72,0xa6,0xae,0x0b,0x43,0xd4,0x65,
+  0xae,0xad,0x69,0xd2,0x9b,0x5a,0x55,0xc6,0xbe,0x1c,0x95,0x2e,0x46,0xc9,0x64,0xc5,
+  0x28,0x2c,0x56,0x86,0x47,0x53,0x14,0x89,0x33,0x35,0xca,0xc5,0xcc,0xbd,0x24,0xb3,
+  0x4a,0x58,0xaa,0x29,0x64,0x62,0x23,0x77,0x5a,0xb9,0x83,0x9d,0x8b,0xdb,0x9f,0x6e,
+  0x72,0xc8,0xd9,0xda,0x8e,0x75,0x5e,0xf6,0xc1,0xae,0x1b,0xb9,0xaa,0xe9,0xb7,0x97,
+  0x0c,0x9d,0xc5,0x81,0x27,0x86,0x01,0x55,0xc0,0xca,0x3b,0x31,0xa5,0x58,0x42,0x1b,
+  0x15,0x41,0xac,0xa1,0xa7,0x15,0x97,0x22,0x9b,0x32,0xb0,0x35,0x11,0xd6,0x5d,0x0d,
+  0x65,0x39,0x10,0x84,0xdc,0x1f,0x76,0xee,0xdc,0x60,0x90,0x04,0x40,0x77,0xba,0xa1,
+  0x1d,0x28,0xd4,0xad,0x69,0x2b,0x40,0x17,0x31,0x4b,0x9d,0x1a,0x24,0x11,0x1b,0x60,
+  0xa4,0x21,0x68,0x26,0x4a,0x3b,0xf2,0x81,0x30,0xb4,0xb9,0xc4,0x84,0x25,0x86,0x37,
+  0x24,0x48,0x2f,0x48,0x93,0x70,0xd2,0x5b,0xca,0x84,0xe8,0xd6,0x56,0x8e,0x54,0x83,
+  0xc4,0x7c,0xfe,0x29,0x2b,0xb3,0x4c,0xa5,0x1b,0x60,0x72,0x4f,0x4d,0x56,0x47,0x6e,
+  0xc8,0x36,0x9f,0x4b,0xa5,0x23,0xad,0x72,0x56,0xa2,0x2d,0x25,0xa2,0x51,0x46,0x13,
+  0x9d,0xb2,0xd6,0x55,0x86,0x31,0x5e,0x39,0x76,0xd6,0x2f,0x16,0x33,0x69,0x90,0x8e,
+  0x69,0x02,0x23,0x91,0x63,0x8f,0x13,0x1b,0x11,0xec,0xae,0x6d,0x46,0xf6,0x5b,0x1e,
+  0xdc,0x6f,0x0d,0x51,0x2d,0x68,0x36,0x2d,0x21,0x2e,0xd0,0x00,0x00,0x00,0xff,0xfc,
+  0xa4,0x00,0x61,0x12,0x47,0x22,0x12,0x12,0x11,0x11,0x11,0x22,0x23,0x42,0x31,0x8d,
+  0x92,0x5a,0x88,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0xa5,0xab,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x04,0x4a,0x71,0xa7,0x19,0x6d,0x97,0x9a,0x72,0x07,0xdb,0x71,
+  0xf7,0x1c,0x7d,0xd6,0xde,0x76,0x08,0x1a,0x7a,0x07,0xdd,0x69,0xa6,0xdc,0x69,0xf7,
+  0x18,0x6d,0xc5,0xd7,0x65,0x86,0x9b,0x6d,0x96,0x5a,0x71,0xc7,0x57,0x55,0x76,0x9a,
+  0x65,0xa6,0x19,0x6d,0xc7,0x1b,0x79,0xa7,0x5f,0x71,0x76,0x18,0x65,0xa7,0x19,0x5d,
+  0x86,0xdd,0x6d,0xf8,0x60,0x86,0x18,0x62,0x91,0xe8,0xa2,0x86,0x18,0xde,0x8a,0x38,
+  0x61,0x91,0xa8,0xa5,0x76,0x28,0xdd,0x92,0x67,0xd0,0x74,0xbe,0xbe,0x3e,0xa6,0xee,
+  0xad,0x52,0x2a,0xb5,0x22,0xe6,0xd5,0xf1,0x15,0x23,0x12,0x18,0x2e,0x4a,0x6a,0x5b,
+  0x49,0xc5,0x20,0x56,0xab,0x05,0xcd,0xdd,0x26,0x86,0xaa,0x8a,0x1a,0xa5,0x5d,0xcf,
+  0x93,0xe3,0xc3,0x69,0x8a,0x46,0xd2,0x96,0x2e,0x1c,0x84,0x6d,0x2d,0xa5,0xec,0x95,
+  0x18,0xcc,0x95,0xd6,0xc6,0x45,0x37,0x1a,0x6b,0x5b,0x0b,0x43,0x27,0x52,0x63,0x1f,
+  0x64,0x92,0x64,0x12,0xe9,0x38,0xb3,0x2b,0x28,0xb8,0xa4,0x0d,0xb1,0x28,0xe3,0xa1,
+  0x26,0x29,0x29,0xa5,0x39,0x04,0x14,0x4c,0x9c,0x64,0x3d,0x02,0x8e,0x26,0x17,0x36,
+  0xb8,0x90,0xa2,0xd2,0x95,0x8c,0xd3,0x94,0x44,0x86,0x2c,0x3a,0x61,0xe1,0x6d,0x82,
+  0x41,0x17,0x15,0x9e,0x59,0x79,0xb3,0x72,0x99,0xca,0x88,0xdb,0x8d,0x5a,0xac,0x93,
+  0x56,0x43,0x00,0x2b,0x5d,0x0c,0x41,0x52,0xbd,0xad,0x5a,0xd0,0x05,0xae,0x73,0x4a,
+  0x60,0x8d,0x88,0x86,0x54,0xad,0x2d,0x49,0x10,0x61,0x43,0x08,0x69,0x11,0x22,0x3d,
+  0x10,0xb5,0x94,0x9e,0xcd,0x5e,0x8d,0x5a,0xa8,0xd9,0x8e,0x48,0x69,0x91,0x01,0x06,
+  0x2d,0x65,0x40,0xd4,0xb4,0x44,0x28,0xb0,0xf1,0x13,0x49,0x36,0xd4,0x53,0x5c,0xb7,
+  0x57,0x6b,0x9a,0xfd,0x35,0x72,0x9a,0x8e,0x6a,0xa8,0xa4,0xd1,0x48,0xc9,0x39,0x27,
+  0x50,0x1c,0xda,0x53,0xd5,0xac,0xc8,0xe6,0x6a,0x6b,0x93,0x18,0x91,0x16,0x1b,0xc7,
+  0x52,0x6c,0x8d,0x9d,0x30,0x8a,0x52,0x0e,0x0f,0x92,0xb0,0x6a,0xd8,0x57,0x23,0x70,
+  0x5b,0x23,0xb1,0xb6,0xae,0x72,0x15,0xe9,0x23,0x68,0x3d,0xcc,0x6b,0x54,0xd5,0x96,
+  0x31,0x8a,0x22,0x7a,0xd9,0x55,0x9a,0x47,0x49,0xcf,0x46,0x35,0x9e,0xa6,0x68,0xd7,
+  0x6b,0x24,0x47,0x85,0x52,0xa6,0xd4,0xdd,0x0a,0x0b,0x1e,0xea,0x57,0x02,0xeb,0x6b,
+  0x9c,0x46,0xa0,0x87,0x63,0xe5,0x6a,0xb3,0x3a,0x3e,0x26,0xcb,0x89,0x51,0x2a,0x9b,
+  0x94,0xe6,0x30,0xd8,0xae,0xd3,0x6a,0x3c,0x52,0x31,0xad,0x54,0x98,0x56,0xb2,0xb0,
+  0x0a,0x98,0x29,0x94,0x1c,0xe5,0xb5,0x6c,0x1a,0xd6,0xb6,0x73,0xc4,0x14,0x6c,0xb9,
+  0x91,0x98,0xd6,0xda,0xa7,0xb8,0xa8,0x2b,0x83,0x35,0xad,0x88,0xea,0x4c,0x95,0xc0,
+  0x77,0xf5,0xe4,0xe2,0x43,0x5c,0xa5,0xf0,0xba,0x56,0x21,0x1a,0xcb,0x35,0xad,0x6b,
+  0x71,0xac,0xcf,0xd5,0x55,0xbc,0x9e,0x36,0x42,0xcb,0x23,0x48,0xa5,0x44,0xb8,0xf1,
+  0xbe,0x8e,0x23,0x50,0xd1,0x04,0x4b,0x63,0x0d,0x36,0xb5,0xa1,0xba,0xa4,0xd1,0x4c,
+  0xb4,0x8c,0x6b,0x5a,0xd6,0xb7,0x17,0xc5,0x55,0x9d,0xac,0x90,0x9a,0x44,0xc8,0xd5,
+  0x65,0xd4,0xbe,0x70,0xf9,0x37,0x78,0xf2,0x0e,0x06,0x24,0xea,0xba,0x6a,0x83,0x52,
+  0x94,0x92,0x4a,0x6d,0xa0,0xbe,0xb1,0x82,0xa2,0xb8,0x42,0x00,0x00,0x00,0xff,0xfc,
+  0xa4,0x00,0x63,0x29,0x45,0x23,0x22,0x34,0x33,0x33,0x23,0x23,0x33,0x23,0x23,0x6d,
+  0x22,0x51,0x45,0x22,0x49,0x24,0x90,0x00,0x00,0x00,0xaa,0x7e,0xbb,0x6a,0xaa,0xea,
+  0xaa,0xeb,0xaa,0xae,0x23,0x4a,0x4d,0x25,0x18,0x49,0x44,0x95,0x4d,0x44,0x95,0x55,
+  0x25,0xd5,0x69,0x55,0x55,0x59,0x45,0x93,0x61,0x55,0x94,0x55,0x45,0x99,0x61,0xb5,
+  0x57,0x5d,0x76,0x56,0x5d,0x75,0xd9,0x61,0xa5,0xd9,0x69,0x66,0xd7,0x73,0xeb,0x6d,
+  0xfa,0x55,0x25,0xa0,0xb6,0xdb,0x61,0xab,0x54,0x06,0x99,0x49,0xb6,0x9a,0x36,0x65,
+  0x25,0x6c,0x8d,0xd2,0x6d,0xb8,0x69,0x44,0x5b,0x70,0xdf,0x39,0xa2,0x5a,0xa9,0xb5,
+  0xeb,0xdd,0xa4,0x23,0x2a,0x72,0x63,0x32,0xc7,0x4e,0xdc,0x56,0xb2,0xb4,0x46,0x39,
+  0x29,0xaa,0x9d,0xc1,0x8c,0xdd,0xd9,0x32,0x73,0x26,0x70,0x35,0x24,0x3a,0x83,0x36,
+  0x9d,0x89,0xc7,0xaf,0x9c,0xd9,0x56,0xcf,0x33,0xe2,0xdb,0x77,0x38,0xed,0x53,0x4e,
+  0x41,0x71,0xde,0x0a,0x15,0x83,0x4c,0x09,0x59,0x86,0xea,0x99,0xb7,0x35,0x7a,0x43,
+  0x68,0xb9,0x32,0x16,0xe4,0xc8,0xd9,0x5c,0x1d,0xa5,0x24,0x53,0xfa,0xed,0x65,0x96,
+  0xba,0x2b,0xea,0x2e,0x2a,0x96,0xb7,0x22,0x70,0xa6,0x62,0x8e,0x28,0x1a,0xaa,0xd0,
+  0x9b,0x6e,0x24,0x43,0x08,0x9b,0x8d,0xc7,0x27,0xb3,0x61,0xbb,0x8c,0x4e,0x29,0x89,
+  0x24,0xf9,0x39,0x95,0xac,0x58,0xc5,0x39,0xfb,0x52,0xad,0x42,0xe6,0xba,0x0f,0x6a,
+  0xda,0xec,0x30,0xd6,0x67,0xb0,0x71,0x99,0xed,0xd2,0xc2,0xa2,0x6c,0xe2,0x75,0x02,
+  0xcb,0x48,0x1d,0x37,0x1d,0x82,0x26,0xd5,0x87,0x1d,0xb2,0xe9,0x8d,0xd7,0x73,0xc8,
+  0x48,0x99,0xd3,0x1b,0x12,0x24,0x20,0xe7,0x46,0x2c,0x69,0x92,0x7e,0xf7,0xb5,0xd8,
+  0xb3,0x29,0x36,0x6c,0xcc,0xcc,0x65,0xb7,0x0c,0x2f,0x14,0x72,0x28,0x4e,0x12,0xe2,
+  };
+
 bool cDvbDevice::SetPlayMode(ePlayMode PlayMode)
 {
   if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) {
@@ -783,6 +988,18 @@
 
   switch (PlayMode) {
     case pmNone:
+         // special handling to return from digital out
+         // (play one sample mpeg audio frame)
+         if(playMode!=pmNone && audioTrack==0xBD) {
+           CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
+           CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
+           CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+           int fd_out = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
+           write(fd_out,testAudio,sizeof(testAudio));
+           usleep(300000);
+           delayedUnmute=0; audioTrack=0; GoAnalog(false);
+           }
+
          // special handling to return from PCM replay:
          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
@@ -798,12 +1015,14 @@
          CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
          if (siProcessor)
             siProcessor->SetStatus(true);
+         SetupPES();
          break;
     case pmAudioVideo:
          if (playMode == pmNone)
             TurnOffLiveMode();
          // continue with next...
     case pmAudioOnlyBlack:
+         SetupPES();
          if (siProcessor)
             siProcessor->SetStatus(false);
          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
@@ -814,6 +1033,7 @@
          CHECK(ioctl(fd_video, VIDEO_PLAY));
          break;
     case pmAudioOnly:
+         SetupPES();
          if (siProcessor)
             siProcessor->SetStatus(false);
          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
@@ -840,6 +1060,7 @@
 {
   if (fd_video >= 0)
      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
+  UseRuleset(1);
 }
 
 void cDvbDevice::Clear(void)
@@ -848,6 +1069,7 @@
      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
   if (fd_audio >= 0)
      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+  ClearPES();
   cDevice::Clear();
 }
 
@@ -863,6 +1085,7 @@
      if (fd_video >= 0)
         CHECK(ioctl(fd_video, VIDEO_CONTINUE));
      }
+  UseRuleset(0);
   cDevice::Play();
 }
 
@@ -908,35 +1131,18 @@
 */
 #define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
 #ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
-  if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
+  if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) {
      // PES data
-     char *buf = MALLOC(char, Length);
-     if (!buf)
-        return;
-     int i = 0;
-     int blen = 0;
-     while (i < Length - 4) {
-           if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01 && (Data[i + 3] & 0xF0) == 0xE0) {
-              // skip PES header
-              int offs = i + 6;
-              int len = Data[i + 4] * 256 + Data[i + 5];
-              // skip header extension
-              if ((Data[i + 6] & 0xC0) == 0x80) {
-                 offs += 3;
-                 offs += Data[i + 8];
-                 len -= 3;
-                 len -= Data[i + 8];
-                 }
-              memcpy(&buf[blen], &Data[offs], len);
-              i = offs + len;
-              blen += len;
-              }
-           else
-              i++;
-           }
-     video_still_picture sp = { buf, blen };
-     CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
-     free(buf);
+     if((stillBuff=MALLOC(unsigned char,Length))) {
+       stillLen=0; stillBuffLen=Length;
+       int set=CurrentRuleset();
+       UseRuleset(2); Process(Data,Length); UseRuleset(set);
+       if(stillLen) {
+         video_still_picture sp = { (char *)stillBuff, stillLen };
+         CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
+         }
+       free(stillBuff);
+       }
      }
   else {
      // non-PES data
@@ -945,10 +1151,19 @@
      }
 #else
 #define MIN_IFRAME 400000
+  int set=CurrentRuleset();
+  UseRuleset(1);
+  fd_out = fd_video;
   for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
-      safe_write(fd_video, Data, Length);
+      for (int u=0 ; u<Length ;) {
+        int w=Process(Data+u, Length-u);
+        if(w<0) { i=MIN_IFRAME; break; } // error
+        if(w==0) usleep(1);
+        u+=w;
+        }
       usleep(1); // allows the buffer to be displayed in case the progress display is active
       }
+  UseRuleset(set);
 #endif
 }
 
@@ -958,18 +1173,117 @@
   return Poller.Poll(TimeoutMs);
 }
 
-int cDvbDevice::PlayVideo(const uchar *Data, int Length)
+void cDvbDevice::ClearPES(void)
 {
-  int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
-  if (fd >= 0)
-     return write(fd, Data, Length);
-  return -1;
+  cPES::Clear();
+  cMultichannelAudio::Clear();
+}
+
+void cDvbDevice::SetupPES(void)
+{
+  Statistics();
+  cPES::Reset();                // default rules is prSkip
+  audioTrack=0xC0; trackSelected=false; skipMode=true; delayedUnmute=0;
+  // we use ruleset 0 for normal replay
+  SetRuleR(0xE0,0xEF,prPass,0); // pass all video streams
+  SetRule (0xC0,     prPass,0); // pass first audio stream
+  SetRule (0xBD,     prAct1,0); // snoop for digital audio
+  SetRule (0xBE,     prPass,0); // pass padding stream
+  // we use ruleset 1 for trickmodes
+  SetRuleR(0xE0,0xEF,prPass,1); // pass all video streams
+  SetRule (0xBE,     prPass,1); // pass padding stream
+  // we use ruleset 2 for still picture
+  SetRuleR(0xE0,0xEF,prAct2,2); // pass all video streams
+  // use ruleset 0 for now
+  UseRuleset(0);
+  cMultichannelAudio::Reset();
+}
+
+void cDvbDevice::GoDigital(void)
+{
+  SetRuleR(0xC0,0xCF,prSkip,0); // mute all tracks
+  SetRule (0xBD,prSkip,0);
+  SetRule(audioTrack,prAct1,0); // enable digital audio
+  if(fd_audio>=0) {
+    CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+    SetVolumeDevice(0);
+    }
+}
+
+void cDvbDevice::GoAnalog(bool fromDigi)
+{
+  SetRuleR(0xC0,0xCF,prSkip,0); // mute all tracks
+  SetRule (0xBD,prSkip,0);
+  SetRule(audioTrack,prPass,0); // enable normal audio
+  if(fromDigi) delayedUnmute=time_ms()+800;
+  else if(!delayedUnmute && !IsMute()) SetVolume(0,false);
+}
+
+int cDvbDevice::Action2(uchar type, uchar *data, int len)
+{
+  if(stillLen+len<=stillBuffLen) {
+    memcpy(stillBuff+stillLen,data,len);
+    stillLen+=len;
+    }
+  return -len;
+}
+
+int cDvbDevice::Action1(uchar type, uchar *data, int len)
+{
+  int off=0;
+  if(SOP) {
+    int n=Check(data,len,header);
+    switch(n) {
+      case 0: // encapsulate and send to cAudios
+        if(Setup.PlayMultichannelAudio) {
+          if(!trackSelected) {
+             // auto-select digital audio
+             audioTrack=0xBD; trackSelected=true;
+             GoDigital();
+             }
+          skipMode=false;
+          break;
+          }
+        if(audioTrack==0xBD) {
+          // auto-unselect digital audio
+          audioTrack=0xC0; trackSelected=false;
+          GoAnalog(true);
+          cMultichannelAudio::Reset();
+          }
+        // fall through
+      case 1: // skip but send to cAudios
+        skipMode=true;
+        break;
+      case 2: // pass
+        Redirect(prPass);
+        return 0;
+      }
+    off=Offset();                  // get offset to skip extra DVD header
+    if(off) ModifyPaketSize(-off); // adjust paket size in the header
+    cDevice::PlayAudio(header,headerSize);
+    }
+
+  if(!skipMode) Encapsulate(data, len);
+  if(len>=off) cDevice::PlayAudio(data+off,len-off);
+  return -len;
 }
 
-void cDvbDevice::PlayAudio(const uchar *Data, int Length)
+int cDvbDevice::Output(const uchar *data, int len)
 {
-  //XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
-  cDevice::PlayAudio(Data, Length);
+  int w = write(fd_out, data, len);
+  if (w < 0 && errno == EAGAIN) w = 0;
+  return w;
+}
+
+int cDvbDevice::PlayVideo(const uchar *Data, int Length)
+{
+  if(delayedUnmute && time_ms()>delayedUnmute) {
+    if(!IsMute()) SetVolume(0,false);
+    delayedUnmute=0;
+    }
+  fd_out = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
+  if (fd_out >= 0) return Process(Data, Length);
+  return -1;
 }
 
 bool cDvbDevice::OpenDvr(void)
diff -uN vdr-1.2.5-orig/dvbdevice.h vdr-1.2.5-ac3/dvbdevice.h
--- vdr-1.2.5-orig/dvbdevice.h	2003-08-15 14:34:55.000000000 +0200
+++ vdr-1.2.5-ac3/dvbdevice.h	2003-10-15 19:39:00.000000000 +0200
@@ -15,6 +15,8 @@
 #include "device.h"
 #include "dvbspu.h"
 #include "eit.h"
+#include "multichannelaudio.h"
+#include "pes.h"
 
 #if DVB_API_VERSION != 3
 #error VDR requires Linux DVB driver API version 3!
@@ -26,7 +28,7 @@
 
 /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
 
-class cDvbDevice : public cDevice {
+class cDvbDevice : public cDevice, public cPES, public cMultichannelAudio {
   friend class cDvbOsd;
 private:
   static bool Probe(const char *FileName);
@@ -91,6 +93,7 @@
   virtual int NumAudioTracksDevice(void) const;
   virtual const char **GetAudioTracksDevice(int *CurrentTrack = NULL) const;
   virtual void SetAudioTrackDevice(int Index);
+  virtual bool HasDigitalAudioOut(void) const { return true; }
 
 // EIT facilities
 
@@ -99,6 +102,18 @@
 
 // Player facilities
 
+private:
+  int fd_out;
+  uchar audioTrack, *stillBuff;
+  bool trackSelected, skipMode;
+  int delayedUnmute, stillLen, stillBuffLen;
+  void ClearPES(void);
+  void SetupPES(void);
+  void GoDigital(void);
+  void GoAnalog(bool fromDigi);
+  virtual int Output(const uchar *data, int len);
+  virtual int Action1(uchar type, uchar *data, int len);
+  virtual int Action2(uchar type, uchar *data, int len);
 protected:
   ePlayMode playMode;
   virtual bool CanReplay(void) const;
@@ -112,7 +127,9 @@
   virtual void StillPicture(const uchar *Data, int Length);
   virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
   virtual int PlayVideo(const uchar *Data, int Length);
-  virtual void PlayAudio(const uchar *Data, int Length);
+  virtual int ReplayNumAudioTracks(void) const;
+  virtual const char **ReplayGetAudioTracks(int *CurrentTrack) const;
+  virtual bool ReplaySetAudioTrack(int Index);
 
 // Receiver facilities
 
diff -uN vdr-1.2.5-orig/dvbplayer.c vdr-1.2.5-ac3/dvbplayer.c
--- vdr-1.2.5-orig/dvbplayer.c	2003-05-24 11:04:26.000000000 +0200
+++ vdr-1.2.5-ac3/dvbplayer.c	2003-10-13 21:36:58.000000000 +0200
@@ -178,7 +178,7 @@
 // The number of frames to back up when resuming an interrupted replay session:
 #define RESUMEBACKUP (10 * FRAMESPERSEC)
 
-class cDvbPlayer : public cPlayer, cThread {
+class cDvbPlayer : public cPlayerAudio, cThread {
 private:
   enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
   enum ePlayDirs { pdForward, pdBackward };
@@ -197,13 +197,10 @@
   ePlayDirs playDir;
   int trickSpeed;
   int readIndex, writeIndex;
-  bool canToggleAudioTrack;
-  uchar audioTrack;
   cFrame *readFrame;
   cFrame *playFrame;
   void TrickSpeed(int Increment);
   void Empty(void);
-  void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
   bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
   int Resume(void);
   bool Save(void);
@@ -223,9 +220,6 @@
   void Goto(int Position, bool Still = false);
   virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
   virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
-  virtual int NumAudioTracks(void) const;
-  virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
-  virtual void SetAudioTrack(int Index);
   };
 
 #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@@ -247,8 +241,6 @@
   playMode = pmPlay;
   playDir = pdForward;
   trickSpeed = NORMAL_SPEED;
-  canToggleAudioTrack = false;
-  audioTrack = 0xC0;
   readIndex = writeIndex = -1;
   readFrame = NULL;
   playFrame = NULL;
@@ -314,41 +306,6 @@
   firstPacket = true;
 }
 
-void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
-  if (index) {
-     for (int i = 0; i < Length - 6; i++) {
-         if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
-            uchar c = b[i + 3];
-            int l = b[i + 4] * 256 + b[i + 5] + 6;
-            switch (c) {
-              case 0xBD: // dolby
-                   if (Except)
-                      PlayAudio(&b[i], l);
-                   // continue with deleting the data - otherwise it disturbs DVB replay
-              case 0xC0 ... 0xC1: // audio
-                   if (c == 0xC1)
-                      canToggleAudioTrack = true;
-                   if (!Except || c != Except)
-                      memset(&b[i], 0x00, min(l, Length-i));
-                   break;
-              case 0xE0 ... 0xEF: // video
-                   break;
-              default:
-                   //esyslog("ERROR: unexpected packet id %02X", c);
-                   l = 0;
-              }
-            if (l)
-               i += l - 1; // the loop increments, too!
-            }
-         /*XXX
-         else
-            esyslog("ERROR: broken packet header");
-            XXX*/
-         }
-     }
-}
-
 bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
 {
   if (FileNumber > 0)
@@ -417,7 +374,6 @@
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
-  int AudioTrack = 0; // -1 = any, 0 = none, >0 = audioTrack
 
   running = true;
   while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available())) {
@@ -449,9 +405,6 @@
                           continue;
                           }
                        readIndex = Index;
-                       AudioTrack = 0;
-                       // must clear all audio packets because the buffer is not emptied
-                       // when falling back from "fast forward" to "play" (see above)
                        }
                     else if (index) {
                        uchar FileNumber;
@@ -462,11 +415,9 @@
                           eof = true;
                           continue;
                           }
-                       AudioTrack = audioTrack;
                        }
                     else { // allows replay even if the index file is missing
                        Length = MAXFRAMESIZE;
-                       AudioTrack = -1;
                        }
                     if (Length == -1)
                        Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
@@ -478,8 +429,6 @@
                     }
                  int r = nonBlockingFileReader->Read(replayFile, b, Length);
                  if (r > 0) {
-                    if (AudioTrack == 0)
-                       StripAudioPackets(b, r);
                     readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
                     b = NULL;
                     }
@@ -520,8 +469,6 @@
                        cRemux::SetBrokenLink(p, pc);
                        firstPacket = false;
                        }
-                    if (AudioTrack > 0)
-                       StripAudioPackets(p, pc, AudioTrack);
                     }
                  }
               if (p) {
@@ -719,7 +666,7 @@
         if (r > 0) {
            if (playMode == pmPause)
               DevicePlay();
-           StripAudioPackets(b, r);
+           //XXX+ StripAudioPackets(b, r);
            DeviceStillPicture(b, r);
            }
         playMode = pmStill;
@@ -759,31 +706,6 @@
   return true;
 }
 
-int cDvbPlayer::NumAudioTracks(void) const
-{
-  return canToggleAudioTrack ? 2 : 1;
-}
-
-const char **cDvbPlayer::GetAudioTracks(int *CurrentTrack) const
-{
-  if (NumAudioTracks()) {
-     if (CurrentTrack)
-        *CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
-     static const char *audioTracks1[] = { "Audio 1", NULL };
-     static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
-     return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
-     }
-  return NULL;
-}
-
-void cDvbPlayer::SetAudioTrack(int Index)
-{
-  if ((audioTrack == 0xC0) != (Index == 0)) {
-     audioTrack = (Index == 1) ? 0xC1 : 0xC0;
-     Empty();
-     }
-}
-
 // --- cDvbPlayerControl -----------------------------------------------------
 
 cDvbPlayerControl::cDvbPlayerControl(const char *FileName)
diff -uN vdr-1.2.5-orig/i18n.c vdr-1.2.5-ac3/i18n.c
--- vdr-1.2.5-orig/i18n.c	2003-09-11 15:51:56.000000000 +0200
+++ vdr-1.2.5-ac3/i18n.c	2003-10-13 21:36:58.000000000 +0200
@@ -2238,6 +2238,38 @@
     "Video formátum",
     "Format del vídeo",
   },
+  { "Setup.DVB$Multichannel Audio (DD/DTS)",
+    "Mehrkanalton (DD/DTS)",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+  },
+  { "Setup.DVB$Force transfermode (LiveAC3)",
+    "Transfermode erzwingen (LiveAC3)",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+  },
   { "Setup.LNB$SLOF (MHz)",
     "SLOF (MHz)",
     "Frekvenca preklopa (MHz)",
diff -uN vdr-1.2.5-orig/menu.c vdr-1.2.5-ac3/menu.c
--- vdr-1.2.5-orig/menu.c	2003-09-14 12:49:28.000000000 +0200
+++ vdr-1.2.5-ac3/menu.c	2003-10-13 21:36:58.000000000 +0200
@@ -2047,6 +2047,8 @@
   SetSection(tr("DVB"));
   Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
   Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"),          &data.VideoFormat, "4:3", "16:9"));
+  Add(new cMenuEditBoolItem(tr("Setup.DVB$Multichannel Audio (DD/DTS)"), &data.PlayMultichannelAudio));
+  Add(new cMenuEditBoolItem(tr("Setup.DVB$Force transfermode (LiveAC3)"), &data.LiveMultichannelAudio));
 }
 
 eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
diff -uN vdr-1.2.5-orig/multichannelaudio.c vdr-1.2.5-ac3/multichannelaudio.c
--- vdr-1.2.5-orig/multichannelaudio.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/multichannelaudio.c	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,616 @@
+/*
+ * multichannelaudio.c:
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#include <malloc.h>
+#include "multichannelaudio.h"
+#include "ringbuffer.h"
+
+#define DEBUG(x...) printf(x)
+//#define DEBUG(x...)
+
+//#define ED(x...) printf(x)
+#define ED(x...)
+
+#define aAC3  0x80
+#define aDTS  0x88
+#define aLPCM 0xA0
+#define aMPEG 0xC0
+
+#define aVDR  0x0B // VDR specific audio substream
+
+#define PES_HDR_SIZE   6     // length of PES header
+#define PTS_SIZE       5     // length of PTS data
+#define MAX_FRAMECOUNT 1536  // max. LPCM payload size
+
+#define SYNC_SIZE      7     // how many bytes we need to sync on a audio header
+
+#define AC3_SIZE       6144  // size of AC3 IEC paket
+#define DTS_SIZE       2048  // size of DTS IEC paket
+#define IEC_HDR_SIZE   8     // size of IEC header
+
+// --- cAudioEncapsulator ------------------------------------------------------
+
+class cAudioEncapsulator {
+private:
+  int totalSize, frameCount;
+  cFrame *frame;
+  uchar *frameData;
+  //
+  uchar syncBuff[SYNC_SIZE];
+  int have, length, skipped;
+  //
+  uchar ptsFlags;
+  const uchar *ptsData;
+  int ptsDelay;
+  //
+  void NewFrame(uchar PTSflags, const uchar *PTSdata);
+  void SyncFound(const uchar *data);
+protected:
+  int streamType;
+  cRingBufferFrame *ringBuffer;
+  int fillup, firstBurst;
+  bool mute;
+  //
+  void StartFrame(int size, uchar PTSflags, const uchar *PTSdata);
+  void FinishFrame(void);
+  void PutData(const uchar *data, int len);
+  void SendIECpause(int type);
+  //
+  virtual int SyncInfo(const uchar *data)=0;
+  virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, const uchar *PTSdata)=0;
+  virtual void FinishIECFrame(void);
+public:
+  cAudioEncapsulator(cRingBufferFrame *rb, int StreamType);
+  virtual ~cAudioEncapsulator();
+  void Clear(void);
+  void Decode(const uchar *data, int len, uchar PTSflags, int PTSdelay, const uchar *PTSdata);
+  int StreamType() { return streamType; }
+  };
+
+cAudioEncapsulator::cAudioEncapsulator(cRingBufferFrame *rb, int StreamType)
+{
+  ringBuffer = rb;
+  streamType = StreamType;
+  frame = 0; firstBurst = 1;
+  Clear();
+}
+
+cAudioEncapsulator::~cAudioEncapsulator()
+{
+  delete frame;
+}
+
+void cAudioEncapsulator::Clear(void)
+{
+  delete frame;
+  frame = 0; frameCount = 0; fillup = 0; mute = false;
+  have = length = skipped = 0;
+}
+
+void cAudioEncapsulator::StartFrame(int size, uchar PTSflags, const uchar *PTSdata)
+{
+  if(frame) {
+    DEBUG("StartFrame() with unfinished frame!\n");
+    FinishFrame();
+    }
+  ED("StartFrame: size=%d ptsFlags=%d\n",size,PTSflags);
+  totalSize=size;
+  NewFrame(PTSflags,PTSdata);
+}
+
+void cAudioEncapsulator::NewFrame(uchar PTSflags, const uchar *PTSdata)
+{
+  if(!totalSize) {
+    DEBUG("NewFrame: new frame requested, but totalSize=0\n");
+    return;
+    }
+  static const int ptslen[] = { 0,0,PTS_SIZE,PTS_SIZE*2 };
+  const int plen = ptslen[PTSflags];
+  int len = min(totalSize, MAX_FRAMECOUNT);
+  ED("NewFrame: totalSize=%d frameCount=%d PTSflags=%d",totalSize,len,PTSflags);
+  totalSize -= len;
+  ED(" new totalSize=%d\n",totalSize);
+  len += (plen + 3 + 7);
+  frameCount = len+PES_HDR_SIZE;
+  frameData = MALLOC(uchar,frameCount);
+  if (frameData) {
+    frame = new cFrame(frameData, -frameCount, ftUnknown);
+    if (frame) {
+      uchar buf[10];
+      // build the PES header
+      buf[0] = 0x00;
+      buf[1] = 0x00;
+      buf[2] = 0x01;
+      buf[3] = 0xBD; // PRIVATE_STREAM1
+      buf[4] = (len >> 8) & 0xFF;
+      buf[5] = len & 0xFF;
+      buf[6] = 0x80;
+      buf[7] = plen ? (PTSflags << 6) : 0;
+      buf[8] = plen;
+      PutData(buf,9);
+
+      if (plen) PutData(PTSdata,plen);
+
+      // build LPCM header
+      buf[0] = aLPCM; // substream ID
+      buf[1] = 0xFF;
+      buf[2] = 0x00;
+      buf[3] = 0x00;
+      buf[4] = 0x00;
+      buf[5] = 0x00;
+      buf[6] = 0x81;
+      PutData(buf,7);
+      return;
+      }
+    else { free(frameData); frameData=0; }
+    }
+  esyslog("Failed to build frame for audio encapsulation");
+}
+
+void cAudioEncapsulator::FinishFrame(void)
+{
+  if (frameCount) {
+    DEBUG("FinishFrame() with frameCount>0\n");
+    PutData(0,frameCount);
+    }
+  if (frame && frameData) {
+    ED("FinishFrame: totalSize=%d\n",totalSize);
+    if (!ringBuffer->Put(frame)) {
+      esyslog("Ringbuffer overflow. Encapsulated audio frame lost");
+      delete frame;
+      }
+    }
+  frame=0; frameData=0; frameCount=0;
+}
+
+void cAudioEncapsulator::PutData(const uchar *data, int len)
+{
+  if(!mute) {
+    if(!frameData) DEBUG("PutData() without frame\n");
+    while (frameData && len > 0) {
+      int l = min(len,frameCount);
+      if(data) {
+        memcpy(frameData,data,l);
+        data += l;
+        }
+      else memset(frameData,0,l);
+      frameData += l; len -= l; frameCount -= l;
+
+      ED("PutData: %s=%d len=%d frameCount=%d\n",data?"put":"zero",l,len,frameCount);
+      if (!frameCount) {
+        FinishFrame();
+        if (totalSize > 0) NewFrame(0,0);
+        }
+      }
+    }
+}
+
+void cAudioEncapsulator::SendIECpause(int type)
+{
+  StartFrame(AC3_SIZE,0,0);
+  uchar burst[IEC_HDR_SIZE];
+  // prepare IEC 60958 data frame
+  burst[0] = 0xF8;
+  burst[1] = 0x72;
+  burst[2] = 0x4E;
+  burst[3] = 0x1F;
+
+  switch (type) {
+    default:
+    case 0:
+      burst[4] = 7 << 5;                      // null frame, stream = 7
+      burst[5] = 0x00;
+      burst[6] = 0x00;                        // No data therein
+      burst[7] = 0x00;
+      break;
+    case 1:
+      burst[4] = 0x00;                        // Audio ES Channel empty, wait
+      burst[5] = 0x03;                        //   for DD Decoder or pause
+      burst[6] = 0x00;                        // Trailing frame size is 32 bits payload
+      burst[7] = 0x20;                        
+      break;
+    case -1:
+      burst[4] = 0x01;                        // User stop, skip or error
+      burst[5] = 0x03;
+      burst[6] = 0x08;                        // Trailing frame size is zero
+      burst[7] = 0x00;
+      break;
+    }
+  PutData(burst,sizeof(burst));
+  PutData(0,AC3_SIZE-sizeof(burst));
+  FinishFrame();
+  mute = true;
+}
+
+void cAudioEncapsulator::FinishIECFrame(void)
+{
+  if(!mute) {
+    ED("FinishIECFrame: fillup=%d\n",fillup);
+    if (fillup) PutData(0,fillup);
+    FinishFrame();
+    }
+  mute=false; fillup=0;
+}
+
+void cAudioEncapsulator::SyncFound(const uchar *data)
+{
+  if(skipped) {
+    DEBUG("Decode: skipped %d bytes\n",skipped);
+    ED("skipped: ");   for(int k=-skipped ; k<0 ; k++) ED("%02x ",data[k]);
+    ED("\ndata: "); for(int k=0 ; k<24 ; k++) ED("%02x ",data[k]);
+    ED("\n");
+    skipped=0;
+    }
+  uchar pf=0;
+  ED("Decode: sync found ptsFlags=%d ptsDelay=%d\n",ptsFlags,ptsDelay);
+  if(ptsFlags && ptsDelay<=1) {
+    pf=ptsFlags; ptsFlags=0;
+    }
+  StartIECFrame(data,length,pf,ptsData);
+  PutData(data,SYNC_SIZE);
+  have = SYNC_SIZE;
+}
+
+void cAudioEncapsulator::Decode(const uchar *data, int len, uchar PTSflags, int PTSdelay, const uchar *PTSdata)
+{
+  ED("Decode: enter length=%d have=%d len=%d PTSflags=%d PTSdelay=%d\n",length,have,len,PTSflags,PTSdelay);
+  if(PTSflags) {
+    // if we are close to the end of an audio frame, but are already receiving
+    // the start of the next frame, assume a corrupted stream and finish the
+    // incomplete frame.
+    if(length && length-have<20 && !PTSdelay && SyncInfo(data)) {
+      int miss=length-have;
+      DEBUG("Decode: incomplete frame (stream corrupt?). syncing to next. miss=%d\n",miss);
+      PutData(0,miss);
+      FinishIECFrame();
+      length=have=0;
+      }
+    // we only send PTS info if we're nearly at frame start, except
+    // if we're signaled to delay the PTS
+    if(length && have>40) {
+      if(PTSdelay) ED("Decode: PTS delayed\n");
+      else {
+        DEBUG("Decode: PTS info dropped length=%d have=%d\n",length,have);
+        PTSflags=0;
+        }
+      }
+    ptsFlags=PTSflags; ptsData=PTSdata; ptsDelay=PTSdelay;
+    ED("Decode: saved PTS flags=%d delay=%d\n",ptsFlags,ptsDelay);
+    }
+
+#if 0
+  {
+  printf("Decode: len=%d\n",len);
+  for(int i=0 ; i<len ; ) {
+    printf("%04x:",i);
+    for(int j=0 ; j<16 && i<len ; j++) {
+      printf(" %02x",data[i++]);
+      }
+    printf("\n");
+    }
+  }
+#endif
+
+  int used=0;
+  while (used < len) {
+    if (!length) { // we are still searching for a header sync
+      if (!have) { // buffer is empty, work without buffering
+        if (used+SYNC_SIZE < len) {
+          length=SyncInfo(&data[used]);
+          if (length) {
+            ED("Decode: sync found at offset %d (len=%d)\n",used,length);
+            SyncFound(&data[used]);
+            used += SYNC_SIZE; ptsDelay -= SYNC_SIZE;
+            continue;
+            }
+          else { used++; skipped++; }
+          }
+        else { // not enough data to try a sync, buffer the rest
+          ED("Decode: buffering started\n");
+          have = len-used;
+          memcpy(syncBuff,&data[used],have);
+          used += have; ptsDelay -= have;
+          }
+        }
+      else { // unfortunaly buffer is not empty, so continue with buffering until sync found
+        int need=min(SYNC_SIZE-have,len-used);
+        if (need) {
+          memcpy(&syncBuff[have],&data[used],need);
+          have += need; used += need; ptsDelay -= need;
+          }
+        if (have==SYNC_SIZE) {
+          length=SyncInfo(syncBuff);
+          if (length) {
+            ED("Decode: (buffered) sync found at offset %d (len=%d)\n",used-7,length);
+            SyncFound(syncBuff);
+            continue;
+            }
+          else {
+            memmove(syncBuff,syncBuff+1,SYNC_SIZE-1);
+            have--; skipped++;
+            }
+          }
+        }
+      }
+    else { // we have a header sync and are copying data
+      int need = min(length-have,len-used);
+      if(need) {
+        ED("Decode: writing %d\n",need);
+        PutData(&data[used],need);
+        have += need; used += need; ptsDelay -= need;
+        if (have == length) {
+          FinishIECFrame();
+          length = have = 0;
+          continue;
+          }
+        }
+      }
+    }
+  ED("Decode: leave length=%d have=%d len=%d used=%d\n",length,have,len,used);
+}
+
+// --- cAudioEncapsulatorAC3 ---------------------------------------------------
+
+class cAudioEncapsulatorAC3 : public cAudioEncapsulator {
+private:
+  virtual int SyncInfo(const uchar *buf);
+  virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, const uchar *PTSdata);
+public:
+  cAudioEncapsulatorAC3(cRingBufferFrame *rb, int StreamType);
+  };
+
+cAudioEncapsulatorAC3::cAudioEncapsulatorAC3(cRingBufferFrame *rb, int StreamType)
+:cAudioEncapsulator(rb, StreamType)
+{}
+
+int cAudioEncapsulatorAC3::SyncInfo(const uchar *buf)
+{
+  static const int rate[] = { 32,  40,  48,  56,  64,  80,  96, 112,
+                             128, 160, 192, 224, 256, 320, 384, 448,
+                             512, 576, 640};
+
+  if ((buf[0] != 0x0B) || (buf[1] != 0x77))   /* syncword */
+     return 0;
+  if (buf[5] >= 0x60)         /* bsid >= 12 */
+     return 0;
+
+  int frmsizecod = buf[4] & 63;
+  if (frmsizecod >= 38)
+     return 0;
+  int bitrate = rate[frmsizecod >> 1];
+
+  switch (buf[4] & 0xC0) {
+  case 0:
+     return 4 * bitrate;
+  case 0x40:
+     return 2 * (320 * bitrate / 147 + (frmsizecod & 1));
+  case 0x80:
+     return 6 * bitrate;
+  default:
+     return 0;
+     }
+}
+
+void cAudioEncapsulatorAC3::StartIECFrame(const uchar *buf, int length, uchar PTSflags, const uchar *PTSdata)
+{
+  if (firstBurst) {
+     SendIECpause(1);
+     if (++firstBurst > 10) firstBurst = 0;
+     }
+  else {
+     StartFrame(AC3_SIZE,PTSflags,PTSdata);
+     fillup = AC3_SIZE-IEC_HDR_SIZE-length;
+
+     // prepare IEC 60958 data frame
+     uchar burst[IEC_HDR_SIZE];
+     burst[0] = 0xF8;
+     burst[1] = 0x72;
+     burst[2] = 0x4E;
+     burst[3] = 0x1F;
+     burst[4] = (buf[5] & 0x07);                          // Pc1
+     burst[5] = 0x01;                                     // Pc2 AC-3
+     burst[6] = ((length * 8) >> 8 ) & 0xFF;              // Pd1
+     burst[7] = (length * 8) & 0xFF;                      // Pd2
+     PutData(burst,sizeof(burst));
+     }
+}
+
+// --- cAudioEncapsulatorDTS ---------------------------------------------------
+
+class cAudioEncapsulatorDTS : public cAudioEncapsulator {
+private:
+  virtual int SyncInfo(const uchar *buf);
+  virtual void StartIECFrame(const uchar *buf, int length, uchar PTSflags, const uchar *PTSdata);
+public:
+  cAudioEncapsulatorDTS(cRingBufferFrame *rb, int StreamType);
+  };
+
+cAudioEncapsulatorDTS::cAudioEncapsulatorDTS(cRingBufferFrame *rb, int StreamType)
+: cAudioEncapsulator(rb, StreamType)
+{}
+
+int cAudioEncapsulatorDTS::SyncInfo(const uchar *buf)
+{
+  if ((buf[0] != 0x7F) || 
+      (buf[1] != 0xfE) ||
+      (buf[2] != 0x80) ||
+      (buf[3] != 0x01))    return 0;
+
+  int length = ((buf[5] & 0x03) << 12) |
+               ((buf[6] & 0xFF) << 4) |
+               ((buf[7] & 0xF0) >> 4);
+
+  return length + 1;
+}
+
+void cAudioEncapsulatorDTS::StartIECFrame(const uchar *buf, int length, uchar PTSflags, const uchar *PTSdata)
+{
+  if (firstBurst) {
+     SendIECpause(1);
+     if (++firstBurst > 10) firstBurst = 0;
+     }
+  else {
+     uchar ac5_type = ((buf[4] & 0x01) <<  6) | ((buf[5] >>2) & 0x3F);
+     uchar ac5_spdif_type;
+     switch(ac5_type) {
+     case 0x0F:
+        ac5_spdif_type = 0x0B;  /* DTS          */
+        break;
+     case 0x1F:
+        ac5_spdif_type = 0x0C;  /* DTS          */
+        break;
+     case 0x3F:
+        ac5_spdif_type = 0x0D;  /* DTS          */
+        break;
+     default:
+        ac5_spdif_type = 0x00;  /* DTS          */
+        esyslog("DTS: SPDIF type not detected: ac5 type = %X!\n", ac5_type);
+        break;
+        }
+
+     if (length > DTS_SIZE-IEC_HDR_SIZE) {
+        DEBUG("DTS: length too long %d\n",length);
+        return;
+        }
+    
+     StartFrame(DTS_SIZE,PTSflags,PTSdata);
+     fillup = DTS_SIZE-IEC_HDR_SIZE-length;
+
+     // prepare IEC 60958 data frame
+     uchar burst[IEC_HDR_SIZE];
+     burst[0] = 0xF8;
+     burst[1] = 0x72;
+     burst[2] = 0x4E;
+     burst[3] = 0x1F;
+     burst[4] = 0x00;
+     burst[5] = ac5_spdif_type;                    /* DTS data         */
+     burst[6] = ((length * 8) >> 8 ) & 0xFF;       /* ac5_length * 8   */
+     burst[7] = (length * 8) & 0xFF;
+     PutData(burst,sizeof(burst));
+     }
+}
+
+// --- cMultichannelAudio ------------------------------------------------------
+
+cMultichannelAudio::cMultichannelAudio(cRingBufferFrame *rb)
+{
+  encapsulator=0; ringBuffer=rb;
+  if(!ringBuffer) DEBUG("multichannel: no ringbuffer!");
+  lpcmSeq=0;
+}
+
+cMultichannelAudio::~cMultichannelAudio()
+{
+  delete encapsulator;
+}
+
+void cMultichannelAudio::Clear()
+{
+  Lock();
+  if(encapsulator) encapsulator->Clear();
+  lpcmSeq=0;
+  Unlock();
+}
+
+void cMultichannelAudio::Reset()
+{
+  Lock();
+  delete encapsulator; encapsulator=0;
+  lpcmSeq=0;
+  Unlock();
+}
+
+int cMultichannelAudio::Check(uchar *b, int length, uchar *header)
+{
+  Lock();
+  int res=0;
+  ptsDelay=0; offset=0; ptsData=0;
+
+  // get PTS information
+  ptsFlags=header[7]>>6;
+  if(ptsFlags) ptsData=&header[9];
+
+  // AC3 frames may span over multiple PES packets. Unfortunaly the continuation
+  // packets starts with the aLPCM code sometimes. Some magic here to detect
+  // this case.
+  uchar subStreamType=b[0];
+  if(subStreamType==aLPCM) lpcmSeq++; else lpcmSeq=0;
+  if(!(header[6]&4) && !ptsFlags) {
+    if(subStreamType!=aLPCM || lpcmSeq<3) {
+      if(encapsulator) subStreamType=encapsulator->StreamType();
+      else {
+        ED("multichannel: boundary crossing -> skip\n");
+        res=1; goto out; // skip
+        }
+      }
+    else ED("multichannel: boundary crossing -> LPCM seq=%d\n",lpcmSeq);
+    }
+
+  switch (subStreamType) {
+    case aDTS:
+    case aAC3:
+      offset=4; // skip the DVD stream infos
+      // fall through
+    case aVDR:
+      // If the SubStreamType has changed then select the right encapsulator
+      if(!encapsulator || encapsulator->StreamType()!=subStreamType) {
+        DEBUG("multichannel: new encapsulator %x\n",subStreamType);
+        Reset();
+        switch(subStreamType) {
+          case aAC3:
+          case aVDR: // AC3
+             encapsulator=new cAudioEncapsulatorAC3(ringBuffer,subStreamType);
+             break;
+          case aDTS: // Dts
+             encapsulator=new cAudioEncapsulatorDTS(ringBuffer,subStreamType);
+             break;
+          }
+        }
+      if(encapsulator) break;
+      DEBUG("multichannel: no encapsulator\n");
+      res=1; goto out; // skip
+    case aLPCM:
+      if(encapsulator) {
+        Reset();
+        DEBUG("multichannel: interrupted encapsulator stream (LPCM)\n");
+        }
+      ED("multichannel: LPCM\n");
+      res=2; goto out; // pass
+    default:
+      if(encapsulator) {
+        Reset();
+        DEBUG("multichannel: interrupted encapsulator stream (unknown)\n");
+        }
+      DEBUG("multichannel: unknown substream type %x (skipped)\n",subStreamType);
+      res=1; goto out; // skip
+    }
+
+out:
+  ED("HEADER type=%x sub=%x ptsflags=%d length=%d\n",header[3],subStreamType,ptsFlags,length);
+  ED("head: ");   for(int k=0 ; k<24 ; k++) ED("%02x ",header[k]);
+  ED("\ndata: "); for(int k=0 ; k<24 ; k++) ED("%02x ",b[k]);
+  ED("\n");
+
+  Unlock(); return res;
+}
+
+void cMultichannelAudio::Encapsulate(uchar *b, int length)
+{
+  Lock();
+  if(offset && ptsFlags) { // get start of the packet to which the PTS belong (DVD only)
+    if(offset>=2 && length>offset-2) ptsDelay|=b[offset-2]*256;
+    if(offset>=1 && length>offset-1) ptsDelay|=b[offset-1];
+    }
+  if(length>=offset) {
+    if(encapsulator)
+      encapsulator->Decode(b+offset,length-offset,ptsFlags,ptsDelay,ptsData);
+    ptsFlags=0; ptsDelay=0; offset=0; ptsData=0;
+    }
+  else offset-=length;
+  Unlock();
+}
diff -uN vdr-1.2.5-orig/multichannelaudio.h vdr-1.2.5-ac3/multichannelaudio.h
--- vdr-1.2.5-orig/multichannelaudio.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/multichannelaudio.h	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,36 @@
+/*
+ * multichannelaudio.h:
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#ifndef __MULTICHANNELAUDIO_H
+#define __MULTICHANNELAUDIO_H
+
+#include "thread.h"
+#include "tools.h"
+
+class cAudioEncapsulator;
+class cRingBufferFrame;
+
+class cMultichannelAudio : public cMutex {
+private:
+  cAudioEncapsulator *encapsulator;
+  cRingBufferFrame *ringBuffer;
+  unsigned int lpcmSeq;
+  int ptsFlags, ptsDelay, offset;
+  uchar *ptsData;
+public:
+  cMultichannelAudio(cRingBufferFrame *rb);
+  virtual ~cMultichannelAudio();
+  int Check(uchar *b, int length, uchar *header);
+  int Offset(void) { return offset; }
+  void Encapsulate(uchar *b, int length);
+  void Clear();
+  void Reset();
+};
+
+#endif // __MULTICHANNELAUDIO_H
+
diff -uN vdr-1.2.5-orig/pes.c vdr-1.2.5-ac3/pes.c
--- vdr-1.2.5-orig/pes.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/pes.c	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,482 @@
+/*
+ * pes.c:
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "pes.h"
+#include "ringbuffer.h"
+
+#define DEBUG(x...) printf(x)
+//#define DEBUG(x...)
+
+//#define PD(x...) printf(x)
+#define PD(x...)
+
+// --- cPES --------------------------------------------------------------------
+
+cPES::cPES(eRule ru)
+{
+  rb=new cRingBufferFrame(KILOBYTE(50));
+  defaultRule=ru;
+  Reset();
+}
+
+cPES::~cPES()
+{
+  delete rb;
+}
+
+void cPES::Reset(void)
+{
+  for(int i=0 ; i<NUM_RULESETS ; i++) SetDefaultRule(defaultRule,i);
+  UseRuleset(0);
+  ClearSeen();
+  Clear();
+}
+
+void cPES::Clear(void)
+{
+  Lock();
+  mode=pmNewSync; frame=0; mpegType=2;
+  rb->Clear();
+  Unlock();
+}
+
+bool cPES::ValidRuleset(const int num)
+{
+  if(num>=0 && num<NUM_RULESETS) return true;
+  DEBUG("PES: illegal ruleset %d\n",num);
+  return false;
+}
+
+void cPES::UseRuleset(const int num)
+{
+  if(ValidRuleset(num)) {
+    currRules=rules[num];
+    currNum=num;
+    }
+}
+
+int cPES::CurrentRuleset(void)
+{
+  return currNum;
+}
+
+void cPES::SetDefaultRule(eRule ru, const int num)
+{
+  if(ValidRuleset(num)) 
+    for(int i=0 ; i<NUM_RULES ; i++) rules[num][i]=ru;
+}
+
+void cPES::SetRule(uchar type, eRule ru, const int num)
+{
+  if(ValidRuleset(num)) rules[num][type]=ru;
+}
+
+void cPES::SetRuleR(uchar ltype, uchar htype, eRule ru, const int num)
+{
+  if(ValidRuleset(num)) {
+    if(ltype<htype) for( ; ltype<=htype ; ltype++) rules[num][ltype]=ru;
+    else DEBUG("PES: bad range %x-%x\n",ltype,htype);
+    }
+}
+
+unsigned int cPES::Seen(uchar type) const
+{
+  return seen[type];
+}
+
+void cPES::ClearSeen(void)
+{
+  memset(seen,0,sizeof(seen));
+  totalBytes=totalSkipped=totalZeros=0;
+  totalSlowSyncs=totalSavedHeaders=0;
+  skipped=zeros=0;
+}
+
+void cPES::Skip(uchar *data, int count)
+{
+  if(data) {
+    Skipped(data,count);
+    while(count>0) {
+      skipped++;
+      if(!*data++) zeros++;
+      count--;
+      }
+    }
+  else if(skipped) {
+   totalSkipped+=skipped;
+   if(skipped==zeros) totalZeros+=zeros;
+   else DEBUG("PES: skipped %d bytes\n",skipped);
+   skipped=zeros=0;
+   }
+}
+
+void cPES::Statistics(void)
+{
+  if(totalBytes) {
+    DEBUG("PES: Stats %lld bytes total, %lld skipped, %lld zero-gaps. %ld slow syncs, %ld saved headers\n",
+          totalBytes,totalSkipped,totalZeros,totalSlowSyncs,totalSavedHeaders);
+    for(int type=0 ; type<=0xFF ; type++)
+     if(seen[type])
+       DEBUG("PES: Stats %02X: %d packets\n",type,seen[type]);
+    }
+}
+
+void cPES::ModifyPaketSize(int mod)
+{
+  if(SOP) {
+    int size=header[4]*256+header[5]+mod;
+    header[4]=(size>>8)&0xFF;
+    header[5]=(size   )&0xFF;
+    }
+  else DEBUG("PES: modify paket size called in middle of packet\n");
+}
+
+void cPES::Redirect(eRule ru)
+{
+  if(SOP) {
+    currRule=ru;
+    redirect=true;
+    }
+  else DEBUG("PES: redirect called in middle of packet\n");
+}
+
+int cPES::HeaderSize(uchar *head, int len)
+{
+  if(len<PES_MIN_SIZE) return -PES_MIN_SIZE;
+  switch(head[3]) {
+    default:
+    // Program end
+    case 0xB9:
+    // Programm stream map
+    case 0xBC:
+    // video stream start codes
+    case 0x00 ... 0xB8:
+    // reserved
+    case 0xF0 ... 0xFF:
+      return PES_MIN_SIZE;
+
+    // Pack header
+    case 0xBA:
+      if(len<5) return -5;
+      switch(head[4]&0xC0) {
+        default:
+          DEBUG("PES: unknown mpegType in pack header (0x%02x)\n",head[4]);
+          // fall through
+        case 0x00:
+          mpegType=1;
+          return 12;
+        case 0x40:
+          mpegType=2;
+          if(len<14) return -14;
+          return 14+(head[13]&0x07); // add stuffing bytes
+        }
+
+    // System header
+    case 0xBB:
+      if(len<6) return -6;
+      return 6+head[4]*256+head[5]; //XXX+ is there a difference between mpeg1 & mpeg2??
+
+    // Padding stream
+    case 0xBE:
+    // Private stream2 (navigation data)
+    case 0xBF:
+      return 6; //XXX+ is there a difference between mpeg1 & mpeg2??
+
+    // Private stream1
+    case 0xBD:
+    // all the rest (the real packets)
+    case 0xC0 ... 0xCF:
+    case 0xD0 ... 0xDF:
+    case 0xE0 ... 0xEF:
+      if(len<7) return -7;
+      int index=6;
+      while((head[index]&0xC0)==0xC0) { // skip stuffing bytes
+        index++; if(index>=len) return -(index+1);
+        }
+      if((head[index]&0xC0)==0x80) { // mpeg2
+        mpegType=2;
+        index+=2; if(index>=len) return -(index+1);
+        return index+1+head[index]; // mpeg2 header data bytes
+        }
+      mpegType=1;
+      if((head[index]&0xC0)==0x40) { // mpeg1 buff size
+        index+=2; if(index>=len) return -(index+1);
+        }
+      switch(head[index]&0x30) {
+        case 0x30: index+=9; break; // mpeg1 pts&dts
+        case 0x20: index+=4; break; // mpeg1 pts
+        case 0x10: DEBUG("PES: bad pts/dts flags in MPEG1 header (0x%02x)\n",head[index]); break;
+        }
+      return index+1;
+    }
+}
+
+int cPES::PacketSize(uchar *head, int len)
+{
+  switch(head[3]) {
+    default:
+    // video stream start codes
+    case 0x00 ... 0xB8:
+    // Program end
+    case 0xB9:
+    // Pack header
+    case 0xBA:
+    // System header
+    case 0xBB:
+    // Programm stream map
+    case 0xBC:
+    // reserved
+    case 0xF0 ... 0xFF:
+      return len; // packet size = header size
+
+    // Private stream1
+    case 0xBD:
+    // Padding stream
+    case 0xBE:
+    // Private stream2 (navigation data)
+    case 0xBF:
+    // all the rest (the real packets)
+    case 0xC0 ... 0xCF:
+    case 0xD0 ... 0xDF:
+    case 0xE0 ... 0xEF:
+      return 6+head[4]*256+head[5];
+    }
+}
+
+int cPES::Return(int used, int len)
+{
+  PD("PES: return used=%d len=%d mode=%d\n",used,len,mode);
+  if(SOP && unsavedHeader && used>=len) {
+    // if we are about to finish the current data packet and we have
+    // an unsaved header inside, we must save the header to the buffer
+    memcpy(hbuff,header,headerSize);
+    header=hbuff; unsavedHeader=false; totalSavedHeaders++;
+    PD("PES: header saved\n");
+    }
+  if(used>len) {
+    DEBUG("PES: BUG! used %d > len %d\n",used,len);
+    used=len;
+    }
+  if(used>0) totalBytes+=used;
+  Unlock(); // release lock from Process()
+  return used;
+}
+
+int cPES::Process(const uchar *data, int len)
+{
+  Lock(); // lock is released in Return()
+  PD("PES: enter data=%p len=%d mode=%d have=%d need=%d old=%d\n",
+      data,len,mode,have,need,old);
+  int used=0;
+  while(used<len) {
+    uchar *c=(uchar *)data+used;
+    int rest=len-used, n;
+    switch(mode) {
+      case pmNewSync:
+        PD("PES: new sync from %d, rest %d\n",used,rest);
+        have=old=need=0;
+        unsavedHeader=false; outputHeader=true; SOP=true; redirect=false;
+        mode=pmFastSync;
+        // fall through
+
+      case pmFastSync:
+        // a short cut for the most common case
+        // if matched here, header isn't copied around
+        if(rest>=PES_MIN_SIZE) {
+          PD("PES: fastsync try used=%d: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                used,c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8]);
+          if(c[2]==0x01 && c[1]==0x00 && c[0]==0x00) {
+            headerSize=HeaderSize(c,rest);
+            if(headerSize>0 && rest>=headerSize) {
+              // found a packet start :-)
+              PD("PES: fastsync hit used=%d headerSize=%d rest=%d\n",used,headerSize,rest);
+              header=c; unsavedHeader=true;
+              used+=headerSize; mode=pmHeaderOk; continue;
+              }
+            }
+          else if(c[2]!=0x00) { used+=3; Skip(c,3); continue; }
+          else { used++; Skip(c); continue; }
+          }
+        // copy remaining bytes to buffer
+        memcpy(hbuff,c,rest);
+        have=old=rest; used+=rest;
+        mode=pmSync;
+        PD("PES: buffering started old=%d\n",old);
+        break;
+
+      case pmSync:
+        PD("PES: slowsync have=%d old=%d\n",have,old);
+        if(have<PES_MIN_SIZE && rest>=1) { hbuff[have++]=c[0]; used++; continue; }
+        if(have>=PES_MIN_SIZE) {
+          PD("PES: slowsync try used=%d: %02x %02x %02x %02x\n",
+                used,hbuff[0],hbuff[1],hbuff[2],hbuff[3]);
+          if(hbuff[0]==0x00 && hbuff[1]==0x00&& hbuff[2]==0x01) {
+            need=abs(HeaderSize(hbuff,have));
+            mode=pmGetHeader; continue;
+            }
+          // no sync found, move buffer one position ahead
+          have--; Skip(hbuff);
+          memmove(hbuff,hbuff+1,have);
+          // if all bytes from previous data block used up, switch to FastSync
+          if(!--old) {
+            used=0; mode=pmFastSync;
+            PD("PES: buffering ended\n");
+            }
+          continue;
+          }
+        break;
+
+      case pmGetHeader:
+        if(have<need) {
+          n=min(need-have,rest);
+          memcpy(hbuff+have,c,n);
+          have+=n; used+=n;
+          PD("PES: get header n=%d need=%d have=%d used=%d\n",n,need,have,used);
+          continue;
+          }
+        if(have>=need) {
+          need=abs(HeaderSize(hbuff,have));
+          if(have<need) continue;
+          // header data complete
+          PD("PES: slowsync hit used=%d\n",used);
+          totalSlowSyncs++;
+          if(have>need) DEBUG("PES: bug, buffered too much. have=%d need=%d\n",have,need);
+          if(have>(int)sizeof(hbuff)) DEBUG("PES: bug, header buffer overflow. have=%d size=%d\n",have,sizeof(hbuff));
+          headerSize=need;
+          header=hbuff;
+          mode=pmHeaderOk;
+          }
+        break;
+
+      case pmHeaderOk:
+        type=header[3]; seen[type]++; Skip(0);
+        if(type<=0xB8) {
+          // packet types 0x00-0xb8 are video stream start codes
+          DEBUG("PES: invalid packet type 0x%02x, skipping\n",type);
+          mode=pmNewSync;
+          break;
+          }
+        payloadSize=PacketSize(header,headerSize)-headerSize;
+        if(payloadSize<0) {
+          DEBUG("PES: invalid payloadsize %d, skipping\n",payloadSize);
+          mode=pmNewSync;
+          break;
+          }
+        PD("PES: found sync at offset %d, type %02x, length %d, next expected %d\n",
+              used-headerSize,type,headerSize+payloadSize,used+payloadSize);
+        PD("PES: header type=%02x mpeg=%d header=%d payload=%d:",
+              type,mpegType,headerSize,payloadSize);
+        for(int i=0 ; i<headerSize ; i++) PD(" %02x",header[i]);
+        PD("\n");
+        currRule=currRules[type];
+        have=need=0;
+        mode=pmPayload;
+        // fall through
+
+      case pmPayload:
+        n=min(payloadSize-have,rest);
+        if(!n) {
+          if(payloadSize==0) n=1;
+          else break;
+          }
+        PD("PES: payload have=%d n=%d SOP=%d\n",have,n,SOP);
+        switch(currRule) {
+          default:
+            DEBUG("PES: bug, unknown rule %d, assuming pass\n",currRule);
+            // fall through
+          case prPass: n=n; break;
+          case prSkip: n=-n; break;
+          case prAct1: n=Action1(type,c,n); break;
+          case prAct2: n=Action2(type,c,n); break;
+          case prAct3: n=Action3(type,c,n); break;
+          case prAct4: n=Action4(type,c,n); break;
+          case prAct5: n=Action5(type,c,n); break;
+          case prAct6: n=Action6(type,c,n); break;
+          case prAct7: n=Action7(type,c,n); break;
+          case prAct8: n=Action8(type,c,n); break;
+          }
+        if(n==0) {
+          if(redirect) { redirect=false; continue; }
+          return Return(used,len);
+          }
+        need=n; SOP=false;
+        mode=pmRingGet;
+        // fall through
+
+      case pmRingGet:
+        frame=rb->Get();
+        if(frame) {
+          outCount=frame->Count();
+          outData=(uchar *)frame->Data();
+          PD("PES: ringbuffer got frame %p count=%d\n",frame,outCount);
+          nextMode=pmRingDrop; mode=pmOutput; break;
+          }
+        mode=pmDataPut;
+        // fall through
+
+      case pmDataPut:
+        if(need<0) {
+          need=-need; outputHeader=false;
+          mode=pmDataReady; continue;
+          }
+        if(outputHeader) {
+          outData=header; outCount=headerSize; outputHeader=false;
+          nextMode=pmDataPut;
+          }
+        else if(payloadSize) {
+          outData=c; outCount=need;
+          nextMode=pmDataReady;
+          }
+        else {
+          mode=pmDataReady;
+          continue;
+          }
+        mode=pmOutput;
+        // fall through
+
+      case pmOutput:
+        for(;;) {
+          PD("PES: output data=%p count=%d -> ",outData,outCount);
+          n=Output(outData,outCount);
+          PD("n=%d\n",n);
+          if(n<0) return Return(-1,len);
+          if(n==0) return Return(used,len);
+
+          outCount-=n; outData+=n;
+          if(outCount<=0) { mode=nextMode; break; }
+          }
+        break;
+
+      case pmDataReady:
+        if(payloadSize) { used+=need; have+=need; }
+        PD("PES: data ready need=%d have=%d paySize=%d used=%d\n",
+            need,have,payloadSize,used);
+        if(have>=payloadSize) {
+          PD("PES: packet finished\n");
+          if(have>payloadSize) DEBUG("PES: payload exceeded, size=%d have=%d\n",payloadSize,have);
+          mode=pmNewSync;
+          }
+        else mode=pmPayload;
+        break;
+
+      case pmRingDrop:
+        PD("PES: ringbuffer drop %p\n",frame);
+        rb->Drop(frame); frame=0;
+        mode=pmRingGet;
+        break;
+
+      default:
+        DEBUG("PES: bug, bad mode %d\n",mode);
+        return Return(-1,len);
+      }
+    }
+  PD("PES: leave\n");
+  return Return(used,len);
+}
diff -uN vdr-1.2.5-orig/pes.h vdr-1.2.5-ac3/pes.h
--- vdr-1.2.5-orig/pes.h	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/pes.h	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,94 @@
+/*
+ * pes.h:
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#ifndef __PES_H
+#define __PES_H
+
+#include "thread.h"
+#include "tools.h"
+
+#define PES_MIN_SIZE 4    // min. number of bytes to identify a packet
+#define PES_HDR_SIZE 6    // length of PES header
+#define PES_EXT_SIZE 3    // length of PES extension
+
+#define NUM_RULESETS 4    // number of rule sets
+#define NUM_RULES    256  // how many rules in every set
+
+class cFrame;
+class cRingBufferFrame;
+
+class cPES : public cMutex {
+protected:
+  enum eRule { prPass, prSkip, prAct1, prAct2, prAct3, prAct4, prAct5, prAct6, prAct7, prAct8 };
+private:
+  eRule rules[NUM_RULESETS][NUM_RULES], *currRules, currRule, defaultRule;
+  int currNum;
+  // Statistics
+  unsigned int seen[256];
+  int skipped, zeros;
+  long long totalBytes, totalSkipped, totalZeros;
+  long totalSlowSyncs, totalSavedHeaders;
+  //
+  enum eMode { pmNewSync, pmFastSync, pmSync, pmGetHeader, pmHeaderOk, pmPayload,
+               pmRingGet, pmRingDrop, pmDataPut, pmDataReady, pmOutput };
+  eMode mode, nextMode;
+  uchar hbuff[PES_HDR_SIZE+PES_EXT_SIZE+256];
+  uchar type;
+  int have, need, old;
+  bool unsavedHeader, outputHeader, redirect;
+  //
+  cFrame *frame;
+  const uchar *outData;
+  int outCount;
+  //
+  bool ValidRuleset(const int num);
+  void Skip(uchar *data, int count=1);
+  int Return(int used, int len);
+  int HeaderSize(uchar *head, int len);
+  int PacketSize(uchar *head, int len);
+protected:
+  bool SOP;        // true if we process the start of packet
+  int headerSize;  // size of the header including additional header data
+  uchar *header;   // the actual header
+  int mpegType;    // gives type of packet 1=mpeg1 2=mpeg2
+  int payloadSize; // number of data bytes in the packet
+  //
+  cRingBufferFrame *rb;
+  //
+  // Rule Management
+  void UseRuleset(int num);
+  int CurrentRuleset(void);
+  void SetDefaultRule(eRule ru, const int num=0);
+  void SetRule(uchar type, eRule ru, const int num=0);
+  void SetRuleR(uchar ltype, uchar htype, eRule ru, const int num=0);
+  void Reset(void);
+  // Misc
+  unsigned int Seen(uchar type) const;
+  void ClearSeen(void);
+  void Statistics(void);
+  void ModifyPaketSize(int mod);
+  // Data Processing
+  int Process(const uchar *data, int len);
+  void Redirect(eRule ru);
+  void Clear(void);
+  virtual int Output(const uchar *data, int len) { return len; }
+  virtual int Action1(uchar type, uchar *data, int len) { return len; }
+  virtual int Action2(uchar type, uchar *data, int len) { return len; }
+  virtual int Action3(uchar type, uchar *data, int len) { return len; }
+  virtual int Action4(uchar type, uchar *data, int len) { return len; }
+  virtual int Action5(uchar type, uchar *data, int len) { return len; }
+  virtual int Action6(uchar type, uchar *data, int len) { return len; }
+  virtual int Action7(uchar type, uchar *data, int len) { return len; }
+  virtual int Action8(uchar type, uchar *data, int len) { return len; }
+  virtual void Skipped(uchar *data, int len) {}
+public:
+  cPES(eRule ru=prPass);
+  virtual ~cPES();
+  };
+
+#endif
diff -uN vdr-1.2.5-orig/player.c vdr-1.2.5-ac3/player.c
--- vdr-1.2.5-orig/player.c	2002-11-02 15:55:37.000000000 +0100
+++ vdr-1.2.5-ac3/player.c	2003-10-13 21:36:58.000000000 +0200
@@ -10,6 +10,23 @@
 #include "player.h"
 #include "i18n.h"
 
+// --- cDvbPlayerAudio ------------------------------------------------------
+
+int cPlayerAudio::NumAudioTracks(void) const
+{
+  return DeviceReplayNumAudioTracks();
+}
+
+const char **cPlayerAudio::GetAudioTracks(int *CurrentTrack) const
+{
+  return DeviceReplayGetAudioTracks(CurrentTrack);
+}
+
+void cPlayerAudio::SetAudioTrack(int Index)
+{
+  DeviceReplaySetAudioTrack(Index);
+}
+
 // --- cPlayer ---------------------------------------------------------------
 
 cPlayer::cPlayer(ePlayMode PlayMode)
diff -uN vdr-1.2.5-orig/player.h vdr-1.2.5-ac3/player.h
--- vdr-1.2.5-orig/player.h	2002-11-03 12:27:30.000000000 +0100
+++ vdr-1.2.5-ac3/player.h	2003-10-13 21:36:58.000000000 +0200
@@ -13,8 +13,13 @@
 #include "device.h"
 #include "osd.h"
 
+#define HAVE_AC3_OVER_DVB 000201 // make the patch detectable
+
+class cPlayerAudio;
+
 class cPlayer {
   friend class cDevice;
+  friend class cPlayerAudio;
 private:
   cDevice *device;
   ePlayMode playMode;
@@ -73,6 +78,18 @@
        // (otherwise nothing will happen).
   };
 
+class cPlayerAudio : public cPlayer {
+protected:
+  bool DeviceHasDigitalAudioOut(void) const { return device && device->HasDigitalAudioOut(); }
+  int DeviceReplayNumAudioTracks(void) const { return device ? device->ReplayNumAudioTracks() : 0; };
+  const char **DeviceReplayGetAudioTracks(int *CurrentTrack) const { return device ? device->ReplayGetAudioTracks(CurrentTrack) : NULL; }
+  bool DeviceReplaySetAudioTrack(int Index) { return device ? device->ReplaySetAudioTrack(Index) : false; }
+public:
+  virtual int NumAudioTracks(void) const;
+  virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
+  virtual void SetAudioTrack(int Index);
+  };
+
 class cControl : public cOsdObject {
 private:
   static cControl *control;
diff -uN vdr-1.2.5-orig/test_cMuCha.c vdr-1.2.5-ac3/test_cMuCha.c
--- vdr-1.2.5-orig/test_cMuCha.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/test_cMuCha.c	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,106 @@
+//
+// g++ test_cMuCha.c pes.o multichannelaudio.o thread.o tools.o ringbuffer.o -lpthread -o test_cMuCha
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "multichannelaudio.h"
+#include "pes.h"
+
+extern const char *I18nTranslate(const char *s, const char *Plugin)
+{
+  return s;
+}
+
+#define BSIZE (128*1024)
+
+class cTMuCha : public cPES, cMultichannelAudio {
+private:
+  FILE *in;
+  uchar buff[BSIZE];
+  bool skipMode;
+protected:
+  virtual int Output(const uchar *data, int len);
+  virtual int Action1(uchar type, uchar *data, int len);
+public:
+  cTMuCha(void);
+  void Work(char *inn);
+  };
+
+cTMuCha::cTMuCha(void)
+:cPES(prSkip)
+,cMultichannelAudio(rb)
+{
+  SetRule(0xBD,prAct1);
+  skipMode=false;
+}
+
+void cTMuCha::Work(char *inn)
+{
+  in=fopen(inn,"r");
+  if(in) {
+    while(!feof(in)) {
+      int n=fread(buff,1,BSIZE,in);
+      if(n>0) {
+        uchar *b=buff;
+        while(n) {
+          int r=Process(b,n);
+          if(r==0) usleep(100000);
+          else if(r>0) {
+            b+=r; n-=r;
+            }
+          else { printf("process failed\n"); exit(1); }
+          }
+        }
+      else {
+        printf("fread failed\n");
+        break;
+        }
+      }
+    Statistics();
+    }
+  else printf("failed to open file\n");
+}
+
+int cTMuCha::Output(const uchar *data, int len)
+{
+  return len;
+}
+
+int cTMuCha::Action1(uchar type, uchar *data, int len)
+{
+  if(SOP) {
+    int n=Check(data,len,header);
+    switch(n) {
+      case -1: // not enough data
+        return 0;
+      case 0: // encapsulate and send to cAudios
+        skipMode=false;
+        break;
+      case 1: // skip but send to cAudios
+        skipMode=true;
+        break;
+      case 2: // pass
+        Redirect(prPass);
+        return 0;
+      }
+    }
+
+  if(!skipMode) Encapsulate(data, len);
+  return -len;
+}
+
+// --------------------------------------------------------
+
+int main(int argc, char *argv[])
+{
+  if(argc<2) {
+    printf("usage: test <infile>\n");
+    exit(1);
+    }
+  cTMuCha mucha;
+  mucha.Work(argv[1]);
+  return 0;
+}
diff -uN vdr-1.2.5-orig/test_cPES.c vdr-1.2.5-ac3/test_cPES.c
--- vdr-1.2.5-orig/test_cPES.c	1970-01-01 01:00:00.000000000 +0100
+++ vdr-1.2.5-ac3/test_cPES.c	2003-10-13 21:36:58.000000000 +0200
@@ -0,0 +1,98 @@
+//
+// g++ test_cPES.c pes.o thread.o tools.o ringbuffer.o -lpthread -o test_cPES
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "multichannelaudio.h"
+#include "pes.h"
+
+extern const char *I18nTranslate(const char *s, const char *Plugin)
+{
+  return s;
+}
+
+#define BSIZE 20480
+#define NUM 40
+
+class cTPES : public cPES {
+private:
+  FILE *in, *out;
+  int num;
+  uchar buff[BSIZE], zero[10];
+protected:
+  virtual int Output(const uchar *data, int len);
+public:
+  cTPES(void);
+  void Work(char *inn, char *outn);
+  };
+
+cTPES::cTPES(void)
+:cPES()
+{
+  memset(zero,0,sizeof(zero));
+  SetRuleR(0xc0,0xc4,prSkip);
+  SetRule(0xe0,prSkip);
+  num=0;
+}
+
+void cTPES::Work(char *inn, char *outn)
+{
+  in=fopen(inn,"r");
+  out=fopen(outn,"w");
+  if(in && out) {
+    //Process(zero,sizeof(zero));
+    //Process(zero,sizeof(zero));
+
+    //for(int i=0 ; i<NUM ; i++) {
+    //while(num<13) {
+    while(!feof(in)) {
+      int n=fread(buff,1,BSIZE,in);
+      if(n>0) {
+        uchar *b=buff;
+        while(n>0) {
+          int w=Process(b,n);
+          if(w>=0) {
+            b+=w; n-=w;
+            }
+          else { printf("process failed\n"); exit(1); }
+          }
+        }
+      else {
+        printf("fread failed\n");
+        break;
+        }
+      }
+    Statistics();
+    }
+  else printf("failed to open files\n");
+}
+
+//#define RAND(x) (rand() % ((x)+1))
+#define RAND(x) (x)
+
+int cTPES::Output(const uchar *data, int len)
+{
+  int l=RAND(len);
+  if(l) {
+    //printf("### write %d -> ",l);
+    int n=fwrite(data,1,l,out);
+    //printf("%d ###\n",n);
+    if(n>0) return n;
+    if(ferror(out)) return -1;
+    }
+  return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+  if(argc<3) {
+    printf("usage: test <infile> <outfile>\n");
+    exit(1);
+    }
+  cTPES pes;
+  pes.Work(argv[1],argv[2]);
+  return 0;
+}
diff -uN vdr-1.2.5-orig/transfer.c vdr-1.2.5-ac3/transfer.c
--- vdr-1.2.5-orig/transfer.c	2003-08-31 14:19:16.000000000 +0200
+++ vdr-1.2.5-ac3/transfer.c	2003-10-13 21:36:58.000000000 +0200
@@ -22,8 +22,6 @@
 {
   ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
   remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
-  canToggleAudioTrack = false;
-  audioTrack = 0xC0;
   gotBufferReserve = false;
   active = false;
 }
@@ -97,7 +95,6 @@
            uchar *p = remux->Process(b, Count, Result);
            ringBuffer->Del(Count);
            if (p) {
-              StripAudioPackets(p, Result, audioTrack);
               while (Result > 0 && active) {
                     cPoller Poller;
                     if (DevicePoll(Poller, 100)) {
@@ -129,64 +126,6 @@
   dsyslog("transfer thread ended (pid=%d)", getpid());
 }
 
-void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
-  for (int i = 0; i < Length - 6; i++) {
-      if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
-         uchar c = b[i + 3];
-         int l = b[i + 4] * 256 + b[i + 5] + 6;
-         switch (c) {
-           case 0xBD: // dolby
-                if (Except)
-                   PlayAudio(&b[i], l);
-                // continue with deleting the data - otherwise it disturbs DVB replay
-           case 0xC0 ... 0xC1: // audio
-                if (c == 0xC1)
-                   canToggleAudioTrack = true;
-                if (!Except || c != Except)
-                   memset(&b[i], 0x00, min(l, Length-i));
-                break;
-           case 0xE0 ... 0xEF: // video
-                break;
-           default:
-                //esyslog("ERROR: unexpected packet id %02X", c);
-                l = 0;
-           }
-         if (l)
-            i += l - 1; // the loop increments, too!
-         }
-      /*XXX
-      else
-         esyslog("ERROR: broken packet header");
-         XXX*/
-      }
-}
-
-int cTransfer::NumAudioTracks(void) const
-{
-  return canToggleAudioTrack ? 2 : 1;
-}
-
-const char **cTransfer::GetAudioTracks(int *CurrentTrack) const
-{
-  if (NumAudioTracks()) {
-     if (CurrentTrack)
-        *CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
-     static const char *audioTracks1[] = { "Audio 1", NULL };
-     static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
-     return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
-     }
-  return NULL;
-}
-
-void cTransfer::SetAudioTrack(int Index)
-{
-  if ((audioTrack == 0xC0) != (Index == 0)) {
-     audioTrack = (Index == 1) ? 0xC1 : 0xC0;
-     DeviceClear();
-     }
-}
-
 // --- cTransferControl ------------------------------------------------------
 
 cDevice *cTransferControl::receiverDevice = NULL;
diff -uN vdr-1.2.5-orig/transfer.h vdr-1.2.5-ac3/transfer.h
--- vdr-1.2.5-orig/transfer.h	2003-05-11 10:48:36.000000000 +0200
+++ vdr-1.2.5-ac3/transfer.h	2003-10-13 21:36:58.000000000 +0200
@@ -16,15 +16,12 @@
 #include "ringbuffer.h"
 #include "thread.h"
 
-class cTransfer : public cReceiver, public cPlayer, public cThread {
+class cTransfer : public cReceiver, public cPlayerAudio, public cThread {
 private:
   cRingBufferLinear *ringBuffer;
   cRemux *remux;
-  bool canToggleAudioTrack;
-  uchar audioTrack;
   bool gotBufferReserve;
   bool active;
-  void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
 protected:
   virtual void Activate(bool On);
   virtual void Receive(uchar *Data, int Length);
@@ -32,9 +29,6 @@
 public:
   cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2);
   virtual ~cTransfer();
-  virtual int NumAudioTracks(void) const;
-  virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
-  virtual void SetAudioTrack(int Index);
   };
 
 class cTransferControl : public cControl {

