videoTexture.cpp

00001 
00002 #include "main.h"
00003 #include "videoTexture.h"
00004 
00005 extern "C"
00006 {
00007 #define __STDC_CONSTANT_MACROS
00008 #define __STDC_LIMIT_MACROS
00009 #include "avformat.h"
00010 #include "avcodec.h"
00011 #include "swscale.h"
00012 }
00013 
00014 
00015 
00016 
00017 // constructor
00018 VideoTexture::VideoTexture()
00019 : texture(NULL),
00020   avFormatContext(NULL),
00021   avCodecContext(NULL),
00022   avCodec(NULL),
00023   avFrameYUV(NULL),
00024   avFrameRGB(NULL),
00025   swsContext(NULL),
00026   videoStreamIndex(-1),
00027   videoStreamFPS(-1),
00028   loopedPlay(true),
00029   timeSinceLastUpdate(-1)
00030 {
00031         logger->log( "VideoTexture ctor()" );
00032     
00033         // Register all formats and codecs within libavcodec
00034         // (i.e. play every movie the libavcodec is able to play)
00035     av_register_all();
00036 }
00037 
00038 
00039 // destructor
00040 VideoTexture::~VideoTexture()
00041 {
00042         // remove texture from driver
00043         if ( NULL != this->texture )
00044         {
00045                 // todo: assume no other photo still uses this texture
00046                 // driver->removeTexture( this->texture );
00047                 this->texture = NULL;
00048         }
00049 
00050         // Free the RGB image
00051         if ( NULL != this->avFrameRGB )
00052         {
00053                 av_free( this->avFrameRGB );
00054                 this->avFrameRGB = NULL;
00055         }
00056         
00057         // Free the YUV image
00058         if ( NULL != this->avFrameYUV )
00059         {
00060                 av_free( this->avFrameYUV );
00061                 this->avFrameYUV = NULL;
00062         }
00063 
00064         // Close the codec
00065         if ( NULL != this->avCodecContext )
00066         {
00067                 avcodec_close( this->avCodecContext );
00068                 this->avCodecContext = NULL;
00069         }
00070 
00071         // Close the video file
00072         if ( NULL != this->avFormatContext )
00073         {
00074                 av_close_input_file( this->avFormatContext );
00075                 this->avFormatContext = NULL;
00076         }
00077 
00078         // Free the software scaler
00079         if ( NULL != this->swsContext )
00080         {
00081                 sws_freeContext( this->swsContext );
00082                 this->swsContext = NULL;
00083         }
00084 
00085         logger->log( "VideoTexture dtor()" );
00086 }
00087 
00088 
00089 ERR_TYPE VideoTexture::openStream( stringc fileName, bool loopedPlay )
00090 {
00091     // Open video file
00092     if ( 0 != av_open_input_file( &this->avFormatContext, fileName.c_str(), NULL, 0, NULL ) )
00093         {
00094                 // Couldn't open file
00095                 stringc info( "openStream() cannot open file " );
00096                 info += fileName;
00097                 logger->log( info.c_str() );
00098         return ERR_UNKWON;
00099         }
00100 
00101     // Retrieve stream information
00102         // This function populates avFormatContext->streams with proper information
00103     if ( av_find_stream_info( this->avFormatContext ) < 0 )
00104         {
00105                 // Couldn't find stream information
00106                 stringc info( "openStream() cannot find stream info in file " );
00107                 info += fileName;
00108                 logger->log( info.c_str() );
00109                 return ERR_UNKWON;
00110         }
00111 
00112         // Dump information about file onto standard error
00113         //dump_format( this->avFormatContext, 0, fileName.c_str(), 0 );
00114 
00115     // Find the first video stream
00116     for ( s32 i = 0; i < (s32) this->avFormatContext->nb_streams; i++ )
00117         {
00118         if ( this->avFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
00119         {
00120             this->videoStreamIndex = i;
00121                         stringc info( "openStream() using stream " );
00122                         info += this->videoStreamIndex;
00123                         info += " as video stream in file ";
00124                         info += fileName;
00125                         logger->log( info.c_str() );
00126             break;
00127         }
00128         }
00129     if ( this->videoStreamIndex == -1 )
00130         {
00131                 // Didn't find a video stream
00132                 stringc info( "openStream() no video stream found in file " );
00133                 info += fileName;
00134                 logger->log( info.c_str() );
00135                 return ERR_UNKWON;
00136         }
00137 
00138     // Get a pointer to the codec context for the video stream
00139     this->avCodecContext = this->avFormatContext->streams[videoStreamIndex]->codec;
00140 
00141     // Find the decoder for the video stream
00142     this->avCodec = avcodec_find_decoder( this->avCodecContext->codec_id );
00143     if ( this->avCodec == NULL )
00144         {
00145                 // Codec not found
00146                 logger->log( "openStream() no proper codec found to handle this video" );
00147                 return ERR_UNKWON;
00148         }
00149 
00150         // dump info about codec
00151         stringc codecName( "openStream() using codec " );
00152         codecName += this->avCodec->name;
00153         logger->log( codecName.c_str() );
00154 
00155         // get video FPS used by update()
00156         f32 num = (f32) this->avCodecContext->time_base.num;
00157         f32 den = (f32) this->avCodecContext->time_base.den;
00158         this->videoStreamFPS = (f32) (den / num);
00159 
00160         // dump info about frame rate
00161         stringc framerate( "openStream() frame rate is " );
00162         framerate += this->videoStreamFPS;
00163         framerate += " FPS";
00164         logger->log( framerate.c_str() );
00165 
00166         // dump info about video dimension
00167         stringc dimension( "openStream() video dimension width=" );
00168         dimension += this->avCodecContext->width;
00169         dimension += " height=";
00170         dimension += this->avCodecContext->height;
00171         logger->log( dimension.c_str() );
00172 
00173         // dump info about video duration
00174         stringc duration( "openStream() video duration is " );
00175         duration += (u32) (this->avFormatContext->duration / AV_TIME_BASE);
00176         duration += " seconds";
00177         logger->log( duration.c_str() );
00178 
00179         // Open codec
00180         if ( avcodec_open( this->avCodecContext, this->avCodec ) < 0 )
00181         {
00182                 // Could not open codec
00183                 stringc err( "openStream() cannot open codec " );
00184                 err += this->avCodec->name;
00185                 logger->log( err.c_str() );
00186                 return ERR_UNKWON;
00187         }
00188 
00189         // Now we need a place to actually store the frame:
00190         // Allocate video frame
00191         this->avFrameYUV = avcodec_alloc_frame();
00192         if ( NULL == this->avFrameYUV )
00193         {
00194                 logger->log( "openStream() cannot allocate video frame" );
00195                 return ERR_UNKWON;
00196         }
00197 
00198         // we're going to have to convert our frame from its native format to ARGB. 
00199         // ffmpeg will do these conversions for us. For most projects (including ours) 
00200         // we're going to want to convert our initial frame to a specific format. 
00201         // Let's allocate a frame for the converted frame now.
00202 
00203         // Allocate an AVFrame structure
00204         this->avFrameRGB = avcodec_alloc_frame();
00205         if ( NULL == this->avFrameRGB )
00206         {
00207                 logger->log( "openStream() cannot allocate RGB frame" );
00208                 return ERR_UNKWON;
00209         }
00210 
00211         // set hardware texture size.
00212         // in terms of performance, we should use source size here,
00213         // but older graphic cards only accept power2 size
00214         s32 targetSizeX = 256;
00215         s32 targetSizeY = 256;
00216 
00217         // we also want to draw a boarder around video
00218         s32 targetBoarderX = 28;
00219         s32 targetBoarderY = 28;
00220 
00221         // get the software scaler, used to copy from YUV to RGB color scheme
00222         // reduce desired target size by boarder size
00223         this->swsContext = sws_getContext(      this->avCodecContext->width,
00224                                                                                 this->avCodecContext->height,
00225                                                                                 this->avCodecContext->pix_fmt,
00226                                                                                 targetSizeX - targetBoarderX,
00227                                                                                 targetSizeY - targetBoarderY,
00228                                                                                 PIX_FMT_RGB32,
00229                                                                                 SWS_FAST_BILINEAR,
00230                                                                                 NULL,
00231                                                                                 NULL,
00232                                                                                 NULL );
00233         if ( NULL == this->swsContext )
00234         {
00235                 logger->log( "openStream() Error creating swsContext" );
00236                 return ERR_UNKWON;      
00237         }
00238 
00239         this->texture = driver->addTexture(     dimension2d<s32>(targetSizeX, targetSizeY),
00240                                                                                 fileName.c_str(),
00241                                                                                 ECF_A8R8G8B8 ); // force 32-bit texture
00242         
00243         if ( NULL == this->texture )
00244         {
00245                 logger->log( "openStream() Error creating VideoTexture" );
00246                 return ERR_UNKWON;
00247         }
00248 
00249         ECOLOR_FORMAT colorFormat = this->texture->getColorFormat();
00250         if ( colorFormat != ECF_A8R8G8B8 )
00251         {
00252                 logger->log( "openStream() Warning, texture color format mismatch" );
00253         }
00254 
00255         // get pointer to irrlicht texture buffer
00256         u8* buffer = (u8*) this->texture->lock();
00257 
00258         // fill whole texture white
00259         memset( buffer, 0xFF, this->texture->getSize().Width * this->texture->getSize().Height * 4 );
00260 
00261         // adjust start of buffer to simulate a boarder
00262         // avcodec thinks now, it handles with a smaller rect
00263         buffer += ( (targetBoarderX*targetSizeX/2) * 4 + (2 * targetBoarderY) );
00264 
00265         // assign buffer to image planes in avFrameRGB
00266         avpicture_fill( (AVPicture*) this->avFrameRGB, buffer, PIX_FMT_RGB32, targetSizeX, targetSizeY );
00267         
00268         this->texture->unlock();
00269 
00270         return ERR_OK;
00271 }
00272 
00273 
00274 
00275 ERR_TYPE VideoTexture::update( u32 timeMS )
00276 {
00277         // this is a non sophisticated attempt to hit desired video FPS:
00278 
00279         // store start time of video
00280         if ( -1 == this->timeSinceLastUpdate )
00281         {
00282                 this->timeSinceLastUpdate = timer->getRealTime();
00283         }
00284 
00285         // get time since last frame was updated
00286         u32 now = timer->getRealTime();
00287         u32 deltaTime = now - this->timeSinceLastUpdate;
00288 
00289         // check if enough time elapsed since last call
00290         if ( deltaTime < (1000.0f / this->videoStreamFPS) )
00291         {
00292                 // no, wait to next update() call
00293                 return ERR_VIDEOTEXTURE_WAIT_FOR_SYNC;
00294         }
00295 
00296         // yes, it's time to process next video frame
00297         this->timeSinceLastUpdate = now;
00298 
00299         s32 frameFinished = 0;  // if not 0 -> copy frame to irrlicht texture 
00300         AVPacket packet;                // chunk of data, loaded by libavcodec from file
00301 
00302         while ( 0 == frameFinished )    // read from file until one frame is completed
00303         {
00304                 if ( av_read_frame( this->avFormatContext, &packet ) >= 0 )
00305                 {
00306                         // Is this a packet from the video stream?
00307                         if ( packet.stream_index == this->videoStreamIndex )
00308                         {
00309                                 // Decode video frame
00310                                 avcodec_decode_video( this->avCodecContext, this->avFrameYUV, &frameFinished, packet.data, packet.size);
00311 
00312                                 // Did we get a video frame?
00313                                 if ( frameFinished )
00314                                 {
00315                                         // Convert the image from its native YUV format to ARGB format
00316                                         // first, lock the irrlicht texture, we are writing into
00317                                         this->texture->lock();
00318 
00319                                         sws_scale(      this->swsContext,
00320                                                                 this->avFrameYUV->data,
00321                                                                 this->avFrameYUV->linesize,
00322                                                                 0,
00323                                                                 this->avCodecContext->height,
00324                                                                 this->avFrameRGB->data,
00325                                                                 this->avFrameRGB->linesize );
00326 
00327                                         // finished writing
00328                                         this->texture->unlock();
00329                                 }
00330                         }
00331                 }
00332                 else
00333                 {
00334                         // av_read_frame() indicated no more frames
00335                         if ( true == this->loopedPlay )
00336                         {
00337                                 // rewind to frame# 0
00338                                 av_seek_frame( this->avFormatContext, this->videoStreamIndex, 0, 0 );
00339                         }
00340                         
00341                         // Free the packet that was allocated by av_read_frame()
00342                         av_free_packet( &packet );
00343 
00344                         return ERR_VIDEOTEXTURE_VIDEO_FINISHED;
00345                 }       
00346         }
00347 
00348         // Free the packet that was allocated by av_read_frame()
00349         av_free_packet( &packet );
00350 
00351         return ERR_OK;
00352 }
00353 
00354 
00355 ITexture* VideoTexture::getTexture()
00356 {
00357         return this->texture;
00358 }

Generated on Sun Dec 2 03:10:23 2007 for TableTop by  doxygen 1.5.4