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
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 */
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 */
58 bool FfmpegVirtualFile::
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
67 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
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 */
111 bool FfmpegVirtualFile::
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 */
158 void FfmpegVirtualFile::
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);
174 VirtualFileSystem::close_read_file(_in);
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 */
184 void FfmpegVirtualFile::
185 register_protocol() {
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::
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 */
272 void 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}
FfmpegVirtualFile
Enables ffmpeg to access panda's VFS.
Definition ffmpegVirtualFile.h:34
FfmpegVirtualFile::register_protocol
static void register_protocol()
Should be called at startup to attach the appropriate hooks between Panda and FFMpeg.
Definition ffmpegVirtualFile.cxx:185
FfmpegVirtualFile::open_vfs
bool open_vfs(const Filename &filename)
Opens the movie file via Panda's VFS.
Definition ffmpegVirtualFile.cxx:59
FfmpegVirtualFile::open_subfile
bool open_subfile(const SubfileInfo &info)
Opens the movie file directly from a file on disk (does not go through the VFS).
Definition ffmpegVirtualFile.cxx:112
FfmpegVirtualFile::close
void close()
Explicitly closes the opened file.
Definition ffmpegVirtualFile.cxx:159
Filename
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
Filename::open_read
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition filename.cxx:1872
Filename::set_binary
void set_binary()
Indicates that the filename represents a binary file.
Definition filename.I:414
SubfileInfo
This class records a particular byte sub-range within an existing file on disk.
Definition subfileInfo.h:26
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition virtualFileSystem.h:40
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition virtualFileSystem.cxx:866
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition virtualFileSystem.cxx:741
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition virtualFile.h:35
config_ffmpeg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ffmpegVirtualFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.