diff --git a/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp b/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp index 410d224df7..fd8ab36a7b 100644 --- a/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp +++ b/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp @@ -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(&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(&m_AVTexfmt), fmtout ); } MovieTexture_FFMpeg::MovieTexture_FFMpeg( RageTextureID ID ): diff --git a/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.h b/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.h index cf6f115c3a..d27725bf76 100644 --- a/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.h +++ b/stepmania/src/arch/MovieTexture/MovieTexture_FFMpeg.h @@ -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 diff --git a/stepmania/src/arch/MovieTexture/MovieTexture_Generic.cpp b/stepmania/src/arch/MovieTexture/MovieTexture_Generic.cpp index 01775f41d1..86ceccf2a8 100644 --- a/stepmania/src/arch/MovieTexture/MovieTexture_Generic.cpp +++ b/stepmania/src/arch/MovieTexture/MovieTexture_Generic.cpp @@ -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. diff --git a/stepmania/src/arch/MovieTexture/MovieTexture_Generic.h b/stepmania/src/arch/MovieTexture/MovieTexture_Generic.h index 7032a20ca7..87b5048dc3 100644 --- a/stepmania/src/arch/MovieTexture/MovieTexture_Generic.h +++ b/stepmania/src/arch/MovieTexture/MovieTexture_Generic.h @@ -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; diff --git a/stepmania/src/arch/MovieTexture/MovieTexture_Theora.cpp b/stepmania/src/arch/MovieTexture/MovieTexture_Theora.cpp index f8859629c6..b633f2fb73 100644 --- a/stepmania/src/arch/MovieTexture/MovieTexture_Theora.cpp +++ b/stepmania/src/arch/MovieTexture/MovieTexture_Theora.cpp @@ -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(&m_OutputPixFmt) ); + return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue(&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(&m_OutputPixFmt) ); }