--- ../vdr-1.3.31-orig/dvbplayer.c	2005-08-14 12:52:45.000000000 +0200
+++ dvbplayer.c	2005-08-28 20:28:52.000000000 +0200
@@ -349,6 +349,202 @@ void cDvbPlayer::Activate(bool On)
      Cancel(9);
 }
 
+// --- BEGIN fix for I frames  -------------------------------------------
+// 
+//  Prior to the introduction of cVideoRepacker, VDR didn't start a new
+//  PES packet when a new frame started. So, it was likely that the tail
+//  of an I frame was at the beginning of the packet which started the
+//  following B frame. Due to the organisation of VDR's index file, VDR
+//  typically didn't read the tail of the I frame and therefore caused
+//  softdevice plugins to not render such a frame as it was incomplete,
+//  e. g. when moving cutting marks.
+//
+//  The following code tries to fix incomplete I frames for recordings
+//  made prior to the introdcution of cVideoRepacker, to be able to
+//  edit cutting marks for example with softdevice plugins like vdr-xine.
+//
+
+#if VDRVERSNUM < 10331
+
+enum ePesHeader {
+  phNeedMoreData = -1,
+  phInvalid = 0,
+  phMPEG1 = 1,
+  phMPEG2 = 2
+  };
+
+static ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL)
+{
+  if (Count < 7)
+     return phNeedMoreData; // too short
+
+  if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
+     if (Count < 9)
+        return phNeedMoreData; // too short
+
+     PesPayloadOffset = 6 + 3 + Data[8];
+     if (Count < PesPayloadOffset)
+        return phNeedMoreData; // too short
+
+     if (ContinuationHeader)
+        *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
+
+     return phMPEG2; // MPEG 2
+     }
+
+  // check for MPEG 1 ...
+  PesPayloadOffset = 6;
+
+  // skip up to 16 stuffing bytes
+  for (int i = 0; i < 16; i++) {
+      if (Data[PesPayloadOffset] != 0xFF)
+         break;
+
+      if (Count <= ++PesPayloadOffset)
+         return phNeedMoreData; // too short
+      }
+
+  // skip STD_buffer_scale/size
+  if ((Data[PesPayloadOffset] & 0xC0) == 0x40) {
+     PesPayloadOffset += 2;
+
+     if (Count <= PesPayloadOffset)
+        return phNeedMoreData; // too short
+     }
+
+  if (ContinuationHeader)
+     *ContinuationHeader = false;
+
+  if ((Data[PesPayloadOffset] & 0xF0) == 0x20) {
+     // skip PTS only
+     PesPayloadOffset += 5;
+     }
+  else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) {
+     // skip PTS and DTS
+     PesPayloadOffset += 10;
+     }
+  else if (Data[PesPayloadOffset] == 0x0F) {
+     // continuation header
+     PesPayloadOffset++;
+
+     if (ContinuationHeader)
+        *ContinuationHeader = true;
+     }
+  else
+     return phInvalid; // unknown
+
+  if (Count < PesPayloadOffset)
+     return phNeedMoreData; // too short
+
+  return phMPEG1; // MPEG 1
+}
+
+#endif
+
+static uchar *findStartCode(uchar *Data, int Length, int &PesPayloadOffset)
+{
+  uchar *limit = Data + Length;
+  if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid)
+     return 0; // neither MPEG1 nor MPEG2
+
+  Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01
+  while (Data < limit) {
+        // possible start codes that appear before/after picture data
+        // 00 00 01 B3: sequence header code
+        // 00 00 01 B8: group start code
+        // 00 00 01 00: picture start code
+        // 00 00 01 B7: sequence end code
+        if (0x01 == Data[-1] && (0xB3 == Data[0] || 0xB8 == Data[0] || 0x00 == Data[0] || 0xB7 == Data[0]) && 0x00 == Data[-2] && 0x00 == Data[-3])
+            return Data - 3;
+        Data++;
+        }
+
+  return 0;
+}
+
+static void fixIFrameHead(uchar *Data, int Length)
+{
+  int pesPayloadOffset = 0;
+  uchar *p = findStartCode(Data, Length, pesPayloadOffset);
+  if (!p) {
+     esyslog("fixIframeHead: start code not found!\n");
+     return;
+     }
+
+  Data += pesPayloadOffset; // move to video payload
+  if (Data < p)
+     memset(Data, 0, p - Data); // zero preceeding bytes
+}
+
+static int fixIFrameTail(uchar *Data, int Length)
+{
+  int pesPayloadOffset = 0;
+  uchar *p = findStartCode(Data, Length, pesPayloadOffset);
+  if (!p) {
+     esyslog("fixIframeTail: start code not found!\n");
+     return Length;
+     }
+
+  // is this PES packet required?
+  uchar *videoPayload = Data + pesPayloadOffset;
+  if (videoPayload >= p)
+     return 0; // no
+  
+  // adjust PES length
+  int lenPES = (p - Data);
+  Data[4] = (lenPES - 6) >> 8;
+  Data[5] = (lenPES - 6) & 0xFF;
+
+  return lenPES;
+}
+
+#define IPACKS 2048 // originally defined in remux.c
+
+static void fixIFrame(uchar *Data, int &Length, const int OriginalLength)
+{
+  int done = 0;
+
+  while (done < Length) {
+        if (0x00 != Data[0] || 0x00 != Data[1] || 0x01 != Data[2]) {
+           esyslog("fixIFrame: PES start code not found at offset %d (data length: %d, original length: %d)!", done, Length, OriginalLength);
+           if (Length > OriginalLength) // roll back additional data
+              Length = OriginalLength;
+           return;
+           }
+
+        int lenPES = 6 + Data[4] * 256 + Data[5];
+        if (0xBA == Data[3]) { // pack header has fixed length
+           if (0x00 == (0xC0 & Data[4]))
+              lenPES = 12; // MPEG1
+           else
+              lenPES = 14 + (Data[13] & 0x07); // MPEG2
+           }
+        else if (0xB9 == Data[3]) // stream end has fixed length
+           lenPES = 4;
+        else if (0xE0 == (0xF0 & Data[3])) { // video packet
+           int todo = Length - done;
+           int bite = (lenPES < todo) ? lenPES : todo;
+           if (0 == done) // first packet
+              fixIFrameHead(Data, bite);
+           else if (done >= OriginalLength) { // last packet
+              Length = done + fixIFrameTail(Data, bite);
+              return;
+              }
+           }
+        else if (0 == done && 0xC0 == (0xE0 & Data[3])) {
+           // if the first I frame packet is an audio packet then this is a radio recording: don't touch it!
+           if (Length > OriginalLength) // roll back additional data
+              Length = OriginalLength;
+           return;
+           }
+
+        done += lenPES;
+        Data += lenPES;
+        }
+}
+
+// --- END fix for I frames  ---------------------------------------------
+
 void cDvbPlayer::Action(void)
 {
   uchar *b = NULL;
@@ -385,6 +581,7 @@ void cDvbPlayer::Action(void)
                        if (Index >= 0) {
                           if (!NextFile(FileNumber, FileOffset))
                              continue;
+                          Length += IPACKS; // fixIFrame needs next video packet
                           }
                        else {
                           // hit begin of recording: wait for device buffers to drain
@@ -423,6 +620,8 @@ void cDvbPlayer::Action(void)
                     }
                  int r = nonBlockingFileReader->Read(replayFile, b, Length);
                  if (r > 0) {
+                    if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
+                       fixIFrame(b, r, Length - IPACKS);
                     readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
                     b = NULL;
                     }
@@ -655,9 +854,11 @@ void cDvbPlayer::Goto(int Index, bool St
      int FileOffset, Length;
      Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
      if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
+        Length += IPACKS; // fixIFrame needs next video packet
         uchar b[MAXFRAMESIZE + 4 + 5 + 4];
         int r = ReadFrame(replayFile, b, Length, sizeof(b));
         if (r > 0) {
+           fixIFrame(b, r, Length - IPACKS);
            if (playMode == pmPause)
               DevicePlay();
            // append sequence end code to get the image shown immediately with softdevices
