summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY7
-rw-r--r--Makefile41
-rw-r--r--config.h4
-rw-r--r--dvbapi.c314
-rw-r--r--remux.c173
-rw-r--r--remux.h51
-rw-r--r--ringbuffer.c170
-rw-r--r--ringbuffer.h55
8 files changed, 589 insertions, 226 deletions
diff --git a/HISTORY b/HISTORY
index a2d05ff4..a888e9ba 100644
--- a/HISTORY
+++ b/HISTORY
@@ -414,7 +414,7 @@ Video Disk Recorder Revision History
given number of seconds. This is mainly useful in combination with the new
'runvdr' script that restarts VDR in case is has exited.
-2001-03-02: Version 0.72
+2001-04-01: Version 0.72
- Fixed SVDRP commands LSTC and LSTT to make them return an error message if
no channels or timers are defined.
@@ -427,3 +427,8 @@ Video Disk Recorder Revision History
- Fixed internationalization of some Main menu texts.
- Updated 'channels.conf' after the recent changes of Premiere World (thanks
to Axel Gruber).
+- Redesigned the ring buffer to make it work with two separate threads for
+ input and output (also prepared for using a remultiplexer).
+- Fixed setting system time from transponders.
+- Fixed a segfault in the Schedule menu in case there is no EPG information.
+- The 'runvdr' script now kills any leftover vdr threads before restarting it.
diff --git a/Makefile b/Makefile
index c1cfabe1..e2b80bb1 100644
--- a/Makefile
+++ b/Makefile
@@ -4,13 +4,14 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.20 2001/02/24 15:52:58 kls Exp $
+# $Id: Makefile 1.21 2001/03/18 16:47:00 kls Exp $
DVBDIR = ../DVB
INCLUDES = -I$(DVBDIR)/driver
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
- recording.o remote.o svdrp.o thread.o tools.o vdr.o videodir.o
+ recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
+ videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
@@ -37,26 +38,28 @@ font: genfontfile fontfix.c fontosd.c
# Implicit rules:
%.o: %.c
- g++ -g -O2 -Wall -m486 -c $(DEFINES) $(INCLUDES) $<
+ g++ -g -O2 -Wall -Woverloaded-virtual -m486 -c $(DEFINES) $(INCLUDES) $<
# Dependencies:
-config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
-dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
-dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
-eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
-font.o : font.c font.h fontfix.c fontosd.c tools.h
-i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
-interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
-menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
-osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
-recording.o: recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
-remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
-svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
-thread.o : thread.c thread.h tools.h
-tools.o : tools.c tools.h
-vdr.o : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
-videodir.o : videodir.c tools.h videodir.h
+config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
+dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h remux.h ringbuffer.h svdrp.h thread.h tools.h videodir.h
+dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
+eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
+font.o : font.c font.h fontfix.c fontosd.c tools.h
+i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
+interface.o : interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
+menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
+osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
+recording.o : recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
+remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
+remux.o : remux.c remux.h tools.h
+ringbuffer.o: ringbuffer.c ringbuffer.h thread.h tools.h
+svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
+thread.o : thread.c thread.h tools.h
+tools.o : tools.c tools.h
+vdr.o : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
+videodir.o : videodir.c tools.h videodir.h
# The main program:
diff --git a/config.h b/config.h
index 60a95c3a..1bc8a535 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.42 2001/02/24 13:19:39 kls Exp $
+ * $Id: config.h 1.43 2001/03/18 16:47:00 kls Exp $
*/
#ifndef __CONFIG_H
@@ -19,7 +19,7 @@
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "0.71"
+#define VDRVERSION "0.72"
#define MaxBuffer 10000
diff --git a/dvbapi.c b/dvbapi.c
index 1c29c8b4..048683b4 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.63 2001/03/14 18:39:53 kls Exp $
+ * $Id: dvbapi.c 1.64 2001/03/18 16:47:16 kls Exp $
*/
#include "dvbapi.h"
@@ -22,6 +22,8 @@ extern "C" {
#include "config.h"
#include "interface.h"
#include "recording.h"
+#include "remux.h"
+#include "ringbuffer.h"
#include "tools.h"
#include "videodir.h"
@@ -29,29 +31,9 @@ extern "C" {
#define VBIDEVICE "/dev/vbi"
// The size of the array used to buffer video data:
+// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE (1024*1024)
-// The minimum amount of video data necessary to identify frames
-// (must be smaller than VIDEOBUFSIZE!):
-#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
-
-// The maximum time the buffer is allowed to write data to disk when recording:
-#define MAXRECORDWRITETIME 50 // ms
-
-// Picture types:
-#define NO_PICTURE 0
-#define I_FRAME 1
-#define P_FRAME 2
-#define B_FRAME 3
-
-// Start codes:
-#define SC_PICTURE 0x00 // "picture header"
-#define SC_SEQU 0xB3 // "sequence header"
-#define SC_PHEAD 0xBA // "pack header"
-#define SC_SHEAD 0xBB // "system header"
-#define SC_AUDIO 0xC0
-#define SC_VIDEO 0xE0
-
#define FRAMESPERSEC 25
// The maximum file size is limited by the range that can be covered
@@ -333,7 +315,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
return -1;
}
-// --- cRingBuffer -----------------------------------------------------------
+// --- cRingBuffer_ -----------------------------------------------------------
/* cRingBuffer reads data from an input file, stores it in a buffer and writes
it to an output file upon request. The Read() and Write() functions should
@@ -344,7 +326,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
will be made.
*/
-class cRingBuffer {
+class cRingBuffer_ {
private:
uchar *buffer;
int size, head, tail, freeLimit, availLimit;
@@ -367,8 +349,8 @@ protected:
int FindStartCode(uchar Code, int Offset = 0);
int GetPacketLength(int Offset = 0);
public:
- cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
- virtual ~cRingBuffer();
+ cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
+ virtual ~cRingBuffer_();
virtual int Read(int Max = -1);
virtual int Write(int Max = -1);
bool EndOfFile(void) { return eof; }
@@ -377,7 +359,7 @@ public:
void Skip(int n);
};
-cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
+cRingBuffer_::cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
{
inFile = InFile;
outFile = OutFile;
@@ -393,13 +375,13 @@ cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
}
-cRingBuffer::~cRingBuffer()
+cRingBuffer_::~cRingBuffer_()
{
dsyslog(LOG_INFO, "buffer stats: %d free, %d overflows, limit exceeded %d times", minFree, countOverflow, countLimit);
delete buffer;
}
-int cRingBuffer::Byte(int Offset)
+int cRingBuffer_::Byte(int Offset)
{
if (buffer && Offset < Available()) {
Offset += head;
@@ -410,7 +392,7 @@ int cRingBuffer::Byte(int Offset)
return -1;
}
-bool cRingBuffer::Set(int Offset, int Length, int Value)
+bool cRingBuffer_::Set(int Offset, int Length, int Value)
{
if (buffer && Offset + Length <= Available() ) {
Offset += head;
@@ -425,7 +407,7 @@ bool cRingBuffer::Set(int Offset, int Length, int Value)
return false;
}
-void cRingBuffer::Skip(int n)
+void cRingBuffer_::Skip(int n)
{
if (n > 0) {
if (head < tail) {
@@ -443,7 +425,7 @@ void cRingBuffer::Skip(int n)
}
}
-int cRingBuffer::Read(int Max)
+int cRingBuffer_::Read(int Max)
{
if (buffer) {
eof = false;
@@ -501,7 +483,7 @@ int cRingBuffer::Read(int Max)
return -1;
}
-int cRingBuffer::Write(int Max)
+int cRingBuffer_::Write(int Max)
{
if (buffer) {
int avail = Available();
@@ -540,7 +522,7 @@ int cRingBuffer::Write(int Max)
return -1;
}
-int cRingBuffer::FindStartCode(uchar Code, int Offset)
+int cRingBuffer_::FindStartCode(uchar Code, int Offset)
{
// Searches for a start code (beginning at Offset) and returns the number
// of bytes from Offset to the start code.
@@ -557,7 +539,7 @@ int cRingBuffer::FindStartCode(uchar Code, int Offset)
return -1;
}
-int cRingBuffer::GetPacketLength(int Offset)
+int cRingBuffer_::GetPacketLength(int Offset)
{
// Returns the entire length of the packet starting at offset.
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
@@ -671,31 +653,29 @@ int cFileName::NextFile(void)
// --- cRecordBuffer ---------------------------------------------------------
-class cRecordBuffer : public cRingBuffer, public cThread {
+class cRecordBuffer : public cRingBuffer {
private:
cFileName fileName;
cIndexFile *index;
+ cRemux remux;
uchar pictureType;
int fileSize;
int videoDev;
int recordFile;
- bool ok, synced, stop;
+ bool recording;
time_t lastDiskSpaceCheck;
bool RunningLowOnDiskSpace(void);
- int ScanVideoPacket(int *PictureType, int Offset);
- int Synchronize(void);
bool NextFile(void);
- virtual int Write(int Max = -1);
- bool WriteWithTimeout(void);
protected:
- virtual void Action(void);
+ virtual void Input(void);
+ virtual void Output(void);
public:
cRecordBuffer(int *InFile, const char *FileName);
virtual ~cRecordBuffer();
};
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
-:cRingBuffer(InFile, &recordFile, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
+:cRingBuffer(VIDEOBUFSIZE)
,fileName(FileName, true)
{
index = NULL;
@@ -703,7 +683,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
fileSize = 0;
videoDev = *InFile;
recordFile = fileName.Open();
- ok = synced = stop = false;
+ recording = false;
lastDiskSpaceCheck = time(NULL);
if (!fileName.Name())
return;
@@ -712,44 +692,15 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
if (!index)
esyslog(LOG_ERR, "ERROR: can't allocate index");
// let's continue without index, so we'll at least have the recording
- ok = true;
Start();
}
cRecordBuffer::~cRecordBuffer()
{
- stop = true;
- Cancel(3);
+ Stop();
delete index;
}
-void cRecordBuffer::Action(void)
-{
- dsyslog(LOG_INFO, "recording thread started (pid=%d)", getpid());
-
- time_t t = time(NULL);
- for (;;) {
- usleep(1); // this keeps the CPU load low
-
- LOCK_THREAD;
-
- int r = Read();
- if (r >= 0) {
- if (r > 0)
- t = time(NULL);
- if (!WriteWithTimeout())
- break;
- }
- if (r < 0 || (r == 0 && time(NULL) - t > 5)) {
- esyslog(LOG_ERR, "ERROR: video data stream broken");
- t = time(NULL);
- }
- }
- SetPlayMode(videoDev, VID_PLAY_RESET);
-
- dsyslog(LOG_INFO, "end recording thread");
-}
-
bool cRecordBuffer::RunningLowOnDiskSpace(void)
{
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
@@ -763,88 +714,6 @@ bool cRecordBuffer::RunningLowOnDiskSpace(void)
return false;
}
-int cRecordBuffer::ScanVideoPacket(int *PictureType, int Offset)
-{
- // Scans the video packet starting at Offset and returns its length.
- // If the return value is -1 the packet was not completely in the buffer.
-
- int Length = GetPacketLength(Offset);
- if (Length <= Available()) {
- int i = Offset + 8; // the minimum length of the video packet header
- i += Byte(i) + 1; // possible additional header bytes
- for (; i < Offset + Length; i++) {
- if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
- switch (Byte(i + 3)) {
- case SC_PICTURE: *PictureType = GetPictureType(i);
- return Length;
- }
- }
- }
- *PictureType = NO_PICTURE;
- return Length;
- }
- return -1;
-}
-
-int cRecordBuffer::Synchronize(void)
-{
- // Positions to the start of a data block (skipping everything up to
- // an I-frame if not synced) and returns the block length.
-
- pictureType = NO_PICTURE;
-
- //XXX remove this once the buffer is handled with two separate threads:
- if (!synced && Free() < 100000) {
- dsyslog(LOG_INFO, "unable to synchronize, dropped %d bytes", Available());
- Clear();
- return 0;
- }
- for (int i = 0; Available() > MINVIDEODATA && i < MINVIDEODATA; i++) {
- if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
- switch (Byte(i + 3)) {
- case SC_VIDEO: {
- int pt = NO_PICTURE;
- int l = ScanVideoPacket(&pt, i);
- if (l < 0)
- return 0; // no useful data found, wait for more
- if (pt != NO_PICTURE) {
- if (pt < I_FRAME || B_FRAME < pt) {
- esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
- }
- else if (pictureType == NO_PICTURE) {
- if (!synced) {
- if (pt == I_FRAME) {
- Skip(i);
- synced = true;
- }
- else {
- Skip(i + l);
- i = 0;
- break;
- }
- }
- if (synced)
- pictureType = pt;
- }
- else
- return i;
- }
- else if (!synced) {
- Skip(i + l);
- i = 0;
- break;
- }
- i += l - 1; // -1 to compensate for i++ in the loop!
- }
- break;
- case SC_AUDIO: i += GetPacketLength(i) - 1; // -1 to compensate for i++ in the loop!
- break;
- }
- }
- }
- return 0; // no useful data found, wait for more
-}
-
bool cRecordBuffer::NextFile(void)
{
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
@@ -856,56 +725,93 @@ bool cRecordBuffer::NextFile(void)
return recordFile >= 0;
}
-int cRecordBuffer::Write(int Max)
+void cRecordBuffer::Input(void)
{
- // This function ignores the incoming 'Max'!
- // It tries to write out exactly *one* frame block.
- if (!ok)
- return -1;
- int n = Synchronize();
- if (n) {
- if (stop && pictureType == I_FRAME) {
- ok = false;
- return -1; // finish the recording before the next 'I' frame
- }
- if (NextFile()) {
- if (index && pictureType != NO_PICTURE)
- index->Write(pictureType, fileName.Number(), fileSize);
- int written = 0;
- for (;;) {
- int w = cRingBuffer::Write(n);
- if (w >= 0) {
- fileSize += w;
- written += w;
- n -= w;
- if (n == 0)
- return written;
+ dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
+
+ uchar b[MINVIDEODATA];
+ time_t t = time(NULL);
+ recording = true;
+ for (;;) {
+ int r = read(videoDev, b, sizeof(b));
+ if (r > 0) {
+ uchar *p = b;
+ while (r > 0) {
+ int w = Put(p, r);
+ p += w;
+ r -= w;
}
- else
- return w;
+ t = time(NULL);
+ }
+ else if (r < 0) {
+ if (errno != EAGAIN) {
+ LOG_ERROR;
+ break;
}
- }
- return -1;
- }
- return 0;
+ }
+ else if (time(NULL) - t > 5) {
+ esyslog(LOG_ERR, "ERROR: video data stream broken");
+ t = time(NULL);
+ }
+ cFile::FileReady(videoDev, 100);
+ if (!recording)
+ break;
+ }
+ SetPlayMode(videoDev, VID_PLAY_RESET);
+
+ dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
}
-bool cRecordBuffer::WriteWithTimeout(void)
+void cRecordBuffer::Output(void)
{
- int t0 = time_ms();
- do {
- int w = Write();
- if (w < 0)
- return false;
- if (w == 0)
- break;
- } while (time_ms() - t0 < MAXRECORDWRITETIME);
- return true;
+ dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
+
+ uchar b[MINVIDEODATA * 2];
+ int r = 0;
+ for (;;) {
+ usleep(1); // this keeps the CPU load low
+ r += Get(b + r, sizeof(b) - r);
+ if (r > 0) {
+ //XXX buffer full???
+ int Count = r, Result;
+ const uchar *p = remux.Process(b, Count, Result, pictureType);
+ if (p) {
+ if (!Busy() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
+ break;
+ if (NextFile()) {
+ if (index && pictureType != NO_PICTURE)
+ index->Write(pictureType, fileName.Number(), fileSize);
+ while (Result > 0) {
+ int w = write(recordFile, p, Result);
+ if (w < 0) {
+ LOG_ERROR_STR(fileName.Name());
+ recording = false;
+ return;
+ }
+ p += w;
+ Result -= w;
+ fileSize += w;
+ }
+ }
+ else
+ break;
+ }
+ if (Count > 0) {
+ r -= Count;
+ memmove(b, b + Count, r);
+ }
+ if (!recording)
+ break;
+ }
+ }
+ recording = false;
+
+ dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
}
// --- cReplayBuffer ---------------------------------------------------------
-class cReplayBuffer : public cRingBuffer, public cThread {
+class cReplayBuffer : public cRingBuffer_, public cThread {
private:
enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
@@ -944,7 +850,7 @@ public:
};
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
-:cRingBuffer(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+:cRingBuffer_(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
,fileName(FileName, false)
{
index = NULL;
@@ -1271,7 +1177,7 @@ int cReplayBuffer::Read(int Max = -1)
int readin = 0;
do {
// If Max is > 0 here we need to make sure we read in the entire block!
- int r = cRingBuffer::Read(Max);
+ int r = cRingBuffer_::Read(Max);
if (r >= 0)
readin += r;
else
@@ -1300,7 +1206,7 @@ int cReplayBuffer::Write(int Max)
if (Max) {
int w;
do {
- w = cRingBuffer::Write(Max);
+ w = cRingBuffer_::Write(Max);
if (w >= 0) {
fileOffset += w;
Written += w;
@@ -1348,7 +1254,7 @@ void cTransferBuffer::Action(void)
{
dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
- cRingBuffer Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
+ cRingBuffer_ Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
active = true;
while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start
Buffer.Read(); // initializes fromDevice for reading
@@ -1364,7 +1270,7 @@ void cTransferBuffer::Action(void)
// --- cCuttingBuffer --------------------------------------------------------
-class cCuttingBuffer : public cRingBuffer, public cThread {
+class cCuttingBuffer : public cRingBuffer_, public cThread {
private:
bool active;
int fromFile, toFile;
@@ -1379,7 +1285,7 @@ public:
};
cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
-:cRingBuffer(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+:cRingBuffer_(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
{
active = false;
fromFile = toFile = -1;
@@ -1438,7 +1344,7 @@ void cCuttingBuffer::Action(void)
CurrentFileNumber = FileNumber;
}
if (fromFile >= 0)
- Length = cRingBuffer::Read(Length);
+ Length = cRingBuffer_::Read(Length);
else
break;
}
@@ -1456,7 +1362,7 @@ void cCuttingBuffer::Action(void)
}
LastIFrame = 0;
}
- cRingBuffer::Write(Length);
+ cRingBuffer_::Write(Length);
toIndex->Write(PictureType, toFileName->Number(), FileSize);
FileSize += Length;
if (!LastIFrame)
diff --git a/remux.c b/remux.c
new file mode 100644
index 00000000..e48296d8
--- /dev/null
+++ b/remux.c
@@ -0,0 +1,173 @@
+/*
+ * remux.c: A streaming MPEG2 remultiplexer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $
+ */
+
+/* The calling interface of the 'cRemux::Process()' function is defined
+ as follows:
+
+ 'Data' points to a chunk of data that consists of 'Count' bytes.
+ The 'Process' function shall try to remultiplex as much of the
+ data as possible and return a pointer to the resulting buffer.
+ That buffer typically is different from the incoming 'Data',
+ but in the simplest case (when 'Process' does nothing) might
+ as well point to the original 'Data'. When returning, 'Count'
+ shall be set to the number of bytes that have been processed
+ (i.e. have been taken from 'Data'), while 'Result' indicates
+ how many bytes the returned buffer contains. 'PictureType' shall
+ be set to NO_PICTURE if the returned data does not start a new
+ picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture
+ starting point has been found. This also means that the returned
+ data buffer may contain at most one entire video frame, because
+ the next frame must be returned with its own value for 'PictureType'.
+
+ 'Process' shall do it's best to keep the latency time as short
+ as possible in order to allow a quick start of VDR's "Transfer
+ mode" (displaying the signal of one DVB card on another card).
+ In order to do that, this function may decide to first pass
+ through the incoming data (almost) unprocessed, and make
+ actual processing kick in after a few seconds (if that is at
+ all possible for the algorithm). This may result in a non-
+ optimal stream at the beginning, which won't matter for normal
+ recordings but may make switching through encrypted channels
+ in "Transfer mode" faster.
+
+ In the resulting data stream, a new packet shall always be started
+ when a frame border is encountered. VDR needs this in order to
+ be able to detect and store the frame indexes, and to easily
+ display single frames in fast forward/back mode. The very first
+ data block returned shall be the starting point of an I_FRAME.
+ Everything before that shall be silently dropped.
+
+ If the incoming data is not enough to do remultiplexing, a value
+ of NULL shall be returned ('Result' has no meaning then). This
+ will tell the caller to wait for more data to be presented in
+ the next call. If NULL is returned and 'Count' is not 0, the
+ caller shall remove 'Count' bytes from the beginning of 'Data'
+ before the next call. This is the way 'Process' indicates that
+ it must skip that data.
+
+ Any data that is not used during this call will appear at the
+ beginning of the incoming 'Data' buffer at the next call, plus
+ any new data that has become available.
+
+ It is guaranteed that the caller will completely process any
+ returned data before the next call to 'Process'. That way, 'Process'
+ can dynamically allocate its return buffer and be sure the caller
+ doesn't keep any pointers into that buffer.
+*/
+
+#include "remux.h"
+#include "tools.h"
+
+#if defined(REMUX_NONE)
+
+cRemux::cRemux(void)
+{
+ synced = false;
+}
+
+cRemux::~cRemux()
+{
+}
+
+int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
+{
+ // Returns the entire length of the packet starting at offset, or -1 in case of error.
+ return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
+}
+
+int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
+{
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+
+ int Length = GetPacketLength(Data, Count, Offset);
+ if (Length > 0 && Offset + Length <= Count) {
+ int i = Offset + 8; // the minimum length of the video packet header
+ i += Data[i] + 1; // possible additional header bytes
+ for (; i < Offset + Length; i++) {
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
+ switch (Data[i + 3]) {
+ case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
+ return Length;
+ }
+ }
+ }
+ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
+const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType)
+{
+ int Skip = 0;
+
+ PictureType = NO_PICTURE;
+
+ if (Count >= MINVIDEODATA) {
+ for (int i = 0; i < Count; i++) {
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
+ switch (Data[i + 3]) {
+ case SC_VIDEO:
+ {
+ uchar pt = NO_PICTURE;
+ int l = ScanVideoPacket(Data, Count, i, pt);
+ if (l < 0) {
+ if (Skip < Count)
+ Count = Skip;
+ return NULL; // no useful data found, wait for more
+ }
+ if (pt != NO_PICTURE) {
+ if (pt < I_FRAME || B_FRAME < pt) {
+ esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
+ }
+ else if (PictureType == NO_PICTURE) {
+ if (!synced) {
+ if (pt == I_FRAME) {
+ Skip = i;
+ synced = true;
+ }
+ else {
+ i += l;
+ Skip = i;
+ break;
+ }
+ }
+ if (synced)
+ PictureType = pt;
+ }
+ else {
+ Count = i;
+ Result = i - Skip;
+ return Data + Skip;
+ }
+ }
+ else if (!synced) {
+ i += l;
+ Skip = i;
+ break;
+ }
+ i += l - 1; // -1 to compensate for i++ in the loop!
+ }
+ break;
+ case SC_AUDIO:
+ i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop!
+ break;
+ }
+ }
+ }
+ }
+ if (Skip < Count)
+ Count = Skip;
+ return NULL; // no useful data found, wait for more
+}
+
+#elif defined(REMUX_TEST)
+#endif
+
diff --git a/remux.h b/remux.h
new file mode 100644
index 00000000..bceb676b
--- /dev/null
+++ b/remux.h
@@ -0,0 +1,51 @@
+/*
+ * remux.h: A streaming MPEG2 remultiplexer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: remux.h 1.1 2001/03/31 08:42:27 kls Exp $
+ */
+
+#ifndef __REMUX_H
+#define __REMUX_H
+
+// There are various experiments with different types of remultiplexers
+// going on at the moment. Select the remultiplexer here:
+#define REMUX_NONE 1
+//#define REMUX_TEST 1
+
+// Picture types:
+#define NO_PICTURE 0
+#define I_FRAME 1
+#define P_FRAME 2
+#define B_FRAME 3
+
+// Start codes:
+#define SC_PICTURE 0x00 // "picture header"
+#define SC_SEQU 0xB3 // "sequence header"
+#define SC_PHEAD 0xBA // "pack header"
+#define SC_SHEAD 0xBB // "system header"
+#define SC_AUDIO 0xC0
+#define SC_VIDEO 0xE0
+
+// The minimum amount of video data necessary to identify frames:
+#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
+
+typedef unsigned char uchar;
+
+class cRemux {
+private:
+#if defined(REMUX_NONE)
+ bool synced;
+ int GetPacketLength(const uchar *Data, int Count, int Offset);
+ int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+#elif defined(REMUX_TEST)
+#endif
+public:
+ cRemux(void);
+ ~cRemux();
+ const uchar *Process(const uchar *Data, int &Count, int &Result, uchar &PictureType);
+ };
+
+#endif // __REMUX_H
diff --git a/ringbuffer.c b/ringbuffer.c
new file mode 100644
index 00000000..3b2f5cac
--- /dev/null
+++ b/ringbuffer.c
@@ -0,0 +1,170 @@
+/*
+ * ringbuffer.c: A threaded ring buffer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Parts of this file were inspired by the 'ringbuffy.c' from the
+ * LinuxDVB driver (see linuxtv.org).
+ *
+ * $Id: ringbuffer.c 1.1 2001/03/18 16:47:00 kls Exp $
+ */
+
+#include "ringbuffer.h"
+#include "tools.h"
+
+// --- cRingBufferInputThread -------------------------------------------------
+
+class cRingBufferInputThread : public cThread {
+private:
+ cRingBuffer *ringBuffer;
+protected:
+ virtual void Action(void) { ringBuffer->Input(); }
+public:
+ cRingBufferInputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
+ };
+
+// --- cRingBufferOutputThread ------------------------------------------------
+
+class cRingBufferOutputThread : public cThread {
+private:
+ cRingBuffer *ringBuffer;
+protected:
+ virtual void Action(void) { ringBuffer->Output(); }
+public:
+ cRingBufferOutputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
+ };
+
+// --- cRingBuffer ------------------------------------------------------------
+
+cRingBuffer::cRingBuffer(int Size)
+{
+ size = Size;
+ 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);
+}
+
+cRingBuffer::~cRingBuffer()
+{
+ delete inputThread;
+ delete outputThread;
+ delete buffer;
+ dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
+}
+
+void cRingBuffer::Clear(void)
+{
+ mutex.Lock();
+ head = tail = 0;
+ mutex.Unlock();
+}
+
+int cRingBuffer::Put(const uchar *Data, int Count)
+{
+ if (Count > 0) {
+ mutex.Lock();
+ int rest = size - head;
+ int diff = tail - head;
+ mutex.Unlock();
+ int free = (diff > 0) ? diff - 1 : size + diff - 1;
+ // 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 cRingBuffer::Get(uchar *Data, int Count)
+{
+ 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;
+}
+
+bool cRingBuffer::Start(void)
+{
+ if (!busy) {
+ busy = true;
+ outputThread = new cRingBufferOutputThread(this);
+ if (!outputThread->Start())
+ DELETENULL(outputThread);
+ inputThread = new cRingBufferInputThread(this);
+ if (!inputThread->Start()) {
+ DELETENULL(inputThread);
+ DELETENULL(outputThread);
+ }
+ busy = outputThread && inputThread;
+ }
+ return busy;
+}
+
+bool cRingBuffer::Active(void)
+{
+ return outputThread && outputThread->Active() && inputThread && inputThread->Active();
+}
+
+void cRingBuffer::Stop(void)
+{
+ busy = false;
+ for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) {
+ if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active())))
+ break;
+ }
+ DELETENULL(inputThread);
+ DELETENULL(outputThread);
+}
+
diff --git a/ringbuffer.h b/ringbuffer.h
new file mode 100644
index 00000000..605f553f
--- /dev/null
+++ b/ringbuffer.h
@@ -0,0 +1,55 @@
+/*
+ * ringbuffer.h: A threaded ring buffer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: ringbuffer.h 1.1 2001/03/18 16:47:00 kls Exp $
+ */
+
+#ifndef __RINGBUFFER_H
+#define __RINGBUFFER_H
+
+#include "thread.h"
+
+typedef unsigned char uchar;
+
+class cRingBufferInputThread;
+class cRingBufferOutputThread;
+
+class cRingBuffer {
+ friend class cRingBufferInputThread;
+ friend class cRingBufferOutputThread;
+private:
+ cRingBufferInputThread *inputThread;
+ cRingBufferOutputThread *outputThread;
+ cMutex mutex;
+ int size, head, tail;
+ uchar *buffer;
+ int maxFill;
+ bool busy;
+protected:
+ 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.
+ virtual void Output(void) = 0;
+ // Runs as a separate thread and shall continuously call Get() to
+ // retrieve data from the ring buffer and write it to a destination.
+public:
+ cRingBuffer(int Size);
+ virtual ~cRingBuffer();
+ bool Start(void);
+ bool Active(void);
+ void Stop(void);
+ };
+
+#endif // __RINGBUFFER_H