fix title
[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_target_fd = fd;
211         }
212         
213         if (m_streaming)
214         {
215                 m_state = stateRecording;
216                 eDebug("start streaming...");
217         } else
218         {
219                 eDebug("start recording...");
220
221                 eDVBServicePMTHandler::program program;
222                 if (m_service_handler.getProgramInfo(program))
223                         eDebug("getting program info failed.");
224                 else
225                 {
226                         std::set<int> pids_to_record;
227
228                         pids_to_record.insert(0); // PAT
229
230                         if (program.pmtPid != -1)
231                                 pids_to_record.insert(program.pmtPid); // PMT
232
233                         int timing_pid = -1;
234
235                         eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
236                         if (!program.videoStreams.empty())
237                         {
238                                 eDebugNoNewLine(" (");
239                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
240                                         i(program.videoStreams.begin()); 
241                                         i != program.videoStreams.end(); ++i)
242                                 {
243                                         pids_to_record.insert(i->pid);
244                                         
245                                         if (timing_pid == -1)
246                                                 timing_pid = i->pid;
247                                         
248                                         if (i != program.videoStreams.begin())
249                                                         eDebugNoNewLine(", ");
250                                         eDebugNoNewLine("%04x", i->pid);
251                                 }
252                                 eDebugNoNewLine(")");
253                         }
254                         eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
255                         if (!program.audioStreams.empty())
256                         {
257                                 eDebugNoNewLine(" (");
258                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
259                                         i(program.audioStreams.begin()); 
260                                         i != program.audioStreams.end(); ++i)
261                                 {
262                                         pids_to_record.insert(i->pid);
263         
264                                         if (timing_pid == -1)
265                                                 timing_pid = i->pid;
266                                 
267                                         if (i != program.audioStreams.begin())
268                                                 eDebugNoNewLine(", ");
269                                         eDebugNoNewLine("%04x", i->pid);
270                                 }
271                                 eDebugNoNewLine(")");
272                         }
273                         if (!program.subtitleStreams.empty())
274                         {
275                                 eDebugNoNewLine(" (");
276                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
277                                         i(program.subtitleStreams.begin());
278                                         i != program.subtitleStreams.end(); ++i)
279                                 {
280                                         pids_to_record.insert(i->pid);
281         
282                                         if (i != program.subtitleStreams.begin())
283                                                 eDebugNoNewLine(", ");
284                                         eDebugNoNewLine("%04x", i->pid);
285                                 }
286                                 eDebugNoNewLine(")");
287                         }
288                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
289                         if (program.pcrPid != 0x1fff)
290                                 pids_to_record.insert(program.pcrPid);
291                         eDebug(", and the text pid is %04x", program.textPid);
292                         if (program.textPid != -1)
293                                 pids_to_record.insert(program.textPid); // Videotext
294
295                                 /* find out which pids are NEW and which pids are obsolete.. */
296                         std::set<int> new_pids, obsolete_pids;
297
298                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
299                                         m_pids_active.begin(), m_pids_active.end(),
300                                         std::inserter(new_pids, new_pids.begin()));
301
302                         std::set_difference(
303                                         m_pids_active.begin(), m_pids_active.end(),
304                                         pids_to_record.begin(), pids_to_record.end(), 
305                                         std::inserter(new_pids, new_pids.begin())
306                                         );
307                         
308                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
309                         {
310                                 eDebug("ADD PID: %04x", *i);
311                                 m_record->addPID(*i);
312                         }
313
314                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
315                         {
316                                 eDebug("REMOVED PID: %04x", *i);
317                                 m_record->removePID(*i);
318                         }
319
320                         if (timing_pid != -1)
321                                 m_record->setTimingPID(timing_pid);
322
323                         m_pids_active = pids_to_record;
324
325                         if (m_state != stateRecording)
326                         {
327                                 m_record->start();
328                                 m_state = stateRecording;
329                         }
330                 }
331         }
332         m_error = 0;
333         m_event((iRecordableService*)this, evRecordRunning);
334         return 0;
335 }
336
337 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
338 {
339         ptr = this;
340         return 0;
341 }
342
343 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
344 {
345         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
346         return 0;
347 }
348
349 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
350 {
351         ptr = this;
352         return 0;
353 }
354
355 PyObject *eDVBServiceRecord::getStreamingData()
356 {
357         eDVBServicePMTHandler::program program;
358         if (!m_tuned || m_service_handler.getProgramInfo(program))
359         {
360                 Py_INCREF(Py_None);
361                 return Py_None;
362         }
363
364         PyObject *r = program.createPythonObject();
365         ePtr<iDVBDemux> demux;
366         if (!m_service_handler.getDataDemux(demux))
367         {
368                 uint8_t demux_id;
369                 demux->getCADemuxID(demux_id);
370                 
371                 PyDict_SetItemString(r, "demux", PyInt_FromLong(demux_id));
372         }
373
374         return r;
375 }
376