mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	First step towards switching to TS (Transport Stream) as recording format
This commit is contained in:
		
							
								
								
									
										22
									
								
								HISTORY
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								HISTORY
									
									
									
									
									
								
							@@ -5762,7 +5762,7 @@ Video Disk Recorder Revision History
 | 
			
		||||
- Increased the time between checking the CAM status to 500ms to avoid problems
 | 
			
		||||
  with some CAMs (reported by Arthur Konovalov).
 | 
			
		||||
 | 
			
		||||
2008-05-02: Version 1.7.1
 | 
			
		||||
2008-08-15: Version 1.7.1
 | 
			
		||||
 | 
			
		||||
- Adapted the tuning code to the new DVBFE_SET_DELSYS API (thanks to Reinhard Nissl).
 | 
			
		||||
  VDR now uses the driver from http://jusst.de/hg/multiproto_plus.
 | 
			
		||||
@@ -5784,6 +5784,26 @@ Video Disk Recorder Revision History
 | 
			
		||||
- Removed the compile time option VFAT to allow users of precompiled binary
 | 
			
		||||
  distributions to have full control over whether or not to use the --vfat option
 | 
			
		||||
  at runtime (suggested by Michael Nork).
 | 
			
		||||
- First step towards switching to TS (Transport Stream) as recording format:
 | 
			
		||||
  + The new function cDevice::PlayTs() is used to play TS packets.
 | 
			
		||||
  + The new functions cDevice::PlayTsVideo() and cDevice::PlayTsAudio()
 | 
			
		||||
    are used to play video and audio TS packets, respectively.
 | 
			
		||||
  + The new function cAudio::PlayTs() is used to play audio TS packets.
 | 
			
		||||
  + The new class cPatPmtGenerator is used to generate a PAT/PMT pair that precedes
 | 
			
		||||
    the TS data in Transfer Mode.
 | 
			
		||||
  + The new class cPatPmtParser is used by cDevice to parse the PAT/PMT data in a
 | 
			
		||||
    TS in order to find out which streams it contains.
 | 
			
		||||
  + The new class cTsToPes is used to convert TS packets to a PES packet.
 | 
			
		||||
  + cTransfer no longer uses cRemux, and doesn't run a separate thread any more.
 | 
			
		||||
    It just generates a PAT/PMT and sends all received TS packets to the primary
 | 
			
		||||
    device's PlayTs().
 | 
			
		||||
  + Live subtitle display no longer uses a ring buffer and separate thread.
 | 
			
		||||
  + cPesAssembler has been removed. Old VDR recordings only contain complete PES
 | 
			
		||||
    packets.
 | 
			
		||||
  + Since a TS needs to have a PAT/PMT, which requires the video stream type to
 | 
			
		||||
    be explicitly given, the format of the VPID field in the channels.conf file
 | 
			
		||||
    and the SVDRP commands NEWC/MODC/LSTC has been extended. The video stream type
 | 
			
		||||
    now follows the VPID and optional PPID, separated by an '=' sign.
 | 
			
		||||
 | 
			
		||||
2008-05-03: Version 1.6.0-2
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								audio.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								audio.c
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: audio.c 1.5 2006/05/28 15:03:24 kls Exp $
 | 
			
		||||
 * $Id: audio.c 2.1 2008/07/06 11:42:58 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
@@ -32,6 +32,12 @@ void cAudios::PlayAudio(const uchar *Data, int Length, uchar Id)
 | 
			
		||||
      audio->Play(Data, Length, Id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cAudios::PlayTsAudio(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  for (cAudio *audio = First(); audio; audio = Next(audio))
 | 
			
		||||
      audio->PlayTs(Data, Length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cAudios::MuteAudio(bool On)
 | 
			
		||||
{
 | 
			
		||||
  for (cAudio *audio = First(); audio; audio = Next(audio))
 | 
			
		||||
@@ -86,6 +92,29 @@ void cExternalAudio::Play(const uchar *Data, int Length, uchar Id)
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cExternalAudio::PlayTs(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (command && !mute) {
 | 
			
		||||
     if (pipe || pipe.Open(command, "w")) {
 | 
			
		||||
        int written = 0;
 | 
			
		||||
        while (Length > 0) {
 | 
			
		||||
              int w = fwrite(Data + written, 1, Length, pipe);
 | 
			
		||||
              if (w < 0) {
 | 
			
		||||
                 LOG_ERROR;
 | 
			
		||||
                 break;
 | 
			
		||||
                 }
 | 
			
		||||
              Length -= w;
 | 
			
		||||
              written += w;
 | 
			
		||||
              }
 | 
			
		||||
        }
 | 
			
		||||
     else {
 | 
			
		||||
        esyslog("ERROR: can't open pipe to audio command '%s'", command);
 | 
			
		||||
        free(command);
 | 
			
		||||
        command = NULL;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cExternalAudio::Mute(bool On)
 | 
			
		||||
{
 | 
			
		||||
  mute = On;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								audio.h
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								audio.h
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: audio.h 1.3 2005/02/12 12:20:19 kls Exp $
 | 
			
		||||
 * $Id: audio.h 2.1 2008/07/06 11:39:21 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __AUDIO_H
 | 
			
		||||
@@ -24,6 +24,11 @@ public:
 | 
			
		||||
       ///< be copied and processed in a separate thread. The Data is always a
 | 
			
		||||
       ///< complete PES audio packet. Id indicates the type of audio data this
 | 
			
		||||
       ///< packet holds.
 | 
			
		||||
  virtual void PlayTs(const uchar *Data, int Length) = 0;
 | 
			
		||||
       ///< Plays the given block of audio Data. Must return as soon as possible.
 | 
			
		||||
       ///< If the entire block of data can't be processed immediately, it must
 | 
			
		||||
       ///< be copied and processed in a separate thread. The Data is always a
 | 
			
		||||
       ///< complete TS audio packet.
 | 
			
		||||
  virtual void Mute(bool On) = 0;
 | 
			
		||||
       ///< Immediately sets the audio device to be silent (On==true) or to
 | 
			
		||||
       ///< normal replay (On==false).
 | 
			
		||||
@@ -34,6 +39,7 @@ public:
 | 
			
		||||
class cAudios : public cList<cAudio> {
 | 
			
		||||
public:
 | 
			
		||||
  void PlayAudio(const uchar *Data, int Length, uchar Id);
 | 
			
		||||
  void PlayTsAudio(const uchar *Data, int Length);
 | 
			
		||||
  void MuteAudio(bool On);
 | 
			
		||||
  void ClearAudio(void);
 | 
			
		||||
  };
 | 
			
		||||
@@ -49,6 +55,7 @@ public:
 | 
			
		||||
  cExternalAudio(const char *Command);
 | 
			
		||||
  virtual ~cExternalAudio();
 | 
			
		||||
  virtual void Play(const uchar *Data, int Length, uchar Id);
 | 
			
		||||
  virtual void PlayTs(const uchar *Data, int Length);
 | 
			
		||||
  virtual void Mute(bool On);
 | 
			
		||||
  virtual void Clear(void);
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								channels.c
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								channels.c
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: channels.c 2.2 2008/04/12 13:49:12 kls Exp $
 | 
			
		||||
 * $Id: channels.c 2.3 2008/07/06 12:59:41 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "channels.h"
 | 
			
		||||
@@ -505,10 +505,10 @@ static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[]
 | 
			
		||||
  return q - s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
 | 
			
		||||
void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
 | 
			
		||||
{
 | 
			
		||||
  int mod = CHANNELMOD_NONE;
 | 
			
		||||
  if (vpid != Vpid || ppid != Ppid || tpid != Tpid)
 | 
			
		||||
  if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
 | 
			
		||||
     mod |= CHANNELMOD_PIDS;
 | 
			
		||||
  int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs);
 | 
			
		||||
  if (m & STRDIFF)
 | 
			
		||||
@@ -542,9 +542,10 @@ void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE
 | 
			
		||||
     q = NewSpidsBuf;
 | 
			
		||||
     q += IntArrayToString(q, Spids, 10, SLangs);
 | 
			
		||||
     *q = 0;
 | 
			
		||||
     dsyslog("changing pids of channel %d from %d+%d:%s:%s:%d to %d+%d:%s:%s:%d", Number(), vpid, ppid, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, NewApidsBuf, NewSpidsBuf, Tpid);
 | 
			
		||||
     dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
 | 
			
		||||
     vpid = Vpid;
 | 
			
		||||
     ppid = Ppid;
 | 
			
		||||
     vtype = Vtype;
 | 
			
		||||
     for (int i = 0; i < MAXAPIDS; i++) {
 | 
			
		||||
         apids[i] = Apids[i];
 | 
			
		||||
         strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
 | 
			
		||||
@@ -752,6 +753,8 @@ cString cChannel::ToText(const cChannel *Channel)
 | 
			
		||||
     q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
 | 
			
		||||
     if (Channel->ppid && Channel->ppid != Channel->vpid)
 | 
			
		||||
        q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
 | 
			
		||||
     if (Channel->vtype)
 | 
			
		||||
        q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
 | 
			
		||||
     *q = 0;
 | 
			
		||||
     const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia
 | 
			
		||||
     char apidbuf[BufferSize];
 | 
			
		||||
@@ -813,22 +816,27 @@ bool cChannel::Parse(const char *s)
 | 
			
		||||
           tpid = 0;
 | 
			
		||||
           }
 | 
			
		||||
        vpid = ppid = 0;
 | 
			
		||||
        vtype = 2; // default is MPEG-2
 | 
			
		||||
        apids[0] = 0;
 | 
			
		||||
        dpids[0] = 0;
 | 
			
		||||
        ok = false;
 | 
			
		||||
        if (parambuf && sourcebuf && vpidbuf && apidbuf) {
 | 
			
		||||
           ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
 | 
			
		||||
 | 
			
		||||
           char *p = strchr(vpidbuf, '+');
 | 
			
		||||
           if (p)
 | 
			
		||||
           char *p;
 | 
			
		||||
           if ((p = strchr(vpidbuf, '=')) != NULL) {
 | 
			
		||||
              *p++ = 0;
 | 
			
		||||
              if (sscanf(p, "%d", &vtype) != 1)
 | 
			
		||||
                 return false;
 | 
			
		||||
              }
 | 
			
		||||
           if ((p = strchr(vpidbuf, '+')) != NULL) {
 | 
			
		||||
              *p++ = 0;
 | 
			
		||||
           if (sscanf(vpidbuf, "%d", &vpid) != 1)
 | 
			
		||||
              return false;
 | 
			
		||||
           if (p) {
 | 
			
		||||
              if (sscanf(p, "%d", &ppid) != 1)
 | 
			
		||||
                 return false;
 | 
			
		||||
              }
 | 
			
		||||
           else
 | 
			
		||||
           if (sscanf(vpidbuf, "%d", &vpid) != 1)
 | 
			
		||||
              return false;
 | 
			
		||||
           if (!ppid)
 | 
			
		||||
              ppid = vpid;
 | 
			
		||||
 | 
			
		||||
           char *dpidbuf = strchr(apidbuf, ';');
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: channels.h 2.2 2008/04/12 13:46:50 kls Exp $
 | 
			
		||||
 * $Id: channels.h 2.3 2008/07/06 11:49:37 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __CHANNELS_H
 | 
			
		||||
@@ -124,6 +124,7 @@ private:
 | 
			
		||||
  int srate;
 | 
			
		||||
  int vpid;
 | 
			
		||||
  int ppid;
 | 
			
		||||
  int vtype;
 | 
			
		||||
  int apids[MAXAPIDS + 1]; // list is zero-terminated
 | 
			
		||||
  char alangs[MAXAPIDS][MAXLANGCODE2];
 | 
			
		||||
  int dpids[MAXDPIDS + 1]; // list is zero-terminated
 | 
			
		||||
@@ -178,6 +179,7 @@ public:
 | 
			
		||||
  int Srate(void) const { return srate; }
 | 
			
		||||
  int Vpid(void) const { return vpid; }
 | 
			
		||||
  int Ppid(void) const { return ppid; }
 | 
			
		||||
  int Vtype(void) const { return vtype; }
 | 
			
		||||
  const int *Apids(void) const { return apids; }
 | 
			
		||||
  const int *Dpids(void) const { return dpids; }
 | 
			
		||||
  const int *Spids(void) const { return spids; }
 | 
			
		||||
@@ -225,7 +227,7 @@ public:
 | 
			
		||||
  void SetId(int Nid, int Tid, int Sid, int Rid = 0);
 | 
			
		||||
  void SetName(const char *Name, const char *ShortName, const char *Provider);
 | 
			
		||||
  void SetPortalName(const char *PortalName);
 | 
			
		||||
  void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
 | 
			
		||||
  void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
 | 
			
		||||
  void SetCaIds(const int *CaIds); // list must be zero-terminated
 | 
			
		||||
  void SetCaDescriptors(int Level);
 | 
			
		||||
  void SetLinkChannels(cLinkChannels *LinkChannels);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										286
									
								
								device.c
									
									
									
									
									
								
							
							
						
						
									
										286
									
								
								device.c
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: device.c 2.2 2008/04/12 14:12:14 kls Exp $
 | 
			
		||||
 * $Id: device.c 2.3 2008/07/06 13:22:21 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "device.h"
 | 
			
		||||
@@ -21,16 +21,9 @@
 | 
			
		||||
 | 
			
		||||
// --- cLiveSubtitle ---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#define LIVESUBTITLEBUFSIZE  KILOBYTE(100)
 | 
			
		||||
 | 
			
		||||
class cLiveSubtitle : public cReceiver, public cThread {
 | 
			
		||||
private:
 | 
			
		||||
  cRingBufferLinear *ringBuffer;
 | 
			
		||||
  cRemux *remux;
 | 
			
		||||
class cLiveSubtitle : public cReceiver {
 | 
			
		||||
protected:
 | 
			
		||||
  virtual void Activate(bool On);
 | 
			
		||||
  virtual void Receive(uchar *Data, int Length);
 | 
			
		||||
  virtual void Action(void);
 | 
			
		||||
public:
 | 
			
		||||
  cLiveSubtitle(int SPid);
 | 
			
		||||
  virtual ~cLiveSubtitle();
 | 
			
		||||
@@ -38,170 +31,17 @@ public:
 | 
			
		||||
 | 
			
		||||
cLiveSubtitle::cLiveSubtitle(int SPid)
 | 
			
		||||
:cReceiver(tChannelID(), -1, SPid)
 | 
			
		||||
,cThread("live subtitle")
 | 
			
		||||
{
 | 
			
		||||
  ringBuffer = new cRingBufferLinear(LIVESUBTITLEBUFSIZE, TS_SIZE * 2, true, "Live Subtitle");
 | 
			
		||||
  int NoPids = 0;
 | 
			
		||||
  int SPids[] = { SPid, 0 };
 | 
			
		||||
  remux = new cRemux(0, &NoPids, &NoPids, SPids);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cLiveSubtitle::~cLiveSubtitle()
 | 
			
		||||
{
 | 
			
		||||
  cReceiver::Detach();
 | 
			
		||||
  delete remux;
 | 
			
		||||
  delete ringBuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cLiveSubtitle::Activate(bool On)
 | 
			
		||||
{
 | 
			
		||||
  if (On)
 | 
			
		||||
     Start();
 | 
			
		||||
  else
 | 
			
		||||
     Cancel(3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cLiveSubtitle::Receive(uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (Running()) {
 | 
			
		||||
     int p = ringBuffer->Put(Data, Length);
 | 
			
		||||
     if (p != Length && Running())
 | 
			
		||||
        ringBuffer->ReportOverflow(Length - p);
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cLiveSubtitle::Action(void)
 | 
			
		||||
{
 | 
			
		||||
  while (Running()) {
 | 
			
		||||
        int Count;
 | 
			
		||||
        uchar *b = ringBuffer->Get(Count);
 | 
			
		||||
        if (b) {
 | 
			
		||||
           Count = remux->Put(b, Count);
 | 
			
		||||
           if (Count)
 | 
			
		||||
              ringBuffer->Del(Count);
 | 
			
		||||
           }
 | 
			
		||||
        b = remux->Get(Count);
 | 
			
		||||
        if (b) {
 | 
			
		||||
           Count = cDevice::PrimaryDevice()->PlaySubtitle(b, Count);
 | 
			
		||||
           remux->Del(Count);
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cPesAssembler ---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
class cPesAssembler {
 | 
			
		||||
private:
 | 
			
		||||
  uchar *data;
 | 
			
		||||
  uint32_t tag;
 | 
			
		||||
  int length;
 | 
			
		||||
  int size;
 | 
			
		||||
  bool Realloc(int Size);
 | 
			
		||||
public:
 | 
			
		||||
  cPesAssembler(void);
 | 
			
		||||
  ~cPesAssembler();
 | 
			
		||||
  int ExpectedLength(void) { return PacketSize(data); }
 | 
			
		||||
  static int PacketSize(const uchar *data);
 | 
			
		||||
  int Length(void) { return length; }
 | 
			
		||||
  const uchar *Data(void) { return data; } // only valid if Length() >= 4
 | 
			
		||||
  void Reset(void);
 | 
			
		||||
  void Put(uchar c);
 | 
			
		||||
  void Put(const uchar *Data, int Length);
 | 
			
		||||
  bool IsPes(void);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
cPesAssembler::cPesAssembler(void)
 | 
			
		||||
{
 | 
			
		||||
  data = NULL;
 | 
			
		||||
  size = 0;
 | 
			
		||||
  Reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cPesAssembler::~cPesAssembler()
 | 
			
		||||
{
 | 
			
		||||
  free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPesAssembler::Reset(void)
 | 
			
		||||
{
 | 
			
		||||
  tag = 0xFFFFFFFF;
 | 
			
		||||
  length = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool cPesAssembler::Realloc(int Size)
 | 
			
		||||
{
 | 
			
		||||
  if (Size > size) {
 | 
			
		||||
     size = max(Size, 2048);
 | 
			
		||||
     data = (uchar *)realloc(data, size);
 | 
			
		||||
     if (!data) {
 | 
			
		||||
        esyslog("ERROR: can't allocate memory for PES assembler");
 | 
			
		||||
        length = 0;
 | 
			
		||||
        size = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPesAssembler::Put(uchar c)
 | 
			
		||||
{
 | 
			
		||||
  if (length < 4) {
 | 
			
		||||
     tag = (tag << 8) | c;
 | 
			
		||||
     if ((tag & 0xFFFFFF00) == 0x00000100) {
 | 
			
		||||
        if (Realloc(4)) {
 | 
			
		||||
           *(uint32_t *)data = htonl(tag);
 | 
			
		||||
           length = 4;
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
     else if (length < 3)
 | 
			
		||||
        length++;
 | 
			
		||||
     }
 | 
			
		||||
  else if (Realloc(length + 1))
 | 
			
		||||
     data[length++] = c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPesAssembler::Put(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  while (length < 4 && Length > 0) {
 | 
			
		||||
        Put(*Data++);
 | 
			
		||||
        Length--;
 | 
			
		||||
        }
 | 
			
		||||
  if (Length && Realloc(length + Length)) {
 | 
			
		||||
     memcpy(data + length, Data, Length);
 | 
			
		||||
     length += Length;
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPesAssembler::PacketSize(const uchar *data)
 | 
			
		||||
{
 | 
			
		||||
  // we need atleast 6 bytes of data here !!!
 | 
			
		||||
  switch (data[3]) {
 | 
			
		||||
    default:
 | 
			
		||||
    case 0x00 ... 0xB8: // video stream start codes
 | 
			
		||||
    case 0xB9: // Program end
 | 
			
		||||
    case 0xBC: // Programm stream map
 | 
			
		||||
    case 0xF0 ... 0xFF: // reserved
 | 
			
		||||
         return 6;
 | 
			
		||||
 | 
			
		||||
    case 0xBA: // Pack header
 | 
			
		||||
         if ((data[4] & 0xC0) == 0x40) // MPEG2
 | 
			
		||||
            return 14;
 | 
			
		||||
         // to be absolutely correct we would have to add the stuffing bytes
 | 
			
		||||
         // as well, but at this point we only may have 6 bytes of data avail-
 | 
			
		||||
         // able. So it's up to the higher level to resync...
 | 
			
		||||
         //return 14 + (data[13] & 0x07); // add stuffing bytes
 | 
			
		||||
         else // MPEG1
 | 
			
		||||
            return 12;
 | 
			
		||||
 | 
			
		||||
    case 0xBB: // System header
 | 
			
		||||
    case 0xBD: // Private stream1
 | 
			
		||||
    case 0xBE: // Padding stream
 | 
			
		||||
    case 0xBF: // Private stream2 (navigation data)
 | 
			
		||||
    case 0xC0 ... 0xCF: // all the rest (the real packets)
 | 
			
		||||
    case 0xD0 ... 0xDF:
 | 
			
		||||
    case 0xE0 ... 0xEF:
 | 
			
		||||
         return 6 + data[4] * 256 + data[5];
 | 
			
		||||
    }
 | 
			
		||||
  cDevice::PrimaryDevice()->PlayTs(Data, Length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cDevice ---------------------------------------------------------------
 | 
			
		||||
@@ -241,7 +81,6 @@ cDevice::cDevice(void)
 | 
			
		||||
  startScrambleDetection = 0;
 | 
			
		||||
 | 
			
		||||
  player = NULL;
 | 
			
		||||
  pesAssembler = new cPesAssembler;
 | 
			
		||||
  ClrAvailableTracks();
 | 
			
		||||
  currentAudioTrack = ttNone;
 | 
			
		||||
  currentAudioTrackMissingCount = 0;
 | 
			
		||||
@@ -265,7 +104,6 @@ cDevice::~cDevice()
 | 
			
		||||
  DetachAllReceivers();
 | 
			
		||||
  delete liveSubtitle;
 | 
			
		||||
  delete dvbSubtitleConverter;
 | 
			
		||||
  delete pesAssembler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool cDevice::WaitForAllDevicesReady(int Timeout)
 | 
			
		||||
@@ -1195,7 +1033,6 @@ bool cDevice::AttachPlayer(cPlayer *Player)
 | 
			
		||||
        Detach(player);
 | 
			
		||||
     DELETENULL(liveSubtitle);
 | 
			
		||||
     DELETENULL(dvbSubtitleConverter);
 | 
			
		||||
     pesAssembler->Reset();
 | 
			
		||||
     player = Player;
 | 
			
		||||
     if (!Transferring())
 | 
			
		||||
        ClrAvailableTracks(false, true);
 | 
			
		||||
@@ -1256,7 +1093,7 @@ int cDevice::PlaySubtitle(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (!dvbSubtitleConverter)
 | 
			
		||||
     dvbSubtitleConverter = new cDvbSubtitleConverter;
 | 
			
		||||
  return dvbSubtitleConverter->Convert(Data, Length);
 | 
			
		||||
  return dvbSubtitleConverter->ConvertFragments(Data, Length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
 | 
			
		||||
@@ -1360,42 +1197,16 @@ pre_1_3_19_PrivateStreamDetected:
 | 
			
		||||
int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
 | 
			
		||||
{
 | 
			
		||||
  if (!Data) {
 | 
			
		||||
     pesAssembler->Reset();
 | 
			
		||||
     if (dvbSubtitleConverter)
 | 
			
		||||
        dvbSubtitleConverter->Reset();
 | 
			
		||||
     return 0;
 | 
			
		||||
     }
 | 
			
		||||
  int Result = 0;
 | 
			
		||||
  if (pesAssembler->Length()) {
 | 
			
		||||
     // Make sure we have a complete PES header:
 | 
			
		||||
     while (pesAssembler->Length() < 6 && Length > 0) {
 | 
			
		||||
           pesAssembler->Put(*Data++);
 | 
			
		||||
           Length--;
 | 
			
		||||
           Result++;
 | 
			
		||||
           }
 | 
			
		||||
     if (pesAssembler->Length() < 6)
 | 
			
		||||
        return Result; // Still no complete PES header - wait for more
 | 
			
		||||
     int l = pesAssembler->ExpectedLength();
 | 
			
		||||
     int Rest = min(l - pesAssembler->Length(), Length);
 | 
			
		||||
     pesAssembler->Put(Data, Rest);
 | 
			
		||||
     Data += Rest;
 | 
			
		||||
     Length -= Rest;
 | 
			
		||||
     Result += Rest;
 | 
			
		||||
     if (pesAssembler->Length() < l)
 | 
			
		||||
        return Result; // Still no complete PES packet - wait for more
 | 
			
		||||
     // Now pesAssembler contains one complete PES packet.
 | 
			
		||||
     int w = PlayPesPacket(pesAssembler->Data(), pesAssembler->Length(), VideoOnly);
 | 
			
		||||
     if (w > 0)
 | 
			
		||||
        pesAssembler->Reset();
 | 
			
		||||
     return Result > 0 ? Result : w < 0 ? w : 0;
 | 
			
		||||
     }
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  while (i <= Length - 6) {
 | 
			
		||||
        if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
 | 
			
		||||
           int l = cPesAssembler::PacketSize(&Data[i]);
 | 
			
		||||
           int l = PesLength(Data + i);
 | 
			
		||||
           if (i + l > Length) {
 | 
			
		||||
              // Store incomplete PES packet for later completion:
 | 
			
		||||
              pesAssembler->Put(Data + i, Length - i);
 | 
			
		||||
              esyslog("ERROR: incomplete PES packet!");
 | 
			
		||||
              return Length;
 | 
			
		||||
              }
 | 
			
		||||
           int w = PlayPesPacket(Data + i, l, VideoOnly);
 | 
			
		||||
@@ -1408,10 +1219,91 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
 | 
			
		||||
           i++;
 | 
			
		||||
        }
 | 
			
		||||
  if (i < Length)
 | 
			
		||||
     pesAssembler->Put(Data + i, Length - i);
 | 
			
		||||
     esyslog("ERROR: leftover PES data!");
 | 
			
		||||
  return Length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDevice::PlayTsVideo(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  // Video PES has no explicit length, so we can only determine the end of
 | 
			
		||||
  // a PES packet when the next TS packet that starts a payload comes in:
 | 
			
		||||
  if (TsPayloadStart(Data)) {
 | 
			
		||||
     if (const uchar *p = tsToPesVideo.GetPes(Length)) {
 | 
			
		||||
        int w = PlayVideo(p, Length);
 | 
			
		||||
        if (w > 0)
 | 
			
		||||
           tsToPesVideo.Reset();
 | 
			
		||||
        else
 | 
			
		||||
           return w;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
  tsToPesVideo.PutTs(Data, Length);
 | 
			
		||||
  return Length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDevice::PlayTsAudio(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  bool PayloadStart = TsPayloadStart(Data);
 | 
			
		||||
  for (int Pass = 0; Pass < 2; Pass++) {
 | 
			
		||||
      if (Pass == 0 && !PayloadStart) // if no new payload is started, we can always put the packet into the converter
 | 
			
		||||
         tsToPesAudio.PutTs(Data, Length);
 | 
			
		||||
      if (const uchar *p = tsToPesAudio.GetPes(Length)) {
 | 
			
		||||
         int w = PlayAudio(p, Length, 0);
 | 
			
		||||
         if (w > 0)
 | 
			
		||||
            tsToPesAudio.Reset();
 | 
			
		||||
         else if (PayloadStart)
 | 
			
		||||
            return w; // must get out the old packet before starting a new one
 | 
			
		||||
         }
 | 
			
		||||
      if (Pass == 0 && PayloadStart)
 | 
			
		||||
         tsToPesAudio.PutTs(Data, Length);
 | 
			
		||||
      }
 | 
			
		||||
  return Length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDevice::PlayTsSubtitle(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (!dvbSubtitleConverter)
 | 
			
		||||
     dvbSubtitleConverter = new cDvbSubtitleConverter;
 | 
			
		||||
  tsToPesSubtitle.PutTs(Data, Length);
 | 
			
		||||
  if (const uchar *p = tsToPesSubtitle.GetPes(Length)) {
 | 
			
		||||
     dvbSubtitleConverter->Convert(p, Length);
 | 
			
		||||
     tsToPesSubtitle.Reset();
 | 
			
		||||
     }
 | 
			
		||||
  return Length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO detect and report continuity errors?
 | 
			
		||||
int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
 | 
			
		||||
{
 | 
			
		||||
  if (Length == TS_SIZE) {
 | 
			
		||||
     if (!TsHasPayload(Data))
 | 
			
		||||
        return Length; // silently ignore TS packets w/o payload
 | 
			
		||||
     int PayloadOffset = TsPayloadOffset(Data);
 | 
			
		||||
     if (PayloadOffset < Length) {
 | 
			
		||||
        int Pid = TsPid(Data);
 | 
			
		||||
        if (Pid == 0)
 | 
			
		||||
           patPmtParser.ParsePat(Data + PayloadOffset, Length - PayloadOffset);
 | 
			
		||||
        else if (Pid == patPmtParser.PmtPid())
 | 
			
		||||
           patPmtParser.ParsePmt(Data + PayloadOffset, Length - PayloadOffset);
 | 
			
		||||
        else if (Pid == patPmtParser.Vpid())
 | 
			
		||||
           return PlayTsVideo(Data, Length);
 | 
			
		||||
        else if (Pid == availableTracks[currentAudioTrack].id) {
 | 
			
		||||
           if (!VideoOnly || HasIBPTrickSpeed()) {
 | 
			
		||||
              int w = PlayTsAudio(Data, Length);
 | 
			
		||||
              if (w > 0)
 | 
			
		||||
                 Audios.PlayTsAudio(Data, Length);
 | 
			
		||||
              return w;
 | 
			
		||||
              }
 | 
			
		||||
           }
 | 
			
		||||
        else if (Pid == availableTracks[currentSubtitleTrack].id) {
 | 
			
		||||
           if (!VideoOnly || HasIBPTrickSpeed())
 | 
			
		||||
              return PlayTsSubtitle(Data, Length);
 | 
			
		||||
           }
 | 
			
		||||
        return Length;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDevice::Priority(void) const
 | 
			
		||||
{
 | 
			
		||||
  int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
 | 
			
		||||
@@ -1448,7 +1340,7 @@ void cDevice::Action(void)
 | 
			
		||||
           uchar *b = NULL;
 | 
			
		||||
           if (GetTSPacket(b)) {
 | 
			
		||||
              if (b) {
 | 
			
		||||
                 int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
 | 
			
		||||
                 int Pid = TsPid(b);
 | 
			
		||||
                 // Check whether the TS packets are scrambled:
 | 
			
		||||
                 bool DetachReceivers = false;
 | 
			
		||||
                 bool DescramblingOk = false;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								device.h
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								device.h
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: device.h 2.1 2008/04/12 11:11:23 kls Exp $
 | 
			
		||||
 * $Id: device.h 2.2 2008/07/06 11:25:42 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __DEVICE_H
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
#include "filter.h"
 | 
			
		||||
#include "nit.h"
 | 
			
		||||
#include "pat.h"
 | 
			
		||||
#include "remux.h"
 | 
			
		||||
#include "ringbuffer.h"
 | 
			
		||||
#include "sdt.h"
 | 
			
		||||
#include "sections.h"
 | 
			
		||||
@@ -30,10 +31,6 @@
 | 
			
		||||
#define MAXVOLUME         255
 | 
			
		||||
#define VOLUMEDELTA         5 // used to increase/decrease the volume
 | 
			
		||||
 | 
			
		||||
#define TS_SIZE          188
 | 
			
		||||
#define TS_SYNC_BYTE     0x47
 | 
			
		||||
#define PID_MASK_HI      0x1F
 | 
			
		||||
 | 
			
		||||
enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
 | 
			
		||||
 | 
			
		||||
enum ePlayMode { pmNone,           // audio/video from decoder
 | 
			
		||||
@@ -89,7 +86,6 @@ struct tTrackId {
 | 
			
		||||
 | 
			
		||||
class cPlayer;
 | 
			
		||||
class cReceiver;
 | 
			
		||||
class cPesAssembler;
 | 
			
		||||
class cLiveSubtitle;
 | 
			
		||||
 | 
			
		||||
/// The cDevice class is the base from which actual devices can be derived.
 | 
			
		||||
@@ -477,7 +473,10 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  cPlayer *player;
 | 
			
		||||
  cPesAssembler *pesAssembler;
 | 
			
		||||
  cPatPmtParser patPmtParser;
 | 
			
		||||
  cTsToPes tsToPesVideo;
 | 
			
		||||
  cTsToPes tsToPesAudio;
 | 
			
		||||
  cTsToPes tsToPesSubtitle;
 | 
			
		||||
protected:
 | 
			
		||||
  virtual bool CanReplay(void) const;
 | 
			
		||||
       ///< Returns true if this device can currently start a replay session.
 | 
			
		||||
@@ -511,6 +510,33 @@ protected:
 | 
			
		||||
       ///< If VideoOnly is true, only the video will be displayed,
 | 
			
		||||
       ///< which is necessary for trick modes like 'fast forward'.
 | 
			
		||||
       ///< Data must point to one single, complete PES packet.
 | 
			
		||||
  virtual int PlayTsVideo(const uchar *Data, int Length);
 | 
			
		||||
       ///< Plays the given data block as video.
 | 
			
		||||
       ///< Data points to exactly one complete TS packet of the given Length
 | 
			
		||||
       ///< (which is always TS_SIZE).
 | 
			
		||||
       ///< PlayTsVideo() shall process the packet either as a whole (returning
 | 
			
		||||
       ///< a positive number, which needs not necessarily be Length) or not at all
 | 
			
		||||
       ///< (returning 0 or -1 and setting 'errno' to EAGAIN).
 | 
			
		||||
       ///< The default implementation collects all incoming TS payload belonging
 | 
			
		||||
       ///< to one PES packet and calls PlayVideo() with the resulting packet.
 | 
			
		||||
  virtual int PlayTsAudio(const uchar *Data, int Length);
 | 
			
		||||
       ///< Plays the given data block as audio.
 | 
			
		||||
       ///< Data points to exactly one complete TS packet of the given Length
 | 
			
		||||
       ///< (which is always TS_SIZE).
 | 
			
		||||
       ///< PlayTsAudio() shall process the packet either as a whole (returning
 | 
			
		||||
       ///< a positive number, which needs not necessarily be Length) or not at all
 | 
			
		||||
       ///< (returning 0 or -1 and setting 'errno' to EAGAIN).
 | 
			
		||||
       ///< The default implementation collects all incoming TS payload belonging
 | 
			
		||||
       ///< to one PES packet and calls PlayAudio() with the resulting packet.
 | 
			
		||||
  virtual int PlayTsSubtitle(const uchar *Data, int Length);
 | 
			
		||||
       ///< Plays the given data block as a subtitle.
 | 
			
		||||
       ///< Data points to exactly one complete TS packet of the given Length
 | 
			
		||||
       ///< (which is always TS_SIZE).
 | 
			
		||||
       ///< PlayTsSubtitle() shall process the packet either as a whole (returning
 | 
			
		||||
       ///< a positive number, which needs not necessarily be Length) or not at all
 | 
			
		||||
       ///< (returning 0 or -1 and setting 'errno' to EAGAIN).
 | 
			
		||||
       ///< The default implementation collects all incoming TS payload belonging
 | 
			
		||||
       ///< to one PES packet and displays the resulting subtitle via the OSD.
 | 
			
		||||
public:
 | 
			
		||||
  virtual int64_t GetSTC(void);
 | 
			
		||||
       ///< Gets the current System Time Counter, which can be used to
 | 
			
		||||
@@ -565,6 +591,21 @@ public:
 | 
			
		||||
       ///< to a complete packet with data from the next call to PlayPes().
 | 
			
		||||
       ///< That way any functions called from within PlayPes() will be
 | 
			
		||||
       ///< guaranteed to always receive complete PES packets.
 | 
			
		||||
  virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false);
 | 
			
		||||
       ///< Plays the given TS packet.
 | 
			
		||||
       ///< If VideoOnly is true, only the video will be displayed,
 | 
			
		||||
       ///< which is necessary for trick modes like 'fast forward'.
 | 
			
		||||
       ///< Data points to a single TS packet, Length is always TS_SIZE (the total
 | 
			
		||||
       ///< size of a single TS packet).
 | 
			
		||||
       ///< A derived device can reimplement this function to handle the
 | 
			
		||||
       ///< TS packets itself. Any packets the derived function can't handle
 | 
			
		||||
       ///< must be sent to the base class function. This applies especially
 | 
			
		||||
       ///< to the PAT/PMT packets.
 | 
			
		||||
       ///< Returns -1 in case of error, otherwise the number of actually
 | 
			
		||||
       ///< processed bytes is returned, which may be less than Length.
 | 
			
		||||
       ///< PlayTs() shall process the packet either as a whole (returning
 | 
			
		||||
       ///< a positive number, which needs not necessarily be Length) or not at all
 | 
			
		||||
       ///< (returning 0 or -1 and setting 'errno' to EAGAIN).
 | 
			
		||||
  bool Replaying(void) const;
 | 
			
		||||
       ///< Returns true if we are currently replaying.
 | 
			
		||||
  bool Transferring(void) const;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								dvbdevice.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								dvbdevice.c
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: dvbdevice.c 2.3 2008/04/19 09:19:08 kls Exp $
 | 
			
		||||
 * $Id: dvbdevice.c 2.4 2008/07/06 13:58:56 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "dvbdevice.h"
 | 
			
		||||
@@ -1236,6 +1236,18 @@ int cDvbDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
 | 
			
		||||
  return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDvbDevice::PlayTsVideo(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  Length = TsGetPayload(&Data);
 | 
			
		||||
  return PlayVideo(Data, Length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDvbDevice::PlayTsAudio(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  Length = TsGetPayload(&Data);
 | 
			
		||||
  return PlayAudio(Data, Length, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool cDvbDevice::OpenDvr(void)
 | 
			
		||||
{
 | 
			
		||||
  CloseDvr();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: dvbdevice.h 2.1 2008/04/12 11:20:48 kls Exp $
 | 
			
		||||
 * $Id: dvbdevice.h 2.2 2008/06/01 09:48:04 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __DVBDEVICE_H
 | 
			
		||||
@@ -138,6 +138,8 @@ protected:
 | 
			
		||||
  virtual bool SetPlayMode(ePlayMode PlayMode);
 | 
			
		||||
  virtual int PlayVideo(const uchar *Data, int Length);
 | 
			
		||||
  virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
 | 
			
		||||
  virtual int PlayTsVideo(const uchar *Data, int Length);
 | 
			
		||||
  virtual int PlayTsAudio(const uchar *Data, int Length);
 | 
			
		||||
public:
 | 
			
		||||
  virtual int64_t GetSTC(void);
 | 
			
		||||
  virtual void TrickSpeed(int Speed);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 * Original author: Marco Schl<68><6C>ler <marco@lordzodiac.de>
 | 
			
		||||
 * With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: dvbsubtitle.c 1.3 2007/11/25 13:33:08 kls Exp $
 | 
			
		||||
 * $Id: dvbsubtitle.c 2.1 2008/05/25 14:36:24 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "dvbsubtitle.h"
 | 
			
		||||
@@ -580,12 +580,12 @@ bool cDvbSubtitleAssembler::Realloc(int Size)
 | 
			
		||||
unsigned char *cDvbSubtitleAssembler::Get(int &Length)
 | 
			
		||||
{
 | 
			
		||||
  if (length > pos + 5) {
 | 
			
		||||
      Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
 | 
			
		||||
      if (length >= pos + Length) {
 | 
			
		||||
         unsigned char *result = data + pos;
 | 
			
		||||
         pos += Length;
 | 
			
		||||
         return result;
 | 
			
		||||
         }
 | 
			
		||||
     Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
 | 
			
		||||
     if (length >= pos + Length) {
 | 
			
		||||
        unsigned char *result = data + pos;
 | 
			
		||||
        pos += Length;
 | 
			
		||||
        return result;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -684,10 +684,10 @@ void cDvbSubtitleConverter::Reset(void)
 | 
			
		||||
  Unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
 | 
			
		||||
int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (Data && Length > 8) {
 | 
			
		||||
     int PayloadOffset = Data[8] + 9;
 | 
			
		||||
     int PayloadOffset = PesPayloadOffset(Data);
 | 
			
		||||
     int SubstreamHeaderLength = 4;
 | 
			
		||||
     bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
 | 
			
		||||
 | 
			
		||||
@@ -699,15 +699,9 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
     if (Length > PayloadOffset + SubstreamHeaderLength) {
 | 
			
		||||
        int64_t pts = 0;
 | 
			
		||||
        if ((Data[7] & 0x80) && Data[8] >= 5) {
 | 
			
		||||
           pts  = (((int64_t)Data[ 9]) & 0x0E) << 29;
 | 
			
		||||
           pts |= ( (int64_t)Data[10])         << 22;
 | 
			
		||||
           pts |= (((int64_t)Data[11]) & 0xFE) << 14;
 | 
			
		||||
           pts |= ( (int64_t)Data[12])         <<  7;
 | 
			
		||||
           pts |= (((int64_t)Data[13]) & 0xFE) >>  1;
 | 
			
		||||
        int64_t pts = PesGetPts(Data);
 | 
			
		||||
        if (pts)
 | 
			
		||||
           dbgconverter("Converter PTS: %lld\n", pts);
 | 
			
		||||
           }
 | 
			
		||||
        const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
 | 
			
		||||
        int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
 | 
			
		||||
        if (ResetSubtitleAssembler)
 | 
			
		||||
@@ -736,6 +730,40 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (Data && Length > 8) {
 | 
			
		||||
     int PayloadOffset = PesPayloadOffset(Data);
 | 
			
		||||
     if (Length > PayloadOffset) {
 | 
			
		||||
        int64_t pts = PesGetPts(Data);
 | 
			
		||||
        if (pts)
 | 
			
		||||
           dbgconverter("Converter PTS: %lld\n", pts);
 | 
			
		||||
        const uchar *data = Data + PayloadOffset;
 | 
			
		||||
        int length = Length - PayloadOffset;
 | 
			
		||||
        if (length > 3) {
 | 
			
		||||
           if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
 | 
			
		||||
              data += 2;
 | 
			
		||||
              length -= 2;
 | 
			
		||||
              }
 | 
			
		||||
           const uchar *b = data;
 | 
			
		||||
           while (length > 0) {
 | 
			
		||||
                 if (b[0] == 0x0F) {
 | 
			
		||||
                    int n = ExtractSegment(b, length, pts);
 | 
			
		||||
                    if (n < 0)
 | 
			
		||||
                       break;
 | 
			
		||||
                    b += n;
 | 
			
		||||
                    length -= n;
 | 
			
		||||
                    }
 | 
			
		||||
                 else
 | 
			
		||||
                    break;
 | 
			
		||||
                 }
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
     return Length;
 | 
			
		||||
     }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define LimitTo32Bit(n) (n & 0x00000000FFFFFFFFL)
 | 
			
		||||
#define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * Original author: Marco Schl<68><6C>ler <marco@lordzodiac.de>
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: dvbsubtitle.h 1.1 2007/10/12 14:27:30 kls Exp $
 | 
			
		||||
 * $Id: dvbsubtitle.h 2.1 2008/05/25 14:36:52 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __DVBSUBTITLE_H
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
#include "tools.h"
 | 
			
		||||
 | 
			
		||||
class cDvbSubtitlePage;
 | 
			
		||||
class cDvbSubtitleAssembler;
 | 
			
		||||
class cDvbSubtitleAssembler; // for legacy PES recordings
 | 
			
		||||
class cDvbSubtitleBitmaps;
 | 
			
		||||
 | 
			
		||||
class cDvbSubtitleConverter : public cThread {
 | 
			
		||||
@@ -36,6 +36,7 @@ public:
 | 
			
		||||
  virtual ~cDvbSubtitleConverter();
 | 
			
		||||
  void Action(void);
 | 
			
		||||
  void Reset(void);
 | 
			
		||||
  int ConvertFragments(const uchar *Data, int Length); // for legacy PES recordings
 | 
			
		||||
  int Convert(const uchar *Data, int Length);
 | 
			
		||||
  static void SetupChanged(void);
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 *   the Free Software Foundation; either version 2 of the License, or     *
 | 
			
		||||
 *   (at your option) any later version.                                   *
 | 
			
		||||
 *                                                                         *
 | 
			
		||||
 *   $Id: util.h 1.7 2006/02/25 10:13:28 kls Exp $
 | 
			
		||||
 *   $Id: util.h 2.1 2008/05/22 10:49:08 kls Exp $
 | 
			
		||||
 *                                                                         *
 | 
			
		||||
 ***************************************************************************/
 | 
			
		||||
 | 
			
		||||
@@ -148,9 +148,9 @@ public:
 | 
			
		||||
   CRC32(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF);
 | 
			
		||||
   bool isValid() { return crc32(data, length, value) == 0; }
 | 
			
		||||
   static bool isValid(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF) { return crc32(d, len, CRCvalue) == 0; }
 | 
			
		||||
   static u_int32_t crc32(const char *d, int len, u_int32_t CRCvalue);
 | 
			
		||||
protected:
 | 
			
		||||
   static u_int32_t crc_table[256];
 | 
			
		||||
   static u_int32_t crc32 (const char *d, int len, u_int32_t CRCvalue);
 | 
			
		||||
 | 
			
		||||
   const char *data;
 | 
			
		||||
   int length;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								pat.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								pat.c
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: pat.c 2.1 2008/04/12 13:34:50 kls Exp $
 | 
			
		||||
 * $Id: pat.c 2.2 2008/07/06 14:01:32 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "pat.h"
 | 
			
		||||
@@ -328,7 +328,8 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
 | 
			
		||||
        // Scan the stream-specific loop:
 | 
			
		||||
        SI::PMT::Stream stream;
 | 
			
		||||
        int Vpid = 0;
 | 
			
		||||
        int Ppid = pmt.getPCRPid();
 | 
			
		||||
        int Ppid = 0;
 | 
			
		||||
        int Vtype = 0;
 | 
			
		||||
        int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
 | 
			
		||||
        int Dpids[MAXDPIDS + 1] = { 0 };
 | 
			
		||||
        int Spids[MAXSPIDS + 1] = { 0 };
 | 
			
		||||
@@ -343,8 +344,10 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
 | 
			
		||||
            switch (stream.getStreamType()) {
 | 
			
		||||
              case 1: // STREAMTYPE_11172_VIDEO
 | 
			
		||||
              case 2: // STREAMTYPE_13818_VIDEO
 | 
			
		||||
//TODO        case 0x1B: // MPEG4
 | 
			
		||||
              case 0x1B: // MPEG4
 | 
			
		||||
                      Vpid = stream.getPid();
 | 
			
		||||
                      Ppid = pmt.getPCRPid();
 | 
			
		||||
                      Vtype = stream.getStreamType();
 | 
			
		||||
                      break;
 | 
			
		||||
              case 3: // STREAMTYPE_11172_AUDIO
 | 
			
		||||
              case 4: // STREAMTYPE_13818_AUDIO
 | 
			
		||||
@@ -440,7 +443,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        if (Setup.UpdateChannels >= 2) {
 | 
			
		||||
           Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
 | 
			
		||||
           Channel->SetPids(Vpid, Ppid, Vtype, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
 | 
			
		||||
           Channel->SetCaIds(CaDescriptors->CaIds());
 | 
			
		||||
           }
 | 
			
		||||
        Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								player.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								player.h
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: player.h 1.21 2008/02/16 13:50:11 kls Exp $
 | 
			
		||||
 * $Id: player.h 2.1 2008/08/15 14:07:48 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __PLAYER_H
 | 
			
		||||
@@ -42,6 +42,10 @@ protected:
 | 
			
		||||
       // Sends the given PES Data to the device and returns the number of
 | 
			
		||||
       // bytes that have actually been accepted by the device (or a
 | 
			
		||||
       // negative value in case of an error).
 | 
			
		||||
  int PlayTs(const uchar *Data, int Length, bool VideoOnly = false) { return device ? device->PlayTs(Data, Length, VideoOnly) : -1; }
 | 
			
		||||
       // Sends the given TS packet to the device and returns a positive number
 | 
			
		||||
       // if the packet has been accepted by the device, or a negative value in
 | 
			
		||||
       // case of an error.
 | 
			
		||||
public:
 | 
			
		||||
  cPlayer(ePlayMode PlayMode = pmAudioVideo);
 | 
			
		||||
  virtual ~cPlayer();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										477
									
								
								remux.c
									
									
									
									
									
								
							
							
						
						
									
										477
									
								
								remux.c
									
									
									
									
									
								
							@@ -11,15 +11,24 @@
 | 
			
		||||
 * The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
 | 
			
		||||
 * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: remux.c 1.64 2007/11/25 13:56:03 kls Exp $
 | 
			
		||||
 * $Id: remux.c 2.1 2008/08/15 14:49:34 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "remux.h"
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "channels.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "libsi/si.h"
 | 
			
		||||
#include "libsi/section.h"
 | 
			
		||||
#include "libsi/descriptor.h"
 | 
			
		||||
#include "shutdown.h"
 | 
			
		||||
#include "tools.h"
 | 
			
		||||
 | 
			
		||||
// Set this to 'true' for debug output:
 | 
			
		||||
static bool DebugPatPmt = false;
 | 
			
		||||
 | 
			
		||||
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
 | 
			
		||||
 | 
			
		||||
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
 | 
			
		||||
{
 | 
			
		||||
  if (Count < 7)
 | 
			
		||||
@@ -1413,7 +1422,6 @@ int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
 | 
			
		||||
//pts_dts flags
 | 
			
		||||
#define PTS_ONLY         0x80
 | 
			
		||||
 | 
			
		||||
#define TS_SIZE        188
 | 
			
		||||
#define PID_MASK_HI    0x1F
 | 
			
		||||
#define CONT_CNT_MASK  0x0F
 | 
			
		||||
 | 
			
		||||
@@ -2007,8 +2015,6 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define TS_SYNC_BYTE 0x47
 | 
			
		||||
 | 
			
		||||
int cRemux::Put(const uchar *Data, int Count)
 | 
			
		||||
{
 | 
			
		||||
  int used = 0;
 | 
			
		||||
@@ -2182,3 +2188,466 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
 | 
			
		||||
  else
 | 
			
		||||
     dsyslog("SetBrokenLink: no video packet in frame");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cPatPmtGenerator ------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cPatPmtGenerator::cPatPmtGenerator(void)
 | 
			
		||||
{
 | 
			
		||||
  numPmtPackets = 0;
 | 
			
		||||
  patCounter = pmtCounter = 0;
 | 
			
		||||
  patVersion = pmtVersion = 0;
 | 
			
		||||
  esInfoLength = NULL;
 | 
			
		||||
  GeneratePat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket)
 | 
			
		||||
{
 | 
			
		||||
  TsPacket[3] = (TsPacket[3] & 0xF0) | Counter;
 | 
			
		||||
  if (++Counter > 0x0F)
 | 
			
		||||
     Counter = 0x00;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtGenerator::IncVersion(int &Version)
 | 
			
		||||
{
 | 
			
		||||
  if (++Version > 0x1F)
 | 
			
		||||
     Version = 0x00;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtGenerator::IncEsInfoLength(int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (esInfoLength) {
 | 
			
		||||
     Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1);
 | 
			
		||||
     *esInfoLength = 0xF0 | (Length >> 8);
 | 
			
		||||
     *(esInfoLength + 1) = Length;
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid)
 | 
			
		||||
{
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  Target[i++] = Type; // stream type
 | 
			
		||||
  Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5)
 | 
			
		||||
  Target[i++] = Pid; // pid lo
 | 
			
		||||
  esInfoLength = &Target[i];
 | 
			
		||||
  Target[i++] = 0xF0; // dummy (4), ES info length hi
 | 
			
		||||
  Target[i++] = 0x00; // ES info length lo
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPatPmtGenerator::MakeAC3Descriptor(uchar *Target)
 | 
			
		||||
{
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  Target[i++] = SI::AC3DescriptorTag;
 | 
			
		||||
  Target[i++] = 0x01; // length
 | 
			
		||||
  Target[i++] = 0x00;
 | 
			
		||||
  IncEsInfoLength(i);
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language)
 | 
			
		||||
{
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  Target[i++] = SI::SubtitlingDescriptorTag;
 | 
			
		||||
  Target[i++] = 0x08; // length
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = 0x00; // subtitling type
 | 
			
		||||
  Target[i++] = 0x00; // composition page id hi
 | 
			
		||||
  Target[i++] = 0x01; // composition page id lo
 | 
			
		||||
  Target[i++] = 0x00; // ancillary page id hi
 | 
			
		||||
  Target[i++] = 0x01; // ancillary page id lo
 | 
			
		||||
  IncEsInfoLength(i);
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
 | 
			
		||||
{
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  Target[i++] = SI::ISO639LanguageDescriptorTag;
 | 
			
		||||
  Target[i++] = 0x04; // length
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = *Language++;
 | 
			
		||||
  Target[i++] = 0x01; // audio type
 | 
			
		||||
  IncEsInfoLength(i);
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF);
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  Target[i++] = crc >> 24;
 | 
			
		||||
  Target[i++] = crc >> 16;
 | 
			
		||||
  Target[i++] = crc >> 8;
 | 
			
		||||
  Target[i++] = crc;
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define P_TSID    0x8008 // pseudo TS ID
 | 
			
		||||
#define P_PNR     0x0084 // pseudo Program Number
 | 
			
		||||
#define P_PMT_PID 0x0084 // pseudo PMT pid
 | 
			
		||||
 | 
			
		||||
void cPatPmtGenerator::GeneratePat(void)
 | 
			
		||||
{
 | 
			
		||||
  memset(pat, 0xFF, sizeof(pat));
 | 
			
		||||
  uchar *p = pat;
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  p[i++] = 0x47; // TS indicator
 | 
			
		||||
  p[i++] = 0x40; // flags (3), pid hi (5)
 | 
			
		||||
  p[i++] = 0x00; // pid lo
 | 
			
		||||
  p[i++] = 0x10; // flags (4), continuity counter (4)
 | 
			
		||||
  int PayloadStart = i;
 | 
			
		||||
  p[i++] = 0x00; // table id
 | 
			
		||||
  p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
 | 
			
		||||
  int SectionLength = i;
 | 
			
		||||
  p[i++] = 0x00; // section length lo (filled in later)
 | 
			
		||||
  p[i++] = P_TSID >> 8;   // TS id hi
 | 
			
		||||
  p[i++] = P_TSID & 0xFF; // TS id lo
 | 
			
		||||
  p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1)
 | 
			
		||||
  p[i++] = 0x00; // section number
 | 
			
		||||
  p[i++] = 0x00; // last section number
 | 
			
		||||
  p[i++] = P_PNR >> 8;   // program number hi
 | 
			
		||||
  p[i++] = P_PNR & 0xFF; // program number lo
 | 
			
		||||
  p[i++] = 0xE0 | (P_PMT_PID >> 8); // dummy (3), PMT pid hi (5)
 | 
			
		||||
  p[i++] = P_PMT_PID & 0xFF; // PMT pid lo
 | 
			
		||||
  pat[SectionLength] = i - SectionLength - 1 + 4; // -2 = SectionLength storage, +4 = length of CRC
 | 
			
		||||
  MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart);
 | 
			
		||||
  IncVersion(patVersion);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtGenerator::GeneratePmt(tChannelID ChannelID)
 | 
			
		||||
{
 | 
			
		||||
  // generate the complete PMT section:
 | 
			
		||||
  uchar buf[MAX_SECTION_SIZE];
 | 
			
		||||
  memset(buf, 0xFF, sizeof(buf));
 | 
			
		||||
  numPmtPackets = 0;
 | 
			
		||||
  cChannel *Channel = Channels.GetByChannelID(ChannelID);
 | 
			
		||||
  if (Channel) {
 | 
			
		||||
     int Vpid = Channel->Vpid();
 | 
			
		||||
     uchar *p = buf;
 | 
			
		||||
     int i = 0;
 | 
			
		||||
     p[i++] = 0x02; // table id
 | 
			
		||||
     int SectionLength = i;
 | 
			
		||||
     p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
 | 
			
		||||
     p[i++] = 0x00; // section length lo (filled in later)
 | 
			
		||||
     p[i++] = P_PNR >> 8;   // program number hi
 | 
			
		||||
     p[i++] = P_PNR & 0xFF; // program number lo
 | 
			
		||||
     p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1)
 | 
			
		||||
     p[i++] = 0x00; // section number
 | 
			
		||||
     p[i++] = 0x00; // last section number
 | 
			
		||||
     p[i++] = 0xE0 | (Vpid >> 8); // dummy (3), PCR pid hi (5)
 | 
			
		||||
     p[i++] = Vpid; // PCR pid lo
 | 
			
		||||
     p[i++] = 0xF0; // dummy (4), program info length hi (4)
 | 
			
		||||
     p[i++] = 0x00; // program info length lo
 | 
			
		||||
 | 
			
		||||
     if (Vpid)
 | 
			
		||||
        i += MakeStream(buf + i, Channel->Vtype(), Vpid);
 | 
			
		||||
     for (int n = 0; Channel->Apid(n); n++) {
 | 
			
		||||
         i += MakeStream(buf + i, 0x04, Channel->Apid(n));
 | 
			
		||||
         const char *Alang = Channel->Alang(n);
 | 
			
		||||
         i += MakeLanguageDescriptor(buf + i, Alang);
 | 
			
		||||
         if (Alang[3] == '+')
 | 
			
		||||
            i += MakeLanguageDescriptor(buf + i, Alang + 3);
 | 
			
		||||
         }
 | 
			
		||||
     for (int n = 0; Channel->Dpid(n); n++) {
 | 
			
		||||
         i += MakeStream(buf + i, 0x06, Channel->Dpid(n));
 | 
			
		||||
         i += MakeAC3Descriptor(buf + i);
 | 
			
		||||
         i += MakeLanguageDescriptor(buf + i, Channel->Dlang(n));
 | 
			
		||||
         }
 | 
			
		||||
     for (int n = 0; Channel->Spid(n); n++) {
 | 
			
		||||
         i += MakeStream(buf + i, 0x06, Channel->Spid(n));
 | 
			
		||||
         i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n));
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
     int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
 | 
			
		||||
     buf[SectionLength] |= (sl >> 8) & 0x0F;
 | 
			
		||||
     buf[SectionLength + 1] = sl;
 | 
			
		||||
     MakeCRC(buf + i, buf, i);
 | 
			
		||||
     // split the PMT section into several TS packets:
 | 
			
		||||
     uchar *q = buf;
 | 
			
		||||
     while (i > 0) {
 | 
			
		||||
           uchar *p = pmt[numPmtPackets++];
 | 
			
		||||
           int j = 0;
 | 
			
		||||
           p[j++] = 0x47; // TS indicator
 | 
			
		||||
           p[j++] = 0x40 | (P_PNR >> 8); // flags (3), pid hi (5)
 | 
			
		||||
           p[j++] = P_PNR & 0xFF; // pid lo
 | 
			
		||||
           p[j++] = 0x10; // flags (4), continuity counter (4)
 | 
			
		||||
           int l = TS_SIZE - j;
 | 
			
		||||
           memcpy(p + j, q, l);
 | 
			
		||||
           q += l;
 | 
			
		||||
           i -= l;
 | 
			
		||||
           }
 | 
			
		||||
     IncVersion(pmtVersion);
 | 
			
		||||
     }
 | 
			
		||||
  else
 | 
			
		||||
     esyslog("ERROR: can't find channel %s", *ChannelID.ToString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uchar *cPatPmtGenerator::GetPat(void)
 | 
			
		||||
{
 | 
			
		||||
  IncCounter(patCounter, pat);
 | 
			
		||||
  return pat;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uchar *cPatPmtGenerator::GetPmt(int &Index)
 | 
			
		||||
{
 | 
			
		||||
  if (Index < numPmtPackets) {
 | 
			
		||||
     IncCounter(patCounter, pmt[Index]);
 | 
			
		||||
     return pmt[Index++];
 | 
			
		||||
     }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cPatPmtParser ---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cPatPmtParser::cPatPmtParser(void)
 | 
			
		||||
{
 | 
			
		||||
  pmtSize = 0;
 | 
			
		||||
  pmtPid = -1;
 | 
			
		||||
  vpid = vtype = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtParser::ParsePat(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  // The PAT is always assumed to fit into a single TS packet
 | 
			
		||||
  SI::PAT Pat(Data, false);
 | 
			
		||||
  if (Pat.CheckCRCAndParse()) {
 | 
			
		||||
     dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber());
 | 
			
		||||
     SI::PAT::Association assoc;
 | 
			
		||||
     for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) {
 | 
			
		||||
         dbgpatpmt("     isNITPid = %d\n", assoc.isNITPid());
 | 
			
		||||
         if (!assoc.isNITPid()) {
 | 
			
		||||
            pmtPid = assoc.getPid();
 | 
			
		||||
            dbgpatpmt("     service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid());
 | 
			
		||||
            }
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
  else
 | 
			
		||||
     esyslog("ERROR: can't parse PAT");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  // The PMT may extend over several TS packets, so we need to assemble them
 | 
			
		||||
  if (pmtSize == 0) {
 | 
			
		||||
     // this is the first packet
 | 
			
		||||
     if (SectionLength(Data, Length) > Length) {
 | 
			
		||||
        if (Length <= int(sizeof(pmt))) {
 | 
			
		||||
           memcpy(pmt, Data, Length);
 | 
			
		||||
           pmtSize = Length;
 | 
			
		||||
           }
 | 
			
		||||
        else
 | 
			
		||||
           esyslog("ERROR: PMT packet length too big (%d byte)!", Length);
 | 
			
		||||
        return;
 | 
			
		||||
        }
 | 
			
		||||
     // the packet contains the entire PMT section, so we run into the actual parsing
 | 
			
		||||
     }
 | 
			
		||||
  else {
 | 
			
		||||
     // this is a following packet, so we add it to the pmt storage
 | 
			
		||||
     if (Length <= int(sizeof(pmt)) - pmtSize) {
 | 
			
		||||
        memcpy(pmt + pmtSize, Data, Length);
 | 
			
		||||
        pmtSize += Length;
 | 
			
		||||
        }
 | 
			
		||||
     else {
 | 
			
		||||
        esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length);
 | 
			
		||||
        pmtSize = 0;
 | 
			
		||||
        }
 | 
			
		||||
     if (SectionLength(pmt, pmtSize) > pmtSize)
 | 
			
		||||
        return; // more packets to come
 | 
			
		||||
     // the PMT section is now complete, so we run into the actual parsing
 | 
			
		||||
     Data = pmt;
 | 
			
		||||
     }
 | 
			
		||||
  SI::PMT Pmt(Data, false);
 | 
			
		||||
  if (Pmt.CheckCRCAndParse()) {
 | 
			
		||||
     dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber());
 | 
			
		||||
     dbgpatpmt("     pcr = %d\n", Pmt.getPCRPid());
 | 
			
		||||
     cDevice::PrimaryDevice()->ClrAvailableTracks(false, true);
 | 
			
		||||
     int NumApids = 0;
 | 
			
		||||
     int NumDpids = 0;
 | 
			
		||||
     int NumSpids = 0;
 | 
			
		||||
     SI::PMT::Stream stream;
 | 
			
		||||
     for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
 | 
			
		||||
         dbgpatpmt("     stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
 | 
			
		||||
         switch (stream.getStreamType()) {
 | 
			
		||||
           case 0x02: // STREAMTYPE_13818_VIDEO
 | 
			
		||||
           case 0x1B: // MPEG4
 | 
			
		||||
                      vpid = stream.getPid();
 | 
			
		||||
                      vtype = stream.getStreamType();
 | 
			
		||||
                      break;
 | 
			
		||||
           case 0x04: // STREAMTYPE_13818_AUDIO
 | 
			
		||||
                      {
 | 
			
		||||
                      if (NumApids < MAXAPIDS) {
 | 
			
		||||
                         char ALangs[MAXLANGCODE2] = "";
 | 
			
		||||
                         SI::Descriptor *d;
 | 
			
		||||
                         for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
 | 
			
		||||
                             switch (d->getDescriptorTag()) {
 | 
			
		||||
                               case SI::ISO639LanguageDescriptorTag: {
 | 
			
		||||
                                    SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
 | 
			
		||||
                                    SI::ISO639LanguageDescriptor::Language l;
 | 
			
		||||
                                    char *s = ALangs;
 | 
			
		||||
                                    int n = 0;
 | 
			
		||||
                                    for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
 | 
			
		||||
                                        if (*ld->languageCode != '-') { // some use "---" to indicate "none"
 | 
			
		||||
                                           dbgpatpmt(" '%s'", l.languageCode);
 | 
			
		||||
                                           if (n > 0)
 | 
			
		||||
                                              *s++ = '+';
 | 
			
		||||
                                           strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1);
 | 
			
		||||
                                           s += strlen(s);
 | 
			
		||||
                                           if (n++ > 1)
 | 
			
		||||
                                              break;
 | 
			
		||||
                                           }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    break;
 | 
			
		||||
                               default: ;
 | 
			
		||||
                               }
 | 
			
		||||
                             delete d;
 | 
			
		||||
                             }
 | 
			
		||||
                         cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, stream.getPid(), ALangs);
 | 
			
		||||
                         NumApids++;
 | 
			
		||||
                         }
 | 
			
		||||
                      }
 | 
			
		||||
                      break;
 | 
			
		||||
           case 0x06: // STREAMTYPE_13818_PES_PRIVATE
 | 
			
		||||
                      {
 | 
			
		||||
                      int dpid = 0;
 | 
			
		||||
                      char lang[MAXLANGCODE1] = "";
 | 
			
		||||
                      SI::Descriptor *d;
 | 
			
		||||
                      for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
 | 
			
		||||
                          switch (d->getDescriptorTag()) {
 | 
			
		||||
                            case SI::AC3DescriptorTag:
 | 
			
		||||
                                 dbgpatpmt(" AC3");
 | 
			
		||||
                                 dpid = stream.getPid();
 | 
			
		||||
                                 break;
 | 
			
		||||
                            case SI::SubtitlingDescriptorTag:
 | 
			
		||||
                                 dbgpatpmt(" subtitling");
 | 
			
		||||
                                 if (NumSpids < MAXSPIDS) {
 | 
			
		||||
                                    SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
 | 
			
		||||
                                    SI::SubtitlingDescriptor::Subtitling sub;
 | 
			
		||||
                                    char SLangs[MAXLANGCODE2] = "";
 | 
			
		||||
                                    char *s = SLangs;
 | 
			
		||||
                                    int n = 0;
 | 
			
		||||
                                    for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
 | 
			
		||||
                                        if (sub.languageCode[0]) {
 | 
			
		||||
                                           dbgpatpmt(" '%s'", sub.languageCode);
 | 
			
		||||
                                           if (n > 0)
 | 
			
		||||
                                              *s++ = '+';
 | 
			
		||||
                                           strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
 | 
			
		||||
                                           s += strlen(s);
 | 
			
		||||
                                           if (n++ > 1)
 | 
			
		||||
                                              break;
 | 
			
		||||
                                           }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), SLangs);
 | 
			
		||||
                                    NumSpids++;
 | 
			
		||||
                                    }
 | 
			
		||||
                                 break;
 | 
			
		||||
                            case SI::ISO639LanguageDescriptorTag: {
 | 
			
		||||
                                 SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
 | 
			
		||||
                                 dbgpatpmt(" '%s'", ld->languageCode);
 | 
			
		||||
                                 strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
 | 
			
		||||
                                 }
 | 
			
		||||
                                 break;
 | 
			
		||||
                            default: ;
 | 
			
		||||
                            }
 | 
			
		||||
                          delete d;
 | 
			
		||||
                          }
 | 
			
		||||
                      if (dpid) {
 | 
			
		||||
                         if (NumDpids < MAXDPIDS) {
 | 
			
		||||
                            cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang);
 | 
			
		||||
                            NumDpids++;
 | 
			
		||||
                            }
 | 
			
		||||
                         }
 | 
			
		||||
                      }
 | 
			
		||||
                      break;
 | 
			
		||||
           }
 | 
			
		||||
         dbgpatpmt("\n");
 | 
			
		||||
         cDevice::PrimaryDevice()->EnsureAudioTrack(true);
 | 
			
		||||
         cDevice::PrimaryDevice()->EnsureSubtitleTrack();
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
  else
 | 
			
		||||
     esyslog("ERROR: can't parse PMT");
 | 
			
		||||
  pmtSize = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cTsToPes --------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cTsToPes::cTsToPes(void)
 | 
			
		||||
{
 | 
			
		||||
  data = NULL;
 | 
			
		||||
  size = length = 0;
 | 
			
		||||
  synced = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cTsToPes::~cTsToPes()
 | 
			
		||||
{
 | 
			
		||||
  free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cTsToPes::PutTs(const uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (TsPayloadStart(Data))
 | 
			
		||||
     Reset();
 | 
			
		||||
  else if (!size)
 | 
			
		||||
     return; // skip everything before the first payload start
 | 
			
		||||
  Length = TsGetPayload(&Data);
 | 
			
		||||
  if (length + Length > size) {
 | 
			
		||||
     size = max(KILOBYTE(2), length + Length);
 | 
			
		||||
     data = (uchar *)realloc(data, size);
 | 
			
		||||
     }
 | 
			
		||||
  memcpy(data + length, Data, Length);
 | 
			
		||||
  length += Length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uchar *cTsToPes::GetPes(int &Length)
 | 
			
		||||
{
 | 
			
		||||
  if (PesLongEnough(length)) {
 | 
			
		||||
     Length = PesLength(data);
 | 
			
		||||
     if (Length <= length) {
 | 
			
		||||
        Length = length; // in case the PES packet has no explicit length, as is the case for video PES
 | 
			
		||||
        return data;
 | 
			
		||||
        }
 | 
			
		||||
     }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cTsToPes::Reset(void)
 | 
			
		||||
{
 | 
			
		||||
  length = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Some helper functions for debugging -----------------------------------
 | 
			
		||||
 | 
			
		||||
void BlockDump(const char *Name, const u_char *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  printf("--- %s\n", Name);
 | 
			
		||||
  for (int i = 0; i < Length; i++) {
 | 
			
		||||
      if (i && (i % 16) == 0)
 | 
			
		||||
         printf("\n");
 | 
			
		||||
      printf(" %02X", Data[i]);
 | 
			
		||||
      }
 | 
			
		||||
  printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TsDump(const char *Name, const u_char *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  printf("%s: %04X", Name, Length);
 | 
			
		||||
  int n = min(Length, 20);
 | 
			
		||||
  for (int i = 0; i < n; i++)
 | 
			
		||||
      printf(" %02X", Data[i]);
 | 
			
		||||
  if (n < Length) {
 | 
			
		||||
     printf(" ...");
 | 
			
		||||
     n = max(n, Length - 10);
 | 
			
		||||
     for (n = max(n, Length - 10); n < Length; n++)
 | 
			
		||||
         printf(" %02X", Data[n]);
 | 
			
		||||
     }
 | 
			
		||||
  printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PesDump(const char *Name, const u_char *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  TsDump(Name, Data, Length);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								remux.h
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								remux.h
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: remux.h 1.17 2007/09/02 10:19:06 kls Exp $
 | 
			
		||||
 * $Id: remux.h 2.1 2008/08/15 14:09:16 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __REMUX_H
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
 | 
			
		||||
#include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!!
 | 
			
		||||
#include <linux/dvb/dmx.h>
 | 
			
		||||
#include "channels.h"
 | 
			
		||||
#include "ringbuffer.h"
 | 
			
		||||
#include "tools.h"
 | 
			
		||||
 | 
			
		||||
@@ -81,4 +82,192 @@ public:
 | 
			
		||||
  static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
// Some TS handling tools.
 | 
			
		||||
// The following functions all take a pointer to one complete TS packet.
 | 
			
		||||
 | 
			
		||||
#define TS_SYNC_BYTE          0x47
 | 
			
		||||
#define TS_SIZE               188
 | 
			
		||||
#define TS_ADAPT_FIELD_EXISTS 0x20
 | 
			
		||||
#define TS_PAYLOAD_EXISTS     0x10
 | 
			
		||||
#define TS_CONT_CNT_MASK      0x0F
 | 
			
		||||
#define TS_PAYLOAD_START      0x40
 | 
			
		||||
#define TS_ERROR              0x80
 | 
			
		||||
#define TS_PID_MASK_HI        0x1F
 | 
			
		||||
 | 
			
		||||
inline int TsHasPayload(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return p[3] & TS_PAYLOAD_EXISTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsPayloadStart(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return p[1] & TS_PAYLOAD_START;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsError(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return p[1] & TS_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsPid(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsPayloadOffset(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsGetPayload(const uchar **p)
 | 
			
		||||
{
 | 
			
		||||
  int o = TsPayloadOffset(*p);
 | 
			
		||||
  *p += o;
 | 
			
		||||
  return TS_SIZE - o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int TsContinuityCounter(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return p[3] & TS_CONT_CNT_MASK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Some PES handling tools:
 | 
			
		||||
// The following functions that take a pointer to PES data all assume that
 | 
			
		||||
// there is enough data so that PesLongEnough() returns true.
 | 
			
		||||
 | 
			
		||||
inline bool PesLongEnough(int Length)
 | 
			
		||||
{
 | 
			
		||||
  return Length >= 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int PesLength(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return 6 + p[4] * 256 + p[5];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int PesPayloadOffset(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  return 9 + p[8];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int64_t PesGetPts(const uchar *p)
 | 
			
		||||
{
 | 
			
		||||
  if ((p[7] & 0x80) && p[8] >= 5) {
 | 
			
		||||
     return ((((int64_t)p[ 9]) & 0x0E) << 29) |
 | 
			
		||||
            (( (int64_t)p[10])         << 22) |
 | 
			
		||||
            ((((int64_t)p[11]) & 0xFE) << 14) |
 | 
			
		||||
            (( (int64_t)p[12])         <<  7) |
 | 
			
		||||
            ((((int64_t)p[13]) & 0xFE) >>  1);
 | 
			
		||||
     }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PAT/PMT Generator:
 | 
			
		||||
 | 
			
		||||
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
 | 
			
		||||
#define MAX_PMT_TS  (MAX_SECTION_SIZE / TS_SIZE + 1)
 | 
			
		||||
 | 
			
		||||
class cPatPmtGenerator {
 | 
			
		||||
private:
 | 
			
		||||
  uchar pat[TS_SIZE]; // the PAT always fits into a single TS packet
 | 
			
		||||
  uchar pmt[MAX_PMT_TS][TS_SIZE]; // the PMT may well extend over several TS packets
 | 
			
		||||
  int numPmtPackets;
 | 
			
		||||
  int patCounter;
 | 
			
		||||
  int pmtCounter;
 | 
			
		||||
  int patVersion;
 | 
			
		||||
  int pmtVersion;
 | 
			
		||||
  uchar *esInfoLength;
 | 
			
		||||
  void IncCounter(int &Counter, uchar *TsPacket);
 | 
			
		||||
  void IncVersion(int &Version);
 | 
			
		||||
  void IncEsInfoLength(int Length);
 | 
			
		||||
protected:
 | 
			
		||||
  int MakeStream(uchar *Target, uchar Type, int Pid);
 | 
			
		||||
  int MakeAC3Descriptor(uchar *Target);
 | 
			
		||||
  int MakeSubtitlingDescriptor(uchar *Target, const char *Language);
 | 
			
		||||
  int MakeLanguageDescriptor(uchar *Target, const char *Language);
 | 
			
		||||
  int MakeCRC(uchar *Target, const uchar *Data, int Length);
 | 
			
		||||
public:
 | 
			
		||||
  cPatPmtGenerator(void);
 | 
			
		||||
  void GeneratePat(void);
 | 
			
		||||
       ///< Generates a PAT section for later use with GetPat().
 | 
			
		||||
       ///< This function is called by default from the constructor.
 | 
			
		||||
  void GeneratePmt(tChannelID ChannelID);
 | 
			
		||||
       ///< Generates a PMT section for the given ChannelId, for later use
 | 
			
		||||
       ///< with GetPmt().
 | 
			
		||||
  uchar *GetPat(void);
 | 
			
		||||
       ///< Returns a pointer to the PAT section, which consist of exactly
 | 
			
		||||
       ///< one TS packet.
 | 
			
		||||
  uchar *GetPmt(int &Index);
 | 
			
		||||
       ///< Returns a pointer to the Index'th TS packet of the PMT section.
 | 
			
		||||
       ///< Index must be initialized to 0 and will be incremented by each
 | 
			
		||||
       ///< call to GetPmt(). Returns NULL is all packets of the PMT section
 | 
			
		||||
       ///< have been fetched..
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
// PAT/PMT Parser:
 | 
			
		||||
 | 
			
		||||
class cPatPmtParser {
 | 
			
		||||
private:
 | 
			
		||||
  uchar pmt[MAX_SECTION_SIZE];
 | 
			
		||||
  int pmtSize;
 | 
			
		||||
  int pmtPid;
 | 
			
		||||
  int vpid;
 | 
			
		||||
  int vtype;
 | 
			
		||||
protected:
 | 
			
		||||
  int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
 | 
			
		||||
public:
 | 
			
		||||
  cPatPmtParser(void);
 | 
			
		||||
  void ParsePat(const uchar *Data, int Length);
 | 
			
		||||
       ///< Parses the given PAT Data, which is the payload of a single TS packet
 | 
			
		||||
       ///< from the PAT stream. The PAT may consist only of a single TS packet.
 | 
			
		||||
  void ParsePmt(const uchar *Data, int Length);
 | 
			
		||||
       ///< Parses the given PMT Data, which is the payload of a single TS packet
 | 
			
		||||
       ///< from the PMT stream. The PMT may consist of several TS packets, which
 | 
			
		||||
       ///< are delivered to the parser through several subsequent calls to
 | 
			
		||||
       ///< ParsePmt(). The whole PMT data will be processed once the last packet
 | 
			
		||||
       ///< has been received.
 | 
			
		||||
  int PmtPid(void) { return pmtPid; }
 | 
			
		||||
       ///< Returns the PMT pid as defined by the current PAT.
 | 
			
		||||
       ///< If no PAT has been received yet, -1 will be returned.
 | 
			
		||||
  int Vpid(void) { return vpid; }
 | 
			
		||||
       ///< Returns the video pid as defined by the current PMT.
 | 
			
		||||
  int Vtype(void) { return vtype; }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
// TS to PES converter:
 | 
			
		||||
// Puts together the payload of several TS packets that form one PES
 | 
			
		||||
// packet.
 | 
			
		||||
 | 
			
		||||
class cTsToPes {
 | 
			
		||||
private:
 | 
			
		||||
  uchar *data;
 | 
			
		||||
  int size;
 | 
			
		||||
  int length;
 | 
			
		||||
  bool synced;
 | 
			
		||||
public:
 | 
			
		||||
  cTsToPes(void);
 | 
			
		||||
  ~cTsToPes();
 | 
			
		||||
  void PutTs(const uchar *Data, int Length);
 | 
			
		||||
       ///< Puts the payload data of the single TS packet at Data into the converter.
 | 
			
		||||
       ///< Length is always 188.
 | 
			
		||||
       ///< If the given TS packet starts a new PES payload packet, the converter
 | 
			
		||||
       ///< will be automatically reset. Any packets before the first one that starts
 | 
			
		||||
       ///< a new PES payload packet will be ignored.
 | 
			
		||||
  const uchar *GetPes(int &Length);
 | 
			
		||||
       ///< Gets a pointer to the complete PES packet, or NULL if the packet
 | 
			
		||||
       ///< is not complete yet. If the packet is complete, Length will contain
 | 
			
		||||
       ///< the total packet length. The returned pointer is only valid until
 | 
			
		||||
       ///< the next call to PutTs() or Reset(), or until this object is destroyed.
 | 
			
		||||
  void Reset(void);
 | 
			
		||||
       ///< Resets the converter. This needs to be called after a PES packet has
 | 
			
		||||
       ///< been fetched by a call to GetPes(), and before the next call to
 | 
			
		||||
       ///< PutTs().
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
// Some helper functions for debugging:
 | 
			
		||||
 | 
			
		||||
void BlockDump(const char *Name, const u_char *Data, int Length);
 | 
			
		||||
void TsDump(const char *Name, const u_char *Data, int Length);
 | 
			
		||||
void PesDump(const char *Name, const u_char *Data, int Length);
 | 
			
		||||
 | 
			
		||||
#endif // __REMUX_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								transfer.c
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								transfer.c
									
									
									
									
									
								
							@@ -4,109 +4,52 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: transfer.c 1.34 2007/01/07 14:45:28 kls Exp $
 | 
			
		||||
 * $Id: transfer.c 2.1 2008/08/15 14:32:12 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "transfer.h"
 | 
			
		||||
 | 
			
		||||
#define TRANSFERBUFSIZE  MEGABYTE(2)
 | 
			
		||||
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6
 | 
			
		||||
 | 
			
		||||
// --- cTransfer -------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
 | 
			
		||||
:cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
 | 
			
		||||
,cThread("transfer")
 | 
			
		||||
{
 | 
			
		||||
  ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
 | 
			
		||||
  remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids);
 | 
			
		||||
  patPmtGenerator.GeneratePmt(ChannelID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cTransfer::~cTransfer()
 | 
			
		||||
{
 | 
			
		||||
  cReceiver::Detach();
 | 
			
		||||
  cPlayer::Detach();
 | 
			
		||||
  delete remux;
 | 
			
		||||
  delete ringBuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cTransfer::Activate(bool On)
 | 
			
		||||
{
 | 
			
		||||
  if (On)
 | 
			
		||||
     Start();
 | 
			
		||||
  else {
 | 
			
		||||
     Cancel(3);
 | 
			
		||||
     cPlayer::Detach();
 | 
			
		||||
  if (On) {
 | 
			
		||||
     PlayTs(patPmtGenerator.GetPat(), TS_SIZE);
 | 
			
		||||
     int Index = 0;
 | 
			
		||||
     while (uchar *pmt = patPmtGenerator.GetPmt(Index))
 | 
			
		||||
           PlayTs(pmt, TS_SIZE);
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cTransfer::Receive(uchar *Data, int Length)
 | 
			
		||||
{
 | 
			
		||||
  if (cPlayer::IsAttached() && Running()) {
 | 
			
		||||
     int p = ringBuffer->Put(Data, Length);
 | 
			
		||||
     if (p != Length && Running())
 | 
			
		||||
        ringBuffer->ReportOverflow(Length - p);
 | 
			
		||||
  if (cPlayer::IsAttached()) {
 | 
			
		||||
     // Transfer Mode means "live tv", so there's no point in doing any additional
 | 
			
		||||
     // buffering here. The TS packets *must* get through here! However, every
 | 
			
		||||
     // now and then there may be conditions where the packet just can't be
 | 
			
		||||
     // handled when offered the first time, so that's why we try several times:
 | 
			
		||||
     for (int i = 0; i < 100; i++) {
 | 
			
		||||
         if (PlayTs(Data, Length) > 0)
 | 
			
		||||
            return;
 | 
			
		||||
         fprintf(stderr, "-");//XXX just for testing - remove when stable
 | 
			
		||||
         cCondWait::SleepMs(10);
 | 
			
		||||
         }
 | 
			
		||||
     esyslog("ERROR: TS packet not accepted in Transfer Mode");
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cTransfer::Action(void)
 | 
			
		||||
{
 | 
			
		||||
  int PollTimeouts = 0;
 | 
			
		||||
  uchar *p = NULL;
 | 
			
		||||
  int Result = 0;
 | 
			
		||||
  while (Running()) {
 | 
			
		||||
        int Count;
 | 
			
		||||
        uchar *b = ringBuffer->Get(Count);
 | 
			
		||||
        if (b) {
 | 
			
		||||
           if (ringBuffer->Available() > TRANSFERBUFSIZE * 9 / 10) {
 | 
			
		||||
              // If the buffer runs full, we have no chance of ever catching up
 | 
			
		||||
              // since the data comes in at the same rate as it goes out (it's "live").
 | 
			
		||||
              // So let's clear the buffer instead of suffering from permanent
 | 
			
		||||
              // overflows.
 | 
			
		||||
              dsyslog("clearing transfer buffer to avoid overflows");
 | 
			
		||||
              DeviceClear();
 | 
			
		||||
              ringBuffer->Clear();
 | 
			
		||||
              remux->Clear();
 | 
			
		||||
              PlayPes(NULL, 0);
 | 
			
		||||
              p = NULL;
 | 
			
		||||
              continue;
 | 
			
		||||
              }
 | 
			
		||||
           Count = remux->Put(b, Count);
 | 
			
		||||
           if (Count)
 | 
			
		||||
              ringBuffer->Del(Count);
 | 
			
		||||
           }
 | 
			
		||||
        if (!p)
 | 
			
		||||
           p = remux->Get(Result);
 | 
			
		||||
        if (p) {
 | 
			
		||||
           cPoller Poller;
 | 
			
		||||
           if (DevicePoll(Poller, 100)) {
 | 
			
		||||
              PollTimeouts = 0;
 | 
			
		||||
              int w = PlayPes(p, Result);
 | 
			
		||||
              if (w > 0) {
 | 
			
		||||
                 p += w;
 | 
			
		||||
                 Result -= w;
 | 
			
		||||
                 remux->Del(w);
 | 
			
		||||
                 if (Result <= 0)
 | 
			
		||||
                    p = NULL;
 | 
			
		||||
                 }
 | 
			
		||||
              else if (w < 0 && FATALERRNO)
 | 
			
		||||
                 LOG_ERROR;
 | 
			
		||||
              }
 | 
			
		||||
           else {
 | 
			
		||||
              PollTimeouts++;
 | 
			
		||||
              if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
 | 
			
		||||
                 dsyslog("clearing device because of consecutive poll timeouts");
 | 
			
		||||
                 DeviceClear();
 | 
			
		||||
                 ringBuffer->Clear();
 | 
			
		||||
                 remux->Clear();
 | 
			
		||||
                 PlayPes(NULL, 0);
 | 
			
		||||
                 p = NULL;
 | 
			
		||||
                 }
 | 
			
		||||
              }
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- cTransferControl ------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cDevice *cTransferControl::receiverDevice = NULL;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								transfer.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								transfer.h
									
									
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
 * See the main source file 'vdr.c' for copyright information and
 | 
			
		||||
 * how to reach the author.
 | 
			
		||||
 *
 | 
			
		||||
 * $Id: transfer.h 1.12 2007/01/07 14:45:45 kls Exp $
 | 
			
		||||
 * $Id: transfer.h 2.1 2008/05/25 12:44:49 kls Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __TRANSFER_H
 | 
			
		||||
@@ -13,17 +13,13 @@
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "receiver.h"
 | 
			
		||||
#include "remux.h"
 | 
			
		||||
#include "ringbuffer.h"
 | 
			
		||||
#include "thread.h"
 | 
			
		||||
 | 
			
		||||
class cTransfer : public cReceiver, public cPlayer, public cThread {
 | 
			
		||||
class cTransfer : public cReceiver, public cPlayer {
 | 
			
		||||
private:
 | 
			
		||||
  cRingBufferLinear *ringBuffer;
 | 
			
		||||
  cRemux *remux;
 | 
			
		||||
  cPatPmtGenerator patPmtGenerator;
 | 
			
		||||
protected:
 | 
			
		||||
  virtual void Activate(bool On);
 | 
			
		||||
  virtual void Receive(uchar *Data, int Length);
 | 
			
		||||
  virtual void Action(void);
 | 
			
		||||
public:
 | 
			
		||||
  cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
 | 
			
		||||
  virtual ~cTransfer();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								vdr.5
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								vdr.5
									
									
									
									
									
								
							@@ -8,7 +8,7 @@
 | 
			
		||||
.\" License as specified in the file COPYING that comes with the
 | 
			
		||||
.\" vdr distribution.
 | 
			
		||||
.\"
 | 
			
		||||
.\" $Id: vdr.5 2.3 2008/05/02 13:48:31 kls Exp $
 | 
			
		||||
.\" $Id: vdr.5 2.4 2008/07/06 13:00:19 kls Exp $
 | 
			
		||||
.\"
 | 
			
		||||
.TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files"
 | 
			
		||||
.SH NAME
 | 
			
		||||
@@ -126,7 +126,13 @@ The symbol rate of this channel (DVB-S and DVB-C only).
 | 
			
		||||
The video PID (set to '0' for radio channels).
 | 
			
		||||
If this channel uses a separate PCR PID, it follows the VPID, separated by a
 | 
			
		||||
plus sign, as in
 | 
			
		||||
 | 
			
		||||
.B ...:164+17:...
 | 
			
		||||
 | 
			
		||||
If this channel has a video mode other than 0, the mode
 | 
			
		||||
follows the pids, separated by an '=' sign, as in
 | 
			
		||||
 | 
			
		||||
.B ...:164+17=27:...
 | 
			
		||||
.TP
 | 
			
		||||
.B APID
 | 
			
		||||
The audio PID (either one number, or several, separated by commas).
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user