1 #include <lib/service/servicedvbrecord.h>
2 #include <lib/base/eerror.h>
3 #include <lib/dvb/epgcache.h>
6 DEFINE_REF(eDVBServiceRecord);
8 eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref): m_ref(ref)
10 CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent);
20 void eDVBServiceRecord::serviceEvent(int event)
22 eDebug("RECORD service event %d", event);
25 case eDVBServicePMTHandler::eventTuned:
29 if (m_state == stateRecording && m_want_record)
31 m_event((iRecordableService*)this, evTunedIn);
34 case eDVBServicePMTHandler::eventTuneFailed:
36 eDebug("record failed to tune");
37 m_event((iRecordableService*)this, evTuneFailed);
40 case eDVBServicePMTHandler::eventNewProgramInfo:
42 if (m_state == stateIdle)
44 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
46 m_event((iRecordableService*)this, evNewProgramInfo);
49 case eDVBServicePMTHandler::eventMisconfiguration:
50 m_error = errMisconfiguration;
51 m_event((iRecordableService*)this, evTuneFailed);
53 case eDVBServicePMTHandler::eventNoResources:
54 m_error = errNoResources;
55 m_event((iRecordableService*)this, evTuneFailed);
60 RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id)
62 m_filename = filename;
65 if (m_state == stateIdle)
67 int ret = doPrepare();
70 eEPGCache::getInstance()->Lock();
71 const eit_event_struct *event = 0;
72 eServiceReferenceDVB ref = m_ref.getParentServiceReference();
75 if ( eit_event_id != -1 )
77 eDebug("query epg event id %d", eit_event_id);
78 eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
80 if ( !event && (begTime != -1 && endTime != -1) )
82 time_t queryTime = begTime + ((endTime-begTime)/2);
84 localtime_r(&begTime, &beg);
85 localtime_r(&endTime, &end);
86 localtime_r(&queryTime, &query);
87 eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
88 beg.tm_hour, beg.tm_min, beg.tm_sec,
89 end.tm_hour, end.tm_min, end.tm_sec,
90 query.tm_hour, query.tm_min, query.tm_sec);
91 eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
95 eDebug("found event.. store to disc");
96 std::string fname = filename;
97 fname.erase(fname.length()-2, 2);
99 int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
102 int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
103 int wr = ::write( fd, (unsigned char*)event, evLen );
105 eDebug("eit write error (%m)");
109 eEPGCache::getInstance()->Unlock();
117 RESULT eDVBServiceRecord::prepareStreaming()
121 if (m_state == stateIdle)
126 RESULT eDVBServiceRecord::start(bool simulate)
128 m_simulate = simulate;
130 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
131 m_event((iRecordableService*)this, evStart);
135 RESULT eDVBServiceRecord::stop()
138 eDebug("stop recording!");
139 if (m_state == stateRecording)
143 if (m_target_fd >= 0)
145 ::close(m_target_fd);
148 m_state = statePrepared;
149 } else if (!m_simulate)
150 eDebug("(was not recording)");
151 if (m_state == statePrepared)
156 m_event((iRecordableService*)this, evRecordStopped);
160 int eDVBServiceRecord::doPrepare()
162 /* allocate a ts recorder if we don't already have one. */
163 if (m_state == stateIdle)
165 m_pids_active.clear();
166 m_state = statePrepared;
167 return m_service_handler.tune(m_ref, 0, 0, m_simulate);
172 int eDVBServiceRecord::doRecord()
174 int err = doPrepare();
177 m_error = errTuneFailed;
178 m_event((iRecordableService*)this, evRecordFailed);
183 return 0; /* try it again when we are tuned in */
185 if (!m_record && m_tuned && !m_streaming && !m_simulate)
187 eDebug("Recording to %s...", m_filename.c_str());
188 ::remove(m_filename.c_str());
189 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
192 eDebug("eDVBServiceRecord - can't open recording file!");
193 m_error = errOpenRecordFile;
194 m_event((iRecordableService*)this, evRecordFailed);
195 return errOpenRecordFile;
198 /* turn off kernel caching strategies */
199 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
201 ePtr<iDVBDemux> demux;
202 if (m_service_handler.getDataDemux(demux))
204 eDebug("eDVBServiceRecord - NO DEMUX available!");
205 m_error = errNoDemuxAvailable;
206 m_event((iRecordableService*)this, evRecordFailed);
207 return errNoDemuxAvailable;
209 demux->createTSRecorder(m_record);
212 eDebug("eDVBServiceRecord - no ts recorder available.");
213 m_error = errNoTsRecorderAvailable;
214 m_event((iRecordableService*)this, evRecordFailed);
215 return errNoTsRecorderAvailable;
217 m_record->setTargetFD(fd);
218 m_record->setTargetFilename(m_filename.c_str());
219 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
226 m_state = stateRecording;
227 eDebug("start streaming...");
230 eDebug("start recording...");
232 eDVBServicePMTHandler::program program;
233 if (m_service_handler.getProgramInfo(program))
234 eDebug("getting program info failed.");
237 std::set<int> pids_to_record;
239 pids_to_record.insert(0); // PAT
241 if (program.pmtPid != -1)
242 pids_to_record.insert(program.pmtPid); // PMT
246 eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
247 if (!program.videoStreams.empty())
249 eDebugNoNewLine(" (");
250 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
251 i(program.videoStreams.begin());
252 i != program.videoStreams.end(); ++i)
254 pids_to_record.insert(i->pid);
256 if (timing_pid == -1)
259 if (i != program.videoStreams.begin())
260 eDebugNoNewLine(", ");
261 eDebugNoNewLine("%04x", i->pid);
263 eDebugNoNewLine(")");
265 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
266 if (!program.audioStreams.empty())
268 eDebugNoNewLine(" (");
269 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
270 i(program.audioStreams.begin());
271 i != program.audioStreams.end(); ++i)
273 pids_to_record.insert(i->pid);
275 if (timing_pid == -1)
278 if (i != program.audioStreams.begin())
279 eDebugNoNewLine(", ");
280 eDebugNoNewLine("%04x", i->pid);
282 eDebugNoNewLine(")");
284 if (!program.subtitleStreams.empty())
286 eDebugNoNewLine(" (");
287 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
288 i(program.subtitleStreams.begin());
289 i != program.subtitleStreams.end(); ++i)
291 pids_to_record.insert(i->pid);
293 if (i != program.subtitleStreams.begin())
294 eDebugNoNewLine(", ");
295 eDebugNoNewLine("%04x", i->pid);
297 eDebugNoNewLine(")");
299 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
300 if (program.pcrPid != 0x1fff)
301 pids_to_record.insert(program.pcrPid);
302 eDebug(", and the text pid is %04x", program.textPid);
303 if (program.textPid != -1)
304 pids_to_record.insert(program.textPid); // Videotext
306 /* find out which pids are NEW and which pids are obsolete.. */
307 std::set<int> new_pids, obsolete_pids;
309 std::set_difference(pids_to_record.begin(), pids_to_record.end(),
310 m_pids_active.begin(), m_pids_active.end(),
311 std::inserter(new_pids, new_pids.begin()));
314 m_pids_active.begin(), m_pids_active.end(),
315 pids_to_record.begin(), pids_to_record.end(),
316 std::inserter(new_pids, new_pids.begin())
319 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
321 eDebug("ADD PID: %04x", *i);
322 m_record->addPID(*i);
325 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
327 eDebug("REMOVED PID: %04x", *i);
328 m_record->removePID(*i);
331 if (timing_pid != -1)
332 m_record->setTimingPID(timing_pid);
334 m_pids_active = pids_to_record;
336 if (m_state != stateRecording)
339 m_state = stateRecording;
344 m_event((iRecordableService*)this, evRecordRunning);
348 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
354 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
356 connection = new eConnection((iRecordableService*)this, m_event.connect(event));
360 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
366 extern void PutToDict(ePyObject &dict, const char*key, long val); // defined in dvb/frontend.cpp
368 PyObject *eDVBServiceRecord::getStreamingData()
370 eDVBServicePMTHandler::program program;
371 if (!m_tuned || m_service_handler.getProgramInfo(program))
376 ePyObject r = program.createPythonObject();
377 ePtr<iDVBDemux> demux;
378 if (!m_service_handler.getDataDemux(demux))
381 if (!demux->getCADemuxID(demux_id))
382 PutToDict(r, "demux", demux_id);
388 void eDVBServiceRecord::recordEvent(int event)
392 case iDVBTSRecorder::eventWriteError:
393 eWarning("[eDVBServiceRecord] record write error");
395 m_event((iRecordableService*)this, evRecordWriteError);
398 eDebug("unhandled record event %d", event);