get rid of hardcoded TS packetsize
[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                                 int packetsize = 188;
250                                 eDVBMetaParser meta;
251                                 if (!meta.parseFile(m_ref.path))
252                                 {
253                                         std::string path = m_ref.path;
254                                         m_ref = meta.m_ref;
255                                         m_ref.path = path;
256                                         packetsize = meta.m_packet_size;
257                                         m_descramble = meta.m_scrambled;
258                                 }
259                                 servicetype = eDVBServicePMTHandler::offline;
260                                 eRawFile *f = new eRawFile(packetsize);
261                                 f->open(m_ref.path.c_str());
262                                 source = ePtr<iTsSource>(f);
263                         }
264                 }
265                 return m_service_handler.tuneExt(m_ref, 0, source, m_ref.path.c_str(), 0, m_simulate, NULL, servicetype, m_descramble);
266         }
267         return 0;
268 }
269
270 int eDVBServiceRecord::doRecord()
271 {
272         int err = doPrepare();
273         if (err)
274         {
275                 m_error = errTuneFailed;
276                 m_event((iRecordableService*)this, evRecordFailed);
277                 return err;
278         }
279
280         if (!m_tuned)
281                 return 0; /* try it again when we are tuned in */
282
283         if (!m_record && m_tuned && !m_streaming && !m_simulate)
284         {
285                 eDebug("[eDVBServiceRecord] Recording to %s...", m_filename.c_str());
286                 ::remove(m_filename.c_str());
287                 int fd = ::open(m_filename.c_str(), O_WRONLY | O_CREAT | O_LARGEFILE | O_CLOEXEC, 0666);
288                 if (fd == -1)
289                 {
290                         eDebug("[eDVBServiceRecord] can't open recording file: %m");
291                         m_error = errOpenRecordFile;
292                         m_event((iRecordableService*)this, evRecordFailed);
293                         return errOpenRecordFile;
294                 }
295
296                 ePtr<iDVBDemux> demux;
297                 if (m_service_handler.getDataDemux(demux))
298                 {
299                         eDebug("[eDVBServiceRecord] NO DEMUX available!");
300                         m_error = errNoDemuxAvailable;
301                         m_event((iRecordableService*)this, evRecordFailed);
302                         return errNoDemuxAvailable;
303                 }
304                 demux->createTSRecorder(m_record);
305                 if (!m_record)
306                 {
307                         eDebug("[eDVBServiceRecord] no ts recorder available.");
308                         m_error = errNoTsRecorderAvailable;
309                         m_event((iRecordableService*)this, evRecordFailed);
310                         return errNoTsRecorderAvailable;
311                 }
312                 m_record->setTargetFD(fd);
313                 m_record->setTargetFilename(m_filename);
314                 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
315
316                 m_target_fd = fd;
317         }
318
319         if (m_streaming)
320         {
321                 m_state = stateRecording;
322                 eDebug("[eDVBServiceRecord] start streaming...");
323         } else
324         {
325                 eDebug("[eDVBServiceRecord] start recording...");
326
327                 eDVBServicePMTHandler::program program;
328                 if (m_service_handler.getProgramInfo(program))
329                         eDebug("[eDVBServiceRecord] getting program info failed.");
330                 else
331                 {
332                         std::set<int> pids_to_record;
333
334                         pids_to_record.insert(0); // PAT
335
336                         if (program.pmtPid != -1)
337                                 pids_to_record.insert(program.pmtPid); // PMT
338
339                         int timing_pid = -1, timing_stream_type = -1;
340                         iDVBTSRecorder::timing_pid_type timing_pid_type = iDVBTSRecorder::none;
341
342                         eDebugNoNewLineStart("[eDVBServiceRecord] RECORD: have %zd video stream(s)", program.videoStreams.size());
343                         if (!program.videoStreams.empty())
344                         {
345                                 eDebugNoNewLine(" (");
346                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
347                                         i(program.videoStreams.begin());
348                                         i != program.videoStreams.end(); ++i)
349                                 {
350                                         pids_to_record.insert(i->pid);
351
352                                         if (timing_pid == -1)
353                                         {
354                                                 timing_pid = i->pid;
355                                                 timing_stream_type = i->type;
356                                                 timing_pid_type = iDVBTSRecorder::video_pid;
357                                         }
358
359                                         if (i != program.videoStreams.begin())
360                                                         eDebugNoNewLine(", ");
361                                         eDebugNoNewLine("%04x", i->pid);
362                                 }
363                                 eDebugNoNewLine(")");
364                         }
365                         eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size());
366                         if (!program.audioStreams.empty())
367                         {
368                                 eDebugNoNewLine(" (");
369                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
370                                         i(program.audioStreams.begin());
371                                         i != program.audioStreams.end(); ++i)
372                                 {
373                                         pids_to_record.insert(i->pid);
374
375                                         if (timing_pid == -1)
376                                         {
377                                                 timing_pid = i->pid;
378                                                 timing_stream_type = i->type;
379                                                 timing_pid_type = iDVBTSRecorder::audio_pid;
380                                         }
381
382                                         if (i != program.audioStreams.begin())
383                                                 eDebugNoNewLine(", ");
384                                         eDebugNoNewLine("%04x", i->pid);
385                                 }
386                                 eDebugNoNewLine(")");
387                         }
388                         if (!program.subtitleStreams.empty())
389                         {
390                                 eDebugNoNewLine(" (");
391                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
392                                         i(program.subtitleStreams.begin());
393                                         i != program.subtitleStreams.end(); ++i)
394                                 {
395                                         pids_to_record.insert(i->pid);
396
397                                         if (i != program.subtitleStreams.begin())
398                                                 eDebugNoNewLine(", ");
399                                         eDebugNoNewLine("%04x", i->pid);
400                                 }
401                                 eDebugNoNewLine(")");
402                         }
403                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
404                         if (program.pcrPid >= 0 && program.pcrPid < 0x1fff)
405                                 pids_to_record.insert(program.pcrPid);
406                         eDebugNoNewLine(", and the text pid is %04x\n", program.textPid);
407                         if (program.textPid != -1)
408                                 pids_to_record.insert(program.textPid); // Videotext
409
410                         if (m_record_ecm)
411                         {
412                                 for (std::list<eDVBServicePMTHandler::program::capid_pair>::const_iterator i(program.caids.begin());
413                                                         i != program.caids.end(); ++i)
414                                 {
415                                         if (i->capid >= 0) pids_to_record.insert(i->capid);
416                                 }
417                         }
418
419                                 /* find out which pids are NEW and which pids are obsolete.. */
420                         std::set<int> new_pids, obsolete_pids;
421
422                         std::set_difference(pids_to_record.begin(), pids_to_record.end(),
423                                         m_pids_active.begin(), m_pids_active.end(),
424                                         std::inserter(new_pids, new_pids.begin()));
425
426                         std::set_difference(
427                                         m_pids_active.begin(), m_pids_active.end(),
428                                         pids_to_record.begin(), pids_to_record.end(),
429                                         std::inserter(obsolete_pids, obsolete_pids.begin())
430                                         );
431
432                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
433                         {
434                                 eDebug("[eDVBServiceRecord] ADD PID: %04x", *i);
435                                 m_record->addPID(*i);
436                         }
437
438                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
439                         {
440                                 eDebug("[eDVBServiceRecord] REMOVED PID: %04x", *i);
441                                 m_record->removePID(*i);
442                         }
443
444                         if (timing_pid != -1)
445                                 m_record->setTimingPID(timing_pid, timing_pid_type, timing_stream_type);
446
447                         m_pids_active = pids_to_record;
448
449                         if (m_state != stateRecording)
450                         {
451                                 m_record->start();
452                                 m_state = stateRecording;
453                         }
454                 }
455         }
456         m_error = 0;
457         m_event((iRecordableService*)this, evRecordRunning);
458         return 0;
459 }
460
461 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
462 {
463         ptr = this;
464         return 0;
465 }
466
467 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
468 {
469         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
470         return 0;
471 }
472
473 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
474 {
475         ptr = this;
476         return 0;
477 }
478
479 ePtr<iStreamData> eDVBServiceRecord::getStreamingData()
480 {
481         ePtr<iStreamData> retval;
482         eDVBServicePMTHandler::program program;
483         if (m_tuned && !m_service_handler.getProgramInfo(program))
484         {
485                 retval = new eDVBServicePMTHandler::eStreamData(program);
486         }
487
488         return retval;
489 }
490
491 void eDVBServiceRecord::recordEvent(int event)
492 {
493         switch (event)
494         {
495         case iDVBTSRecorder::eventWriteError:
496                 eWarning("[eDVBServiceRecord] record write error");
497                 stop();
498                 m_event((iRecordableService*)this, evRecordWriteError);
499                 return;
500         default:
501                 eDebug("[eDVBServiceRecord] unhandled record event %d", event);
502         }
503 }
504
505 void eDVBServiceRecord::gotNewEvent(int /*error*/)
506 {
507         ePtr<eServiceEvent> event_now;
508         m_event_handler.getEvent(event_now, 0);
509
510         if (!event_now)
511                 return;
512
513         int event_id = event_now->getEventId();
514
515         pts_t p;
516
517         if (m_record)
518         {
519                 if (m_record->getCurrentPCR(p))
520                         eDebug("[eDVBServiceRecord] getting PCR failed!");
521                 else
522                 {
523                         m_event_timestamps[event_id] = p;
524                         eDebug("[eDVBServiceRecord] pcr of eit change: %llx", p);
525                 }
526         }
527
528         if (event_id != m_last_event_id)
529                 eDebug("[eDVBServiceRecord] now running: %s (%d seconds)", event_now->getEventName().c_str(), event_now->getDuration());
530
531         m_last_event_id = event_id;
532
533         m_event((iRecordableService*)this, evNewEventInfo);
534 }
535
536 void eDVBServiceRecord::saveCutlist()
537 {
538                         /* XXX: dupe of eDVBServicePlay::saveCuesheet, refactor plz */
539         std::string filename = m_filename + ".cuts";
540
541         eDVBTSTools tstools;
542
543         if (tstools.openFile(m_filename.c_str()))
544         {
545                 eDebug("[eDVBServiceRecord] saving cutlist failed because tstools failed");
546                 return;
547         }
548
549         // If a cuts file exists, append to it (who cares about sorting it)
550         FILE *f = fopen(filename.c_str(), "a+b");
551         if (f)
552         {
553                 unsigned long long where;
554                 int what;
555
556                 for (std::map<int,pts_t>::iterator i(m_event_timestamps.begin()); i != m_event_timestamps.end(); ++i)
557                 {
558                         pts_t p = i->second;
559                         off_t offset = 0; // fixme, we need to note down both
560                         if (tstools.fixupPTS(offset, p))
561                         {
562                                 eDebug("[eDVBServiceRecord] fixing up PTS failed, not saving");
563                                 continue;
564                         }
565                         eDebug("[eDVBServiceRecord] fixed up %llx to %llx (offset %llx)", i->second, p, offset);
566                         where = htobe64(p);
567                         what = htonl(2); /* mark */
568                         fwrite(&where, sizeof(where), 1, f);
569                         fwrite(&what, sizeof(what), 1, f);
570                 }
571                 fclose(f);
572         }
573
574 }
575
576 RESULT eDVBServiceRecord::subServices(ePtr<iSubserviceList> &ptr)
577 {
578         ptr = this;
579         return 0;
580 }
581
582 int eDVBServiceRecord::getNumberOfSubservices()
583 {
584         ePtr<eServiceEvent> evt;
585         if (!m_event_handler.getEvent(evt, 0))
586                 return evt->getNumOfLinkageServices();
587         return 0;
588 }
589
590 RESULT eDVBServiceRecord::getSubservice(eServiceReference &sub, unsigned int n)
591 {
592         ePtr<eServiceEvent> evt;
593         if (!m_event_handler.getEvent(evt, 0))
594         {
595                 if (!evt->getLinkageService(sub, m_ref, n))
596                         return 0;
597         }
598         sub.type=eServiceReference::idInvalid;
599         return -1;
600 }