summaryrefslogtreecommitdiff
path: root/recording.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 /recording.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 'recording.c')
-rw-r--r--recording.c350
1 files changed, 349 insertions, 1 deletions
diff --git a/recording.c b/recording.c
index c65aaaf..7bc896c 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.c 1.62 2002/05/13 16:31:21 kls Exp $
+ * $Id: recording.c 1.63 2002/06/16 11:29:27 kls Exp $
*/
#include "recording.h"
@@ -16,6 +16,7 @@
#include <unistd.h>
#include "i18n.h"
#include "interface.h"
+#include "remux.h" //XXX+ I_FRAME
#include "tools.h"
#include "videodir.h"
@@ -732,3 +733,350 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi
delete cmd;
}
}
+
+// --- XXX+
+
+//XXX+ somewhere else???
+// --- cIndexFile ------------------------------------------------------------
+
+#define INDEXFILESUFFIX "/index.vdr"
+
+// The maximum time to wait before giving up while catching up on an index file:
+#define MAXINDEXCATCHUP 2 // seconds
+
+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 -------------------------------------------------------------
+
+#include <errno.h>
+#include <unistd.h>
+#include "videodir.h"
+
+#define MAXFILESPERRECORDING 255
+#define RECORDFILESUFFIX "/%03d.vdr"
+#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
+
+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);
+}
+
+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;
+}
+
+int SecondsToFrames(int Seconds)
+{
+ return Seconds * FRAMESPERSEC;
+}