diff --git a/CMakeModules/FindFFmpeg.cmake b/CMakeModules/FindFFmpeg.cmake index 266b59c55..4d7eeb995 100644 --- a/CMakeModules/FindFFmpeg.cmake +++ b/CMakeModules/FindFFmpeg.cmake @@ -1,44 +1,165 @@ # Locate ffmpeg # This module defines # FFMPEG_LIBRARIES -# FFMPEG_FOUND, if false, do not try to link to ffmpeg +# FFMPEG_FOUND, if false, do not try to link to ffmpeg # FFMPEG_INCLUDE_DIR, where to find the headers # # $FFMPEG_DIR is an environment variable that would # correspond to the ./configure --prefix=$FFMPEG_DIR # -# Created by Robert Osfield. +# Created by Robert Osfield. -#use pkg-config to find various modes -INCLUDE(FindPkgConfig OPTIONAL) -IF(PKG_CONFIG_FOUND) +#In ffmpeg code, old version use "#include " and newer use "#include " +#In OSG ffmpeg plugin, we use "#include " for compatibility with old version of ffmpeg - INCLUDE(FindPkgConfig) +#We have to search the path which contain the header.h (usefull for old version) +#and search the path which contain the libname/header.h (usefull for new version) - pkg_check_modules(FFMPEG_LIBAVFORMAT libavformat) - pkg_check_modules(FFMPEG_LIBAVDEVICE libavdevice) - pkg_check_modules(FFMPEG_LIBAVCODEC libavcodec) - pkg_check_modules(FFMPEG_LIBAVUTIL libavutil) - pkg_check_modules(FFMPEG_LIBSWSCALE libswscale) +#Then we need to include ${FFMPEG_libname_INCLUDE_DIRS} (in old version case, use by ffmpeg header and osg plugin code) +# (in new version case, use by ffmpeg header) +#and ${FFMPEG_libname_INCLUDE_DIRS/libname} (in new version case, use by osg plugin code) -ENDIF(PKG_CONFIG_FOUND) + +# Macro to find header and lib directories +# example: FFMPEG_FIND(AVFORMAT avformat avformat.h) +MACRO(FFMPEG_FIND varname shortname headername) + # old version of ffmpeg put header in $prefix/include/[ffmpeg] + # so try to find header in include directory + + FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG Headers" + ) + + FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS ${headername} + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG Headers" + ) + + FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES + NAMES ${shortname} + PATHS + ${FFMPEG_ROOT}/lib + $ENV{FFMPEG_DIR}/lib + ~/Library/Frameworks + /Library/Frameworks + /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + /usr/freeware/lib64 + DOC "Location of FFMPEG Libraries" + ) + + IF (FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) + SET(FFMPEG_${varname}_FOUND 1) + ENDIF(FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) + +ENDMACRO(FFMPEG_FIND) + +SET(FFMPEG_ROOT "$ENV{FFMPEG_DIR}" CACHE PATH "Location of FFMPEG") + +# find stdint.h +IF(WIN32) + + FIND_PATH(FFMPEG_STDINT_INCLUDE_DIR stdint.h + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG stdint.h Header" + ) + + IF (FFMPEG_STDINT_INCLUDE_DIR) + SET(STDINT_OK TRUE) + ENDIF() + +ELSE() + + SET(STDINT_OK TRUE) + +ENDIF() + +FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) +FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) +FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) +FFMPEG_FIND(LIBAVUTIL avutil avutil.h) +FFMPEG_FIND(LIBSWSCALE swscale swscale.h) # not sure about the header to look for here. SET(FFMPEG_FOUND "NO") -IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND) - +# Note we don't check FFMPEG_LIBSWSCALE_FOUND here, it's optional. +IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND STDINT_OK) + SET(FFMPEG_FOUND "YES") - SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}) + SET(FFMPEG_INCLUDE_DIRS + ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat + ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/libavdevice + ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/libavcodec + ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/libavutil + ) + + IF (${FFMPEG_STDINT_INCLUDE_DIR}) + SET(FFMPEG_INCLUDE_DIRS + ${FFMPEG_INCLUDE_DIRS} + ${FFMPEG_STDINT_INCLUDE_DIR} + ${FFMPEG_STDINT_INCLUDE_DIR}/libavformat + ${FFMPEG_STDINT_INCLUDE_DIR}/libavdevice + ${FFMPEG_STDINT_INCLUDE_DIR}/libavcodec + ${FFMPEG_STDINT_INCLUDE_DIR}/libavutil + ) + ENDIF() + SET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) + # Note we don't add FFMPEG_LIBSWSCALE_LIBRARIES here, it will be added if found later. SET(FFMPEG_LIBRARIES ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVDEVICE_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) - -ENDIF(FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND) +ELSE () +# MESSAGE(STATUS "Could not find FFMPEG") + +ENDIF() diff --git a/examples/osgmovie/osgmovie.cpp b/examples/osgmovie/osgmovie.cpp index 1a5336d88..3bea15fc1 100644 --- a/examples/osgmovie/osgmovie.cpp +++ b/examples/osgmovie/osgmovie.cpp @@ -119,6 +119,7 @@ protected: bool _playToggle; bool _trackMouse; ImageStreamList _imageStreamList; + unsigned int _seekIncr; }; @@ -239,6 +240,23 @@ bool MovieEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIAction } return true; } + else if (ea.getKey()=='>') + { + for(ImageStreamList::iterator itr=_imageStreamList.begin(); + itr!=_imageStreamList.end(); + ++itr) + { + std::cout<<"Seeking"< 3) _seekIncr = 0; + double length = (*itr)->getLength(); + double t_pos = (length/4.0f)*_seekIncr; + //(*itr)->rewind(); + (*itr)->seek(t_pos); + (*itr)->play(); + _seekIncr++; + } + return true; + } else if (ea.getKey()=='L') { for(ImageStreamList::iterator itr=_imageStreamList.begin(); @@ -272,6 +290,7 @@ void MovieEventHandler::getUsage(osg::ApplicationUsage& usage) const usage.addKeyboardMouseBinding("p","Play/Pause movie"); usage.addKeyboardMouseBinding("r","Restart movie"); usage.addKeyboardMouseBinding("l","Toggle looping of movie"); + usage.addKeyboardMouseBinding(">","Advance the movie using seek"); } @@ -325,7 +344,8 @@ class CustomAudioSink : public osg::AudioSink public: CustomAudioSink(osg::AudioStream* audioStream): - _playing(false), + _started(false), + _paused(false), _audioStream(audioStream) {} virtual void startPlaying() @@ -340,7 +360,9 @@ class CustomAudioSink : public osg::AudioSink } virtual bool playing() const { return _playing; } - bool _playing; + + bool _started; + bool _paused; osg::observer_ptr _audioStream; }; diff --git a/include/osg/AudioStream b/include/osg/AudioStream index b1b105039..a9a0bdd05 100644 --- a/include/osg/AudioStream +++ b/include/osg/AudioStream @@ -25,14 +25,20 @@ public: AudioSink(); - virtual void startPlaying() = 0; + virtual const char * libraryName() const { return "osg"; } + virtual const char * className() const { return "AudioSinkInterface"; } + + virtual void play() = 0; + virtual void pause() = 0; + virtual void stop() = 0; + virtual bool playing() const = 0; virtual double getDelay() const { return _delay; } virtual void setDelay(const double delay) { _delay = delay; } - virtual const char * libraryName() const { return "osgFFmpeg"; } - virtual const char * className() const { return "AudioSinkInterface"; } + virtual void setVolume(float) {} + virtual float getVolume() const { return 0.0f; } private: @@ -59,7 +65,6 @@ class OSG_EXPORT AudioStream : public osg::Object virtual void consumeAudioBuffer(void * const buffer, const size_t size) = 0; - virtual bool audioStream() const = 0; virtual int audioFrequency() const = 0; virtual int audioNbChannels() const = 0; diff --git a/include/osg/Image b/include/osg/Image index a96ef028f..582820249 100644 --- a/include/osg/Image +++ b/include/osg/Image @@ -185,6 +185,9 @@ class OSG_EXPORT Image : public Object void setPacking(unsigned int packing) { _packing = packing; } inline unsigned int getPacking() const { return _packing; } + + inline void setPixelAspectRatio(float pixelAspectRatio) { _pixelAspectRatio = pixelAspectRatio; } + inline float getPixelAspectRatio() const { return _pixelAspectRatio; } /** Return the number of bits required for each pixel. */ inline unsigned int getPixelSizeInBits() const { return computePixelSizeInBits(_pixelFormat,_dataType); } @@ -346,6 +349,7 @@ class OSG_EXPORT Image : public Object GLenum _pixelFormat; GLenum _dataType; unsigned int _packing; + float _pixelAspectRatio; AllocationMode _allocationMode; unsigned char* _data; diff --git a/include/osg/ImageStream b/include/osg/ImageStream index d9449c707..2749cd296 100644 --- a/include/osg/ImageStream +++ b/include/osg/ImageStream @@ -78,7 +78,8 @@ class OSG_EXPORT ImageStream : public Image virtual double getLength() const { return 0.0; } - + virtual double getFrameRate() const { return 0.0; } + virtual void setReferenceTime(double) {} virtual double getReferenceTime() const { return 0.0; } @@ -88,7 +89,6 @@ class OSG_EXPORT ImageStream : public Image virtual void setVolume(float) {} virtual float getVolume() const { return 0.0f; } - typedef std::vector< osg::ref_ptr > AudioStreams; void setAudioStreams(const AudioStreams& asl) { _audioStreams = asl; } AudioStreams& getAudioStreams() { return _audioStreams; } diff --git a/include/osg/PrimitiveSet b/include/osg/PrimitiveSet index 992f57212..7f3f3c315 100644 --- a/include/osg/PrimitiveSet +++ b/include/osg/PrimitiveSet @@ -449,6 +449,12 @@ class DrawElements : public PrimitiveSet { if (_ebo.valid()) _ebo->releaseGLObjects(state); } + + + virtual void reserveElements(unsigned int numIndices) = 0; + virtual void setElement(unsigned int, unsigned int) = 0; + virtual unsigned int getElement(unsigned int) = 0; + virtual void addElement(unsigned int) = 0; protected: @@ -526,6 +532,11 @@ class OSG_EXPORT DrawElementsUByte : public DrawElements, public VectorGLubyte _rangeModifiedCount = _modifiedCount; } + virtual void reserveElements(unsigned int numIndices) { reserve(numIndices); } + virtual void setElement(unsigned int i, unsigned int v) { (*this)[i] = v; } + virtual unsigned int getElement(unsigned int i) { return (*this)[i]; } + virtual void addElement(unsigned int v) { push_back(GLubyte(v)); } + protected: virtual ~DrawElementsUByte(); @@ -601,6 +612,11 @@ class OSG_EXPORT DrawElementsUShort : public DrawElements, public VectorGLushort _rangeModifiedCount = _modifiedCount; } + virtual void reserveElements(unsigned int numIndices) { reserve(numIndices); } + virtual void setElement(unsigned int i, unsigned int v) { (*this)[i] = v; } + virtual unsigned int getElement(unsigned int i) { return (*this)[i]; } + virtual void addElement(unsigned int v) { push_back(GLushort(v)); } + protected: virtual ~DrawElementsUShort(); @@ -676,6 +692,11 @@ class OSG_EXPORT DrawElementsUInt : public DrawElements, public VectorGLuint _rangeModifiedCount = _modifiedCount; } + virtual void reserveElements(unsigned int numIndices) { reserve(numIndices); } + virtual void setElement(unsigned int i, unsigned int v) { (*this)[i] = v; } + virtual unsigned int getElement(unsigned int i) { return (*this)[i]; } + virtual void addElement(unsigned int v) { push_back(GLuint(v)); } + protected: virtual ~DrawElementsUInt(); diff --git a/src/osg/Image.cpp b/src/osg/Image.cpp index 2f5ec4094..fbba3a920 100644 --- a/src/osg/Image.cpp +++ b/src/osg/Image.cpp @@ -33,23 +33,21 @@ using namespace osg; using namespace std; Image::Image() - :Object(true) + :Object(true), + _fileName(""), + _writeHint(NO_PREFERENCE), + _origin(BOTTOM_LEFT), + _s(0), _t(0), _r(0), + _internalTextureFormat(0), + _pixelFormat(0), + _dataType(0), + _packing(4), + _pixelAspectRatio(1.0), + _allocationMode(USE_NEW_DELETE), + _data(0L), + _modifiedCount(0) { setDataVariance(STATIC); - - _fileName = ""; - _writeHint = NO_PREFERENCE; - _origin = BOTTOM_LEFT; - _s = _t = _r = 0; - _internalTextureFormat = 0; - _pixelFormat = (unsigned int)0; - _dataType = (unsigned int)0; - _packing = 4; - - _allocationMode = USE_NEW_DELETE; - _data = (unsigned char *)0L; - - _modifiedCount = 0; } Image::Image(const Image& image,const CopyOp& copyop): @@ -62,6 +60,7 @@ Image::Image(const Image& image,const CopyOp& copyop): _pixelFormat(image._pixelFormat), _dataType(image._dataType), _packing(image._packing), + _pixelAspectRatio(image._pixelAspectRatio), _data(0L), _modifiedCount(image._modifiedCount), _mipmapData(image._mipmapData) diff --git a/src/osgPlugins/ffmpeg/CMakeLists.txt b/src/osgPlugins/ffmpeg/CMakeLists.txt index 577b5afdf..a0dc7d7b1 100644 --- a/src/osgPlugins/ffmpeg/CMakeLists.txt +++ b/src/osgPlugins/ffmpeg/CMakeLists.txt @@ -1,34 +1,16 @@ -# INCLUDE_DIRECTORIES( ${FFMPEG_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES( ${FFMPEG_INCLUDE_DIRS} ) +LINK_DIRECTORIES(${FFMPEG_LIBRARY_DIRS}) +SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ) -IF (FFMPEG_LIBSWSCALE_FOUND) - INCLUDE_DIRECTORIES( - ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/libavdevice ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/libavcodec ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/libavcodec ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBSWSCALE_INCLUDE_DIRS} ${FFMPEG_LIBSWSCALE_INCLUDE_DIRS}/libswscale ${FFMPEG_LIBSWSCALE_INCLUDE_DIRS}/ffmpeg - ) +IF(FFMPEG_LIBSWSCALE_FOUND) + + INCLUDE_DIRECTORIES( ${FFMPEG_LIBSWSCALE_INCLUDE_DIRS} ${FFMPEG_LIBSWSCALE_INCLUDE_DIRS}/libswscale ) ADD_DEFINITIONS(-DUSE_SWSCALE) - LINK_DIRECTORIES(${FFMPEG_LIBRARY_DIRS}) - SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_LIBSWSCALE_LIBRARIES}) -ELSE(FFMPEG_LIBSWSCALE_FOUND) - - INCLUDE_DIRECTORIES( - ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/libavdevice ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/libavcodec ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/ffmpeg - ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/libavcodec ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/ffmpeg - ) - - LINK_DIRECTORIES(${FFMPEG_LIBRARY_DIRS}) - - SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ) - ENDIF() # MESSAGE("FFMPEG_LIBAVFORMAT_INCLUDE_DIRS = " ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ) @@ -60,6 +42,10 @@ SET(TARGET_H MessageQueue.hpp ) +IF(CMAKE_COMPILER_IS_GNUCXX) + # Remove -pedantic flag as it barfs on ffmoeg headers + STRING(REGEX REPLACE "-pedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +ENDIF() #### end var setup ### diff --git a/src/osgPlugins/ffmpeg/FFmpegAudioStream.cpp b/src/osgPlugins/ffmpeg/FFmpegAudioStream.cpp index 8e6809e38..89ce71069 100644 --- a/src/osgPlugins/ffmpeg/FFmpegAudioStream.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegAudioStream.cpp @@ -51,12 +51,6 @@ double FFmpegAudioStream::duration() const } -bool FFmpegAudioStream::audioStream() const -{ - return m_decoder->audio_decoder().validContext(); -} - - int FFmpegAudioStream::audioFrequency() const { diff --git a/src/osgPlugins/ffmpeg/FFmpegAudioStream.hpp b/src/osgPlugins/ffmpeg/FFmpegAudioStream.hpp index 2d6c741f8..5143989fd 100644 --- a/src/osgPlugins/ffmpeg/FFmpegAudioStream.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegAudioStream.hpp @@ -21,7 +21,6 @@ namespace osgFFmpeg void consumeAudioBuffer(void * const buffer, const size_t size); - bool audioStream() const; int audioFrequency() const; int audioNbChannels() const; osg::AudioStream::SampleFormat audioSampleFormat() const; diff --git a/src/osgPlugins/ffmpeg/FFmpegClocks.cpp b/src/osgPlugins/ffmpeg/FFmpegClocks.cpp index 5547461e5..9a6d2cf2d 100644 --- a/src/osgPlugins/ffmpeg/FFmpegClocks.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegClocks.cpp @@ -30,6 +30,8 @@ namespace FFmpegClocks::FFmpegClocks() : m_video_clock(0), m_start_time(0), + m_pause_time(0), + m_seek_time(0), m_last_frame_delay(0.040), m_last_frame_pts(0), m_last_actual_delay(0), @@ -37,7 +39,8 @@ FFmpegClocks::FFmpegClocks() : m_audio_buffer_end_pts(0), m_audio_delay(0.0), m_audio_disabled(false), - m_rewind(false) + m_paused(false), + m_last_current_time(0.0) { } @@ -55,40 +58,53 @@ void FFmpegClocks::reset(const double start_time) m_last_frame_pts = start_time - m_last_frame_delay; m_frame_time = start_time; + m_pause_time = 0; + m_seek_time = 0; + m_audio_buffer_end_pts = start_time; m_audio_timer.setStartTick(); } - - -void FFmpegClocks::rewindAudio() +void FFmpegClocks::pause(bool pause) { - ScopedLock lock(m_mutex); - - m_audio_buffer_end_pts = m_start_time; - m_audio_timer.setStartTick(); - - m_rewind = ! m_rewind; + if(pause) + m_paused = true; + else + { + m_paused = false; + if(!m_audio_disabled) m_audio_timer.setStartTick(); + } } -void FFmpegClocks::rewindVideo() +void FFmpegClocks::rewind() { ScopedLock lock(m_mutex); + m_pause_time = 0; + m_seek_time = 0; + + m_audio_buffer_end_pts = m_start_time; + m_audio_timer.setStartTick(); + + m_last_frame_delay = 0.040; + m_frame_time = m_start_time; + if (m_audio_disabled) return; - m_video_clock = m_start_time; - - m_last_frame_delay = 0.040; - m_last_frame_pts = m_start_time - m_last_frame_delay; - m_frame_time = m_start_time; - - m_rewind = ! m_rewind; + m_video_clock = m_start_time; } +void FFmpegClocks::seek(double seek_time) +{ + ScopedLock lock(m_mutex); + + m_video_clock = seek_time; + m_last_frame_delay = 0.040; + m_frame_time = seek_time; +} void FFmpegClocks::audioSetBufferEndPts(const double pts) @@ -167,8 +183,13 @@ double FFmpegClocks::videoRefreshSchedule(const double pts) // If incorrect delay, use previous one + if (delay <= 0.0 || delay >= 1.0) + { delay = m_last_frame_delay; + if(!m_audio_disabled) m_frame_time = pts - delay; + } + // Save for next time m_last_frame_delay = delay; @@ -181,9 +202,7 @@ double FFmpegClocks::videoRefreshSchedule(const double pts) m_frame_time += delay; const double audio_time = getAudioTime(); - const double actual_delay = (! m_rewind) ? - clamp(m_frame_time - audio_time, -0.5*delay, 2.5*delay) : - m_last_actual_delay; // when rewinding audio or video (but the other has yet to be), get the last used delay + const double actual_delay = clamp(m_frame_time - audio_time, -0.5*delay, 2.5*delay); //m_frame_time += delay; @@ -205,13 +224,33 @@ double FFmpegClocks::getStartTime() const return m_start_time; } - - -double FFmpegClocks::getAudioTime() const +void FFmpegClocks::setPauseTime(double pause_time) { - return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_audio_delay; + m_pause_time += pause_time; +} + +void FFmpegClocks::setSeekTime(double seek_time) +{ + m_seek_time += getAudioTime() - seek_time; } +double FFmpegClocks::getAudioTime() const +{ + if(m_audio_disabled) + return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_pause_time - m_audio_delay - m_seek_time; + else + return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_audio_delay; +} + + +double FFmpegClocks::getCurrentTime() +{ + if(!m_paused) + m_last_current_time = getAudioTime(); + + return m_last_current_time; +} + } // namespace osgFFmpeg diff --git a/src/osgPlugins/ffmpeg/FFmpegClocks.hpp b/src/osgPlugins/ffmpeg/FFmpegClocks.hpp index 904436e40..1fc7a1b91 100644 --- a/src/osgPlugins/ffmpeg/FFmpegClocks.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegClocks.hpp @@ -22,18 +22,23 @@ public: FFmpegClocks(); void reset(double start_time); - void rewindAudio(); - void rewindVideo(); + void pause(bool pause); + void seek(double seek_time); + void rewind(); void audioSetBufferEndPts(double pts); void audioAdjustBufferEndPts(double increment); void audioSetDelay(double delay); void audioDisable(); + bool audioDisabled() const { return m_audio_disabled; } double videoSynchClock(const AVFrame * frame, double time_base, double pts); double videoRefreshSchedule(double pts); double getStartTime() const; + double getCurrentTime(); + void setPauseTime(double pause_time); + void setSeekTime(double seek_time); private: @@ -48,15 +53,19 @@ private: double m_video_clock; double m_start_time; + double m_pause_time; + double m_seek_time; double m_last_frame_delay; double m_last_frame_pts; double m_last_actual_delay; double m_frame_time; double m_audio_buffer_end_pts; double m_audio_delay; - Timer m_audio_timer; - bool m_audio_disabled; - bool m_rewind; + Timer m_audio_timer; + bool m_audio_disabled; + bool m_paused; + double m_last_current_time; + }; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp index fd657aabb..460d1b77a 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp @@ -2,12 +2,13 @@ #include "FFmpegDecoder.hpp" #include +#include #include #include #include #include - +#include namespace osgFFmpeg { @@ -54,24 +55,48 @@ bool FFmpegDecoder::open(const std::string & filename) formatParams.channel = 0; formatParams.standard = 0; +#if 1 + formatParams.width = 320; + formatParams.height = 240; +#else formatParams.width = 640; formatParams.height = 480; +#endif formatParams.time_base.num = 1; - formatParams.time_base.den = 50; + formatParams.time_base.den = 30; - iformat = av_find_input_format("video4linux2"); + std::string format = "video4linux2"; + iformat = av_find_input_format(format.c_str()); if (iformat) { - osg::notify(osg::NOTICE)<<"Found input format"<time_base); if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0) throw std::runtime_error("av_seek_frame failed()"); + m_clocks.rewind(); m_state = REWINDING; } +bool FFmpegDecoder::readNextPacketSeeking() +{ + const FFmpegPacket packet(FFmpegPacket::PACKET_FLUSH); + + if (m_audio_queue.timedPush(packet, 10) && m_video_queue.timedPush(packet, 10)) + m_state = NORMAL; + + return false; +} + +void FFmpegDecoder::seekButDontFlushQueues(double time) +{ + const AVRational AvTimeBaseQ = { 1, AV_TIME_BASE }; // = AV_TIME_BASE_Q + + const int64_t pos = int64_t(m_clocks.getStartTime()+time * double(AV_TIME_BASE)); + const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base); + + m_clocks.setSeekTime(time); + + if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0) + throw std::runtime_error("av_seek_frame failed()"); + + m_clocks.seek(time); + m_state = SEEKING; +} + } // namespace osgFFmpeg diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp index f6c83e714..4d078deb6 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp @@ -68,11 +68,14 @@ public: bool readNextPacket(); void rewind(); + void seek(double time); + void pause(); void loop(bool loop); bool loop() const; double duration() const; + double reference(); FFmpegDecoderAudio & audio_decoder(); FFmpegDecoderVideo & video_decoder(); @@ -84,8 +87,10 @@ protected: enum State { NORMAL, + PAUSE, END_OF_STREAM, - REWINDING + REWINDING, + SEEKING }; typedef BoundedMessageQueue PacketQueue; @@ -97,27 +102,30 @@ protected: bool readNextPacketNormal(); bool readNextPacketEndOfStream(); bool readNextPacketRewinding(); + bool readNextPacketSeeking(); + bool readNextPacketPause(); void rewindButDontFlushQueues(); + void seekButDontFlushQueues(double time); FormatContextPtr m_format_context; - AVStream * m_audio_stream; - AVStream * m_video_stream; + AVStream * m_audio_stream; + AVStream * m_video_stream; - unsigned int m_audio_index; - unsigned int m_video_index; + int m_audio_index; + int m_video_index; FFmpegClocks m_clocks; FFmpegPacket m_pending_packet; - PacketQueue m_audio_queue; - PacketQueue m_video_queue; + PacketQueue m_audio_queue; + PacketQueue m_video_queue; - FFmpegDecoderAudio m_audio_decoder; - FFmpegDecoderVideo m_video_decoder; + FFmpegDecoderAudio m_audio_decoder; + FFmpegDecoderVideo m_video_decoder; - double m_duration; - double m_start; + double m_duration; + double m_start; - State m_state; + State m_state; bool m_loop; }; @@ -142,6 +150,11 @@ inline double FFmpegDecoder::duration() const return double(m_format_context->duration) / AV_TIME_BASE; } +inline double FFmpegDecoder::reference() +{ + return m_clocks.getCurrentTime(); +} + inline FFmpegDecoderAudio & FFmpegDecoder::audio_decoder() { diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp index fd78b8a9e..0c5346030 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp @@ -12,6 +12,25 @@ namespace osgFFmpeg { +static int decode_audio(AVCodecContext *avctx, int16_t *samples, + int *frame_size_ptr, + const uint8_t *buf, int buf_size) +{ +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32) + + // following code segment copied from ffmpeg's avcodec_decode_audio2() + // implementation to avoid warnings about deprecated function usage. + AVPacket avpkt; + av_init_packet(&avpkt); + avpkt.data = const_cast(buf); + avpkt.size = buf_size; + + return avcodec_decode_audio3(avctx, samples, frame_size_ptr, &avpkt); +#else + // fallback for older versions of ffmpeg that don't have avcodec_decode_audio3. + return avcodec_decode_audio2(avctx, samples, frame_size_ptr, buf, buf_size); +#endif +} FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clocks) : @@ -25,6 +44,7 @@ FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clo m_audio_buf_size(0), m_audio_buf_index(0), m_end_of_stream(false), + m_paused(true), m_exit(false) { @@ -88,6 +108,18 @@ void FFmpegDecoderAudio::open(AVStream * const stream) } } +void FFmpegDecoderAudio::pause(bool pause) +{ + if (pause != m_paused) + { + m_paused = pause; + if (m_audio_sink.valid()) + { + if (m_paused) m_audio_sink->pause(); + else m_audio_sink->play(); + } + } +} void FFmpegDecoderAudio::close(bool waitForThreadToExit) { @@ -99,6 +131,22 @@ void FFmpegDecoderAudio::close(bool waitForThreadToExit) } } +void FFmpegDecoderAudio::setVolume(float volume) +{ + if (m_audio_sink.valid()) + { + m_audio_sink->setVolume(volume); + } +} + +float FFmpegDecoderAudio::getVolume() const +{ + if (m_audio_sink.valid()) + { + return m_audio_sink->getVolume(); + } + return 0.0f; +} void FFmpegDecoderAudio::run() { @@ -130,7 +178,6 @@ void FFmpegDecoderAudio::setAudioSink(osg::ref_ptr audio_sink) void FFmpegDecoderAudio::fillBuffer(void * const buffer, size_t size) { - size_t filled = 0; uint8_t * dst_buffer = reinterpret_cast(buffer); while (size != 0) @@ -176,7 +223,7 @@ void FFmpegDecoderAudio::decodeLoop() if (! skip_audio && ! m_audio_sink->playing()) { m_clocks.audioSetDelay(m_audio_sink->getDelay()); - m_audio_sink->startPlaying(); + m_audio_sink->play(); } else { @@ -185,6 +232,21 @@ void FFmpegDecoderAudio::decodeLoop() while (! m_exit) { + + if(m_paused) + { + m_clocks.pause(true); + m_pause_timer.setStartTick(); + + while(m_paused) + { + microSleep(10000); + } + + m_clocks.setPauseTime(m_pause_timer.time_s()); + m_clocks.pause(false); + } + // If skipping audio, make sure the audio stream is still consumed. if (skip_audio) { @@ -194,7 +256,6 @@ void FFmpegDecoderAudio::decodeLoop() if (packet.valid()) packet.clear(); } - // Else, just idle in this thread. // Note: If m_audio_sink has an audio callback, this thread will still be awaken // from time to time to refill the audio buffer. @@ -252,7 +313,7 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size) { int data_size = size; - const int bytes_decoded = avcodec_decode_audio2(m_context, reinterpret_cast(buffer), &data_size, m_packet_data, m_bytes_remaining); + const int bytes_decoded = decode_audio(m_context, reinterpret_cast(buffer), &data_size, m_packet_data, m_bytes_remaining); if (bytes_decoded < 0) { @@ -285,7 +346,7 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size) if (m_packet.type == FFmpegPacket::PACKET_DATA) { - if (m_packet.packet.pts != AV_NOPTS_VALUE) + if (m_packet.packet.pts != int64_t(AV_NOPTS_VALUE)) { const double pts = av_q2d(m_stream->time_base) * m_packet.packet.pts; m_clocks.audioSetBufferEndPts(pts); @@ -301,7 +362,6 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size) else if (m_packet.type == FFmpegPacket::PACKET_FLUSH) { avcodec_flush_buffers(m_context); - m_clocks.rewindAudio(); } // just output silence when we reached the end of stream diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp index 8e0da0b7f..abe87817e 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp @@ -4,6 +4,8 @@ #include +#include + #include "FFmpegClocks.hpp" #include "FFmpegPacket.hpp" @@ -29,8 +31,12 @@ public: ~FFmpegDecoderAudio(); void open(AVStream * stream); + void pause(bool pause); void close(bool waitForThreadToExit); + void setVolume(float volume); + float getVolume() const; + virtual void run(); void setAudioSink(osg::ref_ptr audio_sink); @@ -51,26 +57,29 @@ private: size_t decodeFrame(void * buffer, size_t size); - PacketQueue & m_packets; - FFmpegClocks & m_clocks; - AVStream * m_stream; - AVCodecContext * m_context; - FFmpegPacket m_packet; - const uint8_t * m_packet_data; - int m_bytes_remaining; + PacketQueue & m_packets; + FFmpegClocks & m_clocks; + AVStream * m_stream; + AVCodecContext * m_context; + FFmpegPacket m_packet; + const uint8_t * m_packet_data; + int m_bytes_remaining; - Buffer m_audio_buffer; - size_t m_audio_buf_size; - size_t m_audio_buf_index; + Buffer m_audio_buffer; + size_t m_audio_buf_size; + size_t m_audio_buf_index; - int m_frequency; - int m_nb_channels; - osg::AudioStream::SampleFormat m_sample_format; + int m_frequency; + int m_nb_channels; + osg::AudioStream::SampleFormat m_sample_format; - SinkPtr m_audio_sink; + SinkPtr m_audio_sink; - bool m_end_of_stream; - volatile bool m_exit; + osg::Timer m_pause_timer; + + bool m_end_of_stream; + bool m_paused; + volatile bool m_exit; }; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp index 2982d70c1..468065ecb 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp @@ -8,6 +8,28 @@ namespace osgFFmpeg { +static int decode_video(AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, + const uint8_t *buf, int buf_size) +{ +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32) + // following code segment copied from ffmpeg avcodec_decode_video() implementation + // to avoid warnings about deprecated function usage. + AVPacket avpkt; + av_init_packet(&avpkt); + avpkt.data = const_cast(buf); + avpkt.size = buf_size; + // HACK for CorePNG to decode as normal PNG by default + avpkt.flags = AV_PKT_FLAG_KEY; + + return avcodec_decode_video2(avctx, picture, got_picture_ptr, &avpkt); +#else + // fallback for older versions of ffmpeg that don't have avcodec_decode_video2. + return avcodec_decode_video(avctx, picture, got_picture_ptr, buf, buf_size); +#endif +} + + FFmpegDecoderVideo::FFmpegDecoderVideo(PacketQueue & packets, FFmpegClocks & clocks) : m_packets(packets), m_clocks(clocks), @@ -20,6 +42,7 @@ FFmpegDecoderVideo::FFmpegDecoderVideo(PacketQueue & packets, FFmpegClocks & clo m_writeBuffer(0), m_user_data(0), m_publish_func(0), + m_paused(true), m_exit(false) #ifdef USE_SWSCALE ,m_swscale_ctx(0) @@ -32,7 +55,7 @@ FFmpegDecoderVideo::FFmpegDecoderVideo(PacketQueue & packets, FFmpegClocks & clo FFmpegDecoderVideo::~FFmpegDecoderVideo() { - osg::notify(osg::NOTICE)<<"Destructing FFmpegDecoderVideo..."<opaque != 0 && - *reinterpret_cast(m_frame->opaque) != AV_NOPTS_VALUE) + *reinterpret_cast(m_frame->opaque) != int64_t(AV_NOPTS_VALUE)) { pts = *reinterpret_cast(m_frame->opaque); } - else if (packet.packet.dts != AV_NOPTS_VALUE) + else if (packet.packet.dts != int64_t(AV_NOPTS_VALUE)) { pts = packet.packet.dts; } @@ -190,10 +220,15 @@ void FFmpegDecoderVideo::decodeLoop() const double synched_pts = m_clocks.videoSynchClock(m_frame.get(), av_q2d(m_stream->time_base), pts); const double frame_delay = m_clocks.videoRefreshSchedule(synched_pts); - publishFrame(frame_delay); + publishFrame(frame_delay, m_clocks.audioDisabled()); } } + while(m_paused && !m_exit) + { + microSleep(10000); + } + // Get the next packet pts = 0; @@ -214,7 +249,6 @@ void FFmpegDecoderVideo::decodeLoop() else if (packet.type == FFmpegPacket::PACKET_FLUSH) { avcodec_flush_buffers(m_context); - m_clocks.rewindVideo(); } } } @@ -224,61 +258,69 @@ void FFmpegDecoderVideo::decodeLoop() void FFmpegDecoderVideo::findAspectRatio() { - double ratio = 0.0; + float ratio = 0.0f; if (m_context->sample_aspect_ratio.num != 0) - ratio = (av_q2d(m_context->sample_aspect_ratio) * m_width) / m_height; + ratio = float(av_q2d(m_context->sample_aspect_ratio)); - if (ratio <= 0.0) - ratio = double(m_width) / double(m_height); + if (ratio <= 0.0f) + ratio = 1.0f; - m_aspect_ratio = ratio; + m_pixel_aspect_ratio = ratio; } -int FFmpegDecoderVideo::convert(AVPicture *dst, int dst_pix_fmt, const AVPicture *src, +int FFmpegDecoderVideo::convert(AVPicture *dst, int dst_pix_fmt, AVPicture *src, int src_pix_fmt, int src_width, int src_height) { osg::Timer_t startTick = osg::Timer::instance()->tick(); #ifdef USE_SWSCALE if (m_swscale_ctx==0) { - m_swscale_ctx = sws_getContext(src_width, src_height, src_pix_fmt, - src_width, src_height, dst_pix_fmt, + m_swscale_ctx = sws_getContext(src_width, src_height, (PixelFormat) src_pix_fmt, + src_width, src_height, (PixelFormat) dst_pix_fmt, /*SWS_BILINEAR*/ SWS_BICUBIC, NULL, NULL, NULL); } - osg::notify(osg::NOTICE)<<"Using sws_scale "; - + osg::notify(osg::INFO)<<"Using sws_scale "; + int result = sws_scale(m_swscale_ctx, - src->data, src->linesize, 0, src_height, - dst->data, dst->linesize); + (src->data), (src->linesize), 0, src_height, + (dst->data), (dst->linesize)); #else - osg::notify(osg::NOTICE)<<"Using img_convert "; + osg::notify(osg::INFO)<<"Using img_convert "; int result = img_convert(dst, dst_pix_fmt, src, src_pix_fmt, src_width, src_height); #endif osg::Timer_t endTick = osg::Timer::instance()->tick(); - osg::notify(osg::NOTICE)<<" time = "<delta_m(startTick,endTick)<<"ms"<pix_fmt, width, height); diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp index 2b86e42d5..7883b177c 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp @@ -65,6 +65,7 @@ public: ~FFmpegDecoderVideo(); void open(AVStream * stream); + void pause(bool pause); void close(bool waitForThreadToExit); virtual void run(); @@ -74,7 +75,7 @@ public: int width() const; int height() const; - double aspectRatio() const; + float pixelAspectRatio() const; bool alphaChannel() const; double frameRate() const; const uint8_t * image() const; @@ -85,11 +86,11 @@ private: void decodeLoop(); void findAspectRatio(); - void publishFrame(double delay); + void publishFrame(double delay, bool audio_disabled); double synchronizeVideo(double pts); - void yuva420pToRgba(AVPicture *dst, const AVPicture *src, int width, int height); + void yuva420pToRgba(AVPicture *dst, AVPicture *src, int width, int height); - int convert(AVPicture *dst, int dst_pix_fmt, const AVPicture *src, + int convert(AVPicture *dst, int dst_pix_fmt, AVPicture *src, int src_pix_fmt, int src_width, int src_height); @@ -114,12 +115,13 @@ private: PublishFunc m_publish_func; double m_frame_rate; - double m_aspect_ratio; + float m_pixel_aspect_ratio; int m_width; int m_height; size_t m_next_frame_index; bool m_alpha_channel; + bool m_paused; volatile bool m_exit; #ifdef USE_SWSCALE @@ -155,9 +157,9 @@ inline int FFmpegDecoderVideo::height() const } -inline double FFmpegDecoderVideo::aspectRatio() const +inline float FFmpegDecoderVideo::pixelAspectRatio() const { - return m_aspect_ratio; + return m_pixel_aspect_ratio; } diff --git a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp index 7ae364262..357491ca7 100644 --- a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp @@ -6,6 +6,7 @@ extern "C" { #define __STDC_CONSTANT_MACROS +#include // for error codes defined in avformat.h #include #include #include diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp index 6b849aacd..00a7b23e8 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp @@ -18,7 +18,7 @@ FFmpegImageStream::FFmpegImageStream() : m_commands(0), m_frame_published_flag(false) { - setOrigin(osg::Image::BOTTOM_LEFT); + setOrigin(osg::Image::TOP_LEFT); std::auto_ptr decoder(new FFmpegDecoder); std::auto_ptr commands(new CommandQueue); @@ -39,11 +39,11 @@ FFmpegImageStream::FFmpegImageStream(const FFmpegImageStream & image, const osg: FFmpegImageStream::~FFmpegImageStream() { - osg::notify(osg::NOTICE)<<"Destructing FFMpegImageStream..."<video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, const_cast(m_decoder->video_decoder().image()), NO_DELETE ); - - setOrigin(osg::Image::TOP_LEFT); + + + setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio()); + + osg::notify(osg::NOTICE)<<"ffmpeg::open("<video_decoder().pixelAspectRatio()<video_decoder().setUserData(this); m_decoder->video_decoder().setPublishCallback(publishNewFrame); - + if (m_decoder->audio_decoder().validContext()) { osg::notify(osg::NOTICE)<<"Attaching FFmpegAudioStream"<push(CMD_REWIND); } +void FFmpegImageStream::seek(double time) { + m_seek_time = time; + m_commands->push(CMD_SEEK); +} + void FFmpegImageStream::quit(bool waitForThreadToExit) @@ -139,34 +152,43 @@ void FFmpegImageStream::quit(bool waitForThreadToExit) m_decoder->close(waitForThreadToExit); } +void FFmpegImageStream::setVolume(float volume) +{ + m_decoder->audio_decoder().setVolume(volume); +} -double FFmpegImageStream::duration() const +float FFmpegImageStream::getVolume() const +{ + return m_decoder->audio_decoder().getVolume(); +} + +double FFmpegImageStream::getLength() const { return m_decoder->duration(); } +double FFmpegImageStream::getReferenceTime () const +{ + return m_decoder->reference(); +} -bool FFmpegImageStream::videoAlphaChannel() const + + +double FFmpegImageStream::getFrameRate() const +{ + return m_decoder->video_decoder().frameRate(); +} + + + +bool FFmpegImageStream::isImageTranslucent() const { return m_decoder->video_decoder().alphaChannel(); } -double FFmpegImageStream::videoAspectRatio() const -{ - return m_decoder->video_decoder().aspectRatio(); -} - - - -double FFmpegImageStream::videoFrameRate() const -{ - return m_decoder->video_decoder().frameRate(); -} - - void FFmpegImageStream::run() { try @@ -232,6 +254,10 @@ bool FFmpegImageStream::handleCommand(const Command cmd) cmdRewind(); return true; + case CMD_SEEK: + cmdSeek(m_seek_time); + return true; + case CMD_STOP: return false; @@ -252,6 +278,9 @@ void FFmpegImageStream::cmdPlay() if (! m_decoder->video_decoder().isRunning()) m_decoder->video_decoder().start(); + + m_decoder->video_decoder().pause(false); + m_decoder->audio_decoder().pause(false); } _status = PLAYING; @@ -263,7 +292,8 @@ void FFmpegImageStream::cmdPause() { if (_status == PLAYING) { - + m_decoder->video_decoder().pause(true); + m_decoder->audio_decoder().pause(true); } _status = PAUSED; @@ -276,6 +306,10 @@ void FFmpegImageStream::cmdRewind() m_decoder->rewind(); } +void FFmpegImageStream::cmdSeek(double time) +{ + m_decoder->seek(time); +} void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data) diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp index 8658c62da..cd4b200e4 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp @@ -30,14 +30,17 @@ namespace osgFFmpeg virtual void play(); virtual void pause(); virtual void rewind(); + virtual void seek(double time); virtual void quit(bool waitForThreadToExit = true); - double duration() const; + virtual void setVolume(float volume); + virtual float getVolume() const; - bool videoAlphaChannel() const; - double videoAspectRatio() const; - double videoFrameRate() const; + virtual double getLength() const; + virtual double getReferenceTime () const; + virtual double getFrameRate() const; + virtual bool isImageTranslucent() const; private: @@ -46,7 +49,8 @@ namespace osgFFmpeg CMD_PLAY, CMD_PAUSE, CMD_STOP, - CMD_REWIND + CMD_REWIND, + CMD_SEEK }; typedef MessageQueue CommandQueue; @@ -62,6 +66,7 @@ namespace osgFFmpeg void cmdPlay(); void cmdPause(); void cmdRewind(); + void cmdSeek(double time); static void publishNewFrame(const FFmpegDecoderVideo &, void * user_data); @@ -70,7 +75,8 @@ namespace osgFFmpeg Mutex m_mutex; Condition m_frame_published_cond; - bool m_frame_published_flag; + bool m_frame_published_flag; + double m_seek_time; }; } diff --git a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp index 65afe135b..cfc22da1d 100644 --- a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp +++ b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp @@ -29,14 +29,22 @@ public: ReaderWriterFFmpeg() { supportsProtocol("http","Read video/audio from http using ffmpeg."); + supportsProtocol("rtsp","Read video/audio from rtsp using ffmpeg."); - supportsExtension("avi", ""); - supportsExtension("flv", ""); - supportsExtension("mov", ""); - supportsExtension("ogg", "Theora movie format"); - supportsExtension("mpg", "Mpeg movie format"); - supportsExtension("mpv", "Mpeg movie format"); - supportsExtension("wmv", ""); + supportsExtension("ffmpeg", ""); + supportsExtension("avi", ""); + supportsExtension("flv", "Flash video"); + supportsExtension("mov", "Quicktime"); + supportsExtension("ogg", "Theora movie format"); + supportsExtension("mpg", "Mpeg movie format"); + supportsExtension("mpv", "Mpeg movie format"); + supportsExtension("wmv", "Windows Media Video format"); + supportsExtension("mkv", "Matroska"); + supportsExtension("mjpeg", "Motion JPEG"); + supportsExtension("mp4", "MPEG-4"); + supportsExtension("sav", "MPEG-4"); + supportsExtension("3gp", "MPEG-4"); + supportsExtension("sdp", "MPEG-4"); // Register all FFmpeg formats/codecs av_register_all(); @@ -54,12 +62,14 @@ public: virtual ReadResult readImage(const std::string & filename, const osgDB::ReaderWriter::Options * options) const { + const std::string ext = osgDB::getLowerCaseFileExtension(filename); + if (ext=="ffmpeg") return readImage(osgDB::getNameLessExtension(filename),options); + if (filename.compare(0, 5, "/dev/")==0) { return readImageStream(filename, options); } - const std::string ext = osgDB::getLowerCaseFileExtension(filename); if (! acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; @@ -70,7 +80,7 @@ public: if (path.empty()) return ReadResult::FILE_NOT_FOUND; - return readImageStream(filename, options); + return readImageStream(path, options); } ReadResult readImageStream(const std::string& filename, const osgDB::ReaderWriter::Options * options) const diff --git a/src/osgTerrain/GeometryTechnique.cpp b/src/osgTerrain/GeometryTechnique.cpp index 09898e82b..9f51e374e 100644 --- a/src/osgTerrain/GeometryTechnique.cpp +++ b/src/osgTerrain/GeometryTechnique.cpp @@ -416,9 +416,16 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 // populate primitive sets // bool optimizeOrientations = elevations!=0; bool swapOrientation = !(masterLocator->orientationOpenGL()); + + bool smallTile = numVertices <= 16384; + + // osg::notify(osg::NOTICE)<<"smallTile = "< elements = new osg::DrawElementsUInt(GL_TRIANGLES); - elements->reserve((numRows-1) * (numColumns-1) * 6); + osg::ref_ptr elements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_TRIANGLES)) : + static_cast(new osg::DrawElementsUInt(GL_TRIANGLES)); + + elements->reserveElements((numRows-1) * (numColumns-1) * 6); geometry->addPrimitiveSet(elements.get()); @@ -463,31 +470,31 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 if (fabsf(e00-e11)push_back(i01); - elements->push_back(i00); - elements->push_back(i11); + elements->addElement(i01); + elements->addElement(i00); + elements->addElement(i11); - elements->push_back(i00); - elements->push_back(i10); - elements->push_back(i11); + elements->addElement(i00); + elements->addElement(i10); + elements->addElement(i11); } else { - elements->push_back(i01); - elements->push_back(i00); - elements->push_back(i10); + elements->addElement(i01); + elements->addElement(i00); + elements->addElement(i10); - elements->push_back(i01); - elements->push_back(i10); - elements->push_back(i11); + elements->addElement(i01); + elements->addElement(i10); + elements->addElement(i11); } } else if (numValid==3) { - if (i00>=0) elements->push_back(i00); - if (i01>=0) elements->push_back(i01); - if (i11>=0) elements->push_back(i11); - if (i10>=0) elements->push_back(i10); + if (i00>=0) elements->addElement(i00); + if (i01>=0) elements->addElement(i01); + if (i11>=0) elements->addElement(i11); + if (i10>=0) elements->addElement(i10); } } @@ -506,7 +513,9 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 if (createSkirt) { - osg::ref_ptr skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + osg::ref_ptr skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); // create bottom skirt vertices int r,c; @@ -528,24 +537,28 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 itr->second.first->push_back((*itr->second.first)[orig_i]); } - skirtDrawElements->push_back(orig_i); - skirtDrawElements->push_back(new_i); + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); } else { - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } } } - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } // create right skirt vertices @@ -566,24 +579,28 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 itr->second.first->push_back((*itr->second.first)[orig_i]); } - skirtDrawElements->push_back(orig_i); - skirtDrawElements->push_back(new_i); + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); } else { - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } } } - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } // create top skirt vertices @@ -604,24 +621,28 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 itr->second.first->push_back((*itr->second.first)[orig_i]); } - skirtDrawElements->push_back(orig_i); - skirtDrawElements->push_back(new_i); + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); } else { - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } } } - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } // create left skirt vertices @@ -642,12 +663,12 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 itr->second.first->push_back((*itr->second.first)[orig_i]); } - skirtDrawElements->push_back(orig_i); - skirtDrawElements->push_back(new_i); + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); } else { - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); @@ -656,10 +677,12 @@ void GeometryTechnique::generateGeometry(Locator* masterLocator, const osg::Vec3 } } - if (!skirtDrawElements->empty()) + if (skirtDrawElements->getNumIndices()!=0) { geometry->addPrimitiveSet(skirtDrawElements.get()); - skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); } } diff --git a/src/osgWrappers/osg/PrimitiveSet.cpp b/src/osgWrappers/osg/PrimitiveSet.cpp index 49e70f277..b99285283 100644 --- a/src/osgWrappers/osg/PrimitiveSet.cpp +++ b/src/osgWrappers/osg/PrimitiveSet.cpp @@ -197,6 +197,26 @@ BEGIN_ABSTRACT_OBJECT_REFLECTOR(osg::DrawElements) __void__releaseGLObjects__State_P1, "If State is non-zero, this function releases OpenGL objects for the specified graphics context. ", "Otherwise, releases OpenGL objects for all graphics contexts. "); + I_Method1(void, reserveElements, IN, unsigned int, numIndices, + Properties::PURE_VIRTUAL, + __void__reserveElements__unsigned_int, + "", + ""); + I_Method2(void, setElement, IN, unsigned, int, IN, unsigned, int, + Properties::PURE_VIRTUAL, + __void__setElement__unsigned__unsigned, + "", + ""); + I_Method1(unsigned int, getElement, IN, unsigned, int, + Properties::PURE_VIRTUAL, + __unsigned_int__getElement__unsigned, + "", + ""); + I_Method1(void, addElement, IN, unsigned, int, + Properties::PURE_VIRTUAL, + __void__addElement__unsigned, + "", + ""); I_SimpleProperty(osg::DrawElements *, DrawElements, __DrawElements_P1__getDrawElements, 0);