From 875668b84d6ca9e1d16453891eea4fa5b8f8697f Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 5 Dec 2006 16:29:11 +0000 Subject: [PATCH] From Stephan Huber and Riccardo Corsi, port of QuickTime plugin to Windows --- src/osgPlugins/quicktime/MovieData.cpp | 67 +- src/osgPlugins/quicktime/MovieData.h | 71 +- src/osgPlugins/quicktime/QTUtils.cpp | 92 ++- src/osgPlugins/quicktime/QTUtils.h | 56 +- src/osgPlugins/quicktime/QTtexture.cpp | 36 +- src/osgPlugins/quicktime/QTtexture.h | 6 +- .../quicktime/QuicktimeImageStream.cpp | 343 ++++---- .../quicktime/QuicktimeImageStream.h | 180 +++-- src/osgPlugins/quicktime/ReaderWriterQT.cpp | 732 +++++++++--------- 9 files changed, 941 insertions(+), 642 deletions(-) diff --git a/src/osgPlugins/quicktime/MovieData.cpp b/src/osgPlugins/quicktime/MovieData.cpp index 5febe1253..0158b36cc 100644 --- a/src/osgPlugins/quicktime/MovieData.cpp +++ b/src/osgPlugins/quicktime/MovieData.cpp @@ -6,21 +6,17 @@ * Copyright (c) 2004 __MyCompanyName__. All rights reserved. * */ -#include -#include +#include -#include +#include #include "MovieData.h" #include "QTUtils.h" -using namespace osgQuicktime; - -namespace osg { -MovieData::MovieData() : _movie(NULL), _gw(NULL), _fError(false) +MovieData::MovieData() : _pointer(NULL), _movie(NULL), _gw(NULL), _fError(false) { } @@ -28,6 +24,7 @@ MovieData::MovieData() : _movie(NULL), _gw(NULL), _fError(false) MovieData::~MovieData() { + if (_pointer) free(_pointer); if (_gw) DisposeGWorld(_gw); if (_movie) DisposeMovie(_movie); } @@ -35,10 +32,13 @@ MovieData::~MovieData() -void MovieData::load(osg::Image* image, std::string filename, float startTime) +void MovieData::load(osg::Image* image, std::string afilename, float startTime) { Rect bounds; - + std::string filename = afilename; + if (!osgDB::isFileNameNativeStyle(filename)) + filename = osgDB::convertFileNameToNativeStyle(filename); + osg::notify(osg::INFO) << "MovieData :: opening movie '" << filename << "'" << std::endl; OSStatus err = MakeMovieFromPath(filename.c_str(),&_movie); @@ -75,6 +75,9 @@ void MovieData::load(osg::Image* image, std::string filename, float startTime) UpdateMovie(_movie); SetMovieRate(_movie,0); + SetMovieActive(_movie, true); + UpdateMovie(_movie); + MoviesTask(_movie,0); } } @@ -88,30 +91,30 @@ void MovieData::_initImage(osg::Image* image) { void* buffer; - char* pointer; + _textureWidth = ((_movieWidth + 7) >> 3) << 3; _textureHeight = _movieHeight; // some magic alignment... - pointer = (char*)malloc(4 * _textureWidth * _textureHeight + 32); + _pointer = (char*)malloc(4 * _textureWidth * _textureHeight + 32); - if (pointer == NULL) { + if (_pointer == NULL) { osg::notify(osg::FATAL) << "MovieData: " << "Can't allocate texture buffer" << std::endl; _fError= true; } - buffer = (void*)(((unsigned long)(pointer + 31) >> 5) << 5); + buffer = (void*)(((unsigned long)(_pointer + 31) >> 5) << 5); GLenum internalFormat = (getCpuByteOrder()==osg::BigEndian)? GL_UNSIGNED_INT_8_8_8_8_REV : GL_UNSIGNED_INT_8_8_8_8; - image->setImage(_textureWidth,_textureHeight,0, + image->setImage(_textureWidth,_textureHeight,1, (GLint) GL_RGBA8, (GLenum)GL_BGRA_EXT, - internalFormat, - (unsigned char*) buffer,osg::Image::USE_MALLOC_FREE,4); + internalformat, + (unsigned char*) buffer,osg::Image::NO_DELETE,4); } @@ -162,6 +165,36 @@ void MovieData::_initGWorldStuff(osg::Image * image) { } +void MovieData::setMovieTime(float atime) { + float time = (atime > getMovieDuration()) ? getMovieDuration() : atime; + + TimeValue t = (TimeValue) (time * _timescale); + SetMovieTimeValue(_movie,t); + _checkMovieError("setMovieTime failed"); + UpdateMovie(_movie); + MoviesTask(_movie,0); + + +} + +void MovieData::setMovieRate(float rate) { + // osg::notify(osg::ALWAYS) << "new movierate: " << rate << " current: " << getMovieRate() << std::endl; + _movieRate = rate; + if (rate != 0) { + PrerollMovie(_movie, GetMovieTime(_movie,NULL), X2Fix(rate)); + _checkMovieError("PrerollMovie failed"); + } + + SetMovieRate(_movie, X2Fix(rate)); + _checkMovieError("setMovieRate failed"); + MoviesTask(_movie, 0); + _checkMovieError("MoviesTask failed"); + + UpdateMovie(_movie); + _checkMovieError("UpdateMovie failed"); + +} + + -} // namespace diff --git a/src/osgPlugins/quicktime/MovieData.h b/src/osgPlugins/quicktime/MovieData.h index 0b0001789..037a8ab7b 100644 --- a/src/osgPlugins/quicktime/MovieData.h +++ b/src/osgPlugins/quicktime/MovieData.h @@ -13,35 +13,90 @@ #include #include #include -#include -namespace osg { - +#include "QTUtils.h" + + + /** + * the class MovieData encapsulates all quicktime-related stuff, so it doesn't polute the namespaces + * it handles all calls to quicktime etc... It is mainly used by the QuicktimeImageStream, it is + * rarely needed in other contexts + */ class MovieData { public: + /** default constructor */ MovieData(); + + /** default destructor */ ~MovieData(); + /** + * loads a movie, start it playing at startTime, use Image for the storage + * @param image the image, to use as storage + * @param fileName the movie to open + * @param startTime the starttime to begin with + */ void load(osg::Image* image, std::string fileName, float startTime = 0.0f); - float getMovieDuration() { return GetMovieDuration(_movie)/(float)_timescale;} - float getMovieTime() {return GetMovieTime(_movie,NULL)/(float)_timescale; } - - Movie &getMovie() { return _movie; } + /** @return the duration for this movie in seconds */ + inline float getMovieDuration() { return GetMovieDuration(_movie)/(float)_timescale;} + + /** @return the current position for this movie in seconds */ + inline float getMovieTime() {return GetMovieTime(_movie,NULL)/(float)_timescale; } + + /** stes the movietime */ + void setMovieTime(float atime); + + /** @return the Movie-handle, to use it with other quicktime-calls */ + inline Movie &getMovie() { return _movie; } + + /** @return the current movieRate */ + inline float getMovieRate() { return Fix2X(GetMovieRate(_movie)); } + /** @return returns the cached movierate, may differ to the real movierate */ + inline float getCachedMovieRate() { return _movieRate; } + + /** sets the MovieRate for this movie */ + void setMovieRate(float rate); + + /** sets the volume for the soundtrack of this movie */ + void setVolume(float volume) { SetMovieVolume(_movie,(short)(volume*255));} + float getVolume() { return GetMovieVolume(_movie) / 255.0f; } + + void setAudioBalance(float f) { + Float32 balance = f; + SetMovieAudioBalance(_movie, balance, 0); + } + + float getAudioBalance() { + Float32 balance; + float f; + GetMovieAudioBalance(_movie, &balance, 0); + f = balance; + return f; + } + protected: + char* _pointer; Movie _movie; GWorldPtr _gw; unsigned int _movieWidth, _movieHeight, _textureWidth, _textureHeight; float _timescale; bool _fError; + float _movieRate; + /** inits the image for storage */ void _initImage(osg::Image* image); + + /** inits the gWorld, where the movie gets drawn into */ void _initGWorldStuff(osg::Image * image); + + /** inits the texture */ void _initTexture(); + /** checks for an movie-error */ inline void _checkMovieError(std::string msg) { if (GetMoviesError()) { _fError = true; @@ -52,6 +107,6 @@ namespace osg { }; -} // namespace + #endif diff --git a/src/osgPlugins/quicktime/QTUtils.cpp b/src/osgPlugins/quicktime/QTUtils.cpp index 6a9496270..3a14c14c7 100644 --- a/src/osgPlugins/quicktime/QTUtils.cpp +++ b/src/osgPlugins/quicktime/QTUtils.cpp @@ -6,33 +6,75 @@ * Copyright (c) 2002 digital mind. All rights reserved. * */ - +#include +#include #include -#include -#include #include "QTUtils.h" +#include "osgDB/Registry" + using namespace std; -namespace osgQuicktime { + + + class QuicktimeInitializer : public osg::Referenced { + public: + QuicktimeInitializer() :osg::Referenced() { + static bool s_fQuicktimeInited = 0; + if (!s_fQuicktimeInited) { + #ifndef __APPLE__ + InitializeQTML(0); + #endif + OSErr err = EnterMovies(); + if (err!=0) + osg::notify(osg::FATAL) << "Error while initializing quicktime: " << err << endl; + else + osg::notify(osg::DEBUG_INFO) << "Quicktime initialized successfully" << endl; + registerQTReader(); + s_fQuicktimeInited = true; + } + } + + ~QuicktimeInitializer() { + #ifndef __APPLE__ + ExitMovies(); + #endif + //osg::notify(osg::DEBUG_INFO) << "Quicktime deinitialized successfully" << endl; + } + + protected: + void registerQTReader() { + osgDB::Registry* r = osgDB::Registry::instance(); + r->addFileExtensionAlias("jpg", "qt"); + r->addFileExtensionAlias("jpe", "qt"); + r->addFileExtensionAlias("jpeg", "qt"); + r->addFileExtensionAlias("tif", "qt"); + r->addFileExtensionAlias("tiff", "qt"); + r->addFileExtensionAlias("gif", "qt"); + r->addFileExtensionAlias("png", "qt"); + r->addFileExtensionAlias("psd", "qt"); + r->addFileExtensionAlias("tga", "qt"); + r->addFileExtensionAlias("mov", "qt"); + r->addFileExtensionAlias("avi", "qt"); + r->addFileExtensionAlias("mpg", "qt"); + r->addFileExtensionAlias("mpv", "qt"); + r->addFileExtensionAlias("dv", "qt"); + r->addFileExtensionAlias("mp4", "qt"); + r->addFileExtensionAlias("m4v", "qt"); + } + + }; + + void initQuicktime(bool erase) { + + static osg::ref_ptr s_qt_init = new QuicktimeInitializer(); + if (erase) + s_qt_init = NULL; + } - void initQuicktime() { - - static bool s_fQuicktimeInited = 0; - - OSErr err; - if (!s_fQuicktimeInited) { - err = EnterMovies(); - if (err!=0) - osg::notify(osg::FATAL) << "Error while initializing quicktime: " << err << endl; - else - osg::notify(osg::DEBUG_INFO) << "Quicktime initialized successfully" << endl; - s_fQuicktimeInited = true; - } - else - osg::notify(osg::DEBUG_INFO) << "Quicktime already initialized ..." << endl; - + void exitQuicktime() { + initQuicktime(true); } @@ -43,6 +85,7 @@ namespace osgQuicktime { // wandelt einen Posix-Pfad in ein FSSpec um. // --------------------------------------------------------------------------- OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec) { +#ifdef __APPLE__ OSStatus err; FSRef fsref; Boolean isdir; @@ -58,17 +101,23 @@ namespace osgQuicktime { // Ditto err = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, spec, NULL); return err; +#else + return -1; +#endif } // --------------------------------------------------------------------------- // MakeMovieFromPath - // erzeugt movie-objekt aus Pfad // --------------------------------------------------------------------------- OSStatus MakeMovieFromPath(const char* path, Movie* movie) { OSStatus err; FSSpec spec; short resref; +#ifdef __APPLE__ MakeFSSpecFromPath(path, &spec); +#else + err = NativePathNameToFSSpec((char*)path, &spec, 0 /* flags */); +#endif err = OpenMovieFile(&spec, &resref, fsRdPerm); if (err!=0) return err; err = NewMovieFromFile(movie, resref, NULL, NULL, 0, NULL); @@ -78,4 +127,3 @@ namespace osgQuicktime { -} // namespace diff --git a/src/osgPlugins/quicktime/QTUtils.h b/src/osgPlugins/quicktime/QTUtils.h index ba6113f60..4df4cae75 100644 --- a/src/osgPlugins/quicktime/QTUtils.h +++ b/src/osgPlugins/quicktime/QTUtils.h @@ -7,14 +7,56 @@ * */ -#include -#include +#ifndef QTUTILS_HEADER_ +#define QTUTILS_HEADER_ -namespace osgQuicktime { +#ifdef __APPLE__ + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include - void initQuicktime(); + extern "C" { + /** legacy function for Windows */ + inline void GetPortBounds(GWorldPtr gw, Rect* rect) { + (*rect) = (gw->portRect); + } + /** legacy function for Windows */ + inline PixMapHandle GetPortPixMap (CGrafPtr port) { + return port->portPixMap; + } + + } - OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec); - OSStatus MakeMovieFromPath(const char* path, Movie* movie); +#define SetRect MacSetRect +#define OffsetRect MacOffsetRect -} +#endif + + + /** + * inits Quicktime, if erase = true, the Quicktime-stuff gets cleaned, call it before your app exits + * @param erase true, if you want to cleanup quicktime-related stuff + */ + void initQuicktime(bool erase = false); + + /** cleans up all quicktime-related stuff */ + void exitQuicktime(); + + /** constructs an FSSpec out of an path */ + OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec); + + /** opens a movie from a path */ + OSStatus MakeMovieFromPath(const char* path, Movie* movie); + + +#endif diff --git a/src/osgPlugins/quicktime/QTtexture.cpp b/src/osgPlugins/quicktime/QTtexture.cpp index 706ef6778..2774ca515 100644 --- a/src/osgPlugins/quicktime/QTtexture.cpp +++ b/src/osgPlugins/quicktime/QTtexture.cpp @@ -50,18 +50,23 @@ PORTIONS OF THIS CODE ARE COPYRIGHT APPLE COMPUTER - * */ - +#ifdef __APPLE__ #include - #include // for image loading and decompression #include // for file type support +#else +#include // for image loading and decompression +#include // for file type support +#endif -#include // for OpenGL API -#include // for OpenGL API -#include // for OpenGL extension support - +#include // for OpenGL API +#include "QTUtils.h" #include "QTtexture.h" +#include +#include +#include + // ================================== enum // how to scale image to power of two on read if scaling @@ -346,15 +351,10 @@ FSSpec *darwinPathToFSSpec (char *fname ) { FSSpec *fs; OSStatus result; +#if defined( __APPLE__ ) FSRef ref; - - /* convert the POSIX path to an FSRef */ -#if defined( __APPLE__ ) && ( __GNUC__ > 3 ) - result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory -#else - result = FSPathMakeRef(fname, &ref, false); // fname is not a directory -#endif + result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory if (result!=0) return NULL; /* and then convert the FSRef to an FSSpec */ @@ -365,6 +365,16 @@ FSSpec *darwinPathToFSSpec (char *fname ) { // failed: free(fs); return NULL; +#else + // windows implementation to get a fsspec + fs = (FSSpec *) malloc(sizeof(FSSpec)); + result = NativePathNameToFSSpec(fname, fs, 0 /* flags */); + + if (0 == result) + return fs; + free(fs); + return NULL; +#endif } diff --git a/src/osgPlugins/quicktime/QTtexture.h b/src/osgPlugins/quicktime/QTtexture.h index 77f7f13a6..ad3481c9a 100644 --- a/src/osgPlugins/quicktime/QTtexture.h +++ b/src/osgPlugins/quicktime/QTtexture.h @@ -6,9 +6,9 @@ extern "C" { #endif unsigned char* LoadBufferFromDarwinPath ( const char *fname, long *origWidth, - long *origHeight, long *origDepth, - long *buffWidth, long *buffHeight, long *buffDepth); - + long *origHeight, long *origDepth, + long *buffWidth, long *buffHeight, long *buffDepth); + char* QTfailureMessage(void); FSSpec *darwinPathToFSSpec (char *fname ); diff --git a/src/osgPlugins/quicktime/QuicktimeImageStream.cpp b/src/osgPlugins/quicktime/QuicktimeImageStream.cpp index 21567398c..f46a9e5a4 100644 --- a/src/osgPlugins/quicktime/QuicktimeImageStream.cpp +++ b/src/osgPlugins/quicktime/QuicktimeImageStream.cpp @@ -1,27 +1,27 @@ // -*-c++-*- /* - * Copyright (C) 2004 Stephan Huber http://digitalmind.de - * - * - * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for - * real-time rendering of large 3D photo-realistic models. - * The OSG homepage is http://www.openscenegraph.org/ - * - * This software is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +* Copyright (C) 2004 Stephan Huber http://digitalmind.de +* +* +* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for +* real-time rendering of large 3D photo-realistic models. +* The OSG homepage is http://www.openscenegraph.org/ +* +* This software is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This software is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ #include "QuicktimeImageStream.h" #include @@ -34,201 +34,254 @@ #include "MovieData.h" -using namespace osg; #define IDLE_TIMEOUT 150000L #define ERR_MSG(no,msg) osg::notify(osg::WARN) << "QT-ImageStream: " << msg << " failed with error " << no << std::endl; static OpenThreads::Mutex* s_qtMutex = new OpenThreads::Mutex; +int QuicktimeImageStream::_qtInstanceCount = 0; // Constructor: setup and start thread QuicktimeImageStream::QuicktimeImageStream(std::string fileName) : ImageStream() { + // ricky + if(_qtInstanceCount == 0) + { + OpenThreads::ScopedLock lock(*s_qtMutex); + osg::notify(osg::NOTICE) << "quicktime Init" << std::endl; + initQuicktime(); + } + ++ _qtInstanceCount; + // end ricky - { - OpenThreads::ScopedLock lock(*s_qtMutex); - osgQuicktime::initQuicktime(); - } - - _len = 0; - - _data = new MovieData(); - for (int i = 0; i < NUM_CMD_INDEX; i++) - _cmd[i] = THREAD_IDLE; - _wrIndex = _rdIndex = 0; + _len = 0; + _data = new MovieData(); - load(fileName); + for (int i = 0; i < NUM_CMD_INDEX; i++) + _cmd[i] = THREAD_IDLE; + _wrIndex = _rdIndex = 0; - if (!fileName.empty()) - setFileName(fileName); + load(fileName); + + if (!fileName.empty()) + setFileName(fileName); + + // ricky + _status = ImageStream::PAUSED; } // Deconstructor: stop and terminate thread QuicktimeImageStream::~QuicktimeImageStream() { + if( isRunning() ) + { + quit(true); + } - if( isRunning() ) - { - quit(true); - } + // ricky + -- _qtInstanceCount; + if(_qtInstanceCount == 0) + { + OpenThreads::ScopedLock lock(*s_qtMutex); + osg::notify(osg::NOTICE) << "quicktime Exit" << std::endl; + exitQuicktime(); + } + // end ricky - // clean up quicktime movies. - { - OpenThreads::ScopedLock lock(*s_qtMutex); - delete _data; - } + // clean up quicktime movies. + { + OpenThreads::ScopedLock lock(*s_qtMutex); + delete _data; + } } // Set command -void QuicktimeImageStream::setCmd(ThreadCommand cmd) +void QuicktimeImageStream::setCmd(ThreadCommand cmd, float rate) { - OpenThreads::ScopedLock lock(_mutex); + OpenThreads::ScopedLock lock(_mutex); - _cmd[_wrIndex] = cmd; - _wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX; + _cmd[_wrIndex] = cmd; + _rates[_wrIndex] = rate; + _wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX; } // Get command QuicktimeImageStream::ThreadCommand QuicktimeImageStream::getCmd() { - ThreadCommand cmd = THREAD_IDLE; + ThreadCommand cmd = THREAD_IDLE; - OpenThreads::ScopedLock lock(_mutex); + OpenThreads::ScopedLock lock(_mutex); - if (_rdIndex != _wrIndex) { - cmd = _cmd[_rdIndex]; - _rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX; - } + if (_rdIndex != _wrIndex) { + cmd = _cmd[_rdIndex]; + _currentRate = _rates[_rdIndex]; + _rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX; + } - return cmd; + return cmd; } void QuicktimeImageStream::load(std::string fileName) { - osg::notify(osg::DEBUG_INFO) << "QT-ImageStream: loading quicktime movie from " << fileName << std::endl; - - OpenThreads::ScopedLock lock(*s_qtMutex); + osg::notify(osg::DEBUG_INFO) << "QT-ImageStream: loading quicktime movie from " << fileName << std::endl; + + OpenThreads::ScopedLock lock(*s_qtMutex); + + _data->load(this, fileName); + + _len = _data->getMovieDuration(); + _current = 0; + - _data->load(this, fileName); - - _len = _data->getMovieDuration(); - - } void QuicktimeImageStream::quit(bool wiatForThreadToExit) { - osg::notify(osg::DEBUG_INFO)<<"Sending quit"<getMovieTime() << " state " << getCmd() << std::endl; - // Handle commands - ThreadCommand cmd = getCmd(); + bool playing = false; + bool done = false; - float currentTime=0.0f; - - { - OpenThreads::ScopedLock lock(*s_qtMutex); + /* + OSErr err; + err = EnterMoviesOnThread(0); + ERR_MSG(err,"EnterMoviesOnThread"); + err = AttachMovieToCurrentThread(_data->getMovie()); + */ + while (!done) { - if (cmd != THREAD_IDLE) { - switch (cmd) { + + ThreadCommand cmd = getCmd(); + osg::notify(osg::DEBUG_INFO) << "movietime: " << _data->getMovieTime() << " rate: " << _data->getMovieRate() << " state " << cmd << " playing: " << playing << " done " << done << " " << _wrIndex << "/" << _rdIndex << std::endl; + // Handle commands + { + OpenThreads::ScopedLock lock(*s_qtMutex); + + if (cmd != THREAD_IDLE) { + osg::notify(osg::DEBUG_INFO) << "new cmd: " << cmd << std::endl; + switch (cmd) { case THREAD_START: // Start or continue stream - StartMovie(_data->getMovie()); - playing = true; - break; + _data->setMovieRate(1.0f); + + playing = true; + break; case THREAD_STOP: - SetMovieRate(_data->getMovie(),0); - osg::notify(INFO) << "QT-ImageStream: stop at "<< std::endl; - playing = false; - break; + _data->setMovieRate(0); + osg::notify(osg::INFO) << "QT-ImageStream: stop at "<< std::endl; + playing = false; + break; case THREAD_REWIND: - SetMovieRate(_data->getMovie(),0); - GoToBeginningOfMovie(_data->getMovie()); - break; + SetMovieRate(_data->getMovie(),0); + GoToBeginningOfMovie(_data->getMovie()); + break; + + case THREAD_FORWARD: + SetMovieRate(_data->getMovie(),0); + GoToEndOfMovie(_data->getMovie()); + break; + + case THREAD_SEEK: + _data->setMovieTime(_currentRate); + playing = true; + break; + + case THREAD_SETRATE: + _data->setMovieRate(_currentRate); + playing = (_currentRate != 0.0f); + break; case THREAD_CLOSE: - SetMovieRate(_data->getMovie(),0); - break; + _data->setMovieRate(0); + break; case THREAD_QUIT: // TODO - SetMovieRate(_data->getMovie(),0); - osg::notify(INFO) << "QT-ImageStream: quit" << std::endl; - //playing = false; - done = true; - break; + _data->setMovieRate(0); + osg::notify(osg::INFO) << "QT-ImageStream: quit" << std::endl; + //playing = false; + done = true; + break; default: - osg::notify(osg::WARN) << "QT-ImageStream: Unknown command " << cmd << std::endl; - break; - } + osg::notify(osg::WARN) << "QT-ImageStream: Unknown command " << cmd << std::endl; + break; } + } - MoviesTask(_data->getMovie(),0); + MoviesTask(_data->getMovie(),0); + _current = _data->getMovieTime(); + } - currentTime = _data->getMovieTime(); - } - - if (_lastUpdate!= currentTime) - { + if (_lastUpdate!= _current) + { + // force the texture to update the image + dirty(); + // update internal time and take care of looping + _lastUpdate = _current; - dirty(); - _lastUpdate = currentTime; - - if (currentTime>=_data->getMovieDuration()) - { - if (getLoopingMode()==LOOPING) - { - rewind(); - play(); - } - else - { - pause(); - } + if (getLoopingMode() == LOOPING) + { + // loopen wir rueckwaerts? + if ((_current <= 0.0f) && (_data->getCachedMovieRate() < 0.0f)) { + forward(); + setMovieRate(_data->getCachedMovieRate()); + } + // loppen wir vorwŠrts? + else if ((_current >= _len) && (_data->getCachedMovieRate() > 0.0f)) + { + rewind(); + setMovieRate(_data->getCachedMovieRate()); } + } + else + { // NO LOOPING + //ricky + if(_current >= _len) + { + pause(); + rewind(); + } + // orig + //pause(); - } - - if (playing) - { - // TODO - } - else if (!done) - { - OpenThreads::Thread::microSleep(IDLE_TIMEOUT); - } - } - // err = ExitMoviesOnThread(); - //ERR_MSG(err,"ExitMoviesOnThread"); + } + } + + if (playing) + { + OpenThreads::Thread::microSleep(16000); + } + else if (!done) + { + OpenThreads::Thread::microSleep(IDLE_TIMEOUT); + } + } + /* + err = DetachMovieFromCurrentThread(_data->getMovie()); + err = ExitMoviesOnThread(); + ERR_MSG(err,"ExitMoviesOnThread"); + */ } diff --git a/src/osgPlugins/quicktime/QuicktimeImageStream.h b/src/osgPlugins/quicktime/QuicktimeImageStream.h index 20227548a..7c40937c6 100644 --- a/src/osgPlugins/quicktime/QuicktimeImageStream.h +++ b/src/osgPlugins/quicktime/QuicktimeImageStream.h @@ -32,78 +32,118 @@ #define NUM_CMD_INDEX 20 -namespace osg { - - class MovieData; - - /** - * Quicktime Image Stream class. - */ - class QuicktimeImageStream : public osg::ImageStream, public OpenThreads::Thread - { - public: - QuicktimeImageStream(std::string fileName = ""); - - virtual Object* clone() const { return new QuicktimeImageStream; } - virtual bool isSameKindAs(const Object* obj) const { - return dynamic_cast(obj) != NULL; - } - virtual const char* className() const { return "QuicktimeImageStream"; } - - /// Start or continue stream. - virtual void play() - { - if (!isRunning()) start(); - - setCmd(THREAD_START); - } - - /// Pause stream at current position. - virtual void pause() { setCmd(THREAD_STOP); } - - /// Rewind stream to beginning. - virtual void rewind() { setCmd(THREAD_REWIND); } - - virtual void quit(bool wiatForThreadToExit); - - /// Get total length in seconds. - inline float getLength() const { return _len; } - - void load(std::string fileName); - - virtual void run(); - - protected: - virtual ~QuicktimeImageStream(); - - private: - float _lastUpdate; - float _len; - - MovieData* _data; - - enum ThreadCommand { - THREAD_IDLE = 0, - THREAD_START, - THREAD_STOP, - THREAD_REWIND, - THREAD_CLOSE, - THREAD_QUIT - }; - ThreadCommand _cmd[NUM_CMD_INDEX]; - int _wrIndex, _rdIndex; - - OpenThreads::Mutex _mutex; - - /// Set command. - void setCmd(ThreadCommand cmd); - - /// Get command. - ThreadCommand getCmd(); - }; +class MovieData; + +/** +* Quicktime Image Stream class. streams a quicktime movie into an image +*/ +class QuicktimeImageStream : public osg::ImageStream, public OpenThreads::Thread +{ +public: + /** Constructor + * @param fileName movie to open */ + QuicktimeImageStream(std::string fileName = ""); + virtual Object* clone() const { return new QuicktimeImageStream; } + virtual bool isSameKindAs(const Object* obj) const { + return dynamic_cast(obj) != NULL; + } + + virtual const char* className() const { return "QuicktimeImageStream"; } + + /// Start or continue stream. + virtual void play() + { + if (!isRunning()) start(); + + setCmd(THREAD_START); + + // ricky + _status = ImageStream::PLAYING; + } + + /// sets the movierate of this movie + void setMovieRate(float rate) { + if (!isRunning()) start(); + setCmd(THREAD_SETRATE,rate); + } + + /// Pause stream at current position. + virtual void pause() + { + setCmd(THREAD_STOP); + // ricky + _status = ImageStream::PAUSED; + } + + /// Rewind stream to beginning. + virtual void rewind() { setCmd(THREAD_REWIND); } + + /// forward stream to the end + virtual void forward() { setCmd(THREAD_FORWARD); } + + /// stop playing + virtual void quit(bool wiatForThreadToExit); + + /// Get total length in seconds. + inline float getLength() const { return _len; } + + /// jumps to a specific position + void jumpTo(float pos) { + setCmd(THREAD_SEEK, pos); + } + + /// returns the current playing position + inline float getCurrentTime() const { return _current; } + + /// @return the current moviedata-object + MovieData* getMovieData() { return _data; } + + /// loads a movie from fileName + void load(std::string fileName); + + /// starts the thread + virtual void run(); + +protected: + /// destructor + virtual ~QuicktimeImageStream(); + +private: + float _lastUpdate; + float _len; + float _current; + float _currentRate; + + MovieData* _data; + + enum ThreadCommand { + THREAD_IDLE = 0, + THREAD_START, + THREAD_STOP, + THREAD_REWIND, + THREAD_FORWARD, + THREAD_SEEK, + THREAD_SETRATE, + THREAD_CLOSE, + THREAD_QUIT + }; + ThreadCommand _cmd[NUM_CMD_INDEX]; + float _rates[NUM_CMD_INDEX]; + int _wrIndex, _rdIndex; + + OpenThreads::Mutex _mutex; + + /// Set command. + void setCmd(ThreadCommand cmd, float rate = 0.0f); + + /// Get command. + ThreadCommand getCmd(); + + // ricky + static int _qtInstanceCount; +}; -} // namespace #endif diff --git a/src/osgPlugins/quicktime/ReaderWriterQT.cpp b/src/osgPlugins/quicktime/ReaderWriterQT.cpp index c6e922860..536f3e01c 100644 --- a/src/osgPlugins/quicktime/ReaderWriterQT.cpp +++ b/src/osgPlugins/quicktime/ReaderWriterQT.cpp @@ -16,8 +16,7 @@ #ifndef SEEK_SET # define SEEK_SET 0 #endif -#include -#include +#include "QTUtils.h" #include "QTtexture.h" #include "QuicktimeImageStream.h" @@ -28,375 +27,394 @@ using namespace osg; class ReaderWriterQT : public osgDB::ReaderWriter { - public: - 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"); - } - - 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 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") || - acceptsMovieExtension(extension); - } +public: + virtual const char* className() const { return "Default Quicktime Image Reader/Writer"; } - virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options*) const - { - std::string ext = osgDB::getLowerCaseFileExtension(file); - if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + 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,"swf"); + } - std::string fileName = osgDB::findDataFile( file ); - if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; - - // if the file is a movie file then load as an ImageStream. - if (acceptsMovieExtension(ext)) + 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 + /*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") || + */ + acceptsMovieExtension(extension); + } + + virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options*) const + { + + // ricky + // If using only the QuicktimeImageStream class for the movies, you can comment + // out this line, as the init function is called inside the QTImageStream + // ctor. + // It is left here for compatibility with the original plugin, to handle + // regular 2D images as well. + initQuicktime(); + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile( file ); + if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; + + // 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(); + return moov; + } + + long origWidth, origHeight,buffWidth,buffHeight,buffDepth,origDepth; + + // NOTE - implememntation means that this will always return 32 bits, so it is hard to work out if + // an image was monochrome. So it will waste texture memory unless it gets a monochrome hint. + + unsigned char *pixels = LoadBufferFromDarwinPath ( fileName.c_str(), &origWidth,&origHeight,&origDepth, + &buffWidth,&buffHeight, + &buffDepth); + + // IMPORTANT - + // origDepth in BYTES, buffDepth in BITS + if (pixels == 0) + { + osg::notify(osg::WARN) << "LoadBufferFromDarwinPath failed " << fileName.c_str() << QTfailureMessage() << std::endl; + return 0; + } + + unsigned int pixelFormat; + + switch(origDepth) + { + case 1 : + pixelFormat = GL_RGB; + break; + case 2 : + pixelFormat = GL_LUMINANCE_ALPHA; + break; + case 3 : + pixelFormat = GL_RGB; + break; + case 4 : + pixelFormat = GL_RGBA; + break; + default : + osg::notify(osg::WARN) << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl; + pixelFormat = (GLenum)-1; + return 0; + break; + } + + { + unsigned char *srcp=pixels, *dstp=pixels; + + int i, j; + + // swizzle entire image in-place + unsigned char r, g, b, a; + for (i=0; iplay(); - return moov; - } - long origWidth, origHeight,buffWidth,buffHeight,buffDepth,origDepth; - - // NOTE - implememntation means that this will always return 32 bits, so it is hard to work out if - // an image was monochrome. So it will waste texture memory unless it gets a monochrome hint. - - unsigned char *pixels = LoadBufferFromDarwinPath ( fileName.c_str(), &origWidth,&origHeight,&origDepth, - &buffWidth,&buffHeight, - &buffDepth); - - // IMPORTANT - - // origDepth in BYTES, buffDepth in BITS - - if (pixels == 0) { - osg::notify(osg::WARN) << "LoadBufferFromDarwinPath failed " << fileName.c_str() << QTfailureMessage() << std::endl; - return 0; - } - - unsigned int pixelFormat; - - switch(origDepth) { - case 1 : - pixelFormat = GL_RGB; - break; - case 2 : - pixelFormat = GL_LUMINANCE_ALPHA; - break; - case 3 : - pixelFormat = GL_RGB; - break; - case 4 : - pixelFormat = GL_RGBA; - break; - default : - osg::notify(osg::WARN) << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl; - pixelFormat = (GLenum)-1; - return 0; - break; - } - - { - unsigned char *srcp=pixels, *dstp=pixels; - - int i, j; - - // swizzle entire image in-place - unsigned char r, g, b, a; - for (i=0; isetFileName(fileName.c_str()); - image->setImage(buffWidth,buffHeight,1, - buffDepth >> 3, - pixelFormat, - GL_UNSIGNED_BYTE, - pixels, - osg::Image::USE_NEW_DELETE ); - - notify(INFO) << "image read ok "< 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("tga", kQTFileTypeTargaImage)); - 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; - - OSType desiredType = cur->second; - GraphicsExportComponent geComp = NULL; + case 1 : + for (j=0; jsetFileName(fileName.c_str()); + image->setImage(buffWidth,buffHeight,1, + buffDepth >> 3, + pixelFormat, + GL_UNSIGNED_BYTE, + pixels, + osg::Image::USE_NEW_DELETE ); + + notify(DEBUG_INFO) << "image read ok "< 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("tga", kQTFileTypeTargaImage)); + 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; + + OSType desiredType = cur->second; + GraphicsExportComponent geComp = NULL; + + OSErr err = OpenADefaultComponent(GraphicsExporterComponentType, desiredType, &geComp); + + if (err != noErr) { + osg::notify(osg::WARN) << "ReaderWriterQT: could not open Graphics epxorter for type " << ext << ", Err: " << err << std::endl; + return WriteResult::FILE_NOT_HANDLED; + } + + GWorldPtr gw = NULL; + + // we are converting the images back to 32bit, it seems, that quicktime can't handle others + + unsigned long desiredPixelFormat = k32ARGBPixelFormat; + + + + // we need to swizzle the colours again :) + unsigned int numBytes = img.computeNumComponents(img.getPixelFormat()); + + unsigned int buffWidth = img.s(); + unsigned int buffHeight = img.t(); + char * pixels = (char*) malloc(buffHeight * buffWidth * 4); + + + + const unsigned char *srcp = img.data(); + char *dstp=pixels; + unsigned int i, j; + for (i=0; i(fileName.c_str()) ); + if (fileSpec == NULL) { + osg::notify(osg::WARN) << "ReaderWriterQT: could not get FSSpec" << std::endl; + throw err; + } + + err = GraphicsExportSetInputGWorld(geComp, gw); + if (err != noErr) { + osg::notify(osg::WARN) << "ReaderWriterQT: could not set input gworld for type " << ext << ", Err: " << err << std::endl; + throw err; + } + + err = GraphicsExportSetOutputFile(geComp, fileSpec); + if (err != noErr) { + osg::notify(osg::WARN) << "ReaderWriterQT: could not set output file for type " << ext << ", Err: " << err << std::endl; + throw err; + } + + // Set the compression quality (needed for JPEG, not necessarily for other formats) + if (desiredType == kQTFileTypeJPEG) { + err = GraphicsExportSetCompressionQuality(geComp, codecLosslessQuality); if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not open Graphics epxorter for type " << ext << ", Err: " << err << std::endl; - return WriteResult::FILE_NOT_HANDLED; + osg::notify(osg::WARN) << "ReaderWriterQT: could not set compression for type " << ext << ", Err: " << err << std::endl; + throw err; } - - CGContextRef cg_context = NULL; - - // we are converting the images back to 32bit, it seems, that quicktime can't handle others -// unsigned long desiredPixelFormat = k32ARGBPixelFormat; - - - - // we need to swizzle the colours again :) - unsigned int numBytes = img.computeNumComponents(img.getPixelFormat()); + } - unsigned int buffWidth = img.s(); - unsigned int buffHeight = img.t(); - char * pixels = (char*) malloc(buffHeight * buffWidth * 4); - - - - const unsigned char *srcp = img.data(); - char *dstp=pixels; - unsigned int i, j; - for (i=0; i(fileName.c_str()) ); - if (fileSpec == NULL) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not get FSSpec" << std::endl; - throw err; - } - - err = GraphicsExportSetInputCGBitmapContext(geComp, cg_context); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set input gworld for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - err = GraphicsExportSetOutputFile(geComp, fileSpec); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set output file for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - // Set the compression quality (needed for JPEG, not necessarily for other formats) - if (desiredType == kQTFileTypeJPEG) { - err = GraphicsExportSetCompressionQuality(geComp, codecLosslessQuality); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set compression for type " << ext << ", Err: " << err << std::endl; - throw err; - } - } - - if(32 == numBytes) - { - err = GraphicsExportSetDepth( geComp, - k32ARGBPixelFormat ); // depth - } - // else k24RGBPixelFormat??? - - // do the export - err = GraphicsExportDoExport(geComp, NULL); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not save file for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - if (geComp != NULL) - CloseComponent(geComp); - - CGContextRelease(cg_context); - if (fileSpec != NULL ) free(fileSpec); - if (pixels) free(pixels); - - return WriteResult::FILE_SAVED; - } - - - catch (...) { - - if (geComp != NULL) CloseComponent(geComp); - if (cg_context != NULL) CGContextRelease(cg_context); - if (fileSpec != NULL ) free(fileSpec); - if (pixels) free(pixels); - - return WriteResult::ERROR_IN_WRITING_FILE; - } - - } - - };