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.empty())
64 // eDebug("no recorded stream information available");
71 void eDVBTSTools::closeFile()
77 void eDVBTSTools::setSyncPID(int pid)
82 void eDVBTSTools::setSearchRange(int maxrange)
84 m_maxrange = maxrange;
87 /* getPTS extracts a pts value from any PID at a given offset. */
88 int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
91 if (!m_streaminfo.getPTS(offset, pts))
94 if (!m_source || !m_source->valid())
97 offset -= offset % 188;
99 int left = m_maxrange;
103 unsigned char packet[188];
104 if (m_source->read(offset, packet, 188) != 188)
106 eDebug("read error");
112 if (packet[0] != 0x47)
118 if (packet[i] == 0x47)
126 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
127 int pusi = !!(packet[1] & 0x40);
129 // printf("PID %04x, PUSI %d\n", pid, pusi);
131 unsigned char *payload;
133 /* check for adaption field */
134 if (packet[3] & 0x20)
136 if (packet[4] >= 183)
140 if (packet[5] & 0x10) /* PCR present */
142 pts = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
143 pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
144 pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
145 pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
146 pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
148 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]);
149 if (fixed && fixupPTS(offset, pts))
154 payload = packet + packet[4] + 4 + 1;
156 payload = packet + 4;
164 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
165 if (payload[0] || payload[1] || (payload[2] != 1))
168 if (payload[3] == 0xFD)
169 { // stream use extension mechanism defined in ISO 13818-1 Amendment 2
170 if (payload[7] & 1) // PES extension flag
173 if (payload[7] & 0x80) // pts avail
175 if (payload[7] & 0x40) // dts avail
177 if (payload[7] & 0x20) // escr avail
179 if (payload[7] & 0x10) // es rate
181 if (payload[7] & 0x8) // dsm trickmode
183 if (payload[7] & 0x4) // additional copy info
185 if (payload[7] & 0x2) // crc
187 if (payload[8] < offs)
189 uint8_t pef = payload[9+offs++]; // pes extension field
190 if (pef & 1) // pes extension flag 2
192 if (pef & 0x80) // private data flag
194 if (pef & 0x40) // pack header field flag
196 if (pef & 0x20) // program packet sequence counter flag
198 if (pef & 0x10) // P-STD buffer flag
200 if (payload[8] < offs)
202 uint8_t stream_id_extension_len = payload[9+offs++] & 0x7F;
203 if (stream_id_extension_len >= 1)
205 if (payload[8] < (offs + stream_id_extension_len) )
207 if (payload[9+offs] & 0x80) // stream_id_extension_bit (should not set)
209 switch (payload[9+offs])
211 case 0x55 ... 0x5f: // VC-1
213 case 0x71: // AC3 / DTS
215 case 0x72: // DTS - HD
218 eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]);
231 /* drop non-audio, non-video packets because other streams
232 can be non-compliant.*/
233 else if (((payload[3] & 0xE0) != 0xC0) && // audio
234 ((payload[3] & 0xF0) != 0xE0)) // video
237 if (payload[7] & 0x80) /* PTS */
239 pts = ((unsigned long long)(payload[ 9]&0xE)) << 29;
240 pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
241 pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
242 pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
243 pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
246 eDebug("PTS %16llx found at %lld pid %02x stream: %02x", pts, offset, pid, payload[3]);
248 /* convert to zero-based */
249 if (fixed && fixupPTS(offset, pts))
258 int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now)
260 if (m_use_streaminfo)
262 if (!m_streaminfo.fixupPTS(offset, now))
266 /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
270 eDebug("begin not valid, can't fixup");
274 pts_t pos = m_pts_begin;
275 if ((now < pos) && ((pos - now) < 90000 * 10))
281 if (now < pos) /* wrap around */
282 now = now + 0x200000000LL - pos;
287 eDebug("eDVBTSTools::fixupPTS failed!");
291 int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg)
293 eDebug("getOffset for pts 0x%llx", pts);
294 if (m_use_streaminfo)
296 if (pts >= m_pts_end && marg > 0 && m_end_valid)
297 offset = m_offset_end;
299 offset = m_streaminfo.getAccessPoint(pts, marg);
303 calcBegin(); calcEnd();
310 if (!m_samples_taken)
313 if (!m_samples.empty())
320 /* search entry before and after */
321 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
322 std::map<pts_t, off_t>::const_iterator u = l;
324 if (l != m_samples.begin())
327 /* we could have seeked beyond the end */
328 if (u == m_samples.end())
330 /* use last segment for interpolation. */
331 if (l != m_samples.begin())
338 /* if we don't have enough points */
339 if (u == m_samples.end())
342 pts_t pts_diff = u->first - l->first;
343 off_t offset_diff = u->second - l->second;
347 eDebug("something went wrong when taking samples.");
353 eDebug("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
358 bitrate = offset_diff * 90000 * 8 / pts_diff;
363 offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
364 offset -= offset % 188;
368 if (!takeSample(offset, p))
370 int diff = (p - pts) / 90;
372 eDebug("calculated diff %d ms", diff);
375 eDebug("diff to big, refining");
379 eDebug("no sample taken, refinement not possible.");
384 /* if even the first sample couldn't be taken, fall back. */
385 /* otherwise, return most refined result. */
389 eDebug("aborting. Taking %llx as offset for %lld", offset, pts);
394 int bitrate = calcBitrate();
395 offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
396 eDebug("fallback, bitrate=%d, results in %016llx", bitrate, offset);
397 offset -= offset % 188;
403 int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
405 if (m_use_streaminfo)
406 return m_streaminfo.getNextAccessPoint(ts, start, direction);
409 eDebug("can't get next access point without streaminfo");
414 void eDVBTSTools::calcBegin()
416 if (!m_source || !m_source->valid())
419 if (!(m_begin_valid || m_futile))
422 if (!getPTS(m_offset_begin, m_pts_begin))
429 void eDVBTSTools::calcEnd()
431 if (!m_source || !m_source->valid())
434 off_t end = m_source->lseek(0, SEEK_END);
436 if (llabs(end - m_last_filelength) > 1*1024*1024)
438 m_last_filelength = end;
442 // eDebug("file size changed, recalc length");
447 m_offset_end = m_last_filelength;
449 while (!(m_end_valid || m_futile))
457 m_offset_end -= m_maxrange;
458 if (m_offset_end < 0)
461 /* restore offset if getpts fails */
462 off_t off = m_offset_end;
464 if (!getPTS(m_offset_end, m_pts_end))
477 int eDVBTSTools::calcLen(pts_t &len)
479 calcBegin(); calcEnd();
480 if (!(m_begin_valid && m_end_valid))
482 len = m_pts_end - m_pts_begin;
485 len += 0x200000000LL;
489 int eDVBTSTools::calcBitrate()
491 calcBegin(); calcEnd();
492 if (!(m_begin_valid && m_end_valid))
495 pts_t len_in_pts = m_pts_end - m_pts_begin;
499 len_in_pts += 0x200000000LL;
500 off_t len_in_bytes = m_offset_end - m_offset_begin;
505 unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
506 if ((bitrate < 10000) || (bitrate > 100000000))
513 void eDVBTSTools::takeSamples()
520 if (calcLen(dummy) == -1)
524 off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
525 if (bytes_per_sample < 40*1024*1024)
526 bytes_per_sample = 40*1024*1024;
528 bytes_per_sample -= bytes_per_sample % 188;
530 eDebug("samples step %lld, pts begin %llx, pts end %llx, offs begin %lld, offs end %lld:",
531 bytes_per_sample, m_pts_begin, m_pts_end, m_offset_begin, m_offset_end);
533 for (off_t offset = m_offset_begin; offset < m_offset_end;)
536 if (takeSample(offset, p) && retries--)
539 offset += bytes_per_sample;
541 m_samples[0] = m_offset_begin;
542 m_samples[m_pts_end - m_pts_begin] = m_offset_end;
545 /* returns 0 when a sample was taken. */
546 int eDVBTSTools::takeSample(off_t off, pts_t &p)
548 off_t offset_org = off;
550 if (!eDVBTSTools::getPTS(off, p, 1))
552 /* as we are happily mixing PTS and PCR values (no comment, please), we might
553 end up with some "negative" segments.
555 so check if this new sample is between the previous and the next field*/
557 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
558 std::map<pts_t, off_t>::const_iterator u = l;
560 if (l != m_samples.begin())
563 if (u != m_samples.end())
565 if ((l->second > off) || (u->second < off))
567 eDebug("ignoring sample %lld %lld %lld (%llx %llx %llx)",
568 l->second, off, u->second, l->first, p, u->first);
574 eDebug("adding sample %lld: pts 0x%llx -> pos %lld (diff %lld bytes)", offset_org, p, off, off-offset_org);
581 int eDVBTSTools::findPMT(int &pmt_pid, int &service_id)
583 /* FIXME: this will be factored out soon! */
584 if (!m_source || !m_source->valid())
586 eDebug(" file not valid");
592 int left = 5*1024*1024;
596 unsigned char packet[188];
597 int ret = m_source->read(position, packet, 188);
600 eDebug("read error");
606 if (packet[0] != 0x47)
611 if (packet[i] == 0x47)
618 int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF;
620 int pusi = !!(packet[1] & 0x40);
625 /* ok, now we have a PES header or section header*/
628 /* check for adaption field */
629 if (packet[3] & 0x20)
631 if (packet[4] >= 183)
633 sec = packet + packet[4] + 4 + 1;
637 if (sec[0]) /* table pointer, assumed to be 0 */
640 if (sec[1] == 0x02) /* program map section */
643 service_id = (sec[4] << 8) | sec[5];
651 int eDVBTSTools::findFrame(off_t &_iframe_offset, off_t &_new_offset, size_t &len, int &direction, int frame_types)
653 off_t offset = _iframe_offset;
656 // eDebug("trying to find iFrame at %lld", offset);
658 if (!m_use_streaminfo)
660 // eDebug("can't get next iframe without streaminfo");
664 /* let's find the iframe before the given offset */
665 unsigned long long data;
674 res = m_streaminfo.getStructureEntry_prev(offset, data);
676 res = m_streaminfo.getStructureEntry_next(offset, data);
680 eDebug("getting structure info for origin offset failed.");
683 if (offset == 0x7fffffffffffffffLL) /* eof */
685 eDebug("reached eof");
688 /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */
689 /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */
690 /* TODO: check frame_types */
692 if ((data & 0xE0FF) == 0x0009) /* H.264 NAL unit access delimiter with I-frame*/
694 else if((data & 0x3800FF) == 0x080000) /* MPEG2 picture start code with I-frame */
699 int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00); /* H.264 UAD or MPEG2 start code */
708 // eDebug("%08llx@%llu -> %d, %d (I-frame)", data, offset, is_start, nr_frames);
713 --offset; /* move to previous entry */
714 else if (direction == +1)
717 off_t iframe_start = offset;
718 off_t start = offset;
721 /* backtrack to find the previous sequence start, in case of MPEG2 */
722 if ((data & 0xFF) == 0x00) {
725 if (m_streaminfo.getStructureEntry(start, data, 0))
727 eDebug("get previous failed");
730 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00) && ((data & 0xFF) != 0xB3)); /* sequence start or previous frame */
731 if ((data & 0xFF) != 0xB3)
732 start = offset; /* Failed to find corresponding sequence start, so never mind */
737 /* let's find the next frame after the given offset */
739 if (m_streaminfo.getStructureEntry_next(offset, data))
741 eDebug("get next failed");
744 if (offset == 0x7fffffffffffffffLL) /* eof */
746 eDebug("reached eof (while looking for end of iframe)");
749 // eDebug("%08llx@%llu (next frame)", data, offset);
750 } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */
752 if (is_mpeg) //MPEG2 picture start code with I-frame
754 off_t sequence_offset = start;
755 unsigned long long sequence_data;
757 int frame_length = (nr_frames<0) ? -nr_frames: nr_frames;
761 if (m_streaminfo.getStructureEntry_prev(sequence_offset, sequence_data))
763 eDebug("get previous failed");
766 if ((sequence_data & 0xFF) == 0xB3) /* sequence start or previous frame */
768 if(((sequence_data & 0xFF) == 0x0009) || ((sequence_data & 0xFF) == 0x00)) /* H.264 UAD or MPEG2 start code */
770 // eDebug("[findFrame] %08llx@%llu -> %d, %d (sequence)", sequence_data, sequence_offset, is_sequence, frame_length);
771 if(is_sequence) /* get sequence frame */
774 start = sequence_offset;
777 /* align to TS pkt start */
778 start = start - (start % 188);
779 offset = offset - (offset % 188);
782 len = offset - start;
784 _iframe_offset = iframe_start;
785 direction = nr_frames;
786 // eDebug("result: start=%lld, iframe= %lld, next=%lld, len: %ld",start, iframe_start ,offset, (long int)len);
790 int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
792 int nr_frames, direction;
793 // eDebug("trying to move %d frames at %llu", distance, offset);
795 frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
797 off_t new_offset = offset;
798 size_t new_len = len;
799 off_t iframe_offset = new_offset;
808 distance = -distance+1;
813 if (findFrame(iframe_offset, new_offset, new_len, dir, frame_types))
815 // eDebug("findFrame failed!\n");
819 distance -= abs(dir);
821 // eDebug("we moved %d, %d to go frames (now at %llu)", dir, distance, new_offset);
823 if (distance >= 0 || direction == 0)
828 nr_frames += abs(dir);
834 nr_frames += abs(dir) + distance; // never jump forward during rewind
837 distance = (direction < 0) ? -nr_frames : nr_frames;
838 // eDebug("in total, we moved %d frames", nr_frames);