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/dvb.cpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'lib/dvb/dvb.cpp') diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 68d9a0dd..6edf9e87 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -1195,7 +1195,7 @@ void eDVBChannel::cueSheetEvent(int event) { off_t offset_in, offset_out; pts_t pts_in = i->first, pts_out = i->second; - if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out)) + if (m_tstools.getOffset(offset_in, pts_in, -1) || m_tstools.getOffset(offset_out, pts_out, 1)) { eDebug("span translation failed.\n"); continue; @@ -1323,13 +1323,13 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("AP relative seeking failed!"); } else { - eDebug("next ap is %llx\n", pts); pts = nextap; + eDebug("next ap is %llx\n", pts); } } off_t offset = 0; - if (m_tstools.getOffset(offset, pts)) + if (m_tstools.getOffset(offset, pts, -1)) { eDebug("get offset for pts=%lld failed!", pts); continue; @@ -1403,16 +1403,26 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } } - 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()) { + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) + { + eDebug("reached SOF"); + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + start = current_offset; + size = max; + } else { + off_t tmp = align(m_source_span.rbegin()->second, blocksize); + if (current_offset == tmp) { + start = current_offset; + size = 0; + } else { + start = tmp - align(512*1024, blocksize); + size = align(512*1024, blocksize); + } } - start = current_offset; - size = max; - eDebug("END OF CUESHEET. (%08llx, %d)", start, size); return; } -- cgit v1.2.3 From 58ee3c3a218a0aa5d45e9c465eb9759375b4129c Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Mon, 17 Nov 2008 00:08:58 +0100 Subject: By Anders Holst: * I have checked the effect on DM800 of the margin before GOP:s, introduced by the "timing bugs" patch and needed for DM7025. As was previously noted, the margins are not needed on DM800. Fortunately it turns out also not to have any significant adverse effects: When jumping back or forward I expected some flickering, but there are none at all! There is only one small effect as far as I have found: When a cut list is used, there are somewhat more flickering at the cut points than without the margins. Since there are flickering also without the margins, this may be considered a less serious effect. If you consider this effect serious enough though, or think that it is cleaner to separate the code for DM7025 and DM800 since the margin is needed for one but not the other, then I can try to produce such a patch. Otherwise I suggest to wait with this. * In the original timing bugs patch there were a fix to stop playback after the last OUT cut. It did this by setting the size of the next source span to 0. But this turned out not to be a good idea: It seems that playback stops immediately when the next size is set to zero, and not when the buffer is used up. Therefore playback may stop some seconds before the actual end. If instead a jump is made to the last position in the file and a non-zero size is used there, then for some reason it plays up the whole buffer. Don't ask me why it is like this. A modification to this effect is anyway included below. * Rewind did not work at all for HD movies on DM800. The picture just freezes. It is because HD movies have another sequence at beginning of frames than normal movies. There is a rather simple fix, looking for both HD and normal sequences, in the trickmode playback code. (If the HD movie sequence condition seems complicated, it is because it has to make sure not to be accidentally triggered by normal movies.) --- lib/dvb/dvb.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'lib/dvb/dvb.cpp') diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 6edf9e87..7b05feb4 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -985,9 +985,9 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s unsigned char *ts = data + ts_offset; int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF; - if ((d[3] == 0) && (m_pid == pid)) /* picture start */ + if ((d[3] == 0 || d[3] == 0x09 && d[-1] == 0 && (ts[1] & 0x40)) && (m_pid == pid)) /* picture start */ { - int picture_type = (d[5] >> 3) & 7; + int picture_type = (d[3]==0 ? (d[5] >> 3) & 7 : (d[4] >> 5) + 1); d += 4; // eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid); @@ -1413,10 +1413,13 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off start = current_offset; size = max; } else { - off_t tmp = align(m_source_span.rbegin()->second, blocksize); - if (current_offset == tmp) { - start = current_offset; - size = 0; + 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); -- cgit v1.2.3 From 401863ab3c54d849a22cd0fb677dec487621ab97 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Mon, 17 Nov 2008 15:35:42 +0100 Subject: lock on pids only if video startcode is in a pes header --- lib/dvb/dvb.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'lib/dvb/dvb.cpp') diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 7b05feb4..8afa70db 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -1031,10 +1031,20 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s } } else if ((d[3] & 0xF0) == 0xE0) /* video stream */ { - if (m_pid != pid) + /* verify that this is actually a PES header, not just some ES data */ + if (ts[1] & 0x40) /* PUSI set */ { - eDebug("now locked to pid %04x", pid); - m_pid = pid; + int payload_start = 4; + if (ts[3] & 0x20) /* adaptation field present */ + payload_start += ts[4] + 1; /* skip AF */ + if (payload_start == (offset%184)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */ + { + if (m_pid != pid) + { + eDebug("now locked to pid %04x (%02x %02x %02x %02x)", pid, ts[0], ts[1], ts[2], ts[3]); + m_pid = pid; + } + } } // m_pid = 0x6e; d += 4; -- cgit v1.2.3 From a9efd192b545113282c7c7891a231570f49f27e6 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Mon, 17 Nov 2008 17:19:06 +0100 Subject: fix comment, don't memmem over end of packet --- lib/dvb/dvb.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/dvb/dvb.cpp') diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 3ad086de..07cc611b 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -973,7 +973,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s } #endif -#if 1 /* not yet */ +#if 1 /* This codepath is required on Broadcom-based Dreamboxes (DM800, DM8000) and strips away non-I-frames. */ if (!m_iframe_search) return len; @@ -982,7 +982,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s // eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]); unsigned char *d = data; - while ((d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3))) + while ((d + 3 < data + len) && (d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3))) { int offset = d - data; int ts_offset = offset - offset % 188; /* offset to the start of TS packet */ @@ -1054,7 +1054,6 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s d += 4; } else d += 4; /* ignore */ - } if (m_iframe_state == 1) -- cgit v1.2.3 From 39bd88513deee4a1a00993212c76e503fdd47fe7 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Mon, 17 Nov 2008 22:54:04 +0100 Subject: fix typo --- lib/dvb/dvb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/dvb/dvb.cpp') diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 07cc611b..28012e7a 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -1041,7 +1041,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s int payload_start = 4; if (ts[3] & 0x20) /* adaptation field present */ payload_start += ts[4] + 1; /* skip AF */ - if (payload_start == (offset%184)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */ + if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */ { if (m_pid != pid) { -- 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/dvb.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