VPS-Plugin: initial checkin
[enigma2-plugins.git] / vps / src_cc / vps.cc
1 // Aufruf mit vps [demux] [mode] [onid] [tsid] [sid] [Event-ID] [Timer-ID] [PDC-Time day] [PDC-Time month] [PDC-Time hour] [PDC-Time min]
2 //
3 // mode
4 // 0 Event überwachen
5 // 1 in "other transport stream, present/following" nach Event suchen und bei running_status 2 <= x <= 4 beenden
6 // 2 nach PDC-Zeit suchen und Event-ID zurückgeben
7 // 3 in "other transport stream, present/following" nach PDC suchen und Event-ID zurückgeben
8 // 4 in allen EIT-Tabellen nach PDC suchen und alle gefundenen Event-IDs zurückgeben, ausschließlich der Event-ID im Aufruf
9 // 10 prüfen, ob überhaupt PDC vorhanden
10 //
11
12
13 #include <sys/ioctl.h>
14 #include <fcntl.h>
15 #include <linux/dvb/dmx.h>
16 #include <signal.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <iostream>
20 #include <cstdlib>
21 #include <time.h>
22 #include <set>
23
24 using std::cout;
25 using std::endl;
26 using std::atoi;
27 using std::flush;
28 using std::set;
29
30
31
32 #define READ_BUF_SIZE (8*1024)
33
34 int main(int argc, char *argv[]);
35
36 // 0x12 EIT-PID
37 // 0x4e EIT - actual transport stream, present/following
38 int open_read_demux(unsigned short pid = 0x12, u_char table_id = 0x4e, u_char table_mask = 0xff, int timeout = 15000);
39
40 void process_eit(u_char *b, u_char table_id, u_char table_mask);
41 void process_monitoring(u_char *b, int section_length, int section_number);
42 void process_monitoring2(u_char *b, int section_length, int section_number);
43 void process_search_pdc(u_char *b, int section_length, int section_number);
44 void process_search_multiple_pdc(u_char *b, int section_length, int section_number);
45 void process_search_pdc_available(u_char *b, int section_length, int section_number);
46
47 inline void setNowNext(int section_number, int nevent);
48
49 static long sect_read(int fd, u_char *buf, long buflen);
50 unsigned long getBits(u_char *buf, int byte_offset, int startbit, int bitlen);
51 //time_t time_mjd_utc(u_long mjd, u_long utc);
52 //time_t time_utc(u_long utc);
53
54 void abort_program(int signal);
55
56 const time_t max_wait_time = 20 * 60;
57
58 // vars
59 bool isAbort = false;
60 char *f_demux;
61 char mode = 0;
62 int pdc_time = 0;
63 unsigned short onid = 0;
64 unsigned short tsid = 0;
65 unsigned short sid = 0;
66 unsigned short event_id = 0;
67 unsigned short timer_id = 0;
68
69 unsigned short service_event_now = 0;
70 unsigned short service_event_next = 0;
71 bool service_event_checked_now = false;
72 bool service_event_checked_next = false;
73
74 //int event_last_start_time_MJD = -1;
75 //int event_last_start_time_UTC = -1;
76 //int event_last_duration = -1;
77 char event_last_running_status = -1;
78
79 time_t received_event_last_time;
80 set<unsigned short> pdc_exclude_event_ids;
81
82
83 int main(int argc, char *argv[])
84 {
85         signal(SIGINT, abort_program);
86         signal(SIGTERM, abort_program);
87
88         if (argc < 8)
89         {
90                 cout << "too few arguments" << endl;
91                 return 0;
92         }
93         
94         f_demux = argv[1];
95         mode = atoi(argv[2]);
96         onid = atoi(argv[3]);
97         tsid = atoi(argv[4]);
98         sid = atoi(argv[5]);
99         event_id = atoi(argv[6]);
100         timer_id = atoi(argv[7]);
101         
102         if (mode > 1 && mode < 10 && argc >= 12)
103         {
104                 pdc_time = atoi(argv[8]) << 15; // day
105                 pdc_time += (atoi(argv[9]) << 11); // month
106                 pdc_time += (atoi(argv[10]) << 6); // hour
107                 pdc_time += atoi(argv[11]); // minute
108         }
109         
110         int n = 0;
111         
112         // Startzeit
113         time(&received_event_last_time);
114         
115         if (mode == 0 || mode == 2)
116         {
117                 n = open_read_demux();
118         }
119         else if (mode == 1 || mode == 3)
120         {
121                 n = open_read_demux(18, 0x4f, 0xff, 30000);
122                 if (n == -2)
123                         n = open_read_demux(3218, 0x4f, 0xff, 30000); // Kabel Deutschland
124         }
125         else if (mode == 4)
126         {
127                 if (event_id > 0)
128                         pdc_exclude_event_ids.insert(event_id);
129                 
130                 n = open_read_demux(18, 0x4f, 0x00);
131         }
132         else if (mode == 10)
133         {
134                 n = open_read_demux(18, 0x4e, 0xff, 6000);
135         }
136         
137         if (n == -2)
138                 cout << timer_id << " DMX_ERROR_TIMEOUT\n" << flush;
139         
140         return 0;
141 }
142
143 int open_read_demux(unsigned short pid, u_char table_id, u_char table_mask, int timeout)
144 {
145         // vorbereiten
146         int fd;
147         u_char buf[READ_BUF_SIZE];
148         bool timeout_error = false;
149         
150         if ((fd = open(f_demux, O_RDWR)) < 0)
151         {
152       return -1;
153   }
154   
155   struct dmx_sct_filter_params flt;
156   memset(&flt, 0, sizeof (struct dmx_sct_filter_params));
157   
158   flt.pid = pid;
159   
160   flt.filter.filter[0] = table_id;
161   flt.filter.mask[0] = table_mask;
162   
163   // Service-ID als Filter setzen
164   flt.filter.filter[1] = sid >> 8;
165   flt.filter.mask[1] = 0xff;
166   flt.filter.filter[2] = sid & 0xff;
167   flt.filter.mask[2] = 0xff;
168   
169   
170   flt.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
171   flt.timeout = timeout;
172   
173   if (ioctl(fd, DMX_SET_FILTER, &flt) < 0)
174         {
175                 cout << "DMX_SET_FILTER_ERROR" << endl;
176                 close(fd);
177                 return -1;
178   }
179   
180   
181   // lesen
182   while (!isAbort)
183   {
184                 long n;
185                 
186                 n = sect_read(fd, buf, sizeof(buf));
187                 
188                 if (n == 0) continue;
189                 
190                 if (n < 0)
191                 {
192                         if (errno == ETIMEDOUT)
193                         {
194                                 timeout_error = true;
195                                 break;
196                         }
197                         
198                         continue;
199                 }
200                 
201                 // Daten auswerten
202                 process_eit(buf, table_id, table_mask);
203         }
204   
205   // schließen
206   ioctl(fd, DMX_STOP, 0);
207   close(fd);
208   
209   if (timeout_error)
210         return -2;
211   
212   return 0;
213   
214 }
215
216
217 void process_eit(u_char *b, u_char table_id, u_char table_mask)
218 {
219         // überprüfe Table-ID
220         if ((b[0] & table_mask) != (table_id & table_mask))
221                 return;
222         
223         // überprüfe SID, TSID, ONID
224         if (getBits(b, 0, 24, 16) != sid)
225                 return;
226         if (getBits(b, 0, 80, 16) != onid)
227                 return;
228         if (getBits(b, 0, 64, 16) != tsid)
229                 return;
230         
231         // current_next_indicator
232         if (getBits(b, 0, 47, 1) == 0)
233                 return;
234                 
235         int section_length = getBits(b, 0, 12, 12);
236         int section_number = getBits(b, 0, 48, 8);
237         
238         
239         b += 14;
240         if (mode == 0)
241                 process_monitoring(b, section_length, section_number);
242         else if (mode == 1)
243                 process_monitoring2(b, section_length, section_number);
244         else if (mode == 2 || mode == 3)
245                 process_search_pdc(b, section_length, section_number);
246         else if (mode == 4)
247                 process_search_multiple_pdc(b, section_length, section_number);
248         else if (mode == 10)
249                 process_search_pdc_available(b, section_length, section_number);
250         
251         cout << flush;
252 }
253
254 inline void setNowNext(int section_number, int nevent)
255 {
256         if (section_number == 0)
257         {       
258                 if (service_event_now != nevent)
259                 {
260                         service_event_now = nevent;
261                 }
262         }
263         else if (section_number == 1)
264         {
265                 if (service_event_next != nevent)
266                 {
267                         service_event_next = nevent;
268                 }
269         }
270         
271         if (service_event_now != event_id && service_event_next != event_id && event_last_running_status >= 0)
272         {
273                 if (section_number == 0)
274                         service_event_checked_now = true;
275                 else if (section_number == 1)
276                         service_event_checked_next = true;
277                 
278                 if (service_event_checked_now && service_event_checked_next)
279                 {
280                         cout << timer_id << " EVENT_ENDED \n" << flush;
281                         //event_last_running_status = -1;
282                         abort_program(1);
283                 }
284         }
285         else if ((service_event_checked_now || service_event_checked_next) && (service_event_now == event_id || service_event_next == event_id))
286         {
287                 service_event_checked_now = false;
288                 service_event_checked_next = false;
289         }
290 }
291
292 void process_monitoring(u_char *b, int section_length, int section_number)
293 {
294         // header data after length value
295         if ((section_length - 11) <= 16) // 12 Bytes Event-Header + 4 Bytes CRC
296         {
297                 setNowNext(section_number, 0);
298                 return;
299         }
300         
301         int n_event_id = getBits(b, 0, 0, 16);
302         setNowNext(section_number, n_event_id);
303         
304         if (n_event_id != event_id)
305         {       
306                 time_t newtime;
307                 time(&newtime);
308                 if ((newtime - received_event_last_time) > max_wait_time)
309                 {
310                         cout << timer_id << " EVENT_ENDED TIMEOUT " << (newtime - received_event_last_time) << "\n" << flush;
311                         abort_program(1);
312                 }
313                 return;
314         }
315         
316         // aktualisiere Zeit
317         time(&received_event_last_time);
318         
319         //int start_time_MJD = getBits(b, 0, 16, 16);
320         //int start_time_UTC = getBits(b, 0, 32, 24);
321   //int duration = getBits(b, 0, 56, 24);
322   char running_status = getBits(b, 0, 80, 3);
323   
324   if (running_status != event_last_running_status)
325   {
326         cout << timer_id << " RUNNING_STATUS " << int(running_status) << " " << ((section_number) ? "FOLLOWING" : "PRESENT")<< "\n" << flush;
327                 event_last_running_status = running_status;
328   }
329 }
330
331 void process_monitoring2(u_char *b, int section_length, int section_number)
332 {
333         // header data after length value
334         if ((section_length - 11) <= 16) // 12 Bytes Event-Header + 4 Bytes CRC
335                 return;
336         
337         int n_event_id = getBits(b, 0, 0, 16);
338         
339         if (n_event_id != event_id)
340         {       
341                 time_t newtime;
342                 time(&newtime);
343                 if ((newtime - received_event_last_time) > max_wait_time)
344                 {
345                         cout << timer_id << " TIMEOUT\n" << flush;
346                         abort_program(1);
347                 }
348                 return;
349         }
350                 
351         // aktualisiere Zeit
352         time(&received_event_last_time);
353         
354         //int start_time_MJD = getBits(b, 0, 16, 16);
355         //int start_time_UTC = getBits(b, 0, 32, 24);
356   //int duration = getBits(b, 0, 56, 24);
357   char running_status = getBits(b, 0, 80, 3);
358   
359   if (running_status >= 2 && running_status <= 4)
360   {
361         cout << timer_id << " OTHER_TS_RUNNING_STATUS " << int(running_status) << "\n" << flush;
362                 abort_program(1);
363   }
364 }
365
366 void process_search_pdc(u_char *b, int section_length, int section_number)
367 {
368         time_t newtime;
369         time(&newtime);
370         if ((newtime - received_event_last_time) > max_wait_time)
371         {
372                 cout << timer_id << " TIMEOUT\n" << flush;
373                 abort_program(1);
374         }
375         
376         // header data after length value
377         if ((section_length - 11) <= 16) // 12 Bytes Event-Header + 4 Bytes CRC
378                 return;
379         
380         int n_event_id = getBits(b, 0, 0, 16);
381         
382         int descriptors_loop_length = getBits(b, 0, 84, 12);
383         b += 12;
384         while (descriptors_loop_length > 0)
385         {
386                 if (getBits(b, 0, 0, 8) == 105) // PDC-Descriptor
387                 {
388                         if (getBits(b, 0, 20, 20) == pdc_time)
389                         {
390                                 cout << timer_id << " PDC_FOUND_EVENT_ID " << n_event_id << "\n" << flush;
391                                 abort_program(1);
392                                 return;
393                         }
394                 }
395                 
396                 int desc_length = getBits(b, 0, 8, 8) + 2;
397                 b += desc_length;
398                 descriptors_loop_length -= desc_length;
399         }
400 }
401
402 void process_search_multiple_pdc(u_char *b, int section_length, int section_number)
403 {
404         time_t newtime;
405         time(&newtime);
406         if ((newtime - received_event_last_time) >= 45)
407         {
408                 //cout << timer_id << " SEARCH_DONE\n" << flush;
409                 abort_program(1);
410                 return;
411         }
412         
413         // header data after length value
414         if ((section_length - 11) <= 16) // 12 Bytes Event-Header + 4 Bytes CRC
415                 return;
416         
417         
418         int len1 = section_length - 11;
419         while (len1 > 4)
420         {
421                 int n_event_id = getBits(b, 0, 0, 16);
422                 //u_long start_time_MJD = getBits(b, 0, 16, 16);
423                 //u_long start_time_UTC = getBits(b, 0, 32, 24);
424           //u_long duration = getBits(b, 0, 56, 24);
425                 int descriptors_loop_length = getBits(b, 0, 84, 12);
426                 
427                 len1 -= 12 + descriptors_loop_length;
428                 b += 12;
429                 while (descriptors_loop_length > 0)
430                 {
431                         if (getBits(b, 0, 0, 8) == 105) // PDC-Descriptor
432                         {
433                                 if (getBits(b, 0, 20, 20) == pdc_time && pdc_exclude_event_ids.count(n_event_id) == 0)
434                                 {
435                                         pdc_exclude_event_ids.insert(n_event_id);
436                                         cout << timer_id << " PDC_MULTIPLE_FOUND_EVENT " << n_event_id << "\n" << flush;
437                                 }
438                         }
439                         
440                         int desc_length = getBits(b, 0, 8, 8) + 2;
441                         b += desc_length;
442                         descriptors_loop_length -= desc_length;
443                 }
444         }
445 }
446
447 void process_search_pdc_available(u_char *b, int section_length, int section_number)
448 {
449         time_t newtime;
450         time(&newtime);
451         if ((newtime - received_event_last_time) > 6)
452         {
453                 cout << "NO_PDC_AVAILABLE\n" << flush;
454                 abort_program(1);
455                 return;
456         }
457         
458         // header data after length value
459         if ((section_length - 11) <= 16) // 12 Bytes Event-Header + 4 Bytes CRC
460                 return;
461         
462         bool found_pdc = false;
463         
464         int descriptors_loop_length = getBits(b, 0, 84, 12);
465         b += 12;
466         while (descriptors_loop_length > 0)
467         {
468                 if (getBits(b, 0, 0, 8) == 105) // PDC-Descriptor
469                 {
470                         found_pdc = true;
471                 }
472                 
473                 int desc_length = getBits(b, 0, 8, 8) + 2;
474                 b += desc_length;
475                 descriptors_loop_length -= desc_length;
476         }
477         
478         if (found_pdc)
479                 cout << "PDC_AVAILABLE\n" << flush;
480         else
481                 cout << "NO_PDC_AVAILABLE\n" << flush;
482         
483         abort_program(1);
484
485 }
486
487
488 /**
489  * sect_read und getBits übernommen von dvbsnoop
490  * http://dvbsnoop.sourceforge.net/
491  **/  
492
493 static long sect_read (int fd, u_char *buf, long buflen)
494 {
495         int n;
496         int sect_len;
497
498         n = read(fd, buf, 3);                           // read section header
499         if (n <= 0) return n;                   // error or strange, abort
500
501         // section size
502         // -- table_id  8  uimsbf
503         // -- some stuff        4  bits
504         // -- sectionlength 12 uimsbf
505
506         sect_len = ((buf[1] & 0x0F) << 8) + buf[2];     // get section size
507         if (sect_len > (buflen-3)) return -1;   // something odd?
508
509         n = read(fd, buf+3, sect_len);          // read 1 section
510         if (n >=0) n += 3;                              // we already read header bytes
511
512         return n;
513 }
514
515 unsigned long getBits(u_char *buf, int byte_offset, int startbit, int bitlen)
516 {
517         u_char *b;
518         unsigned long  v;
519         unsigned long mask;
520         unsigned long tmp_long;
521         int bitHigh;
522
523
524         b = &buf[byte_offset + (startbit >> 3)];
525         startbit %= 8;
526
527         switch ((bitlen-1) >> 3)
528         {
529                 case -1:        // -- <=0 bits: always 0
530                 return 0L;
531                 break;
532
533         case 0:         // -- 1..8 bit
534                 tmp_long = (unsigned long)(
535                         (*(b  )<< 8) +  *(b+1) );
536                 bitHigh = 16;
537                 break;
538
539         case 1:         // -- 9..16 bit
540                 tmp_long = (unsigned long)(
541                         (*(b  )<<16) + (*(b+1)<< 8) +  *(b+2) );
542                 bitHigh = 24;
543                 break;
544
545         case 2:         // -- 17..24 bit
546                 tmp_long = (unsigned long)(
547                         (*(b  )<<24) + (*(b+1)<<16) +
548                         (*(b+2)<< 8) +  *(b+3) );
549                 bitHigh = 32;
550                 break;
551
552         default:        // -- 33.. bits: fail, deliver constant fail value
553                 //out_nl (1," Error: getBits() request out of bound!!!! (report!!) \n");
554                 return (unsigned long) 0xFEFEFEFE;
555                 break;
556         }
557
558         startbit = bitHigh - startbit - bitlen;
559         tmp_long = tmp_long >> startbit;
560         mask     = (1ULL << bitlen) - 1;  // 1ULL !!!
561         v        = tmp_long & mask;
562
563  return v;
564 }
565
566
567 void abort_program(int signal)
568 {
569         isAbort = true;
570 }