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