remove some debug, fix abs -> llabs
[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)
213 {
214         if (m_use_streaminfo)
215         {
216                 offset = m_streaminfo.getAccessPoint(pts);
217                 return 0;
218         } else
219         {
220                 calcBegin(); calcEnd();
221                 
222                 if (!m_begin_valid)
223                         return -1;
224                 if (!m_end_valid)
225                         return -1;
226
227                 if (!m_samples_taken)
228                         takeSamples();
229                 
230                 if (!m_samples.empty())
231                 {
232                         int maxtries = 5;
233                         pts_t p = -1;
234                         
235                         while (maxtries--)
236                         {
237                                         /* search entry before and after */
238                                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
239                                 std::map<pts_t, off_t>::const_iterator u = l;
240
241                                 if (l != m_samples.begin())
242                                         --l;
243                                 
244                                         /* we could have seeked beyond the end */
245                                 if (u == m_samples.end())
246                                 {
247                                                 /* use last segment for interpolation. */
248                                         if (l != m_samples.begin())
249                                         {
250                                                 --u;
251                                                 --l;
252                                         }
253                                 }
254                                         
255                                         /* if we don't have enough points */
256                                 if (u == m_samples.end())
257                                         break;
258                                 
259                                 pts_t pts_diff = u->first - l->first;
260                                 off_t offset_diff = u->second - l->second;
261
262                                 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
263
264                                 int bitrate;
265                                 
266                                 if (pts_diff)
267                                         bitrate = offset_diff * 90000 * 8 / pts_diff;
268                                 else
269                                         bitrate = 0;
270
271                                 offset = l->second;
272                                 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
273                                 offset -= offset % 188;
274                                 
275                                 p = pts;
276                                 
277                                 if (!takeSample(offset, p))
278                                 {
279                                         int diff = (p - pts) / 90;
280                         
281                                         eDebug("calculated diff %d ms", diff);
282                                         if (abs(diff) > 300)
283                                         {
284                                                 eDebug("diff to big, refining");
285                                                 continue;
286                                         }
287                                 } else
288                                         eDebug("no sample taken, refinement not possible.");
289
290                                 break;
291                         }
292                         
293                                 /* if even the first sample couldn't be taken, fall back. */
294                                 /* otherwise, return most refined result. */
295                         if (p != -1)
296                         {
297                                 pts = p;
298                                 return 0;
299                         }
300                 }
301                 
302                 int bitrate = calcBitrate();
303                 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
304                 offset -= offset % 188;
305                 
306                 return 0;
307         }
308 }
309
310 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
311 {
312         if (m_use_streaminfo)
313                 return m_streaminfo.getNextAccessPoint(ts, start, direction);
314         else
315         {
316                 eDebug("can't get next access point without streaminfo");
317                 return -1;
318         }
319 }
320
321 void eDVBTSTools::calcBegin()
322 {
323         if (!m_file.valid())
324                 return;
325
326         if (!(m_begin_valid || m_futile))
327         {
328                 m_offset_begin = 0;
329                 if (!getPTS(m_offset_begin, m_pts_begin))
330                         m_begin_valid = 1;
331                 else
332                         m_futile = 1;
333         }
334 }
335
336 void eDVBTSTools::calcEnd()
337 {
338         if (!m_file.valid())
339                 return;
340         
341         off_t end = m_file.lseek(0, SEEK_END);
342         
343         if (llabs(end - m_last_filelength) > 1*1024*1024)
344         {
345                 m_last_filelength = end;
346                 m_end_valid = 0;
347                 
348                 m_futile = 0;
349 //              eDebug("file size changed, recalc length");
350         }
351         
352         int maxiter = 10;
353         
354         m_offset_end = m_last_filelength;
355         
356         while (!(m_end_valid || m_futile))
357         {
358                 if (!--maxiter)
359                 {
360                         m_futile = 1;
361                         return;
362                 }
363
364                 m_offset_end -= m_maxrange;
365                 if (m_offset_end < 0)
366                         m_offset_end = 0;
367
368                         /* restore offset if getpts fails */
369                 off_t off = m_offset_end;
370
371                 if (!getPTS(m_offset_end, m_pts_end))
372                         m_end_valid = 1;
373                 else
374                         m_offset_end = off;
375
376                 if (!m_offset_end)
377                 {
378                         m_futile = 1;
379                         break;
380                 }
381         }
382 }
383
384 int eDVBTSTools::calcLen(pts_t &len)
385 {
386         calcBegin(); calcEnd();
387         if (!(m_begin_valid && m_end_valid))
388                 return -1;
389         len = m_pts_end - m_pts_begin;
390                 /* wrap around? */
391         if (len < 0)
392                 len += 0x200000000LL;
393         return 0;
394 }
395
396 int eDVBTSTools::calcBitrate()
397 {
398         calcBegin(); calcEnd();
399         if (!(m_begin_valid && m_end_valid))
400                 return -1;
401
402         pts_t len_in_pts = m_pts_end - m_pts_begin;
403
404                 /* wrap around? */
405         if (len_in_pts < 0)
406                 len_in_pts += 0x200000000LL;
407         off_t len_in_bytes = m_offset_end - m_offset_begin;
408         
409         if (!len_in_pts)
410                 return -1;
411         
412         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
413         if ((bitrate < 10000) || (bitrate > 100000000))
414                 return -1;
415         
416         return bitrate;
417 }
418
419         /* pts, off */
420 void eDVBTSTools::takeSamples()
421 {
422         m_samples_taken = 1;
423         m_samples.clear();
424         pts_t dummy;
425         if (calcLen(dummy) == -1)
426                 return;
427         
428         int nr_samples = 30;
429         off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
430         if (bytes_per_sample < 40*1024*1024)
431                 bytes_per_sample = 40*1024*1024;
432
433         bytes_per_sample -= bytes_per_sample % 188;
434         
435         for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
436         {
437                 pts_t p;
438                 takeSample(offset, p);
439         }
440         m_samples[0] = m_offset_begin;
441         m_samples[m_pts_end - m_pts_begin] = m_offset_end;
442         
443 //      eDebug("begin, end: %llx %llx", m_offset_begin, m_offset_end); 
444 }
445
446         /* returns 0 when a sample was taken. */
447 int eDVBTSTools::takeSample(off_t off, pts_t &p)
448 {
449         if (!eDVBTSTools::getPTS(off, p, 1))
450         {
451                 m_samples[p] = off;
452                 return 0;
453         }
454         return 1;
455 }
456
457 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
458 {
459                 /* FIXME: this will be factored out soon! */
460         if (!m_file.valid())
461         {
462                 eDebug(" file not valid");
463                 return -1;
464         }
465
466         if (m_file.lseek(0, SEEK_SET) < 0)
467         {
468                 eDebug("seek failed");
469                 return -1;
470         }
471
472         int left = 5*1024*1024;
473         
474         while (left >= 188)
475         {
476                 unsigned char packet[188];
477                 if (m_file.read(packet, 188) != 188)
478                 {
479                         eDebug("read error");
480                         break;
481                 }
482                 left -= 188;
483                 
484                 if (packet[0] != 0x47)
485                 {
486                         int i = 0;
487                         while (i < 188)
488                         {
489                                 if (packet[i] == 0x47)
490                                         break;
491                                 ++i;
492                         }
493                         m_file.lseek(i - 188, SEEK_CUR);
494                         continue;
495                 }
496                 
497                 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
498                 
499                 int pusi = !!(packet[1] & 0x40);
500                 
501                 if (!pusi)
502                         continue;
503                 
504                         /* ok, now we have a PES header or section header*/
505                 unsigned char *sec;
506                 
507                         /* check for adaption field */
508                 if (packet[3] & 0x20)
509                 {
510                         if (packet[4] >= 183)
511                                 continue;
512                         sec = packet + packet[4] + 4 + 1;
513                 } else
514                         sec = packet + 4;
515                 
516                 if (sec[0])     /* table pointer, assumed to be 0 */
517                         continue;
518
519                 if (sec[1] == 0x02) /* program map section */
520                 {
521                         pmt_pid = pid;
522                         service_id = (sec[4] << 8) | sec[5];
523                         return 0;
524                 }
525         }
526         
527         return -1;
528 }