[ANNOUNCE] H.264 updates for VDR-1.5.9

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

the attached vdr-1.5.9-h264.patch adds H.264 support to VDR's remuxer.
The changes to earlier releases are:

- H264::cParser has been enhanced to provide information for
  H264::cContext::GetFramesPerSec() and therefore outsourced
  into separate files.
- cVideoRepacker generates Access Unit Delimiters in case they
  are not part of the stream.

These changes should make VDR ready for the upcoming IFA fair in regard
to the broadcasts on the temporary channel EinsFestival HD.

The other attached patch vdr-1.5.9-framespersec.patch tries to replace
the macro FRAMESPERSEC by a function call GetFramesPerSec() which
analyses a recordings first video frame to determine frames per second
which are used to calculated a recordings length. The changes to earlier
releases are:

- add support for H.264 recordings by making use of the above
  mentioned H264::cParser, as frame timing stuff is quite complex.
  H.264 support can be turned off by putting a comment around the
  macro ADD_H264_SUPPORT in tools.c.

This change provides correct recording length reports for recordings
taken on EinsFestival HD as this channel broadcasts 50p, i. e. without
the patch, VDR will report recording times which are twice the actual time.

The patch in general may especially be useful for people with PAL and
NTSC recordings, as in this scenario it was never possible to find a
single setting for FRAMESPERSEC.
Although FRAMESPERSEC has been replaced on many locations, it is still
used to read/write cutting marks file, to stay compatible.

Have fun while expecting the next vdr-xine-release ;-)

Bye.
-- 
Dipl.-Inform. (FH) Reinhard Nissl
mailto:rnissl@xxxxxx
--- ./Makefile	2007-08-25 10:52:17.000000000 +0200
+++ ../vdr-1.5.9/./Makefile	2007-08-28 22:18:27.000000000 +0200
@@ -41,7 +41,7 @@ OBJS = audio.o channels.o ci.o config.o 
        lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
        receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
        skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
-       timers.o tools.o transfer.o vdr.o videodir.o
+       timers.o tools.o transfer.o vdr.o videodir.o h264parser.o
 
 ifndef NO_KBD
 DEFINES += -DREMOTE_KBD
--- ./channels.h	2007-07-21 16:58:36.000000000 +0200
+++ ../vdr-1.5.9/./channels.h	2007-07-22 21:33:47.000000000 +0200
@@ -47,6 +47,16 @@
 #define CA_ENCRYPTED_MIN 0x0100
 #define CA_ENCRYPTED_MAX 0xFFFF
 
+// VPID can be in the range 0...8191. Offsets of 10000 are used to indicate special video codings.
+#define VPID_OFFSET_BASE         10000
+#define VPID_FROM_ANY(pid)       ((pid) % VPID_OFFSET_BASE) // returns the plain VPID
+#define VPID_TO_XXX(pid, offset) (pid + offset)
+#define VPID_IS_XXX(pid, offset) ((pid - VPID_FROM_ANY(pid)) == offset)
+// 1. special video coding: H.264
+#define VPID_OFFSET_H264         (1 * VPID_OFFSET_BASE)
+#define VPID_TO_H264(pid)        VPID_TO_XXX(pid, VPID_OFFSET_H264)
+#define VPID_IS_H264(pid)        VPID_IS_XXX(pid, VPID_OFFSET_H264)
+
 struct tChannelParameterMap {
   int userValue;
   int driverValue;
--- ./menu.c	2007-08-24 15:15:48.000000000 +0200
+++ ../vdr-1.5.9/./menu.c	2007-08-28 22:25:08.000000000 +0200
@@ -218,6 +218,7 @@ class cMenuEditChannel : public cOsdMenu
 private:
   cChannel *channel;
   cChannel data;
+  int vcodec;
   char name[256];
   void Setup(void);
 public:
@@ -231,6 +232,9 @@ cMenuEditChannel::cMenuEditChannel(cChan
   channel = Channel;
   if (channel) {
      data = *channel;
+     vcodec = data.vpid / VPID_OFFSET_BASE;
+     data.vpid = VPID_FROM_ANY(data.vpid);
+
      if (New) {
         channel = NULL;
         data.nid = 0;
@@ -249,20 +253,26 @@ void cMenuEditChannel::Setup(void)
 
   Clear();
 
+  static const char *VideoCodecValues[] = {
+    tr("MPEG1/2"),
+    tr("H.264")
+    };
+
   // Parameters for all types of sources:
   strn0cpy(name, data.name, sizeof(name));
   Add(new cMenuEditStrItem( tr("Name"),          name, sizeof(name), tr(FileNameChars)));
   Add(new cMenuEditSrcItem( tr("Source"),       &data.source));
   Add(new cMenuEditIntItem( tr("Frequency"),    &data.frequency));
-  Add(new cMenuEditIntItem( tr("Vpid"),         &data.vpid,  0, 0x1FFF));
-  Add(new cMenuEditIntItem( tr("Ppid"),         &data.ppid,  0, 0x1FFF));
+  Add(new cMenuEditIntItem( tr("Vpid"),         &data.vpid,     0, 0x1FFF));
+  Add(new cMenuEditStraItem( tr("Vcodec"),      &vcodec, sizeof(VideoCodecValues) / sizeof(*VideoCodecValues), VideoCodecValues));
+  Add(new cMenuEditIntItem( tr("Ppid"),         &data.ppid,     0, 0x1FFF));
   Add(new cMenuEditIntItem( tr("Apid1"),        &data.apids[0], 0, 0x1FFF));
   Add(new cMenuEditIntItem( tr("Apid2"),        &data.apids[1], 0, 0x1FFF));
   Add(new cMenuEditIntItem( tr("Dpid1"),        &data.dpids[0], 0, 0x1FFF));
   Add(new cMenuEditIntItem( tr("Dpid2"),        &data.dpids[1], 0, 0x1FFF));
-  Add(new cMenuEditIntItem( tr("Tpid"),         &data.tpid,  0, 0x1FFF));
+  Add(new cMenuEditIntItem( tr("Tpid"),         &data.tpid,     0, 0x1FFF));
   Add(new cMenuEditCaItem(  tr("CA"),           &data.caids[0]));
-  Add(new cMenuEditIntItem( tr("Sid"),          &data.sid, 1, 0xFFFF));
+  Add(new cMenuEditIntItem( tr("Sid"),          &data.sid,      1, 0xFFFF));
   /* XXX not yet used
   Add(new cMenuEditIntItem( tr("Nid"),          &data.nid, 0));
   Add(new cMenuEditIntItem( tr("Tid"),          &data.tid, 0));
@@ -294,12 +304,14 @@ eOSState cMenuEditChannel::ProcessKey(eK
         if (Channels.HasUniqueChannelID(&data, channel)) {
            data.name = strcpyrealloc(data.name, name);
            if (channel) {
+              data.vpid += vcodec * VPID_OFFSET_BASE; 
               *channel = data;
               isyslog("edited channel %d %s", channel->Number(), *data.ToText());
               state = osBack;
               }
            else {
               channel = new cChannel;
+              data.vpid += vcodec * VPID_OFFSET_BASE; 
               *channel = data;
               Channels.Add(channel);
               Channels.ReNumber();
--- ./pat.c	2007-01-05 11:41:55.000000000 +0100
+++ ../vdr-1.5.9/./pat.c	2007-05-26 01:30:29.000000000 +0200
@@ -408,6 +408,9 @@ void cPatFilter::Process(u_short Pid, u_
                          }
                       }
                       break;
+              case 0x1b: //MPEG4
+                      Vpid = VPID_TO_H264(stream.getPid());
+                      break;
               //default: printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number());//XXX
               }
             for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
--- ./recorder.c	2007-02-24 17:36:24.000000000 +0100
+++ ../vdr-1.5.9/./recorder.c	2007-02-25 19:22:13.000000000 +0100
@@ -127,7 +127,7 @@ void cFileWriter::Action(void)
 // --- cRecorder -------------------------------------------------------------
 
 cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
-:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
+:cReceiver(ChannelID, Priority, VPID_FROM_ANY(VPid), APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
 ,cThread("recording")
 {
   // Make sure the disk is up and running:
--- ./remux.c	2007-02-24 17:36:10.000000000 +0100
+++ ../vdr-1.5.9/./remux.c	2007-08-28 22:07:37.000000000 +0200
@@ -19,6 +19,8 @@
 #include "channels.h"
 #include "shutdown.h"
 #include "tools.h"
+#include "recording.h"
+#include "h264parser.h"
 
 ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
 {
@@ -100,8 +102,9 @@ protected:
   int suppressedLogMessages;
   bool LogAllowed(void);
   void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); }
+  virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
 public:
-  static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+  static int PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
   cRepacker(void);
   virtual ~cRepacker() {}
   virtual void Reset(void) { initiallySyncing = true; }
@@ -138,6 +141,11 @@ bool cRepacker::LogAllowed(void)
 
 int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
 {
+  return PutAllOrNothing(ResultBuffer, Data, Count, CapacityNeeded);
+}
+
+int cRepacker::PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
+{
   if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
      esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
      return 0;
@@ -156,7 +164,7 @@ protected:
   int packetTodo;
   uchar fragmentData[6 + 65535 + 3];
   int fragmentLen;
-  uchar pesHeader[6 + 3 + 255 + 3];
+  uchar pesHeader[6 + 3 + 255 + 5 + 3]; // 5: H.264 AUD
   int pesHeaderLen;
   uchar pesHeaderBackup[6 + 3 + 255];
   int pesHeaderBackupLen;
@@ -164,7 +172,7 @@ protected:
   uint32_t localScanner;
   int localStart;
   bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
-  virtual int QuerySnoopSize() { return 4; }
+  virtual int QuerySnoopSize(void) { return 4; }
   virtual void Reset(void);
   };
 
@@ -238,6 +246,132 @@ bool cCommonRepacker::PushOutPacket(cRin
   return true;
 }
 
+// --- cAudGenerator ---------------------------------------------------------
+
+class cAudGenerator {
+private:
+  H264::cSimpleBuffer buffer;
+  int overflowByteCount;
+  H264::cSliceHeader::eAccessUnitType accessUnitType;
+  int sliceTypes;
+public:
+  cAudGenerator(void);
+  void CollectSliceType(const H264::cSliceHeader *SH);
+  int CollectData(const uchar *Data, int Count);
+  void Generate(cRingBufferLinear *const ResultBuffer);
+};
+
+cAudGenerator::cAudGenerator()
+  : buffer(MAXFRAMESIZE)
+{
+  overflowByteCount = 0;
+  accessUnitType = H264::cSliceHeader::Frame;
+  sliceTypes = 0;
+}
+
+int cAudGenerator::CollectData(const uchar *Data, int Count)
+{
+  // buffer frame data until AUD can be generated
+  int n = buffer.Put(Data, Count);
+  overflowByteCount += (Count - n);
+  // always report "success" as an error message will be shown in Generate()
+  return Count;
+}
+
+void cAudGenerator::CollectSliceType(const H264::cSliceHeader *SH)
+{
+  if (!SH)
+     return;
+  // remember type of current access unit 
+  accessUnitType = SH->GetAccessUnitType();
+  // translate slice_type into part of primary_pic_type and merge them
+  switch (SH->slice_type) {
+    case 2: // I
+    case 7: // I only => I 
+         sliceTypes |= 0x10000;
+         break;
+    case 0: // P
+    case 5: // P only => I, P
+         sliceTypes |= 0x11000;
+         break;
+    case 1: // B
+    case 6: // B only => I, P, B
+         sliceTypes |= 0x11100;
+         break;
+    case 4: // SI
+    case 9: // SI only => SI
+         sliceTypes |= 0x00010;
+         break;
+    case 3: // SP
+    case 8: // SP only => SI, SP
+         sliceTypes |= 0x00011;
+         break;
+    }
+}
+
+void cAudGenerator::Generate(cRingBufferLinear *const ResultBuffer)
+{
+  int primary_pic_type;
+  // translate the merged primary_pic_type parts into primary_pic_type
+  switch (sliceTypes) {
+    case 0x10000: // I
+         primary_pic_type = 0;
+         break;
+    case 0x11000: // I, P
+         primary_pic_type = 1;
+         break;
+    case 0x11100: // I, P, B
+         primary_pic_type = 2;
+         break;
+    case 0x00010: // SI
+         primary_pic_type = 3;
+         break;
+    case 0x00011: // SI, SP
+         primary_pic_type = 4;
+         break;
+    case 0x10010: // I, SI
+         primary_pic_type = 5;
+         break;
+    case 0x11011: // I, SI, P, SP
+    case 0x10011: // I, SI, SP
+    case 0x11010: // I, SI, P
+         primary_pic_type = 6;
+         break;
+    case 0x11111: // I, SI, P, SP, B
+    case 0x11110: // I, SI, P, B
+         primary_pic_type = 7;
+         break;
+    default:
+         primary_pic_type = -1; // frame without slices?
+    }
+  // drop an incorrect frame
+  if (primary_pic_type < 0)
+     esyslog("ERROR: cAudGenerator::Generate(): dropping frame without slices");
+  else {
+     // drop a partitial frame
+     if (overflowByteCount > 0) 
+        esyslog("ERROR: cAudGenerator::Generate(): frame exceeds MAXFRAMESIZE bytes (required size: %d bytes), dropping frame", buffer.Size() + overflowByteCount);
+     else {
+        int Count;
+        uchar *Data = buffer.Get(Count);
+        int PesPayloadOffset = 0;
+        AnalyzePesHeader(Data, Count, PesPayloadOffset);
+        // enter primary_pic_type into AUD
+        Data[ PesPayloadOffset + 4 ] |= primary_pic_type << 5;
+        // mangle the "start code" to pass the information that this access unit is a
+        // bottom field to ScanVideoPacket() where this modification will be reverted.
+        if (accessUnitType == H264::cSliceHeader::BottomField)
+           Data[ PesPayloadOffset + 3 ] |= 0x80;
+        // store the buffered frame
+        cRepacker::PutAllOrNothing(ResultBuffer, Data, Count, Count);
+        }
+     }
+  // prepare for next run
+  buffer.Clear();
+  overflowByteCount = 0;
+  sliceTypes = 0;
+}
+
 // --- cVideoRepacker --------------------------------------------------------
 
 class cVideoRepacker : public cCommonRepacker {
@@ -248,6 +382,13 @@ private:
     scanPicture
     };
   int state;
+  H264::cParser *h264Parser;
+  int sliceSeen;
+  bool audSeen;
+  cAudGenerator *audGenerator;
+  void CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
+  void PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
+  void HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload);
   void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
   inline bool ScanDataForStartCodeSlow(const uchar *const Data);
   inline bool ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit);
@@ -256,30 +397,155 @@ private:
   inline bool ScanForEndOfPictureSlow(const uchar *&Data);
   inline bool ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit);
   inline bool ScanForEndOfPicture(const uchar *&Data, const uchar *Limit);
+  void CollectNalUnitData(const uchar *Data, int Count);
+protected:
+  virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
 public:
-  cVideoRepacker(void);
+  cVideoRepacker(bool H264);
+  ~cVideoRepacker();
   virtual void Reset(void);
   virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
   virtual int BreakAt(const uchar *Data, int Count);
   };
 
-cVideoRepacker::cVideoRepacker(void)
+cVideoRepacker::cVideoRepacker(bool H264)
 {
+  h264Parser = (H264 ? new H264::cParser() : 0);
+  audGenerator = 0;
   Reset();
 }
 
+cVideoRepacker::~cVideoRepacker()
+{
+  delete h264Parser;
+  delete audGenerator;
+}
+
 void cVideoRepacker::Reset(void)
 {
   cCommonRepacker::Reset();
+  if (h264Parser)
+     h264Parser->Reset();
   scanner = 0xFFFFFFFF;
   state = syncing;
+  sliceSeen = -1;
+  audSeen = false;
+  delete audGenerator;
+  audGenerator = 0;
 }
 
-void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
 {
-  // synchronisation is detected some bytes after frame start.
-  const int SkippedBytesLimit = 4;
+  if (!audGenerator)
+     return cCommonRepacker::Put(ResultBuffer, Data, Count, CapacityNeeded);
+
+  return audGenerator->CollectData(Data, Count);
+}
 
+void cVideoRepacker::CollectNalUnitData(const uchar *Data, int Count)
+{
+  if (h264Parser)
+     h264Parser->PutNalUnitData(Data, Count);
+}
+
+void cVideoRepacker::HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload)
+{
+  // valid NAL units start with a zero bit
+  if (*Data & 0x80) {
+     LOG("cVideoRepacker: found invalid NAL unit: stream seems to be scrambled or not demultiplexed");
+     return;
+     }
+
+  // collect NAL unit's remaining data and process it 
+  CollectNalUnitData(NalPayload, Data - 3 - NalPayload);
+  h264Parser->Process();
+
+  // collect 0x00 0x00 0x01 for current NAL unit
+  static const uchar InitPayload[3] = { 0x00, 0x00, 0x01 };
+  CollectNalUnitData(InitPayload, sizeof (InitPayload));
+  NalPayload = Data;
+
+  // which kind of NAL unit have we got?
+  const int nal_unit_type = *Data & 0x1F;
+  switch (nal_unit_type) {
+    case 1: // coded slice of a non-IDR picture
+    case 2: // coded slice data partition A
+    case 5: // coded slice of an IDR picture
+         CheckAudGeneration(true, false, Data, ResultBuffer, Payload, StreamID, MpegLevel);
+         break;
+    case 3: // coded slice data partition B
+    case 4: // coded slice data partition C
+    case 19: // coded slice of an auxiliary coded picture without partitioning
+         break;
+    case 6: // supplemental enhancement information (SEI)
+    case 7: // sequence parameter set
+    case 8: // picture parameter set
+    case 10: // end of sequence
+    case 11: // end of stream
+    case 13: // sequence parameter set extension
+         CheckAudGeneration(false, nal_unit_type == 7, Data, ResultBuffer, Payload, StreamID, MpegLevel);
+         break;
+    case 12: // filler data
+         break;
+    case 14 ... 18: // reserved
+         CheckAudGeneration(false, false, Data, ResultBuffer, Payload, StreamID, MpegLevel);
+    case 20 ... 23: // reserved
+         LOG("cVideoRepacker: found reserved NAL unit type: stream seems to be scrambled");
+         break;
+    case 0: // unspecified
+    case 24 ... 31: // unspecified
+         LOG("cVideoRepacker: found unspecified NAL unit type: stream seems to be scrambled");
+         break;
+    case 9: { // access unit delimiter
+         audSeen = true;
+         CheckAudGeneration(false, true, Data, ResultBuffer, Payload, StreamID, MpegLevel);
+         // mangle the "start code" to pass the information that the next access unit will be
+         // a bottom field to ScanVideoPacket() where this modification will be reverted.
+         const H264::cSliceHeader *SH = h264Parser->Context().CurrentSlice();
+         if (SH && SH->GetAccessUnitType() == H264::cSliceHeader::TopField)
+            *(uchar *)Data |= 0x80;
+         }
+         break;
+    }
+}
+
+void cVideoRepacker::CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+{
+  // we cannot generate anything until we have reached the synchronisation point
+  if (sliceSeen < 0 && !SyncPoint)
+     return;
+  // detect transition from slice to non-slice NAL units
+  const bool WasSliceSeen = (sliceSeen != false);
+  const bool IsSliceSeen = SliceNalUnitType;
+  sliceSeen = IsSliceSeen;
+  // collect slice types for AUD generation
+  if (WasSliceSeen && audGenerator)
+     audGenerator->CollectSliceType(h264Parser->Context().CurrentSlice());
+  // handle access unit delimiter at the transition from slice to non-slice NAL units
+  if (WasSliceSeen && !IsSliceSeen) {
+     // an Access Unit Delimiter indicates that the current picture is done. So let's
+     // push out the current frame to start a new packet for the next picture.
+     PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel);
+     if (state == findPicture) {
+        // go on with scanning the picture data
+        state++;
+        }
+     // generate the AUD and push out the buffered frame
+     if (audGenerator) {
+        audGenerator->Generate(ResultBuffer);
+        if (audSeen) {
+           // we nolonger need to generate AUDs as they are part of the stream
+           delete audGenerator;
+           audGenerator = 0;
+           }
+        }
+     else if (!audSeen) // we do need to generate AUDs
+        audGenerator = new cAudGenerator;
+     }
+}
+
+void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+{
   // which kind of start code have we got?
   switch (*Data) {
     case 0xB9 ... 0xFF: // system start codes
@@ -298,65 +564,9 @@ void cVideoRepacker::HandleStartCode(con
     case 0xB3: // sequence header code
     case 0xB8: // group start code
     case 0x00: // picture start code
-         if (state == scanPicture) {
-            // the above start codes indicate that the current picture is done. So
-            // push out the packet to start a new packet for the next picuture. If
-            // the byte count get's negative then the current buffer ends in a
-            // partitial start code that must be stripped off, as it shall be put
-            // in the next packet.
-            PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
-            // go on with syncing to the next picture
-            state = syncing;
-            }
-         if (state == syncing) {
-            if (initiallySyncing) // omit report for the typical initial case
-               initiallySyncing = false;
-            else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
-               LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
-            skippedBytes = 0;
-            // if there is a PES header available, then use it ...
-            if (pesHeaderBackupLen > 0) {
-               // ISO 13818-1 says:
-               // In the case of video, if a PTS is present in a PES packet header
-               // it shall refer to the access unit containing the first picture start
-               // code that commences in this PES packet. A picture start code commences
-               // in PES packet if the first byte of the picture start code is present
-               // in the PES packet.
-               memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
-               pesHeaderLen = pesHeaderBackupLen;
-               pesHeaderBackupLen = 0;
-               }
-            else {
-               // ... otherwise create a continuation PES header
-               pesHeaderLen = 0;
-               pesHeader[pesHeaderLen++] = 0x00;
-               pesHeader[pesHeaderLen++] = 0x00;
-               pesHeader[pesHeaderLen++] = 0x01;
-               pesHeader[pesHeaderLen++] = StreamID; // video stream ID
-               pesHeader[pesHeaderLen++] = 0x00; // length still unknown
-               pesHeader[pesHeaderLen++] = 0x00; // length still unknown
-
-               if (MpegLevel == phMPEG2) {
-                  pesHeader[pesHeaderLen++] = 0x80;
-                  pesHeader[pesHeaderLen++] = 0x00;
-                  pesHeader[pesHeaderLen++] = 0x00;
-                  }
-               else
-                  pesHeader[pesHeaderLen++] = 0x0F;
-               }
-            // append the first three bytes of the start code
-            pesHeader[pesHeaderLen++] = 0x00;
-            pesHeader[pesHeaderLen++] = 0x00;
-            pesHeader[pesHeaderLen++] = 0x01;
-            // the next packet's payload will begin with the fourth byte of
-            // the start code (= the actual code)
-            Payload = Data;
-            // as there is no length information available, assume the
-            // maximum we can hold in one PES packet
-            packetTodo = maxPacketSize - pesHeaderLen;
-            // go on with finding the picture data
-            state++;
-            }
+         // the above start codes indicate that the current picture is done. So let's
+         // push out the current frame to start a new packet for the next picture.
+         PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel);
          break;
     case 0x01 ... 0xAF: // slice start codes
          if (state == findPicture) {
@@ -367,6 +577,81 @@ void cVideoRepacker::HandleStartCode(con
     }
 }
 
+void cVideoRepacker::PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+{
+  // synchronisation is detected some bytes after frame start.
+  const int SkippedBytesLimit = 4;
+
+  if (state == scanPicture) {
+     // picture data has been found so let's push out the current frame.
+     // If the byte count get's negative then the current buffer ends in a
+     // partitial start code that must be stripped off, as it shall be put
+     // in the next packet.
+     PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
+     // go on with syncing to the next picture
+     state = syncing;
+     }
+  // when already synced to a picture, just go on collecting data 
+  if (state != syncing)
+     return;
+  // we're synced to a picture so prepare a new packet
+  if (initiallySyncing) // omit report for the typical initial case
+     initiallySyncing = false;
+  else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+     LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
+  skippedBytes = 0;
+  // if there is a PES header available, then use it ...
+  if (pesHeaderBackupLen > 0) {
+     // ISO 13818-1 says:
+     // In the case of video, if a PTS is present in a PES packet header
+     // it shall refer to the access unit containing the first picture start
+     // code that commences in this PES packet. A picture start code commences
+     // in PES packet if the first byte of the picture start code is present
+     // in the PES packet.
+     memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+     pesHeaderLen = pesHeaderBackupLen;
+     pesHeaderBackupLen = 0;
+     }
+  else {
+     // ... otherwise create a continuation PES header
+     pesHeaderLen = 0;
+     pesHeader[pesHeaderLen++] = 0x00;
+     pesHeader[pesHeaderLen++] = 0x00;
+     pesHeader[pesHeaderLen++] = 0x01;
+     pesHeader[pesHeaderLen++] = StreamID; // video stream ID
+     pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+     pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+     if (MpegLevel == phMPEG2) {
+        pesHeader[pesHeaderLen++] = 0x80;
+        pesHeader[pesHeaderLen++] = 0x00;
+        pesHeader[pesHeaderLen++] = 0x00;
+        }
+     else
+        pesHeader[pesHeaderLen++] = 0x0F;
+     }
+  // add an AUD in H.264 mode when not present in stream
+  if (h264Parser && !audSeen) {
+     pesHeader[pesHeaderLen++] = 0x00;
+     pesHeader[pesHeaderLen++] = 0x00;
+     pesHeader[pesHeaderLen++] = 0x01;
+     pesHeader[pesHeaderLen++] = 0x09; // access unit delimiter
+     pesHeader[pesHeaderLen++] = 0x10; // will be filled later
+     }
+  // append the first three bytes of the start code
+  pesHeader[pesHeaderLen++] = 0x00;
+  pesHeader[pesHeaderLen++] = 0x00;
+  pesHeader[pesHeaderLen++] = 0x01;
+  // the next packet's payload will begin with the fourth byte of
+  // the start code (= the actual code)
+  Payload = Data;
+  // as there is no length information available, assume the
+  // maximum we can hold in one PES packet
+  packetTodo = maxPacketSize - pesHeaderLen;
+  // go on with finding the picture data
+  state++;
+}
+
 bool cVideoRepacker::ScanDataForStartCodeSlow(const uchar *const Data)
 {
   scanner <<= 8;
@@ -458,14 +743,19 @@ void cVideoRepacker::Repack(cRingBufferL
   const uchar *data = Data + done;
   // remember start of the data
   const uchar *payload = data;
+  const uchar *NalPayload = payload;
 
   while (todo > 0) {
         // collect number of skipped bytes while syncing
         if (state <= syncing)
            skippedBytes++;
         // did we reach a start code?
-        if (ScanDataForStartCode(data, done, todo))
-           HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel);
+        if (ScanDataForStartCode(data, done, todo)) {
+           if (h264Parser)
+              HandleNalUnit(data, ResultBuffer, payload, Data[3], mpegLevel, NalPayload);
+           else
+              HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel);
+           }
         // move on
         data++;
         done++;
@@ -568,6 +858,8 @@ void cVideoRepacker::Repack(cRingBufferL
         fragmentLen += bite;
         }
      }
+  // always collect remaining NAL unit data (may be needed for syncing)
+  CollectNalUnitData(NalPayload, data - NalPayload);
   // report that syncing dropped some bytes
   if (skippedBytes > SkippedBytesLimit) {
      if (!initiallySyncing) // omit report for the typical initial case
@@ -581,13 +873,22 @@ bool cVideoRepacker::ScanForEndOfPicture
   localScanner <<= 8;
   localScanner |= *Data++;
   // check start codes which follow picture data
-  switch (localScanner) {
-    case 0x00000100: // picture start code
-    case 0x000001B8: // group start code
-    case 0x000001B3: // sequence header code
-    case 0x000001B7: // sequence end code
-         return true;
-    }
+  if (h264Parser) {
+     int nal_unit_type = localScanner & 0x1F;
+     switch (nal_unit_type) {
+       case 9: // access unit delimiter
+            return true;
+       }
+     }
+  else {
+     switch (localScanner) {
+       case 0x00000100: // picture start code
+       case 0x000001B8: // group start code
+       case 0x000001B3: // sequence header code
+       case 0x000001B7: // sequence end code
+            return true;
+       }
+     }
   return false;
 }
 
@@ -601,15 +902,27 @@ bool cVideoRepacker::ScanForEndOfPicture
         else {
            localScanner = 0x00000100 | *++Data;
            // check start codes which follow picture data
-           switch (localScanner) {
-             case 0x00000100: // picture start code
-             case 0x000001B8: // group start code
-             case 0x000001B3: // sequence header code
-             case 0x000001B7: // sequence end code
-                  Data++;
-                  return true;
-             default:
-                  Data += 3;
+           if (h264Parser) {
+              int nal_unit_type = localScanner & 0x1F;
+              switch (nal_unit_type) {
+                case 9: // access unit delimiter
+                     Data++;
+                     return true;
+                default:
+                     Data += 3;
+                }
+              }
+           else {
+              switch (localScanner) {
+                case 0x00000100: // picture start code
+                case 0x000001B8: // group start code
+                case 0x000001B3: // sequence header code
+                case 0x000001B7: // sequence end code
+                     Data++;
+                     return true;
+                default:
+                     Data += 3;
+                }
              }
            }
         }
@@ -1521,7 +1834,7 @@ void cTS2PES::store(uint8_t *Data, int C
   if (repacker)
      repacker->Repack(resultBuffer, Data, Count);
   else
-     cRepacker::Put(resultBuffer, Data, Count, Count);
+     cRepacker::PutAllOrNothing(resultBuffer, Data, Count, Count);
 }
 
 void cTS2PES::reset_ipack(void)
@@ -1855,6 +2168,8 @@ void cTS2PES::ts_to_pes(const uint8_t *B
 
 cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure)
 {
+  h264 = VPID_IS_H264(VPid);
+  VPid = VPID_FROM_ANY(VPid);
   exitOnFailure = ExitOnFailure;
   isRadio = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
   numUPTerrors = 0;
@@ -1867,7 +2182,7 @@ cRemux::cRemux(int VPid, const int *APid
   if (VPid)
 #define TEST_cVideoRepacker
 #ifdef TEST_cVideoRepacker
-     ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker);
+     ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker(h264));
 #else
      ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0);
 #endif
@@ -1919,6 +2234,23 @@ int cRemux::GetPacketLength(const uchar 
   return -1;
 }
 
+bool cRemux::IsFrameH264(const uchar *Data, int Length)
+{
+  int PesPayloadOffset;
+  const uchar *limit = Data + Length;
+  if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid)
+     return false; // neither MPEG1 nor MPEG2
+
+  Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01
+  if (Data < limit) {
+     // cVideoRepacker ensures that in case of H264 we will see an access unit delimiter here
+     if (0x01 == Data[-1] && 9 == Data[0] && 0x00 == Data[-2] && 0x00 == Data[-3])
+        return true;
+     }
+
+  return false;
+}
+
 int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
 {
   // Scans the video packet starting at Offset and returns its length.
@@ -1937,23 +2269,67 @@ int cRemux::ScanVideoPacket(const uchar 
            if (p[-2] || p[-1] || p[0] != 0x01)
               pLimit = 0; // skip scanning: packet doesn't start with 0x000001
            else {
-              switch (p[1]) {
-                case SC_SEQUENCE:
-                case SC_GROUP:
-                case SC_PICTURE:
-                     break;
-                default: // skip scanning: packet doesn't start a new sequence, group or picture
-                     pLimit = 0;
-                }
+              if (h264) {
+                 int nal_unit_type = p[1] & 0x1F;
+                 switch (nal_unit_type) {
+                   case 9: // access unit delimiter
+                        // when the MSB in p[1] is set (which violates H.264) then this is a hint
+                        // from cVideoRepacker::HandleNalUnit() that this bottom field shall not
+                        // be reported as picture.
+                        if (p[1] & 0x80)
+                           ((uchar *)p)[1] &= ~0x80; // revert the hint and fall through
+                        else
+                           break;
+                   default: // skip scanning: packet doesn't start a new picture
+                        pLimit = 0;
+                   }
+                 }
+              else {
+                 switch (p[1]) {
+                   case SC_SEQUENCE:
+                   case SC_GROUP:
+                   case SC_PICTURE:
+                        break;
+                   default: // skip scanning: packet doesn't start a new sequence, group or picture
+                        pLimit = 0;
+                   }
+                 }
               }
            }
 #endif
         while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
               if (!p[-2] && !p[-1]) { // found 0x000001
-                 switch (p[1]) {
-                   case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
-                                    return Length;
-                   }
+                 if (h264) {
+                    int nal_unit_type = p[1] & 0x1F;
+                    switch (nal_unit_type) {
+                      case 9: { // access unit delimiter
+                              int primary_pic_type = p[2] >> 5;
+                              switch (primary_pic_type) {
+                                case 0: // I
+                                case 3: // SI
+                                case 5: // I, SI
+                                     PictureType = I_FRAME;
+                                     break;
+                                case 1: // I, P
+                                case 4: // SI, SP
+                                case 6: // I, SI, P, SP
+                                     PictureType = P_FRAME;
+                                     break;
+                                case 2: // I, P, B
+                                case 7: // I, SI, P, SP, B
+                                     PictureType = B_FRAME;
+                                     break;
+                                }
+                              return Length;
+                              }
+                      }
+                    }
+                 else {
+                    switch (p[1]) {
+                      case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
+                                       return Length;
+                      }
+                    }
                  p += 4; // continue scanning after 0x01ssxxyy
                  }
               else
--- ./remux.h	2006-03-25 13:27:30.000000000 +0100
+++ ../vdr-1.5.9/./remux.h	2007-03-11 13:07:52.000000000 +0100
@@ -38,6 +38,7 @@ class cRemux {
 private:
   bool exitOnFailure;
   bool isRadio;
+  bool h264;
   int numUPTerrors;
   bool synced;
   int skipped;
@@ -46,6 +47,7 @@ private:
   cRingBufferLinear *resultBuffer;
   int resultSkipped;
   int GetPid(const uchar *Data);
+  int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
 public:
   cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false);
        ///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
@@ -78,7 +80,7 @@ public:
        ///< settings as they are.
   static void SetBrokenLink(uchar *Data, int Length);
   static int GetPacketLength(const uchar *Data, int Count, int Offset);
-  static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+  static bool IsFrameH264(const uchar *Data, int Length);
   };
 
 #endif // __REMUX_H
--- ./sdt.c	2007-06-10 10:50:49.000000000 +0200
+++ ../vdr-1.5.9/./sdt.c	2007-06-17 18:35:37.000000000 +0200
@@ -55,6 +55,7 @@ void cSdtFilter::Process(u_short Pid, u_
                    case 0x02: // digital radio sound service
                    case 0x04: // NVOD reference service
                    case 0x05: // NVOD time-shifted service
+                   case 0x19: // digital HD television service
                         {
                         char NameBuf[Utf8BufSize(1024)];
                         char ShortNameBuf[Utf8BufSize(1024)];
--- ./transfer.c	2007-01-05 11:45:28.000000000 +0100
+++ ../vdr-1.5.9/./transfer.c	2007-03-11 20:02:25.000000000 +0100
@@ -15,7 +15,7 @@
 // --- 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)
+:cReceiver(ChannelID, -1, VPID_FROM_ANY(VPid), APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
 ,cThread("transfer")
 {
   ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
--- h264parser.h	1970-01-01 01:00:00.000000000 +0100
+++ ../vdr-1.5.9/h264parser.h	2007-08-28 23:18:31.000000000 +0200
@@ -0,0 +1,397 @@
+/*
+ * h264parser.h: a minimalistic H.264 video stream parser
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ */
+
+#ifndef __H264PARSER_H
+#define __H264PARSER_H
+
+namespace H264
+{
+  // --- cException ----------------------------------------------------------
+
+  class cException {
+  private:
+    cString message;
+  public:
+    cException(const cString &Message) { message = Message; }
+    const cString &Message(void) const { return message; }
+  };
+
+  // --- cBitReader ----------------------------------------------------------
+
+  class cBitReader {
+  public:
+    class cBookMark {
+    private:
+      uint8_t *data;
+      int count;
+      uint32_t bits;
+      uint32_t bitsAvail;
+      int countZeros;
+      cBookMark(void) {}
+      friend class cBitReader;
+    };
+  private:
+    cBookMark bm;
+    uint8_t NextByte(void);
+    uint32_t ReadBits(uint32_t n);
+  public:
+    cBitReader(uint8_t *Data, int Count);
+    uint32_t u(uint32_t n) { return ReadBits(n); } // read n bits as unsigned number
+    uint32_t ue(void); // read Exp-Golomb coded unsigned number
+    int32_t se(void); // read Exp-Golomb coded signed number
+    uint32_t GetBitsAvail(void) { return (bm.bitsAvail & 0x07); }
+    bool GetBytesAvail(void) { return (bm.count > 0); }
+    const cBookMark BookMark(void) const { return bm; }
+    void BookMark(const cBookMark &b) { bm = b; }
+  };
+
+  inline cBitReader::cBitReader(unsigned char *Data, int Count)
+  {
+    bm.data = Data;
+    bm.count = Count;
+    bm.bitsAvail = 0;
+    bm.countZeros = 0;
+  }
+
+  inline uint8_t cBitReader::NextByte(void)
+  {
+    if (bm.count < 1) // there is no more data left in this NAL unit
+       throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
+    // detect 00 00 00, 00 00 01 and 00 00 03 and handle them
+    if (*bm.data == 0x00) {
+       if (bm.countZeros >= 3) // 00 00 00: the current NAL unit should have been terminated already before this sequence
+          throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
+       // increase the zero counter as we have a zero byte
+       bm.countZeros++;
+       }
+    else {
+       if (bm.countZeros >= 2) {
+          if (*bm.data == 0x01) // 00 00 01: the current NAL unit should have been terminated already before this sequence
+             throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
+          if (*bm.data == 0x03) {
+             // 00 00 03 xx: the emulation prevention byte 03 needs to be removed and xx must be returned
+             if (bm.count < 2)
+                throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
+             // drop 03 and xx will be returned below
+             bm.count--;
+             bm.data++;
+             }
+          }
+       // reset the zero counter as we had a non zero byte
+       bm.countZeros = 0;
+       }
+    bm.count--;
+    return *bm.data++;
+  }
+
+  inline uint32_t cBitReader::ReadBits(uint32_t n)
+  {
+    // fill the "shift register" bits with sufficient data
+    while (n > bm.bitsAvail) {
+          bm.bits <<= 8;
+          bm.bits |= NextByte();
+          bm.bitsAvail += 8;
+          if (bm.bitsAvail > 24) { // a further turn will overflow bitbuffer
+             if (n <= bm.bitsAvail)
+                break; // service non overflowing request
+             if (n <= 32) // split overflowing reads into concatenated reads 
+                return (ReadBits(16) << 16) | ReadBits(n - 16);
+             // cannot read more than 32 bits at once
+             throw new cException("ERROR: H264::cBitReader::ReadBits(): bitbuffer overflow");
+             }
+          }
+    // return n most significant bits
+    bm.bitsAvail -= n;
+    return (bm.bits >> bm.bitsAvail) & (((uint32_t)1 << n) - 1);
+  }
+
+  inline uint32_t cBitReader::ue(void)
+  {
+    // read and decode an Exp-Golomb coded unsigned number
+    //
+    // bitstring             resulting number
+    //       1               0
+    //     0 1 x             1 ... 2
+    //   0 0 1 x y           3 ... 6
+    // 0 0 0 1 x y z         7 ... 14
+    // ...
+    int LeadingZeroBits = 0;
+    while (ReadBits(1) == 0)
+          LeadingZeroBits++;
+    if (LeadingZeroBits == 0)
+       return 0;
+    if (LeadingZeroBits >= 32)
+       throw new cException("ERROR: H264::cBitReader::ue(): overflow");
+    return ((uint32_t)1 << LeadingZeroBits) - 1 + ReadBits(LeadingZeroBits);
+  }
+
+  inline int32_t cBitReader::se(void)
+  {
+    // read and decode an Exp-Golomb coded signed number
+    //
+    // unsigned value       resulting signed value
+    // 0                     0
+    // 1                    +1
+    // 2                    -1
+    // 3                    +2
+    // 4                    -2
+    // ...
+    uint32_t r = ue();
+    if (r > 0xFFFFFFFE)
+       throw new cException("ERROR: H264::cBitReader::se(): overflow");
+    return (1 - 2 * (r & 1)) * ((r + 1) / 2);
+  }
+
+  // --- cPictureTiming ------------------------------------------------------
+
+  class cPictureTiming {
+  private:
+    friend class cContext;
+    bool defined;
+  public:
+    cPictureTiming(void) { memset(this, 0, sizeof (*this)); }
+    bool Defined(void) const { return defined; }
+    uint32_t pic_struct;
+  };
+
+  // --- cSequenceParameterSet -----------------------------------------------
+
+  class cSequenceParameterSet {
+  private:
+    friend class cContext;
+    bool defined;
+    uint32_t log2MaxFrameNum;
+    uint32_t log2MaxPicOrderCntLsb;
+    uint32_t cpbRemovalDelayLength;
+    uint32_t dpbOutputDelayLength;
+  public:
+    cSequenceParameterSet(void);
+    bool Defined(void) { return defined; }
+    void log2_max_frame_num_minus4(uint32_t Value) { log2MaxFrameNum = Value + 4; }
+    uint32_t log2_max_frame_num_minus4(void) const { return log2MaxFrameNum - 4; }
+    uint32_t log2_max_frame_num(void) const { return log2MaxFrameNum; }
+    void log2_max_pic_order_cnt_lsb_minus4(uint32_t Value) { log2MaxPicOrderCntLsb = Value + 4; }
+    uint32_t log2_max_pic_order_cnt_lsb_minus4(void) const { return log2MaxPicOrderCntLsb - 4; }
+    uint32_t log2_max_pic_order_cnt_lsb(void) const { return log2MaxPicOrderCntLsb; }
+    void cpb_removal_delay_length_minus1(uint32_t Value) { cpbRemovalDelayLength = Value + 1; }
+    uint32_t cpb_removal_delay_length_minus1(void) const { return cpbRemovalDelayLength - 1; }
+    uint32_t cpb_removal_delay_length(void) const { return cpbRemovalDelayLength; }
+    void dpb_output_delay_length_minus1(uint32_t Value) { dpbOutputDelayLength = Value + 1; }
+    uint32_t dpb_output_delay_length_minus1(void) const { return dpbOutputDelayLength - 1; }
+    uint32_t dpb_output_delay_length(void) const { return dpbOutputDelayLength; }
+    uint32_t seq_parameter_set_id;
+    uint32_t pic_order_cnt_type;
+    uint32_t delta_pic_order_always_zero_flag;
+    uint32_t frame_mbs_only_flag;
+    uint32_t timing_info_present_flag;
+    uint32_t num_units_in_tick;
+    uint32_t time_scale;
+    uint32_t fixed_frame_rate_flag;
+    uint32_t nal_hrd_parameters_present_flag;
+    uint32_t vcl_hrd_parameters_present_flag;
+    uint32_t pic_struct_present_flag;
+    cPictureTiming pic_timing_sei;
+  };
+
+  inline cSequenceParameterSet::cSequenceParameterSet(void)
+  {
+    memset(this, 0, sizeof (*this));
+    log2_max_frame_num_minus4(0);
+    log2_max_pic_order_cnt_lsb_minus4(0);
+    cpb_removal_delay_length_minus1(23);
+    dpb_output_delay_length_minus1(23);
+  }
+
+  // --- cPictureParameterSet ------------------------------------------------
+
+  class cPictureParameterSet {
+  private:
+    friend class cContext;
+    bool defined;
+  public:
+    cPictureParameterSet(void) { memset(this, 0, sizeof (*this)); }
+    bool Defined(void) { return defined; }
+    uint32_t pic_parameter_set_id;
+    uint32_t seq_parameter_set_id;
+    uint32_t pic_order_present_flag;
+  };
+
+  // --- cSliceHeader --------------------------------------------------------
+
+  class cSliceHeader {
+  private:
+    friend class cContext;
+    bool defined;
+    bool isFirstSliceOfCurrentAccessUnit;
+    uint32_t picOrderCntType;
+    uint32_t nalRefIdc;
+    uint32_t nalUnitType;
+  public:
+    cSliceHeader(void) { memset(this, 0, sizeof (*this)); }
+    bool Defined(void) const { return defined; }
+    bool IsFirstSliceOfCurrentAccessUnit(void) const { return isFirstSliceOfCurrentAccessUnit; }
+    void nal_ref_idc(uint32_t Value) { nalRefIdc = Value; }
+    uint32_t nal_ref_idc(void) const { return nalRefIdc; }
+    void nal_unit_type(uint32_t Value) { nalUnitType = Value; }
+    uint32_t nal_unit_type(void) const { return nalUnitType; }
+    uint32_t slice_type;
+    uint32_t pic_parameter_set_id;
+    uint32_t frame_num;
+    uint32_t field_pic_flag;
+    uint32_t bottom_field_flag;
+    uint32_t idr_pic_id;
+    uint32_t pic_order_cnt_lsb;
+    int32_t delta_pic_order_cnt_bottom;
+    int32_t delta_pic_order_cnt[2];
+    enum eAccessUnitType {
+      Frame = 0,
+      TopField,
+      BottomField
+      };
+    eAccessUnitType GetAccessUnitType() const { return (eAccessUnitType)(field_pic_flag + bottom_field_flag); }
+  };
+
+  // --- cContext ------------------------------------------------------------
+
+  class cContext {
+  private:
+    cSequenceParameterSet spsStore[32];
+    cPictureParameterSet ppsStore[256];
+    cSequenceParameterSet *sps; // active Sequence Parameter Set
+    cPictureParameterSet *pps; // active Picture Parameter Set
+    cSliceHeader sh;
+  public:
+    cContext(void) { sps = 0; pps = 0; }
+    void Define(cSequenceParameterSet &SPS);
+    void Define(cPictureParameterSet &PPS);
+    void Define(cSliceHeader &SH);
+    void Define(cPictureTiming &PT);
+    void ActivateSPS(uint32_t ID);
+    void ActivatePPS(uint32_t ID);
+    const cSequenceParameterSet *ActiveSPS(void) const { return sps; }
+    const cPictureParameterSet *ActivePPS(void) const { return pps; }
+    const cSliceHeader *CurrentSlice(void) const { return sh.Defined() ? &sh : 0; }
+    int GetFramesPerSec(void) const;
+  };
+
+  inline void cContext::ActivateSPS(uint32_t ID)
+  {
+    if (ID >= (sizeof (spsStore) / sizeof (*spsStore)))
+       throw new cException("ERROR: H264::cContext::ActivateSPS(): id out of range");
+    if (!spsStore[ID].Defined())
+       throw new cException("ERROR: H264::cContext::ActivateSPS(): requested SPS is undefined");
+    sps = &spsStore[ID];
+  }
+
+  inline void cContext::ActivatePPS(uint32_t ID)
+  {
+    if (ID >= (sizeof (ppsStore) / sizeof (*ppsStore)))
+       throw new cException("ERROR: H264::cContext::ActivatePPS(): id out of range");
+    if (!ppsStore[ID].Defined())
+       throw new cException("ERROR: H264::cContext::ActivatePPS(): requested PPS is undefined");
+    pps = &ppsStore[ID];
+    ActivateSPS(pps->seq_parameter_set_id);
+  }
+
+  inline void cContext::Define(cSequenceParameterSet &SPS)
+  {
+    if (SPS.seq_parameter_set_id >= (sizeof (spsStore) / sizeof (*spsStore)))
+       throw new cException("ERROR: H264::cContext::DefineSPS(): id out of range");
+    SPS.defined = true;
+    spsStore[SPS.seq_parameter_set_id] = SPS;
+  }
+
+  inline void cContext::Define(cPictureParameterSet &PPS)
+  {
+    if (PPS.pic_parameter_set_id >= (sizeof (ppsStore) / sizeof (*ppsStore)))
+       throw new cException("ERROR: H264::cContext::DefinePPS(): id out of range");
+    PPS.defined = true;
+    ppsStore[PPS.pic_parameter_set_id] = PPS;
+  }
+
+  inline void cContext::Define(cSliceHeader &SH)
+  {
+    SH.defined = true;
+    SH.picOrderCntType = ActiveSPS()->pic_order_cnt_type;
+
+    // ITU-T Rec. H.264 (03/2005): 7.4.1.2.4
+    SH.isFirstSliceOfCurrentAccessUnit = !sh.Defined()
+      || (sh.frame_num                  != SH.frame_num)
+      || (sh.pic_parameter_set_id       != SH.pic_parameter_set_id)
+      || (sh.field_pic_flag             != SH.field_pic_flag)
+      || (sh.bottom_field_flag          != SH.bottom_field_flag)
+      || (sh.nalRefIdc                  != SH.nalRefIdc
+      && (sh.nalRefIdc == 0             || SH.nalRefIdc == 0))
+      || (sh.picOrderCntType == 0       && SH.picOrderCntType == 0
+      && (sh.pic_order_cnt_lsb          != SH.pic_order_cnt_lsb
+      ||  sh.delta_pic_order_cnt_bottom != SH.delta_pic_order_cnt_bottom))
+      || (sh.picOrderCntType == 1       && SH.picOrderCntType == 1
+      && (sh.delta_pic_order_cnt[0]     != SH.delta_pic_order_cnt[0]
+      ||  sh.delta_pic_order_cnt[1]     != SH.delta_pic_order_cnt[1]))
+      || (sh.nalUnitType                != SH.nalUnitType
+      && (sh.nalUnitType == 5           || SH.nalUnitType == 5))
+      || (sh.nalUnitType == 5           && SH.nalUnitType == 5
+      &&  sh.idr_pic_id                 != SH.idr_pic_id);
+        
+    sh = SH;
+  }
+
+  inline void cContext::Define(cPictureTiming &PT)
+  {
+    PT.defined = true;
+    ((cSequenceParameterSet *)ActiveSPS())->pic_timing_sei = PT;
+  }
+
+  // --- cSimpleBuffer -------------------------------------------------------
+
+  class cSimpleBuffer {
+  private:
+    uchar *data;
+    int size;
+    int avail;
+    int gotten;
+  public:
+    cSimpleBuffer(int Size);
+    ~cSimpleBuffer();
+    int Size(void) { return size; }
+    int Available(void) { return avail; }
+    int Free(void) { return size - avail; }
+    int Put(const uchar *Data, int Count);
+    uchar *Get(int &Count);
+    void Del(int Count);
+    void Clear(void);
+  };
+
+  // --- cParser -------------------------------------------------------------
+
+  class cParser {
+  private:
+    bool syncing;
+    bool omitPicTiming;
+    cContext context;
+    cSimpleBuffer nalUnitDataBuffer;
+    void hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br);
+    void ParseSequenceParameterSet(uint8_t *Data, int Count);
+    void ParsePictureParameterSet(uint8_t *Data, int Count);
+    void ParseSlice(uint8_t *Data, int Count);
+    void reserved_sei_message(uint32_t payloadSize, cBitReader &br);
+    void pic_timing(uint32_t payloadSize, cBitReader &br);
+    void buffering_period(uint32_t payloadSize, cBitReader &br);
+    void sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br);
+    void sei_message(cBitReader &br);
+    void ParseSEI(uint8_t *Data, int Count);
+  public:
+    cParser(bool OmitPicTiming = true);
+    const cContext &Context(void) const { return context; }
+    void PutNalUnitData(const uchar *Data, int Count);
+    void Reset(void);
+    void Process(void);
+  };
+}
+
+#endif // __H264PARSER_H
+
--- h264parser.c	1970-01-01 01:00:00.000000000 +0100
+++ ../vdr-1.5.9/h264parser.c	2007-08-28 23:28:50.000000000 +0200
@@ -0,0 +1,461 @@
+/*
+ * h264parser.c: a minimalistic H.264 video stream parser
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * The code was originally written by Reinhard Nissl <rnissl@xxxxxx>,
+ * and adapted to the VDR coding style by Klaus.Schmidinger@xxxxxxxxxxx
+ */
+
+#include "tools.h"
+#include "h264parser.h"
+
+namespace H264
+{
+  // --- cContext ------------------------------------------------------------
+
+  int cContext::GetFramesPerSec(void) const
+  {
+    const cSequenceParameterSet *SPS = ActiveSPS();
+    const cSliceHeader *SH = CurrentSlice();
+    if (!SH || !SPS->timing_info_present_flag || !SPS->time_scale || !SPS->num_units_in_tick)
+       return -1;
+    uint32_t DeltaTfiDivisor;
+    if (SPS->pic_struct_present_flag) {
+       if (!SPS->pic_timing_sei.Defined())
+          return -1;
+       switch (SPS->pic_timing_sei.pic_struct) {
+         case 1:
+         case 2:
+              DeltaTfiDivisor = 1;
+              break;
+         case 0:
+         case 3:
+         case 4:
+              DeltaTfiDivisor = 2;
+              break;
+         case 5:
+         case 6:
+              DeltaTfiDivisor = 3;
+              break;
+         case 7:
+              DeltaTfiDivisor = 4;
+              break;
+         case 8:
+              DeltaTfiDivisor = 6;
+              break;
+         default:
+              return -1;
+         }
+       }
+    else if (!SH->field_pic_flag)
+       DeltaTfiDivisor = 2;
+    else
+       DeltaTfiDivisor = 1;
+
+    double FPS = (double)SPS->time_scale / SPS->num_units_in_tick / DeltaTfiDivisor / (SH->field_pic_flag ? 2 : 1);
+    int FramesPerSec = (int)FPS;
+    if ((FPS - FramesPerSec) >= 0.5)
+       FramesPerSec++;
+    return FramesPerSec;
+  }
+
+  // --- cSimpleBuffer -------------------------------------------------------
+
+  cSimpleBuffer::cSimpleBuffer(int Size)
+  {
+    size = Size;
+    data = new uchar[size];
+    avail = 0;
+    gotten = 0;
+  }
+
+  cSimpleBuffer::~cSimpleBuffer()
+  {
+    delete data;
+  }
+
+  int cSimpleBuffer::Put(const uchar *Data, int Count)
+  {
+    if (Count < 0) {
+       if (avail + Count < 0)
+          Count = 0 - avail;
+       if (avail + Count < gotten)
+          Count = gotten - avail;
+       avail += Count;
+       return Count;
+       }
+    if (avail + Count > size)
+       Count = size - avail;
+    memcpy(data + avail, Data, Count);
+    avail += Count;
+    return Count;
+  }
+
+  uchar *cSimpleBuffer::Get(int &Count)
+  {
+    Count = gotten = avail;
+    return data;
+  }
+
+  void cSimpleBuffer::Del(int Count)
+  {
+    if (Count < 0)
+       return;
+    if (Count > gotten) {
+       esyslog("ERROR: invalid Count in H264::cSimpleBuffer::Del: %d (limited to %d)", Count, gotten);
+       Count = gotten;
+       }
+    if (Count < avail)
+       memmove(data, data + Count, avail - Count);
+    avail -= Count;
+    gotten = 0;
+  }
+
+  void cSimpleBuffer::Clear(void)
+  {
+    avail = gotten = 0;
+  }
+
+  // --- cParser -------------------------------------------------------------
+
+  cParser::cParser(bool OmitPicTiming)
+    : nalUnitDataBuffer(1000)
+  {
+    // the above buffer size of 1000 bytes wont hold a complete NAL unit but
+    // should be sufficient for the relevant part used for parsing.
+    omitPicTiming = OmitPicTiming; // only necessary to determine frames per second
+    Reset();
+  }
+
+  void cParser::Reset(void)
+  {
+    context = cContext();
+    nalUnitDataBuffer.Clear();
+    syncing = true;
+  }
+
+  void cParser::ParseSequenceParameterSet(uint8_t *Data, int Count)
+  {
+    cSequenceParameterSet SPS;
+
+    cBitReader br(Data + 1, Count - 1);
+    uint32_t profile_idc = br.u(8);
+    /* uint32_t constraint_set0_flag = */ br.u(1);
+    /* uint32_t constraint_set1_flag = */ br.u(1);
+    /* uint32_t constraint_set2_flag = */ br.u(1);
+    /* uint32_t constraint_set3_flag = */ br.u(1);
+    /* uint32_t reserved_zero_4bits = */ br.u(4);
+    /* uint32_t level_idc = */ br.u(8);
+    SPS.seq_parameter_set_id = br.ue();
+    if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) {
+       uint32_t chroma_format_idc = br.ue();
+       if (chroma_format_idc == 3) {
+          /* uint32_t residual_colour_transform_flag = */ br.u(1);
+          }
+       /* uint32_t bit_depth_luma_minus8 = */ br.ue();
+       /* uint32_t bit_depth_chroma_minus8 = */ br.ue();
+       /* uint32_t qpprime_y_zero_transform_bypass_flag = */ br.u(1);
+       uint32_t seq_scaling_matrix_present_flag = br.u(1);
+       if (seq_scaling_matrix_present_flag) {
+          for (int i = 0; i < 8; i++) {
+              uint32_t seq_scaling_list_present_flag = br.u(1);
+              if (seq_scaling_list_present_flag) {
+                 int sizeOfScalingList = (i < 6) ? 16 : 64;
+                 int lastScale = 8;
+                 int nextScale = 8;
+                 for (int j = 0; j < sizeOfScalingList; j++) {
+                     if (nextScale != 0) {
+                        int32_t delta_scale = br.se();
+                        nextScale = (lastScale + delta_scale + 256) % 256;
+                        }
+                     lastScale = (nextScale == 0) ? lastScale : nextScale;
+                     }
+                 }
+              }
+          }
+       }
+    SPS.log2_max_frame_num_minus4(br.ue());
+    SPS.pic_order_cnt_type = br.ue();
+    if (SPS.pic_order_cnt_type == 0)
+       SPS.log2_max_pic_order_cnt_lsb_minus4(br.ue());
+    else if (SPS.pic_order_cnt_type == 1) {
+       SPS.delta_pic_order_always_zero_flag = br.u(1);
+       /* int32_t offset_for_non_ref_pic = */ br.se();
+       /* int32_t offset_for_top_to_bottom_field = */ br.se();
+       uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ue();
+       for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
+           /* int32_t offset_for_ref_frame = */ br.se();
+           }
+       }
+    /* uint32_t num_ref_frames = */ br.ue();
+    /* uint32_t gaps_in_frame_num_value_allowed_flag = */ br.u(1);
+    /* uint32_t pic_width_in_mbs_minus1 = */ br.ue();
+    /* uint32_t pic_height_in_map_units_minus1 = */ br.ue();
+    SPS.frame_mbs_only_flag = br.u(1);
+
+    if (!omitPicTiming) {
+       if (!SPS.frame_mbs_only_flag) {
+          /* uint32_t mb_adaptive_frame_field_flag = */ br.u(1);
+          }
+       /* uint32_t direct_8x8_inference_flag = */ br.u(1);
+       uint32_t frame_cropping_flag = br.u(1);
+       if (frame_cropping_flag) {
+          /* uint32_t frame_crop_left_offset = */ br.ue();
+          /* uint32_t frame_crop_right_offset = */ br.ue();
+          /* uint32_t frame_crop_top_offset = */ br.ue();
+          /* uint32_t frame_crop_bottom_offset = */ br.ue();
+          }
+       uint32_t vui_parameters_present_flag = br.u(1);
+       if (vui_parameters_present_flag) {
+          uint32_t aspect_ratio_info_present_flag = br.u(1);
+          if (aspect_ratio_info_present_flag) {
+             uint32_t aspect_ratio_idc = br.u(8);
+             const uint32_t Extended_SAR = 255;
+             if (aspect_ratio_idc == Extended_SAR) {
+                /* uint32_t sar_width = */ br.u(16);
+                /* uint32_t sar_height = */ br.u(16);
+                }
+             }
+          uint32_t overscan_info_present_flag = br.u(1);
+          if (overscan_info_present_flag) {
+             /* uint32_t overscan_appropriate_flag = */ br.u(1);
+             }
+          uint32_t video_signal_type_present_flag = br.u(1);
+          if (video_signal_type_present_flag) {
+             /* uint32_t video_format = */ br.u(3);
+             /* uint32_t video_full_range_flag = */ br.u(1);
+             uint32_t colour_description_present_flag = br.u(1);
+             if (colour_description_present_flag) {
+                /* uint32_t colour_primaries = */ br.u(8);
+                /* uint32_t transfer_characteristics = */ br.u(8);
+                /* uint32_t matrix_coefficients = */ br.u(8);
+                }
+             }
+          uint32_t chroma_loc_info_present_flag = br.u(1);
+          if (chroma_loc_info_present_flag) {
+             /* uint32_t chroma_sample_loc_type_top_field = */ br.ue();
+             /* uint32_t chroma_sample_loc_type_bottom_field = */ br.ue();
+             }
+          SPS.timing_info_present_flag = br.u(1);
+          if (SPS.timing_info_present_flag) {
+             SPS.num_units_in_tick = br.u(32);
+             SPS.time_scale = br.u(32);
+             SPS.fixed_frame_rate_flag = br.u(1);
+             }
+          SPS.nal_hrd_parameters_present_flag = br.u(1);
+          if (SPS.nal_hrd_parameters_present_flag)
+             hrd_parameters(SPS, br);
+          SPS.vcl_hrd_parameters_present_flag = br.u(1);
+          if (SPS.vcl_hrd_parameters_present_flag)
+             hrd_parameters(SPS, br);
+          if (SPS.nal_hrd_parameters_present_flag || SPS.vcl_hrd_parameters_present_flag) {
+             /* uint32_t low_delay_hrd_flag = */ br.u(1);
+             }
+          SPS.pic_struct_present_flag = br.u(1);
+          }
+       }
+
+    context.Define(SPS);
+  }
+
+  void cParser::hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br)
+  {
+    uint32_t cpb_cnt_minus1 = br.ue();
+    /* uint32_t bit_rate_scale = */ br.u(4);
+    /* uint32_t cpb_size_scale = */ br.u(4);
+    for (uint32_t i = 0; i <= cpb_cnt_minus1; i++) {
+        /* uint32_t bit_rate_value_minus1 = */ br.ue();
+        /* uint32_t cpb_size_value_minus1 = */ br.ue();
+        /* uint32_t cbr_flag = */ br.u(1);
+        }
+    /* uint32_t initial_cpb_removal_delay_length_minus1 = */ br.u(5);
+    SPS.cpb_removal_delay_length_minus1(br.u(5));
+    SPS.dpb_output_delay_length_minus1(br.u(5));
+    /* uint32_t time_offset_length = */ br.u(5);
+  }
+
+  void cParser::ParsePictureParameterSet(uint8_t *Data, int Count)
+  {
+    cPictureParameterSet PPS;
+
+    cBitReader br(Data + 1, Count - 1);
+    PPS.pic_parameter_set_id = br.ue();
+    PPS.seq_parameter_set_id = br.ue();
+    /* uint32_t entropy_coding_mode_flag = */ br.u(1);
+    PPS.pic_order_present_flag = br.u(1);
+
+    context.Define(PPS);
+  }
+
+  void cParser::ParseSlice(uint8_t *Data, int Count)
+  {
+    cSliceHeader SH;
+
+    cBitReader br(Data + 1, Count - 1);
+    SH.nal_ref_idc(Data[0] >> 5);
+    SH.nal_unit_type(Data[0] & 0x1F);
+    /* uint32_t first_mb_in_slice = */ br.ue();
+    SH.slice_type = br.ue();
+    SH.pic_parameter_set_id = br.ue();
+
+    context.ActivatePPS(SH.pic_parameter_set_id);
+    const cSequenceParameterSet *SPS = context.ActiveSPS();
+
+    SH.frame_num = br.u(SPS->log2_max_frame_num());
+    if (!SPS->frame_mbs_only_flag) {
+       SH.field_pic_flag = br.u(1);
+       if (SH.field_pic_flag)
+          SH.bottom_field_flag = br.u(1);
+       }
+    if (SH.nal_unit_type() == 5)
+       SH.idr_pic_id = br.ue();
+    if (SPS->pic_order_cnt_type == 0) {
+       SH.pic_order_cnt_lsb = br.u(SPS->log2_max_pic_order_cnt_lsb());
+       const cPictureParameterSet *PPS = context.ActivePPS();
+       if (PPS->pic_order_present_flag && !SH.field_pic_flag)
+          SH.delta_pic_order_cnt_bottom = br.se();
+       }
+    if (SPS->pic_order_cnt_type == 1 && !SPS->delta_pic_order_always_zero_flag) {
+       SH.delta_pic_order_cnt[0] = br.se();
+       const cPictureParameterSet *PPS = context.ActivePPS();
+       if (PPS->pic_order_present_flag && !SH.field_pic_flag)
+          SH.delta_pic_order_cnt[1] = br.se();
+       }
+
+    context.Define(SH);
+  }
+
+  void cParser::ParseSEI(uint8_t *Data, int Count)
+  {
+    // currently only used to determine frames per second
+    if (omitPicTiming)
+       return;
+    cBitReader br(Data + 1, Count - 1);
+    do
+      sei_message(br);
+    while (br.GetBytesAvail());
+  }
+
+  void cParser::sei_message(cBitReader &br)
+  {
+    uint32_t payloadType = 0;
+    while (1) {
+          uint32_t last_payload_type_byte = br.u(8);
+          payloadType += last_payload_type_byte;
+          if (last_payload_type_byte != 0xFF)
+             break;
+          }
+    uint32_t payloadSize = 0;
+    while (1) {
+          uint32_t last_payload_size_byte = br.u(8);
+          payloadSize += last_payload_size_byte;
+          if (last_payload_size_byte != 0xFF)
+             break;
+          }
+    sei_payload(payloadType, payloadSize, br);
+  }
+
+  void cParser::sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br)
+  {
+    const cBitReader::cBookMark BookMark = br.BookMark();
+    switch (payloadType) {
+      case 0:
+           buffering_period(payloadSize, br);
+           break;
+      case 1:
+           pic_timing(payloadSize, br);
+           break;
+      }
+    // instead of dealing with trailing bits in each message
+    // go back to start of message and skip it completely
+    br.BookMark(BookMark);
+    reserved_sei_message(payloadSize, br);
+  }
+
+  void cParser::buffering_period(uint32_t payloadSize, cBitReader &br)
+  {
+    uint32_t seq_parameter_set_id = br.ue();
+
+    context.ActivateSPS(seq_parameter_set_id);
+  }
+
+  void cParser::pic_timing(uint32_t payloadSize, cBitReader &br)
+  {
+    cPictureTiming PT;
+
+    const cSequenceParameterSet *SPS = context.ActiveSPS();
+    if (!SPS)
+       return;
+    uint32_t CpbDpbDelaysPresentFlag = SPS->nal_hrd_parameters_present_flag || SPS->vcl_hrd_parameters_present_flag;
+    if (CpbDpbDelaysPresentFlag) {
+       /* uint32_t cpb_removal_delay = */ br.u(SPS->cpb_removal_delay_length());
+       /* uint32_t dpb_output_delay = */ br.u(SPS->dpb_output_delay_length());
+       }
+    if (SPS->pic_struct_present_flag) {
+       PT.pic_struct = br.u(4);
+       }
+
+    context.Define(PT);
+  }
+
+  void cParser::reserved_sei_message(uint32_t payloadSize, cBitReader &br)
+  {
+    for (uint32_t i = 0; i < payloadSize; i++) {
+        /* uint32_t reserved_sei_message_payload_byte = */ br.u(8);
+        }
+  }
+
+  void cParser::PutNalUnitData(const uchar *Data, int Count)
+  {
+    int n = nalUnitDataBuffer.Put(Data, Count);
+    // typically less than a complete NAL unit are needed for parsing the
+    // relevant data, so simply ignore the overflow condition.
+    if (false && n != Count)
+       esyslog("ERROR: H264::cParser::PutNalUnitData(): NAL unit data buffer overflow");
+  }
+
+  void cParser::Process()
+  {
+    // nalUnitDataBuffer contains the head of the current NAL unit -- let's parse it 
+    int Count = 0;
+    uchar *Data = nalUnitDataBuffer.Get(Count);
+    if (Data && Count >= 4) {
+       if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) {
+          int nal_unit_type = Data[3] & 0x1F;
+          try {
+              switch (nal_unit_type) {
+                case 1: // coded slice of a non-IDR picture
+                case 2: // coded slice data partition A
+                case 5: // coded slice of an IDR picture
+                     ParseSlice(Data + 3, Count - 3);
+                     break;
+                case 6: // supplemental enhancement information (SEI)
+                     ParseSEI(Data + 3, Count - 3);
+                     break;
+                case 7: // sequence parameter set
+                     syncing = false; // from now on, we should get reliable results
+                     ParseSequenceParameterSet(Data + 3, Count - 3);
+                     break;
+                case 8: // picture parameter set
+                     ParsePictureParameterSet(Data + 3, Count - 3);
+                     break;
+                }
+              }
+          catch (cException *e) {
+              if (!syncing) // suppress typical error messages while syncing
+                 esyslog(e->Message());
+              delete e;
+              }
+          }
+       else if (!syncing)
+          esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is invalid");
+       }
+    else if (!syncing)
+       esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is too short");
+    // reset the buffer for the next NAL unit
+    nalUnitDataBuffer.Clear();
+  }
+}
+
--- ./dvbplayer.c	2007-04-28 16:55:22.000000000 +0200
+++ ../vdr-1.5.9/./dvbplayer.c	2007-08-28 22:25:08.000000000 +0200
@@ -182,8 +182,8 @@ bool cNonBlockingFileReader::WaitForData
 
 #define PLAYERBUFSIZE  MEGABYTE(1)
 
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
+// The number of seconds to back up when resuming an interrupted replay session:
+#define RESUMEBACKUP 10
 
 class cDvbPlayer : public cPlayer, cThread {
 private:
@@ -196,6 +196,7 @@ private:
   cFileName *fileName;
   cIndexFile *index;
   cUnbufferedFile *replayFile;
+  int framesPerSec;
   bool eof;
   bool firstPacket;
   ePlayModes playMode;
@@ -225,6 +226,7 @@ public:
   void Goto(int Position, bool Still = false);
   virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
   virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
+  int GetFramesPerSec(void) { return framesPerSec; }
   };
 
 #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@@ -253,6 +255,7 @@ cDvbPlayer::cDvbPlayer(const char *FileN
   replayFile = fileName->Open();
   if (!replayFile)
      return;
+  framesPerSec = replayFile->GetFramesPerSec();
   ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
   // Create the index file:
   index = new cIndexFile(FileName, false);
@@ -341,7 +344,7 @@ bool cDvbPlayer::Save(void)
   if (index) {
      int Index = writeIndex;
      if (Index >= 0) {
-        Index -= RESUMEBACKUP;
+        Index -= RESUMEBACKUP * GetFramesPerSec();
         if (Index > 0)
            Index = index->GetNextIFrame(Index, false);
         else
@@ -371,7 +374,7 @@ void cDvbPlayer::Action(void)
 
   readIndex = Resume();
   if (readIndex >= 0)
-     isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
+     isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, GetFramesPerSec()));
 
   nonBlockingFileReader = new cNonBlockingFileReader;
   int Length = 0;
@@ -667,7 +670,7 @@ void cDvbPlayer::SkipSeconds(int Seconds
      Empty();
      int Index = writeIndex;
      if (Index >= 0) {
-        Index = max(Index + Seconds * FRAMESPERSEC, 0);
+        Index = max(Index + Seconds * GetFramesPerSec(), 0);
         if (Index > 0)
            Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
         if (Index >= 0)
@@ -834,3 +838,10 @@ void cDvbPlayerControl::Goto(int Positio
   if (player)
      player->Goto(Position, Still);
 }
+
+int cDvbPlayerControl::GetFramesPerSec()
+{
+  if (player)
+     return player->GetFramesPerSec();
+  return FRAMESPERSEC;
+}
--- ./dvbplayer.h	2002-06-23 12:13:51.000000000 +0200
+++ ../vdr-1.5.9/./dvbplayer.h	2007-08-28 22:25:08.000000000 +0200
@@ -54,6 +54,8 @@ public:
   void Goto(int Index, bool Still = false);
        // Positions to the given index and displays that frame as a still picture
        // if Still is true.
+  int GetFramesPerSec();
+       // Returns the number of frames per second for the current recording.
   };
 
 #endif //__DVBPLAYER_H
--- ./menu.c	2007-08-24 15:15:48.000000000 +0200
+++ ../vdr-1.5.9/./menu.c	2007-08-28 22:25:08.000000000 +0200
@@ -4014,7 +4026,7 @@ bool cReplayControl::ShowProgress(bool I
         lastCurrent = lastTotal = -1;
         }
      if (Total != lastTotal) {
-        displayReplay->SetTotal(IndexToHMSF(Total));
+        displayReplay->SetTotal(IndexToHMSF(Total, false, GetFramesPerSec()));
         if (!Initial)
            displayReplay->Flush();
         }
@@ -4022,7 +4034,7 @@ bool cReplayControl::ShowProgress(bool I
         displayReplay->SetProgress(Current, Total);
         if (!Initial)
            displayReplay->Flush();
-        displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames));
+        displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, GetFramesPerSec()));
         displayReplay->Flush();
         lastCurrent = Current;
         }
@@ -4055,8 +4067,8 @@ void cReplayControl::TimeSearchProcess(e
 {
 #define STAY_SECONDS_OFF_END 10
   int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
-  int Current = (lastCurrent / FRAMESPERSEC);
-  int Total = (lastTotal / FRAMESPERSEC);
+  int Current = (lastCurrent / GetFramesPerSec());
+  int Total = (lastTotal / GetFramesPerSec());
   switch (Key) {
     case k0 ... k9:
          if (timeSearchPos < 4) {
@@ -4083,7 +4095,7 @@ void cReplayControl::TimeSearchProcess(e
     case kDown:
     case kOk:
          Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
-         Goto(Seconds * FRAMESPERSEC, Key == kDown || Key == kPause || Key == kOk);
+         Goto(Seconds * GetFramesPerSec(), Key == kDown || Key == kPause || Key == kOk);
          timeSearchActive = false;
          break;
     default:
@@ -4204,7 +4216,7 @@ void cReplayControl::EditTest(void)
         if ((m->Index() & 0x01) != 0)
            m = marks.Next(m);
         if (m) {
-           Goto(m->position - SecondsToFrames(3));
+           Goto(m->position - SecondsToFrames(3, GetFramesPerSec()));
            Play();
            }
         }
--- ./recording.c	2007-06-17 15:10:12.000000000 +0200
+++ ../vdr-1.5.9/./recording.c	2007-08-28 22:25:08.000000000 +0200
@@ -1481,11 +1481,11 @@ cUnbufferedFile *cFileName::NextFile(voi
 
 // --- Index stuff -----------------------------------------------------------
 
-cString IndexToHMSF(int Index, bool WithFrame)
+cString IndexToHMSF(int Index, bool WithFrame, int FramesPerSec)
 {
   char buffer[16];
-  int f = (Index % FRAMESPERSEC) + 1;
-  int s = (Index / FRAMESPERSEC);
+  int f = (Index % FramesPerSec) + 1;
+  int s = (Index / FramesPerSec);
   int m = s / 60 % 60;
   int h = s / 3600;
   s %= 60;
@@ -1493,17 +1493,17 @@ cString IndexToHMSF(int Index, bool With
   return buffer;
 }
 
-int HMSFToIndex(const char *HMSF)
+int HMSFToIndex(const char *HMSF, int FramesPerSec)
 {
   int h, m, s, f = 0;
   if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
-     return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+     return (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
   return 0;
 }
 
-int SecondsToFrames(int Seconds)
+int SecondsToFrames(int Seconds, int FramesPerSec)
 {
-  return Seconds * FRAMESPERSEC;
+  return Seconds * FramesPerSec;
 }
 
 // --- ReadFrame -------------------------------------------------------------
--- ./recording.h	2007-06-17 14:53:05.000000000 +0200
+++ ../vdr-1.5.9/./recording.h	2007-08-28 22:25:08.000000000 +0200
@@ -233,11 +233,11 @@ public:
   cUnbufferedFile *NextFile(void);
   };
 
-cString IndexToHMSF(int Index, bool WithFrame = false);
+cString IndexToHMSF(int Index, bool WithFrame = false, int FramesPerSec = FRAMESPERSEC);
       // Converts the given index to a string, optionally containing the frame number.
-int HMSFToIndex(const char *HMSF);
+int HMSFToIndex(const char *HMSF, int FramesPerSec = FRAMESPERSEC);
       // Converts the given string (format: "hh:mm:ss.ff") to an index.
-int SecondsToFrames(int Seconds); //XXX+ ->player???
+int SecondsToFrames(int Seconds, int FramesPerSec = FRAMESPERSEC); //XXX+ ->player???
       // Returns the number of frames corresponding to the given number of seconds.
 
 int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
--- ./svdrp.c	2007-08-25 11:28:26.000000000 +0200
+++ ../vdr-1.5.9/./svdrp.c	2007-08-28 22:25:08.000000000 +0200
@@ -1297,8 +1297,10 @@ void cSVDRP::CmdPLAY(const char *Option)
                  int x = sscanf(option, "%d:%d:%d.%d", &h, &m, &s, &f);
                  if (x == 1)
                     pos = h;
-                 else if (x >= 3)
-                    pos = (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+                 else if (x >= 3) {
+                    int FramesPerSec = cUnbufferedFile::GetFramesPerSec(recording->FileName());
+                    pos = (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
+                    }
                  }
               cResumeFile resume(recording->FileName());
               if (pos <= 0)
--- ./tools.c	2007-08-05 14:18:15.000000000 +0200
+++ ../vdr-1.5.9/./tools.c	2007-08-28 23:27:37.000000000 +0200
@@ -27,6 +27,8 @@ extern "C" {
 #include <utime.h>
 #include "i18n.h"
 #include "thread.h"
+#include "remux.h"
+#include "recording.h"
 
 int SysLogLevel = 3;
 
@@ -1566,6 +1568,112 @@ cUnbufferedFile *cUnbufferedFile::Create
   return File;
 }
 
+int cUnbufferedFile::GetFramesPerSec(const char *FileName)
+{
+  // use this constant as a fallback value
+  int FramesPerSec = FRAMESPERSEC;
+  // open the file an determine frames per second
+  cFileName fn(FileName, false);
+  cUnbufferedFile *f = fn.Open();
+  if (f) {
+     FramesPerSec = f->GetFramesPerSec();
+     fn.Close();
+     }
+  return FramesPerSec;
+}
+
+#define ADD_H264_SUPPORT 1
+
+#ifdef ADD_H264_SUPPORT
+#include "h264parser.h"
+#endif
+
+int cUnbufferedFile::GetFramesPerSec(void)
+{
+  // use this constant as a fallback value
+  int FramesPerSec = FRAMESPERSEC;
+  // rember current file position to restore later
+  off_t OrigPos = curpos;
+  // seek to the beginning and read a chunk of data
+  if (0 == Seek(0, SEEK_SET)) {
+     uchar Data[2048];
+     ssize_t Count = Read(Data, sizeof(Data));
+     if (Count > 0) {
+        // this chunk of data should actually be a PES packet
+        uchar *Limit = Data + Count;
+        int PesPayloadOffset = 0;
+        if (AnalyzePesHeader(Data, Count, PesPayloadOffset) == phMPEG2) {
+           // we need a video stream -- radio recordings use the default
+           if ((Data[3] & 0xF0) == 0xE0) {
+              uchar *p = Data + PesPayloadOffset;
+#ifdef ADD_H264_SUPPORT
+              // check whether this is a H.264 video frame
+              if (cRemux::IsFrameH264(Data, Count)) {
+                 // need to have a H264 parser since picture timing is rather complex
+                 H264::cParser H264parser(false);
+                 // send NAL units to parser until it is able to provide frames per second
+                 while (p < Limit) {
+                       // find next NAL unit
+                       uchar *pNext = (uchar *)memmem(p + 4, Limit - (p + 4), "\x00\x00\x01", 3);
+                       if (!pNext) // just pass the remainder
+                          pNext = Limit; 
+                       H264parser.PutNalUnitData(p, pNext - p);
+                       // process NAL unit and check for frames per second
+                       H264parser.Process();
+                       int FPS = H264parser.Context().GetFramesPerSec();
+                       if (FPS != -1) { // there we are ;-)
+                          FramesPerSec = FPS;
+fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
+                          break;
+                          }
+                       // continue with next NAL unit
+                       p = pNext;   
+                       }
+                 }
+              else {
+#endif
+                 // thanks to cVideoRepacker, the payload starts with a sequence header
+                 if (p + 12 <= Limit) {
+                    if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xB3) {
+                       uint32_t frame_rate_code = p[7] & 0x0F;
+                       uint32_t frame_rate_extension_n = 0;
+                       uint32_t frame_rate_extension_d = 0;
+                       // now we need to have a look at the next startcode, 
+                       // as it might be a sequence extension
+                       p = (uchar *)memmem(p + 12, Limit - (p + 12), "\x00\x00\x01", 3);
+                       if (p && p + 4 < Limit && p[3] == 0xB5) { // extension start code
+                          if (p + 5 < Limit && (p[4] >> 4) == 0x1) { // sequence extension
+                             if (p + 10 < Limit) {
+                                frame_rate_extension_n = (p[9] & 0x60) >> 5;
+                                frame_rate_extension_d = (p[9] & 0x1F);
+                                }
+                             }
+                          }
+                       // calculate frame rate and round it for compatibility
+                       if (0x1 <= frame_rate_code && frame_rate_code <= 0x8) {
+                          static const int n[] = { -1, 24000, 24, 25, 30000, 30, 50, 60000, 60 };
+                          static const int d[] = { -1,  1001,  1,  1,  1001,  1,  1,  1001,  1 };
+                          double frame_rate = n[frame_rate_code] * (frame_rate_extension_n + 1) 
+                                   / (double)(d[frame_rate_code] * (frame_rate_extension_d + 1));
+                          FramesPerSec = (int)frame_rate;
+                          if (frame_rate - FramesPerSec > 0.5)
+                             FramesPerSec++;
+fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
+                          }
+                       }
+                    }
+#ifdef ADD_H264_SUPPORT
+                 }
+#endif
+              }
+           }
+        }
+     }
+  // restore original position
+  Seek(OrigPos, SEEK_SET);
+  return FramesPerSec;
+}
+
 // --- cLockFile -------------------------------------------------------------
 
 #define LOCKFILENAME      ".lock-vdr"
--- ./tools.h	2007-08-25 16:16:39.000000000 +0200
+++ ../vdr-1.5.9/./tools.h	2007-08-28 22:25:08.000000000 +0200
@@ -349,6 +349,8 @@ public:
   ssize_t Read(void *Data, size_t Size);
   ssize_t Write(const void *Data, size_t Size);
   static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  static int GetFramesPerSec(const char *FileName);
+  int GetFramesPerSec(void);
   };
 
 class cLockFile {
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux