Improved replay at the begin and end of a recording; cDvbPlayer::Action() no longer calls DeviceFlush()

This commit is contained in:
Klaus Schmidinger 2009-04-05 09:08:02 +02:00
parent 1b02cc9c94
commit c886e69bc6
3 changed files with 64 additions and 57 deletions

View File

@ -1200,6 +1200,7 @@ Reinhard Nissl <rnissl@gmx.de>
to cDevice::PlayTs() to cDevice::PlayTs()
for reporting that the PAT/PMT is processed too often, even if its version for reporting that the PAT/PMT is processed too often, even if its version
hasn't changed hasn't changed
for making sure vdr-xine no longer needs cDvbPlayer::Action() to call DeviceFlush()
Richard Robson <richard_robson@beeb.net> Richard Robson <richard_robson@beeb.net>
for reporting freezing replay if a timer starts while in Transfer Mode from the for reporting freezing replay if a timer starts while in Transfer Mode from the

View File

@ -5979,7 +5979,7 @@ Video Disk Recorder Revision History
cDevice class reimplements PlayTs() or PlayPes(), it also needs to make sure this cDevice class reimplements PlayTs() or PlayPes(), it also needs to make sure this
new function works as expected. new function works as expected.
2009-03-28: Version 1.7.5 2009-04-05: Version 1.7.5
- Fixed a hangup when replaying a TS recording with subtitles activated (reported - Fixed a hangup when replaying a TS recording with subtitles activated (reported
by Timo Helkio). by Timo Helkio).
@ -6009,3 +6009,7 @@ Video Disk Recorder Revision History
- Added command line help for the '-i' option. - Added command line help for the '-i' option.
- Fixed cDvbPlayer::NextFile() to handle files larger than 2GB (thanks to Jose - Fixed cDvbPlayer::NextFile() to handle files larger than 2GB (thanks to Jose
Alberto Reguero). 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).

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbplayer.c 2.6 2009/03/28 21:56:56 kls Exp $ * $Id: dvbplayer.c 2.7 2009/04/05 09:05:54 kls Exp $
*/ */
#include "dvbplayer.h" #include "dvbplayer.h"
@ -18,7 +18,7 @@
// --- cPtsIndex ------------------------------------------------------------- // --- cPtsIndex -------------------------------------------------------------
#define PTSINDEX_ENTRIES 100 #define PTSINDEX_ENTRIES 500
class cPtsIndex { class cPtsIndex {
private: private:
@ -194,7 +194,7 @@ bool cNonBlockingFileReader::WaitForDataMs(int msToWait)
#define PLAYERBUFSIZE MEGABYTE(1) #define PLAYERBUFSIZE MEGABYTE(1)
#define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session #define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session
#define MAXSTUCKATEND 3 // max. number of seconds to wait in case the device doesn't play the last frame #define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame
class cDvbPlayer : public cPlayer, cThread { class cDvbPlayer : public cPlayer, cThread {
private: private:
@ -393,9 +393,11 @@ void cDvbPlayer::Action(void)
int Length = 0; int Length = 0;
bool Sleep = false; bool Sleep = false;
bool WaitingForData = false; bool WaitingForData = false;
time_t StuckAtEnd = 0; time_t StuckAtEof = 0;
uint32_t LastStc = 0;
int LastReadIFrame = -1;
while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) { while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available())) {
if (Sleep) { if (Sleep) {
if (WaitingForData) if (WaitingForData)
nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
@ -407,8 +409,6 @@ void cDvbPlayer::Action(void)
if (DevicePoll(Poller, 100)) { if (DevicePoll(Poller, 100)) {
LOCK_THREAD; LOCK_THREAD;
bool HitBegin = false;
bool HitEnd = false;
// Read the next frame from the file: // Read the next frame from the file:
@ -429,7 +429,10 @@ void cDvbPlayer::Action(void)
int d = int(round(0.4 * framesPerSecond)); int d = int(round(0.4 * framesPerSecond));
if (playDir != pdForward) if (playDir != pdForward)
d = -d; 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
Index = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode);
readIndependent = true; readIndependent = true;
} }
if (Index >= 0) { if (Index >= 0) {
@ -437,27 +440,16 @@ void cDvbPlayer::Action(void)
if (!NextFile(FileNumber, FileOffset)) if (!NextFile(FileNumber, FileOffset))
continue; continue;
} }
else { else if (playDir != pdForward || !TimeShiftMode)
if (playDir == pdForward) { eof = true;
if (!TimeShiftMode) {
// hit end of recording: signal end of file but don't change playMode
eof = true;
HitEnd = true;
}
}
else
HitBegin = true; // hit begin of recording - trigger wait until device has replayed the very first frame:
}
} }
else if (index) { else if (index) {
uint16_t FileNumber; uint16_t FileNumber;
off_t FileOffset; off_t FileOffset;
if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
readIndex++; readIndex++;
else { else
eof = true; eof = true;
HitEnd = true;
}
} }
else // allows replay even if the index file is missing else // allows replay even if the index file is missing
Length = MAXFRAMESIZE; Length = MAXFRAMESIZE;
@ -476,7 +468,9 @@ void cDvbPlayer::Action(void)
uint32_t Pts = 0; uint32_t Pts = 0;
if (readIndependent) if (readIndependent)
Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r); Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r);
readFrame = new cFrame(b, -r, ftUnknown, readIndependent ? readIndex : -1, Pts); // hands over b to the ringBuffer readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts); // hands over b to the ringBuffer
if (readIndependent)
LastReadIFrame = readIndex;
b = NULL; b = NULL;
} }
else if (r == 0) else if (r == 0)
@ -529,22 +523,28 @@ void cDvbPlayer::Action(void)
} }
} }
if (p) { if (p) {
int w; //XXX maybe make PlayTs() play as much as possible, until w == 0? would save this extra code here (and the goto)...
if (isPesRecording) while (pc > 0) {
w = PlayPes(p, pc, playMode != pmPlay && DeviceIsPlayingVideo()); int w;
else if (isPesRecording)
w = PlayTs(p, TS_SIZE, playMode != pmPlay && DeviceIsPlayingVideo()); w = PlayPes(p, pc, playMode != pmPlay && DeviceIsPlayingVideo());
if (w > 0) { else
p += w; w = PlayTs(p, TS_SIZE, playMode != pmPlay && DeviceIsPlayingVideo());
pc -= w; if (w > 0) {
} p += w;
else if (w < 0 && FATALERRNO) { pc -= w;
LOG_ERROR; }
break; else if (w == 0)
} break;
else if (w < 0 && FATALERRNO) {
LOG_ERROR;
goto End;
}
}
} }
if (pc <= 0) { if (pc <= 0) {
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; playFrame = NULL;
p = NULL; p = NULL;
} }
@ -554,34 +554,36 @@ void cDvbPlayer::Action(void)
// Handle hitting begin/end of recording: // Handle hitting begin/end of recording:
if (HitBegin || HitEnd) { if (eof) {
if (DeviceFlush(10)) { // give device a chance to display the last frame bool SwitchToPlay = false;
cCondWait::SleepMs(10); // don't get into a tight loop 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;
} }
else LastStc = Stc;
HitBegin = HitEnd = false; int Index = ptsIndex.FindIndex(Stc);
} if (playDir == pdForward) {
if (HitBegin) { if (Index >= LastReadIFrame)
if (ptsIndex.FindIndex(DeviceGetSTC()) <= 0) { break; // automatically stop at end of recording
}
else if (Index <= 0)
SwitchToPlay = true;
if (SwitchToPlay) {
Empty(); Empty();
DevicePlay(); DevicePlay();
playMode = pmPlay; playMode = pmPlay;
playDir = pdForward; playDir = pdForward;
} }
} }
else if (HitEnd) {
if (ptsIndex.FindIndex(DeviceGetSTC()) >= readIndex)
break;
else if (!StuckAtEnd)
StuckAtEnd = time(NULL);
else if (time(NULL) - StuckAtEnd > MAXSTUCKATEND)
break;
}
else
StuckAtEnd = 0;
} }
} }
End:
cNonBlockingFileReader *nbfr = nonBlockingFileReader; cNonBlockingFileReader *nbfr = nonBlockingFileReader;
nonBlockingFileReader = NULL; nonBlockingFileReader = NULL;
delete nbfr; delete nbfr;