Support scrambled playback.
[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 <lib/base/httpstream.h>
6 #include <lib/base/nconfig.h>
7
8 #include <fcntl.h>
9
10         /* for cutlist */
11 #include <byteswap.h>
12 #include <netinet/in.h>
13
14 #ifndef BYTE_ORDER
15 #error no byte order defined!
16 #endif
17
18 DEFINE_REF(eDVBServiceRecord);
19
20 eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref, bool isstreamclient):
21         m_ref(ref),m_is_stream_client(isstreamclient)
22 {
23         CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent);
24         CONNECT(m_event_handler.m_eit_changed, eDVBServiceRecord::gotNewEvent);
25         m_state = stateIdle;
26         m_want_record = 0;
27         m_record_ecm = false;
28         m_descramble = true;
29         m_pvr_descramble = false;
30         m_tuned = 0;
31         m_target_fd = -1;
32         m_error = 0;
33         m_streaming = 0;
34         m_simulate = false;
35         m_last_event_id = -1;
36         m_serviceType = eDVBServicePMTHandler::recording;
37 }
38
39 void eDVBServiceRecord::serviceEvent(int event)
40 {
41         eDebug("RECORD service event %d", event);
42         switch (event)
43         {
44         case eDVBServicePMTHandler::eventTuned:
45         {
46                 eDebug("tuned..");
47                 m_tuned = 1;
48
49                         /* start feeding EIT updates */
50                 ePtr<iDVBDemux> m_demux;
51                 if (!m_service_handler.getDataDemux(m_demux))
52                 {
53                         eServiceReferenceDVB &ref = (eServiceReferenceDVB&) m_ref;
54                         int sid = ref.getParentServiceID().get();
55                         if (!sid)
56                                 sid = ref.getServiceID().get();
57                         if ( ref.getParentTransportStreamID().get() &&
58                                 ref.getParentTransportStreamID() != ref.getTransportStreamID() )
59                                 m_event_handler.startOther(m_demux, sid);
60                         else
61                                 m_event_handler.start(m_demux, sid);
62                 }
63
64                 if (m_state == stateRecording && m_want_record)
65                         doRecord();
66                 m_event((iRecordableService*)this, evTunedIn);
67                 break;
68         }
69         case eDVBServicePMTHandler::eventTuneFailed:
70         {
71                 eDebug("record failed to tune");
72                 m_event((iRecordableService*)this, evTuneFailed);
73                 break;
74         }
75         case eDVBServicePMTHandler::eventNewProgramInfo:
76         {
77                 if (m_state == stateIdle)
78                         doPrepare();
79                 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
80                 {
81                         if (m_pvr_descramble)
82                                 updateDecoder();
83                         else
84                                 doRecord();
85                 }
86                 m_event((iRecordableService*)this, evNewProgramInfo);
87                 break;
88         }
89         case eDVBServicePMTHandler::eventMisconfiguration:
90                 m_error = errMisconfiguration;
91                 m_event((iRecordableService*)this, evTuneFailed);
92                 break;
93         case eDVBServicePMTHandler::eventNoResources:
94                 m_error = errNoResources;
95                 m_event((iRecordableService*)this, evTuneFailed);
96                 break;
97         case eDVBServicePMTHandler::eventEOF:
98                 m_event((iRecordableService*)this, evPvrEof);
99                 break;
100         case eDVBServicePMTHandler::eventStartPvrDescramble:
101                 if (m_want_record)
102                 {
103                         doRecord();
104                 }
105                 break;
106         }
107 }
108
109 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)
110 {
111         bool config_recording_always_ecm = ePythonConfigQuery::getConfigBoolValue("config.recording.always_ecm", false);
112         bool config_recording_never_decrypt = ePythonConfigQuery::getConfigBoolValue("config.recording.never_decrypt", false);
113         m_filename = filename;
114         m_streaming = 0;
115         m_descramble = config_recording_never_decrypt ? false : descramble;
116         m_record_ecm = config_recording_always_ecm ? true : recordecm;
117
118         // force descramble for _pvrdesc.ts
119         if (strstr(filename, "_pvrdesc.ts"))
120         {
121                 m_pvr_descramble = true;
122                 m_descramble = true;
123         }
124
125         if (m_state == stateIdle)
126         {
127                 int ret = doPrepare();
128                 if (!ret)
129                 {
130                         eServiceReferenceDVB ref = m_ref.getParentServiceReference();
131                         ePtr<eDVBResourceManager> res_mgr;
132                         eDVBMetaParser meta;
133                         std::string service_data;
134                         if (!ref.valid())
135                                 ref = m_ref;
136                         if (!eDVBResourceManager::getInstance(res_mgr))
137                         {
138                                 ePtr<iDVBChannelList> db;
139                                 if (!res_mgr->getChannelList(db))
140                                 {
141                                         ePtr<eDVBService> service;
142                                         if (!db->getService(ref, service))
143                                         {
144                                                 char tmp[255];
145                                                 sprintf(tmp, "f:%x", service->m_flags);
146                                                 service_data += tmp;
147                                                 // cached pids
148                                                 for (int x=0; x < eDVBService::cacheMax; ++x)
149                                                 {
150                                                         int entry = service->getCacheEntry((eDVBService::cacheID)x);
151                                                         if (entry != -1)
152                                                         {
153                                                                 sprintf(tmp, ",c:%02d%04x", x, entry);
154                                                                 service_data += tmp;
155                                                         }
156                                                 }
157                                         }
158                                 }
159                         }
160                         meta.m_time_create = begTime;
161                         meta.m_ref = m_ref;
162                         meta.m_data_ok = 1;
163                         meta.m_service_data = service_data;
164                         if (name)
165                                 meta.m_name = name;
166                         if (descr)
167                                 meta.m_description = descr;
168                         if (tags)
169                                 meta.m_tags = tags;
170                         meta.m_scrambled = !m_descramble;
171                         ret = meta.updateMeta(m_filename) ? -255 : 0;
172                         if (!ret)
173                         {
174                                 const eit_event_struct *event = 0;
175                                 eEPGCache::getInstance()->Lock();
176                                 if ( eit_event_id != -1 )
177                                 {
178                                         eDebug("query epg event id %d", eit_event_id);
179                                         eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
180                                 }
181                                 if ( !event && (begTime != -1 && endTime != -1) )
182                                 {
183                                         time_t queryTime = begTime + ((endTime-begTime)/2);
184                                         tm beg, end, query;
185                                         localtime_r(&begTime, &beg);
186                                         localtime_r(&endTime, &end);
187                                         localtime_r(&queryTime, &query);
188                                         eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
189                                                 beg.tm_hour, beg.tm_min, beg.tm_sec,
190                                                 end.tm_hour, end.tm_min, end.tm_sec,
191                                                 query.tm_hour, query.tm_min, query.tm_sec);
192                                         eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
193                                 }
194                                 if ( event )
195                                 {
196                                         eDebug("found event.. store to disc");
197                                         std::string fname = m_filename;
198                                         fname.erase(fname.length()-2, 2);
199                                         fname+="eit";
200                                         int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
201                                         if (fd>-1)
202                                         {
203                                                 int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
204                                                 int wr = ::write( fd, (unsigned char*)event, evLen );
205                                                 if ( wr != evLen )
206                                                         eDebug("eit write error (%m)");
207                                                 ::close(fd);
208                                         }
209                                 }
210                                 eEPGCache::getInstance()->Unlock();
211                         }
212                 }
213                 return ret;
214         }
215         return -1;
216 }
217
218 RESULT eDVBServiceRecord::prepareStreaming()
219 {
220         m_filename = "";
221         m_streaming = 1;
222         if (m_state == stateIdle)
223                 return doPrepare();
224         return -1;
225 }
226
227 RESULT eDVBServiceRecord::start(bool simulate)
228 {
229         m_simulate = simulate;
230         m_want_record = 1;
231                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
232         m_event((iRecordableService*)this, evStart);
233         return doRecord();
234 }
235
236 RESULT eDVBServiceRecord::stop()
237 {
238         if (!m_simulate)
239                 eDebug("stop recording!");
240         if (m_state == stateRecording)
241         {
242                 if (m_record)
243                         m_record->stop();
244                 if (m_target_fd >= 0)
245                 {
246                         ::close(m_target_fd);
247                         m_target_fd = -1;
248                 }
249                 
250                 saveCutlist();
251                 
252                 m_state = statePrepared;
253         } else if (!m_simulate)
254                 eDebug("(was not recording)");
255         if (m_state == statePrepared)
256         {
257                 m_record = 0;
258                 m_state = stateIdle;
259         }
260         m_event((iRecordableService*)this, evRecordStopped);
261         return 0;
262 }
263
264 int eDVBServiceRecord::doPrepare()
265 {
266                 /* allocate a ts recorder if we don't already have one. */
267         if (m_state == stateIdle)
268         {
269                 int use_decode_demux = 0;
270
271                 if (m_streaming)
272                 {
273                         m_serviceType = m_record_ecm ? eDVBServicePMTHandler::scrambled_streamserver : eDVBServicePMTHandler::streamserver;
274                 }
275                 else
276                 {
277                         m_serviceType = m_record_ecm ? eDVBServicePMTHandler::scrambled_recording : eDVBServicePMTHandler::recording;
278                 }
279
280                 m_pids_active.clear();
281                 m_state = statePrepared;
282                 ePtr<iTsSource> source;
283                 if (!m_simulate && !m_ref.path.empty())
284                 {
285                         if (m_is_stream_client)
286                         {
287                                 m_descramble = true; // assume stream is scrambled
288                                 m_record_ecm = false;
289                                 m_serviceType = eDVBServicePMTHandler::streamclient;
290                                 eHttpStream *f = new eHttpStream();
291                                 f->open(m_ref.path.c_str());
292                                 source = ePtr<iTsSource>(f);
293                         }
294                         else
295                         {
296                                 use_decode_demux = 1;
297
298                                 /* re-record a recording */
299                                 eDVBMetaParser meta;
300                                 if (!meta.parseFile(m_ref.path))
301                                 {
302                                         m_descramble = meta.m_scrambled;
303                                 }
304
305                                 if(m_pvr_descramble)
306                                 {
307                                         m_serviceType = eDVBServicePMTHandler::pvrDescramble;
308                                 }
309                                 else
310                                 {
311                                         m_serviceType = eDVBServicePMTHandler::offline;
312                                 }
313
314                                 eRawFile *f = new eRawFile();
315                                 f->open(m_ref.path.c_str());
316                                 source = ePtr<iTsSource>(f);
317                         }
318                         m_event((iRecordableService*)this, evPvrTuneStart);
319                 }
320                 else
321                 {
322                         m_event((iRecordableService*)this, evTuneStart);
323                 }
324                 return m_service_handler.tuneExt(m_ref, use_decode_demux, source, m_ref.path.c_str(), 0, m_simulate, 0, m_serviceType, m_descramble);
325         }
326         return 0;
327 }
328
329 int eDVBServiceRecord::doRecord()
330 {
331         int err = doPrepare();
332         if (err)
333         {
334                 m_error = errTuneFailed;
335                 m_event((iRecordableService*)this, evRecordFailed);
336                 return err;
337         }
338         
339         if (!m_tuned)
340                 return 0; /* try it again when we are tuned in */
341         
342         if (!m_record && m_tuned && !m_streaming && !m_simulate)
343         {
344                 if (m_pvr_descramble)
345                 {
346                         if (m_service_handler.isPmtReady())
347                         {
348                                 if (!m_service_handler.isCiConnected())
349                                 {
350                                         m_event((iRecordableService*)this, evRecordFailed);
351                                         return errNoCiConnected;
352                                 }
353                         }
354                         else
355                         {
356                                 return 0;
357                         }
358                 }
359
360                 eDebug("Recording to %s...", m_filename.c_str());
361                 ::remove(m_filename.c_str());
362                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
363                 if (fd == -1)
364                 {
365                         eDebug("eDVBServiceRecord - can't open recording file!");
366                         m_error = errOpenRecordFile;
367                         m_event((iRecordableService*)this, evRecordFailed);
368                         return errOpenRecordFile;
369                 }
370
371                         /* turn off kernel caching strategies */
372                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
373
374                 ePtr<iDVBDemux> demux;
375                 if (m_service_handler.getDataDemux(demux))
376                 {
377                         eDebug("eDVBServiceRecord - NO DEMUX available!");
378                         m_error = errNoDemuxAvailable;
379                         m_event((iRecordableService*)this, evRecordFailed);
380                         return errNoDemuxAvailable;
381                 }
382                 demux->createTSRecorder(m_record);
383                 if (!m_record)
384                 {
385                         eDebug("eDVBServiceRecord - no ts recorder available.");
386                         m_error = errNoTsRecorderAvailable;
387                         m_event((iRecordableService*)this, evRecordFailed);
388                         return errNoTsRecorderAvailable;
389                 }
390                 m_record->setTargetFD(fd);
391                 m_record->setTargetFilename(m_filename.c_str());
392                 m_record->connectEvent(slot(*this, &eDVBServiceRecord::recordEvent), m_con_record_event);
393
394                 m_target_fd = fd;
395         }
396         
397         if (m_streaming)
398         {
399                 m_state = stateRecording;
400                 eDebug("start streaming...");
401         } else
402         {
403                 eDebug("start recording...");
404
405                 eDVBServicePMTHandler::program program;
406                 if (m_service_handler.getProgramInfo(program))
407                         eDebug("getting program info failed.");
408                 else
409                 {
410                         std::set<int> pids_to_record;
411
412                         pids_to_record.insert(0); // PAT
413
414                         if (program.pmtPid != -1)
415                                 pids_to_record.insert(program.pmtPid); // PMT
416
417                         int timing_pid = -1, timing_pid_type = -1;
418
419                         eDebugNoNewLine("RECORD: have %zd video stream(s)", program.videoStreams.size());
420                         if (!program.videoStreams.empty())
421                         {
422                                 eDebugNoNewLine(" (");
423                                 for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
424                                         i(program.videoStreams.begin()); 
425                                         i != program.videoStreams.end(); ++i)
426                                 {
427                                         pids_to_record.insert(i->pid);
428                                         
429                                         if (timing_pid == -1)
430                                         {
431                                                 timing_pid = i->pid;
432                                                 timing_pid_type = i->type;
433                                         }
434                                         
435                                         if (i != program.videoStreams.begin())
436                                                         eDebugNoNewLine(", ");
437                                         eDebugNoNewLine("%04x", i->pid);
438                                 }
439                                 eDebugNoNewLine(")");
440                         }
441                         eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size());
442                         if (!program.audioStreams.empty())
443                         {
444                                 eDebugNoNewLine(" (");
445                                 for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
446                                         i(program.audioStreams.begin()); 
447                                         i != program.audioStreams.end(); ++i)
448                                 {
449                                         pids_to_record.insert(i->pid);
450         
451                                         if (timing_pid == -1)
452                                         {
453                                                 timing_pid = i->pid;
454                                                 timing_pid_type = -1;
455                                         }
456                                 
457                                         if (i != program.audioStreams.begin())
458                                                 eDebugNoNewLine(", ");
459                                         eDebugNoNewLine("%04x", i->pid);
460                                 }
461                                 eDebugNoNewLine(")");
462                         }
463                         if (!program.subtitleStreams.empty())
464                         {
465                                 eDebugNoNewLine(" (");
466                                 for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
467                                         i(program.subtitleStreams.begin());
468                                         i != program.subtitleStreams.end(); ++i)
469                                 {
470                                         pids_to_record.insert(i->pid);
471         
472                                         if (i != program.subtitleStreams.begin())
473                                                 eDebugNoNewLine(", ");
474                                         eDebugNoNewLine("%04x", i->pid);
475                                 }
476                                 eDebugNoNewLine(")");
477                         }
478                         eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
479                         if (program.pcrPid != 0x1fff)
480                                 pids_to_record.insert(program.pcrPid);
481                         eDebug(", and the text pid is %04x", program.textPid);
482                         if (program.textPid != -1)
483                                 pids_to_record.insert(program.textPid); // Videotext
484
485                         if (m_record_ecm)
486                         {
487                                 for (std::list<eDVBServicePMTHandler::program::capid_pair>::const_iterator i(program.caids.begin());
488                                                         i != program.caids.end(); ++i)
489                                 {
490                                         if (i->capid >= 0) pids_to_record.insert(i->capid);
491                                 }
492                                 pids_to_record.insert(EventInformationSection::PID);
493                                 pids_to_record.insert(TimeAndDateSection::PID);
494                         }
495
496                         {
497                                 int isCrypted = (int)program.isCrypted();
498                                 int scrambled = !m_descramble;
499                                 if (!m_descramble)
500                                 {
501                                         scrambled = isCrypted;
502                                 }
503                                 else if (!isCrypted)
504                                 {
505                                         scrambled = 0;
506                                 }
507                                 else // m_descramble && isCrypted
508                                 {
509                                         if (!m_service_handler.isCiConnected())
510                                                 scrambled = 1;
511                                         else
512                                                 scrambled = 0;
513                                 }
514                                 eDVBMetaParser dvbParser;
515                                 dvbParser.parseFile(m_filename);
516                                 dvbParser.m_scrambled = scrambled;
517                                 dvbParser.updateMeta(m_filename);
518                         }
519
520                                 /* find out which pids are NEW and which pids are obsolete.. */
521                         std::set<int> new_pids, obsolete_pids;
522
523                         std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
524                                         m_pids_active.begin(), m_pids_active.end(),
525                                         std::inserter(new_pids, new_pids.begin()));
526
527                         std::set_difference(
528                                         m_pids_active.begin(), m_pids_active.end(),
529                                         pids_to_record.begin(), pids_to_record.end(), 
530                                         std::inserter(obsolete_pids, obsolete_pids.begin())
531                                         );
532                         
533                         for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
534                         {
535                                 eDebug("ADD PID: %04x", *i);
536                                 m_record->addPID(*i);
537                         }
538
539                         for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
540                         {
541                                 eDebug("REMOVED PID: %04x", *i);
542                                 m_record->removePID(*i);
543                         }
544
545                         if (timing_pid != -1)
546                                 m_record->setTimingPID(timing_pid, timing_pid_type);
547
548                         m_pids_active = pids_to_record;
549
550                         if (m_state != stateRecording)
551                         {
552                                 m_record->start();
553                                 m_state = stateRecording;
554                         }
555                 }
556         }
557         m_error = 0;
558         m_event((iRecordableService*)this, evRecordRunning);
559         return 0;
560 }
561
562 void eDVBServiceRecord::updateDecoder()
563 {
564         int vpid = -1, vpidtype = -1, apid = -1, apidtype = -1, pcrpid = -1;
565
566         eDVBServicePMTHandler &h = m_service_handler;
567
568         eDVBServicePMTHandler::program program;
569         if (m_service_handler.getProgramInfo(program))
570                 eDebug("getting program info failed.");
571         else
572         {
573                 eDebugNoNewLine("have %zd video stream(s)", program.videoStreams.size());
574                 if (!program.videoStreams.empty())
575                 {
576                         eDebugNoNewLine(" (");
577                         for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
578                                 i(program.videoStreams.begin());
579                                 i != program.videoStreams.end(); ++i)
580                         {
581                                 if (vpid == -1)
582                                 {
583                                         vpid = i->pid;
584                                         vpidtype = i->type;
585                                 }
586                                 if (i != program.videoStreams.begin())
587                                         eDebugNoNewLine(", ");
588                                 eDebugNoNewLine("%04x", i->pid);
589                         }
590                         eDebugNoNewLine(")");
591                 }
592
593                 eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size());
594                 if (!program.audioStreams.empty())
595                 {
596                         eDebugNoNewLine(" (");
597                         for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
598                                 i(program.audioStreams.begin());
599                                 i != program.audioStreams.end(); ++i)
600                         {
601                                 if (i != program.audioStreams.begin())
602                                         eDebugNoNewLine(", ");
603                                 eDebugNoNewLine("%04x", i->pid);
604                         }
605                         eDebugNoNewLine(")");
606                 }
607
608                 apid = program.audioStreams[program.defaultAudioStream].pid;
609                 apidtype = program.audioStreams[program.defaultAudioStream].type;
610
611                 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
612                 pcrpid = program.pcrPid;
613         }
614
615         if (!m_decoder)
616         {
617                 h.getDecodeDemux(m_decode_demux);
618                 if (m_decode_demux)
619                 {
620                         m_decode_demux->getMPEGDecoder(m_decoder, 0);
621                 }
622         }
623
624         if (m_decoder)
625         {
626                 m_decoder->setVideoPID(vpid, vpidtype);
627                 m_decoder->setAudioPID(apid, apidtype);
628                 m_decoder->setSyncPCR(-1);
629                 m_decoder->play();
630         }
631 }
632
633 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
634 {
635         ptr = this;
636         return 0;
637 }
638
639 RESULT eDVBServiceRecord::connectEvent(const Slot2<void,iRecordableService*,int> &event, ePtr<eConnection> &connection)
640 {
641         connection = new eConnection((iRecordableService*)this, m_event.connect(event));
642         return 0;
643 }
644
645 RESULT eDVBServiceRecord::stream(ePtr<iStreamableService> &ptr)
646 {
647         ptr = this;
648         return 0;
649 }
650
651 extern void PutToDict(ePyObject &dict, const char*key, long val);  // defined in dvb/frontend.cpp
652
653 PyObject *eDVBServiceRecord::getStreamingData()
654 {
655         eDVBServicePMTHandler::program program;
656         if (!m_tuned || m_service_handler.getProgramInfo(program))
657         {
658                 Py_RETURN_NONE;
659         }
660
661         ePyObject r = program.createPythonObject();
662         ePtr<iDVBDemux> demux;
663         if (!m_service_handler.getDataDemux(demux))
664         {
665                 uint8_t demux_id;
666                 if (!demux->getCADemuxID(demux_id))
667                         PutToDict(r, "demux", demux_id);
668         }
669
670         return r;
671 }
672
673 void eDVBServiceRecord::recordEvent(int event)
674 {
675         switch (event)
676         {
677         case iDVBTSRecorder::eventWriteError:
678                 eWarning("[eDVBServiceRecord] record write error");
679                 stop();
680                 m_event((iRecordableService*)this, evRecordWriteError);
681                 return;
682         default:
683                 eDebug("unhandled record event %d", event);
684         }
685 }
686
687 void eDVBServiceRecord::gotNewEvent()
688 {
689         ePtr<eServiceEvent> event_now;
690         m_event_handler.getEvent(event_now, 0);
691
692         if (!event_now)
693                 return;
694
695         int event_id = event_now->getEventId();
696
697         pts_t p;
698         
699         if (m_record)
700         {
701                 if (m_record->getCurrentPCR(p))
702                         eDebug("getting PCR failed!");
703                 else
704                 {
705                         m_event_timestamps[event_id] = p;
706                         eDebug("pcr of eit change: %llx", p);
707                 }
708         }
709
710         if (event_id != m_last_event_id)
711                 eDebug("[eDVBServiceRecord] now running: %s (%d seconds)", event_now->getEventName().c_str(), event_now->getDuration());
712         
713         m_last_event_id = event_id;
714
715         m_event((iRecordableService*)this, evNewEventInfo);
716 }
717
718 void eDVBServiceRecord::saveCutlist()
719 {
720                         /* XXX: dupe of eDVBServicePlay::saveCuesheet, refactor plz */
721         std::string filename = m_filename + ".cuts";
722
723         eDVBTSTools tstools;
724         
725         if (tstools.openFile(m_filename.c_str()))
726         {
727                 eDebug("[eDVBServiceRecord] saving cutlist failed because tstools failed");
728                 return;
729         }
730         
731         FILE *f = fopen(filename.c_str(), "wb");
732
733         if (f)
734         {
735                 unsigned long long where;
736                 int what;
737
738                 for (std::map<int,pts_t>::iterator i(m_event_timestamps.begin()); i != m_event_timestamps.end(); ++i)
739                 {
740                         pts_t p = i->second;
741                         off_t offset = 0; // fixme, we need to note down both
742                         if (tstools.fixupPTS(offset, p))
743                         {
744                                 eDebug("[eDVBServiceRecord] fixing up PTS failed, not saving");
745                                 continue;
746                         }
747                         eDebug("fixed up %llx to %llx (offset %llx)", i->second, p, offset);
748 #if BYTE_ORDER == BIG_ENDIAN
749                         where = p;
750 #else
751                         where = bswap_64(p);
752 #endif
753                         what = htonl(2); /* mark */
754                         fwrite(&where, sizeof(where), 1, f);
755                         fwrite(&what, sizeof(what), 1, f);
756                 }
757                 fclose(f);
758         }
759         
760 }
761
762 RESULT eDVBServiceRecord::subServices(ePtr<iSubserviceList> &ptr)
763 {
764         ptr = this;
765         return 0;
766 }
767
768 int eDVBServiceRecord::getNumberOfSubservices()
769 {
770         ePtr<eServiceEvent> evt;
771         if (!m_event_handler.getEvent(evt, 0))
772                 return evt->getNumOfLinkageServices();
773         return 0;
774 }
775
776 RESULT eDVBServiceRecord::getSubservice(eServiceReference &sub, unsigned int n)
777 {
778         ePtr<eServiceEvent> evt;
779         if (!m_event_handler.getEvent(evt, 0))
780         {
781                 if (!evt->getLinkageService(sub, m_ref, n))
782                         return 0;
783         }
784         sub.type=eServiceReference::idInvalid;
785         return -1;
786 }