1 #define _ISOC99_SOURCE /* for llabs */
2 #include <lib/dvb/tstools.h>
3 #include <lib/base/eerror.h>
9 eDVBTSTools::eDVBTSTools()
12 m_maxrange = 256*1024;
20 m_last_filelength = 0;
25 eDVBTSTools::~eDVBTSTools()
30 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
36 eDebug("loading streaminfo for %s", filename);
37 m_streaminfo.load(filename);
40 if (!m_streaminfo.empty())
44 // eDebug("no recorded stream information available");
50 if (m_file.open(filename, 1) < 0)
55 void eDVBTSTools::closeFile()
60 void eDVBTSTools::setSyncPID(int pid)
65 void eDVBTSTools::setSearchRange(int maxrange)
67 m_maxrange = maxrange;
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)
74 return m_streaminfo.getPTS(offset, pts);
79 offset -= offset % 188;
81 if (m_file.lseek(offset, SEEK_SET) < 0)
83 eDebug("lseek failed");
87 int left = m_maxrange;
91 unsigned char packet[188];
92 if (m_file.read(packet, 188) != 188)
100 if (packet[0] != 0x47)
106 if (packet[i] == 0x47)
110 offset = m_file.lseek(i - 188, SEEK_CUR);
114 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
115 int pusi = !!(packet[1] & 0x40);
117 // printf("PID %04x, PUSI %d\n", pid, pusi);
119 unsigned char *payload;
121 /* check for adaption field */
122 if (packet[3] & 0x20)
124 if (packet[4] >= 183)
128 if (packet[5] & 0x10) /* PCR present */
130 pts = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
131 pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
132 pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
133 pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
134 pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
136 eDebug("PCR found at %llx: %16llx", offset, pts);
137 if (fixed && fixupPTS(offset, pts))
142 payload = packet + packet[4] + 4 + 1;
144 payload = packet + 4;
154 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
155 if (payload[0] || payload[1] || (payload[2] != 1))
158 /* drop non-audio, non-video packets because other streams
159 can be non-compliant.*/
160 if (((payload[3] & 0xE0) != 0xC0) && // audio
161 ((payload[3] & 0xF0) != 0xE0)) // video
164 if (payload[7] & 0x80) /* PTS */
166 pts = ((unsigned long long)(payload[ 9]&0xE)) << 29;
167 pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
168 pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
169 pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
170 pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
173 // eDebug("found pts %08llx at %08llx pid %02x stream: %02x", pts, offset, pid, payload[3]);
175 /* convert to zero-based */
176 if (fixed && fixupPTS(offset, pts))
185 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
187 if (m_use_streaminfo)
189 return m_streaminfo.fixupPTS(offset, now);
192 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
196 eDebug("begin not valid, can't fixup");
200 pts_t pos = m_pts_begin;
201 if ((now < pos) && ((pos - now) < 90000 * 10))
207 if (now < pos) /* wrap around */
208 now = now + 0x200000000LL - pos;
215 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
217 if (m_use_streaminfo)
219 if (pts >= m_pts_end && marg > 0 && m_end_valid)
220 offset = m_offset_end;
222 offset = m_streaminfo.getAccessPoint(pts, marg);
226 calcBegin(); calcEnd();
233 if (!m_samples_taken)
236 if (!m_samples.empty())
243 /* search entry before and after */
244 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
245 std::map<pts_t, off_t>::const_iterator u = l;
247 if (l != m_samples.begin())
250 /* we could have seeked beyond the end */
251 if (u == m_samples.end())
253 /* use last segment for interpolation. */
254 if (l != m_samples.begin())
261 /* if we don't have enough points */
262 if (u == m_samples.end())
265 pts_t pts_diff = u->first - l->first;
266 off_t offset_diff = u->second - l->second;
270 eDebug("something went wrong when taking samples.");
276 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
281 bitrate = offset_diff * 90000 * 8 / pts_diff;
286 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
287 offset -= offset % 188;
291 if (!takeSample(offset, p))
293 int diff = (p - pts) / 90;
295 eDebug("calculated diff %d ms", diff);
298 eDebug("diff to big, refining");
302 eDebug("no sample taken, refinement not possible.");
307 /* if even the first sample couldn't be taken, fall back. */
308 /* otherwise, return most refined result. */
312 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
317 int bitrate = calcBitrate();
318 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
319 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
320 offset -= offset % 188;
326 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
328 if (m_use_streaminfo)
329 return m_streaminfo.getNextAccessPoint(ts, start, direction);
332 eDebug("can't get next access point without streaminfo");
337 void eDVBTSTools::calcBegin()
342 if (!(m_begin_valid || m_futile))
345 if (!getPTS(m_offset_begin, m_pts_begin))
352 void eDVBTSTools::calcEnd()
357 off_t end = m_file.lseek(0, SEEK_END);
359 if (llabs(end - m_last_filelength) > 1*1024*1024)
361 m_last_filelength = end;
365 // eDebug("file size changed, recalc length");
370 m_offset_end = m_last_filelength;
372 while (!(m_end_valid || m_futile))
380 m_offset_end -= m_maxrange;
381 if (m_offset_end < 0)
384 /* restore offset if getpts fails */
385 off_t off = m_offset_end;
387 if (!getPTS(m_offset_end, m_pts_end))
400 int eDVBTSTools::calcLen(pts_t &len)
402 calcBegin(); calcEnd();
403 if (!(m_begin_valid && m_end_valid))
405 len = m_pts_end - m_pts_begin;
408 len += 0x200000000LL;
412 int eDVBTSTools::calcBitrate()
414 calcBegin(); calcEnd();
415 if (!(m_begin_valid && m_end_valid))
418 pts_t len_in_pts = m_pts_end - m_pts_begin;
422 len_in_pts += 0x200000000LL;
423 off_t len_in_bytes = m_offset_end - m_offset_begin;
428 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
429 if ((bitrate < 10000) || (bitrate > 100000000))
436 void eDVBTSTools::takeSamples()
441 if (calcLen(dummy) == -1)
445 off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
446 if (bytes_per_sample < 40*1024*1024)
447 bytes_per_sample = 40*1024*1024;
449 bytes_per_sample -= bytes_per_sample % 188;
451 for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
454 takeSample(offset, p);
456 m_samples[0] = m_offset_begin;
457 m_samples[m_pts_end - m_pts_begin] = m_offset_end;
459 // eDebug("begin, end: %llx %llx", m_offset_begin, m_offset_end);
462 /* returns 0 when a sample was taken. */
463 int eDVBTSTools::takeSample(off_t off, pts_t &p)
465 if (!eDVBTSTools::getPTS(off, p, 1))
467 /* as we are happily mixing PTS and PCR values (no comment, please), we might
468 end up with some "negative" segments.
470 so check if this new sample is between the previous and the next field*/
472 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
473 std::map<pts_t, off_t>::const_iterator u = l;
475 if (l != m_samples.begin())
478 if (u != m_samples.end())
480 if ((l->second > off) || (u->second < off))
482 eDebug("ignoring sample %llx %llx %llx (%lld %lld %lld)",
483 l->second, off, u->second, l->first, p, u->first);
496 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
498 /* FIXME: this will be factored out soon! */
501 eDebug(" file not valid");
505 if (m_file.lseek(0, SEEK_SET) < 0)
507 eDebug("seek failed");
511 int left = 5*1024*1024;
515 unsigned char packet[188];
516 if (m_file.read(packet, 188) != 188)
518 eDebug("read error");
523 if (packet[0] != 0x47)
528 if (packet[i] == 0x47)
532 m_file.lseek(i - 188, SEEK_CUR);
536 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
538 int pusi = !!(packet[1] & 0x40);
543 /* ok, now we have a PES header or section header*/
546 /* check for adaption field */
547 if (packet[3] & 0x20)
549 if (packet[4] >= 183)
551 sec = packet + packet[4] + 4 + 1;
555 if (sec[0]) /* table pointer, assumed to be 0 */
558 if (sec[1] == 0x02) /* program map section */
561 service_id = (sec[4] << 8) | sec[5];
569 int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int frame_types)
571 off_t offset = _offset;
573 // eDebug("trying to find iFrame at %llx", offset);
575 if (!m_use_streaminfo)
577 // eDebug("can't get next iframe without streaminfo");
581 /* let's find the iframe before the given offset */
582 unsigned long long data;
589 if (m_streaminfo.getStructureEntry(offset, data, (direction == 0) ? 1 : 0))
591 eDebug("getting structure info for origin offset failed.");
594 if (offset == 0x7fffffffffffffffLL) /* eof */
596 eDebug("reached eof");
599 /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
600 /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
601 /* TODO: check frame_types */
602 int is_start = (data & 0xE0FF) == 0x0009; /* H.264 NAL unit access delimiter with I-frame*/
603 is_start |= (data & 0x3800FF) == 0x080000; /* MPEG2 picture start code with I-frame */
605 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00); /* H.264 UAD or MPEG2 start code */
614 // eDebug("%08llx@%llx -> %d, %d", data, offset, is_start, nr_frames);
619 --offset; /* move to previous entry */
620 else if (direction == +1)
623 /* let's find the next frame after the given offset */
624 off_t start = offset;
627 if (m_streaminfo.getStructureEntry(offset, data, 1))
629 eDebug("get next failed");
632 if (offset == 0x7fffffffffffffffLL) /* eof */
634 eDebug("reached eof (while looking for end of iframe)");
637 // eDebug("%08llx@%llx (next)", data, offset);
638 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */
640 /* align to TS pkt start */
641 // start = start - (start % 188);
642 // offset = offset - (offset % 188);
644 len = offset - start;
646 direction = nr_frames;
647 // eDebug("result: offset=%llx, len: %ld", offset, (int)len);
651 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
654 // eDebug("trying to move %d frames at %llx", distance, offset);
656 frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
658 int direction = distance > 0 ? 0 : -1;
659 distance = abs(distance);
661 off_t new_offset = offset;
662 size_t new_len = len;
667 if (findFrame(new_offset, new_len, dir, frame_types))
669 // eDebug("findFrame failed!\n");
673 distance -= abs(dir);
675 // eDebug("we moved %d, %d to go frames (now at %llx)", dir, distance, new_offset);
681 nr_frames += abs(dir);
685 distance = (direction < 0) ? -nr_frames : nr_frames;
686 // eDebug("in total, we moved %d frames", nr_frames);