From 6c6704a6c897cef2aca87bd8d5a732ae1a2bac4a Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Fri, 31 Oct 2008 02:03:15 +0100 Subject: Patch by Anders Holst: * Undo "sparse-AP-fix" At March 25 a patch was checked in that makes sure that AP:s closer than half a second from each other are filtered away. I don't know the exact purpose of this fix, but I don't think it is a good idea: Besides being the cause of bugs 4 and 5 above, all seek operations are based on the AP:s, and it is a pity to cripple the precision here. And for example, when cutting movies it is important to be able to reach the right GOP boundary. (And the next fix relies on all boundaries being available.) (If you wonder, bug 5 was caused by a destructive interaction of this with the discontinuity handling.) * Hit GOP:s somewhat before GOP start It turns out that if you jump exactly to the GOP start, then that GOP is nevertheless skipped and playback starts from the GOP thereafter. However, if you jump to (at least) one frame before the GOP start, playback starts from that GOP. I don't know if this is a bug in the driver or elsewhere, but the best I can do is this workaround: Hit the GOP by jumping to half a GOP length before the GOP start. (By scanning the ts file it is of course possible to find the exact frame boundaries, but why bother since anywhere between the previous GOP start and the previous frame start will do.) Similarly, to show the first frame of a GOP, a few more frames must be included. Therefore, add half a GOP at the end of each source span. * Jump over discontinuities during AP relative seek The above two fixes together *almost* take care of bugs 1 and 2 above. Now seekRelative(1) moves one forward and seekRelative(-1) one backwards. However, at discontinuities they may get stuck. This is remedied by an extra if statement to check for discontinuities when stepping throught the AP:s in AP relative seek. * Stop after last source span In the function eDVBChannel::getNextSourceSpan there was no code to take care of the case when the seeked-to point is after the last source span. Currently it just goes on until the movie ends. I have added code for this, which takes care of bug 3, and as a fortunate bonus effect bug 6 too. (But please check my code here, I hope I can use current_offset the way I do, and return 0 size when it should stop.) --- lib/dvb/tstools.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/dvb/tstools.cpp') diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp index 5157ef22..bd7ebce2 100644 --- a/lib/dvb/tstools.cpp +++ b/lib/dvb/tstools.cpp @@ -209,11 +209,14 @@ int eDVBTSTools::fixupPTS(const off_t &offset, pts_t &now) } } -int eDVBTSTools::getOffset(off_t &offset, pts_t &pts) +int eDVBTSTools::getOffset(off_t &offset, pts_t &pts, int marg) { if (m_use_streaminfo) { - offset = m_streaminfo.getAccessPoint(pts); + if (pts >= m_pts_end && marg > 0 && m_end_valid) + offset = m_offset_end; + else + offset = m_streaminfo.getAccessPoint(pts, marg); return 0; } else { -- cgit v1.2.3 From dce87891f204f8e1f7151c4a3ba00b9dd048e795 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Fri, 13 Feb 2009 03:41:57 +0100 Subject: - While recording, collect startcodes and save them into ".sc"-files - this allows finding iframes for fast forward/reverse more easily - when in fast forward, strictly just output good (=complete iframes) data (this might break dm7025, we will fix this later) - draw smaller, fixed-size bar in position gauge --- lib/dvb/decoder.cpp | 4 +- lib/dvb/demux.cpp | 28 ++- lib/dvb/demux.h | 2 +- lib/dvb/dvb.cpp | 56 +++-- lib/dvb/idemux.h | 2 +- lib/dvb/pvrparse.cpp | 302 ++++++++++++++++++++------ lib/dvb/pvrparse.h | 26 ++- lib/dvb/tstools.cpp | 70 +++++- lib/dvb/tstools.h | 2 + lib/gui/epositiongauge.cpp | 2 +- lib/python/Components/Converter/StringList.py | 6 + lib/python/Components/Renderer/Listbox.py | 4 + lib/service/servicedvb.cpp | 18 +- lib/service/servicedvbrecord.cpp | 10 +- 14 files changed, 416 insertions(+), 116 deletions(-) (limited to 'lib/dvb/tstools.cpp') diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp index 6f0ead65..a4cffb77 100644 --- a/lib/dvb/decoder.cpp +++ b/lib/dvb/decoder.cpp @@ -1071,7 +1071,9 @@ RESULT eTSMPEGDecoder::setAC3Delay(int delay) } eTSMPEGDecoder::eTSMPEGDecoder(eDVBDemux *demux, int decoder) - :m_demux(demux), m_changed(0), m_decoder(decoder), m_video_clip_fd(-1), m_showSinglePicTimer(eTimer::create(eApp)) + : m_demux(demux), + m_vpid(-1), m_vtype(-1), m_apid(-1), m_atype(-1), m_pcrpid(-1), m_textpid(-1), + m_changed(0), m_decoder(decoder), m_video_clip_fd(-1), m_showSinglePicTimer(eTimer::create(eApp)) { demux->connectEvent(slot(*this, &eTSMPEGDecoder::demux_event), m_demux_event_conn); CONNECT(m_showSinglePicTimer->timeout, eTSMPEGDecoder::finishShowSinglePic); diff --git a/lib/dvb/demux.cpp b/lib/dvb/demux.cpp index 810b10a5..918ecec6 100644 --- a/lib/dvb/demux.cpp +++ b/lib/dvb/demux.cpp @@ -402,9 +402,10 @@ class eDVBRecordFileThread: public eFilePushThread { public: eDVBRecordFileThread(); - void setTimingPID(int pid); + void setTimingPID(int pid, int type); - void saveTimingInformation(const std::string &filename); + void startSaveMetaInformation(const std::string &filename); + void stopSaveMetaInformation(); int getLastPTS(pts_t &pts); protected: int filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining); @@ -422,14 +423,19 @@ eDVBRecordFileThread::eDVBRecordFileThread() m_current_offset = 0; } -void eDVBRecordFileThread::setTimingPID(int pid) +void eDVBRecordFileThread::setTimingPID(int pid, int type) { - m_ts_parser.setPid(pid); + m_ts_parser.setPid(pid, type); } -void eDVBRecordFileThread::saveTimingInformation(const std::string &filename) +void eDVBRecordFileThread::startSaveMetaInformation(const std::string &filename) { - m_stream_info.save(filename.c_str()); + m_stream_info.startSave(filename.c_str()); +} + +void eDVBRecordFileThread::stopSaveMetaInformation() +{ + m_stream_info.stopSave(); } int eDVBRecordFileThread::getLastPTS(pts_t &pts) @@ -520,6 +526,9 @@ RESULT eDVBTSRecorder::start() ::ioctl(m_source_fd, DMX_START); #endif + + if (m_target_filename != "") + m_thread->startSaveMetaInformation(m_target_filename); m_thread->start(m_source_fd, m_target_fd); m_running = 1; @@ -553,11 +562,11 @@ RESULT eDVBTSRecorder::removePID(int pid) return 0; } -RESULT eDVBTSRecorder::setTimingPID(int pid) +RESULT eDVBTSRecorder::setTimingPID(int pid, int type) { if (m_running) return -1; - m_thread->setTimingPID(pid); + m_thread->setTimingPID(pid, type); return 0; } @@ -590,8 +599,7 @@ RESULT eDVBTSRecorder::stop() close(m_source_fd); m_source_fd = -1; - if (m_target_filename != "") - m_thread->saveTimingInformation(m_target_filename + ".ap"); + m_thread->stopSaveMetaInformation(); return 0; } diff --git a/lib/dvb/demux.h b/lib/dvb/demux.h index 14501b98..7a697d49 100644 --- a/lib/dvb/demux.h +++ b/lib/dvb/demux.h @@ -94,7 +94,7 @@ public: RESULT addPID(int pid); RESULT removePID(int pid); - RESULT setTimingPID(int pid); + RESULT setTimingPID(int pid, int type); RESULT setTargetFD(int fd); RESULT setTargetFilename(const char *filename); diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index c320fc78..4bbed519 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -974,7 +974,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s } #endif -#if 1 /* This codepath is required on Broadcom-based Dreamboxes (DM800, DM8000) and strips away non-I-frames. */ +#if 0 if (!m_iframe_search) return len; @@ -1272,6 +1272,21 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m); current_offset += align(m_skipmode_m, blocksize); + + if (m_skipmode_m) + { + eDebug("we are at %llx, and we try to find the iframe here:", current_offset); + size_t iframe_len; + off_t iframe_start = current_offset; + + if (m_tstools.findIFrame(iframe_start, iframe_len, (m_skipmode_m < 0) ? -1 : +1)) + eDebug("failed"); + else + { + current_offset = align(iframe_start, blocksize); + max = align(iframe_len, blocksize); + } + } while (!m_cue->m_seek_requests.empty()) { @@ -1348,6 +1363,10 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("get offset for pts=%lld failed!", pts); continue; } + + size_t iframe_len; + /* try to align to iframe */ + m_tstools.findIFrame(offset, iframe_len, pts < 0 ? -1 : 1); eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */ @@ -1417,30 +1436,23 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } } - if (m_source_span.empty()) { - if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) - { - eDebug("reached SOF"); - m_skipmode_m = 0; - m_pvr_thread->sendEvent(eFilePushThread::evtUser); - } + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) + { + eDebug("reached SOF"); + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + + if (m_source_span.empty()) + { start = current_offset; size = max; - } else { - off_t tmp2, tmp = align(m_source_span.rbegin()->second, blocksize); - pts_t len; - getLength(len); - m_tstools.getOffset(tmp2, len, 1); - if (current_offset == tmp || current_offset == tmp2) { - start = tmp2; - size = max; - } else { - start = tmp - align(512*1024, blocksize); - size = align(512*1024, blocksize); - } + eDebug("NO CUESHEET. (%08llx, %d)", start, size); + } else + { + start = current_offset; + size = 0; } - - eDebug("END OF CUESHEET. (%08llx, %d)", start, size); return; } diff --git a/lib/dvb/idemux.h b/lib/dvb/idemux.h index 9432afb6..e92b1e75 100644 --- a/lib/dvb/idemux.h +++ b/lib/dvb/idemux.h @@ -30,7 +30,7 @@ public: virtual RESULT addPID(int pid) = 0; virtual RESULT removePID(int pid) = 0; - virtual RESULT setTimingPID(int pid) = 0; + virtual RESULT setTimingPID(int pid, int type) = 0; virtual RESULT setTargetFD(int fd) = 0; /* for saving additional meta data. */ diff --git a/lib/dvb/pvrparse.cpp b/lib/dvb/pvrparse.cpp index 71cbd602..1393bf77 100644 --- a/lib/dvb/pvrparse.cpp +++ b/lib/dvb/pvrparse.cpp @@ -6,9 +6,36 @@ #error no byte order defined! #endif -int eMPEGStreamInformation::save(const char *filename) +eMPEGStreamInformation::eMPEGStreamInformation() + : m_structure_cache_valid(0), m_structure_read(0), m_structure_write(0) { - FILE *f = fopen(filename, "wb"); +} + +eMPEGStreamInformation::~eMPEGStreamInformation() +{ + if (m_structure_read) + fclose(m_structure_read); + if (m_structure_write) + fclose(m_structure_write); +} + +int eMPEGStreamInformation::startSave(const char *filename) +{ + m_filename = filename; + m_structure_write = fopen((m_filename + ".sc").c_str(), "wb"); + return 0; +} + +int eMPEGStreamInformation::stopSave(void) +{ + if (m_structure_write) + { + fclose(m_structure_write); + m_structure_write = 0; + } + if (m_filename == "") + return -1; + FILE *f = fopen((m_filename + ".ap").c_str(), "wb"); if (!f) return -1; @@ -31,7 +58,11 @@ int eMPEGStreamInformation::save(const char *filename) int eMPEGStreamInformation::load(const char *filename) { - FILE *f = fopen(filename, "rb"); + m_filename = filename; + if (m_structure_read) + fclose(m_structure_read); + m_structure_read = fopen((std::string(m_filename) + ".sc").c_str(), "rb"); + FILE *f = fopen((std::string(m_filename) + ".ap").c_str(), "rb"); if (!f) return -1; m_access_points.clear(); @@ -284,6 +315,117 @@ int eMPEGStreamInformation::getNextAccessPoint(pts_t &ts, const pts_t &start, in return 0; } +void eMPEGStreamInformation::writeStructureEntry(off_t offset, structure_data data) +{ + unsigned long long d[2]; +#if BYTE_ORDER == BIG_ENDIAN + d[0] = offset; + d[1] = data; +#else + d[0] = bswap_64(offset); + d[1] = bswap_64(data); +#endif + if (m_structure_write) + fwrite(d, sizeof(d), 1, m_structure_write); +} + +int eMPEGStreamInformation::getStructureEntry(off_t &offset, unsigned long long &data, int get_next) +{ + if (!m_structure_read) + { + eDebug("getStructureEntry failed because of no m_structure_read"); + return -1; + } + + const int struture_cache_entries = sizeof(m_structure_cache) / 16; + if ((!m_structure_cache_valid) || ((off_t)m_structure_cache[0] > offset) || ((off_t)m_structure_cache[(struture_cache_entries - (get_next ? 2 : 1)) * 2] <= offset)) + { + fseek(m_structure_read, 0, SEEK_END); + int l = ftell(m_structure_read); + unsigned long long d[2]; + const int entry_size = sizeof d; + + /* do a binary search */ + int count = l / entry_size; + int i = 0; + + while (count) + { + int step = count >> 1; + + fseek(m_structure_read, (i + step) * entry_size, SEEK_SET); + if (!fread(d, 1, entry_size, m_structure_read)) + { + eDebug("read error at entry %d", i); + return -1; + } + +#if BYTE_ORDER != BIG_ENDIAN + d[0] = bswap_64(d[0]); + d[1] = bswap_64(d[1]); +#endif +// eDebug("%d: %08llx > %llx", i, d[0], d[1]); + + if (d[0] < (unsigned long long)offset) + { + i += step + 1; + count -= step + 1; + } else + count = step; + } + + eDebug("found %d", i); + + /* put that in the middle */ + i -= struture_cache_entries / 2; + if (i < 0) + i = 0; + eDebug("cache starts at %d", i); + fseek(m_structure_read, i * entry_size, SEEK_SET); + int num = fread(m_structure_cache, entry_size, struture_cache_entries, m_structure_read); + eDebug("%d entries", num); + for (i = 0; i < struture_cache_entries; ++i) + { + if (i < num) + { +#if BYTE_ORDER != BIG_ENDIAN + m_structure_cache[i * 2] = bswap_64(m_structure_cache[i * 2]); + m_structure_cache[i * 2 + 1] = bswap_64(m_structure_cache[i * 2 + 1]); +#endif + } else + { + m_structure_cache[i * 2] = 0x7fffffffffffffffULL; /* fill with harmless content */ + m_structure_cache[i * 2 + 1] = 0; + } + } + m_structure_cache_valid = 1; + } + + int i = 0; + while ((off_t)m_structure_cache[i * 2] <= offset) + { + ++i; + if (i == struture_cache_entries) + { + eDebug("structure data consistency fail!, we are looking for %llx, but last entry is %llx", offset, m_structure_cache[i*2-2]); + return -1; + } + } + if (!i) + { + eDebug("structure data (first entry) consistency fail!"); + return -1; + } + + if (!get_next) + --i; + +// eDebug("[%d] looked for %llx, found %llx=%llx", sizeof offset, offset, m_structure_cache[i * 2], m_structure_cache[i * 2 + 1]); + offset = m_structure_cache[i * 2]; + data = m_structure_cache[i * 2 + 1]; + return 0; +} + eMPEGStreamParserTS::eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo): m_streaminfo(streaminfo), m_pktptr(0), m_pid(-1), m_need_next_packet(0), m_skip(0), m_last_pts_valid(0) { } @@ -293,16 +435,15 @@ int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset) if (!wantPacket(pkt)) eWarning("something's wrong."); - const unsigned char *end = pkt + 188; + const unsigned char *end = pkt + 188, *begin = pkt; - if (!(pkt[3] & 0x10)) - { - eWarning("[TSPARSE] PUSI set but no payload."); - return 0; - } + int pusi = !!(pkt[1] & 0x40); - if (pkt[3] & 0x20) // adaption field present? - pkt += pkt[4] + 4 + 1; /* skip adaption field and header */ + if (!(pkt[3] & 0x10)) /* no payload? */ + return 0; + + if (pkt[3] & 0x20) // adaptation field present? + pkt += pkt[4] + 4 + 1; /* skip adaptation field and header */ else pkt += 4; /* skip header */ @@ -311,78 +452,96 @@ int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset) eWarning("[TSPARSE] dropping huge adaption field"); return 0; } - - // ok, we now have the start of the payload, aligned with the PES packet start. - if (pkt[0] || pkt[1] || (pkt[2] != 1)) - { - eWarning("broken startcode"); - return 0; - } - - + pts_t pts = 0; int ptsvalid = 0; - if (pkt[7] & 0x80) // PTS present? + if (pusi) { - pts = ((unsigned long long)(pkt[ 9]&0xE)) << 29; - pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22; - pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14; - pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7; - pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1; - ptsvalid = 1; - - m_last_pts = pts; - m_last_pts_valid = 1; - -#if 0 - int sec = pts / 90000; - int frm = pts % 90000; - int min = sec / 60; - sec %= 60; - int hr = min / 60; - min %= 60; - int d = hr / 24; - hr %= 24; + // ok, we now have the start of the payload, aligned with the PES packet start. + if (pkt[0] || pkt[1] || (pkt[2] != 1)) + { + eWarning("broken startcode"); + return 0; + } + + if (pkt[7] & 0x80) // PTS present? + { + pts = ((unsigned long long)(pkt[ 9]&0xE)) << 29; + pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22; + pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14; + pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7; + pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1; + ptsvalid = 1; + + m_last_pts = pts; + m_last_pts_valid = 1; + + #if 0 + int sec = pts / 90000; + int frm = pts % 90000; + int min = sec / 60; + sec %= 60; + int hr = min / 60; + min %= 60; + int d = hr / 24; + hr %= 24; + + eDebug("pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm); + #endif + } - eDebug("pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm); -#endif + /* advance to payload */ + pkt += pkt[8] + 9; } - - /* advance to payload */ - pkt += pkt[8] + 9; - /* sometimes, there are zeros before the startcode. */ while (pkt < (end-4)) - if (pkt[0] || pkt[1] || pkt[2]) - break; - else - pkt++; - - /* if startcode found */ -// eDebug("%02x %02x %02x %02x", pkt[0], pkt[1], pkt[2], pkt[3]); - if (!(pkt[0] || pkt[1] || (pkt[2] != 1))) { - if (pkt[3] == 0xb3) /* sequence header */ + int pkt_offset = pkt - begin; + if (!(pkt[0] || pkt[1] || (pkt[2] != 1))) { - if (ptsvalid) +// eDebug("SC %02x %02x %02x %02x, %02x", pkt[0], pkt[1], pkt[2], pkt[3], pkt[4]); + int sc = pkt[3]; + + if (m_streamtype == 0) /* mpeg2 */ { - m_streaminfo.m_access_points[offset] = pts; -// eDebug("Sequence header at %llx, pts %llx", offset, pts); - } else - /*eDebug("Sequence header but no valid PTS value.")*/; - } + if ((sc == 0x00) || (sc == 0xb3) || (sc == 0xb8)) /* picture, sequence, group start code */ + { + unsigned long long data = sc | (pkt[4] << 8) | (pkt[5] << 16) | (pkt[6] << 24); + m_streaminfo.writeStructureEntry(offset + pkt_offset, data & 0xFFFFFFFFULL); + } + if (pkt[3] == 0xb3) /* sequence header */ + { + if (ptsvalid) + { + m_streaminfo.m_access_points[offset] = pts; + // eDebug("Sequence header at %llx, pts %llx", offset, pts); + } else + /*eDebug("Sequence header but no valid PTS value.")*/; + } + } - if (pkt[3] == 0x09 && /* MPEG4 AVC unit access delimiter */ - (pkt[4] >> 5) == 0) /* and I-frame */ - { - if (ptsvalid) + if (m_streamtype == 1) /* H.264 */ { - m_streaminfo.m_access_points[offset] = pts; -// eDebug("MPEG4 AVC UAD at %llx, pts %llx", offset, pts); - } else - /*eDebug("MPEG4 AVC UAD but no valid PTS value.")*/; + if (sc == 0x09) + { + /* store image type */ + unsigned long long data = sc | (pkt[4] << 8); + m_streaminfo.writeStructureEntry(offset + pkt_offset, data); + } + if (pkt[3] == 0x09 && /* MPEG4 AVC NAL unit access delimiter */ + (pkt[4] >> 5) == 0) /* and I-frame */ + { + if (ptsvalid) + { + m_streaminfo.m_access_points[offset] = pts; + // eDebug("MPEG4 AVC UAD at %llx, pts %llx", offset, pts); + } else + /*eDebug("MPEG4 AVC UAD but no valid PTS value.")*/; + } + } } + ++pkt; } return 0; } @@ -405,7 +564,7 @@ inline int eMPEGStreamParserTS::wantPacket(const unsigned char *hdr) const if (hdr[1] & 0x40) /* pusi set: yes. */ return 1; - return 0; + return m_streamtype == 0; /* we need all packets for MPEG2, but only PUSI packets for H.264 */ } void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int len) @@ -520,10 +679,11 @@ void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int } } -void eMPEGStreamParserTS::setPid(int _pid) +void eMPEGStreamParserTS::setPid(int _pid, int type) { m_pktptr = 0; m_pid = _pid; + m_streamtype = type; } int eMPEGStreamParserTS::getLastPTS(pts_t &last_pts) diff --git a/lib/dvb/pvrparse.h b/lib/dvb/pvrparse.h index 20d33470..28c0314a 100644 --- a/lib/dvb/pvrparse.h +++ b/lib/dvb/pvrparse.h @@ -12,6 +12,8 @@ class eMPEGStreamInformation { public: + eMPEGStreamInformation(); + ~eMPEGStreamInformation(); /* we order by off_t here, since the timestamp may */ /* wrap around. */ /* we only record sequence start's pts values here. */ @@ -23,7 +25,8 @@ public: /* these are non-fixed up pts value (like m_access_points), just used to accelerate stuff. */ std::multimap m_pts_to_offset; - int save(const char *filename); + int startSave(const char *filename); + int stopSave(void); int load(const char *filename); /* recalculates timestampDeltas */ @@ -46,6 +49,23 @@ public: int getNextAccessPoint(pts_t &ts, const pts_t &start, int direction); bool empty(); + + typedef unsigned long long structure_data; + /* this is usually: + sc | (other_information << 8) + but is really specific to the used video encoder. + */ + void writeStructureEntry(off_t offset, structure_data data); + + /* get a structure entry at given offset (or previous one, if no exact match was found). + optionall, return next element. Offset will be returned. this allows you to easily + get previous and next structure elements. */ + int getStructureEntry(off_t &offset, unsigned long long &data, int get_next); + + std::string m_filename; + int m_structure_cache_valid; + unsigned long long m_structure_cache[1024]; + FILE *m_structure_read, *m_structure_write; }; /* Now we define the parser's state: */ @@ -54,7 +74,7 @@ class eMPEGStreamParserTS public: eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo); void parseData(off_t offset, const void *data, unsigned int len); - void setPid(int pid); + void setPid(int pid, int streamtype); int getLastPTS(pts_t &last_pts); private: eMPEGStreamInformation &m_streaminfo; @@ -62,7 +82,7 @@ private: int m_pktptr; int processPacket(const unsigned char *pkt, off_t offset); inline int wantPacket(const unsigned char *hdr) const; - int m_pid; + int m_pid, m_streamtype; int m_need_next_packet; int m_skip; int m_last_pts_valid; diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp index bd7ebce2..7ac86f08 100644 --- a/lib/dvb/tstools.cpp +++ b/lib/dvb/tstools.cpp @@ -32,7 +32,10 @@ int eDVBTSTools::openFile(const char *filename, int nostreaminfo) closeFile(); if (!nostreaminfo) - m_streaminfo.load((std::string(filename) + ".ap").c_str()); + { + eDebug("loading streaminfo for %s", filename); + m_streaminfo.load(filename); + } if (!m_streaminfo.empty()) m_use_streaminfo = 1; @@ -562,3 +565,68 @@ int eDVBTSTools::findPMT(int &pmt_pid, int &service_id) return -1; } + +int eDVBTSTools::findIFrame(off_t &_offset, size_t &len, int direction) +{ + off_t offset = _offset; +// eDebug("trying to find iFrame at %llx", offset); + + if (!m_use_streaminfo) + { + eDebug("can't get next iframe without streaminfo"); + return -1; + } + + /* let's find the iframe before the given offset */ + unsigned long long data; + while (1) + { + if (m_streaminfo.getStructureEntry(offset, data, (direction == 0) ? 1 : 0)) + { + eDebug("getting structure info for origin offset failed."); + return -1; + } + if (offset == 0x7fffffffffffffffLL) /* eof */ + { + eDebug("reached eof"); + return -1; + } + /* data is usually the start code in the lower 8 bit, and the next byte <<8. we extract the picture type from there */ + /* we know that we aren't recording startcode 0x09 for mpeg2, so this is safe */ + int is_start = (data & 0xE0FF) == 0x0009; /* H.264 NAL unit access delimiter with I-frame*/ + is_start |= (data & 0x3800FF) == 0x080000; /* MPEG2 picture start code with I-frame */ +// eDebug("%08llx@%llx -> %d", data, offset, is_start); + if (is_start) + break; + + if (direction == -1) + --offset; /* move to previous entry */ + else if (direction == +1) + direction = 0; + } + /* let's find the next frame after the given offset */ + off_t start = offset; + + do { + if (m_streaminfo.getStructureEntry(offset, data, 1)) + { + eDebug("get next failed"); + return -1; + } + if (offset == 0x7fffffffffffffffLL) /* eof */ + { + eDebug("reached eof (while looking for end of iframe)"); + return -1; + } +// eDebug("%08llx@%llx", data, offset); + } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */ + + /* align to TS pkt start */ + start = start - (start % 188); + offset = offset - (offset % 188); + + len = offset - start; + _offset = start; +// eDebug("result: offset=%llx, len: %ld", offset, (int)len); + return 0; +} diff --git a/lib/dvb/tstools.h b/lib/dvb/tstools.h index 4bc04729..a8e0751e 100644 --- a/lib/dvb/tstools.h +++ b/lib/dvb/tstools.h @@ -55,6 +55,8 @@ public: int takeSample(off_t off, pts_t &p); int findPMT(int &pmt_pid, int &service_id); + + int findIFrame(off_t &offset, size_t &len, int direction); private: int m_pid; int m_maxrange; diff --git a/lib/gui/epositiongauge.cpp b/lib/gui/epositiongauge.cpp index b3ee5111..ff98c080 100644 --- a/lib/gui/epositiongauge.cpp +++ b/lib/gui/epositiongauge.cpp @@ -144,7 +144,7 @@ int ePositionGauge::event(int event, void *data, void *data2) { painter.setForegroundColor(gRGB(m_foreground_color)); int xi = scale(in), xo = scale(out); - painter.fill(eRect(xi, 10, xo-xi, s.height()-14)); + painter.fill(eRect(xi, (s.height()-4) / 2, xo-xi, 4)); } in = m_length; diff --git a/lib/python/Components/Converter/StringList.py b/lib/python/Components/Converter/StringList.py index c9488db0..08794b34 100644 --- a/lib/python/Components/Converter/StringList.py +++ b/lib/python/Components/Converter/StringList.py @@ -19,8 +19,11 @@ class StringList(Converter): def selectionChanged(self, index): self.source.selectionChanged(index) # update all non-master targets + print "changed selection in listbox!" for x in self.downstream_elements: + print "downstream element", x if x is not self.master: + print "is not master, so update to index", index x.index = index @cached @@ -43,3 +46,6 @@ class StringList(Converter): self.master.index = index index = property(getIndex, setIndex) + + def entry_changed(self, index): + self.downstream_elements.entry_changed(index) \ No newline at end of file diff --git a/lib/python/Components/Renderer/Listbox.py b/lib/python/Components/Renderer/Listbox.py index 8e510b4c..7a895330 100644 --- a/lib/python/Components/Renderer/Listbox.py +++ b/lib/python/Components/Renderer/Listbox.py @@ -78,3 +78,7 @@ class Listbox(Renderer, object): def changed(self, what): self.content = self.source.content + + def entry_changed(self, index): + if self.instance is not None: + self.instance.entryChanged(index) diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 33cd865e..e3d960d4 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -370,13 +370,17 @@ int eStaticServiceDVBPVRInformation::getLength(const eServiceReference &ref) struct stat s; stat(ref.path.c_str(), &s); - if (tstools.openFile(ref.path.c_str())) + if (tstools.openFile(ref.path.c_str(), 1)) return 0; /* check if cached data is still valid */ if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length)) return m_parser.m_length / 90000; + /* open again, this time with stream info */ + if (tstools.openFile(ref.path.c_str())) + return 0; + /* otherwise, re-calc length and update meta file */ pts_t len; if (tstools.calcLen(len)) @@ -502,6 +506,7 @@ RESULT eDVBPVRServiceOfflineOperations::getListOfFilenames(std::list= 0)) + { + eDebug("timeshift EOF, so let's go live"); switchToLive(); + } break; } } @@ -1173,9 +1181,9 @@ RESULT eDVBServicePlay::setFastForward_internal(int ratio) if (!m_decoder) return -1; - + if (ffratio == 0) - return 0; + return m_decoder->play(); else if (ffratio != 1) return m_decoder->setFastForward(ffratio); else @@ -1211,6 +1219,7 @@ RESULT eDVBServicePlay::pause() setFastForward_internal(0); if (m_decoder) { + m_is_paused = 1; return m_decoder->pause(); } else return -1; @@ -1222,6 +1231,7 @@ RESULT eDVBServicePlay::unpause() setFastForward_internal(0); if (m_decoder) { + m_is_paused = 0; return m_decoder->play(); } else return -1; @@ -2136,6 +2146,8 @@ void eDVBServicePlay::switchToLive() if (!m_timeshift_active) return; + eDebug("SwitchToLive"); + m_cue = 0; m_decoder = 0; m_decode_demux = 0; diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index c2767e8d..5b7b5d8c 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -270,7 +270,7 @@ int eDVBServiceRecord::doRecord() if (program.pmtPid != -1) pids_to_record.insert(program.pmtPid); // PMT - int timing_pid = -1; + int timing_pid = -1, timing_pid_type = -1; eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size()); if (!program.videoStreams.empty()) @@ -283,7 +283,10 @@ int eDVBServiceRecord::doRecord() pids_to_record.insert(i->pid); if (timing_pid == -1) + { timing_pid = i->pid; + timing_pid_type = i->type; + } if (i != program.videoStreams.begin()) eDebugNoNewLine(", "); @@ -302,7 +305,10 @@ int eDVBServiceRecord::doRecord() pids_to_record.insert(i->pid); if (timing_pid == -1) + { timing_pid = i->pid; + timing_pid_type = -1; + } if (i != program.audioStreams.begin()) eDebugNoNewLine(", "); @@ -358,7 +364,7 @@ int eDVBServiceRecord::doRecord() } if (timing_pid != -1) - m_record->setTimingPID(timing_pid); + m_record->setTimingPID(timing_pid, timing_pid_type); m_pids_active = pids_to_record; -- cgit v1.2.3