From: Felix Domke Date: Sun, 5 Apr 2009 23:35:07 +0000 (+0200) Subject: more accurate fast-forward/rewind X-Git-Tag: 2.6.0~344 X-Git-Url: https://git.cweiske.de/enigma2.git/commitdiff_plain/dc46dad972d745f6e06ecb3324c037aeee479360?ds=sidebyside more accurate fast-forward/rewind --- diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 1c4eef8a..f0186de7 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -1069,7 +1069,7 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte m_pvr_thread = 0; - m_skipmode_n = m_skipmode_m = 0; + m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0; if (m_frontend) m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged); @@ -1170,6 +1170,8 @@ void eDVBChannel::cueSheetEvent(int event) /* i agree that this might look a bit like black magic. */ m_skipmode_n = 512*1024; /* must be 1 iframe at least. */ m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8; + m_skipmode_frames = m_cue->m_skipmode_ratio / 90000; + m_skipmode_frames_remainder = 0; if (m_cue->m_skipmode_ratio < 0) m_skipmode_m -= m_skipmode_n; @@ -1179,12 +1181,12 @@ void eDVBChannel::cueSheetEvent(int event) if (abs(m_skipmode_m) < abs(m_skipmode_n)) { eWarning("something is wrong with this calculation"); - m_skipmode_n = m_skipmode_m = 0; + m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0; } } else { eDebug("skipmode ratio is 0, normal play"); - m_skipmode_n = m_skipmode_m = 0; + m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0; } } m_pvr_thread->setIFrameSearch(m_skipmode_n != 0); @@ -1227,6 +1229,23 @@ static inline long long align(long long x, int align) x -= x % align; + if (sign) + x = -x; + + return x; +} + + /* align toward zero */ +static inline long long align_with_len(long long x, int align, size_t &len) +{ + int sign = x < 0; + + if (sign) + x = -x; + + x -= x % align; + len += x % align; + if (sign) x = -x; @@ -1260,26 +1279,53 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off if (m_skipmode_n) { - eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n); + eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames); max = align(m_skipmode_n, blocksize); } eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m); - - current_offset += align(m_skipmode_m, blocksize); + int frame_skip_success = 0; + if (m_skipmode_m) { - eDebug("we are at %llx, and we try to find the iframe here:", current_offset); + int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder; + eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder); size_t iframe_len; off_t iframe_start = current_offset; + int frames_skipped = frames_to_skip; + if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped)) + { + m_skipmode_frames_remainder = frames_to_skip - frames_skipped; + eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder); + current_offset = align_with_len(iframe_start, blocksize, iframe_len); + max = align(iframe_len + 187, blocksize); + frame_skip_success = 1; + } else + { + m_skipmode_frames_remainder = 0; + eDebug("frame skipping failed, reverting to byte-skipping"); + } + } + + if (!frame_skip_success) + { + current_offset += align(m_skipmode_m, blocksize); - if (m_tstools.findIFrame(iframe_start, iframe_len, (m_skipmode_m < 0) ? -1 : +1)) - eDebug("failed"); - else + if (m_skipmode_m) { - current_offset = align(iframe_start, blocksize); - max = align(iframe_len, blocksize); + 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; + + int direction = (m_skipmode_m < 0) ? -1 : +1; + if (m_tstools.findFrame(iframe_start, iframe_len, direction)) + eDebug("failed"); + else + { + current_offset = align_with_len(iframe_start, blocksize, iframe_len); + max = align(iframe_len, blocksize); + } } } @@ -1361,9 +1407,10 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off size_t iframe_len; /* try to align to iframe */ - m_tstools.findIFrame(offset, iframe_len, pts < 0 ? -1 : 1); + int direction = pts < 0 ? -1 : 1; + m_tstools.findFrame(offset, iframe_len, direction); - eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); + eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx (skipped additional %d frames due to iframe re-align)", relative, pts, offset, direction); current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */ } diff --git a/lib/dvb/dvb.h b/lib/dvb/dvb.h index fa1801db..5399f45e 100644 --- a/lib/dvb/dvb.h +++ b/lib/dvb/dvb.h @@ -291,7 +291,7 @@ private: void cueSheetEvent(int event); ePtr m_conn_cueSheetEvent; - int m_skipmode_m, m_skipmode_n; + int m_skipmode_m, m_skipmode_n, m_skipmode_frames, m_skipmode_frames_remainder; std::list > m_source_span; void getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size); diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp index 7ac86f08..6fe91136 100644 --- a/lib/dvb/tstools.cpp +++ b/lib/dvb/tstools.cpp @@ -566,19 +566,24 @@ int eDVBTSTools::findPMT(int &pmt_pid, int &service_id) return -1; } -int eDVBTSTools::findIFrame(off_t &_offset, size_t &len, int direction) +int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int frame_types) { off_t offset = _offset; + int nr_frames = 0; // eDebug("trying to find iFrame at %llx", offset); if (!m_use_streaminfo) { - eDebug("can't get next iframe without streaminfo"); +// eDebug("can't get next iframe without streaminfo"); return -1; } /* let's find the iframe before the given offset */ unsigned long long data; + + if (direction < 0) + offset--; + while (1) { if (m_streaminfo.getStructureEntry(offset, data, (direction == 0) ? 1 : 0)) @@ -593,9 +598,20 @@ int eDVBTSTools::findIFrame(off_t &_offset, size_t &len, int direction) } /* 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 */ + /* TODO: check frame_types */ 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); + + int is_frame = ((data & 0xFF) == 0x0009) || ((data & 0xFF) == 0x00); /* H.264 UAD or MPEG2 start code */ + + if (is_frame) + { + if (direction < 0) + --nr_frames; + else + ++nr_frames; + } +// eDebug("%08llx@%llx -> %d, %d", data, offset, is_start, nr_frames); if (is_start) break; @@ -618,15 +634,56 @@ int eDVBTSTools::findIFrame(off_t &_offset, size_t &len, int direction) eDebug("reached eof (while looking for end of iframe)"); return -1; } -// eDebug("%08llx@%llx", data, offset); +// eDebug("%08llx@%llx (next)", data, offset); } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */ /* align to TS pkt start */ - start = start - (start % 188); - offset = offset - (offset % 188); +// start = start - (start % 188); +// offset = offset - (offset % 188); len = offset - start; _offset = start; + direction = nr_frames; // eDebug("result: offset=%llx, len: %ld", offset, (int)len); return 0; } + +int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types) +{ + int nr_frames = 0; +// eDebug("trying to move %d frames at %llx", distance, offset); + + frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */ + + int direction = distance > 0 ? 0 : -1; + distance = abs(distance); + + off_t new_offset = offset; + size_t new_len = len; + + while (distance > 0) + { + int dir = direction; + if (findFrame(new_offset, new_len, dir, frame_types)) + { +// eDebug("findFrame failed!\n"); + return -1; + } + + distance -= abs(dir); + +// eDebug("we moved %d, %d to go frames (now at %llx)", dir, distance, new_offset); + + if (distance >= 0) + { + offset = new_offset; + len = new_len; + nr_frames += abs(dir); + } + } + + distance = (direction < 0) ? -nr_frames : nr_frames; +// eDebug("in total, we moved %d frames", nr_frames); + + return 0; +} diff --git a/lib/dvb/tstools.h b/lib/dvb/tstools.h index a8e0751e..c230a341 100644 --- a/lib/dvb/tstools.h +++ b/lib/dvb/tstools.h @@ -56,7 +56,22 @@ public: int findPMT(int &pmt_pid, int &service_id); - int findIFrame(off_t &offset, size_t &len, int direction); + enum { + frametypeI = 1, + frametypeP = 2, + frametypeB = 4, + frametypeAll = frametypeI | frametypeP | frametypeB + }; + /** findFrame: finds a specific frame at a given position + + findFrame will look for the specified frame type starting at the given position, moving forward + (when direction is >0) or backward (when direction is <0). (direction=0 is a special case and also moves + forward, but starts with the last frame.) + + return values are the new offset, the length of the found frame (both unaligned), and the (signed) + number of frames skipped. */ + int findFrame(off_t &offset, size_t &len, int &direction, int frame_types = frametypeI); + int findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types = frametypeAll); private: int m_pid; int m_maxrange;