Files
OpenSceneGraph/src/osgPlugins/quicktime/ReaderWriterQT.cpp
Robert Osfield a1f1f5114c From Stephan Huber, "attached you'll find a refactored and improved quicktime-plugin. I moved all code related to the image-loading, swizzling etc into an extra class. The quicktime plugin supports now reading and writing images from/to streams and the code is less cluttered than previous versions.
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 :)"
2008-04-17 15:30:35 +00:00

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)