I took the theora plugin for ogre and adapt it to my need, I delete all vorbis function, took only the movieclip / driver / seekUtility, and now my driver does not blit the image to memory, but construct a unsigned char* (pixels array) that i use late to texture something.
So I did not change the algo for decodage, but I got the skin of people on video a bit blue :-s
And when I use the seeking, I have like 3 seconds where I have only 1 fps, after the decodage works fine.
I am totally noob in theora so I have problem to find why people are blue...
#include "OMKTheoraDriver.h"
#include "OMKTracer.h"
#include "OgreTimer.h"
#include "OgreHardwarePixelBuffer.h"
//Defines
#define MAX( a, b ) ((a > b) ? a : b)
#define MIN( a, b ) ((a < b) ? a : b)
#define CLIP_RGB_COLOR( rgb_color_test, rgb_char_buffer ) \
rgb_char_buffer = MAX( MIN(rgb_color_test, 255), 0 )
using namespace OMK::Theora ;
//Handle static member vars
unsigned int TheoraDriver::YTable[ 256 ];
unsigned int TheoraDriver::BUTable[ 256 ];
unsigned int TheoraDriver::GUTable[ 256 ];
unsigned int TheoraDriver::GVTable[ 256 ];
unsigned int TheoraDriver::RVTable[ 256 ];
//------------------------------------------------------------------//
TheoraDriver::TheoraDriver() :
m_RGBBitmap(0),
m_Width(0),
m_Height(0),
newTextureSize(0)
{
}
//------------------------------------------------------------------//
TheoraDriver::~TheoraDriver()
{
delete [] m_RGBBitmap;
m_RGBBitmap = 0;
}
//------------------------------------------------------------------//
void TheoraDriver::createPixelArray(
int width, int height )
{
createCoefTables( ) ;
m_Width = width;
m_Height = height;
newTextureSize = width * height;
Ogre::PixelFormat pFormat;
m_BytesPerPixel = 3;
newTextureSize *=3; //RGB Texture
pFormat = Ogre::PF_B8G8R8; //PF_R8G8B8;
//Create our member bitmap memory
m_RGBBitmap = new unsigned char[ newTextureSize ] ;
memset( m_RGBBitmap, 0, newTextureSize );
}
//----------------------------------------------------------------------//
void TheoraDriver::renderToTexture( yuv_buffer *buffer, unsigned char **pixelForTexture )
{
*(pixelForTexture) = decodeYUVtoTexture( buffer ) ;
}
//----------------------------------------------------------------------//
unsigned char* TheoraDriver::decodeYUVtoTexture( yuv_buffer *yuv )
{
//Convert 4:2:0 YUV YCrCb to an RGB24 Bitmap
//convenient pointers
unsigned char *dstBitmap = m_RGBBitmap;
unsigned char *dstBitmapOffset = m_RGBBitmap + (m_BytesPerPixel * m_Width);
unsigned char *ySrc = (unsigned char*)yuv->y,
*uSrc = (unsigned char*)yuv->u,
*vSrc = (unsigned char*)yuv->v,
*ySrc2 = ySrc + yuv->y_stride;
//Calculate buffer offsets
unsigned int dstOff = m_Width * m_BytesPerPixel;//( m_Width*6 ) - ( yuv->y_width*3 );
int yOff = (yuv->y_stride * 2) - yuv->y_width;
//Check if upside down, if so, reverse buffers and offsets
if ( yuv->y_height < 0 )
{
yuv->y_height = -yuv->y_height;
ySrc += (yuv->y_height - 1) * yuv->y_stride;
uSrc += ((yuv->y_height / 2) - 1) * yuv->uv_stride;
vSrc += ((yuv->y_height / 2) - 1) * yuv->uv_stride;
ySrc2 = ySrc - yuv->y_stride;
yOff = -yuv->y_width - ( yuv->y_stride * 2 );
yuv->uv_stride = -yuv->uv_stride;
}
//Cut width and height in half (uv field is only half y field)
yuv->y_height = yuv->y_height >> 1;
yuv->y_width = yuv->y_width >> 1;
//Convientient temp vars
signed int r, g, b, u, v, bU, gUV, rV, rgbY;
int x;
//Loop does four blocks per iteration (2 rows, 2 pixels at a time)
for (int y = yuv->y_height; y > 0; --y)
{
for (x = 0; x < yuv->y_width; ++x)
{
//Get uv pointers for row
u = uSrc
v = vSrc
//get corresponding lookup values
rgbY= YTable[*ySrc];
rV = RVTable[v];
gUV = GUTable
+ GVTable[v];
bU = BUTable;
++ySrc;
//scale down - brings are values back into the 8 bits of a byte
r = (rgbY + rV ) >> 13;
g = (rgbY - gUV) >> 13;
b = (rgbY + bU ) >> 13;
//Clip to RGB values (255 0)
CLIP_RGB_COLOR( r, dstBitmap[0] );
CLIP_RGB_COLOR( g, dstBitmap[1] );
CLIP_RGB_COLOR( b, dstBitmap[2] );
int test = MAX( MIN(r, 255), 0 ) ;
//And repeat for other pixels (note, y is unique for each
//pixel, while uv are not)
rgbY = YTable[*ySrc];
r = (rgbY + rV) >> 13;
g = (rgbY - gUV) >> 13;
b = (rgbY + bU) >> 13;
CLIP_RGB_COLOR( r, dstBitmap[m_BytesPerPixel] );
CLIP_RGB_COLOR( g, dstBitmap[m_BytesPerPixel+1] );
CLIP_RGB_COLOR( b, dstBitmap[m_BytesPerPixel+2] );
++ySrc;
rgbY = YTable[*ySrc2];
r = (rgbY + rV) >> 13;
g = (rgbY - gUV) >> 13;
b = (rgbY + bU) >> 13;
CLIP_RGB_COLOR( r, dstBitmapOffset[0] );
CLIP_RGB_COLOR( g, dstBitmapOffset[1] );
CLIP_RGB_COLOR( b, dstBitmapOffset[2] );
++ySrc2;
rgbY = YTable[*ySrc2];
r = (rgbY + rV) >> 13;
g = (rgbY - gUV) >> 13;
b = (rgbY + bU) >> 13;
CLIP_RGB_COLOR( r, dstBitmapOffset[m_BytesPerPixel] );
CLIP_RGB_COLOR( g, dstBitmapOffset[m_BytesPerPixel+1] );
CLIP_RGB_COLOR( b, dstBitmapOffset[m_BytesPerPixel+2] );
++ySrc2;
//Advance inner loop offsets
dstBitmap += m_BytesPerPixel << 1;
dstBitmapOffset += m_BytesPerPixel << 1;
} // end for x
//Advance destination pointers by offsets
dstBitmap += dstOff;
dstBitmapOffset += dstOff;
ySrc += yOff;
ySrc2 += yOff;
uSrc += yuv->uv_stride;
vSrc += yuv->uv_stride;
} //end for y
return m_RGBBitmap ;
}
//----------------------------------------------------------------------//
void TheoraDriver::createCoefTables()
{
//used to bring the table into the high side (scale up) so we
//can maintain high precision and not use floats (FIXED POINT)
int scale = 1L << 13,
temp;
for ( unsigned int i = 0; i < 256; i++ )
{
temp = i - 128;
YTable = (unsigned int)((1.164 * scale + 0.5) * (i - 16)); //Calc Y component
RVTable = (unsigned int)((1.596 * scale + 0.5) * temp); //Calc R component
GUTable = (unsigned int)((0.391 * scale + 0.5) * temp); //Calc G u & v components
GVTable = (unsigned int)((0.813 * scale + 0.5) * temp);
BUTable = (unsigned int)((2.018 * scale + 0.5) * temp); //Calc B component
}
}
#include "OgreResourceGroupManager.h"
#include "OgreStringConverter.h"
#include "OgreTimer.h"
#include "OMKTheoraMovieClip.h"
#include "OMKTheoraDriver.h"
#include "OMKTracer.h"
using namespace OMK::Theora ;
//--------------------------------------------------------------------//
TheoraMovieClip::TheoraMovieClip() :
//pt::thread( false ),
mPlayMode(TextureEffectPause),
m_FrameNum(0),
m_FramesDropped(0),
m_lastFrameTime(0.0f),
m_EndOfVideo(false),
m_EndOfFile(false),
m_theora_streams(0),
m_VideoFrameReady(false),
videobuf_time(0.0f),
pixels( 0 ),
readyToBeTextured(false),
m_Timer(0),
mMovieLength(0)
{
//Ensure all structures get cleared out. Already bit me in the arse

memset( &m_oggSyncState, 0, sizeof( ogg_sync_state ) );
memset( &m_oggPage, 0, sizeof( ogg_page ) );
memset( &m_theoraStreamState, 0, sizeof( ogg_stream_state ) );
memset( &m_theoraInfo, 0, sizeof( theora_info ) );
memset( &m_theoraComment, 0, sizeof( theora_comment ) );
memset( &m_theoraState, 0, sizeof( theora_state ) );
mDoSeek = false ;
_seeker = 0 ;
}
//--------------------------------------------------------------------//
TheoraMovieClip::~TheoraMovieClip()
{
changePlayMode( TextureEffectPause );
if( m_Timer )
delete m_Timer;
m_Timer = 0;
close();
}
bool TheoraMovieClip::grabANewImage( unsigned char** resPixels, int &height, int &width )
{
bool res = false;
blitFrameCheck ( ) ;
if ( readyToBeTextured )
{
res = true ;
*(resPixels) = pixels ;
height = m_videoInterface.getHeight( ) ;
width = m_videoInterface.getWidth( ) ;
readyToBeTextured = false ;
}
return res;
}
//--------------------------------------------------------------------//
void TheoraMovieClip::createMovieClip(
const std::string &sMovieName, const std::string &sGroupName )
{
mMovieName = sMovieName;
load( mMovieName, sGroupName );
_seeker = new TheoraSeekUtility( m_oggFile, &m_oggSyncState,
&m_theoraStreamState, &m_theoraInfo, &m_theoraState ) ;
if( _seeker )
{
mMovieLength = _seeker->buildTheoraSeekMap();
}
// Attach our video to a texture unit
m_videoInterface.createPixelArray( m_theoraInfo.width,
m_theoraInfo.height );
changePlayMode( TextureEffectPause ) ;
}
//--------------------------------------------------------------------//
void TheoraMovieClip::load( const std::string& filename,
const std::string& groupName )
{
//ensure a file is not already open
/*if( !(m_oggFile.isNull()) )
OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "File name already loded! " + filename, "TheoraMovieClip::load" );*/
OMASSERTM(m_oggFile.isNull() , "File name already loaded! " ) ;
m_EndOfFile = false;
m_oggFile = Ogre::ResourceGroupManager::getSingleton().openResource( filename, groupName );
initTheoraLayer( ) ;
parseTheoraHeaders( ) ;
activateTheoraCodecs( ) ;
}
//--------------------------------------------------------------------//
void TheoraMovieClip::initTheoraLayer( )
{
//start up Ogg stream synchronization layer
ogg_sync_init( &m_oggSyncState );
//init supporting Theora structures needed in header parsing
theora_comment_init(&m_theoraComment);
theora_info_init(&m_theoraInfo);
}
//--------------------------------------------------------------------//
void TheoraMovieClip::parseTheoraHeaders( )
{
ogg_packet tempOggPacket;
bool NotDone = true;
while( NotDone )
{
char *buffer = ogg_sync_buffer( &m_oggSyncState, 4096);
int bytesRead = m_oggFile->read( buffer, 4096 );
ogg_sync_wrote( &m_oggSyncState, bytesRead );
if( bytesRead == 0 )
break;
while( ogg_sync_pageout( &m_oggSyncState, &m_oggPage ) > 0 )
{
ogg_stream_state OggStateTest;
//is this an initial header? If not, stop
if( !ogg_page_bos( &m_oggPage ) )
{
//This is done blindly, because stream only accept them selfs
if(m_theora_streams)
ogg_stream_pagein( &m_theoraStreamState, &m_oggPage );
NotDone = false;
break;
}
ogg_stream_init( &OggStateTest, ogg_page_serialno( &m_oggPage ) );
ogg_stream_pagein( &OggStateTest, &m_oggPage );
ogg_stream_packetout( &OggStateTest, &tempOggPacket );
//identify the codec
if( !m_theora_streams &&
theora_decode_header( &m_theoraInfo, &m_theoraComment, &tempOggPacket) >=0 )
{
//This is the Theora Header
memcpy( &m_theoraStreamState, &OggStateTest, sizeof(OggStateTest));
m_theora_streams = 1;
}
else
{
//Hmm. I guess it's not a header we support, so erase it
ogg_stream_clear(&OggStateTest);
}
} //end while ogg_sync_pageout
} //end while notdone
while( (m_theora_streams && (m_theora_streams < 3)) )
{
//Check 2nd'dary headers... Theora First
int iSuccess;
while( m_theora_streams &&
( m_theora_streams < 3) &&
( iSuccess = ogg_stream_packetout( &m_theoraStreamState, &tempOggPacket)) )
{
/* if( iSuccess < 0 )
OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "Error parsing Theora stream headers.",
"TheoraMovieClip::parseVorbisTheoraHeaders" );
if( theora_decode_header(&m_theoraInfo, &m_theoraComment, &tempOggPacket) )
OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "invalid stream",
"TheoraMovieClip::parseVorbisTheoraHeaders ");
*/
OMASSERTM( iSuccess != 0 , "Error parsing Theora stream headers." ) ;
OMASSERTM( !theora_decode_header(&m_theoraInfo, &m_theoraComment, &tempOggPacket) , "invalid stream" ) ;
m_theora_streams++;
} //end while looking for more theora headers
//Not finished with Headers, get some more file data
if( ogg_sync_pageout( &m_oggSyncState, &m_oggPage ) > 0 )
{
if(m_theora_streams)
ogg_stream_pagein( &m_theoraStreamState, &m_oggPage );
}
else
{
char *buffer = ogg_sync_buffer( &m_oggSyncState, 4096);
int bytesRead = m_oggFile->read( buffer, 4096 );
ogg_sync_wrote( &m_oggSyncState, bytesRead );
/*if( bytesRead == 0 )
OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "End of file found prematurely",
"TheoraMovieClip::parseVorbisTheoraHeaders " );*/
OMASSERTM( bytesRead != 0 , "End of file found prematurely" ) ;
}
} //end while looking for all headers
std::string temp = Ogre::StringConverter::toString( m_theora_streams );
OMTRACEID("Theora" , " Theora Headers : " << temp);
}
//--------------------------------------------------------------------//
void TheoraMovieClip::activateTheoraCodecs( )
{
if( m_theora_streams )
theora_decode_init( &m_theoraState, &m_theoraInfo );
}
//--------------------------------------------------------------------//
void TheoraMovieClip::changePlayMode( eTexturePlayMode eMode )
{
//TextureEffectPause = 0, //! Video starts out paused
//TextureEffectPlay_ASAP = 1, //! Video starts playing as soon as posible
//TextureEffectPlay_Looping = 2 //! Video Plays Instantly && Loops
if( mPlayMode == eMode )
return;
//If current mode is paused than - sent mode must either be loop, or playASAP
if( mPlayMode == TextureEffectPause )
{
mPlayMode = eMode;
}
else if( eMode == TextureEffectPause )
{
mPlayMode = eMode;
}
}
//--------------------------------------------------------------------//
void TheoraMovieClip::blitFrameCheck()
{
static int lastCallThisFunction = 0.0f ;
static int timeJustHere = 0.0f ;
timeJustHere = getMSTime( ) ;
if ( mPlayMode != TextureEffectPause && ( timeJustHere - lastCallThisFunction > 40 ) )
// 40 ms is the time for 24 images secondes, so it's enough
{
getNewTheoraData( ) ;
if( m_VideoFrameReady )
{
lastCallThisFunction = timeJustHere ;
pixels = 0 ;
yuv_buffer yuv;
theora_decode_YUVout( &m_theoraState, &yuv ) ;
m_videoInterface.renderToTexture( &yuv, &pixels ) ;
readyToBeTextured = true ;
m_VideoFrameReady = false ;
m_lastFrameTime = getMovieTime( ) ;
}
}
}
void TheoraMovieClip::getNewTheoraData( )
{
int bytesRead = 1;
if( mDoSeek && _seeker )
{
float seekedTo = _seeker->doSeek( mSeekTime );
mDoSeek = false;
m_EndOfFile = false;
}
if( !m_VideoFrameReady && m_theora_streams )
decodeTheora();
//Buffer data into Ogg Pages
if( bytesRead > 0 )
{
char *buffer = ogg_sync_buffer( &m_oggSyncState, 4096);
bytesRead = m_oggFile->read( buffer, 4096 );
ogg_sync_wrote( &m_oggSyncState, bytesRead );
while ( ogg_sync_pageout( &m_oggSyncState, &m_oggPage ) > 0 )
{
if(m_theora_streams)
ogg_stream_pagein( &m_theoraStreamState, &m_oggPage );
}
}
else
{
m_EndOfFile = true;
}
}
//--------------------------------------------------------------------//
void TheoraMovieClip::decodeTheora()
{
ogg_packet opTheora;
for(;

{
//get one video packet...
if( ogg_stream_packetout( &m_theoraStreamState, &opTheora) > 0 )
{
theora_decode_packetin( &m_theoraState, &opTheora );
videobuf_time = theora_granule_time( &m_theoraState, m_theoraState.granulepos );
//update the frame counter
m_FrameNum++;
//check if this frame time has not passed yet.
//If the frame is late we need to decode additonal
//ones and keep looping, since theora at this stage
//needs to decode all frames
//gran time & our time is in seconds
float nowTime = getMovieTime();
float delay = videobuf_time - nowTime;
if( delay >= 0.0f )
{
//got a good frame, within time window
m_VideoFrameReady = true;
break;
}
else if( nowTime - m_lastFrameTime >= 1.0f )
{
//display at least one frame per second, regardless
m_VideoFrameReady = true;
break;
}
else //frame is dropped
{
m_FramesDropped++;
}
}
else
{
if( m_EndOfFile )
{
m_EndOfVideo = true;
}
//need more data
break;
}
}
}
//--------------------------------------------------------------------//
float TheoraMovieClip::getMovieTime()
{
if ( m_Timer )
{
return m_Timer->getMilliseconds() / 1000.0f;
}
else
{
//Initialize timer variable first time up
m_Timer = new Ogre::Timer();
m_Timer->reset();
return 0.0f;
}
}
//--------------------------------------------------------------------//
float TheoraMovieClip::getMSTime()
{
if ( m_Timer )
{
return m_Timer->getMilliseconds();
}
else
{
//Initialize timer variable first time up
m_Timer = new Ogre::Timer();
m_Timer->reset();
return 0.0f;
}
}
//--------------------------------------------------------------------//
void TheoraMovieClip::close()
{
m_EndOfFile = false ;
delete _seeker ;
_seeker = 0 ;
m_oggFile.setNull();
if(m_theora_streams)
{
ogg_stream_clear( &m_theoraStreamState );
theora_clear( &m_theoraState );
theora_comment_clear( &m_theoraComment );
theora_info_clear( &m_theoraInfo );
m_theora_streams = 0;
}
ogg_sync_clear( &m_oggSyncState );
}
void TheoraMovieClip::seekToTime( float seconds )
{
//No point seeking to the end.. were already there
if( seconds >= mMovieLength || seconds < 0.0f )
return;
//If seeking is not enabled, just return
if( !_seeker )
return;
//Sets seeking flag on for thread to know & carry out later
mDoSeek = true;
mSeekTime = seconds;
}
Since I did not change the decode function, I don't understand why I have this problem with color... The seeking is smooth for you ?