I removed QTtexture.h/.cpp and added QTImportExport.h/.cpp. I updated the CMake-files, I hope they are alright. I used the submitted code in my own apps since two months or so and it seems pretty stable, but as always the migration to the osg-quicktime plugin may have introduced new bugs, so perfect for developer release :)"
382 lines
14 KiB
C++
382 lines
14 KiB
C++
#include "osg/Image"
|
|
#include "osg/Notify"
|
|
|
|
#include <osg/Geode>
|
|
|
|
#include <osg/observer_ptr>
|
|
|
|
#include "osg/GL"
|
|
|
|
#include "osgDB/FileNameUtils"
|
|
#include "osgDB/Registry"
|
|
#include "osgDB/FileUtils"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sstream>
|
|
|
|
|
|
#ifndef __APPLE__
|
|
#include "Components.h"
|
|
#include "QuickTimeComponents.h"
|
|
#else
|
|
#include <QuickTime/QuickTime.h>
|
|
#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<OSG_SGDeviceList> 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<osg::Image> 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<osg::Image> 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<std::string, OSType> extmap;
|
|
|
|
extmap.insert(std::pair<std::string, OSType>("jpg", kQTFileTypeJPEG));
|
|
extmap.insert(std::pair<std::string, OSType>("jpeg", kQTFileTypeJPEG));
|
|
extmap.insert(std::pair<std::string, OSType>("bmp", kQTFileTypeBMP));
|
|
extmap.insert(std::pair<std::string, OSType>("tif", kQTFileTypeTIFF));
|
|
extmap.insert(std::pair<std::string, OSType>("tiff", kQTFileTypeTIFF));
|
|
extmap.insert(std::pair<std::string, OSType>("png", kQTFileTypePNG));
|
|
extmap.insert(std::pair<std::string, OSType>("gif", kQTFileTypeGIF));
|
|
extmap.insert(std::pair<std::string, OSType>("psd", kQTFileTypePhotoShop));
|
|
extmap.insert(std::pair<std::string, OSType>("sgi", kQTFileTypeSGIImage));
|
|
extmap.insert(std::pair<std::string, OSType>("rgb", kQTFileTypeSGIImage));
|
|
extmap.insert(std::pair<std::string, OSType>("rgba", kQTFileTypeSGIImage));
|
|
|
|
std::map<std::string, OSType>::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<osg::Image*>(&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<osg::Image*>(&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)
|