Panda3D
 All Classes Functions Variables Enumerations
ffmpegVirtualFile.cxx
1 // Filename: ffmpegVirtualFile.cxx
2 // Created by: jyelon (02Jul07)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandabase.h"
16 
17 #include "config_ffmpeg.h"
18 #include "ffmpegVirtualFile.h"
19 #include "virtualFileSystem.h"
20 
21 extern "C" {
22  #include "libavcodec/avcodec.h"
23  #include "libavformat/avformat.h"
24 }
25 
26 #ifndef AVSEEK_SIZE
27  #define AVSEEK_SIZE 0x10000
28 #endif
29 
30 ////////////////////////////////////////////////////////////////////
31 // Function: FfmpegVirtualFile::Constructor
32 // Access: Public
33 // Description:
34 ///////////////////////////////p/////////////////////////////////////
35 FfmpegVirtualFile::
36 FfmpegVirtualFile() :
37  _io_context(NULL),
38  _format_context(NULL),
39  _in(NULL),
40  _owns_in(false),
41  _buffer_size(ffmpeg_read_buffer_size)
42 {
43 }
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: FfmpegVirtualFile::Destructor
47 // Access: Public
48 // Description:
49 ////////////////////////////////////////////////////////////////////
50 FfmpegVirtualFile::
51 ~FfmpegVirtualFile() {
52  close();
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: FfmpegVirtualFile::Copy Constructor
57 // Access: Private
58 // Description: These objects are not meant to be copied.
59 ////////////////////////////////////////////////////////////////////
60 FfmpegVirtualFile::
61 FfmpegVirtualFile(const FfmpegVirtualFile &copy) {
62  nassertv(false);
63 }
64 
65 ////////////////////////////////////////////////////////////////////
66 // Function: FfmpegVirtualFile::Copy Assignment Operator
67 // Access: Private
68 // Description: These objects are not meant to be copied.
69 ////////////////////////////////////////////////////////////////////
70 void FfmpegVirtualFile::
71 operator = (const FfmpegVirtualFile &copy) {
72  nassertv(false);
73 }
74 
75 ////////////////////////////////////////////////////////////////////
76 // Function: FfmpegVirtualFile::open_vfs
77 // Access: Public
78 // Description: Opens the movie file via Panda's VFS. Returns true
79 // on success, false on failure. If successful, use
80 // get_format_context() to get the open file handle.
81 ////////////////////////////////////////////////////////////////////
83 open_vfs(const Filename &filename) {
84  close();
85 
86  if (ffmpeg_cat.is_debug()) {
87  ffmpeg_cat.debug()
88  << "ffmpeg open_vfs(" << filename << ")\n";
89  }
90 
92  Filename fname = filename;
93  fname.set_binary();
94  PT(VirtualFile) vfile = vfs->get_file(fname);
95  if (vfile == NULL) {
96  return false;
97  }
98 
99  _in = vfile->open_read_file(true);
100  if (_in == NULL) {
101  return false;
102  }
103 
104  _owns_in = true;
105  _start = 0;
106  _size = vfile->get_file_size(_in);
107 
108  // NOTE: The AVIO system owns the buffer after allocation and may realloc it
109  // internally. Therefore, when we're done with the buffer, we use
110  // _io_context->buffer to deallocate it rather than holding on to this pointer.
111  unsigned char *buffer = (unsigned char*) av_malloc(_buffer_size);
112  _io_context = avio_alloc_context(buffer, _buffer_size, 0, (void*) this,
113  &read_packet, 0, &seek);
114 
115  _format_context = avformat_alloc_context();
116  _format_context->pb = _io_context;
117 
118  // Now we can open the stream.
119  int result =
120 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 4, 0)
121  avformat_open_input(&_format_context, "", NULL, NULL);
122 #else
123  av_open_input_file(&_format_context, "", NULL, 0, NULL);
124 #endif
125  if (result < 0) {
126  close();
127  return false;
128  }
129 
130  return true;
131 }
132 
133 ////////////////////////////////////////////////////////////////////
134 // Function: FfmpegVirtualFile::open_subfile
135 // Access: Public
136 // Description: Opens the movie file directly from a file on disk
137 // (does not go through the VFS). Returns true on
138 // success, false on failure. If successful, use
139 // get_format_context() to get the open file handle.
140 ////////////////////////////////////////////////////////////////////
142 open_subfile(const SubfileInfo &info) {
143  close();
144 
145  Filename fname = info.get_filename();
146  fname.set_binary();
147  if (!fname.open_read(_file_in)) {
148  return false;
149  }
150  if (ffmpeg_cat.is_debug()) {
151  ffmpeg_cat.debug()
152  << "ffmpeg open_subfile(" << fname << ")\n";
153  }
154 
155  _in = &_file_in;
156  _owns_in = false;
157  _start = info.get_start();
158  _size = info.get_size();
159 
160  _in->seekg(_start);
161 
162  // NOTE: The AVIO system owns the buffer after allocation and may realloc it
163  // internally. Therefore, when we're done with the buffer, we use
164  // _io_context->buffer to deallocate it rather than holding on to this pointer.
165  unsigned char *buffer = (unsigned char*) av_malloc(_buffer_size);
166  _io_context = avio_alloc_context(buffer, _buffer_size, 0, (void*) this,
167  &read_packet, 0, &seek);
168 
169  _format_context = avformat_alloc_context();
170  _format_context->pb = _io_context;
171 
172  // Now we can open the stream.
173  int result =
174 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 4, 0)
175  avformat_open_input(&_format_context, fname.c_str(), NULL, NULL);
176 #else
177  av_open_input_file(&_format_context, fname.c_str(), NULL, 0, NULL);
178 #endif
179  if (result < 0) {
180  close();
181  return false;
182  }
183 
184  return true;
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: FfmpegVirtualFile::close
189 // Access: Public
190 // Description: Explicitly closes the opened file. This is also
191 // called implicitly by the destructor if necessary.
192 ////////////////////////////////////////////////////////////////////
194 close() {
195  if (_format_context != NULL) {
196 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0)
197  avformat_close_input(&_format_context);
198 #else
199  av_close_input_file(_format_context);
200  _format_context = NULL;
201 #endif
202  }
203 
204  if (_io_context != NULL) {
205  if (_io_context->buffer != NULL) {
206  av_free(_io_context->buffer);
207  }
208  av_free(_io_context);
209  _io_context = NULL;
210  }
211 
212  if (_owns_in) {
213  nassertv(_in != NULL);
215  _owns_in = false;
216  }
217  _in = NULL;
218 }
219 
220 ////////////////////////////////////////////////////////////////////
221 // Function: FfmpegVirtualFile::register_protocol
222 // Access: Public, Static
223 // Description: Should be called at startup to attach the appropriate
224 // hooks between Panda and FFMpeg.
225 ////////////////////////////////////////////////////////////////////
228  static bool initialized = false;
229  if (initialized) {
230  return;
231  }
232 
233  // Here's a good place to call this global ffmpeg initialization
234  // function.
235  av_register_all();
236 
237  // And this one.
238 #if LIBAVFORMAT_VERSION_INT >= 0x351400
239  avformat_network_init();
240 #endif
241 
242  // Let's also register the logging to Panda's notify callback.
243  av_log_set_callback(&log_callback);
244 }
245 
246 ////////////////////////////////////////////////////////////////////
247 // Function: FfmpegVirtualFile::read_packet
248 // Access: Private, Static
249 // Description: A callback to read a virtual file.
250 ////////////////////////////////////////////////////////////////////
251 int FfmpegVirtualFile::
252 read_packet(void *opaque, uint8_t *buf, int size) {
253  streampos ssize = (streampos)size;
254  FfmpegVirtualFile *self = (FfmpegVirtualFile *) opaque;
255  istream *in = self->_in;
256 
257  // Since we may be simulating a subset of the opened stream, don't
258  // allow it to read past the "end".
259  streampos remaining = self->_start + (streampos)self->_size - in->tellg();
260  if (remaining < ssize) {
261  if (remaining <= 0) {
262  return 0;
263  }
264 
265  ssize = remaining;
266  }
267 
268  in->read((char *)buf, ssize);
269  streamsize gc = in->gcount();
270  in->clear();
271 
272  return (int)gc;
273 }
274 
275 ////////////////////////////////////////////////////////////////////
276 // Function: FfmpegVirtualFile::seek
277 // Access: Private, Static
278 // Description: A callback to change the read position on an istream.
279 ////////////////////////////////////////////////////////////////////
280 int64_t FfmpegVirtualFile::
281 seek(void *opaque, int64_t pos, int whence) {
282  FfmpegVirtualFile *self = (FfmpegVirtualFile *) opaque;
283  istream *in = self->_in;
284 
285  switch (whence) {
286  case SEEK_SET:
287  in->seekg(self->_start + (streampos)pos, ios::beg);
288  break;
289 
290  case SEEK_CUR:
291  in->seekg(pos, ios::cur);
292  break;
293 
294  case SEEK_END:
295  // For seeks relative to the end, we actually compute the end
296  // based on _start + _size, and then use ios::beg.
297  in->seekg(self->_start + (streampos)self->_size + (streampos)pos, ios::beg);
298  break;
299 
300  case AVSEEK_SIZE:
301  return self->_size;
302 
303  default:
304  ffmpeg_cat.error()
305  << "Illegal parameter to seek in FfmpegVirtualFile\n";
306  in->clear();
307  return -1;
308  }
309 
310  in->clear();
311  return in->tellg() - self->_start;
312 }
313 
314 ////////////////////////////////////////////////////////////////////
315 // Function: FfmpegVirtualFile::log_callback
316 // Access: Private, Static
317 // Description: These callbacks are made when ffmpeg wants to write a
318 // log entry; it redirects into Panda's notify.
319 ////////////////////////////////////////////////////////////////////
320 void FfmpegVirtualFile::
321 log_callback(void *ptr, int level, const char *fmt, va_list v1) {
322  NotifySeverity severity;
323 #ifdef AV_LOG_PANIC
324  if (level <= AV_LOG_PANIC) {
325  severity = NS_fatal;
326  } else
327 #endif
328  if (level <= AV_LOG_ERROR) {
329  severity = NS_error;
330 #ifdef AV_LOG_WARNING
331  } else if (level <= AV_LOG_WARNING) {
332  severity = NS_warning;
333 #endif
334  } else if (level <= AV_LOG_INFO) {
335  severity = NS_info;
336 #ifdef AV_LOG_VERBOSE
337  } else if (level <= AV_LOG_VERBOSE) {
338  severity = NS_debug;
339 #endif
340  } else /* level <= AV_LOG_DEBUG */ {
341  severity = NS_spam;
342  }
343 
344  if (ffmpeg_cat.is_on(severity)) {
345  static const size_t buffer_size = 4096;
346  char *buffer = (char *)alloca(buffer_size);
347  vsnprintf(buffer, buffer_size, fmt, v1);
348  nassertv(strlen(buffer) < buffer_size);
349  ffmpeg_cat.out(severity, true)
350  << buffer;
351  }
352 }
streamsize get_size() const
Returns the number of consecutive bytes, beginning at get_start(), that correspond to this file data...
Definition: subfileInfo.I:132
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
bool open_vfs(const Filename &filename)
Opens the movie file via Panda&#39;s VFS.
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
static void register_protocol()
Should be called at startup to attach the appropriate hooks between Panda and FFMpeg.
void close()
Explicitly closes the opened file.
streampos get_start() const
Returns the offset within the file at which this file data begins.
Definition: subfileInfo.I:121
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
Enables ffmpeg to access panda&#39;s VFS.
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
const Filename & get_filename() const
A shortcut to the filename.
Definition: subfileInfo.I:106
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:29
bool open_subfile(const SubfileInfo &info)
Opens the movie file directly from a file on disk (does not go through the VFS).