Split decode and convert, and do them on alternating frames.

This commit is contained in:
Glenn Maynard
2007-03-25 07:46:20 +00:00
parent 87f1f61021
commit 813432c98d
4 changed files with 41 additions and 62 deletions
@@ -234,7 +234,8 @@ public:
void Close();
void Rewind();
int GetFrame( RageSurface *pOut, float fTargetTime );
void GetFrame( RageSurface *pOut );
int DecodeFrame( float fTargetTime );
int GetWidth() const { return m_pStream->codec->width; }
int GetHeight() const { return m_pStream->codec->height; }
@@ -248,8 +249,7 @@ private:
void Init();
RString OpenCodec();
int ReadPacket();
int DecodePacket( RageSurface *pOut, float fTargetTime );
void ConvertToSurface( RageSurface *pSurface ) const;
int DecodePacket( float fTargetTime );
avcodec::AVStream *m_pStream;
avcodec::AVFrame m_Frame;
@@ -315,11 +315,12 @@ void MovieDecoder_FFMpeg::Init()
}
/* Read until we get a frame, EOF or error. Return -1 on error, 0 on EOF, 1 if we have a frame. */
int MovieDecoder_FFMpeg::GetFrame( RageSurface *pOut, float fTargetTime )
int MovieDecoder_FFMpeg::DecodeFrame( float fTargetTime )
{
while( 1 )
{
int ret = DecodePacket( pOut, fTargetTime );
int ret = DecodePacket( fTargetTime );
if( ret == 1 )
return 1;
if( ret == -1 )
@@ -384,7 +385,7 @@ int MovieDecoder_FFMpeg::ReadPacket()
/* Decode data from the current packet. Return -1 on error, 0 if the packet is finished,
* and 1 if we have a frame (we may have more data in the packet). */
int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
int MovieDecoder_FFMpeg::DecodePacket( float fTargetTime )
{
if( m_iEOF == 0 && m_iCurrentPacketOffset == -1 )
return 0; /* no packet */
@@ -393,7 +394,7 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
{
if( m_bGetNextTimestamp )
{
if (m_Packet.pts != int64_t(AV_NOPTS_VALUE))
if (m_Packet.dts != int64_t(AV_NOPTS_VALUE))
{
m_fPTS = m_Packet.dts * av_q2d(m_pStream->time_base);
@@ -488,15 +489,13 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
if( bSkipThisFrame )
continue;
ConvertToSurface( pOut );
return 1;
}
return 0; /* packet done */
}
void MovieDecoder_FFMpeg::ConvertToSurface( RageSurface *pSurface ) const
void MovieDecoder_FFMpeg::GetFrame( RageSurface *pSurface )
{
avcodec::AVPicture pict;
pict.data[0] = (unsigned char *) pSurface->pixels;
@@ -48,7 +48,7 @@ RString MovieTexture_Generic::Init()
CreateFrameRects();
/* Decode one frame, to guarantee that the texture is drawn when this function returns. */
int ret = GetFrame( -1 );
int ret = m_pDecoder->DecodeFrame( -1 );
if( ret == -1 )
return ssprintf( "%s: error getting first frame", GetID().filename.c_str() );
if( ret == 0 )
@@ -57,7 +57,7 @@ RString MovieTexture_Generic::Init()
return ssprintf( "%s: EOF getting first frame", GetID().filename.c_str() );
}
m_ImageWaiting = FRAME_WAITING;
m_ImageWaiting = FRAME_DECODED;
LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)",
m_iSourceWidth, m_iSourceHeight,
@@ -302,8 +302,6 @@ void MovieTexture_Generic::CreateTexture()
* (due to quit, error, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
bool MovieTexture_Generic::DecodeFrame()
{
ASSERT_M( m_ImageWaiting == FRAME_NONE, ssprintf("%i", m_ImageWaiting) );
bool bTriedRewind = false;
do
{
@@ -334,7 +332,7 @@ bool MovieTexture_Generic::DecodeFrame()
if( m_bFrameSkipMode && m_fClock > m_pDecoder->GetTimestamp() )
fTargetTime = m_fClock;
int ret = GetFrame( fTargetTime );
int ret = m_pDecoder->DecodeFrame( fTargetTime );
if( ret == -1 )
return false;
@@ -353,22 +351,18 @@ bool MovieTexture_Generic::DecodeFrame()
}
/* We got a frame. */
m_ImageWaiting = FRAME_DECODED;
} while( m_bWantRewind );
return true;
}
/*
* Call when m_ImageWaiting == FRAME_DECODED.
* Returns:
* == 0 if the currently decoded frame is ready to be displayed
* > 0 (seconds) if it's not yet time to display;
*/
float MovieTexture_Generic::CheckFrameTime()
{
ASSERT_M( m_ImageWaiting == FRAME_DECODED, ssprintf("%i", m_ImageWaiting) );
if( m_fRate == 0 )
return 1; // "a long time until the next frame"
@@ -428,29 +422,31 @@ void MovieTexture_Generic::DecodeSeconds( float fSeconds )
{
/* If we don't have a frame decoded, decode one. */
if( m_ImageWaiting == FRAME_NONE )
DecodeFrame();
/* If we have a frame decoded, see if it's time to display it. */
if( m_ImageWaiting == FRAME_DECODED )
{
float fTime = CheckFrameTime();
if( fTime > 0 )
return;
if( !DecodeFrame() )
break;
m_ImageWaiting = FRAME_WAITING;
m_ImageWaiting = FRAME_DECODED;
}
if( m_ImageWaiting != FRAME_WAITING )
/* If we have a frame decoded, see if it's time to display it. */
float fTime = CheckFrameTime();
if( fTime > 0 )
return;
CHECKPOINT;
UpdateFrame();
m_ImageWaiting = FRAME_NONE;
return;
}
LOG->MapLog( "movie_looping", "MovieTexture_Generic::Update looping" );
}
int MovieTexture_Generic::GetFrame( float fTargetTime )
void MovieTexture_Generic::UpdateFrame()
{
/* Just in case we were invalidated: */
CreateTexture();
@@ -461,19 +457,9 @@ int MovieTexture_Generic::GetFrame( float fTargetTime )
m_pTextureLock->Lock( iHandle, m_pSurface );
}
int ret = m_pDecoder->GetFrame( m_pSurface, fTargetTime );
m_pDecoder->GetFrame( m_pSurface );
if( m_pTextureLock != NULL )
m_pTextureLock->Unlock( m_pSurface, ret > 0 );
return ret;
}
/* Call when m_ImageWaiting == FRAME_WAITING to update the texture. Sets FRAME_NONE. */
void MovieTexture_Generic::UpdateFrame()
{
ASSERT_M( m_ImageWaiting == FRAME_WAITING, ssprintf("%i", m_ImageWaiting) );
/* Just in case we were invalidated: */
CreateTexture();
m_pTextureLock->Unlock( m_pSurface, true );
if( m_pRenderTarget != NULL )
{
@@ -503,8 +489,6 @@ void MovieTexture_Generic::UpdateFrame()
m_iImageWidth, m_iImageHeight );
CHECKPOINT;
}
m_ImageWaiting = FRAME_NONE;
}
static EffectMode EffectModes[] =
@@ -36,7 +36,12 @@ public:
* Otherwise, fTargetTime will be -1, and the next frame should be
* decoded; skip frames only if necessary to recover from errors.
*/
virtual int GetFrame( RageSurface *pOut, float fTargetTime ) = 0;
virtual int DecodeFrame( float fTargetTime ) = 0;
/*
* Get the currently-decoded frame.
*/
virtual void GetFrame( RageSurface *pOut ) = 0;
/* Return the dimensions of the image, in pixels (before aspect ratio
* adjustments). */
@@ -93,30 +98,16 @@ public:
static EffectMode GetEffectMode( MovieDecoderPixelFormatYCbCr fmt );
private:
int GetFrame( float fTargetTime );
MovieDecoder *m_pDecoder;
float m_fRate;
enum {
FRAME_NONE, /* no frame available; call GetFrame to get one */
FRAME_DECODED, /* frame decoded; waiting until it's time to display it */
FRAME_WAITING /* frame waiting to be uploaded */
FRAME_DECODED /* frame decoded; waiting until it's time to display it */
} m_ImageWaiting;
bool m_bLoop;
bool m_bWantRewind;
/*
* Only the main thread can change m_State.
*
* DECODER_QUIT: The decoder thread is not running. We should only
* be in this state internally; when we return after a call, we should
* never be in this state. Start the thread before returning.
*
* PAUSE_DECODER: The decoder thread is idle.
*
* PLAYING: The decoder thread is running.
*/
enum State { DECODER_QUIT, DECODER_RUNNING } m_State;
unsigned m_uTexHandle;
@@ -32,7 +32,8 @@ public:
void Close();
void Rewind() { } // XXX
int GetFrame( RageSurface *pOut, float fTargetTime );
int DecodeFrame( float fTargetTime );
void GetFrame( RageSurface *pOut );
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
int GetWidth() const { return m_TheoraInfo.frame_width; }
@@ -228,7 +229,7 @@ RString MovieDecoder_Theora::Open( RString sFile )
return RString();
}
int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
int MovieDecoder_Theora::DecodeFrame( float fTargetTime )
{
while(1)
{
@@ -245,7 +246,6 @@ int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
continue;
}
ConvertToSurface( pOut );
m_iGranulepos = m_TheoraState.granulepos;
return 1;
}
@@ -266,6 +266,11 @@ int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
}
}
void MovieDecoder_Theora::GetFrame( RageSurface *pOut )
{
ConvertToSurface( pOut );
}
RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
{
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt), fmtout );