record dvb subtitles
[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
5 #include <fcntl.h>
6
7 DEFINE_REF(eDVBServiceRecord);
8
9 eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref): m_ref(ref)
10 {
11         CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent);
12         m_state = stateIdle;
13         m_want_record = 0;
14         m_tuned = 0;
15         m_target_fd = -1;
16 }
17
18 void eDVBServiceRecord::serviceEvent(int event)
19 {
20         eDebug("RECORD service event %d", event);
21         switch (event)
22         {
23         case eDVBServicePMTHandler::eventTuned:
24         {
25                 eDebug("tuned..");
26                 m_tuned = 1;
27                 if (m_state == stateRecording && m_want_record)
28                         doRecord();
29                 break;
30         }
31         case eDVBServicePMTHandler::eventNewProgramInfo:
32         {
33                 if (m_state == stateIdle)
34                         doPrepare();
35                 else if (m_want_record) /* doRecord can be called from Prepared and Recording state */
36                         doRecord();
37                 break;
38         }
39         }
40 }
41
42 RESULT eDVBServiceRecord::prepare(const char *filename, time_t begTime, time_t endTime, int eit_event_id)
43 {
44         m_filename = filename;
45         if (m_state == stateIdle)
46         {
47                 int ret = doPrepare();
48                 if (!ret)
49                 {
50                         eEPGCache::getInstance()->Lock();
51                         const eit_event_struct *event = 0;
52                         eServiceReferenceDVB ref = m_ref.getParentServiceReference();
53                         if (!ref.valid())
54                                 ref = m_ref;
55                         if ( eit_event_id != -1 )
56                         {
57                                 eDebug("query epg event id %d", eit_event_id);
58                                 eEPGCache::getInstance()->lookupEventId(ref, eit_event_id, event);
59                         }
60                         if ( !event && (begTime != -1 && endTime != -1) )
61                         {
62                                 time_t queryTime = begTime + ((endTime-begTime)/2);
63                                 tm beg, end, query;
64                                 localtime_r(&begTime, &beg);
65                                 localtime_r(&endTime, &end);
66                                 localtime_r(&queryTime, &query);
67                                 eDebug("query stime %d:%d:%d, etime %d:%d:%d, qtime %d:%d:%d",
68                                         beg.tm_hour, beg.tm_min, beg.tm_sec,
69                                         end.tm_hour, end.tm_min, end.tm_sec,
70                                         query.tm_hour, query.tm_min, query.tm_sec);
71                                 eEPGCache::getInstance()->lookupEventTime(ref, queryTime, event);
72                         }
73                         if ( event )
74                         {
75                                 eDebug("found event.. store to disc");
76                                 std::string fname = filename;
77                                 fname.erase(fname.length()-2, 2);
78                                 fname+="eit";
79                                 int fd = open(fname.c_str(), O_CREAT|O_WRONLY, 0777);
80                                 if (fd>-1)
81                                 {
82                                         int evLen=HILO(event->descriptors_loop_length)+12/*EIT_LOOP_SIZE*/;
83                                         int wr = ::write( fd, (unsigned char*)event, evLen );
84                                         if ( wr != evLen )
85                                                 eDebug("eit write error (%m)");
86                                         ::close(fd);
87                                 }
88                         }
89                         eEPGCache::getInstance()->Unlock();
90                 }
91                 return ret;
92         }
93         else
94                 return -1;
95 }
96
97 RESULT eDVBServiceRecord::start()
98 {
99         m_want_record = 1;
100                 /* when tune wasn't yet successfully, doRecord stays in "prepared"-state which is fine. */
101         return doRecord();
102 }
103
104
105 RESULT eDVBServiceRecord::stop()
106 {
107         eDebug("stop recording!!");
108         if (m_state == stateRecording)
109         {
110                 if (m_record)
111                         m_record->stop();
112                 if (m_target_fd >= 0)
113                 {
114                         ::close(m_target_fd);
115                         m_target_fd = -1;
116                 }
117                 m_state = statePrepared;
118         }
119         
120         if (m_state == statePrepared)
121         {
122                 m_record = 0;
123                 m_state = stateIdle;
124         }
125         return 0;
126 }
127
128
129 int eDVBServiceRecord::doPrepare()
130 {
131                 /* allocate a ts recorder if we don't already have one. */
132         if (m_state == stateIdle)
133         {
134                 m_pids_active.clear();
135                 m_state = statePrepared;
136                 return m_service_handler.tune(m_ref, 0);
137         }
138         return 0;
139 }
140
141 int eDVBServiceRecord::doRecord()
142 {
143         int err = doPrepare();
144         if (err)
145                 return err;
146         
147         if (!m_tuned)
148                 return 0; /* try it again when we are tuned in */
149         
150         if (!m_record && m_tuned)
151         {
152
153                 eDebug("Recording to %s...", m_filename.c_str());
154                 ::remove(m_filename.c_str());
155                 int fd = ::open(m_filename.c_str(), O_WRONLY|O_CREAT|O_LARGEFILE, 0644);
156                 if (fd == -1)
157                 {
158                         eDebug("eDVBServiceRecord - can't open recording file!");
159                         return -1;
160                 }
161                 
162                         /* turn off kernel caching strategies */
163                 posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
164                 
165                 ePtr<iDVBDemux> demux;
166                 if (m_service_handler.getDataDemux(demux))
167                 {
168                         eDebug("eDVBServiceRecord - NO DEMUX available!");
169                         return -2;
170                 }
171                 demux->createTSRecorder(m_record);
172                 if (!m_record)
173                 {
174                         eDebug("eDVBServiceRecord - no ts recorder available.");
175                         return -3;
176                 }
177                 m_record->setTargetFD(fd);
178                 m_record->setTargetFilename(m_filename.c_str());
179                 m_target_fd = fd;
180         }
181         eDebug("starting recording..");
182         
183         eDVBServicePMTHandler::program program;
184         if (m_service_handler.getProgramInfo(program))
185                 eDebug("getting program info failed.");
186         else
187         {
188                 std::set<int> pids_to_record;
189                 
190                 pids_to_record.insert(0); // PAT
191                 
192                 if (program.pmtPid != -1)
193                         pids_to_record.insert(program.pmtPid); // PMT
194                 
195                 int timing_pid = -1;
196                 
197                 eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size());
198                 if (!program.videoStreams.empty())
199                 {
200                         eDebugNoNewLine(" (");
201                         for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
202                                 i(program.videoStreams.begin()); 
203                                 i != program.videoStreams.end(); ++i)
204                         {
205                                 pids_to_record.insert(i->pid);
206                                 
207                                 if (timing_pid == -1)
208                                         timing_pid = i->pid;
209                                 
210                                 if (i != program.videoStreams.begin())
211                                         eDebugNoNewLine(", ");
212                                 eDebugNoNewLine("%04x", i->pid);
213                         }
214                         eDebugNoNewLine(")");
215                 }
216                 eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
217                 if (!program.audioStreams.empty())
218                 {
219                         eDebugNoNewLine(" (");
220                         for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
221                                 i(program.audioStreams.begin()); 
222                                 i != program.audioStreams.end(); ++i)
223                         {
224                                 pids_to_record.insert(i->pid);
225
226                                 if (timing_pid == -1)
227                                         timing_pid = i->pid;
228                                 
229                                 if (i != program.audioStreams.begin())
230                                         eDebugNoNewLine(", ");
231                                 eDebugNoNewLine("%04x", i->pid);
232                         }
233                         eDebugNoNewLine(")");
234                 }
235                 if (!program.subtitleStreams.empty())
236                 {
237                         eDebugNoNewLine(" (");
238                         for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
239                                 i(program.subtitleStreams.begin());
240                                 i != program.subtitleStreams.end(); ++i)
241                         {
242                                 pids_to_record.insert(i->pid);
243
244                                 if (i != program.subtitleStreams.begin())
245                                         eDebugNoNewLine(", ");
246                                 eDebugNoNewLine("%04x", i->pid);
247                         }
248                         eDebugNoNewLine(")");
249                 }
250                 eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
251                 if (program.pcrPid != 0x1fff)
252                         pids_to_record.insert(program.pcrPid);
253                 eDebug(", and the text pid is %04x", program.textPid);
254                 if (program.textPid != -1)
255                         pids_to_record.insert(program.textPid); // Videotext
256
257                         /* find out which pids are NEW and which pids are obsolete.. */
258                 std::set<int> new_pids, obsolete_pids;
259                 
260                 std::set_difference(pids_to_record.begin(), pids_to_record.end(), 
261                                 m_pids_active.begin(), m_pids_active.end(),
262                                 std::inserter(new_pids, new_pids.begin()));
263                 
264                 std::set_difference(
265                                 m_pids_active.begin(), m_pids_active.end(),
266                                 pids_to_record.begin(), pids_to_record.end(), 
267                                 std::inserter(new_pids, new_pids.begin())
268                                 );
269                 
270                 for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
271                 {
272                         eDebug("ADD PID: %04x", *i);
273                         m_record->addPID(*i);
274                 }
275                 for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
276                 {
277                         eDebug("REMOVED PID: %04x", *i);
278                         m_record->removePID(*i);
279                 }
280                 
281                 if (timing_pid != -1)
282                         m_record->setTimingPID(timing_pid);
283                 
284                 m_pids_active = pids_to_record;
285                 
286                 if (m_state != stateRecording)
287                 {
288                         m_record->start();
289                         m_state = stateRecording;
290                 }
291         }
292         return 0;
293 }
294
295 RESULT eDVBServiceRecord::frontendInfo(ePtr<iFrontendInformation> &ptr)
296 {
297         ptr = this;
298         return 0;
299 }