#include "FFmpegImageStream.hpp" #include "FFmpegAudioStream.hpp" #include #include #include namespace osgFFmpeg { FFmpegImageStream::FFmpegImageStream() : m_decoder(0), m_commands(0), m_frame_published_flag(false) { setOrigin(osg::Image::TOP_LEFT); std::auto_ptr decoder(new FFmpegDecoder); std::auto_ptr 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::notify(osg::INFO)<<"Destructing FFmpegImageStream..."<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(m_decoder->video_decoder().image()), NO_DELETE ); setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio()); m_decoder->video_decoder().setUserData(this); m_decoder->video_decoder().setPublishCallback(publishNewFrame); if (m_decoder->audio_decoder().validContext()) { osg::notify(osg::NOTICE)<<"Attaching FFmpegAudioStream"<push(CMD_PLAY); #if 0 // Wait for at least one frame to be published before returning the call OpenThreads::ScopedLock 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::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); } double FFmpegImageStream::getLength() const { return m_decoder->duration(); } 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::notify(osg::WARN) << "FFmpegImageStream::run : " << error.what() << std::endl; } catch (...) { osg::notify(osg::WARN) << "FFmpegImageStream::run : unhandled exception" << std::endl; } osg::notify(osg::NOTICE)<<"Finished FFmpegImageStream::run()"<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_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(); } _status = PLAYING; } void FFmpegImageStream::cmdPause() { if (_status == PLAYING) { } _status = PAUSED; } void FFmpegImageStream::cmdRewind() { m_decoder->rewind(); } void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data) { FFmpegImageStream * const this_ = reinterpret_cast(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(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 lock(this_->m_mutex); if (! this_->m_frame_published_flag) { this_->m_frame_published_flag = true; this_->m_frame_published_cond.signal(); } } } // namespace osgFFmpeg