diff -Nurp vdr-1.2.6-orig/Makefile vdr-1.2.6/Makefile --- vdr-1.2.6-orig/Makefile 2003-08-09 13:09:45.000000000 +0200 +++ vdr-1.2.6/Makefile 2004-11-22 21:48:42.054952374 +0100 @@ -48,6 +48,10 @@ endif DEFINES += -DREMOTE_$(REMOTE) +ifdef OPTIMIZE_STRIP_AUDIO_PACKETS +DEFINES += -DOPTIMIZE_STRIP_AUDIO_PACKETS=${OPTIMIZE_STRIP_AUDIO_PACKETS} +endif + DEFINES += -D_GNU_SOURCE DEFINES += -DVIDEODIR=\"$(VIDEODIR)\" --- ../vdr-1.2.6-orig/dvbplayer.c 2003-05-24 11:04:26.000000000 +0200 +++ dvbplayer.c 2005-08-28 21:07:01.000000000 +0200 @@ -203,7 +203,7 @@ private: cFrame *playFrame; void TrickSpeed(int Increment); void Empty(void); - void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00); + bool StripAudioPackets(uchar *b, int Length, uchar Except = 0x00); bool NextFile(uchar FileNumber = 0, int FileOffset = -1); int Resume(void); bool Save(void); @@ -314,7 +314,7 @@ void cDvbPlayer::Empty(void) firstPacket = true; } -void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except) +bool cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except) { if (index) { for (int i = 0; i < Length - 6; i++) { @@ -329,8 +329,18 @@ void cDvbPlayer::StripAudioPackets(uchar case 0xC0 ... 0xC1: // audio if (c == 0xC1) canToggleAudioTrack = true; - if (!Except || c != Except) + if (!Except || c != Except) { +#if OPTIMIZE_STRIP_AUDIO_PACKETS > 0 + if (i == 0 && l == Length) + return true; // drop complete frame + b[ i + 3 ] = 0xBE; // padding stream +#if OPTIMIZE_STRIP_AUDIO_PACKETS > 1 + memset(&b[i + 6], 0xFF, min(l, Length-i)-6); +#endif +#else memset(&b[i], 0x00, min(l, Length-i)); +#endif + } break; case 0xE0 ... 0xEF: // video break; @@ -347,6 +357,7 @@ void cDvbPlayer::StripAudioPackets(uchar XXX*/ } } + return false; } bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset) @@ -403,6 +414,202 @@ void cDvbPlayer::Activate(bool On) } } +// --- 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) { dsyslog("dvbplayer thread started (pid=%d)", getpid()); @@ -428,14 +635,15 @@ void cDvbPlayer::Action(void) // Read the next frame from the file: - if (!readFrame && (replayFile >= 0 || readIndex >= 0)) { - if (playMode != pmStill) { + if (playMode != pmStill && playMode != pmPause) { + if (!readFrame && (replayFile >= 0 || readIndex >= 0)) { if (!nonBlockingFileReader->Reading()) { if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) { uchar FileNumber; int FileOffset; int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true); if (Index >= 0) { + Length += IPACKS; // fixIFrame needs next video packet if (!NextFile(FileNumber, FileOffset)) continue; } @@ -478,10 +686,17 @@ 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); if (AudioTrack == 0) - StripAudioPackets(b, r); - readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer - b = NULL; + if (StripAudioPackets(b, r)) { + free(b); //drop complete frame + b = NULL; + } + if (b) { + readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer + b = NULL; + } } else if (r == 0) eof = true; @@ -490,16 +705,16 @@ void cDvbPlayer::Action(void) break; } } - else//XXX - usleep(1); // this keeps the CPU load low - } - // Store the frame in the buffer: + // Store the frame in the buffer: - if (readFrame) { - if (ringBuffer->Put(readFrame)) - readFrame = NULL; + if (readFrame) { + if (ringBuffer->Put(readFrame)) + readFrame = NULL; + } } + else//XXX + usleep(1); // this keeps the CPU load low // Get the next frame from the buffer: @@ -521,7 +736,10 @@ void cDvbPlayer::Action(void) firstPacket = false; } if (AudioTrack > 0) - StripAudioPackets(p, pc, AudioTrack); + if (StripAudioPackets(p, pc, AudioTrack)) { + p = NULL; // drop complete frame + pc = 0; + } } } if (p) { @@ -714,13 +932,39 @@ 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) { - uchar b[MAXFRAMESIZE]; + 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(); - StripAudioPackets(b, r); - DeviceStillPicture(b, r); + if (!StripAudioPackets(b, r)) { + // append sequence end code to get the image shown immediately with softdevices + if (r > 6 && (b[3] & 0xF0) == 0xE0) { // make sure to append it only to a video packet + b[r++] = 0x00; + b[r++] = 0x00; + b[r++] = 0x01; + b[r++] = b[3]; + if (b[6] & 0x80) { // MPEG 2 + b[r++] = 0x00; + b[r++] = 0x07; + b[r++] = 0x80; + b[r++] = 0x00; + b[r++] = 0x00; + } + else { // MPEG 1 + b[r++] = 0x00; + b[r++] = 0x05; + b[r++] = 0x0F; + } + b[r++] = 0x00; + b[r++] = 0x00; + b[r++] = 0x01; + b[r++] = 0xB7; + } + DeviceStillPicture(b, r); + } } playMode = pmStill; }