Panda3D
|
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