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
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
00034
00035 av_register_all();
00036 }
00037
00038
00039
00040 VideoTexture::~VideoTexture()
00041 {
00042
00043 if ( NULL != this->texture )
00044 {
00045
00046
00047 this->texture = NULL;
00048 }
00049
00050
00051 if ( NULL != this->avFrameRGB )
00052 {
00053 av_free( this->avFrameRGB );
00054 this->avFrameRGB = NULL;
00055 }
00056
00057
00058 if ( NULL != this->avFrameYUV )
00059 {
00060 av_free( this->avFrameYUV );
00061 this->avFrameYUV = NULL;
00062 }
00063
00064
00065 if ( NULL != this->avCodecContext )
00066 {
00067 avcodec_close( this->avCodecContext );
00068 this->avCodecContext = NULL;
00069 }
00070
00071
00072 if ( NULL != this->avFormatContext )
00073 {
00074 av_close_input_file( this->avFormatContext );
00075 this->avFormatContext = NULL;
00076 }
00077
00078
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
00092 if ( 0 != av_open_input_file( &this->avFormatContext, fileName.c_str(), NULL, 0, NULL ) )
00093 {
00094
00095 stringc info( "openStream() cannot open file " );
00096 info += fileName;
00097 logger->log( info.c_str() );
00098 return ERR_UNKWON;
00099 }
00100
00101
00102
00103 if ( av_find_stream_info( this->avFormatContext ) < 0 )
00104 {
00105
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
00113
00114
00115
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
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
00139 this->avCodecContext = this->avFormatContext->streams[videoStreamIndex]->codec;
00140
00141
00142 this->avCodec = avcodec_find_decoder( this->avCodecContext->codec_id );
00143 if ( this->avCodec == NULL )
00144 {
00145
00146 logger->log( "openStream() no proper codec found to handle this video" );
00147 return ERR_UNKWON;
00148 }
00149
00150
00151 stringc codecName( "openStream() using codec " );
00152 codecName += this->avCodec->name;
00153 logger->log( codecName.c_str() );
00154
00155
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
00161 stringc framerate( "openStream() frame rate is " );
00162 framerate += this->videoStreamFPS;
00163 framerate += " FPS";
00164 logger->log( framerate.c_str() );
00165
00166
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
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
00180 if ( avcodec_open( this->avCodecContext, this->avCodec ) < 0 )
00181 {
00182
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
00190
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
00199
00200
00201
00202
00203
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
00212
00213
00214 s32 targetSizeX = 256;
00215 s32 targetSizeY = 256;
00216
00217
00218 s32 targetBoarderX = 28;
00219 s32 targetBoarderY = 28;
00220
00221
00222
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 );
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
00256 u8* buffer = (u8*) this->texture->lock();
00257
00258
00259 memset( buffer, 0xFF, this->texture->getSize().Width * this->texture->getSize().Height * 4 );
00260
00261
00262
00263 buffer += ( (targetBoarderX*targetSizeX/2) * 4 + (2 * targetBoarderY) );
00264
00265
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
00278
00279
00280 if ( -1 == this->timeSinceLastUpdate )
00281 {
00282 this->timeSinceLastUpdate = timer->getRealTime();
00283 }
00284
00285
00286 u32 now = timer->getRealTime();
00287 u32 deltaTime = now - this->timeSinceLastUpdate;
00288
00289
00290 if ( deltaTime < (1000.0f / this->videoStreamFPS) )
00291 {
00292
00293 return ERR_VIDEOTEXTURE_WAIT_FOR_SYNC;
00294 }
00295
00296
00297 this->timeSinceLastUpdate = now;
00298
00299 s32 frameFinished = 0;
00300 AVPacket packet;
00301
00302 while ( 0 == frameFinished )
00303 {
00304 if ( av_read_frame( this->avFormatContext, &packet ) >= 0 )
00305 {
00306
00307 if ( packet.stream_index == this->videoStreamIndex )
00308 {
00309
00310 avcodec_decode_video( this->avCodecContext, this->avFrameYUV, &frameFinished, packet.data, packet.size);
00311
00312
00313 if ( frameFinished )
00314 {
00315
00316
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
00328 this->texture->unlock();
00329 }
00330 }
00331 }
00332 else
00333 {
00334
00335 if ( true == this->loopedPlay )
00336 {
00337
00338 av_seek_frame( this->avFormatContext, this->videoStreamIndex, 0, 0 );
00339 }
00340
00341
00342 av_free_packet( &packet );
00343
00344 return ERR_VIDEOTEXTURE_VIDEO_FINISHED;
00345 }
00346 }
00347
00348
00349 av_free_packet( &packet );
00350
00351 return ERR_OK;
00352 }
00353
00354
00355 ITexture* VideoTexture::getTexture()
00356 {
00357 return this->texture;
00358 }