From 8b47c9c85dfdf41271782cc8c7920b9e5a7f6466 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 9 Mar 2004 09:42:07 +0000 Subject: [PATCH] Preliminary work on general support of video textures. --- examples/osgmovie/MpegImageStream.cpp | 228 +++++++++++++++----------- examples/osgmovie/MpegImageStream.h | 11 +- examples/osgmovie/osgmovie.cpp | 6 +- include/osg/ImageStream | 76 +++++++++ src/osg/GNUmakefile | 1 + src/osg/ImageStream.cpp | 33 ++++ 6 files changed, 253 insertions(+), 102 deletions(-) create mode 100644 include/osg/ImageStream create mode 100644 src/osg/ImageStream.cpp diff --git a/examples/osgmovie/MpegImageStream.cpp b/examples/osgmovie/MpegImageStream.cpp index 960c236c9..e6ea470fb 100644 --- a/examples/osgmovie/MpegImageStream.cpp +++ b/examples/osgmovie/MpegImageStream.cpp @@ -27,6 +27,7 @@ #include "MpegImageStream.h" #include +#include #include #include @@ -42,20 +43,25 @@ using namespace osg; // Constructor: setup and start thread MpegImageStream::MpegImageStream(const char* fileName) : ImageStream() { - _useMMX = true; + _useMMX = false; _fps = 0.0f; _frames = 0; _len = 0; + _mpg = 0; for (int i = 0; i < NUM_CMD_INDEX; i++) _cmd[i] = THREAD_IDLE; _wrIndex = _rdIndex = 0; + load(fileName); + ::pthread_mutex_init(&_mutex, NULL); ::pthread_create(&_id, NULL, MpegImageStream::s_decode, this); if (fileName) setFileName(fileName); + + } @@ -66,6 +72,17 @@ MpegImageStream::~MpegImageStream() setCmd(THREAD_QUIT); ::pthread_join(_id, NULL); ::pthread_mutex_destroy(&_mutex); + + mpeg3_t* mpg = (mpeg3_t*)_mpg; + if (mpg) { + mpeg3_close(mpg); + mpg = NULL; + } + if (_rows) { + ::free(_rows); + _rows = NULL; + } + } @@ -101,16 +118,102 @@ void* MpegImageStream::s_decode(void* vp) return ((MpegImageStream*) vp)->decode(vp); } +void MpegImageStream::load(const char* fileName) +{ + mpeg3_t* mpg = mpeg3_open((char*) fileName); + if (!mpg) { + osg::notify(WARN) << "Unable to open " << fileName << std::endl; + return; + } + + if (!mpeg3_has_video(mpg)) { + osg::notify(WARN) << "No video streams in" << fileName << std::endl; + return; + } + if (mpeg3_has_audio(mpg)) { + osg::notify(NOTICE) << "Stream has audio" << std::endl; + } + + _mpg = (void*)mpg; + + mpeg3_set_cpus(mpg, 1); + mpeg3_set_mmx(mpg, _useMMX); + + int str = 0; //mpeg3_total_vstreams(mpg) - 1; + + _fps = mpeg3_frame_rate(mpg, str); + _frames = mpeg3_video_frames(mpg, str); + _len = (float) _frames / _fps; + + + int s = mpeg3_video_width(mpg, str); + int t = mpeg3_video_height(mpg, str); + + // Calculate texture size + // these are also calculated and stored within osg::Texture but + // too late (on the first apply) to be of any use... + int texWidth = 1; + for (; texWidth < s; texWidth <<= 1) + ; + int texHeight = 1; + for (; texHeight < t; texHeight <<= 1) + ; + + + // Allocate image data + // maybe use BGR888 and save some conversion somewhere? + unsigned char* data = (unsigned char*) ::malloc(s * t * 3); + + + setImage(s, t, 0, + GL_RGB, + GL_RGB, GL_UNSIGNED_BYTE, data, + osg::Image::USE_MALLOC_FREE); + + // Allocate decoder rows + // documentation says we need add'l bytes at the end of each + // row for MMX but this is more efficient and works so far. + _rows = (unsigned char**) ::malloc(t * sizeof(unsigned char*)); + unsigned char* dp = data; + for (int i = 0; i < t; i++) { + _rows[i] = dp; + dp += (s * 3); + } + + // + // #if 0 + // // Setup texture matrix + // Matrix mat; + // mat.makeScale((float) s / (float) texWidth, + // ((float) t / (float) texHeight) * -1.0f, 1.0f); + // mat = mat * Matrix::translate(0.0f, (float) t / (float) texHeight, 0.0f); + // _texMat->setMatrix(mat); + // #else + // _texMat->setMatrix(osg::Matrix::scale(s,-t,1.0f)*osg::Matrix::translate(0.0f,t,0.0f)); + // #endif + // XXX + osg::notify(NOTICE) << _frames << " @ " << _fps << " " << _len << "s" << std::endl; + osg::notify(NOTICE) << "img " << s << "x" << t << std::endl; + osg::notify(NOTICE) << "tex " << texWidth << "x" << texHeight << std::endl; +} + + void* MpegImageStream::decode(void*) { bool playing = false; - mpeg3_t* mpg = NULL; + mpeg3_t* mpg = (mpeg3_t*)_mpg; int str = 0; float t0 = 0.0f; - unsigned long delay = 0; - unsigned char** rows = NULL; + unsigned long delay = (unsigned long) ((1.0f / _fps) * 1000.0f); bool done = false; + + const osg::Timer* timer = osg::Timer::instance(); + osg::Timer_t start_tick = timer->tick(); + osg::Timer_t last_frame_tick = start_tick; + double timePerFrame = 1.0f/_fps; + double frameNumber = 0.0; + while (!done) { // Handle commands @@ -118,94 +221,17 @@ void* MpegImageStream::decode(void*) if (cmd != THREAD_IDLE) { switch (cmd) { case THREAD_START: // Start or continue stream - playing = false; - if (!mpg) { - mpg = mpeg3_open((char*) _fileName.c_str()); - if (!mpg) { - osg::notify(WARN) << "Unable to open " << _fileName << std::endl; - continue; - } - - if (!mpeg3_has_video(mpg)) { - osg::notify(WARN) << "No video streams in" << _fileName << std::endl; - continue; - } - if (mpeg3_has_audio(mpg)) { - std::cerr << "Stream has audio" << std::endl; - } - - mpeg3_set_cpus(mpg, 1); - mpeg3_set_mmx(mpg, _useMMX); - - str = 0; //mpeg3_total_vstreams(mpg) - 1; - - _fps = mpeg3_frame_rate(mpg, str); - _frames = mpeg3_video_frames(mpg, str); - _len = (float) _frames / _fps; - - t0 = 0.0f; - delay = (unsigned long) ((1.0f / _fps) * 1000L); - - int s = mpeg3_video_width(mpg, str); - int t = mpeg3_video_height(mpg, str); - - // Calculate texture size - // these are also calculated and stored within osg::Texture but - // too late (on the first apply) to be of any use... - int texWidth = 1; - for (; texWidth < s; texWidth <<= 1) - ; - int texHeight = 1; - for (; texHeight < t; texHeight <<= 1) - ; - - - // Allocate image data - // maybe use BGR888 and save some conversion somewhere? - unsigned char* data = (unsigned char*) ::malloc(s * t * 3); - - - setImage(s, t, 0, - GL_RGB, - GL_RGB, GL_UNSIGNED_BYTE, data, - osg::Image::USE_MALLOC_FREE); - - // Allocate decoder rows - // documentation says we need add'l bytes at the end of each - // row for MMX but this is more efficient and works so far. - rows = (unsigned char**) ::malloc(t * sizeof(unsigned char*)); - unsigned char* dp = data; - for (int i = 0; i < t; i++) { - rows[i] = dp; - dp += (s * 3); - } - - -#if 0 - // Setup texture matrix - Matrix mat; - mat.makeScale((float) s / (float) texWidth, - ((float) t / (float) texHeight) * -1.0f, 1.0f); - mat = mat * Matrix::translate(0.0f, (float) t / (float) texHeight, 0.0f); - _texMat->setMatrix(mat); -#else - _texMat->setMatrix(osg::Matrix::scale(s,-t,1.0f)*osg::Matrix::translate(0.0f,t,0.0f)); -#endif - // XXX - std::cerr << _frames << " @ " << _fps << " " << _len << "s" << std::endl; - std::cerr << "img " << s << "x" << t << std::endl; - std::cerr << "tex " << texWidth << "x" << texHeight << std::endl; - std::cerr << "delay " << delay << "ms" << std::endl; - } playing = true; break; case THREAD_STOP: // XXX - std::cerr << "stop at " << t0 << std::endl; + osg::notify(NOTICE) << "stop at " << t0 << std::endl; playing = false; break; case THREAD_REWIND: // XXX t0 = 0.0; mpeg3_seek_percentage(mpg, 0.0); + start_tick = timer->tick(); + frameNumber = 0; break; case THREAD_CLOSE: // Stop and close playing = false; @@ -215,7 +241,7 @@ void* MpegImageStream::decode(void*) } break; case THREAD_QUIT: // XXX - std::cerr << "quit" << std::endl; + osg::notify(NOTICE) << "quit" << std::endl; done = true; break; default: @@ -225,18 +251,32 @@ void* MpegImageStream::decode(void*) } if (playing) { + + + last_frame_tick = timer->tick(); + // XXX needs more work to be real-time - mpeg3_read_frame(mpg, rows, + mpeg3_read_frame(mpg, _rows, 0, 0, _s, _t, _s, _t, MPEG3_RGB888, str); dirty(); //Image(); - - if (mpeg3_get_time(mpg) > _len) { - rewind(); //stop(); + + ++frameNumber; + + if (frameNumber>=_frames) + { + + rewind(); + //stop(); } else - ::usleep(delay); + { + while (timePerFrame*(frameNumber+1)>timer->delta_s(start_tick,timer->tick())) + { + ::usleep(delay); + } + } } else { ::usleep(IDLE_TIMEOUT); @@ -244,14 +284,6 @@ void* MpegImageStream::decode(void*) } // Cleanup decoder - if (mpg) { - mpeg3_close(mpg); - mpg = NULL; - } - if (rows) { - ::free(rows); - rows = NULL; - } return NULL; } diff --git a/examples/osgmovie/MpegImageStream.h b/examples/osgmovie/MpegImageStream.h index 50fd6436c..233aac51e 100644 --- a/examples/osgmovie/MpegImageStream.h +++ b/examples/osgmovie/MpegImageStream.h @@ -28,7 +28,8 @@ #ifndef _MPEGIMAGESTREAM_H_ #define _MPEGIMAGESTREAM_H_ -#include "ImageStream.h" +#include +//#include "ImageStream.h" #include @@ -39,7 +40,7 @@ namespace osg { /** * MPEG1/2 Image Stream class. */ - class SG_EXPORT MpegImageStream : public ImageStream + class SG_EXPORT MpegImageStream : public osg::ImageStream { public: MpegImageStream(const char* fileName = NULL); @@ -77,6 +78,8 @@ namespace osg { /// Get total length in seconds. inline float getLength() const { return _len; } + + void load(const char* fileName); protected: virtual ~MpegImageStream(); @@ -114,6 +117,10 @@ namespace osg { /// Decoder hook. static void* s_decode(void*); void* decode(void*); + + void* _mpg; + unsigned char** _rows; + }; } // namespace diff --git a/examples/osgmovie/osgmovie.cpp b/examples/osgmovie/osgmovie.cpp index 675e24b9e..5545e0699 100644 --- a/examples/osgmovie/osgmovie.cpp +++ b/examples/osgmovie/osgmovie.cpp @@ -112,13 +112,15 @@ int main(int argc, char** argv) // open MpegImageStream osg::MpegImageStream* mpeg = NULL; - osg::TexMat* texMat = NULL; if (argc > 1) { mpeg = new osg::MpegImageStream(argv[1]); mpeg->start(); - texMat = (osg::TexMat*) mpeg->getTexMat(); } + osg::TexMat* texMat = new osg::TexMat; + texMat->setMatrix(osg::Matrix::scale(mpeg->s(),-mpeg->t(),1.0f)*osg::Matrix::translate(0.0f,mpeg->t(),0.0f)); + + // Create morphed geometry osg::Geode* geode = morphGeom(coords, normals, texCoords, mpeg, texMat); diff --git a/include/osg/ImageStream b/include/osg/ImageStream new file mode 100644 index 000000000..2ebb68dbb --- /dev/null +++ b/include/osg/ImageStream @@ -0,0 +1,76 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library 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 + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSG_IMAGESTREAM +#define OSG_IMAGESTREAM 1 + +#include + +namespace osg { + +/** + * Image Stream class. + */ +class SG_EXPORT ImageStream : public Image +{ + public: + ImageStream(); + + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + ImageStream(const ImageStream& image,const CopyOp& copyop=CopyOp::SHALLOW_COPY); + + virtual Object* cloneType() const { return new ImageStream(); } + virtual Object* clone(const CopyOp& copyop) const { return new ImageStream(*this,copyop); } + virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast(obj)!=0; } + virtual const char* libraryName() const { return "osg"; } + virtual const char* className() const { return "ImageStream"; } + + /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ + virtual int compare(const Image& rhs) const; + + enum StreamStatus + { + STOPPED, + PLAYING, + PAUSED, + REWINDING + }; + + virtual void stop() { _status=STOPPED; } + + virtual void play() { _status=PLAYING; } + + virtual void pause() { _status=PAUSED; } + + virtual void rewind() { _status=REWINDING; } + + StreamStatus getStatus() { return _status; } + + virtual void setReferenceTime(double) {} + virtual double getReferenceTime() const { return 0.0; } + + virtual void setTimeMultiplier(double) {} + virtual double getTimeMultiplier() { return 0.0; } + + virtual void update() {} + + protected: + + virtual ~ImageStream() {} + + StreamStatus _status; +}; + +} // namespace + +#endif diff --git a/src/osg/GNUmakefile b/src/osg/GNUmakefile index 73d981b3e..49115d616 100644 --- a/src/osg/GNUmakefile +++ b/src/osg/GNUmakefile @@ -38,6 +38,7 @@ CXXFILES =\ Geode.cpp\ Group.cpp\ Image.cpp\ + ImageStream.cpp\ Impostor.cpp\ ImpostorSprite.cpp\ LOD.cpp\ diff --git a/src/osg/ImageStream.cpp b/src/osg/ImageStream.cpp new file mode 100644 index 000000000..5d2559ed0 --- /dev/null +++ b/src/osg/ImageStream.cpp @@ -0,0 +1,33 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library 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 + * OpenSceneGraph Public License for more details. +*/ + +#include + +using namespace osg; + +ImageStream::ImageStream(): + _status(STOPPED) +{ +} + +ImageStream::ImageStream(const ImageStream& image,const CopyOp& copyop): + Image(image,copyop), + _status(image._status) +{ +} + +int ImageStream::compare(const Image& rhs) const +{ + return Image::compare(rhs); +} +