mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
New ringbuffer for frames
This commit is contained in:
parent
614113cdcb
commit
c2ed9b5daf
3
HISTORY
3
HISTORY
@ -610,7 +610,7 @@ Video Disk Recorder Revision History
|
||||
- Explicitly switching back to the previously active channel after ending a
|
||||
replay session (to have it shown correctly in case it was in 'Transfer Mode').
|
||||
|
||||
2001-08-03: Version 0.86
|
||||
2001-08-05: Version 0.86
|
||||
|
||||
- Modified the display of the channel group separators (thanks to Markus Lang
|
||||
for this suggestion).
|
||||
@ -618,3 +618,4 @@ Video Disk Recorder Revision History
|
||||
the 'libdvdread' library to be installed.
|
||||
- Fixed replay progress display in case replay is paused while watching an
|
||||
ongoing recording.
|
||||
- Ringbuffer uses semaphores to signal empty/full conditions.
|
||||
|
620
dvbapi.c
620
dvbapi.c
@ -6,7 +6,7 @@
|
||||
*
|
||||
* DVD support initially written by Andreas Schultz <aschultz@warp10.net>
|
||||
*
|
||||
* $Id: dvbapi.c 1.97 2001/08/03 13:08:22 kls Exp $
|
||||
* $Id: dvbapi.c 1.98 2001/08/05 12:17:02 kls Exp $
|
||||
*/
|
||||
|
||||
//#define DVDDEBUG 1
|
||||
@ -448,7 +448,7 @@ int cFileName::NextFile(void)
|
||||
|
||||
// --- cRecordBuffer ---------------------------------------------------------
|
||||
|
||||
class cRecordBuffer : public cRingBuffer {
|
||||
class cRecordBuffer : public cRingBufferLinear {
|
||||
private:
|
||||
cDvbApi *dvbApi;
|
||||
cFileName fileName;
|
||||
@ -471,7 +471,7 @@ public:
|
||||
};
|
||||
|
||||
cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
|
||||
:cRingBuffer(VIDEOBUFSIZE, true)
|
||||
:cRingBufferLinear(VIDEOBUFSIZE, true)
|
||||
,fileName(FileName, true)
|
||||
,remux(VPid, APid1, APid2, DPid1, DPid2, true)
|
||||
{
|
||||
@ -628,23 +628,27 @@ int ReadFrame(int f, uchar *b, int Length, int Max)
|
||||
|
||||
// --- cPlayBuffer ---------------------------------------------------------
|
||||
|
||||
class cPlayBuffer : public cRingBuffer {
|
||||
class cPlayBuffer : public cRingBufferFrame {
|
||||
protected:
|
||||
cDvbApi *dvbApi;
|
||||
int videoDev, audioDev;
|
||||
FILE *dolbyDev;
|
||||
int blockInput, blockOutput;
|
||||
bool paused, fastForward, fastRewind;
|
||||
bool still, paused, fastForward, fastRewind;
|
||||
int readIndex, writeIndex;
|
||||
bool canDoTrickMode;
|
||||
bool canToggleAudioTrack;
|
||||
uchar audioTrack;
|
||||
virtual void Clear(bool Block = false);
|
||||
virtual void Empty(bool Block = false);
|
||||
virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {}
|
||||
virtual void Output(void);
|
||||
public:
|
||||
cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev);
|
||||
virtual ~cPlayBuffer();
|
||||
virtual void Pause(void) {}
|
||||
virtual void Play(void) = 0;
|
||||
virtual void Forward(void) {}
|
||||
virtual void Backward(void) {}
|
||||
virtual void Pause(void);
|
||||
virtual void Play(void);
|
||||
virtual void Forward(void);
|
||||
virtual void Backward(void);
|
||||
virtual int SkipFrames(int Frames) { return -1; }
|
||||
virtual void SkipSeconds(int Seconds) {}
|
||||
virtual void Goto(int Position, bool Still = false) {}
|
||||
@ -654,14 +658,16 @@ public:
|
||||
};
|
||||
|
||||
cPlayBuffer::cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev)
|
||||
:cRingBuffer(VIDEOBUFSIZE)
|
||||
:cRingBufferFrame(VIDEOBUFSIZE)
|
||||
{
|
||||
dvbApi = DvbApi;
|
||||
videoDev = VideoDev;
|
||||
audioDev = AudioDev;
|
||||
dolbyDev = NULL;
|
||||
blockInput = blockOutput = false;
|
||||
paused = fastForward = fastRewind = false;
|
||||
still = paused = fastForward = fastRewind = false;
|
||||
readIndex = writeIndex = -1;
|
||||
canDoTrickMode = false;
|
||||
canToggleAudioTrack = false;
|
||||
audioTrack = 0xC0;
|
||||
if (cDvbApi::AudioCommand()) {
|
||||
@ -677,18 +683,136 @@ cPlayBuffer::~cPlayBuffer()
|
||||
pclose(dolbyDev);
|
||||
}
|
||||
|
||||
void cPlayBuffer::Clear(bool Block)
|
||||
void cPlayBuffer::Output(void)
|
||||
{
|
||||
cRingBuffer::Clear();
|
||||
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
|
||||
|
||||
while (Busy()) {
|
||||
if (blockOutput) {
|
||||
if (blockOutput > 1)
|
||||
blockOutput = 1;
|
||||
continue;
|
||||
}
|
||||
const cFrame *frame = Get();
|
||||
if (frame) {
|
||||
StripAudioPackets((uchar *)frame->Data(), frame->Count(), (fastForward || fastRewind) ? 0x00 : audioTrack);//XXX
|
||||
for (int i = 0; i < ((paused && fastRewind) ? 24 : 1); i++) { // show every I_FRAME 24 times in slow rewind mode to achieve roughly the same speed as in slow forward mode
|
||||
const uchar *p = frame->Data();
|
||||
int r = frame->Count();
|
||||
while (r > 0 && Busy() && !blockOutput) {
|
||||
cFile::FileReadyForWriting(videoDev, 100);
|
||||
int w = write(videoDev, p, r);
|
||||
if (w > 0) {
|
||||
p += w;
|
||||
r -= w;
|
||||
}
|
||||
else if (w < 0 && errno != EAGAIN) {
|
||||
LOG_ERROR;
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
writeIndex = frame->Index();
|
||||
}
|
||||
Drop(frame);
|
||||
}
|
||||
}
|
||||
|
||||
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
|
||||
}
|
||||
|
||||
void cPlayBuffer::Empty(bool Block)
|
||||
{
|
||||
if (!(blockInput || blockOutput)) {
|
||||
blockInput = blockOutput = 2;
|
||||
EnablePut();
|
||||
EnableGet();
|
||||
time_t t0 = time(NULL);
|
||||
while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
|
||||
usleep(1);
|
||||
Lock();
|
||||
readIndex = writeIndex;
|
||||
cRingBufferFrame::Clear();
|
||||
CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
|
||||
CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
|
||||
}
|
||||
if (!Block) {
|
||||
blockInput = blockOutput = 0;
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayBuffer::Pause(void)
|
||||
{
|
||||
paused = !paused;
|
||||
bool empty = fastForward || fastRewind;
|
||||
if (empty)
|
||||
Empty(true);
|
||||
fastForward = fastRewind = false;
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
|
||||
still = false;
|
||||
if (empty)
|
||||
Empty(false);
|
||||
}
|
||||
|
||||
void cPlayBuffer::Play(void)
|
||||
{
|
||||
if (fastForward || fastRewind || paused) {
|
||||
bool empty = !paused || fastRewind;
|
||||
if (empty)
|
||||
Empty(true);
|
||||
still = false;
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
|
||||
if (empty)
|
||||
Empty(false);
|
||||
fastForward = fastRewind = paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayBuffer::Forward(void)
|
||||
{
|
||||
if (canDoTrickMode || paused) {
|
||||
bool empty = !paused || fastRewind;
|
||||
if (empty) {
|
||||
Empty(true);
|
||||
if (fastForward)
|
||||
readIndex -= 150; // this about compensates for the buffered data, so that we don't get too far ahead
|
||||
}
|
||||
still = false;
|
||||
fastForward = !fastForward;
|
||||
fastRewind = false;
|
||||
if (paused)
|
||||
CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
|
||||
if (empty)
|
||||
Empty(false);
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayBuffer::Backward(void)
|
||||
{
|
||||
if (canDoTrickMode) {
|
||||
Empty(true);
|
||||
still = false;
|
||||
fastRewind = !fastRewind;
|
||||
fastForward = false;
|
||||
if (paused)
|
||||
CHECK(ioctl(videoDev, fastRewind ? VIDEO_CONTINUE : VIDEO_FREEZE));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
|
||||
Empty(false);
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayBuffer::ToggleAudioTrack(void)
|
||||
{
|
||||
if (CanToggleAudioTrack()) {
|
||||
audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
|
||||
Clear();
|
||||
Empty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -698,27 +822,19 @@ class cReplayBuffer : public cPlayBuffer {
|
||||
private:
|
||||
cIndexFile *index;
|
||||
cFileName fileName;
|
||||
int fileOffset;
|
||||
int replayFile;
|
||||
bool eof;
|
||||
int lastIndex, stillIndex, playIndex;
|
||||
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
||||
void Clear(bool Block = false);
|
||||
void Close(void);
|
||||
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
|
||||
virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
|
||||
void DisplayFrame(uchar *b, int Length);
|
||||
int Resume(void);
|
||||
bool Save(void);
|
||||
protected:
|
||||
virtual void Input(void);
|
||||
virtual void Output(void);
|
||||
public:
|
||||
cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
|
||||
virtual ~cReplayBuffer();
|
||||
virtual void Pause(void);
|
||||
virtual void Play(void);
|
||||
virtual void Forward(void);
|
||||
virtual void Backward(void);
|
||||
virtual int SkipFrames(int Frames);
|
||||
virtual void SkipSeconds(int Seconds);
|
||||
virtual void Goto(int Position, bool Still = false);
|
||||
@ -730,10 +846,8 @@ cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const
|
||||
,fileName(FileName, false)
|
||||
{
|
||||
index = NULL;
|
||||
fileOffset = 0;
|
||||
replayFile = fileName.Open();
|
||||
eof = false;
|
||||
lastIndex = stillIndex = playIndex = -1;
|
||||
if (!fileName.Name())
|
||||
return;
|
||||
// Create the index file:
|
||||
@ -745,6 +859,7 @@ cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const
|
||||
delete index;
|
||||
index = NULL;
|
||||
}
|
||||
canDoTrickMode = index != NULL;
|
||||
dvbApi->SetModeReplay();
|
||||
Start();
|
||||
}
|
||||
@ -762,22 +877,23 @@ void cReplayBuffer::Input(void)
|
||||
{
|
||||
dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
|
||||
|
||||
int ResumeIndex = Resume();
|
||||
if (ResumeIndex >= 0)
|
||||
isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, IndexToHMSF(ResumeIndex, true));
|
||||
readIndex = Resume();
|
||||
if (readIndex >= 0)
|
||||
isyslog(LOG_INFO, "resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
|
||||
|
||||
int lastIndex = -1;
|
||||
int brakeCounter = 0;
|
||||
uchar b[MAXFRAMESIZE];
|
||||
while (Busy() && (blockInput || NextFile())) {
|
||||
if (!blockInput && stillIndex < 0) {
|
||||
if (blockInput) {
|
||||
if (blockInput > 1)
|
||||
blockInput = 1;
|
||||
continue;
|
||||
}
|
||||
if (!still) {
|
||||
int r = 0;
|
||||
if (fastForward && !paused || fastRewind) {
|
||||
int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
|
||||
uchar FileNumber;
|
||||
int FileOffset, Length;
|
||||
if (!paused || (brakeCounter++ % 24) == 0) // show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
|
||||
Index = index->GetNextIFrame(Index, fastForward, &FileNumber, &FileOffset, &Length);
|
||||
int Index = index->GetNextIFrame(readIndex, fastForward, &FileNumber, &FileOffset, &Length);
|
||||
if (Index >= 0) {
|
||||
if (!NextFile(FileNumber, FileOffset))
|
||||
break;
|
||||
@ -787,83 +903,41 @@ void cReplayBuffer::Input(void)
|
||||
Play();
|
||||
continue;
|
||||
}
|
||||
lastIndex = Index;
|
||||
playIndex = -1;
|
||||
readIndex = Index;
|
||||
r = ReadFrame(replayFile, b, Length, sizeof(b));
|
||||
StripAudioPackets(b, r);
|
||||
}
|
||||
else if (index) {
|
||||
lastIndex = -1;
|
||||
playIndex = (playIndex >= 0) ? playIndex + 1 : index->Get(fileName.Number(), fileOffset);
|
||||
uchar FileNumber;
|
||||
int FileOffset, Length;
|
||||
if (!(index->Get(playIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
|
||||
readIndex++;
|
||||
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
|
||||
break;
|
||||
r = ReadFrame(replayFile, b, Length, sizeof(b));
|
||||
StripAudioPackets(b, r, audioTrack);
|
||||
}
|
||||
else // allows replay even if the index file is missing
|
||||
r = read(replayFile, b, sizeof(b));
|
||||
if (r > 0) {
|
||||
uchar *p = b;
|
||||
while (r > 0 && Busy() && !blockInput) {
|
||||
int w = Put(p, r);
|
||||
p += w;
|
||||
r -= w;
|
||||
usleep(1); // this keeps the CPU load low
|
||||
cFrame *frame = new cFrame(b, r, readIndex);
|
||||
while (Busy() && !blockInput && !Put(frame))
|
||||
;
|
||||
}
|
||||
}
|
||||
else if (r ==0)
|
||||
else if (r == 0)
|
||||
eof = true;
|
||||
else if (r < 0 && errno != EAGAIN) {
|
||||
LOG_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
else//XXX
|
||||
usleep(1); // this keeps the CPU load low
|
||||
if (blockInput > 1)
|
||||
blockInput = 1;
|
||||
}
|
||||
|
||||
dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
|
||||
}
|
||||
|
||||
void cReplayBuffer::Output(void)
|
||||
{
|
||||
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
|
||||
|
||||
uchar b[MINVIDEODATA];
|
||||
while (Busy()) {
|
||||
int r = blockOutput ? 0 : Get(b, sizeof(b));
|
||||
if (r > 0) {
|
||||
uchar *p = b;
|
||||
while (r > 0 && Busy() && !blockOutput) {
|
||||
cFile::FileReadyForWriting(videoDev, 100);
|
||||
int w = write(videoDev, p, r);
|
||||
if (w > 0) {
|
||||
p += w;
|
||||
r -= w;
|
||||
fileOffset += w;
|
||||
}
|
||||
else if (w < 0 && errno != EAGAIN) {
|
||||
LOG_ERROR;
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
usleep(1); // this keeps the CPU load low
|
||||
if (blockOutput > 1)
|
||||
blockOutput = 1;
|
||||
}
|
||||
|
||||
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
|
||||
}
|
||||
|
||||
void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
|
||||
{
|
||||
if (canDoTrickMode) {
|
||||
for (int i = 0; i < Length - 6; i++) {
|
||||
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
|
||||
uchar c = b[i + 3];
|
||||
@ -907,6 +981,7 @@ void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
|
||||
esyslog(LOG_ERR, "ERROR: broken packet header");
|
||||
XXX*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::DisplayFrame(uchar *b, int Length)
|
||||
@ -918,85 +993,11 @@ void cReplayBuffer::DisplayFrame(uchar *b, int Length)
|
||||
CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
|
||||
}
|
||||
|
||||
void cReplayBuffer::Clear(bool Block)
|
||||
{
|
||||
if (!(blockInput || blockOutput)) {
|
||||
blockInput = blockOutput = 2;
|
||||
time_t t0 = time(NULL);
|
||||
while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
|
||||
usleep(1);
|
||||
Lock();
|
||||
playIndex = -1;
|
||||
cPlayBuffer::Clear();
|
||||
}
|
||||
if (!Block) {
|
||||
blockInput = blockOutput = 0;
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::Pause(void)
|
||||
{
|
||||
paused = !paused;
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
|
||||
if (fastForward || fastRewind) {
|
||||
if (paused)
|
||||
Clear();
|
||||
fastForward = fastRewind = false;
|
||||
}
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
|
||||
stillIndex = -1;
|
||||
}
|
||||
|
||||
void cReplayBuffer::Play(void)
|
||||
{
|
||||
if (fastForward || fastRewind || paused) {
|
||||
if (!paused)
|
||||
Clear();
|
||||
stillIndex = -1;
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
|
||||
fastForward = fastRewind = paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::Forward(void)
|
||||
{
|
||||
if (index || paused) {
|
||||
if (!paused)
|
||||
Clear(true);
|
||||
stillIndex = -1;
|
||||
fastForward = !fastForward;
|
||||
fastRewind = false;
|
||||
if (paused)
|
||||
CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
|
||||
if (!paused)
|
||||
Clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::Backward(void)
|
||||
{
|
||||
if (index) {
|
||||
Clear(true);
|
||||
stillIndex = -1;
|
||||
fastRewind = !fastRewind;
|
||||
fastForward = false;
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
|
||||
Clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::Close(void)
|
||||
{
|
||||
if (replayFile >= 0) {
|
||||
fileName.Close();
|
||||
replayFile = -1;
|
||||
fileOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1017,7 +1018,7 @@ int cReplayBuffer::Resume(void)
|
||||
bool cReplayBuffer::Save(void)
|
||||
{
|
||||
if (index) {
|
||||
int Index = index->Get(fileName.Number(), fileOffset);
|
||||
int Index = writeIndex;
|
||||
if (Index >= 0) {
|
||||
Index -= RESUMEBACKUP;
|
||||
if (Index > 0)
|
||||
@ -1046,8 +1047,8 @@ int cReplayBuffer::SkipFrames(int Frames)
|
||||
void cReplayBuffer::SkipSeconds(int Seconds)
|
||||
{
|
||||
if (index && Seconds) {
|
||||
Clear(true);
|
||||
int Index = index->Get(fileName.Number(), fileOffset);
|
||||
Empty(true);
|
||||
int Index = writeIndex;
|
||||
if (Index >= 0) {
|
||||
if (Seconds < 0) {
|
||||
int sec = index->Last() / FRAMESPERSEC;
|
||||
@ -1059,10 +1060,9 @@ void cReplayBuffer::SkipSeconds(int Seconds)
|
||||
Index = 1; // not '0', to allow GetNextIFrame() below to work!
|
||||
uchar FileNumber;
|
||||
int FileOffset;
|
||||
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
|
||||
NextFile(FileNumber, FileOffset);
|
||||
readIndex = writeIndex = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) - 1; // Input() will first increment it!
|
||||
}
|
||||
Clear(false);
|
||||
Empty(false);
|
||||
Play();
|
||||
}
|
||||
}
|
||||
@ -1070,7 +1070,7 @@ void cReplayBuffer::SkipSeconds(int Seconds)
|
||||
void cReplayBuffer::Goto(int Index, bool Still)
|
||||
{
|
||||
if (index) {
|
||||
Clear(true);
|
||||
Empty(true);
|
||||
if (paused)
|
||||
CHECK(ioctl(videoDev, VIDEO_CONTINUE));
|
||||
if (++Index <= 0)
|
||||
@ -1079,28 +1079,27 @@ void cReplayBuffer::Goto(int Index, bool Still)
|
||||
int FileOffset, Length;
|
||||
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
|
||||
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
|
||||
stillIndex = Index;
|
||||
playIndex = -1;
|
||||
still = true;
|
||||
uchar b[MAXFRAMESIZE];
|
||||
int r = ReadFrame(replayFile, b, Length, sizeof(b));
|
||||
if (r > 0)
|
||||
DisplayFrame(b, r);
|
||||
fileOffset += Length;
|
||||
paused = true;
|
||||
}
|
||||
else
|
||||
stillIndex = playIndex = -1;
|
||||
Clear(false);
|
||||
still = false;
|
||||
readIndex = writeIndex = Index;
|
||||
Empty(false);
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
{
|
||||
if (index) {
|
||||
if (stillIndex >= 0)
|
||||
Current = stillIndex;
|
||||
if (still)
|
||||
Current = readIndex;
|
||||
else {
|
||||
Current = index->Get(fileName.Number(), fileOffset);
|
||||
Current = writeIndex;
|
||||
if (SnapToIFrame) {
|
||||
int i1 = index->GetNextIFrame(Current + 1, false);
|
||||
int i2 = index->GetNextIFrame(Current, true);
|
||||
@ -1115,10 +1114,8 @@ void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
|
||||
bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
|
||||
{
|
||||
if (FileNumber > 0) {
|
||||
fileOffset = FileOffset;
|
||||
if (FileNumber > 0)
|
||||
replayFile = fileName.SetOffset(FileNumber, FileOffset);
|
||||
}
|
||||
else if (replayFile >= 0 && eof) {
|
||||
Close();
|
||||
replayFile = fileName.NextFile();
|
||||
@ -1131,12 +1128,6 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
|
||||
|
||||
class cDVDplayBuffer : public cPlayBuffer {
|
||||
private:
|
||||
cCondVar ready4input;
|
||||
cMutex inputMutex;
|
||||
|
||||
cCondVar ready4output;
|
||||
cMutex outputMutex;
|
||||
|
||||
uchar audioTrack;
|
||||
|
||||
cDVD *dvd;//XXX necessary???
|
||||
@ -1186,7 +1177,7 @@ private:
|
||||
int lpcm_count;
|
||||
int is_nav_pack(unsigned char *buffer);
|
||||
void Close(void);
|
||||
void Clear(bool Block = false);
|
||||
virtual void Empty(bool Block = false);
|
||||
int decode_packet(unsigned char *sector, int iframe);
|
||||
int ScanVideoPacket(const uchar *Data, int Count, uchar *PictureType);
|
||||
bool PacketStart(uchar **Data, int len);
|
||||
@ -1201,14 +1192,9 @@ private:
|
||||
void NextState(int State) { prevcycle = cyclestate; cyclestate = State; }
|
||||
protected:
|
||||
virtual void Input(void);
|
||||
virtual void Output(void);
|
||||
public:
|
||||
cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title);
|
||||
virtual ~cDVDplayBuffer();
|
||||
virtual void Pause(void);
|
||||
virtual void Play(void);
|
||||
virtual void Forward(void);
|
||||
virtual void Backward(void);
|
||||
virtual int SkipFrames(int Frames);
|
||||
virtual void SkipSeconds(int Seconds);
|
||||
virtual void Goto(int Position, bool Still = false);
|
||||
@ -1224,6 +1210,41 @@ public:
|
||||
#define cOUTPACK 5
|
||||
#define cOUTFRAMES 6
|
||||
|
||||
cDVDplayBuffer::cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title)
|
||||
:cPlayBuffer(DvbApi, VideoDev, AudioDev)
|
||||
{
|
||||
dvd = DvD;
|
||||
titleid = title;
|
||||
chapid = 0;
|
||||
angle = 0;
|
||||
cyclestate = cOPENDVD;
|
||||
prevcycle = 0;
|
||||
brakeCounter = 0;
|
||||
skipCnt = 0;
|
||||
logAudioTrack = 0;
|
||||
canToggleAudioTrack = true;//XXX determine from cDVD!
|
||||
ac3_config.num_output_ch = 2;
|
||||
// ac3_config.flags = /* mm_accel() | */ MM_ACCEL_MLIB;
|
||||
ac3_config.flags = 0;
|
||||
ac3_init(&ac3_config);
|
||||
data = new uchar[1024 * DVD_VIDEO_LB_LEN];
|
||||
ac3data = new uchar[AC3_BUFFER_SIZE];
|
||||
ac3inp = ac3outp = 0;
|
||||
ac3stat = AC3_START;
|
||||
canDoTrickMode = true;
|
||||
dvbApi->SetModeReplay();
|
||||
Start();
|
||||
}
|
||||
|
||||
cDVDplayBuffer::~cDVDplayBuffer()
|
||||
{
|
||||
Stop();
|
||||
Close();
|
||||
dvbApi->SetModeNormal(false);
|
||||
delete ac3data;
|
||||
delete data;
|
||||
}
|
||||
|
||||
unsigned int cDVDplayBuffer::getAudioStream(unsigned int StreamId)
|
||||
{
|
||||
unsigned int trackID;
|
||||
@ -1267,40 +1288,6 @@ void cDVDplayBuffer::ToggleAudioTrack(void)
|
||||
}
|
||||
}
|
||||
|
||||
cDVDplayBuffer::cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title)
|
||||
:cPlayBuffer(DvbApi, VideoDev, AudioDev)
|
||||
{
|
||||
dvd = DvD;
|
||||
titleid = title;
|
||||
chapid = 0;
|
||||
angle = 0;
|
||||
cyclestate = cOPENDVD;
|
||||
prevcycle = 0;
|
||||
brakeCounter = 0;
|
||||
skipCnt = 0;
|
||||
logAudioTrack = 0;
|
||||
canToggleAudioTrack = true;//XXX determine from cDVD!
|
||||
ac3_config.num_output_ch = 2;
|
||||
// ac3_config.flags = /* mm_accel() | */ MM_ACCEL_MLIB;
|
||||
ac3_config.flags = 0;
|
||||
ac3_init(&ac3_config);
|
||||
data = new uchar[1024 * DVD_VIDEO_LB_LEN];
|
||||
ac3data = new uchar[AC3_BUFFER_SIZE];
|
||||
ac3inp = ac3outp = 0;
|
||||
ac3stat = AC3_START;
|
||||
dvbApi->SetModeReplay();
|
||||
Start();
|
||||
}
|
||||
|
||||
cDVDplayBuffer::~cDVDplayBuffer()
|
||||
{
|
||||
Stop();
|
||||
Close();
|
||||
dvbApi->SetModeNormal(false);
|
||||
delete ac3data;
|
||||
delete data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the pack is a NAV pack. This check is clearly insufficient,
|
||||
* and sometimes we incorrectly think that valid other packs are NAV packs. I
|
||||
@ -1317,15 +1304,13 @@ void cDVDplayBuffer::Input(void)
|
||||
|
||||
doplay = true;
|
||||
while (Busy() && doplay) {
|
||||
inputMutex.Lock();
|
||||
while (blockInput) {
|
||||
if (blockInput) {
|
||||
if (blockInput > 1)
|
||||
blockInput = 1;
|
||||
ready4input.Wait(inputMutex);
|
||||
continue;
|
||||
}
|
||||
inputMutex.Unlock();
|
||||
|
||||
//BEGIN: riped from play_title
|
||||
//BEGIN: ripped from play_title
|
||||
|
||||
/**
|
||||
* Playback by cell in this pgc, starting at the cell for our chapter.
|
||||
@ -1659,8 +1644,6 @@ void cDVDplayBuffer::Input(void)
|
||||
}
|
||||
|
||||
// dsyslog(LOG_INF, "DVD: new cyclestate: %d, pktcnt: %d, cur: %d", cyclestate, pktcnt, cur_output_size);
|
||||
if (blockInput > 1)
|
||||
blockInput = 1;
|
||||
}
|
||||
|
||||
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
|
||||
@ -1761,6 +1744,7 @@ int cDVDplayBuffer::SendPCM(int size)
|
||||
while (p_size) {
|
||||
if (ac3outp != ac3inp) { // data in the buffer
|
||||
buffer[(length + 6) ^ 1] = ac3data[ac3outp]; // swab because ac3dec delivers wrong byteorder
|
||||
// XXX there is no 'swab' here??? (kls)
|
||||
p_size--;
|
||||
length++;
|
||||
ac3outp = (ac3outp + 1) % AC3_BUFFER_SIZE;
|
||||
@ -1791,18 +1775,9 @@ int cDVDplayBuffer::SendPCM(int size)
|
||||
|
||||
length += 6;
|
||||
|
||||
inputMutex.Lock();
|
||||
while (Free() < length && Busy() && !blockInput)
|
||||
ready4input.Wait(inputMutex);
|
||||
inputMutex.Unlock();
|
||||
|
||||
if (Busy() && !blockInput) {
|
||||
if ((Put(buffer, length) != length)) {
|
||||
esyslog(LOG_ERR, "ERROR: Put(buffer, length) != length");
|
||||
return 0;
|
||||
}
|
||||
ready4output.Broadcast();
|
||||
}
|
||||
cFrame *frame = new cFrame(buffer, length);
|
||||
while (Busy() && !blockInput && !Put(frame))
|
||||
;
|
||||
size -= MAXSIZE;
|
||||
}
|
||||
return 0;
|
||||
@ -1834,7 +1809,7 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, int trickMode)
|
||||
uchar *osect = sector;
|
||||
#endif
|
||||
|
||||
//make sure we got an PS packet header
|
||||
//make sure we got a PS packet header
|
||||
if (!PacketStart(§or, DVD_VIDEO_LB_LEN) && GetPacketType(sector) != 0xBA) {
|
||||
esyslog(LOG_ERR, "ERROR: got unexpected packet: %x %x %x %x", sector[0], sector[1], sector[2], sector[3]);
|
||||
return -1;
|
||||
@ -1885,7 +1860,7 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, int trickMode)
|
||||
sector += 6;
|
||||
//we are now at the beginning of the payload
|
||||
|
||||
//correct a3 data lenght - FIXME: why 13 ???
|
||||
//correct ac3 data lenght - FIXME: why 13 ???
|
||||
ac3datalen -= 13;
|
||||
if (audioTrack == *sector) {
|
||||
sector +=4;
|
||||
@ -1933,154 +1908,23 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, int trickMode)
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
inputMutex.Lock();
|
||||
while (Free() < r && Busy() && !blockInput)
|
||||
ready4input.Wait(inputMutex);
|
||||
inputMutex.Unlock();
|
||||
if (Busy() && !blockInput) {
|
||||
if (Put(sector, r) != r) {
|
||||
esyslog(LOG_ERR, "ERROR: Put(sector, r) != r");
|
||||
return 0;
|
||||
}
|
||||
ready4output.Broadcast();
|
||||
}
|
||||
cFrame *frame = new cFrame(sector, r);
|
||||
while (Busy() && !blockInput && !Put(frame))
|
||||
;
|
||||
|
||||
playDecodedAC3();
|
||||
return pt;
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Output(void)
|
||||
{
|
||||
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
|
||||
|
||||
#ifdef DVDDEBUG_BUFFER
|
||||
long long cyl = 0;
|
||||
long long emp = 0;
|
||||
long long low = 0;
|
||||
#endif
|
||||
|
||||
uchar b[MINVIDEODATA];
|
||||
while (Busy()) {
|
||||
#ifdef DVDDEBUG_BUFFER
|
||||
cyl++;
|
||||
#endif
|
||||
outputMutex.Lock();
|
||||
if (blockOutput > 1)
|
||||
blockOutput = 1;
|
||||
|
||||
int r = 0;
|
||||
while (Busy() && ((r = blockOutput ? 0 : Get(b, sizeof(b))) == 0)) {
|
||||
#ifdef DVDDEBUG_BUFFER
|
||||
if (r == 0) {
|
||||
//ups we just emptied the entire buffer
|
||||
dsyslog(LOG_INFO, "DVD: %12Ld warning: Get() failed due to empty buffer %12Ld", cyl, emp);
|
||||
emp++;
|
||||
}
|
||||
#endif
|
||||
ready4output.Wait(outputMutex);
|
||||
if (blockOutput > 1)
|
||||
blockOutput = 1;
|
||||
}
|
||||
outputMutex.Unlock();
|
||||
|
||||
if (r > 0) {
|
||||
#ifdef DVDDEBUG_BUFFER
|
||||
if (Available() != 0 && Available() < (VIDEOBUFSIZE/20)) {
|
||||
//5% warning limit
|
||||
dsyslog(LOG_INFO, "DVD: %12Ld warning: buffer almost empty: %d, %10.2f %12Ld", cyl, Available(), (float)Available() * 100.0 / (float) VIDEOBUFSIZE, low);
|
||||
low++;
|
||||
}
|
||||
#endif
|
||||
ready4input.Broadcast();
|
||||
uchar *p = b;
|
||||
while (r > 0 && Busy() && !blockOutput) {
|
||||
cFile::FileReadyForWriting(videoDev, 100);
|
||||
int w = write(videoDev, p, r);
|
||||
if (w > 0) {
|
||||
p += w;
|
||||
r -= w;
|
||||
}
|
||||
else if (w < 0 && errno != EAGAIN) {
|
||||
LOG_ERROR;
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blockOutput > 1)
|
||||
blockOutput = 1;
|
||||
}
|
||||
|
||||
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Clear(bool Block)
|
||||
void cDVDplayBuffer::Empty(bool Block)
|
||||
{
|
||||
if (!(blockInput || blockOutput)) {
|
||||
blockInput = blockOutput = 2;
|
||||
ready4input.Broadcast();
|
||||
ready4output.Broadcast();
|
||||
time_t t0 = time(NULL);
|
||||
while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
|
||||
usleep(1);
|
||||
Lock();
|
||||
cPlayBuffer::Clear();
|
||||
cPlayBuffer::Empty(true);
|
||||
ac3stat = AC3_START;
|
||||
ac3outp = ac3inp;
|
||||
}
|
||||
if (!Block) {
|
||||
blockInput = blockOutput = 0;
|
||||
ready4input.Broadcast();
|
||||
ready4output.Broadcast();
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
void cDVDplayBuffer::Pause(void)
|
||||
{
|
||||
paused = !paused;
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
|
||||
if (fastForward || fastRewind) {
|
||||
if (paused)
|
||||
Clear();
|
||||
fastForward = fastRewind = false;
|
||||
}
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Play(void)
|
||||
{
|
||||
if (fastForward || fastRewind || paused) {
|
||||
if (!paused)
|
||||
Clear();
|
||||
CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
|
||||
fastForward = fastRewind = paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Forward(void)
|
||||
{
|
||||
if (!paused)
|
||||
Clear(true);
|
||||
fastForward = !fastForward;
|
||||
fastRewind = false;
|
||||
if (paused)
|
||||
CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
|
||||
if (!paused)
|
||||
Clear(false);
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Backward(void)
|
||||
{
|
||||
Clear(true);
|
||||
fastRewind = !fastRewind;
|
||||
fastForward = false;
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
|
||||
CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
|
||||
Clear(false);
|
||||
if (!Block)
|
||||
cPlayBuffer::Empty(false);
|
||||
}
|
||||
|
||||
void cDVDplayBuffer::Close(void)
|
||||
@ -2116,13 +1960,13 @@ void cDVDplayBuffer::SkipSeconds(int Seconds)
|
||||
int newchapid = Seconds > 0 ? chapid + 1 : chapid - 1;
|
||||
|
||||
if (newchapid >= 0 && newchapid < tt_srpt->title[titleid].nr_of_ptts) {
|
||||
Clear(true);
|
||||
Empty(true);
|
||||
chapid = newchapid;
|
||||
NextState(cOPENCHAPTER);
|
||||
if (ac3stat != AC3_STOP)
|
||||
ac3stat=AC3_START;
|
||||
ac3stat = AC3_START;
|
||||
ac3outp = ac3inp;
|
||||
Clear(false);
|
||||
Empty(false);
|
||||
Play();
|
||||
}
|
||||
}
|
||||
@ -2139,7 +1983,7 @@ void cDVDplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
|
||||
// --- cTransferBuffer -------------------------------------------------------
|
||||
|
||||
class cTransferBuffer : public cRingBuffer {
|
||||
class cTransferBuffer : public cRingBufferLinear {
|
||||
private:
|
||||
cDvbApi *dvbApi;
|
||||
int fromDevice, toDevice;
|
||||
@ -2155,7 +1999,7 @@ public:
|
||||
};
|
||||
|
||||
cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
|
||||
:cRingBuffer(VIDEOBUFSIZE, true)
|
||||
:cRingBufferLinear(VIDEOBUFSIZE, true)
|
||||
,remux(VPid, APid, 0, 0, 0)
|
||||
{
|
||||
dvbApi = DvbApi;
|
||||
|
7
menu.c
7
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.91 2001/08/04 08:08:44 kls Exp $
|
||||
* $Id: menu.c 1.92 2001/08/05 10:33:54 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -2438,11 +2438,10 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
|
||||
// Positioning:
|
||||
case kUp: dvbApi->Play(); break;
|
||||
case kDown: dvbApi->Pause(); break;
|
||||
case kLeft: dvbApi->Backward(); break;
|
||||
case kRight: dvbApi->Forward(); break;
|
||||
case kLeft|k_Release:
|
||||
case kLeft: dvbApi->Backward(); break;
|
||||
case kRight|k_Release:
|
||||
dvbApi->Play(); break;
|
||||
case kRight: dvbApi->Forward(); break;
|
||||
case kGreen|k_Repeat:
|
||||
case kGreen: dvbApi->SkipSeconds(-60); break;
|
||||
case kYellow|k_Repeat:
|
||||
|
304
ringbuffer.c
304
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.3 2001/08/02 13:48:38 kls Exp $
|
||||
* $Id: ringbuffer.c 1.4 2001/08/05 12:17:45 kls Exp $
|
||||
*/
|
||||
|
||||
#include "ringbuffer.h"
|
||||
@ -41,108 +41,42 @@ cRingBuffer::cRingBuffer(int Size, bool Statistics)
|
||||
{
|
||||
size = Size;
|
||||
statistics = Statistics;
|
||||
buffer = NULL;
|
||||
inputThread = NULL;
|
||||
outputThread = NULL;
|
||||
maxFill = 0;
|
||||
busy = false;
|
||||
if (size > 1) { // 'size - 1' must not be 0!
|
||||
buffer = new uchar[size];
|
||||
if (!buffer)
|
||||
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
|
||||
Clear();
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", size);
|
||||
maxFill = 0;
|
||||
}
|
||||
|
||||
cRingBuffer::~cRingBuffer()
|
||||
{
|
||||
delete inputThread;
|
||||
delete outputThread;
|
||||
delete buffer;
|
||||
if (statistics)
|
||||
dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
|
||||
}
|
||||
|
||||
int cRingBuffer::Available(void)
|
||||
void cRingBuffer::WaitForPut(void)
|
||||
{
|
||||
mutex.Lock();
|
||||
int diff = head - tail;
|
||||
mutex.Unlock();
|
||||
return (diff >= 0) ? diff : size + diff;
|
||||
putMutex.Lock();
|
||||
readyForPut.Wait(putMutex);
|
||||
putMutex.Unlock();
|
||||
}
|
||||
|
||||
void cRingBuffer::Clear(void)
|
||||
void cRingBuffer::WaitForGet(void)
|
||||
{
|
||||
mutex.Lock();
|
||||
head = tail = 0;
|
||||
mutex.Unlock();
|
||||
getMutex.Lock();
|
||||
readyForGet.Wait(getMutex);
|
||||
getMutex.Unlock();
|
||||
}
|
||||
|
||||
int cRingBuffer::Put(const uchar *Data, int Count)
|
||||
void cRingBuffer::EnablePut(void)
|
||||
{
|
||||
if (Count > 0) {
|
||||
mutex.Lock();
|
||||
int rest = size - head;
|
||||
int diff = tail - head;
|
||||
mutex.Unlock();
|
||||
int free = (diff > 0) ? diff - 1 : size + diff - 1;
|
||||
if (statistics) {
|
||||
int fill = size - free - 1 + Count;
|
||||
if (fill >= size)
|
||||
fill = size - 1;
|
||||
if (fill > maxFill) {
|
||||
maxFill = fill;
|
||||
int percent = maxFill * 100 / (size - 1);
|
||||
if (percent > 75)
|
||||
dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
|
||||
}
|
||||
}
|
||||
if (free <= 0)
|
||||
return 0;
|
||||
if (free < Count)
|
||||
Count = free;
|
||||
if (Count > maxFill)
|
||||
maxFill = Count;
|
||||
if (Count >= rest) {
|
||||
memcpy(buffer + head, Data, rest);
|
||||
if (Count - rest)
|
||||
memcpy(buffer, Data + rest, Count - rest);
|
||||
head = Count - rest;
|
||||
}
|
||||
else {
|
||||
memcpy(buffer + head, Data, Count);
|
||||
head += Count;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
readyForPut.Broadcast();
|
||||
}
|
||||
|
||||
int cRingBuffer::Get(uchar *Data, int Count)
|
||||
void cRingBuffer::EnableGet(void)
|
||||
{
|
||||
if (Count > 0) {
|
||||
mutex.Lock();
|
||||
int rest = size - tail;
|
||||
int diff = head - tail;
|
||||
mutex.Unlock();
|
||||
int cont = (diff >= 0) ? diff : size + diff;
|
||||
if (rest <= 0)
|
||||
return 0;
|
||||
if (cont < Count)
|
||||
Count = cont;
|
||||
if (Count >= rest) {
|
||||
memcpy(Data, buffer + tail, rest);
|
||||
if (Count - rest)
|
||||
memcpy(Data + rest, buffer, Count - rest);
|
||||
tail = Count - rest;
|
||||
}
|
||||
else {
|
||||
memcpy(Data, buffer + tail, Count);
|
||||
tail += Count;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
readyForGet.Broadcast();
|
||||
}
|
||||
|
||||
bool cRingBuffer::Start(void)
|
||||
@ -178,3 +112,213 @@ void cRingBuffer::Stop(void)
|
||||
DELETENULL(outputThread);
|
||||
}
|
||||
|
||||
// --- cRingBufferLinear ----------------------------------------------------
|
||||
|
||||
cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics)
|
||||
:cRingBuffer(Size, Statistics)
|
||||
{
|
||||
buffer = NULL;
|
||||
if (Size > 1) { // 'Size - 1' must not be 0!
|
||||
buffer = new uchar[Size];
|
||||
if (!buffer)
|
||||
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", Size);
|
||||
Clear();
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", Size);
|
||||
}
|
||||
|
||||
cRingBufferLinear::~cRingBufferLinear()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
int cRingBufferLinear::Available(void)
|
||||
{
|
||||
Lock();
|
||||
int diff = head - tail;
|
||||
Unlock();
|
||||
return (diff >= 0) ? diff : Size() + diff;
|
||||
}
|
||||
|
||||
void cRingBufferLinear::Clear(void)
|
||||
{
|
||||
Lock();
|
||||
head = tail = 0;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
int cRingBufferLinear::Put(const uchar *Data, int Count)
|
||||
{
|
||||
if (Count > 0) {
|
||||
Lock();
|
||||
int rest = Size() - head;
|
||||
int diff = tail - head;
|
||||
Unlock();
|
||||
int free = (diff > 0) ? diff - 1 : Size() + diff - 1;
|
||||
if (statistics) {
|
||||
int fill = Size() - free - 1 + Count;
|
||||
if (fill >= Size())
|
||||
fill = Size() - 1;
|
||||
if (fill > maxFill) {
|
||||
maxFill = fill;
|
||||
int percent = maxFill * 100 / (Size() - 1);
|
||||
if (percent > 75)
|
||||
dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
|
||||
}
|
||||
}
|
||||
if (free <= 0)
|
||||
return 0;
|
||||
if (free < Count)
|
||||
Count = free;
|
||||
if (Count > maxFill)
|
||||
maxFill = Count;
|
||||
if (Count >= rest) {
|
||||
memcpy(buffer + head, Data, rest);
|
||||
if (Count - rest)
|
||||
memcpy(buffer, Data + rest, Count - rest);
|
||||
head = Count - rest;
|
||||
}
|
||||
else {
|
||||
memcpy(buffer + head, Data, Count);
|
||||
head += Count;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
int cRingBufferLinear::Get(uchar *Data, int Count)
|
||||
{
|
||||
if (Count > 0) {
|
||||
Lock();
|
||||
int rest = Size() - tail;
|
||||
int diff = head - tail;
|
||||
Unlock();
|
||||
int cont = (diff >= 0) ? diff : Size() + diff;
|
||||
if (rest <= 0)
|
||||
return 0;
|
||||
if (cont < Count)
|
||||
Count = cont;
|
||||
if (Count >= rest) {
|
||||
memcpy(Data, buffer + tail, rest);
|
||||
if (Count - rest)
|
||||
memcpy(Data + rest, buffer, Count - rest);
|
||||
tail = Count - rest;
|
||||
}
|
||||
else {
|
||||
memcpy(Data, buffer + tail, Count);
|
||||
tail += Count;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
// --- cFrame ----------------------------------------------------------------
|
||||
|
||||
cFrame::cFrame(const uchar *Data, int Count, int Index)
|
||||
{
|
||||
count = Count;
|
||||
index = Index;
|
||||
data = new uchar[count];
|
||||
if (data)
|
||||
memcpy(data, Data, count);
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: can't allocate frame buffer (count=%d)", count);
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
cFrame::~cFrame()
|
||||
{
|
||||
delete data;
|
||||
}
|
||||
|
||||
// --- cRingBufferFrame ------------------------------------------------------
|
||||
|
||||
cRingBufferFrame::cRingBufferFrame(int Size, bool Statistics = false)
|
||||
:cRingBuffer(Size, Statistics)
|
||||
{
|
||||
head = NULL;
|
||||
currentFill = 0;
|
||||
}
|
||||
|
||||
cRingBufferFrame::~cRingBufferFrame()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void cRingBufferFrame::Clear(void)
|
||||
{
|
||||
Lock();
|
||||
const cFrame *p;
|
||||
while ((p = Get(false)) != NULL)
|
||||
Drop(p);
|
||||
Unlock();
|
||||
EnablePut();
|
||||
EnableGet();
|
||||
}
|
||||
|
||||
bool cRingBufferFrame::Put(cFrame *Frame)
|
||||
{
|
||||
if (Frame->Count() <= Free()) {
|
||||
Lock();
|
||||
if (head) {
|
||||
Frame->next = head->next;
|
||||
head->next = Frame;
|
||||
head = Frame;
|
||||
}
|
||||
else {
|
||||
head = Frame->next = Frame;
|
||||
}
|
||||
currentFill += Frame->Count();
|
||||
Unlock();
|
||||
EnableGet();
|
||||
return true;
|
||||
}
|
||||
WaitForPut();
|
||||
return false;
|
||||
}
|
||||
|
||||
const cFrame *cRingBufferFrame::Get(bool Wait)
|
||||
{
|
||||
Lock();
|
||||
cFrame *p = head ? head->next : NULL;
|
||||
Unlock();
|
||||
if (!p && Wait)
|
||||
WaitForGet();
|
||||
return p;
|
||||
}
|
||||
|
||||
void cRingBufferFrame::Delete(const cFrame *Frame)
|
||||
{
|
||||
currentFill -= Frame->Count();
|
||||
delete Frame;
|
||||
}
|
||||
|
||||
void cRingBufferFrame::Drop(const cFrame *Frame)
|
||||
{
|
||||
Lock();
|
||||
if (head) {
|
||||
if (Frame == head->next) {
|
||||
if (head->next != head) {
|
||||
head->next = Frame->next;
|
||||
Delete(Frame);
|
||||
}
|
||||
else {
|
||||
Delete(head);
|
||||
head = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: attempt to drop wrong frame from ring buffer!");
|
||||
}
|
||||
Unlock();
|
||||
EnablePut();
|
||||
}
|
||||
|
||||
int cRingBufferFrame::Available(void)
|
||||
{
|
||||
Lock();
|
||||
int av = currentFill;
|
||||
Unlock();
|
||||
return av;
|
||||
}
|
||||
|
85
ringbuffer.h
85
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.3 2001/08/02 13:48:42 kls Exp $
|
||||
* $Id: ringbuffer.h 1.4 2001/08/05 11:12:06 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RINGBUFFER_H
|
||||
@ -24,25 +24,24 @@ private:
|
||||
cRingBufferInputThread *inputThread;
|
||||
cRingBufferOutputThread *outputThread;
|
||||
cMutex mutex;
|
||||
int size, head, tail;
|
||||
uchar *buffer;
|
||||
int maxFill;
|
||||
cCondVar readyForPut, readyForGet;
|
||||
cMutex putMutex, getMutex;
|
||||
int size;
|
||||
bool busy;
|
||||
bool statistics;
|
||||
protected:
|
||||
int maxFill;//XXX
|
||||
bool statistics;//XXX
|
||||
void WaitForPut(void);
|
||||
void WaitForGet(void);
|
||||
void EnablePut(void);
|
||||
void EnableGet(void);
|
||||
virtual void Clear(void) = 0;
|
||||
virtual int Available(void) = 0;
|
||||
int Free(void) { return size - Available() - 1; }
|
||||
void Lock(void) { mutex.Lock(); }
|
||||
void Unlock(void) { mutex.Unlock(); }
|
||||
int Available(void);
|
||||
int Free(void) { return size - Available() - 1; }
|
||||
int Size(void) { return size; }
|
||||
bool Busy(void) { return busy; }
|
||||
void Clear(void);
|
||||
// Immediately clears the ring buffer.
|
||||
int Put(const uchar *Data, int Count);
|
||||
// Puts at most Count bytes of Data into the ring buffer.
|
||||
// Returns the number of bytes actually stored.
|
||||
int Get(uchar *Data, int Count);
|
||||
// Gets at most Count bytes of Data from the ring buffer.
|
||||
// Returns the number of bytes actually retrieved.
|
||||
virtual void Input(void) = 0;
|
||||
// Runs as a separate thread and shall continuously read data from
|
||||
// a source and call Put() to store the data in the ring buffer.
|
||||
@ -57,4 +56,60 @@ public:
|
||||
void Stop(void);
|
||||
};
|
||||
|
||||
class cRingBufferLinear : public cRingBuffer {
|
||||
private:
|
||||
int head, tail;
|
||||
uchar *buffer;
|
||||
protected:
|
||||
virtual int Available(void);
|
||||
virtual void Clear(void);
|
||||
// Immediately clears the ring buffer.
|
||||
int Put(const uchar *Data, int Count);
|
||||
// Puts at most Count bytes of Data into the ring buffer.
|
||||
// Returns the number of bytes actually stored.
|
||||
int Get(uchar *Data, int Count);
|
||||
// Gets at most Count bytes of Data from the ring buffer.
|
||||
// Returns the number of bytes actually retrieved.
|
||||
public:
|
||||
cRingBufferLinear(int Size, bool Statistics = false);
|
||||
virtual ~cRingBufferLinear();
|
||||
};
|
||||
|
||||
class cFrame {
|
||||
friend class cRingBufferFrame;
|
||||
private:
|
||||
cFrame *next;
|
||||
uchar *data;
|
||||
int count;
|
||||
int index;
|
||||
public:
|
||||
cFrame(const uchar *Data, int Count, int Index = -1);
|
||||
~cFrame();
|
||||
const uchar *Data(void) const { return data; }
|
||||
int Count(void) const { return count; }
|
||||
int Index(void) const { return index; }
|
||||
};
|
||||
|
||||
class cRingBufferFrame : public cRingBuffer {
|
||||
private:
|
||||
cFrame *head;
|
||||
int currentFill;
|
||||
void Delete(const cFrame *Frame);
|
||||
protected:
|
||||
virtual int Available(void);
|
||||
virtual void Clear(void);
|
||||
// Immediately clears the ring buffer.
|
||||
bool Put(cFrame *Frame);
|
||||
// Puts the Frame into the ring buffer.
|
||||
// Returns true if this was possible.
|
||||
const cFrame *Get(bool Wait = true);
|
||||
// Gets the next frame from the ring buffer.
|
||||
// The actual data still remains in the buffer until Drop() is called.
|
||||
void Drop(const cFrame *Frame);
|
||||
// Drops the Frame that has just been fetched with Get().
|
||||
public:
|
||||
cRingBufferFrame(int Size, bool Statistics = false);
|
||||
virtual ~cRingBufferFrame();
|
||||
};
|
||||
|
||||
#endif // __RINGBUFFER_H
|
||||
|
12
thread.c
12
thread.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.c 1.10 2001/08/02 13:48:45 kls Exp $
|
||||
* $Id: thread.c 1.11 2001/08/05 10:36:52 kls Exp $
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
@ -26,15 +26,15 @@ cCondVar::~cCondVar()
|
||||
pthread_cond_destroy(&cond);
|
||||
}
|
||||
|
||||
bool cCondVar::Wait(cMutex &_mutex)
|
||||
bool cCondVar::Wait(cMutex &Mutex)
|
||||
{
|
||||
return pthread_cond_wait(&cond, &_mutex.mutex);
|
||||
return pthread_cond_wait(&cond, &Mutex.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
bool cCondVar::TimedWait(cMutex &_mutex, unsigned long tmout)
|
||||
bool cCondVar::TimedWait(cMutex &Mutex, unsigned long tmout)
|
||||
{
|
||||
return pthread_cond_timedwait(&cond, &_mutex.mutex, tmout);
|
||||
return pthread_cond_timedwait(&cond, &Mutex.mutex, tmout);
|
||||
}
|
||||
*/
|
||||
|
||||
@ -43,10 +43,12 @@ void cCondVar::Broadcast(void)
|
||||
pthread_cond_broadcast(&cond);
|
||||
}
|
||||
|
||||
/*
|
||||
void cCondVar::Signal(void)
|
||||
{
|
||||
pthread_cond_signal(&cond);
|
||||
}
|
||||
*/
|
||||
|
||||
// --- cMutex ----------------------------------------------------------------
|
||||
|
||||
|
8
thread.h
8
thread.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.h 1.7 2001/08/02 13:48:48 kls Exp $
|
||||
* $Id: thread.h 1.8 2001/08/05 10:36:47 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __THREAD_H
|
||||
@ -21,10 +21,10 @@ private:
|
||||
public:
|
||||
cCondVar(void);
|
||||
~cCondVar();
|
||||
bool Wait(cMutex &_mutex);
|
||||
//bool TimedWait(cMutex &_mutex, unsigned long tmout);
|
||||
bool Wait(cMutex &Mutex);
|
||||
//bool TimedWait(cMutex &Mutex, unsigned long tmout);
|
||||
void Broadcast(void);
|
||||
void Signal(void);
|
||||
//void Signal(void);
|
||||
};
|
||||
|
||||
class cMutex {
|
||||
|
Loading…
Reference in New Issue
Block a user