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