[haiku-commits] r38706 - haiku/trunk/src/add-ons/media/plugins/ffmpeg

  • From: superstippi@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 18 Sep 2010 17:11:50 +0200 (CEST)

Author: stippi
Date: 2010-09-18 17:11:50 +0200 (Sat, 18 Sep 2010)
New Revision: 38706
Changeset: http://dev.haiku-os.org/changeset/38706

Modified:
   haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp
Log:
 * Seeking by bytes works mostly correctly now. In some
   mpgs, the video does not recover, though.
 * Remember the last reported keyframe information, so we
   avoid rounding artifacts. Not as effective, since we
   cannot use stream time-base for seeking, but have to use
   it for finding the keyframes.


Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp
===================================================================
--- haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp     
2010-09-18 14:44:22 UTC (rev 38705)
+++ haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp     
2010-09-18 15:11:50 UTC (rev 38706)
@@ -129,10 +129,10 @@
                                                                        const 
void** infoBuffer,
                                                                        size_t* 
infoSize) const;
 
+                       status_t                        FindKeyFrame(uint32 
flags, int64* frame,
+                                                                       
bigtime_t* time) const;
                        status_t                        Seek(uint32 flags, 
int64* frame,
                                                                        
bigtime_t* time);
-                       status_t                        FindKeyFrame(uint32 
flags, int64* frame,
-                                                                       
bigtime_t* time) const;
 
                        status_t                        GetNextChunk(const 
void** chunkBuffer,
                                                                        size_t* 
chunkSize,
@@ -180,6 +180,13 @@
 
        mutable bool                            fStreamBuildsIndexWhileReading;
                        bool                            fSeekByBytes;
+
+                       struct KeyframeInfo {
+                               bigtime_t               time;
+                               int64                   frame;
+                               int64                   streamTimeStamp;
+                       };
+       mutable KeyframeInfo            fLastReportedKeyframe;
 };
 
 
@@ -202,6 +209,10 @@
 {
        memset(&fFormat, 0, sizeof(media_format));
        av_new_packet(&fPacket, 0);
+
+       fLastReportedKeyframe.time = 0;
+       fLastReportedKeyframe.frame = 0;
+       fLastReportedKeyframe.streamTimeStamp = 0;
 }
 
 
@@ -286,9 +297,10 @@
                return B_NOT_SUPPORTED;
        }
 
+       fSeekByBytes = (inputFormat->flags & AVFMT_TS_DISCONT) != 0;
        fStreamBuildsIndexWhileReading
-               = (inputFormat->flags & AVFMT_GENERIC_INDEX) != 0;
-       fSeekByBytes = (inputFormat->flags & AVFMT_TS_DISCONT) != 0;
+               = (inputFormat->flags & AVFMT_GENERIC_INDEX) != 0
+                       || fSeekByBytes;
 
        TRACE("AVFormatReader::StreamCookie::Open() - "
                "av_find_stream_info() success! Seeking by bytes: %d\n",
@@ -881,81 +893,6 @@
 
 
 status_t
-AVFormatReader::StreamCookie::Seek(uint32 flags, int64* frame,
-       bigtime_t* time)
-{
-       if (fContext == NULL || fStream == NULL)
-               return B_NO_INIT;
-
-       TRACE_SEEK("AVFormatReader::StreamCookie::Seek(%ld,%s%s%s%s, %lld, "
-               "%lld)\n", VirtualIndex(),
-               (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
-               (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
-               (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " 
B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
-               (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " 
B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
-               *frame, *time);
-
-       // Seeking is always based on time, initialize it when client seeks
-       // based on frame.
-       double frameRate = FrameRate();
-       if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0)
-               *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
-
-       int64_t timeStamp = *time;
-       int64_t minTimeStamp = timeStamp - 1000000;
-       if (minTimeStamp < 0)
-               minTimeStamp = 0;
-       int64_t maxTimeStamp = timeStamp + 1000000;
-
-       TRACE_SEEK("  time: %.5fs -> %lld, current DTS: %lld (time_base: 
%d/%d)\n",
-               *time / 1000000.0, timeStamp, fStream->cur_dts, 
fStream->time_base.num,
-               fStream->time_base.den);
-
-       int searchFlags = AVSEEK_FLAG_BACKWARD;
-       if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0)
-               searchFlags = 0;
-
-       if (fSeekByBytes) {
-               searchFlags |= AVSEEK_FLAG_BYTE;
-               BAutolock _(fStreamLock);
-               off_t fileSize;
-               if (fSource->GetSize(&fileSize) != B_OK)
-                       return B_NOT_SUPPORTED;
-               int64_t duration = Duration();
-               if (duration == 0)
-                       return B_NOT_SUPPORTED;
-
-               timeStamp = fileSize * timeStamp / duration;
-               minTimeStamp = INT64_MIN;
-               maxTimeStamp = INT64_MAX;
-       }
-
-       if (avformat_seek_file(fContext, -1, minTimeStamp, timeStamp,
-               maxTimeStamp, searchFlags) < 0) {
-               TRACE("  avformat_seek_file() failed.\n");
-               return B_ERROR;
-       }
-
-       // Our last packet is toast in any case. Read the next one so we
-       // know where we really seeked.
-       fReusePacket = false;
-       if (_NextPacket(true) == B_OK) {
-               if (fPacket.pts != kNoPTSValue)
-                       *time = _ConvertFromStreamTimeBase(fPacket.pts);
-               TRACE_SEEK("  seeked time: %.2fs\n", *time / 1000000.0);
-               if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
-                       *frame = *time * frameRate / 1000000LL + 0.5;
-                       TRACE_SEEK("  seeked frame: %lld\n", *frame);
-               }
-       } else {
-               TRACE_SEEK("  _NextPacket() failed!\n");
-       }
-
-       return B_OK;
-}
-
-
-status_t
 AVFormatReader::StreamCookie::FindKeyFrame(uint32 flags, int64* frame,
        bigtime_t* time) const
 {
@@ -970,9 +907,6 @@
                (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " 
B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
                *frame, *time);
 
-       if (fSeekByBytes)
-               return B_OK;
-
        double frameRate = FrameRate();
        if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0)
                *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
@@ -988,7 +922,7 @@
 
        int index = av_index_search_timestamp(fStream, timeStamp, searchFlags);
        if (index < 0) {
-               TRACE_FIND("  av_index_search_timestamp() failed.\n");
+               TRACE("  av_index_search_timestamp() failed.\n");
                // Best is to assume we can somehow seek to the time/frame
                // and leave them as they are.
        } else {
@@ -1030,18 +964,187 @@
                } else
                        *time = foundTime;
        }
-       
+
        TRACE_FIND("  found time: %.2fs (%lld)\n", *time / 1000000.0, 
timeStamp);
        if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
                *frame = int64_t(*time * frameRate / 1000000.0 + 0.5);
                TRACE_FIND("  found frame: %lld\n", *frame);
        }
 
+       fLastReportedKeyframe.frame = *frame;
+       fLastReportedKeyframe.time = *time;
+       fLastReportedKeyframe.streamTimeStamp = *time;
+
        return B_OK;
 }
 
 
 status_t
+AVFormatReader::StreamCookie::Seek(uint32 flags, int64* frame,
+       bigtime_t* time)
+{
+       if (fContext == NULL || fStream == NULL)
+               return B_NO_INIT;
+
+       TRACE_SEEK("AVFormatReader::StreamCookie::Seek(%ld,%s%s%s%s, %lld, "
+               "%lld)\n", VirtualIndex(),
+               (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
+               (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
+               (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " 
B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
+               (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " 
B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
+               *frame, *time);
+
+       int64_t timeStamp;
+
+       // Seeking is always based on time, initialize it when client seeks
+       // based on frame.
+       double frameRate = FrameRate();
+       if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
+               *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5);
+               if (fLastReportedKeyframe.frame == *frame)
+                       timeStamp = fLastReportedKeyframe.streamTimeStamp;
+               else
+                       timeStamp = *time;
+       } else {
+               if (fLastReportedKeyframe.time == *time)
+                       timeStamp = fLastReportedKeyframe.streamTimeStamp;
+               else
+                       timeStamp = *time;
+       }
+
+       TRACE_SEEK("  time: %.5fs -> %lld, current DTS: %lld (time_base: 
%d/%d)\n",
+               *time / 1000000.0, timeStamp, fStream->cur_dts, 
fStream->time_base.num,
+               fStream->time_base.den);
+
+       int searchFlags = AVSEEK_FLAG_BACKWARD;
+       if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0)
+               searchFlags = 0;
+
+       if (fSeekByBytes) {
+               searchFlags = AVSEEK_FLAG_BYTE;
+               BAutolock _(fStreamLock);
+               int64_t fileSize;
+               if (fSource->GetSize(&fileSize) != B_OK)
+                       return B_NOT_SUPPORTED;
+               int64_t duration = Duration();
+               if (duration == 0)
+                       return B_NOT_SUPPORTED;
+
+               timeStamp = int64_t(fileSize * ((double)timeStamp / duration));
+
+               bool seekAgain = true;
+               bool seekForward = true;
+               bigtime_t lastFoundTime = -1;
+               int64_t closestTimeStampBackwards = -1;
+               while (seekAgain) {
+                       if (avformat_seek_file(fContext, -1, INT64_MIN, 
timeStamp,
+                               INT64_MAX, searchFlags) < 0) {
+                               TRACE("  avformat_seek_file() (by bytes) 
failed.\n");
+                               return B_ERROR;
+                       }
+                       seekAgain = false;
+       
+                       // Our last packet is toast in any case. Read the next 
one so we
+                       // know where we really seeked.
+                       fReusePacket = false;
+                       if (_NextPacket(true) == B_OK) {
+                               while (fPacket.pts == kNoPTSValue) {
+                                       fReusePacket = false;
+                                       if (_NextPacket(true) != B_OK)
+                                               return B_ERROR;
+                               }
+                               if (fPacket.pos >= 0)
+                                       timeStamp = fPacket.pos;
+                               bigtime_t foundTime
+                                       = 
_ConvertFromStreamTimeBase(fPacket.pts);
+                               if (foundTime != lastFoundTime) {
+                                       lastFoundTime = foundTime;
+                                       if (foundTime > *time) {
+                                               if (closestTimeStampBackwards 
>= 0) {
+                                                       timeStamp = 
closestTimeStampBackwards;
+                                                       seekAgain = true;
+                                                       seekForward = false;
+                                                       continue;
+                                               }
+                                               int64_t diff = int64_t(fileSize
+                                                       * ((double)(foundTime - 
*time) / (2 * duration)));
+                                               if (diff < 8192)
+                                                       break;
+                                               timeStamp -= diff;
+                                               TRACE_SEEK("  need to seek back 
(%lld) (time: %.2f "
+                                                       "-> %.2f)\n", 
timeStamp, *time / 1000000.0,
+                                                       foundTime / 1000000.0);
+                                               if (timeStamp < 0)
+                                                       foundTime = 0;
+                                               else {
+                                                       seekAgain = true;
+                                                       continue;
+                                               }
+                                       } else if (seekForward && foundTime < 
*time - 100000) {
+                                               closestTimeStampBackwards = 
timeStamp;
+                                               int64_t diff = int64_t(fileSize
+                                                       * ((double)(*time - 
foundTime) / (2 * duration)));
+                                               if (diff < 8192)
+                                                       break;
+                                               timeStamp += diff;
+                                               TRACE_SEEK("  need to seek 
forward (%lld) (time: "
+                                                       "%.2f -> %.2f)\n", 
timeStamp, *time / 1000000.0,
+                                                       foundTime / 1000000.0);
+                                               if (timeStamp > duration)
+                                                       foundTime = duration;
+                                               else {
+                                                       seekAgain = true;
+                                                       continue;
+                                               }
+                                       }
+                               }
+                               TRACE_SEEK("  found time: %lld -> %lld 
(%.2f)\n", *time,
+                                       foundTime, foundTime / 1000000.0);
+                               *time = foundTime;
+                               if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
+                                       *frame = *time * frameRate / 1000000LL 
+ 0.5;
+                                       TRACE_SEEK("  seeked frame: %lld\n", 
*frame);
+                               }
+                       } else {
+                               TRACE_SEEK("  _NextPacket() failed!\n");
+                               return B_ERROR;
+                       }
+               }
+
+       } else {
+               int64_t minTimeStamp = timeStamp - 1000000;
+               if (minTimeStamp < 0)
+                       minTimeStamp = 0;
+               int64_t maxTimeStamp = timeStamp + 1000000;
+
+               if (avformat_seek_file(fContext, -1, minTimeStamp, timeStamp,
+                       maxTimeStamp, searchFlags) < 0) {
+                       TRACE("  avformat_seek_file() failed.\n");
+                       return B_ERROR;
+               }
+       
+               // Our last packet is toast in any case. Read the next one so we
+               // know where we really seeked.
+               fReusePacket = false;
+               if (_NextPacket(true) == B_OK) {
+                       if (fPacket.pts != kNoPTSValue) {
+                               *time = _ConvertFromStreamTimeBase(fPacket.pts);
+                               TRACE_SEEK("  seeked time: %.2fs\n", *time / 
1000000.0);
+                               if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) {
+                                       *frame = *time * frameRate / 1000000LL 
+ 0.5;
+                                       TRACE_SEEK("  seeked frame: %lld\n", 
*frame);
+                               }
+                       } else
+                               TRACE_SEEK("  no PTS in packet after 
seeking\n");
+               } else
+                       TRACE_SEEK("  _NextPacket() failed!\n");
+       }
+
+       return B_OK;
+}
+
+
+status_t
 AVFormatReader::StreamCookie::GetNextChunk(const void** chunkBuffer,
        size_t* chunkSize, media_header* mediaHeader)
 {


Other related posts:

  • » [haiku-commits] r38706 - haiku/trunk/src/add-ons/media/plugins/ffmpeg - superstippi