Split decode and convert, and do them on alternating frames.
This commit is contained in:
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user