Patches item #411182, was updated on 2001-03-25 09:16
You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=311050&aid=411182&group_id=11050

Category: None
Group: None
>Status: Closed
>Resolution: Accepted
Priority: 5
Submitted By: J�rgen Keil (jkeil)
>Assigned to: Zdenek Kabelac (kabi)
Summary: fix: broken video on low fps avi files

Initial Comment:
Since last week's video synchronization tweaks,
avifile-0.6-cvs shows some strange behaviour for
certain (non divx?, low frame rate?) avi files.  Video
playback is more or less OK,  but now and then a frame
from the future (or from the past?) is mixed into the
video output.  

As far as I've analysed the problem,  the culprit is
the VideoDecoder class, method
VideoDecoder::DecodeFrame().  The VideoDecoder object
is used in the decoder thread to keep a buffer of upto
20 decoded frames for future display by the separate
video thread.  

The problem seems to happen when all 20 frames contain
valid decoded frames (decpos == 20) and the playback
thead is still busy with frame #0 (playpos == 0).
That is, there's no space for a new decoded frame.

In this case the DecodeFrame() method sleeps for
50mSecs, hoping that the video thread has now used
some frames and has advanced playpos to something > 0 -
making room for some new decoded frames.  But if
there's still no space left after sleeping for 50mSecs,
the DecodeFrame() method simply declares the frame #0
as empty and shifts it to the end of vbuf array,
freeing one frame.  Obviously the frame #0 is still in
use by the video thread,  so stealing this frame to
get room to decode some future frame may result in this
future frame be mixed into the current video streams.

Note that for a 15 frames/second avi file,  the frame
time for one frame (66mSec) is larger than the 50mSecs
delay in DecodeFrame()!


There are several ideas to fix this problem:

1. the DecodeFrame() method could return something like
   an "I currently have no space left for this frame"
   error up to it's caller and not sleep at all.  The
   decode thread could use this error to retry the
   DecodeFrame() operation at a later time
   (watching for hangups, etc...).

2. Use the same trick DS_VideoDecoder::DecodeFrame()
   uses:  DS_VideoDecoder seems to release the decoded
   frame data and allocates a fresh one before
   decoding into a frame structure.

    if (vbuf[decpos]->data)
    {
        // yeah I know it's an overhead but I don't
know now
        // how to do it better
        vbuf[decpos]->data->Release();
    }
    vbuf[decpos]->data = new CImage(m_outFrame);

3. Or simply increase the 50mSec timeout in
   DecodeFrame().



The following patch tries to fix the problem 
according to 3. in the list above.  It sleeps
three times for 50mSecs,  checking for empty
decode space between the sleeps.


diff -rub -x CVS
avifile-0.6-orig/include/videodecoder.h
avifile-0.6/include/videodecoder.h
--- avifile-0.6-orig/include/videodecoder.h     Tue Mar 20
21:18:57 2001
+++ avifile-0.6/include/videodecoder.h  Sat Mar 24
19:17:42 2001
@@ -147,7 +147,7 @@
 protected:
     const CodecInfo& record;
 #ifndef WIN32
-    frame *vbuf[VBUFSIZE + 2]; //one for shifting
+    frame *vbuf[VBUFSIZE+1];
     int decpos;
     int playpos;
     int qual;
diff -rub -x CVS
avifile-0.6-orig/plugins/libwin32/videocodec/DS_VideoDecoder.cpp
avifile-0.6/plugins/libwin32/videocodec/DS_VideoDecoder.cpp
---
avifile-0.6-orig/plugins/libwin32/videocodec/DS_VideoDecoder.cpp
Tue Mar 20 21:18:57 2001
+++
avifile-0.6/plugins/libwin32/videocodec/DS_VideoDecoder.cpp
Sat Mar 24 19:19:51 2001
@@ -275,16 +275,16 @@
 
     pthread_mutex_lock (&playposmut);
     pthread_mutex_lock (&decposmut);
-shiftstuff:
+    for (int retry = 0;; retry++)
+    {
     while (playpos > 0)
     {
        // FIXME - this could be done in one loop
         // with two counters
-       for (int i = 0; i <= VBUFSIZE + 1; i++)
-           if (i == 0)
-               vbuf[VBUFSIZE + 1] = vbuf[0];
-           else
-               vbuf[i - 1] = vbuf[i];
+           frame *tmp_frame = vbuf[0];
+           for (int i = 1; i <= VBUFSIZE; i++)
+               vbuf[i-1] = vbuf[i];
+           vbuf[VBUFSIZE] = tmp_frame;
        playpos--;
        if (decpos > 0)
            decpos--;
@@ -294,8 +294,12 @@
     if (filling && (decpos-playpos)>= QMARKLO)
        filling = 0;
 
-    if (decpos >= VBUFSIZE)
+       if (decpos < VBUFSIZE)
     {
+           // OK, empty space for decoding found
+           break;
+       }
+
        // FIXME maybe passing hangup would help here ???
        pthread_mutex_unlock(&decposmut);
 
@@ -303,18 +307,13 @@
        avi_usleep (50000); // 60ms
        pthread_mutex_lock(&playposmut);
        pthread_mutex_lock(&decposmut);
-       if (realtime)
+
+       if (retry >= 2 && playpos < 1 && decpos >= VBUFSIZE)
        {
-           if (playpos < 1 && decpos >= VBUFSIZE)
                // lets simulate we have played one frame to get
                // a room for new one
                playpos++;
-       } else
-       {
-           cout << "FIXME ERROR:
DS_VideoDecoder::DecodeFrame too long wait "
-               << "d:" << decpos << " p:" << playpos << endl;
        }
-       goto shiftstuff;
     }
 
     m_pAll->vt->GetBuffer(m_pAll, &sample, 0, 0, 0);
diff -rub -x CVS
avifile-0.6-orig/plugins/libwin32/videocodec/VideoDecoder.cpp
avifile-0.6/plugins/libwin32/videocodec/VideoDecoder.cpp
---
avifile-0.6-orig/plugins/libwin32/videocodec/VideoDecoder.cpp
Tue Mar 20 21:18:57 2001
+++
avifile-0.6/plugins/libwin32/videocodec/VideoDecoder.cpp
Sun Mar 25 00:49:20 2001
@@ -49,7 +49,7 @@
        qual = 0;
     else if (qual > 4)
        qual = 4;
-    for (int i = 0; i <= VBUFSIZE + 1; i++)
+    for (int i = 0; i <= VBUFSIZE; i++)
     {
        vbuf[i] = new frame;
        vbuf[i]->time = -1;
@@ -173,7 +173,7 @@
     {
         delete codec;
         delete m_outFrame;
-       for (int i = 0; i <= VBUFSIZE + 1; i++) {
+       for (int i = 0; i <= VBUFSIZE; i++) {
            if (vbuf[i]->data)
            {
                vbuf[i]->data->Release();
@@ -203,7 +203,7 @@
     if(m_outFrame)delete m_outFrame;
     //for(int i=0; i<fields;
i++)if(m_outFrame[i])delete m_outFrame[i];
     // hey dude you tried precaching already? :-)
-    for (int i = 0; i <= VBUFSIZE + 1; i++)
+    for (int i = 0; i <= VBUFSIZE; i++)
     {
        if (vbuf[i] )
            if (vbuf[i]->data)
@@ -262,7 +262,7 @@
     m_outFrame=0;
     FlushCache();
     /*
-    for (int i = 0; i <= VBUFSIZE + 1; i++)
+    for (int i = 0; i <= VBUFSIZE; i++)
        if (vbuf[i]->data)
            vbuf[i]->data->Release();
            */
@@ -297,7 +297,7 @@
     codec=0;
     m_outFrame=0;
     m_iState=0;
-    for (int i = 0; i <= VBUFSIZE + 1; i++)
+    for (int i = 0; i <= VBUFSIZE; i++)
     {
        if (vbuf[i]->data)
        {
@@ -323,40 +323,48 @@
            cerr<<"VideoDecoder: warning: hr="<<hr<<endl;
        return hr;
     }
-
     m_bh.biSizeImage=size;
     pthread_mutex_lock (&playposmut);
     pthread_mutex_lock (&decposmut);
-shiftstuff:
+    for (int retry = 0;; retry++) 
+    {
+       // move old frames to end of vbuf array
     while (playpos > 0)
     {
-       for (int i = 0; i <= VBUFSIZE + 1; i++)
-           if (i == 0)
-               vbuf[VBUFSIZE + 1] = vbuf[0];
-           else
+           frame *tmp_frame = vbuf[0];
+           for (int i = 1; i <= VBUFSIZE; i++)
                vbuf[i - 1] = vbuf[i];
+           vbuf[VBUFSIZE] = tmp_frame;
+
        playpos--;
        if (decpos > 0)
            decpos--;
     }
     pthread_mutex_unlock (&playposmut);
-    if (decpos >= VBUFSIZE) {
+       if (decpos < VBUFSIZE)
+       {
+           // decode space is available
+           break;
+       }
+
+       // no decode space available,  wait for playback
thread
+       // to use some decoded frames (so that playpos is
advanced)
        pthread_mutex_unlock(&decposmut);
        avi_usleep (50000);
        pthread_mutex_lock(&playposmut);
        pthread_mutex_lock(&decposmut);
-       if (realtime)
+       if (retry >= 2 && playpos < 1 && decpos >= VBUFSIZE)
        {
-           if (playpos < 1 && decpos >= VBUFSIZE)
+           // after waiting three times, there's still no
space
+           // available.
                // lets simulate we have played one frame to get
-               // a room for new one
+           // a room for new one.
+           // (note that this will move vbuf[0] - the frame
that
+           // probably is currently in use by the video
playback
+           // thread - to the end of the vbuf array,  and
we'll
+           // decode into that frame!)
                playpos++;
-       } else
-       {
-           cout << "FIXME ERROR VideoDecoder::DecodeFrame
too long wait "
-               << "d:" << decpos << " p:" << playpos << endl;
        }
-       goto shiftstuff;
     }
     if(size) {
        int
hr=codec->Decompress((is_keyframe?0:ICDECOMPRESS_NOTKEYFRAME)


----------------------------------------------------------------------

You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=311050&aid=411182&group_id=11050

_______________________________________________
Avifile mailing list
[EMAIL PROTECTED]
http://prak.org/mailman/listinfo/avifile

Reply via email to