epg fixes
[enigma2.git] / lib / dvb / tstools.cpp
1 #define _ISOC99_SOURCE /* for llabs */
2 #include <lib/dvb/tstools.h>
3 #include <lib/base/eerror.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6
7 #include <stdio.h>
8
9 eDVBTSTools::eDVBTSTools()
10 {
11         m_pid = -1;
12         m_maxrange = 256*1024;
13         
14         m_begin_valid = 0;
15         m_end_valid = 0;
16         
17         m_use_streaminfo = 0;
18         m_samples_taken = 0;
19         
20         m_last_filelength = 0;
21         
22         m_futile = 0;
23 }
24
25 eDVBTSTools::~eDVBTSTools()
26 {
27         closeFile();
28 }
29
30 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
31 {
32         closeFile();
33         
34         if (!nostreaminfo)
35                 m_streaminfo.load((std::string(filename) + ".ap").c_str());
36         
37         if (!m_streaminfo.empty())
38                 m_use_streaminfo = 1;
39         else
40         {
41 //              eDebug("no recorded stream information available");
42                 m_use_streaminfo = 0;
43         }
44         
45         m_samples_taken = 0;
46
47         if (m_file.open(filename, 1) < 0)
48                 return -1;
49         return 0;
50 }
51
52 void eDVBTSTools::closeFile()
53 {
54         m_file.close();
55 }
56
57 void eDVBTSTools::setSyncPID(int pid)
58 {
59         m_pid = pid;
60 }
61
62 void eDVBTSTools::setSearchRange(int maxrange)
63 {
64         m_maxrange = maxrange;
65 }
66
67         /* getPTS extracts a pts value from any PID at a given offset. */
68 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
69 {
70         if (m_use_streaminfo)
71                 return m_streaminfo.getPTS(offset, pts);
72         
73         if (!m_file.valid())
74                 return -1;
75
76         offset -= offset % 188;
77         
78         if (m_file.lseek(offset, SEEK_SET) < 0)
79         {
80                 eDebug("lseek failed");
81                 return -1;
82         }
83         
84         int left = m_maxrange;
85         
86         while (left >= 188)
87         {
88                 unsigned char packet[188];
89                 if (m_file.read(packet, 188) != 188)
90                 {
91                         eDebug("read error");
92                         break;
93                 }
94                 left -= 188;
95                 offset += 188;
96                 
97                 if (packet[0] != 0x47)
98                 {
99                         eDebug("resync");
100                         int i = 0;
101                         while (i < 188)
102                         {
103                                 if (packet[i] == 0x47)
104                                         break;
105                                 ++i;
106                         }
107                         offset = m_file.lseek(i - 188, SEEK_CUR);
108                         continue;
109                 }
110                 
111                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
112                 int pusi = !!(packet[1] & 0x40);
113                 
114 //              printf("PID %04x, PUSI %d\n", pid, pusi);
115
116                 unsigned char *payload;
117                 
118                         /* check for adaption field */
119                 if (packet[3] & 0x20)
120                 {
121                         if (packet[4] >= 183)
122                                 continue;
123                         if (packet[4])
124                         {
125                                 if (packet[5] & 0x10) /* PCR present */
126                                 {
127                                         pts  = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
128                                         pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
129                                         pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
130                                         pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
131                                         pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
132                                         offset -= 188;
133                                         eDebug("PCR  found at %llx: %16llx", offset, pts);
134                                         if (fixed && fixupPTS(offset, pts))
135                                                 return -1;
136                                         return 0;
137                                 }
138                         }
139                         payload = packet + packet[4] + 4 + 1;
140                 } else
141                         payload = packet + 4;
142
143                 
144                 if (m_pid >= 0)
145                         if (pid != m_pid)
146                                 continue;
147                 if (!pusi)
148                         continue;
149                 
150                 
151                         /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
152                 if (payload[0] || payload[1] || (payload[2] != 1))
153                         continue;
154                 
155                         /* drop non-audio, non-video packets because other streams
156                            can be non-compliant.*/
157                 if (((payload[3] & 0xE0) != 0xC0) &&  // audio
158                     ((payload[3] & 0xF0) != 0xE0))    // video
159                         continue;
160                 
161                 if (payload[7] & 0x80) /* PTS */
162                 {
163                         pts  = ((unsigned long long)(payload[ 9]&0xE))  << 29;
164                         pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
165                         pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
166                         pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
167                         pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
168                         offset -= 188;
169
170 //                      eDebug("found pts %08llx at %08llx pid %02x stream: %02x", pts, offset, pid, payload[3]);
171                         
172                                 /* convert to zero-based */
173                         if (fixed && fixupPTS(offset, pts))
174                                         return -1;
175                         return 0;
176                 }
177         }
178         
179         return -1;
180 }
181
182 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
183 {
184         if (m_use_streaminfo)
185         {
186                 return m_streaminfo.fixupPTS(offset, now);
187         } else
188         {
189                         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
190                 calcBegin();
191                 if (!m_begin_valid)
192                 {       
193                         eDebug("begin not valid, can't fixup");
194                         return -1;
195                 }
196                 
197                 pts_t pos = m_pts_begin;
198                 if ((now < pos) && ((pos - now) < 90000 * 10))
199                 {       
200                         pos = 0;
201                         return 0;
202                 }
203                 
204                 if (now < pos) /* wrap around */
205                         now = now + 0x200000000LL - pos;
206                 else
207                         now -= pos;
208                 return 0;
209         }
210 }
211
212 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts)
213 {
214         if (m_use_streaminfo)
215         {
216                 offset = m_streaminfo.getAccessPoint(pts);
217                 return 0;
218         } else
219         {
220                 calcBegin(); calcEnd();
221                 
222                 if (!m_begin_valid)
223                         return -1;
224                 if (!m_end_valid)
225                         return -1;
226
227                 if (!m_samples_taken)
228                         takeSamples();
229                 
230                 if (!m_samples.empty())
231                 {
232                         int maxtries = 5;
233                         pts_t p = -1;
234                         
235                         while (maxtries--)
236                         {
237                                         /* search entry before and after */
238                                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
239                                 std::map<pts_t, off_t>::const_iterator u = l;
240
241                                 if (l != m_samples.begin())
242                                         --l;
243                                 
244                                         /* we could have seeked beyond the end */
245                                 if (u == m_samples.end())
246                                 {
247                                                 /* use last segment for interpolation. */
248                                         if (l != m_samples.begin())
249                                         {
250                                                 --u;
251                                                 --l;
252                                         }
253                                 }
254                                         
255                                         /* if we don't have enough points */
256                                 if (u == m_samples.end())
257                                         break;
258                                 
259                                 pts_t pts_diff = u->first - l->first;
260                                 off_t offset_diff = u->second - l->second;
261                                 
262                                 if (offset_diff < 0)
263                                 {
264                                         eDebug("something went wrong when taking samples.");
265                                         m_samples.clear();
266                                         takeSamples();
267                                         continue;
268                                 }
269
270                                 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
271
272                                 int bitrate;
273                                 
274                                 if (pts_diff)
275                                         bitrate = offset_diff * 90000 * 8 / pts_diff;
276                                 else
277                                         bitrate = 0;
278
279                                 offset = l->second;
280                                 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
281                                 offset -= offset % 188;
282                                 
283                                 p = pts;
284                                 
285                                 if (!takeSample(offset, p))
286                                 {
287                                         int diff = (p - pts) / 90;
288                         
289                                         eDebug("calculated diff %d ms", diff);
290                                         if (abs(diff) > 300)
291                                         {
292                                                 eDebug("diff to big, refining");
293                                                 continue;
294                                         }
295                                 } else
296                                         eDebug("no sample taken, refinement not possible.");
297
298                                 break;
299                         }
300                         
301                                 /* if even the first sample couldn't be taken, fall back. */
302                                 /* otherwise, return most refined result. */
303                         if (p != -1)
304                         {
305                                 pts = p;
306                                 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
307                                 return 0;
308                         }
309                 }
310                 
311                 int bitrate = calcBitrate();
312                 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
313                 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
314                 offset -= offset % 188;
315                 
316                 return 0;
317         }
318 }
319
320 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
321 {
322         if (m_use_streaminfo)
323                 return m_streaminfo.getNextAccessPoint(ts, start, direction);
324         else
325         {
326                 eDebug("can't get next access point without streaminfo");
327                 return -1;
328         }
329 }
330
331 void eDVBTSTools::calcBegin()
332 {
333         if (!m_file.valid())
334                 return;
335
336         if (!(m_begin_valid || m_futile))
337         {
338                 m_offset_begin = 0;
339                 if (!getPTS(m_offset_begin, m_pts_begin))
340                         m_begin_valid = 1;
341                 else
342                         m_futile = 1;
343         }
344 }
345
346 void eDVBTSTools::calcEnd()
347 {
348         if (!m_file.valid())
349                 return;
350         
351         off_t end = m_file.lseek(0, SEEK_END);
352         
353         if (llabs(end - m_last_filelength) > 1*1024*1024)
354         {
355                 m_last_filelength = end;
356                 m_end_valid = 0;
357                 
358                 m_futile = 0;
359 //              eDebug("file size changed, recalc length");
360         }
361         
362         int maxiter = 10;
363         
364         m_offset_end = m_last_filelength;
365         
366         while (!(m_end_valid || m_futile))
367         {
368                 if (!--maxiter)
369                 {
370                         m_futile = 1;
371                         return;
372                 }
373
374                 m_offset_end -= m_maxrange;
375                 if (m_offset_end < 0)
376                         m_offset_end = 0;
377
378                         /* restore offset if getpts fails */
379                 off_t off = m_offset_end;
380
381                 if (!getPTS(m_offset_end, m_pts_end))
382                         m_end_valid = 1;
383                 else
384                         m_offset_end = off;
385
386                 if (!m_offset_end)
387                 {
388                         m_futile = 1;
389                         break;
390                 }
391         }
392 }
393
394 int eDVBTSTools::calcLen(pts_t &len)
395 {
396         calcBegin(); calcEnd();
397         if (!(m_begin_valid && m_end_valid))
398                 return -1;
399         len = m_pts_end - m_pts_begin;
400                 /* wrap around? */
401         if (len < 0)
402                 len += 0x200000000LL;
403         return 0;
404 }
405
406 int eDVBTSTools::calcBitrate()
407 {
408         calcBegin(); calcEnd();
409         if (!(m_begin_valid && m_end_valid))
410                 return -1;
411
412         pts_t len_in_pts = m_pts_end - m_pts_begin;
413
414                 /* wrap around? */
415         if (len_in_pts < 0)
416                 len_in_pts += 0x200000000LL;
417         off_t len_in_bytes = m_offset_end - m_offset_begin;
418         
419         if (!len_in_pts)
420                 return -1;
421         
422         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
423         if ((bitrate < 10000) || (bitrate > 100000000))
424                 return -1;
425         
426         return bitrate;
427 }
428
429         /* pts, off */
430 void eDVBTSTools::takeSamples()
431 {
432         m_samples_taken = 1;
433         m_samples.clear();
434         pts_t dummy;
435         if (calcLen(dummy) == -1)
436                 return;
437         
438         int nr_samples = 30;
439         off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
440         if (bytes_per_sample < 40*1024*1024)
441                 bytes_per_sample = 40*1024*1024;
442
443         bytes_per_sample -= bytes_per_sample % 188;
444         
445         for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
446         {
447                 pts_t p;
448                 takeSample(offset, p);
449         }
450         m_samples[0] = m_offset_begin;
451         m_samples[m_pts_end - m_pts_begin] = m_offset_end;
452         
453 //      eDebug("begin, end: %llx %llx", m_offset_begin, m_offset_end); 
454 }
455
456         /* returns 0 when a sample was taken. */
457 int eDVBTSTools::takeSample(off_t off, pts_t &p)
458 {
459         if (!eDVBTSTools::getPTS(off, p, 1))
460         {
461                         /* as we are happily mixing PTS and PCR values (no comment, please), we might
462                            end up with some "negative" segments. 
463                            
464                            so check if this new sample is between the previous and the next field*/
465
466                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
467                 std::map<pts_t, off_t>::const_iterator u = l;
468
469                 if (l != m_samples.begin())
470                 {
471                         --l;
472                         if (u != m_samples.end())
473                         {
474                                 if ((l->second > off) || (u->second < off))
475                                 {
476                                         eDebug("ignoring sample %llx %llx %llx (%lld %lld %lld)",
477                                                 l->second, off, u->second, l->first, p, u->first);
478                                         return 1;
479                                 }
480                         }
481                 }
482
483                 
484                 m_samples[p] = off;
485                 return 0;
486         }
487         return 1;
488 }
489
490 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
491 {
492                 /* FIXME: this will be factored out soon! */
493         if (!m_file.valid())
494         {
495                 eDebug(" file not valid");
496                 return -1;
497         }
498
499         if (m_file.lseek(0, SEEK_SET) < 0)
500         {
501                 eDebug("seek failed");
502                 return -1;
503         }
504
505         int left = 5*1024*1024;
506         
507         while (left >= 188)
508         {
509                 unsigned char packet[188];
510                 if (m_file.read(packet, 188) != 188)
511                 {
512                         eDebug("read error");
513                         break;
514                 }
515                 left -= 188;
516                 
517                 if (packet[0] != 0x47)
518                 {
519                         int i = 0;
520                         while (i < 188)
521                         {
522                                 if (packet[i] == 0x47)
523                                         break;
524                                 ++i;
525                         }
526                         m_file.lseek(i - 188, SEEK_CUR);
527                         continue;
528                 }
529                 
530                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
531                 
532                 int pusi = !!(packet[1] & 0x40);
533                 
534                 if (!pusi)
535                         continue;
536                 
537                         /* ok, now we have a PES header or section header*/
538                 unsigned char *sec;
539                 
540                         /* check for adaption field */
541                 if (packet[3] & 0x20)
542                 {
543                         if (packet[4] >= 183)
544                                 continue;
545                         sec = packet + packet[4] + 4 + 1;
546                 } else
547                         sec = packet + 4;
548                 
549                 if (sec[0])     /* table pointer, assumed to be 0 */
550                         continue;
551
552                 if (sec[1] == 0x02) /* program map section */
553                 {
554                         pmt_pid = pid;
555                         service_id = (sec[4] << 8) | sec[5];
556                         return 0;
557                 }
558         }
559         
560         return -1;
561 }