work on service events
[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 }
115
116 RESULT eDVBServiceRecord::start()
117 {
118         m_want_record = 1;
119                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
120         m_event((iRecordableService*)this, evStart);
121         return doRecord();
122 }
123
124
125 RESULT eDVBServiceRecord::stop()
126 {
127         eDebug("stop recording!!");
128         if (m_state == stateRecording)
129         {
130                 if (m_record)
131                         m_record->stop();
132                 if (m_target_fd >= 0)
133                 {
134                         ::close(m_target_fd);
135                         m_target_fd = -1;
136                 }
137                 m_state = statePrepared;
138         }
139         if (m_state == statePrepared)
140         {
141                 m_record = 0;
142                 m_state = stateIdle;
143         }
144         m_event((iRecordableService*)this, evRecordStopped);
145         return 0;
146 }
147
148
149 int eDVBServiceRecord::doPrepare()
150 {
151                 /* allocate a ts recorder if we don't already have one. */
152         if (m_state == stateIdle)
153         {
154                 m_pids_active.clear();
155                 m_state = statePrepared;
156                 return m_service_handler.tune(m_ref, 0);
157         }
158         return 0;
159 }
160
161 int eDVBServiceRecord::doRecord()
162 {
163         int err = doPrepare();
164         if (err)
165         {
166                 m_error = errTuneFailed;
167                 m_event((iRecordableService*)this, evRecordFailed);
168                 return err;
169         }
170         
171         if (!m_tuned)
172                 return 0; /* try it again when we are tuned in */
173         
174         if (!m_record && m_tuned && !m_streaming)
175         {
176                 eDebug("Recording to %s...", m_filename.c_str());
177                 ::remove(m_filename.c_str());
178                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
179                 if (fd == -1)
180                 {
181                         eDebug("eDVBServiceRecord - can't open recording file!");
182                         m_error = errOpenRecordFile;
183                         m_event((iRecordableService*)this, evRecordFailed);
184                         return errOpenRecordFile;
185                 }
186
187                         /* turn off kernel caching strategies */
188                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
189
190                 ePtr<iDVBDemux> demux;
191                 if (m_service_handler.getDataDemux(demux))
192                 {
193                         eDebug("eDVBServiceRecord - NO DEMUX available!");
194                         m_error = errNoDemuxAvailable;
195                         m_event((iRecordableService*)this, evRecordFailed);
196                         return errNoDemuxAvailable;
197                 }
198                 demux->createTSRecorder(m_record);
199                 if (!m_record)
200                 {
201                         eDebug("eDVBServiceRecord - no ts recorder available.");
202                         m_error = errNoTsRecorderAvailable;
203                         m_event((iRecordableService*)this, evRecordFailed);
204                         return errNoTsRecorderAvailable;
205                 }
206                 m_record->setTargetFD(fd);
207                 m_record->setTargetFilename(m_filename.c_str());
208                 m_target_fd = fd;
209         }
210         
211         if (m_streaming)
212         {
213                 m_state = stateRecording;
214                 eDebug("start streaming...");
215         } else
216         {
217                 eDebug("start recording...");
218
219                 eDVBServicePMTHandler::program program;
220                 if (m_service_handler.getProgramInfo(program))
221                         eDebug("getting program info failed.");
222                 else
223                 {
224                         std::set<int> pids_to_record;
225
226                         pids_to_record.insert(0); // PAT
227
228                         if (program.pmtPid != -1)
229                                 pids_to_record.insert(program.pmtPid); // PMT
230
231                         int timing_pid = -1;
232
233                         eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
234                         if (!program.videoStreams.empty())
235                         {
236                                 eDebugNoNewLine(" (");
237                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
238                                         i(program.videoStreams.begin()); 
239                                         i != program.videoStreams.end(); ++i)
240                                 {
241                                         pids_to_record.insert(i->pid);
242                                         
243                                         if (timing_pid == -1)
244                                                 timing_pid = i->pid;
245                                         
246                                         if (i != program.videoStreams.begin())
247                                                         eDebugNoNewLine(", ");
248                                         eDebugNoNewLine("%04x", i->pid);
249                                 }
250                                 eDebugNoNewLine(")");
251                         }
252                         eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
253                         if (!program.audioStreams.empty())
254                         {
255                                 eDebugNoNewLine(" (");
256                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
257                                         i(program.audioStreams.begin()); 
258                                         i != program.audioStreams.end(); ++i)
259                                 {
260                                         pids_to_record.insert(i->pid);
261         
262                                         if (timing_pid == -1)
263                                                 timing_pid = i->pid;
264                                 
265                                         if (i != program.audioStreams.begin())
266                                                 eDebugNoNewLine(", ");
267                                         eDebugNoNewLine("%04x", i->pid);
268                                 }
269                                 eDebugNoNewLine(")");
270                         }
271                         if (!program.subtitleStreams.empty())
272                         {
273                                 eDebugNoNewLine(" (");
274                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
275                                         i(program.subtitleStreams.begin());
276                                         i != program.subtitleStreams.end(); ++i)
277                                 {
278                                         pids_to_record.insert(i->pid);
279         
280                                         if (i != program.subtitleStreams.begin())
281                                                 eDebugNoNewLine(", ");
282                                         eDebugNoNewLine("%04x", i->pid);
283                                 }
284                                 eDebugNoNewLine(")");
285                         }
286                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
287                         if (program.pcrPid != 0x1fff)
288                                 pids_to_record.insert(program.pcrPid);
289                         eDebug(", and the text pid is %04x", program.textPid);
290                         if (program.textPid != -1)
291                                 pids_to_record.insert(program.textPid); // Videotext
292
293                                 /* find out which pids are NEW and which pids are obsolete.. */
294                         std::set<int> new_pids, obsolete_pids;
295
296                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
297                                         m_pids_active.begin(), m_pids_active.end(),
298                                         std::inserter(new_pids, new_pids.begin()));
299
300                         std::set_difference(
301                                         m_pids_active.begin(), m_pids_active.end(),
302                                         pids_to_record.begin(), pids_to_record.end(), 
303                                         std::inserter(new_pids, new_pids.begin())
304                                         );
305                         
306                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
307                         {
308                                 eDebug("ADD PID: %04x", *i);
309                                 m_record->addPID(*i);
310                         }
311
312                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
313                         {
314                                 eDebug("REMOVED PID: %04x", *i);
315                                 m_record->removePID(*i);
316                         }
317
318                         if (timing_pid != -1)
319                                 m_record->setTimingPID(timing_pid);
320
321                         m_pids_active = pids_to_record;
322
323                         if (m_state != stateRecording)
324                         {
325                                 m_record->start();
326                                 m_state = stateRecording;
327                         }
328                 }
329         }
330         m_error = 0;
331         m_event((iRecordableService*)this, evRecordRunning);
332         return 0;
333 }
334
335 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
336 {
337         ptr = this;
338         return 0;
339 }
340
341 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
342 {
343         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
344         return 0;
345 }
346
347 static PyObject *createTuple(int pid, const char *type)
348 {
349         PyObject *r = PyTuple_New(2);
350         PyTuple_SetItem(r, 0, PyInt_FromLong(pid));
351         PyTuple_SetItem(r, 1, PyString_FromString(type));
352         return r;
353 }
354
355 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
356 {
357         ptr = this;
358         return 0;
359 }
360
361 PyObject *eDVBServiceRecord::getStreamingData()
362 {
363         eDVBServicePMTHandler::program program;
364         if (!m_tuned || m_service_handler.getProgramInfo(program))
365         {
366                 Py_INCREF(Py_None);
367                 return Py_None;
368         }
369
370         PyObject *r = program.createPythonObject();
371         ePtr<iDVBDemux> demux;
372         if (!m_service_handler.getDataDemux(demux))
373         {
374                 uint8_t demux_id;
375                 demux->getCADemuxID(demux_id);
376                 
377                 PyDict_SetItemString(r, "demux", PyInt_FromLong(demux_id));
378         }
379
380         return r;
381 }
382