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 if (!m_streaminfo.getPTS(offset, pts))
80 offset -= offset % 188;
82 if (m_file.lseek(offset, SEEK_SET) < 0)
84 eDebug("lseek failed");
88 int left = m_maxrange;
92 unsigned char packet[188];
93 if (m_file.read(packet, 188) != 188)
101 if (packet[0] != 0x47)
107 if (packet[i] == 0x47)
111 offset = m_file.lseek(i - 188, SEEK_CUR);
115 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
116 int pusi = !!(packet[1] & 0x40);
118 // printf("PID %04x, PUSI %d\n", pid, pusi);
120 unsigned char *payload;
122 /* check for adaption field */
123 if (packet[3] & 0x20)
125 if (packet[4] >= 183)
129 if (packet[5] & 0x10) /* PCR present */
131 pts = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
132 pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
133 pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
134 pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
135 pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
137 eDebug("PCR found at %llx: %16llx", offset, pts);
138 if (fixed && fixupPTS(offset, pts))
143 payload = packet + packet[4] + 4 + 1;
145 payload = packet + 4;
155 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
156 if (payload[0] || payload[1] || (payload[2] != 1))
159 /* drop non-audio, non-video packets because other streams
160 can be non-compliant.*/
161 if (((payload[3] & 0xE0) != 0xC0) && // audio
162 ((payload[3] & 0xF0) != 0xE0)) // video
165 if (payload[7] & 0x80) /* PTS */
167 pts = ((unsigned long long)(payload[ 9]&0xE)) << 29;
168 pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
169 pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
170 pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
171 pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
174 // eDebug("found pts %08llx at %08llx pid %02x stream: %02x", pts, offset, pid, payload[3]);
176 /* convert to zero-based */
177 if (fixed && fixupPTS(offset, pts))
186 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
188 if (m_use_streaminfo)
190 if (!m_streaminfo.fixupPTS(offset, now))
194 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
198 eDebug("begin not valid, can't fixup");
202 pts_t pos = m_pts_begin;
203 if ((now < pos) && ((pos - now) < 90000 * 10))
209 if (now < pos) /* wrap around */
210 now = now + 0x200000000LL - pos;
217 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
219 if (m_use_streaminfo)
221 if (pts >= m_pts_end && marg > 0 && m_end_valid)
222 offset = m_offset_end;
224 offset = m_streaminfo.getAccessPoint(pts, marg);
228 calcBegin(); calcEnd();
235 if (!m_samples_taken)
238 if (!m_samples.empty())
245 /* search entry before and after */
246 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
247 std::map<pts_t, off_t>::const_iterator u = l;
249 if (l != m_samples.begin())
252 /* we could have seeked beyond the end */
253 if (u == m_samples.end())
255 /* use last segment for interpolation. */
256 if (l != m_samples.begin())
263 /* if we don't have enough points */
264 if (u == m_samples.end())
267 pts_t pts_diff = u->first - l->first;
268 off_t offset_diff = u->second - l->second;
272 eDebug("something went wrong when taking samples.");
278 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
283 bitrate = offset_diff * 90000 * 8 / pts_diff;
288 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
289 offset -= offset % 188;
293 if (!takeSample(offset, p))
295 int diff = (p - pts) / 90;
297 eDebug("calculated diff %d ms", diff);
300 eDebug("diff to big, refining");
304 eDebug("no sample taken, refinement not possible.");
309 /* if even the first sample couldn't be taken, fall back. */
310 /* otherwise, return most refined result. */
314 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
319 int bitrate = calcBitrate();
320 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
321 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
322 offset -= offset % 188;
328 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
330 if (m_use_streaminfo)
331 return m_streaminfo.getNextAccessPoint(ts, start, direction);
334 eDebug("can't get next access point without streaminfo");
339 void eDVBTSTools::calcBegin()
344 if (!(m_begin_valid || m_futile))
347 if (!getPTS(m_offset_begin, m_pts_begin))
354 void eDVBTSTools::calcEnd()
359 off_t end = m_file.lseek(0, SEEK_END);
361 if (llabs(end - m_last_filelength) > 1*1024*1024)
363 m_last_filelength = end;
367 // eDebug("file size changed, recalc length");
372 m_offset_end = m_last_filelength;
374 while (!(m_end_valid || m_futile))
382 m_offset_end -= m_maxrange;
383 if (m_offset_end < 0)
386 /* restore offset if getpts fails */
387 off_t off = m_offset_end;
389 if (!getPTS(m_offset_end, m_pts_end))
402 int eDVBTSTools::calcLen(pts_t &len)
404 calcBegin(); calcEnd();
405 if (!(m_begin_valid && m_end_valid))
407 len = m_pts_end - m_pts_begin;
410 len += 0x200000000LL;
414 int eDVBTSTools::calcBitrate()
416 calcBegin(); calcEnd();
417 if (!(m_begin_valid && m_end_valid))
420 pts_t len_in_pts = m_pts_end - m_pts_begin;
424 len_in_pts += 0x200000000LL;
425 off_t len_in_bytes = m_offset_end - m_offset_begin;
430 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
431 if ((bitrate < 10000) || (bitrate > 100000000))
438 void eDVBTSTools::takeSamples()
443 if (calcLen(dummy) == -1)
447 off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
448 if (bytes_per_sample < 40*1024*1024)
449 bytes_per_sample = 40*1024*1024;
451 bytes_per_sample -= bytes_per_sample % 188;
453 for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
456 takeSample(offset, p);
458 m_samples[0] = m_offset_begin;
459 m_samples[m_pts_end - m_pts_begin] = m_offset_end;
461 // eDebug("begin, end: %llx %llx", m_offset_begin, m_offset_end);
464 /* returns 0 when a sample was taken. */
465 int eDVBTSTools::takeSample(off_t off, pts_t &p)
467 if (!eDVBTSTools::getPTS(off, p, 1))
469 /* as we are happily mixing PTS and PCR values (no comment, please), we might
470 end up with some "negative" segments.
472 so check if this new sample is between the previous and the next field*/
474 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
475 std::map<pts_t, off_t>::const_iterator u = l;
477 if (l != m_samples.begin())
480 if (u != m_samples.end())
482 if ((l->second > off) || (u->second < off))
484 eDebug("ignoring sample %llx %llx %llx (%lld %lld %lld)",
485 l->second, off, u->second, l->first, p, u->first);
498 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
500 /* FIXME: this will be factored out soon! */
503 eDebug(" file not valid");
507 if (m_file.lseek(0, SEEK_SET) < 0)
509 eDebug("seek failed");
513 int left = 5*1024*1024;
517 unsigned char packet[188];
518 if (m_file.read(packet, 188) != 188)
520 eDebug("read error");
525 if (packet[0] != 0x47)
530 if (packet[i] == 0x47)
534 m_file.lseek(i - 188, SEEK_CUR);
538 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
540 int pusi = !!(packet[1] & 0x40);
545 /* ok, now we have a PES header or section header*/
548 /* check for adaption field */
549 if (packet[3] & 0x20)
551 if (packet[4] >= 183)
553 sec = packet + packet[4] + 4 + 1;
557 if (sec[0]) /* table pointer, assumed to be 0 */
560 if (sec[1] == 0x02) /* program map section */
563 service_id = (sec[4] << 8) | sec[5];
571 int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int frame_types)
573 off_t offset = _offset;
575 // eDebug("trying to find iFrame at %llx", offset);
577 if (!m_use_streaminfo)
579 // eDebug("can't get next iframe without streaminfo");
583 /* let's find the iframe before the given offset */
584 unsigned long long data;
591 if (m_streaminfo.getStructureEntry(offset, data, (direction == 0) ? 1 : 0))
593 eDebug("getting structure info for origin offset failed.");
596 if (offset == 0x7fffffffffffffffLL) /* eof */
598 eDebug("reached eof");
601 /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
602 /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
603 /* TODO: check frame_types */
604 int is_start = (data & 0xE0FF) == 0x0009; /* H.264 NAL unit access delimiter with I-frame*/
605 is_start |= (data & 0x3800FF) == 0x080000; /* MPEG2 picture start code with I-frame */
607 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00); /* H.264 UAD or MPEG2 start code */
616 // eDebug("%08llx@%llx -> %d, %d", data, offset, is_start, nr_frames);
621 --offset; /* move to previous entry */
622 else if (direction == +1)
625 /* let's find the next frame after the given offset */
626 off_t start = offset;
629 if (m_streaminfo.getStructureEntry(offset, data, 1))
631 eDebug("get next failed");
634 if (offset == 0x7fffffffffffffffLL) /* eof */
636 eDebug("reached eof (while looking for end of iframe)");
639 // eDebug("%08llx@%llx (next)", data, offset);
640 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */
642 /* align to TS pkt start */
643 // start = start - (start % 188);
644 // offset = offset - (offset % 188);
646 len = offset - start;
648 direction = nr_frames;
649 // eDebug("result: offset=%llx, len: %ld", offset, (int)len);
653 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
656 // eDebug("trying to move %d frames at %llx", distance, offset);
658 frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
660 int direction = distance > 0 ? 0 : -1;
661 distance = abs(distance);
663 off_t new_offset = offset;
664 size_t new_len = len;
670 if (findFrame(new_offset, new_len, dir, frame_types))
672 // eDebug("findFrame failed!\n");
676 distance -= abs(dir);
678 // eDebug("we moved %d, %d to go frames (now at %llx)", dir, distance, new_offset);
680 if (distance >= 0 || first)
685 nr_frames += abs(dir);
689 distance = (direction < 0) ? -nr_frames : nr_frames;
690 // eDebug("in total, we moved %d frames", nr_frames);