VideoTexture: fix a bug with AV sync that was causing a loss of sync in case of rewind to the begining of the file.

This commit is contained in:
Benoit Bolsee 2010-02-25 22:12:16 +00:00
parent 0bef9d9c92
commit af987c12b5

@ -304,6 +304,10 @@ void *VideoFFmpeg::cacheThread(void *data)
CachePacket *cachePacket; CachePacket *cachePacket;
bool endOfFile = false; bool endOfFile = false;
int frameFinished = 0; int frameFinished = 0;
double timeBase = av_q2d(video->m_formatCtx->streams[video->m_videoStream]->time_base);
int64_t startTs = video->m_formatCtx->streams[video->m_videoStream]->start_time;
if (startTs == AV_NOPTS_VALUE)
startTs = 0;
while (!video->m_stopThread) while (!video->m_stopThread)
{ {
@ -390,7 +394,8 @@ void *VideoFFmpeg::cacheThread(void *data)
currentFrame->frame->data, currentFrame->frame->data,
currentFrame->frame->linesize); currentFrame->frame->linesize);
// move frame to queue, this frame is necessarily the next one // move frame to queue, this frame is necessarily the next one
currentFrame->framePosition = ++video->m_curPosition; video->m_curPosition = (long)((cachePacket->packet.dts-startTs) * (video->m_baseFrameRate*timeBase) + 0.5);
currentFrame->framePosition = video->m_curPosition;
pthread_mutex_lock(&video->m_cacheMutex); pthread_mutex_lock(&video->m_cacheMutex);
BLI_addtail(&video->m_frameCacheBase, currentFrame); BLI_addtail(&video->m_frameCacheBase, currentFrame);
pthread_mutex_unlock(&video->m_cacheMutex); pthread_mutex_unlock(&video->m_cacheMutex);
@ -731,14 +736,15 @@ void VideoFFmpeg::calcImage (unsigned int texId, double ts)
// get actual time // get actual time
double startTime = PIL_check_seconds_timer(); double startTime = PIL_check_seconds_timer();
double actTime; double actTime;
if (m_isFile && ts >= 0.0) // timestamp passed from audio actuators can sometimes be slightly negative
if (m_isFile && ts >= -0.5)
{ {
// allow setting timestamp only when not streaming // allow setting timestamp only when not streaming
actTime = ts; actTime = ts;
if (m_eof && actTime * actFrameRate() < m_lastFrame) if (actTime * actFrameRate() < m_lastFrame)
{ {
// user is asking to rewind while the playback is already finished in the cache. // user is asking to rewind, force a cache clear to make sure we will do a seek
// we must clean the cache otherwise the eof condition will prevent any further reading. // note that this does not decrement m_repeat if ts didn't reach m_range[1]
stopCache(); stopCache();
} }
} }
@ -840,8 +846,9 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
int frameFinished; int frameFinished;
int posFound = 1; int posFound = 1;
bool frameLoaded = false; bool frameLoaded = false;
long long targetTs = 0; int64_t targetTs = 0;
CacheFrame *frame; CacheFrame *frame;
int64_t dts = 0;
if (m_cacheStarted) if (m_cacheStarted)
{ {
@ -875,6 +882,10 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
{ {
return frame->frame; return frame->frame;
} }
if (frame->framePosition > position)
// this can happen after rewind if the seek didn't find the first frame
// the frame in the buffer is ahead of time, just leave it there
return NULL;
// this frame is not useful, release it // this frame is not useful, release it
pthread_mutex_lock(&m_cacheMutex); pthread_mutex_lock(&m_cacheMutex);
BLI_remlink(&m_frameCacheBase, frame); BLI_remlink(&m_frameCacheBase, frame);
@ -882,6 +893,11 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
pthread_mutex_unlock(&m_cacheMutex); pthread_mutex_unlock(&m_cacheMutex);
} while (true); } while (true);
} }
double timeBase = av_q2d(m_formatCtx->streams[m_videoStream]->time_base);
int64_t startTs = m_formatCtx->streams[m_videoStream]->start_time;
if (startTs == AV_NOPTS_VALUE)
startTs = 0;
// come here when there is no cache or cache has been stopped // come here when there is no cache or cache has been stopped
// locate the frame, by seeking if necessary (seeking is only possible for files) // locate the frame, by seeking if necessary (seeking is only possible for files)
if (m_isFile) if (m_isFile)
@ -901,7 +917,9 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
m_frame, &frameFinished, m_frame, &frameFinished,
packet.data, packet.size); packet.data, packet.size);
if (frameFinished) if (frameFinished)
m_curPosition++; {
m_curPosition = (long)((packet.dts-startTs) * (m_baseFrameRate*timeBase) + 0.5);
}
} }
av_free_packet(&packet); av_free_packet(&packet);
if (position == m_curPosition+1) if (position == m_curPosition+1)
@ -911,16 +929,13 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
// if the position is not in preseek, do a direct jump // if the position is not in preseek, do a direct jump
if (position != m_curPosition + 1) if (position != m_curPosition + 1)
{ {
double timeBase = av_q2d(m_formatCtx->streams[m_videoStream]->time_base);
int64_t pos = (int64_t)((position - m_preseek) / (m_baseFrameRate*timeBase)); int64_t pos = (int64_t)((position - m_preseek) / (m_baseFrameRate*timeBase));
int64_t startTs = m_formatCtx->streams[m_videoStream]->start_time;
int seekres; int seekres;
if (pos < 0) if (pos < 0)
pos = 0; pos = 0;
if (startTs != AV_NOPTS_VALUE) pos += startTs;
pos += startTs;
if (position <= m_curPosition || !m_eof) if (position <= m_curPosition || !m_eof)
{ {
@ -952,9 +967,7 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
} }
} }
// this is the timestamp of the frame we're looking for // this is the timestamp of the frame we're looking for
targetTs = (int64_t)(position / (m_baseFrameRate * timeBase)); targetTs = (int64_t)(position / (m_baseFrameRate * timeBase)) + startTs;
if (startTs != AV_NOPTS_VALUE)
targetTs += startTs;
posFound = 0; posFound = 0;
avcodec_flush_buffers(m_codecCtx); avcodec_flush_buffers(m_codecCtx);
@ -978,14 +991,17 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
avcodec_decode_video(m_codecCtx, avcodec_decode_video(m_codecCtx,
m_frame, &frameFinished, m_frame, &frameFinished,
packet.data, packet.size); packet.data, packet.size);
// remember dts to compute exact frame number
dts = packet.dts;
if (frameFinished && !posFound) if (frameFinished && !posFound)
{ {
if (packet.dts >= targetTs) if (dts >= targetTs)
{
posFound = 1; posFound = 1;
}
} }
if(frameFinished && posFound == 1) if (frameFinished && posFound == 1)
{ {
AVFrame * input = m_frame; AVFrame * input = m_frame;
@ -1028,7 +1044,7 @@ AVFrame *VideoFFmpeg::grabFrame(long position)
m_eof = m_isFile && !frameLoaded; m_eof = m_isFile && !frameLoaded;
if (frameLoaded) if (frameLoaded)
{ {
m_curPosition = position; m_curPosition = (long)((dts-startTs) * (m_baseFrameRate*timeBase) + 0.5);
if (m_isThreaded) if (m_isThreaded)
{ {
// normal case for file: first locate, then start cache // normal case for file: first locate, then start cache