summaryrefslogtreecommitdiff
path: root/dvbapi.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-07-29 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-07-29 18:00:00 +0200
commit8f9cc68f76c4fd0960f919a77fb16a6455922deb (patch)
tree83f607160a07966e97069397580acfb0d9b1be7a /dvbapi.c
parent610c5600df98b35226536ffe92b1fd231128c7d4 (diff)
downloadvdr-patch-lnbsharing-8f9cc68f76c4fd0960f919a77fb16a6455922deb.tar.gz
vdr-patch-lnbsharing-8f9cc68f76c4fd0960f919a77fb16a6455922deb.tar.bz2
Version 0.85vdr-0.85
- Added Norwegian language texts (thanks to Jørgen Tvedt). - Increased the usleep value in cDvbOsd::Cmd() to 5000 in order to work on systems with the KURT/utime-patch (thanks to Guido Fiala). - Changed the check whether the driver is loaded in runvdr to check for the 'dvb' module (the last one loaded). - Fixed repeat function with LIRC (thanks to Stefan Huelswitt). - Increased the upper limit for the symbol rate to 30000 (thanks to Ulrich Röder). - Made the position of the channel display configurable (thanks to Stefan Huelswitt). - Made the width and height of the OSD configurable (thanks to Stefan Huelswitt). - DiSEqC support can now be generally enabled/disabled in the Setup menu. This may be necessary if your multiswitch gets irritated by the default DiSEqC codes '0' (thanks to Markus Lang). - Fixed replaying in case there is no index file. - Fixed jumping to an editing mark when replay has been paused. - Avoiding unnecessary code execution in the replay progress display (thanks to Guido Fiala). - When entering time values the digits that still have to be entered are now shown as '-' (as in "1-:--"). - When setting an editing mark while the progress display is not active, the display will now be turned on for a short while to indicate the successful setting of the mark. - Updated 'channels.conf' for Premiere World (thanks to Helmut Schächner). Check your timers if you use this channels.conf file, since the sequence of several PW channels has been changed. - Changed the color of "Info" messages to "black on green" and that of the confirmation messages (like "Delete...") to "black on yellow". - Fixed display with DEBUG_OSD (it still crashes sometimes, esp. when replaying, but I can't seem to find what causes this... any ideas anybody?). - Avoiding audio/video distortions in 'Transfer Mode' by no longer actually tuning the primary interface (which can't receive this channel, anyway). Apparently the driver gets irritated when the channel is switched and a replay session is started immediately after that. - Increased timeout until reporting "video data stream broken" when recording. - 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').
Diffstat (limited to 'dvbapi.c')
-rw-r--r--dvbapi.c1621
1 files changed, 927 insertions, 694 deletions
diff --git a/dvbapi.c b/dvbapi.c
index a929b50..07b6f61 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,13 +4,15 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.66 2001/03/31 15:01:57 kls Exp $
+ * $Id: dvbapi.c 1.96 2001/07/29 10:32:50 kls Exp $
*/
#include "dvbapi.h"
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
extern "C" {
+#define HAVE_BOOLEAN
#include <jpeglib.h>
}
#include <stdlib.h>
@@ -20,20 +22,29 @@ extern "C" {
#include <sys/time.h>
#include <unistd.h>
#include "config.h"
-#include "interface.h"
#include "recording.h"
#include "remux.h"
#include "ringbuffer.h"
#include "tools.h"
#include "videodir.h"
-#define VIDEODEVICE "/dev/video"
-#define VBIDEVICE "/dev/vbi"
+#define DEV_VIDEO "/dev/video"
+#define DEV_OST_OSD "/dev/ost/osd"
+#define DEV_OST_QAMFE "/dev/ost/qamfe"
+#define DEV_OST_QPSKFE "/dev/ost/qpskfe"
+#define DEV_OST_SEC "/dev/ost/sec"
+#define DEV_OST_DVR "/dev/ost/dvr"
+#define DEV_OST_DEMUX "/dev/ost/demux"
+#define DEV_OST_VIDEO "/dev/ost/video"
+#define DEV_OST_AUDIO "/dev/ost/audio"
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE (1024*1024)
+// The maximum size of a single frame:
+#define MAXFRAMESIZE (192*1024)
+
#define FRAMESPERSEC 25
// The maximum file size is limited by the range that can be covered
@@ -55,16 +66,13 @@ extern "C" {
// The number of frames to back up when resuming an interrupted replay session:
#define RESUMEBACKUP (10 * FRAMESPERSEC)
-typedef unsigned char uchar;
+// The maximum time we wait before assuming that a recorded video data stream
+// is broken:
+#define MAXBROKENTIMEOUT 30 // seconds
-static void SetPlayMode(int VideoDev, int Mode)
-{
- if (VideoDev >= 0) {
- struct video_play_mode pmode;
- pmode.mode = Mode;
- ioctl(VideoDev, VIDIOCSPLAYMODE, &pmode);
- }
-}
+#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
+
+typedef unsigned char uchar;
const char *IndexToHMSF(int Index, bool WithFrame)
{
@@ -100,6 +108,7 @@ private:
public:
cIndexFile(const char *FileName, bool Record);
~cIndexFile();
+ bool Ok(void) { return index != NULL; }
void Write(uchar PictureType, uchar FileNumber, int FileOffset);
bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL);
@@ -158,8 +167,10 @@ cIndexFile::cIndexFile(const char *FileName, bool Record)
else
LOG_ERROR;
}
+ else if (!Record)
+ isyslog(LOG_INFO, "missing index file %s", fileName);
if (Record) {
- if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP)) >= 0) {
+ if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
if (delta) {
esyslog(LOG_ERR, "ERROR: padding index file with %d '0' bytes", delta);
while (delta--)
@@ -315,236 +326,6 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
return -1;
}
-// --- 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
- be called only when the associated file is ready to provide or receive data
- (use the 'select()' function to determine that), and the files should be
- opened in non-blocking mode.
- The '...Limit' parameters define safety limits. If they are exceeded a log entry
- will be made.
-*/
-
-class cRingBuffer_ {
-private:
- uchar *buffer;
- int size, head, tail, freeLimit, availLimit;
- int countLimit, countOverflow;
- int minFree;
- bool eof;
- int *inFile, *outFile;
-protected:
- int Free(void) { return ((tail >= head) ? size + head - tail : head - tail) - 1; }
-public:
- int Available(void) { return (tail >= head) ? tail - head : size - head + tail; }
-protected:
- int Readable(void) { return (tail >= head) ? size - tail - (head ? 0 : 1) : head - tail - 1; } // keep a 1 byte gap!
- int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
- int Byte(int Offset);
- bool Set(int Offset, int Length, int Value);
-protected:
- int GetStartCode(int Offset) { return (Byte(Offset) == 0x00 && Byte(Offset + 1) == 0x00 && Byte(Offset + 2) == 0x01) ? Byte(Offset + 3) : -1; }
- int GetPictureType(int Offset) { return (Byte(Offset + 5) >> 3) & 0x07; }
- 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_();
- virtual int Read(int Max = -1);
- virtual int Write(int Max = -1);
- bool EndOfFile(void) { return eof; }
- bool Empty(void) { return Available() == 0; }
- void Clear(void) { head = tail = 0; }
- void Skip(int n);
- };
-
-cRingBuffer_::cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
-{
- inFile = InFile;
- outFile = OutFile;
- size = Size;
- Clear();
- freeLimit = FreeLimit;
- availLimit = AvailLimit;
- eof = false;
- countLimit = countOverflow = 0;
- minFree = size - 1;
- buffer = new uchar[size];
- if (!buffer)
- esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
-}
-
-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)
-{
- if (buffer && Offset < Available()) {
- Offset += head;
- if (Offset >= size)
- Offset -= size;
- return buffer[Offset];
- }
- return -1;
-}
-
-bool cRingBuffer_::Set(int Offset, int Length, int Value)
-{
- if (buffer && Offset + Length <= Available() ) {
- Offset += head;
- while (Length--) {
- if (Offset >= size)
- Offset -= size;
- buffer[Offset] = Value;
- Offset++;
- }
- return true;
- }
- return false;
-}
-
-void cRingBuffer_::Skip(int n)
-{
- if (n > 0) {
- if (head < tail) {
- head += n;
- if (head > tail)
- head = tail;
- }
- else if (head > tail) {
- head += n;
- if (head >= size)
- head -= size;
- if (head > tail)
- head = tail;
- }
- }
-}
-
-int cRingBuffer_::Read(int Max)
-{
- if (buffer) {
- eof = false;
- int free = Free();
- if (free < minFree)
- minFree = free;
- if (freeLimit) {
- if (free == 0) {
- esyslog(LOG_ERR, "ERROR: buffer overflow (size=%d)", size);
- countOverflow++;
- }
- else if (free < freeLimit) {
- dsyslog(LOG_INFO, "free buffer space dipped into limit (%d < %d)", free, freeLimit);
- countLimit++;
- }
- }
- if (free == 0)
- return 0; // the buffer is full
- int readin = 0;
- for (int i = 0; i < 2; i++) {
- // If we read in exactly as many bytes as are immediately
- // "readable" we have to do it again, because that means we
- // were at the very end of the physical buffer and possibly only
- // read in very few bytes.
- int immediate = Readable();
- int n = immediate;
- if (Max > 0 && n > Max)
- n = Max;
- if (n > 0) {
- int r = read(*inFile, buffer + tail, n);
- if (r > 0) {
- readin += r;
- tail += r;
- if (tail > size)
- esyslog(LOG_ERR, "ERROR: ooops: buffer tail (%d) exceeds size (%d)", tail, size);
- if (tail >= size)
- tail = 0;
- }
- else if (r < 0) {
- if (errno != EAGAIN) {
- LOG_ERROR;
- return -1;
- }
- }
- else
- eof = true;
- if (r == immediate && Max != immediate && tail == 0)
- Max -= immediate;
- else
- break;
- }
- }
- return readin;
- }
- return -1;
-}
-
-int cRingBuffer_::Write(int Max)
-{
- if (buffer) {
- int avail = Available();
- if (availLimit) {
- //XXX stats???
- if (avail == 0)
- //XXX esyslog(LOG_ERR, "ERROR: buffer empty!");
- {//XXX
- esyslog(LOG_ERR, "ERROR: buffer empty! %d", Max);
- return Max > 0 ? Max : 0;
- }//XXX
- else if (avail < availLimit)
-;//XXX dsyslog(LOG_INFO, "available buffer data dipped into limit (%d < %d)", avail, availLimit);
- }
- if (avail == 0)
- return 0; // the buffer is empty
- int n = Writeable();
- if (Max > 0 && n > Max)
- n = Max;
- int w = write(*outFile, buffer + head, n);
- if (w > 0) {
- head += w;
- if (head > size)
- esyslog(LOG_ERR, "ERROR: ooops: buffer head (%d) exceeds size (%d)", head, size);
- if (head >= size)
- head = 0;
- }
- else if (w < 0) {
- if (errno != EAGAIN)
- LOG_ERROR;
- else
- w = 0;
- }
- return w;
- }
- return -1;
-}
-
-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.
-
- int n = Available() - Offset;
-
- for (int i = 0; i < n; i++) {
- int c = GetStartCode(Offset + i);
- if (c == Code)
- return i;
- if (i > 0 && c == SC_PHEAD)
- break; // found another block start while looking for a different code
- }
- return -1;
-}
-
-int cRingBuffer_::GetPacketLength(int Offset)
-{
- // Returns the entire length of the packet starting at offset.
- return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
-}
-
// --- cFileName -------------------------------------------------------------
class cFileName {
@@ -553,8 +334,9 @@ private:
int fileNumber;
char *fileName, *pFileNumber;
bool record;
+ bool blocking;
public:
- cFileName(const char *FileName, bool Record);
+ cFileName(const char *FileName, bool Record, bool Blocking = false);
~cFileName();
const char *Name(void) { return fileName; }
int Number(void) { return fileNumber; }
@@ -564,11 +346,12 @@ public:
int NextFile(void);
};
-cFileName::cFileName(const char *FileName, bool Record)
+cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
{
file = -1;
fileNumber = 0;
record = Record;
+ blocking = Blocking;
// Prepare the file name:
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
if (!fileName) {
@@ -589,16 +372,17 @@ cFileName::~cFileName()
int cFileName::Open(void)
{
if (file < 0) {
+ int BlockingFlag = blocking ? 0 : O_NONBLOCK;
if (record) {
dsyslog(LOG_INFO, "recording to '%s'", fileName);
- file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
+ file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
if (file < 0)
LOG_ERROR_STR(fileName);
}
else {
if (access(fileName, R_OK) == 0) {
dsyslog(LOG_INFO, "playing '%s'", fileName);
- file = open(fileName, O_RDONLY | O_NONBLOCK);
+ file = open(fileName, O_RDONLY | BlockingFlag);
if (file < 0)
LOG_ERROR_STR(fileName);
}
@@ -655,6 +439,7 @@ int cFileName::NextFile(void)
class cRecordBuffer : public cRingBuffer {
private:
+ cDvbApi *dvbApi;
cFileName fileName;
cIndexFile *index;
cRemux remux;
@@ -670,18 +455,19 @@ protected:
virtual void Input(void);
virtual void Output(void);
public:
- cRecordBuffer(int *InFile, const char *FileName);
+ cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2);
virtual ~cRecordBuffer();
};
-cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
-:cRingBuffer(VIDEOBUFSIZE)
+cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
+:cRingBuffer(VIDEOBUFSIZE, true)
,fileName(FileName, true)
+,remux(VPid, APid1, APid2, DPid1, DPid2, true)
{
+ dvbApi = DvbApi;
index = NULL;
pictureType = NO_PICTURE;
fileSize = 0;
- videoDev = *InFile;
recordFile = fileName.Open();
recording = false;
lastDiskSpaceCheck = time(NULL);
@@ -692,12 +478,14 @@ 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
+ videoDev = dvbApi->SetModeRecord();
Start();
}
cRecordBuffer::~cRecordBuffer()
{
Stop();
+ dvbApi->SetModeNormal(true);
delete index;
}
@@ -746,18 +534,19 @@ void cRecordBuffer::Input(void)
else if (r < 0) {
if (errno != EAGAIN) {
LOG_ERROR;
- break;
+ if (errno != EBUFFEROVERFLOW)
+ break;
}
}
- else if (time(NULL) - t > 5) {
+ if (time(NULL) - t > MAXBROKENTIMEOUT) {
esyslog(LOG_ERR, "ERROR: video data stream broken");
+ cThread::EmergencyExit(true);
t = time(NULL);
}
cFile::FileReady(videoDev, 100);
if (!recording)
break;
}
- SetPlayMode(videoDev, VID_PLAY_RESET);
dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
}
@@ -766,15 +555,14 @@ void cRecordBuffer::Output(void)
{
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
- uchar b[MINVIDEODATA * 2];
+ uchar b[MINVIDEODATA];
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 g = Get(b + r, sizeof(b) - r);
+ if (g > 0) {
+ r += g;
int Count = r, Result;
- const uchar *p = remux.Process(b, Count, Result, pictureType);
+ 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;
@@ -803,166 +591,353 @@ void cRecordBuffer::Output(void)
if (!recording)
break;
}
+ else
+ usleep(1); // this keeps the CPU load low
}
recording = false;
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
}
+// --- ReadFrame -------------------------------------------------------------
+
+int ReadFrame(int f, uchar *b, int Length, int Max)
+{
+ if (Length == -1)
+ Length = Max; // this means we read up to EOF (see cIndex)
+ else if (Length > Max) {
+ esyslog(LOG_ERR, "ERROR: frame larger than buffer (%d > %d)", Length, Max);
+ Length = Max;
+ }
+ int r = read(f, b, Length);
+ if (r < 0)
+ LOG_ERROR;
+ return r;
+}
+
// --- cReplayBuffer ---------------------------------------------------------
-class cReplayBuffer : public cRingBuffer_, public cThread {
+class cReplayBuffer : public cRingBuffer {
private:
- enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
- enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
+ cDvbApi *dvbApi;
cIndexFile *index;
cFileName fileName;
int fileOffset;
- int videoDev;
+ int videoDev, audioDev;
+ FILE *dolbyDev;
int replayFile;
- eReplayMode mode;
- int lastIndex, stillIndex;
- int brakeCounter;
- eReplayCmd command;
- bool active;
+ bool eof;
+ int blockInput, blockOutput;
+ bool paused, fastForward, fastRewind;
+ int lastIndex, stillIndex, playIndex;
+ bool canToggleAudioTrack;
+ uchar audioTrack;
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
+ void Clear(bool Block = false);
void Close(void);
- void SetCmd(eReplayCmd Cmd) { LOCK_THREAD; command = Cmd; }
- void SetTemporalReference(void);
+ void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
+ void DisplayFrame(uchar *b, int Length);
+ int Resume(void);
+ bool Save(void);
protected:
- virtual void Action(void);
+ virtual void Input(void);
+ virtual void Output(void);
public:
- cReplayBuffer(int *OutFile, const char *FileName);
+ cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
virtual ~cReplayBuffer();
- virtual int Read(int Max = -1);
- virtual int Write(int Max = -1);
- void SetMode(eReplayMode Mode);
- int Resume(void);
- bool Save(void);
- void Pause(void) { SetCmd(rcPause); }
- void Play(void) { SetCmd(rcPlay); }
- void Forward(void) { SetCmd(rcForward); }
- void Backward(void) { SetCmd(rcBackward); }
+ void Pause(void);
+ void Play(void);
+ void Forward(void);
+ void Backward(void);
int SkipFrames(int Frames);
void SkipSeconds(int Seconds);
void Goto(int Position, bool Still = false);
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
+ bool CanToggleAudioTrack(void) { return canToggleAudioTrack; }
+ void ToggleAudioTrack(void);
};
-cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
-:cRingBuffer_(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
+cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
+:cRingBuffer(VIDEOBUFSIZE)
,fileName(FileName, false)
{
+ dvbApi = DvbApi;
index = NULL;
fileOffset = 0;
- videoDev = *OutFile;
+ videoDev = VideoDev;
+ audioDev = AudioDev;
+ dolbyDev = NULL;
+ if (cDvbApi::AudioCommand()) {
+ dolbyDev = popen(cDvbApi::AudioCommand(), "w");
+ if (!dolbyDev)
+ esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
+ }
replayFile = fileName.Open();
- mode = rmPlay;
- brakeCounter = 0;
- command = rcNone;
- lastIndex = stillIndex = -1;
- active = false;
+ eof = false;
+ blockInput = blockOutput = false;
+ paused = fastForward = fastRewind = false;
+ lastIndex = stillIndex = playIndex = -1;
+ canToggleAudioTrack = false;
+ audioTrack = 0xC0;
if (!fileName.Name())
return;
// Create the index file:
index = new cIndexFile(FileName, false);
- if (!index)
+ if (!index) {
esyslog(LOG_ERR, "ERROR: can't allocate index");
- // let's continue without index, so we'll at least have the recording
+ }
+ else if (!index->Ok()) {
+ delete index;
+ index = NULL;
+ }
+ dvbApi->SetModeReplay();
Start();
}
cReplayBuffer::~cReplayBuffer()
{
- active = false;
- Cancel(3);
+ Stop();
+ Save();
Close();
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- SetPlayMode(videoDev, VID_PLAY_RESET);
+ if (dolbyDev)
+ pclose(dolbyDev);
+ dvbApi->SetModeNormal(false);
delete index;
}
-void cReplayBuffer::Action(void)
+void cReplayBuffer::Input(void)
{
- dsyslog(LOG_INFO, "replay thread started (pid=%d)", getpid());
-
- bool Paused = false;
- bool FastForward = false;
- bool FastRewind = false;
+ 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));
- active = true;
- while (active) {
- usleep(1); // this keeps the CPU load low
-
- LOCK_THREAD;
-
- if (command != rcNone) {
- switch (command) {
- case rcStill: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- SetPlayMode(videoDev, VID_PLAY_NORMAL);
- SetMode(rmStill);
- Paused = FastForward = FastRewind = false;
- break;
- case rcPause: SetPlayMode(videoDev, Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
- Paused = !Paused;
- if (FastForward || FastRewind) {
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- Clear();
- }
- FastForward = FastRewind = false;
- SetMode(rmPlay);
- if (!Paused)
- stillIndex = -1;
- break;
- case rcPlay: if (FastForward || FastRewind || Paused) {
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- SetPlayMode(videoDev, VID_PLAY_NORMAL);
- FastForward = FastRewind = Paused = false;
- SetMode(rmPlay);
- }
- stillIndex = -1;
- break;
- case rcForward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- Clear();
- FastForward = !FastForward;
- FastRewind = false;
- if (Paused) {
- SetMode(rmPlay);
- SetPlayMode(videoDev, FastForward ? VID_PLAY_SLOW_MOTION : VID_PLAY_PAUSE);
- }
- else {
- SetPlayMode(videoDev, VID_PLAY_NORMAL);
- SetMode(FastForward ? rmFastForward : rmPlay);
- }
- stillIndex = -1;
- break;
- case rcBackward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- Clear();
- FastRewind = !FastRewind;
- FastForward = false;
- if (Paused) {
- SetMode(FastRewind ? rmSlowRewind : rmPlay);
- SetPlayMode(videoDev, FastRewind ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
- }
- else {
- SetPlayMode(videoDev, VID_PLAY_NORMAL);
- SetMode(FastRewind ? rmFastRewind : rmPlay);
- }
- stillIndex = -1;
- break;
- default: break;
- }
- command = rcNone;
+
+ int lastIndex = -1;
+ int brakeCounter = 0;
+ uchar b[MAXFRAMESIZE];
+ while (Busy() && (blockInput || NextFile())) {
+ if (!blockInput && stillIndex < 0) {
+ 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);
+ if (Index >= 0) {
+ if (!NextFile(FileNumber, FileOffset))
+ break;
+ }
+ else {
+ paused = fastForward = fastRewind = false;
+ Play();
+ continue;
+ }
+ lastIndex = Index;
+ playIndex = -1;
+ 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)))
+ 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
+ }
+ }
+ else if (r ==0)
+ eof = true;
+ else if (r < 0 && errno != EAGAIN) {
+ LOG_ERROR;
+ break;
+ }
}
- if (Read() < 0 || Write() < 0)
- break;
+ else
+ usleep(1); // this keeps the CPU load low
+ if (blockInput > 1)
+ blockInput = 1;
}
- Save();
- dsyslog(LOG_INFO, "end replaying thread");
+ 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)
+{
+ 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];
+ int l = b[i + 4] * 256 + b[i + 5] + 6;
+ switch (c) {
+ case 0xBD: // dolby
+ if (Except && dolbyDev) {
+ int written = b[i + 8] + 9; // skips the PES header
+ int n = l - written;
+ while (n > 0) {
+ int w = fwrite(&b[i + written], 1, n, dolbyDev);
+ if (w < 0) {
+ LOG_ERROR;
+ break;
+ }
+ n -= w;
+ written += w;
+ }
+ }
+ // continue with deleting the data - otherwise it disturbs DVB replay
+ case 0xC0 ... 0xC1: // audio
+ if (c == 0xC1)
+ canToggleAudioTrack = true;
+ if (!Except || c != Except) {
+ int n = l;
+ for (int j = i; j < Length && n--; j++)
+ b[j] = 0x00;
+ }
+ break;
+ case 0xE0 ... 0xEF: // video
+ break;
+ default:
+ //esyslog(LOG_ERR, "ERROR: unexpected packet id %02X", c);
+ l = 0;
+ }
+ if (l)
+ i += l - 1; // the loop increments, too!
+ }
+ /*XXX
+ else
+ esyslog(LOG_ERR, "ERROR: broken packet header");
+ XXX*/
+ }
+}
+
+void cReplayBuffer::DisplayFrame(uchar *b, int Length)
+{
+ StripAudioPackets(b, Length);
+ videoDisplayStillPicture sp = { (char *)b, Length };
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, true));
+ 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();
+ cRingBuffer::Clear();
+ playIndex = -1;
+ CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
+ }
+ 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)
@@ -974,14 +949,6 @@ void cReplayBuffer::Close(void)
}
}
-void cReplayBuffer::SetMode(eReplayMode Mode)
-{
- mode = Mode;
- brakeCounter = 0;
- if (mode != rmPlay)
- Clear();
-}
-
int cReplayBuffer::Resume(void)
{
if (index) {
@@ -1016,9 +983,6 @@ bool cReplayBuffer::Save(void)
int cReplayBuffer::SkipFrames(int Frames)
{
if (index && Frames) {
-
- LOCK_THREAD;
-
int Current, Total;
GetIndex(Current, Total, true);
int OldCurrent = Current;
@@ -1030,16 +994,8 @@ int cReplayBuffer::SkipFrames(int Frames)
void cReplayBuffer::SkipSeconds(int Seconds)
{
- LOCK_THREAD;
-
- SetPlayMode(videoDev, VID_PLAY_PAUSE);
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- SetPlayMode(videoDev, VID_PLAY_NORMAL);
- command = rcPlay;
- SetMode(rmPlay);
- Clear();
-
if (index && Seconds) {
+ Clear(true);
int Index = index->Get(fileName.Number(), fileOffset);
if (Index >= 0) {
if (Seconds < 0) {
@@ -1055,31 +1011,41 @@ void cReplayBuffer::SkipSeconds(int Seconds)
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
NextFile(FileNumber, FileOffset);
}
+ Clear(false);
+ Play();
}
}
void cReplayBuffer::Goto(int Index, bool Still)
{
- LOCK_THREAD;
-
- if (Still)
- command = rcStill;
- if (++Index <= 0)
- Index = 1; // not '0', to allow GetNextIFrame() below to work!
- uchar FileNumber;
- int FileOffset;
- if ((stillIndex = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset)) >= 0)
- NextFile(FileNumber, FileOffset);
- SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
- Clear();
+ if (index) {
+ Clear(true);
+ if (paused)
+ CHECK(ioctl(videoDev, VIDEO_CONTINUE));
+ if (++Index <= 0)
+ Index = 1; // not '0', to allow GetNextIFrame() below to work!
+ uchar FileNumber;
+ int FileOffset, Length;
+ Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
+ stillIndex = Index;
+ playIndex = -1;
+ 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);
+ }
}
void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
if (index) {
-
- LOCK_THREAD;
-
if (stillIndex >= 0)
Current = stillIndex;
else {
@@ -1099,178 +1065,145 @@ void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0) {
- Clear();
fileOffset = FileOffset;
replayFile = fileName.SetOffset(FileNumber, FileOffset);
}
- else if (replayFile >= 0 && EndOfFile()) {
+ else if (replayFile >= 0 && eof) {
Close();
replayFile = fileName.NextFile();
}
+ eof = false;
return replayFile >= 0;
}
-void cReplayBuffer::SetTemporalReference(void)
-{
- for (int i = 0; i < Available(); i++) {
- if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
- switch (Byte(i + 3)) {
- case SC_PICTURE: {
- unsigned short m = (Byte(i + 4) << 8) | Byte(i + 5);
- m &= 0x003F;
- Set(i + 4, 1, m >> 8);
- Set(i + 5, 1, m & 0xFF);
- }
- return;
- }
- }
- }
-}
-
-int cReplayBuffer::Read(int Max = -1)
+void cReplayBuffer::ToggleAudioTrack(void)
{
- if (mode != rmPlay) {
- if (index) {
- if (Available())
- return 0; // write out the entire block
- if (mode == rmStill) {
- uchar FileNumber;
- int FileOffset, Length;
- if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- return -1;
- Max = Length;
- }
- command = rcPause;
- }
- else {
- int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
- if (Index >= 0) {
- if (mode == rmSlowRewind && (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, true); // jump ahead one frame
- }
- uchar FileNumber;
- int FileOffset, Length;
- Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
- if (Index >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- return -1;
- Max = Length;
- }
- lastIndex = Index;
- }
- if (Index < 0) {
- // This results in normal replay after a fast rewind.
- // After a fast forward it will stop.
- // TODO Could we cause it to pause at the last frame?
- SetMode(rmPlay);
- return 0;
- }
- }
- }
- }
- else
- lastIndex = -1;
- //XXX timeout as in recording???
- if (NextFile()) {
- 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);
- if (r >= 0)
- readin += r;
- else
- return -1;
- } while (readin < Max && Free() > 0);
- if (mode != rmPlay) {
- // delete the audio data in modes other than rmPlay:
- int AudioOffset, StartOffset = 0;
- while ((AudioOffset = FindStartCode(SC_AUDIO, StartOffset)) >= 0) {
- if (!Set(StartOffset + AudioOffset, GetPacketLength(StartOffset + AudioOffset), 0))
- break; // to be able to replay old AV_PES recordings!
- StartOffset += AudioOffset;
- }
- SetTemporalReference();
- }
- return readin;
- }
- if (Available() > 0)
- return 0;
- return -1;
-}
-
-int cReplayBuffer::Write(int Max)
-{
- int Written = 0;
- if (Max) {
- int w;
- do {
- w = cRingBuffer_::Write(Max);
- if (w >= 0) {
- fileOffset += w;
- Written += w;
- if (Max < 0)
- break;
- Max -= w;
- }
- else
- return w;
- } while (Max > 0); // we MUST write this entire frame block
+ if (CanToggleAudioTrack()) {
+ audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
+ Clear();
}
- return Written;
}
// --- cTransferBuffer -------------------------------------------------------
-class cTransferBuffer : public cThread {
+class cTransferBuffer : public cRingBuffer {
private:
- bool active;
+ cDvbApi *dvbApi;
int fromDevice, toDevice;
+ bool gotBufferReserve;
+ cRemux remux;
protected:
- virtual void Action(void);
+ virtual void Input(void);
+ virtual void Output(void);
public:
- cTransferBuffer(int FromDevice, int ToDevice);
+ cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid);
virtual ~cTransferBuffer();
+ void SetAudioPid(int APid);
};
-cTransferBuffer::cTransferBuffer(int FromDevice, int ToDevice)
+cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
+:cRingBuffer(VIDEOBUFSIZE, true)
+,remux(VPid, APid, 0, 0, 0)
{
- fromDevice = FromDevice;
+ dvbApi = DvbApi;
+ fromDevice = dvbApi->SetModeRecord();
toDevice = ToDevice;
- active = false;
+ gotBufferReserve = false;
Start();
}
cTransferBuffer::~cTransferBuffer()
{
- active = false;
- Cancel(3);
- SetPlayMode(fromDevice, VID_PLAY_RESET);
- SetPlayMode(toDevice, VID_PLAY_RESET);
+ Stop();
+ dvbApi->SetModeNormal(true);
}
-void cTransferBuffer::Action(void)
+void cTransferBuffer::SetAudioPid(int APid)
{
- dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
+ Clear();
+ //XXX we may need to have access to the audio device, too, in order to clear it
+ CHECK(ioctl(toDevice, VIDEO_CLEAR_BUFFER));
+ gotBufferReserve = false;
+ remux.SetAudioPid(APid);
+}
+
+void cTransferBuffer::Input(void)
+{
+ dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
- 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
- usleep(1); // this keeps the CPU load low
+ uchar b[MINVIDEODATA];
+ int n = 0;
+ while (Busy()) {
+ cFile::FileReady(fromDevice, 100);
+ int r = read(fromDevice, b + n, sizeof(b) - n);
+ if (r > 0) {
+ n += r;
+ int Count = n, Result;
+ const uchar *p = remux.Process(b, Count, Result);
+ if (p) {
+ while (Result > 0 && Busy()) {
+ int w = Put(p, Result);
+ p += w;
+ Result -= w;
+ }
+ }
+ if (Count > 0) {
+ n -= Count;
+ memmove(b, b + Count, n);
+ }
+ }
+ else if (r < 0) {
+ if (errno != EAGAIN) {
+ LOG_ERROR;
+ if (errno != EBUFFEROVERFLOW)
+ break;
+ }
+ }
}
- while (active) {
- if (Buffer.Read() < 0 || Buffer.Write() < 0)
- break;
- usleep(1); // this keeps the CPU load low
+
+ dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
+}
+
+void cTransferBuffer::Output(void)
+{
+ dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
+
+ uchar b[MINVIDEODATA];
+ while (Busy()) {
+ if (!gotBufferReserve) {
+ if (Available() < MAXFRAMESIZE) {
+ usleep(100000); // allow the buffer to collect some reserve
+ continue;
+ }
+ else
+ gotBufferReserve = true;
+ }
+ int r = Get(b, sizeof(b));
+ if (r > 0) {
+ uchar *p = b;
+ while (r > 0 && Busy()) {
+ int w = write(toDevice, p, r);
+ if (w > 0) {
+ p += w;
+ r -= w;
+ }
+ else if (w < 0 && errno != EAGAIN) {
+ LOG_ERROR;
+ Stop();
+ return;
+ }
+ }
+ }
+ else
+ usleep(1); // this keeps the CPU load low
}
- dsyslog(LOG_INFO, "data transfer thread stopped (pid=%d)", getpid());
+
+ dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
}
// --- cCuttingBuffer --------------------------------------------------------
-class cCuttingBuffer : public cRingBuffer_, public cThread {
+class cCuttingBuffer : public cThread {
private:
bool active;
int fromFile, toFile;
@@ -1285,15 +1218,14 @@ public:
};
cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
-:cRingBuffer_(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
{
active = false;
fromFile = toFile = -1;
fromFileName = toFileName = NULL;
fromIndex = toIndex = NULL;
if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
- fromFileName = new cFileName(FromFileName, false);
- toFileName = new cFileName(ToFileName, true);
+ fromFileName = new cFileName(FromFileName, false, true);
+ toFileName = new cFileName(ToFileName, true, true);
fromIndex = new cIndexFile(FromFileName, false);
toIndex = new cIndexFile(ToFileName, true);
toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
@@ -1329,22 +1261,24 @@ void cCuttingBuffer::Action(void)
int LastIFrame = 0;
toMarks.Add(0);
toMarks.Save();
+ uchar buffer[MAXFRAMESIZE];
while (active) {
uchar FileNumber;
int FileOffset, Length;
uchar PictureType;
-
- Clear(); // makes sure one frame is completely read and written
-
+
// Read one frame:
-
+
if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
if (FileNumber != CurrentFileNumber) {
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
CurrentFileNumber = FileNumber;
}
- if (fromFile >= 0)
- Length = cRingBuffer_::Read(Length);
+ if (fromFile >= 0) {
+ Length = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
+ if (Length < 0)
+ break;
+ }
else
break;
}
@@ -1362,7 +1296,7 @@ void cCuttingBuffer::Action(void)
}
LastIFrame = 0;
}
- cRingBuffer_::Write(Length);
+ write(toFile, buffer, Length);
toIndex->Write(PictureType, toFileName->Number(), FileSize);
FileSize += Length;
if (!LastIFrame)
@@ -1424,13 +1358,32 @@ bool cVideoCutter::Active(void)
// --- cDvbApi ---------------------------------------------------------------
+
+static const char *OstName(const char *Name, int n)
+{
+ static char buffer[_POSIX_PATH_MAX];
+ snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
+ return buffer;
+}
+
+static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false)
+{
+ const char *FileName = OstName(Name, n);
+ int fd = open(FileName, Mode);
+ if (fd < 0 && ReportError)
+ LOG_ERROR_STR(FileName);
+ return fd;
+}
+
int cDvbApi::NumDvbApis = 0;
int cDvbApi::useDvbApi = 0;
cDvbApi *cDvbApi::dvbApi[MAXDVBAPI] = { NULL };
cDvbApi *cDvbApi::PrimaryDvbApi = NULL;
+char *cDvbApi::audioCommand = NULL;
-cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
+cDvbApi::cDvbApi(int n)
{
+ vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0;
siProcessor = NULL;
recordBuffer = NULL;
replayBuffer = NULL;
@@ -1438,21 +1391,49 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
transferringFromDvbApi = NULL;
ca = 0;
priority = -1;
- videoDev = open(VideoFileName, O_RDWR | O_NONBLOCK);
- if (videoDev >= 0) {
- siProcessor = new cSIProcessor(VbiFileName);
+
+ // Devices that are only present on DVB-C or DVB-S cards:
+
+ fd_qamfe = OstOpen(DEV_OST_QAMFE, n, O_RDWR);
+ fd_qpskfe = OstOpen(DEV_OST_QPSKFE, n, O_RDWR);
+ fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
+
+ // Devices that all DVB cards must have:
+
+ fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxa1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxa2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxd1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxd2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+ fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
+
+ // Devices not present on "budget" cards:
+
+ fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
+ fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
+ fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
+
+ // Devices that may not be available, and are not necessary for normal operation:
+
+ videoDev = OstOpen(DEV_VIDEO, n, O_RDWR);
+
+ // Devices that will be dynamically opened and closed when necessary:
+
+ fd_dvr = -1;
+
+ // Video format:
+
+ SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
+
+ // We only check the devices that must be present - the others will be checked before accessing them:
+
+ if (((fd_qpskfe >= 0 && fd_sec >= 0) || fd_qamfe >= 0) && fd_demuxv >= 0 && fd_demuxa1 >= 0 && fd_demuxa2 >= 0 && fd_demuxd1 >= 0 && fd_demuxd2 >= 0 && fd_demuxt >= 0) {
+ siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
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
- siProcessor->AddFilter(0x12, 0x4e); // event info, actual TS, present/following
- siProcessor->AddFilter(0x12, 0x4f); // event info, other TS, present/following
- siProcessor->AddFilter(0x12, 0x50); // event info, actual TS, schedule
- siProcessor->AddFilter(0x12, 0x60); // event info, other TS, schedule
- siProcessor->Start();
}
else
- LOG_ERROR_STR(VideoFileName);
+ esyslog(LOG_ERR, "ERROR: can't open video device %d", n);
cols = rows = 0;
ovlGeoSet = ovlStat = ovlFbSet = false;
@@ -1480,16 +1461,14 @@ cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName)
cDvbApi::~cDvbApi()
{
- if (videoDev >= 0) {
- delete siProcessor;
- Close();
- StopReplay();
- StopRecord();
- StopTransfer();
- OvlO(false); //Overlay off!
- //XXX the following call sometimes causes a segfault - driver problem?
- //XXX close(videoDev);
- }
+ delete siProcessor;
+ Close();
+ StopReplay();
+ StopRecord();
+ StopTransfer();
+ OvlO(false); //Overlay off!
+ // We're not explicitly closing any device files here, since this sometimes
+ // caused segfaults. Besides, the program is about to terminate anyway...
#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
endwin();
#endif
@@ -1555,37 +1534,32 @@ int cDvbApi::Index(void)
return -1;
}
+bool cDvbApi::Probe(const char *FileName)
+{
+ if (access(FileName, F_OK) == 0) {
+ dsyslog(LOG_INFO, "probing %s", FileName);
+ int f = open(FileName, O_RDONLY);
+ if (f >= 0) {
+ close(f);
+ return true;
+ }
+ else if (errno != ENODEV && errno != EINVAL)
+ LOG_ERROR_STR(FileName);
+ }
+ else if (errno != ENOENT)
+ LOG_ERROR_STR(FileName);
+ return false;
+}
+
bool cDvbApi::Init(void)
{
NumDvbApis = 0;
for (int i = 0; i < MAXDVBAPI; i++) {
if (useDvbApi == 0 || (useDvbApi & (1 << i)) != 0) {
- char fileName[strlen(VIDEODEVICE) + 10];
- sprintf(fileName, "%s%d", VIDEODEVICE, i);
- if (access(fileName, F_OK | R_OK | W_OK) == 0) {
- dsyslog(LOG_INFO, "probing %s", fileName);
- int f = open(fileName, O_RDWR);
- if (f >= 0) {
- struct video_capability cap;
- int r = ioctl(f, VIDIOCGCAP, &cap);
- close(f);
- if (r == 0 && (cap.type & VID_TYPE_DVB)) {
- char vbiFileName[strlen(VBIDEVICE) + 10];
- sprintf(vbiFileName, "%s%d", VBIDEVICE, i);
- dvbApi[NumDvbApis++] = new cDvbApi(fileName, vbiFileName);
- }
- }
- else {
- if (errno != ENODEV)
- LOG_ERROR_STR(fileName);
- break;
- }
- }
- else {
- if (errno != ENOENT)
- LOG_ERROR_STR(fileName);
+ if (Probe(OstName(DEV_OST_QPSKFE, i)) || Probe(OstName(DEV_OST_QAMFE, i)))
+ dvbApi[NumDvbApis++] = new cDvbApi(i);
+ else
break;
- }
}
}
PrimaryDvbApi = dvbApi[0];
@@ -1616,6 +1590,8 @@ const cSchedules *cDvbApi::Schedules(cThreadLock *ThreadLock) const
bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// just do this once?
struct video_mbuf mbuf;
@@ -1711,6 +1687,8 @@ bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX,
bool cDvbApi::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// get the actual X-Server settings???
// plausibility-check problem: can't be verified w/o X-server!!!
@@ -1752,6 +1730,8 @@ bool cDvbApi::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette)
bool cDvbApi::OvlG(int SizeX, int SizeY, int PosX, int PosY)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
// get the actual X-Server settings???
struct video_capability vc;
@@ -1798,6 +1778,8 @@ bool cDvbApi::OvlG(int SizeX, int SizeY, int PosX, int PosY)
bool cDvbApi::OvlC(int ClipCount, CRect *cr)
{
+ if (videoDev < 0)
+ return false;
if (ovlGeoSet && ovlFbSet) {
for (int i = 0; i < ClipCount; i++) {
ovlClipRects[i].x = cr[i].x;
@@ -1815,6 +1797,8 @@ bool cDvbApi::OvlC(int ClipCount, CRect *cr)
bool cDvbApi::OvlP(__u16 Brightness, __u16 Colour, __u16 Hue, __u16 Contrast)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
ovlBrightness = Brightness;
ovlColour = Colour;
@@ -1836,6 +1820,8 @@ bool cDvbApi::OvlP(__u16 Brightness, __u16 Colour, __u16 Hue, __u16 Contrast)
bool cDvbApi::OvlO(bool Value)
{
+ if (videoDev < 0)
+ return false;
int result = 0;
if (!ovlGeoSet && Value)
return false;
@@ -1867,30 +1853,11 @@ void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg)
}
}
}
-#else
-void cDvbApi::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
-{
- if (videoDev >= 0) {
- struct drawcmd dc;
- dc.cmd = cmd;
- dc.color = color;
- dc.x0 = x0;
- dc.y0 = y0;
- dc.x1 = x1;
- dc.y1 = y1;
- dc.data = (void *)data;
- ioctl(videoDev, VIDIOCSOSDCOMMAND, &dc);
- usleep(10); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
- // XXX and sometimes the OSD was no longer displayed).
- // XXX Increase the value if the problem still persists on your particular system.
- // TODO Check if this is still necessary with driver versions after 0.6.
- }
-}
#endif
void cDvbApi::Open(int w, int h)
{
- int d = (h < 0) ? MenuLines + h : 0;
+ int d = (h < 0) ? Setup.OSDheight + h : 0;
h = abs(h);
cols = w;
rows = h;
@@ -1899,16 +1866,7 @@ void cDvbApi::Open(int w, int h)
syncok(window, true);
#define B2C(b) (((b) * 1000) / 255)
#define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b))
-#else
- w *= charWidth;
- h *= lineHeight;
- d *= lineHeight;
- int x = (720 - MenuColumns * charWidth) / 2; //TODO PAL vs. NTSC???
- int y = (576 - MenuLines * lineHeight) / 2 + d;
- osd = new cDvbOsd(videoDev, x, y, x + w - 1, y + h - 1, 4);
- #define SETCOLOR(n, r, g, b, o) Cmd(OSD_SetColor, n, r, g, b, o)
- SETCOLOR(clrTransparent, 0x00, 0x00, 0x00, 0);
-#endif
+ //XXX
SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray
SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255);
SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255);
@@ -1918,6 +1876,35 @@ void cDvbApi::Open(int w, int h)
SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255);
SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255);
SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255);
+#else
+ w *= charWidth;
+ h *= lineHeight;
+ d *= lineHeight;
+ int x = (720 - (Setup.OSDwidth - 1) * charWidth) / 2; //TODO PAL vs. NTSC???
+ int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;
+ //XXX
+ osd = new cDvbOsd(fd_osd, x, y);
+ //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)
+ if (h / lineHeight == 5) { //XXX channel display
+ osd->Create(0, 0, w, h, 4);
+ }
+ else if (h / lineHeight == 1) { //XXX info display
+ osd->Create(0, 0, w, h, 4);
+ }
+ else if (d == 0) { //XXX full menu
+ osd->Create(0, 0, w, lineHeight, 2);
+ osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2, true, clrBackground, clrCyan, clrWhite, clrBlack);
+ osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4);
+ }
+ else { //XXX progress display
+ /*XXX
+ osd->Create(0, 0, w, lineHeight, 1);
+ osd->Create(0, lineHeight, w, lineHeight, 2, false);
+ osd->Create(0, 2 * lineHeight, w, lineHeight, 1);
+ XXX*///XXX some pixels are not drawn correctly with lower bpp values
+ osd->Create(0, 0, w, 3*lineHeight, 4);
+ }
+#endif
}
void cDvbApi::Close(void)
@@ -2037,60 +2024,270 @@ void cDvbApi::Flush(void)
#endif
}
-bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr)
+int cDvbApi::SetModeRecord(void)
{
- 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);
- unsigned int freq = FrequencyMHz;
- if (front.type == FRONT_DVBS) {
- front.ttk = (freq < 11700UL) ? 0 : 1;
- if (freq < 11700UL) {
+ // Sets up the DVB device for recording
+
+ SetPids(true);
+ if (fd_dvr >= 0)
+ close(fd_dvr);
+ fd_dvr = OstOpen(DEV_OST_DVR, Index(), O_RDONLY | O_NONBLOCK);
+ if (fd_dvr < 0)
+ LOG_ERROR;
+ return fd_dvr;
+}
+
+void cDvbApi::SetModeReplay(void)
+{
+ // Sets up the DVB device for replay
+
+ if (fd_video >= 0 && fd_audio >= 0) {
+ if (siProcessor)
+ siProcessor->SetStatus(false);
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_PLAY));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_video, VIDEO_PLAY));
+ }
+}
+
+void cDvbApi::SetModeNormal(bool FromRecording)
+{
+ // Puts the DVB device back into "normal" viewing mode (after replay or recording)
+
+ if (FromRecording) {
+ close(fd_dvr);
+ fd_dvr = -1;
+ SetPids(false);
+ }
+ else {
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_video, VIDEO_STOP, true));
+ CHECK(ioctl(fd_audio, AUDIO_STOP, true));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ if (siProcessor)
+ siProcessor->SetStatus(true);
+ }
+ }
+}
+
+void cDvbApi::SetVideoFormat(videoFormat_t Format)
+{
+ if (fd_video)
+ CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format));
+}
+
+bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output)
+{
+ if (Pid) {
+ CHECK(ioctl(fd, DMX_STOP));
+ dmxPesFilterParams pesFilterParams;
+ pesFilterParams.pid = Pid;
+ pesFilterParams.input = DMX_IN_FRONTEND;
+ pesFilterParams.output = Output;
+ pesFilterParams.pesType = PesType;
+ pesFilterParams.flags = DMX_IMMEDIATE_START;
+ if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
+ if (Pid != 0x1FFF)
+ LOG_ERROR;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cDvbApi::SetPids(bool ForRecording)
+{
+ return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
+ SetApid1(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
+ SetApid2(ForRecording ? aPid2 : 0, DMX_OUT_TS_TAP) &&
+ SetDpid1(ForRecording ? dPid1 : 0, DMX_OUT_TS_TAP) &&
+ SetDpid2(ForRecording ? dPid2 : 0, DMX_OUT_TS_TAP);
+}
+
+bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr)
+{
+ // Make sure the siProcessor won't access the device while switching
+ cThreadLock ThreadLock(siProcessor);
+
+ StopTransfer();
+ StopReplay();
+
+ // Must set this anyway to avoid getting stuck when switching through
+ // channels with 'Up' and 'Down' keys:
+ currentChannel = ChannelNumber;
+ vPid = Vpid;
+ aPid1 = Apid1;
+ aPid2 = Apid2;
+ dPid1 = Dpid1;
+ dPid2 = Dpid2;
+
+ // Avoid noise while switching:
+
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ }
+
+ // If this card can't receive this channel, we must not actually switch
+ // the channel here, because that would irritate the driver when we
+ // start replaying in Transfer Mode immediately after switching the channel:
+ bool NeedsTransferMode = (this == PrimaryDvbApi && Ca && Ca != Index() + 1);
+
+ if (!NeedsTransferMode) {
+
+ // Turn off current PIDs:
+
+ SetVpid( 0x1FFF, DMX_OUT_DECODER);
+ SetApid1(0x1FFF, DMX_OUT_DECODER);
+ SetApid2(0x1FFF, DMX_OUT_DECODER);
+ SetDpid1(0x1FFF, DMX_OUT_DECODER);
+ SetDpid2(0x1FFF, DMX_OUT_DECODER);
+ SetTpid( 0x1FFF, DMX_OUT_DECODER);
+
+ bool ChannelSynced = false;
+
+ if (fd_qpskfe >= 0 && fd_sec >= 0) { // DVB-S
+
+ // Frequency offsets:
+
+ unsigned int freq = FrequencyMHz;
+ int tone = SEC_TONE_OFF;
+
+ if (freq < (unsigned int)Setup.LnbSLOF) {
freq -= Setup.LnbFrequLo;
- front.ttk = 0;
+ tone = SEC_TONE_OFF;
}
else {
freq -= Setup.LnbFrequHi;
- front.ttk = 1;
+ tone = SEC_TONE_ON;
+ }
+
+ qpskParameters qpsk;
+ qpsk.iFrequency = freq * 1000UL;
+ qpsk.SymbolRate = Srate * 1000UL;
+ qpsk.FEC_inner = FEC_AUTO;
+
+ int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
+
+ // DiseqC:
+
+ secCommand scmd;
+ scmd.type = 0;
+ scmd.u.diseqc.addr = 0x10;
+ scmd.u.diseqc.cmd = 0x38;
+ scmd.u.diseqc.numParams = 1;
+ scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
+
+ secCmdSequence scmds;
+ scmds.voltage = volt;
+ scmds.miniCommand = SEC_MINI_NONE;
+ scmds.continuousTone = tone;
+ scmds.numCommands = Setup.DiSEqC ? 1 : 0;
+ scmds.commands = &scmd;
+
+ CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
+
+ // Tuning:
+
+ CHECK(ioctl(fd_qpskfe, QPSK_TUNE, &qpsk));
+
+ // Wait for channel sync:
+
+ if (cFile::FileReady(fd_qpskfe, 5000)) {
+ qpskEvent event;
+ int res = ioctl(fd_qpskfe, QPSK_GET_EVENT, &event);
+ if (res >= 0)
+ ChannelSynced = event.type == FE_COMPLETION_EV;
+ else
+ esyslog(LOG_ERR, "ERROR %d in qpsk get event", res);
}
+ else
+ esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
}
- front.channel_flags = Ca ? DVB_CHANNEL_CA : DVB_CHANNEL_FTA;
- front.pnr = Pnr;
- front.freq = freq * 1000000UL;
- front.diseqc = Diseqc;
- front.srate = Srate * 1000;
- front.volt = (Polarization == 'v' || Polarization == 'V') ? 0 : 1;
- front.video_pid = Vpid;
- front.audio_pid = Apid;
- front.tt_pid = Tpid;
- front.fec = 8;
- front.AFC = 1;
- front.qam = 2;
- ioctl(videoDev, VIDIOCSFRONTEND, &front);
- if (front.sync & 0x1F == 0x1F) {
- if (this == PrimaryDvbApi && siProcessor)
- siProcessor->SetCurrentServiceID(Pnr);
- currentChannel = ChannelNumber;
- // If this DVB card can't receive this channel, let's see if we can
- // use the card that actually can receive it and transfer data from there:
- if (this == PrimaryDvbApi && Ca && Ca != Index() + 1) {
- cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
- if (CaDvbApi) {
- if (!CaDvbApi->Recording()) {
- if (CaDvbApi->SetChannel(ChannelNumber, FrequencyMHz, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr))
- transferringFromDvbApi = CaDvbApi->StartTransfer(videoDev);
- }
+ else if (fd_qamfe >= 0) { // DVB-C
+
+ // Frequency and symbol rate:
+
+ qamParameters qam;
+ qam.Frequency = FrequencyMHz * 1000000UL;
+ qam.SymbolRate = Srate * 1000UL;
+ qam.FEC_inner = FEC_AUTO;
+ qam.QAM = QAM_64;
+
+ // Tuning:
+
+ CHECK(ioctl(fd_qamfe, QAM_TUNE, &qam));
+
+ // Wait for channel sync:
+
+ if (cFile::FileReady(fd_qamfe, 5000)) {
+ qamEvent event;
+ int res = ioctl(fd_qamfe, QAM_GET_EVENT, &event);
+ if (res >= 0)
+ ChannelSynced = event.type == FE_COMPLETION_EV;
+ else
+ esyslog(LOG_ERR, "ERROR %d in qam get event", res);
+ }
+ else
+ esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
+ }
+ else {
+ esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device");
+ return false;
+ }
+
+ if (!ChannelSynced) {
+ esyslog(LOG_ERR, "ERROR: channel %d not sync'ed!", ChannelNumber);
+ if (this == PrimaryDvbApi)
+ cThread::RaisePanic();
+ return false;
+ }
+
+ // PID settings:
+
+ if (!SetPids(false)) {
+ esyslog(LOG_ERR, "ERROR: failed to set PIDs for channel %d", ChannelNumber);
+ return false;
+ }
+ SetTpid(Tpid, DMX_OUT_DECODER);
+ if (fd_audio >= 0)
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ }
+
+ if (this == PrimaryDvbApi && siProcessor)
+ siProcessor->SetCurrentServiceID(Pnr);
+
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+ if (NeedsTransferMode) {
+ cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
+ if (CaDvbApi) {
+ if (!CaDvbApi->Recording()) {
+ if (CaDvbApi->SetChannel(ChannelNumber, FrequencyMHz, Polarization, Diseqc, Srate, Vpid, Apid1, Apid2, Dpid1, Dpid2, Tpid, Ca, Pnr)) {
+ SetModeReplay();
+ transferringFromDvbApi = CaDvbApi->StartTransfer(fd_video);
}
}
- return true;
}
- esyslog(LOG_ERR, "ERROR: channel %d not sync'ed (front.sync=%X)!", ChannelNumber, front.sync);
}
- return false;
+
+ if (fd_video >= 0 && fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
+ }
+
+ return true;
}
bool cDvbApi::Transferring(void)
@@ -2101,7 +2298,7 @@ bool cDvbApi::Transferring(void)
cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev)
{
StopTransfer();
- transferBuffer = new cTransferBuffer(videoDev, TransferToVideoDev);
+ transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid1);
return this;
}
@@ -2110,7 +2307,6 @@ void cDvbApi::StopTransfer(void)
if (transferBuffer) {
delete transferBuffer;
transferBuffer = NULL;
- SetPlayMode(videoDev, VID_PLAY_RESET);
}
if (transferringFromDvbApi) {
transferringFromDvbApi->StopTransfer();
@@ -2143,37 +2339,36 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
esyslog(LOG_ERR, "ERROR: StartRecord() called while recording - ignored!");
return false;
}
- if (videoDev >= 0) {
- StopTransfer();
+ StopTransfer();
- StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
+ StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
- // Check FileName:
+ // Check FileName:
- if (!FileName) {
- esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)");
- return false;
- }
- isyslog(LOG_INFO, "record %s", FileName);
+ if (!FileName) {
+ esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)");
+ return false;
+ }
+ isyslog(LOG_INFO, "record %s", FileName);
- // Create directories if necessary:
+ // Create directories if necessary:
- if (!MakeDirs(FileName, true))
- return false;
+ if (!MakeDirs(FileName, true))
+ return false;
- // Create recording buffer:
+ // Create recording buffer:
- recordBuffer = new cRecordBuffer(&videoDev, FileName);
+ recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2, dPid1, dPid2);
- if (recordBuffer) {
- ca = Ca;
- priority = Priority;
- return true;
- }
- else
- esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
+ if (recordBuffer) {
+ ca = Ca;
+ priority = Priority;
+ return true;
}
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate recording buffer");
+
return false;
}
@@ -2195,7 +2390,7 @@ bool cDvbApi::StartReplay(const char *FileName)
}
StopTransfer();
StopReplay();
- if (videoDev >= 0) {
+ if (fd_video >= 0 && fd_audio >= 0) {
// Check FileName:
@@ -2207,7 +2402,7 @@ bool cDvbApi::StartReplay(const char *FileName)
// Create replay buffer:
- replayBuffer = new cReplayBuffer(&videoDev, FileName);
+ replayBuffer = new cReplayBuffer(this, fd_video, fd_audio, FileName);
if (replayBuffer)
return true;
else
@@ -2221,6 +2416,12 @@ void cDvbApi::StopReplay(void)
if (replayBuffer) {
delete replayBuffer;
replayBuffer = NULL;
+ if (this == PrimaryDvbApi) {
+ // let's explicitly switch the channel back in case it was in Transfer Mode:
+ cChannel *Channel = Channels.GetByNumber(currentChannel);
+ if (Channel)
+ Channel->Switch(this, false);
+ }
}
}
@@ -2276,6 +2477,38 @@ void cDvbApi::Goto(int Position, bool Still)
replayBuffer->Goto(Position, Still);
}
+bool cDvbApi::CanToggleAudioTrack(void)
+{
+ return replayBuffer ? replayBuffer->CanToggleAudioTrack() : (aPid1 && aPid2 && aPid1 != aPid2);
+}
+
+bool cDvbApi::ToggleAudioTrack(void)
+{
+ if (replayBuffer) {
+ replayBuffer->ToggleAudioTrack();
+ return true;
+ }
+ else {
+ int a = aPid2;
+ aPid2 = aPid1;
+ aPid1 = a;
+ if (transferringFromDvbApi)
+ return transferringFromDvbApi->ToggleAudioTrack();
+ else {
+ if (transferBuffer)
+ transferBuffer->SetAudioPid(aPid1);
+ return SetPids(transferBuffer != NULL);
+ }
+ }
+ return false;
+}
+
+void cDvbApi::SetAudioCommand(const char *Command)
+{
+ delete audioCommand;
+ audioCommand = strdup(Command);
+}
+
// --- cEITScanner -----------------------------------------------------------
cEITScanner::cEITScanner(void)
@@ -2318,7 +2551,7 @@ void cEITScanner::Process(void)
time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
for (int i = 0; i < cDvbApi::NumDvbApis; i++) {
- cDvbApi *DvbApi = cDvbApi::GetDvbApi(i, 0);
+ cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, MAXPRIORITY);
if (DvbApi) {
if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) {