Panda3D
|
00001 // Filename: ffmpegVirtualFile.cxx 00002 // Created by: jyelon (02Jul07) 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 #include "pandabase.h" 00016 00017 #ifdef HAVE_FFMPEG 00018 00019 #include "config_movies.h" 00020 #include "ffmpegVirtualFile.h" 00021 #include "virtualFileSystem.h" 00022 00023 extern "C" { 00024 #include "libavcodec/avcodec.h" 00025 #include "libavformat/avformat.h" 00026 } 00027 00028 #ifndef AVSEEK_SIZE 00029 #define AVSEEK_SIZE 0x10000 00030 #endif 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: FfmpegVirtualFile::Constructor 00034 // Access: Public 00035 // Description: 00036 //////////////////////////////////////////////////////////////////// 00037 FfmpegVirtualFile:: 00038 FfmpegVirtualFile() : 00039 _format_context(NULL), 00040 _in(NULL), 00041 _owns_in(false) 00042 { 00043 } 00044 00045 //////////////////////////////////////////////////////////////////// 00046 // Function: FfmpegVirtualFile::Destructor 00047 // Access: Public 00048 // Description: 00049 //////////////////////////////////////////////////////////////////// 00050 FfmpegVirtualFile:: 00051 ~FfmpegVirtualFile() { 00052 close(); 00053 } 00054 00055 //////////////////////////////////////////////////////////////////// 00056 // Function: FfmpegVirtualFile::Copy Constructor 00057 // Access: Private 00058 // Description: These objects are not meant to be copied. 00059 //////////////////////////////////////////////////////////////////// 00060 FfmpegVirtualFile:: 00061 FfmpegVirtualFile(const FfmpegVirtualFile ©) { 00062 nassertv(false); 00063 } 00064 00065 //////////////////////////////////////////////////////////////////// 00066 // Function: FfmpegVirtualFile::Copy Assignment Operator 00067 // Access: Private 00068 // Description: These objects are not meant to be copied. 00069 //////////////////////////////////////////////////////////////////// 00070 void FfmpegVirtualFile:: 00071 operator = (const FfmpegVirtualFile ©) { 00072 nassertv(false); 00073 } 00074 00075 //////////////////////////////////////////////////////////////////// 00076 // Function: FfmpegVirtualFile::open_vfs 00077 // Access: Public 00078 // Description: Opens the movie file via Panda's VFS. Returns true 00079 // on success, false on failure. If successful, use 00080 // get_format_context() to get the open file handle. 00081 //////////////////////////////////////////////////////////////////// 00082 bool FfmpegVirtualFile:: 00083 open_vfs(const Filename &filename) { 00084 close(); 00085 00086 if (ffmpeg_cat.is_debug()) { 00087 ffmpeg_cat.debug() 00088 << "ffmpeg open_vfs(" << filename << ")\n"; 00089 } 00090 00091 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00092 Filename fname = filename; 00093 fname.set_binary(); 00094 PT(VirtualFile) vfile = vfs->get_file(fname); 00095 if (vfile == NULL) { 00096 return false; 00097 } 00098 00099 _in = vfile->open_read_file(true); 00100 if (_in == NULL) { 00101 return false; 00102 } 00103 00104 _owns_in = true; 00105 _start = 0; 00106 _size = vfile->get_file_size(_in); 00107 00108 // I tried to use av_open_input_stream(), but it (a) required a lot 00109 // of low-level stream analysis calls that really should be 00110 // automatic (and are automatic in av_open_input_file()), and (b) 00111 // was broken on the ffmpeg build I happened to grab. Screw it, 00112 // clearly av_open_input_file() is the preferred and more 00113 // heavily-exercised interface. So we'll continue to use url 00114 // synthesis as a hacky hook into this interface. 00115 00116 // Nowadays we synthesize a "url" that references this pointer. 00117 ostringstream strm; 00118 strm << "pandavfs://" << (void *)this; 00119 string url = strm.str(); 00120 00121 // Now we can open the stream. 00122 int result = 00123 av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL); 00124 if (result < 0) { 00125 close(); 00126 return false; 00127 } 00128 00129 return true; 00130 } 00131 00132 //////////////////////////////////////////////////////////////////// 00133 // Function: FfmpegVirtualFile::open_subfile 00134 // Access: Public 00135 // Description: Opens the movie file directly from a file on disk 00136 // (does not go through the VFS). Returns true on 00137 // success, false on failure. If successful, use 00138 // get_format_context() to get the open file handle. 00139 //////////////////////////////////////////////////////////////////// 00140 bool FfmpegVirtualFile:: 00141 open_subfile(const SubfileInfo &info) { 00142 close(); 00143 00144 Filename fname = info.get_filename(); 00145 fname.set_binary(); 00146 if (!fname.open_read(_file_in)) { 00147 return false; 00148 } 00149 if (ffmpeg_cat.is_debug()) { 00150 ffmpeg_cat.debug() 00151 << "ffmpeg open_subfile(" << fname << ")\n"; 00152 } 00153 00154 _in = &_file_in; 00155 _owns_in = false; 00156 _start = info.get_start(); 00157 _size = info.get_size(); 00158 00159 _in->seekg(_start); 00160 00161 // I tried to use av_open_input_stream(), but it (a) required a lot 00162 // of low-level ffmpeg calls that really shouldn't be part of the 00163 // public API (and which aren't necessary with av_open_input_file() 00164 // because they happen implicitly there), and (b) was completely 00165 // broken on the ffmpeg build I happened to grab. Screw it; clearly 00166 // av_open_input_file() is the preferred and more heavily-exercised 00167 // interface. So we'll use it, even though it requires a bit of a 00168 // hack. 00169 00170 // The hack is that we synthesize a "url" that references this 00171 // pointer, then open that url. This calls pandavfs_open(), which 00172 // decodes the pointer and stores it for future callbacks. 00173 ostringstream strm; 00174 strm << "pandavfs://" << (void *)this; 00175 string url = strm.str(); 00176 00177 // Now we can open the stream. 00178 int result = 00179 av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL); 00180 if (result < 0) { 00181 close(); 00182 return false; 00183 } 00184 00185 return true; 00186 } 00187 00188 //////////////////////////////////////////////////////////////////// 00189 // Function: FfmpegVirtualFile::close 00190 // Access: Public 00191 // Description: Explicitly closes the opened file. This is also 00192 // called implicitly by the destructor if necessary. 00193 //////////////////////////////////////////////////////////////////// 00194 void FfmpegVirtualFile:: 00195 close() { 00196 if (_format_context != NULL) { 00197 av_close_input_file(_format_context); 00198 _format_context = NULL; 00199 } 00200 00201 if (_owns_in) { 00202 nassertv(_in != NULL); 00203 VirtualFileSystem::close_read_file(_in); 00204 _owns_in = false; 00205 } 00206 _in = NULL; 00207 } 00208 00209 //////////////////////////////////////////////////////////////////// 00210 // Function: FfmpegVirtualFile::register_protocol 00211 // Access: Public, Static 00212 // Description: Should be called at startup to attach the appropriate 00213 // hooks between Panda and FFMpeg. 00214 //////////////////////////////////////////////////////////////////// 00215 void FfmpegVirtualFile:: 00216 register_protocol() { 00217 static bool initialized = false; 00218 if (initialized) { 00219 return; 00220 } 00221 00222 // Here's a good place to call this global ffmpeg initialization 00223 // function. 00224 av_register_all(); 00225 00226 // And this one. 00227 #if LIBAVFORMAT_VERSION_INT >= 0x351400 00228 avformat_network_init(); 00229 #endif 00230 00231 static URLProtocol protocol; 00232 protocol.name = "pandavfs"; 00233 protocol.url_open = pandavfs_open; 00234 protocol.url_read = pandavfs_read; 00235 00236 #if LIBAVFORMAT_VERSION_INT < 3425280 00237 protocol.url_write = (int (*)(URLContext *, unsigned char *, int))pandavfs_write; 00238 #else 00239 protocol.url_write = pandavfs_write; 00240 #endif 00241 00242 protocol.url_seek = pandavfs_seek; 00243 protocol.url_close = pandavfs_close; 00244 #if LIBAVFORMAT_VERSION_INT < 3415296 00245 ::register_protocol(&protocol); 00246 #elif LIBAVFORMAT_VERSION_MAJOR < 53 00247 av_register_protocol(&protocol); 00248 #else 00249 av_register_protocol2(&protocol, sizeof(protocol)); 00250 #endif 00251 00252 // Let's also register the logging to Panda's notify callback. 00253 av_log_set_callback(&log_callback); 00254 } 00255 00256 //////////////////////////////////////////////////////////////////// 00257 // Function: FfmpegVirtualFile::pandavfs_open 00258 // Access: Private, Static 00259 // Description: A callback to "open" a virtual file. Actually, all 00260 // this does is assign the pointer back to the 00261 // FfmpegVirtualFile instance. 00262 //////////////////////////////////////////////////////////////////// 00263 int FfmpegVirtualFile:: 00264 pandavfs_open(URLContext *h, const char *filename, int flags) { 00265 filename += 11; // Skip over "pandavfs://" 00266 istringstream strm(filename); 00267 void *ptr = 0; 00268 strm >> ptr; 00269 00270 FfmpegVirtualFile *self = (FfmpegVirtualFile *)ptr; 00271 h->priv_data = self; 00272 return 0; 00273 } 00274 00275 //////////////////////////////////////////////////////////////////// 00276 // Function: FfmpegVirtualFile::pandavfs_read 00277 // Access: Private, Static 00278 // Description: A callback to read a virtual file. 00279 //////////////////////////////////////////////////////////////////// 00280 int FfmpegVirtualFile:: 00281 pandavfs_read(URLContext *h, unsigned char *buf, int size) { 00282 FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); 00283 istream *in = self->_in; 00284 00285 // Since we may be simulating a subset of the opened stream, don't 00286 // allow it to read past the "end". 00287 streampos remaining = self->_start + (streampos)self->_size - in->tellg(); 00288 if (remaining < size) { 00289 if (remaining <= 0) { 00290 return 0; 00291 } 00292 00293 size = (int)remaining; 00294 } 00295 00296 in->read((char *)buf, size); 00297 int gc = in->gcount(); 00298 in->clear(); 00299 00300 return gc; 00301 } 00302 00303 //////////////////////////////////////////////////////////////////// 00304 // Function: FfmpegVirtualFile::pandavfs_write 00305 // Access: Private, Static 00306 // Description: A callback to write a virtual file. Unimplemented, 00307 // because we use ffmpeg for playback only, not for 00308 // encoding video streams. 00309 //////////////////////////////////////////////////////////////////// 00310 int FfmpegVirtualFile:: 00311 pandavfs_write(URLContext *h, const unsigned char *buf, int size) { 00312 ffmpeg_cat.warning() 00313 << "ffmpeg is trying to write to the VFS.\n"; 00314 return -1; 00315 } 00316 00317 //////////////////////////////////////////////////////////////////// 00318 // Function: FfmpegVirtualFile::pandavfs_seek 00319 // Access: Private, Static 00320 // Description: A callback to change the read position on an istream. 00321 //////////////////////////////////////////////////////////////////// 00322 int64_t FfmpegVirtualFile:: 00323 pandavfs_seek(URLContext *h, int64_t pos, int whence) { 00324 FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); 00325 istream *in = self->_in; 00326 00327 switch(whence) { 00328 case SEEK_SET: 00329 in->seekg(self->_start + (streampos)pos, ios::beg); 00330 break; 00331 00332 case SEEK_CUR: 00333 in->seekg(pos, ios::cur); 00334 break; 00335 00336 case SEEK_END: 00337 // For seeks relative to the end, we actually compute the end 00338 // based on _start + _size, and then use ios::beg. 00339 in->seekg(self->_start + (streampos)self->_size + (streampos)pos, ios::beg); 00340 break; 00341 00342 case AVSEEK_SIZE: 00343 return self->_size; 00344 00345 default: 00346 ffmpeg_cat.error() 00347 << "Illegal parameter to seek in ffmpegVirtualFile\n"; 00348 in->clear(); 00349 return -1; 00350 } 00351 00352 in->clear(); 00353 return in->tellg() - self->_start; 00354 } 00355 00356 //////////////////////////////////////////////////////////////////// 00357 // Function: FfmpegVirtualFile::pandavfs_close 00358 // Access: Private, Static 00359 // Description: A hook to "close" a panda VFS file. Actually it only 00360 // clears the associated pointer. 00361 //////////////////////////////////////////////////////////////////// 00362 int FfmpegVirtualFile:: 00363 pandavfs_close(URLContext *h) { 00364 //FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); 00365 h->priv_data = 0; 00366 return 0; 00367 } 00368 00369 //////////////////////////////////////////////////////////////////// 00370 // Function: FfmpegVirtualFile::log_callback 00371 // Access: Private, Static 00372 // Description: These callbacks are made when ffmpeg wants to write a 00373 // log entry; it redirects into Panda's notify. 00374 //////////////////////////////////////////////////////////////////// 00375 void FfmpegVirtualFile:: 00376 log_callback(void *ptr, int level, const char *fmt, va_list v1) { 00377 NotifySeverity severity; 00378 #ifdef AV_LOG_PANIC 00379 if (level <= AV_LOG_PANIC) { 00380 severity = NS_fatal; 00381 } else 00382 #endif 00383 if (level <= AV_LOG_ERROR) { 00384 severity = NS_error; 00385 #ifdef AV_LOG_WARNING 00386 } else if (level <= AV_LOG_WARNING) { 00387 severity = NS_warning; 00388 #endif 00389 } else if (level <= AV_LOG_INFO) { 00390 severity = NS_info; 00391 #ifdef AV_LOG_VERBOSE 00392 } else if (level <= AV_LOG_VERBOSE) { 00393 severity = NS_debug; 00394 #endif 00395 } else /* level <= AV_LOG_DEBUG */ { 00396 severity = NS_spam; 00397 } 00398 00399 if (ffmpeg_cat.is_on(severity)) { 00400 static const size_t buffer_size = 4096; 00401 char *buffer = (char *)alloca(buffer_size); 00402 vsnprintf(buffer, buffer_size, fmt, v1); 00403 nassertv(strlen(buffer) < buffer_size); 00404 ffmpeg_cat.out(severity, true) 00405 << buffer; 00406 } 00407 } 00408 00409 #endif // HAVE_FFMPEG