diff options
author | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2009-04-12 22:29:35 +0200 |
---|---|---|
committer | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2009-04-12 11:39:00 +0200 |
commit | 1aadb31fb355550bb415ed971322a9bcb80f9325 (patch) | |
tree | 6a6fb763f2c56073e91e3052d20aa20695fa2667 /dvbplayer.c | |
parent | 084e16c057ab195a76c2117c631a2fe10a904238 (diff) | |
download | vdr-patch-lnbsharing-1aadb31fb355550bb415ed971322a9bcb80f9325.tar.gz vdr-patch-lnbsharing-1aadb31fb355550bb415ed971322a9bcb80f9325.tar.bz2 |
Version 1.7.5vdr-1.7.5
- Fixed a hangup when replaying a TS recording with subtitles activated (reported
by Timo Helkio).
- Fixed handling the 'new' indicator in the recordings menu for TS recordings
(thanks to Derek Kelly).
- Added cap_sys_nice to the capabilities that are not dropped (thanks to Rolf
Ahrenberg).
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- Added cRecordingInfo::GetEvent() (thanks to Marcel Unbehaun).
- Improved synchronizing the progress display, trick modes and subtitle display
to the actual audio/video. This now works independent of any buffer sizes the
output device might use.
+ The cBackTrace class has been replaced with cPtsIndex, which keeps track
of the PTS timestamps of recently played frames.
+ cDevice::GetSTC() is now required to deliver the STC even in trick modes.
It is sufficient if it returns the PTS of the most recently presented
audio/video frame.
+ The full-featured DVB cards need an improved firmware in order to return
proper STC values in trick modes (thanks to Oliver Endriss for enhancing the
av7110 firmware).
- Adapted cFrameDetector::Analyze() to HD NTSC broadcasts that split frames over
several payload units (thanks to Derek Kelly for reporting this and helping in
testing).
- Modified cFrameDetector::Analyze() to make it process whole frames at once, so
that file I/O overhead is minimized during recording (reported by Günter
Niedermeier).
- Added command line help for the '-i' option.
- Fixed cDvbPlayer::NextFile() to handle files larger than 2GB (thanks to Jose
Alberto Reguero).
- Improved replay at the begin and end of a recording. The very first and very last
frame is now sent to the output device repeatedly until GetSTC() reports that it
has been played. cDvbPlayer::Action() no longer calls DeviceFlush() (thanks to
Reinhard Nissl for making sure vdr-xine no longer needs this).
- Added missing '[]' to the delete operator in cMenuEditStrItem::~cMenuEditStrItem().
- Added missing virtual destructor to cPalette.
- Now freeing configDirectory before setting it to a new value in
cPlugin::SetConfigDirectory().
- Fixed a crash when jumping to an editing mark in an audio recording.
- Fixed the 'VideoOnly' condition in the PlayPes() and PlayTs() calls in
cDvbPlayer::Action() (thanks to Reinhard Nissl).
- cDevice::PlayTs() now plays as many TS packets as possible in one call.
- Making sure any floating point numbers written use a decimal point (thanks to
Oliver Endriss for pointing out a problem with the F record in the info file of
a recording).
- Fixed detecting the frame rate for radio recordings.
- Added missing AUDIO_PAUSE/AUDIO_CONTINUE calls to cDvbDevice (thanks to Oliver
Endriss).
- No longer writing the video type into channels.conf if VPID is 0 (thanks to
Oliver Endriss for reporting this).
- Improved efficiency of cEIT::cEIT() (thanks to Tobias Bratfisch).
Diffstat (limited to 'dvbplayer.c')
-rw-r--r-- | dvbplayer.c | 271 |
1 files changed, 154 insertions, 117 deletions
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; |