mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Fixed a race condition in cTransfer (thanks to Klaus Heppenheimer for reporting this one). In doing so, the 'active' variables used by the actual derived cThread classes have been replaced by the cThread::Running() function. Plugin authors may want to check their derived cThread classes and replace any 'active' variables the same way as, for instance, done in transfer.c. - Fixed handling EPG data for time shifted events (thanks to Marco Schlüßler). - Increased the default value for 'Min. user inactivity' to 300 minutes (suggested by Helmut Auer). - Now storing the channel id in the info.vdr file even if there is no EPG info available (thanks to Andreas Brachold for reporting that there are empty info.vdr files created in that case). - Added some 'mkdir -p' to the Makefile's 'install' target (thanks to Wayne Keer). - Changed the title of the recording info menu (thanks to Rolf Ahrenberg). - Fixed handling the frame number display if '7' is pressed before the first editing mark, or '9' after the last one (thanks to Thomas Günther). - Now discarding any previous numerical input to switch channels if Up, Down, Channel+, Channel-, Left or Right is pressed (thanks to Wolfgang Rohdewald for reporting a problem with this). - Pressing Ok while entering a channel number now immediately switches to that channel, without waiting for further input. - Avoiding unnecessary OSD draw operations caused by the audio track description display in the ST:TNG skin's channel display (thanks to Oliver Endriss for reporting this). - Removed the VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES stuff from cDvbDevice::StillPicture(), since apparently the VIDEO_STILLPICTURE call works.
804 lines
21 KiB
C
804 lines
21 KiB
C
/*
|
|
* dvbplayer.c: The DVB player
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: dvbplayer.c 1.38 2005/08/14 10:52:45 kls Exp $
|
|
*/
|
|
|
|
#include "dvbplayer.h"
|
|
#include <stdlib.h>
|
|
#include "recording.h"
|
|
#include "remux.h"
|
|
#include "ringbuffer.h"
|
|
#include "thread.h"
|
|
#include "tools.h"
|
|
|
|
// --- cBackTrace ----------------------------------------------------------
|
|
|
|
#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
|
|
|
|
class cBackTrace {
|
|
private:
|
|
int index[BACKTRACE_ENTRIES];
|
|
int length[BACKTRACE_ENTRIES];
|
|
int pos, num;
|
|
public:
|
|
cBackTrace(void);
|
|
void Clear(void);
|
|
void Add(int Index, int Length);
|
|
int Get(bool Forward);
|
|
};
|
|
|
|
cBackTrace::cBackTrace(void)
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void cBackTrace::Clear(void)
|
|
{
|
|
pos = num = 0;
|
|
}
|
|
|
|
void cBackTrace::Add(int Index, int Length)
|
|
{
|
|
index[pos] = Index;
|
|
length[pos] = Length;
|
|
if (++pos >= BACKTRACE_ENTRIES)
|
|
pos = 0;
|
|
if (num < BACKTRACE_ENTRIES)
|
|
num++;
|
|
}
|
|
|
|
int cBackTrace::Get(bool Forward)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// --- cNonBlockingFileReader ------------------------------------------------
|
|
|
|
class cNonBlockingFileReader : public cThread {
|
|
private:
|
|
int f;
|
|
uchar *buffer;
|
|
int wanted;
|
|
int length;
|
|
bool hasData;
|
|
cCondWait newSet;
|
|
protected:
|
|
void Action(void);
|
|
public:
|
|
cNonBlockingFileReader(void);
|
|
~cNonBlockingFileReader();
|
|
void Clear(void);
|
|
int Read(int FileHandle, uchar *Buffer, int Length);
|
|
bool Reading(void) { return buffer; }
|
|
};
|
|
|
|
cNonBlockingFileReader::cNonBlockingFileReader(void)
|
|
:cThread("non blocking file reader")
|
|
{
|
|
f = -1;
|
|
buffer = NULL;
|
|
wanted = length = 0;
|
|
hasData = false;
|
|
Start();
|
|
}
|
|
|
|
cNonBlockingFileReader::~cNonBlockingFileReader()
|
|
{
|
|
newSet.Signal();
|
|
Cancel(3);
|
|
free(buffer);
|
|
}
|
|
|
|
void cNonBlockingFileReader::Clear(void)
|
|
{
|
|
Lock();
|
|
f = -1;
|
|
free(buffer);
|
|
buffer = NULL;
|
|
wanted = length = 0;
|
|
hasData = false;
|
|
Unlock();
|
|
newSet.Signal();
|
|
}
|
|
|
|
int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
|
|
{
|
|
if (hasData && buffer) {
|
|
if (buffer != Buffer) {
|
|
esyslog("ERROR: cNonBlockingFileReader::Read() called with different buffer!");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
buffer = NULL;
|
|
return length;
|
|
}
|
|
if (!buffer) {
|
|
f = FileHandle;
|
|
buffer = Buffer;
|
|
wanted = Length;
|
|
length = 0;
|
|
hasData = false;
|
|
newSet.Signal();
|
|
}
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
|
|
void cNonBlockingFileReader::Action(void)
|
|
{
|
|
while (Running()) {
|
|
Lock();
|
|
if (!hasData && f >= 0 && buffer) {
|
|
int r = safe_read(f, buffer + length, wanted - length);
|
|
if (r >= 0) {
|
|
length += r;
|
|
if (!r || length == wanted) // r == 0 means EOF
|
|
hasData = true;
|
|
}
|
|
else if (r < 0 && FATALERRNO) {
|
|
LOG_ERROR;
|
|
length = r; // this will forward the error status to the caller
|
|
hasData = true;
|
|
}
|
|
}
|
|
Unlock();
|
|
newSet.Wait(1000);
|
|
}
|
|
}
|
|
|
|
// --- cDvbPlayer ------------------------------------------------------------
|
|
|
|
#define PLAYERBUFSIZE MEGABYTE(1)
|
|
|
|
// The number of frames to back up when resuming an interrupted replay session:
|
|
#define RESUMEBACKUP (10 * FRAMESPERSEC)
|
|
|
|
class cDvbPlayer : public cPlayer, cThread {
|
|
private:
|
|
enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
|
|
enum ePlayDirs { pdForward, pdBackward };
|
|
static int Speeds[];
|
|
cNonBlockingFileReader *nonBlockingFileReader;
|
|
cRingBufferFrame *ringBuffer;
|
|
cBackTrace *backTrace;
|
|
cFileName *fileName;
|
|
cIndexFile *index;
|
|
int replayFile;
|
|
bool eof;
|
|
bool firstPacket;
|
|
ePlayModes playMode;
|
|
ePlayDirs playDir;
|
|
int trickSpeed;
|
|
int readIndex, writeIndex;
|
|
cFrame *readFrame;
|
|
cFrame *playFrame;
|
|
void TrickSpeed(int Increment);
|
|
void Empty(void);
|
|
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
|
int Resume(void);
|
|
bool Save(void);
|
|
protected:
|
|
virtual void Activate(bool On);
|
|
virtual void Action(void);
|
|
public:
|
|
cDvbPlayer(const char *FileName);
|
|
virtual ~cDvbPlayer();
|
|
bool Active(void) { return cThread::Running(); }
|
|
void Pause(void);
|
|
void Play(void);
|
|
void Forward(void);
|
|
void Backward(void);
|
|
int SkipFrames(int Frames);
|
|
void SkipSeconds(int Seconds);
|
|
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);
|
|
};
|
|
|
|
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
|
|
#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
|
|
#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
|
|
#define SPEED_MULT 12 // the speed multiplier
|
|
int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
|
|
|
|
cDvbPlayer::cDvbPlayer(const char *FileName)
|
|
:cThread("dvbplayer")
|
|
{
|
|
nonBlockingFileReader = NULL;
|
|
ringBuffer = NULL;
|
|
backTrace = NULL;
|
|
index = NULL;
|
|
eof = false;
|
|
firstPacket = true;
|
|
playMode = pmPlay;
|
|
playDir = pdForward;
|
|
trickSpeed = NORMAL_SPEED;
|
|
readIndex = writeIndex = -1;
|
|
readFrame = NULL;
|
|
playFrame = NULL;
|
|
isyslog("replay %s", FileName);
|
|
fileName = new cFileName(FileName, false);
|
|
replayFile = fileName->Open();
|
|
if (replayFile < 0)
|
|
return;
|
|
ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
|
|
// Create the index file:
|
|
index = new cIndexFile(FileName, false);
|
|
if (!index)
|
|
esyslog("ERROR: can't allocate index");
|
|
else if (!index->Ok()) {
|
|
delete index;
|
|
index = NULL;
|
|
}
|
|
backTrace = new cBackTrace;
|
|
}
|
|
|
|
cDvbPlayer::~cDvbPlayer()
|
|
{
|
|
Detach();
|
|
Save();
|
|
delete readFrame; // might not have been stored in the buffer in Action()
|
|
delete index;
|
|
delete fileName;
|
|
delete backTrace;
|
|
delete ringBuffer;
|
|
}
|
|
|
|
void cDvbPlayer::TrickSpeed(int Increment)
|
|
{
|
|
int nts = trickSpeed + Increment;
|
|
if (Speeds[nts] == 1) {
|
|
trickSpeed = nts;
|
|
if (playMode == pmFast)
|
|
Play();
|
|
else
|
|
Pause();
|
|
}
|
|
else if (Speeds[nts]) {
|
|
trickSpeed = nts;
|
|
int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
|
|
int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
|
|
if (sp > MAX_VIDEO_SLOWMOTION)
|
|
sp = MAX_VIDEO_SLOWMOTION;
|
|
DeviceTrickSpeed(sp);
|
|
}
|
|
}
|
|
|
|
void cDvbPlayer::Empty(void)
|
|
{
|
|
LOCK_THREAD;
|
|
if (nonBlockingFileReader)
|
|
nonBlockingFileReader->Clear();
|
|
if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
|
|
readIndex = writeIndex;
|
|
delete readFrame; // might not have been stored in the buffer in Action()
|
|
readFrame = NULL;
|
|
playFrame = NULL;
|
|
ringBuffer->Clear();
|
|
backTrace->Clear();
|
|
DeviceClear();
|
|
firstPacket = true;
|
|
}
|
|
|
|
bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
|
|
{
|
|
if (FileNumber > 0)
|
|
replayFile = fileName->SetOffset(FileNumber, FileOffset);
|
|
else if (replayFile >= 0 && eof)
|
|
replayFile = fileName->NextFile();
|
|
eof = false;
|
|
return replayFile >= 0;
|
|
}
|
|
|
|
int cDvbPlayer::Resume(void)
|
|
{
|
|
if (index) {
|
|
int Index = index->GetResume();
|
|
if (Index >= 0) {
|
|
uchar FileNumber;
|
|
int FileOffset;
|
|
if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
|
|
return Index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool cDvbPlayer::Save(void)
|
|
{
|
|
if (index) {
|
|
int Index = writeIndex;
|
|
if (Index >= 0) {
|
|
Index -= RESUMEBACKUP;
|
|
if (Index > 0)
|
|
Index = index->GetNextIFrame(Index, false);
|
|
else
|
|
Index = 0;
|
|
if (Index >= 0)
|
|
return index->StoreResume(Index);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cDvbPlayer::Activate(bool On)
|
|
{
|
|
if (On) {
|
|
if (replayFile >= 0)
|
|
Start();
|
|
}
|
|
else
|
|
Cancel(9);
|
|
}
|
|
|
|
void cDvbPlayer::Action(void)
|
|
{
|
|
uchar *b = NULL;
|
|
uchar *p = NULL;
|
|
int pc = 0;
|
|
|
|
readIndex = Resume();
|
|
if (readIndex >= 0)
|
|
isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
|
|
|
|
nonBlockingFileReader = new cNonBlockingFileReader;
|
|
int Length = 0;
|
|
bool Sleep = false;
|
|
|
|
while (Running() && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) {
|
|
if (Sleep) {
|
|
cCondWait::SleepMs(3); // this keeps the CPU load low
|
|
Sleep = false;
|
|
}
|
|
cPoller Poller;
|
|
if (DevicePoll(Poller, 100)) {
|
|
|
|
LOCK_THREAD;
|
|
|
|
// Read the next frame from the file:
|
|
|
|
if (playMode != pmStill && playMode != pmPause) {
|
|
if (!readFrame && (replayFile >= 0 || readIndex >= 0)) {
|
|
if (!nonBlockingFileReader->Reading()) {
|
|
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
|
|
uchar FileNumber;
|
|
int FileOffset;
|
|
int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
|
|
if (Index >= 0) {
|
|
if (!NextFile(FileNumber, FileOffset))
|
|
continue;
|
|
}
|
|
else {
|
|
// 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 if (index) {
|
|
uchar FileNumber;
|
|
int FileOffset;
|
|
readIndex++;
|
|
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
|
|
readIndex = -1;
|
|
eof = true;
|
|
continue;
|
|
}
|
|
}
|
|
else // allows replay even if the index file is missing
|
|
Length = MAXFRAMESIZE;
|
|
if (Length == -1)
|
|
Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
|
|
else if (Length > MAXFRAMESIZE) {
|
|
esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
|
|
Length = MAXFRAMESIZE;
|
|
}
|
|
b = MALLOC(uchar, Length);
|
|
}
|
|
int r = nonBlockingFileReader->Read(replayFile, b, Length);
|
|
if (r > 0) {
|
|
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 && FATALERRNO) {
|
|
LOG_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Store the frame in the buffer:
|
|
|
|
if (readFrame) {
|
|
if (ringBuffer->Put(readFrame))
|
|
readFrame = NULL;
|
|
}
|
|
}
|
|
else
|
|
Sleep = true;
|
|
|
|
// Get the next frame from the buffer:
|
|
|
|
if (!playFrame) {
|
|
playFrame = ringBuffer->Get();
|
|
p = NULL;
|
|
pc = 0;
|
|
}
|
|
|
|
// Play the frame:
|
|
|
|
if (playFrame) {
|
|
if (!p) {
|
|
p = playFrame->Data();
|
|
pc = playFrame->Count();
|
|
if (p) {
|
|
if (firstPacket) {
|
|
PlayPes(NULL, 0);
|
|
cRemux::SetBrokenLink(p, pc);
|
|
firstPacket = false;
|
|
}
|
|
}
|
|
}
|
|
if (p) {
|
|
int w = PlayPes(p, pc, playMode != pmPlay);
|
|
if (w > 0) {
|
|
p += w;
|
|
pc -= w;
|
|
}
|
|
else if (w < 0 && FATALERRNO) {
|
|
LOG_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
if (pc == 0) {
|
|
writeIndex = playFrame->Index();
|
|
backTrace->Add(playFrame->Index(), playFrame->Count());
|
|
ringBuffer->Drop(playFrame);
|
|
playFrame = NULL;
|
|
p = NULL;
|
|
}
|
|
}
|
|
else
|
|
Sleep = true;
|
|
}
|
|
}
|
|
|
|
cNonBlockingFileReader *nbfr = nonBlockingFileReader;
|
|
nonBlockingFileReader = NULL;
|
|
delete nbfr;
|
|
}
|
|
|
|
void cDvbPlayer::Pause(void)
|
|
{
|
|
if (playMode == pmPause || playMode == pmStill)
|
|
Play();
|
|
else {
|
|
LOCK_THREAD;
|
|
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
|
|
Empty();
|
|
DeviceFreeze();
|
|
playMode = pmPause;
|
|
}
|
|
}
|
|
|
|
void cDvbPlayer::Play(void)
|
|
{
|
|
if (playMode != pmPlay) {
|
|
LOCK_THREAD;
|
|
if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
|
|
Empty();
|
|
DevicePlay();
|
|
playMode = pmPlay;
|
|
playDir = pdForward;
|
|
}
|
|
}
|
|
|
|
void cDvbPlayer::Forward(void)
|
|
{
|
|
if (index) {
|
|
switch (playMode) {
|
|
case pmFast:
|
|
if (Setup.MultiSpeedMode) {
|
|
TrickSpeed(playDir == pdForward ? 1 : -1);
|
|
break;
|
|
}
|
|
else if (playDir == pdForward) {
|
|
Play();
|
|
break;
|
|
}
|
|
// run into pmPlay
|
|
case pmPlay: {
|
|
LOCK_THREAD;
|
|
Empty();
|
|
DeviceMute();
|
|
playMode = pmFast;
|
|
playDir = pdForward;
|
|
trickSpeed = NORMAL_SPEED;
|
|
TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
|
|
}
|
|
break;
|
|
case pmSlow:
|
|
if (Setup.MultiSpeedMode) {
|
|
TrickSpeed(playDir == pdForward ? -1 : 1);
|
|
break;
|
|
}
|
|
else if (playDir == pdForward) {
|
|
Pause();
|
|
break;
|
|
}
|
|
// run into pmPause
|
|
case pmStill:
|
|
case pmPause:
|
|
DeviceMute();
|
|
playMode = pmSlow;
|
|
playDir = pdForward;
|
|
trickSpeed = NORMAL_SPEED;
|
|
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cDvbPlayer::Backward(void)
|
|
{
|
|
if (index) {
|
|
switch (playMode) {
|
|
case pmFast:
|
|
if (Setup.MultiSpeedMode) {
|
|
TrickSpeed(playDir == pdBackward ? 1 : -1);
|
|
break;
|
|
}
|
|
else if (playDir == pdBackward) {
|
|
Play();
|
|
break;
|
|
}
|
|
// run into pmPlay
|
|
case pmPlay: {
|
|
LOCK_THREAD;
|
|
Empty();
|
|
DeviceMute();
|
|
playMode = pmFast;
|
|
playDir = pdBackward;
|
|
trickSpeed = NORMAL_SPEED;
|
|
TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
|
|
}
|
|
break;
|
|
case pmSlow:
|
|
if (Setup.MultiSpeedMode) {
|
|
TrickSpeed(playDir == pdBackward ? -1 : 1);
|
|
break;
|
|
}
|
|
else if (playDir == pdBackward) {
|
|
Pause();
|
|
break;
|
|
}
|
|
// run into pmPause
|
|
case pmStill:
|
|
case pmPause: {
|
|
LOCK_THREAD;
|
|
Empty();
|
|
DeviceMute();
|
|
playMode = pmSlow;
|
|
playDir = pdBackward;
|
|
trickSpeed = NORMAL_SPEED;
|
|
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int cDvbPlayer::SkipFrames(int Frames)
|
|
{
|
|
if (index && Frames) {
|
|
int Current, Total;
|
|
GetIndex(Current, Total, true);
|
|
int OldCurrent = Current;
|
|
Current = index->GetNextIFrame(Current + Frames, Frames > 0);
|
|
return Current >= 0 ? Current : OldCurrent;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void cDvbPlayer::SkipSeconds(int Seconds)
|
|
{
|
|
if (index && Seconds) {
|
|
LOCK_THREAD;
|
|
Empty();
|
|
int Index = writeIndex;
|
|
if (Index >= 0) {
|
|
Index = max(Index + Seconds * FRAMESPERSEC, 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!
|
|
}
|
|
Play();
|
|
}
|
|
}
|
|
|
|
void cDvbPlayer::Goto(int Index, bool Still)
|
|
{
|
|
if (index) {
|
|
LOCK_THREAD;
|
|
Empty();
|
|
if (++Index <= 0)
|
|
Index = 1; // not '0', to allow GetNextIFrame() below to work!
|
|
uchar FileNumber;
|
|
int FileOffset, Length;
|
|
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
|
|
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
|
|
uchar b[MAXFRAMESIZE + 4 + 5 + 4];
|
|
int r = ReadFrame(replayFile, b, Length, sizeof(b));
|
|
if (r > 0) {
|
|
if (playMode == pmPause)
|
|
DevicePlay();
|
|
// append sequence end code to get the image shown immediately with softdevices
|
|
if (r > 6) { // should be always true
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x01;
|
|
b[r++] = b[3];
|
|
if (b[6] & 0x80) { // MPEG 2
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x07;
|
|
b[r++] = 0x80;
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x00;
|
|
}
|
|
else { // MPEG 1
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x05;
|
|
b[r++] = 0x0F;
|
|
}
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x00;
|
|
b[r++] = 0x01;
|
|
b[r++] = 0xB7;
|
|
}
|
|
DeviceStillPicture(b, r);
|
|
}
|
|
playMode = pmStill;
|
|
}
|
|
readIndex = writeIndex = 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;
|
|
}
|
|
}
|
|
Total = index->Last();
|
|
return true;
|
|
}
|
|
Current = Total = -1;
|
|
return false;
|
|
}
|
|
|
|
bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
|
|
{
|
|
Play = (playMode == pmPlay || playMode == pmFast);
|
|
Forward = (playDir == pdForward);
|
|
if (playMode == pmFast || playMode == pmSlow)
|
|
Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
|
|
else
|
|
Speed = -1;
|
|
return true;
|
|
}
|
|
|
|
// --- cDvbPlayerControl -----------------------------------------------------
|
|
|
|
cDvbPlayerControl::cDvbPlayerControl(const char *FileName)
|
|
:cControl(player = new cDvbPlayer(FileName))
|
|
{
|
|
}
|
|
|
|
cDvbPlayerControl::~cDvbPlayerControl()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
bool cDvbPlayerControl::Active(void)
|
|
{
|
|
return player && player->Active();
|
|
}
|
|
|
|
void cDvbPlayerControl::Stop(void)
|
|
{
|
|
delete player;
|
|
player = NULL;
|
|
}
|
|
|
|
void cDvbPlayerControl::Pause(void)
|
|
{
|
|
if (player)
|
|
player->Pause();
|
|
}
|
|
|
|
void cDvbPlayerControl::Play(void)
|
|
{
|
|
if (player)
|
|
player->Play();
|
|
}
|
|
|
|
void cDvbPlayerControl::Forward(void)
|
|
{
|
|
if (player)
|
|
player->Forward();
|
|
}
|
|
|
|
void cDvbPlayerControl::Backward(void)
|
|
{
|
|
if (player)
|
|
player->Backward();
|
|
}
|
|
|
|
void cDvbPlayerControl::SkipSeconds(int Seconds)
|
|
{
|
|
if (player)
|
|
player->SkipSeconds(Seconds);
|
|
}
|
|
|
|
int cDvbPlayerControl::SkipFrames(int Frames)
|
|
{
|
|
if (player)
|
|
return player->SkipFrames(Frames);
|
|
return -1;
|
|
}
|
|
|
|
bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
|
{
|
|
if (player) {
|
|
player->GetIndex(Current, Total, SnapToIFrame);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
|
|
{
|
|
return player && player->GetReplayMode(Play, Forward, Speed);
|
|
}
|
|
|
|
void cDvbPlayerControl::Goto(int Position, bool Still)
|
|
{
|
|
if (player)
|
|
player->Goto(Position, Still);
|
|
}
|