get rid of hardcoded TS packetsize
[openblackhole/openblackhole-enigma2.git] / lib / dvb / tstools.cpp
1 #include <lib/dvb/tstools.h>
2 #include <lib/dvb/specs.h>
3 #include <lib/base/eerror.h>
4 #include <lib/base/cachedtssource.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8 #include <stdio.h>
9
10 static const int m_maxrange = 256*1024;
11
12 DEFINE_REF(eTSFileSectionReader);
13
14 eTSFileSectionReader::eTSFileSectionReader(eMainloop *context)
15 {
16         sectionSize = 0;
17 }
18
19 eTSFileSectionReader::~eTSFileSectionReader()
20 {
21 }
22
23 void eTSFileSectionReader::data(unsigned char *packet, unsigned int size)
24 {
25         if (sectionSize + size <= sizeof(sectionData))
26         {
27                 memcpy(&sectionData[sectionSize], packet, size);
28                 sectionSize += size;
29         }
30         if (sectionSize >= (unsigned int)(3 + ((sectionData[1] & 0x0f) << 8) + sectionData[2]))
31         {
32                 sectionSize = 0;
33                 read(sectionData);
34         }
35 }
36
37 RESULT eTSFileSectionReader::start(const eDVBSectionFilterMask &mask)
38 {
39         sectionSize = 0;
40         return 0;
41 }
42
43 RESULT eTSFileSectionReader::stop()
44 {
45         sectionSize = 0;
46         return 0;
47 }
48
49 RESULT eTSFileSectionReader::connectRead(const Slot1<void,const uint8_t*> &r, ePtr<eConnection> &conn)
50 {
51         conn = new eConnection(this, read.connect(r));
52         return 0;
53 }
54
55 eDVBTSTools::eDVBTSTools():
56         m_pid(-1),
57         m_packet_size(188),
58         m_begin_valid (0),
59         m_end_valid(0),
60         m_samples_taken(0),
61         m_last_filelength(0),
62         m_futile(0)
63 {
64 }
65
66 void eDVBTSTools::closeSource()
67 {
68         m_source = NULL;
69         m_packet_size = 188;
70 }
71
72 eDVBTSTools::~eDVBTSTools()
73 {
74         closeSource();
75 }
76
77 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
78 {
79         eRawFile *f = new eRawFile();
80         ePtr<iTsSource> src = f;
81
82         if (f->open(filename) < 0)
83                 return -1;
84
85         src = new eCachedSource(src);
86
87         setSource(src, nostreaminfo ? NULL : filename);
88
89         return 0;
90 }
91
92 void eDVBTSTools::setSource(ePtr<iTsSource> &source, const char *stream_info_filename)
93 {
94         closeSource();
95         m_source = source;
96         if (stream_info_filename)
97         {
98                 eDebug("[eDVBTSTools] setSource loading streaminfo for %s", stream_info_filename);
99                 m_streaminfo.load(stream_info_filename);
100         }
101         m_samples_taken = 0;
102         m_packet_size = m_source ? m_source->getPacketSize() : 188;
103 }
104
105         /* getPTS extracts a pts value from any PID at a given offset. */
106 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
107 {
108         if (m_streaminfo.getPTS(offset, pts) == 0)
109                 return 0; // Okay, the cache had it
110
111         if (m_streaminfo.hasStructure())
112         {
113                 off_t local_offset = offset;
114                 unsigned long long data;
115                 if (m_streaminfo.getStructureEntryFirst(local_offset, data) == 0)
116                 {
117                         for(int retries = 8; retries != 0; --retries)
118                         {
119                                 if ((data & 0x1000000) != 0)
120                                 {
121                                         pts = data >> 31;
122                                         if (pts == 0)
123                                         {
124                                                 // obsolete data that happens to have a '1' there
125                                                 continue;
126                                         }
127                                         eDebug("[eDVBTSTools] getPTS got it from sc file offset=%llu pts=%llu", local_offset, pts);
128                                         if (fixed && fixupPTS(local_offset, pts))
129                                         {
130                                                 eDebug("[eDVBTSTools]    But failed to fixup!");
131                                                 break;
132                                         }
133                                         offset = local_offset;
134                                         return 0;
135                                 }
136                                 else
137                                 {
138                                         eDebug("[eDVBTSTools] getPTS No PTS, try next");
139                                 }
140                                 if (m_streaminfo.getStructureEntryNext(local_offset, data, 1) != 0)
141                                 {
142                                         eDebug("[eDVBTSTools] getPTS Cannot find next structure entry");
143                                         break;
144                                 }
145                         }
146                 }
147         }
148         if (!m_source || !m_source->valid())
149                 return -1;
150
151         offset -= offset % m_packet_size;
152
153         int left = m_maxrange;
154         int resync_failed_counter = 64;
155
156         while (left >= m_packet_size)
157         {
158                 unsigned char buffer[m_packet_size];
159                 unsigned char *packet = &buffer[m_packet_size - 188];
160                 if (m_source->read(offset, buffer, m_packet_size) != m_packet_size)
161                 {
162                         eDebug("[eDVBTSTools] getPTS read error");
163                         return -1;
164                 }
165                 left -= m_packet_size;
166                 offset += m_packet_size;
167
168                 if (packet[0] != 0x47)
169                 {
170                         const unsigned char* match = (const unsigned char*)memchr(packet+1, 0x47, 188-1);
171                         if (match != NULL)
172                         {
173                                 eDebug("[eDVBTSTools] getPTS resync %d", match - packet);
174                                 offset += (match - packet) - 188;
175                         }
176                         else
177                         {
178                                 eDebug("[eDVBTSTools] getPTS resync failed");
179                                 if (resync_failed_counter == 0)
180                                 {
181                                         eDebug("[eDVBTSTools] getPTS Too many resync failures, probably not a valid stream");
182                                         return -1;
183                                 }
184                                 --resync_failed_counter;
185                         }
186                         continue;
187                 }
188
189                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
190                 int pusi = !!(packet[1] & 0x40);
191
192 //              printf("PID %04x, PUSI %d\n", pid, pusi);
193
194                 unsigned char *payload;
195
196                         /* check for adaption field */
197                 if (packet[3] & 0x20)
198                 {
199                         if (packet[4] >= 183)
200                                 continue;
201                         if (packet[4])
202                         {
203                                 if (packet[5] & 0x10) /* PCR present */
204                                 {
205                                         pts  = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
206                                         pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
207                                         pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
208                                         pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
209                                         pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
210                                         offset -= 188;
211                                         eDebug("[eDVBTSTools] getPTS PCR %16llx found at %lld pid %02x (%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x)", pts, offset, pid, packet[0], packet[1], packet[2], packet[3], packet[4], packet[5], packet[6], packet[7], packet[8], packet[9], packet[10]);
212                                         if (fixed && fixupPTS(offset, pts))
213                                                 return -1;
214                                         return 0;
215                                 }
216                         }
217                         payload = packet + packet[4] + 4 + 1;
218                 } else
219                         payload = packet + 4;
220
221 /*              if (m_pid >= 0)
222                         if (pid != m_pid)
223                                 continue; */
224                 if (!pusi)
225                         continue;
226
227                         /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
228                 if (payload[0] || payload[1] || (payload[2] != 1))
229                         continue;
230
231                 if (payload[3] == 0xFD)
232                 { // stream use extension mechanism defined in ISO 13818-1 Amendment 2
233                         if (payload[7] & 1) // PES extension flag
234                         {
235                                 int offs = 0;
236                                 if (payload[7] & 0x80) // pts avail
237                                         offs += 5;
238                                 if (payload[7] & 0x40) // dts avail
239                                         offs += 5;
240                                 if (payload[7] & 0x20) // escr avail
241                                         offs += 6;
242                                 if (payload[7] & 0x10) // es rate
243                                         offs += 3;
244                                 if (payload[7] & 0x8) // dsm trickmode
245                                         offs += 1;
246                                 if (payload[7] & 0x4) // additional copy info
247                                         offs += 1;
248                                 if (payload[7] & 0x2) // crc
249                                         offs += 2;
250                                 if (payload[8] < offs)
251                                         continue;
252                                 uint8_t pef = payload[9+offs++]; // pes extension field
253                                 if (pef & 1) // pes extension flag 2
254                                 {
255                                         if (pef & 0x80) // private data flag
256                                                 offs += 16;
257                                         if (pef & 0x40) // pack header field flag
258                                                 offs += 1;
259                                         if (pef & 0x20) // program packet sequence counter flag
260                                                 offs += 2;
261                                         if (pef & 0x10) // P-STD buffer flag
262                                                 offs += 2;
263                                         if (payload[8] < offs)
264                                                 continue;
265                                         uint8_t stream_id_extension_len = payload[9+offs++] & 0x7F;
266                                         if (stream_id_extension_len >= 1)
267                                         {
268                                                 if (payload[8] < (offs + stream_id_extension_len) )
269                                                         continue;
270                                                 if (payload[9+offs] & 0x80) // stream_id_extension_bit (should not set)
271                                                         continue;
272                                                 switch (payload[9+offs])
273                                                 {
274                                                 case 0x55 ... 0x5f: // VC-1
275                                                         break;
276                                                 case 0x71: // AC3 / DTS
277                                                         break;
278                                                 case 0x72: // DTS - HD
279                                                         break;
280                                                 default:
281                                                         eDebug("[eDVBTSTools] getPTS skip unknown stream_id_extension %02x\n", payload[9+offs]);
282                                                         continue;
283                                                 }
284                                         }
285                                         else
286                                                 continue;
287                                 }
288                                 else
289                                         continue;
290                         }
291                         else
292                                 continue;
293                 }
294                         /* drop non-audio, non-video packets because other streams
295                            can be non-compliant.*/
296                 else if (((payload[3] & 0xE0) != 0xC0) &&  // audio
297                         ((payload[3] & 0xF0) != 0xE0)) // video
298                         continue;
299
300                 if (payload[7] & 0x80) /* PTS */
301                 {
302                         pts  = ((unsigned long long)(payload[ 9]&0xE))  << 29;
303                         pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
304                         pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
305                         pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
306                         pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
307                         offset -= 188;
308
309                         eDebug("[eDVBTSTools] getPTS PTS %16llx found at %lld pid %02x stream: %02x", pts, offset, pid, payload[3]);
310
311                                 /* convert to zero-based */
312                         if (fixed && fixupPTS(offset, pts))
313                                 return -1;
314                         return 0;
315                 }
316         }
317
318         return -1;
319 }
320
321 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
322 {
323         if (m_streaminfo.fixupPTS(offset, now) == 0)
324         {
325                 return 0;
326         }
327         else
328         {
329                         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
330                 calcBegin();
331                 if (!m_begin_valid)
332                 {
333                         eDebug("[eDVBTSTools] fixupPTS begin not valid, can't fixup");
334                         return -1;
335                 }
336
337                 pts_t pos = m_pts_begin;
338                 if ((now < pos) && ((pos - now) < 90000 * 10))
339                 {
340                         pos = 0;
341                         return 0;
342                 }
343
344                 if (now < pos) /* wrap around */
345                         now = now + 0x200000000LL - pos;
346                 else
347                         now -= pos;
348                 return 0;
349         }
350         eDebug("[eDVBTSTools] fixupPTS failed!");
351         return -1;
352 }
353
354 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
355 {
356         if (m_streaminfo.hasAccessPoints())
357         {
358                 if ((pts >= m_pts_end) && (marg > 0) && m_end_valid)
359                         offset = m_offset_end;
360                 else
361                         offset = m_streaminfo.getAccessPoint(pts, marg);
362                 return 0;
363         }
364         else
365         {
366                 calcBeginAndEnd();
367                 if (!m_begin_valid)
368                         return -1;
369                 if (!m_end_valid)
370                         return -1;
371
372                 if (!m_samples_taken)
373                         takeSamples();
374
375                 if (!m_samples.empty())
376                 {
377                         int maxtries = 5;
378                         pts_t p = -1;
379
380                         while (maxtries--)
381                         {
382                                         /* search entry before and after */
383                                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
384                                 std::map<pts_t, off_t>::const_iterator u = l;
385
386                                 if (l != m_samples.begin())
387                                         --l;
388
389                                         /* we could have seeked beyond the end */
390                                 if (u == m_samples.end())
391                                 {
392                                                 /* use last segment for interpolation. */
393                                         if (l != m_samples.begin())
394                                         {
395                                                 --u;
396                                                 --l;
397                                         }
398                                 }
399
400                                         /* if we don't have enough points */
401                                 if (u == m_samples.end())
402                                         break;
403
404                                 pts_t pts_diff = u->first - l->first;
405                                 off_t offset_diff = u->second - l->second;
406
407                                 if (offset_diff < 0)
408                                 {
409                                         eDebug("[eDVBTSTools] getOffset something went wrong when taking samples.");
410                                         m_samples.clear();
411                                         takeSamples();
412                                         continue;
413                                 }
414
415                                 eDebug("[eDVBTSTools] getOffset using: %llu:%llu -> %llu:%llu", l->first, u->first, l->second, u->second);
416
417                                 int bitrate;
418
419                                 if (pts_diff)
420                                         bitrate = offset_diff * 90000 * 8 / pts_diff;
421                                 else
422                                         bitrate = 0;
423
424                                 offset = l->second;
425                                 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
426                                 offset -= offset % m_packet_size;
427                                 if (offset > m_offset_end)
428                                 {
429                                         /*
430                                          * NOTE: the bitrate calculation can be way off, especially when the pts difference is small.
431                                          * So the calculated offset might be far ahead of the end of the file.
432                                          * When that happens, avoid poisoning our sample list (m_samples) with an invalid value,
433                                          * which could eventually cause (timeshift) playback to be stopped.
434                                          * Because the file could be growing (timeshift), instead of returning the currently known end
435                                          * of file offset, we return an offset 1MB ahead of the end of the file.
436                                          * This allows jumping to the live point of the timeshift, for instance.
437                                          */
438                                         offset = m_offset_end + 1024 * 1024;
439                                         return 0;
440                                 }
441
442                                 p = pts;
443
444                                 if (!takeSample(offset, p))
445                                 {
446                                         int diff = (p - pts) / 90;
447
448                                         eDebug("[eDVBTSTools] getOffset calculated diff %d ms", diff);
449                                         if (abs(diff) > 300)
450                                         {
451                                                 eDebug("[eDVBTSTools] getOffset diff to big, refining");
452                                                 continue;
453                                         }
454                                 } else
455                                         eDebug("[eDVBTSTools] getOffset no sample taken, refinement not possible.");
456
457                                 break;
458                         }
459
460                                 /* if even the first sample couldn't be taken, fall back. */
461                                 /* otherwise, return most refined result. */
462                         if (p != -1)
463                         {
464                                 pts = p;
465                                 eDebug("[eDVBTSTools] getOffset aborting. Taking %llu as offset for %lld", offset, pts);
466                                 return 0;
467                         }
468                 }
469
470                 int bitrate = calcBitrate();
471                 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
472                 eDebug("[eDVBTSTools] getOffset fallback, bitrate=%d, results in %016llx", bitrate, offset);
473                 offset -= offset % m_packet_size;
474                 return 0;
475         }
476 }
477
478 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
479 {
480         return m_streaminfo.getNextAccessPoint(ts, start, direction);
481 }
482
483 void eDVBTSTools::calcBegin()
484 {
485         if (!m_source || !m_source->valid())
486                 return;
487
488         if (!(m_begin_valid || m_futile))
489         {
490                 // Just ask streaminfo
491                 if (m_streaminfo.getFirstFrame(m_offset_begin, m_pts_begin) == 0)
492                 {
493                         off_t begin = m_offset_begin;
494                         pts_t pts = m_pts_begin;
495                         if (m_streaminfo.fixupPTS(begin, pts) == 0)
496                         {
497                                 eDebug("[eDVBTSTools] calcBegin [@ML] m_streaminfo.getLastFrame returned %llu, %llu (%us), fixup to: %llu, %llu (%us)",
498                                        m_offset_begin, m_pts_begin, (unsigned int)(m_pts_begin/90000), begin, pts, (unsigned int)(pts/90000));
499                         }
500                         m_begin_valid = 1;
501                 }
502                 else
503                 {
504                         m_offset_begin = 0;
505                         if (!getPTS(m_offset_begin, m_pts_begin))
506                                 m_begin_valid = 1;
507                         else
508                                 m_futile = 1;
509                 }
510                 if (m_begin_valid)
511                 {
512                         /*
513                          * We've just calculated the begin position, which will have an effect on the
514                          * calculated length.
515                          * (when the end position had been determined before the begin position, the length
516                          * will be invalid)
517                          * So we force the end position to be (re-)calculated after the begin position has
518                          * been determined, in order to ensure m_pts_length will be corrected.
519                          */
520                          m_end_valid = 0;
521
522                 }
523         }
524 }
525
526 static pts_t pts_diff(pts_t low, pts_t high)
527 {
528         high -= low;
529         if (high < 0)
530                 high += 0x200000000LL;
531         return high;
532 }
533
534 void eDVBTSTools::calcEnd()
535 {
536         if (!m_source || !m_source->valid())
537                 return;
538
539         // If there's a structure file, the calculation is much smarter, so we can try more often
540         off_t threshold = m_streaminfo.hasStructure() ? 100*1024 : 1024*1024;
541
542         off_t end = m_source->length();
543         if (llabs(end - m_last_filelength) > threshold)
544         {
545                 m_last_filelength = end;
546                 m_end_valid = 0;
547                 m_futile = 0;
548 //              eDebug("[eDVBTSTools] calcEnd file size changed, recalc length");
549         }
550
551         int maxiter = 10;
552
553         if (!m_end_valid)
554         {
555                 off_t offset = m_offset_end = m_last_filelength;
556                 pts_t pts = m_pts_end;
557                 if (m_streaminfo.getLastFrame(offset, pts) == 0)
558                 {
559                         m_offset_end = offset;
560                         m_pts_length = m_pts_end = pts;
561                         end = m_offset_end;
562                         if (m_streaminfo.fixupPTS(end, m_pts_length) != 0)
563                         {
564                                 /* Not enough structure info, estimate */
565                                 m_pts_length = pts_diff(m_pts_begin, m_pts_end);
566                         }
567                         m_end_valid = 1;
568                 }
569                 else
570                 {
571                         eDebug("[eDVBTSTools] calcEnd [@ML] m_streaminfo.getLastFrame failed, fallback");
572                         while (!(m_end_valid || m_futile))
573                         {
574                                 if (!--maxiter)
575                                 {
576                                         m_futile = 1;
577                                         return;
578                                 }
579
580                                 m_offset_end -= m_maxrange;
581                                 if (m_offset_end < 0)
582                                         m_offset_end = 0;
583
584                                 offset = m_offset_end;
585                                 pts = m_pts_end;
586                                 if (!getPTS(offset, pts))
587                                 {
588                                         offset = m_offset_end;
589                                         m_pts_end = pts;
590                                         m_pts_length = pts_diff(m_pts_begin, m_pts_end);
591                                         m_end_valid = 1;
592                                 }
593
594                                 if (!m_offset_end)
595                                 {
596                                         m_futile = 1;
597                                         break;
598                                 }
599                         }
600                 }
601         }
602 }
603
604 void eDVBTSTools::calcBeginAndEnd()
605 {
606         calcBegin();
607         calcEnd();
608 }
609
610 int eDVBTSTools::calcLen(pts_t &len)
611 {
612         calcBeginAndEnd();
613         if (!(m_begin_valid && m_end_valid))
614                 return -1;
615         len = m_pts_length;
616         return 0;
617 }
618
619 int eDVBTSTools::calcBitrate()
620 {
621         pts_t len_in_pts;
622         if (calcLen(len_in_pts) != 0)
623                 return -1;
624         off_t len_in_bytes = m_offset_end - m_offset_begin;
625
626         if (!len_in_pts)
627                 return -1;
628
629         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
630         if ((bitrate < 10000) || (bitrate > 100000000))
631                 return -1;
632
633         return bitrate;
634 }
635
636         /* pts, off */
637 void eDVBTSTools::takeSamples()
638 {
639         m_samples_taken = 1;
640         m_samples.clear();
641         int retries=2;
642
643         calcBeginAndEnd();
644         if (!(m_begin_valid && m_end_valid))
645                 return;
646
647         int nr_samples = 30;
648         off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
649         if (bytes_per_sample < 40*1024*1024)
650                 bytes_per_sample = 40*1024*1024;
651
652         bytes_per_sample -= bytes_per_sample % m_packet_size;
653
654         eDebug("[eDVBTSTools] takeSamples step %lld, pts begin %llu, pts end %llu, offs begin %lld, offs end %lld:",
655                 bytes_per_sample, m_pts_begin, m_pts_end, m_offset_begin, m_offset_end);
656
657         for (off_t offset = m_offset_begin; offset < m_offset_end;)
658         {
659                 pts_t p;
660                 if (takeSample(offset, p) && retries--)
661                         continue;
662                 retries = 2;
663                 offset += bytes_per_sample;
664         }
665         m_samples[0] = m_offset_begin;
666         m_samples[m_pts_end - m_pts_begin] = m_offset_end;
667 }
668
669         /* returns 0 when a sample was taken. */
670 int eDVBTSTools::takeSample(off_t off, pts_t &p)
671 {
672         off_t offset_org = off;
673
674         if (!eDVBTSTools::getPTS(off, p, 1))
675         {
676                         /* as we are happily mixing PTS and PCR values (no comment, please), we might
677                            end up with some "negative" segments.
678
679                            so check if this new sample is between the previous and the next field*/
680
681                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
682                 std::map<pts_t, off_t>::const_iterator u = l;
683
684                 if (l != m_samples.begin())
685                 {
686                         --l;
687                         if (u != m_samples.end())
688                         {
689                                 if ((l->second > off) || (u->second < off))
690                                 {
691                                         eDebug("[eDVBTSTools] takeSample ignoring sample %lld %lld %lld (%llu %llu %llu)",
692                                                 l->second, off, u->second, l->first, p, u->first);
693                                         return 1;
694                                 }
695                         }
696                 }
697
698                 eDebug("[eDVBTSTools] takeSample adding sample %lld: pts %llu -> pos %lld (diff %lld bytes)", offset_org, p, off, off-offset_org);
699                 m_samples[p] = off;
700                 return 0;
701         }
702         return -1;
703 }
704
705 int eDVBTSTools::findPMT(eDVBPMTParser::program &program)
706 {
707         int pmtpid = -1;
708         ePtr<iDVBSectionReader> sectionreader;
709
710         eDVBPMTParser::clearProgramInfo(program);
711
712                 /* FIXME: this will be factored out soon! */
713         if (!m_source || !m_source->valid())
714         {
715                 eDebug("[eDVBTSTools] findPMT file not valid");
716                 return -1;
717         }
718
719         off_t position=0;
720         m_pmtready = false;
721
722         for (int attempts_left = (5*1024*1024)/m_packet_size; attempts_left != 0; --attempts_left)
723         {
724                 unsigned char buffer[m_packet_size];
725                 unsigned char *packet = &buffer[m_packet_size - 188];
726                 int ret = m_source->read(position, buffer, m_packet_size);
727                 if (ret != m_packet_size)
728                 {
729                         eDebug("[eDVBTSTools] findPMT read error");
730                         break;
731                 }
732                 position += m_packet_size;
733
734                 if (packet[0] != 0x47)
735                 {
736                         int i = 0;
737                         while (i < 188)
738                         {
739                                 if (packet[i] == 0x47)
740                                         break;
741                                 --position;
742                                 ++i;
743                         }
744                         continue;
745                 }
746
747                 if (pmtpid < 0 && !(packet[1] & 0x40)) /* pusi */
748                         continue;
749
750                         /* ok, now we have a PES header or section header*/
751                 unsigned char *sec;
752
753                         /* check for adaption field */
754                 if (packet[3] & 0x20)
755                 {
756                         if (packet[4] >= 183)
757                                 continue;
758                         sec = packet + packet[4] + 4 + 1;
759                 } else
760                         sec = packet + 4;
761
762                 if (pmtpid < 0)
763                 {
764                         if (sec[0]) /* table pointer, assumed to be 0 */
765                                 continue;
766                         if (sec[1] == 0x02) /* program map section */
767                         {
768                                 pmtpid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
769                                 int sid = (sec[4] << 8) | sec[5];
770                                 sectionreader = new eTSFileSectionReader(eApp);
771                                 m_PMT.begin(eApp, eDVBPMTSpec(pmtpid, sid), sectionreader);
772                                 ((eTSFileSectionReader*)(iDVBSectionReader*)sectionreader)->data(&sec[1], 188 - (sec + 1 - packet));
773                         }
774                 }
775                 else if (pmtpid == (((packet[1] << 8) | packet[2]) & 0x1FFF))
776                 {
777                         ((eTSFileSectionReader*)(iDVBSectionReader*)sectionreader)->data(sec, 188 - (sec - packet));
778                 }
779                 if (m_pmtready)
780                 {
781                         program = m_program;
782                         return 0;
783                 }
784         }
785         m_PMT.stop();
786         return -1;
787 }
788
789 int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int frame_types)
790 {
791 //      eDebug("[eDVBTSTools] findFrame trying to find iFrame at %llu", offset);
792         if (!m_streaminfo.hasStructure())
793         {
794 //              eDebug("[eDVBTSTools] findFrame can't get next iframe without streaminfo");
795                 return -1;
796         }
797
798         off_t offset = _offset;
799         int nr_frames = 0;
800         bool is_mpeg2 = false;
801
802                 /* let's find the iframe before the given offset */
803         if (direction < 0)
804                 offset--;
805
806         unsigned long long longdata;
807         if (m_streaminfo.getStructureEntryFirst(offset, longdata) != 0)
808         {
809                 eDebug("[eDVBTSTools] findFramee getStructureEntryFirst failed");
810                 return -1;
811         }
812         if (direction == 0)
813         {
814                 // Special case, move an extra frame ahead
815                 if (m_streaminfo.getStructureEntryNext(offset, longdata, 1) != 0)
816                         return -1;
817                 direction = 1;
818         }
819         while (1)
820         {
821                 unsigned int data = (unsigned int)longdata; // only the lower bits are interesting
822                         /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
823                         /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
824                         /* TODO: check frame_types */
825                 // is_frame
826                 if (((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00)) /* H.264 UAD or MPEG2 start code */
827                 {
828                         ++nr_frames;
829                         if ((data & 0xE0FF) == 0x0009)          /* H.264 NAL unit access delimiter with I-frame*/
830                         {
831                                 break;
832                         }
833                         if ((data & 0x3800FF) == 0x080000)      /* MPEG2 picture start code with I-frame */
834                         {
835                                 is_mpeg2 = true;
836                                 break;
837                         }
838                 }
839                 if (m_streaminfo.getStructureEntryNext(offset, longdata, direction) != 0)
840                         return -1;
841         }
842         off_t start = offset;
843
844         /* let's find the next frame after the given offset */
845         unsigned int data;
846         do
847         {
848                 if (m_streaminfo.getStructureEntryNext(offset, longdata, 1))
849                 {
850                         eDebug("[eDVBTSTools] findFrame get next failed");
851                         return -1;
852                 }
853                 data = ((unsigned int)longdata) & 0xFF;
854         }
855         while ((data != 0x09) && (data != 0x00)); /* next frame */
856
857         if (is_mpeg2)
858         {
859                 // Seek back to sequence start (appears to be needed for e.g. a few TCM streams)
860                 while (nr_frames)
861                 {
862                         if (m_streaminfo.getStructureEntryNext(start, longdata, -1))
863                         {
864                                 eDebug("[eDVBTSTools] findFrame Failed to find MPEG2 start frame");
865                                 break;
866                         }
867                         if ((((unsigned int)longdata) & 0xFF) == 0xB3) /* sequence start or previous frame */
868                                 break;
869                         --nr_frames;
870                 }
871         }
872
873         /* make sure we've ended up in the right direction, ignore the result if we didn't */
874         if ((direction >= 0 && start < _offset) || (direction < 0 && start > _offset)) return -1;
875
876         len = offset - start;
877         _offset = start;
878         if (direction < 0)
879                 direction = -nr_frames;
880         else
881                 direction = nr_frames;
882 //      eDebug("[eDVBTSTools] findFrame result: offset=%llu, len: %ld", offset, (int)len);
883         return 0;
884 }
885
886 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
887 {
888         int nr_frames, direction;
889 //      eDebug("[eDVBTSTools] findNextPicture trying to move %d frames at %llu", distance, offset);
890
891         frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
892
893         off_t new_offset = offset;
894         size_t new_len = len;
895         int first = 1;
896
897         if (distance > 0) {
898                 direction = 0;
899                 nr_frames = 0;
900         } else {
901                 direction = -1;
902                 nr_frames = -1;
903                 distance = -distance+1;
904         }
905         while (distance > 0)
906         {
907                 int dir = direction;
908                 if (findFrame(new_offset, new_len, dir, frame_types))
909                 {
910 //                      eDebug("[eDVBTSTools] findNextPicture findFrame failed!\n");
911                         return -1;
912                 }
913
914                 distance -= abs(dir);
915
916 //              eDebug("[eDVBTSTools] findNextPicture we moved %d, %d to go frames (now at %llu)", dir, distance, new_offset);
917
918                 if (distance >= 0 || direction == 0)
919                 {
920                         first = 0;
921                         offset = new_offset;
922                         len = new_len;
923                         nr_frames += abs(dir);
924                 }
925                 else if (first)
926                 {
927                         first = 0;
928                         offset = new_offset;
929                         len = new_len;
930                         nr_frames += abs(dir) + distance; // never jump forward during rewind
931                 }
932         }
933
934         distance = (direction < 0) ? -nr_frames : nr_frames;
935 //      eDebug("[eDVBTSTools] findNextPicture in total, we moved %d frames", nr_frames);
936
937         return 0;
938 }
939
940 void eDVBTSTools::PMTready(int error)
941 {
942         if (!error)
943         {
944                 if (getProgramInfo(m_program) >= 0)
945                 {
946                         m_PMT.stop();
947                         m_pmtready = true;
948                 }
949         }
950 }