Game Development Community

dev|Pro Game Development Curriculum

1.4 Theora Support In TSE

by Jon Wilsdon · 04/02/2006 (10:28 am) · 26 comments

I assume you have gotten TGE 1.4 and are using Visual Studio. This format should be familiar to you if you got TGE 1.4 theora support to work in 1.3 since this shares quite a few steps with that resource.

Copy over the theora stuff from 1.4.

copy 1.4/engine/core/theoraPlayer.cc to TSE/engine/core/theoraPlayer.cpp (extension change made for consistency)
copy 1.4/engine/core/theoraPlayer.h to TSE/engine/core/theoraPlayer.h

create a new folder in TSE/engine/gui/ that is called shiny/

copy 1.4/engine/gui/shiny/guiTheoraCtrl.cc to TSE/engine/gui/shiny/guiTheoraCtrl.cpp (extension change made for consistency)
copy 1.4/engine/gui/shiny/guiTheoraCtrl.h to TSE/engine/gui/shiny/guiTheoraCtrl.h

copy 1.4/engine/audio/oggMixedStreamSource.h to TSE/engine/audio/oggMixedStreamSource.cpp (extension change made for consistency)
copy 1.4/engine/audio/oggMixedStreamSource.h to TSE/engine/audio/oggMixedStreamSource.h

copy 1.4/engine/core/dataChunker.h to TSE/engine/core/dataChunker.h (this replaces an existing file)

copy the whole 1.4/lib/xiph directory to TSE/lib/xiph (you probably want to remove the CVS directories after copying)

to clean things up that won't be used, delete the TSE/lib/xiph/linux/ and TSE/lib/xiph/macosx/ directories.

Set up Visual Studio

load up the solution file in visual studio

in the solution:
add core/theoraPlayer.cpp
add core/theoraPlayer.h
add a new folder in gui/ called shiny/
add gui/shiny/guiTheoraCtrl.cpp
add gui/shiny/guiTheoraCtrl.h
add audio/oggMixedStreamSource.cpp
add audio/oggMixedStreamSource.h

In your Solution Explorer in Visual Studio right click on Torque Shader Engine and click Properties. Select Linker, then Input on the left and in the Additional Dependencies on the right, change ../lib/vorbis/win32/ogg_static.lib to ../lib/xiph/win32/ogg_static.lib and ../lib/vorbis/win32/vorbis_static_mt.lib to ../lib/xiph/win32/vorbis_static_mt.lib and then add ../lib/xiph/win32/theora_static.lib

Then click on C/C++ on the left and in the Additional Include Directories change ../lib/vorbis/include to ../lib/xiph/include

Make Code Changes

change the includes to be gfx calls

in engine/core/theoraPlayer.h change
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif

#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif

... to ...

#ifndef _GTEXMANAGER_H_
#include "gfx/gfxTextureHandle.h"
#endif

#ifndef _GBITMAP_H_
#include "gfx/gBitmap.h"
#endif

in engine/core/theoraPlayer.h after all the #ifndef's add
#ifndef _PLATFORMMUTEX_H_
#include "platform/platformMutex.h"
#endif

update to the new texture object

in engine/core/theoraPlayer.h change
operator TextureObject*()
   {
      return (TextureObject*)*mTextureHandle;
   }

... to ...

   operator GFXTextureObject*()
   {
      return *mTextureHandle;
   }

no need to include OpenGL functions

in engine/core/theoraPlayer.h change
U32  getGLName();

... to ...

// U32  getGLName();

in engine/core/theoraPlayer.h change
TextureHandle*	mTextureHandle;

... to ...

   GFXTexHandle*	mTextureHandle;

update rendering calls with a new texture profile

in engine/core/theoraPlayer.cpp {createVideoBuffers()} change
const GBitmap *bmp = new GBitmap(
                        getMax((U32)mTheoraInfo.frame_width, (U32)mTheoraInfo.width),
                        getMax((U32)mTheoraInfo.frame_height, (U32)mTheoraInfo.height),
                        false, GBitmap::RGB);

   mTextureHandle = new TextureHandle(NULL, bmp, true);

... to ...

   GBitmap *bmp = new GBitmap(
                        getMax(mTheoraInfo.frame_width, mTheoraInfo.width),
                        getMax(mTheoraInfo.frame_height, mTheoraInfo.height),
                        false, GFXFormatR8G8B8);

   mTextureHandle = new GFXTexHandle(bmp, &GFXTheoraProfile, true);

use getAddress to be consistent

in engine/core/theoraPlayer.cpp {drawFrame()} change
U8 *dst1 = dst0 + bmp->getWidth() * bmp->bytesPerPixel;

... to ...

   U8 *dst1 = bmp->getAddress(0, 1);

we need to make the theora player write out as bgr instead of rgb

in engine/core/theoraPlayer.cpp {drawFrame()} change
// pixel 0x0
         *dst0++ = sClamp[sAdjY[*pY0] + sAdjCrr[*pV]];
         *dst0++ = sClamp[sAdjY[*pY0] - G];
         *dst0++ = sClamp[sAdjY[*pY0++] + sAdjCbb[*pU]];

         // pixel 1x0
         *dst0++ = sClamp[sAdjY[*pY0] + sAdjCrr[*pV]];
         *dst0++ = sClamp[sAdjY[*pY0] - G];
         *dst0++ = sClamp[sAdjY[*pY0++] + sAdjCbb[*pU]];

         // pixel 0x1
         *dst1++ = sClamp[sAdjY[*pY1] + sAdjCrr[*pV]];
         *dst1++ = sClamp[sAdjY[*pY1] - G];
         *dst1++ = sClamp[sAdjY[*pY1++] + sAdjCbb[*pU]];

         // pixel 1x1
         *dst1++ = sClamp[sAdjY[*pY1] + sAdjCrr[*pV++]];
         *dst1++ = sClamp[sAdjY[*pY1] - G];
         *dst1++ = sClamp[sAdjY[*pY1++] + sAdjCbb[*pU++]];

... to ...

         // pixel 0x0
         *dst0++ = sClamp[sAdjY[*pY0++] + sAdjCbb[*pU]];
         *dst0++ = sClamp[sAdjY[*pY0] - G];
         *dst0++ = sClamp[sAdjY[*pY0] + sAdjCrr[*pV]];

         // pixel 1x0
		 *dst0++ = sClamp[sAdjY[*pY0++] + sAdjCbb[*pU]];
         *dst0++ = sClamp[sAdjY[*pY0] - G];
		 *dst0++ = sClamp[sAdjY[*pY0] + sAdjCrr[*pV]];

         // pixel 0x1
         *dst1++ = sClamp[sAdjY[*pY1++] + sAdjCbb[*pU]];
         *dst1++ = sClamp[sAdjY[*pY1] - G];
         *dst1++ = sClamp[sAdjY[*pY1] + sAdjCrr[*pV]];

         // pixel 1x1
         *dst1++ = sClamp[sAdjY[*pY1++] + sAdjCbb[*pU++]];
         *dst1++ = sClamp[sAdjY[*pY1] - G];
         *dst1++ = sClamp[sAdjY[*pY1] + sAdjCrr[*pV++]];

in engine/core/theoraPlayer.cpp {play()} change
mPlayThread = new Thread((ThreadRunFunction)playThread, (S32) this, 1);

... to ...

      mPlayThread = new Thread((ThreadRunFunction)playThread, this, true);

we don't need the OpenGL stuff anymore

in engine/core/theoraPlayer.cpp change
U32 TheoraTexture::getGLName()
{
   if(mTextureHandle)
   {
      mTextureHandle->refresh();
      return mTextureHandle->getGLName();
   }
   return 0;
}

... to ...

/*U32 TheoraTexture::getGLName()
{
   if(mTextureHandle)
   {
      mTextureHandle->refresh();
      return mTextureHandle->getGLName();
   }
   return 0;
}*/

straight dgl to gfx conversions

in engine/gui/shiny/guiTheoraCtrl.cpp change
#include "dgl/dgl.h"

... to ...

#include "gfx/gBitmap.h"

in engine/gui/shiny/guiTheoraCtrl.cpp change
dglClearBitmapModulation();
		dglDrawBitmapStretch(mTheoraTexture, rect);

... to ...

		GFX->clearBitmapModulation();
		GFX->drawBitmapStretch(mTheoraTexture, rect);

in engine/gui/shiny/guiTheoraCtrl.cpp change
dglDrawRectFill(rect, mBackgroundColor); // black rect

... to ...

 		GFX->drawRectFill(rect, mBackgroundColor); // black rect

in engine/audio/audio.cpp change
mHandle[index] |= AUDIOHANDLE_STREAMING_BIT;

... to ...

      mHandle[index] |= AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_LOADING_BIT;

in engine/audio/audio.cpp change
return NULL;
//   AudioStreamSource *streamSource = AudioStreamSourceFactory::getNewInstance(filename);
//   return(streamSource);

... to ...

   AudioStreamSource *streamSource = AudioStreamSourceFactory::getNewInstance(filename);
   return(streamSource);

at the end of engine/audio/audio.cpp add
AudioStreamSource* alxFindAudioStreamSource(AUDIOHANDLE handle)
{
	StreamingList::iterator itr2 = mStreamingList.findImage(handle);
	if(itr2)
		return *itr2;
	return NULL;
}

add the new texture profile

in engine/gfx/gfxTextureProfile.cpp after the other ImplementTextureProfile's add
GFX_ImplementTextureProfile(GFXTheoraProfile, 
                            GFXTextureProfile::DiffuseMap,
                            GFXTextureProfile::KeepBitmap | GFXTextureProfile::Dynamic | GFXTextureProfile::NoMipmap,
                            GFXTextureProfile::None);

in engine/gfx/gfxTextureProfile.h after the other DeclareTextureProfile's add
// Texture for Theora videos
GFX_DeclareTextureProfile( GFXTheoraProfile );

in engine/gfx/gfxTextureManager.h change
virtual bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp)=0;

... to ...

      virtual bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp, bool convert=true)=0;

in engine/gfx/D3D/gfxD3DTextureManager.h change
bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp);

... to ...

      bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp, bool convert = true);

in engine/gfx/D3D/gfxD3DTextureManager.cpp change
bool GFXD3DTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)

... to ...

bool GFXD3DTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL, bool convert)

in engine/gfx/D3D/gfxD3DTextureManager.cpp {_loadTexture()} change
pDL->convertToBGRx(); // This does checking for format inside it, fear not

... to ...

   if( convert ) 
       pDL->convertToBGRx(); // This does checking for format inside it, fear not

in engine/gfx/D3D/gfxD3DTextureManager.cpp {_refreshTexture()} before the texture->mProfile->doStoreBitmap if, add
if(texture->mProfile->doStoreBitmap() && texture->mProfile->isDynamic() ) {
	   
	   realTex->release();
	   innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, texture->mProfile, texture->mMipLevels);

	   if(texture->mBitmap)
		   _loadTexture(texture, texture->mBitmap, false);

	   usedStrategies++;
   }
   else {

and in engine/gfx/D3D/gfxD3DTextureManager.cpp {refreshTexture()} after the last } of the second if add
}

in engine/platform/platformAudio.h after the AudioSampleEnvironment class add:
class AudioStreamSource;

in engine/platform/platformAudio.h after the last alxCreateSource add
AudioStreamSource* alxFindAudioStreamSource(AUDIOHANDLE handle);

in engine/audio/audioStreamSourceFactory.cpp after including vorbisStreamSource.h add
#include "audio/oggMixedStreamSource.h"

in engine/audio/audioStreamSourceFactory.cpp after the #ifndef NO_OGGVORBIS add
if(!dStricmp(filename, "oggMixedStream"))
        return new OggMixedStreamSource(filename);

remove the mutex code in engine/core/theoraPlayer.cpp... it was too much of a pain to try to add and works fine without it... below are all the unique lines that need to be deleted... you can just do a find for these lines.
mMutex = Mutex::createMutex();

   Mutex::destroyMutex(mMutex);

   MutexHandle handle;
   handle.lock(mMutex);

remove the mutex from engine/core/theoraPlayer.h
void *mMutex;

For some of the reasoning behind these changes, take a look at this thread: www.garagegames.com/mg/forums/result.thread.php?qt=35943

Testing

To test it you can create a video folder in your example/demo/data/ directory then copy over the 1.4/example/demo/data/video/soundTest.ogg file to the video directory you just created.

As a simple test, in your example/demo/client/ui/ folder create a new file called videoGui.gui:
//--- OBJECT WRITE BEGIN ---
new GuiTheoraCtrl(theo) {
   profile = "GuiDefaultProfile";
   horizSizing = "center";
   vertSizing = "center";
   position = "40 25";
   extent = "512 512";
   minExtent = "8 2";
   visible = "1";
   done = "0";
   stopOnSleep = "1";
   backgroundColor = "0 0 0 255";
};
//--- OBJECT WRITE END ---

Start the demo and get to the main menu. Bring down the console and type:
exec("demo/client/ui/videoGui.gui");
canvas.pushDialog("theo");
theo.setFile("demo/data/video/soundtest.ogg");

and the video should play... the gui will probably stretch to fill the screen, but this resource just kept getting larger and larger and by the time I got to the gui part, I didn't really want to fix it.

You will also want to make sure that you have one of the latest versions of the OpenAL32.dll... mine is somewhere around 100K.

March 07, 2006 Update

The resource used to say to do this:


trying to use getAddress the way I think it was intended

in engine/core/theoraPlayer.cpp {drawFrame()} change

dst1 += bmp->getWidth() * bmp->bytesPerPixel;

... to ...

      dst1 = bmp->getAddress(0,y+1);

but the getAddress call is actually incorrect, so until I know why that call is off just slighty, using the original line will work.

Jon
Page «Previous 1 2
#1
04/02/2006 (10:50 am)
Awesome. Cant wait to try this.
#2
04/05/2006 (8:54 am)
the implementation is really smooth, thanks! :)
#3
05/22/2006 (8:20 pm)
What an awesome resource. Hats off to you Jon, GREAT JOB!
This resource came in very handy and worked like a charm. The documentation was excellent and I can only hope you will write more resources of this caliber.
#4
05/22/2006 (8:22 pm)
BTW - My first name is Matt but sometimes I go as my middle name UCF#008.
#5
07/16/2006 (3:52 pm)
We have the theora player working in TSE, but it has one annoying bug. This is the code we use to play the video in client/init.cs:
exec("theblob/client/ui/videoGui.gui");
canvas.pushDialog("theo");
theo.setFile("theblob/data/video/fieaLogo.ogg");

The video plays perfectly, but for the duration of the video, whenever you move the mouse over the video, a big red square appears where the mouse is. The square gets resized as you move the mouse around. It's really annoying and we can't seem to debug the problem. After the video is done playing, the square goes away.

Anyone have any ideas?
#6
07/16/2006 (4:18 pm)
nevermind, the problem is fixed. It had to do with the default cursor getting trashed.
#7
07/19/2006 (9:09 pm)
For some reason the audio changes did not work. The video would play, but very slowly and there would not be any audio. So we only incorporated the Theora-specific changes, and now it works perfectly.
#8
07/31/2006 (12:33 pm)
We have theora working in tse, and we had no problems up until recently. I'm not sure what caused it, but now every time we play a video, the video framerate is horrible. We are playing a video before the mission is loaded, so the computer shouldn't be doing much else.

Any idea what would lower theora video framerate?
#9
03/11/2007 (8:42 am)
incorporated the rescource into a starter.racing build, and it'll play just fine. once, and only once. going back to the main menu leaves it black.

script implementation:

//--- OBJECT WRITE BEGIN ---
new GuiTheoraCtrl(MainMenuGui) {
profile = "GuiDefaultProfile";
horizSizing = "center";
vertSizing = "center";
position = "0 0";
extent = "640 480";
minExtent = "8 2";
visible = "1";
done = true;
loops = true;
theoraFile = "content/data/video/video.ogg";
backgroundColor = "0 0 0 255";
new GuiButtonCtrl() {
ect, ect, ect.

error from exiting that i thinks likely the culprit, but...:
MagicalTrevor::advancetime - no last occurrence for buffer -1 found!
Cur. D3DDevice ref count=1
GFXTextureObject Usage Report - 1 extant TOs
---------------------------------------------------------------
Addr Dim. GFXTextureProfile Profiler Path
1496954 ( 640, 480) GFXTheoraProfile MainLoop -> TimeManagerProcessMain -> ProcessTimeEvent -> SimAdvanceTime

any ideas?
#10
03/31/2007 (2:19 pm)
edit: back to the drawing board :/
#11
04/16/2007 (4:26 am)
i get these errors.. any idea what i am doing wrong?

\Program\TGEA\SDK\engine\core/theoraPlayer.h(161) : error C2065: 'mTextureHandle' : undeclared identifier

\engine\gfx\D3D\gfxD3DTextureManager.cpp(225) : error C2511: 'bool GFXD3DTextureManager::_loadTexture(GFXTextureObject *,GBitmap *)' : overloaded member function not found in 'GFXD3DTextureManager'

edit. i think this is the "bad guy"

8>d:\Program\TGEA\SDK\engine\core/theoraPlayer.h(181) : error C2143: syntax error : missing ';' before '*'

i have #include "gfx/gfxTextureHandle.h", but if i right click on it and want to open it i get an error message
"not found in curreent source file directory or in build system paths"
#12
04/16/2007 (6:01 am)
solved one problem.. GFXTextureHandler is called GFXTexHandler.. so i change the GFXtextureHandler to GFXTexHandler and those errors disappered

but i still have the

2>..\engine\gfx\D3D\gfxD3DTextureManager.cpp(225) : error C2511: 'bool GFXD3DTextureManager::_loadTexture(GFXTextureObject *,GBitmap *)' : overloaded member function not found in 'GFXD3DTextureManager'

error..

any ideas?
#13
04/16/2007 (7:07 am)
never mind.. stupid mistake from my side :S
#14
06/26/2007 (4:50 pm)
does anyone have this working in TGEA 1.0.1?
#15
06/27/2007 (7:47 am)
Yes, I have it working with 1.0.1. I have merged it with all of the updates from before it was TGEA and kept it up to date. As far as I know, this resource should work as it is written.

If you or anyone else is having troubles getting it to work, I would recommend creating a new forum post (instead of posting a resource comment) about your problems as most people only look at a resource once then don't look at it again, especially on resources that were created quite awhile ago.
#16
06/29/2007 (1:33 am)
Thank you for posting this, so far the code compile 100%
But now when dragging in the ctl then error : GuiControleProfile::decRefCount::zero ref count while running in Debug mode - do you know how I can fix this?
Thanks for the resource - again.
#17
07/30/2007 (3:38 pm)
I can confirm same error with CDK
#18
07/30/2007 (10:09 pm)
I am unable to resolve error, but I got a work around, just add it in script, use a dummy device coordinates and replace it with the theora control. That should work.
#19
08/31/2007 (1:10 pm)
I am getting linking errors:

oggMixedStreamSource1.obj : error LNK2005: "public: virtual bool __thiscall OggMixedStreamSource::initStream(void)" (?initStream@OggMixedStreamSource@@UAE_NXZ) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: virtual bool __thiscall OggMixedStreamSource::updateBuffers(void)" (?updateBuffers@OggMixedStreamSource@@UAE_NXZ) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: virtual void __thiscall OggMixedStreamSource::freeStream(void)" (?freeStream@OggMixedStreamSource@@UAEXXZ) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: unsigned int __thiscall OggMixedStreamSource::GetAvailableBuffer(void)" (?GetAvailableBuffer@OggMixedStreamSource@@QAEIXZ) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: bool __thiscall OggMixedStreamSource::QueueBuffer(unsigned int)" (?QueueBuffer@OggMixedStreamSource@@QAE_NI@Z) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: virtual __thiscall OggMixedStreamSource::~OggMixedStreamSource(void)" (??1OggMixedStreamSource@@UAE@XZ) already defined in oggMixedStreamSource.obj
oggMixedStreamSource1.obj : error LNK2005: "public: __thiscall OggMixedStreamSource::OggMixedStreamSource(char const *)" (??0OggMixedStreamSource@@QAE@PBD@Z) already defined in oggMixedStreamSource.obj
../example/TGEA.exe : fatal error LNK1169: one or more multiply defined symbols found

Any idea whats wrong? I followed this resource word by word. I am using TGEA 1.02.
#20
08/31/2007 (1:34 pm)
Never mind, I fixed it. My problem now is that TGEA crashes when I load up a video.

Console's last words:

==>exec("HotB/client/ui/videoGui.gui");
Executing HotB/client/ui/videoGui.gui.
==>canvas.pushDialog("theo");
==>theo.setFile("HotB/client/ui/demoVid.ogg");
TheoraTexture - Loading file 'HotB/client/ui/demoVid.ogg'
Ogg logical stream ec0983a is Theora 320x240 30.00 fps video
Page «Previous 1 2