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