From 1aadb31fb355550bb415ed971322a9bcb80f9325 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 12 Apr 2009 22:29:35 +0200 Subject: =?UTF-8?q?Version=201.7.5=20-=20Fixed=20a=20hangup=20when=20repla?= =?UTF-8?q?ying=20a=20TS=20recording=20with=20subtitles=20activated=20(rep?= =?UTF-8?q?orted=20=20=20by=20Timo=20Helkio).=20-=20Fixed=20handling=20the?= =?UTF-8?q?=20'new'=20indicator=20in=20the=20recordings=20menu=20for=20TS?= =?UTF-8?q?=20recordings=20=20=20(thanks=20to=20Derek=20Kelly).=20-=20Adde?= =?UTF-8?q?d=20cap=5Fsys=5Fnice=20to=20the=20capabilities=20that=20are=20n?= =?UTF-8?q?ot=20dropped=20(thanks=20to=20Rolf=20=20=20Ahrenberg).=20-=20Up?= =?UTF-8?q?dated=20the=20Italian=20OSD=20texts=20(thanks=20to=20Diego=20Pi?= =?UTF-8?q?erotto).=20-=20Added=20cRecordingInfo::GetEvent()=20(thanks=20t?= =?UTF-8?q?o=20Marcel=20Unbehaun).=20-=20Improved=20synchronizing=20the=20?= =?UTF-8?q?progress=20display,=20trick=20modes=20and=20subtitle=20display?= =?UTF-8?q?=20=20=20to=20the=20actual=20audio/video.=20This=20now=20works?= =?UTF-8?q?=20independent=20of=20any=20buffer=20sizes=20the=20=20=20output?= =?UTF-8?q?=20device=20might=20use.=20=20=20+=20The=20cBackTrace=20class?= =?UTF-8?q?=20has=20been=20replaced=20with=20cPtsIndex,=20which=20keeps=20?= =?UTF-8?q?track=20=20=20=20=20of=20the=20PTS=20timestamps=20of=20recently?= =?UTF-8?q?=20played=20frames.=20=20=20+=20cDevice::GetSTC()=20is=20now=20?= =?UTF-8?q?required=20to=20deliver=20the=20STC=20even=20in=20trick=20modes?= =?UTF-8?q?.=20=20=20=20=20It=20is=20sufficient=20if=20it=20returns=20the?= =?UTF-8?q?=20PTS=20of=20the=20most=20recently=20presented=20=20=20=20=20a?= =?UTF-8?q?udio/video=20frame.=20=20=20+=20The=20full-featured=20DVB=20car?= =?UTF-8?q?ds=20need=20an=20improved=20firmware=20in=20order=20to=20return?= =?UTF-8?q?=20=20=20=20=20proper=20STC=20values=20in=20trick=20modes=20(th?= =?UTF-8?q?anks=20to=20Oliver=20Endriss=20for=20enhancing=20the=20=20=20?= =?UTF-8?q?=20=20av7110=20firmware).=20-=20Adapted=20cFrameDetector::Analy?= =?UTF-8?q?ze()=20to=20HD=20NTSC=20broadcasts=20that=20split=20frames=20ov?= =?UTF-8?q?er=20=20=20several=20payload=20units=20(thanks=20to=20Derek=20K?= =?UTF-8?q?elly=20for=20reporting=20this=20and=20helping=20in=20=20=20test?= =?UTF-8?q?ing).=20-=20Modified=20cFrameDetector::Analyze()=20to=20make=20?= =?UTF-8?q?it=20process=20whole=20frames=20at=20once,=20so=20=20=20that=20?= =?UTF-8?q?file=20I/O=20overhead=20is=20minimized=20during=20recording=20(?= =?UTF-8?q?reported=20by=20G=C3=BCnter=20=20=20Niedermeier).=20-=20Added?= =?UTF-8?q?=20command=20line=20help=20for=20the=20'-i'=20option.=20-=20Fix?= =?UTF-8?q?ed=20cDvbPlayer::NextFile()=20to=20handle=20files=20larger=20th?= =?UTF-8?q?an=202GB=20(thanks=20to=20Jose=20=20=20Alberto=20Reguero).=20-?= =?UTF-8?q?=20Improved=20replay=20at=20the=20begin=20and=20end=20of=20a=20?= =?UTF-8?q?recording.=20The=20very=20first=20and=20very=20last=20=20=20fra?= =?UTF-8?q?me=20is=20now=20sent=20to=20the=20output=20device=20repeatedly?= =?UTF-8?q?=20until=20GetSTC()=20reports=20that=20it=20=20=20has=20been=20?= =?UTF-8?q?played.=20cDvbPlayer::Action()=20no=20longer=20calls=20DeviceFl?= =?UTF-8?q?ush()=20(thanks=20to=20=20=20Reinhard=20Nissl=20for=20making=20?= =?UTF-8?q?sure=20vdr-xine=20no=20longer=20needs=20this).=20-=20Added=20mi?= =?UTF-8?q?ssing=20'[]'=20to=20the=20delete=20operator=20in=20cMenuEditStr?= =?UTF-8?q?Item::~cMenuEditStrItem().=20-=20Added=20missing=20virtual=20de?= =?UTF-8?q?structor=20to=20cPalette.=20-=20Now=20freeing=20configDirectory?= =?UTF-8?q?=20before=20setting=20it=20to=20a=20new=20value=20in=20=20=20cP?= =?UTF-8?q?lugin::SetConfigDirectory().=20-=20Fixed=20a=20crash=20when=20j?= =?UTF-8?q?umping=20to=20an=20editing=20mark=20in=20an=20audio=20recording?= =?UTF-8?q?.=20-=20Fixed=20the=20'VideoOnly'=20condition=20in=20the=20Play?= =?UTF-8?q?Pes()=20and=20PlayTs()=20calls=20in=20=20=20cDvbPlayer::Action(?= =?UTF-8?q?)=20(thanks=20to=20Reinhard=20Nissl).=20-=20cDevice::PlayTs()?= =?UTF-8?q?=20now=20plays=20as=20many=20TS=20packets=20as=20possible=20in?= =?UTF-8?q?=20one=20call.=20-=20Making=20sure=20any=20floating=20point=20n?= =?UTF-8?q?umbers=20written=20use=20a=20decimal=20point=20(thanks=20to=20?= =?UTF-8?q?=20=20Oliver=20Endriss=20for=20pointing=20out=20a=20problem=20w?= =?UTF-8?q?ith=20the=20F=20record=20in=20the=20info=20file=20of=20=20=20a?= =?UTF-8?q?=20recording).=20-=20Fixed=20detecting=20the=20frame=20rate=20f?= =?UTF-8?q?or=20radio=20recordings.=20-=20Added=20missing=20AUDIO=5FPAUSE/?= =?UTF-8?q?AUDIO=5FCONTINUE=20calls=20to=20cDvbDevice=20(thanks=20to=20Oli?= =?UTF-8?q?ver=20=20=20Endriss).=20-=20No=20longer=20writing=20the=20video?= =?UTF-8?q?=20type=20into=20channels.conf=20if=20VPID=20is=200=20(thanks?= =?UTF-8?q?=20to=20=20=20Oliver=20Endriss=20for=20reporting=20this).=20-?= =?UTF-8?q?=20Improved=20efficiency=20of=20cEIT::cEIT()=20(thanks=20to=20T?= =?UTF-8?q?obias=20Bratfisch).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dvbplayer.c | 271 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 154 insertions(+), 117 deletions(-) (limited to 'dvbplayer.c') diff --git a/dvbplayer.c b/dvbplayer.c index 6e00440..9de0fca 100644 --- a/dvbplayer.c +++ b/dvbplayer.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbplayer.c 2.3 2009/01/25 11:11:39 kls Exp $ + * $Id: dvbplayer.c 2.11 2009/04/05 13:04:33 kls Exp $ */ #include "dvbplayer.h" @@ -16,59 +16,69 @@ #include "thread.h" #include "tools.h" -// --- cBackTrace ------------------------------------------------------------ +// --- cPtsIndex ------------------------------------------------------------- -#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size -#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size -#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents +#define PTSINDEX_ENTRIES 500 -class cBackTrace { +class cPtsIndex { private: - int index[BACKTRACE_ENTRIES]; - int length[BACKTRACE_ENTRIES]; - int pos, num; + struct tPtsIndex { + uint32_t pts; // no need for 33 bit - some devices don't even supply the msb + int index; + }; + tPtsIndex pi[PTSINDEX_ENTRIES]; + int w, r; + int lastFound; + cMutex mutex; public: - cBackTrace(void); + cPtsIndex(void); void Clear(void); - void Add(int Index, int Length); - int Get(bool Forward); + void Put(uint32_t Pts, int Index); + int FindIndex(uint32_t Pts); }; -cBackTrace::cBackTrace(void) +cPtsIndex::cPtsIndex(void) { + lastFound = 0; Clear(); } -void cBackTrace::Clear(void) +void cPtsIndex::Clear(void) { - pos = num = 0; + cMutexLock MutexLock(&mutex); + w = r = 0; } -void cBackTrace::Add(int Index, int Length) +void cPtsIndex::Put(uint32_t Pts, int Index) { - index[pos] = Index; - length[pos] = Length; - if (++pos >= BACKTRACE_ENTRIES) - pos = 0; - if (num < BACKTRACE_ENTRIES) - num++; + cMutexLock MutexLock(&mutex); + pi[w].pts = Pts; + pi[w].index = Index; + w = (w + 1) % PTSINDEX_ENTRIES; + if (w == r) + r = (r + 1) % PTSINDEX_ENTRIES; } -int cBackTrace::Get(bool Forward) +int cPtsIndex::FindIndex(uint32_t Pts) { - int p = pos; - int n = num; - int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ??? - int i = -1; - - while (n && l > 0) { - if (--p < 0) - p = BACKTRACE_ENTRIES - 1; - i = index[p] - 1; - l -= length[p]; - n--; - } - return i; + cMutexLock MutexLock(&mutex); + if (w == r) + return lastFound; // list is empty, let's not jump way off the last known position + uint32_t Delta = 0xFFFFFFFF; + int Index = -1; + for (int i = w; i != r; ) { + if (--i < 0) + i = PTSINDEX_ENTRIES - 1; + uint32_t d = pi[i].pts < Pts ? Pts - pi[i].pts : pi[i].pts - Pts; + if (d > 0x7FFFFFFF) + d = 0xFFFFFFFF - d; // handle rollover + if (d < Delta) { + Delta = d; + Index = pi[i].index; + } + } + lastFound = Index; + return Index; } // --- cNonBlockingFileReader ------------------------------------------------ @@ -183,8 +193,8 @@ bool cNonBlockingFileReader::WaitForDataMs(int msToWait) #define PLAYERBUFSIZE MEGABYTE(1) -// The number of seconds to back up when resuming an interrupted replay session: -#define RESUMEBACKUP 10 +#define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session +#define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame class cDvbPlayer : public cPlayer, cThread { private: @@ -193,7 +203,7 @@ private: static int Speeds[]; cNonBlockingFileReader *nonBlockingFileReader; cRingBufferFrame *ringBuffer; - cBackTrace *backTrace; + cPtsIndex ptsIndex; cFileName *fileName; cIndexFile *index; cUnbufferedFile *replayFile; @@ -204,12 +214,13 @@ private: ePlayModes playMode; ePlayDirs playDir; int trickSpeed; - int readIndex, writeIndex; + int readIndex; + bool readIndependent; cFrame *readFrame; cFrame *playFrame; void TrickSpeed(int Increment); void Empty(void); - bool NextFile(uchar FileNumber = 0, int FileOffset = -1); + bool NextFile(uint16_t FileNumber = 0, off_t FileOffset = -1); int Resume(void); bool Save(void); protected: @@ -242,7 +253,6 @@ cDvbPlayer::cDvbPlayer(const char *FileName) { nonBlockingFileReader = NULL; ringBuffer = NULL; - backTrace = NULL; index = NULL; cRecording Recording(FileName); framesPerSecond = Recording.FramesPerSecond(); @@ -252,7 +262,8 @@ cDvbPlayer::cDvbPlayer(const char *FileName) playMode = pmPlay; playDir = pdForward; trickSpeed = NORMAL_SPEED; - readIndex = writeIndex = -1; + readIndex = -1; + readIndependent = false; readFrame = NULL; playFrame = NULL; isyslog("replay %s", FileName); @@ -269,17 +280,15 @@ cDvbPlayer::cDvbPlayer(const char *FileName) delete index; index = NULL; } - backTrace = new cBackTrace; } cDvbPlayer::~cDvbPlayer() { - Detach(); Save(); + Detach(); delete readFrame; // might not have been stored in the buffer in Action() delete index; delete fileName; - delete backTrace; delete ringBuffer; } @@ -308,18 +317,18 @@ void cDvbPlayer::Empty(void) LOCK_THREAD; if (nonBlockingFileReader) nonBlockingFileReader->Clear(); - if ((readIndex = backTrace->Get(playDir == pdForward)) < 0) - readIndex = writeIndex; + if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once + readIndex = ptsIndex.FindIndex(DeviceGetSTC()); delete readFrame; // might not have been stored in the buffer in Action() readFrame = NULL; playFrame = NULL; ringBuffer->Clear(); - backTrace->Clear(); + ptsIndex.Clear(); DeviceClear(); firstPacket = true; } -bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset) +bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset) { if (FileNumber > 0) replayFile = fileName->SetOffset(FileNumber, FileOffset); @@ -346,7 +355,7 @@ int cDvbPlayer::Resume(void) bool cDvbPlayer::Save(void) { if (index) { - int Index = writeIndex; + int Index = ptsIndex.FindIndex(DeviceGetSTC()); if (Index >= 0) { Index -= int(round(RESUMEBACKUP * framesPerSecond)); if (Index > 0) @@ -384,8 +393,12 @@ void cDvbPlayer::Action(void) int Length = 0; bool Sleep = false; bool WaitingForData = false; + time_t StuckAtEof = 0; + uint32_t LastStc = 0; + int LastReadIFrame = -1; + int SwitchToPlayFrame = 0; - while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) { + while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available())) { if (Sleep) { if (WaitingForData) nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data @@ -403,60 +416,47 @@ void cDvbPlayer::Action(void) if (playMode != pmStill && playMode != pmPause) { if (!readFrame && (replayFile || readIndex >= 0)) { if (!nonBlockingFileReader->Reading()) { - if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) { + if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) { uint16_t FileNumber; off_t FileOffset; bool TimeShiftMode = index->IsStillRecording(); int Index = -1; + readIndependent = false; if (DeviceHasIBPTrickSpeed() && playDir == pdForward) { - if (index->Get(readIndex + 1, &FileNumber, &FileOffset, NULL, &Length)) + if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length)) Index = readIndex + 1; } else { int d = int(round(0.4 * framesPerSecond)); if (playDir != pdForward) d = -d; - Index = index->GetNextIFrame(readIndex + d, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode); + int NewIndex = readIndex + d; + if (NewIndex <= 0 && readIndex > 0) + NewIndex = 1; // make sure the very first frame is delivered + NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode); + if (NewIndex < 0 && TimeShiftMode && playDir == pdForward) + SwitchToPlayFrame = Index; + Index = NewIndex; + readIndependent = true; } if (Index >= 0) { - if (!NextFile(FileNumber, FileOffset)) { - readIndex = Index; - continue; - } - } - else { - if (!TimeShiftMode && playDir == pdForward) { - // hit end of recording: signal end of file but don't change playMode - readIndex = -1; - eof = true; + readIndex = Index; + if (!NextFile(FileNumber, FileOffset)) continue; - } - // hit begin of recording: wait for device buffers to drain - // before changing play mode: - if (!DeviceFlush(100)) - continue; - // can't call Play() here, because those functions may only be - // called from the foreground thread - and we also don't need - // to empty the buffer here - DevicePlay(); - playMode = pmPlay; - playDir = pdForward; - continue; } - readIndex = Index; + else + eof = true; } else if (index) { uint16_t FileNumber; off_t FileOffset; - readIndex++; - if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) { - readIndex = -1; + if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) + readIndex++; + else eof = true; - continue; - } } else // allows replay even if the index file is missing - Length = MAXFRAMESIZE; + Length = MAXFRAMESIZE / TS_SIZE * TS_SIZE;// FIXME: use a linear ringbuffer in this case, and fix cDevice::PlayPes() if (Length == -1) Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex) else if (Length > MAXFRAMESIZE) { @@ -465,19 +465,26 @@ void cDvbPlayer::Action(void) } b = MALLOC(uchar, Length); } - int r = nonBlockingFileReader->Read(replayFile, b, Length); - if (r > 0) { - WaitingForData = false; - readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer - b = NULL; - } - else if (r == 0) - eof = true; - else if (r < 0 && errno == EAGAIN) - WaitingForData = true; - else if (r < 0 && FATALERRNO) { - LOG_ERROR; - break; + if (!eof) { + int r = nonBlockingFileReader->Read(replayFile, b, Length); + if (r > 0) { + WaitingForData = false; + uint32_t Pts = 0; + if (readIndependent) { + Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r); + LastReadIFrame = readIndex; + } + readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts); // hands over b to the ringBuffer + b = NULL; + } + else if (r == 0) + eof = true; + else if (r < 0 && errno == EAGAIN) + WaitingForData = true; + else if (r < 0 && FATALERRNO) { + LOG_ERROR; + break; + } } } @@ -506,6 +513,8 @@ void cDvbPlayer::Action(void) p = playFrame->Data(); pc = playFrame->Count(); if (p) { + if (playFrame->Index() >= 0) + ptsIndex.Put(playFrame->Pts(), playFrame->Index()); if (firstPacket) { if (isPesRecording) { PlayPes(NULL, 0); @@ -520,28 +529,57 @@ void cDvbPlayer::Action(void) if (p) { int w; if (isPesRecording) - w = PlayPes(p, pc, playMode != pmPlay && DeviceIsPlayingVideo()); + w = PlayPes(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo()); else - w = PlayTs(p, TS_SIZE, playMode != pmPlay && DeviceIsPlayingVideo()); + w = PlayTs(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo()); if (w > 0) { p += w; pc -= w; } - else if (w < 0 && FATALERRNO) { + else if (w < 0 && FATALERRNO) LOG_ERROR; - break; - } } if (pc <= 0) { - writeIndex = playFrame->Index(); - backTrace->Add(playFrame->Index(), playFrame->Count()); - ringBuffer->Drop(playFrame); + if (!eof || (playDir != pdForward && playFrame->Index() > 0) || (playDir == pdForward && playFrame->Index() < readIndex)) + ringBuffer->Drop(playFrame); // the very first and last frame are continously repeated to flush data through the device playFrame = NULL; p = NULL; } } else Sleep = true; + + // Handle hitting begin/end of recording: + + if (eof || SwitchToPlayFrame) { + bool SwitchToPlay = false; + uint32_t Stc = DeviceGetSTC(); + if (Stc != LastStc) + StuckAtEof = 0; + else if (!StuckAtEof) + StuckAtEof = time(NULL); + else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) { + if (playDir == pdForward) + break; // automatically stop at end of recording + SwitchToPlay = true; + } + LastStc = Stc; + int Index = ptsIndex.FindIndex(Stc); + if (playDir == pdForward && !SwitchToPlayFrame) { + if (Index >= LastReadIFrame) + break; // automatically stop at end of recording + } + else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame) + SwitchToPlay = true; + if (SwitchToPlay) { + if (!SwitchToPlayFrame) + Empty(); + DevicePlay(); + playMode = pmPlay; + playDir = pdForward; + SwitchToPlayFrame = 0; + } + } } } @@ -614,6 +652,7 @@ void cDvbPlayer::Forward(void) Pause(); break; } + Empty(); // run into pmPause case pmStill: case pmPause: @@ -661,6 +700,7 @@ void cDvbPlayer::Backward(void) Pause(); break; } + Empty(); // run into pmPause case pmStill: case pmPause: { @@ -696,14 +736,14 @@ void cDvbPlayer::SkipSeconds(int Seconds) { if (index && Seconds) { LOCK_THREAD; + int Index = ptsIndex.FindIndex(DeviceGetSTC()); Empty(); - int Index = writeIndex; if (Index >= 0) { Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0); if (Index > 0) Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true); if (Index >= 0) - readIndex = writeIndex = Index - 1; // Action() will first increment it! + readIndex = Index - 1; // Action() will first increment it! } Play(); } @@ -727,25 +767,22 @@ void cDvbPlayer::Goto(int Index, bool Still) if (playMode == pmPause) DevicePlay(); DeviceStillPicture(b, r); + ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index); } playMode = pmStill; } - readIndex = writeIndex = Index; + readIndex = Index; } } bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) { if (index) { - if (playMode == pmStill) - Current = max(readIndex, 0); - else { - Current = max(writeIndex, 0); - if (SnapToIFrame) { - int i1 = index->GetNextIFrame(Current + 1, false); - int i2 = index->GetNextIFrame(Current, true); - Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2; - } + Current = ptsIndex.FindIndex(DeviceGetSTC()); + if (SnapToIFrame) { + int i1 = index->GetNextIFrame(Current + 1, false); + int i2 = index->GetNextIFrame(Current, true); + Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2; } Total = index->Last(); return true; -- cgit v1.2.3