#include "osg/Image" #include "osg/Notify" #include #include #include "osg/GL" #include "osgDB/FileNameUtils" #include "osgDB/Registry" #include "osgDB/FileUtils" #include #include #include #include #ifndef __APPLE__ #include "Components.h" #include "QuickTimeComponents.h" #else #include #endif #ifndef SEEK_SET # define SEEK_SET 0 #endif #include "QTUtils.h" #include "QTLiveUtils.h" #include "QTImportExport.h" #include "QuicktimeImageStream.h" #include "QuicktimeLiveImageStream.h" using namespace osg; // This class is used as a helper to de-initialize // properly quicktime, when the last media loaded // with the quicktime plugin is released. // All loaded media must be added to the observer // (see ReaderWriterQT::readImage() function) class QuicktimeExitObserver : public osg::Observer { public: QuicktimeExitObserver () : _instanceCount(0) { } virtual ~QuicktimeExitObserver() { }; void addMedia(Image* ptr) { ptr->addObserver(this); ++ _instanceCount; } virtual void objectDeleted(void*) { -- _instanceCount; if(_instanceCount== 0) exitQuicktime(); } private: unsigned int _instanceCount; }; class ReaderWriterQT : public osgDB::ReaderWriter { public: ReaderWriterQT::ReaderWriterQT() { } ReaderWriterQT::~ReaderWriterQT() { } virtual const char* className() const { return "Default Quicktime Image Reader/Writer"; } virtual bool acceptsMovieExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension,"mov") || osgDB::equalCaseInsensitive(extension,"mpg") || osgDB::equalCaseInsensitive(extension,"mpv") || osgDB::equalCaseInsensitive(extension,"mp4") || osgDB::equalCaseInsensitive(extension,"m4v") || osgDB::equalCaseInsensitive(extension,"dv") || osgDB::equalCaseInsensitive(extension,"avi") || osgDB::equalCaseInsensitive(extension,"flv") || osgDB::equalCaseInsensitive(extension,"swf"); } virtual bool acceptsLiveExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension,"live"); } virtual bool acceptsExtension(const std::string& extension) const { // this should be the only image importer required on the Mac // dont know what else it supports, but these will do return #ifdef QT_HANDLE_IMAGES_ALSO osgDB::equalCaseInsensitive(extension,"rgb") || osgDB::equalCaseInsensitive(extension,"rgba") || osgDB::equalCaseInsensitive(extension,"jpg") || osgDB::equalCaseInsensitive(extension,"jpeg") || osgDB::equalCaseInsensitive(extension,"tif") || osgDB::equalCaseInsensitive(extension,"tiff") || osgDB::equalCaseInsensitive(extension,"gif") || osgDB::equalCaseInsensitive(extension,"png") || osgDB::equalCaseInsensitive(extension,"pict") || osgDB::equalCaseInsensitive(extension,"pct") || osgDB::equalCaseInsensitive(extension,"tga") || osgDB::equalCaseInsensitive(extension,"psd") || #endif acceptsMovieExtension(extension) || acceptsLiveExtension(extension); } virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(file); if (osgDB::equalCaseInsensitive(ext,"qt")) { return readImage(osgDB::getNameLessExtension(file),options); } if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; // if the file is a ".live" video encoded string then load as an ImageStream if (acceptsLiveExtension(ext)) { long num_video_components; { // Begin QuickTime QTScopedQTMLInitialiser qt_init; QTScopedMovieInitialiser qt_movie_init; // ComponentDescription video_component_description; video_component_description.componentType = 'vdig'; /* A unique 4-byte code indentifying the command set */ video_component_description.componentSubType = 0L; /* Particular flavor of this instance */ video_component_description.componentManufacturer = 0L; /* Vendor indentification */ video_component_description.componentFlags = 0L; /* 8 each for Component,Type,SubType,Manuf/revision */ video_component_description.componentFlagsMask = 0L; /* Mask for specifying which flags to consider in search, zero during registration */ num_video_components = CountComponents (&video_component_description); } if (osgDB::getNameLessExtension(file) == "devices") { osg::notify(osg::ALWAYS) << " available Video DigitizerComponents : " << num_video_components << std::endl; if (num_video_components) { // Probe Video Dig probe_video_digitizer_components(); // Probe SG std::vector devices_list = probe_sequence_grabber_components(); if (devices_list.size()) { // Video OSG_SGDeviceList& video_device_list = devices_list[0]; // Print osg::notify(osg::ALWAYS) << std::endl; osg::notify(osg::ALWAYS) << "Video Component/Input IDs follow: " << std::endl; osg::notify(osg::ALWAYS) << std::endl; for (unsigned int device_input = 0; device_input < video_device_list.size(); ++device_input) { OSG_SGDevicePair device_pair = video_device_list[device_input]; osg::notify(osg::ALWAYS) << device_pair.first.c_str() << " " << device_pair.second.c_str() << std::endl; } } if (devices_list.size() > 1) { // Audio OSG_SGDeviceList& audio_device_list = devices_list[1]; // Print osg::notify(osg::ALWAYS) << std::endl; osg::notify(osg::ALWAYS) << "Audio Component/Input IDs follow: " << std::endl; osg::notify(osg::ALWAYS) << std::endl; for (unsigned int device_input = 0; device_input < audio_device_list.size(); ++device_input) { OSG_SGDevicePair device_pair = audio_device_list[device_input]; osg::notify(osg::ALWAYS) << device_pair.first.c_str() << " " << device_pair.second.c_str() << std::endl; } } } return ReadResult::FILE_NOT_HANDLED; } else { osg::notify(osg::DEBUG_INFO) << " available Video DigitizerComponents : " << num_video_components << std::endl; if (num_video_components) { // Note from Riccardo Corsi // Quicktime initialization is done here, when a media is found // and before any image or movie is loaded. // After the first call the function does nothing. // The cleaning up is left to the QuicktimeExitObserver (see below) initQuicktime(); // QuicktimeLiveImageStream* p_qt_image_stream = new QuicktimeLiveImageStream(osgDB::getNameLessExtension(file)); // add the media to the observer for proper clean up on exit _qtExitObserver.addMedia(p_qt_image_stream); return p_qt_image_stream; } else { osg::notify(osg::DEBUG_INFO) << "No available Video DigitizerComponents : " << std::endl; return ReadResult::FILE_NOT_HANDLED; } } } // Not an encoded "live" psuedo file - so check a real file exists std::string fileName = osgDB::findDataFile( file, options); if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; // Note from Riccardo Corsi // Quicktime initialization is done here, when a media is found // and before any image or movie is loaded. // After the first call the function does nothing. // The cleaning up is left to the QuicktimeExitObserver (see below) initQuicktime(); // if the file is a movie file then load as an ImageStream. if (acceptsMovieExtension(ext)) { // note from Robert Osfield when integrating, we should probably have so // error handling mechanism here. Possibly move the load from // the constructor to a seperate load method, and have a valid // state on the ImageStream... will integrated as is right now // to get things off the ground. QuicktimeImageStream* moov = new QuicktimeImageStream(fileName); // moov->play(); // add the media to the observer for proper clean up on exit _qtExitObserver.addMedia(moov); return moov; } QuicktimeImportExport importer; std::ifstream is; is.open (fileName.c_str(), std::ios::binary | std::ios::in ); is.seekg (0, std::ios::end); long length = is.tellg(); is.seekg (0, std::ios::beg); osg::ref_ptr image = importer.readFromStream(is, fileName, length); is.close(); if (!importer.success() || (image == NULL)) { osg::notify(osg::WARN) << "Error reading file " << file << " : " << importer.getLastErrorString() << std::endl; return ReadResult::ERROR_IN_READING_FILE; } _qtExitObserver.addMedia(image.get()); return image.release(); } virtual ReadResult readImage (std::istream& is, const osgDB::ReaderWriter::Options* options=NULL) const { std::string filename = ""; long sizeHint(0); // check options for a file-type-hint if (options) { std::istringstream iss(options->getOptionString()); std::string opt; while (iss >> opt) { int index = opt.find( "=" ); if( opt.substr( 0, index ) == "filename" || opt.substr( 0, index ) == "FILENAME" ) { filename = opt.substr( index+1 ); } else if( opt.substr( 0, index ) == "size" || opt.substr( 0, index ) == "SIZE" ) { std::string sizestr = opt.substr( index+1 ); sizeHint = atol(sizestr.c_str()); } } } QuicktimeImportExport importer; osg::ref_ptr image = importer.readFromStream(is, filename, sizeHint); if (!importer.success() || (image == NULL)) { osg::notify(osg::WARN) << "Error reading from stream " << importer.getLastErrorString() << std::endl; return ReadResult::ERROR_IN_READING_FILE; } _qtExitObserver.addMedia(image.get()); return image.release(); } virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options*) const { std::string ext = osgDB::getFileExtension(fileName); if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; initQuicktime(); //Buidl map of extension <-> osFileTypes std::map extmap; extmap.insert(std::pair("jpg", kQTFileTypeJPEG)); extmap.insert(std::pair("jpeg", kQTFileTypeJPEG)); extmap.insert(std::pair("bmp", kQTFileTypeBMP)); extmap.insert(std::pair("tif", kQTFileTypeTIFF)); extmap.insert(std::pair("tiff", kQTFileTypeTIFF)); extmap.insert(std::pair("png", kQTFileTypePNG)); extmap.insert(std::pair("gif", kQTFileTypeGIF)); extmap.insert(std::pair("psd", kQTFileTypePhotoShop)); extmap.insert(std::pair("sgi", kQTFileTypeSGIImage)); extmap.insert(std::pair("rgb", kQTFileTypeSGIImage)); extmap.insert(std::pair("rgba", kQTFileTypeSGIImage)); std::map::iterator cur = extmap.find(ext); // can not handle this type of file, perhaps a movie? if (cur == extmap.end()) return WriteResult::FILE_NOT_HANDLED; std::ofstream os(fileName.c_str(), std::ios::binary | std::ios::trunc | std::ios::out); if(os.good()) { QuicktimeImportExport exporter; exporter.writeToStream(os, const_cast(&img), fileName); if (exporter.success()) return WriteResult::FILE_SAVED; } return WriteResult::ERROR_IN_WRITING_FILE; } virtual WriteResult writeImage (const osg::Image& img, std::ostream& os, const Options* options=NULL) const { std::string filename = "file.jpg"; // use jpeg if not otherwise specified if (options) { std::istringstream iss(options->getOptionString()); std::string opt; while (iss >> opt) { int index = opt.find( "=" ); if( opt.substr( 0, index ) == "filename" || opt.substr( 0, index ) == "FILENAME" ) { filename = opt.substr( index+1 ); } } } QuicktimeImportExport exporter; exporter.writeToStream(os, const_cast(&img), filename); if (exporter.success()) return WriteResult::FILE_SAVED; return WriteResult::ERROR_IN_WRITING_FILE; } mutable QuicktimeExitObserver _qtExitObserver; }; // now register with Registry to instantiate the above // reader/writer. REGISTER_OSGPLUGIN(quicktime, ReaderWriterQT)