diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index 95d77c5d32..33985cabb9 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -25,7 +25,7 @@ #include "config_audio.h" #include "fmodAudioSound.h" #include "string_utils.h" -#include "fileSystemInfo.h" +#include "subfileInfo.h" TypeHandle FmodAudioSound::_type_handle; @@ -104,9 +104,10 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { } const char *name_or_data = _file_name.c_str(); + string os_filename; pvector mem_buffer; - FileSystemInfo info; + SubfileInfo info; if (preload) { // Pre-read the file right now, and pass it in as a memory // buffer. This avoids threading issues completely, because all @@ -129,9 +130,10 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { // This is also safe, because FMod uses its own I/O operations // that don't involve Panda, so this can safely happen in an // FMod thread. - name_or_data = info.get_os_file_name().c_str(); - sound_info.fileoffset = (unsigned int)info.get_file_start(); - sound_info.length = (unsigned int)info.get_file_size(); + os_filename = info.get_filename().to_os_specific(); + name_or_data = os_filename.c_str(); + sound_info.fileoffset = (unsigned int)info.get_start(); + sound_info.length = (unsigned int)info.get_size(); flags |= FMOD_CREATESTREAM; if (fmodAudio_cat.is_debug()) { fmodAudio_cat.debug() @@ -144,7 +146,7 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { // Otherwise, if the Panda threading system is compiled in, we // can assign callbacks to read the file through the VFS. name_or_data = (const char *)file.p(); - sound_info.length = (unsigned int)info.get_file_size(); + sound_info.length = (unsigned int)info.get_size(); sound_info.useropen = open_callback; sound_info.userclose = close_callback; sound_info.userread = read_callback; diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index 60e3c634dc..3c8022a857 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -13,8 +13,8 @@ ca_bundle_data_src.c \ checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \ circBuffer.h \ - config_express.h \ compress_string.h \ + config_express.h \ copy_stream.h \ datagram.I datagram.h datagramGenerator.I \ datagramGenerator.h \ @@ -23,7 +23,7 @@ encrypt_string.h \ error_utils.h \ export_dtool.h \ - fileSystemInfo.h fileSystemInfo.I \ + fileReference.h fileReference.I \ hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h \ indirectLess.I indirectLess.h \ @@ -34,11 +34,12 @@ multifile.I multifile.h \ namable.I \ namable.h \ - nodePointerToBase.h nodePointerToBase.I \ nodePointerTo.h nodePointerTo.I \ + nodePointerToBase.h nodePointerToBase.I \ nodeReferenceCount.h nodeReferenceCount.I \ openSSLWrapper.h openSSLWrapper.I \ ordered_vector.h ordered_vector.I ordered_vector.T \ + pStatCollectorForwardBase.h \ password_hash.h \ patchfile.I patchfile.h \ pointerTo.I pointerTo.h \ @@ -47,13 +48,14 @@ pointerToBase.I pointerToBase.h \ pointerToVoid.I pointerToVoid.h \ profileTimer.I profileTimer.h \ - pStatCollectorForwardBase.h \ - pta_uchar.h pta_float.h \ pta_int.h \ + pta_uchar.h pta_float.h \ ramfile.I ramfile.h \ referenceCount.I referenceCount.h \ stringDecoder.h stringDecoder.I \ subStream.I subStream.h subStreamBuf.h \ + subfileInfo.h subfileInfo.I \ + temporaryFile.h temporaryFile.I \ textEncoder.h textEncoder.I \ threadSafePointerTo.I threadSafePointerTo.h \ threadSafePointerToBase.I threadSafePointerToBase.h \ @@ -61,8 +63,8 @@ typedReferenceCount.I typedReferenceCount.h typedef.h \ unicodeLatinMap.h \ vector_uchar.h vector_float.h \ - virtualFileComposite.h virtualFileComposite.I virtualFile.h \ virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \ + virtualFileComposite.h virtualFileComposite.I virtualFile.h \ virtualFileMount.I virtualFileMountMultifile.h \ virtualFileMountMultifile.I virtualFileMountSystem.h \ virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \ @@ -77,24 +79,25 @@ #define INCLUDED_SOURCES \ buffer.cxx checksumHashGenerator.cxx \ - config_express.cxx \ compress_string.cxx \ + config_express.cxx \ copy_stream.cxx \ datagram.cxx datagramGenerator.cxx \ datagramIterator.cxx \ datagramSink.cxx dcast.cxx \ encrypt_string.cxx \ error_utils.cxx \ - fileSystemInfo.cxx \ + fileReference.cxx \ hashGeneratorBase.cxx hashVal.cxx \ memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \ memoryUsagePointers.cxx multifile.cxx \ namable.cxx \ - nodePointerToBase.cxx \ nodePointerTo.cxx \ + nodePointerToBase.cxx \ nodeReferenceCount.cxx \ openSSLWrapper.cxx \ ordered_vector.cxx \ + pStatCollectorForwardBase.cxx \ password_hash.cxx \ patchfile.cxx \ pointerTo.cxx \ @@ -102,13 +105,14 @@ pointerToBase.cxx \ pointerToVoid.cxx \ profileTimer.cxx \ - pStatCollectorForwardBase.cxx \ - pta_uchar.cxx pta_float.cxx \ pta_int.cxx \ + pta_uchar.cxx pta_float.cxx \ ramfile.cxx \ referenceCount.cxx \ stringDecoder.cxx \ subStream.cxx subStreamBuf.cxx \ + subfileInfo.cxx \ + temporaryFile.cxx \ textEncoder.cxx \ threadSafePointerTo.cxx \ threadSafePointerToBase.cxx \ @@ -133,8 +137,8 @@ ca_bundle_data_src.c \ checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \ circBuffer.h \ - config_express.h \ compress_string.h \ + config_express.h \ copy_stream.h \ datagram.I datagram.h datagramGenerator.I \ datagramGenerator.h \ @@ -142,7 +146,7 @@ dcast.T dcast.h \ encrypt_string.h \ error_utils.h \ - fileSystemInfo.h fileSystemInfo.I \ + fileReference.h fileReference.I \ hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h \ indirectLess.I indirectLess.h \ @@ -153,11 +157,12 @@ multifile.I multifile.h \ namable.I \ namable.h \ - nodePointerToBase.h nodePointerToBase.I \ nodePointerTo.h nodePointerTo.I \ + nodePointerToBase.h nodePointerToBase.I \ nodeReferenceCount.h nodeReferenceCount.I \ openSSLWrapper.h openSSLWrapper.I \ ordered_vector.h ordered_vector.I ordered_vector.T \ + pStatCollectorForwardBase.h \ password_hash.h \ patchfile.I patchfile.h \ pointerTo.I pointerTo.h \ @@ -166,13 +171,14 @@ pointerToBase.I pointerToBase.h \ pointerToVoid.I pointerToVoid.h \ profileTimer.I profileTimer.h \ - pStatCollectorForwardBase.h \ - pta_uchar.h pta_float.h \ pta_int.h \ + pta_uchar.h pta_float.h \ ramfile.I ramfile.h \ referenceCount.I referenceCount.h \ stringDecoder.h stringDecoder.I \ subStream.I subStream.h subStreamBuf.h \ + subfileInfo.h subfileInfo.I \ + temporaryFile.h temporaryFile.I \ textEncoder.h textEncoder.I \ threadSafePointerTo.I threadSafePointerTo.h \ threadSafePointerToBase.I threadSafePointerToBase.h \ @@ -180,8 +186,8 @@ typedReferenceCount.I typedReferenceCount.h typedef.h \ unicodeLatinMap.h \ vector_uchar.h vector_float.h \ - virtualFileComposite.h virtualFileComposite.I virtualFile.h \ virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \ + virtualFileComposite.h virtualFileComposite.I virtualFile.h \ virtualFileMount.I virtualFileMountMultifile.h \ virtualFileMountMultifile.I virtualFileMountSystem.h \ virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \ diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx index 9961ec5b8a..626912518d 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -25,6 +25,8 @@ #include "virtualFileMountMultifile.h" #include "virtualFileMountSystem.h" #include "virtualFileSimple.h" +#include "fileReference.h" +#include "temporaryFile.h" #include "pandaSystem.h" #include "numeric_types.h" #include "namable.h" @@ -98,6 +100,8 @@ init_libexpress() { VirtualFileMountMultifile::init_type(); VirtualFileMountSystem::init_type(); VirtualFileSimple::init_type(); + FileReference::init_type(); + TemporaryFile::init_type(); init_system_type_handles(); diff --git a/panda/src/express/datagramGenerator.I b/panda/src/express/datagramGenerator.I index ba70f70c62..a36578c5c3 100644 --- a/panda/src/express/datagramGenerator.I +++ b/panda/src/express/datagramGenerator.I @@ -15,11 +15,10 @@ //////////////////////////////////////////////////////////////////// // Function: DatagramGenerator::Constructor -// Access: Public +// Access: Published // Description: Does nothing since this is class is just // the definition of an interface //////////////////////////////////////////////////////////////////// - -INLINE DatagramGenerator::DatagramGenerator(){ +INLINE DatagramGenerator:: +DatagramGenerator() { } - diff --git a/panda/src/express/datagramGenerator.cxx b/panda/src/express/datagramGenerator.cxx index 61278a2eb8..1248fb8842 100644 --- a/panda/src/express/datagramGenerator.cxx +++ b/panda/src/express/datagramGenerator.cxx @@ -16,10 +16,11 @@ #include "pandabase.h" #include "datagramGenerator.h" +#include "temporaryFile.h" //////////////////////////////////////////////////////////////////// // Function: DatagramGenerator::Destructor -// Access: Public, Virtual +// Access: Published, Virtual // Description: Does nothing since this is class is just // the definition of an interface //////////////////////////////////////////////////////////////////// @@ -28,20 +29,67 @@ DatagramGenerator:: } //////////////////////////////////////////////////////////////////// -// Function: DatagramGenerator::get_file -// Access: Public, Virtual -// Description: Returns the VirtualFile that provides the source for -// these datagrams, if any, or NULL if the datagrams do -// not originate from a VirtualFile. +// Function: DatagramGenerator::save_datagram +// Access: Published, Virtual +// Description: Skips over the next datagram without extracting it, +// but saves the relevant file information in the +// SubfileInfo object so that its data may be read +// later. For non-file-based datagram generators, this +// may mean creating a temporary file and copying the +// contents of the datagram to disk. +// +// Returns true on success, false on failure or if this +// method is unimplemented. //////////////////////////////////////////////////////////////////// -VirtualFile *DatagramGenerator:: +bool DatagramGenerator:: +save_datagram(SubfileInfo &info) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramGenerator::get_filename +// Access: Published, Virtual +// Description: Returns the filename that provides the source for +// these datagrams, if any, or empty string if the +// datagrams do not originate from a file on disk. +//////////////////////////////////////////////////////////////////// +const Filename &DatagramGenerator:: +get_filename() { + const FileReference *file = get_file(); + if (file != (FileReference *)NULL) { + return file->get_filename(); + } + static const Filename empty_filename; + return empty_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramGenerator::get_file +// Access: Published, Virtual +// Description: Returns the FileReference that provides the source for +// these datagrams, if any, or NULL if the datagrams do +// not originate from a file on disk. +//////////////////////////////////////////////////////////////////// +const FileReference *DatagramGenerator:: get_file() { return NULL; } +//////////////////////////////////////////////////////////////////// +// Function: DatagramGenerator::get_vfile +// Access: Published, Virtual +// Description: Returns the VirtualFile that provides the source for +// these datagrams, if any, or NULL if the datagrams do +// not originate from a VirtualFile. +//////////////////////////////////////////////////////////////////// +VirtualFile *DatagramGenerator:: +get_vfile() { + return NULL; +} + //////////////////////////////////////////////////////////////////// // Function: DatagramGenerator::get_file_pos -// Access: Public, Virtual +// Access: Published, Virtual // Description: Returns the current file position within the data // stream, if any, or 0 if the file position is not // meaningful or cannot be determined. diff --git a/panda/src/express/datagramGenerator.h b/panda/src/express/datagramGenerator.h index a1fda45378..9c67a7bf46 100644 --- a/panda/src/express/datagramGenerator.h +++ b/panda/src/express/datagramGenerator.h @@ -19,13 +19,16 @@ #include "datagram.h" +class SubfileInfo; +class FileReference; +class Filename; class VirtualFile; //////////////////////////////////////////////////////////////////// // Class : DatagramGenerator // Description : This class defines the abstract interace to any // source of datagrams, whether it be from a file or -// from the net +// from the net. //////////////////////////////////////////////////////////////////// class EXPCL_PANDAEXPRESS DatagramGenerator { PUBLISHED: @@ -33,10 +36,13 @@ PUBLISHED: virtual ~DatagramGenerator(); virtual bool get_datagram(Datagram &data) = 0; + virtual bool save_datagram(SubfileInfo &info); virtual bool is_eof() = 0; virtual bool is_error() = 0; - virtual VirtualFile *get_file(); + virtual const Filename &get_filename(); + virtual const FileReference *get_file(); + virtual VirtualFile *get_vfile(); virtual streampos get_file_pos(); }; diff --git a/panda/src/express/datagramSink.cxx b/panda/src/express/datagramSink.cxx index 20b2217c13..fa1fe33a2b 100644 --- a/panda/src/express/datagramSink.cxx +++ b/panda/src/express/datagramSink.cxx @@ -13,6 +13,7 @@ //////////////////////////////////////////////////////////////////// #include "datagramSink.h" +#include "fileReference.h" //////////////////////////////////////////////////////////////////// // Function: DatagramSink::Destructor @@ -21,5 +22,86 @@ // the definition of an interface //////////////////////////////////////////////////////////////////// DatagramSink:: -~DatagramSink(){ +~DatagramSink() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::copy_datagram +// Access: Published, Virtual +// Description: Copies the file data from the entire indicated +// file (via the vfs) as the next datagram. This is +// intended to support potentially very large datagrams. +// +// Returns true on success, false on failure or if this +// method is unimplemented. On true, fills "result" +// with the information that references the copied file, +// if possible. +//////////////////////////////////////////////////////////////////// +bool DatagramSink:: +copy_datagram(SubfileInfo &result, const Filename &filename) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::copy_datagram +// Access: Published, Virtual +// Description: Copies the file data from the range of the indicated +// file (outside of the vfs) as the next datagram. This +// is intended to support potentially very large +// datagrams. +// +// Returns true on success, false on failure or if this +// method is unimplemented. On true, fills "result" +// with the information that references the copied file, +// if possible. +//////////////////////////////////////////////////////////////////// +bool DatagramSink:: +copy_datagram(SubfileInfo &result, const SubfileInfo &source) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::get_filename +// Access: Published, Virtual +// Description: Returns the filename that provides the target for +// these datagrams, if any, or empty string if the +// datagrams do not get written to a file on disk. +//////////////////////////////////////////////////////////////////// +const Filename &DatagramSink:: +get_filename() { + const FileReference *file = get_file(); + if (file != (FileReference *)NULL) { + return file->get_filename(); + } + static const Filename empty_filename; + return empty_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::get_file +// Access: Published, Virtual +// Description: Returns the FileReference that provides the target for +// these datagrams, if any, or NULL if the datagrams do +// not written to a file on disk. +//////////////////////////////////////////////////////////////////// +const FileReference *DatagramSink:: +get_file() { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramSink::get_file_pos +// Access: Published, Virtual +// Description: Returns the current file position within the data +// stream, if any, or 0 if the file position is not +// meaningful or cannot be determined. +// +// For DatagramSinks that return a meaningful file +// position, this will be pointing to the first byte +// following the datagram returned after a call to +// put_datagram(). +//////////////////////////////////////////////////////////////////// +streampos DatagramSink:: +get_file_pos() { + return 0; } diff --git a/panda/src/express/datagramSink.h b/panda/src/express/datagramSink.h index d88c15a32a..d5d7182865 100644 --- a/panda/src/express/datagramSink.h +++ b/panda/src/express/datagramSink.h @@ -19,6 +19,10 @@ #include "datagram.h" +class SubfileInfo; +class FileReference; +class Filename; + //////////////////////////////////////////////////////////////////// // Class : DatagramSink // Description : This class defines the abstract interface to sending @@ -31,8 +35,14 @@ PUBLISHED: virtual ~DatagramSink(); virtual bool put_datagram(const Datagram &data) = 0; + virtual bool copy_datagram(SubfileInfo &result, const Filename &filename); + virtual bool copy_datagram(SubfileInfo &result, const SubfileInfo &source); virtual bool is_error() = 0; virtual void flush() = 0; + + virtual const Filename &get_filename(); + virtual const FileReference *get_file(); + virtual streampos get_file_pos(); }; #include "datagramSink.I" diff --git a/panda/src/express/express_composite1.cxx b/panda/src/express/express_composite1.cxx index ad8e49bcf3..6bfb55de8e 100644 --- a/panda/src/express/express_composite1.cxx +++ b/panda/src/express/express_composite1.cxx @@ -10,7 +10,7 @@ #include "dcast.cxx" #include "encrypt_string.cxx" #include "error_utils.cxx" -#include "fileSystemInfo.cxx" +#include "fileReference.cxx" #include "hashGeneratorBase.cxx" #include "hashVal.cxx" #include "memoryInfo.cxx" diff --git a/panda/src/express/express_composite2.cxx b/panda/src/express/express_composite2.cxx index 31eb84d2b5..64d7fe875b 100644 --- a/panda/src/express/express_composite2.cxx +++ b/panda/src/express/express_composite2.cxx @@ -4,8 +4,10 @@ #include "ramfile.cxx" #include "referenceCount.cxx" #include "stringDecoder.cxx" +#include "subfileInfo.cxx" #include "subStream.cxx" #include "subStreamBuf.cxx" +#include "temporaryFile.cxx" #include "textEncoder.cxx" #include "threadSafePointerTo.cxx" #include "threadSafePointerToBase.cxx" diff --git a/panda/src/express/fileReference.I b/panda/src/express/fileReference.I new file mode 100644 index 0000000000..1e2bb1af08 --- /dev/null +++ b/panda/src/express/fileReference.I @@ -0,0 +1,33 @@ +// Filename: fileReference.I +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FileReference::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FileReference:: +FileReference(const Filename &filename) : _filename(filename) { +} + +//////////////////////////////////////////////////////////////////// +// Function: FileReference::get_filename +// Access: Published +// Description: Returns the filename of the reference. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &FileReference:: +get_filename() const { + return _filename; +} diff --git a/panda/src/express/fileReference.cxx b/panda/src/express/fileReference.cxx new file mode 100644 index 0000000000..5bc0ed75f9 --- /dev/null +++ b/panda/src/express/fileReference.cxx @@ -0,0 +1,17 @@ +// Filename: fileReference.cxx +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "fileReference.h" + +TypeHandle FileReference::_type_handle; diff --git a/panda/src/express/fileReference.h b/panda/src/express/fileReference.h new file mode 100644 index 0000000000..6243da7a52 --- /dev/null +++ b/panda/src/express/fileReference.h @@ -0,0 +1,57 @@ +// Filename: fileReference.h +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef FILEREFERENCE_H +#define FILEREFERENCE_H + +#include "pandabase.h" + +#include "typedReferenceCount.h" +#include "filename.h" + +//////////////////////////////////////////////////////////////////// +// Class : FileReference +// Description : Keeps a reference-counted pointer to a file on disk. +// As long as the FileReference is held, someone +// presumably has a use for this file. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS FileReference : public TypedReferenceCount { +PUBLISHED: + INLINE FileReference(const Filename &filename); + INLINE const Filename &get_filename() const; + +protected: + Filename _filename; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "FileReference", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "fileReference.I" + +#endif diff --git a/panda/src/express/fileSystemInfo.I b/panda/src/express/fileSystemInfo.I deleted file mode 100644 index 78a9a267ac..0000000000 --- a/panda/src/express/fileSystemInfo.I +++ /dev/null @@ -1,103 +0,0 @@ -// Filename: fileSystemInfo.I -// Created by: drose (20Jun11) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) Carnegie Mellon University. All rights reserved. -// -// All use of this software is subject to the terms of the revised BSD -// license. You should have received a copy of this license along -// with this source code in a file named "LICENSE." -// -//////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::Default Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -INLINE FileSystemInfo:: -FileSystemInfo() : - _file_start(0), - _file_size(0) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -INLINE FileSystemInfo:: -FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size) : - _os_file_name(os_file_name), - _file_start(file_start), - _file_size(file_size) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::Copy Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -INLINE FileSystemInfo:: -FileSystemInfo(const FileSystemInfo ©) : - _os_file_name(copy._os_file_name), - _file_start(copy._file_start), - _file_size(copy._file_size) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::Copy Assignment Operator -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -INLINE void FileSystemInfo:: -operator = (const FileSystemInfo ©) { - _os_file_name = copy._os_file_name; - _file_start = copy._file_start; - _file_size = copy._file_size; -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::get_os_file_name -// Access: Published -// Description: Returns the os-specific filename that may be used to -// open this file. -//////////////////////////////////////////////////////////////////// -INLINE const string &FileSystemInfo:: -get_os_file_name() const { - return _os_file_name; -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::get_file_start -// Access: Published -// Description: Returns the offset within the file at which this file -// data begins. -//////////////////////////////////////////////////////////////////// -INLINE streampos FileSystemInfo:: -get_file_start() const { - return _file_start; -} - -//////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::get_file_size -// Access: Published -// Description: Returns the number of consecutive bytes, beginning at -// get_file_start(), that correspond to this file data. -//////////////////////////////////////////////////////////////////// -INLINE streamsize FileSystemInfo:: -get_file_size() const { - return _file_size; -} - -INLINE ostream & -operator << (ostream &out, const FileSystemInfo &info) { - info.output(out); - return out; -} diff --git a/panda/src/express/fileSystemInfo.h b/panda/src/express/fileSystemInfo.h deleted file mode 100644 index 3f874e1139..0000000000 --- a/panda/src/express/fileSystemInfo.h +++ /dev/null @@ -1,48 +0,0 @@ -// Filename: fileSystemInfo.h -// Created by: drose (20Jun11) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) Carnegie Mellon University. All rights reserved. -// -// All use of this software is subject to the terms of the revised BSD -// license. You should have received a copy of this license along -// with this source code in a file named "LICENSE." -// -//////////////////////////////////////////////////////////////////// - -#ifndef FILESYSTEMINFO_H -#define FILESYSTEMINFO_H - -#include "pandabase.h" - -//////////////////////////////////////////////////////////////////// -// Class : FileSystemInfo -// Description : This class is used to return data about an actual -// file on disk by VirtualFile::get_system_info(). -//////////////////////////////////////////////////////////////////// -class EXPCL_PANDAEXPRESS FileSystemInfo { -PUBLISHED: - INLINE FileSystemInfo(); - INLINE FileSystemInfo(const string &os_file_name, streampos file_start, streamsize file_size); - INLINE FileSystemInfo(const FileSystemInfo ©); - INLINE void operator = (const FileSystemInfo ©); - - INLINE const string &get_os_file_name() const; - INLINE streampos get_file_start() const; - INLINE streamsize get_file_size() const; - - void output(ostream &out) const; - -private: - string _os_file_name; - streampos _file_start; - streamsize _file_size; -}; - -INLINE ostream &operator << (ostream &out, const FileSystemInfo &info); - -#include "fileSystemInfo.I" - -#endif diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 87a5b883ce..65a73e94b8 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -26,7 +26,6 @@ #include "referenceCount.h" #include "pvector.h" #include "openSSLWrapper.h" -#include "fileSystemInfo.h" //////////////////////////////////////////////////////////////////// // Class : Multifile diff --git a/panda/src/express/subfileInfo.I b/panda/src/express/subfileInfo.I new file mode 100644 index 0000000000..f34791aaca --- /dev/null +++ b/panda/src/express/subfileInfo.I @@ -0,0 +1,140 @@ +// Filename: subfileInfo.I +// Created by: drose (20Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::Default Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SubfileInfo:: +SubfileInfo() : + _start(0), + _size(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SubfileInfo:: +SubfileInfo(const FileReference *file, streampos start, streamsize size) : + _file(file), + _start(start), + _size(size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SubfileInfo:: +SubfileInfo(const Filename &filename, streampos start, streamsize size) : + _file(new FileReference(filename)), + _start(start), + _size(size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SubfileInfo:: +SubfileInfo(const SubfileInfo ©) : + _file(copy._file), + _start(copy._start), + _size(copy._size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void SubfileInfo:: +operator = (const SubfileInfo ©) { + _file = copy._file; + _start = copy._start; + _size = copy._size; +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::is_empty +// Access: Published +// Description: Returns true if this SubfileInfo doesn't define any +// file, false if it has real data. +//////////////////////////////////////////////////////////////////// +INLINE bool SubfileInfo:: +is_empty() const { + return _file == (FileReference *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::get_file +// Access: Published +// Description: Returns the FileReference that represents this file. +//////////////////////////////////////////////////////////////////// +INLINE const FileReference *SubfileInfo:: +get_file() const { + return _file; +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::get_filename +// Access: Published +// Description: A shortcut to the filename. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &SubfileInfo:: +get_filename() const { + if (_file != (FileReference *)NULL) { + return _file->get_filename(); + } + static const Filename empty_filename; + return empty_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::get_start +// Access: Published +// Description: Returns the offset within the file at which this file +// data begins. +//////////////////////////////////////////////////////////////////// +INLINE streampos SubfileInfo:: +get_start() const { + return _start; +} + +//////////////////////////////////////////////////////////////////// +// Function: SubfileInfo::get_size +// Access: Published +// Description: Returns the number of consecutive bytes, beginning at +// get_start(), that correspond to this file data. +//////////////////////////////////////////////////////////////////// +INLINE streamsize SubfileInfo:: +get_size() const { + return _size; +} + +INLINE ostream & +operator << (ostream &out, const SubfileInfo &info) { + info.output(out); + return out; +} diff --git a/panda/src/express/fileSystemInfo.cxx b/panda/src/express/subfileInfo.cxx similarity index 75% rename from panda/src/express/fileSystemInfo.cxx rename to panda/src/express/subfileInfo.cxx index 9071408fdf..163cf2551b 100644 --- a/panda/src/express/fileSystemInfo.cxx +++ b/panda/src/express/subfileInfo.cxx @@ -1,4 +1,4 @@ -// Filename: fileSystemInfo.cxx +// Filename: subfileInfo.cxx // Created by: drose (20Jun11) // //////////////////////////////////////////////////////////////////// @@ -12,15 +12,14 @@ // //////////////////////////////////////////////////////////////////// -#include "fileSystemInfo.h" +#include "subfileInfo.h" //////////////////////////////////////////////////////////////////// -// Function: FileSystemInfo::output +// Function: SubfileInfo::output // Access: Published // Description: //////////////////////////////////////////////////////////////////// -void FileSystemInfo:: +void SubfileInfo:: output(ostream &out) const { - out << "FileSystemInfo(" << _os_file_name << ", " << _file_start - << ", " << _file_size << ")"; + out << "SubfileInfo(" << get_filename() << ", " << _start << ", " << _size << ")"; } diff --git a/panda/src/express/subfileInfo.h b/panda/src/express/subfileInfo.h new file mode 100644 index 0000000000..43db37de8e --- /dev/null +++ b/panda/src/express/subfileInfo.h @@ -0,0 +1,56 @@ +// Filename: subfileInfo.h +// Created by: drose (20Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef SUBFILEINFO_H +#define SUBFILEINFO_H + +#include "pandabase.h" +#include "fileReference.h" +#include "pointerTo.h" + +//////////////////////////////////////////////////////////////////// +// Class : SubfileInfo +// Description : This class records a particular byte sub-range within +// an existing file on disk. Generally, the filename is +// understood as a physical file on disk, and not to be +// looked up via the vfs. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS SubfileInfo { +PUBLISHED: + INLINE SubfileInfo(); + INLINE SubfileInfo(const FileReference *file, streampos start, streamsize size); + INLINE SubfileInfo(const Filename &filename, streampos start, streamsize size); + INLINE SubfileInfo(const SubfileInfo ©); + INLINE void operator = (const SubfileInfo ©); + + INLINE bool is_empty() const; + + INLINE const FileReference *get_file() const; + INLINE const Filename &get_filename() const; + INLINE streampos get_start() const; + INLINE streamsize get_size() const; + + void output(ostream &out) const; + +private: + CPT(FileReference) _file; + streampos _start; + streamsize _size; +}; + +INLINE ostream &operator << (ostream &out, const SubfileInfo &info); + +#include "subfileInfo.I" + +#endif diff --git a/panda/src/express/temporaryFile.I b/panda/src/express/temporaryFile.I new file mode 100644 index 0000000000..759413b004 --- /dev/null +++ b/panda/src/express/temporaryFile.I @@ -0,0 +1,23 @@ +// Filename: temporaryFile.I +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TemporaryFile::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TemporaryFile:: +TemporaryFile(const Filename &filename) : FileReference(filename) { +} diff --git a/panda/src/express/temporaryFile.cxx b/panda/src/express/temporaryFile.cxx new file mode 100644 index 0000000000..7572ddc4c2 --- /dev/null +++ b/panda/src/express/temporaryFile.cxx @@ -0,0 +1,28 @@ +// Filename: temporaryFile.cxx +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "temporaryFile.h" + +TypeHandle TemporaryFile::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TemporaryFile::Destructor +// Access: Published, Virtual +// Description: The destructor is responsible for removing the file +// if it exists. +//////////////////////////////////////////////////////////////////// +TemporaryFile:: +~TemporaryFile() { + _filename.unlink(); +} diff --git a/panda/src/express/temporaryFile.h b/panda/src/express/temporaryFile.h new file mode 100644 index 0000000000..c603eda3ee --- /dev/null +++ b/panda/src/express/temporaryFile.h @@ -0,0 +1,54 @@ +// Filename: temporaryFile.h +// Created by: drose (23Jun11) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef TEMPORARYFILE_H +#define TEMPORARYFILE_H + +#include "pandabase.h" + +#include "fileReference.h" + +//////////////////////////////////////////////////////////////////// +// Class : TemporaryFile +// Description : This is a special kind of FileReference class that +// automatically deletes the file in question when it is +// deleted. It is not responsible for creating, +// opening, or closing the file, however. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS TemporaryFile : public FileReference { +PUBLISHED: + INLINE TemporaryFile(const Filename &filename); + virtual ~TemporaryFile(); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + FileReference::init_type(); + register_type(_type_handle, "TemporaryFile", + FileReference::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "temporaryFile.I" + +#endif diff --git a/panda/src/express/virtualFile.cxx b/panda/src/express/virtualFile.cxx index e03755cb00..c148ef7813 100644 --- a/panda/src/express/virtualFile.cxx +++ b/panda/src/express/virtualFile.cxx @@ -216,7 +216,7 @@ get_timestamp() const { //////////////////////////////////////////////////////////////////// // Function: VirtualFile::get_system_info // Access: Published, Virtual -// Description: Populates the FileSystemInfo structure with the data +// Description: Populates the SubfileInfo structure with the data // representing where the file actually resides on disk, // if this is knowable. Returns true if the file might // reside on disk, and the info is populated, or false @@ -224,7 +224,7 @@ get_timestamp() const { // resides), in which case the info is meaningless. //////////////////////////////////////////////////////////////////// bool VirtualFile:: -get_system_info(FileSystemInfo &info) { +get_system_info(SubfileInfo &info) { return false; } diff --git a/panda/src/express/virtualFile.h b/panda/src/express/virtualFile.h index 5ceeb67d8c..f697410c6a 100644 --- a/panda/src/express/virtualFile.h +++ b/panda/src/express/virtualFile.h @@ -18,7 +18,7 @@ #include "pandabase.h" #include "filename.h" -#include "fileSystemInfo.h" +#include "subfileInfo.h" #include "pointerTo.h" #include "typedReferenceCount.h" #include "ordered_vector.h" @@ -61,7 +61,7 @@ PUBLISHED: BLOCKING virtual off_t get_file_size() const; BLOCKING virtual time_t get_timestamp() const; - virtual bool get_system_info(FileSystemInfo &info); + virtual bool get_system_info(SubfileInfo &info); public: INLINE void set_original_filename(const Filename &filename); diff --git a/panda/src/express/virtualFileMount.cxx b/panda/src/express/virtualFileMount.cxx index 109b42c8f9..8b0c882f3a 100644 --- a/panda/src/express/virtualFileMount.cxx +++ b/panda/src/express/virtualFileMount.cxx @@ -143,7 +143,7 @@ close_read_file(istream *stream) const { //////////////////////////////////////////////////////////////////// // Function: VirtualFileMount::get_system_info // Access: Public, Virtual -// Description: Populates the FileSystemInfo structure with the data +// Description: Populates the SubfileInfo structure with the data // representing where the file actually resides on disk, // if this is knowable. Returns true if the file might // reside on disk, and the info is populated, or false @@ -151,7 +151,7 @@ close_read_file(istream *stream) const { // resides), in which case the info is meaningless. //////////////////////////////////////////////////////////////////// bool VirtualFileMount:: -get_system_info(const Filename &file, FileSystemInfo &info) { +get_system_info(const Filename &file, SubfileInfo &info) { return false; } diff --git a/panda/src/express/virtualFileMount.h b/panda/src/express/virtualFileMount.h index 67c784ca34..cd5789303b 100644 --- a/panda/src/express/virtualFileMount.h +++ b/panda/src/express/virtualFileMount.h @@ -58,7 +58,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const=0; virtual off_t get_file_size(const Filename &file) const=0; virtual time_t get_timestamp(const Filename &file) const=0; - virtual bool get_system_info(const Filename &file, FileSystemInfo &info); + virtual bool get_system_info(const Filename &file, SubfileInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const=0; diff --git a/panda/src/express/virtualFileMountMultifile.cxx b/panda/src/express/virtualFileMountMultifile.cxx index 729aa2b7b7..1df3bcf2e5 100644 --- a/panda/src/express/virtualFileMountMultifile.cxx +++ b/panda/src/express/virtualFileMountMultifile.cxx @@ -174,7 +174,7 @@ get_timestamp(const Filename &file) const { //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountMultifile::get_system_info // Access: Public, Virtual -// Description: Populates the FileSystemInfo structure with the data +// Description: Populates the SubfileInfo structure with the data // representing where the file actually resides on disk, // if this is knowable. Returns true if the file might // reside on disk, and the info is populated, or false @@ -182,7 +182,7 @@ get_timestamp(const Filename &file) const { // resides), in which case the info is meaningless. //////////////////////////////////////////////////////////////////// bool VirtualFileMountMultifile:: -get_system_info(const Filename &file, FileSystemInfo &info) { +get_system_info(const Filename &file, SubfileInfo &info) { Filename multifile_name = _multifile->get_multifile_name(); if (multifile_name.empty()) { return false; @@ -199,7 +199,7 @@ get_system_info(const Filename &file, FileSystemInfo &info) { streampos start = _multifile->get_subfile_internal_start(subfile_index); size_t length = _multifile->get_subfile_internal_length(subfile_index); - info = FileSystemInfo(multifile_name.to_os_specific(), start, length); + info = SubfileInfo(multifile_name, start, length); return true; } diff --git a/panda/src/express/virtualFileMountMultifile.h b/panda/src/express/virtualFileMountMultifile.h index 1fdbaca8fd..2872e67cc8 100644 --- a/panda/src/express/virtualFileMountMultifile.h +++ b/panda/src/express/virtualFileMountMultifile.h @@ -45,7 +45,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const; virtual off_t get_file_size(const Filename &file) const; virtual time_t get_timestamp(const Filename &file) const; - virtual bool get_system_info(const Filename &file, FileSystemInfo &info); + virtual bool get_system_info(const Filename &file, SubfileInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const; diff --git a/panda/src/express/virtualFileMountSystem.cxx b/panda/src/express/virtualFileMountSystem.cxx index 9a4b474b86..5f524feec8 100644 --- a/panda/src/express/virtualFileMountSystem.cxx +++ b/panda/src/express/virtualFileMountSystem.cxx @@ -184,7 +184,7 @@ get_timestamp(const Filename &file) const { //////////////////////////////////////////////////////////////////// // Function: VirtualFileMountSystem::get_system_info // Access: Public, Virtual -// Description: Populates the FileSystemInfo structure with the data +// Description: Populates the SubfileInfo structure with the data // representing where the file actually resides on disk, // if this is knowable. Returns true if the file might // reside on disk, and the info is populated, or false @@ -192,9 +192,9 @@ get_timestamp(const Filename &file) const { // resides), in which case the info is meaningless. //////////////////////////////////////////////////////////////////// bool VirtualFileMountSystem:: -get_system_info(const Filename &file, FileSystemInfo &info) { +get_system_info(const Filename &file, SubfileInfo &info) { Filename pathname(_physical_filename, file); - info = FileSystemInfo(pathname.to_os_specific(), 0, pathname.get_file_size()); + info = SubfileInfo(pathname, 0, pathname.get_file_size()); return true; } diff --git a/panda/src/express/virtualFileMountSystem.h b/panda/src/express/virtualFileMountSystem.h index a4a7cb11a4..baf282856e 100644 --- a/panda/src/express/virtualFileMountSystem.h +++ b/panda/src/express/virtualFileMountSystem.h @@ -39,7 +39,7 @@ public: virtual off_t get_file_size(const Filename &file, istream *stream) const; virtual off_t get_file_size(const Filename &file) const; virtual time_t get_timestamp(const Filename &file) const; - virtual bool get_system_info(const Filename &file, FileSystemInfo &info); + virtual bool get_system_info(const Filename &file, SubfileInfo &info); virtual bool scan_directory(vector_string &contents, const Filename &dir) const; diff --git a/panda/src/express/virtualFileSimple.cxx b/panda/src/express/virtualFileSimple.cxx index 1c2bcf1f08..64a6ca3ac9 100644 --- a/panda/src/express/virtualFileSimple.cxx +++ b/panda/src/express/virtualFileSimple.cxx @@ -163,7 +163,7 @@ get_timestamp() const { //////////////////////////////////////////////////////////////////// // Function: VirtualFileSimple::get_system_info // Access: Published, Virtual -// Description: Populates the FileSystemInfo structure with the data +// Description: Populates the SubfileInfo structure with the data // representing where the file actually resides on disk, // if this is knowable. Returns true if the file might // reside on disk, and the info is populated, or false @@ -171,7 +171,7 @@ get_timestamp() const { // resides), in which case the info is meaningless. //////////////////////////////////////////////////////////////////// bool VirtualFileSimple:: -get_system_info(FileSystemInfo &info) { +get_system_info(SubfileInfo &info) { return _mount->get_system_info(_local_filename, info); } diff --git a/panda/src/express/virtualFileSimple.h b/panda/src/express/virtualFileSimple.h index 1259f32f0f..138238c6ea 100644 --- a/panda/src/express/virtualFileSimple.h +++ b/panda/src/express/virtualFileSimple.h @@ -46,7 +46,7 @@ PUBLISHED: virtual off_t get_file_size(istream *stream) const; virtual off_t get_file_size() const; virtual time_t get_timestamp() const; - virtual bool get_system_info(FileSystemInfo &info); + virtual bool get_system_info(SubfileInfo &info); public: virtual bool read_file(pvector &result, bool auto_unwrap) const; diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index d4809d5d08..8ebb46a4ab 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -680,8 +680,12 @@ get_aux_data(const string &key) const { // Description: Reads the texture from a Panda texture object. This // defines the complete Texture specification, including // the image data as well as all texture properties. +// This only works if the txo file contains a static +// Texture image, as opposed to a subclass of Texture +// such as a movie texture. // -// The filename is just for reference. +// Pass a real filename if it is available, or empty +// string if it is not. //////////////////////////////////////////////////////////////////// bool Texture:: read_txo(istream &in, const string &filename) { @@ -691,6 +695,79 @@ read_txo(istream &in, const string &filename) { return do_read_txo(in, filename); } +//////////////////////////////////////////////////////////////////// +// Function: Texture::make_from_txo +// Access: Published, Static +// Description: Constructs a new Texture object from the txo file. +// This is similar to Texture::read_txo(), but it +// constructs and returns a new object, which allows it +// to return a subclass of Texture (for instance, a +// movie texture). +// +// Pass a real filename if it is available, or empty +// string if it is not. +//////////////////////////////////////////////////////////////////// +PT(Texture) Texture:: +make_from_txo(istream &in, const string &filename) { + DatagramInputFile din; + + if (!din.open(in, filename)) { + gobj_cat.error() + << "Could not read texture object: " << filename << "\n"; + return NULL; + } + + string head; + if (!din.read_header(head, _bam_header.size())) { + gobj_cat.error() + << filename << " is not a texture object file.\n"; + return NULL; + } + + if (head != _bam_header) { + gobj_cat.error() + << filename << " is not a texture object file.\n"; + return NULL; + } + + BamReader reader(&din); + if (!reader.init()) { + return NULL; + } + + TypedWritable *object = reader.read_object(); + + if (object != (TypedWritable *)NULL && + object->is_exact_type(BamCacheRecord::get_class_type())) { + // Here's a special case: if the first object in the file is a + // BamCacheRecord, it's really a cache data file and not a true + // txo file; but skip over the cache data record and let the user + // treat it like an ordinary txo file. + object = reader.read_object(); + } + + if (object == (TypedWritable *)NULL) { + gobj_cat.error() + << "Texture object " << filename << " is empty.\n"; + return NULL; + + } else if (!object->is_of_type(Texture::get_class_type())) { + gobj_cat.error() + << "Texture object " << filename << " contains a " + << object->get_type() << ", not a Texture.\n"; + return NULL; + } + + PT(Texture) other = DCAST(Texture, object); + if (!reader.resolve()) { + gobj_cat.error() + << "Unable to fully resolve texture object file.\n"; + return NULL; + } + + return other; +} + //////////////////////////////////////////////////////////////////// // Function: Texture::write_txo // Access: Published @@ -990,6 +1067,20 @@ get_keep_ram_image() const { return _keep_ram_image; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::is_cacheable +// Access: Published, Virtual +// Description: Returns true if there is enough information in this +// Texture object to write it to the bam cache +// successfully, false otherwise. For most textures, +// this is the same as has_ram_image(). +//////////////////////////////////////////////////////////////////// +bool Texture:: +is_cacheable() const { + MutexHolder holder(_lock); + return do_has_bam_rawdata(); +} + //////////////////////////////////////////////////////////////////// // Function: Texture::get_num_loadable_ram_mipmap_images // Access: Published @@ -3110,59 +3201,8 @@ do_read_txo_file(const Filename &fullpath) { //////////////////////////////////////////////////////////////////// bool Texture:: do_read_txo(istream &in, const string &filename) { - DatagramInputFile din; - - if (!din.open(in)) { - gobj_cat.error() - << "Could not read texture object: " << filename << "\n"; - return false; - } - - string head; - if (!din.read_header(head, _bam_header.size())) { - gobj_cat.error() - << filename << " is not a texture object file.\n"; - return false; - } - - if (head != _bam_header) { - gobj_cat.error() - << filename << " is not a texture object file.\n"; - return false; - } - - BamReader reader(&din, filename); - if (!reader.init()) { - return false; - } - - TypedWritable *object = reader.read_object(); - - if (object != (TypedWritable *)NULL && - object->is_exact_type(BamCacheRecord::get_class_type())) { - // Here's a special case: if the first object in the file is a - // BamCacheRecord, it's really a cache data file and not a true - // txo file; but skip over the cache data record and let the user - // treat it like an ordinary txo file. - object = reader.read_object(); - } - - if (object == (TypedWritable *)NULL) { - gobj_cat.error() - << "Texture object " << filename << " is empty.\n"; - return false; - - } else if (!object->is_of_type(Texture::get_class_type())) { - gobj_cat.error() - << "Texture object " << filename << " contains a " - << object->get_type() << ", not a Texture.\n"; - return false; - } - - PT(Texture) other = DCAST(Texture, object); - if (!reader.resolve()) { - gobj_cat.error() - << "Unable to fully resolve texture object file.\n"; + PT(Texture) other = make_from_txo(in, filename); + if (other == (Texture *)NULL) { return false; } @@ -3506,10 +3546,10 @@ do_read_dds(istream &in, const string &filename, bool header_only) { bool Texture:: do_write(const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) const { if (is_txo_filename(fullpath)) { - if (!do_has_ram_image()) { - ((Texture *)this)->do_get_ram_image(); + if (!do_has_bam_rawdata()) { + ((Texture *)this)->do_get_bam_rawdata(); } - nassertr(do_has_ram_image(), false); + nassertr(do_has_bam_rawdata(), false); return do_write_txo_file(fullpath); } @@ -3671,7 +3711,7 @@ bool Texture:: do_write_txo(ostream &out, const string &filename) const { DatagramOutputFile dout; - if (!dout.open(out)) { + if (!dout.open(out, filename)) { gobj_cat.error() << "Could not write texture object: " << filename << "\n"; return false; @@ -3683,7 +3723,7 @@ do_write_txo(ostream &out, const string &filename) const { return false; } - BamWriter writer(&dout, filename); + BamWriter writer(&dout); if (!writer.init()) { return false; } @@ -3700,7 +3740,7 @@ do_write_txo(ostream &out, const string &filename) const { } _lock.acquire(); - if (!do_has_ram_image()) { + if (!do_has_bam_rawdata()) { gobj_cat.error() << get_name() << " does not have ram image\n"; return false; @@ -5368,6 +5408,31 @@ do_reload() { return false; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_has_bam_rawdata +// Access: Protected, Virtual +// Description: Returns true if there is a rawdata image that we have +// available to write to the bam stream. For a normal +// Texture, this is the same thing as +// do_has_ram_image(), but a movie texture might define +// it differently. +//////////////////////////////////////////////////////////////////// +bool Texture:: +do_has_bam_rawdata() const { + return do_has_ram_image(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_get_bam_rawdata +// Access: Protected, Virtual +// Description: If do_has_bam_rawdata() returned false, this attempts +// to reload the rawdata image if possible. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_get_bam_rawdata() { + do_get_ram_image(); +} + //////////////////////////////////////////////////////////////////// // Function: Texture::convert_from_pnmimage // Access: Private, Static @@ -6727,277 +6792,9 @@ register_with_read_factory() { BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } -//////////////////////////////////////////////////////////////////// -// Function: Texture::make_from_bam -// Access: Protected, Static -// Description: Factory method to generate a Texture object -//////////////////////////////////////////////////////////////////// -TypedWritable *Texture:: -make_from_bam(const FactoryParams ¶ms) { - // The process of making a texture is slightly different than making - // other TypedWritable objects. That is because all creation of - // Textures should be done through calls to TexturePool, which - // ensures that any loads of the same filename refer to the same - // memory. - DatagramIterator scan; - BamReader *manager; - - parse_params(params, scan, manager); - - // Get the filenames and texture type so we can look up the file on - // disk first. - string name = scan.get_string(); - Filename filename = scan.get_string(); - Filename alpha_filename = scan.get_string(); - - int primary_file_num_channels = scan.get_uint8(); - int alpha_file_channel = scan.get_uint8(); - bool has_rawdata = scan.get_bool(); - TextureType texture_type = (TextureType)scan.get_uint8(); - - Texture *me = NULL; - if (has_rawdata) { - // If the raw image data is included, then just create a Texture - // and don't load from the file. - me = new Texture(name); - me->_filename = filename; - me->_alpha_filename = alpha_filename; - me->_primary_file_num_channels = primary_file_num_channels; - me->_alpha_file_channel = alpha_file_channel; - me->_texture_type = texture_type; - - // Read the texture attributes directly from the bam stream. - me->fillin(scan, manager, has_rawdata); - - } else { - // Now create a temporary Texture object to read all the - // attributes from the bam stream. - PT(Texture) dummy = new Texture(""); - dummy->fillin(scan, manager, has_rawdata); - - if (filename.empty()) { - // This texture has no filename; since we don't have an image to - // load, we can't actually create the texture. - gobj_cat.info() - << "Cannot create texture '" << name << "' with no filename.\n"; - - } else { - // This texture does have a filename, so try to load it from disk. - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - if (!manager->get_filename().empty()) { - // If texture filename was given relative to the bam filename, - // expand it now. - Filename bam_dir = manager->get_filename().get_dirname(); - vfs->resolve_filename(filename, bam_dir); - if (!alpha_filename.empty()) { - vfs->resolve_filename(alpha_filename, bam_dir); - } - } - - LoaderOptions options = manager->get_loader_options(); - if (dummy->uses_mipmaps()) { - options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps); - } - - switch (texture_type) { - case TT_1d_texture: - case TT_2d_texture: - if (alpha_filename.empty()) { - me = TexturePool::load_texture(filename, primary_file_num_channels, - false, options); - } else { - me = TexturePool::load_texture(filename, alpha_filename, - primary_file_num_channels, - alpha_file_channel, - false, options); - } - break; - - case TT_3d_texture: - me = TexturePool::load_3d_texture(filename, false, options); - break; - - case TT_2d_texture_array: - me = TexturePool::load_2d_texture_array(filename, false, options); - break; - - case TT_cube_map: - me = TexturePool::load_cube_map(filename, false, options); - break; - } - } - - if (me != (Texture *)NULL) { - me->fillin_from(dummy); - me->set_name(name); - } - } - - return me; -} - -//////////////////////////////////////////////////////////////////// -// Function: Texture::fillin -// Access: Protected -// Description: Function that reads out of the datagram (or asks -// manager to read) all of the data that is needed to -// re-create this object and stores it in the appropiate -// place -//////////////////////////////////////////////////////////////////// -void Texture:: -fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) { - // We have already read in the filenames; don't read them again. - - _wrap_u = (WrapMode)scan.get_uint8(); - _wrap_v = (WrapMode)scan.get_uint8(); - _wrap_w = (WrapMode)scan.get_uint8(); - _minfilter = (FilterType)scan.get_uint8(); - _magfilter = (FilterType)scan.get_uint8(); - _anisotropic_degree = scan.get_int16(); - _border_color.read_datagram(scan); - - if (manager->get_file_minor_ver() >= 1) { - _compression = (CompressionMode)scan.get_uint8(); - } - if (manager->get_file_minor_ver() >= 16) { - _quality_level = (QualityLevel)scan.get_uint8(); - } - - _format = (Format)scan.get_uint8(); - _num_components = scan.get_uint8(); - ++_properties_modified; - - bool has_simple_ram_image = false; - if (manager->get_file_minor_ver() >= 18) { - _orig_file_x_size = scan.get_uint32(); - _orig_file_y_size = scan.get_uint32(); - - has_simple_ram_image = scan.get_bool(); - } - - if (has_simple_ram_image) { - _simple_x_size = scan.get_uint32(); - _simple_y_size = scan.get_uint32(); - _simple_image_date_generated = scan.get_int32(); - - size_t u_size = scan.get_uint32(); - PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type()); - for (size_t u_idx = 0; u_idx < u_size; ++u_idx) { - image[(int)u_idx] = scan.get_uint8(); - } - - _simple_ram_image._image = image; - _simple_ram_image._page_size = u_size; - ++_simple_image_modified; - } - - if (has_rawdata) { - _x_size = scan.get_uint32(); - _y_size = scan.get_uint32(); - _z_size = scan.get_uint32(); - _component_type = (ComponentType)scan.get_uint8(); - _component_width = scan.get_uint8(); - _ram_image_compression = CM_off; - if (manager->get_file_minor_ver() >= 1) { - _ram_image_compression = (CompressionMode)scan.get_uint8(); - } - - int num_ram_images = 1; - if (manager->get_file_minor_ver() >= 3) { - num_ram_images = scan.get_uint8(); - } - - _ram_images.clear(); - _ram_images.reserve(num_ram_images); - for (int n = 0; n < num_ram_images; ++n) { - _ram_images.push_back(RamImage()); - _ram_images[n]._page_size = get_expected_ram_page_size(); - if (manager->get_file_minor_ver() >= 1) { - _ram_images[n]._page_size = scan.get_uint32(); - } - - size_t u_size = scan.get_uint32(); - - // fill the _image buffer with image data - PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type()); - for (size_t u_idx = 0; u_idx < u_size; ++u_idx) { - image[(int)u_idx] = scan.get_uint8(); - } - _ram_images[n]._image = image; - } - _loaded_from_image = true; - do_set_pad_size(0, 0, 0); - ++_image_modified; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: Texture::fillin_from -// Access: Protected -// Description: Called in make_from_bam(), this method properly -// copies the attributes from the bam stream (as stored -// in dummy) into this texture, updating the modified -// flags appropriately. -//////////////////////////////////////////////////////////////////// -void Texture:: -fillin_from(Texture *dummy) { - MutexHolder holder(_lock); - - // Use the setters instead of setting these directly, so we can - // correctly avoid incrementing _properties_modified if none of - // these actually change. (Otherwise, we'd have to reload the - // texture to the GSG every time we loaded a new bam file that - // reference the texture, since each bam file reference passes - // through this function.) - - do_set_wrap_u(dummy->get_wrap_u()); - do_set_wrap_v(dummy->get_wrap_v()); - do_set_wrap_w(dummy->get_wrap_w()); - do_set_border_color(dummy->get_border_color()); - - if (dummy->get_minfilter() != FT_default) { - do_set_minfilter(dummy->get_minfilter()); - } - if (dummy->get_magfilter() != FT_default) { - do_set_magfilter(dummy->get_magfilter()); - } - if (dummy->get_anisotropic_degree() != 0) { - do_set_anisotropic_degree(dummy->get_anisotropic_degree()); - } - if (dummy->get_compression() != CM_default) { - do_set_compression(dummy->get_compression()); - } - if (dummy->get_quality_level() != QL_default) { - do_set_quality_level(dummy->get_quality_level()); - } - - Format format = dummy->get_format(); - int num_components = dummy->get_num_components(); - - if (num_components == _num_components) { - // Only reset the format if the number of components hasn't - // changed, since if the number of components has changed our - // texture no longer matches what it was when the bam was - // written. - do_set_format(format); - } - - if (dummy->has_simple_ram_image()) { - // Only replace the simple ram image if it was generated more - // recently than the one we already have. - if (_simple_ram_image._image.empty() || - dummy->_simple_image_date_generated > _simple_image_date_generated) { - do_set_simple_ram_image(dummy->get_simple_ram_image(), - dummy->get_simple_x_size(), - dummy->get_simple_y_size()); - _simple_image_date_generated = dummy->_simple_image_date_generated; - } - } -} - //////////////////////////////////////////////////////////////////// // Function: Texture::write_datagram -// Access: Public +// Access: Public, Virtual // Description: Function to write the important information in // the particular object to a Datagram //////////////////////////////////////////////////////////////////// @@ -7005,6 +6802,61 @@ void Texture:: write_datagram(BamWriter *manager, Datagram &me) { MutexHolder holder(_lock); + bool has_rawdata = false; + do_write_datagram_header(manager, me, has_rawdata); + + do_write_datagram_body(manager, me); + + // If we are also including the texture's image data, then stuff it + // in here. + if (has_rawdata) { + do_write_datagram_rawdata(manager, me); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::finalize +// Access: Public, Virtual +// Description: Called by the BamReader to perform any final actions +// needed for setting up the object after all objects +// have been read and all pointers have been completed. +//////////////////////////////////////////////////////////////////// +void Texture:: +finalize(BamReader *) { + // Unref the pointer that we explicitly reffed in make_from_bam(). + unref(); + + // We should never get back to zero after unreffing our own count, + // because we expect to have been stored in a pointer somewhere. If + // we do get to zero, it's a memory leak; the way to avoid this is + // to call unref_delete() above instead of unref(), but this is + // dangerous to do from within a virtual function. + nassertv(get_ref_count() != 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_write_datagram_header +// Access: Protected +// Description: Writes the header part of the texture to the +// Datagram. This is the common part that is shared by +// all Texture subclasses, and contains the filename and +// rawdata flags. This method is not virtual because +// all Texture subclasses must write the same data at +// this step. +// +// This part must be read first before calling +// do_fillin_body() to determine whether to load the +// Texture from the TexturePool or directly from the bam +// stream. +// +// After this call, has_rawdata will be filled with +// either true or false, according to whether we expect +// to write the texture rawdata to the bam stream +// following the texture body. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_write_datagram_header(BamWriter *manager, Datagram &me, bool &has_rawdata) { // Write out the texture's raw pixel data if (a) the current Bam // Texture Mode requires that, or (b) there's no filename, so the // file can't be loaded up from disk, but the raw pixel data is @@ -7014,11 +6866,11 @@ write_datagram(BamWriter *manager, Datagram &me) { // loads the bam file later will have access to the image file on // disk. BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode(); - bool has_rawdata = - (file_texture_mode == BamWriter::BTM_rawdata || (do_has_ram_image() && _filename.empty())); - if (has_rawdata && !do_has_ram_image()) { - do_get_ram_image(); - if (!do_has_ram_image()) { + has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata || + (_filename.empty() && do_has_bam_rawdata())); + if (has_rawdata && !do_has_bam_rawdata()) { + do_get_bam_rawdata(); + if (!do_has_bam_rawdata()) { // No image data after all. has_rawdata = false; } @@ -7073,7 +6925,7 @@ write_datagram(BamWriter *manager, Datagram &me) { << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n"; } - if (filename.empty() && do_has_ram_image()) { + if (filename.empty() && do_has_bam_rawdata()) { // If we don't have a filename, we have to store rawdata anyway. has_rawdata = true; } @@ -7085,8 +6937,17 @@ write_datagram(BamWriter *manager, Datagram &me) { me.add_uint8(_alpha_file_channel); me.add_bool(has_rawdata); me.add_uint8(_texture_type); +} - // The data beginning at this point is handled by fillin(). +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_write_datagram_body +// Access: Protected, Virtual +// Description: Writes the body part of the texture to the +// Datagram. This is generally all of the texture +// parameters except for the header and the rawdata. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_write_datagram_body(BamWriter *manager, Datagram &me) { me.add_uint8(_wrap_u); me.add_uint8(_wrap_v); me.add_uint8(_wrap_w); @@ -7114,21 +6975,330 @@ write_datagram(BamWriter *manager, Datagram &me) { me.add_uint32(_simple_ram_image._image.size()); me.append_data(_simple_ram_image._image, _simple_ram_image._image.size()); } +} - // If we are also including the texture's image data, then stuff it - // in here. +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_write_datagram_rawdata +// Access: Protected, Virtual +// Description: Writes the rawdata part of the texture to the +// Datagram. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_write_datagram_rawdata(BamWriter *manager, Datagram &me) { + me.add_uint32(_x_size); + me.add_uint32(_y_size); + me.add_uint32(_z_size); + me.add_uint8(_component_type); + me.add_uint8(_component_width); + me.add_uint8(_ram_image_compression); + me.add_uint8(_ram_images.size()); + for (size_t n = 0; n < _ram_images.size(); ++n) { + me.add_uint32(_ram_images[n]._page_size); + me.add_uint32(_ram_images[n]._image.size()); + me.append_data(_ram_images[n]._image, _ram_images[n]._image.size()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::make_from_bam +// Access: Protected, Static +// Description: Factory method to generate a Texture object +//////////////////////////////////////////////////////////////////// +TypedWritable *Texture:: +make_from_bam(const FactoryParams ¶ms) { + PT(Texture) dummy = new Texture; + return dummy->make_this_from_bam(params); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::make_this_from_bam +// Access: Protected, Virtual +// Description: Called by make_from_bam() once the particular +// subclass of Texture is known. This is called on a +// newly-constructed Texture object of the appropriate +// subclass. It will return either the same Texture +// object (e.g. this), or a different Texture object +// loaded via the TexturePool, as appropriate. +//////////////////////////////////////////////////////////////////// +TypedWritable *Texture:: +make_this_from_bam(const FactoryParams ¶ms) { + // The process of making a texture is slightly different than making + // other TypedWritable objects. That is because all creation of + // Textures should be done through calls to TexturePool, which + // ensures that any loads of the same filename refer to the same + // memory. + + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + + // Get the header information--the filenames and texture type--so we + // can look up the file on disk first. + string name = scan.get_string(); + Filename filename = scan.get_string(); + Filename alpha_filename = scan.get_string(); + + int primary_file_num_channels = scan.get_uint8(); + int alpha_file_channel = scan.get_uint8(); + bool has_rawdata = scan.get_bool(); + TextureType texture_type = (TextureType)scan.get_uint8(); + + Texture *me = NULL; if (has_rawdata) { - me.add_uint32(_x_size); - me.add_uint32(_y_size); - me.add_uint32(_z_size); - me.add_uint8(_component_type); - me.add_uint8(_component_width); - me.add_uint8(_ram_image_compression); - me.add_uint8(_ram_images.size()); - for (size_t n = 0; n < _ram_images.size(); ++n) { - me.add_uint32(_ram_images[n]._page_size); - me.add_uint32(_ram_images[n]._image.size()); - me.append_data(_ram_images[n]._image, _ram_images[n]._image.size()); + // If the raw image data is included, then just load the texture + // directly from the stream, and return it. In this case we + // return the "this" pointer, since it's a newly-created Texture + // object of the appropriate type. + me = this; + me->set_name(name); + me->_filename = filename; + me->_alpha_filename = alpha_filename; + me->_primary_file_num_channels = primary_file_num_channels; + me->_alpha_file_channel = alpha_file_channel; + me->_texture_type = texture_type; + + // Read the texture attributes directly from the bam stream. + me->do_fillin_body(scan, manager); + me->do_fillin_rawdata(scan, manager); + + // To manage the reference count, explicitly ref it now, then + // unref it in the finalize callback. + me->ref(); + manager->register_finalize(me); + + } else { + // The raw image data isn't included, so we'll be loading the + // Texture via the TexturePool. In this case we use the "this" + // pointer as a temporary object to read all of the attributes + // from the bam stream. + Texture *dummy = this; + dummy->do_fillin_body(scan, manager); + + if (filename.empty()) { + // This texture has no filename; since we don't have an image to + // load, we can't actually create the texture. + gobj_cat.info() + << "Cannot create texture '" << name << "' with no filename.\n"; + + } else { + // This texture does have a filename, so try to load it from disk. + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + if (!manager->get_filename().empty()) { + // If texture filename was given relative to the bam filename, + // expand it now. + Filename bam_dir = manager->get_filename().get_dirname(); + vfs->resolve_filename(filename, bam_dir); + if (!alpha_filename.empty()) { + vfs->resolve_filename(alpha_filename, bam_dir); + } + } + + LoaderOptions options = manager->get_loader_options(); + if (dummy->uses_mipmaps()) { + options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps); + } + + switch (texture_type) { + case TT_1d_texture: + case TT_2d_texture: + if (alpha_filename.empty()) { + me = TexturePool::load_texture(filename, primary_file_num_channels, + false, options); + } else { + me = TexturePool::load_texture(filename, alpha_filename, + primary_file_num_channels, + alpha_file_channel, + false, options); + } + break; + + case TT_3d_texture: + me = TexturePool::load_3d_texture(filename, false, options); + break; + + case TT_2d_texture_array: + me = TexturePool::load_2d_texture_array(filename, false, options); + break; + + case TT_cube_map: + me = TexturePool::load_cube_map(filename, false, options); + break; + } + } + + if (me != (Texture *)NULL) { + { + MutexHolder holder(me->_lock); + me->do_fillin_from(dummy); + } + me->set_name(name); + + // Since in this case me was loaded from the TexturePool, + // there's no need to explicitly manage the reference count. + // TexturePool will hold it safely. + } + } + + return me; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_fillin_body +// Access: Protected, Virtual +// Description: Reads in the part of the Texture that was written +// with do_write_datagram_body(). +//////////////////////////////////////////////////////////////////// +void Texture:: +do_fillin_body(DatagramIterator &scan, BamReader *manager) { + _wrap_u = (WrapMode)scan.get_uint8(); + _wrap_v = (WrapMode)scan.get_uint8(); + _wrap_w = (WrapMode)scan.get_uint8(); + _minfilter = (FilterType)scan.get_uint8(); + _magfilter = (FilterType)scan.get_uint8(); + _anisotropic_degree = scan.get_int16(); + _border_color.read_datagram(scan); + + if (manager->get_file_minor_ver() >= 1) { + _compression = (CompressionMode)scan.get_uint8(); + } + if (manager->get_file_minor_ver() >= 16) { + _quality_level = (QualityLevel)scan.get_uint8(); + } + + _format = (Format)scan.get_uint8(); + _num_components = scan.get_uint8(); + ++_properties_modified; + + bool has_simple_ram_image = false; + if (manager->get_file_minor_ver() >= 18) { + _orig_file_x_size = scan.get_uint32(); + _orig_file_y_size = scan.get_uint32(); + + has_simple_ram_image = scan.get_bool(); + } + + if (has_simple_ram_image) { + _simple_x_size = scan.get_uint32(); + _simple_y_size = scan.get_uint32(); + _simple_image_date_generated = scan.get_int32(); + + size_t u_size = scan.get_uint32(); + PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type()); + for (size_t u_idx = 0; u_idx < u_size; ++u_idx) { + image[(int)u_idx] = scan.get_uint8(); + } + + _simple_ram_image._image = image; + _simple_ram_image._page_size = u_size; + ++_simple_image_modified; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_fillin_rawdata +// Access: Protected, Virtual +// Description: Reads in the part of the Texture that was written +// with do_write_datagram_rawdata(). +//////////////////////////////////////////////////////////////////// +void Texture:: +do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) { + _x_size = scan.get_uint32(); + _y_size = scan.get_uint32(); + _z_size = scan.get_uint32(); + _component_type = (ComponentType)scan.get_uint8(); + _component_width = scan.get_uint8(); + _ram_image_compression = CM_off; + if (manager->get_file_minor_ver() >= 1) { + _ram_image_compression = (CompressionMode)scan.get_uint8(); + } + + int num_ram_images = 1; + if (manager->get_file_minor_ver() >= 3) { + num_ram_images = scan.get_uint8(); + } + + _ram_images.clear(); + _ram_images.reserve(num_ram_images); + for (int n = 0; n < num_ram_images; ++n) { + _ram_images.push_back(RamImage()); + _ram_images[n]._page_size = get_expected_ram_page_size(); + if (manager->get_file_minor_ver() >= 1) { + _ram_images[n]._page_size = scan.get_uint32(); + } + + size_t u_size = scan.get_uint32(); + + // fill the _image buffer with image data + PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type()); + for (size_t u_idx = 0; u_idx < u_size; ++u_idx) { + image[(int)u_idx] = scan.get_uint8(); + } + _ram_images[n]._image = image; + } + _loaded_from_image = true; + do_set_pad_size(0, 0, 0); + ++_image_modified; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_fillin_from +// Access: Protected, Virtual +// Description: Called in make_from_bam(), this method properly +// copies the attributes from the bam stream (as stored +// in dummy) into this texture, updating the modified +// flags appropriately. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_fillin_from(Texture *dummy) { + // Use the setters instead of setting these directly, so we can + // correctly avoid incrementing _properties_modified if none of + // these actually change. (Otherwise, we'd have to reload the + // texture to the GSG every time we loaded a new bam file that + // reference the texture, since each bam file reference passes + // through this function.) + + do_set_wrap_u(dummy->get_wrap_u()); + do_set_wrap_v(dummy->get_wrap_v()); + do_set_wrap_w(dummy->get_wrap_w()); + do_set_border_color(dummy->get_border_color()); + + if (dummy->get_minfilter() != FT_default) { + do_set_minfilter(dummy->get_minfilter()); + } + if (dummy->get_magfilter() != FT_default) { + do_set_magfilter(dummy->get_magfilter()); + } + if (dummy->get_anisotropic_degree() != 0) { + do_set_anisotropic_degree(dummy->get_anisotropic_degree()); + } + if (dummy->get_compression() != CM_default) { + do_set_compression(dummy->get_compression()); + } + if (dummy->get_quality_level() != QL_default) { + do_set_quality_level(dummy->get_quality_level()); + } + + Format format = dummy->get_format(); + int num_components = dummy->get_num_components(); + + if (num_components == _num_components) { + // Only reset the format if the number of components hasn't + // changed, since if the number of components has changed our + // texture no longer matches what it was when the bam was + // written. + do_set_format(format); + } + + if (dummy->has_simple_ram_image()) { + // Only replace the simple ram image if it was generated more + // recently than the one we already have. + if (_simple_ram_image._image.empty() || + dummy->_simple_image_date_generated > _simple_image_date_generated) { + do_set_simple_ram_image(dummy->get_simple_ram_image(), + dummy->get_simple_x_size(), + dummy->get_simple_y_size()); + _simple_image_date_generated = dummy->_simple_image_date_generated; } } } diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 94513f580a..c40ced9b08 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -63,6 +63,8 @@ struct DDSHeader; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_GOBJ Texture : public TypedWritableReferenceCount, public Namable { PUBLISHED: + typedef PT(Texture) MakeTextureFunc(); + enum TextureType { TT_1d_texture, TT_2d_texture, @@ -244,9 +246,10 @@ PUBLISHED: INLINE bool write(const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps); - bool read_txo(istream &in, const string &filename = "stream"); - bool write_txo(ostream &out, const string &filename = "stream") const; - bool read_dds(istream &in, const string &filename = "stream", bool header_only = false); + bool read_txo(istream &in, const string &filename = ""); + static PT(Texture) make_from_txo(istream &in, const string &filename = ""); + bool write_txo(ostream &out, const string &filename = "") const; + bool read_dds(istream &in, const string &filename = "", bool header_only = false); INLINE bool load(const PNMImage &pnmimage, const LoaderOptions &options = LoaderOptions()); INLINE bool load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options = LoaderOptions()); @@ -328,6 +331,7 @@ PUBLISHED: INLINE void clear_ram_image(); INLINE void set_keep_ram_image(bool keep_ram_image); virtual bool get_keep_ram_image() const; + virtual bool is_cacheable() const; INLINE bool compress_ram_image(CompressionMode compression = CM_on, QualityLevel quality_level = QL_default, @@ -589,6 +593,9 @@ protected: virtual bool do_can_reload(); bool do_reload(); + virtual bool do_has_bam_rawdata() const; + virtual void do_get_bam_rawdata(); + // This nested class declaration is used below. class RamImage { public: @@ -790,10 +797,17 @@ public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &me); + virtual void finalize(BamReader *manager); + protected: + void do_write_datagram_header(BamWriter *manager, Datagram &me, bool &has_rawdata); + virtual void do_write_datagram_body(BamWriter *manager, Datagram &me); + virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &me); static TypedWritable *make_from_bam(const FactoryParams ¶ms); - void fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata); - void fillin_from(Texture *dummy); + virtual TypedWritable *make_this_from_bam(const FactoryParams ¶ms); + virtual void do_fillin_body(DatagramIterator &scan, BamReader *manager); + virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager); + virtual void do_fillin_from(Texture *dummy); public: static TypeHandle get_class_type() { diff --git a/panda/src/gobj/texturePool.cxx b/panda/src/gobj/texturePool.cxx index 2fb2b9115c..4f16661197 100644 --- a/panda/src/gobj/texturePool.cxx +++ b/panda/src/gobj/texturePool.cxx @@ -264,12 +264,48 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels, // cache; it needs to be loaded from its source image(s). gobj_cat.info() << "Loading texture " << filename << "\n"; - tex = make_texture(filename.get_extension()); - if (!tex->read(filename, Filename(), primary_file_num_channels, 0, - 0, 0, false, read_mipmaps, record, options)) { - // This texture was not found or could not be read. - report_texture_unreadable(filename); - return NULL; + + string ext = downcase(filename.get_extension()); + if (ext == "txo" || ext == "bam") { + // Assume this is a txo file, which might conceivably contain a + // movie file or some other subclass of Texture. In that case, + // use make_from_txo() to load it instead of read(). + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + filename.set_binary(); + PT(VirtualFile) file = vfs->get_file(filename); + if (file == (VirtualFile *)NULL) { + // No such file. + gobj_cat.error() + << "Could not find " << filename << "\n"; + return false; + } + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Reading texture object " << filename << "\n"; + } + + istream *in = file->open_read_file(true); + tex = Texture::make_from_txo(*in, filename); + vfs->close_read_file(in); + + if (tex == (Texture *)NULL) { + return false; + } + tex->set_fullpath(filename); + tex->clear_alpha_fullpath(); + tex->set_keep_ram_image(false); + + } else { + // Read it the conventional way. + tex = make_texture(ext); + if (!tex->read(filename, Filename(), primary_file_num_channels, 0, + 0, 0, false, read_mipmaps, record, options)) { + // This texture was not found or could not be read. + report_texture_unreadable(filename); + return NULL; + } } if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) { @@ -322,7 +358,7 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels, _textures[filename] = tex; } - if (store_record && tex->has_ram_image()) { + if (store_record && tex->is_cacheable()) { // Store the on-disk cache record for next time. record->set_data(tex, tex); cache->store(record); @@ -454,7 +490,7 @@ ns_load_texture(const Filename &orig_filename, _textures[filename] = tex; } - if (store_record && tex->has_ram_image()) { + if (store_record && tex->is_cacheable()) { // Store the on-disk cache record for next time. record->set_data(tex, tex); cache->store(record); @@ -566,7 +602,7 @@ ns_load_3d_texture(const Filename &filename_pattern, _textures[filename] = tex; } - if (store_record && tex->has_ram_image()) { + if (store_record && tex->is_cacheable()) { // Store the on-disk cache record for next time. record->set_data(tex, tex); cache->store(record); @@ -672,7 +708,7 @@ ns_load_2d_texture_array(const Filename &filename_pattern, _textures[unique_filename] = tex; } - if (store_record && tex->has_ram_image()) { + if (store_record && tex->is_cacheable()) { // Store the on-disk cache record for next time. record->set_data(tex, tex); cache->store(record); @@ -771,7 +807,7 @@ ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps, _textures[filename] = tex; } - if (store_record && tex->has_ram_image()) { + if (store_record && tex->is_cacheable()) { // Store the on-disk cache record for next time. record->set_data(tex, tex); cache->store(record); diff --git a/panda/src/gobj/texturePool.h b/panda/src/gobj/texturePool.h index 52a24d4592..248907d568 100644 --- a/panda/src/gobj/texturePool.h +++ b/panda/src/gobj/texturePool.h @@ -85,7 +85,7 @@ PUBLISHED: static void write(ostream &out); public: - typedef PT(Texture) MakeTextureFunc(); + typedef Texture::MakeTextureFunc MakeTextureFunc; void register_texture_type(MakeTextureFunc *func, const string &extensions); void register_filter(TexturePoolFilter *filter); diff --git a/panda/src/grutil/ffmpegTexture.I b/panda/src/grutil/ffmpegTexture.I index f0a1fe8e57..8f178c6f91 100644 --- a/panda/src/grutil/ffmpegTexture.I +++ b/panda/src/grutil/ffmpegTexture.I @@ -51,7 +51,8 @@ VideoPage() { //////////////////////////////////////////////////////////////////// INLINE FFMpegTexture::VideoPage:: VideoPage(const FFMpegTexture::VideoPage ©) : - _color(copy._color) + _color(copy._color), + _alpha(copy._alpha) { } @@ -63,3 +64,14 @@ VideoPage(const FFMpegTexture::VideoPage ©) : INLINE FFMpegTexture::VideoPage:: ~VideoPage() { } + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoPage::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void FFMpegTexture::VideoPage:: +operator = (const FFMpegTexture::VideoPage ©) { + _color = copy._color; + _alpha = copy._alpha; +} diff --git a/panda/src/grutil/ffmpegTexture.cxx b/panda/src/grutil/ffmpegTexture.cxx index 8836d6aa59..faaaa26f68 100644 --- a/panda/src/grutil/ffmpegTexture.cxx +++ b/panda/src/grutil/ffmpegTexture.cxx @@ -21,6 +21,7 @@ #include "config_grutil.h" #include "bamCacheRecord.h" #include "bamReader.h" +#include "bamWriter.h" TypeHandle FFMpegTexture::_type_handle; @@ -96,7 +97,7 @@ do_assign(const FFMpegTexture ©) { } //////////////////////////////////////////////////////////////////// -// Function: FFMPegTexture::modify_page +// Function: FFMpegTexture::modify_page // Access: Private // Description: Returns a reference to the zth VideoPage (level) of // the texture. In the case of a 2-d texture, there is @@ -142,8 +143,8 @@ do_reconsider_video_properties(const FFMpegTexture::VideoStream &stream, num_frames = (int)((stream._format_context->duration*frame_rate)/AV_TIME_BASE); if (grutil_cat.is_debug()) { grutil_cat.debug() - << "Loaded " << stream._filename << ", " << num_frames << " frames at " - << frame_rate << " fps\n"; + << "Loaded " << stream._filename << ", " + << num_frames << " frames at " << frame_rate << " fps\n"; } } @@ -202,7 +203,7 @@ make_texture() { } //////////////////////////////////////////////////////////////////// -// Function: FFMPegTexture::update_frame +// Function: FFMpegTexture::update_frame // Access: Protected, Virtual // Description: Called once per frame, as needed, to load the new // image contents. @@ -419,6 +420,30 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n, return Texture::do_load_one(pnmimage, name, z, n, options); } +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::do_has_bam_rawdata +// Access: Protected, Virtual +// Description: Returns true if there is a rawdata image that we have +// available to write to the bam stream. For a normal +// Texture, this is the same thing as +// do_has_ram_image(), but a movie texture might define +// it differently. +//////////////////////////////////////////////////////////////////// +bool FFMpegTexture:: +do_has_bam_rawdata() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::do_get_bam_rawdata +// Access: Protected, Virtual +// Description: If do_has_bam_rawdata() returned false, this attempts +// to reload the rawdata image if possible. +//////////////////////////////////////////////////////////////////// +void FFMpegTexture:: +do_get_bam_rawdata() { +} + @@ -429,20 +454,77 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n, //////////////////////////////////////////////////////////////////// void FFMpegTexture:: register_with_read_factory() { - // Since Texture is such a funny object that is reloaded from the - // TexturePool each time, instead of actually being read fully from - // the bam file, and since the VideoTexture and FFMpegTexture - // classes don't really add any useful data to the bam record, we - // don't need to define make_from_bam(), fillin(), or - // write_datagram() in this class--we just inherit the same - // functions from Texture. - - // We do, however, have to register this class with the BamReader, - // to avoid warnings about creating the wrong kind of object from - // the bam file. BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::make_from_bam +// Access: Protected, Static +// Description: Factory method to generate an FFMpegTexture object +//////////////////////////////////////////////////////////////////// +TypedWritable *FFMpegTexture:: +make_from_bam(const FactoryParams ¶ms) { + PT(FFMpegTexture) dummy = new FFMpegTexture; + return dummy->make_this_from_bam(params); +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::do_write_datagram_rawdata +// Access: Protected, Virtual +// Description: Writes the rawdata part of the texture to the +// Datagram. +//////////////////////////////////////////////////////////////////// +void FFMpegTexture:: +do_write_datagram_rawdata(BamWriter *manager, Datagram &me) { + me.add_uint32(_x_size); + me.add_uint32(_y_size); + me.add_uint32(_z_size); + me.add_uint8(_component_type); + me.add_uint8(_component_width); + me.add_uint8(_ram_image_compression); + + me.add_uint16(_pages.size()); + for (size_t n = 0; n < _pages.size(); ++n) { + VideoPage &page = _pages[n]; + page.write_datagram_rawdata(manager, me); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::do_fillin_rawdata +// Access: Protected, Virtual +// Description: Reads in the part of the Texture that was written +// with do_write_datagram_rawdata(). +//////////////////////////////////////////////////////////////////// +void FFMpegTexture:: +do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) { + _x_size = scan.get_uint32(); + _y_size = scan.get_uint32(); + _z_size = scan.get_uint32(); + _component_type = (ComponentType)scan.get_uint8(); + _component_width = scan.get_uint8(); + _ram_image_compression = CM_off; + _ram_image_compression = (CompressionMode)scan.get_uint8(); + + int num_pages = scan.get_uint16(); + _pages.clear(); + for (int n = 0; n < num_pages; ++n) { + _pages.push_back(VideoPage()); + VideoPage &page = _pages.back(); + page.fillin_rawdata(scan, manager); + + LoaderOptions options; + do_reconsider_video_properties(page._color, _num_components, n, options); + if (page._alpha.is_valid()) { + do_reconsider_video_properties(page._alpha, _num_components, n, options); + } + } + + _loaded_from_image = true; + do_set_pad_size(0, 0, 0); + ++_image_modified; +} + //////////////////////////////////////////////////////////////////// // Function: FFMpegTexture::VideoStream::Constructor // Access: Public @@ -474,15 +556,17 @@ VideoStream(const FFMpegTexture::VideoStream ©) : { // Rather than copying the _capture pointer, we must open a new // stream that references the same file. - if (copy.is_valid()) { - if (copy.is_from_file()) { + if (copy.is_valid() && copy.is_from_file()) { + if (copy._file_info.is_empty()) { read(copy._filename); + } else { + read(copy._file_info); } } } //////////////////////////////////////////////////////////////////// -// Function: FFMpegTexture::VideoStream::Copy Constructor +// Function: FFMpegTexture::VideoStream::Destructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// @@ -491,6 +575,26 @@ FFMpegTexture::VideoStream:: clear(); } +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoStream:: +operator = (const FFMpegTexture::VideoStream ©) { + clear(); + + // Rather than copying the _capture pointer, we must open a new + // stream that references the same file. + if (copy.is_valid() && copy.is_from_file()) { + if (copy._file_info.is_empty()) { + read(copy._filename); + } else { + read(copy._file_info); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: FFMpegTexture::VideoStream::get_frame_data // Access: Public @@ -546,7 +650,7 @@ get_frame_data(int frame_number) { double time_stamp = ((double)AV_TIME_BASE * frame_number * vstream->r_frame_rate.den) / vstream->r_frame_rate.num; double curr_time_stamp; - + // find point in time av_seek_frame(_format_context, -1, (long long)time_stamp, AVSEEK_FLAG_BACKWARD); @@ -638,32 +742,147 @@ get_frame_data(int frame_number) { //////////////////////////////////////////////////////////////////// // Function: FFMpegTexture::VideoStream::read // Access: Public -// Description: Sets up the stream to read the indicated file. -// Returns true on success, false on failure. +// Description: Sets up the stream to read the indicated file from +// the VFS. Returns true on success, false on failure. //////////////////////////////////////////////////////////////////// bool FFMpegTexture::VideoStream:: read(const Filename &filename) { // Clear out the last stream clear(); - string os_specific = filename.to_os_specific(); // Open video file - int result = av_open_input_file(&_format_context, os_specific.c_str(), NULL, - 0, NULL); - if (result != 0) { - grutil_cat.error() << "ffmpeg AVERROR: " << result << endl; + if (!_ffvfile.open_vfs(filename)) { + grutil_cat.error() + << "couldn't open " << filename << "\n"; // Don't call clear(), because nothing happened yet return false; } + _filename = filename; + return continue_read(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::read +// Access: Public +// Description: Sets up the stream to read the indicated file, +// avoiding the VFS. Returns true on success, false on +// failure. +//////////////////////////////////////////////////////////////////// +bool FFMpegTexture::VideoStream:: +read(const SubfileInfo &info) { + // Clear out the last stream + clear(); + + // Open video file + if (!_ffvfile.open_subfile(info)) { + grutil_cat.error() + << "couldn't open " << info << "\n"; + // Don't call clear(), because nothing happened yet + return false; + } + + _file_info = info; + _filename = _file_info.get_filename(); + return continue_read(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::clear +// Access: Public +// Description: Stops the video playback and frees the associated +// resources. +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoStream:: +clear() { + if (_codec_context) { + avcodec_close(_codec_context); + _codec_context = NULL; + } + + if (_frame) { + av_free(_frame); + _frame = NULL; + } + if (_frame_out) { + av_free(_frame_out); + _frame_out = NULL; + } + + // We cannot close the format_context, since we didn't open it--the + // FfmpegVirtualFile will do that. + _format_context = NULL; + + _next_frame_number = 0; + _filename = Filename(); + _file_info = SubfileInfo(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::write_datagram_rawdata +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoStream:: +write_datagram_rawdata(BamWriter *manager, Datagram &me) { + SubfileInfo result; + if (!_file_info.is_empty()) { + me.add_bool(true); + manager->write_file_data(result, _file_info); + } else if (!_filename.empty()) { + me.add_bool(true); + manager->write_file_data(result, _filename); + } else { + me.add_bool(false); + } + + /* Not sure yet if this is a good idea. + if (!result.is_empty()) { + // If we've just copied the data to a local file, read it from + // there in the future. + _file_info = result; + } + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::fillin_rawdata +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoStream:: +fillin_rawdata(DatagramIterator &scan, BamReader *manager) { + bool got_info = scan.get_bool(); + if (got_info) { + SubfileInfo info; + manager->read_file_data(info); + read(info); + } else { + clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoStream::continue_read +// Access: Private +// Description: Once the FfmpegVirtualFile has been opened, continues +// to make the appropriate ffmpeg calls to open the +// stream. +//////////////////////////////////////////////////////////////////// +bool FFMpegTexture::VideoStream:: +continue_read() { + _format_context = _ffvfile.get_format_context(); + nassertr(_format_context != NULL, false); + // Retrieve stream information - result = av_find_stream_info(_format_context); + int result = av_find_stream_info(_format_context); if (result < 0) { grutil_cat.error() << "ffmpeg AVERROR: " << result << endl; clear(); return false; } - dump_format(_format_context, 0, os_specific.c_str(), false); + dump_format(_format_context, 0, _filename.c_str(), false); _stream_number = -1; for(size_t i = 0; i < _format_context->nb_streams; i++) { @@ -752,41 +971,11 @@ read(const Filename &filename) { // We could put an option here for single channel frames. _next_frame_number = 0; - _filename = filename; return true; } - -//////////////////////////////////////////////////////////////////// -// Function: FFMpegTexture::VideoStream::clear -// Access: Public -// Description: Stops the video playback and frees the associated -// resources. -//////////////////////////////////////////////////////////////////// -void FFMpegTexture::VideoStream:: -clear() { - if (_codec_context) { - avcodec_close(_codec_context); - _codec_context = NULL; - } - if (_format_context) { - av_close_input_file(_format_context); - _format_context = NULL; - } - if (_frame) { - av_free(_frame); - _frame = NULL; - } - if (_frame_out) { - av_free(_frame_out); - _frame_out = NULL; - } - - _next_frame_number = 0; -} - //////////////////////////////////////////////////////////////////// // Function: FFMpegTexture::VideoStream::read_video_frame // Access: Private @@ -830,6 +1019,28 @@ read_video_frame(AVPacket *packet) { return err; } +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoPage::write_datagram_rawdata +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoPage:: +write_datagram_rawdata(BamWriter *manager, Datagram &me) { + _color.write_datagram_rawdata(manager, me); + _alpha.write_datagram_rawdata(manager, me); +} + +//////////////////////////////////////////////////////////////////// +// Function: FFMpegTexture::VideoPage::fillin_rawdata +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FFMpegTexture::VideoPage:: +fillin_rawdata(DatagramIterator &scan, BamReader *manager) { + _color.fillin_rawdata(scan, manager); + _alpha.fillin_rawdata(scan, manager); +} + #endif // HAVE_FFMpeg diff --git a/panda/src/grutil/ffmpegTexture.h b/panda/src/grutil/ffmpegTexture.h index eda8b49361..d8b08d9b20 100644 --- a/panda/src/grutil/ffmpegTexture.h +++ b/panda/src/grutil/ffmpegTexture.h @@ -19,6 +19,7 @@ #ifdef HAVE_FFMPEG #include "videoTexture.h" +#include "ffmpegVirtualFile.h" extern "C" { #include "libavcodec/avcodec.h" @@ -55,6 +56,9 @@ protected: virtual bool do_load_one(const PNMImage &pnmimage, const string &name, int z, int n, const LoaderOptions &options); + virtual bool do_has_bam_rawdata() const; + virtual void do_get_bam_rawdata(); + private: class VideoPage; class VideoStream; @@ -70,25 +74,34 @@ private: VideoStream(); VideoStream(const VideoStream ©); ~VideoStream(); + void operator = (const VideoStream ©); bool read(const Filename &filename); + bool read(const SubfileInfo &info); void clear(); INLINE bool is_valid() const; INLINE bool is_from_file() const; bool get_frame_data(int frame); + void write_datagram_rawdata(BamWriter *manager, Datagram &me); + void fillin_rawdata(DatagramIterator &scan, BamReader *manager); + private: + bool continue_read(); int read_video_frame(AVPacket *packet); public: AVCodecContext *_codec_context; AVFormatContext *_format_context; + FfmpegVirtualFile _ffvfile; int _stream_number; AVFrame *_frame; AVFrame *_frame_out; Filename _filename; + SubfileInfo _file_info; + int _next_frame_number; int _image_size_bytes; @@ -102,6 +115,10 @@ private: INLINE VideoPage(); INLINE VideoPage(const VideoPage ©); INLINE ~VideoPage(); + INLINE void operator = (const VideoPage ©); + + void write_datagram_rawdata(BamWriter *manager, Datagram &me); + void fillin_rawdata(DatagramIterator &scan, BamReader *manager); VideoStream _color, _alpha; }; @@ -112,6 +129,10 @@ private: public: static void register_with_read_factory(); + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &me); + virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/grutil/movieTexture.cxx b/panda/src/grutil/movieTexture.cxx index 8c23e59249..f7ac7384ad 100644 --- a/panda/src/grutil/movieTexture.cxx +++ b/panda/src/grutil/movieTexture.cxx @@ -47,7 +47,7 @@ MovieTexture(const string &name) : // Description: Creates a texture playing the specified movie. //////////////////////////////////////////////////////////////////// MovieTexture:: -MovieTexture(PT(MovieVideo) video) : +MovieTexture(MovieVideo *video) : Texture(video->get_name()) { do_load_one(video->open(), NULL, 0, LoaderOptions()); @@ -134,18 +134,6 @@ make_texture() { return new MovieTexture(""); } -//////////////////////////////////////////////////////////////////// -// Function: MovieTexture::VideoPage::Constructor -// Access: Private -// Description: Creates a completely blank video page. -//////////////////////////////////////////////////////////////////// -MovieTexture::VideoPage:: -VideoPage() : - _color(0), - _alpha(0) -{ -} - //////////////////////////////////////////////////////////////////// // Function: MovieTexture::do_recalculate_image_properties // Access: Protected @@ -161,8 +149,9 @@ do_recalculate_image_properties(CDWriter &cdata, const LoaderOptions &options) { int y_max = 1; bool alpha = false; double len = 0.0; - - for (int i=0; i<_z_size; i++) { + + nassertv(cdata->_pages.size() == (size_t)_z_size); + for (int i = 0; i < _z_size; ++i) { MovieVideoCursor *t = cdata->_pages[i]._color; if (t) { if (t->size_x() > x_max) x_max = t->size_x(); @@ -299,27 +288,6 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n, return false; } -//////////////////////////////////////////////////////////////////// -// Function: MovieTexture::register_with_read_factory -// Access: Public, Static -// Description: Factory method to generate a Texture object -//////////////////////////////////////////////////////////////////// -void MovieTexture:: -register_with_read_factory() { - // Since Texture is such a funny object that is reloaded from the - // TexturePool each time, instead of actually being read fully from - // the bam file, and since the VideoTexture and MovieTexture - // classes don't really add any useful data to the bam record, we - // don't need to define make_from_bam(), fillin(), or - // write_datagram() in this class--we just inherit the same - // functions from Texture. - - // We do, however, have to register this class with the BamReader, - // to avoid warnings about creating the wrong kind of object from - // the bam file. - BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); -} - //////////////////////////////////////////////////////////////////// // Function: MovieTexture::has_cull_callback // Access: Public, Virtual @@ -390,19 +358,6 @@ cull_callback(CullTraverser *, const CullTraverserData &) const { return true; } -//////////////////////////////////////////////////////////////////// -// Function: MovieTexture::get_keep_ram_image -// Access: Published, Virtual -// Description: A MovieTexture must always keep its ram image, -// since there is no way to reload it from the -// source MovieVideo. -//////////////////////////////////////////////////////////////////// -bool MovieTexture:: -get_keep_ram_image() const { - // A MovieTexture should never dump its RAM image. - return true; -} - //////////////////////////////////////////////////////////////////// // Function: MovieTexture::do_make_copy // Access: Protected, Virtual @@ -476,6 +431,43 @@ do_reload_ram_image() { // Therefore, this is not needed. } +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::get_keep_ram_image +// Access: Published, Virtual +// Description: A MovieTexture must always keep its ram image, +// since there is no way to reload it from the +// source MovieVideo. +//////////////////////////////////////////////////////////////////// +bool MovieTexture:: +get_keep_ram_image() const { + // A MovieTexture should never dump its RAM image. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::do_has_bam_rawdata +// Access: Protected, Virtual +// Description: Returns true if there is a rawdata image that we have +// available to write to the bam stream. For a normal +// Texture, this is the same thing as +// do_has_ram_image(), but a movie texture might define +// it differently. +//////////////////////////////////////////////////////////////////// +bool MovieTexture:: +do_has_bam_rawdata() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::do_get_bam_rawdata +// Access: Protected, Virtual +// Description: If do_has_bam_rawdata() returned false, this attempts +// to reload the rawdata image if possible. +//////////////////////////////////////////////////////////////////// +void MovieTexture:: +do_get_bam_rawdata() { +} + //////////////////////////////////////////////////////////////////// // Function: MovieTexture::restart // Access: Published @@ -677,4 +669,117 @@ unsynchronize() { cdata->_synchronize = 0; } +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::register_with_read_factory +// Access: Public, Static +// Description: Factory method to generate a Texture object +//////////////////////////////////////////////////////////////////// +void MovieTexture:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::make_from_bam +// Access: Protected, Static +// Description: Factory method to generate a MovieTexture object +//////////////////////////////////////////////////////////////////// +TypedWritable *MovieTexture:: +make_from_bam(const FactoryParams ¶ms) { + PT(MovieTexture) dummy = new MovieTexture(""); + return dummy->make_this_from_bam(params); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::complete_pointers +// Access: Public, Virtual +// Description: Receives an array of pointers, one for each time +// manager->read_pointer() was called in fillin(). +// Returns the number of pointers processed. +//////////////////////////////////////////////////////////////////// +int MovieTexture:: +complete_pointers(TypedWritable **p_list, BamReader *manager) { + int pi = Texture::complete_pointers(p_list, manager); + + CDWriter cdata(_cycler); + size_t num_pages = cdata->_pages.size(); + for (size_t n = 0; n < num_pages; ++n) { + VideoPage &page = cdata->_pages[n]; + page._color = DCAST(MovieVideoCursor, p_list[pi++]); + page._alpha = DCAST(MovieVideoCursor, p_list[pi++]); + } + + return pi; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::do_write_datagram_rawdata +// Access: Protected, Virtual +// Description: Writes the rawdata part of the texture to the +// Datagram. +//////////////////////////////////////////////////////////////////// +void MovieTexture:: +do_write_datagram_rawdata(BamWriter *manager, Datagram &dg) { + CDReader cdata(_cycler); + + dg.add_uint16(cdata->_pages.size()); + for (size_t n = 0; n < cdata->_pages.size(); ++n) { + const VideoPage &page = cdata->_pages[n]; + manager->write_pointer(dg, page._color); + manager->write_pointer(dg, page._alpha); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::do_fillin_rawdata +// Access: Protected, Virtual +// Description: Reads in the part of the Texture that was written +// with do_write_datagram_rawdata(). +//////////////////////////////////////////////////////////////////// +void MovieTexture:: +do_fillin_rawdata(DatagramIterator &scan, BamReader *manager) { + CDWriter cdata(_cycler); + + size_t num_pages = scan.get_uint16(); + _z_size = (int)num_pages; + + cdata->_pages.reserve(num_pages); + for (size_t n = 0; n < num_pages; ++n) { + cdata->_pages.push_back(VideoPage()); + manager->read_pointer(scan); // page._color + manager->read_pointer(scan); // page._alpha + } + + // We load one or more MovieVideoCursors during the above loop. We + // need a finalize callback so we can initialize ourselves once + // those cursors have been read completely. + manager->register_finalize(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieTexture::finalize +// Access: Public, Virtual +// Description: Called by the BamReader to perform any final actions +// needed for setting up the object after all objects +// have been read and all pointers have been completed. +//////////////////////////////////////////////////////////////////// +void MovieTexture:: +finalize(BamReader *manager) { + CDWriter cdata(_cycler); + + // Insist that each of our video pages gets finalized before we do. + size_t num_pages = cdata->_pages.size(); + for (size_t n = 0; n < num_pages; ++n) { + VideoPage &page = cdata->_pages[n]; + manager->finalize_now(page._color); + manager->finalize_now(page._alpha); + } + + do_recalculate_image_properties(cdata, LoaderOptions()); + + set_loaded_from_image(); + set_loop(true); + play(); +} + #endif // HAVE_AUDIO diff --git a/panda/src/grutil/movieTexture.h b/panda/src/grutil/movieTexture.h index dc006671c0..3f58863f06 100644 --- a/panda/src/grutil/movieTexture.h +++ b/panda/src/grutil/movieTexture.h @@ -34,7 +34,7 @@ class EXPCL_PANDA_GRUTIL MovieTexture : public Texture { PUBLISHED: MovieTexture(const string &name); - MovieTexture(PT(MovieVideo) video); + MovieTexture(MovieVideo *video); private: MovieTexture(const MovieTexture ©); PUBLISHED: @@ -71,6 +71,8 @@ protected: virtual void do_reload_ram_image(); virtual bool get_keep_ram_image() const; + virtual bool do_has_bam_rawdata() const; + virtual void do_get_bam_rawdata(); virtual bool do_read_one(const Filename &fullpath, const Filename &alpha_fullpath, int z, int n, int primary_file_num_channels, int alpha_file_channel, const LoaderOptions &options, @@ -82,7 +84,6 @@ protected: class VideoPage { public: - VideoPage(); PT(MovieVideoCursor) _color; PT(MovieVideoCursor) _alpha; }; @@ -120,6 +121,13 @@ protected: public: static void register_with_read_factory(); + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + virtual int complete_pointers(TypedWritable **plist, BamReader *manager); + virtual void do_write_datagram_rawdata(BamWriter *manager, Datagram &dg); + virtual void do_fillin_rawdata(DatagramIterator &scan, BamReader *manager); + + virtual void finalize(BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index 4bbed30c6a..8de8306ca0 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -15,6 +15,21 @@ #include "config_movies.h" #include "dconfig.h" +#include "movieVideo.h" +#include "movieVideoCursor.h" + +#include "movieAudio.h" +#include "movieAudioCursor.h" + +#include "inkblotVideo.h" +#include "inkblotVideoCursor.h" + +#include "ffmpegVideo.h" +#include "ffmpegVideoCursor.h" + +#include "ffmpegAudio.h" +#include "ffmpegAudioCursor.h" + #ifdef HAVE_FFMPEG extern "C" { #include "libavcodec/avcodec.h" @@ -54,13 +69,17 @@ init_libmovies() { UserDataAudio::init_type(); UserDataAudioCursor::init_type(); MicrophoneAudio::init_type(); + #ifdef HAVE_FFMPEG + FfmpegVirtualFile::register_protocol(); + FfmpegVideo::init_type(); FfmpegVideoCursor::init_type(); FfmpegAudio::init_type(); FfmpegAudioCursor::init_type(); - av_register_all(); - FfmpegVirtualFile::register_protocol(); + + FfmpegVideo::register_with_read_factory(); + FfmpegVideoCursor::register_with_read_factory(); #endif } diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index c91c138c4f..7af6579295 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -21,21 +21,6 @@ #include "configVariableDouble.h" #include "dconfig.h" -#include "movieVideo.h" -#include "movieVideoCursor.h" - -#include "movieAudio.h" -#include "movieAudioCursor.h" - -#include "inkblotVideo.h" -#include "inkblotVideoCursor.h" - -#include "ffmpegVideo.h" -#include "ffmpegVideoCursor.h" - -#include "ffmpegAudio.h" -#include "ffmpegAudioCursor.h" - ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); NotifyCategoryDecl(ffmpeg, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); diff --git a/panda/src/movies/ffmpegAudio.h b/panda/src/movies/ffmpegAudio.h index 62d09a11d1..3a28f1c292 100644 --- a/panda/src/movies/ffmpegAudio.h +++ b/panda/src/movies/ffmpegAudio.h @@ -14,6 +14,9 @@ #ifndef FFMPEGAUDIO_H #define FFMPEGAUDIO_H + +#include "pandabase.h" + #ifdef HAVE_FFMPEG #include "movieAudio.h" diff --git a/panda/src/movies/ffmpegAudioCursor.cxx b/panda/src/movies/ffmpegAudioCursor.cxx index c8eca204ca..cf97597b3e 100644 --- a/panda/src/movies/ffmpegAudioCursor.cxx +++ b/panda/src/movies/ffmpegAudioCursor.cxx @@ -42,13 +42,14 @@ FfmpegAudioCursor(FfmpegAudio *src) : _buffer(0), _buffer_alloc(0) { - string url = "pandavfs:"; - url += _filename; - if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) { + if (!_ffvfile.open_vfs(_filename)) { cleanup(); return; } + _format_ctx = _ffvfile.get_format_context(); + nassertv(_format_ctx != NULL); + if (av_find_stream_info(_format_ctx)<0) { cleanup(); return; @@ -130,22 +131,25 @@ cleanup() { av_free_packet(_packet); } delete _packet; - _packet = 0; + _packet = NULL; } + if (_buffer_alloc) { delete[] _buffer_alloc; _buffer_alloc = 0; - _buffer = 0; + _buffer = NULL; } + if ((_audio_ctx)&&(_audio_ctx->codec)) { avcodec_close(_audio_ctx); } - _audio_ctx = 0; + _audio_ctx = NULL; + if (_format_ctx) { - av_close_input_file(_format_ctx); - _format_ctx = 0; + _ffvfile.close(); + _format_ctx = NULL; } - _audio_ctx = 0; + _audio_index = -1; } diff --git a/panda/src/movies/ffmpegAudioCursor.h b/panda/src/movies/ffmpegAudioCursor.h index db4582d558..9ce5e2db2c 100644 --- a/panda/src/movies/ffmpegAudioCursor.h +++ b/panda/src/movies/ffmpegAudioCursor.h @@ -14,12 +14,16 @@ #ifndef FFMPEGAUDIOCURSOR_H #define FFMPEGAUDIOCURSOR_H -#ifdef HAVE_FFMPEG #include "pandabase.h" + +#ifdef HAVE_FFMPEG + +#include "movieAudioCursor.h" #include "namable.h" #include "texture.h" #include "pointerTo.h" +#include "ffmpegVirtualFile.h" struct AVFormatContext; struct AVCodecContext; @@ -52,6 +56,7 @@ protected: unsigned char *_packet_data; AVFormatContext *_format_ctx; AVCodecContext *_audio_ctx; + FfmpegVirtualFile _ffvfile; int _audio_index; double _audio_timebase; diff --git a/panda/src/movies/ffmpegVideo.cxx b/panda/src/movies/ffmpegVideo.cxx index 9ba4dfdb2a..4ef49f04d9 100644 --- a/panda/src/movies/ffmpegVideo.cxx +++ b/panda/src/movies/ffmpegVideo.cxx @@ -15,6 +15,7 @@ #ifdef HAVE_FFMPEG #include "ffmpegVideo.h" +#include "ffmpegVideoCursor.h" #include "config_movies.h" TypeHandle FfmpegVideo::_type_handle; @@ -22,7 +23,9 @@ TypeHandle FfmpegVideo::_type_handle; //////////////////////////////////////////////////////////////////// // Function: FfmpegVideo::Constructor // Access: Public -// Description: xxx +// Description: Constructs an ffmpeg video that reads its contents +// from the indicate filename, which may be a file in +// the VFS. //////////////////////////////////////////////////////////////////// FfmpegVideo:: FfmpegVideo(const Filename &name) : @@ -31,6 +34,22 @@ FfmpegVideo(const Filename &name) : _filename = name; } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideo::Constructor +// Access: Public +// Description: Constructs an ffmpeg video that reads its contents +// from the indicated subfile information. This is +// normally used for low-level purposes only; you would +// normally use the constructor that takes a filename. +//////////////////////////////////////////////////////////////////// +FfmpegVideo:: +FfmpegVideo(const SubfileInfo &info) : + MovieVideo(info.get_filename()) +{ + _filename = info.get_filename(); + _subfile_info = info; +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVideo::Destructor // Access: Public @@ -52,10 +71,63 @@ open() { movies_cat.error() << "Could not open " << _filename << "\n"; return NULL; } else { - return (MovieVideoCursor*)(FfmpegVideoCursor*)result; + return result.p(); } } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideo::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// FfmpegVideo. +//////////////////////////////////////////////////////////////////// +void FfmpegVideo:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideo::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void FfmpegVideo:: +write_datagram(BamWriter *manager, Datagram &dg) { + MovieVideo::write_datagram(manager, dg); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideo::make_from_bam +// Access: Protected, Static +// Description: This function is called by the BamReader's factory +// when a new object of type FfmpegVideo is encountered +// in the Bam file. It should create the FfmpegVideo +// and extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWritable *FfmpegVideo:: +make_from_bam(const FactoryParams ¶ms) { + FfmpegVideo *video = new FfmpegVideo(""); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + video->fillin(scan, manager); + + return video; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideo::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new FfmpegVideo. +//////////////////////////////////////////////////////////////////// +void FfmpegVideo:: +fillin(DatagramIterator &scan, BamReader *manager) { + MovieVideo::fillin(scan, manager); +} //////////////////////////////////////////////////////////////////// diff --git a/panda/src/movies/ffmpegVideo.h b/panda/src/movies/ffmpegVideo.h index 4ec6dc75de..96209e32f3 100644 --- a/panda/src/movies/ffmpegVideo.h +++ b/panda/src/movies/ffmpegVideo.h @@ -14,11 +14,17 @@ #ifndef FFMPEGVIDEO_H #define FFMPEGVIDEO_H + +#include "pandabase.h" + #ifdef HAVE_FFMPEG #include "movieVideo.h" class FfmpegVideoCursor; +class FactoryParams; +class BamWriter; +class BamReader; //////////////////////////////////////////////////////////////////// // Class : FfmpegVideo @@ -26,13 +32,20 @@ class FfmpegVideoCursor; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo { - PUBLISHED: +PUBLISHED: FfmpegVideo(const Filename &name); + FfmpegVideo(const SubfileInfo &info); + virtual ~FfmpegVideo(); virtual PT(MovieVideoCursor) open(); - private: - friend class FfmpegVideoCursor; +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { @@ -50,6 +63,8 @@ public: private: static TypeHandle _type_handle; + + friend class FfmpegVideoCursor; }; #include "ffmpegVideo.I" diff --git a/panda/src/movies/ffmpegVideoCursor.cxx b/panda/src/movies/ffmpegVideoCursor.cxx index 789e8052ac..44c555ae08 100644 --- a/panda/src/movies/ffmpegVideoCursor.cxx +++ b/panda/src/movies/ffmpegVideoCursor.cxx @@ -32,39 +32,69 @@ TypeHandle FfmpegVideoCursor::_type_handle; #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO #endif - //////////////////////////////////////////////////////////////////// -// Function: FfmpegVideoCursor::Constructor -// Access: Public -// Description: xxx +// Function: FfmpegVideoCursor::Default Constructor +// Access: Protected +// Description: This constructor is only used when reading from a bam +// file. //////////////////////////////////////////////////////////////////// FfmpegVideoCursor:: -FfmpegVideoCursor(FfmpegVideo *src) : - MovieVideoCursor(src), - _filename(src->_filename), - _format_ctx(0), +FfmpegVideoCursor() : + _packet(NULL), + _format_ctx(NULL), + _video_ctx(NULL), _video_index(-1), - _video_ctx(0), - _frame(0), - _frame_out(0), - _packet(0), + _frame(NULL), + _frame_out(NULL), _min_fseek(3.0) { - string url = "pandavfs:"; - url += _filename; - if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) { - cleanup(); - return; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::init_from +// Access: Protected +// Description: Specifies the source of the video cursor. This is +// normally called only by the constructor or when +// reading from a bam file. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +init_from(FfmpegVideo *source) { + nassertv(source != NULL); + _source = source; + _filename = _source->get_filename(); + + if (!_source->get_subfile_info().is_empty()) { + // Read a subfile. + if (!_ffvfile.open_subfile(_source->get_subfile_info())) { + movies_cat.info() + << "Couldn't open " << _source->get_subfile_info() << "\n"; + cleanup(); + return; + } + + } else { + // Read a filename. + if (!_ffvfile.open_vfs(_filename)) { + movies_cat.info() + << "Couldn't open " << _filename << "\n"; + cleanup(); + return; + } } - - if (av_find_stream_info(_format_ctx)<0) { + + _format_ctx = _ffvfile.get_format_context(); + nassertv(_format_ctx != NULL); + + if (av_find_stream_info(_format_ctx) < 0) { + movies_cat.info() + << "Couldn't find stream info\n"; cleanup(); return; } // Find the video stream - for(int i = 0; i < (int)_format_ctx->nb_streams; i++) { - if(_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + for (int i = 0; i < (int)_format_ctx->nb_streams; ++i) { + if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { _video_index = i; _video_ctx = _format_ctx->streams[i]->codec; _video_timebase = av_q2d(_format_ctx->streams[i]->time_base); @@ -72,16 +102,22 @@ FfmpegVideoCursor(FfmpegVideo *src) : } if (_video_ctx == 0) { + movies_cat.info() + << "Couldn't find video_ctx\n"; cleanup(); return; } - AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id); - if(pVideoCodec == 0) { + AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); + if (pVideoCodec == NULL) { + movies_cat.info() + << "Couldn't find codec\n"; cleanup(); return; } - if(avcodec_open(_video_ctx, pVideoCodec)<0) { + if (avcodec_open(_video_ctx, pVideoCodec) < 0) { + movies_cat.info() + << "Couldn't open codec\n"; cleanup(); return; } @@ -109,6 +145,24 @@ FfmpegVideoCursor(FfmpegVideo *src) : _next_start = 0.0; } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FfmpegVideoCursor:: +FfmpegVideoCursor(FfmpegVideo *src) : + _packet(NULL), + _format_ctx(NULL), + _video_ctx(NULL), + _video_index(-1), + _frame(NULL), + _frame_out(NULL), + _min_fseek(3.0) +{ + init_from(src); +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVideoCursor::Destructor // Access: Public @@ -128,31 +182,34 @@ void FfmpegVideoCursor:: cleanup() { if (_frame) { av_free(_frame); - _frame = 0; + _frame = NULL; } + if (_frame_out) { _frame_out->data[0] = 0; av_free(_frame_out); - _frame_out = 0; + _frame_out = NULL; } + if (_packet) { if (_packet->data) { av_free_packet(_packet); } delete _packet; - _packet = 0; + _packet = NULL; } + if ((_video_ctx)&&(_video_ctx->codec)) { avcodec_close(_video_ctx); } - _video_ctx = 0; - if (_format_ctx) { - av_close_input_file(_format_ctx); - _format_ctx = 0; - } - _video_ctx = 0; - _video_index = -1; + _video_ctx = NULL; + if (_format_ctx) { + _ffvfile.close(); + _format_ctx = NULL; + } + + _video_index = -1; } //////////////////////////////////////////////////////////////////// @@ -171,7 +228,7 @@ export_frame(unsigned char *data, bool bgra, int bufx) { #ifdef HAVE_SWSCALE struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, _size_x, _size_y, - PIX_FMT_BGRA, 2, NULL, NULL, NULL); + PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL); nassertv(convert_ctx != NULL); sws_scale(convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); @@ -186,7 +243,7 @@ export_frame(unsigned char *data, bool bgra, int bufx) { #ifdef HAVE_SWSCALE struct SwsContext *convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, _size_x, _size_y, - PIX_FMT_BGR24, 2, NULL, NULL, NULL); + PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL); nassertv(convert_ctx != NULL); sws_scale(convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); @@ -385,5 +442,82 @@ fetch_into_buffer(double time, unsigned char *data, bool bgra) { } //////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// FfmpegVideo. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +write_datagram(BamWriter *manager, Datagram &dg) { + MovieVideoCursor::write_datagram(manager, dg); + + // No need to write any additional data here--all of it comes + // implicitly from the underlying MovieVideo, which we process in + // finalize(). +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::finalize +// Access: Public, Virtual +// Description: Called by the BamReader to perform any final actions +// needed for setting up the object after all objects +// have been read and all pointers have been completed. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +finalize(BamReader *) { + if (_source != (MovieVideo *)NULL) { + FfmpegVideo *video; + DCAST_INTO_V(video, _source); + init_from(video); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::make_from_bam +// Access: Protected, Static +// Description: This function is called by the BamReader's factory +// when a new object of type FfmpegVideo is encountered +// in the Bam file. It should create the FfmpegVideo +// and extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWritable *FfmpegVideoCursor:: +make_from_bam(const FactoryParams ¶ms) { + FfmpegVideoCursor *video = new FfmpegVideoCursor; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + video->fillin(scan, manager); + + return video; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new FfmpegVideo. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +fillin(DatagramIterator &scan, BamReader *manager) { + MovieVideoCursor::fillin(scan, manager); + + // The MovieVideoCursor gets the underlying MovieVideo pointer. We + // need a finalize callback so we can initialize ourselves once that + // has been read completely. + manager->register_finalize(this); +} #endif // HAVE_FFMPEG diff --git a/panda/src/movies/ffmpegVideoCursor.h b/panda/src/movies/ffmpegVideoCursor.h index 669ca9af75..7defd12a59 100644 --- a/panda/src/movies/ffmpegVideoCursor.h +++ b/panda/src/movies/ffmpegVideoCursor.h @@ -14,11 +14,15 @@ #ifndef FFMPEGVIDEOCURSOR_H #define FFMPEGVIDEOCURSOR_H -#ifdef HAVE_FFMPEG #include "pandabase.h" + +#ifdef HAVE_FFMPEG + +#include "movieVideoCursor.h" #include "texture.h" #include "pointerTo.h" +#include "ffmpegVirtualFile.h" struct AVFormatContext; struct AVCodecContext; @@ -31,17 +35,19 @@ struct AVFrame; // Description : //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor { - friend class FfmpegVideo; +protected: + FfmpegVideoCursor(); + void init_from(FfmpegVideo *src); - PUBLISHED: +PUBLISHED: FfmpegVideoCursor(FfmpegVideo *src); virtual ~FfmpegVideoCursor(); - public: +public: virtual void fetch_into_texture(double time, Texture *t, int page); virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba); - protected: +protected: void fetch_packet(double default_time); void fetch_frame(); void seek(double t); @@ -54,13 +60,24 @@ class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor { double _packet_time; AVFormatContext *_format_ctx; AVCodecContext *_video_ctx; - int _video_index; + FfmpegVirtualFile _ffvfile; + int _video_index; double _video_timebase; AVFrame *_frame; AVFrame *_frame_out; int _initial_dts; double _min_fseek; +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + + virtual void finalize(BamReader *manager); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; @@ -77,6 +94,8 @@ public: private: static TypeHandle _type_handle; + + friend class FfmpegVideo; }; #include "ffmpegVideoCursor.I" diff --git a/panda/src/movies/ffmpegVirtualFile.I b/panda/src/movies/ffmpegVirtualFile.I index 2da772d3c7..3cf1542012 100644 --- a/panda/src/movies/ffmpegVirtualFile.I +++ b/panda/src/movies/ffmpegVirtualFile.I @@ -11,3 +11,15 @@ // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::get_format_context +// Access: Public +// Description: Returns a pointer to the opened ffmpeg context, or +// NULL if the file was not successfully opened. +//////////////////////////////////////////////////////////////////// +INLINE AVFormatContext *FfmpegVirtualFile:: +get_format_context() const { + return _format_context; +} diff --git a/panda/src/movies/ffmpegVirtualFile.cxx b/panda/src/movies/ffmpegVirtualFile.cxx index 2ff2d14a74..5c7894fc68 100644 --- a/panda/src/movies/ffmpegVirtualFile.cxx +++ b/panda/src/movies/ffmpegVirtualFile.cxx @@ -18,154 +18,184 @@ #include "config_movies.h" #include "ffmpegVirtualFile.h" #include "virtualFileSystem.h" -extern "C" { - #include "libavformat/avio.h" -} #ifndef AVSEEK_SIZE #define AVSEEK_SIZE 0x10000 #endif //////////////////////////////////////////////////////////////////// -// These functions need to use C calling conventions. +// Function: FfmpegVirtualFile::Constructor +// Access: Public +// Description: //////////////////////////////////////////////////////////////////// -extern "C" { - static int pandavfs_open(URLContext *h, const char *filename, int flags); - static int pandavfs_read(URLContext *h, unsigned char *buf, int size); -// This change happened a few days before 52.68.0 -#if LIBAVFORMAT_VERSION_INT < 3425280 - static int pandavfs_write(URLContext *h, unsigned char *buf, int size); -#else - static int pandavfs_write(URLContext *h, const unsigned char *buf, int size); -#endif - static int64_t pandavfs_seek(URLContext *h, int64_t pos, int whence); - static int pandavfs_close(URLContext *h); +FfmpegVirtualFile:: +FfmpegVirtualFile() : + _format_context(NULL), + _in(NULL), + _owns_in(false) +{ } //////////////////////////////////////////////////////////////////// -// Function: pandavfs_open -// Access: Static Function -// Description: A hook to open a panda VFS file. +// Function: FfmpegVirtualFile::Destructor +// Access: Public +// Description: //////////////////////////////////////////////////////////////////// -static int -pandavfs_open(URLContext *h, const char *filename, int flags) { - if (flags != 0) { - if (movies_cat.is_debug()) { - movies_cat.debug() - << "ffmpeg is trying to write to the VFS.\n"; - } - // We'll allow this, but just fail any actual writes. - } +FfmpegVirtualFile:: +~FfmpegVirtualFile() { + close(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::Copy Constructor +// Access: Private +// Description: These objects are not meant to be copied. +//////////////////////////////////////////////////////////////////// +FfmpegVirtualFile:: +FfmpegVirtualFile(const FfmpegVirtualFile ©) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::Copy Assignment Operator +// Access: Private +// Description: These objects are not meant to be copied. +//////////////////////////////////////////////////////////////////// +void FfmpegVirtualFile:: +operator = (const FfmpegVirtualFile ©) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::open_vfs +// Access: Public +// Description: Opens the movie file via Panda's VFS. Returns true +// on success, false on failure. If successful, use +// get_format_context() to get the open file handle. +//////////////////////////////////////////////////////////////////// +bool FfmpegVirtualFile:: +open_vfs(const Filename &filename) { + close(); - filename += 9; // Skip over "pandavfs:" VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - istream *s = vfs->open_read_file(filename, true); - if (s == 0) { - return -1; + Filename fname = filename; + fname.set_binary(); + PT(VirtualFile) vfile = vfs->get_file(fname); + if (vfile == NULL) { + return false; } - // Test whether seek works. - s->seekg(1, ios::beg); - int tel1 = s->tellg(); - s->seekg(0, ios::beg); - int tel2 = s->tellg(); - if (s->fail() || (tel1!=1) || (tel2!=0)) { - movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n"; - delete s; - return -1; + + _in = vfile->open_read_file(true); + if (_in == NULL) { + return false; } - h->priv_data = s; - return 0; -} -//////////////////////////////////////////////////////////////////// -// Function: pandavfs_read -// Access: Static Function -// Description: A hook to read a panda VFS file. -//////////////////////////////////////////////////////////////////// -static int -pandavfs_read(URLContext *h, unsigned char *buf, int size) { - istream *s = (istream*)(h->priv_data); - s->read((char*)buf, size); - int gc = s->gcount(); - s->clear(); - return gc; -} + _owns_in = true; + _start = 0; + _size = vfile->get_file_size(_in); -//////////////////////////////////////////////////////////////////// -// Function: pandavfs_write -// Access: Static Function -// Description: A hook to write a panda VFS file. -//////////////////////////////////////////////////////////////////// -static int -#if LIBAVFORMAT_VERSION_INT < 3425280 -pandavfs_write(URLContext *h, unsigned char *buf, int size) { -#else -pandavfs_write(URLContext *h, const unsigned char *buf, int size) { -#endif - movies_cat.warning() - << "ffmpeg is trying to write to the VFS.\n"; - return -1; -} + // I tried to use av_open_input_stream(), but it (a) required a lot + // of low-level stream analysis calls that really should be + // automatic (and are automatic in av_open_input_file()), and (b) + // was broken on the ffmpeg build I happened to grab. Screw it, + // clearly av_open_input_file() is the preferred and more + // heavily-exercised interface. So we'll continue to use url + // synthesis as a hacky hook into this interface. -//////////////////////////////////////////////////////////////////// -// Function: pandavfs_seek -// Access: Static Function -// Description: A hook to seek a panda VFS file. -//////////////////////////////////////////////////////////////////// -static int64_t -pandavfs_seek(URLContext *h, int64_t pos, int whence) { - istream *s = (istream*)(h->priv_data); - switch(whence) { - case SEEK_SET: s->seekg(pos, ios::beg); break; - case SEEK_CUR: s->seekg(pos, ios::cur); break; - case SEEK_END: s->seekg(pos, ios::end); break; - case AVSEEK_SIZE: { - s->seekg(0, ios::cur); - int p = s->tellg(); - s->seekg(-1, ios::end); - int size = s->tellg(); - if (size < 0) { - movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n"; - s->clear(); - return -1; - } - size++; - s->seekg(p, ios::beg); - s->clear(); - return size; } - default: - movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n"; - s->clear(); - return -1; + // Nowadays we synthesize a "url" that references this pointer. + ostringstream strm; + strm << "pandavfs://" << (void *)this; + string url = strm.str(); + + // Now we can open the stream. + int result = + av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL); + if (result < 0) { + close(); + return false; } - s->clear(); - int tl = s->tellg(); - return tl; + + return true; } //////////////////////////////////////////////////////////////////// -// Function: pandavfs_close -// Access: Static Function -// Description: A hook to close a panda VFS file. +// Function: FfmpegVirtualFile::open_subfile +// Access: Public +// Description: Opens the movie file directly from a file on disk +// (does not go through the VFS). Returns true on +// success, false on failure. If successful, use +// get_format_context() to get the open file handle. //////////////////////////////////////////////////////////////////// -static int -pandavfs_close(URLContext *h) { - istream *s = (istream*)(h->priv_data); - delete s; - h->priv_data = 0; - return 0; +bool FfmpegVirtualFile:: +open_subfile(const SubfileInfo &info) { + close(); + + Filename fname = info.get_filename(); + fname.set_binary(); + if (!fname.open_read(_file_in)) { + return false; + } + + _in = &_file_in; + _owns_in = false; + _start = info.get_start(); + _size = info.get_size(); + + _in->seekg(_start); + + // I tried to use av_open_input_stream(), but it (a) required a lot + // of low-level ffmpeg calls that really shouldn't be part of the + // public API (and which aren't necessary with av_open_input_file() + // because they happen implicitly there), and (b) was completely + // broken on the ffmpeg build I happened to grab. Screw it; clearly + // av_open_input_file() is the preferred and more heavily-exercised + // interface. So we'll use it, even though it requires a bit of a + // hack. + + // The hack is that we synthesize a "url" that references this + // pointer, then open that url. This calls pandavfs_open(), which + // decodes the pointer and stores it for future callbacks. + ostringstream strm; + strm << "pandavfs://" << (void *)this; + string url = strm.str(); + + // Now we can open the stream. + int result = + av_open_input_file(&_format_context, url.c_str(), NULL, 0, NULL); + if (result < 0) { + close(); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::close +// Access: Public +// Description: Explicitly closes the opened file. This is also +// called implicitly by the destructor if necessary. +//////////////////////////////////////////////////////////////////// +void FfmpegVirtualFile:: +close() { + if (_format_context != NULL) { + av_close_input_file(_format_context); + _format_context = NULL; + } + + if (_owns_in) { + nassertv(_in != NULL); + VirtualFileSystem::close_read_file(_in); + _owns_in = false; + } + _in = NULL; } //////////////////////////////////////////////////////////////////// // Function: FfmpegVirtualFile::register_protocol // Access: Public, Static -// Description: Enables ffmpeg to access panda's VFS. -// -// After calling this method, ffmpeg will be -// able to open "URLs" that look like this: -// -// pandavfs:/c/mygame/foo.avi -// +// Description: Should be called at startup to attach the appropriate +// hooks between Panda and FFMpeg. //////////////////////////////////////////////////////////////////// void FfmpegVirtualFile:: register_protocol() { @@ -173,11 +203,22 @@ register_protocol() { if (initialized) { return; } + + // Here's a good place to call this global ffmpeg initialization + // function. + av_register_all(); + static URLProtocol protocol; protocol.name = "pandavfs"; protocol.url_open = pandavfs_open; protocol.url_read = pandavfs_read; + +#if LIBAVFORMAT_VERSION_INT < 3425280 + protocol.url_write = (int (*)(URLContext *, unsigned char *, int))pandavfs_write; +#else protocol.url_write = pandavfs_write; +#endif + protocol.url_seek = pandavfs_seek; protocol.url_close = pandavfs_close; #if LIBAVFORMAT_VERSION_INT < 3415296 @@ -192,6 +233,119 @@ register_protocol() { av_log_set_callback(&log_callback); } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::pandavfs_open +// Access: Private, Static +// Description: A callback to "open" a virtual file. Actually, all +// this does is assign the pointer back to the +// FfmpegVirtualFile instance. +//////////////////////////////////////////////////////////////////// +int FfmpegVirtualFile:: +pandavfs_open(URLContext *h, const char *filename, int flags) { + filename += 11; // Skip over "pandavfs://" + istringstream strm(filename); + void *ptr = 0; + strm >> ptr; + + FfmpegVirtualFile *self = (FfmpegVirtualFile *)ptr; + h->priv_data = self; + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::pandavfs_read +// Access: Private, Static +// Description: A callback to read a virtual file. +//////////////////////////////////////////////////////////////////// +int FfmpegVirtualFile:: +pandavfs_read(URLContext *h, unsigned char *buf, int size) { + FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); + istream *in = self->_in; + + // Since we may be simulating a subset of the opened stream, don't + // allow it to read past the "end". + streampos remaining = self->_start + (streampos)self->_size - in->tellg(); + if (remaining < size) { + if (remaining <= 0) { + return 0; + } + + size = (int)remaining; + } + + in->read((char *)buf, size); + int gc = in->gcount(); + in->clear(); + + return gc; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::pandavfs_write +// Access: Private, Static +// Description: A callback to write a virtual file. Unimplemented, +// because we use ffmpeg for playback only, not for +// encoding video streams. +//////////////////////////////////////////////////////////////////// +int FfmpegVirtualFile:: +pandavfs_write(URLContext *h, const unsigned char *buf, int size) { + movies_cat.warning() + << "ffmpeg is trying to write to the VFS.\n"; + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::pandavfs_seek +// Access: Private, Static +// Description: A callback to change the read position on an istream. +//////////////////////////////////////////////////////////////////// +int64_t FfmpegVirtualFile:: +pandavfs_seek(URLContext *h, int64_t pos, int whence) { + FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); + istream *in = self->_in; + + switch(whence) { + case SEEK_SET: + in->seekg(self->_start + pos, ios::beg); + break; + + case SEEK_CUR: + in->seekg(pos, ios::cur); + break; + + case SEEK_END: + // For seeks relative to the end, we actually compute the end + // based on _start + _size, and then use ios::beg. + in->seekg(self->_start + (streampos)self->_size + pos, ios::beg); + break; + + case AVSEEK_SIZE: + return self->_size; + + default: + movies_cat.error() + << "Illegal parameter to seek in ffmpegVirtualFile\n"; + in->clear(); + return -1; + } + + in->clear(); + return in->tellg() - self->_start; +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVirtualFile::pandavfs_close +// Access: Private, Static +// Description: A hook to "close" a panda VFS file. Actually it only +// clears the associated pointer. +//////////////////////////////////////////////////////////////////// +int FfmpegVirtualFile:: +pandavfs_close(URLContext *h) { + FfmpegVirtualFile *self = (FfmpegVirtualFile *)(h->priv_data); + h->priv_data = 0; + return 0; +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVirtualFile::log_callback // Access: Private, Static diff --git a/panda/src/movies/ffmpegVirtualFile.h b/panda/src/movies/ffmpegVirtualFile.h index 48cc490939..ce944c6cc6 100644 --- a/panda/src/movies/ffmpegVirtualFile.h +++ b/panda/src/movies/ffmpegVirtualFile.h @@ -14,27 +14,62 @@ #ifndef FFMPEGVIRTUALFILE_H #define FFMPEGVIRTUALFILE_H + +#include "pandabase.h" + #ifdef HAVE_FFMPEG #include "config_movies.h" +#include "filename.h" +#include "subfileInfo.h" + +extern "C" { + #include "libavformat/avio.h" +} #include +struct URLContext; +struct AVFormatContext; + //////////////////////////////////////////////////////////////////// // Class : FfmpegVirtualFile -// Description : Enables ffmpeg to access panda's VFS. -// -// Once register_protocol() is called, ffmpeg will be -// able to open "URLs" that look like this: -// -// pandavfs:/c/mygame/foo.avi -// +// Description : Enables ffmpeg to access panda's VFS. Create an +// instance of the FfmpegVirtualFile for each ffmpeg +// stream you wish to open. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_MOVIES FfmpegVirtualFile { - public: +public: + FfmpegVirtualFile(); + ~FfmpegVirtualFile(); +private: + FfmpegVirtualFile(const FfmpegVirtualFile ©); + void operator = (const FfmpegVirtualFile ©); + +public: + bool open_vfs(const Filename &filename); + bool open_subfile(const SubfileInfo &info); + void close(); + + INLINE AVFormatContext *get_format_context() const; + static void register_protocol(); - private: +private: + static int pandavfs_open(URLContext *h, const char *filename, int flags); + static int pandavfs_read(URLContext *h, unsigned char *buf, int size); + static int pandavfs_write(URLContext *h, const unsigned char *buf, int size); + static int64_t pandavfs_seek(URLContext *h, int64_t pos, int whence); + static int pandavfs_close(URLContext *h); + static void log_callback(void *ptr, int level, const char *fmt, va_list v1); + +private: + AVFormatContext *_format_context; + streampos _start; + streamsize _size; + istream *_in; + pifstream _file_in; + bool _owns_in; }; #include "ffmpegVirtualFile.I" diff --git a/panda/src/movies/movieAudio.cxx b/panda/src/movies/movieAudio.cxx index 59cf2e5e2b..5e59a1aa41 100644 --- a/panda/src/movies/movieAudio.cxx +++ b/panda/src/movies/movieAudio.cxx @@ -14,6 +14,7 @@ #include "movieAudio.h" #include "movieAudioCursor.h" +#include "ffmpegAudio.h" TypeHandle MovieAudio::_type_handle; diff --git a/panda/src/movies/movieVideo.I b/panda/src/movies/movieVideo.I index b939201386..76cd3e3b32 100644 --- a/panda/src/movies/movieVideo.I +++ b/panda/src/movies/movieVideo.I @@ -12,14 +12,27 @@ // //////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////// // Function: MovieVideo::get_filename // Access: Published // Description: Returns the movie's filename. A movie is not // guaranteed to have a filename, if not, then this -// function returns a null filename. +// function returns an empty filename. //////////////////////////////////////////////////////////////////// INLINE const Filename &MovieVideo:: get_filename() const { return _filename; } + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideo::get_subfile_info +// Access: Published +// Description: If the movie is to be loaded from a subfile on disk, +// this returns the subfile info. Check info.is_empty() +// to see if this is valid data. +//////////////////////////////////////////////////////////////////// +INLINE const SubfileInfo &MovieVideo:: +get_subfile_info() const { + return _subfile_info; +} diff --git a/panda/src/movies/movieVideo.cxx b/panda/src/movies/movieVideo.cxx index cd42f54444..1620151a5a 100644 --- a/panda/src/movies/movieVideo.cxx +++ b/panda/src/movies/movieVideo.cxx @@ -14,6 +14,9 @@ #include "movieVideo.h" #include "config_movies.h" +#include "ffmpegVideo.h" +#include "bamReader.h" +#include "bamWriter.h" TypeHandle MovieVideo::_type_handle; @@ -43,11 +46,12 @@ MovieVideo:: //////////////////////////////////////////////////////////////////// // Function: MovieVideo::open // Access: Published, Virtual -// Description: Open this video, returning a MovieVideoCursor. +// Description: Open this video, returning a MovieVideoCursor of the +// appropriate type. Returns NULL on error. //////////////////////////////////////////////////////////////////// PT(MovieVideoCursor) MovieVideo:: open() { - return new MovieVideoCursor(this); + return NULL; } //////////////////////////////////////////////////////////////////// @@ -66,3 +70,57 @@ get(const Filename &name) { #endif } + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideo::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void MovieVideo:: +write_datagram(BamWriter *manager, Datagram &dg) { + TypedWritableReferenceCount::write_datagram(manager, dg); + dg.add_string(_filename); + + // Now we record the raw movie data directly into the bam stream. + // We always do this, regardless of bam-texture-mode; we generally + // won't get to this codepath if bam-texture-mode isn't rawdata + // anyway. + + SubfileInfo result; + if (!_subfile_info.is_empty()) { + dg.add_bool(true); + manager->write_file_data(result, _subfile_info); + } else if (!_filename.empty()) { + dg.add_bool(true); + manager->write_file_data(result, _filename); + } else { + dg.add_bool(false); + } + + /* Not sure yet if this is a good idea. + if (!result.is_empty()) { + // If we've just copied the data to a local file, read it from + // there in the future. + _subfile_info = result; + } + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideo::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new MovieVideo. +//////////////////////////////////////////////////////////////////// +void MovieVideo:: +fillin(DatagramIterator &scan, BamReader *manager) { + TypedWritableReferenceCount::fillin(scan, manager); + _filename = scan.get_string(); + + bool got_info = scan.get_bool(); + if (got_info) { + manager->read_file_data(_subfile_info); + } +} diff --git a/panda/src/movies/movieVideo.h b/panda/src/movies/movieVideo.h index 59f07871dd..d63383bdc2 100644 --- a/panda/src/movies/movieVideo.h +++ b/panda/src/movies/movieVideo.h @@ -19,7 +19,13 @@ #include "namable.h" #include "pointerTo.h" #include "typedWritableReferenceCount.h" +#include "subfileInfo.h" +#include "filename.h" + class MovieVideoCursor; +class FactoryParams; +class BamWriter; +class BamReader; //////////////////////////////////////////////////////////////////// // Class : MovieVideo @@ -35,17 +41,26 @@ class MovieVideoCursor; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable { - PUBLISHED: +PUBLISHED: MovieVideo(const string &name = "Blank Video"); virtual ~MovieVideo(); virtual PT(MovieVideoCursor) open(); static PT(MovieVideo) get(const Filename &name); + INLINE const Filename &get_filename() const; + INLINE const SubfileInfo &get_subfile_info() const; - protected: +protected: Filename _filename; + SubfileInfo _subfile_info; + +public: + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); - public: +public: static TypeHandle get_class_type() { return _type_handle; } @@ -59,7 +74,7 @@ class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - private: +private: static TypeHandle _type_handle; }; diff --git a/panda/src/movies/movieVideoCursor.cxx b/panda/src/movies/movieVideoCursor.cxx index 2a5a2d3e1d..12a5f19f00 100644 --- a/panda/src/movies/movieVideoCursor.cxx +++ b/panda/src/movies/movieVideoCursor.cxx @@ -16,16 +16,17 @@ #include "config_movies.h" #include "pStatCollector.h" #include "pStatTimer.h" +#include "bamReader.h" +#include "bamWriter.h" TypeHandle MovieVideoCursor::_type_handle; //////////////////////////////////////////////////////////////////// -// Function: MovieVideoCursor::Constructor -// Access: Public -// Description: This constructor returns a null video stream --- a -// stream of plain blue and white frames that last one -// second each. To get more interesting video, you need -// to construct a subclass of this class. +// Function: MovieVideoCursor::Default Constructor +// Access: Protected +// Description: This is a virtual base class and should not be +// created directly. Instead, create a more specialized +// class. //////////////////////////////////////////////////////////////////// MovieVideoCursor:: MovieVideoCursor(MovieVideo *src) : @@ -302,3 +303,46 @@ fetch_into_buffer(double time, unsigned char *data, bool bgra) { } } + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void MovieVideoCursor:: +write_datagram(BamWriter *manager, Datagram &dg) { + TypedWritableReferenceCount::write_datagram(manager, dg); + + manager->write_pointer(dg, _source); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::complete_pointers +// Access: Public, Virtual +// Description: Receives an array of pointers, one for each time +// manager->read_pointer() was called in fillin(). +// Returns the number of pointers processed. +//////////////////////////////////////////////////////////////////// +int MovieVideoCursor:: +complete_pointers(TypedWritable **p_list, BamReader *manager) { + int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager); + + _source = DCAST(MovieVideo, p_list[pi++]); + + return pi; +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new MovieVideoCursor. +//////////////////////////////////////////////////////////////////// +void MovieVideoCursor:: +fillin(DatagramIterator &scan, BamReader *manager) { + TypedWritableReferenceCount::fillin(scan, manager); + + manager->read_pointer(scan); // _source +} diff --git a/panda/src/movies/movieVideoCursor.h b/panda/src/movies/movieVideoCursor.h index c2a8102550..5612aa4cfc 100644 --- a/panda/src/movies/movieVideoCursor.h +++ b/panda/src/movies/movieVideoCursor.h @@ -18,7 +18,11 @@ #include "pandabase.h" #include "texture.h" #include "pointerTo.h" + class MovieVideo; +class FactoryParams; +class BamWriter; +class BamReader; //////////////////////////////////////////////////////////////////// // Class : MovieVideoCursor @@ -35,9 +39,10 @@ class MovieVideo; // use separate MovieVideoCursor objects. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount { +protected: + MovieVideoCursor(MovieVideo *src = NULL); - PUBLISHED: - MovieVideoCursor(MovieVideo *src); +PUBLISHED: virtual ~MovieVideoCursor(); PT(MovieVideo) get_source() const; INLINE int size_x() const; @@ -57,14 +62,14 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount { virtual void fetch_into_texture_rgb(double time, Texture *t, int page); virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src); - public: +public: virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra); - private: +private: void allocate_conversion_buffer(); unsigned char *_conversion_buffer; - protected: +protected: PT(MovieVideo) _source; int _size_x; int _size_y; @@ -77,6 +82,13 @@ class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount { double _next_start; bool _streaming; bool _ready; + +public: + virtual void write_datagram(BamWriter *manager, Datagram &dg); + virtual int complete_pointers(TypedWritable **plist, BamReader *manager); + +protected: + void fillin(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/bamFile.cxx b/panda/src/pgraph/bamFile.cxx index bae42f6678..ce053bf028 100644 --- a/panda/src/pgraph/bamFile.cxx +++ b/panda/src/pgraph/bamFile.cxx @@ -417,7 +417,7 @@ continue_open_read(const string &bam_filename, bool report_errors) { return false; } - _reader = new BamReader(&_din, _bam_filename); + _reader = new BamReader(&_din); if (!_reader->init()) { close(); return false; @@ -444,7 +444,7 @@ continue_open_write(const string &bam_filename, bool report_errors) { return false; } - _writer = new BamWriter(&_dout, _bam_filename); + _writer = new BamWriter(&_dout); if (!_writer->init()) { close(); diff --git a/panda/src/putil/bam.h b/panda/src/putil/bam.h index e903515df8..f1a4b3e60f 100644 --- a/panda/src/putil/bam.h +++ b/panda/src/putil/bam.h @@ -33,7 +33,7 @@ static const unsigned short _bam_major_ver = 6; // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData. static const unsigned short _bam_first_minor_ver = 14; -static const unsigned short _bam_minor_ver = 24; +static const unsigned short _bam_minor_ver = 25; // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib. // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort. // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level. @@ -45,6 +45,7 @@ static const unsigned short _bam_minor_ver = 24; // Bumped to minor version 22 on 7/31/09 to add UvScrollNode R speed. // Bumped to minor version 23 on 5/4/10 to add internal TextureAttrib overrides. // Bumped to minor version 24 on 5/4/10 to add internal TexMatrixAttrib overrides. +// Bumped to minor version 25 on 6/22/11 to add support for caching movie files. #endif diff --git a/panda/src/putil/bamCache.cxx b/panda/src/putil/bamCache.cxx index 9210afb1ad..f1bf27e02c 100644 --- a/panda/src/putil/bamCache.cxx +++ b/panda/src/putil/bamCache.cxx @@ -223,16 +223,8 @@ store(BamCacheRecord *record) { temp_pathname.set_extension(extension); temp_pathname.set_binary(); - pofstream temp_file; - if (!temp_pathname.open_write(temp_file)) { - util_cat.error() - << "Could not open cache file: " << temp_pathname << "\n"; - emergency_read_only(); - return false; - } - DatagramOutputFile dout; - if (!dout.open(temp_file)) { + if (!dout.open(temp_pathname)) { util_cat.error() << "Could not write cache file: " << temp_pathname << "\n"; temp_pathname.unlink(); @@ -248,7 +240,7 @@ store(BamCacheRecord *record) { } { - BamWriter writer(&dout, temp_pathname); + BamWriter writer(&dout); if (!writer.init()) { temp_pathname.unlink(); return false; @@ -279,8 +271,8 @@ store(BamCacheRecord *record) { // delete any TypedWritables below that haven't been written yet. } - record->_record_size = temp_file.tellp(); - temp_file.close(); + record->_record_size = dout.get_file_pos(); + dout.close(); // Now move the file into place. if (!temp_pathname.rename_to(cache_pathname) && temp_pathname.exists()) { @@ -669,22 +661,13 @@ check_cache_size() { // success, or NULL on failure. //////////////////////////////////////////////////////////////////// BamCacheIndex *BamCache:: -do_read_index(Filename &index_pathname) { +do_read_index(const Filename &index_pathname) { if (index_pathname.empty()) { return NULL; } - index_pathname.set_binary(); - pifstream index_file; - if (!index_pathname.open_read(index_file)) { - util_cat.error() - << "Could not open index file: " << index_pathname << "\n"; - return NULL; - } - DatagramInputFile din; - - if (!din.open(index_file)) { + if (!din.open(index_pathname)) { util_cat.debug() << "Could not read index file: " << index_pathname << "\n"; return NULL; @@ -703,7 +686,7 @@ do_read_index(Filename &index_pathname) { return NULL; } - BamReader reader(&din, index_pathname); + BamReader reader(&din); if (!reader.init()) { return NULL; } @@ -738,18 +721,9 @@ do_read_index(Filename &index_pathname) { // Description: Writes the given index data to the specified filename. //////////////////////////////////////////////////////////////////// bool BamCache:: -do_write_index(Filename &index_pathname, const BamCacheIndex *index) { - index_pathname.set_binary(); - pofstream index_file; - - if (!index_pathname.open_write(index_file)) { - util_cat.error() - << "Could not open index file: " << index_pathname << "\n"; - return false; - } - +do_write_index(const Filename &index_pathname, const BamCacheIndex *index) { DatagramOutputFile dout; - if (!dout.open(index_file)) { + if (!dout.open(index_pathname)) { util_cat.error() << "Could not write index file: " << index_pathname << "\n"; index_pathname.unlink(); @@ -764,7 +738,7 @@ do_write_index(Filename &index_pathname, const BamCacheIndex *index) { } { - BamWriter writer(&dout, index_pathname); + BamWriter writer(&dout); if (!writer.init()) { index_pathname.unlink(); return false; @@ -776,7 +750,6 @@ do_write_index(Filename &index_pathname, const BamCacheIndex *index) { } } - index_file.close(); return true; } @@ -866,18 +839,9 @@ read_record(const Filename &source_pathname, // Description: Actually reads a record from the file. //////////////////////////////////////////////////////////////////// PT(BamCacheRecord) BamCache:: -do_read_record(Filename &cache_pathname, bool read_data) { - cache_pathname.set_binary(); - pifstream cache_file; - if (!cache_pathname.open_read(cache_file)) { - util_cat.debug() - << "Could not open cache file: " << cache_pathname << "\n"; - return NULL; - } - +do_read_record(const Filename &cache_pathname, bool read_data) { DatagramInputFile din; - - if (!din.open(cache_file)) { + if (!din.open(cache_pathname)) { util_cat.debug() << "Could not read cache file: " << cache_pathname << "\n"; return NULL; @@ -896,7 +860,7 @@ do_read_record(Filename &cache_pathname, bool read_data) { return NULL; } - BamReader reader(&din, cache_pathname); + BamReader reader(&din); if (!reader.init()) { return NULL; } @@ -944,10 +908,11 @@ do_read_record(Filename &cache_pathname, bool read_data) { } } - // Also get the file size. - cache_file.clear(); - cache_file.seekg(0, ios::end); - record->_record_size = cache_file.tellg(); + // Also get the total file size. + istream &in = din.get_stream(); + in.clear(); + in.seekg(0, ios::end); + record->_record_size = in.tellg(); // And the last access time is now, duh. record->_record_access_time = time(NULL); diff --git a/panda/src/putil/bamCache.h b/panda/src/putil/bamCache.h index 33d5c026f4..62e055764f 100644 --- a/panda/src/putil/bamCache.h +++ b/panda/src/putil/bamCache.h @@ -97,15 +97,15 @@ private: void emergency_read_only(); - static BamCacheIndex *do_read_index(Filename &index_pathname); - static bool do_write_index(Filename &index_pathname, const BamCacheIndex *index); + static BamCacheIndex *do_read_index(const Filename &index_pathname); + static bool do_write_index(const Filename &index_pathname, const BamCacheIndex *index); PT(BamCacheRecord) find_and_read_record(const Filename &source_pathname, const Filename &cache_filename); PT(BamCacheRecord) read_record(const Filename &source_pathname, const Filename &cache_filename, int pass); - static PT(BamCacheRecord) do_read_record(Filename &cache_pathname, + static PT(BamCacheRecord) do_read_record(const Filename &cache_pathname, bool read_data); static string hash_filename(const string &filename); diff --git a/panda/src/putil/bamEnums.cxx b/panda/src/putil/bamEnums.cxx index fc535465fe..abc27be284 100644 --- a/panda/src/putil/bamEnums.cxx +++ b/panda/src/putil/bamEnums.cxx @@ -66,6 +66,9 @@ operator << (ostream &out, BamEnums::BamObjectCode boc) { case BamEnums::BOC_remove: return out << "remove"; + + case BamEnums::BOC_file_data: + return out << "file_data"; } return out << "**invalid BamEnums::BamObjectCode value: (" << (int)boc << ")**"; diff --git a/panda/src/putil/bamEnums.h b/panda/src/putil/bamEnums.h index bd625716f0..5b7469a473 100644 --- a/panda/src/putil/bamEnums.h +++ b/panda/src/putil/bamEnums.h @@ -46,12 +46,15 @@ PUBLISHED: // (which does not). A BOC_adjunct includes an object definition // but does not push the level; it is associated with the current // level. BOC_remove lists object ID's that have been deallocated - // on the sender end. + // on the sender end. BOC_file_data may appear at any level and + // indicates the following datagram contains auxiliary file data + // that may be referenced by a later object. enum BamObjectCode { BOC_push, BOC_pop, BOC_adjunct, BOC_remove, + BOC_file_data, }; // This enum is used to control how textures are written to a bam diff --git a/panda/src/putil/bamReader.I b/panda/src/putil/bamReader.I index e0b10ed4e1..71eb0b911d 100644 --- a/panda/src/putil/bamReader.I +++ b/panda/src/putil/bamReader.I @@ -22,6 +22,17 @@ INLINE BamReaderAuxData:: BamReaderAuxData() { } +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_source +// Access: Published +// Description: Returns the current source of the BamReader as set by +// set_source() or the constructor. +//////////////////////////////////////////////////////////////////// +INLINE DatagramGenerator *BamReader:: +get_source() { + return _source; +} + //////////////////////////////////////////////////////////////////// // Function: BamReader::get_filename // Access: Published @@ -32,7 +43,11 @@ BamReaderAuxData() { //////////////////////////////////////////////////////////////////// INLINE const Filename &BamReader:: get_filename() const { - return _filename; + if (_source != (DatagramGenerator *)NULL) { + return _source->get_filename(); + } + static const Filename empty_filename; + return empty_filename; } //////////////////////////////////////////////////////////////////// @@ -134,14 +149,27 @@ get_current_minor_ver() const { //////////////////////////////////////////////////////////////////// // Function: BamReader::get_file // Access: Public +// Description: Returns the FileReference that provides the source for +// these datagrams, if any, or NULL if the datagrams do +// not originate from a file on disk. +//////////////////////////////////////////////////////////////////// +INLINE const FileReference *BamReader:: +get_file() { + nassertr(_source != NULL, NULL); + return _source->get_file(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::get_vfile +// Access: Public // Description: Returns the VirtualFile that provides the source for // these datagrams, if any, or NULL if the datagrams do // not originate from a VirtualFile. //////////////////////////////////////////////////////////////////// INLINE VirtualFile *BamReader:: -get_file() { +get_vfile() { nassertr(_source != NULL, NULL); - return _source->get_file(); + return _source->get_vfile(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/putil/bamReader.cxx b/panda/src/putil/bamReader.cxx index 78214ac345..aee52d2968 100644 --- a/panda/src/putil/bamReader.cxx +++ b/panda/src/putil/bamReader.cxx @@ -39,8 +39,8 @@ const int BamReader::_cur_minor = _bam_minor_ver; // Description: //////////////////////////////////////////////////////////////////// BamReader:: -BamReader(DatagramGenerator *source, const Filename &name) - : _source(source), _filename(name) +BamReader(DatagramGenerator *source) + : _source(source) { _needs_init = true; _num_extra_objects = 0; @@ -599,7 +599,7 @@ read_handle(DatagramIterator &scan) { type = TypeRegistry::ptr()->register_dynamic_type(name); bam_cat.warning() - << "Bam file '" << _filename << "' contains objects of unknown type: " + << "Bam file '" << get_filename() << "' contains objects of unknown type: " << type << "\n"; new_type = true; _new_types.insert(type); @@ -653,7 +653,7 @@ read_handle(DatagramIterator &scan) { // complete_pointers() callback function will be called // with an array of actual pointers, one for each time // read_pointer() was called. It is then the calling -// object's responsibilty to store these pointers in the +// object's responsibility to store these pointers in the // object properly. //////////////////////////////////////////////////////////////////// void BamReader:: @@ -718,6 +718,30 @@ skip_pointer(DatagramIterator &scan) { read_object_id(scan); } +//////////////////////////////////////////////////////////////////// +// Function: BamReader::read_file_data +// Access: Public +// Description: Reads a block of auxiliary file data from the Bam +// file. This can be a block of arbitrary size, and it +// is assumed it may be quite large. Rather than +// reading the entire block into memory, a file +// reference is returned to locate the block on disk. +// The data must have been written by a matching call to +// write_file_data(). +//////////////////////////////////////////////////////////////////// +void BamReader:: +read_file_data(SubfileInfo &info) { + // write_file_data() actually writes the blocks in datagrams prior + // to this particular datagram. Assume we get the calls to + // read_file_data() in the same order as the corresponding calls to + // write_file_data(), and just pop the first one off the + // queue. There's no actual data written to the stream at this + // point. + nassertv(!_file_data_records.empty()); + info = _file_data_records.front(); + _file_data_records.pop_front(); +} + //////////////////////////////////////////////////////////////////// // Function: BamReader::read_cdata // Access: Public @@ -962,7 +986,9 @@ register_change_this(ChangeThisRefFunc func, TypedWritableReferenceCount *object //////////////////////////////////////////////////////////////////// void BamReader:: finalize_now(TypedWritable *whom) { - nassertv(whom != (TypedWritable *)NULL); + if (whom == (TypedWritable *)NULL) { + return; + } Finalize::iterator fi = _finalize_list.find(whom); if (fi != _finalize_list.end()) { @@ -1136,10 +1162,10 @@ read_pta_id(DatagramIterator &scan) { //////////////////////////////////////////////////////////////////// int BamReader:: p_read_object() { - Datagram packet; + Datagram dg; // First, read a datagram for the object. - if (!get_datagram(packet)) { + if (!get_datagram(dg)) { // When we run out of datagrams, we're at the end of the file. if (bam_cat.is_debug()) { bam_cat.debug() @@ -1149,7 +1175,7 @@ p_read_object() { } // Now extract the object definition from the datagram. - DatagramIterator scan(packet); + DatagramIterator scan(dg); // First, read the BamObjectCode. In bam versions prior to 6.21, // there was no BamObjectCode in the stream. @@ -1179,6 +1205,23 @@ p_read_object() { // next object id in the stream. It's easiest to do this by // calling recursively. return p_read_object(); + + case BOC_file_data: + // Another special case. This marks an auxiliary file data record + // that we skip over for now, but we note its position within the + // stream, so that we can hand it to a future object who may + // request it. + { + SubfileInfo info; + if (!_source->save_datagram(info)) { + bam_cat.error() + << "Failed to read file data.\n"; + return 0; + } + _file_data_records.push_back(info); + } + + return p_read_object(); } // An object definition in a Bam file consists of a TypeHandle diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h index 55ec7e20c9..6b2bff07c4 100644 --- a/panda/src/putil/bamReader.h +++ b/panda/src/putil/bamReader.h @@ -25,11 +25,13 @@ #include "datagramIterator.h" #include "bamReaderParam.h" #include "bamEnums.h" +#include "subfileInfo.h" #include "loaderOptions.h" #include "factory.h" #include "vector_int.h" #include "pset.h" #include "pmap.h" +#include "pdeque.h" #include "dcast.h" #include "pipelineCyclerBase.h" #include "referenceCount.h" @@ -125,10 +127,11 @@ public: PUBLISHED: // The primary interface for a caller. - BamReader(DatagramGenerator *source = NULL, const Filename &name = ""); + BamReader(DatagramGenerator *source = NULL); ~BamReader(); void set_source(DatagramGenerator *source); + INLINE DatagramGenerator *get_source(); bool init(); @@ -163,6 +166,8 @@ public: void read_pointers(DatagramIterator &scan, int count); void skip_pointer(DatagramIterator &scan); + void read_file_data(SubfileInfo &info); + void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler); void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler, void *extra_data); @@ -187,7 +192,8 @@ public: TypeHandle read_handle(DatagramIterator &scan); - INLINE VirtualFile *get_file(); + INLINE const FileReference *get_file(); + INLINE VirtualFile *get_vfile(); INLINE streampos get_file_pos(); public: @@ -233,9 +239,6 @@ private: typedef phash_map IndexMap; IndexMap _index_map; - // This is the filename of the BAM, or empty string if not in a file. - Filename _filename; - LoaderOptions _loader_options; // This maps the object ID numbers encountered within the Bam file @@ -307,6 +310,12 @@ private: PTAMap _pta_map; int _pta_id; + // This is a queue of the currently-pending file data blocks that we + // have recently encountered in the stream and still expect a + // subsequent object to request. + typedef pdeque FileDataRecords; + FileDataRecords _file_data_records; + // This is used internally to record all of the new types created // on-the-fly to satisfy bam requirements. We keep track of this // just so we can suppress warning messages from attempts to create diff --git a/panda/src/putil/bamWriter.I b/panda/src/putil/bamWriter.I index 47a7916c4c..89152ee150 100644 --- a/panda/src/putil/bamWriter.I +++ b/panda/src/putil/bamWriter.I @@ -13,6 +13,17 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::get_target +// Access: Published +// Description: Returns the current target of the BamWriter as set by +// set_target() or the constructor. +//////////////////////////////////////////////////////////////////// +INLINE DatagramSink *BamWriter:: +get_target() { + return _target; +} + //////////////////////////////////////////////////////////////////// // Function: BamWriter::get_filename // Access: Published @@ -23,7 +34,11 @@ //////////////////////////////////////////////////////////////////// INLINE const Filename &BamWriter:: get_filename() const { - return _filename; + if (_target != (DatagramSink *)NULL) { + return _target->get_filename(); + } + static const Filename empty_filename; + return empty_filename; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/putil/bamWriter.cxx b/panda/src/putil/bamWriter.cxx index 7156e27833..4301b58504 100644 --- a/panda/src/putil/bamWriter.cxx +++ b/panda/src/putil/bamWriter.cxx @@ -30,8 +30,7 @@ // Description: //////////////////////////////////////////////////////////////////// BamWriter:: -BamWriter(DatagramSink *target, const Filename &name) : - _filename(name), +BamWriter(DatagramSink *target) : _target(target) { ++_writing_seq; @@ -320,6 +319,74 @@ write_pointer(Datagram &packet, const TypedWritable *object) { } } +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::write_file_data +// Access: Public +// Description: Writes a block of auxiliary file data from the +// indicated file (within the vfs). This can be a block +// of arbitrary size, and it is assumed it may be quite +// large. This must be balanced by a matching call to +// read_file_data() on restore. +//////////////////////////////////////////////////////////////////// +void BamWriter:: +write_file_data(SubfileInfo &result, const Filename &filename) { + // We write file data by preceding with a singleton datagram that + // contains only the BOC_file_data token. + Datagram dg; + dg.add_uint8(BOC_file_data); + if (!_target->put_datagram(dg)) { + util_cat.error() + << "Unable to write data to output.\n"; + return; + } + + // Then we can write the file data itself, as its own (possibly + // quite large) followup datagram. + if (!_target->copy_datagram(result, filename)) { + util_cat.error() + << "Unable to write file data to output.\n"; + return; + } + + // Both of those get written to the bam stream prior to the datagram + // that represents this particular object, but they'll get pulled + // out in the same order and queued up in the BamReader. +} + +//////////////////////////////////////////////////////////////////// +// Function: BamWriter::write_file_data +// Access: Public +// Description: Writes a block of auxiliary file data from the +// indicated file (outside of the vfs). This can be a +// block of arbitrary size, and it is assumed it may be +// quite large. This must be balanced by a matching +// call to read_file_data() on restore. +//////////////////////////////////////////////////////////////////// +void BamWriter:: +write_file_data(SubfileInfo &result, const SubfileInfo &source) { + // We write file data by preceding with a singleton datagram that + // contains only the BOC_file_data token. + Datagram dg; + dg.add_uint8(BOC_file_data); + if (!_target->put_datagram(dg)) { + util_cat.error() + << "Unable to write data to output.\n"; + return; + } + + // Then we can write the file data itself, as its own (possibly + // quite large) followup datagram. + if (!_target->copy_datagram(result, source)) { + util_cat.error() + << "Unable to write file data to output.\n"; + return; + } + + // Both of those get written to the bam stream prior to the datagram + // that represents this particular object, but they'll get pulled + // out in the same order and queued up in the BamReader. +} + //////////////////////////////////////////////////////////////////// // Function: BamWriter::write_cdata // Access: Public diff --git a/panda/src/putil/bamWriter.h b/panda/src/putil/bamWriter.h index b40233d1d8..d7be292ca9 100644 --- a/panda/src/putil/bamWriter.h +++ b/panda/src/putil/bamWriter.h @@ -72,10 +72,11 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_PUTIL BamWriter : public BamEnums { PUBLISHED: - BamWriter(DatagramSink *target = NULL, const Filename &name = ""); + BamWriter(DatagramSink *target = NULL); ~BamWriter(); void set_target(DatagramSink *target); + INLINE DatagramSink *get_target(); bool init(); INLINE const Filename &get_filename() const; @@ -94,6 +95,10 @@ public: void consider_update(const TypedWritable *obj); void write_pointer(Datagram &packet, const TypedWritable *dest); + + void write_file_data(SubfileInfo &result, const Filename &filename); + void write_file_data(SubfileInfo &result, const SubfileInfo &source); + void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler); void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler, void *extra_data); @@ -108,9 +113,6 @@ private: int enqueue_object(const TypedWritable *object); bool flush_queue(); - // This is the filename of the BAM, or null string if not in a file. - Filename _filename; - BamEndian _file_endian; BamTextureMode _file_texture_mode; diff --git a/panda/src/putil/datagramInputFile.I b/panda/src/putil/datagramInputFile.I index d0f3510165..e6633e7506 100644 --- a/panda/src/putil/datagramInputFile.I +++ b/panda/src/putil/datagramInputFile.I @@ -15,7 +15,7 @@ //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE DatagramInputFile:: @@ -25,3 +25,26 @@ DatagramInputFile() { _in = (istream *)NULL; _owns_in = false; } + +//////////////////////////////////////////////////////////////////// +// Function: DatagramInputFile::open +// Access: Published +// Description: Opens the indicated filename for reading. Returns +// true on success, false on failure. +//////////////////////////////////////////////////////////////////// +INLINE bool DatagramInputFile:: +open(const Filename &filename) { + return open(new FileReference(filename)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramInputFile::get_stream +// Access: Published +// Description: Returns the istream represented by the input file. +//////////////////////////////////////////////////////////////////// +INLINE istream &DatagramInputFile:: +get_stream() { + static ifstream null_stream; + nassertr(_in != NULL, null_stream); + return *_in; +} diff --git a/panda/src/putil/datagramInputFile.cxx b/panda/src/putil/datagramInputFile.cxx index 158c4657ae..66520ffd80 100644 --- a/panda/src/putil/datagramInputFile.cxx +++ b/panda/src/putil/datagramInputFile.cxx @@ -13,6 +13,7 @@ //////////////////////////////////////////////////////////////////// #include "datagramInputFile.h" +#include "temporaryFile.h" #include "numeric_types.h" #include "datagramIterator.h" #include "profileTimer.h" @@ -24,19 +25,22 @@ //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::open -// Access: Public +// Access: Published // Description: Opens the indicated filename for reading. Returns // true on success, false on failure. //////////////////////////////////////////////////////////////////// bool DatagramInputFile:: -open(Filename filename) { +open(const FileReference *file) { close(); + _file = file; + _filename = _file->get_filename(); + // DatagramInputFiles are always binary. - filename.set_binary(); + _filename.set_binary(); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - _vfile = vfs->get_file(filename); + _vfile = vfs->get_file(_filename); if (_vfile == (VirtualFile *)NULL) { // No such file. return false; @@ -48,7 +52,7 @@ open(Filename filename) { //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::open -// Access: Public +// Access: Published // Description: Starts reading from the indicated stream. Returns // true on success, false on failure. The // DatagramInputFile does not take ownership of the @@ -56,18 +60,23 @@ open(Filename filename) { // it when you are done. //////////////////////////////////////////////////////////////////// bool DatagramInputFile:: -open(istream &in) { +open(istream &in, const Filename &filename) { close(); _in = ∈ _owns_in = false; + _filename = filename; + + if (!filename.empty()) { + _file = new FileReference(filename); + } return !_in->fail(); } //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::close -// Access: Public +// Access: Published // Description: Closes the file. This is also implicitly done when // the DatagramInputFile destructs. //////////////////////////////////////////////////////////////////// @@ -82,13 +91,16 @@ close() { _in = (istream *)NULL; _owns_in = false; + _file.clear(); + _filename = Filename(); + _read_first_datagram = false; _error = false; } //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::read_header -// Access: Public +// Access: Published // Description: Reads a sequence of bytes from the beginning of the // datagram file. This may be called any number of // times after the file has been opened and before the @@ -115,7 +127,7 @@ read_header(string &header, size_t num_bytes) { //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::get_datagram -// Access: Public, Virtual +// Access: Published, Virtual // Description: Reads the next datagram from the file. Returns true // on success, false if there is an error or end of // file. @@ -127,7 +139,7 @@ get_datagram(Datagram &data) { // First, get the size of the upcoming datagram. StreamReader reader(_in, false); - PN_uint32 num_bytes = reader.get_uint32(); + size_t num_bytes = reader.get_uint32(); if (_in->fail() || _in->eof()) { return false; } @@ -175,9 +187,90 @@ get_datagram(Datagram &data) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: DatagramInputFile::save_datagram +// Access: Published, Virtual +// Description: Skips over the next datagram without extracting it, +// but saves the relevant file information in the +// SubfileInfo object so that its data may be read +// later. For non-file-based datagram generators, this +// may mean creating a temporary file and copying the +// contents of the datagram to disk. +// +// Returns true on success, false on failure or if this +// method is unimplemented. +//////////////////////////////////////////////////////////////////// +bool DatagramInputFile:: +save_datagram(SubfileInfo &info) { + nassertr(_in != (istream *)NULL, false); + _read_first_datagram = true; + + // First, get the size of the upcoming datagram. + StreamReader reader(_in, false); + size_t num_bytes = reader.get_uint32(); + if (_in->fail() || _in->eof()) { + return false; + } + + // If this stream is file-based, we can just point the SubfileInfo + // directly into this file. + if (_file != (FileReference *)NULL) { + info = SubfileInfo(_file, _in->tellg(), num_bytes); + _in->seekg(num_bytes, ios::cur); + return true; + } + + // Otherwise, we have to dump the data into a temporary file. + PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", "")); + pofstream out; + Filename filename = tfile->get_filename(); + filename.set_binary(); + if (!filename.open_write(out)) { + util_cat.error() + << "Couldn't write to " << tfile->get_filename() << "\n"; + return false; + } + + if (util_cat.is_debug()) { + util_cat.debug() + << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n"; + } + + size_t num_remaining = num_bytes; + static const size_t buffer_size = 4096; + char buffer[buffer_size]; + + _in->read(buffer, min(buffer_size, num_remaining)); + size_t count = _in->gcount(); + while (count != 0) { + out.write(buffer, count); + if (out.fail()) { + util_cat.error() + << "Couldn't write " << num_bytes << " bytes to " + << tfile->get_filename() << "\n"; + return false; + } + num_remaining -= count; + if (num_remaining == 0) { + break; + } + _in->read(buffer, min(buffer_size, num_remaining)); + count = _in->gcount(); + } + + if (num_remaining != 0) { + util_cat.error() + << "Truncated data stream.\n"; + return false; + } + + info = SubfileInfo(tfile, 0, num_bytes); + return true; +} + //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::is_eof -// Access: Public, Virtual +// Access: Published, Virtual // Description: Returns true if the file has reached the end-of-file. // This test may only be made after a call to // read_header() or get_datagram() has failed. @@ -189,7 +282,7 @@ is_eof() { //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::is_error -// Access: Public, Virtual +// Access: Published, Virtual // Description: Returns true if the file has reached an error // condition. //////////////////////////////////////////////////////////////////// @@ -205,21 +298,45 @@ is_error() { return _error; } +//////////////////////////////////////////////////////////////////// +// Function: DatagramInputFile::get_filename +// Access: Published, Virtual +// Description: Returns the filename that provides the source for +// these datagrams, if any, or empty string if the +// datagrams do not originate from a file on disk. +//////////////////////////////////////////////////////////////////// +const Filename &DatagramInputFile:: +get_filename() { + return _filename; +} + //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::get_file -// Access: Public, Virtual +// Access: Published, Virtual +// Description: Returns the FileReference that provides the source for +// these datagrams, if any, or NULL if the datagrams do +// not originate from a file on disk. +//////////////////////////////////////////////////////////////////// +const FileReference *DatagramInputFile:: +get_file() { + return _file; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramInputFile::get_vfile +// Access: Published, Virtual // Description: Returns the VirtualFile that provides the source for // these datagrams, if any, or NULL if the datagrams do // not originate from a VirtualFile. //////////////////////////////////////////////////////////////////// VirtualFile *DatagramInputFile:: -get_file() { +get_vfile() { return _vfile; } //////////////////////////////////////////////////////////////////// // Function: DatagramInputFile::get_file_pos -// Access: Public, Virtual +// Access: Published, Virtual // Description: Returns the current file position within the data // stream, if any, or 0 if the file position is not // meaningful or cannot be determined. diff --git a/panda/src/putil/datagramInputFile.h b/panda/src/putil/datagramInputFile.h index 1840998bb2..6550005288 100644 --- a/panda/src/putil/datagramInputFile.h +++ b/panda/src/putil/datagramInputFile.h @@ -19,6 +19,7 @@ #include "datagramGenerator.h" #include "filename.h" +#include "fileReference.h" #include "virtualFile.h" //////////////////////////////////////////////////////////////////// @@ -28,29 +29,36 @@ // of datagrams. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_PUTIL DatagramInputFile : public DatagramGenerator { -public: +PUBLISHED: INLINE DatagramInputFile(); - bool open(Filename filename); - bool open(istream &in); + bool open(const FileReference *file); + INLINE bool open(const Filename &filename); + bool open(istream &in, const Filename &filename = Filename()); + INLINE istream &get_stream(); void close(); bool read_header(string &header, size_t num_bytes); virtual bool get_datagram(Datagram &data); + virtual bool save_datagram(SubfileInfo &info); virtual bool is_eof(); virtual bool is_error(); - virtual VirtualFile *get_file(); + virtual const Filename &get_filename(); + virtual const FileReference *get_file(); + virtual VirtualFile *get_vfile(); virtual streampos get_file_pos(); private: bool _read_first_datagram; bool _error; + CPT(FileReference) _file; PT(VirtualFile) _vfile; pifstream _in_file; istream *_in; bool _owns_in; + Filename _filename; }; #include "datagramInputFile.I" diff --git a/panda/src/putil/datagramOutputFile.I b/panda/src/putil/datagramOutputFile.I index 7ea3050cd5..37dc3b684a 100644 --- a/panda/src/putil/datagramOutputFile.I +++ b/panda/src/putil/datagramOutputFile.I @@ -25,3 +25,26 @@ DatagramOutputFile() { _out = (ostream *)NULL; _owns_out = false; } + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::open +// Access: Published +// Description: Opens the indicated filename for writing. Returns +// true on success, false on failure. +//////////////////////////////////////////////////////////////////// +INLINE bool DatagramOutputFile:: +open(const Filename &filename) { + return open(new FileReference(filename)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::get_stream +// Access: Published +// Description: Returns the ostream represented by the output file. +//////////////////////////////////////////////////////////////////// +INLINE ostream &DatagramOutputFile:: +get_stream() { + static ofstream null_stream; + nassertr(_out != NULL, null_stream); + return *_out; +} diff --git a/panda/src/putil/datagramOutputFile.cxx b/panda/src/putil/datagramOutputFile.cxx index 37cda40972..c44c7fde47 100644 --- a/panda/src/putil/datagramOutputFile.cxx +++ b/panda/src/putil/datagramOutputFile.cxx @@ -19,21 +19,24 @@ //////////////////////////////////////////////////////////////////// // Function: DatagramOutputFile::open // Access: Public -// Description: Opens the indicated filename for reading. Returns +// Description: Opens the indicated filename for writing. Returns // true if successful, false on failure. //////////////////////////////////////////////////////////////////// bool DatagramOutputFile:: -open(Filename filename) { +open(const FileReference *file) { close(); + _file = file; + _filename = _file->get_filename(); + // DatagramOutputFiles are always binary. - filename.set_binary(); + _filename.set_binary(); _out = &_out_file; _owns_out = false; #ifdef HAVE_ZLIB - if (filename.get_extension() == "pz") { + if (_filename.get_extension() == "pz") { // The filename ends in .pz, which means to automatically // compress the bam file that we write. _out = new OCompressStream(_out, _owns_out); @@ -41,7 +44,7 @@ open(Filename filename) { } #endif // HAVE_ZLIB - return filename.open_write(_out_file); + return _filename.open_write(_out_file); } //////////////////////////////////////////////////////////////////// @@ -54,11 +57,16 @@ open(Filename filename) { // it when you are done. //////////////////////////////////////////////////////////////////// bool DatagramOutputFile:: -open(ostream &out) { +open(ostream &out, const Filename &filename) { close(); _out = &out; _owns_out = false; + _filename = filename; + + if (!filename.empty()) { + _file = new FileReference(filename); + } return !_out->fail(); } @@ -78,6 +86,9 @@ close() { _out = (ostream *)NULL; _owns_out = false; + _file.clear(); + _filename = Filename(); + _wrote_first_datagram = false; _error = false; } @@ -123,6 +134,130 @@ put_datagram(const Datagram &data) { return !_out->fail(); } +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::copy_datagram +// Access: Published, Virtual +// Description: Copies the file data from the entire indicated +// file (via the vfs) as the next datagram. This is +// intended to support potentially very large datagrams. +// +// Returns true on success, false on failure or if this +// method is unimplemented. On true, fills "result" +// with the information that references the copied file, +// if possible. +//////////////////////////////////////////////////////////////////// +bool DatagramOutputFile:: +copy_datagram(SubfileInfo &result, const Filename &filename) { + nassertr(_out != (ostream *)NULL, false); + _wrote_first_datagram = true; + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + PT(VirtualFile) vfile = vfs->get_file(filename); + if (vfile == NULL) { + return false; + } + istream *in = vfile->open_read_file(true); + if (in == NULL) { + return false; + } + + off_t size = vfile->get_file_size(in); + size_t num_remaining = (size_t)size; + nassertr(num_remaining == size, false); + + StreamWriter writer(_out, false); + writer.add_uint32(num_remaining); + + static const size_t buffer_size = 4096; + char buffer[buffer_size]; + + streampos start = _out->tellp(); + in->read(buffer, min(buffer_size, num_remaining)); + size_t count = in->gcount(); + while (count != 0) { + _out->write(buffer, count); + if (_out->fail()) { + vfile->close_read_file(in); + return false; + } + num_remaining -= count; + if (num_remaining == 0) { + break; + } + in->read(buffer, min(buffer_size, num_remaining)); + count = in->gcount(); + } + + vfile->close_read_file(in); + + if (num_remaining != 0) { + util_cat.error() + << "Truncated input stream.\n"; + return false; + } + + result = SubfileInfo(_file, start, size); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::copy_datagram +// Access: Published, Virtual +// Description: Copies the file data from the range of the indicated +// file (outside of the vfs) as the next datagram. This +// is intended to support potentially very large +// datagrams. +// +// Returns true on success, false on failure or if this +// method is unimplemented. On true, fills "result" +// with the information that references the copied file, +// if possible. +//////////////////////////////////////////////////////////////////// +bool DatagramOutputFile:: +copy_datagram(SubfileInfo &result, const SubfileInfo &source) { + nassertr(_out != (ostream *)NULL, false); + _wrote_first_datagram = true; + + pifstream in; + if (!source.get_filename().open_read(in)) { + return false; + } + + size_t num_remaining = source.get_size(); + + StreamWriter writer(_out, false); + writer.add_uint32(num_remaining); + + static const size_t buffer_size = 4096; + char buffer[buffer_size]; + + streampos start = _out->tellp(); + in.seekg(source.get_start()); + in.read(buffer, min(buffer_size, num_remaining)); + size_t count = in.gcount(); + while (count != 0) { + _out->write(buffer, count); + if (_out->fail()) { + return false; + } + num_remaining -= count; + if (num_remaining == 0) { + break; + } + in.read(buffer, min(buffer_size, num_remaining)); + count = in.gcount(); + } + + if (num_remaining != 0) { + util_cat.error() + << "Truncated input stream.\n"; + return false; + } + + result = SubfileInfo(_file, start, source.get_size()); + return true; +} + //////////////////////////////////////////////////////////////////// // Function: DatagramOutputFile::is_error // Access: Public, Virtual @@ -153,3 +288,48 @@ flush() { _out->flush(); } } + + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::get_filename +// Access: Published, Virtual +// Description: Returns the filename that provides the target for +// these datagrams, if any, or empty string if the +// datagrams do not get written to a file on disk. +//////////////////////////////////////////////////////////////////// +const Filename &DatagramOutputFile:: +get_filename() { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::get_file +// Access: Published, Virtual +// Description: Returns the FileReference that provides the target for +// these datagrams, if any, or NULL if the datagrams do +// not written to a file on disk. +//////////////////////////////////////////////////////////////////// +const FileReference *DatagramOutputFile:: +get_file() { + return _file; +} + +//////////////////////////////////////////////////////////////////// +// Function: DatagramOutputFile::get_file_pos +// Access: Published, Virtual +// Description: Returns the current file position within the data +// stream, if any, or 0 if the file position is not +// meaningful or cannot be determined. +// +// For DatagramOutputFiles that return a meaningful file +// position, this will be pointing to the first byte +// following the datagram returned after a call to +// put_datagram(). +//////////////////////////////////////////////////////////////////// +streampos DatagramOutputFile:: +get_file_pos() { + if (_out == (ostream *)NULL) { + return 0; + } + return _out->tellp(); +} diff --git a/panda/src/putil/datagramOutputFile.h b/panda/src/putil/datagramOutputFile.h index 7b36e023f6..d9004417f6 100644 --- a/panda/src/putil/datagramOutputFile.h +++ b/panda/src/putil/datagramOutputFile.h @@ -19,6 +19,7 @@ #include "datagramSink.h" #include "filename.h" +#include "fileReference.h" //////////////////////////////////////////////////////////////////// // Class : DatagramOutputFile @@ -30,22 +31,32 @@ class EXPCL_PANDA_PUTIL DatagramOutputFile : public DatagramSink { public: INLINE DatagramOutputFile(); - bool open(Filename filename); - bool open(ostream &out); + bool open(const FileReference *file); + INLINE bool open(const Filename &filename); + bool open(ostream &out, const Filename &filename = Filename()); + INLINE ostream &get_stream(); void close(); bool write_header(const string &header); virtual bool put_datagram(const Datagram &data); + virtual bool copy_datagram(SubfileInfo &result, const Filename &filename); + virtual bool copy_datagram(SubfileInfo &result, const SubfileInfo &source); virtual bool is_error(); virtual void flush(); + virtual const Filename &get_filename(); + virtual const FileReference *get_file(); + virtual streampos get_file_pos(); + private: bool _wrote_first_datagram; bool _error; + CPT(FileReference) _file; pofstream _out_file; ostream *_out; bool _owns_out; + Filename _filename; }; #include "datagramOutputFile.I" diff --git a/panda/src/putil/typedWritable.cxx b/panda/src/putil/typedWritable.cxx index 462adaaa03..e34f590377 100644 --- a/panda/src/putil/typedWritable.cxx +++ b/panda/src/putil/typedWritable.cxx @@ -299,7 +299,7 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { return false; } - BamWriter writer(&dout, "bam_stream"); + BamWriter writer(&dout); if (!writer.init()) { return false; } @@ -371,7 +371,7 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, return false; } - BamReader reader(&din, "bam_stream"); + BamReader reader(&din); if (!reader.init()) { return false; }