Merge branch 'bug_617_default_favlist_handling_fix'
[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 <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                         if (tags)
136                                 meta.m_tags = tags;
137                         ret = meta.updateMeta(filename) ? -255 : 0;
138                         if (!ret)
139                         {
140                                 const eit_event_struct *event = 0;
141                                 eEPGCache::getInstance()->Lock();
142                                 if ( eit_event_id != -1 )
143                                 {
144                                         eDebug("query epg event id %d", eit_event_id);
145                                         eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
146                                 }
147                                 if ( !event && (begTime != -1 && endTime != -1) )
148                                 {
149                                         time_t queryTime = begTime + ((endTime-begTime)/2);
150                                         tm beg, end, query;
151                                         localtime_r(&begTime, &beg);
152                                         localtime_r(&endTime, &end);
153                                         localtime_r(&queryTime, &query);
154                                         eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
155                                                 beg.tm_hour, beg.tm_min, beg.tm_sec,
156                                                 end.tm_hour, end.tm_min, end.tm_sec,
157                                                 query.tm_hour, query.tm_min, query.tm_sec);
158                                         eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
159                                 }
160                                 if ( event )
161                                 {
162                                         eDebug("found event.. store to disc");
163                                         std::string fname = filename;
164                                         fname.erase(fname.length()-2, 2);
165                                         fname+="eit";
166                                         int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
167                                         if (fd>-1)
168                                         {
169                                                 int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
170                                                 int wr = ::write( fd, (unsigned char*)event, evLen );
171                                                 if ( wr != evLen )
172                                                         eDebug("eit write error (%m)");
173                                                 ::close(fd);
174                                         }
175                                 }
176                                 eEPGCache::getInstance()->Unlock();
177                         }
178                 }
179                 return ret;
180         }
181         return -1;
182 }
183
184 RESULT eDVBServiceRecord::prepareStreaming()
185 {
186         m_filename = "";
187         m_streaming = 1;
188         if (m_state == stateIdle)
189                 return doPrepare();
190         return -1;
191 }
192
193 RESULT eDVBServiceRecord::start(bool simulate)
194 {
195         m_simulate = simulate;
196         m_want_record = 1;
197                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
198         m_event((iRecordableService*)this, evStart);
199         return doRecord();
200 }
201
202 RESULT eDVBServiceRecord::stop()
203 {
204         if (!m_simulate)
205                 eDebug("stop recording!");
206         if (m_state == stateRecording)
207         {
208                 if (m_record)
209                         m_record->stop();
210                 if (m_target_fd >= 0)
211                 {
212                         ::close(m_target_fd);
213                         m_target_fd = -1;
214                 }
215                 
216                 saveCutlist();
217                 
218                 m_state = statePrepared;
219         } else if (!m_simulate)
220                 eDebug("(was not recording)");
221         if (m_state == statePrepared)
222         {
223                 m_record = 0;
224                 m_state = stateIdle;
225         }
226         m_event((iRecordableService*)this, evRecordStopped);
227         return 0;
228 }
229
230 int eDVBServiceRecord::doPrepare()
231 {
232                 /* allocate a ts recorder if we don't already have one. */
233         if (m_state == stateIdle)
234         {
235                 m_pids_active.clear();
236                 m_state = statePrepared;
237                 return m_service_handler.tune(m_ref, 0, 0, m_simulate);
238         }
239         return 0;
240 }
241
242 int eDVBServiceRecord::doRecord()
243 {
244         int err = doPrepare();
245         if (err)
246         {
247                 m_error = errTuneFailed;
248                 m_event((iRecordableService*)this, evRecordFailed);
249                 return err;
250         }
251         
252         if (!m_tuned)
253                 return 0; /* try it again when we are tuned in */
254         
255         if (!m_record && m_tuned && !m_streaming && !m_simulate)
256         {
257                 eDebug("Recording to %s...", m_filename.c_str());
258                 ::remove(m_filename.c_str());
259                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
260                 if (fd == -1)
261                 {
262                         eDebug("eDVBServiceRecord - can't open recording file!");
263                         m_error = errOpenRecordFile;
264                         m_event((iRecordableService*)this, evRecordFailed);
265                         return errOpenRecordFile;
266                 }
267
268                         /* turn off kernel caching strategies */
269                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
270
271                 ePtr<iDVBDemux> demux;
272                 if (m_service_handler.getDataDemux(demux))
273                 {
274                         eDebug("eDVBServiceRecord - NO DEMUX available!");
275                         m_error = errNoDemuxAvailable;
276                         m_event((iRecordableService*)this, evRecordFailed);
277                         return errNoDemuxAvailable;
278                 }
279                 demux->createTSRecorder(m_record);
280                 if (!m_record)
281                 {
282                         eDebug("eDVBServiceRecord - no ts recorder available.");
283                         m_error = errNoTsRecorderAvailable;
284                         m_event((iRecordableService*)this, evRecordFailed);
285                         return errNoTsRecorderAvailable;
286                 }
287                 m_record->setTargetFD(fd);
288                 m_record->setTargetFilename(m_filename.c_str());
289                 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
290
291                 m_target_fd = fd;
292         }
293         
294         if (m_streaming)
295         {
296                 m_state = stateRecording;
297                 eDebug("start streaming...");
298         } else
299         {
300                 eDebug("start recording...");
301
302                 eDVBServicePMTHandler::program program;
303                 if (m_service_handler.getProgramInfo(program))
304                         eDebug("getting program info failed.");
305                 else
306                 {
307                         std::set<int> pids_to_record;
308
309                         pids_to_record.insert(0); // PAT
310
311                         if (program.pmtPid != -1)
312                                 pids_to_record.insert(program.pmtPid); // PMT
313
314                         int timing_pid = -1, timing_pid_type = -1;
315
316                         eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
317                         if (!program.videoStreams.empty())
318                         {
319                                 eDebugNoNewLine(" (");
320                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
321                                         i(program.videoStreams.begin()); 
322                                         i != program.videoStreams.end(); ++i)
323                                 {
324                                         pids_to_record.insert(i->pid);
325                                         
326                                         if (timing_pid == -1)
327                                         {
328                                                 timing_pid = i->pid;
329                                                 timing_pid_type = i->type;
330                                         }
331                                         
332                                         if (i != program.videoStreams.begin())
333                                                         eDebugNoNewLine(", ");
334                                         eDebugNoNewLine("%04x", i->pid);
335                                 }
336                                 eDebugNoNewLine(")");
337                         }
338                         eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
339                         if (!program.audioStreams.empty())
340                         {
341                                 eDebugNoNewLine(" (");
342                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
343                                         i(program.audioStreams.begin()); 
344                                         i != program.audioStreams.end(); ++i)
345                                 {
346                                         pids_to_record.insert(i->pid);
347         
348                                         if (timing_pid == -1)
349                                         {
350                                                 timing_pid = i->pid;
351                                                 timing_pid_type = -1;
352                                         }
353                                 
354                                         if (i != program.audioStreams.begin())
355                                                 eDebugNoNewLine(", ");
356                                         eDebugNoNewLine("%04x", i->pid);
357                                 }
358                                 eDebugNoNewLine(")");
359                         }
360                         if (!program.subtitleStreams.empty())
361                         {
362                                 eDebugNoNewLine(" (");
363                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
364                                         i(program.subtitleStreams.begin());
365                                         i != program.subtitleStreams.end(); ++i)
366                                 {
367                                         pids_to_record.insert(i->pid);
368         
369                                         if (i != program.subtitleStreams.begin())
370                                                 eDebugNoNewLine(", ");
371                                         eDebugNoNewLine("%04x", i->pid);
372                                 }
373                                 eDebugNoNewLine(")");
374                         }
375                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
376                         if (program.pcrPid != 0x1fff)
377                                 pids_to_record.insert(program.pcrPid);
378                         eDebug(", and the text pid is %04x", program.textPid);
379                         if (program.textPid != -1)
380                                 pids_to_record.insert(program.textPid); // Videotext
381
382                                 /* find out which pids are NEW and which pids are obsolete.. */
383                         std::set<int> new_pids, obsolete_pids;
384
385                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
386                                         m_pids_active.begin(), m_pids_active.end(),
387                                         std::inserter(new_pids, new_pids.begin()));
388
389                         std::set_difference(
390                                         m_pids_active.begin(), m_pids_active.end(),
391                                         pids_to_record.begin(), pids_to_record.end(), 
392                                         std::inserter(obsolete_pids, obsolete_pids.begin())
393                                         );
394                         
395                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
396                         {
397                                 eDebug("ADD PID: %04x", *i);
398                                 m_record->addPID(*i);
399                         }
400
401                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
402                         {
403                                 eDebug("REMOVED PID: %04x", *i);
404                                 m_record->removePID(*i);
405                         }
406
407                         if (timing_pid != -1)
408                                 m_record->setTimingPID(timing_pid, timing_pid_type);
409
410                         m_pids_active = pids_to_record;
411
412                         if (m_state != stateRecording)
413                         {
414                                 m_record->start();
415                                 m_state = stateRecording;
416                         }
417                 }
418         }
419         m_error = 0;
420         m_event((iRecordableService*)this, evRecordRunning);
421         return 0;
422 }
423
424 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
425 {
426         ptr = this;
427         return 0;
428 }
429
430 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
431 {
432         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
433         return 0;
434 }
435
436 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
437 {
438         ptr = this;
439         return 0;
440 }
441
442 extern void PutToDict(ePyObject &dict, const char*key, long val);  // defined in dvb/frontend.cpp
443
444 PyObject *eDVBServiceRecord::getStreamingData()
445 {
446         eDVBServicePMTHandler::program program;
447         if (!m_tuned || m_service_handler.getProgramInfo(program))
448         {
449                 Py_RETURN_NONE;
450         }
451
452         ePyObject r = program.createPythonObject();
453         ePtr<iDVBDemux> demux;
454         if (!m_service_handler.getDataDemux(demux))
455         {
456                 uint8_t demux_id;
457                 if (!demux->getCADemuxID(demux_id))
458                         PutToDict(r, "demux", demux_id);
459         }
460
461         return r;
462 }
463
464 void eDVBServiceRecord::recordEvent(int event)
465 {
466         switch (event)
467         {
468         case iDVBTSRecorder::eventWriteError:
469                 eWarning("[eDVBServiceRecord] record write error");
470                 stop();
471                 m_event((iRecordableService*)this, evRecordWriteError);
472                 return;
473         default:
474                 eDebug("unhandled record event %d", event);
475         }
476 }
477
478 void eDVBServiceRecord::gotNewEvent()
479 {
480         ePtr<eServiceEvent> event_now;
481         m_event_handler.getEvent(event_now, 0);
482
483         if (!event_now)
484                 return;
485
486         int event_id = event_now->getEventId();
487
488         pts_t p;
489         
490         if (m_record)
491         {
492                 if (m_record->getCurrentPCR(p))
493                         eDebug("getting PCR failed!");
494                 else
495                 {
496                         m_event_timestamps[event_id] = p;
497                         eDebug("pcr of eit change: %llx", p);
498                 }
499         }
500
501         if (event_id != m_last_event_id)
502                 eDebug("[eDVBServiceRecord] now running: %s (%d seconds)", event_now->getEventName().c_str(), event_now->getDuration());
503         
504         m_last_event_id = event_id;
505
506         m_event((iRecordableService*)this, evNewEventInfo);
507 }
508
509 void eDVBServiceRecord::saveCutlist()
510 {
511                         /* XXX: dupe of eDVBServicePlay::saveCuesheet, refactor plz */
512         std::string filename = m_filename + ".cuts";
513
514         eDVBTSTools tstools;
515         
516         if (tstools.openFile(m_filename.c_str()))
517         {
518                 eDebug("[eDVBServiceRecord] saving cutlist failed because tstools failed");
519                 return;
520         }
521         
522         FILE *f = fopen(filename.c_str(), "wb");
523
524         if (f)
525         {
526                 unsigned long long where;
527                 int what;
528
529                 for (std::map<int,pts_t>::iterator i(m_event_timestamps.begin()); i != m_event_timestamps.end(); ++i)
530                 {
531                         pts_t p = i->second;
532                         off_t offset = 0; // fixme, we need to note down both
533                         if (tstools.fixupPTS(offset, p))
534                         {
535                                 eDebug("[eDVBServiceRecord] fixing up PTS failed, not saving");
536                                 continue;
537                         }
538                         eDebug("fixed up %llx to %llx (offset %llx)", i->second, p, offset);
539 #if BYTE_ORDER == BIG_ENDIAN
540                         where = p;
541 #else
542                         where = bswap_64(p);
543 #endif
544                         what = htonl(2); /* mark */
545                         fwrite(&where, sizeof(where), 1, f);
546                         fwrite(&what, sizeof(what), 1, f);
547                 }
548                 fclose(f);
549         }
550         
551 }
552
553 RESULT eDVBServiceRecord::subServices(ePtr<iSubserviceList> &ptr)
554 {
555         ptr = this;
556         return 0;
557 }
558
559 int eDVBServiceRecord::getNumberOfSubservices()
560 {
561         ePtr<eServiceEvent> evt;
562         if (!m_event_handler.getEvent(evt, 0))
563                 return evt->getNumOfLinkageServices();
564         return 0;
565 }
566
567 RESULT eDVBServiceRecord::getSubservice(eServiceReference &sub, unsigned int n)
568 {
569         ePtr<eServiceEvent> evt;
570         if (!m_event_handler.getEvent(evt, 0))
571         {
572                 if (!evt->getLinkageService(sub, m_ref, n))
573                         return 0;
574         }
575         sub.type=eServiceReference::idInvalid;
576         return -1;
577 }