[RecordTimer] fix repeat timer issue.
[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 void eDVBTSTools::closeSource()
26 {
27         m_source = NULL;
28 }
29
30 eDVBTSTools::~eDVBTSTools()
31 {
32         closeSource();
33 }
34
35 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
36 {
37         eRawFile *f = new eRawFile();
38         ePtr<iTsSource> src = f;
39
40         if (f->open(filename, 1) < 0)
41                 return -1;
42
43         setSource(src, nostreaminfo ? NULL : filename);
44
45         return 0;
46 }
47
48 void eDVBTSTools::setSource(ePtr<iTsSource> &source, const char *stream_info_filename)
49 {
50         closeFile();
51
52         m_source = source;
53
54         if (stream_info_filename)
55         {
56                 eDebug("loading streaminfo for %s", stream_info_filename);
57                 m_streaminfo.load(stream_info_filename);
58         }
59         
60         if (m_streaminfo.hasAccessPoint())
61                 m_use_streaminfo = 1;
62         else
63         {
64 //              eDebug("no recorded stream information available");
65                 m_use_streaminfo = 0;
66         }
67
68         m_samples_taken = 0;
69 }
70
71 void eDVBTSTools::closeFile()
72 {
73         if (m_source)
74                 closeSource();
75
76         m_streaminfo.readClose();
77 }
78
79 void eDVBTSTools::setSyncPID(int pid)
80 {
81         m_pid = pid;
82 }
83
84 void eDVBTSTools::setSearchRange(int maxrange)
85 {
86         m_maxrange = maxrange;
87 }
88
89         /* getPTS extracts a pts value from any PID at a given offset. */
90 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
91 {
92         if (m_use_streaminfo)
93                 if (!m_streaminfo.getPTS(offset, pts))
94                         return 0;
95         
96         if (!m_source || !m_source->valid())
97                 return -1;
98
99         offset -= offset % 188;
100
101         int left = m_maxrange;
102         
103         while (left >= 188)
104         {
105                 unsigned char packet[188];
106                 if (m_source->read(offset, packet, 188) != 188)
107                 {
108                         eDebug("read error");
109                         break;
110                 }
111                 left -= 188;
112                 offset += 188;
113
114                 if (packet[0] != 0x47)
115                 {
116                         eDebug("resync");
117                         int i = 0;
118                         while (i < 188)
119                         {
120                                 if (packet[i] == 0x47)
121                                         break;
122                                 ++i;
123                                 --offset;
124                         }
125                         continue;
126                 }
127                 
128                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
129                 int pusi = !!(packet[1] & 0x40);
130                 
131 //              printf("PID %04x, PUSI %d\n", pid, pusi);
132
133                 unsigned char *payload;
134                 
135                         /* check for adaption field */
136                 if (packet[3] & 0x20)
137                 {
138                         if (packet[4] >= 183)
139                                 continue;
140                         if (packet[4])
141                         {
142                                 if (packet[5] & 0x10) /* PCR present */
143                                 {
144                                         pts  = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
145                                         pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
146                                         pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
147                                         pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
148                                         pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
149                                         offset -= 188;
150                                         eDebug("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]);
151                                         if (fixed && fixupPTS(offset, pts))
152                                                 return -1;
153                                         return 0;
154                                 }
155                         }
156                         payload = packet + packet[4] + 4 + 1;
157                 } else
158                         payload = packet + 4;
159
160 /*              if (m_pid >= 0)
161                         if (pid != m_pid)
162                                 continue; */
163                 if (!pusi)
164                         continue;
165
166                         /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
167                 if (payload[0] || payload[1] || (payload[2] != 1))
168                         continue;
169
170                 if (payload[3] == 0xFD)
171                 { // stream use extension mechanism defined in ISO 13818-1 Amendment 2
172                         if (payload[7] & 1) // PES extension flag
173                         {
174                                 int offs = 0;
175                                 if (payload[7] & 0x80) // pts avail
176                                         offs += 5;
177                                 if (payload[7] & 0x40) // dts avail
178                                         offs += 5;
179                                 if (payload[7] & 0x20) // escr avail
180                                         offs += 6;
181                                 if (payload[7] & 0x10) // es rate
182                                         offs += 3;
183                                 if (payload[7] & 0x8) // dsm trickmode
184                                         offs += 1;
185                                 if (payload[7] & 0x4) // additional copy info
186                                         offs += 1;
187                                 if (payload[7] & 0x2) // crc
188                                         offs += 2;
189                                 if (payload[8] < offs)
190                                         continue;
191                                 uint8_t pef = payload[9+offs++]; // pes extension field
192                                 if (pef & 1) // pes extension flag 2
193                                 {
194                                         if (pef & 0x80) // private data flag
195                                                 offs += 16;
196                                         if (pef & 0x40) // pack header field flag
197                                                 offs += 1;
198                                         if (pef & 0x20) // program packet sequence counter flag
199                                                 offs += 2;
200                                         if (pef & 0x10) // P-STD buffer flag
201                                                 offs += 2;
202                                         if (payload[8] < offs)
203                                                 continue;
204                                         uint8_t stream_id_extension_len = payload[9+offs++] & 0x7F;
205                                         if (stream_id_extension_len >= 1)
206                                         {
207                                                 if (payload[8] < (offs + stream_id_extension_len) )
208                                                         continue;
209                                                 if (payload[9+offs] & 0x80) // stream_id_extension_bit (should not set)
210                                                         continue;
211                                                 switch (payload[9+offs])
212                                                 {
213                                                 case 0x55 ... 0x5f: // VC-1
214                                                         break;
215                                                 case 0x71: // AC3 / DTS
216                                                         break;
217                                                 case 0x72: // DTS - HD
218                                                         break;
219                                                 default:
220                                                         eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]);
221                                                         continue;
222                                                 }
223                                         }
224                                         else
225                                                 continue;
226                                 }
227                                 else
228                                         continue;
229                         }
230                         else
231                                 continue;
232                 }
233                         /* drop non-audio, non-video packets because other streams
234                            can be non-compliant.*/
235                 else if (((payload[3] & 0xE0) != 0xC0) &&  // audio
236                         ((payload[3] & 0xF0) != 0xE0)) // video
237                         continue;
238
239                 if (payload[7] & 0x80) /* PTS */
240                 {
241                         pts  = ((unsigned long long)(payload[ 9]&0xE))  << 29;
242                         pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
243                         pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
244                         pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
245                         pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
246                         offset -= 188;
247
248                         eDebug("PTS %16llx found at %lld pid %02x stream: %02x", pts, offset, pid, payload[3]);
249
250                                 /* convert to zero-based */
251                         if (fixed && fixupPTS(offset, pts))
252                                 return -1;
253                         return 0;
254                 }
255         }
256         
257         return -1;
258 }
259
260 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
261 {
262         if (m_use_streaminfo)
263         {
264                 if (!m_streaminfo.fixupPTS(offset, now))
265                         return 0;
266         } else
267         {
268                         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
269                 calcBegin();
270                 if (!m_begin_valid)
271                 {       
272                         eDebug("begin not valid, can't fixup");
273                         return -1;
274                 }
275                 
276                 pts_t pos = m_pts_begin;
277                 if ((now < pos) && ((pos - now) < 90000 * 10))
278                 {       
279                         pos = 0;
280                         return 0;
281                 }
282                 
283                 if (now < pos) /* wrap around */
284                         now = now + 0x200000000LL - pos;
285                 else
286                         now -= pos;
287                 return 0;
288         }
289         eDebug("eDVBTSTools::fixupPTS failed!");
290         return -1;
291 }
292
293 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
294 {
295         eDebug("getOffset for pts 0x%llx", pts);
296         if (m_use_streaminfo)
297         {
298                 if (pts >= m_pts_end && marg > 0 && m_end_valid)
299                         offset = m_offset_end;
300                 else
301                         offset = m_streaminfo.getAccessPoint(pts, marg);
302                 return 0;
303         } else
304         {
305                 calcBegin(); calcEnd();
306                 
307                 if (!m_begin_valid)
308                         return -1;
309                 if (!m_end_valid)
310                         return -1;
311
312                 if (!m_samples_taken)
313                         takeSamples();
314                 
315                 if (!m_samples.empty())
316                 {
317                         int maxtries = 5;
318                         pts_t p = -1;
319                         
320                         while (maxtries--)
321                         {
322                                         /* search entry before and after */
323                                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
324                                 std::map<pts_t, off_t>::const_iterator u = l;
325
326                                 if (l != m_samples.begin())
327                                         --l;
328                                 
329                                         /* we could have seeked beyond the end */
330                                 if (u == m_samples.end())
331                                 {
332                                                 /* use last segment for interpolation. */
333                                         if (l != m_samples.begin())
334                                         {
335                                                 --u;
336                                                 --l;
337                                         }
338                                 }
339                                         
340                                         /* if we don't have enough points */
341                                 if (u == m_samples.end())
342                                         break;
343                                 
344                                 pts_t pts_diff = u->first - l->first;
345                                 off_t offset_diff = u->second - l->second;
346                                 
347                                 if (offset_diff < 0)
348                                 {
349                                         eDebug("something went wrong when taking samples.");
350                                         m_samples.clear();
351                                         takeSamples();
352                                         continue;
353                                 }
354
355                                 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
356
357                                 int bitrate;
358                                 
359                                 if (pts_diff)
360                                         bitrate = offset_diff * 90000 * 8 / pts_diff;
361                                 else
362                                         bitrate = 0;
363
364                                 offset = l->second;
365                                 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
366                                 offset -= offset % 188;
367                                 
368                                 p = pts;
369                                 
370                                 if (!takeSample(offset, p))
371                                 {
372                                         int diff = (p - pts) / 90;
373                         
374                                         eDebug("calculated diff %d ms", diff);
375                                         if (abs(diff) > 300)
376                                         {
377                                                 eDebug("diff to big, refining");
378                                                 continue;
379                                         }
380                                 } else
381                                         eDebug("no sample taken, refinement not possible.");
382
383                                 break;
384                         }
385                         
386                                 /* if even the first sample couldn't be taken, fall back. */
387                                 /* otherwise, return most refined result. */
388                         if (p != -1)
389                         {
390                                 pts = p;
391                                 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
392                                 return 0;
393                         }
394                 }
395                 
396                 int bitrate = calcBitrate();
397                 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
398                 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
399                 offset -= offset % 188;
400                 
401                 return 0;
402         }
403 }
404
405 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
406 {
407         if (m_use_streaminfo)
408                 return m_streaminfo.getNextAccessPoint(ts, start, direction);
409         else
410         {
411                 eDebug("can't get next access point without streaminfo");
412                 return -1;
413         }
414 }
415
416 void eDVBTSTools::calcBegin()
417 {
418         if (!m_source || !m_source->valid())
419                 return;
420
421         if (!(m_begin_valid || m_futile))
422         {
423                 m_offset_begin = 0;
424                 if (!getPTS(m_offset_begin, m_pts_begin))
425                         m_begin_valid = 1;
426                 else
427                         m_futile = 1;
428         }
429 }
430
431 void eDVBTSTools::calcEnd()
432 {
433         if (!m_source || !m_source->valid())
434                 return;
435
436         off_t end = m_source->lseek(0, SEEK_END);
437         
438         if (llabs(end - m_last_filelength) > 1*1024*1024)
439         {
440                 m_last_filelength = end;
441                 m_end_valid = 0;
442                 
443                 m_futile = 0;
444 //              eDebug("file size changed, recalc length");
445         }
446         
447         int maxiter = 10;
448         
449         m_offset_end = m_last_filelength;
450         
451         while (!(m_end_valid || m_futile))
452         {
453                 if (!--maxiter)
454                 {
455                         m_futile = 1;
456                         return;
457                 }
458
459                 m_offset_end -= m_maxrange;
460                 if (m_offset_end < 0)
461                         m_offset_end = 0;
462
463                         /* restore offset if getpts fails */
464                 off_t off = m_offset_end;
465
466                 if (!getPTS(m_offset_end, m_pts_end))
467                         m_end_valid = 1;
468                 else
469                         m_offset_end = off;
470
471                 if (!m_offset_end)
472                 {
473                         m_futile = 1;
474                         break;
475                 }
476         }
477 }
478
479 int eDVBTSTools::calcLen(pts_t &len)
480 {
481         calcBegin(); calcEnd();
482         if (!(m_begin_valid && m_end_valid))
483                 return -1;
484         len = m_pts_end - m_pts_begin;
485                 /* wrap around? */
486         if (len < 0)
487                 len += 0x200000000LL;
488         return 0;
489 }
490
491 int eDVBTSTools::calcBitrate()
492 {
493         calcBegin(); calcEnd();
494         if (!(m_begin_valid && m_end_valid))
495                 return -1;
496
497         pts_t len_in_pts = m_pts_end - m_pts_begin;
498
499                 /* wrap around? */
500         if (len_in_pts < 0)
501                 len_in_pts += 0x200000000LL;
502         off_t len_in_bytes = m_offset_end - m_offset_begin;
503         
504         if (!len_in_pts)
505                 return -1;
506         
507         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
508         if ((bitrate < 10000) || (bitrate > 100000000))
509                 return -1;
510         
511         return bitrate;
512 }
513
514         /* pts, off */
515 void eDVBTSTools::takeSamples()
516 {
517         m_samples_taken = 1;
518         m_samples.clear();
519         pts_t dummy;
520         int retries=2;
521
522         if (calcLen(dummy) == -1)
523                 return;
524         
525         int nr_samples = 30;
526         off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
527         if (bytes_per_sample < 40*1024*1024)
528                 bytes_per_sample = 40*1024*1024;
529
530         bytes_per_sample -= bytes_per_sample % 188;
531
532         eDebug("samples step %lld, pts begin %llx, pts end %llx, offs begin %lld, offs end %lld:",
533                 bytes_per_sample, m_pts_begin, m_pts_end, m_offset_begin, m_offset_end);
534
535         for (off_t offset = m_offset_begin; offset < m_offset_end;)
536         {
537                 pts_t p;
538                 if (takeSample(offset, p) && retries--)
539                         continue;
540                 retries = 2;
541                 offset += bytes_per_sample;
542         }
543         m_samples[0] = m_offset_begin;
544         m_samples[m_pts_end - m_pts_begin] = m_offset_end;
545 }
546
547         /* returns 0 when a sample was taken. */
548 int eDVBTSTools::takeSample(off_t off, pts_t &p)
549 {
550         off_t offset_org = off;
551
552         if (!eDVBTSTools::getPTS(off, p, 1))
553         {
554                         /* as we are happily mixing PTS and PCR values (no comment, please), we might
555                            end up with some "negative" segments. 
556                            
557                            so check if this new sample is between the previous and the next field*/
558
559                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
560                 std::map<pts_t, off_t>::const_iterator u = l;
561
562                 if (l != m_samples.begin())
563                 {
564                         --l;
565                         if (u != m_samples.end())
566                         {
567                                 if ((l->second > off) || (u->second < off))
568                                 {
569                                         eDebug("ignoring sample %lld %lld %lld (%llx %llx %llx)",
570                                                 l->second, off, u->second, l->first, p, u->first);
571                                         return 1;
572                                 }
573                         }
574                 }
575
576                 eDebug("adding sample %lld: pts 0x%llx -> pos %lld (diff %lld bytes)", offset_org, p, off, off-offset_org);
577                 m_samples[p] = off;
578                 return 0;
579         }
580         return -1;
581 }
582
583 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
584 {
585                 /* FIXME: this will be factored out soon! */
586         if (!m_source || !m_source->valid())
587         {
588                 eDebug(" file not valid");
589                 return -1;
590         }
591
592         off_t position=0;
593
594         int left = 5*1024*1024;
595         
596         while (left >= 188)
597         {
598                 unsigned char packet[188];
599                 int ret = m_source->read(position, packet, 188);
600                 if (ret != 188)
601                 {
602                         eDebug("read error");
603                         break;
604                 }
605                 left -= 188;
606                 position += 188;
607
608                 if (packet[0] != 0x47)
609                 {
610                         int i = 0;
611                         while (i < 188)
612                         {
613                                 if (packet[i] == 0x47)
614                                         break;
615                                 --position;
616                                 ++i;
617                         }
618                         continue;
619                 }
620                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
621                 
622                 int pusi = !!(packet[1] & 0x40);
623                 
624                 if (!pusi)
625                         continue;
626                 
627                         /* ok, now we have a PES header or section header*/
628                 unsigned char *sec;
629                 
630                         /* check for adaption field */
631                 if (packet[3] & 0x20)
632                 {
633                         if (packet[4] >= 183)
634                                 continue;
635                         sec = packet + packet[4] + 4 + 1;
636                 } else
637                         sec = packet + 4;
638                 
639                 if (sec[0])     /* table pointer, assumed to be 0 */
640                         continue;
641
642                 if (sec[1] == 0x02) /* program map section */
643                 {
644                         pmt_pid = pid;
645                         service_id = (sec[4] << 8) | sec[5];
646                         return 0;
647                 }
648         }
649         
650         return -1;
651 }
652
653 int eDVBTSTools::findFrame(off_t &_iframe_offset, off_t &_new_offset, size_t &len, int &direction, int frame_types)
654 {
655         off_t offset = _iframe_offset;
656         int nr_frames = 0;
657         int is_mpeg = 0;
658 //      eDebug("trying to find iFrame at %lld", offset);
659
660         if (!m_streaminfo.hasStructure())
661         {
662 //              eDebug("can't get next iframe without streaminfo");
663                 return -1;
664         }
665
666                                 /* let's find the iframe before the given offset */
667         unsigned long long data;
668         
669         if (direction < 0)
670                 offset--;
671
672         while (1)
673         {
674                 int res = -1;
675                 if(direction<0)
676                         res = m_streaminfo.getStructureEntry_prev(offset, data);
677                 else
678                         res = m_streaminfo.getStructureEntry_next(offset, data);
679
680                 if (res)
681                 {
682                         eDebug("getting structure info for origin offset failed.");
683                         return -1;
684                 }
685                 if (offset == 0x7fffffffffffffffLL) /* eof */
686                 {
687                         eDebug("reached eof");
688                         return -1;
689                 }
690                         /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
691                         /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
692                         /* TODO: check frame_types */
693                 int is_start = 0;
694                 if ((data & 0xE0FF) == 0x0009) /* H.264 NAL unit access delimiter with I-frame*/
695                         is_start =1;
696                 else if ((data & 0xE07E) == 0x0046) /* H.265 NAL unit access delimiter with I-frame*/
697                         is_start = 1;
698                 else if((data & 0x3800FF) == 0x080000) /* MPEG2 picture start code with I-frame */
699                 {
700                         is_start =1;
701                         is_mpeg =1;
702                 }
703                 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0x7E) == 0x0046) || ((data & 0xFF) == 0x00); /* H.264 UAD or H.265 UAD or MPEG2 start code */
704                 
705                 if (is_frame)
706                 {
707                         if (direction < 0)
708                                 --nr_frames;
709                         else
710                                 ++nr_frames;
711                 }
712 //              eDebug("%08llx@%llu -> %d, %d (I-frame)", data, offset, is_start, nr_frames);
713                 if (is_start)
714                         break;
715
716                 if (direction == -1)
717                         --offset; /* move to previous entry */
718                 else if (direction == +1)
719                         direction = 0;
720         }
721         off_t iframe_start = offset;
722         off_t start = offset;
723
724 #if 0
725                         /* backtrack to find the previous sequence start, in case of MPEG2 */
726         if ((data & 0xFF) == 0x00) {
727                 do {
728                         --start;
729                         if (m_streaminfo.getStructureEntry(start, data, 0))
730                         {
731                                 eDebug("get previous failed");
732                                 return -1;
733                         }
734                 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00) && ((data & 0xFF) != 0xB3)); /* sequence start or previous frame */
735                 if ((data & 0xFF) != 0xB3)
736                         start = offset;  /* Failed to find corresponding sequence start, so never mind */
737         }
738
739 #endif
740
741                         /* let's find the next frame after the given offset */
742         do {
743                 if (m_streaminfo.getStructureEntry_next(offset, data))
744                 {
745                         eDebug("get next failed");
746                         return -1;
747                 }
748                 if (offset == 0x7fffffffffffffffLL) /* eof */
749                 {
750                         eDebug("reached eof (while looking for end of iframe)");
751                         return -1;
752                 }
753 //              eDebug("%08llx@%llu (next frame)", data, offset);
754         } while (((data & 0xFF) != 9) && ((data & 0x7E) != 0x46) && ((data & 0xFF) != 0x00)); /* next frame */
755
756         if (is_mpeg) //MPEG2 picture start code with I-frame
757         {
758                 off_t sequence_offset = start;
759                 unsigned long long sequence_data;
760                 int is_sequence =0;
761                 int frame_length = (nr_frames<0) ? -nr_frames: nr_frames;
762                 while(frame_length)
763                 {
764 //                      --start;
765                         if (m_streaminfo.getStructureEntry_prev(sequence_offset, sequence_data))
766                         {
767                                 eDebug("get previous failed");
768                                 return -1;
769                         }
770                         if ((sequence_data & 0xFF) == 0xB3) /* sequence start or previous frame */
771                                 is_sequence = 1;
772                         if(((sequence_data & 0xFF) == 0x0009) || ((sequence_data & 0xFF) == 0x00)) /* H.264 UAD or MPEG2 start code */
773                                 --frame_length;
774 //                      eDebug("[findFrame] %08llx@%llu -> %d, %d (sequence)", sequence_data, sequence_offset, is_sequence, frame_length);
775                         if(is_sequence) /* get sequence frame */
776                                 break;
777                 }
778                 start = sequence_offset;
779         }
780 #if 0
781                         /* align to TS pkt start */
782         start = start - (start % 188);
783         offset = offset - (offset % 188);
784 #endif
785
786         len = offset - start;
787         _new_offset = start;
788         _iframe_offset = iframe_start;
789         direction = nr_frames;
790 //      eDebug("result: start=%lld, iframe= %lld, next=%lld, len: %ld",start, iframe_start ,offset, (long int)len);
791         return 0;
792 }
793
794 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
795 {
796         int nr_frames, direction;
797 //      eDebug("trying to move %d frames at %llu", distance, offset);
798         
799         frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
800
801         off_t new_offset = offset;
802         size_t new_len = len;
803         off_t iframe_offset = new_offset;
804         int first = 1;
805
806         if (distance > 0) {
807                 direction = 0;
808                 nr_frames = 0;
809         } else {
810                 direction = -1;
811                 nr_frames = -1;
812                 distance = -distance+1;
813         }       
814         while (distance > 0)
815         {
816                 int dir = direction;
817                 if (findFrame(iframe_offset, new_offset, new_len, dir, frame_types))
818                 {
819 //                      eDebug("findFrame failed!\n");
820                         return -1;
821                 }
822                 
823                 distance -= abs(dir);
824                 
825 //              eDebug("we moved %d, %d to go frames (now at %llu)", dir, distance, new_offset);
826
827                 if (distance >= 0 || direction == 0)
828                 {
829                         first = 0;
830                         offset = new_offset;
831                         len = new_len;
832                         nr_frames += abs(dir);
833                 }
834                 else if (first) {
835                         first = 0;
836                         offset = new_offset;
837                         len = new_len;
838                         nr_frames += abs(dir) + distance; // never jump forward during rewind
839                 }
840         }
841         distance = (direction < 0) ? -nr_frames : nr_frames;
842 //      eDebug("in total, we moved %d frames", nr_frames);
843
844         return 0;
845 }