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