From patchwork Tue Aug 28 22:35:02 2007 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Reinhard Nissl X-Patchwork-Id: 12519 Received: from mail.gmx.net ([213.165.64.20]) by www.linuxtv.org with smtp (Exim 4.63) (envelope-from ) id 1IQ9eq-0004ET-05 for vdr@linuxtv.org; Wed, 29 Aug 2007 00:35:36 +0200 Received: (qmail invoked by alias); 28 Aug 2007 22:35:03 -0000 Received: from p54933871.dip0.t-ipconnect.de (EHLO [192.168.101.15]) [84.147.56.113] by mail.gmx.net (mp048) with SMTP; 29 Aug 2007 00:35:03 +0200 X-Authenticated: #527675 X-Provags-ID: V01U2FsdGVkX19b8UZQIltbBUR8abDuvJP9CJT2LkKRDVMUFBcuW9 yLMrHVSUHtSayE Message-ID: <46D4A316.8090409@gmx.de> Date: Wed, 29 Aug 2007 00:35:02 +0200 From: Reinhard Nissl User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20060911 SUSE/1.5.0.12-3.4 Thunderbird/1.5.0.12 Mnenhy/0.7.4.666 MIME-Version: 1.0 To: VDR Mailing List X-Y-GMX-Trusted: 0 Subject: [vdr] [ANNOUNCE] H.264 updates for VDR-1.5.9 X-BeenThere: vdr@linuxtv.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: VDR Mailing List List-Id: VDR Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 28 Aug 2007 22:35:36 -0000 Status: O X-Status: X-Keywords: X-UID: 13961 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. --- ./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 #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 {