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