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