1 #include <lib/dvb/tstools.h>
2 #include <lib/base/eerror.h>
8 eDVBTSTools::eDVBTSTools()
11 m_maxrange = 256*1024;
19 m_last_filelength = 0;
24 eDVBTSTools::~eDVBTSTools()
29 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
34 m_streaminfo.load((std::string(filename) + ".ap").c_str());
36 if (!m_streaminfo.empty())
40 // eDebug("no recorded stream information available");
46 if (m_file.open(filename, 1) < 0)
51 void eDVBTSTools::closeFile()
56 void eDVBTSTools::setSyncPID(int pid)
61 void eDVBTSTools::setSearchRange(int maxrange)
63 m_maxrange = maxrange;
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)
70 return m_streaminfo.getPTS(offset, pts);
75 offset -= offset % 188;
77 if (m_file.lseek(offset, SEEK_SET) < 0)
80 int left = m_maxrange;
84 unsigned char block[188];
85 if (m_file.read(block, 188) != 188)
102 offset = m_file.lseek(i - 188, SEEK_CUR);
106 int pid = ((block[1] << 8) | block[2]) & 0x1FFF;
107 int pusi = !!(block[1] & 0x40);
109 // printf("PID %04x, PUSI %d\n", pid, pusi);
117 /* ok, now we have a PES header */
120 /* check for adaption field */
122 pes = block + block[4] + 4 + 1;
126 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
127 if (pes[0] || pes[1] || (pes[2] != 1))
130 if (pes[7] & 0x80) /* PTS */
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;
139 // eDebug("found pts %08llx at %08llx", pts, offset);
141 /* convert to zero-based */
143 fixupPTS(offset, pts);
151 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
153 if (m_use_streaminfo)
155 return m_streaminfo.fixupPTS(offset, now);
158 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
162 eDebug("begin not valid, can't fixup");
166 pts_t pos = m_pts_begin;
167 if ((now < pos) && ((pos - now) < 90000 * 10))
173 if (now < pos) /* wrap around */
174 now = now + 0x200000000LL - pos;
181 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts)
183 if (m_use_streaminfo)
185 offset = m_streaminfo.getAccessPoint(pts);
189 // eDebug("get offset");
190 calcBegin(); calcEnd();
191 if (!m_samples_taken)
194 if (!m_samples.empty())
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;
205 if (l != m_samples.begin())
208 /* we could have seeked beyond the end */
209 if (u == m_samples.end())
211 /* use last segment for interpolation. */
212 if (l != m_samples.begin())
219 pts_t pts_diff = u->first - l->first;
220 off_t offset_diff = u->second - l->second;
222 // eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
227 bitrate = offset_diff * 90000 * 8 / pts_diff;
232 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
233 offset -= offset % 188;
237 if (!takeSample(offset, p))
239 int diff = (p - pts) / 90;
241 eDebug("calculated diff %d ms", diff);
244 eDebug("diff to big, refining");
248 eDebug("no sample taken, refinement not possible.");
253 /* if even the first sample couldn't be taken, fall back. */
254 /* otherwise, return most refined result. */
262 eDebug("falling back");
263 int bitrate = calcBitrate();
264 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
265 offset -= offset % 188;
271 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
273 if (m_use_streaminfo)
274 return m_streaminfo.getNextAccessPoint(ts, start, direction);
277 eDebug("can't get next access point without streaminfo");
282 void eDVBTSTools::calcBegin()
287 if (!(m_begin_valid || m_futile))
290 if (!getPTS(m_offset_begin, m_pts_begin))
297 void eDVBTSTools::calcEnd()
302 off_t end = m_file.lseek(0, SEEK_END);
304 if (abs(end - m_last_filelength) > 1*1024*1024)
306 m_last_filelength = end;
310 // eDebug("file size changed, recalc length");
315 m_offset_end = m_last_filelength;
317 while (!(m_end_valid || m_futile))
325 m_offset_end -= m_maxrange;
326 if (m_offset_end < 0)
329 /* restore offset if getpts fails */
330 off_t off = m_offset_end;
332 if (!getPTS(m_offset_end, m_pts_end))
345 int eDVBTSTools::calcLen(pts_t &len)
347 calcBegin(); calcEnd();
348 if (!(m_begin_valid && m_end_valid))
350 len = m_pts_end - m_pts_begin;
353 len += 0x200000000LL;
357 int eDVBTSTools::calcBitrate()
359 calcBegin(); calcEnd();
360 if (!(m_begin_valid && m_end_valid))
363 pts_t len_in_pts = m_pts_end - m_pts_begin;
367 len_in_pts += 0x200000000LL;
368 off_t len_in_bytes = m_offset_end - m_offset_begin;
373 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
374 if ((bitrate < 10000) || (bitrate > 100000000))
381 void eDVBTSTools::takeSamples()
386 if (calcLen(dummy) == -1)
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;
394 bytes_per_sample -= bytes_per_sample % 188;
396 for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
399 takeSample(offset, p);
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);
406 /* returns 0 when a sample was taken. */
407 int eDVBTSTools::takeSample(off_t off, pts_t &p)
409 if (!eDVBTSTools::getPTS(off, p, 1))
411 eDebug("sample: %llx, %llx", off, p);
418 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
420 /* FIXME: this will be factored out soon! */
423 eDebug(" file not valid");
427 if (m_file.lseek(0, SEEK_SET) < 0)
429 eDebug("seek failed");
433 int left = 5*1024*1024;
437 unsigned char block[188];
438 if (m_file.read(block, 188) != 188)
440 eDebug("read error");
445 if (block[0] != 0x47)
450 if (block[i] == 0x47)
454 m_file.lseek(i - 188, SEEK_CUR);
458 int pid = ((block[1] << 8) | block[2]) & 0x1FFF;
460 int pusi = !!(block[1] & 0x40);
465 /* ok, now we have a PES header or section header*/
468 /* check for adaption field */
470 sec = block + block[4] + 4 + 1;
474 if (sec[0]) /* table pointer, assumed to be 0 */
477 if (sec[1] == 0x02) /* program map section */
480 service_id = (sec[4] << 8) | sec[5];