- ffmpeg API update
- Add support for doing YUV colorspace conversion in hardware, using a fragment shader. This speeds up high-resolution movies substantially, and provides 32-bit color depth with the data cost of 16-bit textures. - optimize rewinding
This commit is contained in:
@@ -26,7 +26,19 @@ static struct AVPixelFormat_t
|
||||
avcodec::PixelFormat pf;
|
||||
bool bHighColor;
|
||||
bool bByteSwapOnLittleEndian;
|
||||
MovieDecoderPixelFormatYCbCr YUV;
|
||||
} AVPixelFormats[] = {
|
||||
{
|
||||
32,
|
||||
{ 0xFF000000,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF },
|
||||
avcodec::PIX_FMT_YUYV422,
|
||||
false, /* N/A */
|
||||
true,
|
||||
PixelFormatYCbCr_YUYV422,
|
||||
},
|
||||
{
|
||||
/* This format is really ARGB, and is affected by endianness, unlike PIX_FMT_RGB24
|
||||
* and PIX_FMT_BGR24. */
|
||||
@@ -37,7 +49,8 @@ static struct AVPixelFormat_t
|
||||
0xFF000000 },
|
||||
avcodec::PIX_FMT_RGBA32,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
PixelFormatYCbCr_Invalid,
|
||||
},
|
||||
{
|
||||
24,
|
||||
@@ -47,7 +60,8 @@ static struct AVPixelFormat_t
|
||||
0x000000 },
|
||||
avcodec::PIX_FMT_RGB24,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
PixelFormatYCbCr_Invalid,
|
||||
},
|
||||
{
|
||||
24,
|
||||
@@ -57,17 +71,19 @@ static struct AVPixelFormat_t
|
||||
0x000000 },
|
||||
avcodec::PIX_FMT_BGR24,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
PixelFormatYCbCr_Invalid,
|
||||
},
|
||||
{
|
||||
16,
|
||||
{ 0x7C00,
|
||||
0x03E0,
|
||||
0x001F,
|
||||
0x8000 },
|
||||
0x0000 },
|
||||
avcodec::PIX_FMT_RGB555,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
PixelFormatYCbCr_Invalid,
|
||||
},
|
||||
{ 0, { 0,0,0,0 }, avcodec::PIX_FMT_NB, true, false }
|
||||
};
|
||||
@@ -107,8 +123,16 @@ static int FindCompatibleAVFormat( bool bHighColor )
|
||||
for( int i = 0; AVPixelFormats[i].bpp; ++i )
|
||||
{
|
||||
AVPixelFormat_t &fmt = AVPixelFormats[i];
|
||||
if( fmt.bHighColor != bHighColor )
|
||||
if( fmt.YUV != PixelFormatYCbCr_Invalid )
|
||||
{
|
||||
EffectMode em = MovieTexture_Generic::GetEffectMode( fmt.YUV );
|
||||
if( !DISPLAY->IsEffectModeSupported(em) )
|
||||
continue;
|
||||
}
|
||||
else if( fmt.bHighColor != bHighColor )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PixelFormat pixfmt = DISPLAY->FindPixelFormat( fmt.bpp,
|
||||
fmt.masks[0],
|
||||
@@ -133,10 +157,10 @@ public:
|
||||
MovieTexture_FFMpeg( RageTextureID ID );
|
||||
|
||||
static void RegisterProtocols();
|
||||
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt );
|
||||
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout );
|
||||
};
|
||||
|
||||
RageSurface *RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt )
|
||||
RageSurface *RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout )
|
||||
{
|
||||
FixLilEndian();
|
||||
|
||||
@@ -156,10 +180,14 @@ RageSurface *RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( int
|
||||
|
||||
const AVPixelFormat_t *pfd = &AVPixelFormats[iAVTexfmtIndex];
|
||||
iAVTexfmt = pfd->pf;
|
||||
fmtout = pfd->YUV;
|
||||
|
||||
LOG->Trace( "Texture pixel format: %i (%ibpp, %08x %08x %08x %08x)", iAVTexfmt,
|
||||
LOG->Trace( "Texture pixel format: %i %i (%ibpp, %08x %08x %08x %08x)", iAVTexfmt, fmtout,
|
||||
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
||||
|
||||
if( pfd->YUV == PixelFormatYCbCr_YUYV422 )
|
||||
iTextureWidth /= 2;
|
||||
|
||||
return CreateSurface( iTextureWidth, iTextureHeight, pfd->bpp,
|
||||
pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
||||
}
|
||||
@@ -172,19 +200,21 @@ public:
|
||||
|
||||
RString Open( RString sFile );
|
||||
void Close();
|
||||
void Rewind();
|
||||
|
||||
int GetFrame( RageSurface *pOut, float fTargetTime );
|
||||
|
||||
int GetWidth() const { return m_pStream->codec.width; }
|
||||
int GetHeight() const { return m_pStream->codec.height; }
|
||||
int GetWidth() const { return m_pStream->codec->width; }
|
||||
int GetHeight() const { return m_pStream->codec->height; }
|
||||
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor );
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
|
||||
|
||||
float GetTimestamp() const;
|
||||
float GetFrameDuration() const;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
RString OpenCodec();
|
||||
int ReadPacket();
|
||||
int DecodePacket( RageSurface *pOut, float fTargetTime );
|
||||
void ConvertToSurface( RageSurface *pSurface ) const;
|
||||
@@ -276,6 +306,7 @@ float MovieDecoder_FFMpeg::GetFrameDuration() const
|
||||
return m_fLastFrameDelay;
|
||||
}
|
||||
|
||||
|
||||
/* Read a packet. Return -1 on error, 0 on EOF, 1 on OK. */
|
||||
int MovieDecoder_FFMpeg::ReadPacket()
|
||||
{
|
||||
@@ -313,7 +344,6 @@ 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 )
|
||||
@@ -325,8 +355,8 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
|
||||
{
|
||||
if( m_bGetNextTimestamp )
|
||||
{
|
||||
if (m_Packet.dts != int64_t(AV_NOPTS_VALUE))
|
||||
m_fPTS = (float)m_Packet.dts / AV_TIME_BASE;
|
||||
if (m_Packet.pts != int64_t(AV_NOPTS_VALUE))
|
||||
m_fPTS = m_Packet.dts * av_q2d(m_pStream->time_base);
|
||||
else
|
||||
m_fPTS = -1;
|
||||
m_bGetNextTimestamp = false;
|
||||
@@ -342,7 +372,7 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
|
||||
bool bSkipThisFrame =
|
||||
fTargetTime != -1 &&
|
||||
GetTimestamp() + GetFrameDuration() <= fTargetTime &&
|
||||
(m_pStream->codec.frame_number % 2) == 0;
|
||||
(m_pStream->codec->frame_number % 2) == 0;
|
||||
|
||||
int iGotFrame;
|
||||
CHECKPOINT;
|
||||
@@ -350,7 +380,7 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
|
||||
* to give it a buffer to read from since it tries to read anyway. */
|
||||
static uint8_t dummy[FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
|
||||
int len = avcodec::avcodec_decode_video(
|
||||
&m_pStream->codec,
|
||||
m_pStream->codec,
|
||||
&m_Frame, &iGotFrame,
|
||||
m_Packet.size? m_Packet.data:dummy, m_Packet.size );
|
||||
CHECKPOINT;
|
||||
@@ -384,7 +414,7 @@ int MovieDecoder_FFMpeg::DecodePacket( RageSurface *pOut, float fTargetTime )
|
||||
}
|
||||
|
||||
/* Length of this frame: */
|
||||
m_fLastFrameDelay = (float)m_pStream->codec.frame_rate_base / m_pStream->codec.frame_rate;
|
||||
m_fLastFrameDelay = (float)m_pStream->codec->time_base.num / m_pStream->codec->time_base.den;
|
||||
m_fLastFrameDelay += m_Frame.repeat_pict * (m_fLastFrameDelay * 0.5f);
|
||||
|
||||
++m_iFrameNumber;
|
||||
@@ -425,17 +455,17 @@ void MovieDecoder_FFMpeg::ConvertToSurface( RageSurface *pSurface ) const
|
||||
pict.linesize[0] = pSurface->pitch;
|
||||
|
||||
avcodec::img_convert( &pict, m_AVTexfmt,
|
||||
(avcodec::AVPicture *) &m_Frame, m_pStream->codec.pix_fmt,
|
||||
m_pStream->codec.width, m_pStream->codec.height );
|
||||
(avcodec::AVPicture *) &m_Frame, m_pStream->codec->pix_fmt,
|
||||
m_pStream->codec->width, m_pStream->codec->height );
|
||||
}
|
||||
|
||||
static avcodec::AVStream *FindVideoStream( avcodec::AVFormatContext *m_fctx )
|
||||
{
|
||||
ASSERT_M( m_fctx->nb_streams <= MAX_STREAMS, ssprintf( "m_fctx->nb_streams = %d", m_fctx->nb_streams) );
|
||||
for( int stream = 0; stream < m_fctx->nb_streams; ++stream )
|
||||
for( unsigned stream = 0; stream < m_fctx->nb_streams; ++stream )
|
||||
{
|
||||
avcodec::AVStream *enc = m_fctx->streams[stream];
|
||||
if( enc->codec.codec_type == avcodec::CODEC_TYPE_VIDEO )
|
||||
if( enc->codec->codec_type == avcodec::CODEC_TYPE_VIDEO )
|
||||
return enc;
|
||||
}
|
||||
return NULL;
|
||||
@@ -459,7 +489,6 @@ static RString averr_ssprintf( int err, const char *fmt, ... )
|
||||
case AVERROR_INVALIDDATA: Error = "invalid data found"; break;
|
||||
case AVERROR_NOMEM: Error = "not enough memory"; break;
|
||||
case AVERROR_NOFMT: Error = "unknown format"; break;
|
||||
case AVERROR_UNKNOWN: Error = "unknown error"; break;
|
||||
default: Error = ssprintf( "unknown error %i", err ); break;
|
||||
}
|
||||
|
||||
@@ -511,6 +540,12 @@ int URLRageFile_write( avcodec::URLContext *h, unsigned char *buf, int size )
|
||||
avcodec::offset_t URLRageFile_seek( avcodec::URLContext *h, avcodec::offset_t pos, int whence )
|
||||
{
|
||||
RageFile *f = (RageFile *) h->priv_data;
|
||||
if( whence == AVSEEK_SIZE )
|
||||
return f->Tell();
|
||||
|
||||
if( whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END )
|
||||
return -1;
|
||||
|
||||
return f->Seek( (int) pos, whence );
|
||||
}
|
||||
|
||||
@@ -555,34 +590,50 @@ RString MovieDecoder_FFMpeg::Open( RString sFile )
|
||||
if( ret < 0 )
|
||||
return ssprintf( averr_ssprintf(ret, "AVCodec (%s): Couldn't find codec parameters", sFile.c_str()) );
|
||||
|
||||
avcodec::AVStream *stream = FindVideoStream( m_fctx );
|
||||
if ( stream == NULL )
|
||||
return ssprintf( "AVCodec (%s): Couldn't find any video streams", sFile.c_str() );
|
||||
avcodec::AVStream *pStream = FindVideoStream( m_fctx );
|
||||
if( pStream == NULL )
|
||||
return "Couldn't find any video streams";
|
||||
m_pStream = pStream;
|
||||
|
||||
if( stream->codec.codec_id == avcodec::CODEC_ID_NONE )
|
||||
return ssprintf( "AVCodec (%s): Unsupported codec %08x", sFile.c_str(), stream->codec.codec_tag );
|
||||
if( m_pStream->codec->codec_id == avcodec::CODEC_ID_NONE )
|
||||
return ssprintf( "Unsupported codec %08x", m_pStream->codec->codec_tag );
|
||||
|
||||
avcodec::AVCodec *codec = avcodec::avcodec_find_decoder( stream->codec.codec_id );
|
||||
if( codec == NULL )
|
||||
return ssprintf( "AVCodec (%s): Couldn't find decoder %i", sFile.c_str(), stream->codec.codec_id );
|
||||
RString sError = OpenCodec();
|
||||
if( !sError.empty() )
|
||||
return ssprintf( "AVCodec (%s): %s", sFile.c_str(), sError.c_str() );
|
||||
|
||||
LOG->Trace("Opening codec %s", codec->name );
|
||||
ret = avcodec::avcodec_open( &stream->codec, codec );
|
||||
if ( ret < 0 )
|
||||
return ssprintf( averr_ssprintf(ret, "AVCodec (%s): Couldn't open codec \"%s\"", sFile.c_str(), codec->name) );
|
||||
m_pStream = stream;
|
||||
LOG->Trace( "Bitrate: %i", m_pStream->codec->bit_rate );
|
||||
LOG->Trace( "Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(m_pStream->codec->pix_fmt) );
|
||||
|
||||
LOG->Trace( "Bitrate: %i", m_pStream->codec.bit_rate );
|
||||
LOG->Trace( "Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(m_pStream->codec.pix_fmt) );
|
||||
return RString();
|
||||
}
|
||||
|
||||
RString MovieDecoder_FFMpeg::OpenCodec()
|
||||
{
|
||||
Init();
|
||||
|
||||
ASSERT( m_pStream );
|
||||
if( m_pStream->codec->codec )
|
||||
avcodec::avcodec_close( m_pStream->codec );
|
||||
|
||||
avcodec::AVCodec *pCodec = avcodec::avcodec_find_decoder( m_pStream->codec->codec_id );
|
||||
if( pCodec == NULL )
|
||||
return ssprintf( "Couldn't find decoder %i", m_pStream->codec->codec_id );
|
||||
|
||||
LOG->Trace("Opening codec %s", pCodec->name );
|
||||
int ret = avcodec::avcodec_open( m_pStream->codec, pCodec );
|
||||
if( ret < 0 )
|
||||
return ssprintf( averr_ssprintf(ret, "Couldn't open codec \"%s\"", pCodec->name) );
|
||||
ASSERT( m_pStream->codec->codec );
|
||||
|
||||
return RString();
|
||||
}
|
||||
|
||||
void MovieDecoder_FFMpeg::Close()
|
||||
{
|
||||
if( m_pStream )
|
||||
if( m_pStream && m_pStream->codec->codec )
|
||||
{
|
||||
avcodec::avcodec_close( &m_pStream->codec );
|
||||
avcodec::avcodec_close( m_pStream->codec );
|
||||
m_pStream = NULL;
|
||||
}
|
||||
|
||||
@@ -595,10 +646,15 @@ void MovieDecoder_FFMpeg::Close()
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
RageSurface *MovieDecoder_FFMpeg::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor )
|
||||
void MovieDecoder_FFMpeg::Rewind()
|
||||
{
|
||||
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_AVTexfmt) );
|
||||
avcodec::av_seek_frame( m_fctx, -1, 0, 0 );
|
||||
OpenCodec();
|
||||
}
|
||||
|
||||
RageSurface *MovieDecoder_FFMpeg::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
|
||||
{
|
||||
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_AVTexfmt), fmtout );
|
||||
}
|
||||
|
||||
MovieTexture_FFMpeg::MovieTexture_FFMpeg( RageTextureID ID ):
|
||||
|
||||
@@ -10,7 +10,7 @@ class RageMovieTextureDriver_FFMpeg: public RageMovieTextureDriver
|
||||
{
|
||||
public:
|
||||
virtual RageMovieTexture *Create( RageTextureID ID, RString &sError );
|
||||
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt );
|
||||
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "RageLog.h"
|
||||
#include "RageSurface.h"
|
||||
#include "RageTextureManager.h"
|
||||
#include "RageTextureRenderTarget.h"
|
||||
#include "RageUtil.h"
|
||||
#include "Sprite.h"
|
||||
|
||||
#if defined(WIN32) && !defined(XBOX)
|
||||
#include "archutils/Win32/ErrorStrings.h"
|
||||
@@ -22,6 +24,8 @@ MovieTexture_Generic::MovieTexture_Generic( RageTextureID ID, MovieDecoder *pDec
|
||||
m_pDecoder = pDecoder;
|
||||
|
||||
m_uTexHandle = 0;
|
||||
m_pRenderTarget = NULL;
|
||||
m_pTextureIntermediate = NULL;
|
||||
m_bLoop = true;
|
||||
m_State = DECODER_QUIT; /* it's quit until we call StartThread */
|
||||
m_pSurface = NULL;
|
||||
@@ -31,6 +35,7 @@ MovieTexture_Generic::MovieTexture_Generic( RageTextureID ID, MovieDecoder *pDec
|
||||
m_fClock = 0;
|
||||
m_bFrameSkipMode = false;
|
||||
m_bThreaded = PREFSMAN->m_bThreadedMovieDecode.Get();
|
||||
m_pSprite = new Sprite;
|
||||
}
|
||||
|
||||
RString MovieTexture_Generic::Init()
|
||||
@@ -75,6 +80,7 @@ MovieTexture_Generic::~MovieTexture_Generic()
|
||||
DestroyTexture();
|
||||
|
||||
delete m_pDecoder;
|
||||
delete m_pSprite;
|
||||
}
|
||||
|
||||
/* Delete the surface and texture. The decoding thread must be stopped, and this
|
||||
@@ -89,12 +95,91 @@ void MovieTexture_Generic::DestroyTexture()
|
||||
DISPLAY->DeleteTexture( m_uTexHandle );
|
||||
m_uTexHandle = 0;
|
||||
}
|
||||
|
||||
delete m_pRenderTarget;
|
||||
m_pRenderTarget = NULL;
|
||||
delete m_pTextureIntermediate;
|
||||
m_pTextureIntermediate = NULL;
|
||||
}
|
||||
|
||||
class RageMovieTexture_Generic_Intermediate : public RageTexture
|
||||
{
|
||||
public:
|
||||
RageMovieTexture_Generic_Intermediate( RageTextureID ID, int iWidth, int iHeight,
|
||||
int iImageWidth, int iImageHeight, int iTextureWidth, int iTextureHeight,
|
||||
RageSurfaceFormat SurfaceFormat, PixelFormat pixfmt ):
|
||||
RageTexture(ID),
|
||||
m_SurfaceFormat( SurfaceFormat )
|
||||
{
|
||||
m_PixFmt = pixfmt;
|
||||
m_iSourceWidth = iWidth;
|
||||
m_iSourceHeight = iHeight;
|
||||
/* int iMaxSize = min( GetID().iMaxSize, DISPLAY->GetMaxTextureSize() );
|
||||
m_iImageWidth = min( m_iSourceWidth, iMaxSize );
|
||||
m_iImageHeight = min( m_iSourceHeight, iMaxSize );
|
||||
m_iTextureWidth = power_of_two( m_iImageWidth );
|
||||
m_iTextureHeight = power_of_two( m_iImageHeight );
|
||||
*/
|
||||
|
||||
m_iImageWidth = iImageWidth;
|
||||
m_iImageHeight = iImageHeight;
|
||||
m_iTextureWidth = iTextureWidth;
|
||||
m_iTextureHeight = iTextureHeight;
|
||||
|
||||
CreateFrameRects();
|
||||
|
||||
m_uTexHandle = 0;
|
||||
CreateTexture();
|
||||
}
|
||||
virtual ~RageMovieTexture_Generic_Intermediate()
|
||||
{
|
||||
if( m_uTexHandle )
|
||||
{
|
||||
DISPLAY->DeleteTexture( m_uTexHandle );
|
||||
m_uTexHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Invalidate() { m_uTexHandle = 0; }
|
||||
virtual void Reload() { }
|
||||
virtual unsigned GetTexHandle() const
|
||||
{
|
||||
return m_uTexHandle;
|
||||
}
|
||||
|
||||
bool IsAMovie() const { return true; }
|
||||
private:
|
||||
void CreateTexture()
|
||||
{
|
||||
if( m_uTexHandle )
|
||||
return;
|
||||
|
||||
RageSurface *pSurface = CreateSurfaceFrom( m_iImageWidth, m_iImageHeight,
|
||||
m_SurfaceFormat.BitsPerPixel,
|
||||
m_SurfaceFormat.Mask[0],
|
||||
m_SurfaceFormat.Mask[1],
|
||||
m_SurfaceFormat.Mask[2],
|
||||
m_SurfaceFormat.Mask[3], NULL, 1 );
|
||||
|
||||
m_uTexHandle = DISPLAY->CreateTexture( m_PixFmt, pSurface, false );
|
||||
delete pSurface;
|
||||
}
|
||||
|
||||
unsigned m_uTexHandle;
|
||||
RageSurfaceFormat m_SurfaceFormat;
|
||||
PixelFormat m_PixFmt;
|
||||
};
|
||||
|
||||
void MovieTexture_Generic::Invalidate()
|
||||
{
|
||||
m_uTexHandle = 0;
|
||||
if( m_pTextureIntermediate != NULL )
|
||||
m_pTextureIntermediate->Invalidate();
|
||||
}
|
||||
|
||||
void MovieTexture_Generic::CreateTexture()
|
||||
{
|
||||
if( m_uTexHandle )
|
||||
if( m_uTexHandle || m_pRenderTarget != NULL )
|
||||
return;
|
||||
|
||||
CHECKPOINT;
|
||||
@@ -117,9 +202,9 @@ void MovieTexture_Generic::CreateTexture()
|
||||
/* Texture dimensions need to be a power of two; jump to the next. */
|
||||
m_iTextureWidth = power_of_two( m_iImageWidth );
|
||||
m_iTextureHeight = power_of_two( m_iImageHeight );
|
||||
|
||||
MovieDecoderPixelFormatYCbCr fmt = PixelFormatYCbCr_Invalid;
|
||||
if( m_pSurface == NULL )
|
||||
m_pSurface = m_pDecoder->CreateCompatibleSurface( m_iTextureWidth, m_iTextureHeight, TEXTUREMAN->GetPrefs().m_iMovieColorDepth == 32 );
|
||||
m_pSurface = m_pDecoder->CreateCompatibleSurface( m_iImageWidth, m_iImageHeight, TEXTUREMAN->GetPrefs().m_iMovieColorDepth == 32, fmt );
|
||||
|
||||
PixelFormat pixfmt = DISPLAY->FindPixelFormat( m_pSurface->format->BitsPerPixel,
|
||||
m_pSurface->format->Mask[0],
|
||||
@@ -156,10 +241,47 @@ void MovieTexture_Generic::CreateTexture()
|
||||
}
|
||||
}
|
||||
|
||||
if( fmt != PixelFormatYCbCr_Invalid )
|
||||
{
|
||||
SAFE_DELETE( m_pTextureIntermediate );
|
||||
m_pSprite->UnloadTexture();
|
||||
|
||||
/* Create the render target. This will receive the final, converted texture. */
|
||||
RenderTargetParam param;
|
||||
param.iWidth = m_iImageWidth;
|
||||
param.iHeight = m_iImageHeight;
|
||||
|
||||
RageTextureID TargetID( GetID() );
|
||||
TargetID.filename += " target";
|
||||
m_pRenderTarget = new RageTextureRenderTarget( TargetID, param );
|
||||
|
||||
/* Create the intermediate texture. This receives the YUV image. */
|
||||
RageTextureID IntermedID( GetID() );
|
||||
IntermedID.filename += " intermediate";
|
||||
|
||||
m_pTextureIntermediate = new RageMovieTexture_Generic_Intermediate( IntermedID,
|
||||
m_pDecoder->GetWidth(), m_pDecoder->GetHeight(),
|
||||
m_pSurface->w, m_pSurface->h,
|
||||
power_of_two(m_pSurface->w), power_of_two(m_pSurface->h),
|
||||
*m_pSurface->format, pixfmt );
|
||||
|
||||
/* Configure the sprite. This blits the intermediate onto the ifnal render target. */
|
||||
m_pSprite->SetHorizAlign( 0 );
|
||||
m_pSprite->SetVertAlign( 0 );
|
||||
|
||||
/* Hack: Sprite wants to take ownership of the texture, and will decrement the refcount
|
||||
* when it unloads the texture. Normally we'd make a "copy", but we can't access
|
||||
* RageTextureManager from here. Just increment the refcount. */
|
||||
++m_pTextureIntermediate->m_iRefCount;
|
||||
m_pSprite->SetTexture( m_pTextureIntermediate );
|
||||
m_pSprite->SetEffectMode( GetEffectMode(fmt) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, m_pSurface, false );
|
||||
}
|
||||
|
||||
|
||||
/* Handle decoding for a frame. Return true if a frame was decoded, false if not
|
||||
* (due to quit, error, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
|
||||
bool MovieTexture_Generic::DecodeFrame()
|
||||
@@ -187,10 +309,7 @@ bool MovieTexture_Generic::DecodeFrame()
|
||||
float fDelay = m_pDecoder->GetFrameDuration();
|
||||
|
||||
/* Restart. */
|
||||
m_pDecoder->Close();
|
||||
RString sError = m_pDecoder->Open( GetID().filename );
|
||||
if( sError != "" )
|
||||
RageException::Throw( "Error rewinding stream \"%s\": %s", GetID().filename.c_str(), sError.c_str() );
|
||||
m_pDecoder->Rewind();
|
||||
|
||||
m_fClock = -fDelay;
|
||||
}
|
||||
@@ -385,6 +504,7 @@ void MovieTexture_Generic::Update(float fDeltaTime)
|
||||
LOG->MapLog( "movie_looping", "MovieTexture_Generic::Update looping" );
|
||||
}
|
||||
|
||||
|
||||
/* Call from the main thread when m_ImageWaiting == FRAME_WAITING to update the
|
||||
* texture. Sets FRAME_NONE. Does not signal m_BufferFinished. */
|
||||
void MovieTexture_Generic::UpdateFrame()
|
||||
@@ -394,17 +514,47 @@ void MovieTexture_Generic::UpdateFrame()
|
||||
/* Just in case we were invalidated: */
|
||||
CreateTexture();
|
||||
|
||||
CHECKPOINT;
|
||||
DISPLAY->UpdateTexture(
|
||||
m_uTexHandle,
|
||||
m_pSurface,
|
||||
0, 0,
|
||||
m_iImageWidth, m_iImageHeight );
|
||||
CHECKPOINT;
|
||||
if( m_pRenderTarget != NULL )
|
||||
{
|
||||
CHECKPOINT;
|
||||
|
||||
DISPLAY->UpdateTexture(
|
||||
m_pTextureIntermediate->GetTexHandle(),
|
||||
m_pSurface,
|
||||
0, 0,
|
||||
m_pSurface->w, m_pSurface->h );
|
||||
CHECKPOINT;
|
||||
|
||||
m_pRenderTarget->BeginRenderingTo( false );
|
||||
m_pSprite->Draw();
|
||||
m_pRenderTarget->FinishRenderingTo();
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECKPOINT;
|
||||
DISPLAY->UpdateTexture(
|
||||
m_uTexHandle,
|
||||
m_pSurface,
|
||||
0, 0,
|
||||
m_iImageWidth, m_iImageHeight );
|
||||
CHECKPOINT;
|
||||
}
|
||||
|
||||
m_ImageWaiting = FRAME_NONE;
|
||||
}
|
||||
|
||||
static EffectMode EffectModes[] =
|
||||
{
|
||||
EffectMode_YUYV422,
|
||||
};
|
||||
COMPILE_ASSERT( ARRAYSIZE(EffectModes) == NUM_PixelFormatYCbCr );
|
||||
|
||||
EffectMode MovieTexture_Generic::GetEffectMode( MovieDecoderPixelFormatYCbCr fmt )
|
||||
{
|
||||
ASSERT( fmt != PixelFormatYCbCr_Invalid );
|
||||
return EffectModes[fmt];
|
||||
}
|
||||
|
||||
void MovieTexture_Generic::Reload()
|
||||
{
|
||||
}
|
||||
@@ -470,6 +620,14 @@ void MovieTexture_Generic::DecodeSeconds( float fSeconds )
|
||||
Update(0);
|
||||
}
|
||||
|
||||
unsigned MovieTexture_Generic::GetTexHandle() const
|
||||
{
|
||||
if( m_pRenderTarget != NULL )
|
||||
return m_pRenderTarget->GetTexHandle();
|
||||
|
||||
return m_uTexHandle;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2005 Glenn Maynard
|
||||
* All rights reserved.
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
|
||||
class FFMpeg_Helper;
|
||||
struct RageSurface;
|
||||
class RageTextureRenderTarget;
|
||||
class Sprite;
|
||||
|
||||
enum MovieDecoderPixelFormatYCbCr
|
||||
{
|
||||
PixelFormatYCbCr_YUYV422,
|
||||
NUM_PixelFormatYCbCr,
|
||||
PixelFormatYCbCr_Invalid
|
||||
};
|
||||
|
||||
|
||||
class MovieDecoder
|
||||
{
|
||||
@@ -14,6 +24,7 @@ public:
|
||||
|
||||
virtual RString Open( RString sFile ) = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual void Rewind() = 0;
|
||||
|
||||
/*
|
||||
* Decode a frame. Return 1 on success, 0 on EOF, -1 on fatal error.
|
||||
@@ -35,12 +46,18 @@ public:
|
||||
/* Return the aspect ratio of a pixel in the image. Usually 1. */
|
||||
virtual float GetSourceAspectRatio() const { return 1.0f; }
|
||||
|
||||
/* Create a surface acceptable to pass to GetFrame. This should be
|
||||
/*
|
||||
* Create a surface acceptable to pass to GetFrame. This should be
|
||||
* a surface which is realtime-compatible with DISPLAY, and should
|
||||
* attempt to obey bPreferHighColor. 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. */
|
||||
virtual RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor ) = 0;
|
||||
* with limited texture resolution, may be smaller.
|
||||
*
|
||||
* If DISPLAY supports the EffectMode_YUYV422 blend mode, this may be
|
||||
* a packed-pixel YUV surface. UYVY maps to RGBA, respectively. If
|
||||
* used, set fmtout.
|
||||
*/
|
||||
virtual RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout ) = 0;
|
||||
|
||||
/* The following functions return information about the current frame,
|
||||
* decoded by the last successful call to GetFrame, and will never be
|
||||
@@ -63,7 +80,7 @@ public:
|
||||
RString Init();
|
||||
|
||||
/* only called by RageTextureManager::InvalidateTextures */
|
||||
void Invalidate() { m_uTexHandle = 0; }
|
||||
void Invalidate();
|
||||
void Update( float fDeltaTime );
|
||||
|
||||
virtual void Reload();
|
||||
@@ -72,7 +89,9 @@ public:
|
||||
virtual void DecodeSeconds( float fSeconds );
|
||||
virtual void SetPlaybackRate( float fRate ) { m_fRate = fRate; }
|
||||
void SetLooping( bool bLooping=true ) { m_bLoop = bLooping; }
|
||||
unsigned GetTexHandle() const { return m_uTexHandle; }
|
||||
unsigned GetTexHandle() const;
|
||||
|
||||
static EffectMode GetEffectMode( MovieDecoderPixelFormatYCbCr fmt );
|
||||
|
||||
private:
|
||||
MovieDecoder *m_pDecoder;
|
||||
@@ -101,6 +120,9 @@ private:
|
||||
enum State { DECODER_QUIT, DECODER_RUNNING } m_State;
|
||||
|
||||
unsigned m_uTexHandle;
|
||||
RageTextureRenderTarget *m_pRenderTarget;
|
||||
RageTexture *m_pTextureIntermediate;
|
||||
Sprite *m_pSprite;
|
||||
|
||||
RageSurface *m_pSurface;
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@ public:
|
||||
|
||||
RString Open( RString sFile );
|
||||
void Close();
|
||||
void Rewind() { } // XXX
|
||||
|
||||
int GetFrame( RageSurface *pOut, float fTargetTime );
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor );
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
|
||||
|
||||
int GetWidth() const { return m_TheoraInfo.frame_width; }
|
||||
int GetHeight() const { return m_TheoraInfo.frame_height; }
|
||||
@@ -265,9 +266,9 @@ int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
|
||||
}
|
||||
}
|
||||
|
||||
RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor )
|
||||
RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
|
||||
{
|
||||
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt) );
|
||||
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt), fmtout );
|
||||
}
|
||||
|
||||
void MovieDecoder_Theora::ConvertToSurface( RageSurface *pSurface ) const
|
||||
@@ -347,7 +348,7 @@ public:
|
||||
void Close();
|
||||
|
||||
int GetFrame( RageSurface *pOut, float fTargetTime );
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor );
|
||||
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
|
||||
|
||||
int GetWidth() const { return m_TheoraInfo.frame_width; }
|
||||
int GetHeight() const { return m_TheoraInfo.frame_height; }
|
||||
@@ -602,7 +603,7 @@ int MovieDecoder_Theora::GetFrame( RageSurface *pOut, float fTargetTime )
|
||||
}
|
||||
}
|
||||
|
||||
RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor )
|
||||
RageSurface *MovieDecoder_Theora::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
|
||||
{
|
||||
return MovieTexture_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_OutputPixFmt) );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user