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 Close();
void Rewind(); 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 GetWidth() const { return m_pStream->codec->width; }
int GetHeight() const { return m_pStream->codec->height; } int GetHeight() const { return m_pStream->codec->height; }
@@ -248,8 +249,7 @@ private:
void Init(); void Init();
RString OpenCodec(); RString OpenCodec();
int ReadPacket(); int ReadPacket();
int DecodePacket( RageSurface *pOut, float fTargetTime ); int DecodePacket( float fTargetTime );
void ConvertToSurface( RageSurface *pSurface ) const;
avcodec::AVStream *m_pStream; avcodec::AVStream *m_pStream;
avcodec::AVFrame m_Frame; 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. */ /* 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 ) while( 1 )
{ {
int ret = DecodePacket( pOut, fTargetTime ); int ret = DecodePacket( fTargetTime );
if( ret == 1 ) if( ret == 1 )
return 1; return 1;
if( ret == -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, /* 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). */ * 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 ) if( m_iEOF == 0 && m_iCurrentPacketOffset == -1 )
return 0; /* no packet */ return 0; /* no packet */
@@ -393,7 +394,7 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
{ {
if( m_bGetNextTimestamp ) 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); 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 ) if( bSkipThisFrame )
continue; continue;
ConvertToSurface( pOut );
return 1; return 1;
} }
return 0; /* packet done */ return 0; /* packet done */
} }
void MovieDecoder_FFMpeg::ConvertToSurface( RageSurface *pSurface ) const void MovieDecoder_FFMpeg::GetFrame( RageSurface *pSurface )
{ {
avcodec::AVPicture pict; avcodec::AVPicture pict;
pict.data[0] = (unsigned char *) pSurface->pixels; pict.data[0] = (unsigned char *) pSurface->pixels;
@@ -48,7 +48,7 @@ RString MovieTexture_Generic::Init()
CreateFrameRects(); CreateFrameRects();
/* Decode one frame, to guarantee that the texture is drawn when this function returns. */ /* 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 ) if( ret == -1 )
return ssprintf( "%s: error getting first frame", GetID().filename.c_str() ); return ssprintf( "%s: error getting first frame", GetID().filename.c_str() );
if( ret == 0 ) if( ret == 0 )
@@ -57,7 +57,7 @@ RString MovieTexture_Generic::Init()
return ssprintf( "%s: EOF getting first frame", GetID().filename.c_str() ); 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)", LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)",
m_iSourceWidth, m_iSourceHeight, 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. */ * (due to quit, error, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
bool MovieTexture_Generic::DecodeFrame() bool MovieTexture_Generic::DecodeFrame()
{ {
ASSERT_M( m_ImageWaiting == FRAME_NONE, ssprintf("%i", m_ImageWaiting) );
bool bTriedRewind = false; bool bTriedRewind = false;
do do
{ {
@@ -334,7 +332,7 @@ bool MovieTexture_Generic::DecodeFrame()
if( m_bFrameSkipMode && m_fClock > m_pDecoder->GetTimestamp() ) if( m_bFrameSkipMode && m_fClock > m_pDecoder->GetTimestamp() )
fTargetTime = m_fClock; fTargetTime = m_fClock;
int ret = GetFrame( fTargetTime ); int ret = m_pDecoder->DecodeFrame( fTargetTime );
if( ret == -1 ) if( ret == -1 )
return false; return false;
@@ -353,22 +351,18 @@ bool MovieTexture_Generic::DecodeFrame()
} }
/* We got a frame. */ /* We got a frame. */
m_ImageWaiting = FRAME_DECODED;
} while( m_bWantRewind ); } while( m_bWantRewind );
return true; return true;
} }
/* /*
* Call when m_ImageWaiting == FRAME_DECODED.
* Returns: * Returns:
* == 0 if the currently decoded frame is ready to be displayed * == 0 if the currently decoded frame is ready to be displayed
* > 0 (seconds) if it's not yet time to display; * > 0 (seconds) if it's not yet time to display;
*/ */
float MovieTexture_Generic::CheckFrameTime() float MovieTexture_Generic::CheckFrameTime()
{ {
ASSERT_M( m_ImageWaiting == FRAME_DECODED, ssprintf("%i", m_ImageWaiting) );
if( m_fRate == 0 ) if( m_fRate == 0 )
return 1; // "a long time until the next frame" 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 we don't have a frame decoded, decode one. */
if( m_ImageWaiting == FRAME_NONE ) 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( !DecodeFrame() )
if( fTime > 0 ) break;
return;
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; return;
CHECKPOINT; CHECKPOINT;
UpdateFrame(); UpdateFrame();
m_ImageWaiting = FRAME_NONE;
return;
} }
LOG->MapLog( "movie_looping", "MovieTexture_Generic::Update looping" ); LOG->MapLog( "movie_looping", "MovieTexture_Generic::Update looping" );
} }
int MovieTexture_Generic::GetFrame( float fTargetTime ) void MovieTexture_Generic::UpdateFrame()
{ {
/* Just in case we were invalidated: */ /* Just in case we were invalidated: */
CreateTexture(); CreateTexture();
@@ -461,19 +457,9 @@ int MovieTexture_Generic::GetFrame( float fTargetTime )
m_pTextureLock->Lock( iHandle, m_pSurface ); m_pTextureLock->Lock( iHandle, m_pSurface );
} }
int ret = m_pDecoder->GetFrame( m_pSurface, fTargetTime ); m_pDecoder->GetFrame( m_pSurface );
if( m_pTextureLock != NULL ) if( m_pTextureLock != NULL )
m_pTextureLock->Unlock( m_pSurface, ret > 0 ); m_pTextureLock->Unlock( m_pSurface, true );
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();
if( m_pRenderTarget != NULL ) if( m_pRenderTarget != NULL )
{ {
@@ -503,8 +489,6 @@ void MovieTexture_Generic::UpdateFrame()
m_iImageWidth, m_iImageHeight ); m_iImageWidth, m_iImageHeight );
CHECKPOINT; CHECKPOINT;
} }
m_ImageWaiting = FRAME_NONE;
} }
static EffectMode EffectModes[] = static EffectMode EffectModes[] =
@@ -36,7 +36,12 @@ public:
* Otherwise, fTargetTime will be -1, and the next frame should be * Otherwise, fTargetTime will be -1, and the next frame should be
* decoded; skip frames only if necessary to recover from errors. * 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 /* Return the dimensions of the image, in pixels (before aspect ratio
* adjustments). */ * adjustments). */
@@ -93,30 +98,16 @@ public:
static EffectMode GetEffectMode( MovieDecoderPixelFormatYCbCr fmt ); static EffectMode GetEffectMode( MovieDecoderPixelFormatYCbCr fmt );
private: private:
int GetFrame( float fTargetTime );
MovieDecoder *m_pDecoder; MovieDecoder *m_pDecoder;
float m_fRate; float m_fRate;
enum { enum {
FRAME_NONE, /* no frame available; call GetFrame to get one */ FRAME_NONE, /* no frame available; call GetFrame to get one */
FRAME_DECODED, /* frame decoded; waiting until it's time to display it */ FRAME_DECODED /* frame decoded; waiting until it's time to display it */
FRAME_WAITING /* frame waiting to be uploaded */
} m_ImageWaiting; } m_ImageWaiting;
bool m_bLoop; bool m_bLoop;
bool m_bWantRewind; 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; enum State { DECODER_QUIT, DECODER_RUNNING } m_State;
unsigned m_uTexHandle; unsigned m_uTexHandle;
@@ -32,7 +32,8 @@ public:
void Close(); void Close();
void Rewind() { } // XXX 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 ); RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
int GetWidth() const { return m_TheoraInfo.frame_width; } int GetWidth() const { return m_TheoraInfo.frame_width; }
@@ -228,7 +229,7 @@ RString MovieDecoder_Theora::Open( RString sFile )
return RString(); return RString();
} }
int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime ) int MovieDecoder_Theora::DecodeFrame( float fTargetTime )
{ {
while(1) while(1)
{ {
@@ -245,7 +246,6 @@ int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
continue; continue;
} }
ConvertToSurface( pOut );
m_iGranulepos = m_TheoraState.granulepos; m_iGranulepos = m_TheoraState.granulepos;
return 1; 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 ) RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
{ {
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt), fmtout ); return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt), fmtout );