Add a way to retrieve the creation time for MPEG-4 files.
---
include/osg/ImageStream | 2 +-
src/osgPlugins/ffmpeg/FFmpegDecoder.hpp | 8 ++++++++
src/osgPlugins/ffmpeg/FFmpegImageStream.cpp | 5 +++++
src/osgPlugins/ffmpeg/FFmpegImageStream.hpp | 1 +
4 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/include/osg/ImageStream b/include/osg/ImageStream
index 2749cd2..4b4a6d5 100644
--- a/include/osg/ImageStream
+++ b/include/osg/ImageStream
@@ -76,7 +76,7 @@ class OSG_EXPORT ImageStream : public Image
LoopingMode getLoopingMode() const { return _loopingMode; }
-
+ virtual double getCreationTime() const { return HUGE_VAL; }
virtual double getLength() const { return 0.0; }
virtual double getFrameRate() const { return 0.0; }
diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp
b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp
index 6176dcc..a783f01 100644
--- a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp
+++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp
@@ -26,6 +26,8 @@ class FormatContextPtr
T* get() { return _ptr; }
+ operator bool() const { return _ptr != 0; }
+
T * operator-> () const // never throws
{
return _ptr;
@@ -74,6 +76,7 @@ public:
void loop(bool loop);
bool loop() const;
+ double creation_time() const;
double duration() const;
double reference();
@@ -144,6 +147,11 @@ inline bool FFmpegDecoder::loop() const
return m_loop;
}
+inline double FFmpegDecoder::creation_time() const
+{
+ if(m_format_context) return m_format_context->timestamp;
+ else return HUGE_VAL;
+}
inline double FFmpegDecoder::duration() const
{
diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp
b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp
index e14fbd9..bd25ef8 100644
--- a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp
+++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp
@@ -162,6 +162,11 @@ float FFmpegImageStream::getVolume() const
return m_decoder->audio_decoder().getVolume();
}
+double FFmpegImageStream::getCreationTime() const
+{
+ return m_decoder->creation_time();
+}
+
double FFmpegImageStream::getLength() const
{
return m_decoder->duration();
diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp
b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp
index 033a892..4395a58 100644
--- a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp
+++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp
@@ -36,6 +36,7 @@ namespace osgFFmpeg
virtual void setVolume(float volume);
virtual float getVolume() const;
+ virtual double getCreationTime() const;
virtual double getLength() const;
virtual double getReferenceTime () const;
virtual double getFrameRate() const;
--
1.7.0
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 <osg/Image>
#include <osg/AudioStream>
namespace osg {
/**
* Image Stream class.
*/
class OSG_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<const ImageStream*>(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
{
INVALID,
PLAYING,
PAUSED,
REWINDING
};
virtual void seek(double /*time*/) {}
virtual void play() { _status=PLAYING; }
virtual void pause() { _status=PAUSED; }
virtual void rewind() { _status=REWINDING; }
virtual void quit(bool /*waitForThreadToExit*/ = true) {}
StreamStatus getStatus() { return _status; }
enum LoopingMode
{
NO_LOOPING,
LOOPING
};
void setLoopingMode(LoopingMode mode)
{
if (_loopingMode == mode) return;
_loopingMode = mode;
applyLoopingMode();
}
LoopingMode getLoopingMode() const { return _loopingMode; }
virtual double getCreationTime() const { return HUGE_VAL; }
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; }
virtual void setTimeMultiplier(double) {}
virtual double getTimeMultiplier() const { return 0.0; }
virtual void setVolume(float) {}
virtual float getVolume() const { return 0.0f; }
typedef std::vector< osg::ref_ptr<osg::AudioStream> > AudioStreams;
void setAudioStreams(const AudioStreams& asl) { _audioStreams = asl; }
AudioStreams& getAudioStreams() { return _audioStreams; }
const AudioStreams& getAudioStreams() const { return _audioStreams; }
protected:
virtual void applyLoopingMode() {}
virtual ~ImageStream() {}
StreamStatus _status;
LoopingMode _loopingMode;
AudioStreams _audioStreams;
};
} // namespace
#endif
#ifndef HEADER_GUARD_OSGFFMPEG_FFMPEG_DECODER_H
#define HEADER_GUARD_OSGFFMPEG_FFMPEG_DECODER_H
#include "FFmpegDecoderAudio.hpp"
#include "FFmpegDecoderVideo.hpp"
#include <osg/Notify>
namespace osgFFmpeg {
class FormatContextPtr
{
public:
typedef AVFormatContext T;
explicit FormatContextPtr() : _ptr(0) {}
explicit FormatContextPtr(T* ptr) : _ptr(ptr) {}
~FormatContextPtr()
{
cleanup();
}
T* get() { return _ptr; }
operator bool() const { return _ptr != 0; }
T * operator-> () const // never throws
{
return _ptr;
}
void reset(T* ptr)
{
if (ptr==_ptr) return;
cleanup();
_ptr = ptr;
}
void cleanup()
{
if (_ptr)
{
OSG_NOTICE<<"Calling av_close_input_file("<<_ptr<<")"<<std::endl;
av_close_input_file(_ptr);
}
_ptr = 0;
}
protected:
T* _ptr;
};
class FFmpegDecoder : public osg::Referenced
{
public:
FFmpegDecoder();
~FFmpegDecoder();
bool open(const std::string & filename);
void close(bool waitForThreadToExit);
bool readNextPacket();
void rewind();
void seek(double time);
void pause();
void loop(bool loop);
bool loop() const;
double creation_time() const;
double duration() const;
double reference();
FFmpegDecoderAudio & audio_decoder();
FFmpegDecoderVideo & video_decoder();
FFmpegDecoderAudio const & audio_decoder() const;
FFmpegDecoderVideo const & video_decoder() const;
protected:
enum State
{
NORMAL,
PAUSE,
END_OF_STREAM,
REWINDING,
SEEKING
};
typedef BoundedMessageQueue<FFmpegPacket> PacketQueue;
void findAudioStream();
void findVideoStream();
void flushAudioQueue();
void flushVideoQueue();
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;
int m_audio_index;
int m_video_index;
FFmpegClocks m_clocks;
FFmpegPacket m_pending_packet;
PacketQueue m_audio_queue;
PacketQueue m_video_queue;
FFmpegDecoderAudio m_audio_decoder;
FFmpegDecoderVideo m_video_decoder;
double m_duration;
double m_start;
State m_state;
bool m_loop;
};
inline void FFmpegDecoder::loop(const bool loop)
{
m_loop = loop;
}
inline bool FFmpegDecoder::loop() const
{
return m_loop;
}
inline double FFmpegDecoder::creation_time() const
{
if(m_format_context) return m_format_context->timestamp;
else return HUGE_VAL;
}
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()
{
return m_audio_decoder;
}
inline FFmpegDecoderVideo & FFmpegDecoder::video_decoder()
{
return m_video_decoder;
}
inline FFmpegDecoderAudio const & FFmpegDecoder::audio_decoder() const
{
return m_audio_decoder;
}
inline FFmpegDecoderVideo const & FFmpegDecoder::video_decoder() const
{
return m_video_decoder;
}
} // namespace osgFFmpeg
#endif // HEADER_GUARD_OSGFFMPEG_FFMPEG_DECODER_H
#ifndef HEADER_GUARD_OSGFFMPEG_FFMPEG_IMAGE_STREAM_H
#define HEADER_GUARD_OSGFFMPEG_FFMPEG_IMAGE_STREAM_H
#include "FFmpegDecoder.hpp"
#include "MessageQueue.hpp"
#include <osg/ImageStream>
#include <OpenThreads/Condition>
#include <OpenThreads/Thread>
namespace osgFFmpeg
{
template <class T>
class MessageQueue;
class FFmpegImageStream : public osg::ImageStream, public OpenThreads::Thread
{
public:
FFmpegImageStream();
FFmpegImageStream(const FFmpegImageStream & image, const osg::CopyOp & copyop = osg::CopyOp::SHALLOW_COPY);
META_Object(osgFFmpeg, FFmpegImageStream);
bool open(const std::string & filename);
virtual void play();
virtual void pause();
virtual void rewind();
virtual void seek(double time);
virtual void quit(bool waitForThreadToExit = true);
virtual void setVolume(float volume);
virtual float getVolume() const;
virtual double getCreationTime() const;
virtual double getLength() const;
virtual double getReferenceTime () const;
virtual double getFrameRate() const;
virtual bool isImageTranslucent() const;
private:
enum Command
{
CMD_PLAY,
CMD_PAUSE,
CMD_STOP,
CMD_REWIND,
CMD_SEEK
};
typedef MessageQueue<Command> CommandQueue;
typedef OpenThreads::Mutex Mutex;
typedef OpenThreads::Condition Condition;
virtual ~FFmpegImageStream();
virtual void run();
virtual void applyLoopingMode();
bool handleCommand(Command cmd);
void cmdPlay();
void cmdPause();
void cmdRewind();
void cmdSeek(double time);
static void publishNewFrame(const FFmpegDecoderVideo &, void * user_data);
osg::ref_ptr<FFmpegDecoder> m_decoder;
CommandQueue * m_commands;
Mutex m_mutex;
Condition m_frame_published_cond;
bool m_frame_published_flag;
double m_seek_time;
};
}
#endif // HEADER_GUARD_OSGFFMPEG_FFMPEG_IMAGE_STREAM_H
#include "FFmpegImageStream.hpp"
#include "FFmpegAudioStream.hpp"
#include <OpenThreads/ScopedLock>
#include <osg/Notify>
#include <memory>
namespace osgFFmpeg {
FFmpegImageStream::FFmpegImageStream() :
m_decoder(0),
m_commands(0),
m_frame_published_flag(false)
{
setOrigin(osg::Image::TOP_LEFT);
std::auto_ptr<FFmpegDecoder> decoder(new FFmpegDecoder);
std::auto_ptr<CommandQueue> commands(new CommandQueue);
m_decoder = decoder.release();
m_commands = commands.release();
}
FFmpegImageStream::FFmpegImageStream(const FFmpegImageStream & image, const osg::CopyOp & copyop) :
osg::ImageStream(image, copyop)
{
// TODO: probably incorrect or incomplete
}
FFmpegImageStream::~FFmpegImageStream()
{
OSG_INFO<<"Destructing FFmpegImageStream..."<<std::endl;
quit(true);
OSG_INFO<<"Have done quit"<<std::endl;
// release athe audio streams to make sure that the decoder doesn't retain any external
// refences.
getAudioStreams().clear();
// destroy the decoder and associated threads
m_decoder = 0;
delete m_commands;
OSG_INFO<<"Destructed FFMpegImageStream."<<std::endl;
}
bool FFmpegImageStream::open(const std::string & filename)
{
setFileName(filename);
if (! m_decoder->open(filename))
return false;
setImage(
m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
const_cast<unsigned char *>(m_decoder->video_decoder().image()), NO_DELETE
);
setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio());
OSG_NOTICE<<"ffmpeg::open("<<filename<<") size("<<s()<<", "<<t()<<") aspect ratio "<<m_decoder->video_decoder().pixelAspectRatio()<<std::endl;
#if 1
// swscale is reported errors and then crashing when rescaling video of size less than 10 by 10.
if (s()<=10 || t()<=10) return false;
#endif
m_decoder->video_decoder().setUserData(this);
m_decoder->video_decoder().setPublishCallback(publishNewFrame);
if (m_decoder->audio_decoder().validContext())
{
OSG_NOTICE<<"Attaching FFmpegAudioStream"<<std::endl;
getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
}
_status = PAUSED;
applyLoopingMode();
start(); // start thread
return true;
}
void FFmpegImageStream::play()
{
m_commands->push(CMD_PLAY);
#if 0
// Wait for at least one frame to be published before returning the call
OpenThreads::ScopedLock<Mutex> lock(m_mutex);
while (duration() > 0 && ! m_frame_published_flag)
m_frame_published_cond.wait(&m_mutex);
#endif
}
void FFmpegImageStream::pause()
{
m_commands->push(CMD_PAUSE);
}
void FFmpegImageStream::rewind()
{
m_commands->push(CMD_REWIND);
}
void FFmpegImageStream::seek(double time) {
m_seek_time = time;
m_commands->push(CMD_SEEK);
}
void FFmpegImageStream::quit(bool waitForThreadToExit)
{
// Stop the packet producer thread
if (isRunning())
{
m_commands->push(CMD_STOP);
if (waitForThreadToExit)
join();
}
// Close the decoder (i.e. flush the decoder packet queues)
m_decoder->close(waitForThreadToExit);
}
void FFmpegImageStream::setVolume(float volume)
{
m_decoder->audio_decoder().setVolume(volume);
}
float FFmpegImageStream::getVolume() const
{
return m_decoder->audio_decoder().getVolume();
}
double FFmpegImageStream::getCreationTime() const
{
return m_decoder->creation_time();
}
double FFmpegImageStream::getLength() const
{
return m_decoder->duration();
}
double FFmpegImageStream::getReferenceTime () const
{
return m_decoder->reference();
}
double FFmpegImageStream::getFrameRate() const
{
return m_decoder->video_decoder().frameRate();
}
bool FFmpegImageStream::isImageTranslucent() const
{
return m_decoder->video_decoder().alphaChannel();
}
void FFmpegImageStream::run()
{
try
{
bool done = false;
while (! done)
{
if (_status == PLAYING)
{
bool no_cmd;
const Command cmd = m_commands->timedPop(no_cmd, 1);
if (no_cmd)
{
m_decoder->readNextPacket();
}
else
done = ! handleCommand(cmd);
}
else
{
done = ! handleCommand(m_commands->pop());
}
}
}
catch (const std::exception & error)
{
OSG_WARN << "FFmpegImageStream::run : " << error.what() << std::endl;
}
catch (...)
{
OSG_WARN << "FFmpegImageStream::run : unhandled exception" << std::endl;
}
OSG_NOTICE<<"Finished FFmpegImageStream::run()"<<std::endl;
}
void FFmpegImageStream::applyLoopingMode()
{
m_decoder->loop(getLoopingMode() == LOOPING);
}
bool FFmpegImageStream::handleCommand(const Command cmd)
{
switch (cmd)
{
case CMD_PLAY:
cmdPlay();
return true;
case CMD_PAUSE:
cmdPause();
return true;
case CMD_REWIND:
cmdRewind();
return true;
case CMD_SEEK:
cmdSeek(m_seek_time);
return true;
case CMD_STOP:
return false;
default:
assert(false);
return false;
}
}
void FFmpegImageStream::cmdPlay()
{
if (_status == PAUSED)
{
if (! m_decoder->audio_decoder().isRunning())
m_decoder->audio_decoder().start();
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;
}
void FFmpegImageStream::cmdPause()
{
if (_status == PLAYING)
{
m_decoder->video_decoder().pause(true);
m_decoder->audio_decoder().pause(true);
}
_status = PAUSED;
}
void FFmpegImageStream::cmdRewind()
{
m_decoder->rewind();
}
void FFmpegImageStream::cmdSeek(double time)
{
m_decoder->seek(time);
}
void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
{
FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);
#if 1
this_->setImage(
this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE,
const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
);
#else
/** \bug If viewer.realize() hasn't been already called, this doesn't work? */
this_->dirty();
#endif
OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);
if (! this_->m_frame_published_flag)
{
this_->m_frame_published_flag = true;
this_->m_frame_published_cond.signal();
}
}
} // namespace osgFFmpeg
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org