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