From 73c69e8eda4ade55ea95c89a9062340376918af4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 02:35:50 +0200 Subject: [PATCH 001/114] 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 66844b280..da1b3e15d 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 521bbb988..0ba4b84dd 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 64aa1dc21..74ecd531a 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 5a04a90c0..6ff4cee07 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 f0833e82d..98fc4175e 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 359809b71..d0ef3f593 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 000000000..04a6de30b --- /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 000000000..05c04c96b --- /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 a4a9e99fd..1c74a713f 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 a869f882b..330c1c79f 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 834dffe79..06b58c183 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 90cd2151b..5d8c689db 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 04600ce9b..0a0675fa0 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/114] 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 6ff4cee07..378003e39 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 04a6de30b..195369a2d 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 05c04c96b..1d3010e8e 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/114] 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 78388e20f..fcb5e9bbe 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 7b028e1d0..a6e80fc9b 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 2e755d047..000000000 --- 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 000000000..526be5f1b --- /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/114] 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 4679104d8..5e7d8eb55 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 1d3010e8e..5010ac516 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/114] more clean up, video played with correct speed, videos without sound working too (mw_credits.bik) --- apps/openmw/mwrender/videoplayer.cpp | 305 +++++++++++++++++---------- apps/openmw/mwrender/videoplayer.hpp | 45 ++-- 2 files changed, 223 insertions(+), 127 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 5e7d8eb55..e7f1eccde 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; + if (mVideoPacketQueue.getNumPackets ()) + decodeNextVideoFrame(); } - return saved; - - } - - void VideoPlayer::update() - { - if (!mAvContext) - return; - - // Time elapsed since the video started - float realTime = mTimer.getMilliseconds ()/1000.f; + mClock += dt; - // Here is the time we're at in the video - float movieTime = mDisplayedFrameCount * mWantedFrameTime; - - if (movieTime >= realTime) - return; + //curTime += fTime; - if (!mVideoPacketQueue.size() && mEOF) + if(mVideoPacketQueue.getNumPackets()==0 /* && mAudioPacketQueue.getNumPackets()==0 */) close(); - - Ogre::Timer timer; - - if (!mVideoPacketQueue.size()) - { - if (readFrameAndQueue()) - decodeFrontFrame(); - } - else - decodeFrontFrame(); - - mDecodingTime = timer.getMilliseconds ()/1000.f; } - void VideoPlayer::decodeFrontFrame () + void VideoPlayer::decodeNextVideoFrame () { - int didDecodeFrame = 0; - // Make sure there is something to decode - if (!mVideoPacketQueue.size()) - return; + assert (mVideoPacketQueue.getNumPackets ()); // Get the front frame and decode it - AVPacket *videoPacket = mVideoPacketQueue.front(); + AVPacket packet; + mVideoPacketQueue.get(&packet, 1); + int res; - res = avcodec_decode_video2(mVideoStream->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 5010ac516..466bb7902 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; - - int mDisplayedFrameCount; + double mClock; + double mVideoClock; + double mAudioClock; + 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/114] 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 e7f1eccde..b1fee213f 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/114] started over --- CMakeLists.txt | 4 + apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 1079 ++++++++++++++------- apps/openmw/mwrender/videoplayer.hpp | 227 +++-- cmake/FindSDL.cmake | 177 ++++ 5 files changed, 1022 insertions(+), 467 deletions(-) create mode 100644 cmake/FindSDL.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c11fda9f4..084363da7 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 a1a24b7ba..5663ded09 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 b1fee213f..3ddc7961c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1,520 +1,869 @@ #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) - { - } - int AVPacketQueue::put(AVPacket* pkt) - { - if(av_dup_packet(pkt) < 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 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; - - mLastPacket = pkt1; - mNumPackets++; - mSize += pkt1->pkt.size; + q->mutex.lock (); + 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; - int AVPacketQueue::get(AVPacket* pkt, int block) - { - AVPacketList* pkt1; + boost::unique_lock lock(q->mutex); - while (true) - { - pkt1 = mFirstPacket; - if (pkt1 != NULL) - { - mFirstPacket = pkt1->next; + for(;;) { - if (mFirstPacket == NULL) mLastPacket = NULL; + if(global_video_state->quit) { + ret = -1; + break; + } - mNumPackets--; - mSize -= pkt1->pkt.size; + 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); - return 1; + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + + + q->cond.wait(lock); } - else if (block == 0) - { - return 0; + } + 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; } - else - { - return -1; + 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; + } + } + } - 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); - videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); + 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; + } + } - 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); + /* + 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 } + */ - VideoPlayer::~VideoPlayer() + void timer_callback (int delay, VideoState* is) { - if (mAvContext) - deleteContext(); - - delete mRectangle; + boost::this_thread::sleep (boost::posix_time::milliseconds(delay)); + is->refresh++; } - void VideoPlayer::play (const std::string& resourceName) + /* schedule a video refresh in 'delay' ms */ + static void schedule_refresh(VideoState *is, int delay) { - mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName); - - - mVideoStreamId = -1; - mAudioStreamId = -1; - mAudioStream = NULL; - mVideoStream = NULL; - mVideoClock = 0; - mAudioClock = 0; - mClock = 0; + //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); + //is->refresh_queue.push_back (delay); - // if something is already playing, close it - if (mAvContext) - close(); + boost::thread (boost::bind(&timer_callback, delay, is)); + } - mRectangle->setVisible(true); + void video_display(VideoState *is) + { + VideoPicture *vp; - MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + 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); + } - // BASIC INITIALIZATION + free(vp->data); + } - // Load all the decoders - av_register_all(); - AVIOContext *ioContext = 0; + void video_refresh_timer(void *userdata) { - int err = 0; + VideoState *is = (VideoState *)userdata; + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; - mAvContext = avformat_alloc_context(); - if (!mAvContext) - throwError(0); + if(is->video_st) { + if(is->pictq_size == 0) { + schedule_refresh(is, 1); + } else { + vp = &is->pictq[is->pictq_rindex]; - ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throwError(0); + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); - mAvContext->pb = ioContext; + 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; + } + } + } - err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL); - if (err != 0) - throwError(err); + 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)); - err = avformat_find_stream_info(mAvContext, 0); - if (err < 0) - throwError(err); + /* 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; - // Find the video stream among the different streams - for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + /* wait until we have a new pic */ { - if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) - { - mVideoStreamId = i; - mVideoStream = mAvContext->streams[i]; - break; + 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 (mVideoStreamId < 0) - throw std::runtime_error("No video stream found in the video"); - - // Get the video decoder - mVideoCodec = avcodec_find_decoder(mVideoStream->codec->codec_id); - if (NULL == mVideoCodec) - throw std::runtime_error("No video decoder found"); + if(is->quit) + return -1; - // Load the video codec - err = avcodec_open2(mVideoStream->codec, mVideoCodec, 0); - if (err < 0) - throwError (err); + // 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); - // 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"); - } + vp->pts = pts; - // Load the audio codec - err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); - if (err < 0) - throwError (err); + // 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) { - // Create the frame buffers - mRawFrame = avcodec_alloc_frame(); - mRGBAFrame = avcodec_alloc_frame(); - if (!mRawFrame || !mRGBAFrame) - { - throw std::runtime_error("Can't allocate video frames"); + 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); + } - avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoStream->codec->width, mVideoStream->codec->height); + int video_thread(void *arg) { + VideoState *is = (VideoState *)arg; + AVPacket pkt1, *packet = &pkt1; + int len1, frameFinished; + AVFrame *pFrame; + double pts; + pFrame = avcodec_alloc_frame(); - // 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"); + is->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc ((AVPicture *)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); - mTextureUnit->setTextureName (""); - if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) - Ogre::TextureManager::getSingleton().remove("VideoTexture"); + 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); - 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); + // 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); } - memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoStream->codec->width*mVideoStream->codec->height*4); - mVideoTexture->getBuffer()->unlock(); - mTextureUnit->setTextureName ("VideoTexture"); + SDL_CloseAudio(); + av_free(pFrame); - // Queue up some packets - while( - mVideoPacketQueue.getNumPackets()rgbaFrame); + av_free(is->rgbaFrame); - mTimer.reset(); + return 0; } - void VideoPlayer::throwError(int error) + int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) { - char buffer[4096] = {0}; + AVCodecContext *codecCtx; + AVCodec *codec; + SDL_AudioSpec wanted_spec, spec; - if (0 == av_strerror(error, buffer, sizeof(buffer))) - { - std::stringstream msg; - msg << "FFMPEG error: "; - msg << buffer << std::endl; - throw std::runtime_error(msg.str()); + if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + return -1; } - else - throw std::runtime_error("Unknown FFMPEG error"); + + // 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; + } + + } - void VideoPlayer::update() - { - if (!mAvContext) - return; + 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"); - double dt = mTimer.getMilliseconds () / 1000.f; - mTimer.reset (); + // Retrieve stream information + if(avformat_find_stream_info(pFormatCtx, NULL)<0) + throw std::runtime_error("Failed to retrieve stream information"); - //UpdateAudio(fTime); - //std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; - while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) + // 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*/) { - while( - mVideoPacketQueue.getNumPackets()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)); } - - if (mVideoPacketQueue.getNumPackets ()) - decodeNextVideoFrame(); } - mClock += dt; - - //curTime += fTime; + is->quit = 1; - if(mVideoPacketQueue.getNumPackets()==0 /* && mAudioPacketQueue.getNumPackets()==0 */) - close(); - } + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); - void VideoPlayer::decodeNextVideoFrame () - { - // Make sure there is something to decode - assert (mVideoPacketQueue.getNumPackets ()); + is->video_thread.join(); - // Get the front frame and decode it - AVPacket packet; - mVideoPacketQueue.get(&packet, 1); + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); - int res; - int didDecodeFrame = 0; - res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet); + sws_freeContext (is->sws_context); - if (res < 0 || !didDecodeFrame) - throw std::runtime_error ("an error occured while decoding the video frame"); + av_close_input_file(pFormatCtx); + pFormatCtx = NULL; - // 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; + av_free(ioContext); - // Convert the frame to RGB - sws_scale(mSwsContext, - mRawFrame->data, mRawFrame->linesize, - 0, mVideoStream->codec->height, - mRGBAFrame->data, mRGBAFrame->linesize); + return 0; + } - 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); + 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(); - if (packet.data != NULL) av_free_packet(&packet); + 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); } - void VideoPlayer::close () + VideoPlayer::~VideoPlayer () { - mRectangle->setVisible (false); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); - deleteContext(); + if (mState) + close(); } - void VideoPlayer::deleteContext() + void VideoPlayer::playVideo (const std::string &resourceName) { - 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 (mState) + close(); - if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); - if (mAudioStream && mAudioStream->codec != NULL) avcodec_close(mAudioStream->codec); + mRectangle->setVisible(true); - avpicture_free((AVPicture *)mRGBAFrame); + MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); - if (mRawFrame) - av_free(mRawFrame); - if (mRGBAFrame) - av_free(mRGBAFrame); + mState = new VideoState; - sws_freeContext(mSwsContext); + // Register all formats and codecs + av_register_all(); - avformat_close_input(&mAvContext); + if(SDL_Init(SDL_INIT_AUDIO)) { + throw std::runtime_error("Failed to initialize SDL"); + } - mAvContext = NULL; - } + mState->refresh = 0; + mState->resourceName = resourceName; + schedule_refresh(mState, 40); + mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + mState->parse_thread = boost::thread(decode_thread, mState); + } - bool VideoPlayer::addToBuffer() + void VideoPlayer::update () { - if(mAvContext) + if (mState && mState->refresh) { - 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; - } - } + video_refresh_timer (mState); + mState->refresh--; + } + if (mState && mState->quit) + { + close(); } - return false; + if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + } + + void VideoPlayer::close() + { + mState->quit = 1; + + mState->parse_thread.join (); + + delete mState; + mState = NULL; + + mRectangle->setVisible (false); + MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); } -} -//#endif + bool VideoPlayer::isPlaying () + { + return mState != NULL; + } + +} diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 466bb7902..9766ba8ee 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/114] DataStreamPtr fix, indentation fixes --- apps/openmw/mwrender/videoplayer.cpp | 114 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 2 + 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 3ddc7961c..40d1a7188 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; - - /* 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; - } - } + 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; + /* 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 9766ba8ee..67a22a506 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/114] 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 526f865bc..198a20d45 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 6a1c3aa9b..f7cc0f88e 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 5663ded09..811d000a9 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 ee64a371e..02b4bcef6 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 2257b8a07..8eb121d37 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 349fd163c..1c1352913 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/114] 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 40d1a7188..46fa4b9e9 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/114] 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 46fa4b9e9..612b37c0a 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/114] 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 612b37c0a..b65458b63 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/114] 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 b65458b63..7e7fcd0d6 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/114] 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 7e7fcd0d6..c09de3acc 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/114] 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 e5169d878..1e862d77a 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/114] 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 c09de3acc..afdb5e82d 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/114] 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 afdb5e82d..8ae51deba 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/114] 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 1e862d77a..abd63590f 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 fecffa575..4177c6385 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/114] 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 92c177ff3..2fa98cfee 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 8ae51deba..1334c1bca 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 abd63590f..bc6102beb 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 4177c6385..867150741 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 2680ec1db..1cc45559b 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 8c4798c9d..1414dbb76 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 a84aa3b9a..aaa362d84 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/114] fix a bunch of warnings, improved error handling, initialize texture to black --- apps/openmw/mwrender/videoplayer.cpp | 232 ++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 11 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 6 +- 3 files changed, 136 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 46fa4b9e9..07fa723e2 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; + try + { + AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVPacket pkt1, *packet = &pkt1; - int video_index = -1; - int audio_index = -1; - int i; + int video_index = -1; + int audio_index = -1; + unsigned int i; - is->videoStream=-1; - is->audioStream=-1; - is->quit = 0; + 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; + 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; + 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 "); + 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; + pFormatCtx->pb = ioContext; - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); + 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"); + // 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"); + // 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); + // 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; + 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(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*/) - { + if(is->videoStream >= 0 /*|| is->audioStream < 0*/) + { - // main decode loop + // 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; + 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); + } } - // 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)); } } - /* 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->quit = 1; - is->video_thread.join(); + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); - if (is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if (is->videoStream >= 0) - avcodec_close(is->video_st->codec); + is->video_thread.join(); - sws_freeContext (is->sws_context); + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); - av_close_input_file(pFormatCtx); - pFormatCtx = NULL; + sws_freeContext (is->sws_context); - av_free(ioContext); + 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; + } 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 67a22a506..d9dc6ded0 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 5f61ab8f0..9d5942111 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/114] 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 bc6102beb..4c62bb10f 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 729147f75..1b6f50ff4 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/114] 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 9cfe703b4..27fb89130 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/114] 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 4e73573a7..c9b3d11d1 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 0ad026d51..1e1528880 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 9d5942111..6b3a7f9cd 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 a6e80fc9b..88115ce3f 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 7f7a84889..4ec11b349 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 9c28d5ff5..d228a5e98 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/114] 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 4c62bb10f..521e6c357 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 867150741..ce126035f 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 1cc45559b..1229f87d0 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 1414dbb76..2a489a789 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/114] Implement getTimeOffset for OpenAL_SoundStream --- apps/openmw/mwsound/openal_output.cpp | 130 ++++++++++++++++++-------- 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 521e6c357..658483b02 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; - - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); - throwALerror(); + try { + bool finished = mIsFinished; + ALint samples_unqueued = 0; + ALint samples_queued = 0; + ALint processed, state; - 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; + if(processed > 0) + { + 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(); + } - alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); - throwALerror(); - if(queued > 0) + if(state != AL_PLAYING && state != AL_PAUSED) { - alSourcePlay(mSource); + ALint queued = 0; + + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + if(queued > 0) + alSourcePlay(mSource); throwALerror(); } - } - mIsFinished = finished; - return !finished; + 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/114] 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 2fa98cfee..8d204bad4 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 2a489a789..6bb3d59dd 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/114] Remove SDL for playing movie audio and prepare for using an audio track This breaks audio playback on movies --- apps/openmw/mwrender/videoplayer.cpp | 322 ++++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 71 +++--- 2 files changed, 226 insertions(+), 167 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 27fb89130..fa179f8b1 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); - 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; + 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 = 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 open(const std::string&) + { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } + + 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; } - void audio_callback(void *userdata, Uint8 *stream, int len) { - VideoState *is = (VideoState *)userdata; - int len1, audio_size; - double pts; + size_t read(char *stream, size_t len) + { + size_t total = 0; - while(len > 0) { - if(is->audio_buf_index >= is->audio_buf_size) { + 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(SDL_OpenAudio(&wanted_spec, &spec) < 0) { - fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); - return -1; - } - is->audio_hw_buf_size = spec.size; - } + if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) + return -1; + 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 d9dc6ded0..eac906356 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; - int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; - double audio_clock; - AVStream *audio_st; - PacketQueue audioq; + 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/114] 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 658483b02..4bdbf0101 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/114] 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 4bdbf0101..c7b16dd39 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/114] 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 fa179f8b1..0268539d5 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 eac906356..e97fa6e13 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/114] 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 0268539d5..d4fa7688f 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/114] 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 d4fa7688f..94f40e0e9 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 e97fa6e13..2ce58846f 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/114] 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 94f40e0e9..df4d7c8ed 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/114] 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 df4d7c8ed..6e6db5f27 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 6b3a7f9cd..9fee0d9b0 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 c7b16dd39..fd4fdaa9e 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 d228a5e98..f78a1cd28 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 6bb3d59dd..efdb6f7ea 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/114] 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 6e6db5f27..2d43e8ddc 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/114] 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 2d43e8ddc..bd29c69bf 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 c9b3d11d1..788d5ae40 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 1e1528880..8623a3f2c 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 9fee0d9b0..6e60a7b9e 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; +} + +size_t FFmpeg_Decoder::getSampleOffset() +{ + return mSamplesRead; } -FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL) +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 88115ce3f..ff63edf07 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 4ec11b349..fb187f844 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 09082c2f4..52c37bb87 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 fd4fdaa9e..672e52b56 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/114] 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 bd29c69bf..a5743356a 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 2ce58846f..ace36efb9 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/114] 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 a5743356a..21805a792 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/114] 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 21805a792..7e2be7e10 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/114] 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 7e2be7e10..299b34c32 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; + + 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"); + } - 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); + // 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/114] 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 7e2be7e10..6acbe8b79 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 ace36efb9..689b3ad36 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/114] 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 c2781935c..38b519b21 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/114] Reduce some indentation --- apps/openmw/mwrender/videoplayer.cpp | 109 ++++++++++++++------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 38b519b21..a6e033216 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 - { - vp = &is->pictq[is->pictq_rindex]; + schedule_refresh(is, 100); + return; + } + if(is->pictq_size == 0) + { + schedule_refresh(is, 1); + return; + } - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); + vp = &is->pictq[is->pictq_rindex]; - 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; + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); - /* 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; + 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; - /* 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; - } - } + /* 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; - 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 (); + /* 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; } } - 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/114] 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 a6e033216..6307a0028 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 689b3ad36..67b619ad2 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/114] Move a couple packet queue methods into the struct --- apps/openmw/mwrender/videoplayer.cpp | 65 +++++++++++++--------------- apps/openmw/mwrender/videoplayer.hpp | 34 +++++++++------ 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6307a0028..b4fd264ec 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 67b619ad2..5dfa3cf56 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 (); + void put(AVPacket *pkt); + int get(AVPacket *pkt, VideoState *is, int block); + + void flush(); }; + struct VideoPicture { - VideoPicture () : - data(NULL), pts(0) - {} - uint8_t* data; + 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/114] Move some methods into their respective class --- apps/openmw/mwrender/videoplayer.cpp | 289 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 8 +- 2 files changed, 151 insertions(+), 146 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b4fd264ec..ec68359aa 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,122 +11,90 @@ namespace MWRender { - int 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 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(); +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(); +} - this->mutex.unlock(); - } +int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) +{ + AVPacketList *pkt1; + int ret; - int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) + boost::unique_lock lock(this->mutex); + for(;;) { - 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; + ret = -1; + break; + } - *pkt = pkt1->pkt; - av_free(pkt1); + 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; - ret = 1; - break; - } + *pkt = pkt1->pkt; + av_free(pkt1); - if (!block) - { - ret = 0; - break; - } + ret = 1; + break; + } - this->cond.wait(lock); + if (!block) + { + ret = 0; + break; } - return ret; + this->cond.wait(lock); } - void PacketQueue::flush() - { - AVPacketList *pkt, *pkt1; + return ret; +} - 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 (); +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 5dfa3cf56..82a7083ae 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/114] Move some more methods to the class they're part of --- apps/openmw/mwrender/videoplayer.cpp | 796 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 14 + 2 files changed, 401 insertions(+), 409 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ec68359aa..4334bc634 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,478 +412,458 @@ 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; } - - void video_display(VideoState *is) + if(this->pictq_size == 0) { - VideoPicture *vp; + this->refresh = true; + return; + } - vp = &is->pictq[is->pictq_rindex]; + vp = &this->pictq[this->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 () || 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; - } + this->video_current_pts = vp->pts; + this->video_current_pts_time = av_gettime(); - free(vp->data); + 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; - - void video_refresh_timer(void *userdata) + /* update delay to sync to audio if not master source */ + if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - VideoState *is = (VideoState *)userdata; - VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + ref_clock = get_master_clock(this); + diff = vp->pts - ref_clock; - if(!is->video_st) - { - schedule_refresh(is, 100); - return; - } - if(is->pictq_size == 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) { - is->refresh = true; - return; + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } + } + this->frame_timer += delay; - vp = &is->pictq[is->pictq_rindex]; + /* 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)); - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); + /* show the picture! */ + this->video_display(); - 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 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(); +} - /* 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; - } - } +int VideoState::queue_picture(AVFrame *pFrame, double pts) +{ + VideoPicture *vp; - 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)); + /* 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; - /* show the picture! */ - video_display(is); + // windex is set to 0 initially + vp = &this->pictq[this->pictq_windex]; - /* 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 (); + // 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"); } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) - { - VideoPicture *vp; + vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); - /* 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)); - } + sws_scale(this->sws_context, pFrame->data, pFrame->linesize, + 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); - if(is->quit) - return -1; + vp->pts = pts; - // windex is set to 0 initially - vp = &is->pictq[is->pictq_windex]; + // 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(); - // 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"); - } + return 0; +} - vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); +double VideoState::synchronize_video(AVFrame *src_frame, double pts) +{ + double frame_delay; - sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); + /* if we have pts, set video clock to it */ + if(pts != 0) + this->video_clock = pts; + else + pts = this->video_clock; - vp->pts = pts; + /* update the video clock */ + frame_delay = av_q2d(this->video_st->codec->time_base); - // 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(); + /* 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 0; - } + return pts; +} - 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; - } +/* 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); +} - 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); - } +void VideoState::video_thread_loop(VideoState *self) +{ + AVPacket pkt1, *packet = &pkt1; + int frameFinished; + AVFrame *pFrame; + double pts; - int video_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - AVPacket pkt1, *packet = &pkt1; - int frameFinished; - AVFrame *pFrame; - double pts; + pFrame = avcodec_alloc_frame(); - 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); - is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + while(self->videoq.get(packet, self, 1) >= 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) + throw std::runtime_error("Error decoding video frame"); - for(;;) + 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) { - if(is->videoq.get(packet, is, 1) < 0) - { - // means we quit getting packets + pts = self->synchronize_video(pFrame, pts); + if(self->queue_picture(pFrame, pts) < 0) break; - } - 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((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); - - // 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); + av_free(pFrame); - avpicture_free((AVPicture *)is->rgbaFrame); - av_free(is->rgbaFrame); + avpicture_free((AVPicture*)self->rgbaFrame); + av_free(self->rgbaFrame); +} - return 0; - } +void VideoState::decode_thread_loop(VideoState *self) +{ + AVFormatContext *pFormatCtx = self->format_ctx; + AVPacket pkt1, *packet = &pkt1; - int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) + try { - 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; - } + if(self->videoStream < 0 && self->audioStream < 0) + throw std::runtime_error("No streams to decode"); - switch(codecCtx->codec_type) + // main decode loop + for(;;) { - 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)); + if(self->quit) + break; - decoder.reset(new MovieAudioDecoder(is)); - is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); - if(!is->AudioTrack) + if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { - is->audioStream = -1; - avcodec_close(is->audio_st->codec); - is->audio_st = NULL; - return -1; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; } - break; - case AVMEDIA_TYPE_VIDEO: - is->videoStream = stream_index; - is->video_st = pFormatCtx->streams[stream_index]; + if(av_read_frame(pFormatCtx, packet) < 0) + break; - is->frame_timer = (double)av_gettime() / 1000000.0; - is->frame_last_delay = 40e-3; - is->video_current_pts_time = av_gettime(); + // 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 + av_free_packet(packet); + } + /* 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; + } - codecCtx->get_buffer = our_get_buffer; - codecCtx->release_buffer = our_release_buffer; - is->video_thread = boost::thread(video_thread, is); - break; + self->quit = 1; +} - default: - break; - } - return 0; - } +int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) +{ + MWSound::DecoderPtr decoder; + AVCodecContext *codecCtx; + AVCodec *codec; - int decode_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - try - { - AVFormatContext *pFormatCtx = is->format_ctx; - AVPacket pkt1, *packet = &pkt1; + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) + return -1; - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) - { - // main decode loop - for(;;) - { - if(is->quit) - break; + // 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; + } - 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; - } + switch(codecCtx->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + this->audioStream = stream_index; + this->audio_st = pFormatCtx->streams[stream_index]; - if(av_read_frame(pFormatCtx, packet) < 0) - break; + /* 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 */; - // 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)); - } - } + memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); - is->quit = 1; - } - catch (std::runtime_error& e) + decoder.reset(new MovieAudioDecoder(this)); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!this->AudioTrack) { - 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; + this->audioStream = -1; + avcodec_close(this->audio_st->codec); + this->audio_st = NULL; + return -1; } + break; - return 0; - } + case AVMEDIA_TYPE_VIDEO: + this->videoStream = stream_index; + this->video_st = pFormatCtx->streams[stream_index]; - 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->frame_timer = (double)av_gettime() / 1000000.0; + this->frame_last_delay = 40e-3; + this->video_current_pts_time = av_gettime(); - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + this->video_thread = boost::thread(video_thread_loop, this); + break; - 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 "); - } + default: + break; + } - // 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"); - } + return 0; +} - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); +void VideoState::init(const std::string& resourceName) +{ + try + { + int video_index = -1; + int audio_index = -1; + unsigned int i; - // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 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->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); - 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); + 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 "); } - catch(std::runtime_error& e) + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { - this->quit = 1; - throw; + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); } - catch(Ogre::Exception& e) + + // 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 = 1; - 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; } - } - void VideoState::deinit() + 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->audioq.cond.notify_one(); - this->videoq.cond.notify_one(); + this->quit = 1; + throw; + } + catch(Ogre::Exception& e) + { + this->quit = 1; + throw; + } +} - this->parse_thread.join(); - this->video_thread.join(); +void VideoState::deinit() +{ + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); - if(this->audioStream >= 0) - avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); + this->parse_thread.join(); + this->video_thread.join(); - if(this->sws_context) - sws_freeContext(this->sws_context); + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); - if(this->format_ctx) - { - AVIOContext *ioContext = this->format_ctx->pb; - avformat_close_input(&this->format_ctx); - av_free(ioContext); - } + 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) @@ -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 82a7083ae..4bbd81a6e 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/114] 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 4334bc634..6271b2f18 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/114] 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 6271b2f18..432cfae3e 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/114] 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 432cfae3e..b901c097d 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/114] 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 b901c097d..b9ff81de0 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/114] 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 b9ff81de0..9cbac1688 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 4bbd81a6e..428907c4a 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/114] 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 9cbac1688..56f0bc691 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 428907c4a..798611d90 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/114] 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 56f0bc691..4e9dd0ca2 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/114] Don't assume we can write beyond the end of the sample buffer --- apps/openmw/mwrender/videoplayer.cpp | 89 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4e9dd0ca2..93255dd45 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); - - 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; - } + wanted_size = std::min(wanted_size, samples_size*2); + + 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 = len - total; + if(mFramePos >= 0) + { + len1 = std::min(len1, mFrameSize-mFramePos); + memcpy(stream, mFrame->data[0]+mFramePos, len1); } + else + { + len1 = std::min(len1, -mFramePos); - size_t len1 = std::min(len - total, mFrameSize-mFramePos); - memcpy(stream, mFrame->data[0]+mFramePos, len1); + 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/114] 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 93255dd45..396b91c33 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/114] Always try to resync if the clock difference is large --- apps/openmw/mwrender/videoplayer.cpp | 56 +++++++++++----------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 396b91c33..bdfffd0cb 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,37 +160,27 @@ 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 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 { - // 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) { - 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); + 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); + wanted_size = std::max(0, wanted_size); + wanted_size = std::min(wanted_size, samples_size*2); - samples_size = wanted_size; - } + 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; } @@ -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/114] 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 bdfffd0cb..fe228d25e 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 798611d90..1213ca36c 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/114] 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 fe228d25e..2a545f772 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 1213ca36c..7d3e4ac02 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/114] 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 672e52b56..bfead7843 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/114] 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 2a545f772..7281e1c3a 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/114] 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 7281e1c3a..755378c28 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/114] 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 755378c28..e54b4c5d9 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/114] Clean up the rectangle and scene node used for displaying the video --- apps/openmw/mwrender/videoplayer.cpp | 171 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 3 +- 2 files changed, 94 insertions(+), 80 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e54b4c5d9..5921a4e6f 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(); - - 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(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 + mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mNode->attachObject(mRectangle); + + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags(0x1); +} - VideoPlayer::~VideoPlayer () - { - if (mState) - close(); - } +VideoPlayer::~VideoPlayer() +{ + if(mState) + close(); - void VideoPlayer::playVideo (const std::string &resourceName) - { - // Register all formats and codecs - av_register_all(); + if(mNode) + mSceneMgr->destroySceneNode(mNode); + mNode = NULL; - if (mState) - close(); + if(mRectangle) + delete mRectangle; + mRectangle = NULL; +} - mRectangle->setVisible(true); +void VideoPlayer::playVideo(const std::string &resourceName) +{ + // Register all formats and codecs + av_register_all(); - MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + if(mState) + close(); - // 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); + mRectangle->setVisible(true); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); - mState = new VideoState; - mState->init(resourceName); + // 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); + + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + + mState = new VideoState; + mState->init(resourceName); +} - void VideoPlayer::update () +void VideoPlayer::update () +{ + if(mState) { - if(mState) + if(mState->quit) + close(); + else if(mState->refresh) { - if(mState->quit) - close(); - else if(mState->refresh) - { - mState->refresh = false; - mState->video_refresh_timer(); - } + 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"); } - - 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(); +void VideoPlayer::close() +{ + mState->quit = 1; + mState->deinit(); - delete mState; - mState = NULL; + delete mState; + mState = NULL; - MWBase::Environment::get().getSoundManager()->resumeAllSounds(); + MWBase::Environment::get().getSoundManager()->resumeAllSounds(); - mRectangle->setVisible (false); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + mRectangle->setVisible(false); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); - mSceneMgr->clearSpecialCaseRenderQueues(); - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - } + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); +} + +bool VideoPlayer::isPlaying () +{ + return mState != NULL; +} - bool VideoPlayer::isPlaying () - { - return mState != NULL; - } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 7d3e4ac02..9060241bd 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/114] 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 5921a4e6f..4f42c3fb6 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/114] 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 4f42c3fb6..e7bdfab91 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/114] 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 e7bdfab91..bfcb92745 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/114] 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 bfcb92745..407f1c212 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/114] 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 bfcb92745..b0bf37154 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/114] 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 b0bf37154..f6edf9ad9 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) +void VideoState::video_refresh(VideoState* is) { - 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/114] 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 811d000a9..3abf88cca 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 4f9963de5..11b6d2b8f 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 9060241bd..c82a16e15 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/114] 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 11b6d2b8f..740da7a12 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/114] 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 740da7a12..0165d9855 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/114] 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 9aaa5af85..5a13a50ec 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/114] 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 258a40e1c..373546aa8 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/114] 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 6e60a7b9e..205dddd35 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; - - last = &(*iter)->mPackets; - while(*last != NULL) - last = &(*last)->next; + PacketList **last; - *last = packet; - if((*iter)->mStreamIdx == streamidx) - return true; + last = &mStream->mPackets; + while(*last != NULL) + last = &(*last)->next; - 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 ff63edf07..b7d2e1bb2 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/114] 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 205dddd35..70f7682ef 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/114] 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 70f7682ef..7b6144dd0 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); - - /* Set the output buffer size */ - mDecodedDataSize = size; - if(length) *length = mDecodedDataSize; + } while(got_frame == 0 || mFrame->nb_samples == 0); - 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; - - /* Copy the data to the app's buffer and increment */ - if(data != NULL) - { - memcpy(data, mDecodedData, rem); - data = (char*)data + rem; - } - dec += rem; + /* 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); - /* 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/114] 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 7b6144dd0..e0f071568 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 b7d2e1bb2..dbd4f5adc 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/114] 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 0165d9855..d57295f65 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/114] 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 d57295f65..cac25713a 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/114] 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 e0f071568..261a86ca6 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 dbd4f5adc..32b2797ed 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/114] 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 cac25713a..c5976222f 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/114] 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 c5976222f..81eba2417 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/114] 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 81eba2417..817ec0a3f 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/114] We no longer need SDL --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 084363da7..f320c5c3c 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/114] Look for all available sound input libs as needed, and warn if not found --- CMakeLists.txt | 58 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f320c5c3c..182cc268a 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_MPG123) +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 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/114] 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 817ec0a3f..1c22b7b3d 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/114] 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 1c22b7b3d..d2e317b71 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/114] 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 198a20d45..3c707d196 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 e98b75e9b..b19f3de6d 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 fd583d187..ab48f29d9 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 373546aa8..627f8f339 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 8eb121d37..f1fb67632 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 1c1352913..a58e70d12 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/114] 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 2299053cd..08e24b5d6 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 57402c91e..b13ec4368 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 0563fdbbb..6b31f3ade 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 c177912d4..9df75870d 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 b19f3de6d..0f74f6e14 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 627f8f339..1f5966f72 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 2e684b5da..db5c22348 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 f1fb67632..afcdb8d39 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 a58e70d12..8f89ecff3 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/114] 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 d2e317b71..3e1701095 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/114] 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 3e1701095..9c4546d15 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 c82a16e15..d8c1902aa 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/114] Fix for trying to play videos when not supported --- apps/openmw/mwrender/videoplayer.cpp | 113 ++++++++++++++------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9c4546d15..9fdd051b6 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; + int video_index = -1; + int audio_index = -1; + unsigned int i; - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); + this->av_sync_type = AV_SYNC_DEFAULT; + this->refresh_rate_ms = 10; + this->refresh = false; + this->quit = false; - 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->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(); - if(this->format_ctx) - this->format_ctx->pb = ioCtx; + 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"); - // 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"); - } + this->format_ctx = avformat_alloc_context(); + if(this->format_ctx) + this->format_ctx->pb = ioCtx; - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); + // 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"); + } - // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); - 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; - } + // Dump information about file onto standard error + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); - 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); - } - catch(...) + 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/114] 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 9fdd051b6..38f7a9d16 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/114] 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 38f7a9d16..56312aa26 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/114] 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 56312aa26..9c72e9919 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/114] 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 efdb6f7ea..4ec3dfbb3 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/114] 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 bfead7843..94499c8fb 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 ce126035f..41ce56965 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/114] 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 94499c8fb..824a72299 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 41ce56965..29d4102c4 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 1b6f50ff4..e76912d67 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 1229f87d0..baed39da0 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 4ec3dfbb3..ff10e567a 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/114] 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 8d204bad4..de17f8dc8 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 ff10e567a..64c3d9903 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/114] 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 de17f8dc8..a47b9a6ca 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 9c72e9919..e5dee76af 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 64c3d9903..2c4e8eff0 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 26ff309fb..879110b9f 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/114] 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 e76912d67..8deaf26a6 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 2c4e8eff0..34524ba9a 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 879110b9f..f9ceeded6 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/114] 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 a47b9a6ca..d804830fa 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 e5dee76af..8e05a4f82 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 824a72299..406c1bcca 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 29d4102c4..ce35f4e68 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 baed39da0..b5ccc946a 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 34524ba9a..165d501a2 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 f9ceeded6..14b8e5cef 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/114] 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 165d501a2..d801e6618 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 14b8e5cef..d26f51ef0 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/114] 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 406c1bcca..67008e2bc 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 ce35f4e68..e240a8b01 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/114] 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 d804830fa..0c706ab49 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 35e66241e..408a6f29d 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 d801e6618..8a69fc96b 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 d26f51ef0..b475449d9 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 009b325c0..19bc881f7 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/114] 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 08e24b5d6..2299053cd 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 b13ec4368..57402c91e 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 6b31f3ade..0563fdbbb 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 9df75870d..c177912d4 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 0f74f6e14..b19f3de6d 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 1f5966f72..627f8f339 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 db5c22348..2e684b5da 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 afcdb8d39..f1fb67632 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 8f89ecff3..a58e70d12 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/114] 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 3c707d196..198a20d45 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 b19f3de6d..e98b75e9b 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 ab48f29d9..fd583d187 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 627f8f339..373546aa8 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 f1fb67632..8eb121d37 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 a58e70d12..1c1352913 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/114] 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 e240a8b01..02706b50c 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/114] 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 8e05a4f82..2d24edc51 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/114] 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 8623a3f2c..91c07ccac 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 52c37bb87..be52f6f49 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 35f4d09288aa703cae11a4c04e8a039f978fc378 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jan 2013 13:06:16 +0100 Subject: [PATCH 113/114] 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 182cc268a..148f65bdd 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 57e81e5ee..4f290c46f 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 526be5f1b..c80203a25 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 114/114] 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 198a20d45..7ec25f1ea 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 3abf88cca..ae7d6612b 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 02b4bcef6..68f2d79c3 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 8e05a4f82..618331c12 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 d8c1902aa..5e9d18ba4 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 ef9976a0b..80725b1f2 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 8eb121d37..6b06c60f5 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 1c1352913..ce0bbb1c4 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(); }; }