python object refcount debugging code second try
[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 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 }
17
18 void eDVBServiceRecord::serviceEvent(int event)
19 {
20         eDebug("RECORD service event %d", event);
21         switch (event)
22         {
23         case eDVBServicePMTHandler::eventTuned:
24         {
25                 eDebug("tuned..");
26                 m_tuned = 1;
27                 if (m_state == stateRecording && m_want_record)
28                         doRecord();
29                 m_event((iRecordableService*)this, evTunedIn);
30                 break;
31         }
32         case eDVBServicePMTHandler::eventTuneFailed:
33         {
34                 eDebug("record failed to tune");
35                 m_event((iRecordableService*)this, evTuneFailed);
36                 break;
37         }
38         case eDVBServicePMTHandler::eventNewProgramInfo:
39         {
40                 if (m_state == stateIdle)
41                         doPrepare();
42                 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
43                         doRecord();
44                 m_event((iRecordableService*)this, evNewProgramInfo);
45                 break;
46         }
47         }
48 }
49
50 RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id)
51 {
52         m_filename = filename;
53         if (m_state == stateIdle)
54         {
55                 int ret = doPrepare();
56                 if (!ret)
57                 {
58                         eEPGCache::getInstance()->Lock();
59                         const eit_event_struct *event = 0;
60                         eServiceReferenceDVB ref = m_ref.getParentServiceReference();
61                         if (!ref.valid())
62                                 ref = m_ref;
63                         if ( eit_event_id != -1 )
64                         {
65                                 eDebug("query epg event id %d", eit_event_id);
66                                 eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
67                         }
68                         if ( !event && (begTime != -1 && endTime != -1) )
69                         {
70                                 time_t queryTime = begTime + ((endTime-begTime)/2);
71                                 tm beg, end, query;
72                                 localtime_r(&begTime, &beg);
73                                 localtime_r(&endTime, &end);
74                                 localtime_r(&queryTime, &query);
75                                 eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
76                                         beg.tm_hour, beg.tm_min, beg.tm_sec,
77                                         end.tm_hour, end.tm_min, end.tm_sec,
78                                         query.tm_hour, query.tm_min, query.tm_sec);
79                                 eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
80                         }
81                         if ( event )
82                         {
83                                 eDebug("found event.. store to disc");
84                                 std::string fname = filename;
85                                 fname.erase(fname.length()-2, 2);
86                                 fname+="eit";
87                                 int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
88                                 if (fd>-1)
89                                 {
90                                         int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
91                                         int wr = ::write( fd, (unsigned char*)event, evLen );
92                                         if ( wr != evLen )
93                                                 eDebug("eit write error (%m)");
94                                         ::close(fd);
95                                 }
96                         }
97                         eEPGCache::getInstance()->Unlock();
98                 }
99                 return ret;
100         }
101         else
102                 return -1;
103 }
104
105 RESULT eDVBServiceRecord::start()
106 {
107         m_want_record = 1;
108                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
109         m_event((iRecordableService*)this, evStart);
110         return doRecord();
111 }
112
113
114 RESULT eDVBServiceRecord::stop()
115 {
116         eDebug("stop recording!!");
117         m_event((iRecordableService*)this, evStop);
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, evRecordStopped);
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 }