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 void eDVBTSTools::closeSource()
30 eDVBTSTools::~eDVBTSTools()
35 int eDVBTSTools::openFile(const char *filename, int nostreaminfo)
37 eRawFile *f = new eRawFile();
38 ePtr<iTsSource> src = f;
40 if (f->open(filename, 1) < 0)
43 setSource(src, nostreaminfo ? NULL : filename);
48 void eDVBTSTools::setSource(ePtr<iTsSource> &source, const char *stream_info_filename)
54 if (stream_info_filename)
56 eDebug("loading streaminfo for %s", stream_info_filename);
57 m_streaminfo.load(stream_info_filename);
60 if (m_streaminfo.hasAccessPoint())
64 // eDebug("no recorded stream information available");
71 void eDVBTSTools::closeFile()
76 m_streaminfo.readClose();
79 void eDVBTSTools::setSyncPID(int pid)
84 void eDVBTSTools::setSearchRange(int maxrange)
86 m_maxrange = maxrange;
89 /* getPTS extracts a pts value from any PID at a given offset. */
90 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
93 if (!m_streaminfo.getPTS(offset, pts))
96 if (!m_source || !m_source->valid())
99 offset -= offset % 188;
101 int left = m_maxrange;
105 unsigned char packet[188];
106 if (m_source->read(offset, packet, 188) != 188)
108 eDebug("read error");
114 if (packet[0] != 0x47)
120 if (packet[i] == 0x47)
128 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
129 int pusi = !!(packet[1] & 0x40);
131 // printf("PID %04x, PUSI %d\n", pid, pusi);
133 unsigned char *payload;
135 /* check for adaption field */
136 if (packet[3] & 0x20)
138 if (packet[4] >= 183)
142 if (packet[5] & 0x10) /* PCR present */
144 pts = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
145 pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
146 pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
147 pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
148 pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
150 eDebug("PCR %16llx found at %lld pid %02x (%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x)", pts, offset, pid, packet[0], packet[1], packet[2], packet[3], packet[4], packet[5], packet[6], packet[7], packet[8], packet[9], packet[10]);
151 if (fixed && fixupPTS(offset, pts))
156 payload = packet + packet[4] + 4 + 1;
158 payload = packet + 4;
166 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
167 if (payload[0] || payload[1] || (payload[2] != 1))
170 if (payload[3] == 0xFD)
171 { // stream use extension mechanism defined in ISO 13818-1 Amendment 2
172 if (payload[7] & 1) // PES extension flag
175 if (payload[7] & 0x80) // pts avail
177 if (payload[7] & 0x40) // dts avail
179 if (payload[7] & 0x20) // escr avail
181 if (payload[7] & 0x10) // es rate
183 if (payload[7] & 0x8) // dsm trickmode
185 if (payload[7] & 0x4) // additional copy info
187 if (payload[7] & 0x2) // crc
189 if (payload[8] < offs)
191 uint8_t pef = payload[9+offs++]; // pes extension field
192 if (pef & 1) // pes extension flag 2
194 if (pef & 0x80) // private data flag
196 if (pef & 0x40) // pack header field flag
198 if (pef & 0x20) // program packet sequence counter flag
200 if (pef & 0x10) // P-STD buffer flag
202 if (payload[8] < offs)
204 uint8_t stream_id_extension_len = payload[9+offs++] & 0x7F;
205 if (stream_id_extension_len >= 1)
207 if (payload[8] < (offs + stream_id_extension_len) )
209 if (payload[9+offs] & 0x80) // stream_id_extension_bit (should not set)
211 switch (payload[9+offs])
213 case 0x55 ... 0x5f: // VC-1
215 case 0x71: // AC3 / DTS
217 case 0x72: // DTS - HD
220 eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]);
233 /* drop non-audio, non-video packets because other streams
234 can be non-compliant.*/
235 else if (((payload[3] & 0xE0) != 0xC0) && // audio
236 ((payload[3] & 0xF0) != 0xE0)) // video
239 if (payload[7] & 0x80) /* PTS */
241 pts = ((unsigned long long)(payload[ 9]&0xE)) << 29;
242 pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
243 pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
244 pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
245 pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
248 eDebug("PTS %16llx found at %lld pid %02x stream: %02x", pts, offset, pid, payload[3]);
250 /* convert to zero-based */
251 if (fixed && fixupPTS(offset, pts))
260 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
262 if (m_use_streaminfo)
264 if (!m_streaminfo.fixupPTS(offset, now))
268 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
272 eDebug("begin not valid, can't fixup");
276 pts_t pos = m_pts_begin;
277 if ((now < pos) && ((pos - now) < 90000 * 10))
283 if (now < pos) /* wrap around */
284 now = now + 0x200000000LL - pos;
289 eDebug("eDVBTSTools::fixupPTS failed!");
293 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
295 eDebug("getOffset for pts 0x%llx", pts);
296 if (m_use_streaminfo)
298 if (pts >= m_pts_end && marg > 0 && m_end_valid)
299 offset = m_offset_end;
301 offset = m_streaminfo.getAccessPoint(pts, marg);
305 calcBegin(); calcEnd();
312 if (!m_samples_taken)
315 if (!m_samples.empty())
322 /* search entry before and after */
323 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
324 std::map<pts_t, off_t>::const_iterator u = l;
326 if (l != m_samples.begin())
329 /* we could have seeked beyond the end */
330 if (u == m_samples.end())
332 /* use last segment for interpolation. */
333 if (l != m_samples.begin())
340 /* if we don't have enough points */
341 if (u == m_samples.end())
344 pts_t pts_diff = u->first - l->first;
345 off_t offset_diff = u->second - l->second;
349 eDebug("something went wrong when taking samples.");
355 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
360 bitrate = offset_diff * 90000 * 8 / pts_diff;
365 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
366 offset -= offset % 188;
370 if (!takeSample(offset, p))
372 int diff = (p - pts) / 90;
374 eDebug("calculated diff %d ms", diff);
377 eDebug("diff to big, refining");
381 eDebug("no sample taken, refinement not possible.");
386 /* if even the first sample couldn't be taken, fall back. */
387 /* otherwise, return most refined result. */
391 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
396 int bitrate = calcBitrate();
397 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
398 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
399 offset -= offset % 188;
405 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
407 if (m_use_streaminfo)
408 return m_streaminfo.getNextAccessPoint(ts, start, direction);
411 eDebug("can't get next access point without streaminfo");
416 void eDVBTSTools::calcBegin()
418 if (!m_source || !m_source->valid())
421 if (!(m_begin_valid || m_futile))
424 if (!getPTS(m_offset_begin, m_pts_begin))
431 void eDVBTSTools::calcEnd()
433 if (!m_source || !m_source->valid())
436 off_t end = m_source->lseek(0, SEEK_END);
438 if (llabs(end - m_last_filelength) > 1*1024*1024)
440 m_last_filelength = end;
444 // eDebug("file size changed, recalc length");
449 m_offset_end = m_last_filelength;
451 while (!(m_end_valid || m_futile))
459 m_offset_end -= m_maxrange;
460 if (m_offset_end < 0)
463 /* restore offset if getpts fails */
464 off_t off = m_offset_end;
466 if (!getPTS(m_offset_end, m_pts_end))
479 int eDVBTSTools::calcLen(pts_t &len)
481 calcBegin(); calcEnd();
482 if (!(m_begin_valid && m_end_valid))
484 len = m_pts_end - m_pts_begin;
487 len += 0x200000000LL;
491 int eDVBTSTools::calcBitrate()
493 calcBegin(); calcEnd();
494 if (!(m_begin_valid && m_end_valid))
497 pts_t len_in_pts = m_pts_end - m_pts_begin;
501 len_in_pts += 0x200000000LL;
502 off_t len_in_bytes = m_offset_end - m_offset_begin;
507 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
508 if ((bitrate < 10000) || (bitrate > 100000000))
515 void eDVBTSTools::takeSamples()
522 if (calcLen(dummy) == -1)
526 off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
527 if (bytes_per_sample < 40*1024*1024)
528 bytes_per_sample = 40*1024*1024;
530 bytes_per_sample -= bytes_per_sample % 188;
532 eDebug("samples step %lld, pts begin %llx, pts end %llx, offs begin %lld, offs end %lld:",
533 bytes_per_sample, m_pts_begin, m_pts_end, m_offset_begin, m_offset_end);
535 for (off_t offset = m_offset_begin; offset < m_offset_end;)
538 if (takeSample(offset, p) && retries--)
541 offset += bytes_per_sample;
543 m_samples[0] = m_offset_begin;
544 m_samples[m_pts_end - m_pts_begin] = m_offset_end;
547 /* returns 0 when a sample was taken. */
548 int eDVBTSTools::takeSample(off_t off, pts_t &p)
550 off_t offset_org = off;
552 if (!eDVBTSTools::getPTS(off, p, 1))
554 /* as we are happily mixing PTS and PCR values (no comment, please), we might
555 end up with some "negative" segments.
557 so check if this new sample is between the previous and the next field*/
559 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
560 std::map<pts_t, off_t>::const_iterator u = l;
562 if (l != m_samples.begin())
565 if (u != m_samples.end())
567 if ((l->second > off) || (u->second < off))
569 eDebug("ignoring sample %lld %lld %lld (%llx %llx %llx)",
570 l->second, off, u->second, l->first, p, u->first);
576 eDebug("adding sample %lld: pts 0x%llx -> pos %lld (diff %lld bytes)", offset_org, p, off, off-offset_org);
583 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
585 /* FIXME: this will be factored out soon! */
586 if (!m_source || !m_source->valid())
588 eDebug(" file not valid");
594 int left = 5*1024*1024;
598 unsigned char packet[188];
599 int ret = m_source->read(position, packet, 188);
602 eDebug("read error");
608 if (packet[0] != 0x47)
613 if (packet[i] == 0x47)
620 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
622 int pusi = !!(packet[1] & 0x40);
627 /* ok, now we have a PES header or section header*/
630 /* check for adaption field */
631 if (packet[3] & 0x20)
633 if (packet[4] >= 183)
635 sec = packet + packet[4] + 4 + 1;
639 if (sec[0]) /* table pointer, assumed to be 0 */
642 if (sec[1] == 0x02) /* program map section */
645 service_id = (sec[4] << 8) | sec[5];
653 int eDVBTSTools::findFrame(off_t &_iframe_offset, off_t &_new_offset, size_t &len, int &direction, int frame_types)
655 off_t offset = _iframe_offset;
658 // eDebug("trying to find iFrame at %lld", offset);
660 if (!m_streaminfo.hasStructure())
662 // eDebug("can't get next iframe without streaminfo");
666 /* let's find the iframe before the given offset */
667 unsigned long long data;
676 res = m_streaminfo.getStructureEntry_prev(offset, data);
678 res = m_streaminfo.getStructureEntry_next(offset, data);
682 eDebug("getting structure info for origin offset failed.");
685 if (offset == 0x7fffffffffffffffLL) /* eof */
687 eDebug("reached eof");
690 /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
691 /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
692 /* TODO: check frame_types */
694 if ((data & 0xE0FF) == 0x0009) /* H.264 NAL unit access delimiter with I-frame*/
696 else if ((data & 0xE07E) == 0x0046) /* H.265 NAL unit access delimiter with I-frame*/
698 else if((data & 0x3800FF) == 0x080000) /* MPEG2 picture start code with I-frame */
703 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0x7E) == 0x0046) || ((data & 0xFF) == 0x00); /* H.264 UAD or H.265 UAD or MPEG2 start code */
712 // eDebug("%08llx@%llu -> %d, %d (I-frame)", data, offset, is_start, nr_frames);
717 --offset; /* move to previous entry */
718 else if (direction == +1)
721 off_t iframe_start = offset;
722 off_t start = offset;
725 /* backtrack to find the previous sequence start, in case of MPEG2 */
726 if ((data & 0xFF) == 0x00) {
729 if (m_streaminfo.getStructureEntry(start, data, 0))
731 eDebug("get previous failed");
734 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00) && ((data & 0xFF) != 0xB3)); /* sequence start or previous frame */
735 if ((data & 0xFF) != 0xB3)
736 start = offset; /* Failed to find corresponding sequence start, so never mind */
741 /* let's find the next frame after the given offset */
743 if (m_streaminfo.getStructureEntry_next(offset, data))
745 eDebug("get next failed");
748 if (offset == 0x7fffffffffffffffLL) /* eof */
750 eDebug("reached eof (while looking for end of iframe)");
753 // eDebug("%08llx@%llu (next frame)", data, offset);
754 } while (((data & 0xFF) != 9) && ((data & 0x7E) != 0x46) && ((data & 0xFF) != 0x00)); /* next frame */
756 if (is_mpeg) //MPEG2 picture start code with I-frame
758 off_t sequence_offset = start;
759 unsigned long long sequence_data;
761 int frame_length = (nr_frames<0) ? -nr_frames: nr_frames;
765 if (m_streaminfo.getStructureEntry_prev(sequence_offset, sequence_data))
767 eDebug("get previous failed");
770 if ((sequence_data & 0xFF) == 0xB3) /* sequence start or previous frame */
772 if(((sequence_data & 0xFF) == 0x0009) || ((sequence_data & 0xFF) == 0x00)) /* H.264 UAD or MPEG2 start code */
774 // eDebug("[findFrame] %08llx@%llu -> %d, %d (sequence)", sequence_data, sequence_offset, is_sequence, frame_length);
775 if(is_sequence) /* get sequence frame */
778 start = sequence_offset;
781 /* align to TS pkt start */
782 start = start - (start % 188);
783 offset = offset - (offset % 188);
786 len = offset - start;
788 _iframe_offset = iframe_start;
789 direction = nr_frames;
790 // eDebug("result: start=%lld, iframe= %lld, next=%lld, len: %ld",start, iframe_start ,offset, (long int)len);
794 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
796 int nr_frames, direction;
797 // eDebug("trying to move %d frames at %llu", distance, offset);
799 frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
801 off_t new_offset = offset;
802 size_t new_len = len;
803 off_t iframe_offset = new_offset;
812 distance = -distance+1;
817 if (findFrame(iframe_offset, new_offset, new_len, dir, frame_types))
819 // eDebug("findFrame failed!\n");
823 distance -= abs(dir);
825 // eDebug("we moved %d, %d to go frames (now at %llu)", dir, distance, new_offset);
827 if (distance >= 0 || direction == 0)
832 nr_frames += abs(dir);
838 nr_frames += abs(dir) + distance; // never jump forward during rewind
841 distance = (direction < 0) ? -nr_frames : nr_frames;
842 // eDebug("in total, we moved %d frames", nr_frames);