summaryrefslogtreecommitdiff
path: root/dvbapi.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-04-01 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-04-01 18:00:00 +0200
commit610c5600df98b35226536ffe92b1fd231128c7d4 (patch)
treef0df8ce83dd3e4829be7def61126871f425e2f23 /dvbapi.c
parentf2937af95ceaf3a52c327e96571367ef5475b3a1 (diff)
downloadvdr-patch-lnbsharing-610c5600df98b35226536ffe92b1fd231128c7d4.tar.gz
vdr-patch-lnbsharing-610c5600df98b35226536ffe92b1fd231128c7d4.tar.bz2
Version 0.72vdr-0.72
- Fixed SVDRP commands LSTC and LSTT to make them return an error message if no channels or timers are defined. - Enhanced 'channels.conf.cable' (thanks to Hans-Peter Raschke). - Fixed switching to another channel via the EPG while a recording is being replayed. - Fixed a memory leak in the EIT processor that happened when the system time was set. - Removed some redundant code from the cListBase destructor. - 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. - Fixed a problem with Daylight Saving Time when displaying the times of recordings. - Added Dutch language texts (thanks to Arnold Niessen). - The new command line option -t can be used to set the controlling terminal (thanks to Jürgen Sauer). This is especially useful when starting VDR through an entry in /etc/inittab (see INSTALL). - Since the CAM module only works if it is installed in the "highest" DVB card, recordings now search for a free DVB card from lowest to highest index (as opposed to the previous "highest to lowest" search) in order to not use the CAM card for FTA recordings unless necessary. This is only important for systems with three or more DVB cards. - Added the "statdvb2vdr" tool from Hans-Peter Raschke. - Fixed a segfault that sometimes happened when killing VDR. - VDR now returns an exit status of '2' in case of an error at startup, instead of terminating with 'abort()' (which caused a core dump). - SVDRP now also works with clients that don't do line buffering (like the Windows 'telnet'). - Empty lines in config files no longer cause error messages. - New SVDRP command LSTE to list the EPG data. - The SVDRP HELP command now prints the topics in several columns.
Diffstat (limited to 'dvbapi.c')
-rw-r--r--dvbapi.c321
1 files changed, 114 insertions, 207 deletions
diff --git a/dvbapi.c b/dvbapi.c
index 8518d7f..a929b50 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.61 2001/02/24 13:13:19 kls Exp $
+ * $Id: dvbapi.c 1.66 2001/03/31 15:01:57 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)
@@ -1535,7 +1441,7 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
videoDev = open(VideoFileName, O_RDWR | O_NONBLOCK);
if (videoDev >= 0) {
siProcessor = new cSIProcessor(VbiFileName);
- if (!NumDvbApis) // only the first one shall set the system time
+ if (!dvbApi[0]) // only the first one shall set the system time
siProcessor->SetUseTSTime(Setup.SetSystemTime);
siProcessor->AddFilter(0x14, 0x70); // TDT
siProcessor->AddFilter(0x14, 0x73); // TOT
@@ -1582,7 +1488,7 @@ cDvbApi::~cDvbApi()
StopTransfer();
OvlO(false); //Overlay off!
//XXX the following call sometimes causes a segfault - driver problem?
- close(videoDev);
+ //XXX close(videoDev);
}
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
endwin();
@@ -1611,7 +1517,7 @@ cDvbApi *cDvbApi::GetDvbApi(int Ca, int Priority)
{
cDvbApi *d = NULL, *dMinPriority = NULL;
int index = Ca - 1;
- for (int i = MAXDVBAPI; --i >= 0; ) {
+ for (int i = 0; i < MAXDVBAPI; i++) {
if (dvbApi[i]) {
if (i == index) { // means we need exactly _this_ device
d = dvbApi[i];
@@ -2136,6 +2042,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
if (videoDev >= 0) {
cThreadLock ThreadLock(siProcessor); // makes sure the siProcessor won't access the vbi-device while switching
StopTransfer();
+ StopReplay();
SetPlayMode(videoDev, VID_PLAY_RESET);
struct frontend front;
ioctl(videoDev, VIDIOCGFRONTEND, &front);