From 42f6d9e15b642c69ce7adb745a3304c8a2d7a526 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Apr 2015 20:07:18 +0200 Subject: [PATCH] Port video player --- CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwrender/sky.cpp | 13 +-- apps/openmw/mwsound/movieaudiofactory.cpp | 5 +- .../ogre-ffmpeg-videoplayer/videoplayer.cpp | 25 ++--- .../ogre-ffmpeg-videoplayer/videoplayer.hpp | 18 +++- extern/ogre-ffmpeg-videoplayer/videostate.cpp | 100 +++++++++--------- extern/ogre-ffmpeg-videoplayer/videostate.hpp | 22 ++-- 9 files changed, 104 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08d5403337..c5319d6006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -549,7 +549,7 @@ include_directories(libs) add_subdirectory(libs/openengine) # Extern -#add_subdirectory (extern/ogre-ffmpeg-videoplayer) +add_subdirectory (extern/ogre-ffmpeg-videoplayer) add_subdirectory (extern/oics) #add_subdirectory (extern/sdl4ogre) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 09f2e08fa8..46eb794622 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -59,8 +59,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwsound - soundmanagerimp openal_output ffmpeg_decoder sound sound_decoder sound_output loudness libavwrapper -# movieaudiofactory + soundmanagerimp openal_output ffmpeg_decoder sound sound_decoder sound_output loudness libavwrapper movieaudiofactory ) add_openmw_dir (mwworld @@ -138,6 +137,7 @@ target_link_libraries(openmw ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} ${MYGUI_PLATFORM_LIBRARIES} + "ogre-ffmpeg-videoplayer" "oics" components ) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index becd563b96..412d10ba2e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -167,6 +167,7 @@ namespace MWRender { mObjects->update(dt); mEffectManager->update(dt); + mSky->update(dt); } void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f843f9a43f..f4028caa76 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -599,8 +599,8 @@ void SkyManager::update(float duration) mCloudAnimationTimer += duration * mCloudSpeed; /// \todo improve this - //mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); - //mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); + mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); + mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); //mSecunda->setColour ( mMoonRed ? fallback->getFallbackColour("Moons_Script_Color") : ColourValue(1,1,1,1)); //mMasser->setColour (ColourValue(1,1,1,1)); @@ -684,13 +684,8 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) DisableCullingVisitor visitor; mParticleEffect->accept(visitor); - /* - for (size_t i = 0; i < mParticle->mControllers.size(); ++i) - { - if (mParticle->mControllers[i].getSource().isNull()) - mParticle->mControllers[i].setSource(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); - } - */ + SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr(new SceneUtil::FrameTimeSource)); + mParticleEffect->accept(assignVisitor); } } diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 468f8c82c7..198a66c533 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -17,7 +17,8 @@ namespace MWSound { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) - : mDecoder(decoder) + : Sound_Decoder(NULL) + , mDecoder(decoder) { } @@ -51,7 +52,7 @@ namespace MWSound std::string getStreamName() { - return mVideoState->stream->getName(); + return std::string(); } private: diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp index c2daf35795..8dedeaf648 100644 --- a/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp +++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.cpp @@ -1,5 +1,7 @@ #include "videoplayer.hpp" +#include + #include "audiofactory.hpp" #include "videostate.hpp" @@ -23,7 +25,7 @@ void VideoPlayer::setAudioFactory(MovieAudioFactory *factory) mAudioFactory.reset(factory); } -void VideoPlayer::playVideo(const std::string &resourceName) +void VideoPlayer::playVideo(boost::shared_ptr inputstream) { if(mState) close(); @@ -31,10 +33,10 @@ void VideoPlayer::playVideo(const std::string &resourceName) try { mState = new VideoState; mState->setAudioFactory(mAudioFactory.get()); - mState->init(resourceName); + mState->init(inputstream); // wait until we have the first picture - while (mState->video_st && mState->mTexture.isNull()) + while (mState->video_st && !mState->mTexture.get()) { if (!mState->update()) break; @@ -53,27 +55,26 @@ bool VideoPlayer::update () return false; } -std::string VideoPlayer::getTextureName() +osg::ref_ptr VideoPlayer::getVideoTexture() { - std::string name; - if (mState && !mState->mTexture.isNull()) - name = mState->mTexture->getName(); - return name; + if (mState) + return mState->mTexture; + return osg::ref_ptr(); } int VideoPlayer::getVideoWidth() { int width=0; - if (mState && !mState->mTexture.isNull()) - width = mState->mTexture->getWidth(); + if (mState && mState->mTexture.get()) + width = mState->mTexture->getTextureWidth(); return width; } int VideoPlayer::getVideoHeight() { int height=0; - if (mState && !mState->mTexture.isNull()) - height = mState->mTexture->getHeight(); + if (mState && mState->mTexture.get()) + height = mState->mTexture->getTextureHeight(); return height; } diff --git a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp index 2727ac6f08..073dc6be22 100644 --- a/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp +++ b/extern/ogre-ffmpeg-videoplayer/videoplayer.hpp @@ -4,6 +4,17 @@ #include #include +#include + +#include + +#include + +namespace osg +{ + class Texture2D; +} + namespace Video { @@ -30,7 +41,7 @@ namespace Video /// Play the given video. If a video is already playing, the old video is closed first. /// @note The video will be unpaused by default. Use the pause() and play() methods to control pausing. - void playVideo (const std::string& resourceName); + void playVideo (boost::shared_ptr inputstream); /// Get the current playback time position in the video, in seconds double getCurrentTime(); @@ -52,8 +63,9 @@ namespace Video /// Stop the currently playing video, if a video is playing. void close(); - /// Return the texture name of the currently playing video, or "" if no video is playing. - std::string getTextureName(); + /// Return the texture of the currently playing video, or a null pointer if no video is playing. + osg::ref_ptr getVideoTexture(); + /// Return the width of the currently playing video, or 0 if no video is playing. int getVideoWidth(); /// Return the height of the currently playing video, or 0 if no video is playing. diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.cpp b/extern/ogre-ffmpeg-videoplayer/videostate.cpp index 66c7c2ad50..e95a0ca7bb 100644 --- a/extern/ogre-ffmpeg-videoplayer/videostate.cpp +++ b/extern/ogre-ffmpeg-videoplayer/videostate.cpp @@ -11,6 +11,8 @@ #include #include +#include + extern "C" { #include @@ -172,12 +174,14 @@ void PacketQueue::clear() this->mutex.unlock (); } -int VideoState::OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) +int VideoState::istream_read(void *user_data, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; try { - return stream->read(buf, buf_size); + std::istream& stream = *static_cast(user_data)->stream; + stream.read((char*)buf, buf_size); + stream.clear(); + return stream.gcount(); } catch (std::exception& ) { @@ -185,57 +189,57 @@ int VideoState::OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) } } -int VideoState::OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) +int VideoState::istream_write(void *, uint8_t *, int) { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - try - { - return stream->write(buf, buf_size); - } - catch (std::exception& ) - { - return 0; - } + throw std::runtime_error("can't write to read-only stream"); } -int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whence) +int64_t VideoState::istream_seek(void *user_data, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + std::istream& stream = *static_cast(user_data)->stream; whence &= ~AVSEEK_FORCE; + if(whence == AVSEEK_SIZE) - return stream->size(); + { + size_t prev = stream.tellg(); + stream.seekg(0, std::ios_base::end); + size_t size = stream.tellg(); + stream.seekg(prev, std::ios_base::beg); + return size; + } + if(whence == SEEK_SET) - stream->seek(static_cast(offset)); + stream.seekg(offset, std::ios_base::beg); else if(whence == SEEK_CUR) - stream->seek(static_cast(stream->tell()+offset)); + stream.seekg(offset, std::ios_base::cur); else if(whence == SEEK_END) - stream->seek(static_cast(stream->size() + offset)); + stream.seekg(offset, std::ios_base::end); else return -1; - return stream->tell(); + return stream.tellg(); } void VideoState::video_display(VideoPicture *vp) { if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) { - if (mTexture.isNull()) + if (!mTexture.get()) { - static int i = 0; - mTexture = Ogre::TextureManager::getSingleton().createManual( - "ffmpeg/VideoTexture" + Ogre::StringConverter::toString(++i), - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - (*this->video_st)->codec->width, (*this->video_st)->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + mTexture = new osg::Texture2D; + mTexture->setDataVariance(osg::Object::DYNAMIC); + mTexture->setResizeNonPowerOfTwoHint(false); + mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); } - Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); - Ogre::HardwarePixelBufferSharedPtr buffer = mTexture->getBuffer(); - buffer->blitFromMemory(pb); + + osg::ref_ptr image = new osg::Image; + + image->setImage((*this->video_st)->codec->width, (*this->video_st)->codec->height, + 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, &vp->data[0], osg::Image::NO_DELETE); + + mTexture->setImage(image); } } @@ -250,7 +254,7 @@ void VideoState::video_refresh() VideoPicture* vp = &this->pictq[this->pictq_rindex]; this->video_display(vp); - this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; + this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; this->frame_last_pts = vp->pts; this->pictq_size--; this->pictq_cond.notify_one(); @@ -267,12 +271,12 @@ void VideoState::video_refresh() for (; ipictq_size-1; ++i) { if (this->pictq[pictq_rindex].pts + threshold <= this->get_master_clock()) - this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; // not enough time to show this picture + this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; // not enough time to show this picture else break; } - assert (this->pictq_rindex < VIDEO_PICTURE_QUEUE_SIZE); + assert (this->pictq_rindex < VIDEO_PICTURE_ARRAY_SIZE); VideoPicture* vp = &this->pictq[this->pictq_rindex]; this->video_display(vp); @@ -282,7 +286,7 @@ void VideoState::video_refresh() this->pictq_size -= i; // update queue for next picture this->pictq_size--; - this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; + this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; this->pictq_cond.notify_one(); } } @@ -328,7 +332,7 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) 0, (*this->video_st)->codec->height, &dst, this->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready - this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; + this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE; this->pictq_size++; this->pictq_mutex.unlock(); @@ -605,7 +609,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) return 0; } -void VideoState::init(const std::string& resourceName) +void VideoState::init(boost::shared_ptr inputstream) { int video_index = -1; int audio_index = -1; @@ -614,17 +618,19 @@ void VideoState::init(const std::string& resourceName) this->av_sync_type = AV_SYNC_DEFAULT; this->mQuit = false; - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) + this->stream = inputstream; + if(!this->stream.get()) throw std::runtime_error("Failed to open video resource"); - AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, istream_read, istream_write, istream_seek); if(!ioCtx) throw std::runtime_error("Failed to allocate AVIOContext"); this->format_ctx = avformat_alloc_context(); if(this->format_ctx) this->format_ctx->pb = ioCtx; + std::string videoName; + // Open video file /// /// format_ctx->pb->buffer must be freed by hand, @@ -632,7 +638,7 @@ void VideoState::init(const std::string& resourceName) /// /// https://trac.ffmpeg.org/ticket/1357 /// - if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + if(!this->format_ctx || avformat_open_input(&this->format_ctx, videoName.c_str(), NULL, NULL)) { if (this->format_ctx != NULL) { @@ -656,7 +662,7 @@ void VideoState::init(const std::string& resourceName) throw std::runtime_error("Failed to retrieve stream information"); // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + av_dump_format(this->format_ctx, 0, videoName.c_str(), 0); for(i = 0;i < this->format_ctx->nb_streams;i++) { @@ -724,11 +730,7 @@ void VideoState::deinit() avformat_close_input(&this->format_ctx); } - if (!mTexture.isNull()) - { - Ogre::TextureManager::getSingleton().remove(mTexture->getName()); - mTexture.setNull(); - } + mTexture = NULL; } double VideoState::get_external_clock() diff --git a/extern/ogre-ffmpeg-videoplayer/videostate.hpp b/extern/ogre-ffmpeg-videoplayer/videostate.hpp index cdeb2d0e39..40925e0141 100644 --- a/extern/ogre-ffmpeg-videoplayer/videostate.hpp +++ b/extern/ogre-ffmpeg-videoplayer/videostate.hpp @@ -3,11 +3,17 @@ #include -#include +#include +namespace osg +{ + class Texture2D; +} #include "videodefs.hpp" #define VIDEO_PICTURE_QUEUE_SIZE 50 +// allocate one extra to make sure we do not overwrite the osg::Image currently set on the texture +#define VIDEO_PICTURE_ARRAY_SIZE (VIDEO_PICTURE_QUEUE_SIZE+1) extern "C" { @@ -78,7 +84,7 @@ struct VideoState { void setAudioFactory(MovieAudioFactory* factory); - void init(const std::string& resourceName); + void init(boost::shared_ptr inputstream); void deinit(); void setPaused(bool isPaused); @@ -104,18 +110,18 @@ struct VideoState { double get_external_clock(); double get_master_clock(); - static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); - static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); - static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); + static int istream_read(void *user_data, uint8_t *buf, int buf_size); + static int istream_write(void *user_data, uint8_t *buf, int buf_size); + static int64_t istream_seek(void *user_data, int64_t offset, int whence); - Ogre::TexturePtr mTexture; + osg::ref_ptr mTexture; MovieAudioFactory* mAudioFactory; boost::shared_ptr mAudioDecoder; ExternalClock mExternalClock; - Ogre::DataStreamPtr stream; + boost::shared_ptr stream; AVFormatContext* format_ctx; int av_sync_type; @@ -130,7 +136,7 @@ struct VideoState { double video_clock; ///