Panda3D

ffmpegVideoCursor.cxx

00001 // Filename: ffmpegVideoCursor.cxx
00002 // Created by: jyelon (01Aug2007)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #ifdef HAVE_FFMPEG
00016 
00017 #include "ffmpegVideoCursor.h"
00018 #include "config_movies.h"
00019 extern "C" {
00020   #include "libavcodec/avcodec.h"
00021   #include "libavformat/avformat.h"
00022 #ifdef HAVE_SWSCALE
00023   #include "libswscale/swscale.h"
00024 #endif
00025 }
00026 #include "pStatCollector.h"
00027 #include "pStatTimer.h"
00028 
00029 TypeHandle FfmpegVideoCursor::_type_handle;
00030 
00031 ////////////////////////////////////////////////////////////////////
00032 //     Function: FfmpegVideoCursor::Constructor
00033 //       Access: Public
00034 //  Description: xxx
00035 ////////////////////////////////////////////////////////////////////
00036 FfmpegVideoCursor::
00037 FfmpegVideoCursor(FfmpegVideo *src) :
00038   MovieVideoCursor(src),
00039   _filename(src->_filename),
00040   _format_ctx(0),
00041   _video_index(-1),
00042   _video_ctx(0),
00043   _frame(0),
00044   _frame_out(0),
00045   _packet(0),
00046   _min_fseek(3.0)
00047 {
00048   string url = "pandavfs:";
00049   url += _filename;
00050   if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
00051     cleanup();
00052     return;
00053   }
00054   
00055   if (av_find_stream_info(_format_ctx)<0) {
00056     cleanup();
00057     return;
00058   }
00059   
00060   // Find the video stream
00061   for(int i=0; i<_format_ctx->nb_streams; i++) {
00062     if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
00063       _video_index = i;
00064       _video_ctx = _format_ctx->streams[i]->codec;
00065       _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
00066     }
00067   }
00068   
00069   if (_video_ctx == 0) {
00070     cleanup();
00071     return;
00072   }
00073 
00074   AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
00075   if(pVideoCodec == 0) {
00076     cleanup();
00077     return;
00078   }
00079   if(avcodec_open(_video_ctx, pVideoCodec)<0) {
00080     cleanup();
00081     return;
00082   }
00083 
00084   _size_x = _video_ctx->width;
00085   _size_y = _video_ctx->height;
00086   _num_components = 3; // Don't know how to implement RGBA movies yet.
00087   _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
00088   _can_seek = true;
00089   _can_seek_fast = true;
00090 
00091   _packet = new AVPacket;
00092   _frame = avcodec_alloc_frame();
00093   _frame_out = avcodec_alloc_frame();
00094   if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
00095     cleanup();
00096     return;
00097   }
00098   memset(_packet, 0, sizeof(AVPacket));
00099   
00100   fetch_packet(0.0);
00101   _initial_dts = _packet->dts;
00102   _packet_time = 0.0;
00103   _last_start = -1.0;
00104   _next_start = 0.0;
00105 }
00106 
00107 ////////////////////////////////////////////////////////////////////
00108 //     Function: FfmpegVideoCursor::Destructor
00109 //       Access: Public
00110 //  Description: xxx
00111 ////////////////////////////////////////////////////////////////////
00112 FfmpegVideoCursor::
00113 ~FfmpegVideoCursor() {
00114   cleanup();
00115 }
00116 
00117 ////////////////////////////////////////////////////////////////////
00118 //     Function: FfmpegVideoCursor::cleanup
00119 //       Access: Public
00120 //  Description: Reset to a standard inactive state.
00121 ////////////////////////////////////////////////////////////////////
00122 void FfmpegVideoCursor::
00123 cleanup() {
00124   if (_frame) {
00125     av_free(_frame);
00126     _frame = 0;
00127   }
00128   if (_frame_out) {
00129     _frame_out->data[0] = 0;
00130     av_free(_frame_out);
00131     _frame_out = 0;
00132   }
00133   if (_packet) {
00134     if (_packet->data) {
00135       av_free_packet(_packet);
00136     }
00137     delete _packet;
00138     _packet = 0;
00139   }
00140   if ((_video_ctx)&&(_video_ctx->codec)) {
00141     avcodec_close(_video_ctx);
00142   }
00143   _video_ctx = 0;
00144   if (_format_ctx) {
00145     av_close_input_file(_format_ctx);
00146     _format_ctx = 0;
00147   }
00148   _video_ctx = 0;
00149   _video_index = -1;
00150 
00151 }
00152 
00153 ////////////////////////////////////////////////////////////////////
00154 //     Function: FfmpegVideoCursor::export_frame
00155 //       Access: Public, Virtual
00156 //  Description: Exports the contents of the frame buffer into the
00157 //               user's target buffer.
00158 ////////////////////////////////////////////////////////////////////
00159 static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
00160 void FfmpegVideoCursor::
00161 export_frame(unsigned char *data, bool bgra, int bufx) {
00162   PStatTimer timer(export_frame_collector);
00163   if (bgra) {
00164     _frame_out->data[0] = data + ((_size_y - 1) * bufx * 4);
00165     _frame_out->linesize[0] = bufx * -4;
00166 #ifdef HAVE_SWSCALE
00167     struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
00168                                _video_ctx->pix_fmt, _size_x, _size_y,
00169                                PIX_FMT_BGRA, 2, NULL, NULL, NULL);
00170     nassertv(convert_ctx != NULL);
00171     sws_scale(convert_ctx, _frame->data, _frame->linesize,
00172               0, _size_y, _frame_out->data, _frame_out->linesize);
00173     sws_freeContext(convert_ctx);
00174 #else
00175     img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA, 
00176                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
00177 #endif
00178   } else {
00179     _frame_out->data[0] = data + ((_size_y - 1) * bufx * 3);
00180     _frame_out->linesize[0] = bufx * -3;
00181 #ifdef HAVE_SWSCALE
00182     struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y,
00183                                _video_ctx->pix_fmt, _size_x, _size_y,
00184                                PIX_FMT_BGR24, 2, NULL, NULL, NULL);
00185     nassertv(convert_ctx != NULL);
00186     sws_scale(convert_ctx, _frame->data, _frame->linesize,
00187               0, _size_y, _frame_out->data, _frame_out->linesize);
00188     sws_freeContext(convert_ctx);
00189 #else
00190     img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, 
00191                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
00192 #endif
00193   }
00194 }
00195 
00196 ////////////////////////////////////////////////////////////////////
00197 //     Function: FfmpegVideoCursor::fetch_packet
00198 //       Access: Protected
00199 //  Description: Fetches a video packet and stores it in the 
00200 //               packet buffer.  Sets packet_time to the packet's
00201 //               timestamp.  If a packet could not be read, the
00202 //               packet is cleared and the packet_time is set to
00203 //               the specified default value.
00204 ////////////////////////////////////////////////////////////////////
00205 void FfmpegVideoCursor::
00206 fetch_packet(double default_time) {
00207   if (_packet->data) {
00208     av_free_packet(_packet);
00209   }
00210   while (av_read_frame(_format_ctx, _packet) >= 0) {
00211     if (_packet->stream_index == _video_index) {
00212       _packet_time = _packet->dts * _video_timebase;
00213       return;
00214     }
00215     av_free_packet(_packet);
00216   }
00217   _packet->data = 0;
00218   _packet_time = default_time;
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //     Function: FfmpegVideoCursor::fetch_frame
00223 //       Access: Protected
00224 //  Description: Fetches a frame from the stream and stores it in
00225 //               the frame buffer.  Sets last_start and next_start
00226 //               to indicate the extents of the frame.
00227 ////////////////////////////////////////////////////////////////////
00228 void FfmpegVideoCursor::
00229 fetch_frame() {
00230   int finished = 0;
00231   _last_start = _packet_time;
00232   while (!finished && _packet->data) {
00233 #if LIBAVCODEC_VERSION_INT < 3414272
00234     avcodec_decode_video(_video_ctx, _frame,
00235                           &finished, _packet->data, _packet->size);
00236 #else
00237     avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
00238 #endif
00239     fetch_packet(_last_start + 1.0);
00240   }
00241   _next_start = _packet_time;
00242 }
00243 
00244 ////////////////////////////////////////////////////////////////////
00245 //     Function: FfmpegVideoCursor::seek
00246 //       Access: Protected
00247 //  Description: Seeks to a target location.  Afterward, the
00248 //               packet_time is guaranteed to be less than or 
00249 //               equal to the specified time.
00250 ////////////////////////////////////////////////////////////////////
00251 void FfmpegVideoCursor::
00252 seek(double t) {
00253   PN_int64 target_ts = (PN_int64)(t / _video_timebase);
00254   if (target_ts < (PN_int64)(_initial_dts)) {
00255     // Attempts to seek before the first packet will fail.
00256     target_ts = _initial_dts;
00257   }
00258   if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
00259     if (t >= _packet_time) {
00260       return;
00261     }
00262     movies_cat.error() << "Seek failure. Shutting down movie.\n";
00263     cleanup();
00264     _packet_time = t;
00265     return;
00266   }
00267   avcodec_close(_video_ctx);
00268   AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
00269   if(pVideoCodec == 0) {
00270     cleanup();
00271     return;
00272   }
00273   if(avcodec_open(_video_ctx, pVideoCodec)<0) {
00274     cleanup();
00275     return;
00276   }
00277   fetch_packet(t);
00278   if (_packet_time > t) {
00279     _packet_time = t;
00280   }
00281 }
00282 
00283 ////////////////////////////////////////////////////////////////////
00284 //     Function: FfmpegVideoCursor::fetch_time
00285 //       Access: Public, Virtual
00286 //  Description: Advance until the specified time is in the 
00287 //               export buffer.
00288 ////////////////////////////////////////////////////////////////////
00289 void FfmpegVideoCursor::
00290 fetch_time(double time) {
00291   if (time < _last_start) {
00292     // Time is in the past.
00293     seek(time);
00294     while (_packet_time <= time) {
00295       fetch_frame();
00296     }
00297   } else if (time < _next_start) {
00298     // Time is in the present: already have the frame.
00299   } else if (time < _next_start + _min_fseek) {
00300     // Time is in the near future.
00301     while ((_packet_time <= time) && (_packet->data)) {
00302       fetch_frame();
00303     }
00304   } else {
00305     // Time is in the far future.  Seek forward, then read.
00306     // There's a danger here: because keyframes are spaced
00307     // unpredictably, trying to seek forward could actually
00308     // move us backward in the stream!  This must be avoided.
00309     // So the rule is, try the seek.  If it hurts us by moving
00310     // us backward, we increase the minimum threshold distance
00311     // for forward-seeking in the future.
00312     
00313     double base = _packet_time;
00314     seek(time);
00315     if (_packet_time < base) {
00316       _min_fseek += (base - _packet_time);
00317     }
00318     while (_packet_time <= time) {
00319       fetch_frame();
00320     }
00321   }
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: FfmpegVideoCursor::fetch_into_texture
00326 //       Access: Public, Virtual
00327 //  Description: See MovieVideoCursor::fetch_into_texture.
00328 ////////////////////////////////////////////////////////////////////
00329 static PStatCollector fetch_into_texture_pcollector("*:FFMPEG Video Decoding");
00330 void FfmpegVideoCursor::
00331 fetch_into_texture(double time, Texture *t, int page) {
00332   PStatTimer timer(fetch_into_texture_pcollector);
00333   
00334   nassertv(t->get_x_size() >= size_x());
00335   nassertv(t->get_y_size() >= size_y());
00336   nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
00337   nassertv(t->get_component_width() == 1);
00338   nassertv(page < t->get_z_size());
00339   
00340   PTA_uchar img = t->modify_ram_image();
00341   
00342   unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
00343   
00344   // If there was an error at any point, synthesize black.
00345   if (_format_ctx==0) {
00346     if (data) {
00347       memset(data,0,t->get_x_size() * t->get_y_size() * t->get_num_components());
00348     }
00349     _last_start = time;
00350     _next_start = time + 1.0;
00351     return;
00352   }
00353   
00354   fetch_time(time);
00355   export_frame(data, (t->get_num_components()==4), t->get_x_size());
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: FfmpegVideoCursor::fetch_into_buffer
00360 //       Access: Public, Virtual
00361 //  Description: See MovieVideoCursor::fetch_into_buffer.
00362 ////////////////////////////////////////////////////////////////////
00363 static PStatCollector fetch_into_buffer_pcollector("*:FFMPEG Video Decoding");
00364 void FfmpegVideoCursor::
00365 fetch_into_buffer(double time, unsigned char *data, bool bgra) {
00366   PStatTimer timer(fetch_into_buffer_pcollector);
00367   
00368   // If there was an error at any point, synthesize black.
00369   if (_format_ctx==0) {
00370     if (data) {
00371       memset(data,0,size_x()*size_y()*(bgra?4:3));
00372     }
00373     _last_start = time;
00374     _next_start = time + 1.0;
00375     return;
00376   }
00377 
00378   fetch_time(time);
00379   export_frame(data, bgra, _size_x);
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 
00384 #endif // HAVE_FFMPEG
 All Classes Functions Variables Enumerations