1 #define _ISOC99_SOURCE /* for llabs */
2 #include <lib/dvb/tstools.h>
3 #include <lib/base/eerror.h>
9 eDVBTSTools::eDVBTSTools()
13 m_maxrange = 256*1024;
21 m_last_filelength = 0;
26 eDVBTSTools::~eDVBTSTools()
31 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
37 eDebug("loading streaminfo for %s", filename);
38 m_streaminfo.load(filename);
41 if (!m_streaminfo.empty())
45 // eDebug("no recorded stream information available");
51 eSingleLocker l(m_file_lock);
52 if (m_file.open(filename, 1) < 0)
57 void eDVBTSTools::closeFile()
59 eSingleLocker l(m_file_lock);
63 void eDVBTSTools::setSyncPID(int pid)
68 void eDVBTSTools::setSearchRange(int maxrange)
70 m_maxrange = maxrange;
73 /* getPTS extracts a pts value from any PID at a given offset. */
74 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
77 if (!m_streaminfo.getPTS(offset, pts))
83 offset -= offset % 188;
85 eSingleLocker l(m_file_lock);
86 if (m_file.lseek(offset, SEEK_SET) < 0)
88 eDebug("lseek failed");
92 int left = m_maxrange;
96 unsigned char packet[188];
97 if (m_file.read(packet, 188) != 188)
105 if (packet[0] != 0x47)
111 if (packet[i] == 0x47)
115 offset = m_file.lseek(i - 188, SEEK_CUR);
119 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
120 int pusi = !!(packet[1] & 0x40);
122 // printf("PID %04x, PUSI %d\n", pid, pusi);
124 unsigned char *payload;
126 /* check for adaption field */
127 if (packet[3] & 0x20)
129 if (packet[4] >= 183)
133 if (packet[5] & 0x10) /* PCR present */
135 pts = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
136 pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
137 pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
138 pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
139 pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
141 eDebug("PCR found at %llx: %16llx", offset, pts);
142 if (fixed && fixupPTS(offset, pts))
147 payload = packet + packet[4] + 4 + 1;
149 payload = packet + 4;
159 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
160 if (payload[0] || payload[1] || (payload[2] != 1))
163 /* drop non-audio, non-video packets because other streams
164 can be non-compliant.*/
165 if (((payload[3] & 0xE0) != 0xC0) && // audio
166 ((payload[3] & 0xF0) != 0xE0)) // video
169 if (payload[7] & 0x80) /* PTS */
171 pts = ((unsigned long long)(payload[ 9]&0xE)) << 29;
172 pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
173 pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
174 pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
175 pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
178 // eDebug("found pts %08llx at %08llx pid %02x stream: %02x", pts, offset, pid, payload[3]);
180 /* convert to zero-based */
181 if (fixed && fixupPTS(offset, pts))
190 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
192 if (m_use_streaminfo)
194 if (!m_streaminfo.fixupPTS(offset, now))
198 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
202 eDebug("begin not valid, can't fixup");
206 pts_t pos = m_pts_begin;
207 if ((now < pos) && ((pos - now) < 90000 * 10))
213 if (now < pos) /* wrap around */
214 now = now + 0x200000000LL - pos;
221 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
223 if (m_use_streaminfo)
225 if (pts >= m_pts_end && marg > 0 && m_end_valid)
226 offset = m_offset_end;
228 offset = m_streaminfo.getAccessPoint(pts, marg);
232 calcBegin(); calcEnd();
239 if (!m_samples_taken)
242 if (!m_samples.empty())
249 /* search entry before and after */
250 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
251 std::map<pts_t, off_t>::const_iterator u = l;
253 if (l != m_samples.begin())
256 /* we could have seeked beyond the end */
257 if (u == m_samples.end())
259 /* use last segment for interpolation. */
260 if (l != m_samples.begin())
267 /* if we don't have enough points */
268 if (u == m_samples.end())
271 pts_t pts_diff = u->first - l->first;
272 off_t offset_diff = u->second - l->second;
276 eDebug("something went wrong when taking samples.");
282 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
287 bitrate = offset_diff * 90000 * 8 / pts_diff;
292 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
293 offset -= offset % 188;
297 if (!takeSample(offset, p))
299 int diff = (p - pts) / 90;
301 eDebug("calculated diff %d ms", diff);
304 eDebug("diff to big, refining");
308 eDebug("no sample taken, refinement not possible.");
313 /* if even the first sample couldn't be taken, fall back. */
314 /* otherwise, return most refined result. */
318 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
323 int bitrate = calcBitrate();
324 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
325 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
326 offset -= offset % 188;
332 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
334 if (m_use_streaminfo)
335 return m_streaminfo.getNextAccessPoint(ts, start, direction);
338 eDebug("can't get next access point without streaminfo");
343 void eDVBTSTools::calcBegin()
348 if (!(m_begin_valid || m_futile))
351 if (!getPTS(m_offset_begin, m_pts_begin))
358 void eDVBTSTools::calcEnd()
363 eSingleLocker l(m_file_lock);
364 off_t end = m_file.lseek(0, SEEK_END);
366 if (llabs(end - m_last_filelength) > 1*1024*1024)
368 m_last_filelength = end;
372 // eDebug("file size changed, recalc length");
377 m_offset_end = m_last_filelength;
379 while (!(m_end_valid || m_futile))
387 m_offset_end -= m_maxrange;
388 if (m_offset_end < 0)
391 /* restore offset if getpts fails */
392 off_t off = m_offset_end;
394 if (!getPTS(m_offset_end, m_pts_end))
407 int eDVBTSTools::calcLen(pts_t &len)
409 calcBegin(); calcEnd();
410 if (!(m_begin_valid && m_end_valid))
412 len = m_pts_end - m_pts_begin;
415 len += 0x200000000LL;
419 int eDVBTSTools::calcBitrate()
421 calcBegin(); calcEnd();
422 if (!(m_begin_valid && m_end_valid))
425 pts_t len_in_pts = m_pts_end - m_pts_begin;
429 len_in_pts += 0x200000000LL;
430 off_t len_in_bytes = m_offset_end - m_offset_begin;
435 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
436 if ((bitrate < 10000) || (bitrate > 100000000))
443 void eDVBTSTools::takeSamples()
448 if (calcLen(dummy) == -1)
452 off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
453 if (bytes_per_sample < 40*1024*1024)
454 bytes_per_sample = 40*1024*1024;
456 bytes_per_sample -= bytes_per_sample % 188;
458 for (off_t offset = m_offset_begin; offset < m_offset_end; offset += bytes_per_sample)
461 takeSample(offset, p);
463 m_samples[0] = m_offset_begin;
464 m_samples[m_pts_end - m_pts_begin] = m_offset_end;
466 // eDebug("begin, end: %llx %llx", m_offset_begin, m_offset_end);
469 /* returns 0 when a sample was taken. */
470 int eDVBTSTools::takeSample(off_t off, pts_t &p)
472 if (!eDVBTSTools::getPTS(off, p, 1))
474 /* as we are happily mixing PTS and PCR values (no comment, please), we might
475 end up with some "negative" segments.
477 so check if this new sample is between the previous and the next field*/
479 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
480 std::map<pts_t, off_t>::const_iterator u = l;
482 if (l != m_samples.begin())
485 if (u != m_samples.end())
487 if ((l->second > off) || (u->second < off))
489 eDebug("ignoring sample %llx %llx %llx (%lld %lld %lld)",
490 l->second, off, u->second, l->first, p, u->first);
503 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
505 /* FIXME: this will be factored out soon! */
508 eDebug(" file not valid");
512 eSingleLocker l(m_file_lock);
513 if (m_file.lseek(0, SEEK_SET) < 0)
515 eDebug("seek failed");
519 int left = 5*1024*1024;
523 unsigned char packet[188];
524 if (m_file.read(packet, 188) != 188)
526 eDebug("read error");
531 if (packet[0] != 0x47)
536 if (packet[i] == 0x47)
540 m_file.lseek(i - 188, SEEK_CUR);
544 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
546 int pusi = !!(packet[1] & 0x40);
551 /* ok, now we have a PES header or section header*/
554 /* check for adaption field */
555 if (packet[3] & 0x20)
557 if (packet[4] >= 183)
559 sec = packet + packet[4] + 4 + 1;
563 if (sec[0]) /* table pointer, assumed to be 0 */
566 if (sec[1] == 0x02) /* program map section */
569 service_id = (sec[4] << 8) | sec[5];
577 int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int frame_types)
579 off_t offset = _offset;
581 // eDebug("trying to find iFrame at %llx", offset);
583 if (!m_use_streaminfo)
585 // eDebug("can't get next iframe without streaminfo");
589 /* let's find the iframe before the given offset */
590 unsigned long long data;
597 if (m_streaminfo.getStructureEntry(offset, data, (direction == 0) ? 1 : 0))
599 eDebug("getting structure info for origin offset failed.");
602 if (offset == 0x7fffffffffffffffLL) /* eof */
604 eDebug("reached eof");
607 /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
608 /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
609 /* TODO: check frame_types */
610 int is_start = (data & 0xE0FF) == 0x0009; /* H.264 NAL unit access delimiter with I-frame*/
611 is_start |= (data & 0x3800FF) == 0x080000; /* MPEG2 picture start code with I-frame */
613 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00); /* H.264 UAD or MPEG2 start code */
622 // eDebug("%08llx@%llx -> %d, %d", data, offset, is_start, nr_frames);
627 --offset; /* move to previous entry */
628 else if (direction == +1)
631 /* let's find the next frame after the given offset */
632 off_t start = offset;
635 if (m_streaminfo.getStructureEntry(offset, data, 1))
637 eDebug("get next failed");
640 if (offset == 0x7fffffffffffffffLL) /* eof */
642 eDebug("reached eof (while looking for end of iframe)");
645 // eDebug("%08llx@%llx (next)", data, offset);
646 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */
648 /* align to TS pkt start */
649 // start = start - (start % 188);
650 // offset = offset - (offset % 188);
652 len = offset - start;
654 direction = nr_frames;
655 // eDebug("result: offset=%llx, len: %ld", offset, (int)len);
659 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
662 // eDebug("trying to move %d frames at %llx", distance, offset);
664 frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
666 int direction = distance > 0 ? 0 : -1;
667 distance = abs(distance);
669 off_t new_offset = offset;
670 size_t new_len = len;
676 if (findFrame(new_offset, new_len, dir, frame_types))
678 // eDebug("findFrame failed!\n");
682 distance -= abs(dir);
684 // eDebug("we moved %d, %d to go frames (now at %llx)", dir, distance, new_offset);
686 if (distance >= 0 || first)
691 nr_frames += abs(dir);
695 distance = (direction < 0) ? -nr_frames : nr_frames;
696 // eDebug("in total, we moved %d frames", nr_frames);