summaryrefslogtreecommitdiff
path: root/dvbapi.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-06-16 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-06-16 18:00:00 +0200
commitf06d2c27fca449148d9d8fac19d81c668744f170 (patch)
tree8f21a604339719672bf7ab8b6e9d69bfa1817ac5 /dvbapi.c
parentd07e3829f7c44d713b2677ef73f864af80b3dea3 (diff)
downloadvdr-patch-lnbsharing-f06d2c27fca449148d9d8fac19d81c668744f170.tar.gz
vdr-patch-lnbsharing-f06d2c27fca449148d9d8fac19d81c668744f170.tar.bz2
Version 1.1.3vdr-1.1.3
- Improved the VDR Makefile to avoid a warning if the '.dependencies' file does not exist, and also using $(MAKE) to call recursive makes. - Changed the name of the 'package' target in the plugin Makefiles to 'dist' (following the suggestions in the "GNU Make" manual). If you already have started a plugin project, you may want to change this in your Makefile accordingly. - Improved the plugin Makefile to avoid a warning if the '.dependencies' file does not exist, and also using $(shell...) to get the version numbers. If you already have started a plugin project, you may want to change this in your Makefile accordingly. - Fixed some function headers to make them compile with gcc 3.x (thanks to Gregoire Favre). - Fixed the cutting mechanism to make it re-sync in case a frame is larger than the buffer (thanks to Sven Grothklags). - Added an error message if the directory specified in the '-L' option can't be accessed (suggested by Stefan Huelswitt). - Rearranged OSD class names to make 'cOsd' available for the main OSD interface. - Completely moved OSD handling out of the cDvbApi class, into the new cOsd. - Implemented cStatus to allow plugins to set up a status monitor. See PLUGINS.html for details. - Moved the cEITScanner out of dvbapi.h/.c, into the new eitscan.h/.c. - Added Swedish language texts (thanks to Tomas Prybil). - Fixed parsing 'E' records in epg2html.pl (thanks to Matthias Fechner for pointing out this one). - Removed compiler option '-m486' to make it work on non-Intel platforms. If you have already started a plugin project, you may want to make sure you remove this option from your existing Makefile. - Completely rearranged the recording and replay functions to make them available to plugins. - Replay is now done in a single thread (no more syncing between input and output thread necessary). - It is now possible to record several channels on the same transponder with "budget cards". VDR automatically attaches a recording timer to a card that already records on the appropriate transponder. How many parallel recordings can actually be done depends on the computer's performance. Currently any number of recordings gets attached to a card, so you should carefully plan your timers to not exceed the limit. On a K6-II/450 it was possible to record three channels from transponder 12480 with a single WinTV NOVA-S. - Timers that record two successive shows on the same channel may now overlap and will use the same DVB card. During the time where both timers record the data is simply saved to both files. - The following limitations apply to this version: + Transfer mode doesn't work yet. + The '-a' option (for Dolby Digital audio) doesn't work yet. + Switching between different language tracks doesn't work yet. + Cutting doesn't work yet.
Diffstat (limited to 'dvbapi.c')
-rw-r--r--dvbapi.c2846
1 files changed, 0 insertions, 2846 deletions
diff --git a/dvbapi.c b/dvbapi.c
deleted file mode 100644
index 0b14a87..0000000
--- a/dvbapi.c
+++ /dev/null
@@ -1,2846 +0,0 @@
-/*
- * dvbapi.c: Interface to the DVB driver
- *
- * See the main source file 'vdr.c' for copyright information and
- * how to reach the author.
- *
- * $Id: dvbapi.c 1.175 2002/05/13 16:29:17 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>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "config.h"
-#include "recording.h"
-#include "remux.h"
-#include "ringbuffer.h"
-#include "tools.h"
-#include "videodir.h"
-
-#define DEV_VIDEO "/dev/video"
-#define DEV_OST_OSD "/dev/ost/osd"
-#define DEV_OST_FRONTEND "/dev/ost/frontend"
-#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 MEGABYTE(1)
-
-// The maximum size of a single frame:
-#define MAXFRAMESIZE KILOBYTE(192)
-
-#define MAXFILESPERRECORDING 255
-
-#define MINFREEDISKSPACE (512) // MB
-#define DISKCHECKINTERVAL 100 // seconds
-
-#define INDEXFILESUFFIX "/index.vdr"
-#define RECORDFILESUFFIX "/%03d.vdr"
-#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
-
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
-
-// The maximum time we wait before assuming that a recorded video data stream
-// is broken:
-#define MAXBROKENTIMEOUT 30 // seconds
-
-// The maximum time to wait before giving up while catching up on an index file:
-#define MAXINDEXCATCHUP 2 // seconds
-
-// The default priority for non-primary DVB cards:
-#define DEFAULTPRIORITY -2
-
-#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
-
-#define FATALERRNO (errno != EAGAIN && errno != EINTR)
-
-typedef unsigned char uchar;
-
-const char *IndexToHMSF(int Index, bool WithFrame)
-{
- static char buffer[16];
- int f = (Index % FRAMESPERSEC) + 1;
- int s = (Index / FRAMESPERSEC);
- int m = s / 60 % 60;
- int h = s / 3600;
- s %= 60;
- snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
- return buffer;
-}
-
-int HMSFToIndex(const char *HMSF)
-{
- int h, m, s, f = 0;
- if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
- return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
- return 0;
-}
-
-// --- cIndexFile ------------------------------------------------------------
-
-class cIndexFile {
-private:
- struct tIndex { int offset; uchar type; uchar number; short reserved; };
- int f;
- char *fileName;
- int size, last;
- tIndex *index;
- cResumeFile resumeFile;
- bool CatchUp(int Index = -1);
-public:
- cIndexFile(const char *FileName, bool Record);
- ~cIndexFile();
- bool Ok(void) { return index != NULL; }
- bool 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, bool StayOffEnd = false);
- int Get(uchar FileNumber, int FileOffset);
- int Last(void) { CatchUp(); return last; }
- int GetResume(void) { return resumeFile.Read(); }
- bool StoreResume(int Index) { return resumeFile.Save(Index); }
- };
-
-cIndexFile::cIndexFile(const char *FileName, bool Record)
-:resumeFile(FileName)
-{
- f = -1;
- fileName = NULL;
- size = 0;
- last = -1;
- index = NULL;
- if (FileName) {
- fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1];
- if (fileName) {
- strcpy(fileName, FileName);
- char *pFileExt = fileName + strlen(fileName);
- strcpy(pFileExt, INDEXFILESUFFIX);
- int delta = 0;
- if (access(fileName, R_OK) == 0) {
- struct stat buf;
- if (stat(fileName, &buf) == 0) {
- delta = buf.st_size % sizeof(tIndex);
- if (delta) {
- delta = sizeof(tIndex) - delta;
- esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
- }
- last = (buf.st_size + delta) / sizeof(tIndex) - 1;
- if (!Record && last >= 0) {
- size = last + 1;
- index = new tIndex[size];
- if (index) {
- f = open(fileName, O_RDONLY);
- if (f >= 0) {
- if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
- esyslog("ERROR: can't read from file '%s'", fileName);
- delete index;
- index = NULL;
- close(f);
- f = -1;
- }
- // we don't close f here, see CatchUp()!
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
- }
- }
- else
- LOG_ERROR;
- }
- else if (!Record)
- isyslog("missing index file %s", fileName);
- if (Record) {
- if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
- if (delta) {
- esyslog("ERROR: padding index file with %d '0' bytes", delta);
- while (delta--)
- writechar(f, 0);
- }
- }
- else
- LOG_ERROR_STR(fileName);
- }
- }
- else
- esyslog("ERROR: can't copy file name '%s'", FileName);
- }
-}
-
-cIndexFile::~cIndexFile()
-{
- if (f >= 0)
- close(f);
- delete fileName;
- delete index;
-}
-
-bool cIndexFile::CatchUp(int Index)
-{
- if (index && f >= 0) {
- for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
- struct stat buf;
- if (fstat(f, &buf) == 0) {
- int newLast = buf.st_size / sizeof(tIndex) - 1;
- if (newLast > last) {
- if (size <= newLast) {
- size *= 2;
- if (size <= newLast)
- size = newLast + 1;
- }
- index = (tIndex *)realloc(index, size * sizeof(tIndex));
- if (index) {
- int offset = (last + 1) * sizeof(tIndex);
- int delta = (newLast - last) * sizeof(tIndex);
- if (lseek(f, offset, SEEK_SET) == offset) {
- if (safe_read(f, &index[last + 1], delta) != delta) {
- esyslog("ERROR: can't read from index");
- delete index;
- index = NULL;
- close(f);
- f = -1;
- break;
- }
- last = newLast;
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't realloc() index");
- }
- }
- else
- LOG_ERROR_STR(fileName);
- if (Index >= last)
- sleep(1);
- else
- return true;
- }
- }
- return false;
-}
-
-bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
-{
- if (f >= 0) {
- tIndex i = { FileOffset, PictureType, FileNumber, 0 };
- if (safe_write(f, &i, sizeof(i)) < 0) {
- LOG_ERROR_STR(fileName);
- close(f);
- f = -1;
- return false;
- }
- last++;
- }
- return f >= 0;
-}
-
-bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
-{
- if (index) {
- CatchUp(Index);
- if (Index >= 0 && Index <= last) {
- *FileNumber = index[Index].number;
- *FileOffset = index[Index].offset;
- if (PictureType)
- *PictureType = index[Index].type;
- if (Length) {
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else
- *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
- }
- return true;
- }
- }
- return false;
-}
-
-int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
-{
- if (index) {
- CatchUp();
- int d = Forward ? 1 : -1;
- for (;;) {
- Index += d;
- if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) {
- if (index[Index].type == I_FRAME) {
- if (FileNumber)
- *FileNumber = index[Index].number;
- else
- FileNumber = &index[Index].number;
- if (FileOffset)
- *FileOffset = index[Index].offset;
- else
- FileOffset = &index[Index].offset;
- if (Length) {
- // all recordings end with a non-I_FRAME, so the following should be safe:
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else {
- esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
- *Length = -1;
- }
- }
- return Index;
- }
- }
- else
- break;
- }
- }
- return -1;
-}
-
-int cIndexFile::Get(uchar FileNumber, int FileOffset)
-{
- if (index) {
- CatchUp();
- //TODO implement binary search!
- int i;
- for (i = 0; i < last; i++) {
- if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
- break;
- }
- return i;
- }
- return -1;
-}
-
-// --- cFileName -------------------------------------------------------------
-
-class cFileName {
-private:
- int file;
- int fileNumber;
- char *fileName, *pFileNumber;
- bool record;
- bool blocking;
-public:
- cFileName(const char *FileName, bool Record, bool Blocking = false);
- ~cFileName();
- const char *Name(void) { return fileName; }
- int Number(void) { return fileNumber; }
- int Open(void);
- void Close(void);
- int SetOffset(int Number, int Offset = 0);
- int NextFile(void);
- };
-
-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) {
- esyslog("ERROR: can't copy file name '%s'", fileName);
- return;
- }
- strcpy(fileName, FileName);
- pFileNumber = fileName + strlen(fileName);
- SetOffset(1);
-}
-
-cFileName::~cFileName()
-{
- Close();
- delete fileName;
-}
-
-int cFileName::Open(void)
-{
- if (file < 0) {
- int BlockingFlag = blocking ? 0 : O_NONBLOCK;
- if (record) {
- dsyslog("recording to '%s'", fileName);
- file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else {
- if (access(fileName, R_OK) == 0) {
- dsyslog("playing '%s'", fileName);
- file = open(fileName, O_RDONLY | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else if (errno != ENOENT)
- LOG_ERROR_STR(fileName);
- }
- }
- return file;
-}
-
-void cFileName::Close(void)
-{
- if (file >= 0) {
- if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
- LOG_ERROR_STR(fileName);
- file = -1;
- }
-}
-
-int cFileName::SetOffset(int Number, int Offset)
-{
- if (fileNumber != Number)
- Close();
- if (0 < Number && Number <= MAXFILESPERRECORDING) {
- fileNumber = Number;
- sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
- if (record) {
- if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
- return SetOffset(Number + 1);
- else if (errno != ENOENT) { // something serious has happened
- LOG_ERROR_STR(fileName);
- return -1;
- }
- // found a non existing file suffix
- }
- if (Open() >= 0) {
- if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
- LOG_ERROR_STR(fileName);
- return -1;
- }
- }
- return file;
- }
- esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
- return -1;
-}
-
-int cFileName::NextFile(void)
-{
- return SetOffset(fileNumber + 1);
-}
-
-// --- cRecordBuffer ---------------------------------------------------------
-
-class cRecordBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- cFileName fileName;
- cIndexFile *index;
- cRemux remux;
- uchar pictureType;
- int fileSize;
- int videoDev;
- int recordFile;
- bool recording;
- time_t lastDiskSpaceCheck;
- bool RunningLowOnDiskSpace(void);
- bool NextFile(void);
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2);
- virtual ~cRecordBuffer();
- };
-
-cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,fileName(FileName, true)
-,remux(VPid, APid1, APid2, DPid1, DPid2, true)
-{
- dvbApi = DvbApi;
- index = NULL;
- pictureType = NO_PICTURE;
- fileSize = 0;
- recordFile = fileName.Open();
- recording = false;
- lastDiskSpaceCheck = time(NULL);
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, true);
- if (!index)
- esyslog("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;
-}
-
-bool cRecordBuffer::RunningLowOnDiskSpace(void)
-{
- if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
- int Free = FreeDiskSpaceMB(fileName.Name());
- lastDiskSpaceCheck = time(NULL);
- if (Free < MINFREEDISKSPACE) {
- dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
- return true;
- }
- }
- return false;
-}
-
-bool cRecordBuffer::NextFile(void)
-{
- if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
- recordFile = fileName.NextFile();
- fileSize = 0;
- }
- }
- return recordFile >= 0;
-}
-
-void cRecordBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- time_t t = time(NULL);
- recording = true;
- for (;;) {
- if (cFile::FileReady(videoDev, 100)) {
- 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;
- if (r > 0)
- usleep(1); // this keeps the CPU load low
- }
- t = time(NULL);
- }
- else if (r < 0) {
- if (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- if (time(NULL) - t > MAXBROKENTIMEOUT) {
- esyslog("ERROR: video data stream broken");
- cThread::EmergencyExit(true);
- t = time(NULL);
- }
- if (!recording)
- break;
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cRecordBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int r = 0;
- for (;;) {
- int g = Get(b + r, sizeof(b) - r);
- if (g > 0)
- r += g;
- if (r > 0) {
- 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);
- if (safe_write(recordFile, p, Result) < 0) {
- LOG_ERROR_STR(fileName.Name());
- recording = false;
- return;
- }
- fileSize += Result;
- }
- else
- break;
- }
- if (Count > 0) {
- r -= Count;
- memmove(b, b + Count, r);
- }
- if (!recording)
- break;
- }
- else
- usleep(1); // this keeps the CPU load low
- }
- recording = false;
-
- dsyslog("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("ERROR: frame larger than buffer (%d > %d)", Length, Max);
- Length = Max;
- }
- int r = safe_read(f, b, Length);
- if (r < 0)
- LOG_ERROR;
- return r;
-}
-
-// --- cBackTrace ----------------------------------------------------------
-
-#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size
-#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size
-#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents
-
-class cBackTrace {
-private:
- int index[BACKTRACE_ENTRIES];
- int length[BACKTRACE_ENTRIES];
- int pos, num;
-public:
- cBackTrace(void);
- void Clear(void);
- void Add(int Index, int Length);
- int Get(bool Forward);
- };
-
-cBackTrace::cBackTrace(void)
-{
- Clear();
-}
-
-void cBackTrace::Clear(void)
-{
- pos = num = 0;
-}
-
-void cBackTrace::Add(int Index, int Length)
-{
- index[pos] = Index;
- length[pos] = Length;
- if (++pos >= BACKTRACE_ENTRIES)
- pos = 0;
- if (num < BACKTRACE_ENTRIES)
- num++;
-}
-
-int cBackTrace::Get(bool Forward)
-{
- int p = pos;
- int n = num;
- int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ???
- int i = -1;
-
- while (n && l > 0) {
- if (--p < 0)
- p = BACKTRACE_ENTRIES - 1;
- i = index[p] - 1;
- l -= length[p];
- n--;
- }
- return i;
-}
-
-// --- cPlayBuffer ---------------------------------------------------------
-
-#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
-
-class cPlayBuffer : public cRingBufferFrame {
-private:
- cBackTrace backTrace;
-protected:
- enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
- enum ePlayDirs { pdForward, pdBackward };
- static int Speeds[];
- cDvbApi *dvbApi;
- int videoDev, audioDev;
- cPipe dolbyDev;
- int blockInput, blockOutput;
- ePlayModes playMode;
- ePlayDirs playDir;
- int trickSpeed;
- int readIndex, writeIndex;
- bool canDoTrickMode;
- bool canToggleAudioTrack;
- bool skipAC3bytes;
- uchar audioTrack;
- void TrickSpeed(int Increment);
- virtual void Empty(bool Block = false);
- virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {}
- virtual void PlayExternalDolby(const uchar *b, int MaxLength);
- virtual void Output(void);
- void putFrame(cFrame *Frame);
- void putFrame(unsigned char *Data, int Length, eFrameType Type = ftUnknown);
-public:
- cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev);
- virtual ~cPlayBuffer();
- virtual void Pause(void);
- virtual void Play(void);
- virtual void Forward(void);
- virtual void Backward(void);
- virtual int SkipFrames(int Frames) { return -1; }
- virtual void SkipSeconds(int Seconds) {}
- virtual void Goto(int Position, bool Still = false) {}
- virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { Current = Total = -1; }
- bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
- bool CanToggleAudioTrack(void) { return canToggleAudioTrack; };
- virtual void ToggleAudioTrack(void);
- };
-
-#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
-#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
-#define SPEED_MULT 12 // the speed multiplier
-int cPlayBuffer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
-
-cPlayBuffer::cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev)
-:cRingBufferFrame(VIDEOBUFSIZE)
-{
- dvbApi = DvbApi;
- videoDev = VideoDev;
- audioDev = AudioDev;
- blockInput = blockOutput = false;
- playMode = pmPlay;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- readIndex = writeIndex = -1;
- canDoTrickMode = false;
- canToggleAudioTrack = false;
- skipAC3bytes = false;
- audioTrack = 0xC0;
-}
-
-cPlayBuffer::~cPlayBuffer()
-{
-}
-
-void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength)
-{
- if (cDvbApi::AudioCommand()) {
- if (!dolbyDev && !dolbyDev.Open(cDvbApi::AudioCommand(), "w")) {
- esyslog("ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
- return;
- }
- if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) {
- if (b[3] == 0xBD) { // dolby
- int l = b[4] * 256 + b[5] + 6;
- int written = b[8] + (skipAC3bytes ? 13 : 9); // skips the PES header
- int n = min(l - written, MaxLength);
- while (n > 0) {
- int w = fwrite(&b[written], 1, n, dolbyDev);
- if (w < 0) {
- LOG_ERROR;
- break;
- }
- n -= w;
- written += w;
- }
- }
- }
- }
-}
-
-void cPlayBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- while (Busy()) {
- if (blockOutput) {
- if (blockOutput > 1)
- blockOutput = 1;
- continue;
- }
- const cFrame *frame = Get();
- if (frame) {
- if (frame->Type() == ftDolby)
- PlayExternalDolby(frame->Data(), frame->Count());
- else {
- StripAudioPackets((uchar *)frame->Data(), frame->Count(), (playMode == pmFast || playMode == pmSlow) ? 0x00 : audioTrack);//XXX
- const uchar *p = frame->Data();
- int r = frame->Count();
- while (r > 0 && Busy() && !blockOutput) {
- if (cFile::FileReadyForWriting(videoDev, 100)) {
- int w = write(videoDev, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- writeIndex = frame->Index();
- backTrace.Add(frame->Index(), frame->Count());
- }
- Drop(frame);
- }
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-void cPlayBuffer::putFrame(cFrame *Frame)
-{
- while (Busy() && !blockInput) {
- if (Put(Frame))
- return;
- }
- delete Frame; // caller relies on frame being put, otherwise this would be a memory leak!
-}
-
-void cPlayBuffer::putFrame(unsigned char *Data, int Length, eFrameType Type)
-{
- putFrame(new cFrame(Data, Length, Type));
-}
-
-void cPlayBuffer::TrickSpeed(int Increment)
-{
- int nts = trickSpeed + Increment;
- if (Speeds[nts] == 1) {
- trickSpeed = nts;
- if (playMode == pmFast)
- Play();
- else
- Pause();
- }
- else if (Speeds[nts]) {
- trickSpeed = nts;
- int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
- int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
- if (sp > MAX_VIDEO_SLOWMOTION)
- sp = MAX_VIDEO_SLOWMOTION;
- CHECK(ioctl(videoDev, VIDEO_SLOWMOTION, sp));
- }
-}
-
-void cPlayBuffer::Empty(bool Block)
-{
- if (!(blockInput || blockOutput)) {
- blockInput = blockOutput = 2;
- EnablePut();
- EnableGet();
- time_t t0 = time(NULL);
- while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
- usleep(1);
- Lock();
- if ((readIndex = backTrace.Get(playDir == pdForward)) < 0)
- readIndex = writeIndex;
- cRingBufferFrame::Clear();
- CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
- }
- if (!Block) {
- blockInput = blockOutput = 0;
- backTrace.Clear();
- Unlock();
- }
-}
-
-void cPlayBuffer::Pause(void)
-{
- if (playMode == pmPause || playMode == pmStill)
- Play();
- else {
- bool empty = (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(videoDev, VIDEO_FREEZE));
- playMode = pmPause;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Play(void)
-{
- if (playMode != pmPlay) {
- bool empty = (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Forward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? 1 : -1);
- break;
- }
- else if (playDir == pdForward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? -1 : 1);
- break;
- }
- else if (playDir == pdForward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- break;
- }
- }
-}
-
-void cPlayBuffer::Backward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? 1 : -1);
- break;
- }
- else if (playDir == pdBackward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? -1 : 1);
- break;
- }
- else if (playDir == pdBackward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- Empty(false);
- break;
- }
- }
-}
-
-bool cPlayBuffer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- Play = (playMode == pmPlay || playMode == pmFast);
- Forward = (playDir == pdForward);
- if (playMode == pmFast || playMode == pmSlow)
- Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
- else
- Speed = -1;
- return true;
-}
-
-void cPlayBuffer::ToggleAudioTrack(void)
-{
- if (CanToggleAudioTrack()) {
- audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
- Empty();
- }
-}
-
-// --- cReplayBuffer ---------------------------------------------------------
-
-class cReplayBuffer : public cPlayBuffer {
-private:
- cIndexFile *index;
- cFileName fileName;
- int replayFile;
- bool eof;
- bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
- void Close(void);
- virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
- void DisplayFrame(uchar *b, int Length);
- int Resume(void);
- bool Save(void);
-protected:
- virtual void Input(void);
-public:
- cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
- virtual ~cReplayBuffer();
- virtual int SkipFrames(int Frames);
- virtual void SkipSeconds(int Seconds);
- virtual void Goto(int Position, bool Still = false);
- virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
- };
-
-cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
-:cPlayBuffer(DvbApi, VideoDev, AudioDev)
-,fileName(FileName, false)
-{
- index = NULL;
- replayFile = fileName.Open();
- eof = false;
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, false);
- if (!index)
- esyslog("ERROR: can't allocate index");
- else if (!index->Ok()) {
- delete index;
- index = NULL;
- }
- canDoTrickMode = index != NULL;
- dvbApi->SetModeReplay();
- Start();
-}
-
-cReplayBuffer::~cReplayBuffer()
-{
- Stop();
- Save();
- Close();
- dvbApi->SetModeNormal(false);
- delete index;
-}
-
-void cReplayBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- readIndex = Resume();
- if (readIndex >= 0)
- isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
-
- uchar b[MAXFRAMESIZE];
- while (Busy() && (blockInput || NextFile())) {
- if (blockInput) {
- if (blockInput > 1)
- blockInput = 1;
- continue;
- }
- if (playMode != pmStill) {
- int r = 0;
- if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
- uchar FileNumber;
- int FileOffset, Length;
- int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
- if (Index >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- break;
- }
- else {
- // can't call Play() here, because those functions may only be
- // called from the foreground thread - and we also don't need
- // to empty the buffer here
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- continue;
- }
- readIndex = Index;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- // must call StripAudioPackets() here because the buffer is not emptied
- // when falling back from "fast forward" to "play" (see above)
- StripAudioPackets(b, r);
- }
- else if (index) {
- uchar FileNumber;
- int FileOffset, Length;
- readIndex++;
- if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
- break;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- }
- else // allows replay even if the index file is missing
- r = read(replayFile, b, sizeof(b));
- if (r > 0)
- putFrame(new cFrame(b, r, ftUnknown, readIndex));
- else if (r == 0)
- eof = true;
- else if (r < 0 && FATALERRNO) {
- LOG_ERROR;
- break;
- }
- }
- else//XXX
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
- if (canDoTrickMode) {
- for (int i = 0; i < Length - 6; i++) {
- if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
- uchar c = b[i + 3];
- int l = b[i + 4] * 256 + b[i + 5] + 6;
- switch (c) {
- case 0xBD: // dolby
- if (Except)
- PlayExternalDolby(&b[i], Length - i);
- // 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("ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
- }
- /*XXX
- else
- esyslog("ERROR: broken packet header");
- XXX*/
- }
- }
-}
-
-void cReplayBuffer::DisplayFrame(uchar *b, int Length)
-{
- StripAudioPackets(b, Length);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, true));
-/* Using the VIDEO_STILLPICTURE ioctl call would be the
- correct way to display a still frame, but unfortunately this
- doesn't work with frames from VDR. So let's do pretty much the
- same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely
- no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE.
- If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
- could be used, please let me know!
- kls 2002-03-23
-*/
-//#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
-#ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
- videoDisplayStillPicture sp = { (char *)b, Length };
- CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
-#else
-#define MIN_IFRAME 400000
- for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
- safe_write(videoDev, b, Length);
- usleep(1); // allows the buffer to be displayed in case the progress display is active
- }
-#endif
-}
-
-void cReplayBuffer::Close(void)
-{
- if (replayFile >= 0) {
- fileName.Close();
- replayFile = -1;
- }
-}
-
-int cReplayBuffer::Resume(void)
-{
- if (index) {
- int Index = index->GetResume();
- if (Index >= 0) {
- uchar FileNumber;
- int FileOffset;
- if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
- return Index;
- }
- }
- return -1;
-}
-
-bool cReplayBuffer::Save(void)
-{
- if (index) {
- int Index = writeIndex;
- if (Index >= 0) {
- Index -= RESUMEBACKUP;
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false);
- else
- Index = 0;
- if (Index >= 0)
- return index->StoreResume(Index);
- }
- }
- return false;
-}
-
-int cReplayBuffer::SkipFrames(int Frames)
-{
- if (index && Frames) {
- int Current, Total;
- GetIndex(Current, Total, true);
- int OldCurrent = Current;
- Current = index->GetNextIFrame(Current + Frames, Frames > 0);
- return Current >= 0 ? Current : OldCurrent;
- }
- return -1;
-}
-
-void cReplayBuffer::SkipSeconds(int Seconds)
-{
- if (index && Seconds) {
- Empty(true);
- int Index = writeIndex;
- if (Index >= 0) {
- Index = max(Index + Seconds * FRAMESPERSEC, 0);
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
- if (Index >= 0)
- readIndex = writeIndex = Index - 1; // Input() will first increment it!
- }
- Empty(false);
- Play();
- }
-}
-
-void cReplayBuffer::Goto(int Index, bool Still)
-{
- if (index) {
- Empty(true);
- 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) {
- uchar b[MAXFRAMESIZE];
- int r = ReadFrame(replayFile, b, Length, sizeof(b));
- if (r > 0) {
- if (playMode == pmPause)
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- DisplayFrame(b, r);
- }
- playMode = pmStill;
- }
- readIndex = writeIndex = Index;
- Empty(false);
- }
-}
-
-void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (index) {
- if (playMode == pmStill)
- Current = max(readIndex, 0);
- else {
- Current = max(writeIndex, 0);
- if (SnapToIFrame) {
- int i1 = index->GetNextIFrame(Current + 1, false);
- int i2 = index->GetNextIFrame(Current, true);
- Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
- }
- }
- Total = index->Last();
- }
- else
- Current = Total = -1;
-}
-
-bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
-{
- if (FileNumber > 0)
- replayFile = fileName.SetOffset(FileNumber, FileOffset);
- else if (replayFile >= 0 && eof) {
- Close();
- replayFile = fileName.NextFile();
- }
- eof = false;
- return replayFile >= 0;
-}
-
-// --- cTransferBuffer -------------------------------------------------------
-
-class cTransferBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- int fromDevice, toDevice;
- bool gotBufferReserve;
- cRemux remux;
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid);
- virtual ~cTransferBuffer();
- void SetAudioPid(int APid);
- };
-
-cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,remux(VPid, APid, 0, 0, 0)
-{
- dvbApi = DvbApi;
- fromDevice = dvbApi->SetModeRecord();
- toDevice = ToDevice;
- gotBufferReserve = false;
- Start();
-}
-
-cTransferBuffer::~cTransferBuffer()
-{
- Stop();
- dvbApi->SetModeNormal(true);
-}
-
-void cTransferBuffer::SetAudioPid(int APid)
-{
- 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("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int n = 0;
- while (Busy()) {
- if (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 (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cTransferBuffer::Output(void)
-{
- dsyslog("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() && cFile::FileReadyForWriting(toDevice, 100)) {
- int w = write(toDevice, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- else
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-// --- cCuttingBuffer --------------------------------------------------------
-
-class cCuttingBuffer : public cThread {
-private:
- const char *error;
- bool active;
- int fromFile, toFile;
- cFileName *fromFileName, *toFileName;
- cIndexFile *fromIndex, *toIndex;
- cMarks fromMarks, toMarks;
-protected:
- virtual void Action(void);
-public:
- cCuttingBuffer(const char *FromFileName, const char *ToFileName);
- virtual ~cCuttingBuffer();
- const char *Error(void) { return error; }
- };
-
-cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
-{
- error = NULL;
- active = false;
- fromFile = toFile = -1;
- fromFileName = toFileName = NULL;
- fromIndex = toIndex = NULL;
- if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
- 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
- Start();
- }
- else
- esyslog("no editing marks found for %s", FromFileName);
-}
-
-cCuttingBuffer::~cCuttingBuffer()
-{
- active = false;
- Cancel(3);
- delete fromFileName;
- delete toFileName;
- delete fromIndex;
- delete toIndex;
-}
-
-void cCuttingBuffer::Action(void)
-{
- dsyslog("video cutting thread started (pid=%d)", getpid());
-
- cMark *Mark = fromMarks.First();
- if (Mark) {
- fromFile = fromFileName->Open();
- toFile = toFileName->Open();
- active = fromFile >= 0 && toFile >= 0;
- int Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- int FileSize = 0;
- int CurrentFileNumber = 0;
- int LastIFrame = 0;
- toMarks.Add(0);
- toMarks.Save();
- uchar buffer[MAXFRAMESIZE];
- while (active) {
- uchar FileNumber;
- int FileOffset, Length;
- uchar PictureType;
-
- // Make sure there is enough disk space:
-
- AssertFreeDiskSpace();
-
- // 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 = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
- if (Length < 0) {
- error = "ReadFrame";
- break;
- }
- }
- else {
- error = "fromFile";
- break;
- }
- }
- else
- break;
-
- // Write one frame:
-
- if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (!Mark) // edited version shall end before next I-frame
- break;
- if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 1";
- break;
- }
- FileSize = 0;
- }
- LastIFrame = 0;
- }
- if (safe_write(toFile, buffer, Length) < 0) {
- error = "safe_write";
- break;
- }
- if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) {
- error = "toIndex";
- break;
- }
- FileSize += Length;
- if (!LastIFrame)
- LastIFrame = toIndex->Last();
-
- // Check editing marks:
-
- if (Mark && Index >= Mark->position) {
- Mark = fromMarks.Next(Mark);
- toMarks.Add(LastIFrame);
- if (Mark)
- toMarks.Add(toIndex->Last() + 1);
- toMarks.Save();
- if (Mark) {
- Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- CurrentFileNumber = 0; // triggers SetOffset before reading next frame
- if (Setup.SplitEditedFiles) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 2";
- break;
- }
- FileSize = 0;
- }
- }
- // the 'else' case (i.e. 'final end mark reached') is handled above
- // in 'Write one frame', so that the edited version will end right
- // before the next I-frame.
- }
- }
- }
- else
- esyslog("no editing marks found!");
- dsyslog("end video cutting thread");
-}
-
-// --- cVideoCutter ----------------------------------------------------------
-
-char *cVideoCutter::editedVersionName = NULL;
-cCuttingBuffer *cVideoCutter::cuttingBuffer = NULL;
-bool cVideoCutter::error = false;
-bool cVideoCutter::ended = false;
-
-bool cVideoCutter::Start(const char *FileName)
-{
- if (!cuttingBuffer) {
- error = false;
- ended = false;
- cRecording Recording(FileName);
- const char *evn = Recording.PrefixFileName('%');
- if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
- // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
- // remove a possible deleted recording with the same name to avoid symlink mixups:
- char *s = strdup(evn);
- char *e = strrchr(s, '.');
- if (e) {
- if (strcmp(e, ".rec") == 0) {
- strcpy(e, ".del");
- RemoveVideoFile(s);
- }
- }
- delete s;
- // XXX
- editedVersionName = strdup(evn);
- Recording.WriteSummary();
- cuttingBuffer = new cCuttingBuffer(FileName, editedVersionName);
- return true;
- }
- }
- return false;
-}
-
-void cVideoCutter::Stop(void)
-{
- bool Interrupted = cuttingBuffer && cuttingBuffer->Active();
- const char *Error = cuttingBuffer ? cuttingBuffer->Error() : NULL;
- delete cuttingBuffer;
- cuttingBuffer = NULL;
- if ((Interrupted || Error) && editedVersionName) {
- if (Interrupted)
- isyslog("editing process has been interrupted");
- if (Error)
- esyslog("ERROR: '%s' during editing process", Error);
- RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
- }
-}
-
-bool cVideoCutter::Active(void)
-{
- if (cuttingBuffer) {
- if (cuttingBuffer->Active())
- return true;
- error = cuttingBuffer->Error();
- Stop();
- if (!error)
- cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName);
- delete editedVersionName;
- editedVersionName = NULL;
- ended = true;
- }
- return false;
-}
-
-bool cVideoCutter::Error(void)
-{
- bool result = error;
- error = false;
- return result;
-}
-
-bool cVideoCutter::Ended(void)
-{
- bool result = ended;
- ended = false;
- return result;
-}
-
-// --- 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(int n)
-{
- frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
- vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0;
- siProcessor = NULL;
- recordBuffer = NULL;
- replayBuffer = NULL;
- transferBuffer = NULL;
- transferringFromDvbApi = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- cardIndex = n;
-
- // Devices that are only present on DVB-C or DVB-S cards:
-
- fd_frontend = OstOpen(DEV_OST_FRONTEND, 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 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_frontend >= 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));
- FrontendInfo feinfo;
- CHECK(ioctl(fd_frontend, FE_GET_INFO, &feinfo));
- frontendType = feinfo.type;
- }
- else
- esyslog("ERROR: can't open video device %d", n);
- cols = rows = 0;
-
-#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
- initscr();
- keypad(stdscr, true);
- nonl();
- cbreak();
- noecho();
- timeout(10);
-#endif
-#if defined(DEBUG_OSD)
- memset(&colorPairs, 0, sizeof(colorPairs));
- start_color();
- leaveok(stdscr, true);
- window = NULL;
-#else
- osd = NULL;
-#endif
- currentChannel = 1;
- mute = false;
- volume = Setup.CurrentVolume;
-}
-
-cDvbApi::~cDvbApi()
-{
- delete siProcessor;
- Close();
- StopReplay();
- StopRecord();
- StopTransfer();
- // 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
-}
-
-void cDvbApi::SetUseDvbApi(int n)
-{
- if (n < MAXDVBAPI)
- useDvbApi |= (1 << n);
-}
-
-bool cDvbApi::SetPrimaryDvbApi(int n)
-{
- n--;
- if (0 <= n && n < NumDvbApis && dvbApi[n]) {
- isyslog("setting primary DVB to %d", n + 1);
- PrimaryDvbApi = dvbApi[n];
- return true;
- }
- esyslog("invalid DVB interface: %d", n + 1);
- return false;
-}
-
-int cDvbApi::CanShift(int Ca, int Priority, int UsedCards)
-{
- // Test whether a recording on this DVB device can be shifted to another one
- // in order to perform a new recording with the given Ca and Priority on this device:
- int ShiftLevel = -1; // default means this device can't be shifted
- if (UsedCards & (1 << CardIndex()) != 0)
- return ShiftLevel; // otherwise we would get into a loop
- if (Recording()) {
- if (ProvidesCa(Ca) // this device provides the requested Ca
- && (Ca != this->Ca() // the requested Ca is different from the one currently used...
- || Priority > this->Priority())) { // ...or the request comes from a higher priority
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- UsedCards |= (1 << CardIndex());
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job
- if (dvbApi[i] != this) { // it is not _this_ device
- int sl = dvbApi[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority!
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i];
- ShiftLevel = sl;
- }
- }
- }
- }
- if (ShiftLevel >= 0)
- ShiftLevel++; // adds the device's own shift
- }
- }
- else if (Priority > this->Priority())
- ShiftLevel = 0; // no shifting necessary, this device can do the job
- return ShiftLevel;
-}
-
-cDvbApi *cDvbApi::GetDvbApi(int Ca, int Priority)
-{
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- // Check which devices provide Ca:
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job
- if (Priority > dvbApi[i]->Priority() // Priority is high enough to use this device
- && (!d // we don't have a device yet, or...
- || dvbApi[i]->Priority() < d->Priority() // ...this one has an even lower Priority
- || (dvbApi[i]->Priority() == d->Priority() // ...same Priority...
- && Provides[i] < Provides[d->CardIndex()]))) // ...but this one provides fewer Ca values
- d = dvbApi[i];
- }
- }
- if (!d && Ca > MAXDVBAPI) {
- // We didn't find one the easy way, so now we have to try harder:
- int ShiftLevel = -1;
- for (int i = 0; i < NumDvbApis; i++) {
- if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above
- int sl = dvbApi[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i]; // found one that can be shifted with the fewest number of subsequent shifts
- ShiftLevel = sl;
- }
- }
- }
- }
- return d;
-}
-
-void cDvbApi::SetCaCaps(void)
-{
- for (int d = 0; d < NumDvbApis; d++) {
- for (int i = 0; i < MAXCACAPS; i++)
- dvbApi[d]->caCaps[i] = Setup.CaCaps[dvbApi[d]->CardIndex()][i];
- }
-}
-
-int cDvbApi::ProvidesCa(int Ca)
-{
- if (Ca == CardIndex() + 1)
- return 1; // exactly _this_ card was requested
- if (Ca && Ca <= MAXDVBAPI)
- return 0; // a specific card was requested, but not _this_ one
- int result = Ca ? 0 : 1; // by default every card can provide FTA
- int others = Ca ? 1 : 0;
- for (int i = 0; i < MAXCACAPS; i++) {
- if (caCaps[i]) {
- if (caCaps[i] == Ca)
- result = 1;
- else
- others++;
- }
- }
- return result ? result + others : 0;
-}
-
-bool cDvbApi::Probe(const char *FileName)
-{
- if (access(FileName, F_OK) == 0) {
- dsyslog("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) {
- if (Probe(OstName(DEV_OST_FRONTEND, i)))
- dvbApi[NumDvbApis++] = new cDvbApi(i);
- else
- break;
- }
- }
- PrimaryDvbApi = dvbApi[0];
- if (NumDvbApis > 0)
- isyslog("found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
- else
- esyslog("ERROR: no video device found, giving up!");
- SetCaCaps();
- return NumDvbApis > 0;
-}
-
-void cDvbApi::Cleanup(void)
-{
- for (int i = 0; i < NumDvbApis; i++) {
- delete dvbApi[i];
- dvbApi[i] = NULL;
- }
- PrimaryDvbApi = NULL;
-}
-
-bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
-{
- int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true);
- if (videoDev >= 0) {
- int result = 0;
- struct video_mbuf mbuf;
- result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
- if (result == 0) {
- int msize = mbuf.size;
- unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
- if (mem && mem != (unsigned char *)-1) {
- // set up the size and RGB
- struct video_capability vc;
- result |= ioctl(videoDev, VIDIOCGCAP, &vc);
- struct video_mmap vm;
- vm.frame = 0;
- if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
- (SizeY > 0) && (SizeY <= vc.maxheight)) {
- vm.width = SizeX;
- vm.height = SizeY;
- }
- else {
- vm.width = vc.maxwidth;
- vm.height = vc.maxheight;
- }
- vm.format = VIDEO_PALETTE_RGB24;
- result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
- result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
- // make RGB out of BGR:
- int memsize = vm.width * vm.height;
- unsigned char *mem1 = mem;
- for (int i = 0; i < memsize; i++) {
- unsigned char tmp = mem1[2];
- mem1[2] = mem1[0];
- mem1[0] = tmp;
- mem1 += 3;
- }
-
- if (Quality < 0)
- Quality = 255; //XXX is this 'best'???
-
- isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
- FILE *f = fopen(FileName, "wb");
- if (f) {
- if (Jpeg) {
- // write JPEG file:
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- jpeg_stdio_dest(&cinfo, f);
- cinfo.image_width = vm.width;
- cinfo.image_height = vm.height;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_RGB;
-
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, Quality, true);
- jpeg_start_compress(&cinfo, true);
-
- int rs = vm.width * 3;
- JSAMPROW rp[vm.height];
- for (int k = 0; k < vm.height; k++)
- rp[k] = &mem[rs * k];
- jpeg_write_scanlines(&cinfo, rp, vm.height);
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
- }
- else {
- // write PNM file:
- if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
- fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- }
- fclose(f);
- }
- else {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- munmap(mem, msize);
- }
- else
- result |= 1;
- }
- close(videoDev);
- return result == 0;
- }
- return false;
-}
-
-#ifdef DEBUG_OSD
-void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg)
-{
- int color = (colorBg << 16) | colorFg | 0x80000000;
- for (int i = 0; i < MaxColorPairs; i++) {
- if (!colorPairs[i]) {
- colorPairs[i] = color;
- init_pair(i + 1, colorFg, colorBg);
- wattrset(window, COLOR_PAIR(i + 1));
- break;
- }
- else if (color == colorPairs[i]) {
- wattrset(window, COLOR_PAIR(i + 1));
- break;
- }
- }
-}
-#endif
-
-void cDvbApi::Open(int w, int h)
-{
- int d = (h < 0) ? Setup.OSDheight + h : 0;
- h = abs(h);
- cols = w;
- rows = h;
-#ifdef DEBUG_OSD
- window = subwin(stdscr, h, w, d, (Setup.OSDwidth - w) / 2);
- 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))
- //XXX
- SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray
- SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255);
- SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255);
- SETCOLOR(clrGreen, 0x24, 0xFC, 0x24, 255);
- SETCOLOR(clrYellow, 0xFC, 0xC0, 0x24, 255);
- SETCOLOR(clrBlue, 0x00, 0x00, 0xFC, 255);
- 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 - w + 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);
- osd->AddColor(clrBackground);
- osd->AddColor(clrCyan);
- osd->AddColor(clrWhite);
- osd->AddColor(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)
-{
-#ifdef DEBUG_OSD
- if (window) {
- delwin(window);
- window = 0;
- }
-#else
- delete osd;
- osd = NULL;
-#endif
-}
-
-void cDvbApi::Clear(void)
-{
-#ifdef DEBUG_OSD
- SetColor(clrBackground, clrBackground);
- Fill(0, 0, cols, rows, clrBackground);
-#else
- osd->Clear();
-#endif
-}
-
-void cDvbApi::Fill(int x, int y, int w, int h, eDvbColor color)
-{
- if (x < 0) x = cols + x;
- if (y < 0) y = rows + y;
-#ifdef DEBUG_OSD
- SetColor(color, color);
- for (int r = 0; r < h; r++) {
- wmove(window, y + r, x); // ncurses wants 'y' before 'x'!
- whline(window, ' ', w);
- }
- wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work
-#else
- osd->Fill(x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1, color);
-#endif
-}
-
-void cDvbApi::SetBitmap(int x, int y, const cBitmap &Bitmap)
-{
-#ifndef DEBUG_OSD
- osd->SetBitmap(x, y, Bitmap);
-#endif
-}
-
-void cDvbApi::ClrEol(int x, int y, eDvbColor color)
-{
- Fill(x, y, cols - x, 1, color);
-}
-
-int cDvbApi::CellWidth(void)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return charWidth;
-#endif
-}
-
-int cDvbApi::LineHeight(void)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return lineHeight;
-#endif
-}
-
-int cDvbApi::Width(unsigned char c)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return osd->Width(c);
-#endif
-}
-
-int cDvbApi::WidthInCells(const char *s)
-{
-#ifdef DEBUG_OSD
- return strlen(s);
-#else
- return (osd->Width(s) + charWidth - 1) / charWidth;
-#endif
-}
-
-eDvbFont cDvbApi::SetFont(eDvbFont Font)
-{
-#ifdef DEBUG_OSD
- return Font;
-#else
- return osd->SetFont(Font);
-#endif
-}
-
-void cDvbApi::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg)
-{
- if (x < 0) x = cols + x;
- if (y < 0) y = rows + y;
-#ifdef DEBUG_OSD
- SetColor(colorFg, colorBg);
- wmove(window, y, x); // ncurses wants 'y' before 'x'!
- waddnstr(window, s, cols - x);
-#else
- osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg);
-#endif
-}
-
-void cDvbApi::Flush(void)
-{
-#ifdef DEBUG_OSD
- refresh();
-#else
- if (osd)
- osd->Flush();
-#endif
-}
-
-int cDvbApi::Priority(void)
-{
- return (this == PrimaryDvbApi && !Recording()) ? Setup.PrimaryLimit - 1 : priority;
-}
-
-int cDvbApi::SetModeRecord(void)
-{
- // Sets up the DVB device for recording
-
- SetPids(true);
- if (fd_dvr >= 0)
- close(fd_dvr);
- fd_dvr = OstOpen(DEV_OST_DVR, CardIndex(), 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 >= 0)
- 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));
- if (Pid != 0x1FFF) {
- 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) {
- 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) && (!Setup.RecordDolbyDigital ||
- SetDpid1(ForRecording ? dPid1 : 0, DMX_OUT_TS_TAP) &&
- SetDpid2(ForRecording ? dPid2 : 0, DMX_OUT_TS_TAP));
-}
-
-eSetChannelResult cDvbApi::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr)
-{
- 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));
- }
-
- // Stop setting system time:
-
- if (siProcessor)
- siProcessor->SetCurrentTransponder(0);
-
- // 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 && !ProvidesCa(Ca));
-
- 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);
-
- FrontendParameters Frontend;
-
- switch (frontendType) {
- case FE_QPSK: { // DVB-S
-
- // Frequency offsets:
-
- unsigned int freq = Frequency;
- int tone = SEC_TONE_OFF;
-
- if (freq < (unsigned int)Setup.LnbSLOF) {
- freq -= Setup.LnbFrequLo;
- tone = SEC_TONE_OFF;
- }
- else {
- freq -= Setup.LnbFrequHi;
- tone = SEC_TONE_ON;
- }
-
- Frontend.Frequency = freq * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qpsk.SymbolRate = Srate * 1000UL;
- Frontend.u.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));
- }
- break;
- case FE_QAM: { // DVB-C
-
- // Frequency and symbol rate:
-
- Frontend.Frequency = Frequency * 1000000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qam.SymbolRate = Srate * 1000UL;
- Frontend.u.qam.FEC_inner = FEC_AUTO;
- Frontend.u.qam.QAM = QAM_64;
- }
- break;
- case FE_OFDM: { // DVB-T
-
- // Frequency and OFDM paramaters:
-
- Frontend.Frequency = Frequency * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
- Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
- Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
- Frontend.u.ofdm.Constellation=QAM_64;
- Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
- Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
- Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
- }
- break;
- default:
- esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
- return scrFailed;
- }
-
- // Tuning:
-
- CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
-
- // Wait for channel sync:
-
- if (cFile::FileReady(fd_frontend, 5000)) {
- FrontendEvent event;
- int res = ioctl(fd_frontend, FE_GET_EVENT, &event);
- if (res >= 0) {
- if (event.type != FE_COMPLETION_EV) {
- esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1);
- if (this == PrimaryDvbApi)
- cThread::RaisePanic();
- return scrFailed;
- }
- }
- else
- esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1);
- }
- else
- esyslog("ERROR: timeout while tuning");
-
- // PID settings:
-
- if (!SetPids(false)) {
- esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber);
- return scrFailed;
- }
- 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);
-
- eSetChannelResult Result = scrOk;
-
- // 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 && !CaDvbApi->Recording()) {
- if ((Result = CaDvbApi->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid1, Apid2, Dpid1, Dpid2, Tpid, Ca, Pnr)) == scrOk) {
- SetModeReplay();
- transferringFromDvbApi = CaDvbApi->StartTransfer(fd_video);
- }
- }
- else
- Result = scrNoTransfer;
- }
-
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
- }
-
- // Start setting system time:
-
- if (Result == scrOk && siProcessor)
- siProcessor->SetCurrentTransponder(Frequency);
-
- return Result;
-}
-
-bool cDvbApi::Transferring(void)
-{
- return transferBuffer;
-}
-
-cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev)
-{
- StopTransfer();
- transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid1);
- return this;
-}
-
-void cDvbApi::StopTransfer(void)
-{
- if (transferBuffer) {
- delete transferBuffer;
- transferBuffer = NULL;
- }
- if (transferringFromDvbApi) {
- transferringFromDvbApi->StopTransfer();
- transferringFromDvbApi = NULL;
- }
-}
-
-int cDvbApi::SecondsToFrames(int Seconds)
-{
- return Seconds * FRAMESPERSEC;
-}
-
-bool cDvbApi::Recording(void)
-{
- if (recordBuffer && !recordBuffer->Active())
- StopRecord();
- return recordBuffer != NULL;
-}
-
-bool cDvbApi::Replaying(void)
-{
- if (replayBuffer && !replayBuffer->Active())
- StopReplay();
- return replayBuffer != NULL;
-}
-
-bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
-{
- if (Recording()) {
- esyslog("ERROR: StartRecord() called while recording - ignored!");
- return false;
- }
-
- StopTransfer();
-
- StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartRecord: file name is (null)");
- return false;
- }
- isyslog("record %s", FileName);
-
- // Create directories if necessary:
-
- if (!MakeDirs(FileName, true))
- return false;
-
- // Make sure the disk is up and running:
-
- SpinUpDisk(FileName);
-
- // Create recording buffer:
-
- recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2, dPid1, dPid2);
-
- if (recordBuffer) {
- ca = Ca;
- priority = Priority;
- return true;
- }
- else
- esyslog("ERROR: can't allocate recording buffer");
-
- return false;
-}
-
-void cDvbApi::StopRecord(void)
-{
- if (recordBuffer) {
- delete recordBuffer;
- recordBuffer = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- }
-}
-
-bool cDvbApi::StartReplay(const char *FileName)
-{
- if (Recording()) {
- esyslog("ERROR: StartReplay() called while recording - ignored!");
- return false;
- }
- StopTransfer();
- StopReplay();
- if (fd_video >= 0 && fd_audio >= 0) {
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartReplay: file name is (null)");
- return false;
- }
- isyslog("replay %s", FileName);
-
- // Create replay buffer:
-
- replayBuffer = new cReplayBuffer(this, fd_video, fd_audio, FileName);
- if (replayBuffer)
- return true;
- else
- esyslog("ERROR: can't allocate replaying buffer");
- }
- return false;
-}
-
-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);
- usleep(100000); // allow driver to sync in case a new replay will start immediately
- }
- }
- }
-}
-
-void cDvbApi::Pause(void)
-{
- if (replayBuffer)
- replayBuffer->Pause();
-}
-
-void cDvbApi::Play(void)
-{
- if (replayBuffer)
- replayBuffer->Play();
-}
-
-void cDvbApi::Forward(void)
-{
- if (replayBuffer)
- replayBuffer->Forward();
-}
-
-void cDvbApi::Backward(void)
-{
- if (replayBuffer)
- replayBuffer->Backward();
-}
-
-void cDvbApi::SkipSeconds(int Seconds)
-{
- if (replayBuffer)
- replayBuffer->SkipSeconds(Seconds);
-}
-
-int cDvbApi::SkipFrames(int Frames)
-{
- if (replayBuffer)
- return replayBuffer->SkipFrames(Frames);
- return -1;
-}
-
-bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (replayBuffer) {
- replayBuffer->GetIndex(Current, Total, SnapToIFrame);
- return true;
- }
- return false;
-}
-
-bool cDvbApi::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- return replayBuffer && replayBuffer->GetReplayMode(Play, Forward, Speed);
-}
-
-void cDvbApi::Goto(int Position, bool Still)
-{
- if (replayBuffer)
- 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;
-}
-
-bool cDvbApi::ToggleMute(void)
-{
- int OldVolume = volume;
- mute = !mute;
- SetVolume(0, mute);
- volume = OldVolume;
- return mute;
-}
-
-void cDvbApi::SetVolume(int Volume, bool Absolute)
-{
- if (fd_audio >= 0) {
- volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
- audioMixer_t am;
- am.volume_left = am.volume_right = volume;
- CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
- if (volume > 0)
- mute = false;
- }
-}
-
-void cDvbApi::SetAudioCommand(const char *Command)
-{
- delete audioCommand;
- audioCommand = strdup(Command);
-}
-
-// --- cEITScanner -----------------------------------------------------------
-
-cEITScanner::cEITScanner(void)
-{
- lastScan = lastActivity = time(NULL);
- currentChannel = 0;
- lastChannel = 0;
- numTransponders = 0;
- transponders = NULL;
-}
-
-cEITScanner::~cEITScanner()
-{
- delete transponders;
-}
-
-bool cEITScanner::TransponderScanned(cChannel *Channel)
-{
- for (int i = 0; i < numTransponders; i++) {
- if (transponders[i] == Channel->frequency)
- return true;
- }
- transponders = (int *)realloc(transponders, ++numTransponders * sizeof(int));
- transponders[numTransponders - 1] = Channel->frequency;
- return false;
-}
-
-void cEITScanner::Activity(void)
-{
- if (currentChannel) {
- Channels.SwitchTo(currentChannel);
- currentChannel = 0;
- }
- lastActivity = time(NULL);
-}
-
-void cEITScanner::Process(void)
-{
- if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
- time_t now = time(NULL);
- if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
- for (int i = 0; i < MAXDVBAPI; i++) {
- cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, MAXPRIORITY + 1);
- if (DvbApi) {
- if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
- if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) {
- int oldCh = lastChannel;
- int ch = oldCh + 1;
- while (ch != oldCh) {
- if (ch > Channels.MaxNumber()) {
- ch = 1;
- numTransponders = 0;
- }
- cChannel *Channel = Channels.GetByNumber(ch);
- if (Channel) {
- if (Channel->ca <= MAXDVBAPI && !DvbApi->ProvidesCa(Channel->ca))
- break; // the channel says it explicitly needs a different card
- if (Channel->pnr && !TransponderScanned(Channel)) {
- if (DvbApi == cDvbApi::PrimaryDvbApi && !currentChannel)
- currentChannel = DvbApi->Channel();
- Channel->Switch(DvbApi, false);
- lastChannel = ch;
- break;
- }
- }
- ch++;
- }
- }
- }
- }
- }
- lastScan = time(NULL);
- }
- }
-}
-