86461d051d2e64cb3c95d1781e1a96038e872daa
[enigma2.git] / lib / service / servicedvbrecord.cpp
1 #include <lib/service/servicedvbrecord.h>
2 #include <lib/base/eerror.h>
3 #include <lib/dvb/epgcache.h>
4 #include <fcntl.h>
5
6 DEFINE_REF(eDVBServiceRecord);
7
8 eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref): m_ref(ref)
9 {
10         CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent);
11         m_state = stateIdle;
12         m_want_record = 0;
13         m_tuned = 0;
14         m_target_fd = -1;
15         m_error = 0;
16         m_streaming = 0;
17 }
18
19 void eDVBServiceRecord::serviceEvent(int event)
20 {
21         eDebug("RECORD service event %d", event);
22         switch (event)
23         {
24         case eDVBServicePMTHandler::eventTuned:
25         {
26                 eDebug("tuned..");
27                 m_tuned = 1;
28                 if (m_state == stateRecording && m_want_record)
29                         doRecord();
30                 m_event((iRecordableService*)this, evTunedIn);
31                 break;
32         }
33         case eDVBServicePMTHandler::eventTuneFailed:
34         {
35                 eDebug("record failed to tune");
36                 m_event((iRecordableService*)this, evTuneFailed);
37                 break;
38         }
39         case eDVBServicePMTHandler::eventNewProgramInfo:
40         {
41                 if (m_state == stateIdle)
42                         doPrepare();
43                 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
44                         doRecord();
45                 m_event((iRecordableService*)this, evNewProgramInfo);
46                 break;
47         }
48         case eDVBServicePMTHandler::eventMisconfiguration:
49                 m_error = errMisconfiguration;
50                 m_event((iRecordableService*)this, evTuneFailed);
51                 break;
52         case eDVBServicePMTHandler::eventNoResources:
53                 m_error = errNoResources;
54                 m_event((iRecordableService*)this, evTuneFailed);
55                 break;
56         }
57 }
58
59 RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id)
60 {
61         m_filename = filename;
62         m_streaming = 0;
63         
64         if (m_state == stateIdle)
65         {
66                 int ret = doPrepare();
67                 if (!ret)
68                 {
69                         eEPGCache::getInstance()->Lock();
70                         const eit_event_struct *event = 0;
71                         eServiceReferenceDVB ref = m_ref.getParentServiceReference();
72                         if (!ref.valid())
73                                 ref = m_ref;
74                         if ( eit_event_id != -1 )
75                         {
76                                 eDebug("query epg event id %d", eit_event_id);
77                                 eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
78                         }
79                         if ( !event && (begTime != -1 && endTime != -1) )
80                         {
81                                 time_t queryTime = begTime + ((endTime-begTime)/2);
82                                 tm beg, end, query;
83                                 localtime_r(&begTime, &beg);
84                                 localtime_r(&endTime, &end);
85                                 localtime_r(&queryTime, &query);
86                                 eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
87                                         beg.tm_hour, beg.tm_min, beg.tm_sec,
88                                         end.tm_hour, end.tm_min, end.tm_sec,
89                                         query.tm_hour, query.tm_min, query.tm_sec);
90                                 eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
91                         }
92                         if ( event )
93                         {
94                                 eDebug("found event.. store to disc");
95                                 std::string fname = filename;
96                                 fname.erase(fname.length()-2, 2);
97                                 fname+="eit";
98                                 int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
99                                 if (fd>-1)
100                                 {
101                                         int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
102                                         int wr = ::write( fd, (unsigned char*)event, evLen );
103                                         if ( wr != evLen )
104                                                 eDebug("eit write error (%m)");
105                                         ::close(fd);
106                                 }
107                         }
108                         eEPGCache::getInstance()->Unlock();
109                 }
110                 return ret;
111         }
112         else
113                 return -1;
114 }
115
116 RESULT eDVBServiceRecord::prepareStreaming()
117 {
118         m_filename = "";
119         m_streaming = 1;
120         if (m_state == stateIdle)
121                 return doPrepare();
122         return -1;
123 }
124
125 RESULT eDVBServiceRecord::start()
126 {
127         m_want_record = 1;
128                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
129         m_event((iRecordableService*)this, evStart);
130         return doRecord();
131 }
132
133
134 RESULT eDVBServiceRecord::stop()
135 {
136         eDebug("stop recording!");
137         if (m_state == stateRecording)
138         {
139                 if (m_record)
140                         m_record->stop();
141                 if (m_target_fd >= 0)
142                 {
143                         ::close(m_target_fd);
144                         m_target_fd = -1;
145                 }
146                 m_state = statePrepared;
147         } else
148                 eDebug("(was not recording)");
149         if (m_state == statePrepared)
150         {
151                 m_record = 0;
152                 m_state = stateIdle;
153         }
154         m_event((iRecordableService*)this, evRecordStopped);
155         return 0;
156 }
157
158
159 int eDVBServiceRecord::doPrepare()
160 {
161                 /* allocate a ts recorder if we don't already have one. */
162         if (m_state == stateIdle)
163         {
164                 m_pids_active.clear();
165                 m_state = statePrepared;
166                 return m_service_handler.tune(m_ref, 0);
167         }
168         return 0;
169 }
170
171 int eDVBServiceRecord::doRecord()
172 {
173         int err = doPrepare();
174         if (err)
175         {
176                 m_error = errTuneFailed;
177                 m_event((iRecordableService*)this, evRecordFailed);
178                 return err;
179         }
180         
181         if (!m_tuned)
182                 return 0; /* try it again when we are tuned in */
183         
184         if (!m_record && m_tuned && !m_streaming)
185         {
186                 eDebug("Recording to %s...", m_filename.c_str());
187                 ::remove(m_filename.c_str());
188                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
189                 if (fd == -1)
190                 {
191                         eDebug("eDVBServiceRecord - can't open recording file!");
192                         m_error = errOpenRecordFile;
193                         m_event((iRecordableService*)this, evRecordFailed);
194                         return errOpenRecordFile;
195                 }
196
197                         /* turn off kernel caching strategies */
198                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
199
200                 ePtr<iDVBDemux> demux;
201                 if (m_service_handler.getDataDemux(demux))
202                 {
203                         eDebug("eDVBServiceRecord - NO DEMUX available!");
204                         m_error = errNoDemuxAvailable;
205                         m_event((iRecordableService*)this, evRecordFailed);
206                         return errNoDemuxAvailable;
207                 }
208                 demux->createTSRecorder(m_record);
209                 if (!m_record)
210                 {
211                         eDebug("eDVBServiceRecord - no ts recorder available.");
212                         m_error = errNoTsRecorderAvailable;
213                         m_event((iRecordableService*)this, evRecordFailed);
214                         return errNoTsRecorderAvailable;
215                 }
216                 m_record->setTargetFD(fd);
217                 m_record->setTargetFilename(m_filename.c_str());
218                 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
219
220                 m_target_fd = fd;
221         }
222         
223         if (m_streaming)
224         {
225                 m_state = stateRecording;
226                 eDebug("start streaming...");
227         } else
228         {
229                 eDebug("start recording...");
230
231                 eDVBServicePMTHandler::program program;
232                 if (m_service_handler.getProgramInfo(program))
233                         eDebug("getting program info failed.");
234                 else
235                 {
236                         std::set<int> pids_to_record;
237
238                         pids_to_record.insert(0); // PAT
239
240                         if (program.pmtPid != -1)
241                                 pids_to_record.insert(program.pmtPid); // PMT
242
243                         int timing_pid = -1;
244
245                         eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
246                         if (!program.videoStreams.empty())
247                         {
248                                 eDebugNoNewLine(" (");
249                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
250                                         i(program.videoStreams.begin()); 
251                                         i != program.videoStreams.end(); ++i)
252                                 {
253                                         pids_to_record.insert(i->pid);
254                                         
255                                         if (timing_pid == -1)
256                                                 timing_pid = i->pid;
257                                         
258                                         if (i != program.videoStreams.begin())
259                                                         eDebugNoNewLine(", ");
260                                         eDebugNoNewLine("%04x", i->pid);
261                                 }
262                                 eDebugNoNewLine(")");
263                         }
264                         eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
265                         if (!program.audioStreams.empty())
266                         {
267                                 eDebugNoNewLine(" (");
268                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
269                                         i(program.audioStreams.begin()); 
270                                         i != program.audioStreams.end(); ++i)
271                                 {
272                                         pids_to_record.insert(i->pid);
273         
274                                         if (timing_pid == -1)
275                                                 timing_pid = i->pid;
276                                 
277                                         if (i != program.audioStreams.begin())
278                                                 eDebugNoNewLine(", ");
279                                         eDebugNoNewLine("%04x", i->pid);
280                                 }
281                                 eDebugNoNewLine(")");
282                         }
283                         if (!program.subtitleStreams.empty())
284                         {
285                                 eDebugNoNewLine(" (");
286                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
287                                         i(program.subtitleStreams.begin());
288                                         i != program.subtitleStreams.end(); ++i)
289                                 {
290                                         pids_to_record.insert(i->pid);
291         
292                                         if (i != program.subtitleStreams.begin())
293                                                 eDebugNoNewLine(", ");
294                                         eDebugNoNewLine("%04x", i->pid);
295                                 }
296                                 eDebugNoNewLine(")");
297                         }
298                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
299                         if (program.pcrPid != 0x1fff)
300                                 pids_to_record.insert(program.pcrPid);
301                         eDebug(", and the text pid is %04x", program.textPid);
302                         if (program.textPid != -1)
303                                 pids_to_record.insert(program.textPid); // Videotext
304
305                                 /* find out which pids are NEW and which pids are obsolete.. */
306                         std::set<int> new_pids, obsolete_pids;
307
308                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
309                                         m_pids_active.begin(), m_pids_active.end(),
310                                         std::inserter(new_pids, new_pids.begin()));
311
312                         std::set_difference(
313                                         m_pids_active.begin(), m_pids_active.end(),
314                                         pids_to_record.begin(), pids_to_record.end(), 
315                                         std::inserter(new_pids, new_pids.begin())
316                                         );
317                         
318                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
319                         {
320                                 eDebug("ADD PID: %04x", *i);
321                                 m_record->addPID(*i);
322                         }
323
324                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
325                         {
326                                 eDebug("REMOVED PID: %04x", *i);
327                                 m_record->removePID(*i);
328                         }
329
330                         if (timing_pid != -1)
331                                 m_record->setTimingPID(timing_pid);
332
333                         m_pids_active = pids_to_record;
334
335                         if (m_state != stateRecording)
336                         {
337                                 m_record->start();
338                                 m_state = stateRecording;
339                         }
340                 }
341         }
342         m_error = 0;
343         m_event((iRecordableService*)this, evRecordRunning);
344         return 0;
345 }
346
347 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
348 {
349         ptr = this;
350         return 0;
351 }
352
353 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
354 {
355         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
356         return 0;
357 }
358
359 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
360 {
361         ptr = this;
362         return 0;
363 }
364
365 PyObject *eDVBServiceRecord::getStreamingData()
366 {
367         eDVBServicePMTHandler::program program;
368         if (!m_tuned || m_service_handler.getProgramInfo(program))
369         {
370                 Py_INCREF(Py_None);
371                 return Py_None;
372         }
373
374         PyObject *r = program.createPythonObject();
375         ePtr<iDVBDemux> demux;
376         if (!m_service_handler.getDataDemux(demux))
377         {
378                 uint8_t demux_id;
379                 demux->getCADemuxID(demux_id);
380                 
381                 PyDict_SetItemString(r, "demux", PyInt_FromLong(demux_id));
382         }
383
384         return r;
385 }
386
387 void eDVBServiceRecord::recordEvent(int event)
388 {
389         switch (event)
390         {
391         case iDVBTSRecorder::eventWriteError:
392                 eWarning("[eDVBServiceRecord] record write error");
393                 stop();
394                 m_event((iRecordableService*)this, evRecordWriteError);
395                 return;
396         default:
397                 eDebug("unhandled record event %d", event);
398         }
399 }