summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS2
-rw-r--r--HISTORY13
-rw-r--r--device.h9
-rw-r--r--dvbplayer.c235
-rw-r--r--player.h3
-rw-r--r--remux.c17
-rw-r--r--remux.h6
-rw-r--r--ringbuffer.c5
-rw-r--r--ringbuffer.h6
9 files changed, 186 insertions, 110 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 3a1e8ee0..032b7f5d 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -662,6 +662,8 @@ Oliver Endriss <o.endriss@gmx.de>
Transfer Mode
for providing a driver patch that allows direct replaying of TS video on full-featured
DVB cards
+ for improving the firmware of FF DVB cards to allow getting the current STC value
+ even in trick modes
Reinhard Walter Buchner <rw.buchner@freenet.de>
for adding some satellites to 'sources.conf'
diff --git a/HISTORY b/HISTORY
index a7865959..8e8ec2f7 100644
--- a/HISTORY
+++ b/HISTORY
@@ -5979,7 +5979,7 @@ Video Disk Recorder Revision History
cDevice class reimplements PlayTs() or PlayPes(), it also needs to make sure this
new function works as expected.
-2009-02-28: Version 1.7.5
+2009-03-13: Version 1.7.5
- Fixed a hangup when replaying a TS recording with subtitles activated (reported
by Timo Helkio).
@@ -5989,3 +5989,14 @@ Video Disk Recorder Revision History
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).
diff --git a/device.h b/device.h
index 67c9be44..4648149c 100644
--- a/device.h
+++ b/device.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.h 2.6 2009/01/25 11:04:39 kls Exp $
+ * $Id: device.h 2.7 2009/03/01 11:20:34 kls Exp $
*/
#ifndef __DEVICE_H
@@ -543,6 +543,13 @@ public:
///< Gets the current System Time Counter, which can be used to
///< synchronize audio and video. If this device is unable to
///< provide the STC, -1 will be returned.
+ ///< The value returned doesn't need to be an actual "clock" value,
+ ///< it is sufficient if it holds the PTS (Presentation Time Stamp) of
+ ///< the most recently presented frame. A proper value must be returned
+ ///< in normal replay mode as well as in any trick modes (like slow motion,
+ ///< fast forward/rewind).
+ ///< Only the lower 32 bit of this value are actually used, since some
+ ///< devices can't handle the msb correctly.
virtual bool IsPlayingVideo(void) const { return isPlayingVideo; }
///< \return Returns true if the currently attached player has delivered
///< any video packets.
diff --git a/dvbplayer.c b/dvbplayer.c
index 6e004403..c206cac3 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.4 2009/03/13 14:42:56 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 100
-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 MAXSTUCKATEND 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,7 +214,8 @@ private:
ePlayModes playMode;
ePlayDirs playDir;
int trickSpeed;
- int readIndex, writeIndex;
+ int readIndex;
+ bool readIndependent;
cFrame *readFrame;
cFrame *playFrame;
void TrickSpeed(int Increment);
@@ -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,13 +317,13 @@ 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;
}
@@ -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,6 +393,7 @@ void cDvbPlayer::Action(void)
int Length = 0;
bool Sleep = false;
bool WaitingForData = false;
+ time_t StuckAtEnd = 0;
while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) {
if (Sleep) {
@@ -397,6 +407,8 @@ void cDvbPlayer::Action(void)
if (DevicePoll(Poller, 100)) {
LOCK_THREAD;
+ bool HitBegin = false;
+ bool HitEnd = false;
// Read the next frame from the file:
@@ -408,8 +420,9 @@ void cDvbPlayer::Action(void)
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 {
@@ -417,42 +430,33 @@ void cDvbPlayer::Action(void)
if (playDir != pdForward)
d = -d;
Index = index->GetNextIFrame(readIndex + d, playDir == pdForward, &FileNumber, &FileOffset, &Length, TimeShiftMode);
+ readIndependent = true;
}
if (Index >= 0) {
- if (!NextFile(FileNumber, FileOffset)) {
- readIndex = Index;
+ readIndex = Index;
+ if (!NextFile(FileNumber, FileOffset))
continue;
- }
}
else {
- if (!TimeShiftMode && playDir == pdForward) {
- // hit end of recording: signal end of file but don't change playMode
- readIndex = -1;
- eof = true;
- continue;
+ if (playDir == pdForward) {
+ if (!TimeShiftMode) {
+ // hit end of recording: signal end of file but don't change playMode
+ eof = true;
+ HitEnd = true;
+ }
}
- // 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;
+ else
+ HitBegin = true; // hit begin of recording - trigger wait until device has replayed the very first frame:
}
- readIndex = Index;
}
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;
+ HitEnd = true;
}
}
else // allows replay even if the index file is missing
@@ -465,19 +469,24 @@ 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);
+ readFrame = new cFrame(b, -r, ftUnknown, readIndependent ? readIndex : -1, 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 +515,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);
@@ -533,8 +544,6 @@ void cDvbPlayer::Action(void)
}
}
if (pc <= 0) {
- writeIndex = playFrame->Index();
- backTrace->Add(playFrame->Index(), playFrame->Count());
ringBuffer->Drop(playFrame);
playFrame = NULL;
p = NULL;
@@ -542,6 +551,31 @@ void cDvbPlayer::Action(void)
}
else
Sleep = true;
+
+ // Handle hitting begin/end of recording:
+
+ if (HitBegin || HitEnd) {
+ if (DeviceFlush(10)) // give device a chance to display the last frame
+ cCondWait::SleepMs(10); // don't get into a tight loop
+ }
+ if (HitBegin) {
+ if (ptsIndex.FindIndex(DeviceGetSTC()) <= 0) {
+ Empty();
+ DevicePlay();
+ playMode = pmPlay;
+ 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;
}
}
@@ -614,6 +648,7 @@ void cDvbPlayer::Forward(void)
Pause();
break;
}
+ Empty();
// run into pmPause
case pmStill:
case pmPause:
@@ -661,6 +696,7 @@ void cDvbPlayer::Backward(void)
Pause();
break;
}
+ Empty();
// run into pmPause
case pmStill:
case pmPause: {
@@ -696,14 +732,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 +763,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;
diff --git a/player.h b/player.h
index d252c574..77d00d5e 100644
--- a/player.h
+++ b/player.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: player.h 2.3 2009/01/25 11:03:44 kls Exp $
+ * $Id: player.h 2.4 2009/03/08 12:29:10 kls Exp $
*/
#ifndef __PLAYER_H
@@ -34,6 +34,7 @@ protected:
void DeviceMute(void) { if (device) device->Mute(); }
void DeviceSetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) { if (device) device->SetVideoDisplayFormat(VideoDisplayFormat); }
void DeviceStillPicture(const uchar *Data, int Length) { if (device) device->StillPicture(Data, Length); }
+ uint64_t DeviceGetSTC(void) { return device ? device->GetSTC() : -1; }
void Detach(void);
virtual void Activate(bool On) {}
// This function is called right after the cPlayer has been attached to
diff --git a/remux.c b/remux.c
index b0086214..a22c024b 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.13 2009/01/24 13:44:45 kls Exp $
+ * $Id: remux.c 2.14 2009/03/08 12:12:17 kls Exp $
*/
#include "remux.h"
@@ -109,6 +109,21 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
dsyslog("SetBrokenLink: no video packet in frame");
}
+// --- Some TS handling tools ------------------------------------------------
+
+int64_t TsGetPts(const uchar *p, int l)
+{
+ // Find the first packet with a PTS and use it:
+ while (l > 0) {
+ const uchar *d = p;
+ if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d))
+ return PesGetPts(d);
+ p += TS_SIZE;
+ l -= TS_SIZE;
+ }
+ return -1;
+}
+
// --- cPatPmtGenerator ------------------------------------------------------
cPatPmtGenerator::cPatPmtGenerator(cChannel *Channel)
diff --git a/remux.h b/remux.h
index 8cb1223a..b41b4c8d 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.7 2009/01/24 13:38:10 kls Exp $
+ * $Id: remux.h 2.8 2009/03/08 12:05:12 kls Exp $
*/
#ifndef __REMUX_H
@@ -101,6 +101,10 @@ inline int TsGetAdaptationField(const uchar *p)
return TsHasAdaptationField(p) ? p[5] : 0x00;
}
+// The following functions all take a pointer to a sequence of complete TS packets.
+
+int64_t TsGetPts(const uchar *p, int l);
+
// Some PES handling tools:
// The following functions that take a pointer to PES data all assume that
// there is enough data so that PesLongEnough() returns true.
diff --git a/ringbuffer.c b/ringbuffer.c
index d74706ab..25a01596 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -7,7 +7,7 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
- * $Id: ringbuffer.c 1.25 2007/11/17 13:49:34 kls Exp $
+ * $Id: ringbuffer.c 2.1 2009/03/01 11:20:34 kls Exp $
*/
#include "ringbuffer.h"
@@ -335,11 +335,12 @@ void cRingBufferLinear::Del(int Count)
// --- cFrame ----------------------------------------------------------------
-cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index)
+cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index, uint32_t Pts)
{
count = abs(Count);
type = Type;
index = Index;
+ pts = Pts;
if (Count < 0)
data = (uchar *)Data;
else {
diff --git a/ringbuffer.h b/ringbuffer.h
index f6664cf8..fa9ccedf 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ringbuffer.h 1.18 2007/11/17 13:49:34 kls Exp $
+ * $Id: ringbuffer.h 2.1 2009/03/01 11:20:34 kls Exp $
*/
#ifndef __RINGBUFFER_H
@@ -108,8 +108,9 @@ private:
int count;
eFrameType type;
int index;
+ uint32_t pts;
public:
- cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1);
+ cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1, uint32_t Pts = 0);
///< Creates a new cFrame object.
///< If Count is negative, the cFrame object will take ownership of the given
///< Data. Otherwise it will allocate Count bytes of memory and copy Data.
@@ -118,6 +119,7 @@ public:
int Count(void) const { return count; }
eFrameType Type(void) const { return type; }
int Index(void) const { return index; }
+ uint32_t Pts(void) const { return pts; }
};
class cRingBufferFrame : public cRingBuffer {