better abstraction
This commit is contained in:
@@ -112,7 +112,7 @@ static void FixLilEndian()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int FindCompatibleAVFormat( PixelFormat &pixfmt, bool bHighColor )
|
static int FindCompatibleAVFormat( bool bHighColor )
|
||||||
{
|
{
|
||||||
for( int i = 0; AVPixelFormats[i].bpp; ++i )
|
for( int i = 0; AVPixelFormats[i].bpp; ++i )
|
||||||
{
|
{
|
||||||
@@ -120,7 +120,7 @@ static int FindCompatibleAVFormat( PixelFormat &pixfmt, bool bHighColor )
|
|||||||
if( fmt.bHighColor != bHighColor )
|
if( fmt.bHighColor != bHighColor )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pixfmt = DISPLAY->FindPixelFormat( fmt.bpp,
|
PixelFormat pixfmt = DISPLAY->FindPixelFormat( fmt.bpp,
|
||||||
fmt.masks[0],
|
fmt.masks[0],
|
||||||
fmt.masks[1],
|
fmt.masks[1],
|
||||||
fmt.masks[2],
|
fmt.masks[2],
|
||||||
@@ -142,12 +142,28 @@ class FFMpeg_Helper
|
|||||||
public:
|
public:
|
||||||
FFMpeg_Helper();
|
FFMpeg_Helper();
|
||||||
~FFMpeg_Helper();
|
~FFMpeg_Helper();
|
||||||
int GetFrame();
|
|
||||||
void Init();
|
|
||||||
|
|
||||||
CString Open( CString sFile );
|
CString Open( CString sFile );
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
/* Decode a frame internally. */
|
||||||
|
int GetFrame();
|
||||||
|
|
||||||
|
/* Convert the current frame to an RGB surface. This may be skipped
|
||||||
|
* in frame skip mode. */
|
||||||
|
void ConvertToSurface( RageSurface *pSurface ) const;
|
||||||
|
|
||||||
|
int GetWidth() const { return m_stream->codec.width; }
|
||||||
|
int GetHeight() const { return m_stream->codec.height; }
|
||||||
|
|
||||||
|
/* Create a surface. This must be compatible with ConvertToSurface,
|
||||||
|
* should be a surface which is realtime-compatible with DISPLAY, and
|
||||||
|
* should attempt to obey TEXTUREMAN->GetPrefs().m_iMovieColorDepth.
|
||||||
|
* The given size will usually be the next power of two higher than
|
||||||
|
* GetWidth/GetHeight, but on systems with limited texture resolution,
|
||||||
|
* may be smaller. */
|
||||||
|
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight );
|
||||||
|
|
||||||
/* Get the timestamp, in seconds, when the current frame should be
|
/* Get the timestamp, in seconds, when the current frame should be
|
||||||
* displayed. The first frame will always be 0. */
|
* displayed. The first frame will always be 0. */
|
||||||
float GetTimestamp() const;
|
float GetTimestamp() const;
|
||||||
@@ -155,10 +171,13 @@ public:
|
|||||||
/* Get the duration, in seconds, to display the current frame. */
|
/* Get the duration, in seconds, to display the current frame. */
|
||||||
float GetFrameDuration() const;
|
float GetFrameDuration() const;
|
||||||
|
|
||||||
avcodec::AVStream *m_stream;
|
bool SkippableFrame() const { return (m_stream->codec.frame_number % 2) == 0; }
|
||||||
avcodec::AVFrame frame;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
avcodec::AVStream *m_stream;
|
||||||
|
avcodec::AVFrame frame;
|
||||||
|
int m_AVTexfmt; /* AVPixelFormat_t of m_img */
|
||||||
|
|
||||||
float m_fPTS;
|
float m_fPTS;
|
||||||
avcodec::AVFormatContext *m_fctx;
|
avcodec::AVFormatContext *m_fctx;
|
||||||
bool m_bGetNextTimestamp;
|
bool m_bGetNextTimestamp;
|
||||||
@@ -174,6 +193,7 @@ private:
|
|||||||
* 2 = EOF from ReadPacket and DecodePacket */
|
* 2 = EOF from ReadPacket and DecodePacket */
|
||||||
int m_iEOF;
|
int m_iEOF;
|
||||||
|
|
||||||
|
void Init();
|
||||||
int ReadPacket();
|
int ReadPacket();
|
||||||
int DecodePacket();
|
int DecodePacket();
|
||||||
float m_fTimestampOffset;
|
float m_fTimestampOffset;
|
||||||
@@ -381,21 +401,28 @@ int FFMpeg_Helper::DecodePacket()
|
|||||||
return 0; /* packet done */
|
return 0; /* packet done */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert the frame from the native (typically YUV) internal format to
|
||||||
|
* our RGB RageSurface. */
|
||||||
void MovieTexture_FFMpeg::ConvertFrame()
|
void MovieTexture_FFMpeg::ConvertFrame()
|
||||||
{
|
{
|
||||||
ASSERT_M( m_ImageWaiting == FRAME_DECODED, ssprintf("%i", m_ImageWaiting ) );
|
ASSERT_M( m_ImageWaiting == FRAME_DECODED, ssprintf("%i", m_ImageWaiting ) );
|
||||||
|
|
||||||
avcodec::AVPicture pict;
|
m_pDecoder->ConvertToSurface( m_pSurface );
|
||||||
pict.data[0] = (unsigned char *) m_pSurface->pixels;
|
|
||||||
pict.linesize[0] = m_pSurface->pitch;
|
|
||||||
|
|
||||||
avcodec::img_convert( &pict, AVPixelFormats[m_AVTexfmt].pf,
|
|
||||||
(avcodec::AVPicture *) &m_pDecoder->frame, m_pDecoder->m_stream->codec.pix_fmt,
|
|
||||||
m_pDecoder->m_stream->codec.width, m_pDecoder->m_stream->codec.height );
|
|
||||||
|
|
||||||
m_ImageWaiting = FRAME_WAITING;
|
m_ImageWaiting = FRAME_WAITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FFMpeg_Helper::ConvertToSurface( RageSurface *pSurface ) const
|
||||||
|
{
|
||||||
|
avcodec::AVPicture pict;
|
||||||
|
pict.data[0] = (unsigned char *) pSurface->pixels;
|
||||||
|
pict.linesize[0] = pSurface->pitch;
|
||||||
|
|
||||||
|
avcodec::img_convert( &pict, AVPixelFormats[m_AVTexfmt].pf,
|
||||||
|
(avcodec::AVPicture *) &frame, m_stream->codec.pix_fmt,
|
||||||
|
m_stream->codec.width, m_stream->codec.height );
|
||||||
|
}
|
||||||
|
|
||||||
static avcodec::AVStream *FindVideoStream( avcodec::AVFormatContext *m_fctx )
|
static avcodec::AVStream *FindVideoStream( avcodec::AVFormatContext *m_fctx )
|
||||||
{
|
{
|
||||||
for( int stream = 0; stream < m_fctx->nb_streams; ++stream )
|
for( int stream = 0; stream < m_fctx->nb_streams; ++stream )
|
||||||
@@ -431,13 +458,10 @@ MovieTexture_FFMpeg::MovieTexture_FFMpeg( RageTextureID ID ):
|
|||||||
|
|
||||||
CString MovieTexture_FFMpeg::Init()
|
CString MovieTexture_FFMpeg::Init()
|
||||||
{
|
{
|
||||||
CString sError = CreateDecoder();
|
CString sError = m_pDecoder->Open( GetID().filename );
|
||||||
if( sError != "" )
|
if( sError != "" )
|
||||||
return sError;
|
return sError;
|
||||||
|
|
||||||
LOG->Trace( "Bitrate: %i", m_pDecoder->m_stream->codec.bit_rate );
|
|
||||||
LOG->Trace( "Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(m_pDecoder->m_stream->codec.pix_fmt) );
|
|
||||||
|
|
||||||
/* 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 = m_pDecoder->GetFrame();
|
int ret = m_pDecoder->GetFrame();
|
||||||
if( ret == -1 )
|
if( ret == -1 )
|
||||||
@@ -454,7 +478,6 @@ CString MovieTexture_FFMpeg::Init()
|
|||||||
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,
|
||||||
m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight );
|
m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight );
|
||||||
LOG->Trace( "Texture pixel format: %i", m_AVTexfmt );
|
|
||||||
|
|
||||||
CreateFrameRects();
|
CreateFrameRects();
|
||||||
|
|
||||||
@@ -471,7 +494,8 @@ CString MovieTexture_FFMpeg::Init()
|
|||||||
MovieTexture_FFMpeg::~MovieTexture_FFMpeg()
|
MovieTexture_FFMpeg::~MovieTexture_FFMpeg()
|
||||||
{
|
{
|
||||||
StopThread();
|
StopThread();
|
||||||
DestroyDecoder();
|
if( m_pDecoder )
|
||||||
|
m_pDecoder->Close();
|
||||||
DestroyTexture();
|
DestroyTexture();
|
||||||
|
|
||||||
delete m_pDecoder;
|
delete m_pDecoder;
|
||||||
@@ -601,11 +625,11 @@ CString FFMpeg_Helper::Open( CString sFile )
|
|||||||
ret = avcodec::avcodec_open( &stream->codec, codec );
|
ret = avcodec::avcodec_open( &stream->codec, codec );
|
||||||
if ( ret < 0 )
|
if ( ret < 0 )
|
||||||
return ssprintf( averr_ssprintf(ret, "AVCodec (%s): Couldn't open codec \"%s\"", sFile.c_str(), codec->name) );
|
return ssprintf( averr_ssprintf(ret, "AVCodec (%s): Couldn't open codec \"%s\"", sFile.c_str(), codec->name) );
|
||||||
|
|
||||||
/* Don't set this until we successfully open stream->codec, so we don't try to close it
|
|
||||||
* on an exception unless it was really opened. */
|
|
||||||
m_stream = stream;
|
m_stream = stream;
|
||||||
|
|
||||||
|
LOG->Trace( "Bitrate: %i", m_stream->codec.bit_rate );
|
||||||
|
LOG->Trace( "Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(m_stream->codec.pix_fmt) );
|
||||||
|
|
||||||
return CString();
|
return CString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,18 +646,8 @@ void FFMpeg_Helper::Close()
|
|||||||
avcodec::av_close_input_file( m_fctx );
|
avcodec::av_close_input_file( m_fctx );
|
||||||
m_fctx = NULL;
|
m_fctx = NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CString MovieTexture_FFMpeg::CreateDecoder()
|
Init();
|
||||||
{
|
|
||||||
return m_pDecoder->Open( GetID().filename );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Delete the decoder. The decoding thread must be stopped. */
|
|
||||||
void MovieTexture_FFMpeg::DestroyDecoder()
|
|
||||||
{
|
|
||||||
m_pDecoder->Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete the surface and texture. The decoding thread must be stopped, and this
|
/* Delete the surface and texture. The decoding thread must be stopped, and this
|
||||||
@@ -658,40 +672,31 @@ void MovieTexture_FFMpeg::CreateTexture()
|
|||||||
|
|
||||||
CHECKPOINT;
|
CHECKPOINT;
|
||||||
|
|
||||||
RageTextureID actualID = GetID();
|
m_iSourceWidth = m_pDecoder->GetWidth();
|
||||||
actualID.iAlphaBits = 0;
|
m_iSourceHeight = m_pDecoder->GetHeight();
|
||||||
|
|
||||||
/* Cap the max texture size to the hardware max. */
|
/* Cap the max texture size to the hardware max. */
|
||||||
actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );
|
int iMaxSize = min( GetID().iMaxSize, DISPLAY->GetMaxTextureSize() );
|
||||||
|
m_iImageWidth = min( m_iSourceWidth, iMaxSize );
|
||||||
m_iSourceWidth = m_pDecoder->m_stream->codec.width;
|
m_iImageHeight = min( m_iSourceHeight, iMaxSize );
|
||||||
m_iSourceHeight = m_pDecoder->m_stream->codec.height;
|
|
||||||
|
|
||||||
/* image size cannot exceed max size */
|
|
||||||
m_iImageWidth = min( m_iSourceWidth, actualID.iMaxSize );
|
|
||||||
m_iImageHeight = min( m_iSourceHeight, actualID.iMaxSize );
|
|
||||||
|
|
||||||
/* Texture dimensions need to be a power of two; jump to the next. */
|
/* Texture dimensions need to be a power of two; jump to the next. */
|
||||||
m_iTextureWidth = power_of_two( m_iImageWidth );
|
m_iTextureWidth = power_of_two( m_iImageWidth );
|
||||||
m_iTextureHeight = power_of_two( m_iImageHeight );
|
m_iTextureHeight = power_of_two( m_iImageHeight );
|
||||||
|
|
||||||
/* Bogus assignment to shut gcc up. */
|
if( m_pSurface == NULL )
|
||||||
PixelFormat pixfmt = PixelFormat_RGBA8;
|
m_pSurface = m_pDecoder->CreateCompatibleSurface( m_iTextureWidth, m_iTextureHeight );
|
||||||
bool bPreferHighColor = (TEXTUREMAN->GetPrefs().m_iMovieColorDepth == 32);
|
|
||||||
m_AVTexfmt = FindCompatibleAVFormat( pixfmt, bPreferHighColor );
|
|
||||||
|
|
||||||
if( m_AVTexfmt == -1 )
|
PixelFormat pixfmt = DISPLAY->FindPixelFormat( m_pSurface->format->BitsPerPixel,
|
||||||
m_AVTexfmt = FindCompatibleAVFormat( pixfmt, !bPreferHighColor );
|
m_pSurface->format->Mask[0],
|
||||||
|
m_pSurface->format->Mask[1],
|
||||||
|
m_pSurface->format->Mask[2],
|
||||||
|
m_pSurface->format->Mask[3] );
|
||||||
|
|
||||||
if( m_AVTexfmt == -1 )
|
if( pixfmt == PixelFormat_INVALID )
|
||||||
{
|
{
|
||||||
/* No dice. Use the first avcodec format of the preferred bit depth,
|
/* We weren't given a natively-supported pixel format. Pick a supported
|
||||||
* and let the display system convert. */
|
* one. This is a fallback case, and implies a second conversion. */
|
||||||
for( m_AVTexfmt = 0; AVPixelFormats[m_AVTexfmt].bpp; ++m_AVTexfmt )
|
|
||||||
if( AVPixelFormats[m_AVTexfmt].bHighColor == bPreferHighColor )
|
|
||||||
break;
|
|
||||||
ASSERT( AVPixelFormats[m_AVTexfmt].bpp );
|
|
||||||
|
|
||||||
switch( TEXTUREMAN->GetPrefs().m_iMovieColorDepth )
|
switch( TEXTUREMAN->GetPrefs().m_iMovieColorDepth )
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
@@ -717,19 +722,36 @@ void MovieTexture_FFMpeg::CreateTexture()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_pSurface == NULL )
|
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, m_pSurface, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
RageSurface *FFMpeg_Helper::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight )
|
||||||
{
|
{
|
||||||
|
bool bPreferHighColor = (TEXTUREMAN->GetPrefs().m_iMovieColorDepth == 32);
|
||||||
|
m_AVTexfmt = FindCompatibleAVFormat( bPreferHighColor );
|
||||||
|
|
||||||
|
if( m_AVTexfmt == -1 )
|
||||||
|
m_AVTexfmt = FindCompatibleAVFormat( !bPreferHighColor );
|
||||||
|
|
||||||
|
if( m_AVTexfmt == -1 )
|
||||||
|
{
|
||||||
|
/* No dice. Use the first avcodec format of the preferred bit depth,
|
||||||
|
* and let the display system convert. */
|
||||||
|
for( m_AVTexfmt = 0; AVPixelFormats[m_AVTexfmt].bpp; ++m_AVTexfmt )
|
||||||
|
if( AVPixelFormats[m_AVTexfmt].bHighColor == bPreferHighColor )
|
||||||
|
break;
|
||||||
|
ASSERT( AVPixelFormats[m_AVTexfmt].bpp );
|
||||||
|
}
|
||||||
|
|
||||||
const AVPixelFormat_t *pfd = &AVPixelFormats[m_AVTexfmt];
|
const AVPixelFormat_t *pfd = &AVPixelFormats[m_AVTexfmt];
|
||||||
|
|
||||||
LOG->Trace("format %i, %08x %08x %08x %08x",
|
LOG->Trace( "Texture pixel format: %i (%ibpp, %08x %08x %08x %08x)", m_AVTexfmt,
|
||||||
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
||||||
|
|
||||||
m_pSurface = CreateSurface( m_iTextureWidth, m_iTextureHeight, pfd->bpp,
|
return CreateSurface( iTextureWidth, iTextureHeight, pfd->bpp,
|
||||||
pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
||||||
}
|
}
|
||||||
|
|
||||||
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, m_pSurface, false );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle decoding for a frame. Return true if a frame was decoded, false if not
|
/* Handle decoding for a frame. Return true if a frame was decoded, false if not
|
||||||
* (due to pause, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
|
* (due to pause, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
|
||||||
@@ -768,12 +790,11 @@ bool MovieTexture_FFMpeg::DecodeFrame()
|
|||||||
float fDelay = m_pDecoder->GetFrameDuration();
|
float fDelay = m_pDecoder->GetFrameDuration();
|
||||||
|
|
||||||
/* Restart. */
|
/* Restart. */
|
||||||
DestroyDecoder();
|
m_pDecoder->Close();
|
||||||
CString sError = CreateDecoder();
|
CString sError = m_pDecoder->Open( GetID().filename );
|
||||||
if( sError != "" )
|
if( sError != "" )
|
||||||
RageException::Throw( "Error rewinding stream %s: %s", GetID().filename.c_str(), sError.c_str() );
|
RageException::Throw( "Error rewinding stream %s: %s", GetID().filename.c_str(), sError.c_str() );
|
||||||
|
|
||||||
m_pDecoder->Init();
|
|
||||||
m_fClock = -fDelay;
|
m_fClock = -fDelay;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -838,7 +859,7 @@ float MovieTexture_FFMpeg::CheckFrameTime()
|
|||||||
m_bFrameSkipMode = true;
|
m_bFrameSkipMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_bFrameSkipMode && m_pDecoder->m_stream->codec.frame_number % 2 )
|
if( m_bFrameSkipMode && m_pDecoder->SkippableFrame() )
|
||||||
return -1; /* skip */
|
return -1; /* skip */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ private:
|
|||||||
unsigned m_uTexHandle;
|
unsigned m_uTexHandle;
|
||||||
|
|
||||||
RageSurface *m_pSurface;
|
RageSurface *m_pSurface;
|
||||||
int m_AVTexfmt; /* AVPixelFormat_t of m_img */
|
|
||||||
|
|
||||||
RageSemaphore m_BufferFinished;
|
RageSemaphore m_BufferFinished;
|
||||||
|
|
||||||
@@ -77,9 +76,7 @@ private:
|
|||||||
void ConvertFrame();
|
void ConvertFrame();
|
||||||
void UpdateFrame();
|
void UpdateFrame();
|
||||||
|
|
||||||
CString CreateDecoder();
|
|
||||||
void CreateTexture();
|
void CreateTexture();
|
||||||
void DestroyDecoder();
|
|
||||||
void DestroyTexture();
|
void DestroyTexture();
|
||||||
void StartThread();
|
void StartThread();
|
||||||
void StopThread();
|
void StopThread();
|
||||||
|
|||||||
Reference in New Issue
Block a user