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