From 73c69e8eda4ade55ea95c89a9062340376918af4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 02:35:50 +0200 Subject: [PATCH 001/147] video playback --- apps/openmw/CMakeLists.txt | 7 +- apps/openmw/mwbase/world.hpp | 4 + apps/openmw/mwgui/mode.hpp | 4 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 + apps/openmw/mwrender/renderingmanager.cpp | 11 + apps/openmw/mwrender/renderingmanager.hpp | 5 + apps/openmw/mwrender/videoplayer.cpp | 395 ++++++++++++++++++++++ apps/openmw/mwrender/videoplayer.hpp | 105 ++++++ apps/openmw/mwscript/docs/vmformat.txt | 3 +- apps/openmw/mwscript/miscextensions.cpp | 16 + apps/openmw/mwworld/worldimp.cpp | 5 + apps/openmw/mwworld/worldimp.hpp | 3 + files/gbuffer/gbuffer.compositor | 2 +- 13 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 apps/openmw/mwrender/videoplayer.cpp create mode 100644 apps/openmw/mwrender/videoplayer.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 66844b2806..da1b3e15df 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors characterpreview externalrendering globalmap + compositors characterpreview externalrendering globalmap videoplayer ) add_openmw_dir (mwinput @@ -108,6 +108,11 @@ target_link_libraries(openmw components ) +if (USE_FFMPEG) + target_link_libraries(openmw + "swscale") +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 521bbb988e..0ba4b84dd6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -291,6 +291,10 @@ namespace MWBase /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + + + /// \todo this does not belong here + virtual void playVideo(const std::string& name) = 0; }; } diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 64aa1dc216..74ecd531ad 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -41,7 +41,9 @@ namespace MWGui GM_Loading, GM_LoadingWallpaper, - GM_QuickKeysMenu + GM_QuickKeysMenu, + + GM_Video }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5a04a90c0f..6ff4cee071 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -371,6 +371,9 @@ void WindowManager::updateVisible() case GM_Loading: MyGUI::PointerManager::getInstance().setVisible(false); break; + case GM_Video: + mHud->setVisible(false); + break; default: // Unsupported mode, switch back to game break; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f0833e82df..98fc4175ec 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -36,6 +36,7 @@ #include "npcanimation.hpp" #include "externalrendering.hpp" #include "globalmap.hpp" +#include "videoplayer.hpp" using namespace MWRender; using namespace Ogre; @@ -159,6 +160,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); + mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mSun = 0; mDebugging = new Debugging(mMwRoot, engine); @@ -180,6 +183,7 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; + delete mVideoPlayer; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -339,6 +343,8 @@ void RenderingManager::update (float duration) mRendering.update(duration); + mVideoPlayer->update (); + MWWorld::RefData &data = MWBase::Environment::get() .getWorld() @@ -898,4 +904,9 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } +void RenderingManager::playVideo(const std::string& name) +{ + mVideoPlayer->play ("video/" + name); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 359809b71d..d0ef3f593c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -45,6 +45,7 @@ namespace MWRender class Compositors; class ExternalRendering; class GlobalMap; + class VideoPlayer; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -195,6 +196,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); + void playVideo(const std::string& name); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); @@ -248,6 +251,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Shadows* mShadows; MWRender::Compositors* mCompositors; + + VideoPlayer* mVideoPlayer; }; } diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp new file mode 100644 index 0000000000..04a6de30b0 --- /dev/null +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -0,0 +1,395 @@ +#include "videoplayer.hpp" + +//#ifdef OPENMW_USE_FFMPEG + +#include +#include +#include +#include + + +extern "C" +{ +#include +#include +#include +} + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + + +namespace MWRender +{ + + int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + int num_read = stream->size() - stream->tell(); + + if (num_read > buf_size) + num_read = buf_size; + + stream->read(buf, num_read); + return num_read; + } + + int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + int num_write = stream->size() - stream->tell(); + + if (num_write > buf_size) + num_write = buf_size; + + stream->write (buf, num_write); + return num_write; + } + + int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + switch (whence) + { + case SEEK_SET: + stream->seek(offset); + case SEEK_CUR: + stream->seek(stream->tell() + offset); + case SEEK_END: + stream->seek(stream->size() + offset); + case AVSEEK_SIZE: + return stream->size(); + default: + return -1; + } + + return stream->tell(); + } + + VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) + : mAvContext(NULL) + , mVideoStreamId(-1) + { + Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); + videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); + + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("VideoMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + // Attach background to the scene + Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags (0x1); + } + + VideoPlayer::~VideoPlayer() + { + if (mAvContext) + deleteContext(); + + delete mRectangle; + } + + void VideoPlayer::play (const std::string& resourceName) + { + mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName); + + + mVideoStreamId = -1; + mEOF = false; + + // if something is already playing, close it + if (mAvContext) + close(); + + mRectangle->setVisible(true); + + MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + + + // BASIC INITIALIZATION + + // Load all the decoders + av_register_all(); + + AVIOContext *ioContext = 0; + + int err = 0; + + mAvContext = avformat_alloc_context(); + if (!mAvContext) + throwError(0); + + ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throwError(0); + + mAvContext->pb = ioContext; + + err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL); + if (err != 0) + throwError(err); + + err = avformat_find_stream_info(mAvContext, 0); + if (err < 0) + throwError(err); + + + + + // INIT VIDEO + + // Find the video stream among the different streams + for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + { + if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + mVideoStreamId = i; + break; + } + } + + if (-1 == mVideoStreamId) + throw std::runtime_error("No video stream found in the video"); + + // Get the video codec + mVideoCodecContext = mAvContext->streams[mVideoStreamId]->codec; + if (!mVideoCodecContext) + throw std::runtime_error("Stream doesn't have a codec"); + + // Get the video decoder + mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id); + if (NULL == mVideoCodec) + throw std::runtime_error("No decoder found"); + + // Load the video codec + err = avcodec_open2(mVideoCodecContext, mVideoCodec, 0); + if (err < 0) + throwError (err); + + // Create the frame buffers + mRawFrame = avcodec_alloc_frame(); + mRGBAFrame = avcodec_alloc_frame(); + if (!mRawFrame || !mRGBAFrame) + { + throw std::runtime_error("Can't allocate video frames"); + } + + + avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoCodecContext->width, mVideoCodecContext->height); + + + // Setup the image scaler + // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, + // but i'm not worried about performance just yet + mSwsContext = sws_getContext(mVideoCodecContext->width, mVideoCodecContext->height, + mVideoCodecContext->pix_fmt, + mVideoCodecContext->width, mVideoCodecContext->height, + PIX_FMT_RGBA, + SWS_BICUBIC, NULL, NULL, NULL); + if (!mSwsContext) + throw std::runtime_error("Can't create SWS Context"); + + // Get the frame time we need for this video + AVRational r = mAvContext->streams[mVideoStreamId]->avg_frame_rate; + AVRational r2 = mAvContext->streams[mVideoStreamId]->r_frame_rate; + if ((!r.num || !r.den) && + (!r2.num || !r2.den)) + { + std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl; + mWantedFrameTime = 1.f / 29.97f; + } + else + { + if (r.num && r.den) + mWantedFrameTime = 1.f/((float)r.num / r.den); + else + mWantedFrameTime = 1.f/((float)r2.num / r2.den); + } + + + mTextureUnit->setTextureName (""); + + if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) + Ogre::TextureManager::getSingleton().remove("VideoTexture"); + + mVideoTexture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + mVideoCodecContext->width, mVideoCodecContext->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DEFAULT); + + mTextureUnit->setTextureName ("VideoTexture"); + + } + + void VideoPlayer::throwError(int error) + { + char buffer[4096] = {0}; + + if (0 == av_strerror(error, buffer, sizeof(buffer))) + { + std::stringstream msg; + msg << "FFMPEG error: "; + msg << buffer << std::endl; + throw std::runtime_error(msg.str()); + } + else + throw std::runtime_error("Unknown FFMPEG error"); + } + + bool VideoPlayer::readFrameAndQueue () + { + bool ret = true; + AVPacket *pkt = NULL; + + // check we're not at eof + if (mEOF) + ret = false; + else + { + // read frame + pkt = (AVPacket *)av_malloc(sizeof(*pkt)); + int res = av_read_frame(mAvContext, pkt); + + // check we didn't reach eof right now + if (res < 0) + { + mEOF = true; + ret = false; + av_free(pkt); + } + else + { + // When a frame has been read, save it + if (!saveFrame(pkt)) + { + // we failed to save it, which means it was unknown type of frame - delete it here + av_free_packet(pkt); + av_free(pkt); + } + } + } + + return ret; + } + + bool VideoPlayer::saveFrame(AVPacket* frame) + { + bool saved = false; + + if (frame->stream_index == mVideoStreamId) + { + // If it was a video frame... + mVideoPacketQueue.push(frame); + saved = true; + } + + return saved; + + } + + void VideoPlayer::update() + { + if (!mAvContext) + return; + + + if (!mVideoPacketQueue.size() && mEOF) + close(); + + Ogre::Timer timer; + + if (!mVideoPacketQueue.size()) + { + if (readFrameAndQueue()) + decodeFrontFrame(); + } + else + decodeFrontFrame(); + + mDecodingTime = timer.getMilliseconds ()/1000.f; + } + + void VideoPlayer::decodeFrontFrame () + { + int didDecodeFrame = 0; + + // Make sure there is something to decode + if (!mVideoPacketQueue.size()) + return; + + // Get the front frame and decode it + AVPacket *videoPacket = mVideoPacketQueue.front(); + int res; + res = avcodec_decode_video2(mVideoCodecContext, mRawFrame, &didDecodeFrame, videoPacket); + + if (res < 0 || !didDecodeFrame) + throw std::runtime_error ("an error occured while decoding the video frame"); + + // Convert the frame to RGB + sws_scale(mSwsContext, + mRawFrame->data, mRawFrame->linesize, + 0, mVideoCodecContext->height, + mRGBAFrame->data, mRGBAFrame->linesize); + + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); + Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); + pixelBuffer->blitFromMemory(pb); + + //m_displayedFrameCount++; + av_free_packet(mVideoPacketQueue.front()); + av_free(mVideoPacketQueue.front()); + mVideoPacketQueue.pop(); + } + + void VideoPlayer::close () + { + mRectangle->setVisible (false); + MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + deleteContext(); + } + + void VideoPlayer::deleteContext() + { + while (mVideoPacketQueue.size()) + { + av_free_packet(mVideoPacketQueue.front()); + av_free(mVideoPacketQueue.front()); + mVideoPacketQueue.pop(); + } + + avcodec_close(mVideoCodecContext); + + avpicture_free((AVPicture *)mRGBAFrame); + + if (mRawFrame) + av_free(mRawFrame); + if (mRGBAFrame) + av_free(mRGBAFrame); + + sws_freeContext(mSwsContext); + + avformat_close_input(&mAvContext); + + mAvContext = NULL; + } +} + +//#endif diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp new file mode 100644 index 0000000000..05c04c96b4 --- /dev/null +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -0,0 +1,105 @@ +#ifndef MWRENDER_VIDEOPLAYER_H +#define MWRENDER_VIDEOPLAYER_H + +//#ifdef OPENMW_USE_FFMPEG + + + +#include + +#include +#include + +namespace Ogre +{ + class Rectangle2D; + class SceneManager; + class TextureUnitState; +} + +struct AVFormatContext; +struct AVCodecContext; +struct AVCodec; +struct AVFrame; +struct SwsContext; +struct AVPacket; + +namespace MWRender +{ + + class VideoPlayer + { + public: + VideoPlayer(Ogre::SceneManager* sceneMgr); + ~VideoPlayer(); + + void play (const std::string& resourceName); + + void update(); + + private: + Ogre::Rectangle2D* mRectangle; + Ogre::TextureUnitState* mTextureUnit; + + Ogre::DataStreamPtr mStream; + + Ogre::TexturePtr mVideoTexture; + + + private: + + AVFormatContext* mAvContext; + + + bool mEOF; + + // VIDEO + AVCodecContext* mVideoCodecContext; + AVCodec* mVideoCodec; + int mVideoStreamId; + AVFrame* mRawFrame; + AVFrame* mRGBAFrame; + SwsContext* mSwsContext; + float mWantedFrameTime; + float mDecodingTime; + std::queue mVideoPacketQueue; + + + bool readFrameAndQueue(); + bool saveFrame(AVPacket* frame); + + void decodeFrontFrame(); + + void close(); + void deleteContext(); + + + void throwError(int error); + }; + +} + +//#else +/* + + +// If FFMPEG not available, dummy implentation that does nothing + +namespace MWRender +{ + + class VideoPlayer + { + public: + VideoPlayer(Ogre::SceneManager* sceneMgr){} + + void play (const std::string& resourceName) {} + void update() {} + }; + +} +*/ +//#endif + + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a4a9e99fd8..1c74a713fd 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -205,5 +205,6 @@ op 0x200019e: PlaceAtMe Explicit op 0x200019f: GetPcSleep op 0x20001a0: ShowMap op 0x20001a1: FillMap -opcodes 0x20001a2-0x3ffffff unused +op 0x20001a2: PlayBink +opcodes 0x20001a3-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a869f882b1..330c1c79f1 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -21,6 +21,19 @@ namespace MWScript { namespace Misc { + class OpPlayBink : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWBase::Environment::get().getWorld ()->playVideo (name); + } + }; + class OpGetPcSleep : public Interpreter::Opcode0 { public: @@ -261,6 +274,7 @@ namespace MWScript const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; + const int opcodePlayBink = 0x20001a2; void registerExtensions (Compiler::Extensions& extensions) { @@ -286,6 +300,7 @@ namespace MWScript extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); + extensions.registerInstruction ("playbink", "S", opcodePlayBink); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -307,6 +322,7 @@ namespace MWScript interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (opcodePlayBink, new OpPlayBink); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 834dffe793..06b58c1836 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1281,4 +1281,9 @@ namespace MWWorld return 0; } + + void World::playVideo (const std::string &name) + { + mRendering->playVideo(name); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 90cd2151b6..5d8c689dbf 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -322,6 +322,9 @@ namespace MWWorld /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + + /// \todo this does not belong here + virtual void playVideo(const std::string& name); }; } diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor index 04600ce9b7..0a0675fa00 100644 --- a/files/gbuffer/gbuffer.compositor +++ b/files/gbuffer/gbuffer.compositor @@ -72,7 +72,7 @@ compositor gbufferFinalizer pass render_scene { first_render_queue 51 - last_render_queue 100 + last_render_queue 105 } } target_output From 05eb307bfbe6b15ac39f5eae564b69797ba7f849 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 02:54:29 +0200 Subject: [PATCH 002/147] added video timing --- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwrender/videoplayer.cpp | 14 +++++++++++++- apps/openmw/mwrender/videoplayer.hpp | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6ff4cee071..378003e394 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -372,6 +372,7 @@ void WindowManager::updateVisible() MyGUI::PointerManager::getInstance().setVisible(false); break; case GM_Video: + MyGUI::PointerManager::getInstance().setVisible(false); mHud->setVisible(false); break; default: diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 04a6de30b0..195369a2d8 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -109,6 +109,7 @@ namespace MWRender mVideoStreamId = -1; mEOF = false; + mDisplayedFrameCount = 0; // if something is already playing, close it if (mAvContext) @@ -236,6 +237,8 @@ namespace MWRender mTextureUnit->setTextureName ("VideoTexture"); + mTimer.reset(); + } void VideoPlayer::throwError(int error) @@ -309,6 +312,14 @@ namespace MWRender if (!mAvContext) return; + // Time elapsed since the video started + float realTime = mTimer.getMilliseconds ()/1000.f; + + // Here is the time we're at in the video + float movieTime = mDisplayedFrameCount * mWantedFrameTime; + + if (movieTime >= realTime) + return; if (!mVideoPacketQueue.size() && mEOF) close(); @@ -353,10 +364,11 @@ namespace MWRender Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); - //m_displayedFrameCount++; av_free_packet(mVideoPacketQueue.front()); av_free(mVideoPacketQueue.front()); mVideoPacketQueue.pop(); + + ++mDisplayedFrameCount; } void VideoPlayer::close () diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 05c04c96b4..1d3010e8e9 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -9,6 +9,7 @@ #include #include +#include namespace Ogre { @@ -53,6 +54,8 @@ namespace MWRender bool mEOF; + Ogre::Timer mTimer; + // VIDEO AVCodecContext* mVideoCodecContext; AVCodec* mVideoCodec; @@ -64,6 +67,8 @@ namespace MWRender float mDecodingTime; std::queue mVideoPacketQueue; + int mDisplayedFrameCount; + bool readFrameAndQueue(); bool saveFrame(AVPacket* frame); From d432420a324843b47a04bd181efa88d56544d98c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 1 Dec 2012 20:53:28 +0100 Subject: [PATCH 003/147] fix FindFFmpeg.cmake --- CMakeLists.txt | 5 +- apps/openmw/mwsound/ffmpeg_decoder.hpp | 4 +- cmake/FindFFMPEG.cmake | 105 ------------------ cmake/FindFFmpeg.cmake | 148 +++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 109 deletions(-) delete mode 100644 cmake/FindFFMPEG.cmake create mode 100644 cmake/FindFFmpeg.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 78388e20f4..fcb5e9bbee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,8 +140,9 @@ set(SOUND_INPUT_INCLUDES "") set(SOUND_INPUT_LIBRARY "") set(SOUND_DEFINE "") if (USE_FFMPEG) - find_package(FFMPEG REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR}) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) + find_package(FFmpeg REQUIRED) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) endif (USE_FFMPEG) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 7b028e1d0b..a6e80fc9b6 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -10,8 +10,8 @@ #include extern "C" { -#include -#include +#include +#include } #include "sound_decoder.hpp" diff --git a/cmake/FindFFMPEG.cmake b/cmake/FindFFMPEG.cmake deleted file mode 100644 index 2e755d047d..0000000000 --- a/cmake/FindFFMPEG.cmake +++ /dev/null @@ -1,105 +0,0 @@ -# Find the FFmpeg library -# -# Sets -# FFMPEG_FOUND. If false, don't try to use ffmpeg -# FFMPEG_INCLUDE_DIR -# FFMPEG_LIBRARIES -# -# Modified by Nicolay Korslund for OpenMW - -SET( FFMPEG_FOUND "NO" ) - -FIND_PATH( FFMPEG_general_INCLUDE_DIR libavcodec/avcodec.h libavformat/avformat.h - HINTS - PATHS - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavcodec - /usr/local/include/ffmpeg/libavcodec - /usr/include/libavcodec - /usr/local/include/libavcodec - ) - -FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h - HINTS - PATHS - ${FFMPEG_general_INCLUDE_DIR}/libavcodec - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavcodec - /usr/local/include/ffmpeg/libavcodec - /usr/include/libavcodec - /usr/local/include/libavcodec -) - -FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h - HINTS - PATHS - ${FFMPEG_general_INCLUDE_DIR}/libavformat - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavformat - /usr/local/include/ffmpeg/libavformat - /usr/include/libavformat - /usr/local/include/libavformat -) - -set(FFMPEG_INCLUDE_DIR ${FFMPEG_general_INCLUDE_DIR} ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR}) - -IF( FFMPEG_INCLUDE_DIR ) - -FIND_PROGRAM( FFMPEG_CONFIG ffmpeg-config - /usr/bin - /usr/local/bin - ${HOME}/bin -) - -IF( FFMPEG_CONFIG ) - EXEC_PROGRAM( ${FFMPEG_CONFIG} ARGS "--libs avformat" OUTPUT_VARIABLE FFMPEG_LIBS ) - SET( FFMPEG_FOUND "YES" ) - SET( FFMPEG_LIBRARIES "${FFMPEG_LIBS}" ) - -ELSE( FFMPEG_CONFIG ) - - FIND_LIBRARY( FFMPEG_avcodec_LIBRARY avcodec - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - FIND_LIBRARY( FFMPEG_avformat_LIBRARY avformat - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - FIND_LIBRARY( FFMPEG_avutil_LIBRARY avutil - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - IF( FFMPEG_avcodec_LIBRARY ) - IF( FFMPEG_avformat_LIBRARY ) - - SET( FFMPEG_FOUND "YES" ) - SET( FFMPEG_LIBRARIES ${FFMPEG_avformat_LIBRARY} ${FFMPEG_avcodec_LIBRARY} ) - IF( FFMPEG_avutil_LIBRARY ) - SET( FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_avutil_LIBRARY} ) - ENDIF( FFMPEG_avutil_LIBRARY ) - - ENDIF( FFMPEG_avformat_LIBRARY ) - ENDIF( FFMPEG_avcodec_LIBRARY ) - -ENDIF( FFMPEG_CONFIG ) - -ENDIF( FFMPEG_INCLUDE_DIR ) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 0000000000..526be5f1be --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,148 @@ +# vim: ts=2 sw=2 +# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) +# +# Once done this will define +# FFMPEG_FOUND - System has the all required components. +# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. +# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. +# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. +# +# For each of the components it will additionaly set. +# - AVCODEC +# - AVDEVICE +# - AVFORMAT +# - AVUTIL +# - POSTPROCESS +# - SWSCALE +# the following variables will be defined +# _FOUND - System has +# _INCLUDE_DIRS - Include directory necessary for using the headers +# _LIBRARIES - Link these to use +# _DEFINITIONS - Compiler switches required for using +# _VERSION - The components version +# +# Copyright (c) 2006, Matthias Kretz, +# Copyright (c) 2008, Alexander Neundorf, +# Copyright (c) 2011, Michael Jansen, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindPackageHandleStandardArgs) + +# The default components were taken from a survey over other FindFFMPEG.cmake files +if (NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) +endif () + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component ) + if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) + # message(STATUS " - ${_component} found.") + set(${_component}_FOUND TRUE) + else () + # message(STATUS " - ${_component} not found.") + endif () +endmacro() + +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + + if (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + endif () + endif (NOT WIN32) + + find_path(${_component}_INCLUDE_DIRS ${_header} + HINTS + ${PC_LIB${_component}_INCLUDEDIR} + ${PC_LIB${_component}_INCLUDE_DIRS} + PATH_SUFFIXES + ffmpeg + ) + + find_library(${_component}_LIBRARIES NAMES ${_library} + HINTS + ${PC_LIB${_component}_LIBDIR} + ${PC_LIB${_component}_LIBRARY_DIRS} + ) + + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") + + set_component_found(${_component}) + + mark_as_advanced( + ${_component}_INCLUDE_DIRS + ${_component}_LIBRARIES + ${_component}_DEFINITIONS + ${_component}_VERSION) + +endmacro() + + +# Check for cached results. If there are skip the costly part. +if (NOT FFMPEG_LIBRARIES) + + # Check for all possible component. + find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) + find_component(AVFORMAT libavformat avformat libavformat/avformat.h) + find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) + find_component(AVUTIL libavutil avutil libavutil/avutil.h) + find_component(SWSCALE libswscale swscale libswscale/swscale.h) + find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) + + # Check if the required components were found and add their stuff to the FFMPEG_* vars. + foreach (_component ${FFmpeg_FIND_COMPONENTS}) + if (${_component}_FOUND) + # message(STATUS "Required component ${_component} present.") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) + else () + # message(STATUS "Required component ${_component} missing.") + endif () + endforeach () + + # Build the include path with duplicates removed. + if (FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) + endif () + + # cache the vars. + set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) + + mark_as_advanced(FFMPEG_INCLUDE_DIRS + FFMPEG_LIBRARIES + FFMPEG_DEFINITIONS) + +endif () + +# Now set the noncached _FOUND vars for the components. +foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE) + set_component_found(${_component}) +endforeach () + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach (_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) +endforeach () + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) From a77d910aaf10e90dde397c92daf8bdef2e927a41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 16:44:41 +0100 Subject: [PATCH 004/147] audio codec is opened, some cleanup --- apps/openmw/mwrender/videoplayer.cpp | 77 +++++++++++++++++++--------- apps/openmw/mwrender/videoplayer.hpp | 12 +++-- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4679104d87..5e7d8eb55f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -72,6 +72,7 @@ namespace MWRender VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) : mAvContext(NULL) , mVideoStreamId(-1) + , mAudioStreamId(-1) { Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); @@ -108,6 +109,7 @@ namespace MWRender mVideoStreamId = -1; + mAudioStreamId = -1; mEOF = false; mDisplayedFrameCount = 0; @@ -150,36 +152,60 @@ namespace MWRender - // INIT VIDEO - // Find the video stream among the different streams for (unsigned int i = 0; i < mAvContext->nb_streams; i++) { if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { mVideoStreamId = i; + mVideoStream = mAvContext->streams[i]; break; } } - if (-1 == mVideoStreamId) + if (mVideoStreamId < 0) throw std::runtime_error("No video stream found in the video"); - // Get the video codec - mVideoCodecContext = mAvContext->streams[mVideoStreamId]->codec; - if (!mVideoCodecContext) - throw std::runtime_error("Stream doesn't have a codec"); - // Get the video decoder - mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id); + mVideoCodec = avcodec_find_decoder(mVideoStream->codec->codec_id); if (NULL == mVideoCodec) - throw std::runtime_error("No decoder found"); + throw std::runtime_error("No video decoder found"); // Load the video codec - err = avcodec_open2(mVideoCodecContext, mVideoCodec, 0); + err = avcodec_open2(mVideoStream->codec, mVideoCodec, 0); if (err < 0) throwError (err); + + + // Find the audio stream among the different streams + for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + { + if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + { + mAudioStreamId = i; + mAudioStream = mAvContext->streams[i]; + break; + } + } + + if (-1 == mAudioStreamId) + throw std::runtime_error("No audio stream found in the video"); + + // Get the audio decoder + mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); + if (mAudioCodec == NULL) + { + throw std::runtime_error("Stream doesn't have an audio codec"); + } + + // Load the audio codec + err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); + if (err < 0) + throwError (err); + + + // Create the frame buffers mRawFrame = avcodec_alloc_frame(); mRGBAFrame = avcodec_alloc_frame(); @@ -189,23 +215,23 @@ namespace MWRender } - avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoCodecContext->width, mVideoCodecContext->height); + avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoStream->codec->width, mVideoStream->codec->height); // Setup the image scaler // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, // but i'm not worried about performance just yet - mSwsContext = sws_getContext(mVideoCodecContext->width, mVideoCodecContext->height, - mVideoCodecContext->pix_fmt, - mVideoCodecContext->width, mVideoCodecContext->height, + mSwsContext = sws_getContext(mVideoStream->codec->width, mVideoStream->codec->height, + mVideoStream->codec->pix_fmt, + mVideoStream->codec->width, mVideoStream->codec->height, PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); if (!mSwsContext) throw std::runtime_error("Can't create SWS Context"); // Get the frame time we need for this video - AVRational r = mAvContext->streams[mVideoStreamId]->avg_frame_rate; - AVRational r2 = mAvContext->streams[mVideoStreamId]->r_frame_rate; + AVRational r = mVideoStream->avg_frame_rate; + AVRational r2 = mVideoStream->r_frame_rate; if ((!r.num || !r.den) && (!r2.num || !r2.den)) { @@ -230,19 +256,19 @@ namespace MWRender "VideoTexture", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, - mVideoCodecContext->width, mVideoCodecContext->height, + mVideoStream->codec->width, mVideoStream->codec->height, 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); // initialize to (0, 0, 0, 1) std::vector buffer; - buffer.resize(mVideoCodecContext->width * mVideoCodecContext->height); - for (int p=0; pwidth * mVideoCodecContext->height; ++p) + buffer.resize(mVideoStream->codec->width * mVideoStream->codec->height); + for (int p=0; pcodec->width * mVideoStream->codec->height; ++p) { buffer[p] = (255 << 24); } - memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoCodecContext->width*mVideoCodecContext->height*4); + memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoStream->codec->width*mVideoStream->codec->height*4); mVideoTexture->getBuffer()->unlock(); mTextureUnit->setTextureName ("VideoTexture"); @@ -358,7 +384,7 @@ namespace MWRender // Get the front frame and decode it AVPacket *videoPacket = mVideoPacketQueue.front(); int res; - res = avcodec_decode_video2(mVideoCodecContext, mRawFrame, &didDecodeFrame, videoPacket); + res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, videoPacket); if (res < 0 || !didDecodeFrame) throw std::runtime_error ("an error occured while decoding the video frame"); @@ -366,12 +392,12 @@ namespace MWRender // Convert the frame to RGB sws_scale(mSwsContext, mRawFrame->data, mRawFrame->linesize, - 0, mVideoCodecContext->height, + 0, mVideoStream->codec->height, mRGBAFrame->data, mRGBAFrame->linesize); Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); - Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); + Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); av_free_packet(mVideoPacketQueue.front()); @@ -397,7 +423,8 @@ namespace MWRender mVideoPacketQueue.pop(); } - avcodec_close(mVideoCodecContext); + if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); + if (mAudioStream && mAudioStream->codec != NULL) avcodec_close(mAudioStream->codec); avpicture_free((AVPicture *)mRGBAFrame); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 1d3010e8e9..5010ac5163 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,6 +21,7 @@ namespace Ogre struct AVFormatContext; struct AVCodecContext; struct AVCodec; +struct AVStream; struct AVFrame; struct SwsContext; struct AVPacket; @@ -56,10 +57,14 @@ namespace MWRender Ogre::Timer mTimer; - // VIDEO - AVCodecContext* mVideoCodecContext; AVCodec* mVideoCodec; - int mVideoStreamId; + AVCodec* mAudioCodec; + + AVStream* mVideoStream; + AVStream* mAudioStream; + int mVideoStreamId; ///< ID of the first video stream + int mAudioStreamId; ///< ID of the first audio stream + AVFrame* mRawFrame; AVFrame* mRGBAFrame; SwsContext* mSwsContext; @@ -67,6 +72,7 @@ namespace MWRender float mDecodingTime; std::queue mVideoPacketQueue; + int mDisplayedFrameCount; From bc90c751760dc9bdb894a8e781e3da335d2c92e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 17:33:02 +0100 Subject: [PATCH 005/147] more clean up, video played with correct speed, videos without sound working too (mw_credits.bik) --- apps/openmw/mwrender/videoplayer.cpp | 315 +++++++++++++++++---------- apps/openmw/mwrender/videoplayer.hpp | 45 +++- 2 files changed, 228 insertions(+), 132 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 5e7d8eb55f..e7f1eccde6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -19,6 +19,9 @@ extern "C" #include "../mwbase/environment.hpp" +#define MIN_QUEUED_PACKETS 30 + + namespace MWRender { @@ -69,10 +72,76 @@ namespace MWRender return stream->tell(); } + //------------------------------------------------------------------------------------------- + + AVPacketQueue::AVPacketQueue(): + mFirstPacket(NULL), mLastPacket(NULL), mNumPackets(0), mSize(0) + + { + } + + int AVPacketQueue::put(AVPacket* pkt) + { + if(av_dup_packet(pkt) < 0) + { + return -1; + } + + AVPacketList* pkt1; + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + if (pkt1 == NULL) return -1; + pkt1->pkt = *pkt; + pkt1->next = NULL; + + if (mLastPacket == NULL) mFirstPacket = pkt1; + else mLastPacket->next = pkt1; + + mLastPacket = pkt1; + mNumPackets++; + mSize += pkt1->pkt.size; + + return 0; + } + + int AVPacketQueue::get(AVPacket* pkt, int block) + { + AVPacketList* pkt1; + + while (true) + { + pkt1 = mFirstPacket; + if (pkt1 != NULL) + { + mFirstPacket = pkt1->next; + + if (mFirstPacket == NULL) mLastPacket = NULL; + + mNumPackets--; + mSize -= pkt1->pkt.size; + *pkt = pkt1->pkt; + av_free(pkt1); + return 1; + } + else if (block == 0) + { + return 0; + } + else + { + return -1; + } + } + } + + //------------------------------------------------------------------------------------------- + VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) : mAvContext(NULL) , mVideoStreamId(-1) , mAudioStreamId(-1) + , mVideoClock(0) + , mAudioClock(0) + , mClock(0) { Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); @@ -110,8 +179,11 @@ namespace MWRender mVideoStreamId = -1; mAudioStreamId = -1; - mEOF = false; - mDisplayedFrameCount = 0; + mAudioStream = NULL; + mVideoStream = NULL; + mVideoClock = 0; + mAudioClock = 0; + mClock = 0; // if something is already playing, close it if (mAvContext) @@ -189,20 +261,20 @@ namespace MWRender } } - if (-1 == mAudioStreamId) - throw std::runtime_error("No audio stream found in the video"); - - // Get the audio decoder - mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); - if (mAudioCodec == NULL) + if (mAudioStreamId >= 0) { - throw std::runtime_error("Stream doesn't have an audio codec"); - } + // Get the audio decoder + mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); + if (mAudioCodec == NULL) + { + throw std::runtime_error("Stream doesn't have an audio codec"); + } - // Load the audio codec - err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); - if (err < 0) - throwError (err); + // Load the audio codec + err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); + if (err < 0) + throwError (err); + } @@ -229,24 +301,6 @@ namespace MWRender if (!mSwsContext) throw std::runtime_error("Can't create SWS Context"); - // Get the frame time we need for this video - AVRational r = mVideoStream->avg_frame_rate; - AVRational r2 = mVideoStream->r_frame_rate; - if ((!r.num || !r.den) && - (!r2.num || !r2.den)) - { - std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl; - mWantedFrameTime = 1.f / 29.97f; - } - else - { - if (r.num && r.den) - mWantedFrameTime = 1.f/((float)r.num / r.den); - else - mWantedFrameTime = 1.f/((float)r2.num / r2.den); - } - - mTextureUnit->setTextureName (""); if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) @@ -273,8 +327,23 @@ namespace MWRender mTextureUnit->setTextureName ("VideoTexture"); - mTimer.reset(); + // Queue up some packets + while( + mVideoPacketQueue.getNumPackets()stream_index == mVideoStreamId) - { - // If it was a video frame... - mVideoPacketQueue.push(frame); - saved = true; - } - - return saved; - - } - void VideoPlayer::update() { if (!mAvContext) return; - // Time elapsed since the video started - float realTime = mTimer.getMilliseconds ()/1000.f; + double dt = mTimer.getMilliseconds () / 1000.f; + mTimer.reset (); - // Here is the time we're at in the video - float movieTime = mDisplayedFrameCount * mWantedFrameTime; - - if (movieTime >= realTime) - return; - - if (!mVideoPacketQueue.size() && mEOF) - close(); - - Ogre::Timer timer; - - if (!mVideoPacketQueue.size()) + //UpdateAudio(fTime); + std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; + while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) { - if (readFrameAndQueue()) - decodeFrontFrame(); - } - else - decodeFrontFrame(); + while( + mVideoPacketQueue.getNumPackets()codec, mRawFrame, &didDecodeFrame, videoPacket); + int didDecodeFrame = 0; + res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet); if (res < 0 || !didDecodeFrame) throw std::runtime_error ("an error occured while decoding the video frame"); + // Set video clock to the PTS of this packet (presentation timestamp) + double pts = 0; + if (packet.pts != -1.0) pts = packet.pts; + pts *= av_q2d(mVideoStream->time_base); + mVideoClock = pts; + // Convert the frame to RGB sws_scale(mSwsContext, mRawFrame->data, mRawFrame->linesize, @@ -400,11 +433,7 @@ namespace MWRender Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); - av_free_packet(mVideoPacketQueue.front()); - av_free(mVideoPacketQueue.front()); - mVideoPacketQueue.pop(); - - ++mDisplayedFrameCount; + if (packet.data != NULL) av_free_packet(&packet); } void VideoPlayer::close () @@ -416,11 +445,17 @@ namespace MWRender void VideoPlayer::deleteContext() { - while (mVideoPacketQueue.size()) + while (mVideoPacketQueue.getNumPackets ()) { - av_free_packet(mVideoPacketQueue.front()); - av_free(mVideoPacketQueue.front()); - mVideoPacketQueue.pop(); + AVPacket packet; + mVideoPacketQueue.get(&packet, 1); + if (packet.data != NULL) av_free_packet(&packet); + } + while (mAudioPacketQueue.getNumPackets ()) + { + AVPacket packet; + mAudioPacketQueue.get(&packet, 1); + if (packet.data != NULL) av_free_packet(&packet); } if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); @@ -439,6 +474,46 @@ namespace MWRender mAvContext = NULL; } + + + + bool VideoPlayer::addToBuffer() + { + if(mAvContext) + { + AVPacket packet; + if (av_read_frame(mAvContext, &packet) >= 0) + { + if (packet.stream_index == mVideoStreamId) + { + /* + if(curTime==0) + { + curTime = packet.dts; + curTime *= av_q2d(m_pVideoSt->time_base); + std::cout << "Initializing curtime to: " << curTime << std::endl; + } + */ + + mVideoPacketQueue.put(&packet); + + return true; + } + else if (packet.stream_index == mAudioStreamId && mAudioStream) + { + mAudioPacketQueue.put(&packet); + return true; + } + else + { + av_free_packet(&packet); + return false; + } + } + } + + return false; + } } //#endif diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 5010ac5163..466bb79027 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -25,10 +25,31 @@ struct AVStream; struct AVFrame; struct SwsContext; struct AVPacket; +struct AVPacketList; namespace MWRender { + /// A simple queue used to queue raw audio and video data. + class AVPacketQueue + { + public: + AVPacketQueue(); + int put(AVPacket* pkt); + int get(AVPacket* pkt, int block); + + bool isEmpty() const { return mNumPackets == 0; } + int getNumPackets() const { return mNumPackets; } + int getSize() const { return mSize; } + + private: + AVPacketList* mFirstPacket; + AVPacketList* mLastPacket; + int mNumPackets; + int mSize; + }; + + class VideoPlayer { public: @@ -52,9 +73,6 @@ namespace MWRender AVFormatContext* mAvContext; - - bool mEOF; - Ogre::Timer mTimer; AVCodec* mVideoCodec; @@ -68,24 +86,27 @@ namespace MWRender AVFrame* mRawFrame; AVFrame* mRGBAFrame; SwsContext* mSwsContext; - float mWantedFrameTime; - float mDecodingTime; - std::queue mVideoPacketQueue; + double mClock; + double mVideoClock; + double mAudioClock; - int mDisplayedFrameCount; + AVPacketQueue mVideoPacketQueue; + AVPacketQueue mAudioPacketQueue; - bool readFrameAndQueue(); - bool saveFrame(AVPacket* frame); - - void decodeFrontFrame(); - void close(); void deleteContext(); void throwError(int error); + + + + + bool addToBuffer(); ///< try to add the next audio or video packet into the queue. + + void decodeNextVideoFrame(); ///< decode the next video frame in the queue and display it. }; } From 3106db0379fabb77b8c17b601c39274360bf4d47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 17:41:38 +0100 Subject: [PATCH 006/147] commented out debug output --- apps/openmw/mwrender/videoplayer.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e7f1eccde6..b1fee213fc 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -370,7 +370,7 @@ namespace MWRender mTimer.reset (); //UpdateAudio(fTime); - std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; + //std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) { while( @@ -486,12 +486,13 @@ namespace MWRender { if (packet.stream_index == mVideoStreamId) { + // I don't believe this is necessary. /* - if(curTime==0) + if(mClock==0) { - curTime = packet.dts; - curTime *= av_q2d(m_pVideoSt->time_base); - std::cout << "Initializing curtime to: " << curTime << std::endl; + mClock = packet.dts; + mClock *= av_q2d(mVideoStream->time_base); + std::cout << "Initializing clock to: " << mClock << std::endl; } */ From c49966dd29c61f0f5e1bf26348e5e24a0cf25785 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Dec 2012 22:49:31 +0100 Subject: [PATCH 007/147] started over --- CMakeLists.txt | 4 + apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 1177 +++++++++++++-------- apps/openmw/mwrender/videoplayer.hpp | 231 ++-- cmake/FindSDL.cmake | 177 ++++ 5 files changed, 1073 insertions(+), 518 deletions(-) create mode 100644 cmake/FindSDL.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c11fda9f4a..084363da71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,10 @@ if (USE_MPG123) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) endif (USE_MPG123) +find_package (SDL REQUIRED) +set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${SDL_INCLUDE_DIR}) +set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${SDL_LIBRARY}) + # Platform specific if (WIN32) set(Boost_USE_STATIC_LIBS ON) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a1a24b7ba2..5663ded092 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -928,7 +928,7 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend void RenderingManager::playVideo(const std::string& name) { - mVideoPlayer->play ("video/" + name); + mVideoPlayer->playVideo ("video/" + name); } } // namespace diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b1fee213fc..3ddc7961c7 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1,153 +1,788 @@ #include "videoplayer.hpp" -//#ifdef OPENMW_USE_FFMPEG - -#include -#include -#include -#include - - -extern "C" -{ -#include -#include -#include -} #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" -#define MIN_QUEUED_PACKETS 30 - namespace MWRender { int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - int num_read = stream->size() - stream->tell(); + int num_read = stream->size() - stream->tell(); - if (num_read > buf_size) - num_read = buf_size; + if (num_read > buf_size) + num_read = buf_size; - stream->read(buf, num_read); - return num_read; + stream->read(buf, num_read); + return num_read; } int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - int num_write = stream->size() - stream->tell(); + int num_write = stream->size() - stream->tell(); - if (num_write > buf_size) - num_write = buf_size; + if (num_write > buf_size) + num_write = buf_size; - stream->write (buf, num_write); - return num_write; + stream->write (buf, num_write); + return num_write; } int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - switch (whence) - { - case SEEK_SET: - stream->seek(offset); - case SEEK_CUR: - stream->seek(stream->tell() + offset); - case SEEK_END: - stream->seek(stream->size() + offset); - case AVSEEK_SIZE: - return stream->size(); - default: - return -1; - } + switch (whence) + { + case SEEK_SET: + stream->seek(offset); + case SEEK_CUR: + stream->seek(stream->tell() + offset); + case SEEK_END: + stream->seek(stream->size() + offset); + case AVSEEK_SIZE: + return stream->size(); + default: + return -1; + } - return stream->tell(); + return stream->tell(); } - //------------------------------------------------------------------------------------------- - AVPacketQueue::AVPacketQueue(): - mFirstPacket(NULL), mLastPacket(NULL), mNumPackets(0), mSize(0) - { + + /* Since we only have one decoding thread, the Big Struct + can be global in case we need it. */ + VideoState *global_video_state; + + void packet_queue_init(PacketQueue *q) { + memset(q, 0, sizeof(PacketQueue)); } - - int AVPacketQueue::put(AVPacket* pkt) - { - if(av_dup_packet(pkt) < 0) - { + int packet_queue_put(PacketQueue *q, AVPacket *pkt) { + AVPacketList *pkt1; + if(av_dup_packet(pkt) < 0) { return -1; } - - AVPacketList* pkt1; pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if (pkt1 == NULL) return -1; + if (!pkt1) + return -1; pkt1->pkt = *pkt; pkt1->next = NULL; - if (mLastPacket == NULL) mFirstPacket = pkt1; - else mLastPacket->next = pkt1; + q->mutex.lock (); - mLastPacket = pkt1; - mNumPackets++; - mSize += pkt1->pkt.size; + if (!q->last_pkt) + q->first_pkt = pkt1; + else + q->last_pkt->next = pkt1; + q->last_pkt = pkt1; + q->nb_packets++; + q->size += pkt1->pkt.size; + q->cond.notify_one(); + q->mutex.unlock (); + return 0; + } + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { + AVPacketList *pkt1; + int ret; + + boost::unique_lock lock(q->mutex); + + for(;;) { + + if(global_video_state->quit) { + ret = -1; + break; + } + + pkt1 = q->first_pkt; + if (pkt1) { + q->first_pkt = pkt1->next; + if (!q->first_pkt) + q->last_pkt = NULL; + q->nb_packets--; + q->size -= pkt1->pkt.size; + *pkt = pkt1->pkt; + av_free(pkt1); + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + + + q->cond.wait(lock); + } + } + return ret; + } + static void packet_queue_flush(PacketQueue *q) { + AVPacketList *pkt, *pkt1; + + q->mutex.lock(); + for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { + pkt1 = pkt->next; + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + q->last_pkt = NULL; + q->first_pkt = NULL; + q->nb_packets = 0; + q->size = 0; + q->mutex.unlock (); + } + double get_audio_clock(VideoState *is) { + double pts; + int hw_buf_size, bytes_per_sec, n; + + pts = is->audio_clock; /* maintained in the audio thread */ + hw_buf_size = is->audio_buf_size - is->audio_buf_index; + bytes_per_sec = 0; + n = is->audio_st->codec->channels * 2; + if(is->audio_st) { + bytes_per_sec = is->audio_st->codec->sample_rate * n; + } + if(bytes_per_sec) { + pts -= (double)hw_buf_size / bytes_per_sec; + } + return pts; + } + double get_video_clock(VideoState *is) { + double delta; + + delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; + return is->video_current_pts + delta; + } + double get_external_clock(VideoState *is) { + return av_gettime() / 1000000.0; + } + double get_master_clock(VideoState *is) { + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) { + return get_video_clock(is); + } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) { + return get_audio_clock(is); + } else { + return get_external_clock(is); + } + } + /* Add or subtract samples to get a better sync, return new + audio buffer size */ + int synchronize_audio(VideoState *is, short *samples, + int samples_size, double pts) { + int n; + double ref_clock; + + n = 2 * is->audio_st->codec->channels; + + if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) { + double diff, avg_diff; + int wanted_size, min_size, max_size; + // int nb_samples; + + ref_clock = get_master_clock(is); + diff = get_audio_clock(is) - ref_clock; + if(diff < AV_NOSYNC_THRESHOLD) { + // accumulate the diffs + is->audio_diff_cum = diff + is->audio_diff_avg_coef + * is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { + is->audio_diff_avg_count++; + } else { + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + if(wanted_size < min_size) { + wanted_size = min_size; + } else if (wanted_size > max_size) { + wanted_size = max_size; + } + if(wanted_size < samples_size) { + /* remove samples */ + samples_size = wanted_size; + } else if(wanted_size > samples_size) { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = (uint8_t *)samples + samples_size - n; + q = samples_end + n; + while(nb > 0) { + memcpy(q, samples_end, n); + q += n; + nb -= n; + } + samples_size = wanted_size; + } + } + } + } else { + /* difference is TOO big; reset diff stuff */ + is->audio_diff_avg_count = 0; + is->audio_diff_cum = 0; + } + } + return samples_size; + } + int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) { + int len1, data_size, n; + AVPacket *pkt = &is->audio_pkt; + double pts; + + for(;;) { + while(is->audio_pkt_size > 0) { + data_size = buf_size; + len1 = avcodec_decode_audio3(is->audio_st->codec, + (int16_t *)audio_buf, &data_size, pkt); + + + if(len1 < 0) { + /* if error, skip frame */ + is->audio_pkt_size = 0; + break; + } + is->audio_pkt_data += len1; + is->audio_pkt_size -= len1; + if(data_size <= 0) { + /* No data yet, get more frames */ + continue; + } + pts = is->audio_clock; + *pts_ptr = pts; + n = 2 * is->audio_st->codec->channels; + is->audio_clock += (double)data_size / + (double)(n * is->audio_st->codec->sample_rate); + + /* We have data, return it and come back for more later */ + return data_size; + } + if(pkt->data) + av_free_packet(pkt); + + if(is->quit) { + return -1; + } + /* next packet */ + if(packet_queue_get(&is->audioq, pkt, 1) < 0) { + return -1; + } + is->audio_pkt_data = pkt->data; + is->audio_pkt_size = pkt->size; + /* if update, update the audio clock w/pts */ + if(pkt->pts != AV_NOPTS_VALUE) { + is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + } + } + } + + void audio_callback(void *userdata, Uint8 *stream, int len) { + VideoState *is = (VideoState *)userdata; + int len1, audio_size; + double pts; + + while(len > 0) { + if(is->audio_buf_index >= is->audio_buf_size) { + /* We have already sent all our data; get more */ + audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); + if(audio_size < 0) { + /* If error, output silence */ + is->audio_buf_size = 1024; + memset(is->audio_buf, 0, is->audio_buf_size); + } else { + audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, + audio_size, pts); + is->audio_buf_size = audio_size; + } + is->audio_buf_index = 0; + } + len1 = is->audio_buf_size - is->audio_buf_index; + if(len1 > len) + len1 = len; + memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); + len -= len1; + stream += len1; + is->audio_buf_index += len1; + } + } + + /* + static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { + SDL_Event event; + event.type = FF_REFRESH_EVENT; + event.user.data1 = opaque; + SDL_PushEvent(&event); + return 0; // 0 means stop timer + } + */ + + void timer_callback (int delay, VideoState* is) + { + boost::this_thread::sleep (boost::posix_time::milliseconds(delay)); + is->refresh++; + } + + /* schedule a video refresh in 'delay' ms */ + static void schedule_refresh(VideoState *is, int delay) + { + //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); + //is->refresh_queue.push_back (delay); + + boost::thread (boost::bind(&timer_callback, delay, is)); + } + + void video_display(VideoState *is) + { + VideoPicture *vp; + + vp = &is->pictq[is->pictq_rindex]; + + if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) + { + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); + if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + { + Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); + texture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + is->video_st->codec->width, is->video_st->codec->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + } + Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + buffer->blitFromMemory(pb); + } + + free(vp->data); + } + + + void video_refresh_timer(void *userdata) { + + VideoState *is = (VideoState *)userdata; + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + if(is->video_st) { + if(is->pictq_size == 0) { + schedule_refresh(is, 1); + } else { + vp = &is->pictq[is->pictq_rindex]; + + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); + + delay = vp->pts - is->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; + } + /* save for next time */ + is->frame_last_delay = delay; + is->frame_last_pts = vp->pts; + + /* update delay to sync to audio if not master source */ + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { + if(diff <= -sync_threshold) { + delay = 0; + } else if(diff >= sync_threshold) { + delay = 2 * delay; + } + } + } + + is->frame_timer += delay; + /* computer the REAL delay */ + actual_delay = is->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); + + /* show the picture! */ + video_display(is); + + /* update queue for next picture! */ + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { + is->pictq_rindex = 0; + } + is->pictq_mutex.lock(); + is->pictq_size--; + is->pictq_cond.notify_one (); + is->pictq_mutex.unlock (); + } + } else { + schedule_refresh(is, 100); + } + } + + int queue_picture(VideoState *is, AVFrame *pFrame, double pts) { + + VideoPicture *vp; + + /* wait until we have a new pic */ + { + boost::unique_lock lock(is->pictq_mutex); + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && + !is->quit) { + is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); + } + } + + if(is->quit) + return -1; + + // windex is set to 0 initially + vp = &is->pictq[is->pictq_windex]; + + // Convert the image into YUV format that SDL uses + if(is->sws_context == NULL) { + int w = is->video_st->codec->width; + int h = is->video_st->codec->height; + is->sws_context = sws_getContext(w, h, + is->video_st->codec->pix_fmt, w, h, + PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + if(is->sws_context == NULL) + throw std::runtime_error("Cannot initialize the conversion context!\n"); + } + + vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); + + sws_scale(is->sws_context, pFrame->data, pFrame->linesize, + 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); + + + vp->pts = pts; + + // now we inform our display thread that we have a pic ready + if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) { + is->pictq_windex = 0; + } + is->pictq_mutex.lock(); + is->pictq_size++; + is->pictq_mutex.unlock(); return 0; } - int AVPacketQueue::get(AVPacket* pkt, int block) - { - AVPacketList* pkt1; + double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) { - while (true) - { - pkt1 = mFirstPacket; - if (pkt1 != NULL) - { - mFirstPacket = pkt1->next; + double frame_delay; - if (mFirstPacket == NULL) mLastPacket = NULL; - - mNumPackets--; - mSize -= pkt1->pkt.size; - *pkt = pkt1->pkt; - av_free(pkt1); - return 1; - } - else if (block == 0) - { - return 0; - } - else - { - return -1; - } + if(pts != 0) { + /* if we have pts, set video clock to it */ + is->video_clock = pts; + } else { + /* if we aren't given a pts, set it to the clock */ + pts = is->video_clock; } + /* update the video clock */ + frame_delay = av_q2d(is->video_st->codec->time_base); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + is->video_clock += frame_delay; + return pts; } - //------------------------------------------------------------------------------------------- + uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; - VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) - : mAvContext(NULL) - , mVideoStreamId(-1) - , mAudioStreamId(-1) - , mVideoClock(0) - , mAudioClock(0) - , mClock(0) + /* These are called whenever we allocate a frame + * buffer. We use this to store the global_pts in + * a frame at the time it is allocated. + */ + int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { + int ret = avcodec_default_get_buffer(c, pic); + uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + *pts = global_video_pkt_pts; + pic->opaque = pts; + return ret; + } + void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) { + if(pic) av_freep(&pic->opaque); + avcodec_default_release_buffer(c, pic); + } + + int video_thread(void *arg) { + VideoState *is = (VideoState *)arg; + AVPacket pkt1, *packet = &pkt1; + int len1, frameFinished; + AVFrame *pFrame; + double pts; + + pFrame = avcodec_alloc_frame(); + + is->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc ((AVPicture *)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + + + for(;;) { + if(packet_queue_get(&is->videoq, packet, 1) < 0) { + // means we quit getting packets + break; + } + pts = 0; + + // Save global pts to be stored in pFrame + global_video_pkt_pts = packet->pts; + // Decode video frame + len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, + packet); + if(packet->dts == AV_NOPTS_VALUE + && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { + pts = *(uint64_t *)pFrame->opaque; + } else if(packet->dts != AV_NOPTS_VALUE) { + pts = packet->dts; + } else { + pts = 0; + } + pts *= av_q2d(is->video_st->time_base); + + + // Did we get a video frame? + if(frameFinished) { + pts = synchronize_video(is, pFrame, pts); + if(queue_picture(is, pFrame, pts) < 0) { + break; + } + } + av_free_packet(packet); + } + + SDL_CloseAudio(); + + av_free(pFrame); + + avpicture_free((AVPicture *)is->rgbaFrame); + av_free(is->rgbaFrame); + + return 0; + } + + int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) { - Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); - videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); + AVCodecContext *codecCtx; + AVCodec *codec; + SDL_AudioSpec wanted_spec, spec; + + if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + return -1; + } + + // Get a pointer to the codec context for the video stream + codecCtx = pFormatCtx->streams[stream_index]->codec; + + if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) { + // Set audio settings from codec info + wanted_spec.freq = codecCtx->sample_rate; + wanted_spec.format = AUDIO_S16SYS; + wanted_spec.channels = codecCtx->channels; + wanted_spec.silence = 0; + wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback = audio_callback; + wanted_spec.userdata = is; + + if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { + fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); + return -1; + } + is->audio_hw_buf_size = spec.size; + } + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + + switch(codecCtx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + is->audioStream = stream_index; + is->audio_st = pFormatCtx->streams[stream_index]; + is->audio_buf_size = 0; + is->audio_buf_index = 0; + + /* averaging filter for audio sync */ + is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); + is->audio_diff_avg_count = 0; + /* Correct audio only if larger error than this */ + is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / codecCtx->sample_rate; + + memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); + packet_queue_init(&is->audioq); + SDL_PauseAudio(0); + break; + case AVMEDIA_TYPE_VIDEO: + is->videoStream = stream_index; + is->video_st = pFormatCtx->streams[stream_index]; + + is->frame_timer = (double)av_gettime() / 1000000.0; + is->frame_last_delay = 40e-3; + is->video_current_pts_time = av_gettime(); + + packet_queue_init(&is->videoq); + is->video_thread = boost::thread(video_thread, is); + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + + break; + default: + break; + } + + + } + + int decode_interrupt_cb(void) { + return (global_video_state && global_video_state->quit); + } + + int decode_thread(void *arg) { + + VideoState *is = (VideoState *)arg; + AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVPacket pkt1, *packet = &pkt1; + + int video_index = -1; + int audio_index = -1; + int i; + + is->videoStream=-1; + is->audioStream=-1; + is->quit = 0; + + Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); + if(stream.isNull ()) + throw std::runtime_error("Failed to open video resource"); + + AVIOContext *ioContext = 0; + + ioContext = avio_alloc_context(NULL, 0, 0, &stream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throw std::runtime_error("Failed to allocate ioContext "); + + pFormatCtx->pb = ioContext; + + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); + + // Retrieve stream information + if(avformat_find_stream_info(pFormatCtx, NULL)<0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); + + for(i=0; inb_streams; i++) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && + video_index < 0) { + video_index=i; + } + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && + audio_index < 0) { + audio_index=i; + } + } + + if(audio_index >= 0) { + stream_component_open(is, audio_index, pFormatCtx); + } + if(video_index >= 0) { + stream_component_open(is, video_index, pFormatCtx); + } + + if(is->videoStream >= 0 /*|| is->audioStream < 0*/) + { + + // main decode loop + + for(;;) { + if(is->quit) { + break; + } + if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; + } + if(av_read_frame(pFormatCtx, packet) < 0) { + break; + } + // Is this a packet from the video stream? + if(packet->stream_index == is->videoStream) { + packet_queue_put(&is->videoq, packet); + } else if(packet->stream_index == is->audioStream) { + packet_queue_put(&is->audioq, packet); + } else { + av_free_packet(packet); + } + } + /* all done - wait for it */ + while(!is->quit) { + // EOF reached, all packets processed, we can exit now + if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + } + + is->quit = 1; + + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); + + is->video_thread.join(); + + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); + + sws_freeContext (is->sws_context); + + av_close_input_file(pFormatCtx); + pFormatCtx = NULL; + + av_free(ioContext); + + return 0; + } + + + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) + : mState(NULL) + , mSceneMgr(sceneMgr) + { + mVideoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); @@ -164,357 +799,71 @@ namespace MWRender mRectangle->setVisibilityFlags (0x1); } - VideoPlayer::~VideoPlayer() + VideoPlayer::~VideoPlayer () { - if (mAvContext) - deleteContext(); - - delete mRectangle; + if (mState) + close(); } - void VideoPlayer::play (const std::string& resourceName) + void VideoPlayer::playVideo (const std::string &resourceName) { - mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName); - - - mVideoStreamId = -1; - mAudioStreamId = -1; - mAudioStream = NULL; - mVideoStream = NULL; - mVideoClock = 0; - mAudioClock = 0; - mClock = 0; - - // if something is already playing, close it - if (mAvContext) + if (mState) close(); mRectangle->setVisible(true); MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + mState = new VideoState; - // BASIC INITIALIZATION - - // Load all the decoders + // Register all formats and codecs av_register_all(); - AVIOContext *ioContext = 0; - - int err = 0; - - mAvContext = avformat_alloc_context(); - if (!mAvContext) - throwError(0); - - ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throwError(0); - - mAvContext->pb = ioContext; - - err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL); - if (err != 0) - throwError(err); - - err = avformat_find_stream_info(mAvContext, 0); - if (err < 0) - throwError(err); - - - - - // Find the video stream among the different streams - for (unsigned int i = 0; i < mAvContext->nb_streams; i++) - { - if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) - { - mVideoStreamId = i; - mVideoStream = mAvContext->streams[i]; - break; - } + if(SDL_Init(SDL_INIT_AUDIO)) { + throw std::runtime_error("Failed to initialize SDL"); } - if (mVideoStreamId < 0) - throw std::runtime_error("No video stream found in the video"); + mState->refresh = 0; + mState->resourceName = resourceName; - // Get the video decoder - mVideoCodec = avcodec_find_decoder(mVideoStream->codec->codec_id); - if (NULL == mVideoCodec) - throw std::runtime_error("No video decoder found"); + schedule_refresh(mState, 40); - // Load the video codec - err = avcodec_open2(mVideoStream->codec, mVideoCodec, 0); - if (err < 0) - throwError (err); - - - - // Find the audio stream among the different streams - for (unsigned int i = 0; i < mAvContext->nb_streams; i++) - { - if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) - { - mAudioStreamId = i; - mAudioStream = mAvContext->streams[i]; - break; - } - } - - if (mAudioStreamId >= 0) - { - // Get the audio decoder - mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); - if (mAudioCodec == NULL) - { - throw std::runtime_error("Stream doesn't have an audio codec"); - } - - // Load the audio codec - err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); - if (err < 0) - throwError (err); - } - - - - // Create the frame buffers - mRawFrame = avcodec_alloc_frame(); - mRGBAFrame = avcodec_alloc_frame(); - if (!mRawFrame || !mRGBAFrame) - { - throw std::runtime_error("Can't allocate video frames"); - } - - - avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoStream->codec->width, mVideoStream->codec->height); - - - // Setup the image scaler - // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, - // but i'm not worried about performance just yet - mSwsContext = sws_getContext(mVideoStream->codec->width, mVideoStream->codec->height, - mVideoStream->codec->pix_fmt, - mVideoStream->codec->width, mVideoStream->codec->height, - PIX_FMT_RGBA, - SWS_BICUBIC, NULL, NULL, NULL); - if (!mSwsContext) - throw std::runtime_error("Can't create SWS Context"); - - mTextureUnit->setTextureName (""); - - if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) - Ogre::TextureManager::getSingleton().remove("VideoTexture"); - - mVideoTexture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - mVideoStream->codec->width, mVideoStream->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - - // initialize to (0, 0, 0, 1) - std::vector buffer; - buffer.resize(mVideoStream->codec->width * mVideoStream->codec->height); - for (int p=0; pcodec->width * mVideoStream->codec->height; ++p) - { - buffer[p] = (255 << 24); - } - memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoStream->codec->width*mVideoStream->codec->height*4); - mVideoTexture->getBuffer()->unlock(); - - mTextureUnit->setTextureName ("VideoTexture"); - - - // Queue up some packets - while( - mVideoPacketQueue.getNumPackets()av_sync_type = DEFAULT_AV_SYNC_TYPE; + mState->parse_thread = boost::thread(decode_thread, mState); } - void VideoPlayer::throwError(int error) + void VideoPlayer::update () { - char buffer[4096] = {0}; - - if (0 == av_strerror(error, buffer, sizeof(buffer))) + if (mState && mState->refresh) { - std::stringstream msg; - msg << "FFMPEG error: "; - msg << buffer << std::endl; - throw std::runtime_error(msg.str()); + video_refresh_timer (mState); + mState->refresh--; } - else - throw std::runtime_error("Unknown FFMPEG error"); - } - - void VideoPlayer::update() - { - if (!mAvContext) - return; - - double dt = mTimer.getMilliseconds () / 1000.f; - mTimer.reset (); - - //UpdateAudio(fTime); - //std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; - while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) + if (mState && mState->quit) { - while( - mVideoPacketQueue.getNumPackets()getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); } - void VideoPlayer::decodeNextVideoFrame () + void VideoPlayer::close() { - // Make sure there is something to decode - assert (mVideoPacketQueue.getNumPackets ()); + mState->quit = 1; - // Get the front frame and decode it - AVPacket packet; - mVideoPacketQueue.get(&packet, 1); + mState->parse_thread.join (); - int res; - int didDecodeFrame = 0; - res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet); + delete mState; + mState = NULL; - if (res < 0 || !didDecodeFrame) - throw std::runtime_error ("an error occured while decoding the video frame"); - - // Set video clock to the PTS of this packet (presentation timestamp) - double pts = 0; - if (packet.pts != -1.0) pts = packet.pts; - pts *= av_q2d(mVideoStream->time_base); - mVideoClock = pts; - - // Convert the frame to RGB - sws_scale(mSwsContext, - mRawFrame->data, mRawFrame->linesize, - 0, mVideoStream->codec->height, - mRGBAFrame->data, mRGBAFrame->linesize); - - - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); - Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); - pixelBuffer->blitFromMemory(pb); - - if (packet.data != NULL) av_free_packet(&packet); - } - - void VideoPlayer::close () - { mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); - deleteContext(); } - void VideoPlayer::deleteContext() + bool VideoPlayer::isPlaying () { - while (mVideoPacketQueue.getNumPackets ()) - { - AVPacket packet; - mVideoPacketQueue.get(&packet, 1); - if (packet.data != NULL) av_free_packet(&packet); - } - while (mAudioPacketQueue.getNumPackets ()) - { - AVPacket packet; - mAudioPacketQueue.get(&packet, 1); - if (packet.data != NULL) av_free_packet(&packet); - } - - if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); - if (mAudioStream && mAudioStream->codec != NULL) avcodec_close(mAudioStream->codec); - - avpicture_free((AVPicture *)mRGBAFrame); - - if (mRawFrame) - av_free(mRawFrame); - if (mRGBAFrame) - av_free(mRGBAFrame); - - sws_freeContext(mSwsContext); - - avformat_close_input(&mAvContext); - - mAvContext = NULL; + return mState != NULL; } - - - bool VideoPlayer::addToBuffer() - { - if(mAvContext) - { - AVPacket packet; - if (av_read_frame(mAvContext, &packet) >= 0) - { - if (packet.stream_index == mVideoStreamId) - { - // I don't believe this is necessary. - /* - if(mClock==0) - { - mClock = packet.dts; - mClock *= av_q2d(mVideoStream->time_base); - std::cout << "Initializing clock to: " << mClock << std::endl; - } - */ - - mVideoPacketQueue.put(&packet); - - return true; - } - else if (packet.stream_index == mAudioStreamId && mAudioStream) - { - mAudioPacketQueue.put(&packet); - return true; - } - else - { - av_free_packet(&packet); - return false; - } - } - } - - return false; - } } - -//#endif diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 466bb79027..9766ba8eec 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -1,137 +1,162 @@ -#ifndef MWRENDER_VIDEOPLAYER_H -#define MWRENDER_VIDEOPLAYER_H +#ifndef VIDEOPLAYER_H +#define VIDEOPLAYER_H -//#ifdef OPENMW_USE_FFMPEG +#include +#include + +#include - -#include - -#include -#include -#include - -namespace Ogre +#define __STDC_CONSTANT_MACROS +#include +extern "C" { - class Rectangle2D; - class SceneManager; - class TextureUnitState; +#include +#include +#include } -struct AVFormatContext; -struct AVCodecContext; -struct AVCodec; -struct AVStream; -struct AVFrame; -struct SwsContext; -struct AVPacket; -struct AVPacketList; +#include +#include + +#include +#include + +#define SDL_AUDIO_BUFFER_SIZE 1024 +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 1 +#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER + + namespace MWRender { - /// A simple queue used to queue raw audio and video data. - class AVPacketQueue - { - public: - AVPacketQueue(); - int put(AVPacket* pkt); - int get(AVPacket* pkt, int block); - bool isEmpty() const { return mNumPackets == 0; } - int getNumPackets() const { return mNumPackets; } - int getSize() const { return mSize; } + struct PacketQueue { + PacketQueue () : + first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + {} + AVPacketList *first_pkt, *last_pkt; + int nb_packets; + int size; - private: - AVPacketList* mFirstPacket; - AVPacketList* mLastPacket; - int mNumPackets; - int mSize; + boost::mutex mutex; + boost::condition_variable cond; + }; + struct VideoPicture { + VideoPicture () : + data(NULL), pts(0) + {} + uint8_t* data; + + double pts; + }; + + static void packet_queue_flush(PacketQueue *q); + + struct VideoState { + VideoState () : + videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), + external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), + audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), + video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL) + {} + + + ~VideoState() + { + packet_queue_flush (&audioq); + packet_queue_flush (&videoq); + + if (pictq_size >= 1) + free (pictq[0].data); + } + + int videoStream, audioStream; + + int av_sync_type; + double external_clock; /* external clock base */ + int64_t external_clock_time; + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; + DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); + unsigned int audio_buf_size; + unsigned int audio_buf_index; + AVPacket audio_pkt; + uint8_t *audio_pkt_data; + int audio_pkt_size; + int audio_hw_buf_size; + double audio_diff_cum; /* used for AV difference average computation */ + double audio_diff_avg_coef; + double audio_diff_threshold; + int audio_diff_avg_count; + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double video_clock; ///. This is done for portability +# reasons because not all systems place things in SDL/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PATH(SDL_INCLUDE_DIR SDL.h + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES include/SDL include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL12 + /usr/local/include/SDL11 # FreeBSD ports + /usr/include/SDL12 + /usr/include/SDL11 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}") + +# SDL-1.1 is the name used by FreeBSD ports... +# don't confuse it for the version number. +FIND_LIBRARY(SDL_LIBRARY_TEMP + NAMES SDL SDL-1.1 + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}") + +IF(NOT SDL_BUILDING_LIBRARY) + IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDLmain for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDLMAIN_LIBRARY + NAMES SDLmain SDLmain-1.1 + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL_BUILDING_LIBRARY) + +# SDL may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL_FOUND "NO") +IF(SDL_LIBRARY_TEMP) + # For SDLmain + IF(NOT SDL_BUILDING_LIBRARY) + IF(SDLMAIN_LIBRARY) + SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP}) + ENDIF(SDLMAIN_LIBRARY) + ENDIF(NOT SDL_BUILDING_LIBRARY) + + # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL_FOUND "YES") +ENDIF(SDL_LIBRARY_TEMP) + +#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}") + From 0ce5ade6d816cb4eaf9867bfb596e316d8a9806a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Dec 2012 23:06:06 +0100 Subject: [PATCH 008/147] DataStreamPtr fix, indentation fixes --- apps/openmw/mwrender/videoplayer.cpp | 112 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 2 + 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 3ddc7961c7..40d1a71886 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,49 +11,49 @@ namespace MWRender int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - int num_read = stream->size() - stream->tell(); + int num_read = stream->size() - stream->tell(); - if (num_read > buf_size) - num_read = buf_size; + if (num_read > buf_size) + num_read = buf_size; - stream->read(buf, num_read); - return num_read; + stream->read(buf, num_read); + return num_read; } int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - int num_write = stream->size() - stream->tell(); + int num_write = stream->size() - stream->tell(); - if (num_write > buf_size) - num_write = buf_size; + if (num_write > buf_size) + num_write = buf_size; - stream->write (buf, num_write); - return num_write; + stream->write (buf, num_write); + return num_write; } int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - switch (whence) - { + switch (whence) + { case SEEK_SET: - stream->seek(offset); + stream->seek(offset); case SEEK_CUR: - stream->seek(stream->tell() + offset); + stream->seek(stream->tell() + offset); case SEEK_END: - stream->seek(stream->size() + offset); + stream->seek(stream->size() + offset); case AVSEEK_SIZE: - return stream->size(); + return stream->size(); default: - return -1; - } + return -1; + } - return stream->tell(); + return stream->tell(); } @@ -107,7 +107,7 @@ namespace MWRender if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) - q->last_pkt = NULL; + q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; @@ -247,21 +247,21 @@ namespace MWRender if(len1 < 0) { - /* if error, skip frame */ - is->audio_pkt_size = 0; - break; + /* if error, skip frame */ + is->audio_pkt_size = 0; + break; } is->audio_pkt_data += len1; is->audio_pkt_size -= len1; if(data_size <= 0) { /* No data yet, get more frames */ - continue; + continue; } pts = is->audio_clock; *pts_ptr = pts; n = 2 * is->audio_st->codec->channels; is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + (double)(n * is->audio_st->codec->sample_rate); /* We have data, return it and come back for more later */ return data_size; @@ -295,13 +295,13 @@ namespace MWRender /* We have already sent all our data; get more */ audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); if(audio_size < 0) { - /* If error, output silence */ - is->audio_buf_size = 1024; - memset(is->audio_buf, 0, is->audio_buf_size); + /* If error, output silence */ + is->audio_buf_size = 1024; + memset(is->audio_buf, 0, is->audio_buf_size); } else { - audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); - is->audio_buf_size = audio_size; + audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, + audio_size, pts); + is->audio_buf_size = audio_size; } is->audio_buf_index = 0; } @@ -387,8 +387,8 @@ namespace MWRender delay = vp->pts - is->frame_last_pts; /* the pts from last time */ if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; } /* save for next time */ is->frame_last_delay = delay; @@ -396,27 +396,27 @@ namespace MWRender /* update delay to sync to audio if not master source */ if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - if(diff <= -sync_threshold) { - delay = 0; - } else if(diff >= sync_threshold) { - delay = 2 * delay; - } - } + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { + if(diff <= -sync_threshold) { + delay = 0; + } else if(diff >= sync_threshold) { + delay = 2 * delay; + } + } } is->frame_timer += delay; /* computer the REAL delay */ actual_delay = is->frame_timer - (av_gettime() / 1000000.0); if(actual_delay < 0.010) { - /* Really it should skip the picture instead */ - actual_delay = 0.010; + /* Really it should skip the picture instead */ + actual_delay = 0.010; } schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); @@ -425,14 +425,15 @@ namespace MWRender /* update queue for next picture! */ if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { - is->pictq_rindex = 0; + is->pictq_rindex = 0; } is->pictq_mutex.lock(); is->pictq_size--; is->pictq_cond.notify_one (); is->pictq_mutex.unlock (); } - } else { + } + else { schedule_refresh(is, 100); } } @@ -563,7 +564,7 @@ namespace MWRender if(frameFinished) { pts = synchronize_video(is, pFrame, pts); if(queue_picture(is, pFrame, pts) < 0) { - break; + break; } } av_free_packet(packet); @@ -673,10 +674,11 @@ namespace MWRender Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); if(stream.isNull ()) throw std::runtime_error("Failed to open video resource"); + is->stream = stream; AVIOContext *ioContext = 0; - ioContext = avio_alloc_context(NULL, 0, 0, &stream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); if (!ioContext) throw std::runtime_error("Failed to allocate ioContext "); @@ -845,7 +847,7 @@ namespace MWRender } if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); } void VideoPlayer::close() diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 9766ba8eec..67a22a506d 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -108,6 +108,8 @@ namespace MWRender AVStream *video_st; PacketQueue videoq; + Ogre::DataStreamPtr stream; + SwsContext* sws_context; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; From faad64b2545608a8781800ebfcf3ab265411c944 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 01:13:53 +0100 Subject: [PATCH 009/147] Esc cancels the video --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 6 files changed, 15 insertions(+) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 526f865bc6..198a20d454 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -306,6 +306,7 @@ namespace MWBase /// \todo this does not belong here virtual void playVideo(const std::string& name) = 0; + virtual void stopVideo() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6a1c3aa9bd..f7cc0f88e3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -508,6 +508,8 @@ namespace MWInput { if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) mWindows.popGuiMode(); + else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video) + MWBase::Environment::get().getWorld ()->stopVideo (); else mWindows.pushGuiMode (MWGui::GM_MainMenu); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5663ded092..811d000a9f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -931,4 +931,9 @@ void RenderingManager::playVideo(const std::string& name) mVideoPlayer->playVideo ("video/" + name); } +void RenderingManager::stopVideo() +{ + mVideoPlayer->close (); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ee64a371e6..02b4bcef61 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -197,6 +197,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); void playVideo(const std::string& name); + void stopVideo(); protected: virtual void windowResized(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2257b8a072..8eb121d374 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1291,4 +1291,9 @@ namespace MWWorld { mRendering->playVideo(name); } + + void World::stopVideo () + { + mRendering->stopVideo(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 349fd163cd..1c13529136 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -335,6 +335,7 @@ namespace MWWorld /// \todo this does not belong here virtual void playVideo(const std::string& name); + virtual void stopVideo(); }; } From fe384a1600b1bf057faff7d7d4f1fa5a1bc58ae1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 01:30:34 +0100 Subject: [PATCH 010/147] pause 3d rendering while the video plays --- apps/openmw/mwrender/videoplayer.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 40d1a71886..46fa4b9e93 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -816,6 +816,17 @@ namespace MWRender MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + // Turn off rendering except the GUI + mSceneMgr->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. + for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) + { + if (i > 0 && i < 96) + mSceneMgr->addSpecialCaseRenderQueue(i); + } + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + mState = new VideoState; // Register all formats and codecs @@ -861,6 +872,9 @@ namespace MWRender mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); } bool VideoPlayer::isPlaying () From 9e2d4f8b7cc4751552c8f4d9a31dbf7ed04c083c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:32:10 -0800 Subject: [PATCH 011/147] Avoid potential NULL dereference --- apps/openmw/mwrender/videoplayer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 46fa4b9e93..612b37c0a1 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -142,16 +142,12 @@ namespace MWRender } double get_audio_clock(VideoState *is) { double pts; - int hw_buf_size, bytes_per_sec, n; pts = is->audio_clock; /* maintained in the audio thread */ - hw_buf_size = is->audio_buf_size - is->audio_buf_index; - bytes_per_sec = 0; - n = is->audio_st->codec->channels * 2; if(is->audio_st) { - bytes_per_sec = is->audio_st->codec->sample_rate * n; - } - if(bytes_per_sec) { + int n = is->audio_st->codec->channels * 2; + int bytes_per_sec = is->audio_st->codec->sample_rate * n; + int hw_buf_size = is->audio_buf_size - is->audio_buf_index; pts -= (double)hw_buf_size / bytes_per_sec; } return pts; From 3519934f273a2ddbe81e284c811cd577cf68e1fd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:36:04 -0800 Subject: [PATCH 012/147] Add a missing return value --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 612b37c0a1..b65458b632 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -646,7 +646,7 @@ namespace MWRender break; } - + return 0; } int decode_interrupt_cb(void) { From 277248cdcbf1caa3673af3ee45957690aec98579 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:43:07 -0800 Subject: [PATCH 013/147] Fix some "comparison between signed and unsigned" warnings --- apps/openmw/mwrender/videoplayer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b65458b632..7e7fcd0d6f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -275,7 +275,7 @@ namespace MWRender is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if(pkt->pts != AV_NOPTS_VALUE) { + if(pkt->pts != (int64_t)AV_NOPTS_VALUE) { is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -345,7 +345,8 @@ namespace MWRender if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + if (texture.isNull () || texture->getWidth() != (size_t)is->video_st->codec->width || + texture->getHeight() != (size_t)is->video_st->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( @@ -545,10 +546,10 @@ namespace MWRender // Decode video frame len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet); - if(packet->dts == AV_NOPTS_VALUE + if(packet->dts == (int64_t)AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; - } else if(packet->dts != AV_NOPTS_VALUE) { + } else if(packet->dts != (int64_t)AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; @@ -582,7 +583,7 @@ namespace MWRender AVCodec *codec; SDL_AudioSpec wanted_spec, spec; - if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams) { return -1; } @@ -661,7 +662,7 @@ namespace MWRender int video_index = -1; int audio_index = -1; - int i; + unsigned int i; is->videoStream=-1; is->audioStream=-1; From 2efdafecd9d7c6b5fb0d044cf67cefccb5db461b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 20:11:48 -0800 Subject: [PATCH 014/147] Indentation fixes --- apps/openmw/mwrender/videoplayer.cpp | 98 +++++++++++++--------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e7fcd0d6f..c09de3acc3 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -188,39 +188,39 @@ namespace MWRender diff = get_audio_clock(is) - ref_clock; if(diff < AV_NOSYNC_THRESHOLD) { // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef - * is->audio_diff_cum; + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { - is->audio_diff_avg_count++; + is->audio_diff_avg_count++; } else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); - if(wanted_size < min_size) { - wanted_size = min_size; - } else if (wanted_size > max_size) { - wanted_size = max_size; - } - if(wanted_size < samples_size) { - /* remove samples */ - samples_size = wanted_size; - } else if(wanted_size > samples_size) { - uint8_t *samples_end, *q; - int nb; - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = (uint8_t *)samples + samples_size - n; - q = samples_end + n; - while(nb > 0) { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - samples_size = wanted_size; - } - } + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + if(wanted_size < min_size) { + wanted_size = min_size; + } else if (wanted_size > max_size) { + wanted_size = max_size; + } + if(wanted_size < samples_size) { + /* remove samples */ + samples_size = wanted_size; + } else if(wanted_size > samples_size) { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = (uint8_t *)samples + samples_size - n; + q = samples_end + n; + while(nb > 0) { + memcpy(q, samples_end, n); + q += n; + nb -= n; + } + samples_size = wanted_size; + } + } } } else { /* difference is TOO big; reset diff stuff */ @@ -239,7 +239,7 @@ namespace MWRender while(is->audio_pkt_size > 0) { data_size = buf_size; len1 = avcodec_decode_audio3(is->audio_st->codec, - (int16_t *)audio_buf, &data_size, pkt); + (int16_t*)audio_buf, &data_size, pkt); if(len1 < 0) { @@ -250,14 +250,14 @@ namespace MWRender is->audio_pkt_data += len1; is->audio_pkt_size -= len1; if(data_size <= 0) { - /* No data yet, get more frames */ + /* No data yet, get more frames */ continue; } pts = is->audio_clock; *pts_ptr = pts; n = 2 * is->audio_st->codec->channels; is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + (double)(n * is->audio_st->codec->sample_rate); /* We have data, return it and come back for more later */ return data_size; @@ -296,7 +296,7 @@ namespace MWRender memset(is->audio_buf, 0, is->audio_buf_size); } else { audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); + audio_size, pts); is->audio_buf_size = audio_size; } is->audio_buf_index = 0; @@ -397,7 +397,7 @@ namespace MWRender diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ + FFPlay still doesn't "know if this is the best guess." */ sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; if(fabs(diff) < AV_NOSYNC_THRESHOLD) { if(diff <= -sync_threshold) { @@ -442,8 +442,7 @@ namespace MWRender /* wait until we have a new pic */ { boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && - !is->quit) { + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); } } @@ -458,9 +457,9 @@ namespace MWRender if(is->sws_context == NULL) { int w = is->video_st->codec->width; int h = is->video_st->codec->height; - is->sws_context = sws_getContext(w, h, - is->video_st->codec->pix_fmt, w, h, - PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, + w, h, PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); if(is->sws_context == NULL) throw std::runtime_error("Cannot initialize the conversion context!\n"); } @@ -468,7 +467,7 @@ namespace MWRender vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); + 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); vp->pts = pts; @@ -544,8 +543,7 @@ namespace MWRender // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet); + len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet); if(packet->dts == (int64_t)AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; @@ -698,12 +696,10 @@ namespace MWRender av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && video_index < 0) { video_index=i; } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audio_index < 0) { audio_index=i; } } @@ -717,15 +713,13 @@ namespace MWRender if(is->videoStream >= 0 /*|| is->audioStream < 0*/) { - // main decode loop - for(;;) { if(is->quit) { break; } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { + if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; } From c2e1595445b73dc349b7f05b300ee5ac1b7a9c82 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 21:01:32 -0800 Subject: [PATCH 015/147] Treat paused sounds as still playing --- apps/openmw/mwsound/openal_output.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index e5169d878b..1e862d77ad 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -254,7 +254,7 @@ bool OpenAL_SoundStream::isPlaying() alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); - if(state == AL_PLAYING) + if(state == AL_PLAYING || state == AL_PAUSED) return true; return !mIsFinished; } @@ -393,7 +393,7 @@ bool OpenAL_Sound::isPlaying() alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); - return state==AL_PLAYING; + return state==AL_PLAYING || state==AL_PAUSED; } void OpenAL_Sound::update() From a62d5bbfe41f2041900df3cfce46b4b7f20515e9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 23:54:41 -0800 Subject: [PATCH 016/147] Sleep using the absolute time, so the thread creation doesn't add to the wait --- apps/openmw/mwrender/videoplayer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c09de3acc3..afdb5e82db 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -321,9 +321,9 @@ namespace MWRender } */ - void timer_callback (int delay, VideoState* is) + void timer_callback (boost::system_time t, VideoState* is) { - boost::this_thread::sleep (boost::posix_time::milliseconds(delay)); + boost::this_thread::sleep (t); is->refresh++; } @@ -332,8 +332,8 @@ namespace MWRender { //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); //is->refresh_queue.push_back (delay); - - boost::thread (boost::bind(&timer_callback, delay, is)); + boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); + boost::thread (boost::bind(&timer_callback, t, is)); } void video_display(VideoState *is) From e82c4afd500aa808a81da700d433b577020a837f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 00:36:52 -0800 Subject: [PATCH 017/147] close SDL when closing the video, not after the video loop is finished --- apps/openmw/mwrender/videoplayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index afdb5e82db..8ae51deba7 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -565,8 +565,6 @@ namespace MWRender av_free_packet(packet); } - SDL_CloseAudio(); - av_free(pFrame); avpicture_free((AVPicture *)is->rgbaFrame); @@ -861,6 +859,8 @@ namespace MWRender delete mState; mState = NULL; + SDL_CloseAudio(); + mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); From 973b5faf25405e2e6ac94c5682844b6169e976de Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 01:32:16 -0800 Subject: [PATCH 018/147] Keep track of all allocated sources --- apps/openmw/mwsound/openal_output.cpp | 12 ++++++------ apps/openmw/mwsound/openal_output.hpp | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 1e862d77ad..abd63590ff 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -504,8 +504,9 @@ void OpenAL_Output::init(const std::string &devname) ALuint src = 0; alGenSources(1, &src); throwALerror(); - mFreeSources.push_back(src); + mSources.push_back(src); } + mFreeSources.insert(mFreeSources.begin(), mSources.begin(), mSources.end()); } catch(std::exception &e) { @@ -521,11 +522,10 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); - while(!mFreeSources.empty()) - { - alDeleteSources(1, &mFreeSources.front()); - mFreeSources.pop_front(); - } + mFreeSources.clear(); + if(mSources.size() > 0) + alDeleteSources(mSources.size(), &mSources[0]); + mSources.clear(); mBufferRefs.clear(); mUnusedBuffers.clear(); diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index fecffa5752..4177c6385d 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -21,6 +21,9 @@ namespace MWSound ALCdevice *mDevice; ALCcontext *mContext; + typedef std::vector IDVec; + IDVec mSources; + typedef std::deque IDDq; IDDq mFreeSources; IDDq mUnusedBuffers; From 2c1eceb9f093c209240a47be839acb6d086dae07 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 02:33:12 -0800 Subject: [PATCH 019/147] Add methods to pause and stop all playing sounds (and music) --- apps/openmw/mwbase/soundmanager.hpp | 6 ++++++ apps/openmw/mwrender/videoplayer.cpp | 3 +++ apps/openmw/mwsound/openal_output.cpp | 27 +++++++++++++++++++++++++ apps/openmw/mwsound/openal_output.hpp | 3 +++ apps/openmw/mwsound/sound_output.hpp | 3 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 13 ++++++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 6 ++++++ 7 files changed, 61 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 92c177ff30..2fa98cfee0 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -112,6 +112,12 @@ namespace MWBase virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? + virtual void pauseAllSounds() = 0; + ///< Pauses all currently playing sounds, including music. + + virtual void resumeAllSounds() = 0; + ///< Resumes all previously paused sounds. + virtual void update(float duration) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8ae51deba7..1334c1bca1 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -3,6 +3,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" @@ -821,6 +822,7 @@ namespace MWRender // Register all formats and codecs av_register_all(); + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); if(SDL_Init(SDL_INIT_AUDIO)) { throw std::runtime_error("Failed to initialize SDL"); } @@ -860,6 +862,7 @@ namespace MWRender mState = NULL; SDL_CloseAudio(); + MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index abd63590ff..bc6102beb1 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -814,6 +814,33 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 } +void OpenAL_Output::pauseAllSounds() +{ + IDVec sources = mSources; + IDDq::const_iterator iter = mFreeSources.begin(); + while(iter != mFreeSources.end()) + { + sources.erase(std::find(sources.begin(), sources.end(), *iter)); + iter++; + } + if(sources.size() > 0) + alSourcePausev(sources.size(), &sources[0]); +} + +void OpenAL_Output::resumeAllSounds() +{ + IDVec sources = mSources; + IDDq::const_iterator iter = mFreeSources.begin(); + while(iter != mFreeSources.end()) + { + sources.erase(std::find(sources.begin(), sources.end(), *iter)); + iter++; + } + if(sources.size() > 0) + alSourcePlayv(sources.size(), &sources[0]); +} + + OpenAL_Output::OpenAL_Output(SoundManager &mgr) : Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), mLastEnvironment(Env_Normal), mStreamThread(new StreamThread) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 4177c6385d..867150741b 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -52,6 +52,9 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); + virtual void pauseAllSounds(); + virtual void resumeAllSounds(); + OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 2680ec1dbe..1cc45559bf 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -31,6 +31,9 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; + virtual void pauseAllSounds() = 0; + virtual void resumeAllSounds() = 0; + Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8c4798c9d7..1414dbb767 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -404,6 +404,19 @@ namespace MWSound } + void SoundManager::pauseAllSounds() + { + if(mOutput->isInitialized()) + mOutput->pauseAllSounds(); + } + + void SoundManager::resumeAllSounds() + { + if(mOutput->isInitialized()) + mOutput->resumeAllSounds(); + } + + void SoundManager::updateRegionSound(float duration) { MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index a84aa3b9a7..aaa362d848 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -127,6 +127,12 @@ namespace MWSound virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? + virtual void pauseAllSounds(); + ///< Pauses all currently playing sounds, including music. + + virtual void resumeAllSounds(); + ///< Resumes all previously paused sounds. + virtual void update(float duration); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); From 18d8c767bd8c521b2f06eecadd762de00b516742 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 15:15:55 +0100 Subject: [PATCH 020/147] fix a bunch of warnings, improved error handling, initialize texture to black --- apps/openmw/mwrender/videoplayer.cpp | 268 +++++++++++++------------ apps/openmw/mwrender/videoplayer.hpp | 11 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 6 +- 3 files changed, 154 insertions(+), 131 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 46fa4b9e93..07fa723e20 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -125,21 +125,23 @@ namespace MWRender } return ret; } - static void packet_queue_flush(PacketQueue *q) { + + void PacketQueue::flush() { AVPacketList *pkt, *pkt1; - q->mutex.lock(); - for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { + this->mutex.lock(); + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_free_packet(&pkt->pkt); av_freep(&pkt); } - q->last_pkt = NULL; - q->first_pkt = NULL; - q->nb_packets = 0; - q->size = 0; - q->mutex.unlock (); + this->last_pkt = NULL; + this->first_pkt = NULL; + this->nb_packets = 0; + this->size = 0; + this->mutex.unlock (); } + double get_audio_clock(VideoState *is) { double pts; int hw_buf_size, bytes_per_sec, n; @@ -279,7 +281,7 @@ namespace MWRender is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if(pkt->pts != AV_NOPTS_VALUE) { + if((uint64_t)pkt->pts != AV_NOPTS_VALUE) { is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -349,7 +351,7 @@ namespace MWRender if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + if (texture.isNull () || static_cast(texture->getWidth()) != is->video_st->codec->width || static_cast(texture->getHeight()) != is->video_st->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( @@ -364,6 +366,7 @@ namespace MWRender Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); + is->display_ready = 1; } free(vp->data); @@ -527,7 +530,7 @@ namespace MWRender int video_thread(void *arg) { VideoState *is = (VideoState *)arg; AVPacket pkt1, *packet = &pkt1; - int len1, frameFinished; + int frameFinished; AVFrame *pFrame; double pts; @@ -547,12 +550,15 @@ namespace MWRender // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet); - if(packet->dts == AV_NOPTS_VALUE + if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, + packet) < 0) + { + throw std::runtime_error("Error decoding video frame"); + } + if((uint64_t)packet->dts == AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; - } else if(packet->dts != AV_NOPTS_VALUE) { + } else if((uint64_t)packet->dts != AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; @@ -586,7 +592,7 @@ namespace MWRender AVCodec *codec; SDL_AudioSpec wanted_spec, spec; - if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { return -1; } @@ -650,127 +656,141 @@ namespace MWRender break; } - + return 0; } int decode_interrupt_cb(void) { return (global_video_state && global_video_state->quit); } - int decode_thread(void *arg) { - + int decode_thread(void *arg) + { VideoState *is = (VideoState *)arg; - AVFormatContext *pFormatCtx = avformat_alloc_context (); - AVPacket pkt1, *packet = &pkt1; - - int video_index = -1; - int audio_index = -1; - int i; - - is->videoStream=-1; - is->audioStream=-1; - is->quit = 0; - - Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); - if(stream.isNull ()) - throw std::runtime_error("Failed to open video resource"); - is->stream = stream; - - AVIOContext *ioContext = 0; - - ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throw std::runtime_error("Failed to allocate ioContext "); - - pFormatCtx->pb = ioContext; - - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); - - // Retrieve stream information - if(avformat_find_stream_info(pFormatCtx, NULL)<0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); - - for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { - video_index=i; - } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { - audio_index=i; - } - } - - if(audio_index >= 0) { - stream_component_open(is, audio_index, pFormatCtx); - } - if(video_index >= 0) { - stream_component_open(is, video_index, pFormatCtx); - } - - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) + try { + AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVPacket pkt1, *packet = &pkt1; - // main decode loop + int video_index = -1; + int audio_index = -1; + unsigned int i; - for(;;) { - if(is->quit) { - break; + is->videoStream=-1; + is->audioStream=-1; + is->quit = 0; + + Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); + if(stream.isNull ()) + throw std::runtime_error("Failed to open video resource"); + is->stream = stream; + + AVIOContext *ioContext = 0; + + ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throw std::runtime_error("Failed to allocate ioContext "); + + pFormatCtx->pb = ioContext; + + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); + + // Retrieve stream information + if(avformat_find_stream_info(pFormatCtx, NULL)<0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); + + for(i=0; inb_streams; i++) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && + video_index < 0) { + video_index=i; } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - continue; - } - if(av_read_frame(pFormatCtx, packet) < 0) { - break; - } - // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) { - packet_queue_put(&is->videoq, packet); - } else if(packet->stream_index == is->audioStream) { - packet_queue_put(&is->audioq, packet); - } else { - av_free_packet(packet); + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && + audio_index < 0) { + audio_index=i; } } - /* all done - wait for it */ - while(!is->quit) { - // EOF reached, all packets processed, we can exit now - if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + if(audio_index >= 0) { + stream_component_open(is, audio_index, pFormatCtx); } + if(video_index >= 0) { + stream_component_open(is, video_index, pFormatCtx); + } + + if(is->videoStream >= 0 /*|| is->audioStream < 0*/) + { + + // main decode loop + + for(;;) { + if(is->quit) { + break; + } + if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; + } + if(av_read_frame(pFormatCtx, packet) < 0) { + break; + } + // Is this a packet from the video stream? + if(packet->stream_index == is->videoStream) { + packet_queue_put(&is->videoq, packet); + } else if(packet->stream_index == is->audioStream) { + packet_queue_put(&is->audioq, packet); + } else { + av_free_packet(packet); + } + } + /* all done - wait for it */ + while(!is->quit) { + // EOF reached, all packets processed, we can exit now + if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + } + + + is->quit = 1; + + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); + + is->video_thread.join(); + + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); + + sws_freeContext (is->sws_context); + + avformat_close_input(&pFormatCtx); + pFormatCtx = NULL; + + av_free(ioContext); + } + catch (std::runtime_error& e) + { + std::cerr << "An error occured playing the video: " << e.what () << std::endl; + is->quit = 1; + } + catch (Ogre::Exception& e) + { + std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; + is->quit = 1; } - - is->quit = 1; - - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); - - is->video_thread.join(); - - if (is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if (is->videoStream >= 0) - avcodec_close(is->video_st->codec); - - sws_freeContext (is->sws_context); - - av_close_input_file(pFormatCtx); - pFormatCtx = NULL; - - av_free(ioContext); return 0; } @@ -857,8 +877,10 @@ namespace MWRender close(); } - if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) + if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + else + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("black.png"); } void VideoPlayer::close() diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 67a22a506d..d9dc6ded00 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -48,6 +48,8 @@ namespace MWRender boost::mutex mutex; boost::condition_variable cond; + + void flush (); }; struct VideoPicture { VideoPicture () : @@ -58,8 +60,6 @@ namespace MWRender double pts; }; - static void packet_queue_flush(PacketQueue *q); - struct VideoState { VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), @@ -67,14 +67,14 @@ namespace MWRender audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL) + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) {} ~VideoState() { - packet_queue_flush (&audioq); - packet_queue_flush (&videoq); + audioq.flush (); + videoq.flush(); if (pictq_size >= 1) free (pictq[0].data); @@ -126,6 +126,7 @@ namespace MWRender int quit; int refresh; + int display_ready; }; enum { AV_SYNC_AUDIO_MASTER, diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 5f61ab8f08..9d5942111a 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -277,7 +277,7 @@ void FFmpeg_Decoder::open(const std::string &fname) ss << stream->mCodecCtx->codec_id; fail(ss.str()); } - if(avcodec_open(stream->mCodecCtx, codec) < 0) + if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); @@ -293,7 +293,7 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { - av_close_input_file(mFormatCtx); + avformat_close_input(&mFormatCtx); mFormatCtx = NULL; throw; } @@ -317,7 +317,7 @@ void FFmpeg_Decoder::close() AVIOContext* context = mFormatCtx->pb; av_free(context); mFormatCtx->pb = NULL; - av_close_input_file(mFormatCtx); + avformat_close_input(&mFormatCtx); } mFormatCtx = NULL; From 34e36fb85269775821efe8a07f7f47518bd1dbb1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 16:50:35 -0800 Subject: [PATCH 021/147] Add a method to get the time offset from sounds --- apps/openmw/mwsound/openal_output.cpp | 17 +++++++++++++++++ apps/openmw/mwsound/sound.hpp | 1 + 2 files changed, 18 insertions(+) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index bc6102beb1..4c62bb10f3 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -95,6 +95,7 @@ public: virtual void stop(); virtual bool isPlaying(); + virtual double getTimeOffset(); virtual void update(); void play(); @@ -259,6 +260,11 @@ bool OpenAL_SoundStream::isPlaying() return !mIsFinished; } +double OpenAL_SoundStream::getTimeOffset() +{ + return 0.0; +} + void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; @@ -348,6 +354,7 @@ public: virtual void stop(); virtual bool isPlaying(); + virtual double getTimeOffset(); virtual void update(); }; @@ -396,6 +403,16 @@ bool OpenAL_Sound::isPlaying() return state==AL_PLAYING || state==AL_PAUSED; } +double OpenAL_Sound::getTimeOffset() +{ + ALfloat t; + + alGetSourcef(mSource, AL_SEC_OFFSET, &t); + throwALerror(); + + return t; +} + void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 729147f75a..1b6f50ff47 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -26,6 +26,7 @@ namespace MWSound public: virtual void stop() = 0; virtual bool isPlaying() = 0; + virtual double getTimeOffset() = 0; void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } From 1fb9eef27b511281e7c179012a7082dcc169a5b7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:02:33 -0800 Subject: [PATCH 022/147] Detach the thread used for frame timing --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9cfe703b42..27fb89130e 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -336,7 +336,7 @@ namespace MWRender //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); //is->refresh_queue.push_back (delay); boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread (boost::bind(&timer_callback, t, is)); + boost::thread (boost::bind(&timer_callback, t, is)).detach(); } void video_display(VideoState *is) From 9c831d303983c5bf77f92ec05d7f1ae8555ed1d9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:19:44 -0800 Subject: [PATCH 023/147] Add a decoder method to get the "file" name --- apps/openmw/mwsound/audiere_decoder.cpp | 11 ++++++++++- apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 5 +++++ apps/openmw/mwsound/ffmpeg_decoder.hpp | 1 + apps/openmw/mwsound/mpgsnd_decoder.cpp | 5 +++++ apps/openmw/mwsound/sound_decoder.hpp | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index 4e73573a74..c9b3d11d1f 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -53,6 +53,9 @@ public: : mStream(stream), refs(1) { } virtual ~OgreFile() { } + + Ogre::String getName() + { return mStream->getName(); } }; @@ -60,7 +63,7 @@ void Audiere_Decoder::open(const std::string &fname) { close(); - audiere::FilePtr file(new OgreFile(mResourceMgr.openResource(fname))); + mSoundFile = audiere::FilePtr(new OgreFile(mResourceMgr.openResource(fname))); mSoundSource = audiere::OpenSampleSource(file); int channels, srate; @@ -86,9 +89,15 @@ void Audiere_Decoder::open(const std::string &fname) void Audiere_Decoder::close() { + mSoundFile = NULL; mSoundSource = NULL; } +std::string Audiere_Decoder::getName() +{ + return mSoundFile->getName(); +} + void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { *samplerate = mSampleRate; diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 0ad026d515..1e1528880a 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -12,6 +12,7 @@ namespace MWSound { class Audiere_Decoder : public Sound_Decoder { + audiere::FilePtr mSoundFile; audiere::SampleSourcePtr mSoundSource; int mSampleRate; SampleType mSampleType; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 9d5942111a..6b3a7f9cd0 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -324,6 +324,11 @@ void FFmpeg_Decoder::close() mDataStream.setNull(); } +std::string FFmpeg_Decoder::getName() +{ + return mFormatCtx->filename; +} + void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { if(mStreams.empty()) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index a6e80fc9b6..88115ce3fc 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -36,6 +36,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.cpp b/apps/openmw/mwsound/mpgsnd_decoder.cpp index 7f7a84889a..4ec11b3490 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.cpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.cpp @@ -155,6 +155,11 @@ void MpgSnd_Decoder::close() mDataStream.setNull(); } +std::string MpgSnd_Decoder::getName() +{ + return mDataStream->getName(); +} + void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { if(!mSndFile && !mMpgFile) diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index 9c28d5ff55..d228a5e980 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -29,6 +29,7 @@ namespace MWSound virtual void open(const std::string &fname) = 0; virtual void close() = 0; + virtual std::string getName() = 0; virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual size_t read(char *buffer, size_t bytes) = 0; From 86bf6388c64ad0784bd428de479ce9eb81edb389 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:32:02 -0800 Subject: [PATCH 024/147] Pass a decoder to the playStream sound output method --- apps/openmw/mwsound/openal_output.cpp | 8 +++----- apps/openmw/mwsound/openal_output.hpp | 4 ++-- apps/openmw/mwsound/sound_output.hpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4c62bb10f3..521e6c357f 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -764,7 +764,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre } -MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src; @@ -774,12 +774,10 @@ MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volu src = mFreeSources.front(); mFreeSources.pop_front(); + if((flags&MWBase::SoundManager::Play_Loop)) + std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { - if((flags&MWBase::SoundManager::Play_Loop)) - std::cout <<"Warning: cannot loop stream "<open(fname); sound.reset(new OpenAL_SoundStream(*this, src, decoder)); } catch(std::exception &e) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 867150741b..ce126035fd 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -47,8 +47,8 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags); - virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); + float volume, float pitch, float min, float max, int flags); + virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 1cc45559bf..1229f87d0e 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -26,8 +26,8 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags) = 0; - virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; + float volume, float pitch, float min, float max, int flags) = 0; + virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1414dbb767..2a489a7894 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -167,7 +167,11 @@ namespace MWSound { float basevol = mMasterVolume * mMusicVolume; stopMusic(); - mMusic = mOutput->streamSound(filename, basevol, 1.0f, Play_NoEnv); + + DecoderPtr decoder = getDecoder(); + decoder->open(filename); + + mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); mMusic->mBaseVolume = basevol; mMusic->mFlags = Play_NoEnv; } From 1571243ef00dce1c22753c791cfb879f2b50ef2f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 23:13:35 -0800 Subject: [PATCH 025/147] Implement getTimeOffset for OpenAL_SoundStream --- apps/openmw/mwsound/openal_output.cpp | 132 ++++++++++++++++++-------- 1 file changed, 91 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 521e6c357f..658483b02c 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -65,6 +65,18 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) return AL_NONE; } +static ALint getBufferSampleCount(ALuint buf) +{ + ALint size, bits, channels; + + alGetBufferi(buf, AL_SIZE, &size); + alGetBufferi(buf, AL_BITS, &bits); + alGetBufferi(buf, AL_CHANNELS, &channels); + throwALerror(); + + return size / channels * 8 / bits; +} + // // A streaming OpenAL sound. // @@ -82,6 +94,9 @@ class OpenAL_SoundStream : public Sound ALsizei mSampleRate; ALuint mBufferSize; + ALuint mSamplesTotal; + ALuint mSamplesQueued; + DecoderPtr mDecoder; volatile bool mIsFinished; @@ -172,7 +187,8 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mDecoder(decoder), mIsFinished(true) + : mOutput(output), mSource(src), mSamplesTotal(0), mSamplesQueued(0), + mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -215,16 +231,19 @@ OpenAL_SoundStream::~OpenAL_SoundStream() void OpenAL_SoundStream::play() { std::vector data(mBufferSize); + ALuint count = 0; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); + mSamplesQueued = 0; for(ALuint i = 0;i < sNumBuffers;i++) { size_t got; got = mDecoder->read(&data[0], data.size()); alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate); + count += getBufferSampleCount(mBuffers[i]); } throwALerror(); @@ -232,6 +251,9 @@ void OpenAL_SoundStream::play() alSourcePlay(mSource); throwALerror(); + mSamplesTotal += count; + mSamplesQueued = count; + mIsFinished = false; mOutput.mStreamThread->add(this); } @@ -244,8 +266,10 @@ void OpenAL_SoundStream::stop() alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); + mSamplesQueued = 0; mDecoder->rewind(); + mSamplesTotal = 0; } bool OpenAL_SoundStream::isPlaying() @@ -262,7 +286,21 @@ bool OpenAL_SoundStream::isPlaying() double OpenAL_SoundStream::getTimeOffset() { - return 0.0; + ALint state = AL_STOPPED; + ALfloat offset = 0.0f; + double t; + + mOutput.mStreamThread->mMutex.lock(); + alGetSourcef(mSource, AL_SEC_OFFSET, &offset); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + if(state == AL_PLAYING || state == AL_PAUSED) + t = (double)(mSamplesTotal - mSamplesQueued)/(double)mSampleRate + offset; + else + t = (double)mSamplesTotal / (double)mSampleRate; + mOutput.mStreamThread->mMutex.unlock(); + + throwALerror(); + return t; } void OpenAL_SoundStream::update() @@ -285,52 +323,64 @@ void OpenAL_SoundStream::update() bool OpenAL_SoundStream::process() { - bool finished = mIsFinished; - ALint processed, state; + try { + bool finished = mIsFinished; + ALint samples_unqueued = 0; + ALint samples_queued = 0; + ALint processed, state; - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); - throwALerror(); - - if(processed > 0) - { - std::vector data(mBufferSize); - do { - ALuint bufid; - size_t got; - - alSourceUnqueueBuffers(mSource, 1, &bufid); - processed--; - - if(finished) - continue; - - got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - } - } while(processed > 0); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); throwALerror(); - } - if(state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued; - - alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); - throwALerror(); - if(queued > 0) + if(processed > 0) { - alSourcePlay(mSource); + std::vector data(mBufferSize); + do { + ALuint bufid = 0; + size_t got; + + alSourceUnqueueBuffers(mSource, 1, &bufid); + samples_unqueued += getBufferSampleCount(bufid); + processed--; + + if(finished) + continue; + + got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + samples_queued += getBufferSampleCount(bufid); + } + } while(processed > 0); throwALerror(); } - } - mIsFinished = finished; - return !finished; + if(state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + if(queued > 0) + alSourcePlay(mSource); + throwALerror(); + } + + mSamplesQueued -= samples_unqueued; + mSamplesQueued += samples_queued; + mSamplesTotal += samples_queued; + mIsFinished = finished; + } + catch(std::exception &e) { + std::cout<< "Error updating stream \""<getName()<<"\"" < Date: Thu, 13 Dec 2012 00:05:57 -0800 Subject: [PATCH 026/147] Add a method to play an audio track with a custom decoder --- apps/openmw/mwbase/soundmanager.hpp | 5 +++++ apps/openmw/mwsound/soundmanagerimp.cpp | 20 ++++++++++++++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 5 +++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 2fa98cfee0..8d204bad4a 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -22,6 +22,8 @@ namespace MWWorld namespace MWSound { class Sound; + class Sound_Decoder; + typedef boost::shared_ptr DecoderPtr; } namespace MWBase @@ -89,6 +91,9 @@ namespace MWBase virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking + virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder) = 0; + ///< Play a 2D audio track, using a custom decoder + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2a489a7894..6bb3d59dd6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -275,6 +275,26 @@ namespace MWSound } + MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder) + { + MWBase::SoundPtr track; + if(!mOutput->isInitialized()) + return track; + try + { + float basevol = mMasterVolume; + + track = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); + track->mBaseVolume = basevol; + track->mFlags = Play_NoEnv; + } + catch(std::exception &e) + { + std::cout <<"Sound Error: "< DecoderPtr; - enum Environment { Env_Normal, Env_Underwater @@ -105,6 +103,9 @@ namespace MWSound virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking + virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder); + ///< Play a 2D audio track, using a custom decoder + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound, independently of 3D-position From 1ffaf6625a37ba8ba0f1463926dc8b94e2900623 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 01:42:20 -0800 Subject: [PATCH 027/147] Remove SDL for playing movie audio and prepare for using an audio track This breaks audio playback on movies --- apps/openmw/mwrender/videoplayer.cpp | 320 ++++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 71 +++--- 2 files changed, 225 insertions(+), 166 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 27fb89130e..fa179f8b10 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -4,7 +4,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" - +#include "../mwsound/sound_decoder.hpp" +#include "../mwsound/sound.hpp" namespace MWRender @@ -143,19 +144,13 @@ namespace MWRender this->mutex.unlock (); } - double get_audio_clock(VideoState *is) { - double pts; - - pts = is->audio_clock; /* maintained in the audio thread */ - if(is->audio_st) { - int n = is->audio_st->codec->channels * 2; - int bytes_per_sec = is->audio_st->codec->sample_rate * n; - int hw_buf_size = is->audio_buf_size - is->audio_buf_index; - pts -= (double)hw_buf_size / bytes_per_sec; - } - return pts; + double get_audio_clock(VideoState *is) + { + return is->AudioTrack->getTimeOffset(); } - double get_video_clock(VideoState *is) { + + double get_video_clock(VideoState *is) + { double delta; delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; @@ -173,86 +168,110 @@ namespace MWRender return get_external_clock(is); } } + +class MovieAudioDecoder : public MWSound::Sound_Decoder +{ + static void fail(const std::string &str) + { + throw std::runtime_error(str); + } + + VideoState *is; + /* Add or subtract samples to get a better sync, return new - audio buffer size */ - int synchronize_audio(VideoState *is, short *samples, - int samples_size, double pts) { - int n; - double ref_clock; + * audio buffer size */ + int synchronize_audio(uint8_t *samples, int samples_size, double pts) + { + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + return samples_size; + + double diff, avg_diff, ref_clock; + int wanted_size, min_size, max_size, n; + // int nb_samples; n = 2 * is->audio_st->codec->channels; - if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) { - double diff, avg_diff; - int wanted_size, min_size, max_size; - // int nb_samples; + ref_clock = get_master_clock(is); + diff = get_audio_clock(is) - ref_clock; + if(diff < AV_NOSYNC_THRESHOLD) + { + // accumulate the diffs + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) + is->audio_diff_avg_count++; + else + { + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) + { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); - ref_clock = get_master_clock(is); - diff = get_audio_clock(is) - ref_clock; - if(diff < AV_NOSYNC_THRESHOLD) { - // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { - is->audio_diff_avg_count++; - } else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); - if(wanted_size < min_size) { - wanted_size = min_size; - } else if (wanted_size > max_size) { - wanted_size = max_size; - } - if(wanted_size < samples_size) { - /* remove samples */ - samples_size = wanted_size; - } else if(wanted_size > samples_size) { - uint8_t *samples_end, *q; - int nb; - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = (uint8_t *)samples + samples_size - n; - q = samples_end + n; - while(nb > 0) { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - samples_size = wanted_size; + if(wanted_size < min_size) + wanted_size = min_size; + else if (wanted_size > max_size) + wanted_size = max_size; + + if(wanted_size < samples_size) + { + /* remove samples */ + samples_size = wanted_size; + } + else if(wanted_size > samples_size) + { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = samples + samples_size - n; + q = samples_end + n; + while(nb > 0) + { + memcpy(q, samples_end, n); + q += n; + nb -= n; } + samples_size = wanted_size; } } - } else { - /* difference is TOO big; reset diff stuff */ - is->audio_diff_avg_count = 0; - is->audio_diff_cum = 0; } } + else + { + /* difference is TOO big; reset diff stuff */ + is->audio_diff_avg_count = 0; + is->audio_diff_cum = 0; + } + return samples_size; } - int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) { - int len1, data_size, n; + + int audio_decode_frame(uint8_t *audio_buf, int buf_size, double *pts_ptr) + { AVPacket *pkt = &is->audio_pkt; + int len1, data_size, n; double pts; - for(;;) { - while(is->audio_pkt_size > 0) { + for(;;) + { + while(is->audio_pkt_size > 0) + { data_size = buf_size; + len1 = avcodec_decode_audio3(is->audio_st->codec, (int16_t*)audio_buf, &data_size, pkt); - - - if(len1 < 0) { + if(len1 < 0) + { /* if error, skip frame */ is->audio_pkt_size = 0; break; } is->audio_pkt_data += len1; is->audio_pkt_size -= len1; - if(data_size <= 0) { + if(data_size <= 0) + { /* No data yet, get more frames */ continue; } @@ -268,52 +287,111 @@ namespace MWRender if(pkt->data) av_free_packet(pkt); - if(is->quit) { + if(is->quit) return -1; - } + /* next packet */ - if(packet_queue_get(&is->audioq, pkt, 1) < 0) { + if(packet_queue_get(&is->audioq, pkt, 1) < 0) return -1; - } + is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if((uint64_t)pkt->pts != AV_NOPTS_VALUE) { + if((uint64_t)pkt->pts != AV_NOPTS_VALUE) is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; - } } } - void audio_callback(void *userdata, Uint8 *stream, int len) { - VideoState *is = (VideoState *)userdata; - int len1, audio_size; - double pts; + void open(const std::string&) + { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } - while(len > 0) { - if(is->audio_buf_index >= is->audio_buf_size) { + void close() { } + + std::string getName() + { return is->stream->getName(); } + + void rewind() { } + +public: + MovieAudioDecoder(VideoState *_is) : is(_is) { } + + void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) + { + if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_U8) + *type = MWSound::SampleType_UInt8; + else if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_S16) + *type = MWSound::SampleType_Int16; + else + fail(std::string("Unsupported sample format: ")+ + av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + + if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) + *chans = MWSound::ChannelConfig_Mono; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) + *chans = MWSound::ChannelConfig_Stereo; + else if(is->audio_st->codec->channel_layout == 0) + { + /* Unknown channel layout. Try to guess. */ + if(is->audio_st->codec->channels == 1) + *chans = MWSound::ChannelConfig_Mono; + else if(is->audio_st->codec->channels == 2) + *chans = MWSound::ChannelConfig_Stereo; + else + { + std::stringstream sstr("Unsupported raw channel count: "); + sstr << is->audio_st->codec->channels; + fail(sstr.str()); + } + } + else + { + char str[1024]; + av_get_channel_layout_string(str, sizeof(str), is->audio_st->codec->channels, + is->audio_st->codec->channel_layout); + fail(std::string("Unsupported channel layout: ")+str); + } + + *samplerate = is->audio_st->codec->sample_rate; + } + + size_t read(char *stream, size_t len) + { + size_t total = 0; + + while(total < len) + { + if(is->audio_buf_index >= is->audio_buf_size) + { + int audio_size; + double pts; /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); - if(audio_size < 0) { - /* If error, output silence */ - is->audio_buf_size = 1024; - memset(is->audio_buf, 0, is->audio_buf_size); - } else { - audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); - is->audio_buf_size = audio_size; + audio_size = audio_decode_frame(is->audio_buf, sizeof(is->audio_buf), &pts); + if(audio_size < 0) + { + /* If error, we're done */ + break; } + + audio_size = synchronize_audio(is->audio_buf, audio_size, pts); + is->audio_buf_size = audio_size; is->audio_buf_index = 0; } - len1 = is->audio_buf_size - is->audio_buf_index; - if(len1 > len) - len1 = len; - memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); - len -= len1; + + size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, + len - total); + memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); + + total += len1; stream += len1; is->audio_buf_index += len1; } + + return total; } +}; + + /* static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { SDL_Event event; @@ -371,8 +449,8 @@ namespace MWRender } - void video_refresh_timer(void *userdata) { - + void video_refresh_timer(void *userdata) + { VideoState *is = (VideoState *)userdata; VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; @@ -439,8 +517,8 @@ namespace MWRender } } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) { - + int queue_picture(VideoState *is, AVFrame *pFrame, double pts) + { VideoPicture *vp; /* wait until we have a new pic */ @@ -487,8 +565,8 @@ namespace MWRender return 0; } - double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) { - + double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) + { double frame_delay; if(pts != 0) { @@ -512,14 +590,16 @@ namespace MWRender * buffer. We use this to store the global_pts in * a frame at the time it is allocated. */ - int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { + int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) + { int ret = avcodec_default_get_buffer(c, pic); uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); *pts = global_video_pkt_pts; pic->opaque = pts; return ret; } - void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) { + void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) + { if(pic) av_freep(&pic->opaque); avcodec_default_release_buffer(c, pic); } @@ -583,9 +663,9 @@ namespace MWRender int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) { + MWSound::DecoderPtr decoder; AVCodecContext *codecCtx; AVCodec *codec; - SDL_AudioSpec wanted_spec, spec; if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { return -1; @@ -594,22 +674,9 @@ namespace MWRender // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; - if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) { - // Set audio settings from codec info - wanted_spec.freq = codecCtx->sample_rate; - wanted_spec.format = AUDIO_S16SYS; - wanted_spec.channels = codecCtx->channels; - wanted_spec.silence = 0; - wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; - wanted_spec.callback = audio_callback; - wanted_spec.userdata = is; + if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) + return -1; - if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { - fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); - return -1; - } - is->audio_hw_buf_size = spec.size; - } codec = avcodec_find_decoder(codecCtx->codec_id); if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { fprintf(stderr, "Unsupported codec!\n"); @@ -627,11 +694,13 @@ namespace MWRender is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); is->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / codecCtx->sample_rate; + is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); packet_queue_init(&is->audioq); - SDL_PauseAudio(0); + + decoder.reset(new MovieAudioDecoder(is)); + is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); break; case AVMEDIA_TYPE_VIDEO: is->videoStream = stream_index; @@ -841,17 +910,13 @@ namespace MWRender } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); mState = new VideoState; // Register all formats and codecs av_register_all(); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); - if(SDL_Init(SDL_INIT_AUDIO)) { - throw std::runtime_error("Failed to initialize SDL"); - } - mState->refresh = 0; mState->resourceName = resourceName; @@ -869,9 +934,7 @@ namespace MWRender mState->refresh--; } if (mState && mState->quit) - { close(); - } if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); @@ -888,7 +951,6 @@ namespace MWRender delete mState; mState = NULL; - SDL_CloseAudio(); MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible (false); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index d9dc6ded00..eac9063565 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -16,13 +16,11 @@ extern "C" #include } -#include -#include - #include #include -#define SDL_AUDIO_BUFFER_SIZE 1024 +#include "../mwbase/soundmanager.hpp" + #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 @@ -33,11 +31,8 @@ extern "C" #define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER - namespace MWRender { - - struct PacketQueue { PacketQueue () : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) @@ -64,7 +59,7 @@ namespace MWRender VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), - audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) @@ -80,41 +75,44 @@ namespace MWRender free (pictq[0].data); } - int videoStream, audioStream; + int videoStream, audioStream; - int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; - double audio_clock; - AVStream *audio_st; - PacketQueue audioq; + int av_sync_type; + double external_clock; /* external clock base */ + int64_t external_clock_time; + + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); - unsigned int audio_buf_size; - unsigned int audio_buf_index; - AVPacket audio_pkt; - uint8_t *audio_pkt_data; - int audio_pkt_size; - int audio_hw_buf_size; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; - double frame_timer; - double frame_last_pts; - double frame_last_delay; - double video_clock; /// Date: Thu, 13 Dec 2012 02:32:21 -0800 Subject: [PATCH 028/147] Don't initially fill buffers in OpenAL_SoundStream::play --- apps/openmw/mwsound/openal_output.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 658483b02c..4bdbf01014 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -230,30 +230,19 @@ OpenAL_SoundStream::~OpenAL_SoundStream() void OpenAL_SoundStream::play() { - std::vector data(mBufferSize); - ALuint count = 0; - alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); - mSamplesQueued = 0; + for(ALuint i = 0;i < sNumBuffers;i++) - { - size_t got; - got = mDecoder->read(&data[0], data.size()); - alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate); - count += getBufferSampleCount(mBuffers[i]); - } + alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); throwALerror(); alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); alSourcePlay(mSource); throwALerror(); - mSamplesTotal += count; - mSamplesQueued = count; - mIsFinished = false; mOutput.mStreamThread->add(this); } From f067b22b3f6c7c19d3e0cabb9b89b36b8975c287 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 02:33:35 -0800 Subject: [PATCH 029/147] Use a recursive mutex for the OpenAL stream thread --- apps/openmw/mwsound/openal_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4bdbf01014..c7b16dd396 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -125,7 +125,7 @@ const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; struct OpenAL_Output::StreamThread { typedef std::vector StreamVec; StreamVec mStreams; - boost::mutex mMutex; + boost::recursive_mutex mMutex; boost::thread mThread; StreamThread() From d2fbae9760aeaab5b7be705ff736714937ff4ff3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 02:52:37 -0800 Subject: [PATCH 030/147] Init and deinit the VideoState synchronously, and re-enable audio playback --- apps/openmw/mwrender/videoplayer.cpp | 252 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 3 +- 2 files changed, 133 insertions(+), 122 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index fa179f8b10..0268539d52 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -68,14 +68,14 @@ namespace MWRender void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); } - int packet_queue_put(PacketQueue *q, AVPacket *pkt) { + int packet_queue_put(PacketQueue *q, AVPacket *pkt) + { AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) { + if(av_dup_packet(pkt) < 0) return -1; - } + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if (!pkt1) - return -1; + if(!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; @@ -92,21 +92,24 @@ namespace MWRender q->mutex.unlock (); return 0; } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) + { AVPacketList *pkt1; int ret; boost::unique_lock lock(q->mutex); - for(;;) { - - if(global_video_state->quit) { + for(;;) + { + if(global_video_state->quit) + { ret = -1; break; } pkt1 = q->first_pkt; - if (pkt1) { + if (pkt1) + { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; @@ -116,15 +119,17 @@ namespace MWRender av_free(pkt1); ret = 1; break; - } else if (!block) { + } + + if (!block) + { ret = 0; break; - } else { - - - q->cond.wait(lock); } + + q->cond.wait(lock); } + return ret; } @@ -673,17 +678,15 @@ public: // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; - - if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) - return -1; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) + { fprintf(stderr, "Unsupported codec!\n"); return -1; } - switch(codecCtx->codec_type) { + switch(codecCtx->codec_type) + { case AVMEDIA_TYPE_AUDIO: is->audioStream = stream_index; is->audio_st = pFormatCtx->streams[stream_index]; @@ -701,7 +704,15 @@ public: decoder.reset(new MovieAudioDecoder(is)); is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!is->AudioTrack) + { + is->audioStream = -1; + avcodec_close(is->audio_st->codec); + is->audio_st = NULL; + return -1; + } break; + case AVMEDIA_TYPE_VIDEO: is->videoStream = stream_index; is->video_st = pFormatCtx->streams[stream_index]; @@ -711,11 +722,12 @@ public: is->video_current_pts_time = av_gettime(); packet_queue_init(&is->videoq); - is->video_thread = boost::thread(video_thread, is); + codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; - + is->video_thread = boost::thread(video_thread, is); break; + default: break; } @@ -732,118 +744,46 @@ public: VideoState *is = (VideoState *)arg; try { - AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVFormatContext *pFormatCtx = is->format_ctx; AVPacket pkt1, *packet = &pkt1; - int video_index = -1; - int audio_index = -1; - unsigned int i; - - is->videoStream=-1; - is->audioStream=-1; - is->quit = 0; - - Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); - if(stream.isNull ()) - throw std::runtime_error("Failed to open video resource"); - is->stream = stream; - - AVIOContext *ioContext = 0; - - ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throw std::runtime_error("Failed to allocate ioContext "); - - pFormatCtx->pb = ioContext; - - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); - - // Retrieve stream information - if(avformat_find_stream_info(pFormatCtx, NULL)<0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); - - for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { - video_index=i; - } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { - audio_index=i; - } - } - - if(audio_index >= 0) { - stream_component_open(is, audio_index, pFormatCtx); - } - if(video_index >= 0) { - stream_component_open(is, video_index, pFormatCtx); - } - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) { - // main decode loop - - for(;;) { - if(is->quit) { + for(;;) + { + if(is->quit) break; - } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { + + if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) + { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; } - if(av_read_frame(pFormatCtx, packet) < 0) { + + if(av_read_frame(pFormatCtx, packet) < 0) break; - } + // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) { + if(packet->stream_index == is->videoStream) packet_queue_put(&is->videoq, packet); - } else if(packet->stream_index == is->audioStream) { + else if(packet->stream_index == is->audioStream) packet_queue_put(&is->audioq, packet); - } else { + else av_free_packet(packet); - } } /* all done - wait for it */ - while(!is->quit) { + while(!is->quit) + { // EOF reached, all packets processed, we can exit now - if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + if(is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) break; boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } } - is->quit = 1; - - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); - - is->video_thread.join(); - - if (is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if (is->videoStream >= 0) - avcodec_close(is->video_st->codec); - - sws_freeContext (is->sws_context); - - avformat_close_input(&pFormatCtx); - pFormatCtx = NULL; - - av_free(ioContext); } catch (std::runtime_error& e) { @@ -859,6 +799,73 @@ public: return 0; } + void init_state(VideoState *is, const std::string& resourceName) + { + int video_index = -1; + int audio_index = -1; + unsigned int i; + + is->videoStream = -1; + is->audioStream = -1; + is->quit = 0; + + is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); + if(is->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!is->format_ctx->pb) + throw std::runtime_error("Failed to allocate ioContext "); + + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); + + // Retrieve stream information + if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < is->format_ctx->nb_streams;i++) + { + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + stream_component_open(is, audio_index, is->format_ctx); + if(video_index >= 0) + stream_component_open(is, video_index, is->format_ctx); + } + + void deinit_state(VideoState *is) + { + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); + + is->parse_thread.join(); + is->video_thread.join(); + + if(is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if(is->videoStream >= 0) + avcodec_close(is->video_st->codec); + + sws_freeContext(is->sws_context); + + AVIOContext *ioContext = is->format_ctx->pb; + avformat_close_input(&is->format_ctx); + av_free(ioContext); + } VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -919,22 +926,27 @@ public: mState->refresh = 0; mState->resourceName = resourceName; + mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + mState->format_ctx = avformat_alloc_context(); schedule_refresh(mState, 40); - mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + init_state(mState, resourceName); mState->parse_thread = boost::thread(decode_thread, mState); } void VideoPlayer::update () { - if (mState && mState->refresh) + if(mState) { - video_refresh_timer (mState); - mState->refresh--; + if(mState->quit) + close(); + else if(mState->refresh) + { + video_refresh_timer(mState); + mState->refresh--; + } } - if (mState && mState->quit) - close(); if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); @@ -945,8 +957,7 @@ public: void VideoPlayer::close() { mState->quit = 1; - - mState->parse_thread.join (); + deinit_state(mState); delete mState; mState = NULL; @@ -964,5 +975,4 @@ public: { return mState != NULL; } - } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index eac9063565..e97fa6e13b 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -62,7 +62,7 @@ namespace MWRender audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} @@ -108,6 +108,7 @@ namespace MWRender MWBase::SoundPtr AudioTrack; + AVFormatContext* format_ctx; SwsContext* sws_context; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; From 7e8b844b2e46ca1b4b58acb5aa65313792aaa403 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:05:37 -0800 Subject: [PATCH 031/147] Clean up some unused code --- apps/openmw/mwrender/videoplayer.cpp | 92 ++++++++++++---------------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 0268539d52..d4fa7688fd 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -393,20 +393,9 @@ public: return total; } - }; - /* - static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { - SDL_Event event; - event.type = FF_REFRESH_EVENT; - event.user.data1 = opaque; - SDL_PushEvent(&event); - return 0; // 0 means stop timer - } - */ - void timer_callback (boost::system_time t, VideoState* is) { boost::this_thread::sleep (t); @@ -416,8 +405,6 @@ public: /* schedule a video refresh in 'delay' ms */ static void schedule_refresh(VideoState *is, int delay) { - //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); - //is->refresh_queue.push_back (delay); boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); boost::thread (boost::bind(&timer_callback, t, is)).detach(); } @@ -460,10 +447,12 @@ public: VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; - if(is->video_st) { - if(is->pictq_size == 0) { + if(is->video_st) + { + if(is->pictq_size == 0) schedule_refresh(is, 1); - } else { + else + { vp = &is->pictq[is->pictq_rindex]; is->video_current_pts = vp->pts; @@ -479,26 +468,28 @@ public: is->frame_last_pts = vp->pts; /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) + { ref_clock = get_master_clock(is); diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account FFPlay still doesn't "know if this is the best guess." */ sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - if(diff <= -sync_threshold) { + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) delay = 0; - } else if(diff >= sync_threshold) { + else if(diff >= sync_threshold) delay = 2 * delay; - } } } is->frame_timer += delay; /* computer the REAL delay */ actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) { + if(actual_delay < 0.010) + { /* Really it should skip the picture instead */ actual_delay = 0.010; } @@ -508,18 +499,16 @@ public: video_display(is); /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_rindex = 0; - } is->pictq_mutex.lock(); is->pictq_size--; is->pictq_cond.notify_one (); is->pictq_mutex.unlock (); } } - else { + else schedule_refresh(is, 100); - } } int queue_picture(VideoState *is, AVFrame *pFrame, double pts) @@ -529,9 +518,8 @@ public: /* wait until we have a new pic */ { boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } } if(is->quit) @@ -541,7 +529,8 @@ public: vp = &is->pictq[is->pictq_windex]; // Convert the image into YUV format that SDL uses - if(is->sws_context == NULL) { + if(is->sws_context == NULL) + { int w = is->video_st->codec->width; int h = is->video_st->codec->height; is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, @@ -556,13 +545,11 @@ public: sws_scale(is->sws_context, pFrame->data, pFrame->linesize, 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); - vp->pts = pts; // now we inform our display thread that we have a pic ready - if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) { + if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_windex = 0; - } is->pictq_mutex.lock(); is->pictq_size++; is->pictq_mutex.unlock(); @@ -574,10 +561,13 @@ public: { double frame_delay; - if(pts != 0) { + if(pts != 0) + { /* if we have pts, set video clock to it */ is->video_clock = pts; - } else { + } + else + { /* if we aren't given a pts, set it to the clock */ pts = is->video_clock; } @@ -619,11 +609,12 @@ public: pFrame = avcodec_alloc_frame(); is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc ((AVPicture *)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); - - for(;;) { - if(packet_queue_get(&is->videoq, packet, 1) < 0) { + for(;;) + { + if(packet_queue_get(&is->videoq, packet, 1) < 0) + { // means we quit getting packets break; } @@ -632,28 +623,24 @@ public: // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet) < 0) - { + if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - } - if((uint64_t)packet->dts == AV_NOPTS_VALUE - && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { + + if((uint64_t)packet->dts == AV_NOPTS_VALUE && + pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; - } else if((uint64_t)packet->dts != AV_NOPTS_VALUE) { + else if((uint64_t)packet->dts != AV_NOPTS_VALUE) pts = packet->dts; - } else { + else pts = 0; - } pts *= av_q2d(is->video_st->time_base); - // Did we get a video frame? - if(frameFinished) { + if(frameFinished) + { pts = synchronize_video(is, pFrame, pts); - if(queue_picture(is, pFrame, pts) < 0) { + if(queue_picture(is, pFrame, pts) < 0) break; - } } av_free_packet(packet); } @@ -672,9 +659,8 @@ public: AVCodecContext *codecCtx; AVCodec *codec; - if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) return -1; - } // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; From f7ff8b33741b4a7fbf30d1e89d19b24253ec4a53 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:13:44 -0800 Subject: [PATCH 032/147] A bit more cleanup --- apps/openmw/mwrender/videoplayer.cpp | 16 +++++++--------- apps/openmw/mwrender/videoplayer.hpp | 3 +-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d4fa7688fd..94f40e0e9d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -791,8 +791,11 @@ public: int audio_index = -1; unsigned int i; + is->av_sync_type = DEFAULT_AV_SYNC_TYPE; + is->format_ctx = avformat_alloc_context(); is->videoStream = -1; is->audioStream = -1; + is->refresh = 0; is->quit = 0; is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); @@ -886,6 +889,9 @@ public: void VideoPlayer::playVideo (const std::string &resourceName) { + // Register all formats and codecs + av_register_all(); + if (mState) close(); @@ -907,17 +913,9 @@ public: mState = new VideoState; - // Register all formats and codecs - av_register_all(); - - mState->refresh = 0; - mState->resourceName = resourceName; - mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; - mState->format_ctx = avformat_alloc_context(); + init_state(mState, resourceName); schedule_refresh(mState, 40); - - init_state(mState, resourceName); mState->parse_thread = boost::thread(decode_thread, mState); } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index e97fa6e13b..2ce58846f4 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -121,8 +121,7 @@ namespace MWRender boost::thread parse_thread; boost::thread video_thread; - std::string resourceName; - int quit; + volatile int quit; int refresh; int display_ready; From 600494eed8ee8037f4a02f3ef11287347fecfe39 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:37:04 -0800 Subject: [PATCH 033/147] More cleanup of unused code --- apps/openmw/mwrender/videoplayer.cpp | 57 ++++++++++++---------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 94f40e0e9d..df4d7c8ed6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -59,15 +59,9 @@ namespace MWRender } + void packet_queue_init(PacketQueue *q) + { memset(q, 0, sizeof(PacketQueue)); } - - /* Since we only have one decoding thread, the Big Struct - can be global in case we need it. */ - VideoState *global_video_state; - - void packet_queue_init(PacketQueue *q) { - memset(q, 0, sizeof(PacketQueue)); - } int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; @@ -92,7 +86,8 @@ namespace MWRender q->mutex.unlock (); return 0; } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) + + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, VideoState *is, int block) { AVPacketList *pkt1; int ret; @@ -101,7 +96,7 @@ namespace MWRender for(;;) { - if(global_video_state->quit) + if(is->quit) { ret = -1; break; @@ -133,7 +128,8 @@ namespace MWRender return ret; } - void PacketQueue::flush() { + void PacketQueue::flush() + { AVPacketList *pkt, *pkt1; this->mutex.lock(); @@ -149,6 +145,7 @@ namespace MWRender this->mutex.unlock (); } + double get_audio_clock(VideoState *is) { return is->AudioTrack->getTimeOffset(); @@ -161,17 +158,19 @@ namespace MWRender delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; return is->video_current_pts + delta; } - double get_external_clock(VideoState *is) { + + double get_external_clock(VideoState *is) + { return av_gettime() / 1000000.0; } - double get_master_clock(VideoState *is) { - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) { + + double get_master_clock(VideoState *is) + { + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) return get_video_clock(is); - } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) { + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return get_audio_clock(is); - } else { - return get_external_clock(is); - } + return get_external_clock(is); } class MovieAudioDecoder : public MWSound::Sound_Decoder @@ -296,7 +295,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(packet_queue_get(&is->audioq, pkt, 1) < 0) + if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) return -1; is->audio_pkt_data = pkt->data; @@ -328,7 +327,7 @@ public: *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; @@ -382,8 +381,8 @@ public: is->audio_buf_index = 0; } - size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, - len - total); + size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, + len - total); memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); total += len1; @@ -398,7 +397,7 @@ public: void timer_callback (boost::system_time t, VideoState* is) { - boost::this_thread::sleep (t); + boost::this_thread::sleep(t); is->refresh++; } @@ -406,7 +405,7 @@ public: static void schedule_refresh(VideoState *is, int delay) { boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread (boost::bind(&timer_callback, t, is)).detach(); + boost::thread(boost::bind(&timer_callback, t, is)).detach(); } void video_display(VideoState *is) @@ -613,7 +612,7 @@ public: for(;;) { - if(packet_queue_get(&is->videoq, packet, 1) < 0) + if(packet_queue_get(&is->videoq, packet, is, 1) < 0) { // means we quit getting packets break; @@ -721,10 +720,6 @@ public: return 0; } - int decode_interrupt_cb(void) { - return (global_video_state && global_video_state->quit); - } - int decode_thread(void *arg) { VideoState *is = (VideoState *)arg; @@ -806,10 +801,6 @@ public: if(!is->format_ctx->pb) throw std::runtime_error("Failed to allocate ioContext "); - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); - // Open video file /// \todo leak here, ffmpeg or valgrind bug ? if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) From 1ea1407707c4651bcbd94eb2119e27bfde364cd4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 04:10:19 -0800 Subject: [PATCH 034/147] Support quad, 5.1, and 7.1 with OpenAL and ffmpeg The other decoders don't guarantee any channel ordering, which makes them useless. --- apps/openmw/mwrender/videoplayer.cpp | 6 ++++++ apps/openmw/mwsound/ffmpeg_decoder.cpp | 6 ++++++ apps/openmw/mwsound/openal_output.cpp | 28 +++++++++++++++++++++++++ apps/openmw/mwsound/sound_decoder.hpp | 5 ++++- apps/openmw/mwsound/soundmanagerimp.cpp | 14 +++++++++---- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index df4d7c8ed6..6e6db5f27e 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -333,6 +333,12 @@ public: *chans = MWSound::ChannelConfig_Mono; else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_QUAD) + *chans = MWSound::ChannelConfig_Quad; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + *chans = MWSound::ChannelConfig_5point1; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + *chans = MWSound::ChannelConfig_7point1; else if(is->audio_st->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 6b3a7f9cd0..9fee0d9b09 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -347,6 +347,12 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * *chans = ChannelConfig_Mono; else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + *chans = ChannelConfig_Quad; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + *chans = ChannelConfig_5point1; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + *chans = ChannelConfig_7point1; else if(stream->mCodecCtx->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index c7b16dd396..fd4fdaa9e5 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -61,6 +61,34 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) if(fmtlist[i].chans == chans && fmtlist[i].type == type) return fmtlist[i].format; } + + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } mcfmtlist[] = { + { "AL_FORMAT_QUAD16", ChannelConfig_Quad, SampleType_Int16 }, + { "AL_FORMAT_QUAD8", ChannelConfig_Quad, SampleType_UInt8 }, + { "AL_FORMAT_51CHN16", ChannelConfig_5point1, SampleType_Int16 }, + { "AL_FORMAT_51CHN8", ChannelConfig_5point1, SampleType_UInt8 }, + { "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 }, + { "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 }, + }; + static const size_t mcfmtlistsize = sizeof(mcfmtlist)/sizeof(mcfmtlist[0]); + + for(size_t i = 0;i < mcfmtlistsize;i++) + { + if(mcfmtlist[i].chans == chans && mcfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(mcfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + } + fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); return AL_NONE; } diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index d228a5e980..f78a1cd28b 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -15,7 +15,10 @@ namespace MWSound enum ChannelConfig { ChannelConfig_Mono, - ChannelConfig_Stereo + ChannelConfig_Stereo, + ChannelConfig_Quad, + ChannelConfig_5point1, + ChannelConfig_7point1 }; const char *getChannelConfigName(ChannelConfig config); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 6bb3d59dd6..efdb6f7ead 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -622,8 +622,11 @@ namespace MWSound { switch(config) { - case ChannelConfig_Mono: return "Mono"; - case ChannelConfig_Stereo: return "Stereo"; + case ChannelConfig_Mono: return "Mono"; + case ChannelConfig_Stereo: return "Stereo"; + case ChannelConfig_Quad: return "Quad"; + case ChannelConfig_5point1: return "5.1 Surround"; + case ChannelConfig_7point1: return "7.1 Surround"; } return "(unknown channel config)"; } @@ -632,8 +635,11 @@ namespace MWSound { switch(config) { - case ChannelConfig_Mono: frames *= 1; break; - case ChannelConfig_Stereo: frames *= 2; break; + case ChannelConfig_Mono: frames *= 1; break; + case ChannelConfig_Stereo: frames *= 2; break; + case ChannelConfig_Quad: frames *= 4; break; + case ChannelConfig_5point1: frames *= 6; break; + case ChannelConfig_7point1: frames *= 8; break; } switch(type) { From cab68df2574eec34254640770ca371450b341f42 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 04:25:41 -0800 Subject: [PATCH 035/147] Use the decoded frame pts when available --- apps/openmw/mwrender/videoplayer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6e6db5f27e..2d43e8ddcd 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -631,11 +631,12 @@ public: if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - if((uint64_t)packet->dts == AV_NOPTS_VALUE && - pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + if((uint64_t)pFrame->pts != AV_NOPTS_VALUE) + pts = pFrame->pts; + else if((uint64_t)packet->pts != AV_NOPTS_VALUE) + pts = packet->pts; + else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; - else if((uint64_t)packet->dts != AV_NOPTS_VALUE) - pts = packet->dts; else pts = 0; pts *= av_q2d(is->video_st->time_base); From 0a5ab977b75ddd246b8fd37d8a8a0b545d114b03 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 05:04:53 -0800 Subject: [PATCH 036/147] Use the decoder's sample offset for calculating the stream offset --- apps/openmw/mwrender/videoplayer.cpp | 5 +++++ apps/openmw/mwsound/audiere_decoder.cpp | 5 +++++ apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 14 ++++++++++++-- apps/openmw/mwsound/ffmpeg_decoder.hpp | 2 ++ apps/openmw/mwsound/mpgsnd_decoder.cpp | 5 +++++ apps/openmw/mwsound/mpgsnd_decoder.hpp | 1 + apps/openmw/mwsound/openal_output.cpp | 11 +++-------- apps/openmw/mwsound/sound_decoder.hpp | 1 + 9 files changed, 35 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2d43e8ddcd..bd29c69bf2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -398,6 +398,11 @@ public: return total; } + + size_t getSampleOffset() + { + return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate); + } }; diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index c9b3d11d1f..788d5ae40e 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -117,6 +117,11 @@ void Audiere_Decoder::rewind() mSoundSource->reset(); } +size_t Audiere_Decoder::getSampleOffset() +{ + return 0; +} + Audiere_Decoder::Audiere_Decoder() { } diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 1e1528880a..8623a3f2c1 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -25,6 +25,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void rewind(); + virtual size_t getSampleOffset(); Audiere_Decoder& operator=(const Audiere_Decoder &rhs); Audiere_Decoder(const Audiere_Decoder &rhs); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 9fee0d9b09..6e60a7b9ef 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -383,7 +383,11 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) if(mStreams.empty()) fail("No audio streams"); - return mStreams.front()->readAVAudioData(buffer, bytes); + MyStream *stream = mStreams.front(); + size_t got = stream->readAVAudioData(buffer, bytes); + mSamplesRead += got / av_samples_get_buffer_size(NULL, stream->mCodecCtx->channels, 1, + stream->mCodecCtx->sample_fmt, 1); + return got; } void FFmpeg_Decoder::readAll(std::vector &output) @@ -402,9 +406,15 @@ void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); + mSamplesRead = 0; } -FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL) +size_t FFmpeg_Decoder::getSampleOffset() +{ + return mSamplesRead; +} + +FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL), mSamplesRead(0) { static bool done_init = false; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 88115ce3fc..ff63edf07d 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -25,6 +25,7 @@ namespace MWSound struct MyStream; std::vector mStreams; + size_t mSamplesRead; bool getNextPacket(int streamidx); @@ -42,6 +43,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void readAll(std::vector &output); virtual void rewind(); + virtual size_t getSampleOffset(); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.cpp b/apps/openmw/mwsound/mpgsnd_decoder.cpp index 4ec11b3490..fb187f8442 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.cpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.cpp @@ -218,6 +218,11 @@ void MpgSnd_Decoder::rewind() } } +size_t MpgSnd_Decoder::getSampleOffset() +{ + return 0; +} + MpgSnd_Decoder::MpgSnd_Decoder() : mSndInfo() , mSndFile(NULL) diff --git a/apps/openmw/mwsound/mpgsnd_decoder.hpp b/apps/openmw/mwsound/mpgsnd_decoder.hpp index 09082c2f47..52c37bb877 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.hpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.hpp @@ -39,6 +39,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void readAll(std::vector &output); virtual void rewind(); + virtual size_t getSampleOffset(); MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs); MpgSnd_Decoder(const MpgSnd_Decoder &rhs); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index fd4fdaa9e5..672e52b56e 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -122,7 +122,6 @@ class OpenAL_SoundStream : public Sound ALsizei mSampleRate; ALuint mBufferSize; - ALuint mSamplesTotal; ALuint mSamplesQueued; DecoderPtr mDecoder; @@ -215,8 +214,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mSamplesTotal(0), mSamplesQueued(0), - mDecoder(decoder), mIsFinished(true) + : mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -286,7 +284,6 @@ void OpenAL_SoundStream::stop() mSamplesQueued = 0; mDecoder->rewind(); - mSamplesTotal = 0; } bool OpenAL_SoundStream::isPlaying() @@ -311,9 +308,9 @@ double OpenAL_SoundStream::getTimeOffset() alGetSourcef(mSource, AL_SEC_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) - t = (double)(mSamplesTotal - mSamplesQueued)/(double)mSampleRate + offset; + t = (double)(mDecoder->getSampleOffset() - mSamplesQueued)/(double)mSampleRate + offset; else - t = (double)mSamplesTotal / (double)mSampleRate; + t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; mOutput.mStreamThread->mMutex.unlock(); throwALerror(); @@ -388,13 +385,11 @@ bool OpenAL_SoundStream::process() mSamplesQueued -= samples_unqueued; mSamplesQueued += samples_queued; - mSamplesTotal += samples_queued; mIsFinished = finished; } catch(std::exception &e) { std::cout<< "Error updating stream \""<getName()<<"\"" < &output); virtual void rewind() = 0; + virtual size_t getSampleOffset() = 0; Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton()) { } From 43481ad117da4da25aedd4aad6ff379d1ff74887 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 06:11:32 -0800 Subject: [PATCH 037/147] Use the external clock by default --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- apps/openmw/mwrender/videoplayer.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bd29c69bf2..a5743356a2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -609,7 +609,8 @@ public: avcodec_default_release_buffer(c, pic); } - int video_thread(void *arg) { + int video_thread(void *arg) + { VideoState *is = (VideoState *)arg; AVPacket pkt1, *packet = &pkt1; int frameFinished; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 2ce58846f4..ace36efb98 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -28,7 +28,7 @@ extern "C" #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 -#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER +#define DEFAULT_AV_SYNC_TYPE AV_SYNC_EXTERNAL_MASTER namespace MWRender From 9d6f6568224578614bb3e118583786e5f2032d05 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 00:10:54 +0100 Subject: [PATCH 038/147] fixed ogre resource functions --- apps/openmw/mwrender/videoplayer.cpp | 55 ++++++++++------------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index a5743356a2..21805a7920 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,54 +11,37 @@ namespace MWRender { - int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) + int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - - int num_read = stream->size() - stream->tell(); - - if (num_read > buf_size) - num_read = buf_size; - - stream->read(buf, num_read); - return num_read; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->read(buf, buf_size); } - int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) + int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - - int num_write = stream->size() - stream->tell(); - - if (num_write > buf_size) - num_write = buf_size; - - stream->write (buf, num_write); - return num_write; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->write(buf, buf_size); } - int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) + int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - switch (whence) - { - case SEEK_SET: - stream->seek(offset); - case SEEK_CUR: - stream->seek(stream->tell() + offset); - case SEEK_END: - stream->seek(stream->size() + offset); - case AVSEEK_SIZE: - return stream->size(); - default: - return -1; - } + whence &= ~AVSEEK_FORCE; + if(whence == AVSEEK_SIZE) + return stream->size(); + if(whence == SEEK_SET) + stream->seek(offset); + else if(whence == SEEK_CUR) + stream->seek(stream->tell()+offset); + else if(whence == SEEK_END) + stream->seek(stream->size()+offset); + else + return -1; return stream->tell(); } - void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); } From 27cd9ff7322269c7d0e8c91ac9f6a1ddc300cf2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 01:03:49 +0100 Subject: [PATCH 039/147] Revert "Use the decoded frame pts when available" This reverts commit cab68df2574eec34254640770ca371450b341f42. --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 21805a7920..7e2be7e10c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -620,12 +620,11 @@ public: if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - if((uint64_t)pFrame->pts != AV_NOPTS_VALUE) - pts = pFrame->pts; - else if((uint64_t)packet->pts != AV_NOPTS_VALUE) - pts = packet->pts; - else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + if((uint64_t)packet->dts == AV_NOPTS_VALUE && + pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; + else if((uint64_t)packet->dts != AV_NOPTS_VALUE) + pts = packet->dts; else pts = 0; pts *= av_q2d(is->video_st->time_base); From 82564e07c7edefe5ac8ab0b8e23db55ce39b0dd3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 01:44:00 +0100 Subject: [PATCH 040/147] fix crash when video file doesn't exist --- apps/openmw/mwrender/videoplayer.cpp | 115 ++++++++++++++++----------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e2be7e10c..299b34c326 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -777,49 +777,70 @@ public: void init_state(VideoState *is, const std::string& resourceName) { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - is->av_sync_type = DEFAULT_AV_SYNC_TYPE; - is->format_ctx = avformat_alloc_context(); - is->videoStream = -1; - is->audioStream = -1; - is->refresh = 0; - is->quit = 0; - - is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); - if(is->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); - - is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!is->format_ctx->pb) - throw std::runtime_error("Failed to allocate ioContext "); - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); - - // Retrieve stream information - if(avformat_find_stream_info(is->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); - - for(i = 0;i < is->format_ctx->nb_streams;i++) + try { - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } + int video_index = -1; + int audio_index = -1; + unsigned int i; - if(audio_index >= 0) - stream_component_open(is, audio_index, is->format_ctx); - if(video_index >= 0) - stream_component_open(is, video_index, is->format_ctx); + is->av_sync_type = DEFAULT_AV_SYNC_TYPE; + is->videoStream = -1; + is->audioStream = -1; + is->refresh = 0; + is->quit = 0; + + is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); + if(is->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + is->format_ctx = avformat_alloc_context(); + + is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!is->format_ctx->pb) + { + avformat_free_context(is->format_ctx); + throw std::runtime_error("Failed to allocate ioContext "); + } + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + { + // "Note that a user-supplied AVFormatContext will be freed on failure." + is->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); + } + + // Retrieve stream information + if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < is->format_ctx->nb_streams;i++) + { + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + stream_component_open(is, audio_index, is->format_ctx); + if(video_index >= 0) + stream_component_open(is, video_index, is->format_ctx); + } + catch (std::runtime_error& e) + { + is->quit = 1; + throw; + } + catch (Ogre::Exception& e) + { + is->quit = 1; + throw; + } } void deinit_state(VideoState *is) @@ -835,11 +856,15 @@ public: if(is->videoStream >= 0) avcodec_close(is->video_st->codec); - sws_freeContext(is->sws_context); + if (is->sws_context) + sws_freeContext(is->sws_context); - AVIOContext *ioContext = is->format_ctx->pb; - avformat_close_input(&is->format_ctx); - av_free(ioContext); + if (is->format_ctx) + { + AVIOContext *ioContext = is->format_ctx->pb; + avformat_close_input(&is->format_ctx); + av_free(ioContext); + } } VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) From 606fb982a821d1805d5b51a02a9e84e6b857f033 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 17:53:22 -0800 Subject: [PATCH 041/147] Update to use avcodec_decode_audio4 --- apps/openmw/mwrender/videoplayer.cpp | 82 +++++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 15 ++--- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e2be7e10c..6acbe8b79b 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -165,6 +165,10 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder VideoState *is; + AVFrame *mFrame; + size_t mFramePos; + size_t mFrameSize; + /* Add or subtract samples to get a better sync, return new * audio buffer size */ int synchronize_audio(uint8_t *samples, int samples_size, double pts) @@ -210,16 +214,19 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { uint8_t *samples_end, *q; int nb; + /* add samples by copying final sample*/ nb = (samples_size - wanted_size); samples_end = samples + samples_size - n; q = samples_end + n; + while(nb > 0) { memcpy(q, samples_end, n); q += n; nb -= n; } + samples_size = wanted_size; } } @@ -235,38 +242,47 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return samples_size; } - int audio_decode_frame(uint8_t *audio_buf, int buf_size, double *pts_ptr) + int audio_decode_frame(AVFrame *frame, double *pts_ptr) { AVPacket *pkt = &is->audio_pkt; - int len1, data_size, n; - double pts; + int len1, data_size; for(;;) { - while(is->audio_pkt_size > 0) + while(pkt->size > 0) { - data_size = buf_size; + int got_frame; - len1 = avcodec_decode_audio3(is->audio_st->codec, - (int16_t*)audio_buf, &data_size, pkt); - if(len1 < 0) + len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); + if(len1 < 0 || len1 > pkt->size) { - /* if error, skip frame */ - is->audio_pkt_size = 0; + /* if error, skip packet */ break; } - is->audio_pkt_data += len1; - is->audio_pkt_size -= len1; + + if(len1 <= pkt->size) + { + /* Move the unread data to the front and clear the end bits */ + int remaining = pkt->size - len1; + memmove(pkt->data, &pkt->data[len1], remaining); + memset(&pkt->data[remaining], 0, pkt->size - remaining); + pkt->size -= len1; + } + if(!got_frame) + continue; + + int smp_size = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, + is->audio_st->codec->sample_fmt, 1); + data_size = frame->nb_samples * smp_size; if(data_size <= 0) { /* No data yet, get more frames */ continue; } - pts = is->audio_clock; - *pts_ptr = pts; - n = 2 * is->audio_st->codec->channels; - is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + + *pts_ptr = is->audio_clock; + is->audio_clock += (double)(data_size/smp_size) / + (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ return data_size; @@ -281,8 +297,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) return -1; - is->audio_pkt_data = pkt->data; - is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; @@ -300,7 +314,16 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder void rewind() { } public: - MovieAudioDecoder(VideoState *_is) : is(_is) { } + MovieAudioDecoder(VideoState *_is) + : is(_is) + , mFrame(avcodec_alloc_frame()) + , mFramePos(0) + , mFrameSize(0) + { } + virtual ~MovieAudioDecoder() + { + av_freep(&mFrame); + } void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { @@ -353,30 +376,29 @@ public: while(total < len) { - if(is->audio_buf_index >= is->audio_buf_size) + if(mFramePos >= mFrameSize) { int audio_size; double pts; + /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(is->audio_buf, sizeof(is->audio_buf), &pts); + audio_size = audio_decode_frame(mFrame, &pts); if(audio_size < 0) { /* If error, we're done */ break; } - audio_size = synchronize_audio(is->audio_buf, audio_size, pts); - is->audio_buf_size = audio_size; - is->audio_buf_index = 0; + mFrameSize = synchronize_audio(mFrame->data[0], audio_size, pts); + mFramePos = 0; } - size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, - len - total); - memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); + size_t len1 = std::min(len - total, mFrameSize-mFramePos); + memcpy(stream, mFrame->data[0]+mFramePos, len1); total += len1; stream += len1; - is->audio_buf_index += len1; + mFramePos += len1; } return total; @@ -670,8 +692,6 @@ public: case AVMEDIA_TYPE_AUDIO: is->audioStream = stream_index; is->audio_st = pFormatCtx->streams[stream_index]; - is->audio_buf_size = 0; - is->audio_buf_index = 0; /* averaging filter for audio sync */ is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index ace36efb98..689b3ad36a 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -58,11 +58,11 @@ namespace MWRender struct VideoState { VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), - external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), - audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), - video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), + audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), + frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), + video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), + pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} @@ -84,12 +84,7 @@ namespace MWRender double audio_clock; AVStream *audio_st; PacketQueue audioq; - DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); - unsigned int audio_buf_size; - unsigned int audio_buf_index; AVPacket audio_pkt; - uint8_t *audio_pkt_data; - int audio_pkt_size; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; From 6cedd64509d0b59a4714f853408b7bcf22b582a2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:05:27 -0800 Subject: [PATCH 042/147] Fix audio sync correction sizes --- apps/openmw/mwrender/videoplayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c2781935c8..38b519b21d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -197,8 +197,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(fabs(avg_diff) >= is->audio_diff_threshold) { wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; if(wanted_size < min_size) wanted_size = min_size; From f555dc60eb05cbf0716e3785185c4da69b22a9ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:12:17 -0800 Subject: [PATCH 043/147] Reduce some indentation --- apps/openmw/mwrender/videoplayer.cpp | 117 ++++++++++++++------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 38b519b21d..a6e033216b 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -462,68 +462,69 @@ public: VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; - if(is->video_st) + if(!is->video_st) { - if(is->pictq_size == 0) - schedule_refresh(is, 1); - else + schedule_refresh(is, 100); + return; + } + if(is->pictq_size == 0) + { + schedule_refresh(is, 1); + return; + } + + vp = &is->pictq[is->pictq_rindex]; + + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); + + delay = vp->pts - is->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; + } + /* save for next time */ + is->frame_last_delay = delay; + is->frame_last_pts = vp->pts; + + /* update delay to sync to audio if not master source */ + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) + { + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - vp = &is->pictq[is->pictq_rindex]; - - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); - - delay = vp->pts - is->frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } - /* save for next time */ - is->frame_last_delay = delay; - is->frame_last_pts = vp->pts; - - /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; - - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } - - is->frame_timer += delay; - /* computer the REAL delay */ - actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Really it should skip the picture instead */ - actual_delay = 0.010; - } - schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); - - /* show the picture! */ - video_display(is); - - /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; - is->pictq_mutex.lock(); - is->pictq_size--; - is->pictq_cond.notify_one (); - is->pictq_mutex.unlock (); + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } } - else - schedule_refresh(is, 100); + + is->frame_timer += delay; + /* computer the REAL delay */ + actual_delay = is->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) + { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); + + /* show the picture! */ + video_display(is); + + /* update queue for next picture! */ + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) + is->pictq_rindex = 0; + is->pictq_mutex.lock(); + is->pictq_size--; + is->pictq_cond.notify_one (); + is->pictq_mutex.unlock (); } int queue_picture(VideoState *is, AVFrame *pFrame, double pts) From 90294c589ba3f79e2c760dcd3317d4b30b035710 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:24:57 -0800 Subject: [PATCH 044/147] Use a volatile bool for the refresh --- apps/openmw/mwrender/videoplayer.cpp | 10 +++++----- apps/openmw/mwrender/videoplayer.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index a6e033216b..6307a0028c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -411,10 +411,10 @@ public: }; - void timer_callback (boost::system_time t, VideoState* is) + void timer_callback(boost::system_time t, VideoState* is) { boost::this_thread::sleep(t); - is->refresh++; + is->refresh = true; } /* schedule a video refresh in 'delay' ms */ @@ -469,7 +469,7 @@ public: } if(is->pictq_size == 0) { - schedule_refresh(is, 1); + is->refresh = true; return; } @@ -807,7 +807,7 @@ public: is->av_sync_type = DEFAULT_AV_SYNC_TYPE; is->videoStream = -1; is->audioStream = -1; - is->refresh = 0; + is->refresh = false; is->quit = 0; is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); @@ -959,8 +959,8 @@ public: close(); else if(mState->refresh) { + mState->refresh = false; video_refresh_timer(mState); - mState->refresh--; } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 689b3ad36a..67b619ad20 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -117,8 +117,8 @@ namespace MWRender boost::thread video_thread; volatile int quit; + volatile bool refresh; - int refresh; int display_ready; }; enum { From 5221298a7f99847f3a00e22ddbbf79fb2a89f011 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 01:14:14 -0800 Subject: [PATCH 045/147] Move a couple packet queue methods into the struct --- apps/openmw/mwrender/videoplayer.cpp | 65 +++++++++++++--------------- apps/openmw/mwrender/videoplayer.hpp | 38 +++++++++------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6307a0028c..b4fd264ec9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -42,41 +42,38 @@ namespace MWRender return stream->tell(); } - void packet_queue_init(PacketQueue *q) - { memset(q, 0, sizeof(PacketQueue)); } - int packet_queue_put(PacketQueue *q, AVPacket *pkt) + void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; if(av_dup_packet(pkt) < 0) - return -1; + throw std::runtime_error("Failed to duplicate packet"); pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if(!pkt1) return -1; + if(!pkt1) throw std::bad_alloc(); pkt1->pkt = *pkt; pkt1->next = NULL; - q->mutex.lock (); + this->mutex.lock (); - if (!q->last_pkt) - q->first_pkt = pkt1; + if(!last_pkt) + this->first_pkt = pkt1; else - q->last_pkt->next = pkt1; - q->last_pkt = pkt1; - q->nb_packets++; - q->size += pkt1->pkt.size; - q->cond.notify_one(); - q->mutex.unlock (); - return 0; + this->last_pkt->next = pkt1; + this->last_pkt = pkt1; + this->nb_packets++; + this->size += pkt1->pkt.size; + this->cond.notify_one(); + + this->mutex.unlock(); } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, VideoState *is, int block) + int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) { AVPacketList *pkt1; int ret; - boost::unique_lock lock(q->mutex); - + boost::unique_lock lock(this->mutex); for(;;) { if(is->quit) @@ -85,16 +82,18 @@ namespace MWRender break; } - pkt1 = q->first_pkt; - if (pkt1) + pkt1 = this->first_pkt; + if(pkt1) { - q->first_pkt = pkt1->next; - if (!q->first_pkt) - q->last_pkt = NULL; - q->nb_packets--; - q->size -= pkt1->pkt.size; + this->first_pkt = pkt1->next; + if(!this->first_pkt) + this->last_pkt = NULL; + this->nb_packets--; + this->size -= pkt1->pkt.size; + *pkt = pkt1->pkt; av_free(pkt1); + ret = 1; break; } @@ -105,7 +104,7 @@ namespace MWRender break; } - q->cond.wait(lock); + this->cond.wait(lock); } return ret; @@ -116,7 +115,8 @@ namespace MWRender AVPacketList *pkt, *pkt1; this->mutex.lock(); - for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) { + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) + { pkt1 = pkt->next; av_free_packet(&pkt->pkt); av_freep(&pkt); @@ -294,7 +294,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) + if(is->audioq.get(pkt, is, 1) < 0) return -1; /* if update, update the audio clock w/pts */ @@ -630,7 +630,7 @@ public: for(;;) { - if(packet_queue_get(&is->videoq, packet, is, 1) < 0) + if(is->videoq.get(packet, is, 1) < 0) { // means we quit getting packets break; @@ -701,7 +701,6 @@ public: is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - packet_queue_init(&is->audioq); decoder.reset(new MovieAudioDecoder(is)); is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); @@ -722,8 +721,6 @@ public: is->frame_last_delay = 40e-3; is->video_current_pts_time = av_gettime(); - packet_queue_init(&is->videoq); - codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; is->video_thread = boost::thread(video_thread, is); @@ -764,9 +761,9 @@ public: // Is this a packet from the video stream? if(packet->stream_index == is->videoStream) - packet_queue_put(&is->videoq, packet); + is->videoq.put(packet); else if(packet->stream_index == is->audioStream) - packet_queue_put(&is->audioq, packet); + is->audioq.put(packet); else av_free_packet(packet); } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 67b619ad20..5dfa3cf56c 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -33,10 +33,13 @@ extern "C" namespace MWRender { + struct VideoState; + struct PacketQueue { - PacketQueue () : - first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) - {} + PacketQueue() + : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + { } + AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; @@ -44,20 +47,23 @@ namespace MWRender boost::mutex mutex; boost::condition_variable cond; - void flush (); - }; - struct VideoPicture { - VideoPicture () : - data(NULL), pts(0) - {} - uint8_t* data; + void put(AVPacket *pkt); + int get(AVPacket *pkt, VideoState *is, int block); + void flush(); + }; + + struct VideoPicture { + VideoPicture () : data(NULL), pts(0) + { } + + uint8_t *data; double pts; }; struct VideoState { - VideoState () : - videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), + VideoState () + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), @@ -68,11 +74,11 @@ namespace MWRender ~VideoState() { - audioq.flush (); + audioq.flush(); videoq.flush(); - if (pictq_size >= 1) - free (pictq[0].data); + if(pictq_size >= 1) + free(pictq[0].data); } int videoStream, audioStream; @@ -82,7 +88,7 @@ namespace MWRender int64_t external_clock_time; double audio_clock; - AVStream *audio_st; + AVStream *audio_st; PacketQueue audioq; AVPacket audio_pkt; double audio_diff_cum; /* used for AV difference average computation */ From 26a09ee7ba3afd56b83442763c1aaaad7846da55 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 01:38:00 -0800 Subject: [PATCH 046/147] Move some methods into their respective class --- apps/openmw/mwrender/videoplayer.cpp | 303 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 8 +- 2 files changed, 158 insertions(+), 153 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b4fd264ec9..ec68359aa4 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,123 +11,91 @@ namespace MWRender { - int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) +void PacketQueue::put(AVPacket *pkt) +{ + AVPacketList *pkt1; + if(av_dup_packet(pkt) < 0) + throw std::runtime_error("Failed to duplicate packet"); + + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + if(!pkt1) throw std::bad_alloc(); + pkt1->pkt = *pkt; + pkt1->next = NULL; + + this->mutex.lock (); + + if(!last_pkt) + this->first_pkt = pkt1; + else + this->last_pkt->next = pkt1; + this->last_pkt = pkt1; + this->nb_packets++; + this->size += pkt1->pkt.size; + this->cond.notify_one(); + + this->mutex.unlock(); +} + +int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) +{ + AVPacketList *pkt1; + int ret; + + boost::unique_lock lock(this->mutex); + for(;;) { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - return stream->read(buf, buf_size); - } - - int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) - { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - return stream->write(buf, buf_size); - } - - int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence) - { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - - whence &= ~AVSEEK_FORCE; - if(whence == AVSEEK_SIZE) - return stream->size(); - if(whence == SEEK_SET) - stream->seek(offset); - else if(whence == SEEK_CUR) - stream->seek(stream->tell()+offset); - else if(whence == SEEK_END) - stream->seek(stream->size()+offset); - else - return -1; - - return stream->tell(); - } - - - void PacketQueue::put(AVPacket *pkt) - { - AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); - - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if(!pkt1) throw std::bad_alloc(); - pkt1->pkt = *pkt; - pkt1->next = NULL; - - this->mutex.lock (); - - if(!last_pkt) - this->first_pkt = pkt1; - else - this->last_pkt->next = pkt1; - this->last_pkt = pkt1; - this->nb_packets++; - this->size += pkt1->pkt.size; - this->cond.notify_one(); - - this->mutex.unlock(); - } - - int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) - { - AVPacketList *pkt1; - int ret; - - boost::unique_lock lock(this->mutex); - for(;;) + if(is->quit) { - if(is->quit) - { - ret = -1; - break; - } - - pkt1 = this->first_pkt; - if(pkt1) - { - this->first_pkt = pkt1->next; - if(!this->first_pkt) - this->last_pkt = NULL; - this->nb_packets--; - this->size -= pkt1->pkt.size; - - *pkt = pkt1->pkt; - av_free(pkt1); - - ret = 1; - break; - } - - if (!block) - { - ret = 0; - break; - } - - this->cond.wait(lock); + ret = -1; + break; } - return ret; - } - - void PacketQueue::flush() - { - AVPacketList *pkt, *pkt1; - - this->mutex.lock(); - for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) + pkt1 = this->first_pkt; + if(pkt1) { - pkt1 = pkt->next; - av_free_packet(&pkt->pkt); - av_freep(&pkt); + this->first_pkt = pkt1->next; + if(!this->first_pkt) + this->last_pkt = NULL; + this->nb_packets--; + this->size -= pkt1->pkt.size; + + *pkt = pkt1->pkt; + av_free(pkt1); + + ret = 1; + break; } - this->last_pkt = NULL; - this->first_pkt = NULL; - this->nb_packets = 0; - this->size = 0; - this->mutex.unlock (); + + if (!block) + { + ret = 0; + break; + } + + this->cond.wait(lock); } + return ret; +} + +void PacketQueue::flush() +{ + AVPacketList *pkt, *pkt1; + + this->mutex.lock(); + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) + { + pkt1 = pkt->next; + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + this->last_pkt = NULL; + this->first_pkt = NULL; + this->nb_packets = 0; + this->size = 0; + this->mutex.unlock (); +} + double get_audio_clock(VideoState *is) { @@ -411,6 +379,38 @@ public: }; +int VideoState::OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->read(buf, buf_size); +} + +int VideoState::OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->write(buf, buf_size); +} + +int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whence) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + + whence &= ~AVSEEK_FORCE; + if(whence == AVSEEK_SIZE) + return stream->size(); + if(whence == SEEK_SET) + stream->seek(offset); + else if(whence == SEEK_CUR) + stream->seek(stream->tell()+offset); + else if(whence == SEEK_END) + stream->seek(stream->size()+offset); + else + return -1; + + return stream->tell(); +} + + void timer_callback(boost::system_time t, VideoState* is) { boost::this_thread::sleep(t); @@ -793,7 +793,7 @@ public: return 0; } - void init_state(VideoState *is, const std::string& resourceName) + void VideoState::init(const std::string& resourceName) { try { @@ -801,90 +801,90 @@ public: int audio_index = -1; unsigned int i; - is->av_sync_type = DEFAULT_AV_SYNC_TYPE; - is->videoStream = -1; - is->audioStream = -1; - is->refresh = false; - is->quit = 0; + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 0; - is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); - if(is->stream.isNull()) + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) throw std::runtime_error("Failed to open video resource"); - is->format_ctx = avformat_alloc_context(); - - is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!is->format_ctx->pb) + this->format_ctx = avformat_alloc_context(); + this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!this->format_ctx->pb) { - avformat_free_context(is->format_ctx); + avformat_free_context(this->format_ctx); throw std::runtime_error("Failed to allocate ioContext "); } // Open video file /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { // "Note that a user-supplied AVFormatContext will be freed on failure." - is->format_ctx = NULL; + this->format_ctx = NULL; throw std::runtime_error("Failed to open video input"); } // Retrieve stream information - if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) throw std::runtime_error("Failed to retrieve stream information"); // Dump information about file onto standard error - av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); - for(i = 0;i < is->format_ctx->nb_streams;i++) + for(i = 0;i < this->format_ctx->nb_streams;i++) { - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) video_index = i; - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) audio_index = i; } if(audio_index >= 0) - stream_component_open(is, audio_index, is->format_ctx); + stream_component_open(this, audio_index, this->format_ctx); if(video_index >= 0) - stream_component_open(is, video_index, is->format_ctx); + stream_component_open(this, video_index, this->format_ctx); } - catch (std::runtime_error& e) + catch(std::runtime_error& e) { - is->quit = 1; + this->quit = 1; throw; } - catch (Ogre::Exception& e) + catch(Ogre::Exception& e) { - is->quit = 1; + this->quit = 1; throw; } } - void deinit_state(VideoState *is) + void VideoState::deinit() { - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); - is->parse_thread.join(); - is->video_thread.join(); + this->parse_thread.join(); + this->video_thread.join(); - if(is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if(is->videoStream >= 0) - avcodec_close(is->video_st->codec); + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); - if (is->sws_context) - sws_freeContext(is->sws_context); + if(this->sws_context) + sws_freeContext(this->sws_context); - if (is->format_ctx) + if(this->format_ctx) { - AVIOContext *ioContext = is->format_ctx->pb; - avformat_close_input(&is->format_ctx); + AVIOContext *ioContext = this->format_ctx->pb; + avformat_close_input(&this->format_ctx); av_free(ioContext); } } + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) , mSceneMgr(sceneMgr) @@ -941,8 +941,7 @@ public: MWBase::Environment::get().getSoundManager()->pauseAllSounds(); mState = new VideoState; - - init_state(mState, resourceName); + mState->init(resourceName); schedule_refresh(mState, 40); mState->parse_thread = boost::thread(decode_thread, mState); @@ -970,7 +969,7 @@ public: void VideoPlayer::close() { mState->quit = 1; - deinit_state(mState); + mState->deinit(); delete mState; mState = NULL; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 5dfa3cf56c..82a7083ae6 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -71,7 +71,6 @@ namespace MWRender pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} - ~VideoState() { audioq.flush(); @@ -81,6 +80,13 @@ namespace MWRender free(pictq[0].data); } + void init(const std::string& resourceName); + void deinit(); + + 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); + int videoStream, audioStream; int av_sync_type; From c2b711d195c6ce6702f693703c249b22add7f2e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 02:36:29 -0800 Subject: [PATCH 047/147] Move some more methods to the class they're part of --- apps/openmw/mwrender/videoplayer.cpp | 892 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 14 + 2 files changed, 449 insertions(+), 457 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ec68359aa4..4334bc634c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -97,32 +97,33 @@ void PacketQueue::flush() } - double get_audio_clock(VideoState *is) - { - return is->AudioTrack->getTimeOffset(); - } +static double get_audio_clock(VideoState *is) +{ + return is->AudioTrack->getTimeOffset(); +} - double get_video_clock(VideoState *is) - { - double delta; +static double get_video_clock(VideoState *is) +{ + double delta; - delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; - return is->video_current_pts + delta; - } + delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; + return is->video_current_pts + delta; +} - double get_external_clock(VideoState *is) - { - return av_gettime() / 1000000.0; - } +static double get_external_clock(VideoState *is) +{ + return av_gettime() / 1000000.0; +} + +static double get_master_clock(VideoState *is) +{ + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) + return get_video_clock(is); + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + return get_audio_clock(is); + return get_external_clock(is); +} - double get_master_clock(VideoState *is) - { - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(is); - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(is); - return get_external_clock(is); - } class MovieAudioDecoder : public MWSound::Sound_Decoder { @@ -411,479 +412,459 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc } - void timer_callback(boost::system_time t, VideoState* is) +void VideoState::timer_callback(VideoState* is, boost::system_time t) +{ + boost::this_thread::sleep(t); + is->refresh = true; +} + +/* schedule a video refresh in 'delay' ms */ +void VideoState::schedule_refresh(int delay) +{ + boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); + boost::thread(boost::bind(&timer_callback, this, t)).detach(); +} + + +void VideoState::video_display() +{ + VideoPicture *vp = &this->pictq[this->pictq_rindex]; + + if(this->video_st->codec->width != 0 && this->video_st->codec->height != 0) { - boost::this_thread::sleep(t); - is->refresh = true; + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); + if(texture.isNull () || static_cast(texture->getWidth()) != this->video_st->codec->width + || static_cast(texture->getHeight()) != this->video_st->codec->height) + { + Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); + texture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + 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); + } + Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + buffer->blitFromMemory(pb); + this->display_ready = 1; } - /* schedule a video refresh in 'delay' ms */ - static void schedule_refresh(VideoState *is, int delay) + free(vp->data); +} + +void VideoState::video_refresh_timer() +{ + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + if(!this->video_st) { - boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread(boost::bind(&timer_callback, t, is)).detach(); + this->schedule_refresh(100); + return; + } + if(this->pictq_size == 0) + { + this->refresh = true; + return; } - void video_display(VideoState *is) + vp = &this->pictq[this->pictq_rindex]; + + this->video_current_pts = vp->pts; + this->video_current_pts_time = av_gettime(); + + delay = vp->pts - this->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = this->frame_last_delay; + } + /* save for next time */ + this->frame_last_delay = delay; + this->frame_last_pts = vp->pts; + + /* update delay to sync to audio if not master source */ + if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - VideoPicture *vp; + ref_clock = get_master_clock(this); + diff = vp->pts - ref_clock; - vp = &is->pictq[is->pictq_rindex]; - - if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || static_cast(texture->getWidth()) != is->video_st->codec->width - || static_cast(texture->getHeight()) != is->video_st->codec->height) - { - Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); - texture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - is->video_st->codec->width, is->video_st->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - } - Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); - Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); - buffer->blitFromMemory(pb); - is->display_ready = 1; + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } + } + this->frame_timer += delay; - free(vp->data); + /* compute the REAL delay */ + actual_delay = this->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) + { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); + + /* show the picture! */ + this->video_display(); + + /* update queue for next picture! */ + if(++this->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) + this->pictq_rindex = 0; + this->pictq_mutex.lock(); + this->pictq_size--; + this->pictq_cond.notify_one(); + this->pictq_mutex.unlock(); +} + + +int VideoState::queue_picture(AVFrame *pFrame, double pts) +{ + VideoPicture *vp; + + /* wait until we have a new pic */ + { + boost::unique_lock lock(this->pictq_mutex); + while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit) + this->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); + } + if(this->quit) + return -1; + + // windex is set to 0 initially + vp = &this->pictq[this->pictq_windex]; + + // Convert the image into RGBA format for Ogre + if(this->sws_context == NULL) + { + int w = this->video_st->codec->width; + int h = this->video_st->codec->height; + this->sws_context = sws_getContext(w, h, this->video_st->codec->pix_fmt, + w, h, PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); + if(this->sws_context == NULL) + throw std::runtime_error("Cannot initialize the conversion context!\n"); } + vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); - void video_refresh_timer(void *userdata) + sws_scale(this->sws_context, pFrame->data, pFrame->linesize, + 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); + + vp->pts = pts; + + // 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_mutex.lock(); + this->pictq_size++; + this->pictq_mutex.unlock(); + + return 0; +} + +double VideoState::synchronize_video(AVFrame *src_frame, double pts) +{ + double frame_delay; + + /* if we have pts, set video clock to it */ + if(pts != 0) + this->video_clock = pts; + else + pts = this->video_clock; + + /* update the video clock */ + frame_delay = av_q2d(this->video_st->codec->time_base); + + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + this->video_clock += frame_delay; + + return pts; +} + + +/* These are called whenever we allocate a frame + * buffer. We use this to store the global_pts in + * a frame at the time it is allocated. + */ +static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; +static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + int ret = avcodec_default_get_buffer(c, pic); + uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + *pts = global_video_pkt_pts; + pic->opaque = pts; + return ret; +} +static void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + if(pic) av_freep(&pic->opaque); + avcodec_default_release_buffer(c, pic); +} + + +void VideoState::video_thread_loop(VideoState *self) +{ + AVPacket pkt1, *packet = &pkt1; + int frameFinished; + AVFrame *pFrame; + double pts; + + pFrame = avcodec_alloc_frame(); + + self->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); + + while(self->videoq.get(packet, self, 1) >= 0) { - VideoState *is = (VideoState *)userdata; - VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + // Save global pts to be stored in pFrame + global_video_pkt_pts = packet->pts; + // Decode video frame + if(avcodec_decode_video2(self->video_st->codec, pFrame, &frameFinished, packet) < 0) + throw std::runtime_error("Error decoding video frame"); - if(!is->video_st) + pts = 0; + if((uint64_t)packet->dts != AV_NOPTS_VALUE) + pts = packet->dts; + else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + pts = *(uint64_t*)pFrame->opaque; + pts *= av_q2d(self->video_st->time_base); + + // Did we get a video frame? + if(frameFinished) { - schedule_refresh(is, 100); - return; + pts = self->synchronize_video(pFrame, pts); + if(self->queue_picture(pFrame, pts) < 0) + break; } - if(is->pictq_size == 0) - { - is->refresh = true; - return; - } - - vp = &is->pictq[is->pictq_rindex]; - - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); - - delay = vp->pts - is->frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } - /* save for next time */ - is->frame_last_delay = delay; - is->frame_last_pts = vp->pts; - - /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; - - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } - - is->frame_timer += delay; - /* computer the REAL delay */ - actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Really it should skip the picture instead */ - actual_delay = 0.010; - } - schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); - - /* show the picture! */ - video_display(is); - - /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; - is->pictq_mutex.lock(); - is->pictq_size--; - is->pictq_cond.notify_one (); - is->pictq_mutex.unlock (); + av_free_packet(packet); } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) + av_free(pFrame); + + avpicture_free((AVPicture*)self->rgbaFrame); + av_free(self->rgbaFrame); +} + +void VideoState::decode_thread_loop(VideoState *self) +{ + AVFormatContext *pFormatCtx = self->format_ctx; + AVPacket pkt1, *packet = &pkt1; + + try { - VideoPicture *vp; - - /* wait until we have a new pic */ - { - boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) - is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } - - if(is->quit) - return -1; - - // windex is set to 0 initially - vp = &is->pictq[is->pictq_windex]; - - // Convert the image into YUV format that SDL uses - if(is->sws_context == NULL) - { - int w = is->video_st->codec->width; - int h = is->video_st->codec->height; - is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, - w, h, PIX_FMT_RGBA, SWS_BICUBIC, - NULL, NULL, NULL); - if(is->sws_context == NULL) - throw std::runtime_error("Cannot initialize the conversion context!\n"); - } - - vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); - - sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); - - vp->pts = pts; - - // now we inform our display thread that we have a pic ready - if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_windex = 0; - is->pictq_mutex.lock(); - is->pictq_size++; - is->pictq_mutex.unlock(); - - return 0; - } - - double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) - { - double frame_delay; - - if(pts != 0) - { - /* if we have pts, set video clock to it */ - is->video_clock = pts; - } - else - { - /* if we aren't given a pts, set it to the clock */ - pts = is->video_clock; - } - /* update the video clock */ - frame_delay = av_q2d(is->video_st->codec->time_base); - /* if we are repeating a frame, adjust clock accordingly */ - frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); - is->video_clock += frame_delay; - return pts; - } - - uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; - - /* These are called whenever we allocate a frame - * buffer. We use this to store the global_pts in - * a frame at the time it is allocated. - */ - int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) - { - int ret = avcodec_default_get_buffer(c, pic); - uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); - *pts = global_video_pkt_pts; - pic->opaque = pts; - return ret; - } - void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) - { - if(pic) av_freep(&pic->opaque); - avcodec_default_release_buffer(c, pic); - } - - int video_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - AVPacket pkt1, *packet = &pkt1; - int frameFinished; - AVFrame *pFrame; - double pts; - - pFrame = avcodec_alloc_frame(); - - is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + if(self->videoStream < 0 && self->audioStream < 0) + throw std::runtime_error("No streams to decode"); + // main decode loop for(;;) { - if(is->videoq.get(packet, is, 1) < 0) - { - // means we quit getting packets + if(self->quit) break; + + if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; } - pts = 0; - // Save global pts to be stored in pFrame - global_video_pkt_pts = packet->pts; - // Decode video frame - if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) - throw std::runtime_error("Error decoding video frame"); + if(av_read_frame(pFormatCtx, packet) < 0) + break; - if((uint64_t)packet->dts == AV_NOPTS_VALUE && - pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) - pts = *(uint64_t *)pFrame->opaque; - else if((uint64_t)packet->dts != AV_NOPTS_VALUE) - pts = packet->dts; + // Is this a packet from the video stream? + if(packet->stream_index == self->videoStream) + self->videoq.put(packet); + else if(packet->stream_index == self->audioStream) + self->audioq.put(packet); else - pts = 0; - pts *= av_q2d(is->video_st->time_base); - - // Did we get a video frame? - if(frameFinished) - { - pts = synchronize_video(is, pFrame, pts); - if(queue_picture(is, pFrame, pts) < 0) - break; - } - av_free_packet(packet); + av_free_packet(packet); } - - av_free(pFrame); - - avpicture_free((AVPicture *)is->rgbaFrame); - av_free(is->rgbaFrame); - - return 0; + /* all done - wait for it */ + while(!self->quit) + { + // EOF reached, all packets processed, we can exit now + if(self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + } + catch(std::runtime_error& e) { + std::cerr << "An error occured playing the video: " << e.what () << std::endl; + } + catch(Ogre::Exception& e) { + std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; } - int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) + self->quit = 1; +} + + +int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) +{ + MWSound::DecoderPtr decoder; + AVCodecContext *codecCtx; + AVCodec *codec; + + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) + return -1; + + // Get a pointer to the codec context for the video stream + codecCtx = pFormatCtx->streams[stream_index]->codec; + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { - MWSound::DecoderPtr decoder; - AVCodecContext *codecCtx; - AVCodec *codec; - - if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) - return -1; - - // Get a pointer to the codec context for the video stream - codecCtx = pFormatCtx->streams[stream_index]->codec; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) - { - fprintf(stderr, "Unsupported codec!\n"); - return -1; - } - - switch(codecCtx->codec_type) - { - case AVMEDIA_TYPE_AUDIO: - is->audioStream = stream_index; - is->audio_st = pFormatCtx->streams[stream_index]; - - /* averaging filter for audio sync */ - is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); - is->audio_diff_avg_count = 0; - /* Correct audio only if larger error than this */ - is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; - - memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - - decoder.reset(new MovieAudioDecoder(is)); - is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); - if(!is->AudioTrack) - { - is->audioStream = -1; - avcodec_close(is->audio_st->codec); - is->audio_st = NULL; - return -1; - } - break; - - case AVMEDIA_TYPE_VIDEO: - is->videoStream = stream_index; - is->video_st = pFormatCtx->streams[stream_index]; - - is->frame_timer = (double)av_gettime() / 1000000.0; - is->frame_last_delay = 40e-3; - is->video_current_pts_time = av_gettime(); - - codecCtx->get_buffer = our_get_buffer; - codecCtx->release_buffer = our_release_buffer; - is->video_thread = boost::thread(video_thread, is); - break; - - default: - break; - } - - return 0; + fprintf(stderr, "Unsupported codec!\n"); + return -1; } - int decode_thread(void *arg) + switch(codecCtx->codec_type) { - VideoState *is = (VideoState *)arg; - try + case AVMEDIA_TYPE_AUDIO: + this->audioStream = stream_index; + this->audio_st = pFormatCtx->streams[stream_index]; + + /* averaging filter for audio sync */ + this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); + this->audio_diff_avg_count = 0; + /* Correct audio only if larger error than this */ + this->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; + + memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); + + decoder.reset(new MovieAudioDecoder(this)); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!this->AudioTrack) { - AVFormatContext *pFormatCtx = is->format_ctx; - AVPacket pkt1, *packet = &pkt1; - - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) - { - // main decode loop - for(;;) - { - if(is->quit) - break; - - if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - continue; - } - - if(av_read_frame(pFormatCtx, packet) < 0) - break; - - // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) - is->videoq.put(packet); - else if(packet->stream_index == is->audioStream) - is->audioq.put(packet); - else - av_free_packet(packet); - } - /* all done - wait for it */ - while(!is->quit) - { - // EOF reached, all packets processed, we can exit now - if(is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - } - - is->quit = 1; - } - catch (std::runtime_error& e) - { - std::cerr << "An error occured playing the video: " << e.what () << std::endl; - is->quit = 1; - } - catch (Ogre::Exception& e) - { - std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; - is->quit = 1; - } - - return 0; - } - - void VideoState::init(const std::string& resourceName) - { - try - { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - this->av_sync_type = DEFAULT_AV_SYNC_TYPE; - this->videoStream = -1; this->audioStream = -1; - this->refresh = false; - this->quit = 0; - - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); - - this->format_ctx = avformat_alloc_context(); - this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!this->format_ctx->pb) - { - avformat_free_context(this->format_ctx); - throw std::runtime_error("Failed to allocate ioContext "); - } - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) - { - // "Note that a user-supplied AVFormatContext will be freed on failure." - this->format_ctx = NULL; - throw std::runtime_error("Failed to open video input"); - } - - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - 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); - - for(i = 0;i < this->format_ctx->nb_streams;i++) - { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } - - if(audio_index >= 0) - stream_component_open(this, audio_index, this->format_ctx); - if(video_index >= 0) - stream_component_open(this, video_index, this->format_ctx); - } - catch(std::runtime_error& e) - { - this->quit = 1; - throw; - } - catch(Ogre::Exception& e) - { - this->quit = 1; - throw; - } - } - - void VideoState::deinit() - { - this->audioq.cond.notify_one(); - this->videoq.cond.notify_one(); - - this->parse_thread.join(); - this->video_thread.join(); - - if(this->audioStream >= 0) avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); - - if(this->sws_context) - sws_freeContext(this->sws_context); - - if(this->format_ctx) - { - AVIOContext *ioContext = this->format_ctx->pb; - avformat_close_input(&this->format_ctx); - av_free(ioContext); + this->audio_st = NULL; + return -1; } + break; + + case AVMEDIA_TYPE_VIDEO: + this->videoStream = stream_index; + this->video_st = pFormatCtx->streams[stream_index]; + + this->frame_timer = (double)av_gettime() / 1000000.0; + this->frame_last_delay = 40e-3; + this->video_current_pts_time = av_gettime(); + + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + this->video_thread = boost::thread(video_thread_loop, this); + break; + + default: + break; } + return 0; +} + +void VideoState::init(const std::string& resourceName) +{ + try + { + int video_index = -1; + int audio_index = -1; + unsigned int i; + + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 0; + + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + this->format_ctx = avformat_alloc_context(); + this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!this->format_ctx->pb) + { + avformat_free_context(this->format_ctx); + throw std::runtime_error("Failed to allocate ioContext "); + } + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + { + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); + } + + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + 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); + + for(i = 0;i < this->format_ctx->nb_streams;i++) + { + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + this->stream_open(audio_index, this->format_ctx); + if(video_index >= 0) + this->stream_open(video_index, this->format_ctx); + + this->schedule_refresh(40); + this->parse_thread = boost::thread(decode_thread_loop, this); + } + catch(std::runtime_error& e) + { + this->quit = 1; + throw; + } + catch(Ogre::Exception& e) + { + this->quit = 1; + throw; + } +} + +void VideoState::deinit() +{ + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); + + this->parse_thread.join(); + this->video_thread.join(); + + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); + + if(this->sws_context) + sws_freeContext(this->sws_context); + + if(this->format_ctx) + { + AVIOContext *ioContext = this->format_ctx->pb; + avformat_close_input(&this->format_ctx); + av_free(ioContext); + } +} + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -942,9 +923,6 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc mState = new VideoState; mState->init(resourceName); - - schedule_refresh(mState, 40); - mState->parse_thread = boost::thread(decode_thread, mState); } void VideoPlayer::update () @@ -956,7 +934,7 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc else if(mState->refresh) { mState->refresh = false; - video_refresh_timer(mState); + mState->video_refresh_timer(); } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 82a7083ae6..4bbd81a6e7 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -83,6 +83,20 @@ namespace MWRender void init(const std::string& resourceName); void deinit(); + int stream_open(int stream_index, AVFormatContext *pFormatCtx); + + static void video_thread_loop(VideoState *is); + static void decode_thread_loop(VideoState *is); + + void video_display(); + void video_refresh_timer(); + + int queue_picture(AVFrame *pFrame, double pts); + double synchronize_video(AVFrame *src_frame, double pts); + + static void timer_callback(VideoState* is, boost::system_time t); + void schedule_refresh(int delay); + 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); From 157cb10f561112fe174346faef925ec6b32219c8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 02:39:46 -0800 Subject: [PATCH 048/147] Fix a 16-bit audio assumption --- apps/openmw/mwrender/videoplayer.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4334bc634c..6271b2f186 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -145,14 +145,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return samples_size; - double diff, avg_diff, ref_clock; - int wanted_size, min_size, max_size, n; - // int nb_samples; - - n = 2 * is->audio_st->codec->channels; - - ref_clock = get_master_clock(is); - diff = get_audio_clock(is) - ref_clock; + double ref_clock = get_master_clock(is); + double diff = get_audio_clock(is) - ref_clock; if(diff < AV_NOSYNC_THRESHOLD) { // accumulate the diffs @@ -162,12 +156,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder is->audio_diff_avg_count++; else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; - max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, + is->audio_st->codec->sample_fmt, 1); + int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + int min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; if(wanted_size < min_size) wanted_size = min_size; From 8db5d10f1013ba5735819a479a627587d03b2c9a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:11:59 -0800 Subject: [PATCH 049/147] Avoid showing a video picture if we're late Ideally we should skip decoding, or at least YUV->RGB conversion, too. --- apps/openmw/mwrender/videoplayer.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6271b2f186..432cfae3e1 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -447,8 +447,6 @@ void VideoState::video_display() buffer->blitFromMemory(pb); this->display_ready = 1; } - - free(vp->data); } void VideoState::video_refresh_timer() @@ -504,17 +502,21 @@ void VideoState::video_refresh_timer() actual_delay = this->frame_timer - (av_gettime() / 1000000.0); if(actual_delay < 0.010) { - /* Really it should skip the picture instead */ - actual_delay = 0.010; + /* Skip this picture */ + this->refresh = true; + } + else + { + this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); + /* show the picture! */ + this->video_display(); } - this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); - /* show the picture! */ - this->video_display(); + free(vp->data); + vp->data = NULL; /* update queue for next picture! */ - if(++this->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - this->pictq_rindex = 0; + this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_mutex.lock(); this->pictq_size--; this->pictq_cond.notify_one(); @@ -550,13 +552,12 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) throw std::runtime_error("Cannot initialize the conversion context!\n"); } + vp->pts = pts; vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); sws_scale(this->sws_context, pFrame->data, pFrame->linesize, 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); - vp->pts = pts; - // 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_mutex.lock(); From 4d6c05f6ccd4855f6538cb4f7f5e4961a9316cd3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:19:32 -0800 Subject: [PATCH 050/147] Tighten audio skew allowance --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 432cfae3e1..b901c097d3 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -733,7 +733,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); this->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; + this->audio_diff_threshold = 2.0 * 0.025/* 25 ms */; memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); From a6e627001a128948708028170e846243a787f987 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:23:04 -0800 Subject: [PATCH 051/147] Avoid a for(;;) construct --- apps/openmw/mwrender/videoplayer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b901c097d3..b9ff81de01 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -662,11 +662,8 @@ void VideoState::decode_thread_loop(VideoState *self) throw std::runtime_error("No streams to decode"); // main decode loop - for(;;) + while(!self->quit) { - if(self->quit) - break; - if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { From 2f37d31108f5190ee5312d724431b20b8fabe879 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 04:12:34 -0800 Subject: [PATCH 052/147] Move some definitions into the source file they're used in --- apps/openmw/mwrender/videoplayer.cpp | 18 +++++++++++++++++- apps/openmw/mwrender/videoplayer.hpp | 13 ------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b9ff81de01..9cbac16884 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -8,9 +8,25 @@ #include "../mwsound/sound.hpp" +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define AUDIO_DIFF_AVG_NB 20 + + namespace MWRender { +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, + + AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER +}; + void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; @@ -773,7 +789,7 @@ void VideoState::init(const std::string& resourceName) int audio_index = -1; unsigned int i; - this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->av_sync_type = AV_SYNC_DEFAULT; this->videoStream = -1; this->audioStream = -1; this->refresh = false; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 4bbd81a6e7..428907c4ad 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,15 +21,7 @@ extern "C" #include "../mwbase/soundmanager.hpp" -#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) -#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) -#define AV_SYNC_THRESHOLD 0.01 -#define AV_NOSYNC_THRESHOLD 10.0 -#define SAMPLE_CORRECTION_PERCENT_MAX 10 -#define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 -#define DEFAULT_AV_SYNC_TYPE AV_SYNC_EXTERNAL_MASTER - namespace MWRender { @@ -147,11 +139,6 @@ namespace MWRender int display_ready; }; - enum { - AV_SYNC_AUDIO_MASTER, - AV_SYNC_VIDEO_MASTER, - AV_SYNC_EXTERNAL_MASTER - }; class VideoPlayer From 05c6483257f2947f1ea24b3e102dc8b8e03e60af Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 04:35:57 -0800 Subject: [PATCH 053/147] Fix external clock --- apps/openmw/mwrender/videoplayer.cpp | 5 +++-- apps/openmw/mwrender/videoplayer.hpp | 17 ++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9cbac16884..56f0bc691c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -128,7 +128,7 @@ static double get_video_clock(VideoState *is) static double get_external_clock(VideoState *is) { - return av_gettime() / 1000000.0; + return ((uint64_t)av_gettime()-is->external_clock_base) / 1000000.0; } static double get_master_clock(VideoState *is) @@ -746,7 +746,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); this->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.025/* 25 ms */; + this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); @@ -831,6 +831,7 @@ void VideoState::init(const std::string& resourceName) audio_index = i; } + this->external_clock_base = av_gettime(); if(audio_index >= 0) this->stream_open(audio_index, this->format_ctx); if(video_index >= 0) diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 428907c4ad..798611d90d 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -55,12 +55,12 @@ namespace MWRender struct VideoState { VideoState () - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), - external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), - audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), - frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), - video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), - pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), + audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), + video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), + quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} ~VideoState() @@ -95,9 +95,8 @@ namespace MWRender int videoStream, audioStream; - int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; + int av_sync_type; + uint64_t external_clock_base; double audio_clock; AVStream *audio_st; From 7332ffb0f865e61e2359d816b9156bf1f732652c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 06:02:34 -0800 Subject: [PATCH 054/147] Let the wanted sample size go down to 0 --- apps/openmw/mwrender/videoplayer.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 56f0bc691c..4e9dd0ca21 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -178,13 +178,10 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, is->audio_st->codec->sample_fmt, 1); int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - int min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; - if(wanted_size < min_size) - wanted_size = min_size; - else if (wanted_size > max_size) - wanted_size = max_size; + wanted_size = std::max(0, wanted_size); + wanted_size = std::min(wanted_size, max_size); if(wanted_size < samples_size) { From d66d8a3118926c5577501980ead59ad24ab03026 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 06:43:51 -0800 Subject: [PATCH 055/147] Don't assume we can write beyond the end of the sample buffer --- apps/openmw/mwrender/videoplayer.cpp | 87 ++++++++++++++++------------ 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4e9dd0ca21..93255dd458 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -151,8 +151,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder VideoState *is; AVFrame *mFrame; - size_t mFramePos; - size_t mFrameSize; + ssize_t mFramePos; + ssize_t mFrameSize; /* Add or subtract samples to get a better sync, return new * audio buffer size */ @@ -175,38 +175,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, - is->audio_st->codec->sample_fmt, 1); + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, max_size); + wanted_size = std::min(wanted_size, samples_size*2); - if(wanted_size < samples_size) - { - /* remove samples */ - samples_size = wanted_size; - } - else if(wanted_size > samples_size) - { - uint8_t *samples_end, *q; - int nb; - - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = samples + samples_size - n; - q = samples_end + n; - - while(nb > 0) - { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - - samples_size = wanted_size; - } + samples_size = wanted_size; } } } @@ -249,8 +225,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame) continue; - int smp_size = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, - is->audio_st->codec->sample_fmt, 1); + int smp_size = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; data_size = frame->nb_samples * smp_size; if(data_size <= 0) { @@ -360,19 +336,54 @@ public: double pts; /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(mFrame, &pts); - if(audio_size < 0) + mFrameSize = audio_decode_frame(mFrame, &pts); + if(mFrameSize < 0) { /* If error, we're done */ break; } - mFrameSize = synchronize_audio(mFrame->data[0], audio_size, pts); - mFramePos = 0; + audio_size = synchronize_audio(mFrame->data[0], mFrameSize, pts); + mFramePos = mFrameSize - audio_size; } - size_t len1 = std::min(len - total, mFrameSize-mFramePos); - memcpy(stream, mFrame->data[0]+mFramePos, len1); + size_t len1 = len - total; + if(mFramePos >= 0) + { + len1 = std::min(len1, mFrameSize-mFramePos); + memcpy(stream, mFrame->data[0]+mFramePos, len1); + } + else + { + len1 = std::min(len1, -mFramePos); + + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; + + /* add samples by copying the first sample*/ + if(n == 1) + memset(stream, *mFrame->data[0], len1); + else if(n == 2) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int16_t*)(stream+nb)) = *((int16_t*)mFrame->data[0]); + } + else if(n == 4) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int32_t*)(stream+nb)) = *((int32_t*)mFrame->data[0]); + } + else if(n == 8) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int64_t*)(stream+nb)) = *((int64_t*)mFrame->data[0]); + } + else + { + for(size_t nb = 0;nb < len1;nb += n) + memcpy(stream+nb, mFrame->data[0], n); + } + } total += len1; stream += len1; From f97eaec7ab447ab8d0f2818b38118f7cb561ad83 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 07:25:28 -0800 Subject: [PATCH 056/147] Consolidate some code --- apps/openmw/mwrender/videoplayer.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 93255dd458..396b91c333 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -199,20 +199,15 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int audio_decode_frame(AVFrame *frame, double *pts_ptr) { AVPacket *pkt = &is->audio_pkt; - int len1, data_size; for(;;) { while(pkt->size > 0) { - int got_frame; + int len1, got_frame; len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); - if(len1 < 0 || len1 > pkt->size) - { - /* if error, skip packet */ - break; - } + if(len1 < 0) break; if(len1 <= pkt->size) { @@ -222,24 +217,18 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder memset(&pkt->data[remaining], 0, pkt->size - remaining); pkt->size -= len1; } - if(!got_frame) - continue; - int smp_size = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - data_size = frame->nb_samples * smp_size; - if(data_size <= 0) - { - /* No data yet, get more frames */ + /* No data yet? Look for more frames */ + if(!got_frame || frame->nb_samples <= 0) continue; - } *pts_ptr = is->audio_clock; - is->audio_clock += (double)(data_size/smp_size) / + is->audio_clock += (double)frame->nb_samples / (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ - return data_size; + return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; } if(pkt->data) av_free_packet(pkt); From 582efcdb9bc13bac35abcd9d65a4a7a285a3eeb5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 07:40:19 -0800 Subject: [PATCH 057/147] Always try to resync if the clock difference is large --- apps/openmw/mwrender/videoplayer.cpp | 62 +++++++++++----------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 396b91c333..bdfffd0cb2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,7 +11,6 @@ #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 -#define AV_NOSYNC_THRESHOLD 10.0 #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 @@ -161,36 +160,26 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return samples_size; - double ref_clock = get_master_clock(is); - double diff = get_audio_clock(is) - ref_clock; - if(diff < AV_NOSYNC_THRESHOLD) - { - // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) - is->audio_diff_avg_count++; - else - { - double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) - { - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - - wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, samples_size*2); - - samples_size = wanted_size; - } - } - } + // accumulate the clock difference + double diff = get_audio_clock(is) - get_master_clock(is); + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) + is->audio_diff_avg_count++; else { - /* difference is TOO big; reset diff stuff */ - is->audio_diff_avg_count = 0; - is->audio_diff_cum = 0; + double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) + { + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; + int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + + wanted_size = std::max(0, wanted_size); + wanted_size = std::min(wanted_size, samples_size*2); + + samples_size = wanted_size; + } } return samples_size; @@ -499,15 +488,12 @@ void VideoState::video_refresh_timer() diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } + * FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } this->frame_timer += delay; From 71ff90aaee5bcd8208996a66de53ec3ceee2088a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 08:42:37 -0800 Subject: [PATCH 058/147] Don't use sub-frame timing for the video clock --- apps/openmw/mwrender/videoplayer.cpp | 11 +++-------- apps/openmw/mwrender/videoplayer.hpp | 11 +++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bdfffd0cb2..fe228d25e6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -119,10 +119,7 @@ static double get_audio_clock(VideoState *is) static double get_video_clock(VideoState *is) { - double delta; - - delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; - return is->video_current_pts + delta; + return is->video_current_pts; } static double get_external_clock(VideoState *is) @@ -469,9 +466,6 @@ void VideoState::video_refresh_timer() vp = &this->pictq[this->pictq_rindex]; - this->video_current_pts = vp->pts; - this->video_current_pts_time = av_gettime(); - delay = vp->pts - this->frame_last_pts; /* the pts from last time */ if(delay <= 0 || delay >= 1.0) { /* if incorrect delay, use previous one */ @@ -481,6 +475,8 @@ void VideoState::video_refresh_timer() this->frame_last_delay = delay; this->frame_last_pts = vp->pts; + this->video_current_pts = vp->pts; + /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { @@ -750,7 +746,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->frame_timer = (double)av_gettime() / 1000000.0; this->frame_last_delay = 40e-3; - this->video_current_pts_time = av_gettime(); codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 798611d90d..1213ca36c5 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -58,9 +58,9 @@ namespace MWRender : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), - video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), - quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), + rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), + refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} ~VideoState() @@ -68,8 +68,8 @@ namespace MWRender audioq.flush(); videoq.flush(); - if(pictq_size >= 1) - free(pictq[0].data); + for(int i = 0;i < VIDEO_PICTURE_QUEUE_SIZE;i++) + free(pictq[i].data); } void init(const std::string& resourceName); @@ -112,7 +112,6 @@ namespace MWRender double frame_last_delay; double video_clock; /// Date: Fri, 14 Dec 2012 09:07:59 -0800 Subject: [PATCH 059/147] Move more stuff to where it should be, and improve cleanup --- apps/openmw/mwrender/videoplayer.cpp | 174 +++++++++++++++++++++------ apps/openmw/mwrender/videoplayer.hpp | 115 ------------------ 2 files changed, 138 insertions(+), 151 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index fe228d25e6..2a545f7725 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -13,6 +13,7 @@ #define AV_SYNC_THRESHOLD 0.01 #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 1 namespace MWRender @@ -26,6 +27,135 @@ enum { AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER }; +struct PacketQueue { + PacketQueue() + : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + { } + ~PacketQueue() + { flush(); } + + AVPacketList *first_pkt, *last_pkt; + int nb_packets; + int size; + + boost::mutex mutex; + boost::condition_variable cond; + + void put(AVPacket *pkt); + int get(AVPacket *pkt, VideoState *is, int block); + + void flush(); +}; + +struct VideoPicture { + VideoPicture() : pts(0.0) + { } + + std::vector data; + double pts; +}; + +struct VideoState { + VideoState() + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), + audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), + rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), + refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + { } + + ~VideoState() + { } + + void init(const std::string& resourceName); + void deinit(); + + int stream_open(int stream_index, AVFormatContext *pFormatCtx); + + static void video_thread_loop(VideoState *is); + static void decode_thread_loop(VideoState *is); + + void video_display(); + void video_refresh_timer(); + + int queue_picture(AVFrame *pFrame, double pts); + double synchronize_video(AVFrame *src_frame, double pts); + + static void timer_callback(VideoState* is, boost::system_time t); + void schedule_refresh(int delay); + + + double get_audio_clock() + { return this->AudioTrack->getTimeOffset(); } + + double get_video_clock() + { return this->video_current_pts; } + + double get_external_clock() + { return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0; } + + double get_master_clock() + { + if(this->av_sync_type == AV_SYNC_VIDEO_MASTER) + return this->get_video_clock(); + if(this->av_sync_type == AV_SYNC_AUDIO_MASTER) + return this->get_audio_clock(); + return this->get_external_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); + + + int videoStream, audioStream; + + int av_sync_type; + uint64_t external_clock_base; + + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; + AVPacket audio_pkt; + double audio_diff_cum; /* used for AV difference average computation */ + double audio_diff_avg_coef; + double audio_diff_threshold; + int audio_diff_avg_count; + + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double video_clock; ///AudioTrack->getTimeOffset(); -} - -static double get_video_clock(VideoState *is) -{ - return is->video_current_pts; -} - -static double get_external_clock(VideoState *is) -{ - return ((uint64_t)av_gettime()-is->external_clock_base) / 1000000.0; -} - -static double get_master_clock(VideoState *is) -{ - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(is); - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(is); - return get_external_clock(is); -} - - class MovieAudioDecoder : public MWSound::Sound_Decoder { static void fail(const std::string &str) @@ -158,7 +263,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return samples_size; // accumulate the clock difference - double diff = get_audio_clock(is) - get_master_clock(is); + double diff = is->get_audio_clock() - is->get_master_clock(); is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) @@ -441,7 +546,7 @@ void VideoState::video_display() Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); } - Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); this->display_ready = 1; @@ -451,7 +556,7 @@ void VideoState::video_display() void VideoState::video_refresh_timer() { VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + double actual_delay, delay; if(!this->video_st) { @@ -480,12 +585,11 @@ void VideoState::video_refresh_timer() /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - ref_clock = get_master_clock(this); - diff = vp->pts - ref_clock; + double diff = this->get_video_clock() - this->get_master_clock(); /* Skip or repeat the frame. Take delay into account * FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); + double sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); if(diff <= -sync_threshold) delay = 0; else if(diff >= sync_threshold) @@ -507,9 +611,6 @@ void VideoState::video_refresh_timer() this->video_display(); } - free(vp->data); - vp->data = NULL; - /* update queue for next picture! */ this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_mutex.lock(); @@ -548,10 +649,11 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); + vp->data.resize(this->video_st->codec->width * this->video_st->codec->height * 4); + uint8_t *dst = &vp->data[0]; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); + 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; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 1213ca36c5..7d3e4ac02b 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,127 +21,13 @@ extern "C" #include "../mwbase/soundmanager.hpp" -#define VIDEO_PICTURE_QUEUE_SIZE 1 namespace MWRender { struct VideoState; - struct PacketQueue { - PacketQueue() - : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) - { } - - AVPacketList *first_pkt, *last_pkt; - int nb_packets; - int size; - - boost::mutex mutex; - boost::condition_variable cond; - - void put(AVPacket *pkt); - int get(AVPacket *pkt, VideoState *is, int block); - - void flush(); - }; - - struct VideoPicture { - VideoPicture () : data(NULL), pts(0) - { } - - uint8_t *data; - double pts; - }; - - struct VideoState { - VideoState () - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), - rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), - refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) - {} - - ~VideoState() - { - audioq.flush(); - videoq.flush(); - - for(int i = 0;i < VIDEO_PICTURE_QUEUE_SIZE;i++) - free(pictq[i].data); - } - - void init(const std::string& resourceName); - void deinit(); - - int stream_open(int stream_index, AVFormatContext *pFormatCtx); - - static void video_thread_loop(VideoState *is); - static void decode_thread_loop(VideoState *is); - - void video_display(); - void video_refresh_timer(); - - int queue_picture(AVFrame *pFrame, double pts); - double synchronize_video(AVFrame *src_frame, double pts); - - static void timer_callback(VideoState* is, boost::system_time t); - void schedule_refresh(int delay); - - 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); - - int videoStream, audioStream; - - int av_sync_type; - uint64_t external_clock_base; - - double audio_clock; - AVStream *audio_st; - PacketQueue audioq; - AVPacket audio_pkt; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; - - double frame_timer; - double frame_last_pts; - double frame_last_delay; - double video_clock; /// Date: Fri, 14 Dec 2012 23:42:49 -0800 Subject: [PATCH 060/147] Update the queued sample count immediately --- apps/openmw/mwsound/openal_output.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 672e52b56e..bfead78433 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -339,8 +339,6 @@ bool OpenAL_SoundStream::process() { try { bool finished = mIsFinished; - ALint samples_unqueued = 0; - ALint samples_queued = 0; ALint processed, state; alGetSourcei(mSource, AL_SOURCE_STATE, &state); @@ -355,7 +353,7 @@ bool OpenAL_SoundStream::process() size_t got; alSourceUnqueueBuffers(mSource, 1, &bufid); - samples_unqueued += getBufferSampleCount(bufid); + mSamplesQueued -= getBufferSampleCount(bufid); processed--; if(finished) @@ -367,7 +365,7 @@ bool OpenAL_SoundStream::process() { alBufferData(bufid, mFormat, &data[0], got, mSampleRate); alSourceQueueBuffers(mSource, 1, &bufid); - samples_queued += getBufferSampleCount(bufid); + mSamplesQueued += getBufferSampleCount(bufid); } } while(processed > 0); throwALerror(); @@ -383,8 +381,6 @@ bool OpenAL_SoundStream::process() throwALerror(); } - mSamplesQueued -= samples_unqueued; - mSamplesQueued += samples_queued; mIsFinished = finished; } catch(std::exception &e) { From 62a995d492ecf02cf0ac02bcec92cd0360736e95 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 00:18:42 -0800 Subject: [PATCH 061/147] Calculate audio sync once per read --- apps/openmw/mwrender/videoplayer.cpp | 36 ++++++++++++---------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2a545f7725..7281e1c3a6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -255,15 +255,17 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; - /* Add or subtract samples to get a better sync, return new - * audio buffer size */ - int synchronize_audio(uint8_t *samples, int samples_size, double pts) + /* Add or subtract samples to get a better sync, return number of bytes to + * skip (negative means to duplicate). */ + int synchronize_audio() { if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return samples_size; + return 0; + + int sample_skip = 0; // accumulate the clock difference - double diff = is->get_audio_clock() - is->get_master_clock(); + double diff = is->get_master_clock() - is->get_audio_clock(); is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) @@ -275,19 +277,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * is->audio_st->codec->channels; - int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - - wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, samples_size*2); - - samples_size = wanted_size; + sample_skip = ((int)(diff * is->audio_st->codec->sample_rate) * n); } } - return samples_size; + return sample_skip; } - int audio_decode_frame(AVFrame *frame, double *pts_ptr) + int audio_decode_frame(AVFrame *frame) { AVPacket *pkt = &is->audio_pkt; @@ -313,7 +310,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - *pts_ptr = is->audio_clock; is->audio_clock += (double)frame->nb_samples / (double)is->audio_st->codec->sample_rate; @@ -406,25 +402,23 @@ public: size_t read(char *stream, size_t len) { + int sample_skip = synchronize_audio(); size_t total = 0; while(total < len) { - if(mFramePos >= mFrameSize) + while(mFramePos >= mFrameSize) { - int audio_size; - double pts; - /* We have already sent all our data; get more */ - mFrameSize = audio_decode_frame(mFrame, &pts); + mFrameSize = audio_decode_frame(mFrame); if(mFrameSize < 0) { /* If error, we're done */ break; } - audio_size = synchronize_audio(mFrame->data[0], mFrameSize, pts); - mFramePos = mFrameSize - audio_size; + mFramePos = std::min(mFrameSize, sample_skip); + sample_skip -= mFramePos; } size_t len1 = len - total; From b41a77648ec9444b3136e9303fc00b59306c6a1d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 02:33:59 -0800 Subject: [PATCH 062/147] Avoid re-reading the source sample to duplicate, to avoid pointer aliasing --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7281e1c3a6..755378c284 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -439,18 +439,21 @@ public: memset(stream, *mFrame->data[0], len1); else if(n == 2) { + const int16_t val = *((int16_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int16_t*)(stream+nb)) = *((int16_t*)mFrame->data[0]); + *((int16_t*)(stream+nb)) = val; } else if(n == 4) { + const int32_t val = *((int32_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int32_t*)(stream+nb)) = *((int32_t*)mFrame->data[0]); + *((int32_t*)(stream+nb)) = val; } else if(n == 8) { + const int64_t val = *((int64_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int64_t*)(stream+nb)) = *((int64_t*)mFrame->data[0]); + *((int64_t*)(stream+nb)) = val; } else { From eb0e8d9e37043e8d9e312ed7a22fcf9ca96d0f2c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 02:50:59 -0800 Subject: [PATCH 063/147] Simplify PacketQueue::get --- apps/openmw/mwrender/videoplayer.cpp | 32 +++++++--------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 755378c284..e54b4c5d9c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -42,7 +42,7 @@ struct PacketQueue { boost::condition_variable cond; void put(AVPacket *pkt); - int get(AVPacket *pkt, VideoState *is, int block); + int get(AVPacket *pkt, VideoState *is); void flush(); }; @@ -181,21 +181,12 @@ void PacketQueue::put(AVPacket *pkt) this->mutex.unlock(); } -int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) +int PacketQueue::get(AVPacket *pkt, VideoState *is) { - AVPacketList *pkt1; - int ret; - boost::unique_lock lock(this->mutex); - for(;;) + while(!is->quit) { - if(is->quit) - { - ret = -1; - break; - } - - pkt1 = this->first_pkt; + AVPacketList *pkt1 = this->first_pkt; if(pkt1) { this->first_pkt = pkt1->next; @@ -207,20 +198,13 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) *pkt = pkt1->pkt; av_free(pkt1); - ret = 1; - break; - } - - if (!block) - { - ret = 0; - break; + return 1; } this->cond.wait(lock); } - return ret; + return -1; } void PacketQueue::flush() @@ -324,7 +308,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(is->audioq.get(pkt, is, 1) < 0) + if(is->audioq.get(pkt, is) < 0) return -1; /* if update, update the audio clock w/pts */ @@ -714,7 +698,7 @@ void VideoState::video_thread_loop(VideoState *self) self->rgbaFrame = avcodec_alloc_frame(); avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); - while(self->videoq.get(packet, self, 1) >= 0) + while(self->videoq.get(packet, self) >= 0) { // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; From d50698d7d1598e79fa6fb6bbfbfc47f5bc386b9f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 04:01:52 -0800 Subject: [PATCH 064/147] Clean up the rectangle and scene node used for displaying the video --- apps/openmw/mwrender/videoplayer.cpp | 195 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 3 +- 2 files changed, 106 insertions(+), 92 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e54b4c5d9c..5921a4e6f2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -938,103 +938,116 @@ void VideoState::deinit() } - VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) - : mState(NULL) - , mSceneMgr(sceneMgr) - { - mVideoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); +VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) + : mState(NULL) + , mSceneMgr(sceneMgr) + , mVideoMaterial(NULL) + , mRectangle(NULL) + , mNode(NULL) +{ + mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); - mRectangle = new Ogre::Rectangle2D(true); - mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); - // Use infinite AAB to always stay visible - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - mRectangle->setBoundingBox(aabInf); - // Attach background to the scene - Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(mRectangle); - mRectangle->setVisible(false); - mRectangle->setVisibilityFlags (0x1); + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("VideoMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + + // Attach background to the scene + mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mNode->attachObject(mRectangle); + + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags(0x1); +} + +VideoPlayer::~VideoPlayer() +{ + if(mState) + close(); + + if(mNode) + mSceneMgr->destroySceneNode(mNode); + mNode = NULL; + + if(mRectangle) + delete mRectangle; + mRectangle = NULL; +} + +void VideoPlayer::playVideo(const std::string &resourceName) +{ + // Register all formats and codecs + av_register_all(); + + if(mState) + close(); + + mRectangle->setVisible(true); + + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); + + // Turn off rendering except the GUI + mSceneMgr->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. + for(int i = 0;i < Ogre::RENDER_QUEUE_MAX;++i) + { + if(i > 0 && i < 96) + mSceneMgr->addSpecialCaseRenderQueue(i); } + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - VideoPlayer::~VideoPlayer () + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + + mState = new VideoState; + mState->init(resourceName); +} + +void VideoPlayer::update () +{ + if(mState) { - if (mState) + if(mState->quit) close(); - } - - void VideoPlayer::playVideo (const std::string &resourceName) - { - // Register all formats and codecs - av_register_all(); - - if (mState) - close(); - - mRectangle->setVisible(true); - - MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); - - // Turn off rendering except the GUI - mSceneMgr->clearSpecialCaseRenderQueues(); - // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. - for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) + else if(mState->refresh) { - if (i > 0 && i < 96) - mSceneMgr->addSpecialCaseRenderQueue(i); + mState->refresh = false; + mState->video_refresh_timer(); + // Would be nice not to do this all the time... + if(mState->display_ready) + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); } - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); - - mState = new VideoState; - mState->init(resourceName); - } - - void VideoPlayer::update () - { - if(mState) - { - if(mState->quit) - close(); - else if(mState->refresh) - { - mState->refresh = false; - mState->video_refresh_timer(); - } - } - - if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); - else - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("black.png"); - } - - void VideoPlayer::close() - { - mState->quit = 1; - mState->deinit(); - - delete mState; - mState = NULL; - - MWBase::Environment::get().getSoundManager()->resumeAllSounds(); - - mRectangle->setVisible (false); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); - - mSceneMgr->clearSpecialCaseRenderQueues(); - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - } - - bool VideoPlayer::isPlaying () - { - return mState != NULL; } } + +void VideoPlayer::close() +{ + mState->quit = 1; + mState->deinit(); + + delete mState; + mState = NULL; + + MWBase::Environment::get().getSoundManager()->resumeAllSounds(); + + mRectangle->setVisible(false); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); + + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); +} + +bool VideoPlayer::isPlaying () +{ + return mState != NULL; +} + +} diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 7d3e4ac02b..9060241bdb 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -45,8 +45,9 @@ namespace MWRender VideoState* mState; Ogre::SceneManager* mSceneMgr; - Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mVideoMaterial; + Ogre::Rectangle2D* mRectangle; + Ogre::SceneNode* mNode; }; } From da44141b95dc712d3872c5398cf4b21adb5f3ed7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 04:17:28 -0800 Subject: [PATCH 065/147] Avoid creating extra texture unit states on the video material --- apps/openmw/mwrender/videoplayer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 5921a4e6f2..4f42c3fb6f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,10 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + if(mVideoMaterial->getTechnique(0)->getPass(0)->getNumTextureUnitStates() == 0) + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + else + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); From 6008cf0d1511f3c2a05c6696bbd457bcfd181236 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 05:02:01 -0800 Subject: [PATCH 066/147] Remove unneeded video_current_pts field --- apps/openmw/mwrender/videoplayer.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4f42c3fb6f..e7bdfab91a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -60,9 +60,9 @@ struct VideoState { : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), - rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), - refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), + sws_context(NULL), display_ready(0) { } ~VideoState() @@ -90,7 +90,7 @@ struct VideoState { { return this->AudioTrack->getTimeOffset(); } double get_video_clock() - { return this->video_current_pts; } + { return this->frame_last_pts; } double get_external_clock() { return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0; } @@ -128,7 +128,6 @@ struct VideoState { double frame_last_pts; double frame_last_delay; double video_clock; ///frame_last_delay = delay; this->frame_last_pts = vp->pts; - this->video_current_pts = vp->pts; - /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { From db23c8152e5f0030fea540364ac69d5dc9cff7ab Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 07:33:27 -0800 Subject: [PATCH 067/147] Only duplicate AVPackets as needed Packets that don't have a destruct method are using static memory, which will only be valid until the next av_read_frame call. Otherwise, it's already dynamically allocated and will remain valid. --- apps/openmw/mwrender/videoplayer.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e7bdfab91a..bfcb927459 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -158,14 +158,21 @@ struct VideoState { void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if(!pkt1) throw std::bad_alloc(); pkt1->pkt = *pkt; pkt1->next = NULL; + if(pkt1->pkt.destruct == NULL) + { + if(av_dup_packet(&pkt1->pkt) < 0) + { + av_free(pkt1); + throw std::runtime_error("Failed to duplicate packet"); + } + av_free_packet(pkt); + } + this->mutex.lock (); if(!last_pkt) From 74773454814a0106821574e28b8d2d13594aeefa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 17:10:21 +0100 Subject: [PATCH 068/147] fixed video material --- apps/openmw/mwrender/videoplayer.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bfcb927459..407f1c2129 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -407,7 +407,7 @@ public: break; } - mFramePos = std::min(mFrameSize, sample_skip); + mFramePos = std::min(static_cast(mFrameSize), sample_skip); sample_skip -= mFramePos; } @@ -949,14 +949,16 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) , mRectangle(NULL) , mNode(NULL) { - mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - if(mVideoMaterial->getTechnique(0)->getPass(0)->getNumTextureUnitStates() == 0) + mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); + if (mVideoMaterial.isNull ()) + { + mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); - else - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); + } + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); @@ -1016,6 +1018,8 @@ void VideoPlayer::playVideo(const std::string &resourceName) mState = new VideoState; mState->init(resourceName); + + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); } void VideoPlayer::update () From fa1ad381da72c76ea8f5f85c19959630aab7e3ea Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 08:42:54 -0800 Subject: [PATCH 069/147] Make sure packets are cleaned up properly --- apps/openmw/mwrender/videoplayer.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bfcb927459..b0bf371549 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -118,7 +118,6 @@ struct VideoState { double audio_clock; AVStream *audio_st; PacketQueue audioq; - AVPacket audio_pkt; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; @@ -239,8 +238,19 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder throw std::runtime_error(str); } + struct AutoAVPacket : public AVPacket { + AutoAVPacket(int size=0) + { + if(av_new_packet(this, size) < 0) + throw std::bad_alloc(); + } + ~AutoAVPacket() + { av_free_packet(this); } + }; + VideoState *is; + AutoAVPacket mPacket; AVFrame *mFrame; ssize_t mFramePos; ssize_t mFrameSize; @@ -276,7 +286,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int audio_decode_frame(AVFrame *frame) { - AVPacket *pkt = &is->audio_pkt; + AVPacket *pkt = &mPacket; for(;;) { @@ -292,8 +302,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* Move the unread data to the front and clear the end bits */ int remaining = pkt->size - len1; memmove(pkt->data, &pkt->data[len1], remaining); - memset(&pkt->data[remaining], 0, pkt->size - remaining); - pkt->size -= len1; + av_shrink_packet(pkt, remaining); } /* No data yet? Look for more frames */ @@ -307,8 +316,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * is->audio_st->codec->channels; } - if(pkt->data) - av_free_packet(pkt); + av_free_packet(pkt); if(is->quit) return -1; @@ -717,6 +725,8 @@ void VideoState::video_thread_loop(VideoState *self) pts = *(uint64_t*)pFrame->opaque; pts *= av_q2d(self->video_st->time_base); + av_free_packet(packet); + // Did we get a video frame? if(frameFinished) { @@ -724,7 +734,6 @@ void VideoState::video_thread_loop(VideoState *self) if(self->queue_picture(pFrame, pts) < 0) break; } - av_free_packet(packet); } av_free(pFrame); @@ -814,8 +823,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) /* Correct audio only if larger error than this */ this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; - memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); - decoder.reset(new MovieAudioDecoder(this)); this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) From 9b3cf5c1593c72dc2e2fe85f02074b57d69e1e93 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 09:14:58 -0800 Subject: [PATCH 070/147] Use a looping thread to trigger refreshes --- apps/openmw/mwrender/videoplayer.cpp | 63 ++++++++++------------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b0bf371549..f6edf9ad90 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -59,7 +59,7 @@ struct VideoState { VideoState() : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) @@ -82,8 +82,7 @@ struct VideoState { int queue_picture(AVFrame *pFrame, double pts); double synchronize_video(AVFrame *src_frame, double pts); - static void timer_callback(VideoState* is, boost::system_time t); - void schedule_refresh(int delay); + static void video_refresh(VideoState *is); double get_audio_clock() @@ -123,7 +122,6 @@ struct VideoState { double audio_diff_threshold; int audio_diff_avg_count; - double frame_timer; double frame_last_pts; double frame_last_delay; double video_clock; ///refresh = true; -} - -/* schedule a video refresh in 'delay' ms */ -void VideoState::schedule_refresh(int delay) -{ - boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread(boost::bind(&timer_callback, this, t)).detach(); + boost::system_time t = boost::get_system_time(); + while(!is->quit) + { + t += boost::posix_time::milliseconds(is->refresh_rate_ms); + boost::this_thread::sleep(t); + is->refresh = true; + } } @@ -551,18 +550,10 @@ void VideoState::video_display() void VideoState::video_refresh_timer() { VideoPicture *vp; - double actual_delay, delay; + double delay; - if(!this->video_st) - { - this->schedule_refresh(100); - return; - } if(this->pictq_size == 0) - { - this->refresh = true; return; - } vp = &this->pictq[this->pictq_rindex]; @@ -575,6 +566,8 @@ void VideoState::video_refresh_timer() this->frame_last_delay = delay; this->frame_last_pts = vp->pts; + /* FIXME: Syncing should be done in the decoding stage, where frames can be + * skipped or duplicated as needed. */ /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { @@ -588,21 +581,10 @@ void VideoState::video_refresh_timer() else if(diff >= sync_threshold) delay = 2 * delay; } - this->frame_timer += delay; - /* compute the REAL delay */ - actual_delay = this->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Skip this picture */ - this->refresh = true; - } - else - { - this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); - /* show the picture! */ - this->video_display(); - } + this->refresh_rate_ms = std::max(1, (int)(delay*1000.0)); + /* show the picture! */ + this->video_display(); /* update queue for next picture! */ this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; @@ -838,12 +820,12 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->videoStream = stream_index; this->video_st = pFormatCtx->streams[stream_index]; - this->frame_timer = (double)av_gettime() / 1000000.0; this->frame_last_delay = 40e-3; codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; this->video_thread = boost::thread(video_thread_loop, this); + this->refresh_thread = boost::thread(video_refresh, this); break; default: @@ -864,6 +846,7 @@ void VideoState::init(const std::string& resourceName) this->av_sync_type = AV_SYNC_DEFAULT; this->videoStream = -1; this->audioStream = -1; + this->refresh_rate_ms = 10; this->refresh = false; this->quit = 0; @@ -909,7 +892,6 @@ void VideoState::init(const std::string& resourceName) if(video_index >= 0) this->stream_open(video_index, this->format_ctx); - this->schedule_refresh(40); this->parse_thread = boost::thread(decode_thread_loop, this); } catch(std::runtime_error& e) @@ -931,6 +913,7 @@ void VideoState::deinit() this->parse_thread.join(); this->video_thread.join(); + this->refresh_thread.join(); if(this->audioStream >= 0) avcodec_close(this->audio_st->codec); From 5ed04ae53ef6465b3cc167f720c58267872a8167 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 19:23:03 +0100 Subject: [PATCH 071/147] added black bars --- apps/openmw/mwrender/renderingmanager.cpp | 3 ++ apps/openmw/mwrender/videoplayer.cpp | 44 +++++++++++++++++++---- apps/openmw/mwrender/videoplayer.hpp | 7 ++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 811d000a9f..3abf88cca3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -162,6 +162,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video")); mSun = 0; @@ -843,6 +844,8 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) mCompositors->recreate(); mWater->assignTextures(); + mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); + const Settings::CategorySettingVector& changed = Settings::Manager::apply(); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); //FIXME MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); // FIXME diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4f9963de58..11b6d2b8fb 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -950,22 +950,41 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); + Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General"); + if (blackMaterial.isNull ()) + { + blackMaterial = Ogre::MaterialManager::getSingleton().create("BlackBarsMaterial", "General"); + blackMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + } + mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+2); + mBackgroundRectangle = new Ogre::Rectangle2D(true); + mBackgroundRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mBackgroundRectangle->setMaterial("BlackBarsMaterial"); + mBackgroundRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); // Use infinite AAB to always stay visible Ogre::AxisAlignedBox aabInf; aabInf.setInfinite(); mRectangle->setBoundingBox(aabInf); + mBackgroundRectangle->setBoundingBox(aabInf); // Attach background to the scene mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); mNode->attachObject(mRectangle); + mBackgroundNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mBackgroundNode->attachObject(mBackgroundRectangle); mRectangle->setVisible(false); mRectangle->setVisibilityFlags(0x1); + mBackgroundRectangle->setVisible(false); + mBackgroundRectangle->setVisibilityFlags(0x1); } VideoPlayer::~VideoPlayer() @@ -973,13 +992,11 @@ VideoPlayer::~VideoPlayer() if(mState) close(); - if(mNode) - mSceneMgr->destroySceneNode(mNode); - mNode = NULL; + mSceneMgr->destroySceneNode(mNode); + mSceneMgr->destroySceneNode(mBackgroundNode); - if(mRectangle) - delete mRectangle; - mRectangle = NULL; + delete mRectangle; + delete mBackgroundRectangle; } void VideoPlayer::playVideo(const std::string &resourceName) @@ -991,6 +1008,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) close(); mRectangle->setVisible(true); + mBackgroundRectangle->setVisible(true); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); @@ -1025,6 +1043,17 @@ void VideoPlayer::update () // Would be nice not to do this all the time... if(mState->display_ready) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); + + // Correct aspect ratio by adding black bars + int width = (mState->video_st->codec->width); + int height = (mState->video_st->codec->height); + + float screenaspect = static_cast(mWidth) / mHeight; + float videoaspect = static_cast(width) / height; + float aspect_correction = videoaspect / screenaspect; + + mRectangle->setCorners (std::max(-1.f, -1.f * aspect_correction), std::min(1.f, 1.f / aspect_correction), + std::min(1.f, 1.f * aspect_correction), std::max(-1.f, -1.f / aspect_correction)); } } } @@ -1040,6 +1069,7 @@ void VideoPlayer::close() MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible(false); + mBackgroundRectangle->setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); mSceneMgr->clearSpecialCaseRenderQueues(); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 9060241bdb..c82a16e15a 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -40,6 +40,8 @@ namespace MWRender bool isPlaying(); + void setResolution (int w, int h) { mWidth = w; mHeight = h; } + private: VideoState* mState; @@ -47,7 +49,12 @@ namespace MWRender Ogre::SceneManager* mSceneMgr; Ogre::MaterialPtr mVideoMaterial; Ogre::Rectangle2D* mRectangle; + Ogre::Rectangle2D* mBackgroundRectangle; Ogre::SceneNode* mNode; + Ogre::SceneNode* mBackgroundNode; + + int mWidth; + int mHeight; }; } From 63e86555b64e33ceb2fdfcdcf4c3fba9ed3292dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 19:40:59 +0100 Subject: [PATCH 072/147] use sample_aspect_ratio if available --- apps/openmw/mwrender/videoplayer.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 11b6d2b8fb..740da7a127 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1045,15 +1045,16 @@ void VideoPlayer::update () mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); // Correct aspect ratio by adding black bars - int width = (mState->video_st->codec->width); - int height = (mState->video_st->codec->height); + double videoaspect = static_cast(mState->video_st->codec->width) / mState->video_st->codec->height; - float screenaspect = static_cast(mWidth) / mHeight; - float videoaspect = static_cast(width) / height; - float aspect_correction = videoaspect / screenaspect; + if (av_q2d(mState->video_st->codec->sample_aspect_ratio) != 0) + videoaspect *= av_q2d(mState->video_st->codec->sample_aspect_ratio); - mRectangle->setCorners (std::max(-1.f, -1.f * aspect_correction), std::min(1.f, 1.f / aspect_correction), - std::min(1.f, 1.f * aspect_correction), std::max(-1.f, -1.f / aspect_correction)); + double screenaspect = static_cast(mWidth) / mHeight; + double aspect_correction = videoaspect / screenaspect; + + mRectangle->setCorners (std::max(-1.0, -1.0 * aspect_correction), std::min(1.0, 1.0 / aspect_correction), + std::min(1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); } } } From c869444dcf34920d08bc7ef22d6cfe8bfae88140 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 11:31:50 -0800 Subject: [PATCH 073/147] Don't leak the IO context if avformat_open_input fails --- apps/openmw/mwrender/videoplayer.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 740da7a127..0165d98557 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -854,20 +854,20 @@ void VideoState::init(const std::string& resourceName) if(this->stream.isNull()) throw std::runtime_error("Failed to open video resource"); + AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!ioCtx) throw std::runtime_error("Failed to allocate AVIOContext"); + this->format_ctx = avformat_alloc_context(); - this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!this->format_ctx->pb) - { - avformat_free_context(this->format_ctx); - throw std::runtime_error("Failed to allocate ioContext "); - } + if(this->format_ctx) + this->format_ctx->pb = ioCtx; // Open video file /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = NULL; + av_free(ioCtx); throw std::runtime_error("Failed to open video input"); } From edf18a7d6ec12d9519d00e14f5a532c940a463d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 22:01:45 +0100 Subject: [PATCH 074/147] change destruction order to fix crash on exit when a video is playing --- apps/openmw/mwbase/environment.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 9aaa5af85a..5a13a50ec9 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -128,12 +128,6 @@ float MWBase::Environment::getFrameDuration() const void MWBase::Environment::cleanup() { - delete mInputManager; - mInputManager = 0; - - delete mSoundManager; - mSoundManager = 0; - delete mMechanicsManager; mMechanicsManager = 0; @@ -146,11 +140,17 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; + delete mWorld; + mWorld = 0; + + delete mSoundManager; + mSoundManager = 0; + delete mWindowManager; mWindowManager = 0; - delete mWorld; - mWorld = 0; + delete mInputManager; + mInputManager = 0; } const MWBase::Environment& MWBase::Environment::get() From a3bd3a40ca6494494daf69aaff68284c7792f062 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 00:47:29 +0100 Subject: [PATCH 075/147] fix 2 unrelated leaks --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 258a40e1cf..373546aa80 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -225,6 +225,8 @@ WindowManager::~WindowManager() delete mSpellCreationDialog; delete mEnchantingDialog; delete mTrainingWindow; + delete mCountDialog; + delete mQuickKeysMenu; cleanupGarbage(); From 9d86890d9d71da789fc92dd9ea83b00dcd65e76d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 22:13:19 -0800 Subject: [PATCH 076/147] Only use one stream for the ffmpeg decoder --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 165 ++++++++++++------------- apps/openmw/mwsound/ffmpeg_decoder.hpp | 7 +- 2 files changed, 80 insertions(+), 92 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 6e60a7b9ef..205dddd355 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -72,39 +72,26 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) /* Used by getAV*Data to search for more compressed data, and buffer it in the * correct stream. It won't buffer data for streams that the app doesn't have a * handle for. */ -bool FFmpeg_Decoder::getNextPacket(int streamidx) +bool FFmpeg_Decoder::getNextPacket() { - PacketList *packet; + if(!mStream.get()) + return false; - packet = (PacketList*)av_malloc(sizeof(*packet)); + PacketList *packet = (PacketList*)av_malloc(sizeof(*packet)); packet->next = NULL; - -next_packet: while(av_read_frame(mFormatCtx, &packet->pkt) >= 0) { - std::vector::iterator iter = mStreams.begin(); - - /* Check each stream the user has a handle for, looking for the one - * this packet belongs to */ - while(iter != mStreams.end()) + /* Check if the packet belongs to this stream */ + if(mStream->mStreamIdx == packet->pkt.stream_index) { - if((*iter)->mStreamIdx == packet->pkt.stream_index) - { - PacketList **last; + PacketList **last; - last = &(*iter)->mPackets; - while(*last != NULL) - last = &(*last)->next; + last = &mStream->mPackets; + while(*last != NULL) + last = &(*last)->next; - *last = packet; - if((*iter)->mStreamIdx == streamidx) - return true; - - packet = (PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - goto next_packet; - } - iter++; + *last = packet; + return true; } /* Free the packet and look for another */ av_free_packet(&packet->pkt); @@ -138,7 +125,7 @@ void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) mDecodedDataSize = 0; next_packet: - if(!mPackets && !mParent->getNextPacket(mStreamIdx)) + if(!mPackets && !mParent->getNextPacket()) return NULL; /* Decode some data, and check for errors */ @@ -173,8 +160,7 @@ next_packet: /* Move the unread data to the front and clear the end bits */ int remaining = mPackets->pkt.size - len; memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); - memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining); - mPackets->pkt.size -= len; + av_shrink_packet(&mPackets->pkt, remaining); } else { @@ -261,35 +247,38 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avformat_find_stream_info(mFormatCtx, NULL) < 0) fail("Failed to find stream info in "+fname); + int audio_idx = -1; for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - std::auto_ptr stream(new MyStream); - stream->mCodecCtx = mFormatCtx->streams[j]->codec; - stream->mStreamIdx = j; - stream->mPackets = NULL; - - AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); - if(!codec) - { - std::stringstream ss("No codec found for id "); - ss << stream->mCodecCtx->codec_id; - fail(ss.str()); - } - if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) - fail("Failed to open audio codec " + std::string(codec->long_name)); - - stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); - stream->mDecodedDataSize = 0; - - stream->mParent = this; - mStreams.push_back(stream.release()); + audio_idx = j; break; } } - if(mStreams.empty()) + if(audio_idx == -1) fail("No audio streams in "+fname); + + std::auto_ptr stream(new MyStream); + stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; + stream->mStreamIdx = audio_idx; + stream->mPackets = NULL; + + AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); + if(!codec) + { + std::stringstream ss("No codec found for id "); + ss << stream->mCodecCtx->codec_id; + fail(ss.str()); + } + if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) + fail("Failed to open audio codec " + std::string(codec->long_name)); + + stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); + stream->mDecodedDataSize = 0; + + stream->mParent = this; + mStream = stream; } catch(std::exception &e) { @@ -301,23 +290,19 @@ void FFmpeg_Decoder::open(const std::string &fname) void FFmpeg_Decoder::close() { - while(!mStreams.empty()) + if(mStream.get()) { - MyStream *stream = mStreams.front(); - - stream->clearPackets(); - avcodec_close(stream->mCodecCtx); - av_free(stream->mDecodedData); - delete stream; - - mStreams.erase(mStreams.begin()); + mStream->clearPackets(); + avcodec_close(mStream->mCodecCtx); + av_free(mStream->mDecodedData); } + mStream.reset(); + if(mFormatCtx) { AVIOContext* context = mFormatCtx->pb; - av_free(context); - mFormatCtx->pb = NULL; avformat_close_input(&mFormatCtx); + av_free(context); } mFormatCtx = NULL; @@ -331,81 +316,83 @@ std::string FFmpeg_Decoder::getName() void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { - if(mStreams.empty()) + if(!mStream.get()) fail("No audio stream info"); - MyStream *stream = mStreams[0]; - if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) + if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; - else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) + else if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt)); + av_get_sample_fmt_name(mStream->mCodecCtx->sample_fmt)); - if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; - else if(stream->mCodecCtx->channel_layout == 0) + else if(mStream->mCodecCtx->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(stream->mCodecCtx->channels == 1) + if(mStream->mCodecCtx->channels == 1) *chans = ChannelConfig_Mono; - else if(stream->mCodecCtx->channels == 2) + else if(mStream->mCodecCtx->channels == 2) *chans = ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << stream->mCodecCtx->channels; + sstr << mStream->mCodecCtx->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels, - stream->mCodecCtx->channel_layout); + av_get_channel_layout_string(str, sizeof(str), mStream->mCodecCtx->channels, + mStream->mCodecCtx->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = stream->mCodecCtx->sample_rate; + *samplerate = mStream->mCodecCtx->sample_rate; } size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { - if(mStreams.empty()) - fail("No audio streams"); + if(!mStream.get()) + fail("No audio stream"); - MyStream *stream = mStreams.front(); - size_t got = stream->readAVAudioData(buffer, bytes); - mSamplesRead += got / av_samples_get_buffer_size(NULL, stream->mCodecCtx->channels, 1, - stream->mCodecCtx->sample_fmt, 1); + size_t got = mStream->readAVAudioData(buffer, bytes); + mSamplesRead += got / mStream->mCodecCtx->channels / + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); return got; } void FFmpeg_Decoder::readAll(std::vector &output) { - if(mStreams.empty()) - fail("No audio streams"); - MyStream *stream = mStreams.front(); + if(!mStream.get()) + fail("No audio stream"); + char *inbuf; size_t got; - - while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0) + while((inbuf=(char*)mStream->getAVAudioData(&got)) != NULL && got > 0) + { output.insert(output.end(), inbuf, inbuf+got); + mSamplesRead += got / mStream->mCodecCtx->channels / + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + } } void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); - std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); + if(mStream.get()) + mStream->clearPackets(); mSamplesRead = 0; } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index ff63edf07d..b7d2e1bb2b 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -21,13 +21,14 @@ namespace MWSound { class FFmpeg_Decoder : public Sound_Decoder { + struct MyStream; + AVFormatContext *mFormatCtx; - struct MyStream; - std::vector mStreams; + std::auto_ptr mStream; size_t mSamplesRead; - bool getNextPacket(int streamidx); + bool getNextPacket(); Ogre::DataStreamPtr mDataStream; static int readPacket(void *user_data, uint8_t *buf, int buf_size); From 5f4c33f8960f2d9ba18fe135f01d103b307e1375 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 22:54:54 -0800 Subject: [PATCH 077/147] Only store one packet at a time --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 111 ++++++------------------- 1 file changed, 24 insertions(+), 87 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 205dddd355..70f7682efe 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -15,23 +15,17 @@ static void fail(const std::string &msg) { throw std::runtime_error("FFmpeg exception: "+msg); } -struct PacketList { - AVPacket pkt; - PacketList *next; -}; - struct FFmpeg_Decoder::MyStream { AVCodecContext *mCodecCtx; int mStreamIdx; - PacketList *mPackets; + AVPacket mPacket; char *mDecodedData; size_t mDecodedDataSize; FFmpeg_Decoder *mParent; - void clearPackets(); void *getAVAudioData(size_t *length); size_t readAVAudioData(void *data, size_t length); }; @@ -77,104 +71,47 @@ bool FFmpeg_Decoder::getNextPacket() if(!mStream.get()) return false; - PacketList *packet = (PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - while(av_read_frame(mFormatCtx, &packet->pkt) >= 0) + while(av_read_frame(mFormatCtx, &mStream->mPacket) >= 0) { /* Check if the packet belongs to this stream */ - if(mStream->mStreamIdx == packet->pkt.stream_index) - { - PacketList **last; - - last = &mStream->mPackets; - while(*last != NULL) - last = &(*last)->next; - - *last = packet; + if(mStream->mStreamIdx == mStream->mPacket.stream_index) return true; - } + /* Free the packet and look for another */ - av_free_packet(&packet->pkt); + av_free_packet(&mStream->mPacket); } - av_free(packet); return false; } -void FFmpeg_Decoder::MyStream::clearPackets() -{ - while(mPackets) - { - PacketList *self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } -} - void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) { - int size; - int len; + int size, len; if(length) *length = 0; if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) return NULL; mDecodedDataSize = 0; + do { + if(mPacket.size == 0 && !mParent->getNextPacket()) + return NULL; -next_packet: - if(!mPackets && !mParent->getNextPacket()) - return NULL; - - /* Decode some data, and check for errors */ - size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - while((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, - &mPackets->pkt)) == 0) - { - PacketList *self; - - if(size > 0) - break; - - /* Packet went unread and no data was given? Drop it and try the next, - * I guess... */ - self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - - if(!mPackets) - goto next_packet; - + /* Decode some data, and check for errors */ size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - } + if((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, &mPacket)) < 0) + return NULL; - if(len < 0) - return NULL; - - if(len < mPackets->pkt.size) - { /* Move the unread data to the front and clear the end bits */ - int remaining = mPackets->pkt.size - len; - memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); - av_shrink_packet(&mPackets->pkt, remaining); - } - else - { - PacketList *self; - - self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } - - if(size == 0) - goto next_packet; + int remaining = mPacket.size - len; + if(remaining <= 0) + av_free_packet(&mPacket); + else + { + memmove(mPacket.data, &mPacket.data[len], remaining); + av_shrink_packet(&mPacket, remaining); + } + } while(size == 0); /* Set the output buffer size */ mDecodedDataSize = size; @@ -262,7 +199,7 @@ void FFmpeg_Decoder::open(const std::string &fname) std::auto_ptr stream(new MyStream); stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; stream->mStreamIdx = audio_idx; - stream->mPackets = NULL; + memset(&stream->mPacket, 0, sizeof(stream->mPacket)); AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); if(!codec) @@ -292,7 +229,7 @@ void FFmpeg_Decoder::close() { if(mStream.get()) { - mStream->clearPackets(); + av_free_packet(&mStream->mPacket); avcodec_close(mStream->mCodecCtx); av_free(mStream->mDecodedData); } @@ -392,7 +329,7 @@ void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); if(mStream.get()) - mStream->clearPackets(); + av_free_packet(&mStream->mPacket); mSamplesRead = 0; } From 5fff1c4e47b387aa433d436342909b4ace43626d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 23:22:37 -0800 Subject: [PATCH 078/147] Update the ffmpeg decoder to use avcodec_decode_audio4 --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 82 ++++++++++---------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 70f7682efe..7b6144dd08 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -20,13 +20,14 @@ struct FFmpeg_Decoder::MyStream { int mStreamIdx; AVPacket mPacket; + AVFrame *mFrame; - char *mDecodedData; - size_t mDecodedDataSize; + int mFrameSize; + int mFramePos; FFmpeg_Decoder *mParent; - void *getAVAudioData(size_t *length); + bool getAVAudioData(); size_t readAVAudioData(void *data, size_t length); }; @@ -84,23 +85,20 @@ bool FFmpeg_Decoder::getNextPacket() return false; } -void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) +bool FFmpeg_Decoder::MyStream::getAVAudioData() { - int size, len; + int got_frame, len; - if(length) *length = 0; if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return NULL; + return false; - mDecodedDataSize = 0; do { if(mPacket.size == 0 && !mParent->getNextPacket()) - return NULL; + return false; /* Decode some data, and check for errors */ - size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - if((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, &mPacket)) < 0) - return NULL; + if((len=avcodec_decode_audio4(mCodecCtx, mFrame, &got_frame, &mPacket)) < 0) + return false; /* Move the unread data to the front and clear the end bits */ int remaining = mPacket.size - len; @@ -111,13 +109,9 @@ void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) memmove(mPacket.data, &mPacket.data[len], remaining); av_shrink_packet(&mPacket, remaining); } - } while(size == 0); + } while(got_frame == 0 || mFrame->nb_samples == 0); - /* Set the output buffer size */ - mDecodedDataSize = size; - if(length) *length = mDecodedDataSize; - - return mDecodedData; + return true; } size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) @@ -127,34 +121,24 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) while(dec < length) { /* If there's no decoded data, find some */ - if(mDecodedDataSize == 0) + if(mFramePos >= mFrameSize) { - if(getAVAudioData(NULL) == NULL) + if(!getAVAudioData()) break; + mFramePos = 0; + mFrameSize = mFrame->nb_samples * mCodecCtx->channels * + av_get_bytes_per_sample(mCodecCtx->sample_fmt); } - if(mDecodedDataSize > 0) - { - /* Get the amount of bytes remaining to be written, and clamp to - * the amount of decoded data we have */ - size_t rem = length-dec; - if(rem > mDecodedDataSize) - rem = mDecodedDataSize; + /* Get the amount of bytes remaining to be written, and clamp to + * the amount of decoded data we have */ + size_t rem = std::min(length-dec, mFrameSize-mFramePos); - /* Copy the data to the app's buffer and increment */ - if(data != NULL) - { - memcpy(data, mDecodedData, rem); - data = (char*)data + rem; - } - dec += rem; - - /* If there's any decoded data left, move it to the front of the - * buffer for next time */ - if(rem < mDecodedDataSize) - memmove(mDecodedData, &mDecodedData[rem], mDecodedDataSize - rem); - mDecodedDataSize -= rem; - } + /* Copy the data to the app's buffer and increment */ + memcpy(data, mFrame->data[0]+mFramePos, rem); + data = (char*)data + rem; + dec += rem; + mFramePos += rem; } /* Return the number of bytes we were able to get */ @@ -162,7 +146,6 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) } - void FFmpeg_Decoder::open(const std::string &fname) { close(); @@ -211,8 +194,7 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); - stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); - stream->mDecodedDataSize = 0; + stream->mFrame = avcodec_alloc_frame(); stream->mParent = this; mStream = stream; @@ -231,7 +213,7 @@ void FFmpeg_Decoder::close() { av_free_packet(&mStream->mPacket); avcodec_close(mStream->mCodecCtx); - av_free(mStream->mDecodedData); + av_free(mStream->mFrame); } mStream.reset(); @@ -315,13 +297,13 @@ void FFmpeg_Decoder::readAll(std::vector &output) if(!mStream.get()) fail("No audio stream"); - char *inbuf; - size_t got; - while((inbuf=(char*)mStream->getAVAudioData(&got)) != NULL && got > 0) + while(mStream->getAVAudioData()) { + size_t got = mStream->mFrame->nb_samples * mStream->mCodecCtx->channels * + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + const char *inbuf = reinterpret_cast(mStream->mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += got / mStream->mCodecCtx->channels / - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + mSamplesRead += mStream->mFrame->nb_samples; } } From 1a771ae67150a2ed4539cdb44db40f0e4fe8097c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 23:46:32 -0800 Subject: [PATCH 079/147] Merge the stream struct into the parent decoder --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 142 +++++++++++-------------- apps/openmw/mwsound/ffmpeg_decoder.hpp | 13 ++- 2 files changed, 71 insertions(+), 84 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 7b6144dd08..e0f071568a 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -15,23 +15,6 @@ static void fail(const std::string &msg) { throw std::runtime_error("FFmpeg exception: "+msg); } -struct FFmpeg_Decoder::MyStream { - AVCodecContext *mCodecCtx; - int mStreamIdx; - - AVPacket mPacket; - AVFrame *mFrame; - - int mFrameSize; - int mFramePos; - - FFmpeg_Decoder *mParent; - - bool getAVAudioData(); - size_t readAVAudioData(void *data, size_t length); -}; - - int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) { Ogre::DataStreamPtr stream = static_cast(user_data)->mDataStream; @@ -69,35 +52,36 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) * handle for. */ bool FFmpeg_Decoder::getNextPacket() { - if(!mStream.get()) + if(!mStream) return false; - while(av_read_frame(mFormatCtx, &mStream->mPacket) >= 0) + int stream_idx = mStream - mFormatCtx->streams; + while(av_read_frame(mFormatCtx, &mPacket) >= 0) { /* Check if the packet belongs to this stream */ - if(mStream->mStreamIdx == mStream->mPacket.stream_index) + if(stream_idx == mPacket.stream_index) return true; /* Free the packet and look for another */ - av_free_packet(&mStream->mPacket); + av_free_packet(&mPacket); } return false; } -bool FFmpeg_Decoder::MyStream::getAVAudioData() +bool FFmpeg_Decoder::getAVAudioData() { int got_frame, len; - if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) + if((*mStream)->codec->codec_type != AVMEDIA_TYPE_AUDIO) return false; do { - if(mPacket.size == 0 && !mParent->getNextPacket()) + if(mPacket.size == 0 && !getNextPacket()) return false; /* Decode some data, and check for errors */ - if((len=avcodec_decode_audio4(mCodecCtx, mFrame, &got_frame, &mPacket)) < 0) + if((len=avcodec_decode_audio4((*mStream)->codec, mFrame, &got_frame, &mPacket)) < 0) return false; /* Move the unread data to the front and clear the end bits */ @@ -114,7 +98,7 @@ bool FFmpeg_Decoder::MyStream::getAVAudioData() return true; } -size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) +size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) { size_t dec = 0; @@ -126,8 +110,8 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) if(!getAVAudioData()) break; mFramePos = 0; - mFrameSize = mFrame->nb_samples * mCodecCtx->channels * - av_get_bytes_per_sample(mCodecCtx->sample_fmt); + mFrameSize = mFrame->nb_samples * (*mStream)->codec->channels * + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); } /* Get the amount of bytes remaining to be written, and clamp to @@ -167,55 +151,46 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avformat_find_stream_info(mFormatCtx, NULL) < 0) fail("Failed to find stream info in "+fname); - int audio_idx = -1; for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - audio_idx = j; + mStream = &mFormatCtx->streams[j]; break; } } - if(audio_idx == -1) + if(!mStream) fail("No audio streams in "+fname); - std::auto_ptr stream(new MyStream); - stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; - stream->mStreamIdx = audio_idx; - memset(&stream->mPacket, 0, sizeof(stream->mPacket)); + memset(&mPacket, 0, sizeof(mPacket)); - AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); + AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { std::stringstream ss("No codec found for id "); - ss << stream->mCodecCtx->codec_id; + ss << (*mStream)->codec->codec_id; fail(ss.str()); } - if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) + if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); - stream->mFrame = avcodec_alloc_frame(); - - stream->mParent = this; - mStream = stream; + mFrame = avcodec_alloc_frame(); } catch(std::exception &e) { avformat_close_input(&mFormatCtx); - mFormatCtx = NULL; throw; } } void FFmpeg_Decoder::close() { - if(mStream.get()) - { - av_free_packet(&mStream->mPacket); - avcodec_close(mStream->mCodecCtx); - av_free(mStream->mFrame); - } - mStream.reset(); + if(mStream) + avcodec_close((*mStream)->codec); + mStream = NULL; + + av_free_packet(&mPacket); + av_freep(&mFrame); if(mFormatCtx) { @@ -223,7 +198,6 @@ void FFmpeg_Decoder::close() avformat_close_input(&mFormatCtx); av_free(context); } - mFormatCtx = NULL; mDataStream.setNull(); } @@ -235,83 +209,82 @@ std::string FFmpeg_Decoder::getName() void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { - if(!mStream.get()) + if(!mStream) fail("No audio stream info"); - if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) + if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; - else if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) + else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(mStream->mCodecCtx->sample_fmt)); + av_get_sample_fmt_name((*mStream)->codec->sample_fmt)); - if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; - else if(mStream->mCodecCtx->channel_layout == 0) + else if((*mStream)->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(mStream->mCodecCtx->channels == 1) + if((*mStream)->codec->channels == 1) *chans = ChannelConfig_Mono; - else if(mStream->mCodecCtx->channels == 2) + else if((*mStream)->codec->channels == 2) *chans = ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << mStream->mCodecCtx->channels; + sstr << (*mStream)->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), mStream->mCodecCtx->channels, - mStream->mCodecCtx->channel_layout); + av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, + (*mStream)->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = mStream->mCodecCtx->sample_rate; + *samplerate = (*mStream)->codec->sample_rate; } size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { - if(!mStream.get()) + if(!mStream) fail("No audio stream"); - size_t got = mStream->readAVAudioData(buffer, bytes); - mSamplesRead += got / mStream->mCodecCtx->channels / - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + size_t got = readAVAudioData(buffer, bytes); + mSamplesRead += got / (*mStream)->codec->channels / + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); return got; } void FFmpeg_Decoder::readAll(std::vector &output) { - if(!mStream.get()) + if(!mStream) fail("No audio stream"); - while(mStream->getAVAudioData()) + while(getAVAudioData()) { - size_t got = mStream->mFrame->nb_samples * mStream->mCodecCtx->channels * - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); - const char *inbuf = reinterpret_cast(mStream->mFrame->data[0]); + size_t got = mFrame->nb_samples * (*mStream)->codec->channels * + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + const char *inbuf = reinterpret_cast(mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += mStream->mFrame->nb_samples; + mSamplesRead += mFrame->nb_samples; } } void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); - if(mStream.get()) - av_free_packet(&mStream->mPacket); + av_free_packet(&mPacket); mSamplesRead = 0; } @@ -320,12 +293,19 @@ size_t FFmpeg_Decoder::getSampleOffset() return mSamplesRead; } -FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL), mSamplesRead(0) +FFmpeg_Decoder::FFmpeg_Decoder() + : mFormatCtx(NULL) + , mStream(NULL) + , mFrame(NULL) + , mFrameSize(0) + , mFramePos(0) + , mSamplesRead(0) { - static bool done_init = false; + memset(&mPacket, 0, sizeof(mPacket)); /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ + static bool done_init = false; if(!done_init) { av_register_all(); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index b7d2e1bb2b..dbd4f5adc8 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -21,11 +21,15 @@ namespace MWSound { class FFmpeg_Decoder : public Sound_Decoder { - struct MyStream; - AVFormatContext *mFormatCtx; + AVStream **mStream; + + AVPacket mPacket; + AVFrame *mFrame; + + int mFrameSize; + int mFramePos; - std::auto_ptr mStream; size_t mSamplesRead; bool getNextPacket(); @@ -35,6 +39,9 @@ namespace MWSound static int writePacket(void *user_data, uint8_t *buf, int buf_size); static int64_t seek(void *user_data, int64_t offset, int whence); + bool getAVAudioData(); + size_t readAVAudioData(void *data, size_t length); + virtual void open(const std::string &fname); virtual void close(); From 4561c22e2b5f55253ea179907f7dfe62aa475faf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 00:07:56 -0800 Subject: [PATCH 080/147] More fixes for the audio clock The audio_clock for the decoder represents the end of the current packet, so it needs to be adjusted back to match the position that's actually going to be read next. --- apps/openmw/mwrender/videoplayer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 0165d98557..d57295f656 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -416,7 +416,7 @@ public: break; } - mFramePos = std::min(static_cast(mFrameSize), sample_skip); + mFramePos = std::min(mFrameSize, sample_skip); sample_skip -= mFramePos; } @@ -471,7 +471,9 @@ public: size_t getSampleOffset() { - return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate); + ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / + av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); + return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; } }; From 0edc87825d1ea553a9ec8f7d990945608c6e8a09 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 01:17:58 -0800 Subject: [PATCH 081/147] Move audio_clock to the decoder where it's used --- apps/openmw/mwrender/videoplayer.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d57295f656..cac25713aa 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -58,11 +58,10 @@ struct VideoPicture { struct VideoState { VideoState() : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), - sws_context(NULL), display_ready(0) + audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), + audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), + video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), + quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) { } ~VideoState() @@ -114,7 +113,6 @@ struct VideoState { int av_sync_type; uint64_t external_clock_base; - double audio_clock; AVStream *audio_st; PacketQueue audioq; double audio_diff_cum; /* used for AV difference average computation */ @@ -256,6 +254,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; + double audio_clock; + /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ int synchronize_audio() @@ -310,8 +310,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - is->audio_clock += (double)frame->nb_samples / - (double)is->audio_st->codec->sample_rate; + this->audio_clock += (double)frame->nb_samples / + (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * @@ -328,7 +328,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + this->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -348,6 +348,7 @@ public: , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) + , audio_clock(0.0) { } virtual ~MovieAudioDecoder() { @@ -473,7 +474,7 @@ public: { ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); - return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; + return (size_t)(this->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; } }; From e9d833be036879b650911bf65a9ae1a582eed5bb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 01:56:52 -0800 Subject: [PATCH 082/147] Use the packet pts to calculate the decoder sample offset --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 27 ++++++++++++++------------ apps/openmw/mwsound/ffmpeg_decoder.hpp | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index e0f071568a..261a86ca60 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -60,7 +60,11 @@ bool FFmpeg_Decoder::getNextPacket() { /* Check if the packet belongs to this stream */ if(stream_idx == mPacket.stream_index) + { + if((uint64_t)mPacket.pts != AV_NOPTS_VALUE) + mNextPts = av_q2d((*mStream)->time_base)*mPacket.pts; return true; + } /* Free the packet and look for another */ av_free_packet(&mPacket); @@ -94,6 +98,7 @@ bool FFmpeg_Decoder::getAVAudioData() av_shrink_packet(&mPacket, remaining); } } while(got_frame == 0 || mFrame->nb_samples == 0); + mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate; return true; } @@ -162,8 +167,6 @@ void FFmpeg_Decoder::open(const std::string &fname) if(!mStream) fail("No audio streams in "+fname); - memset(&mPacket, 0, sizeof(mPacket)); - AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { @@ -259,11 +262,7 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { if(!mStream) fail("No audio stream"); - - size_t got = readAVAudioData(buffer, bytes); - mSamplesRead += got / (*mStream)->codec->channels / - av_get_bytes_per_sample((*mStream)->codec->sample_fmt); - return got; + return readAVAudioData(buffer, bytes); } void FFmpeg_Decoder::readAll(std::vector &output) @@ -277,20 +276,24 @@ void FFmpeg_Decoder::readAll(std::vector &output) av_get_bytes_per_sample((*mStream)->codec->sample_fmt); const char *inbuf = reinterpret_cast(mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += mFrame->nb_samples; } } void FFmpeg_Decoder::rewind() { - av_seek_frame(mFormatCtx, -1, 0, 0); + int stream_idx = mStream - mFormatCtx->streams; + if(av_seek_frame(mFormatCtx, stream_idx, 0, 0) < 0) + fail("Failed to seek in audio stream"); av_free_packet(&mPacket); - mSamplesRead = 0; + mFrameSize = mFramePos = 0; + mNextPts = 0.0; } size_t FFmpeg_Decoder::getSampleOffset() { - return mSamplesRead; + int delay = (mFrameSize-mFramePos) / (*mStream)->codec->channels / + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay; } FFmpeg_Decoder::FFmpeg_Decoder() @@ -299,7 +302,7 @@ FFmpeg_Decoder::FFmpeg_Decoder() , mFrame(NULL) , mFrameSize(0) , mFramePos(0) - , mSamplesRead(0) + , mNextPts(0.0) { memset(&mPacket, 0, sizeof(mPacket)); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index dbd4f5adc8..32b2797ed7 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -30,7 +30,7 @@ namespace MWSound int mFrameSize; int mFramePos; - size_t mSamplesRead; + double mNextPts; bool getNextPacket(); From 3f6d36c7127bd5cb6144a56497ae5fe3daeaf663 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 02:19:19 -0800 Subject: [PATCH 083/147] Avoid double-setting the material texture --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index cac25713aa..c5976222ff 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,7 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); From dd20db5dc2ff26eda2b5dad4274eac3228b7310a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:05:44 -0800 Subject: [PATCH 084/147] Remove the stream indices from the VideoState --- apps/openmw/mwrender/videoplayer.cpp | 180 +++++++++++++-------------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c5976222ff..81eba24170 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -57,11 +57,14 @@ struct VideoPicture { struct VideoState { VideoState() - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), - audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), - video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), - quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + : format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT) + , external_clock_base(0.0) + , audio_st(NULL), audio_diff_cum(0.0), audio_diff_avg_coef(0.0), + audio_diff_threshold(0.0), audio_diff_avg_count(0) + , video_st(NULL), frame_last_pts(0.0), frame_last_delay(0.0), + video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0) + , refresh_rate_ms(10), refresh(false), quit(false), display_ready(false) { } ~VideoState() @@ -108,38 +111,33 @@ struct VideoState { static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); - int videoStream, audioStream; + Ogre::DataStreamPtr stream; + AVFormatContext* format_ctx; int av_sync_type; uint64_t external_clock_base; - AVStream *audio_st; + AVStream** audio_st; PacketQueue audioq; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; int audio_diff_avg_count; + MWBase::SoundPtr AudioTrack; + AVStream** video_st; double frame_last_pts; double frame_last_delay; double video_clock; ///audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - sample_skip = ((int)(diff * is->audio_st->codec->sample_rate) * n); + int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; + sample_skip = ((int)(diff * (*is->audio_st)->codec->sample_rate) * n); } } @@ -295,7 +292,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int len1, got_frame; - len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); + len1 = avcodec_decode_audio4((*is->audio_st)->codec, frame, &got_frame, pkt); if(len1 < 0) break; if(len1 <= pkt->size) @@ -311,11 +308,11 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder continue; this->audio_clock += (double)frame->nb_samples / - (double)is->audio_st->codec->sample_rate; + (double)(*is->audio_st)->codec->sample_rate; /* We have data, return it and come back for more later */ - return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; + return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; } av_free_packet(pkt); @@ -328,7 +325,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - this->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + this->audio_clock = av_q2d((*is->audio_st)->time_base)*pkt->pts; } } @@ -357,47 +354,47 @@ public: void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { - if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_U8) + if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = MWSound::SampleType_UInt8; - else if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_S16) + else if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + av_get_sample_fmt_name((*is->audio_st)->codec->sample_fmt)); - if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) + if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_QUAD) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = MWSound::ChannelConfig_Quad; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = MWSound::ChannelConfig_5point1; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = MWSound::ChannelConfig_7point1; - else if(is->audio_st->codec->channel_layout == 0) + else if((*is->audio_st)->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(is->audio_st->codec->channels == 1) + if((*is->audio_st)->codec->channels == 1) *chans = MWSound::ChannelConfig_Mono; - else if(is->audio_st->codec->channels == 2) + else if((*is->audio_st)->codec->channels == 2) *chans = MWSound::ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << is->audio_st->codec->channels; + sstr << (*is->audio_st)->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), is->audio_st->codec->channels, - is->audio_st->codec->channel_layout); + av_get_channel_layout_string(str, sizeof(str), (*is->audio_st)->codec->channels, + (*is->audio_st)->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = is->audio_st->codec->sample_rate; + *samplerate = (*is->audio_st)->codec->sample_rate; } size_t read(char *stream, size_t len) @@ -431,8 +428,8 @@ public: { len1 = std::min(len1, -mFramePos); - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; + int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; /* add samples by copying the first sample*/ if(n == 1) @@ -472,9 +469,9 @@ public: size_t getSampleOffset() { - ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / - av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); - return (size_t)(this->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; + ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / + av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); + return (size_t)(this->audio_clock*(*is->audio_st)->codec->sample_rate) - clock_delay; } }; @@ -527,26 +524,26 @@ void VideoState::video_display() { VideoPicture *vp = &this->pictq[this->pictq_rindex]; - if(this->video_st->codec->width != 0 && this->video_st->codec->height != 0) + if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); - if(texture.isNull () || static_cast(texture->getWidth()) != this->video_st->codec->width - || static_cast(texture->getHeight()) != this->video_st->codec->height) + if(texture.isNull() || static_cast(texture->getWidth()) != (*this->video_st)->codec->width + || static_cast(texture->getHeight()) != (*this->video_st)->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( "VideoTexture", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, - this->video_st->codec->width, this->video_st->codec->height, + (*this->video_st)->codec->width, (*this->video_st)->codec->height, 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); } - Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); + Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); - this->display_ready = 1; + this->display_ready = true; } } @@ -617,9 +614,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // Convert the image into RGBA format for Ogre if(this->sws_context == NULL) { - int w = this->video_st->codec->width; - int h = this->video_st->codec->height; - this->sws_context = sws_getContext(w, h, this->video_st->codec->pix_fmt, + int w = (*this->video_st)->codec->width; + int h = (*this->video_st)->codec->height; + this->sws_context = sws_getContext(w, h, (*this->video_st)->codec->pix_fmt, w, h, PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); if(this->sws_context == NULL) @@ -627,11 +624,11 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data.resize(this->video_st->codec->width * this->video_st->codec->height * 4); + vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4); uint8_t *dst = &vp->data[0]; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, this->video_st->codec->height, &dst, this->rgbaFrame->linesize); + 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; @@ -653,7 +650,7 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) pts = this->video_clock; /* update the video clock */ - frame_delay = av_q2d(this->video_st->codec->time_base); + frame_delay = av_q2d((*this->video_st)->codec->time_base); /* if we are repeating a frame, adjust clock accordingly */ frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); @@ -693,14 +690,14 @@ void VideoState::video_thread_loop(VideoState *self) pFrame = avcodec_alloc_frame(); self->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); + avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, (*self->video_st)->codec->width, (*self->video_st)->codec->height); while(self->videoq.get(packet, self) >= 0) { // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - if(avcodec_decode_video2(self->video_st->codec, pFrame, &frameFinished, packet) < 0) + if(avcodec_decode_video2((*self->video_st)->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); pts = 0; @@ -708,7 +705,7 @@ void VideoState::video_thread_loop(VideoState *self) pts = packet->dts; else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t*)pFrame->opaque; - pts *= av_q2d(self->video_st->time_base); + pts *= av_q2d((*self->video_st)->time_base); av_free_packet(packet); @@ -734,14 +731,14 @@ void VideoState::decode_thread_loop(VideoState *self) try { - if(self->videoStream < 0 && self->audioStream < 0) + if(!self->video_st && !self->audio_st < 0) throw std::runtime_error("No streams to decode"); // main decode loop while(!self->quit) { - if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; @@ -751,9 +748,9 @@ void VideoState::decode_thread_loop(VideoState *self) break; // Is this a packet from the video stream? - if(packet->stream_index == self->videoStream) + if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams) self->videoq.put(packet); - else if(packet->stream_index == self->audioStream) + else if(self->audio_st && packet->stream_index == self->audio_st-pFormatCtx->streams) self->audioq.put(packet); else av_free_packet(packet); @@ -774,7 +771,7 @@ void VideoState::decode_thread_loop(VideoState *self) std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; } - self->quit = 1; + self->quit = true; } @@ -799,8 +796,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) switch(codecCtx->codec_type) { case AVMEDIA_TYPE_AUDIO: - this->audioStream = stream_index; - this->audio_st = pFormatCtx->streams[stream_index]; + this->audio_st = pFormatCtx->streams + stream_index; /* averaging filter for audio sync */ this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); @@ -812,16 +808,14 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) { - this->audioStream = -1; - avcodec_close(this->audio_st->codec); + avcodec_close((*this->audio_st)->codec); this->audio_st = NULL; return -1; } break; case AVMEDIA_TYPE_VIDEO: - this->videoStream = stream_index; - this->video_st = pFormatCtx->streams[stream_index]; + this->video_st = pFormatCtx->streams + stream_index; this->frame_last_delay = 40e-3; @@ -847,11 +841,9 @@ void VideoState::init(const std::string& resourceName) unsigned int i; this->av_sync_type = AV_SYNC_DEFAULT; - this->videoStream = -1; - this->audioStream = -1; this->refresh_rate_ms = 10; this->refresh = false; - this->quit = 0; + this->quit = false; this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); if(this->stream.isNull()) @@ -899,12 +891,12 @@ void VideoState::init(const std::string& resourceName) } catch(std::runtime_error& e) { - this->quit = 1; + this->quit = true; throw; } catch(Ogre::Exception& e) { - this->quit = 1; + this->quit = true; throw; } } @@ -918,13 +910,16 @@ void VideoState::deinit() this->video_thread.join(); this->refresh_thread.join(); - if(this->audioStream >= 0) - avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); + if(this->audio_st) + avcodec_close((*this->audio_st)->codec); + this->audio_st = NULL; + if(this->video_st) + avcodec_close((*this->video_st)->codec); + this->video_st = NULL; if(this->sws_context) sws_freeContext(this->sws_context); + this->sws_context = NULL; if(this->format_ctx) { @@ -1012,6 +1007,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) mRectangle->setVisible(true); mBackgroundRectangle->setVisible(true); + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); @@ -1029,8 +1025,6 @@ void VideoPlayer::playVideo(const std::string &resourceName) mState = new VideoState; mState->init(resourceName); - - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); } void VideoPlayer::update () @@ -1048,23 +1042,23 @@ void VideoPlayer::update () mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); // Correct aspect ratio by adding black bars - double videoaspect = static_cast(mState->video_st->codec->width) / mState->video_st->codec->height; - - if (av_q2d(mState->video_st->codec->sample_aspect_ratio) != 0) - videoaspect *= av_q2d(mState->video_st->codec->sample_aspect_ratio); + double videoaspect = av_q2d((*mState->video_st)->codec->sample_aspect_ratio); + if(videoaspect == 0.0) + videoaspect = 1.0; + videoaspect *= static_cast((*mState->video_st)->codec->width) / (*mState->video_st)->codec->height; double screenaspect = static_cast(mWidth) / mHeight; double aspect_correction = videoaspect / screenaspect; - mRectangle->setCorners (std::max(-1.0, -1.0 * aspect_correction), std::min(1.0, 1.0 / aspect_correction), - std::min(1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); + mRectangle->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), + std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); } } } void VideoPlayer::close() { - mState->quit = 1; + mState->quit = true; mState->deinit(); delete mState; From 254a623319126d233d65321c03bce9f6cd094c1a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:09:24 -0800 Subject: [PATCH 085/147] Remove a redundant check --- apps/openmw/mwrender/videoplayer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 81eba24170..817ec0a3f0 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -316,9 +316,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder } av_free_packet(pkt); - if(is->quit) - return -1; - /* next packet */ if(is->audioq.get(pkt, is) < 0) return -1; From 8cde6db665056dd35aab98ceb036e405c0b34d02 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:17:17 -0800 Subject: [PATCH 086/147] We no longer need SDL --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 084363da71..f320c5c3c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,9 +163,6 @@ if (USE_MPG123) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) endif (USE_MPG123) -find_package (SDL REQUIRED) -set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${SDL_INCLUDE_DIR}) -set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${SDL_LIBRARY}) # Platform specific if (WIN32) From 3829bbfeca4aaa0b9889e6bffbe9137c751257ba Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 04:01:27 -0800 Subject: [PATCH 087/147] Look for all available sound input libs as needed, and warn if not found --- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f320c5c3c0..182cc268af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,8 @@ option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) # Sound source selection -option(USE_FFMPEG "use ffmpeg for sound" OFF) -option(USE_AUDIERE "use audiere for sound" OFF) +option(USE_FFMPEG "use ffmpeg for sound" ON) +option(USE_AUDIERE "use audiere for sound" ON) option(USE_MPG123 "use mpg123 + libsndfile for sound" ON) # OS X deployment @@ -137,31 +137,53 @@ set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup +set(GOT_SOUND_INPUT 0) set(SOUND_INPUT_INCLUDES "") set(SOUND_INPUT_LIBRARY "") set(SOUND_DEFINE "") if (USE_FFMPEG) set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) - find_package(FFmpeg REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) + find_package(FFmpeg) + if (FFMPEG_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) + set(GOT_SOUND_INPUT 1) + endif (FFMPEG_FOUND) endif (USE_FFMPEG) -if (USE_AUDIERE) - find_package(Audiere REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE) -endif (USE_AUDIERE) +if (USE_AUDIERE AND NOT GOT_SOUND_INPUT) + find_package(Audiere) + if (AUDIERE_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE) + set(GOT_SOUND_INPUT 1) + endif (AUDIERE_FOUND) +endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT) -if (USE_MPG123) +if (USE_MPG123 AND NOT GOT_SOUND_INPUT) find_package(MPG123 REQUIRED) find_package(SNDFILE REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) -endif (USE_MPG123) + if (MPG123_FOUND AND SNDFILE_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) + set(GOT_SOUND_INPUT 1) + endif (MPG123_FOUND AND SNDFILE_FOUND) +endif (USE_MPG123 AND NOT GOT_SOUND_INPUT) + +if (NOT GOT_SOUND_INPUT) + message(WARNING "--------------------") + message(WARNING "Failed to find any sound input packages") + message(WARNING "--------------------") +endif (NOT GOT_SOUND_INPUT) + +if (NOT FFMPEG_FOUND) + message(WARNING "--------------------") + message(WARNING "FFmpeg not found, video playback will be disabled") + message(WARNING "--------------------") +endif (NOT FFMPEG_FOUND) # Platform specific From 6bc526b74d7c939d6ebc1e471b12b719c388f772 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 05:30:38 -0800 Subject: [PATCH 088/147] Avoid another loop for decoding audio --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 817ec0a3f0..1c22b7b3d1 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -401,7 +401,7 @@ public: while(total < len) { - while(mFramePos >= mFrameSize) + if(mFramePos >= mFrameSize) { /* We have already sent all our data; get more */ mFrameSize = audio_decode_frame(mFrame); @@ -413,6 +413,7 @@ public: mFramePos = std::min(mFrameSize, sample_skip); sample_skip -= mFramePos; + continue; } size_t len1 = len - total; From c92cde2be92f092cbb00249457f39e355146fde3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 05:50:20 -0800 Subject: [PATCH 089/147] Properly flush packet queues when at EOF Note: the previous flush method was renamed to clear. Flushing a queue allows consumers to retrieve queued packets, but not expect any more to come in. --- apps/openmw/mwrender/videoplayer.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 1c22b7b3d1..d2e317b712 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -29,12 +29,13 @@ enum { struct PacketQueue { PacketQueue() - : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + : first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0) { } ~PacketQueue() - { flush(); } + { clear(); } AVPacketList *first_pkt, *last_pkt; + volatile bool flushing; int nb_packets; int size; @@ -45,6 +46,7 @@ struct PacketQueue { int get(AVPacket *pkt, VideoState *is); void flush(); + void clear(); }; struct VideoPicture { @@ -202,6 +204,8 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) return 1; } + if(this->flushing) + break; this->cond.wait(lock); } @@ -209,6 +213,12 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) } void PacketQueue::flush() +{ + this->flushing = true; + this->cond.notify_one(); +} + +void PacketQueue::clear() { AVPacketList *pkt, *pkt1; @@ -753,7 +763,10 @@ void VideoState::decode_thread_loop(VideoState *self) else av_free_packet(packet); } + /* all done - wait for it */ + self->videoq.flush(); + self->audioq.flush(); while(!self->quit) { // EOF reached, all packets processed, we can exit now From c5dd0e19688b388b6e4e978d5ecf07ec14f5085d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 16:05:31 +0100 Subject: [PATCH 090/147] New Game button --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 25 ++++++++++++++++++++----- apps/openmw/mwgui/mainmenu.hpp | 8 +++++++- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 198a20d454..3c707d196b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -82,6 +82,8 @@ namespace MWBase virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. + virtual void newGame() = 0; + virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e98b75e9be..b19f3de6d4 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -6,12 +6,15 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "confirmationdialog.hpp" + namespace MWGui { - MainMenu::MainMenu(int w, int h) + MainMenu::MainMenu(MWBase::WindowManager& parWindowManager, int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) + , mDialog(parWindowManager) { onResChange(w,h); } @@ -20,7 +23,7 @@ namespace MWGui { setCoord(0,0,w,h); - int height = 64 * 3; + int height = 64 * 4; if (mButtonBox) MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); @@ -33,12 +36,11 @@ namespace MWGui mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); curH += 64; - - /* mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mNewGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::newGame); mNewGame->setImageResource ("Menu_NewGame"); curH += 64; - +/* mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mLoadGame->setImageResource ("Menu_LoadGame"); curH += 64; @@ -81,4 +83,17 @@ namespace MWGui Ogre::Root::getSingleton ().queueEndRendering (); } + void MainMenu::newGame(MyGUI::Widget* sender) + { + mDialog.open ("#{sNotifyMessage54}"); + mDialog.eventOkClicked.clear(); + mDialog.eventCancelClicked.clear(); + mDialog.eventOkClicked += MyGUI::newDelegate(this, &MainMenu::newGameConfirmed); + } + + void MainMenu::newGameConfirmed() + { + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + MWBase::Environment::get().getWorld ()->newGame(); + } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index fd583d1876..ab48f29d92 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,12 +1,14 @@ #include +#include "confirmationdialog.hpp" + namespace MWGui { class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(int w, int h); + MainMenu(MWBase::WindowManager& parWindowManager, int w, int h); void onResChange(int w, int h); @@ -24,6 +26,10 @@ namespace MWGui void returnToGame(MyGUI::Widget* sender); void showOptions(MyGUI::Widget* sender); void exitGame(MyGUI::Widget* sender); + void newGame(MyGUI::Widget* sender); + void newGameConfirmed(); + + ConfirmationDialog mDialog; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 373546aa80..627f8f3390 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -139,7 +139,7 @@ WindowManager::WindowManager( mDragAndDrop->mDraggedWidget = 0; mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - mMenu = new MainMenu(w,h); + mMenu = new MainMenu(*this, w,h); mMap = new MapWindow(*this, cacheDir); mStatsWindow = new StatsWindow(*this); mConsole = new Console(w,h, consoleOnlyScripts); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8eb121d374..f1fb676320 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mCells (mStore, mEsm), + mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); @@ -1015,6 +1015,12 @@ namespace MWWorld } } } + + if (mNewGameStarted) + { + playVideo ("mw_intro.bik"); + mNewGameStarted = false; + } } bool World::isCellExterior() const @@ -1296,4 +1302,11 @@ namespace MWWorld { mRendering->stopVideo(); } + + void World::newGame () + { + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); + mNewGameStarted = true; // in order to play the intro video at the end of the next frame + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1c13529136..a58e70d124 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -60,6 +60,7 @@ namespace MWWorld MWWorld::Globals *mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; + bool mNewGameStarted; Cells mCells; @@ -102,6 +103,8 @@ namespace MWWorld virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. + virtual void newGame(); + virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); From 86671096ec89960405726594a50e5a7fa787b075 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 16:14:49 +0100 Subject: [PATCH 091/147] remove commandline switch for new game --- apps/openmw/engine.cpp | 10 ++-------- apps/openmw/engine.hpp | 4 ---- apps/openmw/main.cpp | 4 ---- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 12 +++++++++--- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +++- apps/openmw/mwworld/worldimp.cpp | 8 +------- apps/openmw/mwworld/worldimp.hpp | 2 +- 9 files changed, 19 insertions(+), 28 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2299053cdc..08e24b5d6d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -125,7 +125,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) - , mNewGame (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -237,11 +236,6 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } -void OMW::Engine::setNewGame(bool newGame) -{ - mNewGame = newGame; -} - // Initialise and enter main loop. void OMW::Engine::go() @@ -332,13 +326,13 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); // Create sound system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 57402c91e2..b13ec43689 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,7 +68,6 @@ namespace OMW int mFpsLevel; bool mDebug; bool mVerboseScripts; - bool mNewGame; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -138,9 +137,6 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); - /// Start as a new game. - void setNewGame(bool newGame); - /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 0563fdbbbf..6b31f3ade6 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -133,9 +133,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -238,7 +235,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); - engine.setNewGame(variables["new-game"].as()); // other settings engine.setDebugMode(variables["debug"].as()); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c177912d4f..9df75870de 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -79,6 +79,8 @@ namespace MWBase */ virtual void update() = 0; + virtual void newGame() = 0; + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index b19f3de6d4..0f74f6e14f 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -94,6 +94,7 @@ namespace MWGui void MainMenu::newGameConfirmed() { MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + MWBase::Environment::get().getWindowManager ()->newGame (); MWBase::Environment::get().getWorld ()->newGame(); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 627f8f3390..1f5966f728 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -54,7 +54,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, + const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) @@ -95,8 +95,8 @@ WindowManager::WindowManager( , mGui(NULL) , mGarbageDialogs() , mShown(GW_ALL) - , mAllowed(newGame ? GW_None : GW_ALL) - , mRestAllowed(newGame ? false : true) + , mAllowed(GW_ALL) + , mRestAllowed(true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) @@ -1041,3 +1041,9 @@ void WindowManager::startTraining(MWWorld::Ptr actor) { mTrainingWindow->startTraining(actor); } + +void WindowManager::newGame () +{ + mAllowed = GW_None; + mRestAllowed = false; +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 2e684b5da2..db5c22348c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -74,7 +74,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts); virtual ~WindowManager(); @@ -86,6 +86,8 @@ namespace MWGui */ virtual void update(); + virtual void newGame(); + virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f1fb676320..afcdb8d394 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -167,7 +167,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), @@ -197,12 +197,6 @@ namespace MWWorld // global variables mGlobalVariables = new Globals (mStore); - if (newGame) - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - } - mGlobalVariables->setInt ("pcrace", 3); mWorldScene = new Scene(*mRendering, mPhysics); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a58e70d124..8f89ecff3c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const std::string& encoding, std::map fallbackMap); virtual ~World(); From 06fd66e99dfd6c8aea5b29c8e881baa852e2315a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 11:49:46 -0800 Subject: [PATCH 092/147] Move some fields to the class they're used in --- apps/openmw/mwrender/videoplayer.cpp | 47 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d2e317b712..3e1701095f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -61,8 +61,7 @@ struct VideoState { VideoState() : format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT) , external_clock_base(0.0) - , audio_st(NULL), audio_diff_cum(0.0), audio_diff_avg_coef(0.0), - audio_diff_threshold(0.0), audio_diff_avg_count(0) + , audio_st(NULL) , video_st(NULL), frame_last_pts(0.0), frame_last_delay(0.0), video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0) @@ -121,10 +120,6 @@ struct VideoState { AVStream** audio_st; PacketQueue audioq; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; MWBase::SoundPtr AudioTrack; AVStream** video_st; @@ -261,7 +256,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; - double audio_clock; + double mAudioClock; + + /* averaging filter for audio sync */ + double mAudioDiffAccum; + double mAudioDiffAvgCoef; + double mAudioDiffThreshold; + int mAudioDiffAvgCount; /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ @@ -274,14 +275,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder // accumulate the clock difference double diff = is->get_master_clock() - is->get_audio_clock(); - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) - is->audio_diff_avg_count++; + mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum; + if(mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB) + mAudioDiffAvgCount++; else { - double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) + double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef); + if(fabs(avg_diff) >= mAudioDiffThreshold) { int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * (*is->audio_st)->codec->channels; @@ -317,8 +317,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - this->audio_clock += (double)frame->nb_samples / - (double)(*is->audio_st)->codec->sample_rate; + mAudioClock += (double)frame->nb_samples / + (double)(*is->audio_st)->codec->sample_rate; /* We have data, return it and come back for more later */ return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * @@ -332,7 +332,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - this->audio_clock = av_q2d((*is->audio_st)->time_base)*pkt->pts; + mAudioClock = av_q2d((*is->audio_st)->time_base)*pkt->pts; } } @@ -352,7 +352,12 @@ public: , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) - , audio_clock(0.0) + , mAudioClock(0.0) + , mAudioDiffAccum(0.0) + , mAudioDiffAvgCoef(exp(log(0.01 / AUDIO_DIFF_AVG_NB))) + /* Correct audio only if larger error than this */ + , mAudioDiffThreshold(2.0 * 0.050/* 50 ms */) + , mAudioDiffAvgCount(0) { } virtual ~MovieAudioDecoder() { @@ -479,7 +484,7 @@ public: { ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); - return (size_t)(this->audio_clock*(*is->audio_st)->codec->sample_rate) - clock_delay; + return (size_t)(mAudioClock*(*is->audio_st)->codec->sample_rate) - clock_delay; } }; @@ -809,12 +814,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) case AVMEDIA_TYPE_AUDIO: this->audio_st = pFormatCtx->streams + stream_index; - /* averaging filter for audio sync */ - this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); - this->audio_diff_avg_count = 0; - /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; - decoder.reset(new MovieAudioDecoder(this)); this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) From 26660110e5f900c5ca93993b212fec66c390b0bd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 00:20:56 -0800 Subject: [PATCH 093/147] Allow building the video player without ffmpeg (playVideo will always throw an exception) --- apps/openmw/mwrender/videoplayer.cpp | 122 ++++++++++++++++++--------- apps/openmw/mwrender/videoplayer.hpp | 22 ++--- 2 files changed, 89 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 3e1701095f..9c4546d15a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1,5 +1,15 @@ #include "videoplayer.hpp" +#define __STDC_CONSTANT_MACROS +#include + +#include +#include + +#include +#include + +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -8,17 +18,24 @@ #include "../mwsound/sound.hpp" +namespace MWRender +{ + +#ifdef OPENMW_USE_FFMPEG + +extern "C" +{ +#include +#include +#include +} + #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 -#define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 - -namespace MWRender -{ - enum { AV_SYNC_AUDIO_MASTER, AV_SYNC_VIDEO_MASTER, @@ -27,6 +44,7 @@ enum { AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER }; + struct PacketQueue { PacketQueue() : first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0) @@ -66,16 +84,21 @@ struct VideoState { video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0) , refresh_rate_ms(10), refresh(false), quit(false), display_ready(false) - { } + { + // Register all formats and codecs + av_register_all(); + } ~VideoState() - { } + { deinit(); } void init(const std::string& resourceName); void deinit(); int stream_open(int stream_index, AVFormatContext *pFormatCtx); + bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height); + static void video_thread_loop(VideoState *is); static void decode_thread_loop(VideoState *is); @@ -791,6 +814,35 @@ void VideoState::decode_thread_loop(VideoState *self) } +bool VideoState::update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) +{ + if(this->quit) + return false; + + if(this->refresh) + { + this->refresh = false; + this->video_refresh_timer(); + // Would be nice not to do this all the time... + if(this->display_ready) + mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); + + // Correct aspect ratio by adding black bars + double videoaspect = av_q2d((*this->video_st)->codec->sample_aspect_ratio); + if(videoaspect == 0.0) + videoaspect = 1.0; + videoaspect *= static_cast((*this->video_st)->codec->width) / (*this->video_st)->codec->height; + + double screenaspect = static_cast(screen_width) / screen_height; + double aspect_correction = videoaspect / screenaspect; + + rect->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), + std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); + } + return true; +} + + int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) { MWSound::DecoderPtr decoder; @@ -899,12 +951,7 @@ void VideoState::init(const std::string& resourceName) this->parse_thread = boost::thread(decode_thread_loop, this); } - catch(std::runtime_error& e) - { - this->quit = true; - throw; - } - catch(Ogre::Exception& e) + catch(...) { this->quit = true; throw; @@ -913,6 +960,8 @@ void VideoState::init(const std::string& resourceName) void VideoState::deinit() { + this->quit = true; + this->audioq.cond.notify_one(); this->videoq.cond.notify_one(); @@ -939,6 +988,27 @@ void VideoState::deinit() } } +#else // defined OPENMW_USE_FFMPEG + +class VideoState +{ +public: + VideoState() { } + + void init(const std::string& resourceName) + { + throw std::runtime_error("FFmpeg not supported, cannot play video \""+resourceName+"\""); + } + void deinit() { } + + void close() { } + + bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) + { return false; } +}; + +#endif // defined OPENMW_USE_FFMPEG + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -1009,9 +1079,6 @@ VideoPlayer::~VideoPlayer() void VideoPlayer::playVideo(const std::string &resourceName) { - // Register all formats and codecs - av_register_all(); - if(mState) close(); @@ -1041,34 +1108,13 @@ void VideoPlayer::update () { if(mState) { - if(mState->quit) + if(!mState->update(mVideoMaterial, mRectangle, mWidth, mHeight)) close(); - else if(mState->refresh) - { - mState->refresh = false; - mState->video_refresh_timer(); - // Would be nice not to do this all the time... - if(mState->display_ready) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); - - // Correct aspect ratio by adding black bars - double videoaspect = av_q2d((*mState->video_st)->codec->sample_aspect_ratio); - if(videoaspect == 0.0) - videoaspect = 1.0; - videoaspect *= static_cast((*mState->video_st)->codec->width) / (*mState->video_st)->codec->height; - - double screenaspect = static_cast(mWidth) / mHeight; - double aspect_correction = videoaspect / screenaspect; - - mRectangle->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), - std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); - } } } void VideoPlayer::close() { - mState->quit = true; mState->deinit(); delete mState; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index c82a16e15a..d8c1902aa4 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -1,27 +1,15 @@ #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H -#include -#include +#include -#include - - -#define __STDC_CONSTANT_MACROS -#include -extern "C" +namespace Ogre { -#include -#include -#include + class SceneManager; + class SceneNode; + class Rectangle2D; } -#include -#include - -#include "../mwbase/soundmanager.hpp" - - namespace MWRender { struct VideoState; From 9e842a0bbb96b5a282307342d89b8a29a9718dc3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 00:41:04 -0800 Subject: [PATCH 094/147] Fix for trying to play videos when not supported --- apps/openmw/mwrender/videoplayer.cpp | 125 ++++++++++++++------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9c4546d15a..9fdd051b6c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -896,66 +896,58 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) void VideoState::init(const std::string& resourceName) { - try + int video_index = -1; + int audio_index = -1; + unsigned int i; + + this->av_sync_type = AV_SYNC_DEFAULT; + this->refresh_rate_ms = 10; + this->refresh = false; + this->quit = false; + + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_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; + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - this->av_sync_type = AV_SYNC_DEFAULT; - this->refresh_rate_ms = 10; - this->refresh = false; - this->quit = false; - - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); - - AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_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; - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) - { - // "Note that a user-supplied AVFormatContext will be freed on failure." - this->format_ctx = NULL; - av_free(ioCtx); - throw std::runtime_error("Failed to open video input"); - } - - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - 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); - - for(i = 0;i < this->format_ctx->nb_streams;i++) - { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } - - this->external_clock_base = av_gettime(); - if(audio_index >= 0) - this->stream_open(audio_index, this->format_ctx); - if(video_index >= 0) - this->stream_open(video_index, this->format_ctx); - - this->parse_thread = boost::thread(decode_thread_loop, this); + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + av_free(ioCtx); + throw std::runtime_error("Failed to open video input"); } - catch(...) + + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + 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); + + for(i = 0;i < this->format_ctx->nb_streams;i++) { - this->quit = true; - throw; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; } + + this->external_clock_base = av_gettime(); + if(audio_index >= 0) + this->stream_open(audio_index, this->format_ctx); + if(video_index >= 0) + this->stream_open(video_index, this->format_ctx); + + this->parse_thread = boost::thread(decode_thread_loop, this); } void VideoState::deinit() @@ -997,7 +989,7 @@ public: void init(const std::string& resourceName) { - throw std::runtime_error("FFmpeg not supported, cannot play video \""+resourceName+"\""); + throw std::runtime_error("FFmpeg not supported, cannot play \""+resourceName+"\""); } void deinit() { } @@ -1100,8 +1092,14 @@ void VideoPlayer::playVideo(const std::string &resourceName) MWBase::Environment::get().getSoundManager()->pauseAllSounds(); - mState = new VideoState; - mState->init(resourceName); + try { + mState = new VideoState; + mState->init(resourceName); + } + catch(std::exception& e) { + std::cerr<< "Failed to play video: "<deinit(); + if(mState) + { + mState->deinit(); - delete mState; - mState = NULL; + delete mState; + mState = NULL; + } MWBase::Environment::get().getSoundManager()->resumeAllSounds(); From 58ab3407b7ccbe5b395db4ed66e2587a68c091f4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 04:17:06 -0800 Subject: [PATCH 095/147] Constify a couple fields --- apps/openmw/mwrender/videoplayer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9fdd051b6c..38f7a9d167 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -283,9 +283,9 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* averaging filter for audio sync */ double mAudioDiffAccum; - double mAudioDiffAvgCoef; - double mAudioDiffThreshold; - int mAudioDiffAvgCount; + const double mAudioDiffAvgCoef; + const double mAudioDiffThreshold; + int mAudioDiffAvgCount; /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ From 4373218746eea967d4275c9363ae65f80b4202b4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 06:56:30 -0800 Subject: [PATCH 096/147] Fix audio stream check --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 38f7a9d167..56312aa26d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -767,7 +767,7 @@ void VideoState::decode_thread_loop(VideoState *self) try { - if(!self->video_st && !self->audio_st < 0) + if(!self->video_st && !self->audio_st) throw std::runtime_error("No streams to decode"); // main decode loop From 67485d3454ea3dca7812b951ed76f3b96c2b807e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 07:15:53 -0800 Subject: [PATCH 097/147] Store the AVStream in the decoder for easy referencing --- apps/openmw/mwrender/videoplayer.cpp | 72 ++++++++++++++-------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 56312aa26d..9c72e99192 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -272,7 +272,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { av_free_packet(this); } }; - VideoState *is; + VideoState *mVideoState; + AVStream *mAVStream; AutoAVPacket mPacket; AVFrame *mFrame; @@ -291,13 +292,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder * skip (negative means to duplicate). */ int synchronize_audio() { - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + if(mVideoState->av_sync_type == AV_SYNC_AUDIO_MASTER) return 0; int sample_skip = 0; // accumulate the clock difference - double diff = is->get_master_clock() - is->get_audio_clock(); + double diff = mVideoState->get_master_clock() - mVideoState->get_audio_clock(); mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum; if(mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB) mAudioDiffAvgCount++; @@ -306,9 +307,9 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef); if(fabs(avg_diff) >= mAudioDiffThreshold) { - int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; - sample_skip = ((int)(diff * (*is->audio_st)->codec->sample_rate) * n); + int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * + mAVStream->codec->channels; + sample_skip = ((int)(diff * mAVStream->codec->sample_rate) * n); } } @@ -325,7 +326,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int len1, got_frame; - len1 = avcodec_decode_audio4((*is->audio_st)->codec, frame, &got_frame, pkt); + len1 = avcodec_decode_audio4(mAVStream->codec, frame, &got_frame, pkt); if(len1 < 0) break; if(len1 <= pkt->size) @@ -341,21 +342,21 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder continue; mAudioClock += (double)frame->nb_samples / - (double)(*is->audio_st)->codec->sample_rate; + (double)mAVStream->codec->sample_rate; /* We have data, return it and come back for more later */ - return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; + return frame->nb_samples * mAVStream->codec->channels * + av_get_bytes_per_sample(mAVStream->codec->sample_fmt); } av_free_packet(pkt); /* next packet */ - if(is->audioq.get(pkt, is) < 0) + if(mVideoState->audioq.get(pkt, mVideoState) < 0) return -1; /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - mAudioClock = av_q2d((*is->audio_st)->time_base)*pkt->pts; + mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts; } } @@ -365,13 +366,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder void close() { } std::string getName() - { return is->stream->getName(); } + { return mVideoState->stream->getName(); } void rewind() { } public: - MovieAudioDecoder(VideoState *_is) - : is(_is) + MovieAudioDecoder(VideoState *is) + : mVideoState(is) + , mAVStream(*is->audio_st) , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) @@ -389,47 +391,47 @@ public: void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { - if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_U8) + if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = MWSound::SampleType_UInt8; - else if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_S16) + else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name((*is->audio_st)->codec->sample_fmt)); + av_get_sample_fmt_name(mAVStream->codec->sample_fmt)); - if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_MONO) + if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_STEREO) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_QUAD) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = MWSound::ChannelConfig_Quad; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = MWSound::ChannelConfig_5point1; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = MWSound::ChannelConfig_7point1; - else if((*is->audio_st)->codec->channel_layout == 0) + else if(mAVStream->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if((*is->audio_st)->codec->channels == 1) + if(mAVStream->codec->channels == 1) *chans = MWSound::ChannelConfig_Mono; - else if((*is->audio_st)->codec->channels == 2) + else if(mAVStream->codec->channels == 2) *chans = MWSound::ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << (*is->audio_st)->codec->channels; + sstr << mAVStream->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), (*is->audio_st)->codec->channels, - (*is->audio_st)->codec->channel_layout); + av_get_channel_layout_string(str, sizeof(str), mAVStream->codec->channels, + mAVStream->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = (*is->audio_st)->codec->sample_rate; + *samplerate = mAVStream->codec->sample_rate; } size_t read(char *stream, size_t len) @@ -464,8 +466,8 @@ public: { len1 = std::min(len1, -mFramePos); - int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; + int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * + mAVStream->codec->channels; /* add samples by copying the first sample*/ if(n == 1) @@ -505,9 +507,9 @@ public: size_t getSampleOffset() { - ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / - av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); - return (size_t)(mAudioClock*(*is->audio_st)->codec->sample_rate) - clock_delay; + ssize_t clock_delay = (mFrameSize-mFramePos) / mAVStream->codec->channels / + av_get_bytes_per_sample(mAVStream->codec->sample_fmt); + return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay; } }; From d348435a1d550fbfd27b4e1fe78a4a39ce56cefe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 21:09:57 -0800 Subject: [PATCH 098/147] Improve audio open error message --- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index efdb6f7ead..4ec3dfbb37 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -86,7 +86,7 @@ namespace MWSound { if(devname.empty()) throw; - std::cout <<"Failed to open device \""<init(); Settings::Manager::setString("device", "Sound", ""); } From 20321c45528851c7ab0ea26ab46e3aa33b338025 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 21:50:01 -0800 Subject: [PATCH 099/147] Keep track of the actual active sounds --- apps/openmw/mwsound/openal_output.cpp | 52 ++++++++++++++++++++++----- apps/openmw/mwsound/openal_output.hpp | 3 ++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index bfead78433..94499c8fb9 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -131,6 +131,8 @@ class OpenAL_SoundStream : public Sound OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); + friend class OpenAL_Output; + public: OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder); virtual ~OpenAL_SoundStream(); @@ -232,6 +234,8 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode mBufferSize = static_cast(sBufferLength*srate); mBufferSize = framesToBytes(mBufferSize, chans, type); + + mOutput.mActiveSounds.push_back(this); } catch(std::exception &e) { @@ -252,6 +256,9 @@ OpenAL_SoundStream::~OpenAL_SoundStream() alGetError(); mDecoder->close(); + + mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), + mOutput.mActiveSounds.end(), this)); } void OpenAL_SoundStream::play() @@ -402,6 +409,8 @@ protected: ALuint mSource; ALuint mBuffer; + friend class OpenAL_Output; + private: OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs); @@ -435,6 +444,7 @@ public: OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) : mOutput(output), mSource(src), mBuffer(buf) { + mOutput.mActiveSounds.push_back(this); } OpenAL_Sound::~OpenAL_Sound() { @@ -443,6 +453,9 @@ OpenAL_Sound::~OpenAL_Sound() mOutput.mFreeSources.push_back(mSource); mOutput.bufferFinished(mBuffer); + + mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), + mOutput.mActiveSounds.end(), this)); } void OpenAL_Sound::stop() @@ -597,6 +610,7 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); + std::cerr<< "There are "< 0) alDeleteSources(mSources.size(), &mSources[0]); @@ -889,11 +903,22 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 void OpenAL_Output::pauseAllSounds() { - IDVec sources = mSources; - IDDq::const_iterator iter = mFreeSources.begin(); - while(iter != mFreeSources.end()) + IDVec sources; + SoundVec::const_iterator iter = mActiveSounds.begin(); + while(iter != mActiveSounds.end()) { - sources.erase(std::find(sources.begin(), sources.end(), *iter)); + const OpenAL_SoundStream *stream = dynamic_cast(*iter); + if(stream) + { + if(stream->mSource) + sources.push_back(stream->mSource); + } + else + { + const OpenAL_Sound *sound = dynamic_cast(*iter); + if(sound && sound->mSource) + sources.push_back(sound->mSource); + } iter++; } if(sources.size() > 0) @@ -902,11 +927,22 @@ void OpenAL_Output::pauseAllSounds() void OpenAL_Output::resumeAllSounds() { - IDVec sources = mSources; - IDDq::const_iterator iter = mFreeSources.begin(); - while(iter != mFreeSources.end()) + IDVec sources; + SoundVec::const_iterator iter = mActiveSounds.begin(); + while(iter != mActiveSounds.end()) { - sources.erase(std::find(sources.begin(), sources.end(), *iter)); + const OpenAL_SoundStream *stream = dynamic_cast(*iter); + if(stream) + { + if(stream->mSource) + sources.push_back(stream->mSource); + } + else + { + const OpenAL_Sound *sound = dynamic_cast(*iter); + if(sound && sound->mSource) + sources.push_back(sound->mSource); + } iter++; } if(sources.size() > 0) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index ce126035fd..41ce569659 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -36,6 +36,9 @@ namespace MWSound uint64_t mBufferCacheMemSize; + typedef std::vector SoundVec; + SoundVec mActiveSounds; + ALuint getBuffer(const std::string &fname); void bufferFinished(ALuint buffer); From dd3e568a003d19ce4717a61cb03b8b194a3b8601 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 23:35:20 -0800 Subject: [PATCH 100/147] Set the sound properties at initialization --- apps/openmw/mwsound/openal_output.cpp | 134 +++++++++++------------- apps/openmw/mwsound/openal_output.hpp | 4 +- apps/openmw/mwsound/sound.hpp | 15 +-- apps/openmw/mwsound/sound_output.hpp | 4 +- apps/openmw/mwsound/soundmanagerimp.cpp | 36 ++----- 5 files changed, 80 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 94499c8fb9..824a722994 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -128,13 +128,15 @@ class OpenAL_SoundStream : public Sound volatile bool mIsFinished; + void updateAll(bool local); + OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); friend class OpenAL_Output; public: - OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder); + OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags); virtual ~OpenAL_SoundStream(); virtual void stop(); @@ -215,8 +217,9 @@ private: }; -OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) +OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) + : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -324,6 +327,25 @@ double OpenAL_SoundStream::getTimeOffset() return t; } +void OpenAL_SoundStream::updateAll(bool local) +{ + alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); + alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); + if(local) + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + } + else + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); + } + alSourcei(mSource, AL_LOOPING, AL_FALSE); + + update(); +} + void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; @@ -411,12 +433,14 @@ protected: friend class OpenAL_Output; + void updateAll(bool local); + private: OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs); public: - OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf); + OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags); virtual ~OpenAL_Sound(); virtual void stop(); @@ -434,15 +458,16 @@ class OpenAL_Sound3D : public OpenAL_Sound OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs); public: - OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf) - : OpenAL_Sound(output, src, buf) + OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : OpenAL_Sound(output, src, buf, pos, vol, basevol, pitch, mindist, maxdist, flags) { } virtual void update(); }; -OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) - : mOutput(output), mSource(src), mBuffer(buf) +OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) + , mOutput(output), mSource(src), mBuffer(buf) { mOutput.mActiveSounds.push_back(this); } @@ -484,10 +509,30 @@ double OpenAL_Sound::getTimeOffset() return t; } +void OpenAL_Sound::updateAll(bool local) +{ + alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); + alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); + if(local) + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + } + else + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); + } + alSourcei(mSource, AL_LOOPING, (mFlags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); + + update(); +} + void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; @@ -731,7 +776,7 @@ void OpenAL_Output::bufferFinished(ALuint buf) } -MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -744,7 +789,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume try { buf = getBuffer(fname); - sound.reset(new OpenAL_Sound(*this, src, buf)); + sound.reset(new OpenAL_Sound(*this, src, buf, Ogre::Vector3(0.0f), vol, basevol, pitch, 1.0f, 1000.0f, flags)); } catch(std::exception &e) { @@ -755,25 +800,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume throw; } - alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f); - alSourcef(src, AL_MAX_DISTANCE, 1000.0f); - alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - throwALerror(); + sound->updateAll(true); alSourcei(src, AL_BUFFER, buf); alSourcePlay(src); @@ -782,8 +809,8 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume return sound; } -MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, - float min, float max, int flags) +MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float vol, float basevol, float pitch, + float min, float max, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -796,7 +823,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre try { buf = getBuffer(fname); - sound.reset(new OpenAL_Sound3D(*this, src, buf)); + sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags)); } catch(std::exception &e) { @@ -807,26 +834,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre throw; } - alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, min); - alSourcef(src, AL_MAX_DISTANCE, max); - alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ? - 0.0f : volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - throwALerror(); + sound->updateAll(false); alSourcei(src, AL_BUFFER, buf); alSourcePlay(src); @@ -850,7 +858,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, fl std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { - sound.reset(new OpenAL_SoundStream(*this, src, decoder)); + sound.reset(new OpenAL_SoundStream(*this, src, decoder, volume, pitch, flags)); } catch(std::exception &e) { @@ -858,25 +866,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, fl throw; } - alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f); - alSourcef(src, AL_MAX_DISTANCE, 1000.0f); - alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, AL_FALSE); - throwALerror(); + sound->updateAll(true); sound->play(); return sound; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 41ce569659..29d4102c48 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -48,9 +48,9 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags); virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags); + float vol, float basevol, float pitch, float min, float max, int flags); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 1b6f50ff47..e76912d675 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -30,13 +30,14 @@ namespace MWSound void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } - Sound() : mPos(0.0f, 0.0f, 0.0f) - , mVolume(1.0f) - , mBaseVolume(1.0f) - , mPitch(1.0f) - , mMinDistance(20.0f) /* 1 * min_range_scale */ - , mMaxDistance(12750.0f) /* 255 * max_range_scale */ - , mFlags(MWBase::SoundManager::Play_Normal) + Sound(const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : mPos(pos) + , mVolume(vol) + , mBaseVolume(basevol) + , mPitch(pitch) + , mMinDistance(mindist) + , mMaxDistance(maxdist) + , mFlags(flags) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 1229f87d0e..baed39da09 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -24,9 +24,9 @@ namespace MWSound virtual void init(const std::string &devname="") = 0; virtual void deinit() = 0; - virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) = 0; virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags) = 0; + float vol, float basevol, float pitch, float min, float max, int flags) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 4ec3dfbb37..ff10e567a7 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -221,11 +221,8 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal); - sound->mPos = objpos; - sound->mBaseVolume = basevol; - + MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, + 20.0f, 12750.0f, Play_Normal); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -243,9 +240,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - MWBase::SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); - sound->mBaseVolume = basevol; - + MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); } catch(std::exception &e) @@ -282,11 +277,7 @@ namespace MWSound return track; try { - float basevol = mMasterVolume; - - track = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); - track->mBaseVolume = basevol; - track->mFlags = Play_NoEnv; + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv); } catch(std::exception &e) { @@ -307,14 +298,7 @@ namespace MWSound float min, max; std::string file = lookup(soundId, basevol, min, max); - sound = mOutput->playSound(file, volume*basevol, pitch, mode); - sound->mVolume = volume; - sound->mBaseVolume = basevol; - sound->mPitch = pitch; - sound->mMinDistance = min; - sound->mMaxDistance = max; - sound->mFlags = mode; - + sound = mOutput->playSound(file, volume, basevol, pitch, mode); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } catch(std::exception &e) @@ -339,15 +323,7 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); - sound->mPos = objpos; - sound->mVolume = volume; - sound->mBaseVolume = basevol; - sound->mPitch = pitch; - sound->mMinDistance = min; - sound->mMaxDistance = max; - sound->mFlags = mode; - + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); else From 72ffceb2066ef343108676aa404499bb07944f50 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 00:56:29 -0800 Subject: [PATCH 101/147] Add type flags to the sound play mode --- apps/openmw/mwbase/soundmanager.hpp | 8 ++++++- apps/openmw/mwsound/soundmanagerimp.cpp | 30 +++++++++---------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 8d204bad4a..de17f8dc80 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -39,9 +39,15 @@ namespace MWBase Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ + + Play_TypeSfx = 0, /* Normal SFX sound */ + Play_TypeVoice = 1<<3, /* Voice sound */ + Play_TypeMusic = 1<<4, /* Music track */ + Play_TypeMovie = 1<<5, /* Movie audio track */ + Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie }; private: diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index ff10e567a7..64c3d99037 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -171,9 +171,7 @@ namespace MWSound DecoderPtr decoder = getDecoder(); decoder->open(filename); - mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); - mMusic->mBaseVolume = basevol; - mMusic->mFlags = Play_NoEnv; + mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv|Play_TypeMusic); } catch(std::exception &e) { @@ -222,7 +220,7 @@ namespace MWSound const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal); + 20.0f, 12750.0f, Play_Normal|Play_TypeVoice); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -240,7 +238,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal); + MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); } catch(std::exception &e) @@ -277,7 +275,7 @@ namespace MWSound return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv); + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|Play_TypeMovie); } catch(std::exception &e) { @@ -296,7 +294,7 @@ namespace MWSound { float basevol = mMasterVolume * mSFXVolume; float min, max; - std::string file = lookup(soundId, basevol, min, max); + std::string file = lookup(soundId, volume, min, max); sound = mOutput->playSound(file, volume, basevol, pitch, mode); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); @@ -309,7 +307,7 @@ namespace MWSound } MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, int mode) + float volume, float pitch, int mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) @@ -319,7 +317,7 @@ namespace MWSound // Look up the sound in the ESM data float basevol = mMasterVolume * mSFXVolume; float min, max; - std::string file = lookup(soundId, basevol, min, max); + std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); @@ -538,18 +536,10 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if(snditer->second.second != "_say_sound") - { - float basevol = mMasterVolume * mSFXVolume; - float min, max; - lookup(snditer->second.second, basevol, min, max); - snditer->first->mBaseVolume = basevol; - } + if((snditer->first->mFlags&MWBase::SoundManager::Play_TypeVoice)) + snditer->first->mBaseVolume = mMasterVolume * mVoiceVolume; else - { - float basevol = mMasterVolume * mVoiceVolume; - snditer->first->mBaseVolume = basevol; - } + snditer->first->mBaseVolume = mMasterVolume * mSFXVolume; snditer->first->update(); snditer++; } From a5356e194efe2af54ea838ca42693eed99f497a5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 01:35:20 -0800 Subject: [PATCH 102/147] Allow specifying a type for the playTrack method --- apps/openmw/mwbase/soundmanager.hpp | 9 +++++---- apps/openmw/mwrender/videoplayer.cpp | 2 +- apps/openmw/mwsound/soundmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index de17f8dc80..a47b9a6ca3 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -34,15 +34,16 @@ namespace MWBase class SoundManager { public: - + /* These must all fit together */ enum PlayMode { Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ - + }; + enum PlayType { Play_TypeSfx = 0, /* Normal SFX sound */ Play_TypeVoice = 1<<3, /* Voice sound */ Play_TypeMusic = 1<<4, /* Music track */ @@ -97,7 +98,7 @@ namespace MWBase virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking - virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder) = 0; + virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9c72e99192..e5dee76af7 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -869,7 +869,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_st = pFormatCtx->streams + stream_index; decoder.reset(new MovieAudioDecoder(this)); - this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder, MWBase::SoundManager::Play_TypeMovie); if(!this->AudioTrack) { avcodec_close((*this->audio_st)->codec); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 64c3d99037..2c4e8eff03 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -268,14 +268,14 @@ namespace MWSound } - MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder) + MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) { MWBase::SoundPtr track; if(!mOutput->isInitialized()) return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|Play_TypeMovie); + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|type); } catch(std::exception &e) { diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 26ff309fb2..879110b9f9 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -103,7 +103,7 @@ namespace MWSound virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking - virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder); + virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); From b4e36d4f3104f8eb873c0bc63372cf6a45d9d70a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 02:01:04 -0800 Subject: [PATCH 103/147] Add a method to get the volume from the sound type --- apps/openmw/mwsound/sound.hpp | 4 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 42 ++++++++++++++++++------- apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index e76912d675..8deaf26a64 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -30,6 +30,10 @@ namespace MWSound void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } + MWBase::SoundManager::PlayType getPlayType() const + { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } + + Sound(const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) : mPos(pos) , mVolume(vol) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2c4e8eff03..34524ba9a4 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -137,6 +137,27 @@ namespace MWSound return "Sound/"+snd->mSound; } + // Gets the combined volume settings for the given sound type + float SoundManager::volumeFromType(PlayType type) const + { + float volume = mMasterVolume; + switch(type) + { + case Play_TypeSfx: + volume *= mSFXVolume; + break; + case Play_TypeVoice: + volume *= mVoiceVolume; + break; + case Play_TypeMusic: + case Play_TypeMovie: + volume *= mMusicVolume; + break; + case Play_TypeMask: + break; + } + return volume; + } bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const { @@ -165,13 +186,13 @@ namespace MWSound std::cout <<"Playing "<open(filename); - mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv|Play_TypeMusic); + mMusic = mOutput->streamSound(decoder, volumeFromType(Play_TypeMusic), + 1.0f, Play_NoEnv|Play_TypeMusic); } catch(std::exception &e) { @@ -214,7 +235,7 @@ namespace MWSound try { // The range values are not tested - float basevol = mMasterVolume * mVoiceVolume; + float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); @@ -235,7 +256,7 @@ namespace MWSound return; try { - float basevol = mMasterVolume * mVoiceVolume; + float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice); @@ -275,7 +296,7 @@ namespace MWSound return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|type); + track = mOutput->streamSound(decoder, volumeFromType(type), 1.0f, Play_NoEnv|type); } catch(std::exception &e) { @@ -292,7 +313,7 @@ namespace MWSound return sound; try { - float basevol = mMasterVolume * mSFXVolume; + float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); float min, max; std::string file = lookup(soundId, volume, min, max); @@ -315,7 +336,7 @@ namespace MWSound try { // Look up the sound in the ESM data - float basevol = mMasterVolume * mSFXVolume; + float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; @@ -536,16 +557,13 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if((snditer->first->mFlags&MWBase::SoundManager::Play_TypeVoice)) - snditer->first->mBaseVolume = mMasterVolume * mVoiceVolume; - else - snditer->first->mBaseVolume = mMasterVolume * mSFXVolume; + snditer->first->mBaseVolume = volumeFromType(snditer->first->getPlayType()); snditer->first->update(); snditer++; } if(mMusic) { - mMusic->mBaseVolume = mMasterVolume * mMusicVolume; + mMusic->mBaseVolume = volumeFromType(mMusic->getPlayType()); mMusic->update(); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 879110b9f9..f9ceeded64 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -59,6 +59,8 @@ namespace MWSound void updateSounds(float duration); void updateRegionSound(float duration); + float volumeFromType(PlayType type) const; + SoundManager(const SoundManager &rhs); SoundManager& operator=(const SoundManager &rhs); From 2f8daec379644128e0bec352376029b1f23f26b3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 04:19:35 -0800 Subject: [PATCH 104/147] Allow pausing only certain types of sounds --- apps/openmw/mwbase/soundmanager.hpp | 17 +++++++++++------ apps/openmw/mwrender/videoplayer.cpp | 4 ++-- apps/openmw/mwsound/openal_output.cpp | 12 ++++++------ apps/openmw/mwsound/openal_output.hpp | 4 ++-- apps/openmw/mwsound/sound_output.hpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 16 ++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 4 ++-- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a47b9a6ca3..d804830fab 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -44,10 +44,10 @@ namespace MWBase * the object and will not stop when the object is deleted. */ }; enum PlayType { - Play_TypeSfx = 0, /* Normal SFX sound */ - Play_TypeVoice = 1<<3, /* Voice sound */ - Play_TypeMusic = 1<<4, /* Music track */ - Play_TypeMovie = 1<<5, /* Movie audio track */ + Play_TypeSfx = 1<<3, /* Normal SFX sound */ + Play_TypeVoice = 1<<4, /* Voice sound */ + Play_TypeMusic = 1<<5, /* Music track */ + Play_TypeMovie = 1<<6, /* Movie audio track */ Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie }; @@ -124,10 +124,10 @@ namespace MWBase virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? - virtual void pauseAllSounds() = 0; + virtual void pauseSounds(int types=Play_TypeMask) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeAllSounds() = 0; + virtual void resumeSounds(int types=Play_TypeMask) = 0; ///< Resumes all previously paused sounds. virtual void update(float duration) = 0; @@ -139,6 +139,11 @@ namespace MWBase { return static_cast (a) | static_cast (b); } inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) { return static_cast (a) & static_cast (b); } + + inline int operator|(SoundManager::PlayType a, SoundManager::PlayType b) + { return static_cast (a) | static_cast (b); } + inline int operator&(SoundManager::PlayType a, SoundManager::PlayType b) + { return static_cast (a) & static_cast (b); } } #endif diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e5dee76af7..8e05a4f82b 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1092,7 +1092,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + MWBase::Environment::get().getSoundManager()->pauseSounds(); try { mState = new VideoState; @@ -1123,7 +1123,7 @@ void VideoPlayer::close() mState = NULL; } - MWBase::Environment::get().getSoundManager()->resumeAllSounds(); + MWBase::Environment::get().getSoundManager()->resumeSounds(); mRectangle->setVisible(false); mBackgroundRectangle->setVisible(false); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 824a722994..406c1bccad 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -891,7 +891,7 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 } -void OpenAL_Output::pauseAllSounds() +void OpenAL_Output::pauseSounds(int types) { IDVec sources; SoundVec::const_iterator iter = mActiveSounds.begin(); @@ -900,13 +900,13 @@ void OpenAL_Output::pauseAllSounds() const OpenAL_SoundStream *stream = dynamic_cast(*iter); if(stream) { - if(stream->mSource) + if(stream->mSource && (stream->getPlayType()&types)) sources.push_back(stream->mSource); } else { const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource) + if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } iter++; @@ -915,7 +915,7 @@ void OpenAL_Output::pauseAllSounds() alSourcePausev(sources.size(), &sources[0]); } -void OpenAL_Output::resumeAllSounds() +void OpenAL_Output::resumeSounds(int types) { IDVec sources; SoundVec::const_iterator iter = mActiveSounds.begin(); @@ -924,13 +924,13 @@ void OpenAL_Output::resumeAllSounds() const OpenAL_SoundStream *stream = dynamic_cast(*iter); if(stream) { - if(stream->mSource) + if(stream->mSource && (stream->getPlayType()&types)) sources.push_back(stream->mSource); } else { const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource) + if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } iter++; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 29d4102c48..ce35f4e682 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -55,8 +55,8 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); - virtual void pauseAllSounds(); - virtual void resumeAllSounds(); + virtual void pauseSounds(int types); + virtual void resumeSounds(int types); OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index baed39da09..b5ccc946af 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -31,8 +31,8 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; - virtual void pauseAllSounds() = 0; - virtual void resumeAllSounds() = 0; + virtual void pauseSounds(int types) = 0; + virtual void resumeSounds(int types) = 0; Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 34524ba9a4..165d501a23 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -313,11 +313,11 @@ namespace MWSound return sound; try { - float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); + float basevol = volumeFromType(Play_TypeSfx); float min, max; std::string file = lookup(soundId, volume, min, max); - sound = mOutput->playSound(file, volume, basevol, pitch, mode); + sound = mOutput->playSound(file, volume, basevol, pitch, mode|Play_TypeSfx); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } catch(std::exception &e) @@ -336,13 +336,13 @@ namespace MWSound try { // Look up the sound in the ESM data - float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); + float basevol = volumeFromType(Play_TypeSfx); float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode); + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|Play_TypeSfx); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); else @@ -423,16 +423,16 @@ namespace MWSound } - void SoundManager::pauseAllSounds() + void SoundManager::pauseSounds(int types) { if(mOutput->isInitialized()) - mOutput->pauseAllSounds(); + mOutput->pauseSounds(types); } - void SoundManager::resumeAllSounds() + void SoundManager::resumeSounds(int types) { if(mOutput->isInitialized()) - mOutput->resumeAllSounds(); + mOutput->resumeSounds(types); } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f9ceeded64..14b8e5cef6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -130,10 +130,10 @@ namespace MWSound virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseAllSounds(); + virtual void pauseSounds(int types=Play_TypeMask); ///< Pauses all currently playing sounds, including music. - virtual void resumeAllSounds(); + virtual void resumeSounds(int types=Play_TypeMask); ///< Resumes all previously paused sounds. virtual void update(float duration); From fe36cc1de76724ed540ce16654ad7904c7c43961 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 04:35:24 -0800 Subject: [PATCH 105/147] Don't try to resume sound types that aren't paused --- apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 165d501a23..d801e66181 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -52,6 +52,7 @@ namespace MWSound , mMusicVolume(1.0f) , mFootstepsVolume(1.0f) , mVoiceVolume(1.0f) + , mPausedSoundTypes(0) { if(!useSound) return; @@ -426,13 +427,21 @@ namespace MWSound void SoundManager::pauseSounds(int types) { if(mOutput->isInitialized()) + { + types &= Play_TypeMask; mOutput->pauseSounds(types); + mPausedSoundTypes |= types; + } } void SoundManager::resumeSounds(int types) { if(mOutput->isInitialized()) + { + types &= types&Play_TypeMask&mPausedSoundTypes; mOutput->resumeSounds(types); + mPausedSoundTypes &= ~types; + } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 14b8e5cef6..d26f51ef09 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -52,6 +52,8 @@ namespace MWSound Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerUp; + int mPausedSoundTypes; + std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); From 3b7edae7c3858d83b548c0c140b847d596bb45f5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 05:19:32 -0800 Subject: [PATCH 106/147] Don't hold a list of all sound sources --- apps/openmw/mwsound/openal_output.cpp | 19 +++++++++++-------- apps/openmw/mwsound/openal_output.hpp | 3 --- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 406c1bccad..67008e2bcd 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -637,9 +637,8 @@ void OpenAL_Output::init(const std::string &devname) ALuint src = 0; alGenSources(1, &src); throwALerror(); - mSources.push_back(src); + mFreeSources.push_back(src); } - mFreeSources.insert(mFreeSources.begin(), mSources.begin(), mSources.end()); } catch(std::exception &e) { @@ -655,11 +654,9 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); - std::cerr<< "There are "< 0) - alDeleteSources(mSources.size(), &mSources[0]); - mSources.clear(); mBufferRefs.clear(); mUnusedBuffers.clear(); @@ -893,7 +890,7 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 void OpenAL_Output::pauseSounds(int types) { - IDVec sources; + std::vector sources; SoundVec::const_iterator iter = mActiveSounds.begin(); while(iter != mActiveSounds.end()) { @@ -912,12 +909,15 @@ void OpenAL_Output::pauseSounds(int types) iter++; } if(sources.size() > 0) + { alSourcePausev(sources.size(), &sources[0]); + throwALerror(); + } } void OpenAL_Output::resumeSounds(int types) { - IDVec sources; + std::vector sources; SoundVec::const_iterator iter = mActiveSounds.begin(); while(iter != mActiveSounds.end()) { @@ -936,7 +936,10 @@ void OpenAL_Output::resumeSounds(int types) iter++; } if(sources.size() > 0) + { alSourcePlayv(sources.size(), &sources[0]); + throwALerror(); + } } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index ce35f4e682..e240a8b016 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -21,9 +21,6 @@ namespace MWSound ALCdevice *mDevice; ALCcontext *mContext; - typedef std::vector IDVec; - IDVec mSources; - typedef std::deque IDDq; IDDq mFreeSources; IDDq mUnusedBuffers; From 7b2c3e6cd37f597777734a9ddaca20040079d47d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 06:01:21 -0800 Subject: [PATCH 107/147] Pass a proper PlayMode enum to playSound and playSound3D --- apps/openmw/mwbase/soundmanager.hpp | 14 ++------------ apps/openmw/mwscript/soundextensions.cpp | 6 ++++-- apps/openmw/mwsound/soundmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.hpp | 4 ++-- apps/openmw/mwworld/weather.cpp | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index d804830fab..0c706ab496 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -102,11 +102,11 @@ namespace MWBase ///< Play a 2D audio track, using a custom decoder virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, - int mode=Play_Normal) = 0; + PlayMode mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, int mode=Play_Normal) = 0; + float volume, float pitch, PlayMode mode=Play_Normal) = 0; ///< Play a sound from an object virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; @@ -134,16 +134,6 @@ namespace MWBase virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; }; - - inline int operator|(SoundManager::PlayMode a, SoundManager::PlayMode b) - { return static_cast (a) | static_cast (b); } - inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) - { return static_cast (a) & static_cast (b); } - - inline int operator|(SoundManager::PlayType a, SoundManager::PlayType b) - { return static_cast (a) | static_cast (b); } - inline int operator&(SoundManager::PlayType a, SoundManager::PlayType b) - { return static_cast (a) & static_cast (b); } } #endif diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 35e66241eb..408a6f29d2 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -118,7 +118,8 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; @@ -144,7 +145,8 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d801e66181..8a69fc96bc 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -307,7 +307,7 @@ namespace MWSound } - MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) @@ -329,7 +329,7 @@ namespace MWSound } MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, int mode) + float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index d26f51ef09..b475449d90 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -110,11 +110,11 @@ namespace MWSound virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder - virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound, independently of 3D-position virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, int mode=Play_Normal); + float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound from an object virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 009b325c06..19bc881f7f 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -738,7 +738,7 @@ void WeatherManager::update(float duration) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(ambientSnd); - MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, true); + MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } @@ -749,7 +749,7 @@ void WeatherManager::update(float duration) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(rainSnd); - MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, true); + MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } From 1c73a3f2fb967d1edb324668c029746efe8c3a30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Dec 2012 19:09:08 +0100 Subject: [PATCH 108/147] Revert "remove commandline switch for new game" This reverts commit 86671096ec89960405726594a50e5a7fa787b075. --- apps/openmw/engine.cpp | 10 ++++++++-- apps/openmw/engine.hpp | 4 ++++ apps/openmw/main.cpp | 4 ++++ apps/openmw/mwbase/windowmanager.hpp | 2 -- apps/openmw/mwgui/mainmenu.cpp | 1 - apps/openmw/mwgui/windowmanagerimp.cpp | 12 +++--------- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +--- apps/openmw/mwworld/worldimp.cpp | 8 +++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 08e24b5d6d..2299053cdc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -125,6 +125,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) + , mNewGame (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -236,6 +237,11 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } +void OMW::Engine::setNewGame(bool newGame) +{ + mNewGame = newGame; +} + // Initialise and enter main loop. void OMW::Engine::go() @@ -326,13 +332,13 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); // Create sound system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index b13ec43689..57402c91e2 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,6 +68,7 @@ namespace OMW int mFpsLevel; bool mDebug; bool mVerboseScripts; + bool mNewGame; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -137,6 +138,9 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); + /// Start as a new game. + void setNewGame(bool newGame); + /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 6b31f3ade6..0563fdbbbf 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -133,6 +133,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") + ("new-game", bpo::value()->implicit_value(true) + ->default_value(false), "activate char gen/new game mechanics") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -235,6 +238,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); + engine.setNewGame(variables["new-game"].as()); // other settings engine.setDebugMode(variables["debug"].as()); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 9df75870de..c177912d4f 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -79,8 +79,6 @@ namespace MWBase */ virtual void update() = 0; - virtual void newGame() = 0; - virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 0f74f6e14f..b19f3de6d4 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -94,7 +94,6 @@ namespace MWGui void MainMenu::newGameConfirmed() { MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - MWBase::Environment::get().getWindowManager ()->newGame (); MWBase::Environment::get().getWorld ()->newGame(); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1f5966f728..627f8f3390 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -54,7 +54,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) @@ -95,8 +95,8 @@ WindowManager::WindowManager( , mGui(NULL) , mGarbageDialogs() , mShown(GW_ALL) - , mAllowed(GW_ALL) - , mRestAllowed(true) + , mAllowed(newGame ? GW_None : GW_ALL) + , mRestAllowed(newGame ? false : true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) @@ -1041,9 +1041,3 @@ void WindowManager::startTraining(MWWorld::Ptr actor) { mTrainingWindow->startTraining(actor); } - -void WindowManager::newGame () -{ - mAllowed = GW_None; - mRestAllowed = false; -} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index db5c22348c..2e684b5da2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -74,7 +74,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts); virtual ~WindowManager(); @@ -86,8 +86,6 @@ namespace MWGui */ virtual void update(); - virtual void newGame(); - virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index afcdb8d394..f1fb676320 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -167,7 +167,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), @@ -197,6 +197,12 @@ namespace MWWorld // global variables mGlobalVariables = new Globals (mStore); + if (newGame) + { + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); + } + mGlobalVariables->setInt ("pcrace", 3); mWorldScene = new Scene(*mRendering, mPhysics); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8f89ecff3c..a58e70d124 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap); virtual ~World(); From 64210e6efaf5e536e353d5357900252fab437a34 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Dec 2012 19:09:27 +0100 Subject: [PATCH 109/147] Revert "New Game button" This reverts commit c5dd0e19688b388b6e4e978d5ecf07ec14f5085d. --- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwgui/mainmenu.cpp | 25 +++++-------------------- apps/openmw/mwgui/mainmenu.hpp | 8 +------- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 +-------------- apps/openmw/mwworld/worldimp.hpp | 3 --- 6 files changed, 8 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c707d196b..198a20d454 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -82,8 +82,6 @@ namespace MWBase virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. - virtual void newGame() = 0; - virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index b19f3de6d4..e98b75e9be 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -6,15 +6,12 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "confirmationdialog.hpp" - namespace MWGui { - MainMenu::MainMenu(MWBase::WindowManager& parWindowManager, int w, int h) + MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) - , mDialog(parWindowManager) { onResChange(w,h); } @@ -23,7 +20,7 @@ namespace MWGui { setCoord(0,0,w,h); - int height = 64 * 4; + int height = 64 * 3; if (mButtonBox) MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); @@ -36,11 +33,12 @@ namespace MWGui mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); curH += 64; + + /* mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mNewGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::newGame); mNewGame->setImageResource ("Menu_NewGame"); curH += 64; -/* + mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mLoadGame->setImageResource ("Menu_LoadGame"); curH += 64; @@ -83,17 +81,4 @@ namespace MWGui Ogre::Root::getSingleton ().queueEndRendering (); } - void MainMenu::newGame(MyGUI::Widget* sender) - { - mDialog.open ("#{sNotifyMessage54}"); - mDialog.eventOkClicked.clear(); - mDialog.eventCancelClicked.clear(); - mDialog.eventOkClicked += MyGUI::newDelegate(this, &MainMenu::newGameConfirmed); - } - - void MainMenu::newGameConfirmed() - { - MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - MWBase::Environment::get().getWorld ()->newGame(); - } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index ab48f29d92..fd583d1876 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,14 +1,12 @@ #include -#include "confirmationdialog.hpp" - namespace MWGui { class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(MWBase::WindowManager& parWindowManager, int w, int h); + MainMenu(int w, int h); void onResChange(int w, int h); @@ -26,10 +24,6 @@ namespace MWGui void returnToGame(MyGUI::Widget* sender); void showOptions(MyGUI::Widget* sender); void exitGame(MyGUI::Widget* sender); - void newGame(MyGUI::Widget* sender); - void newGameConfirmed(); - - ConfirmationDialog mDialog; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 627f8f3390..373546aa80 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -139,7 +139,7 @@ WindowManager::WindowManager( mDragAndDrop->mDraggedWidget = 0; mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - mMenu = new MainMenu(*this, w,h); + mMenu = new MainMenu(w,h); mMap = new MapWindow(*this, cacheDir); mStatsWindow = new StatsWindow(*this); mConsole = new Console(w,h, consoleOnlyScripts); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f1fb676320..8eb121d374 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), + mSky (true), mCells (mStore, mEsm), mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); @@ -1015,12 +1015,6 @@ namespace MWWorld } } } - - if (mNewGameStarted) - { - playVideo ("mw_intro.bik"); - mNewGameStarted = false; - } } bool World::isCellExterior() const @@ -1302,11 +1296,4 @@ namespace MWWorld { mRendering->stopVideo(); } - - void World::newGame () - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - mNewGameStarted = true; // in order to play the intro video at the end of the next frame - } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a58e70d124..1c13529136 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -60,7 +60,6 @@ namespace MWWorld MWWorld::Globals *mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; - bool mNewGameStarted; Cells mCells; @@ -103,8 +102,6 @@ namespace MWWorld virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. - virtual void newGame(); - virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); From 85850c7440d54d612c9ab8fd9026a1ae5c06f095 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 10:22:40 -0800 Subject: [PATCH 110/147] Fix DEFAULT_OUTPUT declaration --- apps/openmw/mwsound/openal_output.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index e240a8b016..02706b50ca 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -70,7 +70,7 @@ namespace MWSound friend class SoundManager; }; #ifndef DEFAULT_OUTPUT -#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output) +#define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) #endif } From ade4ec04532382fec14d6963324be55731911672 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Dec 2012 19:01:47 +0100 Subject: [PATCH 111/147] fix texture edge bleeding due to wrong addressing mode --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8e05a4f82b..2d24edc516 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1019,7 +1019,8 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); - } + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General"); From 1dd9276cebdd6035f6c0071d5dea1a28b873d4d6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 28 Dec 2012 11:26:41 -0800 Subject: [PATCH 112/147] Add missing decoder method declarations --- apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/mpgsnd_decoder.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 8623a3f2c1..91c07ccacb 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -21,6 +21,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.hpp b/apps/openmw/mwsound/mpgsnd_decoder.hpp index 52c37bb877..be52f6f491 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.hpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.hpp @@ -34,6 +34,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); From 9afe4467d8e5ea3f6fc9960e338bc16bcc1d7bbc Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Wed, 2 Jan 2013 13:50:44 -0800 Subject: [PATCH 113/147] cache results of query for spash screen names ResourceGroupManager::listResourceNames returns a list of all resource accessable which is expensive, this change caches the result of the processed query so additional splash screen changes are quicker. --- apps/openmw/mwgui/loadingscreen.cpp | 28 ++++++++++++++++------------ apps/openmw/mwgui/loadingscreen.hpp | 1 + 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index d721e209a6..a508bcd345 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -213,22 +213,26 @@ namespace MWGui void LoadingScreen::changeWallpaper () { - std::vector splash; - - Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); - for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) + if (mResources.isNull ()) { - if (it->size() < 6) - continue; - std::string start = it->substr(0, 6); - boost::to_lower(start); + mResources = Ogre::StringVectorPtr (new Ogre::StringVector); - if (start == "splash") - splash.push_back (*it); + Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); + for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) + { + if (it->size() < 6) + continue; + std::string start = it->substr(0, 6); + boost::to_lower(start); + + if (start == "splash") + mResources->push_back (*it); + } } - if (splash.size()) + + if (mResources->size()) { - std::string randomSplash = splash[rand() % splash.size()]; + std::string randomSplash = mResources->at (rand() % mResources->size()); Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); mBackgroundImage->setImageTexture (randomSplash); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index a012793caf..c14087a3b9 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -42,6 +42,7 @@ namespace MWGui Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mBackgroundMaterial; + Ogre::StringVectorPtr mResources; bool mLoadingOn; From 740e2b5769d85bec22b8478846bd3800d2552049 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 2 Jan 2013 23:02:13 +0100 Subject: [PATCH 114/147] components/to_utf8: add class Utf8Encoder --- components/to_utf8/to_utf8.cpp | 743 +++++++++++++++++++++++---------- components/to_utf8/to_utf8.hpp | 67 ++- 2 files changed, 563 insertions(+), 247 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 7db6112475..8ac582b81d 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library @@ -46,334 +48,611 @@ static std::vector buf (50*1024); static std::vector output (50*1024); static int size; -// Make sure the given vector is large enough for 'size' bytes, +using namespace ToUTF8; + +Utf8Encoder::Utf8Encoder(void): + mOutput(50*1024) +{ +} + +void Utf8Encoder::setEncoding(const FromType sourceEncoding) +{ + mEncoding = sourceEncoding; + + switch (mEncoding) + { + case ToUTF8::WINDOWS_1252: + { + translationArray = ToUTF8::windows_1252; + break; + } + case ToUTF8::WINDOWS_1250: + { + translationArray = ToUTF8::windows_1250; + break; + } + case ToUTF8::WINDOWS_1251: + { + translationArray = ToUTF8::windows_1251; + break; + } + default: + { + assert(0); + } + } +} + +std::string Utf8Encoder::getUtf8(const char* input, int size) +{ + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength(input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(outlen); + char *out = &mOutput[0]; + + // Translate + while (*input) + copyFromArray(*(input++), out); + + // Make sure that we wrote the correct number of bytes + assert((out-&mOutput[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(mOutput.size() > outlen); + assert(mOutput[outlen] == 0); + + // Return a string + return std::string(&mOutput[0], outlen); +} + +std::string Utf8Encoder::getLegacyEnc(const char *input, int size) +{ + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength2(input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(outlen); + char *out = &mOutput[0]; + + // Translate + while(*input) + copyFromArray2(input, out); + + // Make sure that we wrote the correct number of bytes + assert((out-&mOutput[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(mOutput.size() > outlen); + assert(mOutput[outlen] == 0); + + // Return a string + return std::string(&mOutput[0], outlen); +} + +// Make sure the output vector is large enough for 'size' bytes, // including a terminating zero after it. +void Utf8Encoder::resize(size_t size) +{ + if (mOutput.size() <= size) + // Add some extra padding to reduce the chance of having to resize + // again later. + mOutput.resize(3*size); + + // And make sure the string is zero terminated + mOutput[size] = 0; +} + +/** Get the total length length needed to decode the given string with + the given translation array. The arrays are encoded with 6 bytes + per character, with the first giving the length and the next 5 the + actual data. + + The function serves a dual purpose for optimization reasons: it + checks if the input is pure ascii (all values are <= 127). If this + is the case, then the ascii parameter is set to true, and the + caller can optimize for this case. + */ +size_t Utf8Encoder::getLength(const char* input, bool &ascii) +{ + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; + + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while (inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); + + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if (inp) + { + ascii = false; + while (inp) + { + // Find the translated length of this character in the + // lookup table. + len += translationArray[inp*6]; + inp = *(++ptr); + } + } + return len; +} + +// Translate one character 'ch' using the translation array 'arr', and +// advance the output pointer accordingly. +void Utf8Encoder::copyFromArray(unsigned char ch, char* &out) +{ + // Optimize for ASCII values + if (ch < 128) + { + *(out++) = ch; + return; + } + + const char *in = translationArray + ch*6; + int len = *(in++); + for (int i=0; i &buf, size_t size) { - if(buf.size() <= size) - // Add some extra padding to reduce the chance of having to resize - // again later. - buf.resize(3*size); + if(buf.size() <= size) + // Add some extra padding to reduce the chance of having to resize + // again later. + buf.resize(3*size); - // And make sure the string is zero terminated - buf[size] = 0; + // And make sure the string is zero terminated + buf[size] = 0; } // This is just used to spew out a reusable input buffer for the // conversion process. char *ToUTF8::getBuffer(int s) { - // Remember the requested size - size = s; - resize(buf, size); - return &buf[0]; + // Remember the requested size + size = s; + resize(buf, size); + return &buf[0]; } /** Get the total length length needed to decode the given string with - the given translation array. The arrays are encoded with 6 bytes - per character, with the first giving the length and the next 5 the - actual data. + the given translation array. The arrays are encoded with 6 bytes + per character, with the first giving the length and the next 5 the + actual data. - The function serves a dual purpose for optimization reasons: it - checks if the input is pure ascii (all values are <= 127). If this - is the case, then the ascii parameter is set to true, and the - caller can optimize for this case. + The function serves a dual purpose for optimization reasons: it + checks if the input is pure ascii (all values are <= 127). If this + is the case, then the ascii parameter is set to true, and the + caller can optimize for this case. */ static size_t getLength(const char *arr, const char* input, bool &ascii) { - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while(inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if(inp) { - ascii = false; - while(inp) + ascii = false; + while(inp) { - // Find the translated length of this character in the - // lookup table. - len += arr[inp*6]; - inp = *(++ptr); + // Find the translated length of this character in the + // lookup table. + len += arr[inp*6]; + inp = *(++ptr); } } - return len; + return len; } // Translate one character 'ch' using the translation array 'arr', and // advance the output pointer accordingly. static void copyFromArray(const char *arr, unsigned char ch, char* &out) { - // Optimize for ASCII values - if(ch < 128) + // Optimize for ASCII values + if(ch < 128) { - *(out++) = ch; - return; + *(out++) = ch; + return; } - const char *in = arr + ch*6; - int len = *(in++); - for(int i=0; i outlen); - assert(output[outlen] == 0); + // And make extra sure the output is null terminated + assert(output.size() > outlen); + assert(output[outlen] == 0); - // Return a string - return std::string(&output[0], outlen); + // Return a string + return std::string(&output[0], outlen); } static size_t getLength2(const char *arr, const char* input, bool &ascii) { - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while(inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if(inp) { - ascii = false; - while(inp) + ascii = false; + while(inp) { len += 1; - // Find the translated length of this character in the - // lookup table. + // Find the translated length of this character in the + // lookup table. switch(inp) { - case 0xe2: len -= 2; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len -= 1; break; + case 0xe2: len -= 2; break; + case 0xc2: + case 0xcb: + case 0xc4: + case 0xc6: + case 0xc3: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xc5: len -= 1; break; } - inp = *(++ptr); + inp = *(++ptr); } } - return len; + return len; } -#include -#include - static void copyFromArray2(const char *arr, char*& chp, char* &out) { unsigned char ch = *(chp++); - // Optimize for ASCII values - if(ch < 128) + // Optimize for ASCII values + if(ch < 128) { - *(out++) = ch; - return; + *(out++) = ch; + return; } - int len = 1; - switch (ch) - { - case 0xe2: len = 3; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len = 2; break; - } + int len = 1; + switch (ch) + { + case 0xe2: len = 3; break; + case 0xc2: + case 0xcb: + case 0xc4: + case 0xc6: + case 0xc3: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xc5: len = 2; break; + } - if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) - { - *(out++) = ch; - return; - } + if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) + { + *(out++) = ch; + return; + } - unsigned char ch2 = *(chp++); - unsigned char ch3 = '\0'; - if (len == 3) - ch3 = *(chp++); + unsigned char ch2 = *(chp++); + unsigned char ch3 = '\0'; + if (len == 3) + ch3 = *(chp++); - for (int i = 128; i < 256; i++) - { - unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; - if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) - { - *(out++) = (char)i; - return; - } - } + for (int i = 128; i < 256; i++) + { + unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; + if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) + { + *(out++) = (char)i; + return; + } + } - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; + std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - *(out++) = ch; // Could not find glyph, just put whatever + *(out++) = ch; // Could not find glyph, just put whatever } std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) { - // Pick translation array - const char *arr; - switch (to) - { - case ToUTF8::WINDOWS_1252: + // Pick translation array + const char *arr; + switch (to) { - arr = ToUTF8::windows_1252; - break; + case ToUTF8::WINDOWS_1252: + { + arr = ToUTF8::windows_1252; + break; + } + case ToUTF8::WINDOWS_1250: + { + arr = ToUTF8::windows_1250; + break; + } + case ToUTF8::WINDOWS_1251: + { + arr = ToUTF8::windows_1251; + break; + } + default: + { + assert(0); + } } - case ToUTF8::WINDOWS_1250: - { - arr = ToUTF8::windows_1250; - break; - } - case ToUTF8::WINDOWS_1251: - { - arr = ToUTF8::windows_1251; - break; - } - default: - { - assert(0); - } - } - // Double check that the input string stops at some point (it might - // contain zero terminators before this, inside its own data, which - // is also ok.) - char* input = &buf[0]; - assert(input[size] == 0); + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + char* input = &buf[0]; + assert(input[size] == 0); - // TODO: The rest of this function is designed for single-character - // input encodings only. It also assumes that the input the input - // encoding shares its first 128 values (0-127) with ASCII. These - // conditions must be checked again if you add more input encodings - // later. + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. - // Compute output length, and check for pure ascii input at the same - // time. - bool ascii; - size_t outlen = getLength2(arr, input, ascii); + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength2(arr, input, ascii); - // If we're pure ascii, then don't bother converting anything. - if(ascii) - return std::string(input, outlen); + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); - // Make sure the output is large enough - resize(output, outlen); - char *out = &output[0]; + // Make sure the output is large enough + resize(output, outlen); + char *out = &output[0]; - // Translate - while(*input) - copyFromArray2(arr, input, out); + // Translate + while(*input) + copyFromArray2(arr, input, out); - // Make sure that we wrote the correct number of bytes - assert((out-&output[0]) == (int)outlen); + // Make sure that we wrote the correct number of bytes + assert((out-&output[0]) == (int)outlen); - // And make extra sure the output is null terminated - assert(output.size() > outlen); - assert(output[outlen] == 0); + // And make extra sure the output is null terminated + assert(output.size() > outlen); + assert(output[outlen] == 0); - // Return a string - return std::string(&output[0], outlen); + // Return a string + return std::string(&output[0], outlen); } ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) { - if (encodingName == "win1250") - return ToUTF8::WINDOWS_1250; - else if (encodingName == "win1251") - return ToUTF8::WINDOWS_1251; - else - return ToUTF8::WINDOWS_1252; + if (encodingName == "win1250") + return ToUTF8::WINDOWS_1250; + else if (encodingName == "win1251") + return ToUTF8::WINDOWS_1251; + else + return ToUTF8::WINDOWS_1252; } std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) { - if (encodingName == "win1250") - return "Using Central and Eastern European font encoding."; - else if (encodingName == "win1251") - return "Using Cyrillic font encoding."; - else - return "Using default (English) font encoding."; + if (encodingName == "win1250") + return "Using Central and Eastern European font encoding."; + else if (encodingName == "win1251") + return "Using Cyrillic font encoding."; + else + return "Using default (English) font encoding."; } diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index f52ae73bd8..6877e2dc17 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -2,29 +2,66 @@ #define COMPONENTS_TOUTF8_H #include +#include +#include namespace ToUTF8 { - // These are all the currently supported code pages - enum FromType + // These are all the currently supported code pages + enum FromType { - WINDOWS_1250, // Central ane Eastern European languages - WINDOWS_1251, // Cyrillic languages - WINDOWS_1252 // Used by English version of Morrowind (and - // probably others) + WINDOWS_1250, // Central ane Eastern European languages + WINDOWS_1251, // Cyrillic languages + WINDOWS_1252 // Used by English version of Morrowind (and + // probably others) }; - // Return a writable buffer of at least 'size' bytes. The buffer - // does not have to be freed. - char* getBuffer(int size); + // Return a writable buffer of at least 'size' bytes. The buffer + // does not have to be freed. + char* getBuffer(int size); - // Convert the previously written buffer to UTF8 from the given code - // page. - std::string getUtf8(FromType from); - std::string getLegacyEnc(FromType to); + // Convert the previously written buffer to UTF8 from the given code + // page. + std::string getUtf8(FromType from); + std::string getLegacyEnc(FromType to); - FromType calculateEncoding(const std::string& encodingName); - std::string encodingUsingMessage(const std::string& encodingName); + FromType calculateEncoding(const std::string& encodingName); + std::string encodingUsingMessage(const std::string& encodingName); + + // class + + class Utf8Encoder + { + public: + Utf8Encoder(void); + + void setEncoding(const FromType sourceEncoding); + + // Convert to UTF8 from the previously given code page. + std::string getUtf8(const char *input, int size); + inline std::string getUtf8(const std::string &str) + { + return getUtf8(str.c_str(), str.size()); + } + + std::string getLegacyEnc(const char *input, int size); + inline std::string getLegacyEnc(const std::string &str) + { + return getLegacyEnc(str.c_str(), str.size()); + } + + private: + void resize(size_t size); + size_t getLength(const char* input, bool &ascii); + void copyFromArray(unsigned char chp, char* &out); + size_t getLength2(const char* input, bool &ascii); + void copyFromArray2(const char*& chp, char* &out); + + FromType mEncoding; + std::vector mOutput; + int mSize; + char* translationArray; + }; } #endif From 67273fc1777b45d9860fe114689cbdc9836c1f48 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 2 Jan 2013 23:39:21 +0100 Subject: [PATCH 115/147] mwiniimporter: use Utf8Encoder --- apps/mwiniimporter/importer.cpp | 12 +++--------- apps/mwiniimporter/importer.hpp | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index def70615bc..5c3dedd047 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -649,11 +649,13 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string section(""); MwIniImporter::multistrmap map; boost::iostreams::streamfile(filename.c_str()); + ToUTF8::Utf8Encoder encoder; + encoder.setEncoding(mEncoding); std::string line; while (std::getline(file, line)) { - line = toUTF8(line); + line = encoder.getUtf8(line); // unify Unix-style and Windows file ending if (!(line.empty()) && (line[line.length()-1]) == '\r') { @@ -829,14 +831,6 @@ void MwIniImporter::writeToFile(boost::iostreams::stream #include -#include "../../components/to_utf8/to_utf8.hpp" +#include class MwIniImporter { public: From 9906c3051dddaaf435d55841f5e9d0d392d575c5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 15:01:08 +0100 Subject: [PATCH 116/147] components/translation: use Utf8Encoder --- components/translation/translation.cpp | 6 ++---- components/translation/translation.hpp | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index fb5b038612..002446e4f9 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -50,10 +50,7 @@ namespace Translation if (!line.empty()) { - char* buffer = ToUTF8::getBuffer(line.size() + 1); - //buffer has at least line.size() + 1 bytes, so it must be safe - strcpy(buffer, line.c_str()); - line = ToUTF8::getUtf8(mEncoding); + line = mEncoder.getUtf8(line); size_t tab_pos = line.find('\t'); if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) @@ -107,6 +104,7 @@ namespace Translation void Storage::setEncoding (const ToUTF8::FromType& encoding) { mEncoding = encoding; + mEncoder.setEncoding(encoding); } bool Storage::hasTranslation() const diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 80d44d871d..6c3e4df868 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -35,6 +35,7 @@ namespace Translation ToUTF8::FromType mEncoding; + ToUTF8::Utf8Encoder mEncoder; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } From 02bf02f288c407ad639ad15c8bc3a72ae56597b4 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 21:15:18 +0100 Subject: [PATCH 117/147] ESMReader, ESMWriter: use Utf8Encoder --- components/esm/esmreader.cpp | 22 +++++++++++++++++++--- components/esm/esmreader.hpp | 6 ++++++ components/esm/esmwriter.cpp | 8 +++----- components/esm/esmwriter.hpp | 1 + 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 580e006dfe..5cd99a64a2 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -13,6 +13,11 @@ ESM_Context ESMReader::getContext() return mCtx; } +ESMReader::ESMReader(void): + mBuffer(50*1024) +{ +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -325,11 +330,21 @@ void ESMReader::getExact(void*x, int size) std::string ESMReader::getString(int size) { - char *ptr = ToUTF8::getBuffer(size); - mEsm->read(ptr, size); + size_t s = size; + if (mBuffer.size() <= s) + // Add some extra padding to reduce the chance of having to resize + // again later. + mBuffer.resize(3*s); + + // And make sure the string is zero terminated + mBuffer[s] = 0; + + // read ESM data + char *ptr = &mBuffer[0]; + getExact(ptr, size); // Convert to UTF8 and return - return ToUTF8::getUtf8(mEncoding); + return mEncoder.getUtf8(ptr, size); } void ESMReader::fail(const std::string &msg) @@ -350,6 +365,7 @@ void ESMReader::fail(const std::string &msg) void ESMReader::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; + mEncoder.setEncoding(encoding); } } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 1d0f6f5806..57503aea77 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -20,6 +20,8 @@ class ESMReader { public: + ESMReader(void); + /************************************************************************* * * Public type definitions @@ -244,9 +246,13 @@ private: // Special file signifier (see SpecialFile enum above) int mSpf; + // Buffer for ESM strings + std::vector mBuffer; + SaveData mSaveData; MasterList mMasters; ToUTF8::FromType mEncoding; + ToUTF8::Utf8Encoder mEncoder; }; } #endif diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index c1ae064903..a00c7971d0 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -157,12 +157,8 @@ void ESMWriter::writeHString(const std::string& data) write("\0", 1); else { - char *ptr = ToUTF8::getBuffer(data.size()+1); - strncpy(ptr, &data[0], data.size()); - ptr[data.size()] = '\0'; - // Convert to UTF8 and return - std::string ascii = ToUTF8::getLegacyEnc(m_encoding); + std::string ascii = m_encoder.getLegacyEnc(data); write(ascii.c_str(), ascii.size()); } @@ -207,6 +203,8 @@ void ESMWriter::setEncoding(const std::string& encoding) // Default Latin encoding m_encoding = ToUTF8::WINDOWS_1252; } + + m_encoder.setEncoding(m_encoding); } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index d3777be813..20bc5da128 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -95,6 +95,7 @@ private: std::ostream* m_stream; std::streampos m_headerPos; ToUTF8::FromType m_encoding; + ToUTF8::Utf8Encoder m_encoder; int m_recordCount; HEDRstruct m_header; From 0bdf52a0719a756f3be97a988ff33784d02f7fcc Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 23:21:14 +0100 Subject: [PATCH 118/147] components/to_utf8: keep only Utf8Encoder --- components/to_utf8/to_utf8.cpp | 314 --------------------------------- components/to_utf8/to_utf8.hpp | 9 - 2 files changed, 323 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 8ac582b81d..5efec36a4d 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -41,13 +41,6 @@ // Generated tables #include "tables_gen.hpp" -// Shared global buffers, we love you. These initial sizes are large -// enough to hold the largest books in Morrowind.esm, but we will -// resize automaticall if necessary. -static std::vector buf (50*1024); -static std::vector output (50*1024); -static int size; - using namespace ToUTF8; Utf8Encoder::Utf8Encoder(void): @@ -330,313 +323,6 @@ void Utf8Encoder::copyFromArray2(const char*& chp, char* &out) *(out++) = ch; // Could not find glyph, just put whatever } -static void resize(std::vector &buf, size_t size) -{ - if(buf.size() <= size) - // Add some extra padding to reduce the chance of having to resize - // again later. - buf.resize(3*size); - - // And make sure the string is zero terminated - buf[size] = 0; -} - -// This is just used to spew out a reusable input buffer for the -// conversion process. -char *ToUTF8::getBuffer(int s) -{ - // Remember the requested size - size = s; - resize(buf, size); - return &buf[0]; -} - -/** Get the total length length needed to decode the given string with - the given translation array. The arrays are encoded with 6 bytes - per character, with the first giving the length and the next 5 the - actual data. - - The function serves a dual purpose for optimization reasons: it - checks if the input is pure ascii (all values are <= 127). If this - is the case, then the ascii parameter is set to true, and the - caller can optimize for this case. - */ -static size_t getLength(const char *arr, const char* input, bool &ascii) -{ - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) - { - ascii = false; - while(inp) - { - // Find the translated length of this character in the - // lookup table. - len += arr[inp*6]; - inp = *(++ptr); - } - } - return len; -} - -// Translate one character 'ch' using the translation array 'arr', and -// advance the output pointer accordingly. -static void copyFromArray(const char *arr, unsigned char ch, char* &out) -{ - // Optimize for ASCII values - if(ch < 128) - { - *(out++) = ch; - return; - } - - const char *in = arr + ch*6; - int len = *(in++); - for(int i=0; i outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); -} - -static size_t getLength2(const char *arr, const char* input, bool &ascii) -{ - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) - { - ascii = false; - while(inp) - { - len += 1; - // Find the translated length of this character in the - // lookup table. - switch(inp) - { - case 0xe2: len -= 2; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len -= 1; break; - } - - inp = *(++ptr); - } - } - return len; -} - -static void copyFromArray2(const char *arr, char*& chp, char* &out) -{ - unsigned char ch = *(chp++); - // Optimize for ASCII values - if(ch < 128) - { - *(out++) = ch; - return; - } - - int len = 1; - switch (ch) - { - case 0xe2: len = 3; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len = 2; break; - } - - if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) - { - *(out++) = ch; - return; - } - - unsigned char ch2 = *(chp++); - unsigned char ch3 = '\0'; - if (len == 3) - ch3 = *(chp++); - - for (int i = 128; i < 256; i++) - { - unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; - if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) - { - *(out++) = (char)i; - return; - } - } - - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - - *(out++) = ch; // Could not find glyph, just put whatever -} - -std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) -{ - // Pick translation array - const char *arr; - switch (to) - { - case ToUTF8::WINDOWS_1252: - { - arr = ToUTF8::windows_1252; - break; - } - case ToUTF8::WINDOWS_1250: - { - arr = ToUTF8::windows_1250; - break; - } - case ToUTF8::WINDOWS_1251: - { - arr = ToUTF8::windows_1251; - break; - } - default: - { - assert(0); - } - } - - // Double check that the input string stops at some point (it might - // contain zero terminators before this, inside its own data, which - // is also ok.) - char* input = &buf[0]; - assert(input[size] == 0); - - // TODO: The rest of this function is designed for single-character - // input encodings only. It also assumes that the input the input - // encoding shares its first 128 values (0-127) with ASCII. These - // conditions must be checked again if you add more input encodings - // later. - - // Compute output length, and check for pure ascii input at the same - // time. - bool ascii; - size_t outlen = getLength2(arr, input, ascii); - - // If we're pure ascii, then don't bother converting anything. - if(ascii) - return std::string(input, outlen); - - // Make sure the output is large enough - resize(output, outlen); - char *out = &output[0]; - - // Translate - while(*input) - copyFromArray2(arr, input, out); - - // Make sure that we wrote the correct number of bytes - assert((out-&output[0]) == (int)outlen); - - // And make extra sure the output is null terminated - assert(output.size() > outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); -} - ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) { if (encodingName == "win1250") diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 6877e2dc17..bfba8a1ac4 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -16,15 +16,6 @@ namespace ToUTF8 // probably others) }; - // Return a writable buffer of at least 'size' bytes. The buffer - // does not have to be freed. - char* getBuffer(int size); - - // Convert the previously written buffer to UTF8 from the given code - // page. - std::string getUtf8(FromType from); - std::string getLegacyEnc(FromType to); - FromType calculateEncoding(const std::string& encodingName); std::string encodingUsingMessage(const std::string& encodingName); From 019893b5c677c1fd605e106cb22295805f2e019a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 3 Jan 2013 23:44:58 -0800 Subject: [PATCH 119/147] Get rid of some unnecessary case-insensitive compares --- components/nifogre/ogre_nif_loader.cpp | 21 ++++++++------------- components/nifogre/ogre_nif_loader.hpp | 17 ----------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ad51d50b95..b3ceb089e7 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -43,6 +43,7 @@ #include +#include #include #include @@ -219,7 +220,7 @@ struct KeyTimeSort }; -typedef std::map LoaderMap; +typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -918,7 +919,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } - typedef std::map LoaderMap; + typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -1101,13 +1102,6 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke return entitylist; } -struct checklow { - bool operator()(const char &a, const char &b) const - { - return ::tolower(a) == ::tolower(b); - } -}; - EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, const std::string &name, @@ -1120,18 +1114,19 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo return entitylist; Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "Tri "+bonename; + std::string filter = "tri "+bonename; + std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); for(size_t i = 0;i < meshes.size();i++) { Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); if(ent->hasSkeleton()) { + std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); + if(meshes[i].second.length() < filter.length() || - std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) + meshes[i].second.compare(0, filter.length(), filter) != 0) { sceneMgr->destroyEntity(ent); - meshes.erase(meshes.begin()+i); - i--; continue; } if(!entitylist.mSkelBase) diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index a203112b50..08a233245e 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -30,23 +30,6 @@ #include #include -#include -#include - -#include "../nif/node.hpp" - -#include - -class BoundsFinder; - -struct ciLessBoost : std::binary_function -{ - bool operator() (const std::string & s1, const std::string & s2) const - { - //case insensitive version of is_less - return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless()); - } -}; namespace Nif { From 61ad8bb3ddcc1ec2a599a34a9a63d6707faaaae8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 00:14:41 -0800 Subject: [PATCH 120/147] Use a list of mesh names instead of mesh objects --- components/nifogre/ogre_nif_loader.cpp | 6 +++--- components/nifogre/ogre_nif_loader.hpp | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index b3ceb089e7..9e17736ef7 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1005,7 +1005,7 @@ public: mesh->setAutoBuildEdgeLists(false); } - meshes.push_back(std::make_pair(mesh, shape->name)); + meshes.push_back(std::make_pair(mesh->getName(), shape->name)); } else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && node->recType != Nif::RC_NiRotatingParticles) @@ -1072,7 +1072,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke Ogre::SceneManager *sceneMgr = parent->getCreator(); for(size_t i = 0;i < meshes.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first)); Ogre::Entity *entity = entitylist.mEntities.back(); if(!entitylist.mSkelBase && entity->hasSkeleton()) entitylist.mSkelBase = entity; @@ -1118,7 +1118,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); for(size_t i = 0;i < meshes.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); if(ent->hasSkeleton()) { std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 08a233245e..74b6f44df8 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -52,9 +52,8 @@ struct EntityList { }; -/** This holds a list of meshes along with the names of their parent nodes - */ -typedef std::vector< std::pair > MeshPairList; +/** This holds a list of mesh names along with the names of their parent nodes */ +typedef std::vector< std::pair > MeshPairList; /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into From c947d87ab9e9510e322d8cb030f42b64f6f07dc4 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 4 Jan 2013 15:10:30 +0100 Subject: [PATCH 121/147] Add a test for to_utf8 component --- components/to_utf8/tests/.gitignore | 1 + .../to_utf8/tests/output/to_utf8_test.out | 4 ++ components/to_utf8/tests/test.sh | 18 ++++++ components/to_utf8/tests/to_utf8_test.cpp | 61 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 components/to_utf8/tests/.gitignore create mode 100644 components/to_utf8/tests/output/to_utf8_test.out create mode 100755 components/to_utf8/tests/test.sh create mode 100644 components/to_utf8/tests/to_utf8_test.cpp diff --git a/components/to_utf8/tests/.gitignore b/components/to_utf8/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/components/to_utf8/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/components/to_utf8/tests/output/to_utf8_test.out b/components/to_utf8/tests/output/to_utf8_test.out new file mode 100644 index 0000000000..dcb32359ab --- /dev/null +++ b/components/to_utf8/tests/output/to_utf8_test.out @@ -0,0 +1,4 @@ +original: Без вопроÑов отдаете ему рулет, знаÑ, что позже вы Ñможете привеÑти Ñ Ñобой Ñвоих друзей и тогда он получит по заÑлугам? +converted: Без вопроÑов отдаете ему рулет, знаÑ, что позже вы Ñможете привеÑти Ñ Ñобой Ñвоих друзей и тогда он получит по заÑлугам? +original: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. +converted: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. diff --git a/components/to_utf8/tests/test.sh b/components/to_utf8/tests/test.sh new file mode 100755 index 0000000000..2d07708adc --- /dev/null +++ b/components/to_utf8/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp new file mode 100644 index 0000000000..8c25c483e0 --- /dev/null +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include "../to_utf8.hpp" + +std::string getFirstLine(const std::string &filename); +void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, + const std::string &utf8File); + +/// Test character encoding conversion to and from UTF-8 +void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, + const std::string &utf8File) +{ + // get some test data + std::string legacyEncLine = getFirstLine(legacyEncFile); + std::string utf8Line = getFirstLine(utf8File); + + // create an encoder for specified character encoding + ToUTF8::Utf8Encoder encoder; + encoder.setEncoding(encoding); + + // convert text to UTF-8 + std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine); + + std::cout << "original: " << utf8Line << std::endl; + std::cout << "converted: " << convertedUtf8Line << std::endl; + + // check correctness + assert(convertedUtf8Line == utf8Line); + + // convert UTF-8 text to legacy encoding + std::string convertedLegacyEncLine = encoder.getLegacyEnc(utf8Line); + // check correctness + assert(convertedLegacyEncLine == legacyEncLine); +} + +std::string getFirstLine(const std::string &filename) +{ + std::string line; + std::ifstream text (filename.c_str()); + + if (!text.is_open()) + { + throw std::runtime_error("Unable to open file " + filename); + } + + std::getline(text, line); + text.close(); + + return line; +} + +int main() +{ + testEncoder(ToUTF8::WINDOWS_1251, "data/russian-win1251.txt", "data/russian-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1252, "data/french-win1252.txt", "data/french-utf8.txt"); + return 0; +} From cc792da85895c85db8b76a5c9692bc260f35f649 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 4 Jan 2013 15:24:07 +0100 Subject: [PATCH 122/147] Fix to_utf8 test: add test data directory and remove unused include --- components/to_utf8/tests/test_data/french-utf8.txt | 1 + components/to_utf8/tests/test_data/french-win1252.txt | 1 + components/to_utf8/tests/test_data/russian-utf8.txt | 1 + components/to_utf8/tests/test_data/russian-win1251.txt | 1 + components/to_utf8/tests/to_utf8_test.cpp | 5 ++--- 5 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 components/to_utf8/tests/test_data/french-utf8.txt create mode 100644 components/to_utf8/tests/test_data/french-win1252.txt create mode 100644 components/to_utf8/tests/test_data/russian-utf8.txt create mode 100644 components/to_utf8/tests/test_data/russian-win1251.txt diff --git a/components/to_utf8/tests/test_data/french-utf8.txt b/components/to_utf8/tests/test_data/french-utf8.txt new file mode 100644 index 0000000000..aaaccac737 --- /dev/null +++ b/components/to_utf8/tests/test_data/french-utf8.txt @@ -0,0 +1 @@ +Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/french-win1252.txt b/components/to_utf8/tests/test_data/french-win1252.txt new file mode 100644 index 0000000000..1de4593e94 --- /dev/null +++ b/components/to_utf8/tests/test_data/french-win1252.txt @@ -0,0 +1 @@ +Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/russian-utf8.txt b/components/to_utf8/tests/test_data/russian-utf8.txt new file mode 100644 index 0000000000..eb20b32dd6 --- /dev/null +++ b/components/to_utf8/tests/test_data/russian-utf8.txt @@ -0,0 +1 @@ +Без вопроÑов отдаете ему рулет, знаÑ, что позже вы Ñможете привеÑти Ñ Ñобой Ñвоих друзей и тогда он получит по заÑлугам? \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/russian-win1251.txt b/components/to_utf8/tests/test_data/russian-win1251.txt new file mode 100644 index 0000000000..086e57edd6 --- /dev/null +++ b/components/to_utf8/tests/test_data/russian-win1251.txt @@ -0,0 +1 @@ +Áåç âîïðîñîâ îòäàåòå åìó ðóëåò, çíàÿ, ÷òî ïîçæå âû ñìîæåòå ïðèâåñòè ñ ñîáîé ñâîèõ äðóçåé è òîãäà îí ïîëó÷èò ïî çàñëóãàì? \ No newline at end of file diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp index 8c25c483e0..4bba0cf90d 100644 --- a/components/to_utf8/tests/to_utf8_test.cpp +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include "../to_utf8.hpp" @@ -55,7 +54,7 @@ std::string getFirstLine(const std::string &filename) int main() { - testEncoder(ToUTF8::WINDOWS_1251, "data/russian-win1251.txt", "data/russian-utf8.txt"); - testEncoder(ToUTF8::WINDOWS_1252, "data/french-win1252.txt", "data/french-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1251, "test_data/russian-win1251.txt", "test_data/russian-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1252, "test_data/french-win1252.txt", "test_data/french-utf8.txt"); return 0; } From 3c91f7793b29d2c9479b3ca15e4b327cd87e08f2 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 08:40:33 -0800 Subject: [PATCH 123/147] overrode MyGUI::DataManager::isDataExist Created a override of MyGUI::DataManager::isDataExist to fix a performance issue with MyGUI startup. This required moving the functionality of MyGUI::OgrePlatform into OEngine::GUI::MyGUIManager so that a new version of the MyGUI::DataManager could be created. --- libs/openengine/gui/manager.cpp | 47 +++++++++++++++++++++++++++------ libs/openengine/gui/manager.hpp | 11 +++++--- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index acb4ed9df7..925891e1b3 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -6,6 +6,19 @@ using namespace OEngine::GUI; +/* + * As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex + * this override fixes the resulting performance issue. + */ +class FixedOgreDataManager : public MyGUI::OgreDataManager +{ +public: + bool isDataExist(const std::string& _name) + { + return Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup (_name); + } +}; + void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { assert(wnd); @@ -25,11 +38,18 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool if(!logDir.empty()) theLogFile.insert(0, logDir); - // Set up OGRE platform. We might make this more generic later. - mPlatform = new OgrePlatform(); - LogManager::getInstance().setSTDOutputEnabled(logging); - mPlatform->initialise(wnd, mgr, "General", theLogFile); + // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. + mLogManager = new LogManager(); + mRenderManager = new OgreRenderManager(); + mDataManager = new FixedOgreDataManager(); + LogManager::getInstance().setSTDOutputEnabled(logging); + + if (!theLogFile.empty()) + LogManager::getInstance().createDefaultSource(theLogFile); + + mRenderManager->initialise(wnd, mgr); + mDataManager->initialise("General"); // Create GUI mGui = new Gui(); @@ -40,11 +60,22 @@ void MyGUIManager::shutdown() { mGui->shutdown (); delete mGui; - if(mPlatform) + if(mRenderManager) { - mPlatform->shutdown(); - delete mPlatform; + mRenderManager->shutdown(); + delete mRenderManager; + mRenderManager = NULL; + } + if(mDataManager) + { + mDataManager->shutdown(); + delete mDataManager; + mDataManager = NULL; + } + if (mLogManager) + { + delete mLogManager; + mLogManager = NULL; } mGui = NULL; - mPlatform = NULL; } diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 1ec2e2fcf3..c0f98da88b 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -3,8 +3,10 @@ namespace MyGUI { - class OgrePlatform; class Gui; + class LogManager; + class OgreDataManager; + class OgreRenderManager; } namespace Ogre @@ -18,12 +20,15 @@ namespace GUI { class MyGUIManager { - MyGUI::OgrePlatform *mPlatform; MyGUI::Gui *mGui; + MyGUI::LogManager* mLogManager; + MyGUI::OgreDataManager* mDataManager; + MyGUI::OgreRenderManager* mRenderManager; Ogre::SceneManager* mSceneMgr; + public: - MyGUIManager() : mPlatform(NULL), mGui(NULL) {} + MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); From 218139351830d00b3b564b13ab7d16276b99c6d3 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 11:36:22 -0800 Subject: [PATCH 124/147] change texture renaming logic to increase performance ResourceGroupManager::resourceExistsInAnyGroup is slow (at least on windows) if the tested path does not exist, but is fast if it does (due to finding it in the index). This change tries the '.dds' version of the name first, and reverts to the original if the '.dds' version was not found. --- components/nifogre/ogre_nif_loader.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ad51d50b95..dca459119b 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -536,11 +536,23 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ - texName = "textures\\" + st->filename; - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + + static const char * path = "textures\\"; + + texName = path + st->filename; + + Ogre::String::size_type pos = texName.rfind('.'); + + if (texName.compare (pos, texName.size () - pos, ".dds") != 0) { - Ogre::String::size_type pos = texName.rfind('.'); + // since we know all (GOTY edition or less) textures end + // in .dds, we change the extension texName.replace(pos, texName.length(), ".dds"); + + // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) + // verify, and revert if false (this call succeeds quickly, but fails slowly) + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + texName = path + st->filename; } } else warn("Found internal texture, ignoring."); From dcfa902525ef76cfab2426c03965a6a0291b7c53 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 13:08:09 -0800 Subject: [PATCH 125/147] fixed error in cmake when disabling esmtool --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36e45d78a2..766167672e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -534,7 +534,9 @@ if (WIN32) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif (BUILD_LAUNCHER) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) - set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) + if (BUILD_ESMTOOL) + set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) + endif (BUILD_ESMTOOL) endif(MSVC) # Same for MinGW From 5c7f1bd497d620e1d219b485eebb6fb9f8c598ab Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 12:55:39 -0800 Subject: [PATCH 126/147] moved ConstrainedDataStream into its own file moved the ConstrainedDataStream into its own source file and changed BSAFile to use it though the exposed factory function. This is in preperation foreimplementing it based on feedback from profiling that (at least on windows) the C++ iostreams library is quite slow. --- components/CMakeLists.txt | 2 +- components/bsa/bsa_file.cpp | 89 +------------- .../files/constrainedfiledatastream.cpp | 112 ++++++++++++++++++ .../files/constrainedfiledatastream.hpp | 8 ++ 4 files changed, 126 insertions(+), 85 deletions(-) create mode 100644 components/files/constrainedfiledatastream.cpp create mode 100644 components/files/constrainedfiledatastream.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f3d5f4aee9..f54efab226 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin + filelibrary ogreplugin constrainedfiledatastream ) add_component_dir (compiler diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 1700e1aabb..5e529e18e8 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -23,94 +23,15 @@ #include "bsa_file.hpp" -#include -#include -#include +//#include +//#include +//#include -#include +#include "../files/constrainedfiledatastream.hpp" using namespace std; using namespace Bsa; -class ConstrainedDataStream : public Ogre::DataStream { - std::ifstream mStream; - const size_t mStart; - size_t mPos; - bool mIsEOF; - -public: - ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) - : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } - - ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, - size_t start, size_t length) - : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), - mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } - - - virtual size_t read(void *buf, size_t count) - { - mStream.clear(); - - if(count > mSize-mPos) - { - count = mSize-mPos; - mIsEOF = true; - } - mStream.read(reinterpret_cast(buf), count); - - count = mStream.gcount(); - mPos += count; - return count; - } - - virtual void skip(long count) - { - if((count >= 0 && (size_t)count <= mSize-mPos) || - (count < 0 && (size_t)-count <= mPos)) - { - mStream.clear(); - if(mStream.seekg(count, std::ios_base::cur)) - { - mPos += count; - mIsEOF = false; - } - } - } - - virtual void seek(size_t pos) - { - if(pos < mSize) - { - mStream.clear(); - if(mStream.seekg(pos+mStart, std::ios_base::beg)) - { - mPos = pos; - mIsEOF = false; - } - } - } - - virtual size_t tell() const - { return mPos; } - - virtual bool eof() const - { return mIsEOF; } - - virtual void close() - { mStream.close(); } -}; - /// Error handling void BSAFile::fail(const string &msg) @@ -253,5 +174,5 @@ Ogre::DataStreamPtr BSAFile::getFile(const char *file) fail("File not found: " + string(file)); const FileStruct &fs = files[i]; - return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize)); + return openConstrainedFileDataStream (filename.c_str (), fs.offset, fs.fileSize); } diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp new file mode 100644 index 0000000000..037ffe6f09 --- /dev/null +++ b/components/files/constrainedfiledatastream.cpp @@ -0,0 +1,112 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008-2010 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.sourceforge.net/ + + This file (bsa_file.cpp) is part of the OpenMW package. + + OpenMW is distributed as free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License + version 3, as published by the Free Software Foundation. + + This program 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 GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + version 3 along with this program. If not, see + http://www.gnu.org/licenses/ . + + */ + +#include "constrainedfiledatastream.hpp" + +#include + +class ConstrainedDataStream : public Ogre::DataStream { + std::ifstream mStream; + const size_t mStart; + size_t mPos; + bool mIsEOF; + +public: + ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) + : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, + size_t start, size_t length) + : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), + mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + + virtual size_t read(void *buf, size_t count) + { + mStream.clear(); + + if(count > mSize-mPos) + { + count = mSize-mPos; + mIsEOF = true; + } + mStream.read(reinterpret_cast(buf), count); + + count = mStream.gcount(); + mPos += count; + return count; + } + + virtual void skip(long count) + { + if((count >= 0 && (size_t)count <= mSize-mPos) || + (count < 0 && (size_t)-count <= mPos)) + { + mStream.clear(); + if(mStream.seekg(count, std::ios_base::cur)) + { + mPos += count; + mIsEOF = false; + } + } + } + + virtual void seek(size_t pos) + { + if(pos < mSize) + { + mStream.clear(); + if(mStream.seekg(pos+mStart, std::ios_base::beg)) + { + mPos = pos; + mIsEOF = false; + } + } + } + + virtual size_t tell() const + { return mPos; } + + virtual bool eof() const + { return mIsEOF; } + + virtual void close() + { mStream.close(); } +}; + +Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length) +{ + assert (length != 0xFFFFFFFF); // reserved for future use... + + return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length)); +} diff --git a/components/files/constrainedfiledatastream.hpp b/components/files/constrainedfiledatastream.hpp new file mode 100644 index 0000000000..367defcbcb --- /dev/null +++ b/components/files/constrainedfiledatastream.hpp @@ -0,0 +1,8 @@ +#ifndef COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP +#define COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP + +#include + +Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset = 0, size_t length = 0xFFFFFFFF); + +#endif // COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP From 278337116b93e62e7458e03463e83c2055eb15e7 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 15:07:33 -0800 Subject: [PATCH 127/147] increased performance of ConstrainedDataStream Reimplemented ConstrainedDataStream to use low-level IO calls and a custom buffering scheme to avoid using C++ iostreams. --- components/CMakeLists.txt | 2 +- .../files/constrainedfiledatastream.cpp | 228 ++++++++----- components/files/lowlevelfile.cpp | 299 ++++++++++++++++++ components/files/lowlevelfile.hpp | 54 ++++ 4 files changed, 497 insertions(+), 86 deletions(-) create mode 100644 components/files/lowlevelfile.cpp create mode 100644 components/files/lowlevelfile.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f54efab226..3da09ecb8f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin constrainedfiledatastream + filelibrary ogreplugin constrainedfiledatastream lowlevelfile ) add_component_dir (compiler diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 037ffe6f09..2f75ce41bf 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -1,112 +1,170 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (bsa_file.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program 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 GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - #include "constrainedfiledatastream.hpp" +#include "lowlevelfile.hpp" #include +#include + +namespace { class ConstrainedDataStream : public Ogre::DataStream { - std::ifstream mStream; - const size_t mStart; - size_t mPos; - bool mIsEOF; - public: + + static const size_t sBufferSize = 4096; // somewhat arbitrary though 64KB buffers didn't seem to improve performance any + static const size_t sBufferThreshold = 1024; // reads larger than this bypass buffering as cost of memcpy outweighs cost of system call + ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) - : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) + { + mFile.open (fname.c_str ()); + mSize = length != 0xFFFFFFFF ? length : mFile.size () - start; + + mPos = 0; + mOrigin = start; + mExtent = start + mSize; + + mBufferOrigin = 0; + mBufferExtent = 0; + } + + + size_t read(void* buf, size_t count) + { + assert (mPos <= mSize); + + uint8_t * out = reinterpret_cast (buf); + + size_t posBeg = mOrigin + mPos; + size_t posEnd = posBeg + count; + + if (posEnd > mExtent) + posEnd = mExtent; + + size_t posCur = posBeg; + + while (posCur != posEnd) + { + size_t readLeft = posEnd - posCur; + + if (posCur < mBufferOrigin || posCur >= mBufferExtent) + { + if (readLeft >= sBufferThreshold || (posCur == mOrigin && posEnd == mExtent)) + { + assert (mFile.tell () == mBufferExtent); + + if (posCur != mBufferExtent) + mFile.seek (posCur); + + posCur += mFile.read (out, readLeft); + + mBufferOrigin = mBufferExtent = posCur; + + mPos = posCur - mOrigin; + + return posCur - posBeg; + } + else + { + size_t newBufferOrigin; + + if ((posCur < mBufferOrigin) && (mBufferOrigin - posCur < sBufferSize)) + newBufferOrigin = std::max (mOrigin, mBufferOrigin > sBufferSize ? mBufferOrigin - sBufferSize : 0); + else + newBufferOrigin = posCur; + + fill (newBufferOrigin); + } + } + + size_t xfer = std::min (readLeft, mBufferExtent - posCur); + + memcpy (out, mBuffer + (posCur - mBufferOrigin), xfer); + + posCur += xfer; + out += xfer; + } + + count = posEnd - posBeg; + mPos += count; + return count; + } + + void skip(long count) { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } + assert (mPos <= mSize); - ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, - size_t start, size_t length) - : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), - mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } - - - virtual size_t read(void *buf, size_t count) - { - mStream.clear(); - - if(count > mSize-mPos) - { - count = mSize-mPos; - mIsEOF = true; - } - mStream.read(reinterpret_cast(buf), count); - - count = mStream.gcount(); - mPos += count; - return count; - } - - virtual void skip(long count) - { if((count >= 0 && (size_t)count <= mSize-mPos) || (count < 0 && (size_t)-count <= mPos)) - { - mStream.clear(); - if(mStream.seekg(count, std::ios_base::cur)) - { - mPos += count; - mIsEOF = false; - } - } + mPos += count; } - virtual void seek(size_t pos) + void seek(size_t pos) { - if(pos < mSize) - { - mStream.clear(); - if(mStream.seekg(pos+mStart, std::ios_base::beg)) - { - mPos = pos; - mIsEOF = false; - } - } + assert (mPos <= mSize); + + if (pos < mSize) + mPos = pos; } virtual size_t tell() const - { return mPos; } + { + assert (mPos <= mSize); + + return mPos; + } virtual bool eof() const - { return mIsEOF; } + { + assert (mPos <= mSize); + + return mPos == mSize; + } virtual void close() - { mStream.close(); } + { + mFile.close(); + } + +private: + + void fill (size_t newOrigin) + { + assert (mFile.tell () == mBufferExtent); + + size_t newExtent = newOrigin + sBufferSize; + + if (newExtent > mExtent) + newExtent = mExtent; + + size_t oldExtent = mBufferExtent; + + if (newOrigin != oldExtent) + mFile.seek (newOrigin); + + mBufferOrigin = mBufferExtent = newOrigin; + + size_t amountRequested = newExtent - newOrigin; + + size_t amountRead = mFile.read (mBuffer, amountRequested); + + if (amountRead != amountRequested) + throw std::runtime_error ("An unexpected condition occurred while reading from a file."); + + mBufferExtent = newExtent; + } + + LowLevelFile mFile; + + size_t mOrigin; + size_t mExtent; + size_t mPos; + + uint8_t mBuffer [sBufferSize]; + size_t mBufferOrigin; + size_t mBufferExtent; }; +} // end of unnamed namespace + Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length) { - assert (length != 0xFFFFFFFF); // reserved for future use... - return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length)); } diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp new file mode 100644 index 0000000000..71fd1b523c --- /dev/null +++ b/components/files/lowlevelfile.cpp @@ -0,0 +1,299 @@ +#include "lowlevelfile.hpp" + +#include +#include +#include + +#if FILE_API == FILE_API_POSIX +#include +#include +#include +#endif + +#if FILE_API == FILE_API_STDIO +/* + * + * Implementation of LowLevelFile methods using c stdio + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = NULL; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle != NULL) + fclose (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == NULL); + + mHandle = fopen (filename, "rb"); + + if (mHandle == NULL) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } +} + +void LowLevelFile::close () +{ + assert (mHandle != NULL); + + fclose (mHandle); + + mHandle = NULL; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != NULL); + + long oldPosition = ftell (mHandle); + + if (oldPosition == -1) + throw std::runtime_error ("A query operation on a file failed."); + + if (fseek (mHandle, 0, SEEK_END) != 0) + throw std::runtime_error ("A query operation on a file failed."); + + long Size = ftell (mHandle); + + if (Size == -1) + throw std::runtime_error ("A query operation on a file failed."); + + if (fseek (mHandle, oldPosition, SEEK_SET) != 0) + throw std::runtime_error ("A query operation on a file failed."); + + return size_t (Size); +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != NULL); + + if (fseek (mHandle, Position, SEEK_SET) != 0) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != NULL); + + long Position = ftell (mHandle); + + if (Position == -1) + throw std::runtime_error ("A query operation on a file failed."); + + return size_t (Position); +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != NULL); + + int amount = fread (data, 1, size, mHandle); + + if (amount == 0 && ferror (mHandle)) + throw std::runtime_error ("A read operation on a file failed."); + + return amount; +} + +#elif FILE_API == FILE_API_POSIX +/* + * + * Implementation of LowLevelFile methods using posix IO calls + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = -1; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle != -1) + ::close (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == -1); + +#ifdef O_BINARY + static const int openFlags = O_RDONLY | O_BINARY; +#else + static const int openFlags = O_RDONLY; +#endif + + mHandle = ::open (filename, openFlags, 0); + + if (mHandle == -1) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } +} + +void LowLevelFile::close () +{ + assert (mHandle != -1); + + ::close (mHandle); + + mHandle = -1; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != -1); + + size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR); + + if (oldPosition == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + size_t Size = ::lseek (mHandle, 0, SEEK_END); + + if (Size == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + if (lseek (mHandle, oldPosition, SEEK_SET) == -1) + throw std::runtime_error ("A query operation on a file failed."); + + return Size; +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != -1); + + if (::lseek (mHandle, Position, SEEK_SET) == -1) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != -1); + + size_t Position = ::lseek (mHandle, 0, SEEK_CUR); + + if (Position == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + return Position; +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != -1); + + int amount = ::read (mHandle, data, size); + + if (amount == -1) + throw std::runtime_error ("A read operation on a file failed."); + + return amount; +} + +#elif FILE_API == FILE_API_WIN32 +/* + * + * Implementation of LowLevelFile methods using Win32 API calls + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = INVALID_HANDLE_VALUE; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle == INVALID_HANDLE_VALUE) + CloseHandle (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == INVALID_HANDLE_VALUE); + + HANDLE handle = CreateFileA (filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (handle == NULL) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } + + mHandle = handle; +} + +void LowLevelFile::close () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + CloseHandle (mHandle); + + mHandle = INVALID_HANDLE_VALUE; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + BY_HANDLE_FILE_INFORMATION info; + + if (!GetFileInformationByHandle (mHandle, &info)) + throw std::runtime_error ("A query operation on a file failed."); + + if (info.nFileSizeHigh != 0) + throw std::runtime_error ("Files greater that 4GB are not supported."); + + return info.nFileSizeLow; +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (GetLastError () != NO_ERROR) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); + + if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + throw std::runtime_error ("A query operation on a file failed."); + + return value; +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + DWORD read; + + if (!ReadFile (mHandle, data, size, &read, NULL)) + throw std::runtime_error ("A read operation on a file failed."); + + return read; +} + +#endif diff --git a/components/files/lowlevelfile.hpp b/components/files/lowlevelfile.hpp new file mode 100644 index 0000000000..a6de6df76d --- /dev/null +++ b/components/files/lowlevelfile.hpp @@ -0,0 +1,54 @@ +#ifndef COMPONENTS_FILES_LOWLEVELFILE_HPP +#define COMPONENTS_FILES_LOWLEVELFILE_HPP + +#include + +#define FILE_API_STDIO 0 +#define FILE_API_POSIX 1 +#define FILE_API_WIN32 2 + +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX +#define FILE_API FILE_API_POSIX +#elif OGRE_PLATFORM == OGRE_PLATFORM_WIN32 +#define FILE_API FILE_API_WIN32 +#else +#define FILE_API FILE_API_STDIO +#endif + +#if FILE_API == FILE_API_STDIO +#include +#elif FILE_API == FILE_API_POSIX +#elif FILE_API == FILE_API_WIN32 +#include +#else +#error Unsupported File API +#endif + +class LowLevelFile +{ +public: + + LowLevelFile (); + ~LowLevelFile (); + + void open (char const * filename); + void close (); + + size_t size (); + + void seek (size_t Position); + size_t tell (); + + size_t read (void * data, size_t size); + +private: +#if FILE_API == FILE_API_STDIO + FILE* mHandle; +#elif FILE_API == FILE_API_POSIX + int mHandle; +#elif FILE_API == FILE_API_WIN32 + HANDLE mHandle; +#endif +}; + +#endif From fec9a5923767747e9d845ac84cbadc60342e7da5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 15:13:36 -0800 Subject: [PATCH 128/147] changed EMSLoader to use ConstrainedDataStream Changed the EMSLoader class to use the ConstrainedDataStream so that future changes may benifit from its increased performance. --- components/esm/esmreader.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 580e006dfe..62078977e2 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -1,6 +1,8 @@ #include "esmreader.hpp" #include +#include "../files/constrainedfiledatastream.hpp" + namespace ESM { @@ -108,16 +110,12 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) void ESMReader::open(const std::string &file) { - std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us - open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); + open (openConstrainedFileDataStream (file.c_str ()), file); } void ESMReader::openRaw(const std::string &file) { - std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us - openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); + openRaw (openConstrainedFileDataStream (file.c_str ()), file); } int64_t ESMReader::getHNLong(const char *name) From b4d63814cc13bcd0ef9d46682b73b0f36dad2d0f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 4 Jan 2013 23:12:56 +0100 Subject: [PATCH 129/147] post merge fix --- components/files/lowlevelfile.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/files/lowlevelfile.hpp b/components/files/lowlevelfile.hpp index a6de6df76d..f49c466a51 100644 --- a/components/files/lowlevelfile.hpp +++ b/components/files/lowlevelfile.hpp @@ -3,6 +3,8 @@ #include +#include + #define FILE_API_STDIO 0 #define FILE_API_POSIX 1 #define FILE_API_WIN32 2 From 1d4d67f81153fc9a299158daa20b161bb9add69a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 15:03:57 -0800 Subject: [PATCH 130/147] Avoid underflows if the texture name doesn't include a '.' --- components/nifogre/ogre_nif_loader.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ae2a0ae159..93f00b1784 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -537,14 +537,11 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ - - static const char * path = "textures\\"; + static const char path[] = "textures\\"; texName = path + st->filename; - Ogre::String::size_type pos = texName.rfind('.'); - - if (texName.compare (pos, texName.size () - pos, ".dds") != 0) + if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) { // since we know all (GOTY edition or less) textures end // in .dds, we change the extension From b52904a6ea4f9acd5f76a89a1eecabd27fc5562c Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Fri, 4 Jan 2013 19:47:24 -0400 Subject: [PATCH 131/147] Include headers necessary for compilation under VS2010 --- components/files/constrainedfiledatastream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 2f75ce41bf..9e36f90758 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace { From a3d33db4153c26f17dfc632551e09c2b62226f29 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 15:55:47 -0800 Subject: [PATCH 132/147] Store a NiNode's NiTextKeyExtraData in a user object binding on Ogre::Bone --- components/nifogre/ogre_nif_loader.cpp | 78 +++++++++++++++++--------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 93f00b1784..badadfe011 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -49,10 +49,21 @@ typedef unsigned char ubyte; -using namespace std; -using namespace Nif; -using namespace NifOgre; +namespace Nif +{ +// These operators allow the Nif extra data types to be stored in an Ogre::Any +// object, which can then be stored in user object bindings on the nodes + +// TODO: Do something useful +std::ostream& operator<<(std::ostream &o, const Nif::NiTextKeyExtraData&) +{ return o; } + +} // namespace Nif + + +namespace NifOgre +{ // Helper class that computes the bounding box and of a mesh class BoundsFinder @@ -63,7 +74,7 @@ class BoundsFinder MaxMinFinder() { - min = numeric_limits::infinity(); + min = std::numeric_limits::infinity(); max = -min; } @@ -198,6 +209,17 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vectornext; } + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + bone->getUserObjectBindings().setUserAny(tk->recName, Ogre::Any(*tk)); + } + e = e->extra; + } + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { @@ -270,16 +292,16 @@ void loadResource(Ogre::Resource *resource) Nif::NiKeyframeData *kf = kfc->data.getPtr(); /* Get the keyframes and make sure they're sorted first to last */ - QuaternionKeyList quatkeys = kf->mRotations; - Vector3KeyList trankeys = kf->mTranslations; - FloatKeyList scalekeys = kf->mScales; + Nif::QuaternionKeyList quatkeys = kf->mRotations; + Nif::Vector3KeyList trankeys = kf->mTranslations; + Nif::FloatKeyList scalekeys = kf->mScales; std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort()); std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort()); std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort()); - QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); Ogre::Bone *bone = skel->getBone(targets[i]); const Ogre::Quaternion startquat = bone->getInitialOrientation(); @@ -347,7 +369,7 @@ void loadResource(Ogre::Resource *resource) kframe->setRotation(curquat); else { - QuaternionKeyList::VecType::const_iterator last = quatiter-1; + Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); } @@ -355,7 +377,7 @@ void loadResource(Ogre::Resource *resource) kframe->setTranslate(curtrans); else { - Vector3KeyList::VecType::const_iterator last = traniter-1; + Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); } @@ -363,7 +385,7 @@ void loadResource(Ogre::Resource *resource) kframe->setScale(curscale); else { - FloatKeyList::VecType::const_iterator last = scaleiter-1; + Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); kframe->setScale(lastscale + ((curscale-lastscale)*diff)); } @@ -485,7 +507,7 @@ static void fail(const std::string &msg) public: -static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) +static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -505,24 +527,24 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam bool vertexColour = (shape->data->colors.size() != 0); // These are set below if present - const NiTexturingProperty *t = NULL; - const NiMaterialProperty *m = NULL; - const NiAlphaProperty *a = NULL; + const Nif::NiTexturingProperty *t = NULL; + const Nif::NiMaterialProperty *m = NULL; + const Nif::NiAlphaProperty *a = NULL; // Scan the property list for material information - const PropertyList &list = shape->props; + const Nif::PropertyList &list = shape->props; for (size_t i = 0;i < list.length();i++) { // Entries may be empty if (list[i].empty()) continue; - const Property *pr = list[i].getPtr(); - if (pr->recType == RC_NiTexturingProperty) - t = static_cast(pr); - else if (pr->recType == RC_NiMaterialProperty) - m = static_cast(pr); - else if (pr->recType == RC_NiAlphaProperty) - a = static_cast(pr); + const Nif::Property *pr = list[i].getPtr(); + if (pr->recType == Nif::RC_NiTexturingProperty) + t = static_cast(pr); + else if (pr->recType == Nif::RC_NiMaterialProperty) + m = static_cast(pr); + else if (pr->recType == Nif::RC_NiAlphaProperty) + a = static_cast(pr); else warn("Skipped property type: "+pr->recName); } @@ -530,7 +552,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam // Texture if (t && t->textures[0].inUse) { - NiSourceTexture *st = t->textures[0].texture.getPtr(); + Nif::NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { /* Bethesda at some at some point converted all their BSA @@ -991,7 +1013,7 @@ public: if(node->recType == Nif::RC_NiTriShape) { - const NiTriShape *shape = dynamic_cast(node); + const Nif::NiTriShape *shape = dynamic_cast(node); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@shape="+shape->name; @@ -1220,3 +1242,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, */ + +} // nsmaepace NifOgre From 683ced54a09eefd8dc38f8d64341a30d289f45a2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 19:41:37 -0800 Subject: [PATCH 133/147] Store and retrieve the node text keys in the bones' user object bindings --- components/nifogre/ogre_nif_loader.cpp | 67 +++++++++++++------------- components/nifogre/ogre_nif_loader.hpp | 2 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index badadfe011..6ee02438a1 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -49,22 +49,20 @@ typedef unsigned char ubyte; -namespace Nif +namespace std { -// These operators allow the Nif extra data types to be stored in an Ogre::Any +// These operators allow extra data types to be stored in an Ogre::Any // object, which can then be stored in user object bindings on the nodes // TODO: Do something useful -std::ostream& operator<<(std::ostream &o, const Nif::NiTextKeyExtraData&) +ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) { return o; } -} // namespace Nif - +} namespace NifOgre { - // Helper class that computes the bounding box and of a mesh class BoundsFinder { @@ -164,8 +162,9 @@ static void fail(const std::string &msg) } -static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textkeys) +static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) { + TextKeyMap textkeys; for(size_t i = 0;i < tk->list.size();i++) { const std::string &str = tk->list[i].text; @@ -178,11 +177,12 @@ static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textke break; std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - textkeys->insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); + textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); pos = nextpos; } } + return textkeys; } @@ -215,7 +215,7 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vectorrecType == Nif::RC_NiTextKeyExtraData) { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - bone->getUserObjectBindings().setUserAny(tk->recName, Ogre::Any(*tk)); + bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk))); } e = e->extra; } @@ -394,35 +394,18 @@ void loadResource(Ogre::Resource *resource) anim->optimise(); } -bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) +bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) { - if(textkeys) - { - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - insertTextKeys(tk, textkeys); - } - e = e->extra; - } - } - if(node->boneTrafo != NULL) { Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(name); if(skel.isNull()) { NIFSkeletonLoader *loader = &sLoaders[name]; skel = skelMgr.create(name, group, true, loader); } - - if(!textkeys || textkeys->size() > 0) - return true; + return true; } const Nif::NiNode *ninode = dynamic_cast(node); @@ -433,7 +416,7 @@ bool createSkeleton(const std::string &name, const std::string &group, TextKeyMa { if(!children[i].empty()) { - if(createSkeleton(name, group, textkeys, children[i].getPtr())) + if(createSkeleton(name, group, children[i].getPtr())) return true; } } @@ -1057,7 +1040,7 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) +MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { MeshPairList meshes; @@ -1084,7 +1067,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); + bool hasSkel = skelldr.createSkeleton(skelName, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); @@ -1096,7 +1079,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke { EntityList entitylist; - MeshPairList meshes = load(name, name, textkeys, group); + MeshPairList meshes = load(name, name, group); if(meshes.size() == 0) return entitylist; @@ -1109,6 +1092,24 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke entitylist.mSkelBase = entity; } + if(entitylist.mSkelBase) + { + // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName()); + Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator(); + while(iter.hasMoreElements()) + { + Ogre::Bone *bone = iter.getNext(); + const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData"); + if(!data.isEmpty()) + { + *textkeys = Ogre::any_cast(data); + break; + } + } + } + if(entitylist.mSkelBase) { parent->attachObject(entitylist.mSkelBase); @@ -1140,7 +1141,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo { EntityList entitylist; - MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); if(meshes.size() == 0) return entitylist; diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 74b6f44df8..3e05c58734 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -69,7 +69,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); + static MeshPairList load(std::string name, std::string skelName, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, From 917bbc4e110672e8c0ef7bc8ea8d6282213fdeed Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Sat, 5 Jan 2013 00:26:33 -0400 Subject: [PATCH 134/147] Create static and non-discardable textures with the right parameters (should fix Issue 443) --- libs/openengine/ogre/imagerotate.cpp | 21 ++++++++++++++++++--- libs/openengine/ogre/renderer.cpp | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp index 11fd5eea6a..3dd5840785 100644 --- a/libs/openengine/ogre/imagerotate.cpp +++ b/libs/openengine/ogre/imagerotate.cpp @@ -16,6 +16,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest { Root* root = Ogre::Root::getSingletonPtr(); + std::string destImageRot = std::string(destImage) + std::string("_rot"); + SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); @@ -48,8 +50,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest unsigned int width = sourceTexture->getWidth(); unsigned int height = sourceTexture->getHeight(); - TexturePtr destTexture = TextureManager::getSingleton().createManual( - destImage, + TexturePtr destTextureRot = TextureManager::getSingleton().createManual( + destImageRot, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, width, height, @@ -57,7 +59,7 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest PF_FLOAT16_RGBA, TU_RENDERTARGET); - RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); + RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget(); rtt->setAutoUpdated(false); Viewport* vp = rtt->addViewport(camera); vp->setOverlaysEnabled(false); @@ -66,7 +68,20 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest rtt->update(); + //copy the rotated image to a static texture + TexturePtr destTexture = TextureManager::getSingleton().createManual( + destImage, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + width, height, + 0, + PF_FLOAT16_RGBA, + Ogre::TU_STATIC); + + destTexture->getBuffer()->blit(destTextureRot->getBuffer()); + // remove all the junk we've created + TextureManager::getSingleton().remove(destImageRot); MaterialManager::getSingleton().remove("ImageRotateMaterial"); root->destroySceneManager(sceneMgr); delete rect; diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 87ebe11397..3cdb005186 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -226,7 +226,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& 1, 1, 0, Ogre::PF_A8R8G8B8, - Ogre::TU_DYNAMIC_WRITE_ONLY); + Ogre::TU_WRITE_ONLY); } void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) From 3131e8dae6de127c4d3c8ed95c773b56442da92f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 20:33:26 -0800 Subject: [PATCH 135/147] Don't get the text keys if they're not being requested --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 6ee02438a1..27e4e7440f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1092,7 +1092,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke entitylist.mSkelBase = entity; } - if(entitylist.mSkelBase) + if(entitylist.mSkelBase && textkeys) { // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); From 8a086e3afbc510d70c66955c19a99d44b797bed7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 20:58:35 -0800 Subject: [PATCH 136/147] Cache the mesh names from the mesh/skeleton pairs --- components/nifogre/ogre_nif_loader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 27e4e7440f..31d873489c 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1040,13 +1040,19 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; +typedef std::map MeshPairMap; +static MeshPairMap sMeshPairMap; + MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { - MeshPairList meshes; - std::transform(name.begin(), name.end(), name.begin(), ::tolower); std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); + MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName); + if(meshiter != sMeshPairMap.end()) + return meshiter->second; + + MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; Nif::NIFFile nif(name); if (nif.numRecords() < 1) { @@ -1067,7 +1073,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, node); + bool hasSkel = skelldr.createSkeleton(name, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); From 3ed77ca1890b90c9c1d9fe7a4cddbfea1e06933b Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Sat, 5 Jan 2013 01:17:07 -0400 Subject: [PATCH 137/147] Include C++ header instead --- components/files/constrainedfiledatastream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 9e36f90758..63bd3490f6 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace { From 63f09462fd1395f1550df11eb400e0e81959c14b Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 6 Jan 2013 01:37:58 +0100 Subject: [PATCH 138/147] to_utf8, Utf8Encoder: pass encoding as constructor parameter Edit other files accordingly. --- apps/esmtool/esmtool.cpp | 25 ++++++++----------------- apps/launcher/model/datafilesmodel.cpp | 6 ++++-- apps/mwiniimporter/importer.cpp | 3 +-- apps/openmw/engine.cpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- components/esm/esmreader.cpp | 7 +++---- components/esm/esmreader.hpp | 7 +++---- components/esm/esmwriter.cpp | 20 +++----------------- components/esm/esmwriter.hpp | 8 +++----- components/to_utf8/to_utf8.cpp | 10 ++-------- components/to_utf8/to_utf8.hpp | 6 +----- components/translation/translation.cpp | 7 +++---- components/translation/translation.hpp | 5 ++--- 14 files changed, 41 insertions(+), 76 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 0cd6e39053..fbfc884d70 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -165,23 +165,12 @@ bool parseOptions (int argc, char** argv, Arguments &info) // Font encoding settings info.encoding = variables["encoding"].as(); - if (info.encoding == "win1250") + if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252") { - std::cout << "Using Central and Eastern European font encoding." << std::endl; - } - else if (info.encoding == "win1251") - { - std::cout << "Using Cyrillic font encoding." << std::endl; - } - else - { - if(info.encoding != "win1252") - { - std::cout << info.encoding << " is not a valid encoding option." << std::endl; - info.encoding = "win1252"; - } - std::cout << "Using default (English) font encoding." << std::endl; + std::cout << info.encoding << " is not a valid encoding option." << std::endl; + info.encoding = "win1252"; } + std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl; return true; } @@ -262,7 +251,8 @@ void printRaw(ESM::ESMReader &esm) int load(Arguments& info) { ESM::ESMReader& esm = info.reader; - esm.setEncoding(ToUTF8::calculateEncoding(info.encoding)); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); + esm.setEncoder(&encoder); std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; @@ -432,7 +422,8 @@ int clone(Arguments& info) std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl; ESM::ESMWriter& esm = info.writer; - esm.setEncoding(info.encoding); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); + esm.setEncoder(&encoder); esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); diff --git a/apps/launcher/model/datafilesmodel.cpp b/apps/launcher/model/datafilesmodel.cpp index 716c9e9026..e84dbe0acc 100644 --- a/apps/launcher/model/datafilesmodel.cpp +++ b/apps/launcher/model/datafilesmodel.cpp @@ -272,7 +272,8 @@ void DataFilesModel::addMasters(const QString &path) foreach (const QString &path, dir.entryList()) { try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); @@ -335,7 +336,8 @@ void DataFilesModel::addPlugins(const QString &path) try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 5c3dedd047..6a7274e0a1 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -649,8 +649,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string section(""); MwIniImporter::multistrmap map; boost::iostreams::streamfile(filename.c_str()); - ToUTF8::Utf8Encoder encoder; - encoder.setEncoding(mEncoding); + ToUTF8::Utf8Encoder encoder(mEncoding); std::string line; while (std::getline(file, line)) { diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e2d28a808d..aadeb7f3a2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -331,11 +331,15 @@ void OMW::Engine::go() // cursor replacer (converts the cursor from the bsa so they can be used by mygui) MWGui::CursorReplace replacer; + // Create encoder + ToUTF8::Utf8Encoder encoder (mEncoding); + // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap)); //Load translation data + mTranslationDataStorage.setEncoder(&encoder); mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); // Create window manager - this manages all the MW-specific GUI windows @@ -494,7 +498,6 @@ void OMW::Engine::showFPS(int level) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; - mTranslationDataStorage.setEncoding (encoding); } void OMW::Engine::setFallbackValues(std::map fallbackMap) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3995123b0e..4f60f6ef42 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const ToUTF8::FromType& encoding, std::map fallbackMap) + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNumFacing(0) @@ -187,7 +187,7 @@ namespace MWWorld std::cout << "Loading ESM " << masterPath.string() << "\n"; // This parses the ESM file and loads a sample cell - mEsm.setEncoding(encoding); + mEsm.setEncoder(encoder); mEsm.open (masterPath.string()); mStore.load (mEsm); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 71ae9597f8..d29adebf44 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const ToUTF8::FromType& encoding, std::map fallbackMap); + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap); virtual ~World(); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 5cd99a64a2..99f7971b18 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -344,7 +344,7 @@ std::string ESMReader::getString(int size) getExact(ptr, size); // Convert to UTF8 and return - return mEncoder.getUtf8(ptr, size); + return mEncoder->getUtf8(ptr, size); } void ESMReader::fail(const std::string &msg) @@ -362,10 +362,9 @@ void ESMReader::fail(const std::string &msg) throw std::runtime_error(ss.str()); } -void ESMReader::setEncoding(const ToUTF8::FromType& encoding) +void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder) { - mEncoding = encoding; - mEncoder.setEncoding(encoding); + mEncoder = encoder; } } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 57503aea77..d52be25aa2 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -235,8 +235,8 @@ public: /// Used for error handling void fail(const std::string &msg); - /// Sets font encoding for ESM strings - void setEncoding(const ToUTF8::FromType& encoding); + /// Sets font encoder for ESM strings + void setEncoder(ToUTF8::Utf8Encoder* encoder); private: Ogre::DataStreamPtr mEsm; @@ -251,8 +251,7 @@ private: SaveData mSaveData; MasterList mMasters; - ToUTF8::FromType mEncoding; - ToUTF8::Utf8Encoder mEncoder; + ToUTF8::Utf8Encoder* mEncoder; }; } #endif diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index a00c7971d0..e2f878a257 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -158,7 +158,7 @@ void ESMWriter::writeHString(const std::string& data) else { // Convert to UTF8 and return - std::string ascii = m_encoder.getLegacyEnc(data); + std::string ascii = m_encoder->getLegacyEnc(data); write(ascii.c_str(), ascii.size()); } @@ -188,23 +188,9 @@ void ESMWriter::write(const char* data, int size) m_stream->write(data, size); } -void ESMWriter::setEncoding(const std::string& encoding) +void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) { - if (encoding == "win1250") - { - m_encoding = ToUTF8::WINDOWS_1250; - } - else if (encoding == "win1251") - { - m_encoding = ToUTF8::WINDOWS_1251; - } - else - { - // Default Latin encoding - m_encoding = ToUTF8::WINDOWS_1252; - } - - m_encoder.setEncoding(m_encoding); + m_encoder = encoder; } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 20bc5da128..b557a29ad8 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -6,7 +6,7 @@ #include #include "esmcommon.hpp" -#include "../to_utf8/to_utf8.hpp" +#include namespace ESM { @@ -24,7 +24,7 @@ public: void setVersion(int ver); int getType(); void setType(int type); - void setEncoding(const std::string& encoding); // Write strings as UTF-8? + void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); @@ -94,12 +94,10 @@ private: std::list m_records; std::ostream* m_stream; std::streampos m_headerPos; - ToUTF8::FromType m_encoding; - ToUTF8::Utf8Encoder m_encoder; + ToUTF8::Utf8Encoder* m_encoder; int m_recordCount; HEDRstruct m_header; - SaveData m_saveData; }; } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 5efec36a4d..275f5483f3 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -43,16 +43,10 @@ using namespace ToUTF8; -Utf8Encoder::Utf8Encoder(void): +Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): mOutput(50*1024) { -} - -void Utf8Encoder::setEncoding(const FromType sourceEncoding) -{ - mEncoding = sourceEncoding; - - switch (mEncoding) + switch (sourceEncoding) { case ToUTF8::WINDOWS_1252: { diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index bfba8a1ac4..e150cf17ba 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -24,9 +24,7 @@ namespace ToUTF8 class Utf8Encoder { public: - Utf8Encoder(void); - - void setEncoding(const FromType sourceEncoding); + Utf8Encoder(FromType sourceEncoding); // Convert to UTF8 from the previously given code page. std::string getUtf8(const char *input, int size); @@ -48,9 +46,7 @@ namespace ToUTF8 size_t getLength2(const char* input, bool &ascii); void copyFromArray2(const char*& chp, char* &out); - FromType mEncoding; std::vector mOutput; - int mSize; char* translationArray; }; } diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 002446e4f9..184cf399cd 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -50,7 +50,7 @@ namespace Translation if (!line.empty()) { - line = mEncoder.getUtf8(line); + line = mEncoder->getUtf8(line); size_t tab_pos = line.find('\t'); if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) @@ -101,10 +101,9 @@ namespace Translation return phrase; } - void Storage::setEncoding (const ToUTF8::FromType& encoding) + void Storage::setEncoder(ToUTF8::Utf8Encoder* encoder) { - mEncoding = encoding; - mEncoder.setEncoding(encoding); + mEncoder = encoder; } bool Storage::hasTranslation() const diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 6c3e4df868..bca9ea255c 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -19,7 +19,7 @@ namespace Translation // Standard form usually means nominative case std::string topicStandardForm(const std::string& phrase) const; - void setEncoding (const ToUTF8::FromType& encoding); + void setEncoder(ToUTF8::Utf8Encoder* encoder); bool hasTranslation() const; @@ -34,8 +34,7 @@ namespace Translation void loadDataFromStream(ContainerType& container, std::istream& stream); - ToUTF8::FromType mEncoding; - ToUTF8::Utf8Encoder mEncoder; + ToUTF8::Utf8Encoder* mEncoder; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } From 0b7d11d38d99033a09bed6b22c75378f692653a5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 6 Jan 2013 11:39:18 +0100 Subject: [PATCH 139/147] to_utf8 test: fix Utf8Encoder constructor --- components/to_utf8/tests/to_utf8_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp index 4bba0cf90d..3fcddd1581 100644 --- a/components/to_utf8/tests/to_utf8_test.cpp +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -18,8 +18,7 @@ void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, std::string utf8Line = getFirstLine(utf8File); // create an encoder for specified character encoding - ToUTF8::Utf8Encoder encoder; - encoder.setEncoding(encoding); + ToUTF8::Utf8Encoder encoder (encoding); // convert text to UTF-8 std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine); From 1d3f3bcce30b211ba92b03fee3b7814042f0048c Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 6 Jan 2013 19:19:12 +0400 Subject: [PATCH 140/147] clang build fix --- components/files/constrainedfiledatastream.cpp | 4 ++++ components/translation/translation.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 63bd3490f6..dd2985e561 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,7 +3,11 @@ #include #include +#ifndef __clang__ #include +#else +#include +#endif namespace { diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index fb5b038612..37bceafbfe 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -30,7 +30,7 @@ namespace Translation { std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string(); - std::ifstream stream (path); + std::ifstream stream (path.c_str()); if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); From 0f3712f28435fdf2133d0c5c8eb22814349aed55 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 6 Jan 2013 10:45:54 -0800 Subject: [PATCH 141/147] change flickering light's brightness pattern --- apps/openmw/mwrender/objects.cpp | 134 +++++++++++++++++++------------ apps/openmw/mwrender/objects.hpp | 13 ++- 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 36f09e6d96..825ce2a142 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -235,8 +235,9 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f else info.type = LT_Normal; - // random starting phase for the animation - info.time = Ogre::Math::RangeRandom(0, 2 * Ogre::Math::PI); + // randomize lights animations + info.time = Ogre::Math::RangeRandom(-500, +500); + info.phase = Ogre::Math::RangeRandom(-500, +500); // adjust the lights depending if we're in an interior or exterior cell // quadratic means the light intensity falls off quite fast, resulting in a @@ -373,6 +374,48 @@ void Objects::disableLights() } } +namespace pulse +{ + static float frequency (float x) + { + return x; + } + + static float amplitude (float phase) + { + return sin (phase); + } +} + +namespace flicker +{ + static const float fa = 0.785398f; + static const float fb = 1.17024f; + + static const float tdo = 0.94f; + static const float tdm = 2.48f; + + static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m [3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; + + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + + static float frequency (float x) + { + return tdo + tdm * sin (fa * x); + } + + static float amplitude (float x) + { + float v = 0.0f; + for (int i = 0; i < 3; ++i) + v += sin (fb*x*f[i] + o[1])*m[i]; + return v * s; + } +} + void Objects::update(const float dt) { std::vector::iterator it = mLights.begin(); @@ -382,77 +425,64 @@ void Objects::update(const float dt) { Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); - // Light animation (pulse & flicker) - it->time += dt; - const float phase = std::fmod(static_cast (it->time), static_cast(32 * 2 * Ogre::Math::PI)) * 20; - float pulseConstant; + + float brightness; + float cycle_time; + float time_distortion; + + if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow)) + { + cycle_time = 2 * Ogre::Math::PI; + time_distortion = 20.0f; + } + else + { + cycle_time = 500.0f; + it->phase = fmod (it->phase + dt, flicker::phase_wavelength); + time_distortion = flicker::frequency (it->phase); + } + + it->time += it->dir*dt*time_distortion; + if (it->dir > 0 && it->time > +cycle_time) + { + it->dir = -1.0f; + it->time = +2*cycle_time - it->time; + } + if (it->dir < 0 && it->time < -cycle_time) + { + it->dir = +1.0f; + it->time = -2*cycle_time - it->time; + } + + static const float fast = 4.0f/1.0f; + static const float slow = 1.0f/1.0f; // These formulas are just guesswork, but they work pretty well if (it->type == LT_Normal) { // Less than 1/255 light modifier for a constant light: - pulseConstant = (const float)(1.0 + sin(phase) / 255.0 ); + brightness = (const float)(1.0 + flicker::amplitude(it->time*slow) / 255.0 ); } else if (it->type == LT_Flicker) { - // Let's do a 50% -> 100% sine wave pulse over 1 second: - // This is 75% +/- 25% - pulseConstant = (const float)(0.75 + sin(phase) * 0.25); - - // Then add a 25% flicker variation: - it->resetTime -= dt; - if (it->resetTime < 0) - { - it->flickerVariation = (rand() % 1000) / 1000 * 0.25; - it->resetTime = 0.5; - } - if (it->resetTime > 0.25) - { - pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime * 2.0f) + pulseConstant * it->resetTime * 2.0f; - } - else - { - pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime * 2.0f) + pulseConstant * (1-it->resetTime * 2.0f); - } + brightness = (const float)(0.75 + flicker::amplitude(it->time*fast) * 0.25); } else if (it->type == LT_FlickerSlow) { - // Let's do a 50% -> 100% sine wave pulse over 1 second: - // This is 75% +/- 25% - pulseConstant = (const float)(0.75 + sin(phase / 4.0) * 0.25); - - // Then add a 25% flicker variation: - it->resetTime -= dt; - if (it->resetTime < 0) - { - it->flickerVariation = (rand() % 1000) / 1000 * 0.25; - it->resetTime = 0.5; - } - if (it->resetTime > 0.5) - { - pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime) + pulseConstant * it->resetTime; - } - else - { - pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime) + pulseConstant * (1-it->resetTime); - } + brightness = (const float)(0.75 + flicker::amplitude(it->time*slow) * 0.25); } else if (it->type == LT_Pulse) { - // Let's do a 75% -> 125% sine wave pulse over 1 second: - // This is 100% +/- 25% - pulseConstant = (const float)(1.0 + sin(phase) * 0.25); + brightness = (const float)(1.0 + pulse::amplitude (it->time*fast) * 0.25); } else if (it->type == LT_PulseSlow) { - // Let's do a 75% -> 125% sine wave pulse over 1 second: - // This is 100% +/- 25% - pulseConstant = (const float)(1.0 + sin(phase / 4.0) * 0.25); + brightness = (const float)(1.0 + pulse::amplitude (it->time*slow) * 0.25); } else assert(0 && "Invalid light type"); - light->setDiffuseColour( it->colour * pulseConstant ); + light->setDiffuseColour(it->colour * brightness); ++it; } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 8594e4fe40..48632dba83 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -34,16 +34,13 @@ struct LightInfo LightType type; // Runtime variables - float flickerVariation; // 25% flicker variation, reset once every 0.5 seconds - float flickerSlowVariation; // 25% flicker variation, reset once every 1.0 seconds - float resetTime; - long double time; - + float dir; // direction time is running... + float time; // current time + float phase; // current phase LightInfo() : - flickerVariation(0), resetTime(0.5), - flickerSlowVariation(0), time(0), interior(true), - type(LT_Normal), radius(1.0) + dir(1.0f), time(0.0f), phase (0.0f), + interior(true), type(LT_Normal), radius(1.0) { } }; From 2557ef4d7d43e950b7f5db74ccde91fef35fa668 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Jan 2013 12:17:46 +0100 Subject: [PATCH 142/147] post merge fixes and some misc clean up --- apps/openmw/mwrender/objects.cpp | 77 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 825ce2a142..ee36126f8d 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -17,7 +17,7 @@ using namespace MWRender; -// These are the Morrowind.ini defaults +/// \todo Replace these, once fallback values from the ini file are available. float Objects::lightLinearValue = 3; float Objects::lightLinearRadiusMult = 1; @@ -31,7 +31,6 @@ int Objects::uniqueID = 0; void Objects::clearSceneNode (Ogre::SceneNode *node) { - /// \todo This should probably be moved into OpenEngine at some point. for (int i=node->numAttachedObjects()-1; i>=0; --i) { Ogre::MovableObject *object = node->getAttachedObject (i); @@ -374,45 +373,43 @@ void Objects::disableLights() } } -namespace pulse +namespace MWRender { - static float frequency (float x) + namespace Pulse { - return x; + static float amplitude (float phase) + { + return sin (phase); + } } - static float amplitude (float phase) + namespace Flicker { - return sin (phase); - } -} + static const float fa = 0.785398f; + static const float fb = 1.17024f; -namespace flicker -{ - static const float fa = 0.785398f; - static const float fb = 1.17024f; + static const float tdo = 0.94f; + static const float tdm = 2.48f; - static const float tdo = 0.94f; - static const float tdm = 2.48f; + static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m [3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; - static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; - static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; - static const float m [3] = { 1.0f, 0.785f, 0.876f }; - static const float s = 0.394f; + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; - static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + static float frequency (float x) + { + return tdo + tdm * sin (fa * x); + } - static float frequency (float x) - { - return tdo + tdm * sin (fa * x); - } - - static float amplitude (float x) - { - float v = 0.0f; - for (int i = 0; i < 3; ++i) - v += sin (fb*x*f[i] + o[1])*m[i]; - return v * s; + static float amplitude (float x) + { + float v = 0.0f; + for (int i = 0; i < 3; ++i) + v += sin (fb*x*f[i] + o[1])*m[i]; + return v * s; + } } } @@ -425,7 +422,6 @@ void Objects::update(const float dt) { Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); - float brightness; float cycle_time; float time_distortion; @@ -438,8 +434,8 @@ void Objects::update(const float dt) else { cycle_time = 500.0f; - it->phase = fmod (it->phase + dt, flicker::phase_wavelength); - time_distortion = flicker::frequency (it->phase); + it->phase = fmod (it->phase + dt, Flicker::phase_wavelength); + time_distortion = Flicker::frequency (it->phase); } it->time += it->dir*dt*time_distortion; @@ -461,23 +457,23 @@ void Objects::update(const float dt) if (it->type == LT_Normal) { // Less than 1/255 light modifier for a constant light: - brightness = (const float)(1.0 + flicker::amplitude(it->time*slow) / 255.0 ); + brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 ); } else if (it->type == LT_Flicker) { - brightness = (const float)(0.75 + flicker::amplitude(it->time*fast) * 0.25); + brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25); } else if (it->type == LT_FlickerSlow) { - brightness = (const float)(0.75 + flicker::amplitude(it->time*slow) * 0.25); + brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25); } else if (it->type == LT_Pulse) { - brightness = (const float)(1.0 + pulse::amplitude (it->time*fast) * 0.25); + brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25); } else if (it->type == LT_PulseSlow) { - brightness = (const float)(1.0 + pulse::amplitude (it->time*slow) * 0.25); + brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25); } else assert(0 && "Invalid light type"); @@ -506,8 +502,7 @@ void Objects::rebuildStaticGeometry() } } -void -Objects::updateObjectCell(const MWWorld::Ptr &ptr) +void Objects::updateObjectCell(const MWWorld::Ptr &ptr) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = ptr.getCell(); From 35f4d09288aa703cae11a4c04e8a039f978fc378 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jan 2013 13:06:16 +0100 Subject: [PATCH 143/147] swscale handled better (cmake) --- CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 5 ----- cmake/FindFFmpeg.cmake | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 182cc268af..148f65bddb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,7 @@ if (USE_FFMPEG) find_package(FFmpeg) if (FFMPEG_FOUND) set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) set(GOT_SOUND_INPUT 1) endif (FFMPEG_FOUND) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57e81e5ee1..4f290c46fa 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -110,11 +110,6 @@ target_link_libraries(openmw components ) -if (USE_FFMPEG) - target_link_libraries(openmw - "swscale") -endif() - # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index 526be5f1be..c80203a25b 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -32,7 +32,7 @@ include(FindPackageHandleStandardArgs) # The default components were taken from a survey over other FindFFMPEG.cmake files if (NOT FFmpeg_FIND_COMPONENTS) - set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) endif () # From 282601d6e9d2ccdbf82ab793218ac210bc270489 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jan 2013 13:19:52 +0100 Subject: [PATCH 144/147] support the allowSkipping extra parameter for playBink command. --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +++--- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 11 ++++++++++- apps/openmw/mwrender/videoplayer.hpp | 5 ++++- apps/openmw/mwscript/miscextensions.cpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 198a20d454..7ec25f1ead 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -305,7 +305,7 @@ namespace MWBase /// \todo this does not belong here - virtual void playVideo(const std::string& name) = 0; + virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void stopVideo() = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3abf88cca3..ae7d6612bd 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -929,14 +929,14 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } -void RenderingManager::playVideo(const std::string& name) +void RenderingManager::playVideo(const std::string& name, bool allowSkipping) { - mVideoPlayer->playVideo ("video/" + name); + mVideoPlayer->playVideo ("video/" + name, allowSkipping); } void RenderingManager::stopVideo() { - mVideoPlayer->close (); + mVideoPlayer->stopVideo (); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 02b4bcef61..68f2d79c37 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -196,7 +196,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); - void playVideo(const std::string& name); + void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); protected: diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8e05a4f82b..618331c12e 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1010,6 +1010,7 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) , mVideoMaterial(NULL) , mRectangle(NULL) , mNode(NULL) + , mAllowSkipping(false) { mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); if (mVideoMaterial.isNull ()) @@ -1071,8 +1072,10 @@ VideoPlayer::~VideoPlayer() delete mBackgroundRectangle; } -void VideoPlayer::playVideo(const std::string &resourceName) +void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) { + mAllowSkipping = allowSkipping; + if(mState) close(); @@ -1113,6 +1116,12 @@ void VideoPlayer::update () } } +void VideoPlayer::stopVideo () +{ + if (mAllowSkipping) + close(); +} + void VideoPlayer::close() { if(mState) diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index d8c1902aa4..5e9d18ba41 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -20,11 +20,12 @@ namespace MWRender VideoPlayer(Ogre::SceneManager* sceneMgr); ~VideoPlayer(); - void playVideo (const std::string& resourceName); + void playVideo (const std::string& resourceName, bool allowSkipping); void update(); void close(); + void stopVideo(); bool isPlaying(); @@ -34,6 +35,8 @@ namespace MWRender private: VideoState* mState; + bool mAllowSkipping; + Ogre::SceneManager* mSceneMgr; Ogre::MaterialPtr mVideoMaterial; Ogre::Rectangle2D* mRectangle; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ef9976a0b3..80725b1f29 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -34,7 +34,10 @@ namespace MWScript std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getWorld ()->playVideo (name); + bool allowSkipping = runtime[0].mInteger; + runtime.pop(); + + MWBase::Environment::get().getWorld ()->playVideo (name, allowSkipping); } }; @@ -465,7 +468,7 @@ namespace MWScript extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); - extensions.registerInstruction ("playbink", "S", opcodePlayBink); + extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8eb121d374..6b06c60f51 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1287,9 +1287,9 @@ namespace MWWorld } - void World::playVideo (const std::string &name) + void World::playVideo (const std::string &name, bool allowSkipping) { - mRendering->playVideo(name); + mRendering->playVideo(name, allowSkipping); } void World::stopVideo () diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1c13529136..ce0bbb1c46 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -334,7 +334,7 @@ namespace MWWorld /// 3 - enemies are nearby (not implemented) /// \todo this does not belong here - virtual void playVideo(const std::string& name); + virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); }; } From c2901fe6ccb70b5511e631aab98946ac70d35f60 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 18:16:50 +0000 Subject: [PATCH 145/147] added addsoulgem scripting function --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/miscextensions.cpp | 33 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 89cacf65cb..8fd65bf8a7 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -306,5 +306,7 @@ op 0x20001ef: GetCurrentAIPackage op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference +op 0x20001f3: AddSoulGem +op 0x20001f4: AddSoulGem, explicit reference -opcodes 0x20001f3-0x3ffffff unused +opcodes 0x20001f5-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index b687471aa9..ff273a3337 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -14,6 +14,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -305,6 +307,32 @@ namespace MWScript MWMechanics::EffectKey(key)).mMagnitude > 0); } }; + + template + class OpAddSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string creature = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string gem = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); + + ref.getPtr().getRefData().setCount (1); + + ref.getPtr().getCellRef().mSoul = creature; + + MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); + + } + }; template class OpGetAttacked : public Interpreter::Opcode0 @@ -414,6 +442,8 @@ namespace MWScript const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; + const int opcodeAddSoulGem = 0x20001f3; + const int opcodeAddSoulGemExplicit = 0x20001f4; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; @@ -452,6 +482,7 @@ namespace MWScript extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); + extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); @@ -485,6 +516,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); + interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem); + interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); From 4c5ed43cc9cd94e4f4438da43813fc8939d07b6d Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 21:08:04 +0000 Subject: [PATCH 146/147] added removesoulgem, and fixed addsoulgem --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/miscextensions.cpp | 38 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 8fd65bf8a7..f0a346c9dc 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -308,5 +308,7 @@ op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference op 0x20001f3: AddSoulGem op 0x20001f4: AddSoulGem, explicit reference +op 0x20001f5: RemoveSoulGem +op 0x20001f6: RemoveSoulGem, explicit reference -opcodes 0x20001f5-0x3ffffff unused +opcodes 0x20001f7-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ff273a3337..9ca3503cf3 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -322,6 +322,9 @@ namespace MWScript std::string gem = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + store.get().find(creature); // This line throws an exception if it can't find the creature MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); @@ -334,6 +337,36 @@ namespace MWScript } }; + template + class OpRemoveSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + + MWWorld::Ptr ptr = R()(runtime); + + std::string soul = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + + + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) + { + if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) + { + if (iter->getRefData().getCount() <= 1) + iter->getRefData().setCount (0); + else + iter->getRefData().setCount (iter->getRefData().getCount() - 1); + break; + } + } + } + }; + template class OpGetAttacked : public Interpreter::Opcode0 { @@ -444,6 +477,8 @@ namespace MWScript const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; + const int opcodeRemoveSoulGem = 0x20001f5; + const int opcodeRemoveSoulGemExplicit = 0x20001f6; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; @@ -483,6 +518,7 @@ namespace MWScript extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); + extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); @@ -518,6 +554,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem); interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); + interpreter.installSegment5 (opcodeRemoveSoulGem, new OpRemoveSoulGem); + interpreter.installSegment5 (opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); From 9ee823d8f847c30629af818df9907973e9da5358 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 21:09:03 +0000 Subject: [PATCH 147/147] fixed typo in vmformat.txt --- apps/openmw/mwscript/docs/vmformat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index f0a346c9dc..66be100e56 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -301,7 +301,7 @@ op 0x20001ea: LowerRank op 0x20001eb: LowerRank, explicit op 0x20001ec: GetPCCrimeLevel op 0x20001ed: SetPCCrimeLevel -op 0x20001ee: SetPCCrimeLevel +op 0x20001ee: ModPCCrimeLevel op 0x20001ef: GetCurrentAIPackage op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f1: GetDetected