From 72d342ce02cbeddfccf1c56fc0e614dfbe569608 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 12 Jun 2011 14:06:11 +0200 Subject: [PATCH] Fixed detecting frames in case the Picture Start Code or Access Unit Delimiter extends over TS packet boundaries (cont'd) --- HISTORY | 3 +++ recorder.c | 12 ++++++--- recording.c | 15 ++++++++--- remux.c | 71 +++++++++++++++++++++++++++++++---------------------- remux.h | 15 +++++++---- 5 files changed, 75 insertions(+), 41 deletions(-) diff --git a/HISTORY b/HISTORY index 5064596e..00c66b90 100644 --- a/HISTORY +++ b/HISTORY @@ -6634,6 +6634,9 @@ Video Disk Recorder Revision History channel is being received with. - Fixed detecting frames in case the Picture Start Code or Access Unit Delimiter extends over TS packet boundaries (reported by Johan Andersson). + In order to fix this, the semantics of cFrameDetector had to be changed a little. + See cRecorder::Action() and cIndexFileGenerator::Action() on how to use the new + cFrameDetector::NewPayload() function. - The frame detector now only starts collecting PTS values after it has seen the first I-frame, otherwise it might get MaxPtsValues values and stop analyzing even though the incoming data is still garbage (reported by Derek Kelly). diff --git a/recorder.c b/recorder.c index f7c3fb03..ee7a1e9b 100644 --- a/recorder.c +++ b/recorder.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 2.9 2010/12/27 11:35:46 kls Exp $ + * $Id: recorder.c 2.10 2011/06/12 13:35:20 kls Exp $ */ #include "recorder.h" @@ -31,7 +31,7 @@ cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority SpinUpDisk(FileName); - ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "Recorder"); + ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE, true, "Recorder"); ringBuffer->SetTimeouts(0, 100); int Pid = Channel->Vpid(); @@ -119,6 +119,8 @@ void cRecorder::Action(void) time_t t = time(NULL); bool InfoWritten = false; bool FirstIframeSeen = false; + int FileNumber = 0; + off_t FrameOffset = -1; while (Running()) { int r; uchar *b = ringBuffer->Get(r); @@ -139,12 +141,16 @@ void cRecorder::Action(void) } InfoWritten = true; } + if (frameDetector->NewPayload()) { + FileNumber = fileName->Number(); + FrameOffset = fileSize; + } if (FirstIframeSeen || frameDetector->IndependentFrame()) { FirstIframeSeen = true; // start recording with the first I-frame if (!NextFile()) break; if (index && frameDetector->NewFrame()) - index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize); + index->Write(frameDetector->IndependentFrame(), FileNumber, FrameOffset); if (frameDetector->IndependentFrame()) { recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE); fileSize += TS_SIZE; diff --git a/recording.c b/recording.c index d4cf9c34..a8b40f2d 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 2.30 2011/04/17 13:53:11 kls Exp $ + * $Id: recording.c 2.31 2011/06/12 13:04:28 kls Exp $ */ #include "recording.h" @@ -1403,11 +1403,12 @@ void cIndexFileGenerator::Action(void) bool Rewind = false; cFileName FileName(recordingName, false); cUnbufferedFile *ReplayFile = FileName.Open(); - cRingBufferLinear Buffer(IFG_BUFFER_SIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE); + cRingBufferLinear Buffer(IFG_BUFFER_SIZE, TS_SIZE); cPatPmtParser PatPmtParser; cFrameDetector FrameDetector; cIndexFile IndexFile(recordingName, true); int BufferChunks = KILOBYTE(1); // no need to read a lot at the beginning when parsing PAT/PMT + int FileNumber = 0; off_t FileSize = 0; off_t FrameOffset = -1; Skins.QueueMessage(mtInfo, tr("Regenerating index file")); @@ -1424,12 +1425,18 @@ void cIndexFileGenerator::Action(void) if (Data) { if (FrameDetector.Synced()) { // Step 3 - generate the index: - if (TsPid(Data) == PATPID) + if (FrameOffset < 0 && TsPid(Data) == PATPID) { + FileNumber = FileName.Number(); FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame + } int Processed = FrameDetector.Analyze(Data, Length); if (Processed > 0) { + if (FrameDetector.NewPayload() && FrameOffset < 0) { + FileNumber = FileName.Number(); + FrameOffset = FileSize; + } if (FrameDetector.NewFrame()) { - IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize); + IndexFile.Write(FrameDetector.IndependentFrame(), FileNumber, FrameOffset); FrameOffset = -1; } FileSize += Processed; diff --git a/remux.c b/remux.c index a6c3c25e..318334fd 100644 --- a/remux.c +++ b/remux.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.c 2.55 2011/06/11 11:35:18 kls Exp $ + * $Id: remux.c 2.56 2011/06/12 13:51:59 kls Exp $ */ #include "remux.h" @@ -781,7 +781,8 @@ cFrameDetector::cFrameDetector(int Pid, int Type) { SetPid(Pid, Type); synced = false; - newFrame = independentFrame = false; + newPayload = newFrame = independentFrame = false; + frameTypeOffset = -1; numPtsValues = 0; numFrames = 0; numIFrames = 0; @@ -808,7 +809,8 @@ void cFrameDetector::SetPid(int Pid, int Type) void cFrameDetector::Reset(void) { - newFrame = independentFrame = false; + newPayload = newFrame = independentFrame = false; + frameTypeOffset = -1; payloadUnitOfFrame = 0; scanning = false; scanner = EMPTY_SCANNER; @@ -816,9 +818,8 @@ void cFrameDetector::Reset(void) int cFrameDetector::Analyze(const uchar *Data, int Length) { - int SeenPayloadStart = false; int Processed = 0; - newFrame = independentFrame = false; + newPayload = newFrame = independentFrame = false; while (Length >= TS_SIZE) { if (Data[0] != TS_SYNC_BYTE) { int Skipped = 1; @@ -831,11 +832,8 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) int Pid = TsPid(Data); if (Pid == pid) { if (TsPayloadStart(Data)) { - SeenPayloadStart = true; if (synced && Processed) - return Processed; - if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE) - return Processed; // need more data, in case the frame type is not stored in the first TS packet + return Processed; // flush everything before this new payload if (framesPerSecond <= 0.0) { // frame rate unknown, so collect a sequence of PTS values: if (numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames @@ -900,35 +898,41 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) if (scanning) { int PayloadOffset = TsPayloadOffset(Data); if (TsPayloadStart(Data)) { + if (synced && Processed) + return Processed; // flush everything before this new payload + newPayload = true; + scanner = EMPTY_SCANNER; PayloadOffset += PesPayloadOffset(Data + PayloadOffset); if (!framesPerPayloadUnit) framesPerPayloadUnit = framesInPayloadUnit; if (DebugFrames && !synced) dbgframes("/"); } - int PacketsForFrameDetector = MIN_TS_PACKETS_FOR_FRAME_DETECTOR; for (int i = PayloadOffset; scanning && i < TS_SIZE; i++) { - scanner <<= 8; - scanner |= Data[i]; - if (i == TS_SIZE - 1) { // it was the last byte in this TS packet - if (SeenPayloadStart && --PacketsForFrameDetector > 0) { - Data += TS_SIZE; - Length -= TS_SIZE; - Processed += TS_SIZE; - if (Length < TS_SIZE) - return Processed; - i = TsPayloadOffset(Data) - 1; // one before the first payload byte of the next TS packet - } + if (frameTypeOffset < 0) { + scanner <<= 8; + scanner |= Data[i]; } + else + frameTypeOffset += PayloadOffset; switch (type) { case 0x01: // MPEG 1 video case 0x02: // MPEG 2 video if (scanner == 0x00000100) { // Picture Start Code + if (frameTypeOffset < 0) { + frameTypeOffset = i + 2; + if (frameTypeOffset >= TS_SIZE) { // the byte to check is in the next TS packet + frameTypeOffset -= TS_SIZE; + if (!synced) + dbgframes("%d>", frameTypeOffset); + break; + } + } scanner = EMPTY_SCANNER; - if (synced && !SeenPayloadStart && Processed) - return Processed; // flush everything before this new frame newFrame = true; - independentFrame = ((Data[i + 2] >> 3) & 0x07) == 1; // I-Frame + uchar FrameType = (Data[frameTypeOffset] >> 3) & 0x07; + frameTypeOffset = -1; + independentFrame = FrameType == 1; // I-Frame if (synced) { if (framesPerPayloadUnit <= 1) scanning = false; @@ -939,7 +943,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) numIFrames++; if (numIFrames == 1) numFrames++; - dbgframes("%d ", (Data[i + 2] >> 3) & 0x07); + dbgframes("%u ", FrameType); } if (synced) return Processed + TS_SIZE; // flag this new frame @@ -947,11 +951,20 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) break; case 0x1B: // MPEG 4 video if (scanner == 0x00000109) { // Access Unit Delimiter + if (frameTypeOffset < 0) { + frameTypeOffset = i + 1; + if (frameTypeOffset >= TS_SIZE) { // the byte to check is in the next TS packet + frameTypeOffset -= TS_SIZE; + if (!synced) + dbgframes("%d>", frameTypeOffset); + break; + } + } scanner = EMPTY_SCANNER; - if (synced && !SeenPayloadStart && Processed) - return Processed; // flush everything before this new frame newFrame = true; - independentFrame = Data[i + 1] == 0x10; + uchar FrameType = Data[frameTypeOffset]; + frameTypeOffset = -1; + independentFrame = FrameType == 0x10; if (synced) { if (framesPerPayloadUnit < 0) { payloadUnitOfFrame = (payloadUnitOfFrame + 1) % -framesPerPayloadUnit; @@ -969,7 +982,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) numIFrames++; if (numIFrames == 1) numFrames++; - dbgframes("%02X ", Data[i + 1]); + dbgframes("%02X ", FrameType); } if (synced) return Processed + TS_SIZE; // flag this new frame diff --git a/remux.h b/remux.h index 0e060b7e..3204bb41 100644 --- a/remux.h +++ b/remux.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.h 2.29 2011/05/21 09:53:54 kls Exp $ + * $Id: remux.h 2.30 2011/06/12 12:49:17 kls Exp $ */ #ifndef __REMUX_H @@ -297,7 +297,7 @@ public: ~cTsToPes(); void PutTs(const uchar *Data, int Length); ///< Puts the payload data of the single TS packet at Data into the converter. - ///< Length is always 188. + ///< Length is always TS_SIZE. ///< If the given TS packet starts a new PES payload packet, the converter ///< will be automatically reset. Any packets before the first one that starts ///< a new PES payload packet will be ignored. @@ -336,16 +336,16 @@ void PesDump(const char *Name, const u_char *Data, int Length); // Frame detector: -#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 2 - class cFrameDetector { private: enum { MaxPtsValues = 150 }; int pid; int type; bool synced; + bool newPayload; bool newFrame; bool independentFrame; + int frameTypeOffset; uint32_t ptsValues[MaxPtsValues]; // 32 bit is enough - we only need the delta int numPtsValues; int numFrames; @@ -371,12 +371,17 @@ public: ///< the frame detector for actual work. int Analyze(const uchar *Data, int Length); ///< Analyzes the TS packets pointed to by Data. Length is the number of - ///< bytes Data points to, and must be a multiple of 188. + ///< bytes Data points to, and must be a multiple of TS_SIZE. ///< Returns the number of bytes that have been analyzed. ///< If the return value is 0, the data was not sufficient for analyzing and ///< Analyze() needs to be called again with more actual data. bool Synced(void) { return synced; } ///< Returns true if the frame detector has synced on the data stream. + bool NewPayload(void) { return newPayload; } + ///< Returns true if the data given to the last call to Analyze() started a + ///< new payload. The caller should remember the current file offset in + ///< order to be able to generate an index entry later, when NewFrame() + ///< returns true. bool NewFrame(void) { return newFrame; } ///< Returns true if the data given to the last call to Analyze() started a ///< new frame.