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