Panda3D
Loading...
Searching...
No Matches
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
20using std::streampos;
21using std::streamsize;
22
23extern "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 */
35FfmpegVirtualFile::
36FfmpegVirtualFile() :
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 */
48FfmpegVirtualFile::
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 */
59open_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 */
112open_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 */
159close() {
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 */
207int FfmpegVirtualFile::
208read_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 */
234int64_t FfmpegVirtualFile::
235seek(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 */
272void FfmpegVirtualFile::
273log_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}
Enables ffmpeg to access panda's VFS.
static void register_protocol()
Should be called at startup to attach the appropriate hooks between Panda and FFMpeg.
bool open_vfs(const Filename &filename)
Opens the movie file via Panda's VFS.
bool open_subfile(const SubfileInfo &info)
Opens the movie file directly from a file on disk (does not go through the VFS).
void close()
Explicitly closes the opened file.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
void set_binary()
Indicates that the filename represents a binary file.
Definition filename.I:414
This class records a particular byte sub-range within an existing file on disk.
Definition subfileInfo.h:26
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.