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