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
