00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00034
00035
00036
00037 FfmpegVirtualFile::
00038 FfmpegVirtualFile() :
00039 _format_context(NULL),
00040 _in(NULL),
00041 _owns_in(false)
00042 {
00043 }
00044
00045
00046
00047
00048
00049
00050 FfmpegVirtualFile::
00051 ~FfmpegVirtualFile() {
00052 close();
00053 }
00054
00055
00056
00057
00058
00059
00060 FfmpegVirtualFile::
00061 FfmpegVirtualFile(const FfmpegVirtualFile ©) {
00062 nassertv(false);
00063 }
00064
00065
00066
00067
00068
00069
00070 void FfmpegVirtualFile::
00071 operator = (const FfmpegVirtualFile ©) {
00072 nassertv(false);
00073 }
00074
00075
00076
00077
00078
00079
00080
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
00109
00110
00111
00112
00113
00114
00115
00116
00117 ostringstream strm;
00118 strm << "pandavfs://" << (void *)this;
00119 string url = strm.str();
00120
00121
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
00134
00135
00136
00137
00138
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
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 ostringstream strm;
00174 strm << "pandavfs://" << (void *)this;
00175 string url = strm.str();
00176
00177
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
00190
00191
00192
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
00211
00212
00213
00214
00215 void FfmpegVirtualFile::
00216 register_protocol() {
00217 static bool initialized = false;
00218 if (initialized) {
00219 return;
00220 }
00221
00222
00223
00224 av_register_all();
00225
00226
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
00253 av_log_set_callback(&log_callback);
00254 }
00255
00256
00257
00258
00259
00260
00261
00262
00263 int FfmpegVirtualFile::
00264 pandavfs_open(URLContext *h, const char *filename, int flags) {
00265 filename += 11;
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
00277
00278
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
00286
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
00305
00306
00307
00308
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
00319
00320
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
00338
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
00358
00359
00360
00361
00362 int FfmpegVirtualFile::
00363 pandavfs_close(URLContext *h) {
00364
00365 h->priv_data = 0;
00366 return 0;
00367 }
00368
00369
00370
00371
00372
00373
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 {
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