Add service flags and pid cache to recordings meta file. This adds possibility to...
[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 <lib/dvb/metaparser.h>
5 #include <fcntl.h>
6
7         /* for cutlist */
8 #include <byteswap.h>
9 #include <netinet/in.h>
10
11 #ifndef BYTE_ORDER
12 #error no byte order defined!
13 #endif
14
15 DEFINE_REF(eDVBServiceRecord);
16
17 eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref): m_ref(ref)
18 {
19         CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent);
20         CONNECT(m_event_handler.m_eit_changed, eDVBServiceRecord::gotNewEvent);
21         m_state = stateIdle;
22         m_want_record = 0;
23         m_tuned = 0;
24         m_target_fd = -1;
25         m_error = 0;
26         m_streaming = 0;
27         m_simulate = false;
28         m_last_event_id = -1;
29 }
30
31 void eDVBServiceRecord::serviceEvent(int event)
32 {
33         eDebug("RECORD service event %d", event);
34         switch (event)
35         {
36         case eDVBServicePMTHandler::eventTuned:
37         {
38                 eDebug("tuned..");
39                 m_tuned = 1;
40
41                         /* start feeding EIT updates */
42                 ePtr<iDVBDemux> m_demux;
43                 if (!m_service_handler.getDataDemux(m_demux))
44                 {
45                         eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_ref;
46                         int sid = ref.getParentServiceID().get();
47                         if (!sid)
48                                 sid = ref.getServiceID().get();
49                         if ( ref.getParentTransportStreamID().get() &&
50                                 ref.getParentTransportStreamID() != ref.getTransportStreamID() )
51                                 m_event_handler.startOther(m_demux, sid);
52                         else
53                                 m_event_handler.start(m_demux, sid);
54                 }
55
56                 if (m_state == stateRecording && m_want_record)
57                         doRecord();
58                 m_event((iRecordableService*)this, evTunedIn);
59                 break;
60         }
61         case eDVBServicePMTHandler::eventTuneFailed:
62         {
63                 eDebug("record failed to tune");
64                 m_event((iRecordableService*)this, evTuneFailed);
65                 break;
66         }
67         case eDVBServicePMTHandler::eventNewProgramInfo:
68         {
69                 if (m_state == stateIdle)
70                         doPrepare();
71                 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
72                         doRecord();
73                 m_event((iRecordableService*)this, evNewProgramInfo);
74                 break;
75         }
76         case eDVBServicePMTHandler::eventMisconfiguration:
77                 m_error = errMisconfiguration;
78                 m_event((iRecordableService*)this, evTuneFailed);
79                 break;
80         case eDVBServicePMTHandler::eventNoResources:
81                 m_error = errNoResources;
82                 m_event((iRecordableService*)this, evTuneFailed);
83                 break;
84         }
85 }
86
87 RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id, const char *name, const char *descr, const char *tags)
88 {
89         m_filename = filename;
90         m_streaming = 0;
91         
92         if (m_state == stateIdle)
93         {
94                 int ret = doPrepare();
95                 if (!ret)
96                 {
97                         eServiceReferenceDVB ref = m_ref.getParentServiceReference();
98                         ePtr<eDVBResourceManager> res_mgr;
99                         eDVBMetaParser meta;
100                         std::string service_data;
101                         if (!ref.valid())
102                                 ref = m_ref;
103                         if (!eDVBResourceManager::getInstance(res_mgr))
104                         {
105                                 ePtr<iDVBChannelList> db;
106                                 if (!res_mgr->getChannelList(db))
107                                 {
108                                         ePtr<eDVBService> service;
109                                         if (!db->getService(ref, service))
110                                         {
111                                                 char tmp[255];
112                                                 sprintf(tmp, "f:%x", service->m_flags);
113                                                 service_data += tmp;
114                                                 // cached pids
115                                                 for (int x=0; x < eDVBService::cacheMax; ++x)
116                                                 {
117                                                         int entry = service->getCacheEntry((eDVBService::cacheID)x);
118                                                         if (entry != -1)
119                                                         {
120                                                                 sprintf(tmp, ",c:%02d%04x", x, entry);
121                                                                 service_data += tmp;
122                                                         }
123                                                 }
124                                         }
125                                 }
126                         }
127                         meta.m_time_create = begTime;
128                         meta.m_ref = m_ref;
129                         meta.m_data_ok = 1;
130                         meta.m_service_data = service_data;
131                         if (name)
132                                 meta.m_name = name;
133                         if (descr)
134                                 meta.m_description = descr;
135                         ret = meta.updateMeta(filename) ? -255 : 0;
136                         if (!ret)
137                         {
138                                 const eit_event_struct *event = 0;
139                                 eEPGCache::getInstance()->Lock();
140                                 if ( eit_event_id != -1 )
141                                 {
142                                         eDebug("query epg event id %d", eit_event_id);
143                                         eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
144                                 }
145                                 if ( !event && (begTime != -1 && endTime != -1) )
146                                 {
147                                         time_t queryTime = begTime + ((endTime-begTime)/2);
148                                         tm beg, end, query;
149                                         localtime_r(&begTime, &beg);
150                                         localtime_r(&endTime, &end);
151                                         localtime_r(&queryTime, &query);
152                                         eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
153                                                 beg.tm_hour, beg.tm_min, beg.tm_sec,
154                                                 end.tm_hour, end.tm_min, end.tm_sec,
155                                                 query.tm_hour, query.tm_min, query.tm_sec);
156                                         eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
157                                 }
158                                 if ( event )
159                                 {
160                                         eDebug("found event.. store to disc");
161                                         std::string fname = filename;
162                                         fname.erase(fname.length()-2, 2);
163                                         fname+="eit";
164                                         int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
165                                         if (fd>-1)
166                                         {
167                                                 int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
168                                                 int wr = ::write( fd, (unsigned char*)event, evLen );
169                                                 if ( wr != evLen )
170                                                         eDebug("eit write error (%m)");
171                                                 ::close(fd);
172                                         }
173                                 }
174                                 eEPGCache::getInstance()->Unlock();
175                         }
176                 }
177                 return ret;
178         }
179         return -1;
180 }
181
182 RESULT eDVBServiceRecord::prepareStreaming()
183 {
184         m_filename = "";
185         m_streaming = 1;
186         if (m_state == stateIdle)
187                 return doPrepare();
188         return -1;
189 }
190
191 RESULT eDVBServiceRecord::start(bool simulate)
192 {
193         m_simulate = simulate;
194         m_want_record = 1;
195                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
196         m_event((iRecordableService*)this, evStart);
197         return doRecord();
198 }
199
200 RESULT eDVBServiceRecord::stop()
201 {
202         if (!m_simulate)
203                 eDebug("stop recording!");
204         if (m_state == stateRecording)
205         {
206                 if (m_record)
207                         m_record->stop();
208                 if (m_target_fd >= 0)
209                 {
210                         ::close(m_target_fd);
211                         m_target_fd = -1;
212                 }
213                 
214                 saveCutlist();
215                 
216                 m_state = statePrepared;
217         } else if (!m_simulate)
218                 eDebug("(was not recording)");
219         if (m_state == statePrepared)
220         {
221                 m_record = 0;
222                 m_state = stateIdle;
223         }
224         m_event((iRecordableService*)this, evRecordStopped);
225         return 0;
226 }
227
228 int eDVBServiceRecord::doPrepare()
229 {
230                 /* allocate a ts recorder if we don't already have one. */
231         if (m_state == stateIdle)
232         {
233                 m_pids_active.clear();
234                 m_state = statePrepared;
235                 return m_service_handler.tune(m_ref, 0, 0, m_simulate);
236         }
237         return 0;
238 }
239
240 int eDVBServiceRecord::doRecord()
241 {
242         int err = doPrepare();
243         if (err)
244         {
245                 m_error = errTuneFailed;
246                 m_event((iRecordableService*)this, evRecordFailed);
247                 return err;
248         }
249         
250         if (!m_tuned)
251                 return 0; /* try it again when we are tuned in */
252         
253         if (!m_record && m_tuned && !m_streaming && !m_simulate)
254         {
255                 eDebug("Recording to %s...", m_filename.c_str());
256                 ::remove(m_filename.c_str());
257                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
258                 if (fd == -1)
259                 {
260                         eDebug("eDVBServiceRecord - can't open recording file!");
261                         m_error = errOpenRecordFile;
262                         m_event((iRecordableService*)this, evRecordFailed);
263                         return errOpenRecordFile;
264                 }
265
266                         /* turn off kernel caching strategies */
267                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
268
269                 ePtr<iDVBDemux> demux;
270                 if (m_service_handler.getDataDemux(demux))
271                 {
272                         eDebug("eDVBServiceRecord - NO DEMUX available!");
273                         m_error = errNoDemuxAvailable;
274                         m_event((iRecordableService*)this, evRecordFailed);
275                         return errNoDemuxAvailable;
276                 }
277                 demux->createTSRecorder(m_record);
278                 if (!m_record)
279                 {
280                         eDebug("eDVBServiceRecord - no ts recorder available.");
281                         m_error = errNoTsRecorderAvailable;
282                         m_event((iRecordableService*)this, evRecordFailed);
283                         return errNoTsRecorderAvailable;
284                 }
285                 m_record->setTargetFD(fd);
286                 m_record->setTargetFilename(m_filename.c_str());
287                 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
288
289                 m_target_fd = fd;
290         }
291         
292         if (m_streaming)
293         {
294                 m_state = stateRecording;
295                 eDebug("start streaming...");
296         } else
297         {
298                 eDebug("start recording...");
299
300                 eDVBServicePMTHandler::program program;
301                 if (m_service_handler.getProgramInfo(program))
302                         eDebug("getting program info failed.");
303                 else
304                 {
305                         std::set<int> pids_to_record;
306
307                         pids_to_record.insert(0); // PAT
308
309                         if (program.pmtPid != -1)
310                                 pids_to_record.insert(program.pmtPid); // PMT
311
312                         int timing_pid = -1, timing_pid_type = -1;
313
314                         eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
315                         if (!program.videoStreams.empty())
316                         {
317                                 eDebugNoNewLine(" (");
318                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
319                                         i(program.videoStreams.begin()); 
320                                         i != program.videoStreams.end(); ++i)
321                                 {
322                                         pids_to_record.insert(i->pid);
323                                         
324                                         if (timing_pid == -1)
325                                         {
326                                                 timing_pid = i->pid;
327                                                 timing_pid_type = i->type;
328                                         }
329                                         
330                                         if (i != program.videoStreams.begin())
331                                                         eDebugNoNewLine(", ");
332                                         eDebugNoNewLine("%04x", i->pid);
333                                 }
334                                 eDebugNoNewLine(")");
335                         }
336                         eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
337                         if (!program.audioStreams.empty())
338                         {
339                                 eDebugNoNewLine(" (");
340                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
341                                         i(program.audioStreams.begin()); 
342                                         i != program.audioStreams.end(); ++i)
343                                 {
344                                         pids_to_record.insert(i->pid);
345         
346                                         if (timing_pid == -1)
347                                         {
348                                                 timing_pid = i->pid;
349                                                 timing_pid_type = -1;
350                                         }
351                                 
352                                         if (i != program.audioStreams.begin())
353                                                 eDebugNoNewLine(", ");
354                                         eDebugNoNewLine("%04x", i->pid);
355                                 }
356                                 eDebugNoNewLine(")");
357                         }
358                         if (!program.subtitleStreams.empty())
359                         {
360                                 eDebugNoNewLine(" (");
361                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
362                                         i(program.subtitleStreams.begin());
363                                         i != program.subtitleStreams.end(); ++i)
364                                 {
365                                         pids_to_record.insert(i->pid);
366         
367                                         if (i != program.subtitleStreams.begin())
368                                                 eDebugNoNewLine(", ");
369                                         eDebugNoNewLine("%04x", i->pid);
370                                 }
371                                 eDebugNoNewLine(")");
372                         }
373                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
374                         if (program.pcrPid != 0x1fff)
375                                 pids_to_record.insert(program.pcrPid);
376                         eDebug(", and the text pid is %04x", program.textPid);
377                         if (program.textPid != -1)
378                                 pids_to_record.insert(program.textPid); // Videotext
379
380                                 /* find out which pids are NEW and which pids are obsolete.. */
381                         std::set<int> new_pids, obsolete_pids;
382
383                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
384                                         m_pids_active.begin(), m_pids_active.end(),
385                                         std::inserter(new_pids, new_pids.begin()));
386
387                         std::set_difference(
388                                         m_pids_active.begin(), m_pids_active.end(),
389                                         pids_to_record.begin(), pids_to_record.end(), 
390                                         std::inserter(new_pids, new_pids.begin())
391                                         );
392                         
393                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
394                         {
395                                 eDebug("ADD PID: %04x", *i);
396                                 m_record->addPID(*i);
397                         }
398
399                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
400                         {
401                                 eDebug("REMOVED PID: %04x", *i);
402                                 m_record->removePID(*i);
403                         }
404
405                         if (timing_pid != -1)
406                                 m_record->setTimingPID(timing_pid, timing_pid_type);
407
408                         m_pids_active = pids_to_record;
409
410                         if (m_state != stateRecording)
411                         {
412                                 m_record->start();
413                                 m_state = stateRecording;
414                         }
415                 }
416         }
417         m_error = 0;
418         m_event((iRecordableService*)this, evRecordRunning);
419         return 0;
420 }
421
422 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
423 {
424         ptr = this;
425         return 0;
426 }
427
428 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
429 {
430         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
431         return 0;
432 }
433
434 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
435 {
436         ptr = this;
437         return 0;
438 }
439
440 extern void PutToDict(ePyObject &dict, const char*key, long val);  // defined in dvb/frontend.cpp
441
442 PyObject *eDVBServiceRecord::getStreamingData()
443 {
444         eDVBServicePMTHandler::program program;
445         if (!m_tuned || m_service_handler.getProgramInfo(program))
446         {
447                 Py_RETURN_NONE;
448         }
449
450         ePyObject r = program.createPythonObject();
451         ePtr<iDVBDemux> demux;
452         if (!m_service_handler.getDataDemux(demux))
453         {
454                 uint8_t demux_id;
455                 if (!demux->getCADemuxID(demux_id))
456                         PutToDict(r, "demux", demux_id);
457         }
458
459         return r;
460 }
461
462 void eDVBServiceRecord::recordEvent(int event)
463 {
464         switch (event)
465         {
466         case iDVBTSRecorder::eventWriteError:
467                 eWarning("[eDVBServiceRecord] record write error");
468                 stop();
469                 m_event((iRecordableService*)this, evRecordWriteError);
470                 return;
471         default:
472                 eDebug("unhandled record event %d", event);
473         }
474 }
475
476 void eDVBServiceRecord::gotNewEvent()
477 {
478         ePtr<eServiceEvent> event_now;
479         m_event_handler.getEvent(event_now, 0);
480
481         if (!event_now)
482                 return;
483
484         int event_id = event_now->getEventId();
485
486         pts_t p;
487         
488         if (m_record)
489         {
490                 if (m_record->getCurrentPCR(p))
491                         eDebug("getting PCR failed!");
492                 else
493                 {
494                         static int i;
495                         m_event_timestamps[/* event_id*/ ++i] = p;
496                         eDebug("pcr of eit change: %llx", p);
497                 }
498         }
499
500         if (event_id != m_last_event_id)
501                 eDebug("[eDVBServiceRecord] now running: %s (%d seconds)", event_now->getEventName().c_str(), event_now->getDuration());
502         
503         m_last_event_id = event_id;
504 }
505
506 void eDVBServiceRecord::saveCutlist()
507 {
508                         /* XXX: dupe of eDVBServicePlay::saveCuesheet, refactor plz */
509         std::string filename = m_filename + ".cuts";
510
511         eDVBTSTools tstools;
512         
513         if (tstools.openFile(m_filename.c_str()))
514         {
515                 eDebug("[eDVBServiceRecord] saving cutlist failed because tstools failed");
516                 return;
517         }
518         
519         FILE *f = fopen(filename.c_str(), "wb");
520
521         if (f)
522         {
523                 unsigned long long where;
524                 int what;
525
526                 for (std::map<int,pts_t>::iterator i(m_event_timestamps.begin()); i != m_event_timestamps.end(); ++i)
527                 {
528                         pts_t p = i->second;
529                         off_t offset = 0; // fixme, we need to note down both
530                         if (tstools.fixupPTS(offset, p))
531                         {
532                                 eDebug("[eDVBServiceRecord] fixing up PTS failed, not saving");
533                                 continue;
534                         }
535                         eDebug("fixed up %llx to %llx (offset %llx)", i->second, p, offset);
536 #if BYTE_ORDER == BIG_ENDIAN
537                         where = p;
538 #else
539                         where = bswap_64(p);
540 #endif
541                         what = htonl(2); /* mark */
542                         fwrite(&where, sizeof(where), 1, f);
543                         fwrite(&what, sizeof(what), 1, f);
544                 }
545                 fclose(f);
546         }
547         
548 }