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