New ringbuffer for frames

This commit is contained in:
Klaus Schmidinger 2001-08-05 12:23:24 +02:00
parent 614113cdcb
commit c2ed9b5daf
7 changed files with 588 additions and 543 deletions

View File

@ -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.

616
dvbapi.c
View File

@ -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,31 +903,23 @@ 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)
eof = true;
@ -820,50 +928,16 @@ void cReplayBuffer::Input(void)
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];
@ -908,6 +982,7 @@ void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
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(&sector, 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;
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
View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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 ----------------------------------------------------------------

View File

@ -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 {