Irrlicht VideoMaster [Audio support] [portable] [stable]

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Bate
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Irrlicht VideoMaster [Audio support] [portable] [stable]

Post by Bate »

Irrlicht VideoMaster



This is my initial Irrlicht implementation of the Theora Playback Library. It's portable, stable (1.0 RC2) and awesome. :D You can stream or RAM-play any number of Ogg Theora videos and use them as an IImage or ITexture for all purposes in Irrlicht. It also features an Audio Interface abstraction, so it's possible to use different audio libraries. For the demo I implemented an OpenAL interface based on the library's examples.

Features:
  • Multi-threaded
  • D3D and OpenGL
  • Multiple video clips
  • Audio playback (in sync)
  • Video Scaling
  • Start, pause, stop, repeat, seek to position, playback speed...
  • Texture format is A8R8G8B8 (for now)
Features coming up:
Improved scaling, more texture formats, alpha and maybe some color manipulation.


Dependencies: VisualStudio binaries are provided with the demo. For other platforms you will have to compile them yourself (it's straightforward though).
Download package here.

The demo requires the OpenAL binaries to be installed. Get them here.

Demo controls:

KEY_ESCAPE : exit
LMOUSE : create videos
RMOUSE : pause/resume video
KEY_BACK : remove all videos


Have fun! :)


Image
Never take advice from someone who likes to give advice, so take my advice and don't take it.
grumpymonkey
Posts: 222
Joined: Mon Jan 19, 2009 10:03 pm
Location: Miami, Florida
Contact:

Post by grumpymonkey »

WOW nice job bate thats 2 great releases from you that I've seen keep up the good work :D
Image
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Post by d3jake »

*me bookmarks for future reference.
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
kazymjir
Posts: 727
Joined: Sat Feb 20, 2010 4:05 pm
Location: Munich, Bayern

Post by kazymjir »

Cool! I was looking for something like this.
Good job!
TCM
Posts: 53
Joined: Mon May 24, 2010 9:29 pm

Post by TCM »

This is great !!!

But you only mention that you are using the Theora Playback Libary. However it is not the one that is available in that link, it's another library modified for irrLicht.

Problem: the library you are providing is compiled using VC2010. Only usable when compiling with VC2010. For all other platforms it's useless.

If you could compile it using VC2005, or 2008. Also, a DLL version would be nice, instead of one big static library.

Probably posting the source code is out of question.....
Bate
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Post by Bate »

TCM wrote: But you only mention that you are using the Theora Playback Libary. However it is not the one that is available in that link, it's another library modified for irrLicht.
All links are fine, Theora Playback Library = libtheoraplayer.
Problem: the library you are providing is compiled using VC2010. Only usable when compiling with VC2010. For all other platforms it's useless.
The provided library is the Theora Playback Library. For different platforms follow the link to the official website, get the source and compile it yourself ...
If you could compile it using VC2005, or 2008. Also, a DLL version would be nice, instead of one big static library.
... a DLL is possible, too.
Probably posting the source code is out of question.....
It's all there and the Theora Lib is open-source as well.
Link against the Theora Lib and create a CVideoMaster instance, that's all you need to do.
Never take advice from someone who likes to give advice, so take my advice and don't take it.
Midnight
Posts: 1772
Joined: Fri Jul 02, 2004 2:37 pm
Location: Wonderland

Post by Midnight »

I seem to remember the last irrlicht video lib/snippet which was using vorbis. somebody had mentioned theora as a better choice. I wouldn't know much about the subject myself as of yet but it is nice to see someone using something new.

I will certainly come back to this topic in the future, for now though, good job. (i think, without having tried it)

you get points for showing a working example with screenshot though!
TCM
Posts: 53
Joined: Mon May 24, 2010 9:29 pm

Post by TCM »

Again, great job !!!!

I did compile the libtheoraplayer for the VC 2008. It works fine. Also, i could not compile the videoplayer demo using static libraries. When linking against static libs i get "'P1' version '20080116' and 'P2' version '20070207'

But the demo works fine. The only thing is that i can hear some ticking in the left audio channel. That's odd, because i use the very same files, just recompiled for VC2008. Did you encounter anything like this ?

TCM
TCM
Posts: 53
Joined: Mon May 24, 2010 9:29 pm

Post by TCM »

It would be just great if it would be possible to play a movie while the map is loading. No actual rendering would be necessary, only a 2D image from the texture.

See this post: http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=29121


TCM
Bate
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Post by Bate »

Well, a 2D Image also requires rendering and displaying a movie is done by drawing a lot of them. As Blindside demonstrated, it's possible though. However, there's no reasonable way to implement it in the video class. You have to take care of that in your project design.

As for the audio playback, yeah, now that you mention it -- there really is a slight noise I've never noticed before. It's a problem within the Theora Lib, though; nothing I can do about it on my end. Anyway, I'm gonna add it to the bug tracker and see if I can figure out why that is.
Never take advice from someone who likes to give advice, so take my advice and don't take it.
TCM
Posts: 53
Joined: Mon May 24, 2010 9:29 pm

Post by TCM »

Bate wrote: As Blindside demonstrated, it's possible though.
I meant to say that using two threads, they will not use the same resources at the same time. For example, one thread will play a video and another will load models,textures and so on. Unless i am missing something.... :?
MolokoTheMole
Posts: 109
Joined: Tue Jan 09, 2007 1:18 pm

Post by MolokoTheMole »

Very nice, clean library!
Crimson Glory full Irrlicht game source code!
What I Do - my blog & dev log
Currently developing Link-Dead
pilesofspam
Posts: 62
Joined: Mon May 11, 2009 4:31 am

Post by pilesofspam »

Quick update- I compiled this under Linux and Irrlicht 1.7.2, and it works great.

Few gotchas- don't forget to check the Theora Playback Library out out of SVN and compile it (this is pretty easy if you've already got autoconf and libtool installed) and there are a bunch of errors that pop up in CVideoMaster.cpp, specifically about goto FAIL_CLEANUP jumping across automatic variables. Easy fix is to add some brackets around the offending code, and it should maintain its portability.

CVideoMaster.cpp:

Code: Select all

#include <CVideoMaster.h>
#include <TheoraDataSource.h>

#ifdef _MSC_VER
  // goto warning, everything is safe though
  #pragma warning (disable : 4533)
#endif

using namespace irr;
using namespace video;
using namespace core;
using namespace io;

CVideoMaster::CVideoMaster(irr::IrrlichtDevice* pDevice, bool readAudio, irr::u32 workerThreads) :
              mDevice(pDevice), mDriver(0), mTimer(0), mVmgr(0), mAIF(0), mTimeThen(0)
{
  mDriver   = mDevice->getVideoDriver();
  mTimer    = mDevice->getTimer();

  if (!workerThreads) workerThreads = 1;

  mVmgr = new TheoraVideoManager(workerThreads);

  if (readAudio)
  {
    mAIF = new CAudioInterfaceFactory_OpenAL();
    mVmgr->setAudioInterfaceFactory(mAIF);
  }

  mTimeThen = (f32)mTimer->getRealTime();
}

CVideoMaster::~CVideoMaster()
{
  removeVideoClipAll();

  if (mVmgr) delete mVmgr;
  if (mAIF)  delete mAIF;
}

void CVideoMaster::processFrames()
{
  if (mIrrClip.empty()) return;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    TheoraVideoFrame* frame = mIrrClip[i]->pTheoClip->getNextFrame();

    if (frame)
    {
      IImage* pImg  = mIrrClip[i]->pImg;
      void* imgData = pImg->lock();
      u8* frameData = frame->getBuffer();

      if (imgData && frameData)
      {
        memcpy(imgData, frameData, pImg->getImageDataSizeInBytes());
      }
      if (imgData) pImg->unlock();


      ITexture* pTex = mIrrClip[i]->pTex;
      void* texData  = pTex->lock();

      if(texData)
      {
        if (pTex->getSize() == pImg->getDimension())
        {
          memcpy(texData, frameData, pImg->getImageDataSizeInBytes());
        }
        else
        {
          pImg->copyToScaling(texData, pTex->getSize().Width, pTex->getSize().Height, ECF_A8R8G8B8, pTex->getPitch());
        }

        pTex->unlock();
      }

      mIrrClip[i]->pTheoClip->popFrame();
    }
  }
}

void CVideoMaster::update()
{
  processFrames();

  f32 timeNow   = (f32)mTimer->getRealTime();
  f32 timeDelta = (timeNow - mTimeThen) / 1000.0f;

  if (timeDelta > 0.25f) timeDelta = 0.05f;

  mVmgr->update(timeDelta);

  mTimeThen = timeNow;
}

SIrrVideoClip* CVideoMaster::getIrrClipByName(const irr::io::path &clipname, irr::u32 &out_clipindex)
{
  if (mIrrClip.empty()) return NULL;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    if (mIrrClip[i]->clipName == clipname)
    {
      out_clipindex = i;
      return mIrrClip[i];
    }
  }

  return NULL;
}

SIrrVideoClip* CVideoMaster::getIrrClipByName(const irr::io::path &clipname)
{
  if (mIrrClip.empty()) return NULL;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    if (mIrrClip[i]->clipName == clipname)
    {
      return mIrrClip[i];
    }
  }

  return NULL;
}

TheoraVideoClip* CVideoMaster::getTheoClipByName(const irr::io::path &clipname)
{
  if (mIrrClip.empty()) return NULL;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    if (mIrrClip[i]->clipName == clipname)
    {
      return mIrrClip[i]->pTheoClip;
    }
  }

  return NULL;
}

irr::video::IImage* CVideoMaster::getImageByName(const irr::io::path &clipname)
{
  if (mIrrClip.empty()) return NULL;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    if (mIrrClip[i]->clipName == clipname)
    {
      return mIrrClip[i]->pImg;
    }
  }

  return NULL;
}

irr::video::ITexture* CVideoMaster::getTextureByName(const irr::io::path &clipname)
{
  if (mIrrClip.empty()) return NULL;

  for (u32 i = 0; i < mIrrClip.size(); ++i)
  {
    if (mIrrClip[i]->clipName == clipname)
    {
      return mIrrClip[i]->pTex;
    }
  }

  return NULL;
}

const bool CVideoMaster::isPOT(const irr::core::dimension2du &size)
{
  u32 x = size.Width;
  u32 y = size.Height;

  bool xTrue = ((x != 0) && !(x & (x - 1)));
  bool yTrue = ((y != 0) && !(y & (y - 1)));

  return (xTrue && yTrue);
}

const irr::core::dimension2du CVideoMaster::getNextPOT(const irr::core::dimension2du &size, bool up)
{
  dimension2du temp(1, 1);

  if (isPOT(size))
  {
    temp = size;
    return temp;
  }

  while (temp.Width  < size.Width)  { temp.Width  *= 2; }
  while (temp.Height < size.Height) { temp.Height *= 2; }

  if (up)
  {
    return temp;
  }
  else
  {
    temp.Width  /= 2;
    temp.Height /= 2;
    return temp;
  }
}

irr::video::ITexture* CVideoMaster::addVideoClip(const irr::io::path &filename,
                                                 const irr::io::path &clipname,
                                                 E_SCALE_MODE        scaleMode,
                                                 bool                repeatPlayback,
                                                 bool                startPlayback,
                                                 bool                preloadIntoRAM)
{
  const bool oldFlag = mDriver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
  mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);

  if(mDriver->queryFeature(EVDF_TEXTURE_NPOT))
    mDriver->setTextureCreationFlag(ETCF_ALLOW_NON_POWER_2, true);

  TheoraVideoClip *pClip = NULL;
  IImage          *pImg  = NULL;
  ITexture        *pTex  = NULL;

  if (filename == "" || clipname == "") goto FAIL_CLEANUP;

  if (preloadIntoRAM) pClip = mVmgr->createVideoClip(new TheoraMemoryFileDataSource(std::string(filename.c_str())), TH_BGRA);
  else                pClip = mVmgr->createVideoClip(std::string(filename.c_str()), TH_BGRA);

  if (!pClip) goto FAIL_CLEANUP;

  if (startPlayback) pClip->play();
  else               pClip->stop();

  pClip->setAutoRestart(repeatPlayback);
{
  const dimension2du sizeClip(pClip->getWidth(), pClip->getHeight());
  const dimension2du sizeScreen(mDriver->getScreenSize());

  pImg = mDriver->createImage(ECF_A8R8G8B8, sizeClip);

  if (!pImg) goto FAIL_CLEANUP;
{
  void* imgData = pImg->lock();
  if (imgData)
  {
    memset(imgData, 0xFF, pImg->getImageDataSizeInBytes());
    pImg->unlock();
  }
}
  switch (scaleMode)
  {
    case ESM_NONE :
    {
      pTex = mDriver->addTexture(sizeClip, clipname, ECF_A8R8G8B8);
    } break;

    case ESM_WINDOW_FIT :
    {
      pTex = mDriver->addTexture(sizeScreen, clipname, ECF_A8R8G8B8);
    } break;

    case ESM_WINDOW_RATIO :
    {
      f32 ratioX = (f32)sizeScreen.Width  / (f32)sizeClip.Width;
      f32 ratioY = (f32)sizeScreen.Height / (f32)sizeClip.Height;

      if (ratioX < ratioY)
      {
        dimension2du dim(sizeScreen.Width, sizeClip.Height * (u32)ratioX);
        pTex = mDriver->addTexture(dim, clipname, ECF_A8R8G8B8);
      }
      else if (ratioX > ratioY)
      {
        dimension2du dim(sizeClip.Width * (u32)ratioY, sizeScreen.Height);
        pTex = mDriver->addTexture(dim, clipname, ECF_A8R8G8B8);
      }
      else
      {
        pTex = mDriver->addTexture(sizeScreen, clipname, ECF_A8R8G8B8);
      }
    } break;

    case ESM_POT_UP :
    {
      pTex = mDriver->addTexture(getNextPOT(sizeClip, true), clipname, ECF_A8R8G8B8);
    } break;

    case ESM_POT_DOWN :
    {
      pTex = mDriver->addTexture(getNextPOT(sizeClip, false), clipname, ECF_A8R8G8B8);
    } break;
  }
}
  if (!pTex) goto FAIL_CLEANUP;



  {

  void* texData = pTex->lock();

  if(texData)
  {
    pImg->copyToScaling(texData, pTex->getSize().Width, pTex->getSize().Height, ECF_A8R8G8B8, pTex->getPitch());

    pTex->unlock();

  }}
{

  SIrrVideoClip *temp = new SIrrVideoClip();

  temp->pTheoClip = pClip;
  temp->pImg      = pImg;
  temp->pTex      = pTex;
  temp->clipName  = clipname;

  mIrrClip.push_back(temp);
  mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, oldFlag);
  return pTex;
}

  FAIL_CLEANUP :

    if (pClip) mVmgr->destroyVideoClip(pClip);
    if (pImg)  pImg->drop();
    if (pTex)  mDriver->removeTexture(pTex);
    mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, oldFlag);
    return NULL;
}

irr::video::ITexture* CVideoMaster::addVideoClip(const irr::io::path           &filename,
                                                 const irr::io::path           &clipname,
                                                 const irr::core::dimension2du &scaleToSize,
                                                 bool                          repeatPlayback,
                                                 bool                          startPlayback,
                                                 bool                          preloadIntoRAM)
{
  const bool oldFlag = mDriver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
  mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);

  if(mDriver->queryFeature(EVDF_TEXTURE_NPOT))
    mDriver->setTextureCreationFlag(ETCF_ALLOW_NON_POWER_2, true);

  TheoraVideoClip *pClip = NULL;
  IImage          *pImg  = NULL;
  ITexture        *pTex  = NULL;

  if (filename == "" || clipname == "") goto FAIL_CLEANUP;

  if (preloadIntoRAM) pClip = mVmgr->createVideoClip(new TheoraMemoryFileDataSource(std::string(filename.c_str())), TH_BGRA);
  else                pClip = mVmgr->createVideoClip(std::string(filename.c_str()), TH_BGRA);

  if (!pClip) goto FAIL_CLEANUP;
{
  if (startPlayback) pClip->play();
  else               pClip->stop();

  pClip->setAutoRestart(repeatPlayback);

  const dimension2du sizeClip(pClip->getWidth(), pClip->getHeight());
  const dimension2du sizeScreen(mDriver->getScreenSize());

  pImg = mDriver->createImage(ECF_A8R8G8B8, sizeClip);
}
  if (!pImg) goto FAIL_CLEANUP;
{
  void* imgData = pImg->lock();
  if (imgData)
  {
    memset(imgData, 0xFF, pImg->getImageDataSizeInBytes());
    pImg->unlock();
  }
}
  pTex = mDriver->addTexture(scaleToSize, clipname, ECF_A8R8G8B8);

  if (!pTex) goto FAIL_CLEANUP;
{
  void* texData = pTex->lock();
  if(texData)
  {
    pImg->copyToScaling(texData, pTex->getSize().Width, pTex->getSize().Height, ECF_A8R8G8B8, pTex->getPitch());
    pTex->unlock();
  }

  SIrrVideoClip *temp = new SIrrVideoClip();
  temp->pTheoClip = pClip;
  temp->pImg      = pImg;
  temp->pTex      = pTex;
  temp->clipName  = clipname;

  mIrrClip.push_back(temp);
  mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, oldFlag);
  return pTex;
}
  FAIL_CLEANUP :

    if (pClip) mVmgr->destroyVideoClip(pClip);
    if (pImg)  pImg->drop();
    if (pTex)  mDriver->removeTexture(pTex);
    mDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, oldFlag);
    return NULL;
}

bool CVideoMaster::removeVideoClip(const irr::io::path &clipname, bool irrRemoveTexture, bool irrDropImage)
{
  u32 clipIndex;
  SIrrVideoClip* pIrrClip = getIrrClipByName(clipname, clipIndex);

  if (!pIrrClip) return false;

  if (pIrrClip->pTheoClip)                mVmgr->destroyVideoClip(pIrrClip->pTheoClip);
  if (pIrrClip->pImg && irrDropImage)     pIrrClip->pImg->drop();
  if (pIrrClip->pTex && irrRemoveTexture) mDriver->removeTexture(pIrrClip->pTex);

  delete pIrrClip;
  mIrrClip.erase(clipIndex);

  return true;
}

void CVideoMaster::removeVideoClipAll(bool irrRemoveTexture, bool irrDropImage)
{
  while (!mIrrClip.empty())
  {
    SIrrVideoClip* pIrrClip = mIrrClip.getLast();

    if (pIrrClip)
    {
      if (pIrrClip->pTheoClip)                mVmgr->destroyVideoClip(pIrrClip->pTheoClip);
      if (pIrrClip->pImg && irrDropImage)     pIrrClip->pImg->drop();
      if (pIrrClip->pTex && irrRemoveTexture) mDriver->removeTexture(pIrrClip->pTex);
      delete pIrrClip;
    }

    mIrrClip.erase(mIrrClip.size() - 1);
  }
}
Justei
Posts: 47
Joined: Fri Aug 20, 2010 11:20 am

Re: Irrlicht VideoMaster [Audio support] [portable] [stable]

Post by Justei »

I know this is a little bit of a bump but does anyone happen to have the library files for MINGW (Code::Blocks)? :/ having some trouble getting this to run with Code::Blocks cus of that -.-, would really appreciate some help with that :S.
Image
andreujuanc
Posts: 1
Joined: Mon Jan 02, 2012 6:49 pm

Re: Irrlicht VideoMaster [Audio support] [portable] [stable]

Post by andreujuanc »

Hello!

I also hear the ticking on the left channel. If someone found the fix please post it. I will try to get some free time and try to figure our where is it coming from.

PS: Something like this should be integrated on the engine with some open source and free codex with an abstraction layer in a way the user could change the codecs..

Regards,

Juan
Post Reply