Simple frame skip support
This commit is contained in:
@@ -310,6 +310,9 @@ void MovieTexture_AVCodec::DecoderThread()
|
|||||||
bool GetNextTimestamp = true;
|
bool GetNextTimestamp = true;
|
||||||
float CurrentTimestamp = 0, LastGoodTimestamp = 0, Last_IP_Timestamp=0;
|
float CurrentTimestamp = 0, LastGoodTimestamp = 0, Last_IP_Timestamp=0;
|
||||||
|
|
||||||
|
bool FrameSkipMode = false;
|
||||||
|
unsigned Frame = 0;
|
||||||
|
|
||||||
CHECKPOINT;
|
CHECKPOINT;
|
||||||
while( m_State != DECODER_QUIT )
|
while( m_State != DECODER_QUIT )
|
||||||
{
|
{
|
||||||
@@ -384,15 +387,13 @@ void MovieTexture_AVCodec::DecoderThread()
|
|||||||
if (!got_frame)
|
if (!got_frame)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
++Frame;
|
||||||
|
|
||||||
/* We got a frame. Convert it. */
|
/* We got a frame. Convert it. */
|
||||||
avcodec::AVPicture pict;
|
avcodec::AVPicture pict;
|
||||||
pict.data[0] = (unsigned char *)m_img->pixels;
|
pict.data[0] = (unsigned char *)m_img->pixels;
|
||||||
pict.linesize[0] = m_img->pitch;
|
pict.linesize[0] = m_img->pitch;
|
||||||
|
|
||||||
img_convert(&pict, AVPixelFormats[m_AVTexfmt].pf,
|
|
||||||
(avcodec::AVPicture *) &frame, m_stream->codec.pix_fmt,
|
|
||||||
m_stream->codec.width, m_stream->codec.height);
|
|
||||||
|
|
||||||
if ( m_stream->codec.has_b_frames &&
|
if ( m_stream->codec.has_b_frames &&
|
||||||
frame.pict_type != FF_B_TYPE )
|
frame.pict_type != FF_B_TYPE )
|
||||||
swap( CurrentTimestamp, Last_IP_Timestamp );
|
swap( CurrentTimestamp, Last_IP_Timestamp );
|
||||||
@@ -410,33 +411,78 @@ void MovieTexture_AVCodec::DecoderThread()
|
|||||||
CurrentTimestamp += frame.repeat_pict * (frame_delay * 0.5);
|
CurrentTimestamp += frame.repeat_pict * (frame_delay * 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
const float Offset = CurrentTimestamp - m_Timer;
|
|
||||||
|
|
||||||
/* If we're ahead, we're decoding too fast; delay. */
|
|
||||||
if( Offset > 0 )
|
|
||||||
SDL_Delay( int(1000*(CurrentTimestamp - m_Timer)) );
|
|
||||||
|
|
||||||
/* XXX: If we're behind, Offset will be negative and we
|
|
||||||
* simply won't delay, so the video will play fast until
|
|
||||||
* it catches up. However, if we're too far behind, we
|
|
||||||
* can take some steps to catch up. 1: We can stop updating
|
|
||||||
* the texture; 2: we can play with hurry_up. */
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Position = CurrentTimestamp;
|
m_Position = CurrentTimestamp;
|
||||||
|
|
||||||
/* Signal the main thread to update the image on the next Update. */
|
const float Offset = CurrentTimestamp - m_Timer;
|
||||||
m_ImageWaiting=true;
|
bool SkipThisTextureUpdate = false;
|
||||||
|
|
||||||
|
/* If we're ahead, we're decoding too fast; delay. */
|
||||||
|
if( Offset > 0 )
|
||||||
|
{
|
||||||
|
SDL_Delay( int(1000*(CurrentTimestamp - m_Timer)) );
|
||||||
|
if( FrameSkipMode )
|
||||||
|
{
|
||||||
|
/* We're caught up; stop skipping frames. */
|
||||||
|
LOG->Trace( "stopped skipping frames" );
|
||||||
|
FrameSkipMode = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* We're behind by -Offset seconds.
|
||||||
|
*
|
||||||
|
* If we're just slightly behind, don't worry about it; we'll simply
|
||||||
|
* not sleep, so we'll move as fast as we can to catch up.
|
||||||
|
*
|
||||||
|
* If we're far behind, we're short on CPU and we need to do something
|
||||||
|
* about it. We have at least two options:
|
||||||
|
*
|
||||||
|
* 1: We can skip texture updates. This is a big bottleneck on many
|
||||||
|
* systems.
|
||||||
|
*
|
||||||
|
* 2: If that's not enough, we can play with hurry_up.
|
||||||
|
*
|
||||||
|
* If we hit a threshold, start skipping frames via #1. If we do that,
|
||||||
|
* don't stop once we hit the threshold; keep doing it until we're fully
|
||||||
|
* caught up.
|
||||||
|
*
|
||||||
|
* I'm not sure when we should do #2. Also, we should try to notice if
|
||||||
|
* we simply don't have enough CPU for the video; it's better to just
|
||||||
|
* stay in frame skip mode than to enter and exit it constantly, but we
|
||||||
|
* don't want to do that due to a single timing glitch.
|
||||||
|
*/
|
||||||
|
const float FrameSkipThreshold = 0.5f;
|
||||||
|
|
||||||
|
if( -Offset >= FrameSkipThreshold && !FrameSkipMode )
|
||||||
|
{
|
||||||
|
LOG->Trace( "Entering frame skip mode" );
|
||||||
|
FrameSkipMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( FrameSkipMode )
|
||||||
|
if( Frame % 2 )
|
||||||
|
SkipThisTextureUpdate = true;
|
||||||
|
|
||||||
if( m_State == PLAYING_ONE )
|
if( m_State == PLAYING_ONE )
|
||||||
SDL_SemPost( m_OneFrameDecoded );
|
SkipThisTextureUpdate = false;
|
||||||
|
|
||||||
CHECKPOINT;
|
if( !SkipThisTextureUpdate )
|
||||||
SDL_SemWait( m_BufferFinished );
|
{
|
||||||
CHECKPOINT;
|
img_convert(&pict, AVPixelFormats[m_AVTexfmt].pf,
|
||||||
/* If the frame wasn't used, then we must be shutting down. */
|
(avcodec::AVPicture *) &frame, m_stream->codec.pix_fmt,
|
||||||
ASSERT( !m_ImageWaiting || m_State == DECODER_QUIT );
|
m_stream->codec.width, m_stream->codec.height);
|
||||||
|
|
||||||
|
/* Signal the main thread to update the image on the next Update. */
|
||||||
|
m_ImageWaiting=true;
|
||||||
|
|
||||||
|
if( m_State == PLAYING_ONE )
|
||||||
|
SDL_SemPost( m_OneFrameDecoded );
|
||||||
|
|
||||||
|
CHECKPOINT;
|
||||||
|
SDL_SemWait( m_BufferFinished );
|
||||||
|
CHECKPOINT;
|
||||||
|
/* If the frame wasn't used, then we must be shutting down. */
|
||||||
|
ASSERT( !m_ImageWaiting || m_State == DECODER_QUIT );
|
||||||
|
}
|
||||||
|
|
||||||
GetNextTimestamp = true;
|
GetNextTimestamp = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user