summaryrefslogtreecommitdiff
path: root/recorder.c
blob: 3a0941bb8b3ad321971f25ed14d44097e7ed8bf0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * recorder.h: The actual DVB recorder
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: recorder.c 1.1 2002/06/16 10:03:25 kls Exp $
 */

#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include "recorder.h"

// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE  MEGABYTE(1)

#define MINFREEDISKSPACE    (512) // MB
#define DISKCHECKINTERVAL   100 // seconds

cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
:cReceiver(Ca, Priority, 5, VPid, APid1, APid2, DPid1, DPid2)
{
  ringBuffer = NULL;
  remux = NULL;
  fileName = NULL;
  index = NULL;
  pictureType = NO_PICTURE;
  fileSize = 0;
  active = false;
  lastDiskSpaceCheck = time(NULL);
  isyslog("record %s", FileName);

  // Create directories if necessary:

  if (!MakeDirs(FileName, true))
     return;

  // Make sure the disk is up and running:

  SpinUpDisk(FileName);

  ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true);
  remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true);
  fileName = new cFileName(FileName, true);
  recordFile = fileName->Open();
  if (recordFile < 0)
     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
}

cRecorder::~cRecorder()
{
  Detach();
  delete index;
  delete fileName;
  delete remux;
  delete ringBuffer;
}

void cRecorder::Activate(bool On)
{
  if (On) {
     if (recordFile >= 0)
        Start();
     }
  else if (active) {
     active = false;
     Cancel(3);
     }
}

bool cRecorder::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 cRecorder::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 cRecorder::Receive(uchar *Data, int Length)
{
  int p = ringBuffer->Put(Data, Length);
  if (p != Length && active)
     esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p);
}

void cRecorder::Action(void)
{
  dsyslog("recording thread started (pid=%d)", getpid());

  uchar b[MINVIDEODATA];
  int r = 0;
  active = true;
  while (active) {
        int g = ringBuffer->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) {
              //XXX+ active??? see old version (Busy)
              if (!active && 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());
                    break;
                    }
                 fileSize += Result;
                 }
              else
                 break;
              }
           if (Count > 0) {
              r -= Count;
              memmove(b, b + Count, r);
              }
           }
        else
           usleep(1); // this keeps the CPU load low
        }

  dsyslog("recording thread ended (pid=%d)", getpid());
}