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