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