@@ -182,8 +182,8 @@ bool cNonBlockingFileReader::WaitForData
#define PLAYERBUFSIZE MEGABYTE(1)
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
+// The number of seconds to back up when resuming an interrupted replay session:
+#define RESUMEBACKUP 10
class cDvbPlayer : public cPlayer, cThread {
private:
@@ -196,6 +196,7 @@ private:
cFileName *fileName;
cIndexFile *index;
cUnbufferedFile *replayFile;
+ int framesPerSec;
bool eof;
bool firstPacket;
ePlayModes playMode;
@@ -225,6 +226,7 @@ public:
void Goto(int Position, bool Still = false);
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
+ int GetFramesPerSec(void) { return framesPerSec; }
};
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@@ -253,6 +255,7 @@ cDvbPlayer::cDvbPlayer(const char *FileN
replayFile = fileName->Open();
if (!replayFile)
return;
+ framesPerSec = replayFile->GetFramesPerSec();
ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
// Create the index file:
index = new cIndexFile(FileName, false);
@@ -341,7 +344,7 @@ bool cDvbPlayer::Save(void)
if (index) {
int Index = writeIndex;
if (Index >= 0) {
- Index -= RESUMEBACKUP;
+ Index -= RESUMEBACKUP * GetFramesPerSec();
if (Index > 0)
Index = index->GetNextIFrame(Index, false);
else
@@ -371,7 +374,7 @@ void cDvbPlayer::Action(void)
readIndex = Resume();
if (readIndex >= 0)
- isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
+ isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, GetFramesPerSec()));
nonBlockingFileReader = new cNonBlockingFileReader;
int Length = 0;
@@ -665,7 +668,7 @@ void cDvbPlayer::SkipSeconds(int Seconds
Empty();
int Index = writeIndex;
if (Index >= 0) {
- Index = max(Index + Seconds * FRAMESPERSEC, 0);
+ Index = max(Index + Seconds * GetFramesPerSec(), 0);
if (Index > 0)
Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
if (Index >= 0)
@@ -832,3 +835,10 @@ void cDvbPlayerControl::Goto(int Positio
if (player)
player->Goto(Position, Still);
}
+
+int cDvbPlayerControl::GetFramesPerSec()
+{
+ if (player)
+ return player->GetFramesPerSec();
+ return FRAMESPERSEC;
+}
@@ -54,6 +54,8 @@ public:
void Goto(int Index, bool Still = false);
// Positions to the given index and displays that frame as a still picture
// if Still is true.
+ int GetFramesPerSec();
+ // Returns the number of frames per second for the current recording.
};
#endif //__DVBPLAYER_H
@@ -3931,7 +3931,7 @@ bool cReplayControl::ShowProgress(bool I
lastCurrent = lastTotal = -1;
}
if (Total != lastTotal) {
- displayReplay->SetTotal(IndexToHMSF(Total));
+ displayReplay->SetTotal(IndexToHMSF(Total, false, GetFramesPerSec()));
if (!Initial)
displayReplay->Flush();
}
@@ -3939,7 +3939,7 @@ bool cReplayControl::ShowProgress(bool I
displayReplay->SetProgress(Current, Total);
if (!Initial)
displayReplay->Flush();
- displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames));
+ displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, GetFramesPerSec()));
displayReplay->Flush();
lastCurrent = Current;
}
@@ -3971,8 +3971,8 @@ void cReplayControl::TimeSearchProcess(e
{
#define STAY_SECONDS_OFF_END 10
int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
- int Current = (lastCurrent / FRAMESPERSEC);
- int Total = (lastTotal / FRAMESPERSEC);
+ int Current = (lastCurrent / GetFramesPerSec());
+ int Total = (lastTotal / GetFramesPerSec());
switch (Key) {
case k0 ... k9:
if (timeSearchPos < 4) {
@@ -3999,7 +3999,7 @@ void cReplayControl::TimeSearchProcess(e
case kDown:
case kOk:
Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
- Goto(Seconds * FRAMESPERSEC, Key == kDown || Key == kPause || Key == kOk);
+ Goto(Seconds * GetFramesPerSec(), Key == kDown || Key == kPause || Key == kOk);
timeSearchActive = false;
break;
default:
@@ -4120,7 +4120,7 @@ void cReplayControl::EditTest(void)
if ((m->Index() & 0x01) != 0)
m = marks.Next(m);
if (m) {
- Goto(m->position - SecondsToFrames(3));
+ Goto(m->position - SecondsToFrames(3, GetFramesPerSec()));
Play();
}
}
@@ -1496,11 +1496,11 @@ cUnbufferedFile *cFileName::NextFile(voi
// --- Index stuff -----------------------------------------------------------
-cString IndexToHMSF(int Index, bool WithFrame)
+cString IndexToHMSF(int Index, bool WithFrame, int FramesPerSec)
{
char buffer[16];
- int f = (Index % FRAMESPERSEC) + 1;
- int s = (Index / FRAMESPERSEC);
+ int f = (Index % FramesPerSec) + 1;
+ int s = (Index / FramesPerSec);
int m = s / 60 % 60;
int h = s / 3600;
s %= 60;
@@ -1508,17 +1508,17 @@ cString IndexToHMSF(int Index, bool With
return buffer;
}
-int HMSFToIndex(const char *HMSF)
+int HMSFToIndex(const char *HMSF, int FramesPerSec)
{
int h, m, s, f = 0;
if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
- return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+ return (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
return 0;
}
-int SecondsToFrames(int Seconds)
+int SecondsToFrames(int Seconds, int FramesPerSec)
{
- return Seconds * FRAMESPERSEC;
+ return Seconds * FramesPerSec;
}
// --- ReadFrame -------------------------------------------------------------
@@ -231,11 +231,11 @@ public:
cUnbufferedFile *NextFile(void);
};
-cString IndexToHMSF(int Index, bool WithFrame = false);
+cString IndexToHMSF(int Index, bool WithFrame = false, int FramesPerSec = FRAMESPERSEC);
// Converts the given index to a string, optionally containing the frame number.
-int HMSFToIndex(const char *HMSF);
+int HMSFToIndex(const char *HMSF, int FramesPerSec = FRAMESPERSEC);
// Converts the given string (format: "hh:mm:ss.ff") to an index.
-int SecondsToFrames(int Seconds); //XXX+ ->player???
+int SecondsToFrames(int Seconds, int FramesPerSec = FRAMESPERSEC); //XXX+ ->player???
// Returns the number of frames corresponding to the given number of seconds.
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
@@ -1301,8 +1301,10 @@ void cSVDRP::CmdPLAY(const char *Option)
int x = sscanf(option, "%d:%d:%d.%d", &h, &m, &s, &f);
if (x == 1)
pos = h;
- else if (x >= 3)
- pos = (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+ else if (x >= 3) {
+ int FramesPerSec = cUnbufferedFile::GetFramesPerSec(recording->FileName());
+ pos = (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
+ }
}
cResumeFile resume(recording->FileName());
if (pos <= 0)
@@ -27,6 +27,8 @@ extern "C" {
#include <utime.h>
#include "i18n.h"
#include "thread.h"
+#include "remux.h"
+#include "recording.h"
int SysLogLevel = 3;
@@ -1244,6 +1246,77 @@ cUnbufferedFile *cUnbufferedFile::Create
return File;
}
+int cUnbufferedFile::GetFramesPerSec(const char *FileName)
+{
+ // use this constant as a fallback value
+ int FramesPerSec = FRAMESPERSEC;
+ // open the file an determine frames per second
+ cFileName fn(FileName, false);
+ cUnbufferedFile *f = fn.Open();
+ if (f) {
+ FramesPerSec = f->GetFramesPerSec();
+ fn.Close();
+ }
+ return FramesPerSec;
+}
+
+int cUnbufferedFile::GetFramesPerSec(void)
+{
+ // use this constant as a fallback value
+ int FramesPerSec = FRAMESPERSEC;
+ // rember current file position to restore later
+ off_t OrigPos = curpos;
+ // seek to the beginning and read a chunk of data
+ if (0 == Seek(0, SEEK_SET)) {
+ uchar Data[2048];
+ ssize_t Count = Read(Data, sizeof(Data));
+ if (Count > 0) {
+ // this chunk of data should actually be a PES packet
+ uchar *Limit = Data + Count;
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) == phMPEG2) {
+ // we need a video stream -- radio recordings use the default
+ if ((Data[3] & 0xF0) == 0xE0) {
+ // thanks to cVideoRepacker, the payload starts with a sequence header
+ uchar *p = Data + PesPayloadOffset;
+ if (p + 12 <= Limit) {
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xB3) {
+ uint32_t frame_rate_code = p[7] & 0x0F;
+ uint32_t frame_rate_extension_n = 0;
+ uint32_t frame_rate_extension_d = 0;
+ // now we need to have a look at the next startcode,
+ // as it might be a sequence extension
+ p = (uchar *)memmem(p + 12, Limit - (p + 12), "\x00\x00\x01", 3);
+ if (p && p + 4 < Limit && p[3] == 0xB5) { // extension start code
+ if (p + 5 < Limit && (p[4] >> 4) == 0x1) { // sequence extension
+ if (p + 10 < Limit) {
+ frame_rate_extension_n = (p[9] & 0x60) >> 5;
+ frame_rate_extension_d = (p[9] & 0x1F);
+ }
+ }
+ }
+ // calculate frame rate and round it for compatibility
+ if (0x1 <= frame_rate_code && frame_rate_code <= 0x8) {
+ static const int n[] = { -1, 24000, 24, 25, 30000, 30, 50, 60000, 60 };
+ static const int d[] = { -1, 1001, 1, 1, 1001, 1, 1, 1001, 1 };
+ double frame_rate = n[frame_rate_code] * (frame_rate_extension_n + 1)
+ / (double)(d[frame_rate_code] * (frame_rate_extension_d + 1));
+ FramesPerSec = (int)frame_rate;
+ if (frame_rate - FramesPerSec > 0.5)
+ FramesPerSec++;
+fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // restore original position
+ Seek(OrigPos, SEEK_SET);
+ return FramesPerSec;
+}
+
// --- cLockFile -------------------------------------------------------------
#define LOCKFILENAME ".lock-vdr"
@@ -263,6 +263,8 @@ public:
ssize_t Read(void *Data, size_t Size);
ssize_t Write(const void *Data, size_t Size);
static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+ static int GetFramesPerSec(const char *FileName);
+ int GetFramesPerSec(void);
};
class cLockFile {