more accurate fast-forward/rewind
authorFelix Domke <tmbinc@elitedvb.net>
Sun, 5 Apr 2009 23:35:07 +0000 (01:35 +0200)
committerFelix Domke <tmbinc@elitedvb.net>
Sun, 5 Apr 2009 23:35:07 +0000 (01:35 +0200)
lib/dvb/dvb.cpp
lib/dvb/dvb.h
lib/dvb/tstools.cpp
lib/dvb/tstools.h

index 1c4eef8aa92bf2b065e8d853c87a7cf5bf5b8e46..f0186de70b24a84c529b8aaa6e86ad63f5f3d99a 100644 (file)
@@ -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 */
        }
 
index fa1801db9a5403e0bb763e5bb057fac5984398bd..5399f45e6e7a21d40279730f4851c2a9304a14e0 100644 (file)
@@ -291,7 +291,7 @@ private:
        
        void cueSheetEvent(int event);
        ePtr<eConnection> 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<std::pair<off_t, off_t> > m_source_span;
        void getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size);
index 7ac86f08acff7fa86694fcf63554440eb29baca5..6fe9113613aa2b01537783f2d6ca1e7a2ee43d2b 100644 (file)
@@ -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;
+}
index a8e0751ec1e7668d0420089c3dcb42a3353f335f..c230a341e580f8e914de48cda41370f7681153a4 100644 (file)
@@ -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;